diff --git a/.gitignore b/.gitignore index df59e932..af6bc42e 100644 --- a/.gitignore +++ b/.gitignore @@ -41,3 +41,4 @@ sbom/ # Ignore generated support bundles *.tar.gz +!testdata/supportbundle/*.tar.gz diff --git a/Makefile b/Makefile index 641fe9e4..e34dc3a2 100644 --- a/Makefile +++ b/Makefile @@ -35,7 +35,7 @@ endef BUILDFLAGS = -tags "netgo containers_image_ostree_stub exclude_graphdriver_devicemapper exclude_graphdriver_btrfs containers_image_openpgp" -installsuffix netgo -all: test support-bundle preflight collect +all: test support-bundle preflight collect analyze .PHONY: ffi ffi: fmt vet @@ -202,8 +202,8 @@ scan: .PHONY: lint lint: - golangci-lint run -c .golangci.yaml + golangci-lint run --new -c .golangci.yaml pkg/... cmd/... .PHONY: lint-and-fix lint-and-fix: - golangci-lint run --fix -c .golangci.yaml + golangci-lint run --new --fix -c .golangci.yaml pkg/... cmd/... diff --git a/README.md b/README.md index d255932d..dcfc2528 100644 --- a/README.md +++ b/README.md @@ -12,13 +12,13 @@ To run a sample preflight check from a sample application, install the preflight ``` curl https://krew.sh/preflight | bash ``` -and run, where https://preflight.replicated.com provides an **example** preflight spec: - + and run, where https://preflight.replicated.com provides an **example** preflight spec: + ``` kubectl preflight https://preflight.replicated.com ``` -**NOTE** this is an example. Do **not** use to validate real scenarios. +**NOTE** this is an example. Do **not** use to validate real scenarios. For more details on creating the custom resource files that drive preflight checks, visit [creating preflight checks](https://troubleshoot.sh/docs/preflight/introduction/). @@ -31,13 +31,13 @@ To collect a sample support bundle, install the troubleshoot kubectl plugin: ``` curl https://krew.sh/support-bundle | bash ``` -and run, where https://support-bundle.replicated.com provides an **example** support bundle spec: - + and run, where https://support-bundle.replicated.com provides an **example** support bundle spec: + ``` kubectl support-bundle https://support-bundle.replicated.com ``` -**NOTE** this is an example. Do **not** use to validate real scenarios. +**NOTE** this is an example. Do **not** use to validate real scenarios. For more details on creating the custom resource files that drive support-bundle collection, visit [creating collectors](https://troubleshoot.sh/docs/collect/) and [creating analyzers](https://troubleshoot.sh/docs/analyze/). @@ -47,9 +47,9 @@ And see our other tool [sbctl](https://github.com/replicatedhq/sbctl) that makes For questions about using Troubleshoot, there's a [Replicated Community](https://help.replicated.com/community) forum, and a [#app-troubleshoot channel in Kubernetes Slack](https://kubernetes.slack.com/channels/app-troubleshoot). -# Software Bill of Materials -A signed SBOM that includes Troubleshoot dependencies is included in each release. -- **troubleshoot-sbom.tgz** contains a software bill of materials for Troubleshoot. +# Software Bill of Materials +A signed SBOM that includes Troubleshoot dependencies is included in each release. +- **troubleshoot-sbom.tgz** contains a software bill of materials for Troubleshoot. - **troubleshoot-sbom.tgz.sig** is the digital signature for troubleshoot-sbom.tgz - **key.pub** is the public key from the key pair used to sign troubleshoot-sbom.tgz diff --git a/cmd/collect/cli/run.go b/cmd/collect/cli/run.go index d5388742..dc56f1ce 100644 --- a/cmd/collect/cli/run.go +++ b/cmd/collect/cli/run.go @@ -91,16 +91,15 @@ func runCollect(v *viper.Viper, arg string) error { troubleshootclientsetscheme.AddToScheme(scheme.Scheme) decode := scheme.Codecs.UniversalDeserializer().Decode - additionalRedactors := &troubleshootv1beta2.Redactor{} - for idx, redactor := range v.GetStringSlice("redactors") { - redactorObj, err := supportbundle.GetRedactorFromURI(redactor) - if err != nil { - return errors.Wrapf(err, "failed to get redactor spec %s, #%d", redactor, idx) - } + redactors, err := supportbundle.GetRedactorsFromURIs(v.GetStringSlice("redactors")) + if err != nil { + return errors.Wrap(err, "failed to get redactors") + } - if redactorObj != nil { - additionalRedactors.Spec.Redactors = append(additionalRedactors.Spec.Redactors, redactorObj.Spec.Redactors...) - } + additionalRedactors := &troubleshootv1beta2.Redactor{ + Spec: troubleshootv1beta2.RedactorSpec{ + Redactors: redactors, + }, } for i, additionalDoc := range multidocs { diff --git a/cmd/troubleshoot/cli/redact.go b/cmd/troubleshoot/cli/redact.go new file mode 100644 index 00000000..4031a670 --- /dev/null +++ b/cmd/troubleshoot/cli/redact.go @@ -0,0 +1,85 @@ +package cli + +import ( + "fmt" + "os" + "time" + + "github.com/pkg/errors" + analyzer "github.com/replicatedhq/troubleshoot/pkg/analyze" + "github.com/replicatedhq/troubleshoot/pkg/collect" + "github.com/replicatedhq/troubleshoot/pkg/logger" + "github.com/replicatedhq/troubleshoot/pkg/supportbundle" + "github.com/spf13/cobra" + "github.com/spf13/viper" +) + +func Redact() *cobra.Command { + cmd := &cobra.Command{ + Use: "redact [urls...]", + Args: cobra.MinimumNArgs(1), // TODO + Short: "Redact information from a generated support bundle archive", + Long: `Redaction is the process of masking sensitive information from collected data in a support bundle. +This is done using rules defined in the list of redactor manifests provided in the [urls...] command line +argument. Default built in redactors will also be run, but these would have been run when the support +bundle was generated. After redaction, the support bundle is archived once more. The resulting file will +be stored in the current directory in the path provided by the --output flag. + +The [urls...] argument is a list of either oci://.., http://.., https://.. or local paths to yaml files. + +For more information on redactors visit https://troubleshoot.sh/docs/redact/ + `, + PreRunE: func(cmd *cobra.Command, args []string) error { + return viper.BindPFlags(cmd.Flags()) + }, + RunE: func(cmd *cobra.Command, args []string) error { + v := viper.GetViper() + + logger.SetQuiet(v.GetBool("quiet")) + + // 1. Decode redactors from provided URLs + redactors, err := supportbundle.GetRedactorsFromURIs(args) + if err != nil { + return err + } + + // 2. Download the bundle and extract it + tmpDir, bundleDir, err := analyzer.DownloadAndExtractSupportBundle(v.GetString("bundle")) + if err != nil { + return err + } + defer os.RemoveAll(tmpDir) + + // 3. Represent bundle as a CollectorResult + collectorResult, err := collect.CollectorResultFromBundle(bundleDir) + if err != nil { + return err + } + + // 4. Perform redaction on the bundle + err = collect.RedactResult(bundleDir, collectorResult, redactors) + if err != nil { + return errors.Wrap(err, "failed to redact support bundle") + } + + // 5. Compress the bundle once more after redacting + output := v.GetString("output") + if output == "" { + output = fmt.Sprintf("redacted-support-bundle-%s.tar.gz", time.Now().Format("2006-01-02T15_04_05")) + } + err = collectorResult.ArchiveSupportBundle(bundleDir, output) + if err != nil { + return errors.Wrap(err, "failed to create support bundle archive") + } + fmt.Println("Redacted support bundle:", output) + return nil + }, + } + + cmd.Flags().String("bundle", "", "file path of the support bundle archive to redact") + cmd.MarkFlagRequired("bundle") + cmd.Flags().BoolP("quiet", "q", false, "enable/disable error messaging and only show parseable output") + cmd.Flags().StringP("output", "o", "", "file path of where to save the redacted support bundle archive (default \"redacted-support-bundle-YYYY-MM-DDTHH_MM_SS.tar.gz\")") + + return cmd +} diff --git a/cmd/troubleshoot/cli/root.go b/cmd/troubleshoot/cli/root.go index 6e12fd5a..2bf8cfac 100644 --- a/cmd/troubleshoot/cli/root.go +++ b/cmd/troubleshoot/cli/root.go @@ -15,7 +15,7 @@ import ( func RootCmd() *cobra.Command { cmd := &cobra.Command{ - Use: "support-bundle [url]", + Use: "support-bundle [urls...]", Args: cobra.MinimumNArgs(0), Short: "Generate a support bundle", Long: `A support bundle is an archive of files, output, metrics and state @@ -40,6 +40,7 @@ from a server that can be used to assist when troubleshooting a Kubernetes clust cobra.OnInitialize(initConfig) cmd.AddCommand(Analyze()) + cmd.AddCommand(Redact()) cmd.AddCommand(VersionCmd()) cmd.Flags().StringSlice("redactors", []string{}, "names of the additional redactors to use") diff --git a/cmd/troubleshoot/cli/run.go b/cmd/troubleshoot/cli/run.go index 00359739..e5ecce6a 100644 --- a/cmd/troubleshoot/cli/run.go +++ b/cmd/troubleshoot/cli/run.go @@ -208,16 +208,11 @@ func runTroubleshoot(v *viper.Viper, arg []string) error { return errors.New("no collectors specified in support bundle") } - for idx, redactor := range v.GetStringSlice("redactors") { - redactorObj, err := supportbundle.GetRedactorFromURI(redactor) - if err != nil { - return errors.Wrapf(err, "failed to get redactor spec %s, #%d", redactor, idx) - } - - if redactorObj != nil { - additionalRedactors.Spec.Redactors = append(additionalRedactors.Spec.Redactors, redactorObj.Spec.Redactors...) - } + redactors, err := supportbundle.GetRedactorsFromURIs(v.GetStringSlice("redactors")) + if err != nil { + return errors.Wrap(err, "failed to get redactors") } + additionalRedactors.Spec.Redactors = append(additionalRedactors.Spec.Redactors, redactors...) var collectorCB func(chan interface{}, string) progressChan := make(chan interface{}) // non-zero buffer can result in missed messages diff --git a/cmd/troubleshoot/cli/version.go b/cmd/troubleshoot/cli/version.go index a19d384a..7baf5805 100644 --- a/cmd/troubleshoot/cli/version.go +++ b/cmd/troubleshoot/cli/version.go @@ -7,6 +7,7 @@ import ( "path/filepath" troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2" + "github.com/replicatedhq/troubleshoot/pkg/constants" "github.com/replicatedhq/troubleshoot/pkg/version" "github.com/spf13/cobra" "gopkg.in/yaml.v2" @@ -26,8 +27,6 @@ func VersionCmd() *cobra.Command { return cmd } -const VersionFilename = "version.yaml" - func writeVersionFile(path string) error { version := troubleshootv1beta2.SupportBundleVersion{ ApiVersion: "troubleshoot.sh/v1beta2", @@ -41,7 +40,7 @@ func writeVersionFile(path string) error { return err } - filename := filepath.Join(path, VersionFilename) + filename := filepath.Join(path, constants.VersionFilename) err = ioutil.WriteFile(filename, b, 0644) if err != nil { return err diff --git a/docs/preflight.md b/docs/preflight.md index d34920bf..a43ddd7f 100644 --- a/docs/preflight.md +++ b/docs/preflight.md @@ -17,7 +17,7 @@ preflight [url] [flags] --as string Username to impersonate for the operation. User could be a regular user or a service account in a namespace. --as-group stringArray Group to impersonate for the operation, this flag can be repeated to specify multiple groups. --as-uid string UID to impersonate for the operation. - --cache-dir string Default cache directory (default "/Users/xavpaice/.kube/cache") + --cache-dir string Default cache directory (default "$HOME/.kube/cache") --certificate-authority string Path to a cert file for the certificate authority --client-certificate string Path to a client certificate file for TLS --client-key string Path to a client key file for TLS @@ -48,4 +48,4 @@ preflight [url] [flags] * [preflight version](preflight_version.md) - Print the current version and exit -###### Auto generated by spf13/cobra on 21-Nov-2022 +###### Auto generated by spf13/cobra on 22-Dec-2022 diff --git a/docs/preflight_version.md b/docs/preflight_version.md index 2744ea16..a26d1ea9 100644 --- a/docs/preflight_version.md +++ b/docs/preflight_version.md @@ -35,4 +35,4 @@ preflight version [flags] * [preflight](preflight.md) - Run and retrieve preflight checks in a cluster -###### Auto generated by spf13/cobra on 21-Nov-2022 +###### Auto generated by spf13/cobra on 22-Dec-2022 diff --git a/docs/support-bundle.md b/docs/support-bundle.md index c0dba200..88a68a3a 100644 --- a/docs/support-bundle.md +++ b/docs/support-bundle.md @@ -8,7 +8,7 @@ A support bundle is an archive of files, output, metrics and state from a server that can be used to assist when troubleshooting a Kubernetes cluster. ``` -support-bundle [url] [flags] +support-bundle [urls...] [flags] ``` ### Options @@ -17,7 +17,7 @@ support-bundle [url] [flags] --as string Username to impersonate for the operation. User could be a regular user or a service account in a namespace. --as-group stringArray Group to impersonate for the operation, this flag can be repeated to specify multiple groups. --as-uid string UID to impersonate for the operation. - --cache-dir string Default cache directory (default "/Users/xavpaice/.kube/cache") + --cache-dir string Default cache directory (default "$HOME/.kube/cache") --certificate-authority string Path to a cert file for the certificate authority --client-certificate string Path to a client certificate file for TLS --client-key string Path to a client key file for TLS @@ -47,7 +47,8 @@ support-bundle [url] [flags] ### SEE ALSO -* [support-bundle analyze](support-bundle_analyze.md) - analyze a support bundle -* [support-bundle version](support-bundle_version.md) - Print the current version and exit +* [support-bundle analyze](support-bundle_analyze.md) - analyze a support bundle +* [support-bundle redact](support-bundle_redact.md) - Redact information from a generated support bundle archive +* [support-bundle version](support-bundle_version.md) - Print the current version and exit -###### Auto generated by spf13/cobra on 21-Nov-2022 +###### Auto generated by spf13/cobra on 22-Dec-2022 diff --git a/docs/support-bundle_analyze.md b/docs/support-bundle_analyze.md index 535fd9b5..b0848b83 100644 --- a/docs/support-bundle_analyze.md +++ b/docs/support-bundle_analyze.md @@ -23,4 +23,4 @@ support-bundle analyze [url] [flags] * [support-bundle](support-bundle.md) - Generate a support bundle -###### Auto generated by spf13/cobra on 21-Nov-2022 +###### Auto generated by spf13/cobra on 22-Dec-2022 diff --git a/docs/support-bundle_redact.md b/docs/support-bundle_redact.md new file mode 100644 index 00000000..c6f3f22d --- /dev/null +++ b/docs/support-bundle_redact.md @@ -0,0 +1,35 @@ +## support-bundle redact + +Redact information from a generated support bundle archive + +### Synopsis + +Redaction is the process of masking sensitive information from collected data in a support bundle. +This is done using rules defined in the list of redactor manifests provided in the [urls...] command line +argument. Default built in redactors will also be run, but these would have been run when the support +bundle was generated. After redaction, the support bundle is archived once more. The resulting file will +be stored in the current directory in the path provided by the --output flag. + +The [urls...] argument is a list of either oci://.., http://.., https://.. or local paths to yaml files. + +For more information on redactors visit https://troubleshoot.sh/docs/redact/ + + +``` +support-bundle redact [urls...] [flags] +``` + +### Options + +``` + --bundle string file path of the support bundle archive to redact + -h, --help help for redact + -o, --output string file path of where to save the redacted support bundle archive (default "redacted-support-bundle-YYYY-MM-DDTHH_MM_SS.tar.gz") + -q, --quiet enable/disable error messaging and only show parseable output +``` + +### SEE ALSO + +* [support-bundle](support-bundle.md) - Generate a support bundle + +###### Auto generated by spf13/cobra on 22-Dec-2022 diff --git a/docs/support-bundle_version.md b/docs/support-bundle_version.md index 30cf864e..bfa3b7a0 100644 --- a/docs/support-bundle_version.md +++ b/docs/support-bundle_version.md @@ -20,4 +20,4 @@ support-bundle version [flags] * [support-bundle](support-bundle.md) - Generate a support bundle -###### Auto generated by spf13/cobra on 21-Nov-2022 +###### Auto generated by spf13/cobra on 22-Dec-2022 diff --git a/examples/redact/e2e.yaml b/examples/redact/e2e.yaml new file mode 100644 index 00000000..65b3833f --- /dev/null +++ b/examples/redact/e2e.yaml @@ -0,0 +1,10 @@ +apiVersion: troubleshoot.sh/v1beta2 +kind: Redactor +metadata: + name: e2e-redactor +spec: + redactors: + - name: redact-static-text + removals: + values: + - static diff --git a/internal/testutils/utils.go b/internal/testutils/utils.go new file mode 100644 index 00000000..cbb0a0f9 --- /dev/null +++ b/internal/testutils/utils.go @@ -0,0 +1,24 @@ +package testutils + +import ( + "os" + "path/filepath" + "runtime" + "testing" + + "github.com/stretchr/testify/require" +) + +func GetTestFixture(t *testing.T, path string) string { + t.Helper() + p := filepath.Join("../../testdata", path) + b, err := os.ReadFile(p) + require.NoError(t, err) + return string(b) +} + +// FileDir returns the directory of the current source file. +func FileDir() string { + _, filename, _, _ := runtime.Caller(0) + return filepath.Dir(filename) +} diff --git a/pkg/analyze/download.go b/pkg/analyze/download.go index 6b5cbe7c..2c0b112a 100644 --- a/pkg/analyze/download.go +++ b/pkg/analyze/download.go @@ -4,7 +4,6 @@ import ( "archive/tar" "compress/gzip" "io" - "io/ioutil" "os" "path/filepath" @@ -12,6 +11,7 @@ import ( "github.com/pkg/errors" troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2" troubleshootscheme "github.com/replicatedhq/troubleshoot/pkg/client/troubleshootclientset/scheme" + "github.com/replicatedhq/troubleshoot/pkg/constants" "github.com/replicatedhq/troubleshoot/pkg/docrewrite" "github.com/replicatedhq/troubleshoot/pkg/logger" "k8s.io/client-go/kubernetes/scheme" @@ -55,27 +55,13 @@ func AnalyzeLocal(localBundlePath string, analyzers []*troubleshootv1beta2.Analy } func DownloadAndAnalyze(bundleURL string, analyzersSpec string) ([]*AnalyzeResult, error) { - tmpDir, err := ioutil.TempDir("", "troubleshoot-k8s") - if err != nil { - return nil, errors.Wrap(err, "failed to create temp dir") - } - defer os.RemoveAll(tmpDir) - - if err := downloadTroubleshootBundle(bundleURL, tmpDir); err != nil { - return nil, errors.Wrap(err, "failed to download bundle") - } - - rootDir, err := FindBundleRootDir(tmpDir) + tmpDir, rootDir, err := DownloadAndExtractSupportBundle(bundleURL) if err != nil { return nil, errors.Wrap(err, "failed to find root dir") } + defer os.RemoveAll(tmpDir) - _, err = os.Stat(filepath.Join(rootDir, "version.yaml")) - if err != nil { - return nil, errors.Wrap(err, "failed to read version.yaml") - } - - analyzers := []*troubleshootv1beta2.Analyze{} + var analyzers []*troubleshootv1beta2.Analyze hostAnalyzers := []*troubleshootv1beta2.HostAnalyze{} if analyzersSpec == "" { @@ -96,7 +82,34 @@ func DownloadAndAnalyze(bundleURL string, analyzersSpec string) ([]*AnalyzeResul return AnalyzeLocal(rootDir, analyzers, hostAnalyzers) } +func DownloadAndExtractSupportBundle(bundleURL string) (string, string, error) { + tmpDir, err := os.MkdirTemp("", "troubleshoot-k8s") + if err != nil { + return "", "", errors.Wrap(err, "failed to create temp dir") + } + + if err := downloadTroubleshootBundle(bundleURL, tmpDir); err != nil { + os.RemoveAll(tmpDir) + return "", "", errors.Wrap(err, "failed to download bundle") + } + + bundleDir, err := FindBundleRootDir(tmpDir) + if err != nil { + os.RemoveAll(tmpDir) + return "", "", errors.Wrap(err, "failed to find root dir") + } + + _, err = os.Stat(filepath.Join(bundleDir, constants.VersionFilename)) + if err != nil { + os.RemoveAll(tmpDir) + return "", "", errors.Wrap(err, "failed to read "+constants.VersionFilename) + } + + return tmpDir, bundleDir, nil +} + func downloadTroubleshootBundle(bundleURL string, destDir string) error { + // TODO: Move to separate package support bundle utils package if bundleURL[0] == os.PathSeparator { f, err := os.Open(bundleURL) if err != nil { @@ -111,7 +124,7 @@ func downloadTroubleshootBundle(bundleURL string, destDir string) error { return errors.Wrap(err, "failed to get workdir") } - tmpDir, err := ioutil.TempDir("", "troubleshoot") + tmpDir, err := os.MkdirTemp("", "troubleshoot") if err != nil { return errors.Wrap(err, "failed to create tmp dir") } @@ -137,6 +150,8 @@ func downloadTroubleshootBundle(bundleURL string, destDir string) error { } func ExtractTroubleshootBundle(reader io.Reader, destDir string) error { + // TODO: Move to separate package e.g support bundle package, or sbutils + // if there are cyclic dependencies gzReader, err := gzip.NewReader(reader) if err != nil { return errors.Wrap(err, "failed to create gzip reader") @@ -159,14 +174,14 @@ func ExtractTroubleshootBundle(reader io.Reader, destDir string) error { return errors.Wrap(err, "failed to mkdir") } case tar.TypeReg: - name := filepath.Join(destDir, header.Name) + destFileName := filepath.Join(destDir, header.Name) - dirName := filepath.Dir(name) + dirName := filepath.Dir(destFileName) if err := os.MkdirAll(dirName, 0755); err != nil { return errors.Wrapf(err, "failed to mkdir for file %s", header.Name) } - file, err := os.OpenFile(name, os.O_RDWR|os.O_CREATE, os.FileMode(header.Mode)) + file, err := os.OpenFile(destFileName, os.O_RDWR|os.O_CREATE, os.FileMode(header.Mode)) if err != nil { return errors.Wrap(err, "failed to open tar file") } @@ -175,6 +190,21 @@ func ExtractTroubleshootBundle(reader io.Reader, destDir string) error { if err != nil { return errors.Wrap(err, "failed to extract file") } + case tar.TypeSymlink: + destFileName := filepath.Join(destDir, header.Name) + + dirName := filepath.Dir(destFileName) + if err := os.MkdirAll(dirName, 0755); err != nil { + return errors.Wrapf(err, "failed to mkdir for symlink %s", header.Name) + } + + // Symlink targets should be absolute paths after extraction + // for other parts of the code to work correctly e.g redaction, CollectorResult + targetPath := filepath.Join(filepath.Dir(destFileName), header.Linkname) + err = os.Symlink(targetPath, destFileName) + if err != nil { + return errors.Wrap(err, "failed to create symlink") + } } } @@ -251,7 +281,7 @@ func FindBundleRootDir(localBundlePath string) (string, error) { isInSubDir := true for _, name := range names { - if name == "version.yaml" { + if name == constants.VersionFilename { isInSubDir = false break } @@ -265,7 +295,7 @@ func FindBundleRootDir(localBundlePath string) (string, error) { } func (f fileContentProvider) getFileContents(fileName string) ([]byte, error) { - return ioutil.ReadFile(filepath.Join(f.rootDir, fileName)) + return os.ReadFile(filepath.Join(f.rootDir, fileName)) } func excludeFilePaths(files, excludeFiles []string) []string { @@ -306,7 +336,7 @@ func (f fileContentProvider) getChildFileContents(dirName string, excludeFiles [ fileArr := map[string][]byte{} for _, filePath := range files { - bytes, err := ioutil.ReadFile(filePath) + bytes, err := os.ReadFile(filePath) if err != nil { return nil, errors.Wrapf(err, "read %q", filePath) } diff --git a/pkg/analyze/download_test.go b/pkg/analyze/download_test.go new file mode 100644 index 00000000..2b76d636 --- /dev/null +++ b/pkg/analyze/download_test.go @@ -0,0 +1,49 @@ +package analyzer + +import ( + "os" + "path/filepath" + "testing" + + "github.com/replicatedhq/troubleshoot/internal/testutils" + "github.com/stretchr/testify/assert" +) + +func TestDownloadAndExtractSupportBundle(t *testing.T) { + // TODO: Add tests for web url downloads + tests := []struct { + name string + bundleURL string + wantErr bool + }{ + { + name: "extract a bundle from a local file path", + bundleURL: filepath.Join(testutils.FileDir(), "../../testdata/supportbundle/support-bundle.tar.gz"), + wantErr: false, + }, + { + name: "extract a bundle from a non-existent file path", + bundleURL: "/home/someone/gibberish", + wantErr: true, + }, + { + name: "extract an invalid support bundle which has no version file", + bundleURL: filepath.Join(testutils.FileDir(), "../../testdata/supportbundle/missing-version.tar.gz"), + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tmpDir, bundleDir, err := DownloadAndExtractSupportBundle(tt.bundleURL) + defer os.RemoveAll(tmpDir) // clean up. Ignore error + + if err == nil { + assert.DirExists(t, bundleDir) + assert.FileExists(t, filepath.Join(bundleDir, "version.yaml")) + } else { + assert.Equal(t, "", tmpDir) + assert.Equal(t, "", bundleDir) + } + }) + } +} diff --git a/pkg/collect/postgres_test.go b/pkg/collect/postgres_test.go index d2ddb3e1..476666f0 100644 --- a/pkg/collect/postgres_test.go +++ b/pkg/collect/postgres_test.go @@ -4,6 +4,7 @@ import ( "context" "testing" + "github.com/replicatedhq/troubleshoot/internal/testutils" "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -101,9 +102,9 @@ func TestCollectPostgres_createConnectConfigTLS(t *testing.T) { Collector: &v1beta2.Database{ URI: "postgresql://user:password@my-pghost:5432/defaultdb?sslmode=require", TLS: &v1beta2.TLSParams{ - CACert: getTestFixture(t, "db/ca.pem"), - ClientCert: getTestFixture(t, "db/client.pem"), - ClientKey: getTestFixture(t, "db/client-key.pem"), + CACert: testutils.GetTestFixture(t, "db/ca.pem"), + ClientCert: testutils.GetTestFixture(t, "db/client.pem"), + ClientKey: testutils.GetTestFixture(t, "db/client-key.pem"), }, }, } diff --git a/pkg/collect/redact.go b/pkg/collect/redact.go index 376d94e4..aa6b027e 100644 --- a/pkg/collect/redact.go +++ b/pkg/collect/redact.go @@ -13,6 +13,7 @@ import ( "github.com/pkg/errors" troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2" "github.com/replicatedhq/troubleshoot/pkg/redact" + "k8s.io/klog/v2" ) func RedactResult(bundlePath string, input CollectorResult, additionalRedactors []*troubleshootv1beta2.Redact) error { @@ -32,11 +33,25 @@ func RedactResult(bundlePath string, input CollectorResult, additionalRedactors } // Redact the target file of a symlink + // There is an opportunity for improving performance here by skipping symlinks + // if a target has been redacted already, but that would require + // some extra logic to ensure that a spec filtering only symlinks still works. if info.Mode().Type() == os.ModeSymlink { - file, err = os.Readlink(filepath.Join(bundlePath, file)) + symlink := file + target, err := os.Readlink(filepath.Join(bundlePath, symlink)) if err != nil { return errors.Wrap(err, "failed to read symlink") } + + // Get the relative path to the target file to conform with + // the path formats of the CollectorResult + file, err = filepath.Rel(bundlePath, target) + if err != nil { + return errors.Wrap(err, "failed to get relative path") + } + klog.V(4).Infof("Redacting %s (symlink => %s)\n", file, symlink) + } else { + klog.V(4).Infof("Redacting %s\n", file) } r, err := input.GetReader(bundlePath, file) if err != nil { @@ -53,8 +68,8 @@ func RedactResult(bundlePath string, input CollectorResult, additionalRedactors reader = bytes.NewBuffer(v) } - //If the file is .tar, .tgz or .tar.gz, it must not be redacted. Instead it is decompressed and each file inside the - //tar is decompressed, redacted and compressed back into the tar. + // If the file is .tar, .tgz or .tar.gz, it must not be redacted. Instead it is + // decompressed and each file inside the tar redacted and compressed back into the archive. if filepath.Ext(file) == ".tar" || filepath.Ext(file) == ".tgz" || strings.HasSuffix(file, ".tar.gz") { tmpDir, err := ioutil.TempDir("", "troubleshoot-subresult-") if err != nil { diff --git a/pkg/collect/redis_test.go b/pkg/collect/redis_test.go index debee832..e3a1c3e9 100644 --- a/pkg/collect/redis_test.go +++ b/pkg/collect/redis_test.go @@ -3,6 +3,7 @@ package collect import ( "testing" + "github.com/replicatedhq/troubleshoot/internal/testutils" v1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -90,9 +91,9 @@ func TestCollectRedis_createTLSClient(t *testing.T) { Collector: &v1beta2.Database{ URI: "redis://localhost:6379", TLS: &v1beta2.TLSParams{ - CACert: getTestFixture(t, "db/ca.pem"), - ClientCert: getTestFixture(t, "db/client.pem"), - ClientKey: getTestFixture(t, "db/client-key.pem"), + CACert: testutils.GetTestFixture(t, "db/ca.pem"), + ClientCert: testutils.GetTestFixture(t, "db/client.pem"), + ClientKey: testutils.GetTestFixture(t, "db/client-key.pem"), }, }, } diff --git a/pkg/collect/result.go b/pkg/collect/result.go index 3ee3f61f..d21c2007 100644 --- a/pkg/collect/result.go +++ b/pkg/collect/result.go @@ -65,7 +65,7 @@ func (r CollectorResult) SymLinkResult(bundlePath, relativeLinkPath, relativeFil // Create the symlink // NOTE: When creating an archive, relative paths are used // to make the bundle more portable. That implementation - // lives in TarSupportBundleDir function. This path needs to + // lives in CollectorResultFromBundle function. This path needs to // remain as-is to support memory only bundles e.g preflight err = os.Symlink(filePath, linkPath) if err != nil { @@ -222,7 +222,7 @@ func (r CollectorResult) CloseWriter(bundlePath string, relativePath string, wri return errors.Errorf("cannot close writer of type %T", writer) } -func TarSupportBundleDir(bundlePath string, input CollectorResult, outputFilename string) error { +func (r CollectorResult) ArchiveSupportBundle(bundlePath string, outputFilename string) error { fileWriter, err := os.Create(outputFilename) if err != nil { return errors.Wrap(err, "failed to create output file") @@ -235,7 +235,7 @@ func TarSupportBundleDir(bundlePath string, input CollectorResult, outputFilenam tarWriter := tar.NewWriter(gzipWriter) defer tarWriter.Close() - for relativeName := range input { + for relativeName := range r { filename := filepath.Join(bundlePath, relativeName) info, err := os.Lstat(filename) if err != nil { @@ -315,3 +315,44 @@ func TarSupportBundleDir(bundlePath string, input CollectorResult, outputFilenam return nil } + +// CollectorResultFromBundle creates a CollectorResult from a bundle directory +// The bundle directory is not necessarily a support bundle, it can be any directory +// of collected files as part of other operations or files that are already on disk. +func CollectorResultFromBundle(bundleDir string) (CollectorResult, error) { + // Check directory exists + if _, err := os.Stat(bundleDir); os.IsNotExist(err) { + return nil, errors.Wrap(err, "bundle directory does not exist") + } + + // Walk the directory and add all files to the collector result + result := make(CollectorResult) + err := filepath.Walk(bundleDir, func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + if info.IsDir() { + return nil + } + + rel, err := filepath.Rel(bundleDir, path) + if err != nil { + return err + } + + result[rel] = nil + return nil + }) + if err != nil { + return nil, errors.Wrap(err, "failed to walk bundle directory") + } + + return result, nil +} + +// TarSupportBundleDir wraps ArchiveSupportBundle for backwards compatibility +// Deprecated: Remove in a future version (v1.0) +func TarSupportBundleDir(bundlePath string, input CollectorResult, outputFilename string) error { + // Is this used anywhere external anyway? + return input.ArchiveSupportBundle(bundlePath, outputFilename) +} diff --git a/pkg/collect/result_test.go b/pkg/collect/result_test.go index d3cdb63a..b06f7026 100644 --- a/pkg/collect/result_test.go +++ b/pkg/collect/result_test.go @@ -1,8 +1,10 @@ package collect import ( + "path/filepath" "testing" + "github.com/replicatedhq/troubleshoot/internal/testutils" "github.com/stretchr/testify/assert" ) @@ -16,3 +18,36 @@ func TestCollectorResult_AddResult(t *testing.T) { assert.Equal(t, []byte("a"), r["a"]) assert.Equal(t, []byte("b"), r["b"]) } + +func TestCollectorResultFromBundle(t *testing.T) { + tests := []struct { + name string + bundleDir string + want CollectorResult + wantErr bool + }{ + { + name: "creates collector results from a bundle successfully", + bundleDir: filepath.Join(testutils.FileDir(), "../../testdata/supportbundle/extracted-sb"), + want: CollectorResult{ + "cluster-resources/pods/logs/default/static-hi/static-hi.log": nil, + "static-hi.log": nil, + }, + wantErr: false, + }, + { + name: "fails to create collector results from a missing directory", + bundleDir: "gibberish", + want: nil, + wantErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := CollectorResultFromBundle(tt.bundleDir) + assert.Equal(t, tt.want, got) + assert.Equal(t, (err != nil), tt.wantErr) + }) + } +} diff --git a/pkg/collect/util_test.go b/pkg/collect/util_test.go index 80783193..beffbdef 100644 --- a/pkg/collect/util_test.go +++ b/pkg/collect/util_test.go @@ -3,10 +3,9 @@ package collect import ( "context" "math/rand" - "os" - "path/filepath" "testing" + "github.com/replicatedhq/troubleshoot/internal/testutils" "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2" troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2" "github.com/stretchr/testify/assert" @@ -130,18 +129,18 @@ func Test_createTLSConfig(t *testing.T) { { name: "complete tls params creates config successfully", tlsParams: v1beta2.TLSParams{ - CACert: getTestFixture(t, "db/ca.pem"), - ClientCert: getTestFixture(t, "db/client.pem"), - ClientKey: getTestFixture(t, "db/client-key.pem"), + CACert: testutils.GetTestFixture(t, "db/ca.pem"), + ClientCert: testutils.GetTestFixture(t, "db/client.pem"), + ClientKey: testutils.GetTestFixture(t, "db/client-key.pem"), }, }, { name: "complete tls params in secret creates config successfully", tlsParams: v1beta2.TLSParams{ Secret: createTLSSecret(t, k8sClient, map[string]string{ - "cacert": getTestFixture(t, "db/ca.pem"), - "clientCert": getTestFixture(t, "db/client.pem"), - "clientKey": getTestFixture(t, "db/client-key.pem"), + "cacert": testutils.GetTestFixture(t, "db/ca.pem"), + "clientCert": testutils.GetTestFixture(t, "db/client.pem"), + "clientKey": testutils.GetTestFixture(t, "db/client-key.pem"), }), }, }, @@ -155,7 +154,7 @@ func Test_createTLSConfig(t *testing.T) { name: "tls params with CA cert only in secret creates config successfully", tlsParams: v1beta2.TLSParams{ Secret: createTLSSecret(t, k8sClient, map[string]string{ - "cacert": getTestFixture(t, "db/ca.pem"), + "cacert": testutils.GetTestFixture(t, "db/ca.pem"), }), }, caCertOnly: true, @@ -163,7 +162,7 @@ func Test_createTLSConfig(t *testing.T) { { name: "tls params with CA cert only creates config successfully", tlsParams: v1beta2.TLSParams{ - CACert: getTestFixture(t, "db/ca.pem"), + CACert: testutils.GetTestFixture(t, "db/ca.pem"), }, caCertOnly: true, }, @@ -174,24 +173,24 @@ func Test_createTLSConfig(t *testing.T) { { name: "missing CA cert fails to create config with error", tlsParams: v1beta2.TLSParams{ - ClientCert: getTestFixture(t, "db/client.pem"), - ClientKey: getTestFixture(t, "db/client-key.pem"), + ClientCert: testutils.GetTestFixture(t, "db/client.pem"), + ClientKey: testutils.GetTestFixture(t, "db/client-key.pem"), }, hasError: true, }, { name: "missing client cert fails to create config with error", tlsParams: v1beta2.TLSParams{ - CACert: getTestFixture(t, "db/ca.pem"), - ClientKey: getTestFixture(t, "db/client-key.pem"), + CACert: testutils.GetTestFixture(t, "db/ca.pem"), + ClientKey: testutils.GetTestFixture(t, "db/client-key.pem"), }, hasError: true, }, { name: "missing client key fails to create config with error", tlsParams: v1beta2.TLSParams{ - CACert: getTestFixture(t, "db/ca.pem"), - ClientCert: getTestFixture(t, "db/client.pem"), + CACert: testutils.GetTestFixture(t, "db/ca.pem"), + ClientCert: testutils.GetTestFixture(t, "db/client.pem"), }, hasError: true, }, @@ -199,8 +198,8 @@ func Test_createTLSConfig(t *testing.T) { name: "missing CA cert in secret fails to create config with error", tlsParams: v1beta2.TLSParams{ Secret: createTLSSecret(t, k8sClient, map[string]string{ - "clientCert": getTestFixture(t, "db/client.pem"), - "clientKey": getTestFixture(t, "db/client-key.pem"), + "clientCert": testutils.GetTestFixture(t, "db/client.pem"), + "clientKey": testutils.GetTestFixture(t, "db/client-key.pem"), }), }, hasError: true, @@ -209,8 +208,8 @@ func Test_createTLSConfig(t *testing.T) { name: "missing client cert in secret fails to create config with error", tlsParams: v1beta2.TLSParams{ Secret: createTLSSecret(t, k8sClient, map[string]string{ - "cacert": getTestFixture(t, "db/ca.pem"), - "clientKey": getTestFixture(t, "db/client-key.pem"), + "cacert": testutils.GetTestFixture(t, "db/ca.pem"), + "clientKey": testutils.GetTestFixture(t, "db/client-key.pem"), }), }, hasError: true, @@ -219,8 +218,8 @@ func Test_createTLSConfig(t *testing.T) { name: "missing client key in secret fails to create config with error", tlsParams: v1beta2.TLSParams{ Secret: createTLSSecret(t, k8sClient, map[string]string{ - "cacert": getTestFixture(t, "db/ca.pem"), - "clientCert": getTestFixture(t, "db/client.pem"), + "cacert": testutils.GetTestFixture(t, "db/ca.pem"), + "clientCert": testutils.GetTestFixture(t, "db/client.pem"), }), }, hasError: true, @@ -287,11 +286,3 @@ func createTLSSecret(t *testing.T, client kubernetes.Interface, secretData map[s Name: secretName, } } - -func getTestFixture(t *testing.T, path string) string { - t.Helper() - p := filepath.Join("../../testdata", path) - b, err := os.ReadFile(p) - require.NoError(t, err) - return string(b) -} diff --git a/pkg/constants/constants.go b/pkg/constants/constants.go index 4f140761..9a991465 100644 --- a/pkg/constants/constants.go +++ b/pkg/constants/constants.go @@ -7,4 +7,6 @@ const ( DEFAULT_CLIENT_BURST = 100 // DEFAULT_CLIENT_USER_AGENT is an field that specifies the caller of troubleshoot request. DEFAULT_CLIENT_USER_AGENT = "ReplicatedTroubleshoot" + // VersionFilename is the name of the file that contains the support bundle version. + VersionFilename = "version.yaml" ) diff --git a/pkg/supportbundle/collect.go b/pkg/supportbundle/collect.go index fc490e90..cb81b453 100644 --- a/pkg/supportbundle/collect.go +++ b/pkg/supportbundle/collect.go @@ -185,8 +185,6 @@ func findFileName(basename, extension string) (string, error) { } } -const VersionFilename = "version.yaml" - func getVersionFile() (io.Reader, error) { version := troubleshootv1beta2.SupportBundleVersion{ ApiVersion: "troubleshoot.sh/v1beta2", diff --git a/pkg/supportbundle/load.go b/pkg/supportbundle/load.go index e773d5b2..65988f73 100644 --- a/pkg/supportbundle/load.go +++ b/pkg/supportbundle/load.go @@ -105,24 +105,15 @@ func ParseSupportBundleFromDoc(doc []byte) (*troubleshootv1beta2.SupportBundle, } func GetRedactorFromURI(redactorURI string) (*troubleshootv1beta2.Redactor, error) { - decode := scheme.Codecs.UniversalDeserializer().Decode - redactorContent, err := LoadRedactorSpec(redactorURI) if err != nil { return nil, errors.Wrapf(err, "failed to load redactor spec %s", redactorURI) } - redactorContent, err = docrewrite.ConvertToV1Beta2(redactorContent) + redactor, ok, err := toRedactGVK([]byte(redactorContent)) if err != nil { - return nil, errors.Wrap(err, "failed to convert to v1beta2") + return nil, errors.Wrapf(err, "failed to parse redactor from doc") } - - obj, _, err := decode([]byte(redactorContent), nil, nil) - if err != nil { - return nil, errors.Wrapf(err, "failed to parse redactors %s", redactorURI) - } - - redactor, ok := obj.(*troubleshootv1beta2.Redactor) if !ok { return nil, fmt.Errorf("%s is not a troubleshootv1beta2 redactor type", redactorURI) } @@ -130,6 +121,22 @@ func GetRedactorFromURI(redactorURI string) (*troubleshootv1beta2.Redactor, erro return redactor, nil } +func GetRedactorsFromURIs(redactorURIs []string) ([]*troubleshootv1beta2.Redact, error) { + redactors := []*troubleshootv1beta2.Redact{} + for _, redactor := range redactorURIs { + redactorObj, err := GetRedactorFromURI(redactor) + if err != nil { + return nil, err + } + + if redactorObj != nil { + redactors = append(redactors, redactorObj.Spec.Redactors...) + } + } + + return redactors, nil +} + func LoadSupportBundleSpec(arg string) ([]byte, error) { if strings.HasPrefix(arg, "secret/") { // format secret/namespace-name/secret-name @@ -251,25 +258,32 @@ func loadSpecFromURL(arg string) ([]byte, error) { func ParseRedactorsFromDocs(docs []string) ([]*troubleshootv1beta2.Redact, error) { var redactors []*troubleshootv1beta2.Redact - decode := scheme.Codecs.UniversalDeserializer().Decode - for i, doc := range docs { - doc, err := docrewrite.ConvertToV1Beta2([]byte(doc)) - if err != nil { - return nil, errors.Wrap(err, "failed to convert to v1beta2") - } - - obj, _, err := decode(doc, nil, nil) + multidocRedactors, ok, err := toRedactGVK([]byte(doc)) if err != nil { return nil, errors.Wrapf(err, "failed to parse redactor from doc %d", i) } - - multidocRedactors, ok := obj.(*troubleshootv1beta2.Redactor) if !ok { continue } + redactors = append(redactors, multidocRedactors.Spec.Redactors...) } return redactors, nil } + +func toRedactGVK(doc []byte) (*troubleshootv1beta2.Redactor, bool, error) { + doc, err := docrewrite.ConvertToV1Beta2(doc) + if err != nil { + return nil, false, errors.Wrap(err, "failed to convert to v1beta2") + } + + obj, _, err := scheme.Codecs.UniversalDeserializer().Decode([]byte(doc), nil, nil) + if err != nil { + return nil, false, err + } + + multidocRedactors, ok := obj.(*troubleshootv1beta2.Redactor) + return multidocRedactors, ok, nil +} diff --git a/pkg/supportbundle/supportbundle.go b/pkg/supportbundle/supportbundle.go index aca3b380..8061fe40 100644 --- a/pkg/supportbundle/supportbundle.go +++ b/pkg/supportbundle/supportbundle.go @@ -14,6 +14,7 @@ import ( analyzer "github.com/replicatedhq/troubleshoot/pkg/analyze" troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2" "github.com/replicatedhq/troubleshoot/pkg/collect" + "github.com/replicatedhq/troubleshoot/pkg/constants" "github.com/replicatedhq/troubleshoot/pkg/convert" "k8s.io/client-go/rest" "k8s.io/klog/v2" @@ -127,7 +128,7 @@ func CollectSupportBundleFromSpec(spec *troubleshootv1beta2.SupportBundleSpec, a return nil, errors.Wrap(err, "failed to get version file") } - err = result.SaveResult(bundlePath, VersionFilename, version) + err = result.SaveResult(bundlePath, constants.VersionFilename, version) if err != nil { return nil, errors.Wrap(err, "failed to write version") } @@ -155,7 +156,7 @@ func CollectSupportBundleFromSpec(spec *troubleshootv1beta2.SupportBundleSpec, a return nil, errors.Wrap(err, "failed to write analysis") } - if err := collect.TarSupportBundleDir(bundlePath, result, filename); err != nil { + if err := result.ArchiveSupportBundle(bundlePath, filename); err != nil { return nil, errors.Wrap(err, "create bundle file") } @@ -188,17 +189,12 @@ func CollectSupportBundleFromURI(specURI string, redactorURIs []string, opts Sup return nil, errors.Wrap(err, "could not bundle from URI") } - additionalRedactors := &troubleshootv1beta2.Redactor{} - for _, redactor := range redactorURIs { - redactorObj, err := GetRedactorFromURI(redactor) - if err != nil { - return nil, errors.Wrapf(err, "failed to get redactor spec %s", redactor) - } - - if redactorObj != nil { - additionalRedactors.Spec.Redactors = append(additionalRedactors.Spec.Redactors, redactorObj.Spec.Redactors...) - } + redactors, err := GetRedactorsFromURIs(redactorURIs) + if err != nil { + return nil, err } + additionalRedactors := &troubleshootv1beta2.Redactor{} + additionalRedactors.Spec.Redactors = redactors return CollectSupportBundleFromSpec(&supportBundle.Spec, additionalRedactors, opts) } diff --git a/test/validate-support-bundle-e2e.sh b/test/validate-support-bundle-e2e.sh index f1a7941c..7c9300af 100755 --- a/test/validate-support-bundle-e2e.sh +++ b/test/validate-support-bundle-e2e.sh @@ -6,23 +6,23 @@ tmpdir="$(mktemp -d)" bundle_archive_name="support-bundle.tar.gz" bundle_directory_name="support-bundle" +echo "====== Generating support bundle from k8s cluster ======" ./bin/support-bundle --debug --interactive=false examples/support-bundle/e2e.yaml --output=$tmpdir/$bundle_archive_name if [ $? -ne 0 ]; then echo "support-bundle command failed" - exit $EXIT_STATUS + exit $? fi -EXIT_STATUS=0 if ! tar -xvzf $tmpdir/$bundle_archive_name --directory $tmpdir; then -echo "A valid support bundle archive was not generated" -EXIT_STATUS=1 + echo "A valid support bundle archive was not generated" + exit 1 fi echo "$(cat $tmpdir/$bundle_directory_name/analysis.json)" if grep -q "No matching files" "$tmpdir/$bundle_directory_name/analysis.json"; then -echo "Some files were not collected" -EXIT_STATUS=1 + echo "Some files were not collected" + exit 1 fi EXIT_STATUS=0 @@ -37,7 +37,29 @@ jq -r '.[].insight.severity' "$tmpdir/$bundle_directory_name/analysis.json" | wh echo "Analyzers with severity of \"warn\" found" fi done +if [ $EXIT_STATUS -ne 0 ]; then + echo "support-bundle command failed" + exit $EXIT_STATUS +fi -rm -rf "$tmpdir" +echo "======= Redact an existing support bundle ======" +redact_tmpdir="$(mktemp -d)" +redacted_archive_name="$redact_tmpdir/redacted-support-bundle.tar.gz" +./bin/support-bundle redact examples/redact/e2e.yaml --bundle=$tmpdir/$bundle_archive_name --output=$redacted_archive_name +if [ $? -ne 0 ]; then + echo "support-bundle redact command failed" + exit $? +fi -exit $EXIT_STATUS +if ! tar -xvzf $redacted_archive_name --directory $redact_tmpdir; then + echo "Failed to extract redacted support bundle archive" + exit 1 +fi + +if ! grep "\*\*\*HIDDEN\*\*\*" "$redact_tmpdir/$bundle_directory_name/static-hi.log"; then + echo "$(cat $redact_tmpdir/$bundle_directory_name/static-hi.log)" + echo "Hidden content not found in redacted static-hi.log file" + exit 1 +fi + +rm -rf "$tmpdir" "$redact_tmpdir" diff --git a/testdata/supportbundle/extracted-sb/cluster-resources/pods/logs/default/static-hi/static-hi.log b/testdata/supportbundle/extracted-sb/cluster-resources/pods/logs/default/static-hi/static-hi.log new file mode 100644 index 00000000..06459998 --- /dev/null +++ b/testdata/supportbundle/extracted-sb/cluster-resources/pods/logs/default/static-hi/static-hi.log @@ -0,0 +1,2 @@ +hi static! + diff --git a/testdata/supportbundle/extracted-sb/static-hi.log b/testdata/supportbundle/extracted-sb/static-hi.log new file mode 120000 index 00000000..062624ba --- /dev/null +++ b/testdata/supportbundle/extracted-sb/static-hi.log @@ -0,0 +1 @@ +cluster-resources/pods/logs/default/static-hi/static-hi.log \ No newline at end of file diff --git a/testdata/supportbundle/missing-version.tar.gz b/testdata/supportbundle/missing-version.tar.gz new file mode 100644 index 00000000..5653b38a Binary files /dev/null and b/testdata/supportbundle/missing-version.tar.gz differ diff --git a/testdata/supportbundle/support-bundle.tar.gz b/testdata/supportbundle/support-bundle.tar.gz new file mode 100644 index 00000000..30e2706a Binary files /dev/null and b/testdata/supportbundle/support-bundle.tar.gz differ