Skip to content

Commit f6f51ac

Browse files
authored
feat: save YAML spec used to generate support bundle/preflight (#1713)
* save YAML spec of support bundle * save YAML spec of preflight * add unit test * redact TLS private key by default in output spec * update YAML path for HTTP TLS redactor
1 parent 64ee9e5 commit f6f51ac

File tree

5 files changed

+167
-0
lines changed

5 files changed

+167
-0
lines changed

pkg/constants/constants.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ const (
1111
DEFAULT_CLIENT_USER_AGENT = "ReplicatedTroubleshoot"
1212
// VERSION_FILENAME is the name of the file that contains the support bundle version.
1313
VERSION_FILENAME = "version.yaml"
14+
// SPEC_FILENAME is the name of the file that contains the support bundle/preflight spec.
15+
SPEC_FILENAME = "spec.yaml"
1416
// DEFAULT_LOGS_COLLECTOR_TIMEOUT is the default timeout for logs collector.
1517
DEFAULT_LOGS_COLLECTOR_TIMEOUT = 60 * time.Second
1618
// MAX_TIME_TO_WAIT_FOR_POD_DELETION is the maximum time to wait for pod deletion.

pkg/preflight/run.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"github.com/replicatedhq/troubleshoot/pkg/constants"
2121
"github.com/replicatedhq/troubleshoot/pkg/convert"
2222
"github.com/replicatedhq/troubleshoot/pkg/k8sutil"
23+
"github.com/replicatedhq/troubleshoot/pkg/loader"
2324
"github.com/replicatedhq/troubleshoot/pkg/types"
2425
"github.com/replicatedhq/troubleshoot/pkg/version"
2526
"github.com/spf13/viper"
@@ -180,6 +181,13 @@ func RunPreflights(interactive bool, output string, format string, args []string
180181
return errors.Wrap(err, "failed to save version file")
181182
}
182183

184+
// save final preflight spec used to geneate the preflight checks
185+
err = savePreflightSpecToBundle(specs, collectorResults, bundlePath)
186+
if err != nil {
187+
// still allow the preflight to be created
188+
klog.Errorf("failed to save preflight YAML spec: %v", err)
189+
}
190+
183191
analyzeResults, err := analyzer.AnalyzeLocal(ctx, bundlePath, analyzers, hostAnalyzers)
184192
if err != nil {
185193
return errors.Wrap(err, "failed to analyze support bundle")
@@ -469,3 +477,24 @@ func parseTimeFlags(v *viper.Viper, collectors []*troubleshootv1beta2.Collect) e
469477
}
470478
return nil
471479
}
480+
481+
func savePreflightSpecToBundle(specs *loader.TroubleshootKinds, result collect.CollectorResult, bundlePath string) error {
482+
yamlContent, err := specs.ToYaml()
483+
if err != nil {
484+
return errors.Wrap(err, "failed to convert preflight specs to yaml")
485+
}
486+
err = result.SaveResult(bundlePath, constants.SPEC_FILENAME, bytes.NewBuffer([]byte(yamlContent)))
487+
if err != nil {
488+
return errors.Wrap(err, "failed to write preflight spec to bundle")
489+
}
490+
// redact the final YAML spec
491+
singleResult := map[string][]byte{
492+
constants.SPEC_FILENAME: []byte(yamlContent),
493+
}
494+
495+
err = collect.RedactResult(bundlePath, singleResult, nil)
496+
if err != nil {
497+
return errors.Wrap(err, "failed to redact final preflight yaml spec")
498+
}
499+
return nil
500+
}

pkg/redact/redact.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -470,6 +470,20 @@ func getRedactors(path string) ([]Redactor, error) {
470470
}
471471
}
472472

473+
// redact final YAML spec used to generate the support bundle/preflight
474+
// redact TLS private key if any
475+
// todo: any other TLS keys to redact?
476+
tlsKeys := []string{"clientKey"}
477+
for _, key := range tlsKeys {
478+
yamlPaths := []string{
479+
fmt.Sprintf("spec.collectors.*.*.tls.%s", key), // Database collector
480+
fmt.Sprintf("spec.collectors.*.*.*.tls.%s", key), // HTTP collector
481+
}
482+
for _, yamlPath := range yamlPaths {
483+
redactors = append(redactors, NewYamlRedactor(yamlPath, constants.SPEC_FILENAME, "Redact TLS private key"))
484+
}
485+
}
486+
473487
return redactors, nil
474488
}
475489

pkg/supportbundle/supportbundle.go

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"github.com/replicatedhq/troubleshoot/pkg/collect"
2121
"github.com/replicatedhq/troubleshoot/pkg/constants"
2222
"github.com/replicatedhq/troubleshoot/pkg/convert"
23+
"github.com/replicatedhq/troubleshoot/pkg/loader"
2324
"github.com/replicatedhq/troubleshoot/pkg/version"
2425
"go.opentelemetry.io/otel"
2526
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -165,6 +166,13 @@ func CollectSupportBundleFromSpec(
165166
return nil, errors.Wrap(err, "failed to write version")
166167
}
167168

