Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: save YAML spec used to generate support bundle/preflight #1713

Merged
merged 5 commits into from
Jan 4, 2025
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions pkg/constants/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ const (
DEFAULT_CLIENT_USER_AGENT = "ReplicatedTroubleshoot"
// VERSION_FILENAME is the name of the file that contains the support bundle version.
VERSION_FILENAME = "version.yaml"
// SPEC_FILENAME is the name of the file that contains the support bundle/preflight spec.
SPEC_FILENAME = "spec.yaml"
// DEFAULT_LOGS_COLLECTOR_TIMEOUT is the default timeout for logs collector.
DEFAULT_LOGS_COLLECTOR_TIMEOUT = 60 * time.Second
// MAX_TIME_TO_WAIT_FOR_POD_DELETION is the maximum time to wait for pod deletion.
Expand Down
29 changes: 29 additions & 0 deletions pkg/preflight/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"github.com/replicatedhq/troubleshoot/pkg/constants"
"github.com/replicatedhq/troubleshoot/pkg/convert"
"github.com/replicatedhq/troubleshoot/pkg/k8sutil"
"github.com/replicatedhq/troubleshoot/pkg/loader"
"github.com/replicatedhq/troubleshoot/pkg/types"
"github.com/replicatedhq/troubleshoot/pkg/version"
"github.com/spf13/viper"
Expand Down Expand Up @@ -180,6 +181,13 @@ func RunPreflights(interactive bool, output string, format string, args []string
return errors.Wrap(err, "failed to save version file")
}

// save final preflight spec used to geneate the preflight checks
err = savePreflightSpecToBundle(specs, collectorResults, bundlePath)
if err != nil {
// still allow the preflight to be created
klog.Errorf("failed to save preflight YAML spec: %v", err)
}

analyzeResults, err := analyzer.AnalyzeLocal(ctx, bundlePath, analyzers, hostAnalyzers)
if err != nil {
return errors.Wrap(err, "failed to analyze support bundle")
Expand Down Expand Up @@ -469,3 +477,24 @@ func parseTimeFlags(v *viper.Viper, collectors []*troubleshootv1beta2.Collect) e
}
return nil
}

func savePreflightSpecToBundle(specs *loader.TroubleshootKinds, result collect.CollectorResult, bundlePath string) error {
yamlContent, err := specs.ToYaml()
if err != nil {
return errors.Wrap(err, "failed to convert preflight specs to yaml")
}
err = result.SaveResult(bundlePath, constants.SPEC_FILENAME, bytes.NewBuffer([]byte(yamlContent)))
if err != nil {
return errors.Wrap(err, "failed to write preflight spec to bundle")
}
// redact the final YAML spec
singleResult := map[string][]byte{
constants.SPEC_FILENAME: []byte(yamlContent),
}

err = collect.RedactResult(bundlePath, singleResult, nil)
if err != nil {
return errors.Wrap(err, "failed to redact final preflight yaml spec")
}
return nil
}
9 changes: 9 additions & 0 deletions pkg/redact/redact.go
Original file line number Diff line number Diff line change
Expand Up @@ -470,6 +470,15 @@ func getRedactors(path string) ([]Redactor, error) {
}
}

// redact final YAML spec used to generate the support bundle/preflight
// redact TLS private key if any
// todo: any other TLS keys to redact?
tlsKeys := []string{"clientKey"}
for _, key := range tlsKeys {
yamlPath := fmt.Sprintf("spec.collectors.*.*.tls.%s", key)
nvanthao marked this conversation as resolved.
Show resolved Hide resolved
redactors = append(redactors, NewYamlRedactor(yamlPath, constants.SPEC_FILENAME, "Redact TLS private key"))
}

return redactors, nil
}

Expand Down
48 changes: 48 additions & 0 deletions pkg/supportbundle/supportbundle.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"github.com/replicatedhq/troubleshoot/pkg/collect"
"github.com/replicatedhq/troubleshoot/pkg/constants"
"github.com/replicatedhq/troubleshoot/pkg/convert"
"github.com/replicatedhq/troubleshoot/pkg/loader"
"github.com/replicatedhq/troubleshoot/pkg/version"
"go.opentelemetry.io/otel"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
Expand Down Expand Up @@ -165,6 +166,13 @@ func CollectSupportBundleFromSpec(
return nil, errors.Wrap(err, "failed to write version")
}

