Skip to content

Commit

Permalink
Merge pull request #53 from replicatedhq/divolgin/upload
Browse files Browse the repository at this point in the history
Automatically upload generated support bundle file
  • Loading branch information
divolgin authored Aug 21, 2019
2 parents 123225c + 68344b2 commit ed9264e
Show file tree
Hide file tree
Showing 8 changed files with 542 additions and 279 deletions.
127 changes: 105 additions & 22 deletions cmd/troubleshoot/cli/run_nocrd.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,15 @@ import (
"fmt"
"io/ioutil"
"net/http"
"net/url"
"os"
"path/filepath"
"time"

"github.com/ahmetalpbalkan/go-cursor"
"github.com/fatih/color"
"github.com/mholt/archiver"
"github.com/pkg/errors"
troubleshootv1beta1 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta1"
"github.com/replicatedhq/troubleshoot/pkg/collect"
"github.com/spf13/viper"
Expand All @@ -32,25 +34,25 @@ func runTroubleshootNoCRD(v *viper.Viper, arg string) error {

b, err := ioutil.ReadFile(arg)
if err != nil {
return err
return errors.Wrap(err, "read spec file")
}

collectorContent = string(b)
} else {
req, err := http.NewRequest("GET", arg, nil)
if err != nil {
return err
return errors.Wrap(err, "make request")
}
req.Header.Set("User-Agent", "Replicated_Troubleshoot/v1beta1")
resp, err := http.DefaultClient.Do(req)
if err != nil {
return err
return errors.Wrap(err, "execute request")
}
defer resp.Body.Close()

body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return err
return errors.Wrap(err, "read responce body")
}

collectorContent = string(body)
Expand Down Expand Up @@ -83,7 +85,7 @@ func runTroubleshootNoCRD(v *viper.Viper, arg string) error {
if currentDir == "" {
fmt.Printf("\r%s \033[36mCollecting support bundle\033[m %s", cursor.ClearEntireLine(), s.Next())
} else {
fmt.Printf("\r%s \033[36mCollecting support bundle\033[m %s: %s", cursor.ClearEntireLine(), s.Next(), currentDir)
fmt.Printf("\r%s \033[36mCollecting support bundle\033[m %s %s", cursor.ClearEntireLine(), s.Next(), currentDir)
}
}
}
Expand All @@ -94,38 +96,55 @@ func runTroubleshootNoCRD(v *viper.Viper, arg string) error {

archivePath, err := runCollectors(v, collector, progressChan)
if err != nil {
return err
return errors.Wrap(err, "run collectors")
}

fmt.Printf("\r%s", cursor.ClearEntireLine())

msg := archivePath
if appName := collector.Labels["applicationName"]; appName != "" {
f := `A support bundle for %s has been created in this directory
if len(collector.Spec.AfterCollection) == 0 {
msg := archivePath
if appName := collector.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, archivePath, appName)
}

fmt.Printf("%s\n", msg)

return nil
}

fmt.Printf("%s\n", msg)
for _, ac := range collector.Spec.AfterCollection {
if ac.UploadResultsTo != nil {
if err := uploadSupportBundle(ac.UploadResultsTo, archivePath); err != nil {
return errors.Wrap(err, "upload support bundle")
}
} else if ac.Callback != nil {
if err := callbackSupportBundleAPI(ac.Callback, archivePath); err != nil {
return errors.Wrap(err, "execute callback")
}
}
}

fmt.Printf("A support bundle has been created in the current directory named %q\n", archivePath)
return nil
}

func runCollectors(v *viper.Viper, collector troubleshootv1beta1.Collector, progressChan chan interface{}) (string, error) {
bundlePath, err := ioutil.TempDir("", "troubleshoot")
if err != nil {
return "", err
return "", errors.Wrap(err, "create temp dir")
}
defer os.RemoveAll(bundlePath)

versionFilename, err := writeVersionFile(bundlePath)
if err != nil {
return "", err
return "", errors.Wrap(err, "write version file")
}

desiredCollectors := make([]*troubleshootv1beta1.Collect, 0, 0)
for _, definedCollector := range collector.Spec {
for _, definedCollector := range collector.Spec.Collectors {
desiredCollectors = append(desiredCollectors, definedCollector)
}
desiredCollectors = ensureCollectorInList(desiredCollectors, troubleshootv1beta1.Collect{ClusterInfo: &troubleshootv1beta1.ClusterInfo{}})
Expand Down Expand Up @@ -175,7 +194,7 @@ func runCollectors(v *viper.Viper, collector troubleshootv1beta1.Collector, prog
}

if err := tarGz.Archive(paths, "support-bundle.tar.gz"); err != nil {
return "", err
return "", errors.Wrap(err, "create archive")
}

return "support-bundle.tar.gz", nil
Expand All @@ -186,7 +205,7 @@ func parseAndSaveCollectorOutput(output string, bundlePath string) (string, erro

input := make(map[string]interface{})
if err := json.Unmarshal([]byte(output), &input); err != nil {
return "", err
return "", errors.Wrap(err, "unmarshal output")
}

for filename, maybeContents := range input {
Expand All @@ -195,37 +214,101 @@ func parseAndSaveCollectorOutput(output string, bundlePath string) (string, erro
dir = outPath

if err := os.MkdirAll(outPath, 0777); err != nil {
return "", err
return "", errors.Wrap(err, "create output file")
}

switch maybeContents.(type) {
case string:
decoded, err := base64.StdEncoding.DecodeString(maybeContents.(string))
if err != nil {
return "", err
return "", errors.Wrap(err, "decode collector output")
}

if err := writeFile(filepath.Join(outPath, fileName), decoded); err != nil {
return "", err
return "", errors.Wrap(err, "write collector output")
}

case map[string]interface{}:
for k, v := range maybeContents.(map[string]interface{}) {
s, _ := filepath.Split(filepath.Join(outPath, fileName, k))
if err := os.MkdirAll(s, 0777); err != nil {
return "", err
return "", errors.Wrap(err, "write output directories")
}

decoded, err := base64.StdEncoding.DecodeString(v.(string))
if err != nil {
return "", err
return "", errors.Wrap(err, "decode output")
}
if err := writeFile(filepath.Join(outPath, fileName, k), decoded); err != nil {
return "", err
return "", errors.Wrap(err, "write output")
}
}
}
}

return dir, nil
}

func uploadSupportBundle(r *troubleshootv1beta1.ResultRequest, archivePath string) error {
contentType := getExpectedContentType(r.URI)
if contentType != "" && contentType != "application/tar+gzip" {
return fmt.Errorf("cannot upload content type %s", contentType)
}

f, err := os.Open(archivePath)
if err != nil {
return errors.Wrap(err, "open file")
}
defer f.Close()

fileStat, err := f.Stat()
if err != nil {
return errors.Wrap(err, "stat file")
}

req, err := http.NewRequest(r.Method, r.URI, f)
if err != nil {
return errors.Wrap(err, "create request")
}
req.ContentLength = fileStat.Size()
if contentType != "" {
req.Header.Set("Content-Type", contentType)
}

resp, err := http.DefaultClient.Do(req)
if err != nil {
return errors.Wrap(err, "execute request")
}

if resp.StatusCode >= 300 {
return fmt.Errorf("unexpected status code %d", resp.StatusCode)
}

return nil
}

func getExpectedContentType(uploadURL string) string {
parsedURL, err := url.Parse(uploadURL)
if err != nil {
return ""
}
return parsedURL.Query().Get("Content-Type")
}

func callbackSupportBundleAPI(r *troubleshootv1beta1.ResultRequest, archivePath string) error {
req, err := http.NewRequest(r.Method, r.URI, nil)
if err != nil {
return errors.Wrap(err, "create request")
}

resp, err := http.DefaultClient.Do(req)
if err != nil {
return errors.Wrap(err, "execute request")
}

if resp.StatusCode >= 300 {
return fmt.Errorf("unexpected status code %d", resp.StatusCode)
}

return nil
}
Loading

0 comments on commit ed9264e

Please sign in to comment.