diff --git a/cmd/troubleshoot/cli/root.go b/cmd/troubleshoot/cli/root.go index f02712b7..16ca54bd 100644 --- a/cmd/troubleshoot/cli/root.go +++ b/cmd/troubleshoot/cli/root.go @@ -18,9 +18,17 @@ func RootCmd() *cobra.Command { cmd := &cobra.Command{ Use: "support-bundle [urls...]", Args: cobra.MinimumNArgs(0), - Short: "Generate a support bundle", - Long: `A support bundle is an archive of files, output, metrics and state -from a server that can be used to assist when troubleshooting a Kubernetes cluster.`, + Short: "Generate a support bundle from a Kubernetes cluster or specified sources", + Long: `Generate a support bundle, an archive containing files, output, metrics, and cluster state to aid in troubleshooting Kubernetes clusters. + +If no arguments are provided, specs are automatically loaded from the cluster by default. + +**Argument Types**: +1. **Secret**: Load specs from a Kubernetes Secret. Format: "secret/namespace-name/secret-name[/data-key]" +2. **ConfigMap**: Load specs from a Kubernetes ConfigMap. Format: "configmap/namespace-name/configmap-name[/data-key]" +3. **File**: Load specs from a local file. Format: Local file path +4. **Standard Input**: Read specs from stdin. Format: "-" +5. **URL**: Load specs from a URL. Supports HTTP and OCI registry URLs.`, SilenceUsage: true, PersistentPreRun: func(cmd *cobra.Command, args []string) { v := viper.GetViper() @@ -69,7 +77,7 @@ from a server that can be used to assist when troubleshooting a Kubernetes clust cmd.Flags().Bool("interactive", true, "enable/disable interactive mode") cmd.Flags().Bool("collect-without-permissions", true, "always generate a support bundle, even if it some require additional permissions") cmd.Flags().StringSliceP("selector", "l", []string{"troubleshoot.sh/kind=support-bundle"}, "selector to filter on for loading additional support bundle specs found in secrets within the cluster") - cmd.Flags().Bool("load-cluster-specs", false, "enable/disable loading additional troubleshoot specs found within the cluster. required when no specs are provided on the command line") + cmd.Flags().Bool("load-cluster-specs", false, "enable/disable loading additional troubleshoot specs found within the cluster. This is the default behavior if no spec is provided as an argument") cmd.Flags().String("since-time", "", "force pod logs collectors to return logs after a specific date (RFC3339)") cmd.Flags().String("since", "", "force pod logs collectors to return logs newer than a relative duration like 5s, 2m, or 3h.") cmd.Flags().StringP("output", "o", "", "specify the output file path for the support bundle") diff --git a/cmd/troubleshoot/cli/run.go b/cmd/troubleshoot/cli/run.go index 65ad2fe1..f1ee69c4 100644 --- a/cmd/troubleshoot/cli/run.go +++ b/cmd/troubleshoot/cli/run.go @@ -38,9 +38,6 @@ import ( func runTroubleshoot(v *viper.Viper, args []string) error { ctx := context.Background() - if !v.GetBool("load-cluster-specs") && len(args) < 1 { - return errors.New("flag load-cluster-specs must be set if no specs are provided on the command line") - } restConfig, err := k8sutil.GetRESTConfig() if err != nil { @@ -285,11 +282,24 @@ func loadSupportBundleSpecsFromURIs(ctx context.Context, kinds *loader.Troublesh } func loadSpecs(ctx context.Context, args []string, client kubernetes.Interface) (*troubleshootv1beta2.SupportBundle, *troubleshootv1beta2.Redactor, error) { - // Append redactor uris to the args - allArgs := append(args, viper.GetStringSlice("redactors")...) - kinds, err := specs.LoadFromCLIArgs(ctx, client, allArgs, viper.GetViper()) - if err != nil { - return nil, nil, err + var ( + kinds = loader.NewTroubleshootKinds() + vp = viper.GetViper() + allArgs = append(args, vp.GetStringSlice("redactors")...) + err error + ) + + if len(args) < 1 { + fmt.Println("\r\033[36mNo specs provided, attempting to load from cluster...\033[m") + kinds, err = specs.LoadFromCluster(ctx, client, vp.GetStringSlice("selector"), vp.GetString("namespace")) + if err != nil { + return nil, nil, errors.Wrap(err, "failed to load specs from cluster, and no specs were provided as arguments") + } + } else { + kinds, err = specs.LoadFromCLIArgs(ctx, client, allArgs, vp) + if err != nil { + return nil, nil, errors.Wrap(err, "failed to load specs from CLI args") + } } // Load additional specs from support bundle URIs @@ -306,7 +316,7 @@ func loadSpecs(ctx context.Context, args []string, client kubernetes.Interface) if len(kinds.CollectorsV1Beta2) == 0 && len(kinds.HostCollectorsV1Beta2) == 0 && len(kinds.SupportBundlesV1Beta2) == 0 { - return nil, nil, errors.New("no collectors specified to run") + return nil, nil, types.NewExitCodeError(constants.EXIT_CODE_CATCH_ALL, errors.Wrap(err, "no collectors specified to run. Use --debug and/or -v=2 to see more information")) } // Merge specs diff --git a/docs/preflight.md b/docs/preflight.md index 70a790cc..5c2a34c0 100644 --- a/docs/preflight.md +++ b/docs/preflight.md @@ -37,6 +37,7 @@ preflight [url] [flags] --kubeconfig string Path to the kubeconfig file to use for CLI requests. --memprofile string File path to write memory profiling data -n, --namespace string If present, the namespace scope for this CLI request + --no-uri When this flag is used, Preflight does not attempt to retrieve the spec referenced by the uri: field` -o, --output string specify the output file path for the preflight checks --request-timeout string The length of time to wait before giving up on a single server request. Non-zero values should contain a corresponding time unit (e.g. 1s, 2m, 3h). A value of zero means don't timeout requests. (default "0") --selector string selector (label query) to filter remote collection nodes on. @@ -54,4 +55,4 @@ preflight [url] [flags] * [preflight oci-fetch](preflight_oci-fetch.md) - Fetch a preflight from an OCI registry and print it to standard out * [preflight version](preflight_version.md) - Print the current version and exit -###### Auto generated by spf13/cobra on 25-Jan-2024 +###### Auto generated by spf13/cobra on 23-Aug-2024 diff --git a/docs/preflight_oci-fetch.md b/docs/preflight_oci-fetch.md index 9a43c7e0..c3c056c3 100644 --- a/docs/preflight_oci-fetch.md +++ b/docs/preflight_oci-fetch.md @@ -34,4 +34,4 @@ preflight oci-fetch [URI] [flags] * [preflight](preflight.md) - Run and retrieve preflight checks in a cluster -###### Auto generated by spf13/cobra on 25-Jan-2024 +###### Auto generated by spf13/cobra on 23-Aug-2024 diff --git a/docs/preflight_version.md b/docs/preflight_version.md index 211bc434..14d50fb4 100644 --- a/docs/preflight_version.md +++ b/docs/preflight_version.md @@ -37,4 +37,4 @@ preflight version [flags] * [preflight](preflight.md) - Run and retrieve preflight checks in a cluster -###### Auto generated by spf13/cobra on 25-Jan-2024 +###### Auto generated by spf13/cobra on 23-Aug-2024 diff --git a/docs/support-bundle.md b/docs/support-bundle.md index 23a14f6c..2980079e 100644 --- a/docs/support-bundle.md +++ b/docs/support-bundle.md @@ -1,11 +1,19 @@ ## support-bundle -Generate a support bundle +Generate a support bundle from a Kubernetes cluster or specified sources ### Synopsis -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. +Generate a support bundle, an archive containing files, output, metrics, and cluster state to aid in troubleshooting Kubernetes clusters. + +If no arguments are provided, specs are automatically loaded from the cluster by default. + +**Argument Types**: +1. **Secret**: Load specs from a Kubernetes Secret. Format: "secret/namespace-name/secret-name[/data-key]" +2. **ConfigMap**: Load specs from a Kubernetes ConfigMap. Format: "configmap/namespace-name/configmap-name[/data-key]" +3. **File**: Load specs from a local file. Format: Local file path +4. **Standard Input**: Read specs from stdin. Format: "-" +5. **URL**: Load specs from a URL. Supports HTTP and OCI registry URLs. ``` support-bundle [urls...] [flags] @@ -32,7 +40,7 @@ support-bundle [urls...] [flags] --insecure-skip-tls-verify If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure --interactive enable/disable interactive mode (default true) --kubeconfig string Path to the kubeconfig file to use for CLI requests. - --load-cluster-specs enable/disable loading additional troubleshoot specs found within the cluster. required when no specs are provided on the command line + --load-cluster-specs enable/disable loading additional troubleshoot specs found within the cluster. This is the default behavior if no spec is provided as an argument --memprofile string File path to write memory profiling data -n, --namespace string If present, the namespace scope for this CLI request --no-uri When this flag is used, Troubleshoot does not attempt to retrieve the spec referenced by the uri: field` @@ -56,4 +64,4 @@ support-bundle [urls...] [flags] * [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 25-Jan-2024 +###### Auto generated by spf13/cobra on 23-Aug-2024 diff --git a/docs/support-bundle_analyze.md b/docs/support-bundle_analyze.md index 21f5d4bf..68b35617 100644 --- a/docs/support-bundle_analyze.md +++ b/docs/support-bundle_analyze.md @@ -28,6 +28,6 @@ support-bundle analyze [url] [flags] ### SEE ALSO -* [support-bundle](support-bundle.md) - Generate a support bundle +* [support-bundle](support-bundle.md) - Generate a support bundle from a Kubernetes cluster or specified sources -###### Auto generated by spf13/cobra on 25-Jan-2024 +###### Auto generated by spf13/cobra on 23-Aug-2024 diff --git a/docs/support-bundle_redact.md b/docs/support-bundle_redact.md index 75c43ccc..1fd5b329 100644 --- a/docs/support-bundle_redact.md +++ b/docs/support-bundle_redact.md @@ -13,7 +13,7 @@ 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] @@ -37,6 +37,6 @@ support-bundle redact [urls...] [flags] ### SEE ALSO -* [support-bundle](support-bundle.md) - Generate a support bundle +* [support-bundle](support-bundle.md) - Generate a support bundle from a Kubernetes cluster or specified sources -###### Auto generated by spf13/cobra on 25-Jan-2024 +###### Auto generated by spf13/cobra on 23-Aug-2024 diff --git a/docs/support-bundle_version.md b/docs/support-bundle_version.md index e457b774..f399f232 100644 --- a/docs/support-bundle_version.md +++ b/docs/support-bundle_version.md @@ -25,6 +25,6 @@ support-bundle version [flags] ### SEE ALSO -* [support-bundle](support-bundle.md) - Generate a support bundle +* [support-bundle](support-bundle.md) - Generate a support bundle from a Kubernetes cluster or specified sources -###### Auto generated by spf13/cobra on 25-Jan-2024 +###### Auto generated by spf13/cobra on 23-Aug-2024 diff --git a/test/validate-support-bundle-e2e.sh b/test/validate-support-bundle-e2e.sh index ce45addb..a54764e5 100755 --- a/test/validate-support-bundle-e2e.sh +++ b/test/validate-support-bundle-e2e.sh @@ -20,7 +20,10 @@ bundle_directory_name="support-bundle" echo "====== Generating support bundle from k8s cluster ======" recreate_tmpdir -./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 echo "support-bundle command failed" exit $? @@ -111,6 +114,57 @@ if ! grep "labelled-support-bundle-4 \*\*\*HIDDEN\*\*\*" "$tmpdir/$bundle_direct echo "Hidden content not found in redacted echo-hi-4 file" exit 1 fi +kubectl delete -f "$PRJ_ROOT/testdata/supportbundle/labelled-specs" + +echo "======= Generating support bundle from k8s cluster using 0 arguments and a spec in the cluster ======" +recreate_tmpdir +kubectl apply -f "$PRJ_ROOT/testdata/supportbundle/labelled-specs" +./bin/support-bundle -v1 --interactive=false --output=$tmpdir/$bundle_archive_name +if [ $? -ne 0 ]; then + echo "support-bundle command failed" + exit $? +fi + +if ! tar -xvzf $tmpdir/$bundle_archive_name --directory $tmpdir; then + echo "A valid support bundle archive was not generated" + exit 1 +fi + +if ! grep "labelled-support-bundle-1 \*\*\*HIDDEN\*\*\*" "$tmpdir/$bundle_directory_name/echo-hi-1"; then + echo "$(cat $tmpdir/$bundle_directory_name/echo-hi-1)" + echo "Hidden content not found in redacted echo-hi-1 file" + exit 1 +fi + +if ! grep "labelled-support-bundle-2 \*\*\*HIDDEN\*\*\*" "$tmpdir/$bundle_directory_name/echo-hi-2"; then + echo "$(cat $tmpdir/$bundle_directory_name/echo-hi-2)" + echo "Hidden content not found in redacted echo-hi-2 file" + exit 1 +fi + +if ! grep "labelled-support-bundle-3 \*\*\*HIDDEN\*\*\*" "$tmpdir/$bundle_directory_name/echo-hi-3"; then + echo "$(cat $tmpdir/$bundle_directory_name/echo-hi-3)" + echo "Hidden content not found in redacted echo-hi-3 file" + exit 1 +fi + +if ! grep "labelled-support-bundle-4 \*\*\*HIDDEN\*\*\*" "$tmpdir/$bundle_directory_name/echo-hi-4"; then + echo "$(cat $tmpdir/$bundle_directory_name/echo-hi-4)" + echo "Hidden content not found in redacted echo-hi-4 file" + exit 1 +fi +kubectl delete -f "$PRJ_ROOT/testdata/supportbundle/labelled-specs" + +echo "======= Generating support bundle from k8s cluster using 0 arguments and no spec in the cluster ======" +recreate_tmpdir +set +e +./bin/support-bundle -v1 --interactive=false --output="$tmpdir/$bundle_archive_name" +exit_code=$? +set -e +if [ $exit_code -eq 0 ]; then + echo "support-bundle command should have failed" + exit 1 +fi echo "======= Generating support bundle from k8s secret/// ======" recreate_tmpdir @@ -133,11 +187,15 @@ if ! grep "custom-spec-key \*\*\*HIDDEN\*\*\*" "$tmpdir/$bundle_directory_name/e echo "Hidden content not found in redacted echo-hi-3 file" exit 1 fi +kubectl delete -f "$PRJ_ROOT/testdata/supportbundle/labelled-specs" echo "======= Generating support bundle from k8s configmap// ======" recreate_tmpdir kubectl apply -f "$PRJ_ROOT/testdata/supportbundle/labelled-specs" -./bin/support-bundle -v1 --interactive=false configmap/labelled-specs/labelled-support-bundle-2 --output=$tmpdir/$bundle_archive_name +./bin/support-bundle -v1 \ + --interactive=false \ + configmap/labelled-specs/labelled-support-bundle-2 \ + --output=$tmpdir/$bundle_archive_name if [ $? -ne 0 ]; then echo "support-bundle command failed" exit $? @@ -153,3 +211,4 @@ if ! grep "labelled-support-bundle-2 REDACT" "$tmpdir/$bundle_directory_name/ech echo "Hidden content not found in redacted echo-hi-2 file" exit 1 fi +kubectl delete -f "$PRJ_ROOT/testdata/supportbundle/labelled-specs"