// save final YAML spec used to geneate the support bundle
err = saveAndRedactFinalSpec(spec, &result, bundlePath, additionalRedactors)
if err != nil {
// still allow the support bundle to be created
klog.Errorf("failed to save and redact final spec: %v", err)
}

// Run Analyzers
analyzeResults, err := AnalyzeSupportBundle(ctx, spec, bundlePath)
if err != nil {
Expand Down Expand Up @@ -312,3 +320,43 @@ func getNodeList(clientset kubernetes.Interface, opts SupportBundleCreateOpts) (

return &nodeList, nil
}

func saveAndRedactFinalSpec(spec *troubleshootv1beta2.SupportBundleSpec, result *collect.CollectorResult, bundlePath string, additionalRedactors *troubleshootv1beta2.Redactor) error {
// generate the final YAML spec
k := loader.TroubleshootKinds{
SupportBundlesV1Beta2: []troubleshootv1beta2.SupportBundle{
{
TypeMeta: metav1.TypeMeta{
APIVersion: "troubleshoot.sh/v1beta2",
Kind: "SupportBundle",
},
Spec: *spec,
},
},
}
yamlContent, err := k.ToYaml()
if err != nil {
return errors.Wrap(err, "failed to convert final support bundle spec to yaml")
}

err = result.SaveResult(bundlePath, constants.SPEC_FILENAME, bytes.NewBuffer([]byte(yamlContent)))
if err != nil {
return errors.Wrap(err, "failed to write final support bundle yaml spec")
}

// redact the final YAML spec
singleResult := map[string][]byte{
constants.SPEC_FILENAME: []byte(yamlContent),
}

var redactors []*troubleshootv1beta2.Redact
if additionalRedactors != nil {
redactors = additionalRedactors.Spec.Redactors
}
err = collect.RedactResult(bundlePath, singleResult, redactors)
if err != nil {
return errors.Wrap(err, "failed to redact final support bundle yaml spec")
}

return nil
}
61 changes: 61 additions & 0 deletions pkg/supportbundle/supportbundle_test.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
package supportbundle

import (
"os"
"path/filepath"
"reflect"
"testing"

troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
"github.com/replicatedhq/troubleshoot/pkg/collect"
"github.com/replicatedhq/troubleshoot/pkg/constants"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"gopkg.in/yaml.v2"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
Expand Down Expand Up @@ -114,3 +121,57 @@ func Test_getNodeList(t *testing.T) {
})
}
}

func Test_saveAndRedactFinalSpec(t *testing.T) {
spec := &troubleshootv1beta2.SupportBundleSpec{
Collectors: []*troubleshootv1beta2.Collect{
{
ClusterInfo: &troubleshootv1beta2.ClusterInfo{},
ClusterResources: &troubleshootv1beta2.ClusterResources{},
Postgres: &troubleshootv1beta2.Database{
URI: "postgresql://user:password@hostname:5432/defaultdb?sslmode=require",
TLS: &troubleshootv1beta2.TLSParams{
CACert: `CA CERT`,
ClientCert: `CLIENT CERT`,
ClientKey: `PRIVATE KEY`,
},
},
},
},
}

result := make(collect.CollectorResult)
bundlePath := t.TempDir()
expectedYAML := `
apiVersion: troubleshoot.sh/v1beta2
kind: SupportBundle
metadata:
creationTimestamp: null
spec:
collectors:
- clusterInfo: {}
clusterResources: {}
postgres:
uri: postgresql://***HIDDEN***:***HIDDEN***@***HIDDEN***:5432/***HIDDEN***
tls:
cacert: CA CERT
clientCert: CLIENT CERT
clientKey: "***HIDDEN***"
status: {}
`

err := saveAndRedactFinalSpec(spec, &result, bundlePath, nil)
if err != nil {
t.Fatal(err)
}

actualYAML, err := os.ReadFile(filepath.Join(bundlePath, constants.SPEC_FILENAME))
require.NoError(t, err)

var expectedData, actualData interface{}
err = yaml.Unmarshal([]byte(expectedYAML), &expectedData)
require.NoError(t, err)
err = yaml.Unmarshal(actualYAML, &actualData)
require.NoError(t, err)
assert.Equal(t, expectedData, actualData)
}
Loading