diff --git a/pkg/apis/troubleshoot/v1beta2/hostcollector_shared.go b/pkg/apis/troubleshoot/v1beta2/hostcollector_shared.go index 260a416ef..317d0b3e0 100644 --- a/pkg/apis/troubleshoot/v1beta2/hostcollector_shared.go +++ b/pkg/apis/troubleshoot/v1beta2/hostcollector_shared.go @@ -182,6 +182,7 @@ type HostRun struct { HostCollectorMeta `json:",inline" yaml:",inline"` Command string `json:"command"` Args []string `json:"args"` + OutputDir string `json:"outputDir" yaml:"outputDir"` } type HostCollect struct { diff --git a/pkg/collect/host_run.go b/pkg/collect/host_run.go index f3ede9907..2cea2b977 100644 --- a/pkg/collect/host_run.go +++ b/pkg/collect/host_run.go @@ -3,7 +3,10 @@ package collect import ( "bytes" "encoding/json" + "fmt" + "os" "os/exec" + "path" "path/filepath" "strings" @@ -12,9 +15,10 @@ import ( ) type HostRunInfo struct { - Command string `json:"command"` - ExitCode string `json:"exitCode"` - Error string `json:"error"` + Command string `json:"command"` + ExitCode string `json:"exitCode"` + Error string `json:"error"` + OutputDir string `json:"outputDir"` } type CollectHostRun struct { @@ -32,16 +36,33 @@ func (c *CollectHostRun) IsExcluded() (bool, error) { func (c *CollectHostRun) Collect(progressChan chan<- interface{}) (map[string][]byte, error) { runHostCollector := c.hostCollector + collectorName := runHostCollector.CollectorName + if collectorName == "" { + collectorName = "run-host" + } + var bundleOutputPath string cmd := exec.Command(runHostCollector.Command, runHostCollector.Args...) + cmd.Env = append(os.Environ(), + fmt.Sprintf("BUNDLE_PATH=%s", c.BundlePath), + ) + + // if we choose to save result for the command run + if runHostCollector.OutputDir != "" { + bundleOutputPath = path.Join(c.BundlePath, "host-collectors/run-host", collectorName, runHostCollector.OutputDir) + cmd.Env = append(cmd.Env, + fmt.Sprintf("WORKSPACE_DIR=%s", bundleOutputPath), + ) + } var stdout, stderr bytes.Buffer cmd.Stdout = &stdout cmd.Stderr = &stderr runInfo := HostRunInfo{ - Command: cmd.String(), - ExitCode: "0", + Command: cmd.String(), + ExitCode: "0", + OutputDir: bundleOutputPath, } err := cmd.Run() @@ -54,12 +75,8 @@ func (c *CollectHostRun) Collect(progressChan chan<- interface{}) (map[string][] } } - collectorName := c.hostCollector.CollectorName - if collectorName == "" { - collectorName = "run-host" - } - resultInfo := filepath.Join("host-collectors/run-host", collectorName+"-info.json") - result := filepath.Join("host-collectors/run-host", collectorName+".txt") + resultInfo := filepath.Join("host-collectors/run-host", collectorName, "collector-info.json") + result := filepath.Join("host-collectors/run-host", collectorName, "collector.txt") b, err := json.Marshal(runInfo) if err != nil { @@ -69,11 +86,11 @@ func (c *CollectHostRun) Collect(progressChan chan<- interface{}) (map[string][] output := NewResult() output.SaveResult(c.BundlePath, resultInfo, bytes.NewBuffer(b)) output.SaveResult(c.BundlePath, result, bytes.NewBuffer(stdout.Bytes())) - - runHostOutput := map[string][]byte{ - resultInfo: b, - result: stdout.Bytes(), + // walkthrough the output directory and save result for each file + if runHostCollector.OutputDir != "" { + output.SaveResults(c.BundlePath, + filepath.Join("host-collectors/run-host", collectorName, runHostCollector.OutputDir)) } - return runHostOutput, nil + return output, nil } diff --git a/pkg/collect/result.go b/pkg/collect/result.go index 45e6d299c..b962b74fe 100644 --- a/pkg/collect/result.go +++ b/pkg/collect/result.go @@ -4,9 +4,12 @@ import ( "archive/tar" "bytes" "compress/gzip" + "fmt" "io" "os" + "path" "path/filepath" + "strings" "github.com/pkg/errors" "k8s.io/klog/v2" @@ -131,6 +134,44 @@ func (r CollectorResult) SaveResult(bundlePath string, relativePath string, read return nil } +// SaveResults walk a directory and call SaveResult on all files retrieved from the walk. +func (r CollectorResult) SaveResults(bundlePath string, relativePath string) error { + dirPath := path.Join(bundlePath, relativePath) + if err := os.MkdirAll(dirPath, 0777); err != nil { + return errors.Wrap(err, "failed to create output file directory") + } + + err := filepath.WalkDir(dirPath, func(path string, d os.DirEntry, err error) error { + if err != nil { + return errors.Wrap(err, "error from WalkDirFunc") + } + + if !d.IsDir() { + file, err := os.Open(path) + if err != nil { + return errors.Wrap(err, fmt.Sprintf("failed to open file: %s", path)) + } + fileBytes, err := io.ReadAll(file) + if err != nil { + return errors.Wrap(err, fmt.Sprintf("failed to read file: %s", path)) + } + + err = r.SaveResult(bundlePath, strings.TrimPrefix(path, bundlePath+"/"), bytes.NewBuffer(fileBytes)) + if err != nil { + return errors.Wrap(err, "error from SaveResult call") + } + } + + return nil + }) + + if err != nil { + return errors.Wrap(err, "error from WalkDir call") + } + + return nil +} + func (r CollectorResult) ReplaceResult(bundlePath string, relativePath string, reader io.Reader) error { if bundlePath == "" { data, err := io.ReadAll(reader)