diff --git a/Makefile b/Makefile index 31ff1cf2..a3afeaa3 100644 --- a/Makefile +++ b/Makefile @@ -128,7 +128,7 @@ run-preflight: preflight .PHONY: run-troubleshoot run-troubleshoot: support-bundle - ./bin/support-bundle ./examples/troubleshoot/sample-troubleshoot.yaml + ./bin/support-bundle ./examples/troubleshoot/sample-supportbundle.yaml .PHONY: run-analyze run-analyze: analyze diff --git a/cmd/troubleshoot/cli/run.go b/cmd/troubleshoot/cli/run.go index 4a8f7cdc..7d3f5996 100644 --- a/cmd/troubleshoot/cli/run.go +++ b/cmd/troubleshoot/cli/run.go @@ -21,10 +21,12 @@ import ( "github.com/mholt/archiver" "github.com/pkg/errors" "github.com/replicatedhq/troubleshoot/cmd/util" + analyzer "github.com/replicatedhq/troubleshoot/pkg/analyze" troubleshootv1beta1 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta1" "github.com/replicatedhq/troubleshoot/pkg/client/troubleshootclientset/scheme" troubleshootclientsetscheme "github.com/replicatedhq/troubleshoot/pkg/client/troubleshootclientset/scheme" "github.com/replicatedhq/troubleshoot/pkg/collect" + "github.com/replicatedhq/troubleshoot/pkg/convert" "github.com/replicatedhq/troubleshoot/pkg/redact" "github.com/spf13/viper" spin "github.com/tj/go-spin" @@ -132,7 +134,63 @@ func runTroubleshoot(v *viper.Viper, arg string) error { fmt.Printf("\r%s\r", cursor.ClearEntireLine()) - if len(supportBundleSpec.Spec.AfterCollection) == 0 { + // upload if needed + fileUploaded := false + if len(supportBundleSpec.Spec.AfterCollection) > 0 { + for _, ac := range supportBundleSpec.Spec.AfterCollection { + if ac.UploadResultsTo != nil { + if err := uploadSupportBundle(ac.UploadResultsTo, archivePath); err != nil { + c := color.New(color.FgHiRed) + c.Printf("%s\r * Failed to upload support bundle: %v\n", cursor.ClearEntireLine(), err) + } else { + fileUploaded = true + } + } else if ac.Callback != nil { + if err := callbackSupportBundleAPI(ac.Callback, archivePath); err != nil { + c := color.New(color.FgHiRed) + c.Printf("%s\r * Failed to notify API that support bundle has been uploaded: %v\n", cursor.ClearEntireLine(), err) + } + } + } + + } + + // perform analysis, if possible + if len(supportBundleSpec.Spec.Analyzers) > 0 { + tmpDir, err := ioutil.TempDir("", "troubleshoot") + if err != nil { + c := color.New(color.FgHiRed) + c.Printf("%s\r * Failed to make directory for analysis: %v\n", cursor.ClearEntireLine(), err) + } + + f, err := os.Open(archivePath) + if err != nil { + c := color.New(color.FgHiRed) + c.Printf("%s\r * Failed to open support bundle for analysis: %v\n", cursor.ClearEntireLine(), err) + + } + if err := analyzer.ExtractTroubleshootBundle(f, tmpDir); err != nil { + c := color.New(color.FgHiRed) + c.Printf("%s\r * Failed to extract support bundle for analysis: %v\n", cursor.ClearEntireLine(), err) + } + + analyzeResults, err := analyzer.AnalyzeLocal(tmpDir, supportBundleSpec.Spec.Analyzers) + if err != nil { + c := color.New(color.FgHiRed) + c.Printf("%s\r * Failed to analyze support bundle: %v\n", cursor.ClearEntireLine(), err) + } + + data := convert.FromAnalyzerResult(analyzeResults) + formatted, err := json.MarshalIndent(data, "", " ") + if err != nil { + c := color.New(color.FgHiRed) + c.Printf("%s\r * Failed to format analysis: %v\n", cursor.ClearEntireLine(), err) + } + + fmt.Printf("%s", formatted) + } + + if !fileUploaded { msg := archivePath if appName := supportBundleSpec.Labels["applicationName"]; appName != "" { f := `A support bundle for %s has been created in this directory @@ -146,23 +204,6 @@ the %s Admin Console to begin analysis.` return nil } - fileUploaded := false - for _, ac := range supportBundleSpec.Spec.AfterCollection { - if ac.UploadResultsTo != nil { - if err := uploadSupportBundle(ac.UploadResultsTo, archivePath); err != nil { - c := color.New(color.FgHiRed) - c.Printf("%s\r * Failed to upload support bundle: %v\n", cursor.ClearEntireLine(), err) - } else { - fileUploaded = true - } - } else if ac.Callback != nil { - if err := callbackSupportBundleAPI(ac.Callback, archivePath); err != nil { - c := color.New(color.FgHiRed) - c.Printf("%s\r * Failed to notify API that support bundle has been uploaded: %v\n", cursor.ClearEntireLine(), err) - } - } - } - fmt.Printf("\r%s\r", cursor.ClearEntireLine()) if fileUploaded { fmt.Printf("A support bundle has been created and uploaded to your cluster for analysis. Please visit the Troubleshoot page to continue.\n") diff --git a/examples/troubleshoot/sample-supportbundle.yaml b/examples/troubleshoot/sample-supportbundle.yaml index 44a1fc34..c85a3c45 100644 --- a/examples/troubleshoot/sample-supportbundle.yaml +++ b/examples/troubleshoot/sample-supportbundle.yaml @@ -4,4 +4,17 @@ metadata: name: supportbundle-sample spec: collectors: [] - analyzers: [] \ No newline at end of file + analyzers: + - clusterVersion: + outcomes: + - fail: + when: "< 1.13.0" + message: The application requires at Kubernetes 1.13.0 or later, and recommends 1.15.0. + uri: https://www.kubernetes.io + - warn: + when: "< 1.15.0" + message: Your cluster meets the minimum version of Kubernetes, but we recommend you update to 1.15.0 or later. + uri: https://kubernetes.io + - pass: + when: ">= 1.15.0" + message: Your cluster meets the recommended and required versions of Kubernetes. diff --git a/pkg/analyze/download.go b/pkg/analyze/download.go index 2c40617e..c24602e0 100644 --- a/pkg/analyze/download.go +++ b/pkg/analyze/download.go @@ -20,6 +20,26 @@ type fileContentProvider struct { rootDir string } +// Analyze local will analyze a locally available (already downloaded) bundle +func AnalyzeLocal(localBundlePath string, analyzers []*troubleshootv1beta1.Analyze) ([]*AnalyzeResult, error) { + fcp := fileContentProvider{rootDir: localBundlePath} + + analyzeResults := []*AnalyzeResult{} + for _, analyzer := range analyzers { + analyzeResult, err := Analyze(analyzer, fcp.getFileContents, fcp.getChildFileContents) + if err != nil { + logger.Printf("an analyzer failed to run: %v\n", err) + continue + } + + if analyzeResult != nil { + analyzeResults = append(analyzeResults, analyzeResult) + } + } + + return analyzeResults, nil +} + func DownloadAndAnalyze(bundleURL string, analyzersSpec string) ([]*AnalyzeResult, error) { tmpDir, err := ioutil.TempDir("", "troubleshoot-k8s") if err != nil { @@ -52,22 +72,7 @@ func DownloadAndAnalyze(bundleURL string, analyzersSpec string) ([]*AnalyzeResul analyzers = parsedAnalyzers } - fcp := fileContentProvider{rootDir: tmpDir} - - analyzeResults := []*AnalyzeResult{} - for _, analyzer := range analyzers { - analyzeResult, err := Analyze(analyzer, fcp.getFileContents, fcp.getChildFileContents) - if err != nil { - logger.Printf("an analyzer failed to run: %v\n", err) - continue - } - - if analyzeResult != nil { - analyzeResults = append(analyzeResults, analyzeResult) - } - } - - return analyzeResults, nil + return AnalyzeLocal(tmpDir, analyzers) } func downloadTroubleshootBundle(bundleURL string, destDir string) error { @@ -77,7 +82,7 @@ func downloadTroubleshootBundle(bundleURL string, destDir string) error { return errors.Wrap(err, "failed to open support bundle") } defer f.Close() - return extractTroubleshootBundle(f, destDir) + return ExtractTroubleshootBundle(f, destDir) } pwd, err := os.Getwd() @@ -85,7 +90,7 @@ func downloadTroubleshootBundle(bundleURL string, destDir string) error { return errors.Wrap(err, "failed to get workdir") } - tmpDir, err := ioutil.TempDir("", "getter") + tmpDir, err := ioutil.TempDir("", "troubleshoot") if err != nil { return errors.Wrap(err, "failed to create tmp dir") } @@ -107,10 +112,10 @@ func downloadTroubleshootBundle(bundleURL string, destDir string) error { } defer f.Close() - return extractTroubleshootBundle(f, destDir) + return ExtractTroubleshootBundle(f, destDir) } -func extractTroubleshootBundle(reader io.Reader, destDir string) error { +func ExtractTroubleshootBundle(reader io.Reader, destDir string) error { gzReader, err := gzip.NewReader(reader) if err != nil { return errors.Wrap(err, "failed to create gzip reader")