diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index cacf80b5..af5247b3 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -11,7 +11,7 @@ Thank you for your interest in Troubleshoot, we welcome your participation. Plea To get started we recommend: 1. Go (v1.17 or later) -2. Kubernetes (we recommend https://k3d.io/. requires Docker v20.10.5 or later) +2. A Kubernetes cluster (we recommend https://k3d.io/. This requires Docker v20.10.5 or later) 3. Fork and clone the repo to $GOPATH/src/github.com/replicatedhq/ 4. Run `make support-bundle preflight` to generate binaries 5. Run `make run-troubleshoot` to generate a support bundle with the `sample-troubleshoot.yaml` in the root of the repo diff --git a/pkg/analyze/ceph_test.go b/pkg/analyze/ceph_test.go index f745ab70..aa97d8bc 100644 --- a/pkg/analyze/ceph_test.go +++ b/pkg/analyze/ceph_test.go @@ -219,14 +219,14 @@ func Test_cephStatus(t *testing.T) { }`, }, { - name: "warn case with multiple health status messages", + name: "warn case with health status message and summary", analyzer: troubleshootv1beta2.CephStatusAnalyze{}, expectResult: AnalyzeResult{ IsPass: false, IsWarn: true, IsFail: false, Title: "Ceph Status", - Message: "Ceph status is HEALTH_WARN\nPOOL_NO_REDUNDANCY: 11 pool(s) have no replicas configured\nPOOL_PG_NUM_NOT_POWER_OF_TWO: 8 pool(s) have non-power-of-two pg_num", + Message: "Ceph status is HEALTH_WARN\nPOOL_NO_REDUNDANCY: 11 pool(s) have no replicas configured", URI: "https://rook.io/docs/rook/v1.4/ceph-common-issues.html", IconKey: "rook", IconURI: "https://troubleshoot.sh/images/analyzer-icons/rook.svg?w=11&h=16", @@ -244,12 +244,6 @@ func Test_cephStatus(t *testing.T) { "count": 11 }, "muted": false - }, - "POOL_PG_NUM_NOT_POWER_OF_TWO": { - "severity": "HEALTH_WARN", - "summary": { - "message": "8 pool(s) have non-power-of-two pg_num" - } } } } diff --git a/pkg/analyze/postgres.go b/pkg/analyze/postgres.go index 818635a5..9b15ce85 100644 --- a/pkg/analyze/postgres.go +++ b/pkg/analyze/postgres.go @@ -39,12 +39,6 @@ func analyzePostgres(analyzer *troubleshootv1beta2.DatabaseAnalyze, getCollected IconURI: "https://troubleshoot.sh/images/analyzer-icons/postgres-analyze.svg", } - if databaseConnection.Error != "" { - result.IsFail = true - result.Message = databaseConnection.Error - return result, nil - } - for _, outcome := range analyzer.Outcomes { if outcome.Fail != nil { if outcome.Fail.When == "" { @@ -61,8 +55,14 @@ func analyzePostgres(analyzer *troubleshootv1beta2.DatabaseAnalyze, getCollected } if isMatch { + + if databaseConnection.Error != "" { + result.Message = outcome.Fail.Message + " " + databaseConnection.Error + } else { + result.Message = outcome.Fail.Message + } + result.IsFail = true - result.Message = outcome.Fail.Message result.URI = outcome.Fail.URI return result, nil diff --git a/pkg/apis/troubleshoot/v1beta2/hostcollector_shared.go b/pkg/apis/troubleshoot/v1beta2/hostcollector_shared.go index bb6b2d0e..fb40dd30 100644 --- a/pkg/apis/troubleshoot/v1beta2/hostcollector_shared.go +++ b/pkg/apis/troubleshoot/v1beta2/hostcollector_shared.go @@ -150,6 +150,12 @@ type HostServices struct { HostCollectorMeta `json:",inline" yaml:",inline"` } +type HostRun struct { + HostCollectorMeta `json:",inline" yaml:",inline"` + Command string `json:"command"` + Args []string `json:"args"` +} + type HostCollect struct { CPU *CPU `json:"cpu,omitempty" yaml:"cpu,omitempty"` Memory *Memory `json:"memory,omitempty" yaml:"memory,omitempty"` @@ -169,6 +175,7 @@ type HostCollect struct { Certificate *Certificate `json:"certificate,omitempty" yaml:"certificate,omitempty"` HostServices *HostServices `json:"hostServices,omitempty" yaml:"hostServices,omitempty"` HostOS *HostOS `json:"hostOS,omitempty" yaml:"hostOS,omitempty"` + HostRun *HostRun `json:"run,omitempty" yaml:"run,omitempty"` } func (c *HostCollect) GetName() string { diff --git a/pkg/collect/host_collector.go b/pkg/collect/host_collector.go index 4d708129..d7ba5749 100644 --- a/pkg/collect/host_collector.go +++ b/pkg/collect/host_collector.go @@ -51,6 +51,8 @@ func GetHostCollector(collector *troubleshootv1beta2.HostCollect, bundlePath str return &CollectHostServices{collector.HostServices, bundlePath}, true case collector.HostOS != nil: return &CollectHostOS{collector.HostOS, bundlePath}, true + case collector.HostRun != nil: + return &CollectHostRun{collector.HostRun, bundlePath}, true default: return nil, false } diff --git a/pkg/collect/host_run.go b/pkg/collect/host_run.go new file mode 100644 index 00000000..f3ede990 --- /dev/null +++ b/pkg/collect/host_run.go @@ -0,0 +1,79 @@ +package collect + +import ( + "bytes" + "encoding/json" + "os/exec" + "path/filepath" + "strings" + + "github.com/pkg/errors" + troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2" +) + +type HostRunInfo struct { + Command string `json:"command"` + ExitCode string `json:"exitCode"` + Error string `json:"error"` +} + +type CollectHostRun struct { + hostCollector *troubleshootv1beta2.HostRun + BundlePath string +} + +func (c *CollectHostRun) Title() string { + return hostCollectorTitleOrDefault(c.hostCollector.HostCollectorMeta, "Run Host") +} + +func (c *CollectHostRun) IsExcluded() (bool, error) { + return isExcluded(c.hostCollector.Exclude) +} + +func (c *CollectHostRun) Collect(progressChan chan<- interface{}) (map[string][]byte, error) { + runHostCollector := c.hostCollector + + cmd := exec.Command(runHostCollector.Command, runHostCollector.Args...) + + var stdout, stderr bytes.Buffer + cmd.Stdout = &stdout + cmd.Stderr = &stderr + + runInfo := HostRunInfo{ + Command: cmd.String(), + ExitCode: "0", + } + + err := cmd.Run() + if err != nil { + if werr, ok := err.(*exec.ExitError); ok { + runInfo.ExitCode = strings.TrimPrefix(werr.Error(), "exit status ") + runInfo.Error = stderr.String() + } else { + return nil, errors.Wrap(err, "failed to run") + } + } + + 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") + + b, err := json.Marshal(runInfo) + if err != nil { + return nil, errors.Wrap(err, "failed to marshal run host result") + } + + 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(), + } + + return runHostOutput, nil +}