mirror of
https://github.com/replicatedhq/troubleshoot.git
synced 2026-02-14 18:29:53 +00:00
store analysis in the support bundle (#417)
* store analysis in the support bundle
This commit is contained in:
committed by
GitHub
parent
10785987c5
commit
465a533640
@@ -153,41 +153,29 @@ func runTroubleshoot(v *viper.Viper, arg string) error {
|
||||
Namespace: v.GetString("namespace"),
|
||||
ProgressChan: progressChan,
|
||||
SinceTime: sinceTime,
|
||||
}
|
||||
|
||||
archivePath, err := supportbundle.CollectSupportBundleFromSpec(&supportBundle.Spec, additionalRedactors, createOpts)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "run collectors")
|
||||
FromCLI: true,
|
||||
}
|
||||
|
||||
c := color.New()
|
||||
c.Println(fmt.Sprintf("\r%s\r", cursor.ClearEntireLine()))
|
||||
|
||||
fileUploaded, err := supportbundle.ProcessSupportBundleAfterCollection(&supportBundle.Spec, archivePath)
|
||||
response, err := supportbundle.CollectSupportBundleFromSpec(&supportBundle.Spec, additionalRedactors, createOpts)
|
||||
if err != nil {
|
||||
c := color.New(color.FgHiRed)
|
||||
c.Printf("%s\r * %v\n", cursor.ClearEntireLine(), err)
|
||||
// don't die
|
||||
return errors.Wrap(err, "failed to run collect and analyze process")
|
||||
}
|
||||
|
||||
analyzeResults, err := supportbundle.AnalyzeAndExtractSupportBundle(&supportBundle.Spec, archivePath)
|
||||
if err != nil {
|
||||
c := color.New(color.FgHiRed)
|
||||
c.Printf("%s\r * %v\n", cursor.ClearEntireLine(), err)
|
||||
// Don't die
|
||||
} else if len(analyzeResults) > 0 {
|
||||
|
||||
if len(response.AnalyzerResults) > 0 {
|
||||
interactive := v.GetBool("interactive") && isatty.IsTerminal(os.Stdout.Fd())
|
||||
|
||||
if interactive {
|
||||
close(finishedCh) // this removes the spinner
|
||||
isFinishedChClosed = true
|
||||
|
||||
if err := showInteractiveResults(supportBundle.Name, analyzeResults); err != nil {
|
||||
if err := showInteractiveResults(supportBundle.Name, response.AnalyzerResults); err != nil {
|
||||
interactive = false
|
||||
}
|
||||
} else {
|
||||
data := convert.FromAnalyzerResult(analyzeResults)
|
||||
data := convert.FromAnalyzerResult(response.AnalyzerResults)
|
||||
formatted, err := json.MarshalIndent(data, "", " ")
|
||||
if err != nil {
|
||||
c := color.New(color.FgHiRed)
|
||||
@@ -198,13 +186,13 @@ func runTroubleshoot(v *viper.Viper, arg string) error {
|
||||
}
|
||||
}
|
||||
|
||||
if !fileUploaded {
|
||||
msg := archivePath
|
||||
if !response.FileUploaded {
|
||||
msg := response.ArchivePath
|
||||
if appName := supportBundle.Labels["applicationName"]; appName != "" {
|
||||
f := `A support bundle for %s has been created in this directory
|
||||
named %s. Please upload it on the Troubleshoot page of
|
||||
the %s Admin Console to begin analysis.`
|
||||
msg = fmt.Sprintf(f, appName, archivePath, appName)
|
||||
msg = fmt.Sprintf(f, appName, response.ArchivePath, appName)
|
||||
}
|
||||
|
||||
fmt.Printf("%s\n", msg)
|
||||
@@ -213,11 +201,11 @@ the %s Admin Console to begin analysis.`
|
||||
}
|
||||
|
||||
fmt.Printf("\r%s\r", cursor.ClearEntireLine())
|
||||
if fileUploaded {
|
||||
if response.FileUploaded {
|
||||
fmt.Printf("A support bundle has been created and uploaded to your cluster for analysis. Please visit the Troubleshoot page to continue.\n")
|
||||
fmt.Printf("A copy of this support bundle was written to the current directory, named %q\n", archivePath)
|
||||
fmt.Printf("A copy of this support bundle was written to the current directory, named %q\n", response.ArchivePath)
|
||||
} else {
|
||||
fmt.Printf("A support bundle has been created in the current directory named %q\n", archivePath)
|
||||
fmt.Printf("A support bundle has been created in the current directory named %q\n", response.ArchivePath)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
@@ -14,8 +15,10 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
analyze "github.com/replicatedhq/troubleshoot/pkg/analyze"
|
||||
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
|
||||
"github.com/replicatedhq/troubleshoot/pkg/collect"
|
||||
"github.com/replicatedhq/troubleshoot/pkg/convert"
|
||||
"github.com/replicatedhq/troubleshoot/pkg/version"
|
||||
"gopkg.in/yaml.v2"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
@@ -156,6 +159,24 @@ func writeVersionFile(path string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
const AnalysisFilename = "analysis.json"
|
||||
|
||||
func writeAnalysisFile(path string, analyzeResults []*analyze.AnalyzeResult) error {
|
||||
data := convert.FromAnalyzerResult(analyzeResults)
|
||||
analysis, err := json.MarshalIndent(data, "", " ")
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to marshal analysis")
|
||||
}
|
||||
|
||||
filename := filepath.Join(path, AnalysisFilename)
|
||||
err = ioutil.WriteFile(filename, analysis, 0644)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to write file")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func applyLogSinceTime(sinceTime time.Time, collectors *collect.Collectors) {
|
||||
|
||||
for _, collector := range *collectors {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package supportbundle
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
@@ -8,6 +9,8 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
cursor "github.com/ahmetalpbalkan/go-cursor"
|
||||
"github.com/fatih/color"
|
||||
"github.com/pkg/errors"
|
||||
analyzer "github.com/replicatedhq/troubleshoot/pkg/analyze"
|
||||
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
|
||||
@@ -22,19 +25,20 @@ type SupportBundleCreateOpts struct {
|
||||
Namespace string
|
||||
ProgressChan chan interface{}
|
||||
SinceTime *time.Time
|
||||
FromCLI bool
|
||||
}
|
||||
|
||||
type SupportBundleResponse struct {
|
||||
AnalyzerResults []*analyzer.AnalyzeResult
|
||||
ArchivePath string
|
||||
fileUploaded bool
|
||||
FileUploaded bool
|
||||
}
|
||||
|
||||
// SupportBundleCollectAnalyzeProcess collects support bundle from start to finish, including running
|
||||
// CollectSupportBundleFromSpec collects support bundle from start to finish, including running
|
||||
// collectors, analyzers and after collection steps. Input arguments are specifications.
|
||||
// The support bundle is archived in the OS temp folder (os.TempDir()).
|
||||
func SupportBundleCollectAnalyzeProcess(spec *troubleshootv1beta2.SupportBundleSpec, additionalRedactors *troubleshootv1beta2.Redactor, opts SupportBundleCreateOpts) (*SupportBundleResponse, error) {
|
||||
|
||||
// if FromCLI option is set to true, the output is the name of the archive on disk in the cwd.
|
||||
// if FromCLI option is set to false, the support bundle is archived in the OS temp folder (os.TempDir()).
|
||||
func CollectSupportBundleFromSpec(spec *troubleshootv1beta2.SupportBundleSpec, additionalRedactors *troubleshootv1beta2.Redactor, opts SupportBundleCreateOpts) (*SupportBundleResponse, error) {
|
||||
resultsResponse := SupportBundleResponse{}
|
||||
|
||||
if opts.KubernetesRestConfig == nil {
|
||||
@@ -51,7 +55,11 @@ func SupportBundleCollectAnalyzeProcess(spec *troubleshootv1beta2.SupportBundleS
|
||||
}
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
basename := filepath.Join(os.TempDir(), "support-bundle-"+time.Now().Format("2006-01-02T15_04_05"))
|
||||
basename := fmt.Sprintf("support-bundle-%s", time.Now().Format("2006-01-02T15_04_05"))
|
||||
if !opts.FromCLI {
|
||||
basename = filepath.Join(os.TempDir(), basename)
|
||||
}
|
||||
|
||||
filename, err := findFileName(basename, "tar.gz")
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "find file name")
|
||||
@@ -74,21 +82,38 @@ func SupportBundleCollectAnalyzeProcess(spec *troubleshootv1beta2.SupportBundleS
|
||||
}
|
||||
|
||||
// Run Analyzers
|
||||
analyzeResults, err := AnalyzeSupportBundle(spec, tmpDir)
|
||||
analyzeResults, err := AnalyzeSupportBundle(spec, bundlePath)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to run analysis")
|
||||
if opts.FromCLI {
|
||||
c := color.New(color.FgHiRed)
|
||||
c.Printf("%s\r * %v\n", cursor.ClearEntireLine(), err)
|
||||
// don't die
|
||||
} else {
|
||||
return nil, errors.Wrap(err, "failed to run analysis")
|
||||
}
|
||||
}
|
||||
resultsResponse.AnalyzerResults = analyzeResults
|
||||
|
||||
// Add the analysis to the support bundle
|
||||
if err = writeAnalysisFile(bundlePath, analyzeResults); err != nil {
|
||||
return nil, errors.Wrap(err, "write version file")
|
||||
}
|
||||
|
||||
if err := tarSupportBundleDir(bundlePath, filename); err != nil {
|
||||
return nil, errors.Wrap(err, "create bundle file")
|
||||
}
|
||||
|
||||
fileUploaded, err := ProcessSupportBundleAfterCollection(spec, filename)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to process bundle after collection")
|
||||
if opts.FromCLI {
|
||||
c := color.New(color.FgHiRed)
|
||||
c.Printf("%s\r * %v\n", cursor.ClearEntireLine(), err)
|
||||
// don't die
|
||||
} else {
|
||||
return nil, errors.Wrap(err, "failed to process bundle after collection")
|
||||
}
|
||||
}
|
||||
resultsResponse.fileUploaded = fileUploaded
|
||||
resultsResponse.FileUploaded = fileUploaded
|
||||
|
||||
return &resultsResponse, nil
|
||||
}
|
||||
@@ -97,7 +122,6 @@ func SupportBundleCollectAnalyzeProcess(spec *troubleshootv1beta2.SupportBundleS
|
||||
// collectors, analyzers and after collection steps. Input arguments are the URIs of the support bundle and redactor specs.
|
||||
// The support bundle is archived in the OS temp folder (os.TempDir()).
|
||||
func CollectSupportBundleFromURI(specURI string, redactorURIs []string, opts SupportBundleCreateOpts) (*SupportBundleResponse, error) {
|
||||
|
||||
supportbundle, err := GetSupportBundleFromURI(specURI)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not bundle from URI")
|
||||
@@ -115,53 +139,7 @@ func CollectSupportBundleFromURI(specURI string, redactorURIs []string, opts Sup
|
||||
}
|
||||
}
|
||||
|
||||
return SupportBundleCollectAnalyzeProcess(&supportbundle.Spec, additionalRedactors, opts)
|
||||
}
|
||||
|
||||
// CollectSupportBundleFromSpec run the support bundle collectors and creates an archive. The output is the name of the archive on disk
|
||||
// in the pwd (the caller must remove)
|
||||
func CollectSupportBundleFromSpec(spec *troubleshootv1beta2.SupportBundleSpec, additionalRedactors *troubleshootv1beta2.Redactor, opts SupportBundleCreateOpts) (string, error) {
|
||||
|
||||
if opts.KubernetesRestConfig == nil {
|
||||
return "", errors.New("did not receive kube rest config")
|
||||
}
|
||||
|
||||
if opts.ProgressChan == nil {
|
||||
return "", errors.New("did not receive collector progress chan")
|
||||
}
|
||||
|
||||
tmpDir, err := ioutil.TempDir("", "supportbundle")
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "create temp dir")
|
||||
}
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
// Do we need to put this in some kind of swap space?
|
||||
filename, err := findFileName("support-bundle-"+time.Now().Format("2006-01-02T15_04_05"), "tar.gz")
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "find file name")
|
||||
}
|
||||
|
||||
bundlePath := filepath.Join(tmpDir, strings.TrimSuffix(filename, ".tar.gz"))
|
||||
if err := os.MkdirAll(bundlePath, 0777); err != nil {
|
||||
return "", errors.Wrap(err, "create bundle dir")
|
||||
}
|
||||
|
||||
if err = writeVersionFile(bundlePath); err != nil {
|
||||
return "", errors.Wrap(err, "write version file")
|
||||
}
|
||||
|
||||
// Run collectors
|
||||
err = runCollectors(spec.Collectors, additionalRedactors, filename, bundlePath, opts)
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "run collectors")
|
||||
}
|
||||
|
||||
if err := tarSupportBundleDir(bundlePath, filename); err != nil {
|
||||
return "", errors.Wrap(err, "create bundle file")
|
||||
}
|
||||
|
||||
return filename, nil
|
||||
return CollectSupportBundleFromSpec(&supportbundle.Spec, additionalRedactors, opts)
|
||||
}
|
||||
|
||||
// ProcessSupportBundleAfterCollection performs the after collection actions, like Callbacks and sending the archive to a remote server.
|
||||
@@ -185,49 +163,15 @@ func ProcessSupportBundleAfterCollection(spec *troubleshootv1beta2.SupportBundle
|
||||
return fileUploaded, nil
|
||||
}
|
||||
|
||||
// AnalyzeAndExtractSupportBundle performs analysis on a support bundle using the archive and spec.
|
||||
func AnalyzeAndExtractSupportBundle(spec *troubleshootv1beta2.SupportBundleSpec, archivePath string) ([]*analyzer.AnalyzeResult, error) {
|
||||
|
||||
var analyzeResults []*analyzer.AnalyzeResult
|
||||
|
||||
if len(spec.Analyzers) > 0 {
|
||||
|
||||
tmpDir, err := ioutil.TempDir("", "troubleshoot")
|
||||
if err != nil {
|
||||
return analyzeResults, errors.Wrap(err, "failed to make directory for analysis")
|
||||
}
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
f, err := os.Open(archivePath)
|
||||
if err != nil {
|
||||
return analyzeResults, errors.Wrap(err, "failed to open support bundle for analysis")
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
if err := analyzer.ExtractTroubleshootBundle(f, tmpDir); err != nil {
|
||||
return analyzeResults, errors.Wrap(err, "failed to extract support bundle for analysis")
|
||||
}
|
||||
|
||||
analyzeResults, err = analyzer.AnalyzeLocal(tmpDir, spec.Analyzers)
|
||||
if err != nil {
|
||||
return analyzeResults, errors.Wrap(err, "failed to analyze support bundle")
|
||||
}
|
||||
}
|
||||
return analyzeResults, nil
|
||||
}
|
||||
|
||||
// AnalyzeSupportBundle performs analysis on a support bundle using the support bundle spec and an already unpacked support
|
||||
// bundle on disk
|
||||
func AnalyzeSupportBundle(spec *troubleshootv1beta2.SupportBundleSpec, tmpDir string) ([]*analyzer.AnalyzeResult, error) {
|
||||
|
||||
var analyzeResults []*analyzer.AnalyzeResult
|
||||
|
||||
if len(spec.Analyzers) > 0 {
|
||||
|
||||
analyzeResults, err := analyzer.AnalyzeLocal(tmpDir, spec.Analyzers)
|
||||
if err != nil {
|
||||
return analyzeResults, errors.Wrap(err, "failed to analyze support bundle")
|
||||
}
|
||||
if len(spec.Analyzers) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
analyzeResults, err := analyzer.AnalyzeLocal(tmpDir, spec.Analyzers)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to analyze support bundle")
|
||||
}
|
||||
return analyzeResults, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user