mirror of
https://github.com/replicatedhq/troubleshoot.git
synced 2026-02-14 10:19:54 +00:00
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:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -41,3 +41,4 @@ sbom/
|
|||||||
|
|
||||||
# Ignore generated support bundles
|
# Ignore generated support bundles
|
||||||
*.tar.gz
|
*.tar.gz
|
||||||
|
!testdata/supportbundle/*.tar.gz
|
||||||
|
|||||||
6
Makefile
6
Makefile
@@ -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/...
|
||||||
|
|||||||
18
README.md
18
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
|
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
|
||||||
```
|
```
|
||||||
|
|
||||||
**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/).
|
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
|
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
|
||||||
```
|
```
|
||||||
|
|
||||||
**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/).
|
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).
|
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
|
# Software Bill of Materials
|
||||||
A signed SBOM that includes Troubleshoot dependencies is included in each release.
|
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** contains a software bill of materials for Troubleshoot.
|
||||||
- **troubleshoot-sbom.tgz.sig** is the digital signature for troubleshoot-sbom.tgz
|
- **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
|
- **key.pub** is the public key from the key pair used to sign troubleshoot-sbom.tgz
|
||||||
|
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
85
cmd/troubleshoot/cli/redact.go
Normal file
85
cmd/troubleshoot/cli/redact.go
Normal 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
|
||||||
|
}
|
||||||
@@ -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")
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
35
docs/support-bundle_redact.md
Normal file
35
docs/support-bundle_redact.md
Normal 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
|
||||||
@@ -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
10
examples/redact/e2e.yaml
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
apiVersion: troubleshoot.sh/v1beta2
|
||||||
|
kind: Redactor
|
||||||
|
metadata:
|
||||||
|
name: e2e-redactor
|
||||||
|
spec:
|
||||||
|
redactors:
|
||||||
|
- name: redact-static-text
|
||||||
|
removals:
|
||||||
|
values:
|
||||||
|
- static
|
||||||
24
internal/testutils/utils.go
Normal file
24
internal/testutils/utils.go
Normal 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)
|
||||||
|
}
|
||||||
@@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
49
pkg/analyze/download_test.go
Normal file
49
pkg/analyze/download_test.go
Normal 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)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -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)
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -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"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -0,0 +1,2 @@
|
|||||||
|
hi static!
|
||||||
|
|
||||||
1
testdata/supportbundle/extracted-sb/static-hi.log
vendored
Symbolic link
1
testdata/supportbundle/extracted-sb/static-hi.log
vendored
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
cluster-resources/pods/logs/default/static-hi/static-hi.log
|
||||||
BIN
testdata/supportbundle/missing-version.tar.gz
vendored
Normal file
BIN
testdata/supportbundle/missing-version.tar.gz
vendored
Normal file
Binary file not shown.
BIN
testdata/supportbundle/support-bundle.tar.gz
vendored
Normal file
BIN
testdata/supportbundle/support-bundle.tar.gz
vendored
Normal file
Binary file not shown.
Reference in New Issue
Block a user