feat(redactors): Run redactors on an existing support bundle (#887)

* feat(redactors): Run redactors on an existing support bundle

Add redact subcommand to support-bundle to allow running redactors on an
existing bundle to creating a new redacted bundle.

The command will be launched like so

support-bundle redact <redactor urls> --bundle support-bundle.tar.gz

Fixes: #705
This commit is contained in:
Evans Mungai
2023-01-03 18:05:15 +00:00
committed by GitHub
parent d73d5c6a3a
commit a523551da9
33 changed files with 502 additions and 154 deletions

1
.gitignore vendored
View File

@@ -41,3 +41,4 @@ sbom/
# Ignore generated support bundles # Ignore generated support bundles
*.tar.gz *.tar.gz
!testdata/supportbundle/*.tar.gz

View File

@@ -35,7 +35,7 @@ endef
BUILDFLAGS = -tags "netgo containers_image_ostree_stub exclude_graphdriver_devicemapper exclude_graphdriver_btrfs containers_image_openpgp" -installsuffix netgo 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 .PHONY: ffi
ffi: fmt vet ffi: fmt vet
@@ -202,8 +202,8 @@ scan:
.PHONY: lint .PHONY: lint
lint: lint:
golangci-lint run -c .golangci.yaml golangci-lint run --new -c .golangci.yaml pkg/... cmd/...
.PHONY: lint-and-fix .PHONY: lint-and-fix
lint-and-fix: lint-and-fix:
golangci-lint run --fix -c .golangci.yaml golangci-lint run --new --fix -c .golangci.yaml pkg/... cmd/...

View File

@@ -12,7 +12,7 @@ To run a sample preflight check from a sample application, install the preflight
``` ```
curl https://krew.sh/preflight | bash 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 kubectl preflight https://preflight.replicated.com
@@ -31,7 +31,7 @@ To collect a sample support bundle, install the troubleshoot kubectl plugin:
``` ```
curl https://krew.sh/support-bundle | bash 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 kubectl support-bundle https://support-bundle.replicated.com

View File

@@ -91,16 +91,15 @@ func runCollect(v *viper.Viper, arg string) error {
troubleshootclientsetscheme.AddToScheme(scheme.Scheme) troubleshootclientsetscheme.AddToScheme(scheme.Scheme)
decode := scheme.Codecs.UniversalDeserializer().Decode decode := scheme.Codecs.UniversalDeserializer().Decode
additionalRedactors := &troubleshootv1beta2.Redactor{} redactors, err := supportbundle.GetRedactorsFromURIs(v.GetStringSlice("redactors"))
for idx, redactor := range v.GetStringSlice("redactors") { if err != nil {
redactorObj, err := supportbundle.GetRedactorFromURI(redactor) return errors.Wrap(err, "failed to get redactors")
if err != nil { }
return errors.Wrapf(err, "failed to get redactor spec %s, #%d", redactor, idx)
}
if redactorObj != nil { additionalRedactors := &troubleshootv1beta2.Redactor{
additionalRedactors.Spec.Redactors = append(additionalRedactors.Spec.Redactors, redactorObj.Spec.Redactors...) Spec: troubleshootv1beta2.RedactorSpec{
} Redactors: redactors,
},
} }
for i, additionalDoc := range multidocs { for i, additionalDoc := range multidocs {

View File

@@ -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
}

View File

@@ -15,7 +15,7 @@ import (
func RootCmd() *cobra.Command { func RootCmd() *cobra.Command {
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "support-bundle [url]", Use: "support-bundle [urls...]",
Args: cobra.MinimumNArgs(0), Args: cobra.MinimumNArgs(0),
Short: "Generate a support bundle", Short: "Generate a support bundle",
Long: `A support bundle is an archive of files, output, metrics and state 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) cobra.OnInitialize(initConfig)
cmd.AddCommand(Analyze()) cmd.AddCommand(Analyze())
cmd.AddCommand(Redact())
cmd.AddCommand(VersionCmd()) cmd.AddCommand(VersionCmd())
cmd.Flags().StringSlice("redactors", []string{}, "names of the additional redactors to use") cmd.Flags().StringSlice("redactors", []string{}, "names of the additional redactors to use")

View File

@@ -208,16 +208,11 @@ func runTroubleshoot(v *viper.Viper, arg []string) error {
return errors.New("no collectors specified in support bundle") return errors.New("no collectors specified in support bundle")
} }
for idx, redactor := range v.GetStringSlice("redactors") { redactors, err := supportbundle.GetRedactorsFromURIs(v.GetStringSlice("redactors"))
redactorObj, err := supportbundle.GetRedactorFromURI(redactor) if err != nil {
if err != nil { return errors.Wrap(err, "failed to get redactors")
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...)
}
} }
additionalRedactors.Spec.Redactors = append(additionalRedactors.Spec.Redactors, redactors...)
var collectorCB func(chan interface{}, string) var collectorCB func(chan interface{}, string)
progressChan := make(chan interface{}) // non-zero buffer can result in missed messages progressChan := make(chan interface{}) // non-zero buffer can result in missed messages

View File

@@ -7,6 +7,7 @@ import (
"path/filepath" "path/filepath"
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2" troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
"github.com/replicatedhq/troubleshoot/pkg/constants"
"github.com/replicatedhq/troubleshoot/pkg/version" "github.com/replicatedhq/troubleshoot/pkg/version"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"gopkg.in/yaml.v2" "gopkg.in/yaml.v2"
@@ -26,8 +27,6 @@ func VersionCmd() *cobra.Command {
return cmd return cmd
} }
const VersionFilename = "version.yaml"
func writeVersionFile(path string) error { func writeVersionFile(path string) error {
version := troubleshootv1beta2.SupportBundleVersion{ version := troubleshootv1beta2.SupportBundleVersion{
ApiVersion: "troubleshoot.sh/v1beta2", ApiVersion: "troubleshoot.sh/v1beta2",
@@ -41,7 +40,7 @@ func writeVersionFile(path string) error {
return err return err
} }
filename := filepath.Join(path, VersionFilename) filename := filepath.Join(path, constants.VersionFilename)
err = ioutil.WriteFile(filename, b, 0644) err = ioutil.WriteFile(filename, b, 0644)
if err != nil { if err != nil {
return err return err

View File

@@ -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 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-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. --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 --certificate-authority string Path to a cert file for the certificate authority
--client-certificate string Path to a client certificate file for TLS --client-certificate string Path to a client certificate file for TLS
--client-key string Path to a client key 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 * [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

View File

@@ -35,4 +35,4 @@ preflight version [flags]
* [preflight](preflight.md) - Run and retrieve preflight checks in a cluster * [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

View File

@@ -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. from a server that can be used to assist when troubleshooting a Kubernetes cluster.
``` ```
support-bundle [url] [flags] support-bundle [urls...] [flags]
``` ```
### Options ### 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 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-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. --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 --certificate-authority string Path to a cert file for the certificate authority
--client-certificate string Path to a client certificate file for TLS --client-certificate string Path to a client certificate file for TLS
--client-key string Path to a client key file for TLS --client-key string Path to a client key file for TLS
@@ -47,7 +47,8 @@ support-bundle [url] [flags]
### SEE ALSO ### SEE ALSO
* [support-bundle analyze](support-bundle_analyze.md) - analyze a support bundle * [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 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

View File

@@ -23,4 +23,4 @@ support-bundle analyze [url] [flags]
* [support-bundle](support-bundle.md) - Generate a support bundle * [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

View File

@@ -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

View File

@@ -20,4 +20,4 @@ support-bundle version [flags]
* [support-bundle](support-bundle.md) - Generate a support bundle * [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

10
examples/redact/e2e.yaml Normal file
View File

@@ -0,0 +1,10 @@
apiVersion: troubleshoot.sh/v1beta2
kind: Redactor
metadata:
name: e2e-redactor
spec:
redactors:
- name: redact-static-text
removals:
values:
- static

View File

@@ -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)
}

View File

@@ -4,7 +4,6 @@ import (
"archive/tar" "archive/tar"
"compress/gzip" "compress/gzip"
"io" "io"
"io/ioutil"
"os" "os"
"path/filepath" "path/filepath"
@@ -12,6 +11,7 @@ import (
"github.com/pkg/errors" "github.com/pkg/errors"
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2" troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
troubleshootscheme "github.com/replicatedhq/troubleshoot/pkg/client/troubleshootclientset/scheme" 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/docrewrite"
"github.com/replicatedhq/troubleshoot/pkg/logger" "github.com/replicatedhq/troubleshoot/pkg/logger"
"k8s.io/client-go/kubernetes/scheme" "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) { func DownloadAndAnalyze(bundleURL string, analyzersSpec string) ([]*AnalyzeResult, error) {
tmpDir, err := ioutil.TempDir("", "troubleshoot-k8s") tmpDir, rootDir, err := DownloadAndExtractSupportBundle(bundleURL)
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)
if err != nil { if err != nil {
return nil, errors.Wrap(err, "failed to find root dir") return nil, errors.Wrap(err, "failed to find root dir")
} }
defer os.RemoveAll(tmpDir)
_, err = os.Stat(filepath.Join(rootDir, "version.yaml")) var analyzers []*troubleshootv1beta2.Analyze
if err != nil {
return nil, errors.Wrap(err, "failed to read version.yaml")
}
analyzers := []*troubleshootv1beta2.Analyze{}
hostAnalyzers := []*troubleshootv1beta2.HostAnalyze{} hostAnalyzers := []*troubleshootv1beta2.HostAnalyze{}
if analyzersSpec == "" { if analyzersSpec == "" {
@@ -96,7 +82,34 @@ func DownloadAndAnalyze(bundleURL string, analyzersSpec string) ([]*AnalyzeResul
return AnalyzeLocal(rootDir, analyzers, hostAnalyzers) 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 { func downloadTroubleshootBundle(bundleURL string, destDir string) error {
// TODO: Move to separate package support bundle utils package
if bundleURL[0] == os.PathSeparator { if bundleURL[0] == os.PathSeparator {
f, err := os.Open(bundleURL) f, err := os.Open(bundleURL)
if err != nil { if err != nil {
@@ -111,7 +124,7 @@ func downloadTroubleshootBundle(bundleURL string, destDir string) error {
return errors.Wrap(err, "failed to get workdir") return errors.Wrap(err, "failed to get workdir")
} }
tmpDir, err := ioutil.TempDir("", "troubleshoot") tmpDir, err := os.MkdirTemp("", "troubleshoot")
if err != nil { if err != nil {
return errors.Wrap(err, "failed to create tmp dir") 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 { 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) gzReader, err := gzip.NewReader(reader)
if err != nil { if err != nil {
return errors.Wrap(err, "failed to create gzip reader") 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") return errors.Wrap(err, "failed to mkdir")
} }
case tar.TypeReg: 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 { if err := os.MkdirAll(dirName, 0755); err != nil {
return errors.Wrapf(err, "failed to mkdir for file %s", header.Name) 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 { if err != nil {
return errors.Wrap(err, "failed to open tar file") return errors.Wrap(err, "failed to open tar file")
} }
@@ -175,6 +190,21 @@ func ExtractTroubleshootBundle(reader io.Reader, destDir string) error {
if err != nil { if err != nil {
return errors.Wrap(err, "failed to extract file") 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 isInSubDir := true
for _, name := range names { for _, name := range names {
if name == "version.yaml" { if name == constants.VersionFilename {
isInSubDir = false isInSubDir = false
break break
} }
@@ -265,7 +295,7 @@ func FindBundleRootDir(localBundlePath string) (string, error) {
} }
func (f fileContentProvider) getFileContents(fileName string) ([]byte, 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 { func excludeFilePaths(files, excludeFiles []string) []string {
@@ -306,7 +336,7 @@ func (f fileContentProvider) getChildFileContents(dirName string, excludeFiles [
fileArr := map[string][]byte{} fileArr := map[string][]byte{}
for _, filePath := range files { for _, filePath := range files {
bytes, err := ioutil.ReadFile(filePath) bytes, err := os.ReadFile(filePath)
if err != nil { if err != nil {
return nil, errors.Wrapf(err, "read %q", filePath) return nil, errors.Wrapf(err, "read %q", filePath)
} }

View File

@@ -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)
}
})
}
}

View File

@@ -4,6 +4,7 @@ import (
"context" "context"
"testing" "testing"
"github.com/replicatedhq/troubleshoot/internal/testutils"
"github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2" "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
@@ -101,9 +102,9 @@ func TestCollectPostgres_createConnectConfigTLS(t *testing.T) {
Collector: &v1beta2.Database{ Collector: &v1beta2.Database{
URI: "postgresql://user:password@my-pghost:5432/defaultdb?sslmode=require", URI: "postgresql://user:password@my-pghost:5432/defaultdb?sslmode=require",
TLS: &v1beta2.TLSParams{ TLS: &v1beta2.TLSParams{
CACert: getTestFixture(t, "db/ca.pem"), CACert: testutils.GetTestFixture(t, "db/ca.pem"),
ClientCert: getTestFixture(t, "db/client.pem"), ClientCert: testutils.GetTestFixture(t, "db/client.pem"),
ClientKey: getTestFixture(t, "db/client-key.pem"), ClientKey: testutils.GetTestFixture(t, "db/client-key.pem"),
}, },
}, },
} }

View File

@@ -13,6 +13,7 @@ import (
"github.com/pkg/errors" "github.com/pkg/errors"
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2" troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
"github.com/replicatedhq/troubleshoot/pkg/redact" "github.com/replicatedhq/troubleshoot/pkg/redact"
"k8s.io/klog/v2"
) )
func RedactResult(bundlePath string, input CollectorResult, additionalRedactors []*troubleshootv1beta2.Redact) error { 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 // 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 { 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 { if err != nil {
return errors.Wrap(err, "failed to read symlink") 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) r, err := input.GetReader(bundlePath, file)
if err != nil { if err != nil {
@@ -53,8 +68,8 @@ func RedactResult(bundlePath string, input CollectorResult, additionalRedactors
reader = bytes.NewBuffer(v) 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 // If the file is .tar, .tgz or .tar.gz, it must not be redacted. Instead it is
//tar is decompressed, redacted and compressed back into the tar. // 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") { if filepath.Ext(file) == ".tar" || filepath.Ext(file) == ".tgz" || strings.HasSuffix(file, ".tar.gz") {
tmpDir, err := ioutil.TempDir("", "troubleshoot-subresult-") tmpDir, err := ioutil.TempDir("", "troubleshoot-subresult-")
if err != nil { if err != nil {

View File

@@ -3,6 +3,7 @@ package collect
import ( import (
"testing" "testing"
"github.com/replicatedhq/troubleshoot/internal/testutils"
v1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2" v1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
@@ -90,9 +91,9 @@ func TestCollectRedis_createTLSClient(t *testing.T) {
Collector: &v1beta2.Database{ Collector: &v1beta2.Database{
URI: "redis://localhost:6379", URI: "redis://localhost:6379",
TLS: &v1beta2.TLSParams{ TLS: &v1beta2.TLSParams{
CACert: getTestFixture(t, "db/ca.pem"), CACert: testutils.GetTestFixture(t, "db/ca.pem"),
ClientCert: getTestFixture(t, "db/client.pem"), ClientCert: testutils.GetTestFixture(t, "db/client.pem"),
ClientKey: getTestFixture(t, "db/client-key.pem"), ClientKey: testutils.GetTestFixture(t, "db/client-key.pem"),
}, },
}, },
} }

View File

@@ -65,7 +65,7 @@ func (r CollectorResult) SymLinkResult(bundlePath, relativeLinkPath, relativeFil
// Create the symlink // Create the symlink
// NOTE: When creating an archive, relative paths are used // NOTE: When creating an archive, relative paths are used
// to make the bundle more portable. That implementation // 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 // remain as-is to support memory only bundles e.g preflight
err = os.Symlink(filePath, linkPath) err = os.Symlink(filePath, linkPath)
if err != nil { 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) 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) fileWriter, err := os.Create(outputFilename)
if err != nil { if err != nil {
return errors.Wrap(err, "failed to create output file") return errors.Wrap(err, "failed to create output file")
@@ -235,7 +235,7 @@ func TarSupportBundleDir(bundlePath string, input CollectorResult, outputFilenam
tarWriter := tar.NewWriter(gzipWriter) tarWriter := tar.NewWriter(gzipWriter)
defer tarWriter.Close() defer tarWriter.Close()
for relativeName := range input { for relativeName := range r {
filename := filepath.Join(bundlePath, relativeName) filename := filepath.Join(bundlePath, relativeName)
info, err := os.Lstat(filename) info, err := os.Lstat(filename)
if err != nil { if err != nil {
@@ -315,3 +315,44 @@ func TarSupportBundleDir(bundlePath string, input CollectorResult, outputFilenam
return nil 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)
}

View File

@@ -1,8 +1,10 @@
package collect package collect
import ( import (
"path/filepath"
"testing" "testing"
"github.com/replicatedhq/troubleshoot/internal/testutils"
"github.com/stretchr/testify/assert" "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("a"), r["a"])
assert.Equal(t, []byte("b"), r["b"]) 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)
})
}
}

View File

@@ -3,10 +3,9 @@ package collect
import ( import (
"context" "context"
"math/rand" "math/rand"
"os"
"path/filepath"
"testing" "testing"
"github.com/replicatedhq/troubleshoot/internal/testutils"
"github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2" "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2" troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
@@ -130,18 +129,18 @@ func Test_createTLSConfig(t *testing.T) {
{ {
name: "complete tls params creates config successfully", name: "complete tls params creates config successfully",
tlsParams: v1beta2.TLSParams{ tlsParams: v1beta2.TLSParams{
CACert: getTestFixture(t, "db/ca.pem"), CACert: testutils.GetTestFixture(t, "db/ca.pem"),
ClientCert: getTestFixture(t, "db/client.pem"), ClientCert: testutils.GetTestFixture(t, "db/client.pem"),
ClientKey: getTestFixture(t, "db/client-key.pem"), ClientKey: testutils.GetTestFixture(t, "db/client-key.pem"),
}, },
}, },
{ {
name: "complete tls params in secret creates config successfully", name: "complete tls params in secret creates config successfully",
tlsParams: v1beta2.TLSParams{ tlsParams: v1beta2.TLSParams{
Secret: createTLSSecret(t, k8sClient, map[string]string{ Secret: createTLSSecret(t, k8sClient, map[string]string{
"cacert": getTestFixture(t, "db/ca.pem"), "cacert": testutils.GetTestFixture(t, "db/ca.pem"),
"clientCert": getTestFixture(t, "db/client.pem"), "clientCert": testutils.GetTestFixture(t, "db/client.pem"),
"clientKey": getTestFixture(t, "db/client-key.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", name: "tls params with CA cert only in secret creates config successfully",
tlsParams: v1beta2.TLSParams{ tlsParams: v1beta2.TLSParams{
Secret: createTLSSecret(t, k8sClient, map[string]string{ Secret: createTLSSecret(t, k8sClient, map[string]string{
"cacert": getTestFixture(t, "db/ca.pem"), "cacert": testutils.GetTestFixture(t, "db/ca.pem"),
}), }),
}, },
caCertOnly: true, caCertOnly: true,
@@ -163,7 +162,7 @@ func Test_createTLSConfig(t *testing.T) {
{ {
name: "tls params with CA cert only creates config successfully", name: "tls params with CA cert only creates config successfully",
tlsParams: v1beta2.TLSParams{ tlsParams: v1beta2.TLSParams{
CACert: getTestFixture(t, "db/ca.pem"), CACert: testutils.GetTestFixture(t, "db/ca.pem"),
}, },
caCertOnly: true, caCertOnly: true,
}, },
@@ -174,24 +173,24 @@ func Test_createTLSConfig(t *testing.T) {
{ {
name: "missing CA cert fails to create config with error", name: "missing CA cert fails to create config with error",
tlsParams: v1beta2.TLSParams{ tlsParams: v1beta2.TLSParams{
ClientCert: getTestFixture(t, "db/client.pem"), ClientCert: testutils.GetTestFixture(t, "db/client.pem"),
ClientKey: getTestFixture(t, "db/client-key.pem"), ClientKey: testutils.GetTestFixture(t, "db/client-key.pem"),
}, },
hasError: true, hasError: true,
}, },
{ {
name: "missing client cert fails to create config with error", name: "missing client cert fails to create config with error",
tlsParams: v1beta2.TLSParams{ tlsParams: v1beta2.TLSParams{
CACert: getTestFixture(t, "db/ca.pem"), CACert: testutils.GetTestFixture(t, "db/ca.pem"),
ClientKey: getTestFixture(t, "db/client-key.pem"), ClientKey: testutils.GetTestFixture(t, "db/client-key.pem"),
}, },
hasError: true, hasError: true,
}, },
{ {
name: "missing client key fails to create config with error", name: "missing client key fails to create config with error",
tlsParams: v1beta2.TLSParams{ tlsParams: v1beta2.TLSParams{
CACert: getTestFixture(t, "db/ca.pem"), CACert: testutils.GetTestFixture(t, "db/ca.pem"),
ClientCert: getTestFixture(t, "db/client.pem"), ClientCert: testutils.GetTestFixture(t, "db/client.pem"),
}, },
hasError: true, hasError: true,
}, },
@@ -199,8 +198,8 @@ func Test_createTLSConfig(t *testing.T) {
name: "missing CA cert in secret fails to create config with error", name: "missing CA cert in secret fails to create config with error",
tlsParams: v1beta2.TLSParams{ tlsParams: v1beta2.TLSParams{
Secret: createTLSSecret(t, k8sClient, map[string]string{ Secret: createTLSSecret(t, k8sClient, map[string]string{
"clientCert": getTestFixture(t, "db/client.pem"), "clientCert": testutils.GetTestFixture(t, "db/client.pem"),
"clientKey": getTestFixture(t, "db/client-key.pem"), "clientKey": testutils.GetTestFixture(t, "db/client-key.pem"),
}), }),
}, },
hasError: true, hasError: true,
@@ -209,8 +208,8 @@ func Test_createTLSConfig(t *testing.T) {
name: "missing client cert in secret fails to create config with error", name: "missing client cert in secret fails to create config with error",
tlsParams: v1beta2.TLSParams{ tlsParams: v1beta2.TLSParams{
Secret: createTLSSecret(t, k8sClient, map[string]string{ Secret: createTLSSecret(t, k8sClient, map[string]string{
"cacert": getTestFixture(t, "db/ca.pem"), "cacert": testutils.GetTestFixture(t, "db/ca.pem"),
"clientKey": getTestFixture(t, "db/client-key.pem"), "clientKey": testutils.GetTestFixture(t, "db/client-key.pem"),
}), }),
}, },
hasError: true, hasError: true,
@@ -219,8 +218,8 @@ func Test_createTLSConfig(t *testing.T) {
name: "missing client key in secret fails to create config with error", name: "missing client key in secret fails to create config with error",
tlsParams: v1beta2.TLSParams{ tlsParams: v1beta2.TLSParams{
Secret: createTLSSecret(t, k8sClient, map[string]string{ Secret: createTLSSecret(t, k8sClient, map[string]string{
"cacert": getTestFixture(t, "db/ca.pem"), "cacert": testutils.GetTestFixture(t, "db/ca.pem"),
"clientCert": getTestFixture(t, "db/client.pem"), "clientCert": testutils.GetTestFixture(t, "db/client.pem"),
}), }),
}, },
hasError: true, hasError: true,
@@ -287,11 +286,3 @@ func createTLSSecret(t *testing.T, client kubernetes.Interface, secretData map[s
Name: secretName, 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)
}

View File

@@ -7,4 +7,6 @@ const (
DEFAULT_CLIENT_BURST = 100 DEFAULT_CLIENT_BURST = 100
// DEFAULT_CLIENT_USER_AGENT is an field that specifies the caller of troubleshoot request. // DEFAULT_CLIENT_USER_AGENT is an field that specifies the caller of troubleshoot request.
DEFAULT_CLIENT_USER_AGENT = "ReplicatedTroubleshoot" DEFAULT_CLIENT_USER_AGENT = "ReplicatedTroubleshoot"
// VersionFilename is the name of the file that contains the support bundle version.
VersionFilename = "version.yaml"
) )

View File

@@ -185,8 +185,6 @@ func findFileName(basename, extension string) (string, error) {
} }
} }
const VersionFilename = "version.yaml"
func getVersionFile() (io.Reader, error) { func getVersionFile() (io.Reader, error) {
version := troubleshootv1beta2.SupportBundleVersion{ version := troubleshootv1beta2.SupportBundleVersion{
ApiVersion: "troubleshoot.sh/v1beta2", ApiVersion: "troubleshoot.sh/v1beta2",

View File

@@ -105,24 +105,15 @@ func ParseSupportBundleFromDoc(doc []byte) (*troubleshootv1beta2.SupportBundle,
} }
func GetRedactorFromURI(redactorURI string) (*troubleshootv1beta2.Redactor, error) { func GetRedactorFromURI(redactorURI string) (*troubleshootv1beta2.Redactor, error) {
decode := scheme.Codecs.UniversalDeserializer().Decode
redactorContent, err := LoadRedactorSpec(redactorURI) redactorContent, err := LoadRedactorSpec(redactorURI)
if err != nil { if err != nil {
return nil, errors.Wrapf(err, "failed to load redactor spec %s", redactorURI) 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 { 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 { if !ok {
return nil, fmt.Errorf("%s is not a troubleshootv1beta2 redactor type", redactorURI) 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 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) { func LoadSupportBundleSpec(arg string) ([]byte, error) {
if strings.HasPrefix(arg, "secret/") { if strings.HasPrefix(arg, "secret/") {
// format secret/namespace-name/secret-name // format secret/namespace-name/secret-name
@@ -251,25 +258,32 @@ func loadSpecFromURL(arg string) ([]byte, error) {
func ParseRedactorsFromDocs(docs []string) ([]*troubleshootv1beta2.Redact, error) { func ParseRedactorsFromDocs(docs []string) ([]*troubleshootv1beta2.Redact, error) {
var redactors []*troubleshootv1beta2.Redact var redactors []*troubleshootv1beta2.Redact
decode := scheme.Codecs.UniversalDeserializer().Decode
for i, doc := range docs { for i, doc := range docs {
doc, err := docrewrite.ConvertToV1Beta2([]byte(doc)) multidocRedactors, ok, err := toRedactGVK([]byte(doc))
if err != nil {
return nil, errors.Wrap(err, "failed to convert to v1beta2")
}
obj, _, err := decode(doc, nil, nil)
if err != nil { if err != nil {
return nil, errors.Wrapf(err, "failed to parse redactor from doc %d", i) return nil, errors.Wrapf(err, "failed to parse redactor from doc %d", i)
} }
multidocRedactors, ok := obj.(*troubleshootv1beta2.Redactor)
if !ok { if !ok {
continue continue
} }
redactors = append(redactors, multidocRedactors.Spec.Redactors...) redactors = append(redactors, multidocRedactors.Spec.Redactors...)
} }
return redactors, nil 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
}

View File

@@ -14,6 +14,7 @@ import (
analyzer "github.com/replicatedhq/troubleshoot/pkg/analyze" analyzer "github.com/replicatedhq/troubleshoot/pkg/analyze"
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2" troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
"github.com/replicatedhq/troubleshoot/pkg/collect" "github.com/replicatedhq/troubleshoot/pkg/collect"
"github.com/replicatedhq/troubleshoot/pkg/constants"
"github.com/replicatedhq/troubleshoot/pkg/convert" "github.com/replicatedhq/troubleshoot/pkg/convert"
"k8s.io/client-go/rest" "k8s.io/client-go/rest"
"k8s.io/klog/v2" "k8s.io/klog/v2"
@@ -127,7 +128,7 @@ func CollectSupportBundleFromSpec(spec *troubleshootv1beta2.SupportBundleSpec, a
return nil, errors.Wrap(err, "failed to get version file") 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 { if err != nil {
return nil, errors.Wrap(err, "failed to write version") 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") 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") 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") return nil, errors.Wrap(err, "could not bundle from URI")
} }
additionalRedactors := &troubleshootv1beta2.Redactor{} redactors, err := GetRedactorsFromURIs(redactorURIs)
for _, redactor := range redactorURIs { if err != nil {
redactorObj, err := GetRedactorFromURI(redactor) return nil, err
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...)
}
} }
additionalRedactors := &troubleshootv1beta2.Redactor{}
additionalRedactors.Spec.Redactors = redactors
return CollectSupportBundleFromSpec(&supportBundle.Spec, additionalRedactors, opts) return CollectSupportBundleFromSpec(&supportBundle.Spec, additionalRedactors, opts)
} }

View File

@@ -6,23 +6,23 @@ tmpdir="$(mktemp -d)"
bundle_archive_name="support-bundle.tar.gz" bundle_archive_name="support-bundle.tar.gz"
bundle_directory_name="support-bundle" 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 ./bin/support-bundle --debug --interactive=false examples/support-bundle/e2e.yaml --output=$tmpdir/$bundle_archive_name
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
echo "support-bundle command failed" echo "support-bundle command failed"
exit $EXIT_STATUS exit $?
fi fi
EXIT_STATUS=0
if ! tar -xvzf $tmpdir/$bundle_archive_name --directory $tmpdir; then if ! tar -xvzf $tmpdir/$bundle_archive_name --directory $tmpdir; then
echo "A valid support bundle archive was not generated" echo "A valid support bundle archive was not generated"
EXIT_STATUS=1 exit 1
fi fi
echo "$(cat $tmpdir/$bundle_directory_name/analysis.json)" echo "$(cat $tmpdir/$bundle_directory_name/analysis.json)"
if grep -q "No matching files" "$tmpdir/$bundle_directory_name/analysis.json"; then if grep -q "No matching files" "$tmpdir/$bundle_directory_name/analysis.json"; then
echo "Some files were not collected" echo "Some files were not collected"
EXIT_STATUS=1 exit 1
fi fi
EXIT_STATUS=0 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" echo "Analyzers with severity of \"warn\" found"
fi fi
done 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"

View File

@@ -0,0 +1 @@
cluster-resources/pods/logs/default/static-hi/static-hi.log

Binary file not shown.

Binary file not shown.