169+
// save final YAML spec used to geneate the support bundle
170+
err = saveAndRedactFinalSpec(spec, &result, bundlePath, additionalRedactors)
171+
if err != nil {
172+
// still allow the support bundle to be created
173+
klog.Errorf("failed to save and redact final spec: %v", err)
174+
}
175+
168176
// Run Analyzers
169177
analyzeResults, err := AnalyzeSupportBundle(ctx, spec, bundlePath)
170178
if err != nil {
@@ -312,3 +320,43 @@ func getNodeList(clientset kubernetes.Interface, opts SupportBundleCreateOpts) (
312320

313321
return &nodeList, nil
314322
}
323+
324+
func saveAndRedactFinalSpec(spec *troubleshootv1beta2.SupportBundleSpec, result *collect.CollectorResult, bundlePath string, additionalRedactors *troubleshootv1beta2.Redactor) error {
325+
// generate the final YAML spec
326+
k := loader.TroubleshootKinds{
327+
SupportBundlesV1Beta2: []troubleshootv1beta2.SupportBundle{
328+
{
329+
TypeMeta: metav1.TypeMeta{
330+
APIVersion: "troubleshoot.sh/v1beta2",
331+
Kind: "SupportBundle",
332+
},
333+
Spec: *spec,
334+
},
335+
},
336+
}
337+
yamlContent, err := k.ToYaml()
338+
if err != nil {
339+
return errors.Wrap(err, "failed to convert final support bundle spec to yaml")
340+
}
341+
342+
err = result.SaveResult(bundlePath, constants.SPEC_FILENAME, bytes.NewBuffer([]byte(yamlContent)))
343+
if err != nil {
344+
return errors.Wrap(err, "failed to write final support bundle yaml spec")
345+
}
346+
347+
// redact the final YAML spec
348+
singleResult := map[string][]byte{
349+
constants.SPEC_FILENAME: []byte(yamlContent),
350+
}
351+
352+
var redactors []*troubleshootv1beta2.Redact
353+
if additionalRedactors != nil {
354+
redactors = additionalRedactors.Spec.Redactors
355+
}
356+
err = collect.RedactResult(bundlePath, singleResult, redactors)
357+
if err != nil {
358+
return errors.Wrap(err, "failed to redact final support bundle yaml spec")
359+
}
360+
361+
return nil
362+
}

pkg/supportbundle/supportbundle_test.go

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,17 @@
11
package supportbundle
22

33
import (
4+
"os"
5+
"path/filepath"
46
"reflect"
57
"testing"
68

79
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
10+
"github.com/replicatedhq/troubleshoot/pkg/collect"
11+
"github.com/replicatedhq/troubleshoot/pkg/constants"
12+
"github.com/stretchr/testify/assert"
13+
"github.com/stretchr/testify/require"
14+
"gopkg.in/yaml.v2"
815
corev1 "k8s.io/api/core/v1"
916
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1017
"k8s.io/client-go/kubernetes"
@@ -114,3 +121,70 @@ func Test_getNodeList(t *testing.T) {
114121
})
115122
}
116123
}
124+
125+
func Test_saveAndRedactFinalSpec(t *testing.T) {
126+
spec := &troubleshootv1beta2.SupportBundleSpec{
127+
Collectors: []*troubleshootv1beta2.Collect{
128+
{
129+
ClusterInfo: &troubleshootv1beta2.ClusterInfo{},
130+
ClusterResources: &troubleshootv1beta2.ClusterResources{},
131+
Postgres: &troubleshootv1beta2.Database{
132+
URI: "postgresql://user:password@hostname:5432/defaultdb?sslmode=require",
133+
TLS: &troubleshootv1beta2.TLSParams{
134+
CACert: `CA CERT`,
135+
ClientCert: `CLIENT CERT`,
136+
ClientKey: `PRIVATE KEY`,
137+
},
138+
},
139+
HTTP: &troubleshootv1beta2.HTTP{
140+
Get: &troubleshootv1beta2.Get{
141+
URL: "http:api:3000/healthz",
142+
TLS: &troubleshootv1beta2.TLSParams{
143+
ClientKey: `PRIVATE KEY`,
144+
},
145+
},
146+
},
147+
},
148+
},
149+
}
150+
151+
result := make(collect.CollectorResult)
152+
bundlePath := t.TempDir()
153+
expectedYAML := `
154+
apiVersion: troubleshoot.sh/v1beta2
155+
kind: SupportBundle
156+
metadata:
157+
creationTimestamp: null
158+
spec:
159+
collectors:
160+
- clusterInfo: {}
161+
clusterResources: {}
162+
postgres:
163+
uri: postgresql://***HIDDEN***:***HIDDEN***@***HIDDEN***:5432/***HIDDEN***
164+
tls:
165+
cacert: CA CERT
166+
clientCert: CLIENT CERT
167+
clientKey: "***HIDDEN***"
168+
http:
169+
get:
170+
url: http:api:3000/healthz
171+
tls:
172+
clientKey: "***HIDDEN***"
173+
status: {}
174+
`
175+
176+
err := saveAndRedactFinalSpec(spec, &result, bundlePath, nil)
177+
if err != nil {
178+
t.Fatal(err)
179+
}
180+
181+
actualYAML, err := os.ReadFile(filepath.Join(bundlePath, constants.SPEC_FILENAME))
182+
require.NoError(t, err)
183+
184+
var expectedData, actualData interface{}
185+
err = yaml.Unmarshal([]byte(expectedYAML), &expectedData)
186+
require.NoError(t, err)
187+
err = yaml.Unmarshal(actualYAML, &actualData)
188+
require.NoError(t, err)
189+
assert.Equal(t, expectedData, actualData)
190+
}

0 commit comments

Comments
 (0)