store analysis in the support bundle (#417)

* store analysis in the support bundle
This commit is contained in:
Salah Aldeen Al Saleh
2021-09-10 11:58:16 -07:00
committed by GitHub
parent 10785987c5
commit 465a533640
3 changed files with 75 additions and 122 deletions

View File

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

View File

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

View File

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