diff --git a/pkg/analyze/collected_contents.go b/pkg/analyze/collected_contents.go index e8abe7ee5..753a6a65c 100644 --- a/pkg/analyze/collected_contents.go +++ b/pkg/analyze/collected_contents.go @@ -10,11 +10,9 @@ import ( type collectedContent struct { NodeName string - Data collectorData + Data []byte } -type collectorData interface{} - type nodeNames struct { Nodes []string `json:"nodes"` } diff --git a/pkg/analyze/host_analyzer.go b/pkg/analyze/host_analyzer.go index 6edc709d7..5f25f312c 100644 --- a/pkg/analyze/host_analyzer.go +++ b/pkg/analyze/host_analyzer.go @@ -89,7 +89,7 @@ func (c *resultCollector) get(title string) []*AnalyzeResult { return []*AnalyzeResult{{Title: title, IsWarn: true, Message: "no results"}} } -func analyzeHostCollectorResults(collectedContent []collectedContent, outcomes []*troubleshootv1beta2.Outcome, checkCondition func(string, collectorData) (bool, error), title string) ([]*AnalyzeResult, error) { +func analyzeHostCollectorResults(collectedContent []collectedContent, outcomes []*troubleshootv1beta2.Outcome, checkCondition func(string, []byte) (bool, error), title string) ([]*AnalyzeResult, error) { var results []*AnalyzeResult for _, content := range collectedContent { currentTitle := title @@ -108,7 +108,7 @@ func analyzeHostCollectorResults(collectedContent []collectedContent, outcomes [ return results, nil } -func evaluateOutcomes(outcomes []*troubleshootv1beta2.Outcome, checkCondition func(string, collectorData) (bool, error), data collectorData, title string) ([]*AnalyzeResult, error) { +func evaluateOutcomes(outcomes []*troubleshootv1beta2.Outcome, checkCondition func(string, []byte) (bool, error), data []byte, title string) ([]*AnalyzeResult, error) { var results []*AnalyzeResult for _, outcome := range outcomes { diff --git a/pkg/analyze/host_analyzer_test.go b/pkg/analyze/host_analyzer_test.go index 21eca0f71..213752c11 100644 --- a/pkg/analyze/host_analyzer_test.go +++ b/pkg/analyze/host_analyzer_test.go @@ -1,6 +1,7 @@ package analyzer import ( + "encoding/json" "testing" "github.com/pkg/errors" @@ -10,12 +11,17 @@ import ( "github.com/stretchr/testify/require" ) +func collectorToBytes(t *testing.T, collector any) []byte { + jsonData, err := json.Marshal(collector) + require.NoError(t, err) + return jsonData +} + func TestAnalyzeHostCollectorResults(t *testing.T) { tests := []struct { name string outcomes []*troubleshootv1beta2.Outcome collectedContent []collectedContent - checkCondition func(string, collectorData) (bool, error) expectResult []*AnalyzeResult }{ { @@ -23,12 +29,12 @@ func TestAnalyzeHostCollectorResults(t *testing.T) { collectedContent: []collectedContent{ { NodeName: "node1", - Data: collect.HostOSInfo{ + Data: collectorToBytes(t, collect.HostOSInfo{ Name: "myhost", KernelVersion: "5.4.0-1034-gcp", PlatformVersion: "00.1.2", Platform: "ubuntu", - }, + }), }, }, outcomes: []*troubleshootv1beta2.Outcome{ @@ -44,10 +50,6 @@ func TestAnalyzeHostCollectorResults(t *testing.T) { }, }, }, - checkCondition: func(when string, data collectorData) (bool, error) { - osInfo := data.(collect.HostOSInfo) - return osInfo.Platform == "ubuntu" && osInfo.PlatformVersion >= "00.1.2", nil - }, expectResult: []*AnalyzeResult{ { Title: "Host OS Info - Node node1", @@ -61,21 +63,21 @@ func TestAnalyzeHostCollectorResults(t *testing.T) { collectedContent: []collectedContent{ { NodeName: "node1", - Data: collect.HostOSInfo{ + Data: collectorToBytes(t, collect.HostOSInfo{ Name: "myhost", KernelVersion: "5.4.0-1034-gcp", PlatformVersion: "11.04", Platform: "ubuntu", - }, + }), }, { NodeName: "node2", - Data: collect.HostOSInfo{ + Data: collectorToBytes(t, collect.HostOSInfo{ Name: "myhost", KernelVersion: "5.4.0-1034-gcp", PlatformVersion: "11.04", Platform: "ubuntu", - }, + }), }, }, outcomes: []*troubleshootv1beta2.Outcome{ @@ -91,10 +93,6 @@ func TestAnalyzeHostCollectorResults(t *testing.T) { }, }, }, - checkCondition: func(when string, data collectorData) (bool, error) { - osInfo := data.(collect.HostOSInfo) - return osInfo.Platform == "ubuntu" && osInfo.PlatformVersion <= "11.04", nil - }, expectResult: []*AnalyzeResult{ { Title: "Host OS Info - Node node1", @@ -113,12 +111,12 @@ func TestAnalyzeHostCollectorResults(t *testing.T) { collectedContent: []collectedContent{ { NodeName: "", - Data: collect.HostOSInfo{ + Data: collectorToBytes(t, collect.HostOSInfo{ Name: "myhost", KernelVersion: "5.4.0-1034-gcp", PlatformVersion: "20.04", Platform: "ubuntu", - }, + }), }, }, outcomes: []*troubleshootv1beta2.Outcome{ @@ -134,10 +132,6 @@ func TestAnalyzeHostCollectorResults(t *testing.T) { }, }, }, - checkCondition: func(when string, data collectorData) (bool, error) { - osInfo := data.(collect.HostOSInfo) - return osInfo.Platform == "ubuntu" && osInfo.PlatformVersion >= "20.04", nil - }, expectResult: []*AnalyzeResult{ { Title: "Host OS Info", // Ensuring the title does not include node name if it's empty @@ -150,8 +144,9 @@ func TestAnalyzeHostCollectorResults(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { + a := AnalyzeHostOS{} // Call the new analyzeHostCollectorResults function with the test data - result, err := analyzeHostCollectorResults(test.collectedContent, test.outcomes, test.checkCondition, "Host OS Info") + result, err := analyzeHostCollectorResults(test.collectedContent, test.outcomes, a.CheckCondition, "Host OS Info") require.NoError(t, err) assert.Equal(t, test.expectResult, result) }) @@ -162,8 +157,8 @@ func TestEvaluateOutcomes(t *testing.T) { tests := []struct { name string outcomes []*troubleshootv1beta2.Outcome - checkCondition func(string, collectorData) (bool, error) - data collectorData + checkCondition func(string, []byte) (bool, error) + data []byte expectedResult []*AnalyzeResult }{ { @@ -176,11 +171,11 @@ func TestEvaluateOutcomes(t *testing.T) { }, }, }, - checkCondition: func(when string, data collectorData) (bool, error) { + checkCondition: func(when string, data []byte) (bool, error) { // Return true if the condition being checked matches "failCondition" return when == "failCondition", nil }, - data: "someData", + data: []byte("someData"), expectedResult: []*AnalyzeResult{ { Title: "Test Title", @@ -199,11 +194,11 @@ func TestEvaluateOutcomes(t *testing.T) { }, }, }, - checkCondition: func(when string, data collectorData) (bool, error) { + checkCondition: func(when string, data []byte) (bool, error) { // Return true if the condition being checked matches "warnCondition" return when == "warnCondition", nil }, - data: "someData", + data: []byte("someData"), expectedResult: []*AnalyzeResult{ { Title: "Test Title", @@ -222,11 +217,11 @@ func TestEvaluateOutcomes(t *testing.T) { }, }, }, - checkCondition: func(when string, data collectorData) (bool, error) { + checkCondition: func(when string, data []byte) (bool, error) { // Return true if the condition being checked matches "passCondition" return when == "passCondition", nil }, - data: "someData", + data: []byte("someData"), expectedResult: []*AnalyzeResult{ { Title: "Test Title", @@ -253,11 +248,11 @@ func TestEvaluateOutcomes(t *testing.T) { }, }, }, - checkCondition: func(when string, data collectorData) (bool, error) { + checkCondition: func(when string, data []byte) (bool, error) { // Always return false to simulate no condition matching return false, nil }, - data: "someData", + data: []byte("someData"), expectedResult: nil, // No condition matches, so we expect no results }, { @@ -270,11 +265,11 @@ func TestEvaluateOutcomes(t *testing.T) { }, }, }, - checkCondition: func(when string, data collectorData) (bool, error) { + checkCondition: func(when string, data []byte) (bool, error) { // Simulate an error occurring during condition evaluation return false, errors.New("mock error") }, - data: "someData", + data: []byte("someData"), expectedResult: []*AnalyzeResult{ { Title: "Test Title", diff --git a/pkg/analyze/host_block_devices.go b/pkg/analyze/host_block_devices.go index 0a409e8c3..d5f8d8268 100644 --- a/pkg/analyze/host_block_devices.go +++ b/pkg/analyze/host_block_devices.go @@ -27,90 +27,24 @@ func (a *AnalyzeHostBlockDevices) IsExcluded() (bool, error) { func (a *AnalyzeHostBlockDevices) Analyze( getCollectedFileContents func(string) ([]byte, error), findFiles getChildCollectedFileContents, ) ([]*AnalyzeResult, error) { - hostAnalyzer := a.hostAnalyzer - - contents, err := getCollectedFileContents(collect.HostBlockDevicesPath) + result := AnalyzeResult{Title: a.Title()} + + collectedContents, err := retrieveCollectedContents( + getCollectedFileContents, + collect.HostBlockDevicesPath, + collect.NodeInfoBaseDir, + collect.HostBlockDevicesFileName, + ) if err != nil { - return nil, errors.Wrap(err, "failed to get collected file") + return []*AnalyzeResult{&result}, err } - var devices []collect.BlockDeviceInfo - if err := json.Unmarshal(contents, &devices); err != nil { - return nil, errors.Wrap(err, "failed to unmarshal block devices info") - } - - result := AnalyzeResult{} - - result.Title = a.Title() - - for _, outcome := range hostAnalyzer.Outcomes { - if outcome.Fail != nil { - if outcome.Fail.When == "" { - result.IsFail = true - result.Message = outcome.Fail.Message - result.URI = outcome.Fail.URI - - return []*AnalyzeResult{&result}, nil - } - - isMatch, err := compareHostBlockDevicesConditionalToActual(outcome.Fail.When, hostAnalyzer.MinimumAcceptableSize, hostAnalyzer.IncludeUnmountedPartitions, devices) - if err != nil { - return nil, errors.Wrapf(err, "failed to compare %s", outcome.Fail.When) - } - - if isMatch { - result.IsFail = true - result.Message = outcome.Fail.Message - result.URI = outcome.Fail.URI - - return []*AnalyzeResult{&result}, nil - } - } else if outcome.Warn != nil { - if outcome.Warn.When == "" { - result.IsWarn = true - result.Message = outcome.Warn.Message - result.URI = outcome.Warn.URI - - return []*AnalyzeResult{&result}, nil - } - - isMatch, err := compareHostBlockDevicesConditionalToActual(outcome.Warn.When, hostAnalyzer.MinimumAcceptableSize, hostAnalyzer.IncludeUnmountedPartitions, devices) - if err != nil { - return nil, errors.Wrapf(err, "failed to compare %s", outcome.Warn.When) - } - - if isMatch { - result.IsWarn = true - result.Message = outcome.Warn.Message - result.URI = outcome.Warn.URI - - return []*AnalyzeResult{&result}, nil - } - } else if outcome.Pass != nil { - if outcome.Pass.When == "" { - result.IsPass = true - result.Message = outcome.Pass.Message - result.URI = outcome.Pass.URI - - return []*AnalyzeResult{&result}, nil - } - - isMatch, err := compareHostBlockDevicesConditionalToActual(outcome.Pass.When, hostAnalyzer.MinimumAcceptableSize, hostAnalyzer.IncludeUnmountedPartitions, devices) - if err != nil { - return nil, errors.Wrapf(err, "failed to compare %s", outcome.Pass.When) - } - - if isMatch { - result.IsPass = true - result.Message = outcome.Pass.Message - result.URI = outcome.Pass.URI - - return []*AnalyzeResult{&result}, nil - } - } + results, err := analyzeHostCollectorResults(collectedContents, a.hostAnalyzer.Outcomes, a.CheckCondition, a.Title()) + if err != nil { + return nil, errors.Wrap(err, "failed to analyze block devices") } - return []*AnalyzeResult{&result}, nil + return results, nil } // @@ -205,3 +139,14 @@ func isEligibleBlockDevice(rx *regexp.Regexp, minimumAcceptableSize uint64, incl return true } + +func (a *AnalyzeHostBlockDevices) CheckCondition(when string, data []byte) (bool, error) { + + var devices []collect.BlockDeviceInfo + if err := json.Unmarshal(data, &devices); err != nil { + return false, errors.Wrap(err, "failed to unmarshal block devices info") + } + + return compareHostBlockDevicesConditionalToActual(when, a.hostAnalyzer.MinimumAcceptableSize, a.hostAnalyzer.IncludeUnmountedPartitions, devices) + +} diff --git a/pkg/analyze/host_certificate.go b/pkg/analyze/host_certificate.go index a6bcd4ff9..c09196f9c 100644 --- a/pkg/analyze/host_certificate.go +++ b/pkg/analyze/host_certificate.go @@ -1,7 +1,7 @@ package analyzer import ( - "path/filepath" + "fmt" "strings" "github.com/pkg/errors" @@ -23,52 +23,34 @@ func (a *AnalyzeHostCertificate) IsExcluded() (bool, error) { func (a *AnalyzeHostCertificate) Analyze( getCollectedFileContents func(string) ([]byte, error), findFiles getChildCollectedFileContents, ) ([]*AnalyzeResult, error) { - hostAnalyzer := a.hostAnalyzer - collectorName := hostAnalyzer.CollectorName + collectorName := a.hostAnalyzer.CollectorName if collectorName == "" { collectorName = "certificate" } - name := filepath.Join("host-collectors/certificate", collectorName+".json") - contents, err := getCollectedFileContents(name) - if err != nil { - return nil, errors.Wrap(err, "failed to get collected file") - } - status := string(contents) - - trimmedStatus := strings.TrimSpace(status) - - var coll resultCollector - - for _, outcome := range hostAnalyzer.Outcomes { - result := &AnalyzeResult{Title: a.Title()} - - if outcome.Fail != nil { - if outcome.Fail.When == "" || outcome.Fail.When == trimmedStatus { - result.IsFail = true - result.Message = outcome.Fail.Message - result.URI = outcome.Fail.URI - coll.push(result) - } - } else if outcome.Warn != nil { - if outcome.Warn.When == "" || outcome.Warn.When == trimmedStatus { - result.IsWarn = true - result.Message = outcome.Warn.Message - result.URI = outcome.Warn.URI + const nodeBaseDir = "host-collectors/certificate" + localPath := fmt.Sprintf("%s/%s", nodeBaseDir, collectorName) + fileName := fmt.Sprintf("%s.json", collectorName) - coll.push(result) - } - } else if outcome.Pass != nil { - if outcome.Pass.When == "" || outcome.Pass.When == trimmedStatus { - result.IsPass = true - result.Message = outcome.Pass.Message - result.URI = outcome.Pass.URI + collectedContents, err := retrieveCollectedContents( + getCollectedFileContents, + localPath, + nodeBaseDir, + fileName, + ) + if err != nil { + return []*AnalyzeResult{{Title: a.Title()}}, err + } - coll.push(result) - } - } + results, err := analyzeHostCollectorResults(collectedContents, a.hostAnalyzer.Outcomes, a.CheckCondition, a.Title()) + if err != nil { + return nil, errors.Wrap(err, "failed to analyze certificate") } - return coll.get(a.Title()), nil + return results, nil +} + +func (a *AnalyzeHostCertificate) CheckCondition(when string, data []byte) (bool, error) { + return strings.TrimSpace(string(data)) == when, nil } diff --git a/pkg/analyze/host_disk_usage.go b/pkg/analyze/host_disk_usage.go index 3d8dce407..efa49f17f 100644 --- a/pkg/analyze/host_disk_usage.go +++ b/pkg/analyze/host_disk_usage.go @@ -3,7 +3,6 @@ package analyzer import ( "encoding/json" "fmt" - "path/filepath" "strconv" "strings" @@ -28,97 +27,32 @@ func (a *AnalyzeHostDiskUsage) IsExcluded() (bool, error) { func (a *AnalyzeHostDiskUsage) Analyze( getCollectedFileContents func(string) ([]byte, error), findFiles getChildCollectedFileContents, ) ([]*AnalyzeResult, error) { - hostAnalyzer := a.hostAnalyzer - name := filepath.Join("host-collectors/diskUsage", "diskUsage.json") - if hostAnalyzer.CollectorName != "" { - name = filepath.Join("host-collectors/diskUsage", hostAnalyzer.CollectorName+".json") - } - contents, err := getCollectedFileContents(name) - if err != nil { - return nil, errors.Wrapf(err, "failed to get collected file %s", name) + collectorName := a.hostAnalyzer.CollectorName + if collectorName == "" { + collectorName = "diskUsage" } - diskUsageInfo := collect.DiskUsageInfo{} - if err := json.Unmarshal(contents, &diskUsageInfo); err != nil { - return nil, errors.Wrapf(err, "failed to unmarshal disk usage info from %s", name) - } + const nodeBaseDir = "host-collectors/diskUsage" + localPath := fmt.Sprintf("%s/%s.json", nodeBaseDir, collectorName) + fileName := fmt.Sprintf("%s.json", collectorName) - result := AnalyzeResult{ - Title: a.Title(), + collectedContents, err := retrieveCollectedContents( + getCollectedFileContents, + localPath, + nodeBaseDir, + fileName, + ) + if err != nil { + return []*AnalyzeResult{{Title: a.Title()}}, err } - for _, outcome := range hostAnalyzer.Outcomes { - result := &AnalyzeResult{Title: a.Title()} - - if outcome.Fail != nil { - if outcome.Fail.When == "" { - result.IsFail = true - result.Message = outcome.Fail.Message - result.URI = outcome.Fail.URI - - return []*AnalyzeResult{result}, nil - } - - isMatch, err := compareHostDiskUsageConditionalToActual(outcome.Fail.When, diskUsageInfo.TotalBytes, diskUsageInfo.UsedBytes) - if err != nil { - return nil, errors.Wrapf(err, "failed to compare %q", outcome.Fail.When) - } - - if isMatch { - result.IsFail = true - result.Message = outcome.Fail.Message - result.URI = outcome.Fail.URI - - return []*AnalyzeResult{result}, nil - } - } else if outcome.Warn != nil { - if outcome.Warn.When == "" { - result.IsWarn = true - result.Message = outcome.Warn.Message - result.URI = outcome.Warn.URI - - return []*AnalyzeResult{result}, nil - } - - isMatch, err := compareHostDiskUsageConditionalToActual(outcome.Warn.When, diskUsageInfo.TotalBytes, diskUsageInfo.UsedBytes) - if err != nil { - return nil, errors.Wrapf(err, "failed to compare %q", outcome.Warn.When) - } - - if isMatch { - result.IsWarn = true - result.Message = outcome.Warn.Message - result.URI = outcome.Warn.URI - - return []*AnalyzeResult{result}, nil - } - } else if outcome.Pass != nil { - if outcome.Pass.When == "" { - result.IsPass = true - result.Message = outcome.Pass.Message - result.URI = outcome.Pass.URI - - return []*AnalyzeResult{result}, nil - } - - isMatch, err := compareHostDiskUsageConditionalToActual(outcome.Pass.When, diskUsageInfo.TotalBytes, diskUsageInfo.UsedBytes) - if err != nil { - return nil, errors.Wrapf(err, "failed to compare %q", outcome.Pass.When) - } - - if isMatch { - result.IsPass = true - result.Message = outcome.Pass.Message - result.URI = outcome.Pass.URI - - return []*AnalyzeResult{result}, nil - } - - } + results, err := analyzeHostCollectorResults(collectedContents, a.hostAnalyzer.Outcomes, a.CheckCondition, a.Title()) + if err != nil { + return nil, errors.Wrap(err, "failed to analyze disk usage") } - return []*AnalyzeResult{&result}, nil + return results, nil } func compareHostDiskUsageConditionalToActual(conditional string, totalBytes uint64, usedBytes uint64) (res bool, err error) { @@ -202,3 +136,13 @@ func doCompareHostDiskUsagePercent(operator string, desired string, actual float return false, errors.New("unknown operator") } + +func (a *AnalyzeHostDiskUsage) CheckCondition(when string, data []byte) (bool, error) { + + var diskUsageInfo collect.DiskUsageInfo + if err := json.Unmarshal(data, &diskUsageInfo); err != nil { + return false, fmt.Errorf("failed to unmarshal data into DiskUsageInfo: %v", err) + } + + return compareHostDiskUsageConditionalToActual(when, diskUsageInfo.TotalBytes, diskUsageInfo.UsedBytes) +} diff --git a/pkg/analyze/host_filesystem_performance.go b/pkg/analyze/host_filesystem_performance.go index 3f9ae3706..cb83f7572 100644 --- a/pkg/analyze/host_filesystem_performance.go +++ b/pkg/analyze/host_filesystem_performance.go @@ -5,7 +5,6 @@ import ( "encoding/json" "fmt" "log" - "path/filepath" "strings" "text/template" "time" @@ -30,18 +29,23 @@ func (a *AnalyzeHostFilesystemPerformance) IsExcluded() (bool, error) { func (a *AnalyzeHostFilesystemPerformance) Analyze( getCollectedFileContents func(string) ([]byte, error), findFiles getChildCollectedFileContents, ) ([]*AnalyzeResult, error) { + result := &AnalyzeResult{Title: a.Title()} hostAnalyzer := a.hostAnalyzer + collectorName := a.hostAnalyzer.CollectorName - result := &AnalyzeResult{ - Title: a.Title(), - } - - collectorName := hostAnalyzer.CollectorName if collectorName == "" { collectorName = "filesystemPerformance" } - name := filepath.Join("host-collectors/filesystemPerformance", collectorName+".json") - contents, err := getCollectedFileContents(name) + const nodeBaseDir = "host-collectors/filesystemPerformance" + localPath := fmt.Sprintf("%s/%s.json", nodeBaseDir, collectorName) + fileName := fmt.Sprintf("%s.json", collectorName) + + collectedContents, err := retrieveCollectedContents( + getCollectedFileContents, + localPath, + nodeBaseDir, + fileName, + ) if err != nil { if len(hostAnalyzer.Outcomes) >= 1 { // if the very first outcome is FILE_NOT_COLLECTED', then use that outcome @@ -64,104 +68,26 @@ func (a *AnalyzeHostFilesystemPerformance) Analyze( result.URI = hostAnalyzer.Outcomes[0].Pass.URI return []*AnalyzeResult{result}, nil } + return nil, errors.Wrapf(err, "failed to get collected file %s", localPath) } - - return nil, errors.Wrapf(err, "failed to get collected file %s", name) } - fioResult := collect.FioResult{} - if err := json.Unmarshal(contents, &fioResult); err != nil { - return nil, errors.Wrapf(err, "failed to unmarshal fio results from %s", name) - } - - var job *collect.FioJobs - for _, j := range fioResult.Jobs { - if j.JobName == collect.FioJobName { - job = &j - break + var results []*AnalyzeResult + for _, content := range collectedContents { + currentTitle := a.Title() + if content.NodeName != "" { + currentTitle = fmt.Sprintf("%s - Node %s", a.Title(), content.NodeName) } - } - if job == nil { - return nil, errors.Errorf("no job named 'fsperf' found in fio results from %s", name) - } - - fioWriteLatency := job.Sync - - fsPerf := fioWriteLatency.FSPerfResults() - if err := json.Unmarshal(contents, &fsPerf); err != nil { - return nil, errors.Wrapf(err, "failed to unmarshal filesystem performance results from %s", name) - } - - for _, outcome := range hostAnalyzer.Outcomes { - - if outcome.Fail != nil { - if outcome.Fail.When == "" { - result.IsFail = true - result.Message = renderFSPerfOutcome(outcome.Fail.Message, fsPerf) - result.URI = outcome.Fail.URI - - return []*AnalyzeResult{result}, nil - } - - isMatch, err := compareHostFilesystemPerformanceConditionalToActual(outcome.Fail.When, fsPerf) - if err != nil { - return nil, errors.Wrapf(err, "failed to compare %q", outcome.Fail.When) - } - - if isMatch { - result.IsFail = true - result.Message = renderFSPerfOutcome(outcome.Fail.Message, fsPerf) - result.URI = outcome.Fail.URI - - return []*AnalyzeResult{result}, nil - } - } else if outcome.Warn != nil { - if outcome.Warn.When == "" { - result.IsWarn = true - result.Message = renderFSPerfOutcome(outcome.Warn.Message, fsPerf) - result.URI = outcome.Warn.URI - - return []*AnalyzeResult{result}, nil - } - - isMatch, err := compareHostFilesystemPerformanceConditionalToActual(outcome.Warn.When, fsPerf) - if err != nil { - return nil, errors.Wrapf(err, "failed to compare %q", outcome.Warn.When) - } - - if isMatch { - result.IsWarn = true - result.Message = renderFSPerfOutcome(outcome.Warn.Message, fsPerf) - result.URI = outcome.Warn.URI - - return []*AnalyzeResult{result}, nil - } - } else if outcome.Pass != nil { - if outcome.Pass.When == "" { - result.IsPass = true - result.Message = renderFSPerfOutcome(outcome.Pass.Message, fsPerf) - result.URI = outcome.Pass.URI - - return []*AnalyzeResult{result}, nil - } - - isMatch, err := compareHostFilesystemPerformanceConditionalToActual(outcome.Pass.When, fsPerf) - if err != nil { - return nil, errors.Wrapf(err, "failed to compare %q", outcome.Pass.When) - } - - if isMatch { - result.IsPass = true - result.Message = renderFSPerfOutcome(outcome.Pass.Message, fsPerf) - result.URI = outcome.Pass.URI - - return []*AnalyzeResult{result}, nil - } - + result, err := a.analyzeSingleNode(content, currentTitle) + if err != nil { + return nil, errors.Wrapf(err, "failed to analyze filesystem performance for %s", currentTitle) + } + if result != nil { + results = append(results, result...) } } - return []*AnalyzeResult{result}, nil + return results, nil } func compareHostFilesystemPerformanceConditionalToActual(conditional string, fsPerf collect.FSPerfResults) (res bool, err error) { @@ -258,3 +184,105 @@ func renderFSPerfOutcome(outcome string, fsPerf collect.FSPerfResults) string { } return buf.String() } + +func (a *AnalyzeHostFilesystemPerformance) analyzeSingleNode(content collectedContent, currentTitle string) ([]*AnalyzeResult, error) { + hostAnalyzer := a.hostAnalyzer + result := &AnalyzeResult{ + Title: currentTitle, + } + fioResult := collect.FioResult{} + if err := json.Unmarshal(content.Data, &fioResult); err != nil { + return nil, errors.Wrapf(err, "failed to unmarshal fio results from %s", currentTitle) + } + + var job *collect.FioJobs + for _, j := range fioResult.Jobs { + if j.JobName == collect.FioJobName { + job = &j + break + } + } + if job == nil { + return nil, errors.Errorf("no job named 'fsperf' found in fio results from %s", currentTitle) + } + + fioWriteLatency := job.Sync + + fsPerf := fioWriteLatency.FSPerfResults() + if err := json.Unmarshal(content.Data, &fsPerf); err != nil { + return nil, errors.Wrapf(err, "failed to unmarshal filesystem performance results from %s", currentTitle) + } + + for _, outcome := range hostAnalyzer.Outcomes { + + switch { + case outcome.Fail != nil: + if outcome.Fail.When == "" { + result.IsFail = true + result.Message = renderFSPerfOutcome(outcome.Fail.Message, fsPerf) + result.URI = outcome.Fail.URI + + return []*AnalyzeResult{result}, nil + } + + isMatch, err := compareHostFilesystemPerformanceConditionalToActual(outcome.Fail.When, fsPerf) + if err != nil { + return nil, errors.Wrapf(err, "failed to compare %q", outcome.Fail.When) + } + + if isMatch { + result.IsFail = true + result.Message = renderFSPerfOutcome(outcome.Fail.Message, fsPerf) + result.URI = outcome.Fail.URI + + return []*AnalyzeResult{result}, nil + } + case outcome.Warn != nil: + if outcome.Warn.When == "" { + result.IsWarn = true + result.Message = renderFSPerfOutcome(outcome.Warn.Message, fsPerf) + result.URI = outcome.Warn.URI + + return []*AnalyzeResult{result}, nil + } + + isMatch, err := compareHostFilesystemPerformanceConditionalToActual(outcome.Warn.When, fsPerf) + if err != nil { + return nil, errors.Wrapf(err, "failed to compare %q", outcome.Warn.When) + } + + if isMatch { + result.IsWarn = true + result.Message = renderFSPerfOutcome(outcome.Warn.Message, fsPerf) + result.URI = outcome.Warn.URI + + return []*AnalyzeResult{result}, nil + } + case outcome.Pass != nil: + if outcome.Pass.When == "" { + result.IsPass = true + result.Message = renderFSPerfOutcome(outcome.Pass.Message, fsPerf) + result.URI = outcome.Pass.URI + + return []*AnalyzeResult{result}, nil + } + + isMatch, err := compareHostFilesystemPerformanceConditionalToActual(outcome.Pass.When, fsPerf) + if err != nil { + return nil, errors.Wrapf(err, "failed to compare %q", outcome.Pass.When) + } + + if isMatch { + result.IsPass = true + result.Message = renderFSPerfOutcome(outcome.Pass.Message, fsPerf) + result.URI = outcome.Pass.URI + + return []*AnalyzeResult{result}, nil + } + default: + return nil, errors.New("unexpected outcome") + } + } + + return []*AnalyzeResult{result}, nil +} diff --git a/pkg/analyze/host_http.go b/pkg/analyze/host_http.go index caccec94c..264661c69 100644 --- a/pkg/analyze/host_http.go +++ b/pkg/analyze/host_http.go @@ -3,7 +3,6 @@ package analyzer import ( "encoding/json" "fmt" - "path/filepath" "strconv" "strings" @@ -32,14 +31,32 @@ func (a *AnalyzeHostHTTP) IsExcluded() (bool, error) { func (a *AnalyzeHostHTTP) Analyze( getCollectedFileContents func(string) ([]byte, error), findFiles getChildCollectedFileContents, ) ([]*AnalyzeResult, error) { - hostAnalyzer := a.hostAnalyzer - name := filepath.Join("host-collectors/http", "result.json") - if hostAnalyzer.CollectorName != "" { - name = filepath.Join("host-collectors/http", hostAnalyzer.CollectorName+".json") + collectorName := a.hostAnalyzer.CollectorName + if collectorName == "" { + collectorName = "result" } - return analyzeHTTPResult(hostAnalyzer, name, getCollectedFileContents, a.Title()) + const nodeBaseDir = "host-collectors/http" + localPath := fmt.Sprintf("%s/%s.json", nodeBaseDir, collectorName) + fileName := fmt.Sprintf("%s.json", collectorName) + + collectedContents, err := retrieveCollectedContents( + getCollectedFileContents, + localPath, + nodeBaseDir, + fileName, + ) + if err != nil { + return []*AnalyzeResult{{Title: a.Title()}}, err + } + + results, err := analyzeHostCollectorResults(collectedContents, a.hostAnalyzer.Outcomes, a.CheckCondition, a.Title()) + if err != nil { + return nil, errors.Wrap(err, "failed to analyze http request") + } + + return results, nil } func compareHostHTTPConditionalToActual(conditional string, result *httpResult) (res bool, err error) { @@ -156,3 +173,12 @@ func analyzeHTTPResult(analyzer *troubleshootv1beta2.HTTPAnalyze, fileName strin return []*AnalyzeResult{result}, nil } + +func (a *AnalyzeHostHTTP) CheckCondition(when string, data []byte) (bool, error) { + + var httpInfo httpResult + if err := json.Unmarshal(data, &httpInfo); err != nil { + return false, fmt.Errorf("failed to unmarshal data into httpResult: %v", err) + } + return compareHostHTTPConditionalToActual(when, &httpInfo) +} diff --git a/pkg/analyze/host_httploadbalancer.go b/pkg/analyze/host_httploadbalancer.go index 0002e4759..35f6ca827 100644 --- a/pkg/analyze/host_httploadbalancer.go +++ b/pkg/analyze/host_httploadbalancer.go @@ -3,7 +3,6 @@ package analyzer import ( "encoding/json" "fmt" - "path" "github.com/pkg/errors" troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2" @@ -25,82 +24,43 @@ func (a *AnalyzeHostHTTPLoadBalancer) IsExcluded() (bool, error) { func (a *AnalyzeHostHTTPLoadBalancer) Analyze( getCollectedFileContents func(string) ([]byte, error), findFiles getChildCollectedFileContents, ) ([]*AnalyzeResult, error) { - hostAnalyzer := a.hostAnalyzer - - collectorName := hostAnalyzer.CollectorName + collectorName := a.hostAnalyzer.CollectorName if collectorName == "" { collectorName = "httpLoadBalancer" } - fullPath := path.Join("host-collectors/httpLoadBalancer", fmt.Sprintf("%s.json", collectorName)) - collected, err := getCollectedFileContents(fullPath) + const nodeBaseDir = "host-collectors/httpLoadBalancer" + localPath := fmt.Sprintf("%s/%s.json", nodeBaseDir, collectorName) + fileName := fmt.Sprintf("%s.json", collectorName) + + collectedContents, err := retrieveCollectedContents( + getCollectedFileContents, + localPath, + nodeBaseDir, + fileName, + ) if err != nil { - return nil, errors.Wrapf(err, "failed to read collected file name: %s", fullPath) + return []*AnalyzeResult{{Title: a.Title()}}, err } - actual := collect.NetworkStatusResult{} - if err := json.Unmarshal(collected, &actual); err != nil { - return nil, errors.Wrap(err, "failed to unmarshal collected") - } - - var coll resultCollector - - for _, outcome := range hostAnalyzer.Outcomes { - result := &AnalyzeResult{Title: a.Title()} - - if outcome.Fail != nil { - if outcome.Fail.When == "" { - result.IsFail = true - result.Message = outcome.Fail.Message - result.URI = outcome.Fail.URI - - coll.push(result) - continue - } - - if string(actual.Status) == outcome.Fail.When { - result.IsFail = true - result.Message = outcome.Fail.Message - result.URI = outcome.Fail.URI - coll.push(result) - } - } else if outcome.Warn != nil { - if outcome.Warn.When == "" { - result.IsWarn = true - result.Message = outcome.Warn.Message - result.URI = outcome.Warn.URI - - coll.push(result) - continue - } - - if string(actual.Status) == outcome.Warn.When { - result.IsWarn = true - result.Message = outcome.Warn.Message - result.URI = outcome.Warn.URI - - coll.push(result) - } - } else if outcome.Pass != nil { - if outcome.Pass.When == "" { - result.IsPass = true - result.Message = outcome.Pass.Message - result.URI = outcome.Pass.URI + results, err := analyzeHostCollectorResults(collectedContents, a.hostAnalyzer.Outcomes, a.CheckCondition, a.Title()) + if err != nil { + return nil, errors.Wrap(err, "failed to analyze HTTP Load Balancer") + } - coll.push(result) - continue - } + return results, nil +} - if string(actual.Status) == outcome.Pass.When { - result.IsPass = true - result.Message = outcome.Pass.Message - result.URI = outcome.Pass.URI +func (a *AnalyzeHostHTTPLoadBalancer) CheckCondition(when string, data []byte) (bool, error) { - coll.push(result) - } + var httpLoadBalancer collect.NetworkStatusResult + if err := json.Unmarshal(data, &httpLoadBalancer); err != nil { + return false, fmt.Errorf("failed to unmarshal data into NetworkStatusResult: %v", err) + } - } + if string(httpLoadBalancer.Status) == when { + return true, nil } - return coll.get(a.Title()), nil + return false, nil } diff --git a/pkg/analyze/host_httploadbalancer_test.go b/pkg/analyze/host_httploadbalancer_test.go new file mode 100644 index 000000000..5d0884bb3 --- /dev/null +++ b/pkg/analyze/host_httploadbalancer_test.go @@ -0,0 +1,112 @@ +package analyzer + +import ( + "encoding/json" + "testing" + + troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2" + "github.com/replicatedhq/troubleshoot/pkg/collect" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestAnalyzeHTTPLoadBalancer(t *testing.T) { + tests := []struct { + name string + info *collect.NetworkStatusResult + hostAnalyzer *troubleshootv1beta2.HTTPLoadBalancerAnalyze + result []*AnalyzeResult + expectErr bool + }{ + { + name: "connection refused, fail", + info: &collect.NetworkStatusResult{ + Status: collect.NetworkStatusConnectionRefused, + }, + hostAnalyzer: &troubleshootv1beta2.HTTPLoadBalancerAnalyze{ + Outcomes: []*troubleshootv1beta2.Outcome{ + { + Fail: &troubleshootv1beta2.SingleOutcome{ + When: "connection-refused", + Message: "Connection was refused", + }, + }, + { + Pass: &troubleshootv1beta2.SingleOutcome{ + When: "connected", + Message: "Connection was successful", + }, + }, + { + Warn: &troubleshootv1beta2.SingleOutcome{ + Message: "Unexpected HTTP connection status", + }, + }, + }, + }, + result: []*AnalyzeResult{ + { + Title: "HTTP Load Balancer", + IsFail: true, + Message: "Connection was refused", + }, + }, + }, + { + name: "connected, success", + info: &collect.NetworkStatusResult{ + Status: collect.NetworkStatusConnected, + }, + hostAnalyzer: &troubleshootv1beta2.HTTPLoadBalancerAnalyze{ + Outcomes: []*troubleshootv1beta2.Outcome{ + { + Fail: &troubleshootv1beta2.SingleOutcome{ + When: "connection-refused", + Message: "Connection was refused", + }, + }, + { + Pass: &troubleshootv1beta2.SingleOutcome{ + When: "connected", + Message: "Connection was successful", + }, + }, + { + Warn: &troubleshootv1beta2.SingleOutcome{ + Message: "Unexpected HTTP connection status", + }, + }, + }, + }, + result: []*AnalyzeResult{ + { + Title: "HTTP Load Balancer", + IsPass: true, + Message: "Connection was successful", + }, + }, + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + req := require.New(t) + b, err := json.Marshal(test.info) + if err != nil { + t.Fatal(err) + } + + getCollectedFileContents := func(filename string) ([]byte, error) { + return b, nil + } + + result, err := (&AnalyzeHostHTTPLoadBalancer{test.hostAnalyzer}).Analyze(getCollectedFileContents, nil) + if test.expectErr { + req.Error(err) + } else { + req.NoError(err) + } + + assert.Equal(t, test.result, result) + }) + } +} diff --git a/pkg/analyze/host_ipv4interfaces.go b/pkg/analyze/host_ipv4interfaces.go index 9b07b30f7..c16379b20 100644 --- a/pkg/analyze/host_ipv4interfaces.go +++ b/pkg/analyze/host_ipv4interfaces.go @@ -27,94 +27,24 @@ func (a *AnalyzeHostIPV4Interfaces) IsExcluded() (bool, error) { func (a *AnalyzeHostIPV4Interfaces) Analyze( getCollectedFileContents func(string) ([]byte, error), findFiles getChildCollectedFileContents, ) ([]*AnalyzeResult, error) { - hostAnalyzer := a.hostAnalyzer - - contents, err := getCollectedFileContents(collect.HostIPV4InterfacesPath) + result := AnalyzeResult{Title: a.Title()} + + collectedContents, err := retrieveCollectedContents( + getCollectedFileContents, + collect.HostIPV4InterfacesPath, + collect.NodeInfoBaseDir, + collect.HostIPV4FileName, + ) if err != nil { - return nil, errors.Wrap(err, "failed to get collected file") - } - - var ipv4Interfaces []net.Interface - if err := json.Unmarshal(contents, &ipv4Interfaces); err != nil { - return nil, errors.Wrap(err, "failed to unmarshal ipv4Interfaces") + return []*AnalyzeResult{&result}, err } - var coll resultCollector - - for _, outcome := range hostAnalyzer.Outcomes { - result := &AnalyzeResult{Title: a.Title()} - - if outcome.Fail != nil { - if outcome.Fail.When == "" { - result.IsFail = true - result.Message = outcome.Fail.Message - result.URI = outcome.Fail.URI - - coll.push(result) - continue - } - - isMatch, err := compareHostIPV4InterfacesConditionalToActual(outcome.Fail.When, ipv4Interfaces) - if err != nil { - return nil, errors.Wrapf(err, "failed to compare %s", outcome.Fail.When) - } - - if isMatch { - result.IsFail = true - result.Message = outcome.Fail.Message - result.URI = outcome.Fail.URI - - coll.push(result) - } - } else if outcome.Warn != nil { - if outcome.Warn.When == "" { - result.IsWarn = true - result.Message = outcome.Warn.Message - result.URI = outcome.Warn.URI - - coll.push(result) - continue - } - - isMatch, err := compareHostIPV4InterfacesConditionalToActual(outcome.Warn.When, ipv4Interfaces) - if err != nil { - return nil, errors.Wrapf(err, "failed to compare %s", outcome.Warn.When) - } - - if isMatch { - result.IsWarn = true - result.Message = outcome.Warn.Message - result.URI = outcome.Warn.URI - - coll.push(result) - } - } else if outcome.Pass != nil { - if outcome.Pass.When == "" { - result.IsPass = true - result.Message = outcome.Pass.Message - result.URI = outcome.Pass.URI - - coll.push(result) - continue - } - - isMatch, err := compareHostIPV4InterfacesConditionalToActual(outcome.Pass.When, ipv4Interfaces) - if err != nil { - return nil, errors.Wrapf(err, "failed to compare %s", outcome.Pass.When) - } - - if isMatch { - result.IsPass = true - result.Message = outcome.Pass.Message - result.URI = outcome.Pass.URI - - coll.push(result) - } - - } + results, err := analyzeHostCollectorResults(collectedContents, a.hostAnalyzer.Outcomes, a.CheckCondition, a.Title()) + if err != nil { + return nil, errors.Wrap(err, "failed to analyze IPv4 interfaces") } - return coll.get(a.Title()), nil + return results, nil } func compareHostIPV4InterfacesConditionalToActual(conditional string, ipv4Interfaces []net.Interface) (res bool, err error) { @@ -153,3 +83,13 @@ func compareHostIPV4InterfacesConditionalToActual(conditional string, ipv4Interf return false, fmt.Errorf("Unknown operator %q. Supported operators are: <, <=, ==, >=, >", operator) } + +func (a *AnalyzeHostIPV4Interfaces) CheckCondition(when string, data []byte) (bool, error) { + + var ipv4Interfaces []net.Interface + if err := json.Unmarshal(data, &ipv4Interfaces); err != nil { + return false, fmt.Errorf("failed to unmarshal data into []net.Interface: %v", err) + } + + return compareHostIPV4InterfacesConditionalToActual(when, ipv4Interfaces) +} diff --git a/pkg/analyze/host_kernel_configs.go b/pkg/analyze/host_kernel_configs.go index d7b3455d2..f59259ee0 100644 --- a/pkg/analyze/host_kernel_configs.go +++ b/pkg/analyze/host_kernel_configs.go @@ -2,6 +2,7 @@ package analyzer import ( "encoding/json" + "fmt" "regexp" "strings" @@ -16,6 +17,8 @@ type AnalyzeHostKernelConfigs struct { hostAnalyzer *troubleshootv1beta2.KernelConfigsAnalyze } +var kConfigRegex = regexp.MustCompile("^(CONFIG_[A-Z0-9_]+)=([ymn]+)$") + func (a *AnalyzeHostKernelConfigs) Title() string { return hostAnalyzerTitleOrDefault(a.hostAnalyzer.AnalyzeMeta, "Kernel Configs") } @@ -27,20 +30,50 @@ func (a *AnalyzeHostKernelConfigs) IsExcluded() (bool, error) { func (a *AnalyzeHostKernelConfigs) Analyze( getCollectedFileContents func(string) ([]byte, error), findFiles getChildCollectedFileContents, ) ([]*AnalyzeResult, error) { - hostAnalyzer := a.hostAnalyzer - - contents, err := getCollectedFileContents(collect.HostKernelConfigsPath) + collectedContents, err := retrieveCollectedContents( + getCollectedFileContents, + collect.HostKernelConfigsPath, + collect.NodeInfoBaseDir, + collect.HostKernelConfigsFileName, + ) if err != nil { - return nil, errors.Wrap(err, "failed to get collected file") + return []*AnalyzeResult{{Title: a.Title()}}, err + } + + var results []*AnalyzeResult + + for _, content := range collectedContents { + currentTitle := a.Title() + if content.NodeName != "" { + currentTitle = fmt.Sprintf("%s - Node %s", a.Title(), content.NodeName) + } + result, err := a.analyzeSingleNode(content, currentTitle) + if err != nil { + return nil, errors.Wrapf(err, "failed to analyze kernel configs for %s", currentTitle) + } + if result != nil { + results = append(results, result...) + } + } + + return results, nil +} + +func addMissingKernelConfigs(message string, missingConfigs []string) string { + if message == "" && len(missingConfigs) == 0 { + return message } + return strings.ReplaceAll(message, "{{ .ConfigsNotFound }}", strings.Join(missingConfigs, ", ")) +} +func (a *AnalyzeHostKernelConfigs) analyzeSingleNode(content collectedContent, currentTitle string) ([]*AnalyzeResult, error) { + hostAnalyzer := a.hostAnalyzer kConfigs := collect.KConfigs{} - if err := json.Unmarshal(contents, &kConfigs); err != nil { + if err := json.Unmarshal(content.Data, &kConfigs); err != nil { return nil, errors.Wrap(err, "failed to read kernel configs") } var configsNotFound []string - kConfigRegex := regexp.MustCompile("^(CONFIG_[A-Z0-9_]+)=([ymn]+)$") for _, config := range hostAnalyzer.SelectedConfigs { matches := kConfigRegex.FindStringSubmatch(config) // zero tolerance for invalid kernel config @@ -66,8 +99,8 @@ func (a *AnalyzeHostKernelConfigs) Analyze( var results []*AnalyzeResult for _, outcome := range hostAnalyzer.Outcomes { result := &AnalyzeResult{ - Title: a.Title(), - Strict: hostAnalyzer.Strict.BoolOrDefaultFalse(), + Title: currentTitle, + Strict: a.hostAnalyzer.Strict.BoolOrDefaultFalse(), } if outcome.Pass != nil && len(configsNotFound) == 0 { @@ -88,10 +121,3 @@ func (a *AnalyzeHostKernelConfigs) Analyze( return results, nil } - -func addMissingKernelConfigs(message string, missingConfigs []string) string { - if message == "" && len(missingConfigs) == 0 { - return message - } - return strings.ReplaceAll(message, "{{ .ConfigsNotFound }}", strings.Join(missingConfigs, ", ")) -} diff --git a/pkg/analyze/host_memory.go b/pkg/analyze/host_memory.go index 92ed2f450..42639fe74 100644 --- a/pkg/analyze/host_memory.go +++ b/pkg/analyze/host_memory.go @@ -3,7 +3,6 @@ package analyzer import ( "encoding/json" "fmt" - "reflect" "strings" "github.com/pkg/errors" @@ -49,14 +48,10 @@ func (a *AnalyzeHostMemory) Analyze( } // checkCondition checks the condition of the when clause -func (a *AnalyzeHostMemory) CheckCondition(when string, data collectorData) (bool, error) { - rawData, ok := data.([]byte) - if !ok { - return false, fmt.Errorf("expected data to be []uint8 (raw bytes), got: %v", reflect.TypeOf(data)) - } +func (a *AnalyzeHostMemory) CheckCondition(when string, data []byte) (bool, error) { var memInfo collect.MemoryInfo - if err := json.Unmarshal(rawData, &memInfo); err != nil { + if err := json.Unmarshal(data, &memInfo); err != nil { return false, fmt.Errorf("failed to unmarshal data into MemoryInfo: %v", err) } diff --git a/pkg/analyze/host_os_info.go b/pkg/analyze/host_os_info.go index 917a0c437..7ae93301d 100644 --- a/pkg/analyze/host_os_info.go +++ b/pkg/analyze/host_os_info.go @@ -3,7 +3,6 @@ package analyzer import ( "encoding/json" "fmt" - "reflect" "regexp" "strings" @@ -50,14 +49,10 @@ func (a *AnalyzeHostOS) Analyze( } // checkCondition checks the condition of the when clause -func (a *AnalyzeHostOS) CheckCondition(when string, data collectorData) (bool, error) { - rawData, ok := data.([]byte) - if !ok { - return false, fmt.Errorf("expected data to be []uint8 (raw bytes), got: %v", reflect.TypeOf(data)) - } +func (a *AnalyzeHostOS) CheckCondition(when string, data []byte) (bool, error) { var osInfo collect.HostOSInfo - if err := json.Unmarshal(rawData, &osInfo); err != nil { + if err := json.Unmarshal(data, &osInfo); err != nil { return false, fmt.Errorf("failed to unmarshal data into HostOSInfo: %v", err) } diff --git a/pkg/analyze/host_services.go b/pkg/analyze/host_services.go index bed36feca..7adf7191c 100644 --- a/pkg/analyze/host_services.go +++ b/pkg/analyze/host_services.go @@ -25,94 +25,24 @@ func (a *AnalyzeHostServices) IsExcluded() (bool, error) { func (a *AnalyzeHostServices) Analyze( getCollectedFileContents func(string) ([]byte, error), findFiles getChildCollectedFileContents, ) ([]*AnalyzeResult, error) { - hostAnalyzer := a.hostAnalyzer - - contents, err := getCollectedFileContents(collect.HostServicesPath) + result := AnalyzeResult{Title: a.Title()} + + collectedContents, err := retrieveCollectedContents( + getCollectedFileContents, + collect.HostServicesPath, + collect.NodeInfoBaseDir, + collect.HostServicesFileName, + ) if err != nil { - return nil, errors.Wrap(err, "failed to get collected file") + return []*AnalyzeResult{&result}, err } - var services []collect.ServiceInfo - if err := json.Unmarshal(contents, &services); err != nil { - return nil, errors.Wrap(err, "failed to unmarshal systemctl service info") - } - - var coll resultCollector - - for _, outcome := range hostAnalyzer.Outcomes { - result := &AnalyzeResult{Title: a.Title()} - - if outcome.Fail != nil { - if outcome.Fail.When == "" { - result.IsFail = true - result.Message = outcome.Fail.Message - result.URI = outcome.Fail.URI - - coll.push(result) - continue - } - - isMatch, err := compareHostServicesConditionalToActual(outcome.Fail.When, services) - if err != nil { - return nil, errors.Wrapf(err, "failed to compare %s", outcome.Fail.When) - } - - if isMatch { - result.IsFail = true - result.Message = outcome.Fail.Message - result.URI = outcome.Fail.URI - - coll.push(result) - } - } else if outcome.Warn != nil { - if outcome.Warn.When == "" { - result.IsWarn = true - result.Message = outcome.Warn.Message - result.URI = outcome.Warn.URI - - coll.push(result) - continue - } - - isMatch, err := compareHostServicesConditionalToActual(outcome.Warn.When, services) - if err != nil { - return nil, errors.Wrapf(err, "failed to compare %s", outcome.Warn.When) - } - - if isMatch { - result.IsWarn = true - result.Message = outcome.Warn.Message - result.URI = outcome.Warn.URI - - coll.push(result) - } - } else if outcome.Pass != nil { - if outcome.Pass.When == "" { - result.IsPass = true - result.Message = outcome.Pass.Message - result.URI = outcome.Pass.URI - - coll.push(result) - continue - } - - isMatch, err := compareHostServicesConditionalToActual(outcome.Pass.When, services) - if err != nil { - return nil, errors.Wrapf(err, "failed to compare %s", outcome.Pass.When) - } - - if isMatch { - result.IsPass = true - result.Message = outcome.Pass.Message - result.URI = outcome.Pass.URI - - coll.push(result) - } - - } + results, err := analyzeHostCollectorResults(collectedContents, a.hostAnalyzer.Outcomes, a.CheckCondition, a.Title()) + if err != nil { + return nil, errors.Wrap(err, "failed to analyze host services") } - return coll.get(a.Title()), nil + return results, nil } // @@ -187,3 +117,13 @@ func isServiceMatch(serviceName string, matchName string) bool { return false } + +func (a *AnalyzeHostServices) CheckCondition(when string, data []byte) (bool, error) { + + var services []collect.ServiceInfo + if err := json.Unmarshal(data, &services); err != nil { + return false, fmt.Errorf("failed to unmarshal data into ServiceInfo: %v", err) + } + + return compareHostServicesConditionalToActual(when, services) +} diff --git a/pkg/analyze/host_subnetavailable.go b/pkg/analyze/host_subnetavailable.go index 13bd0db0d..fb0d7da30 100644 --- a/pkg/analyze/host_subnetavailable.go +++ b/pkg/analyze/host_subnetavailable.go @@ -2,7 +2,7 @@ package analyzer import ( "encoding/json" - "path/filepath" + "fmt" "github.com/pkg/errors" troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2" @@ -24,61 +24,38 @@ func (a *AnalyzeHostSubnetAvailable) IsExcluded() (bool, error) { func (a *AnalyzeHostSubnetAvailable) Analyze( getCollectedFileContents func(string) ([]byte, error), findFiles getChildCollectedFileContents, ) ([]*AnalyzeResult, error) { - hostAnalyzer := a.hostAnalyzer - - name := filepath.Join("host-collectors/subnetAvailable", "result.json") - if hostAnalyzer.CollectorName != "" { - name = filepath.Join("host-collectors/subnetAvailable", hostAnalyzer.CollectorName+".json") - } - contents, err := getCollectedFileContents(name) - if err != nil { - return nil, errors.Wrap(err, "failed to get collected file") + collectorName := a.hostAnalyzer.CollectorName + if collectorName == "" { + collectorName = "result" } - isSubnetAvailable := &collect.SubnetAvailableResult{} - if err := json.Unmarshal(contents, isSubnetAvailable); err != nil { - return nil, errors.Wrap(err, "failed to unmarshal subnetAvailable result") - } + const nodeBaseDir = "host-collectors/subnetAvailable" + localPath := fmt.Sprintf("%s/%s.json", nodeBaseDir, collectorName) + fileName := fmt.Sprintf("%s.json", collectorName) - result := &AnalyzeResult{ - Title: a.Title(), + collectedContents, err := retrieveCollectedContents( + getCollectedFileContents, + localPath, + nodeBaseDir, + fileName, + ) + if err != nil { + return []*AnalyzeResult{{Title: a.Title()}}, err } - for _, outcome := range hostAnalyzer.Outcomes { - if outcome.Fail != nil { - if outcome.Fail.When == "" { - result.IsFail = true - result.Message = outcome.Fail.Message - result.URI = outcome.Fail.URI - - return []*AnalyzeResult{result}, nil - } - - if string(isSubnetAvailable.Status) == outcome.Fail.When { - result.IsFail = true - result.Message = outcome.Fail.Message - result.URI = outcome.Fail.URI - - return []*AnalyzeResult{result}, nil - } - } else if outcome.Pass != nil { - if outcome.Pass.When == "" { - result.IsPass = true - result.Message = outcome.Pass.Message - result.URI = outcome.Pass.URI - - return []*AnalyzeResult{result}, nil - } + results, err := analyzeHostCollectorResults(collectedContents, a.hostAnalyzer.Outcomes, a.CheckCondition, a.Title()) + if err != nil { + return nil, errors.Wrap(err, "failed to analyze HTTP Load Balancer") + } - if string(isSubnetAvailable.Status) == outcome.Pass.When { - result.IsPass = true - result.Message = outcome.Pass.Message - result.URI = outcome.Pass.URI + return results, nil +} - return []*AnalyzeResult{result}, nil - } - } +func (a *AnalyzeHostSubnetAvailable) CheckCondition(when string, data []byte) (bool, error) { + isSubnetAvailable := &collect.SubnetAvailableResult{} + if err := json.Unmarshal(data, isSubnetAvailable); err != nil { + return false, errors.Wrap(err, "failed to unmarshal subnetAvailable result") } - return []*AnalyzeResult{result}, nil + return string(isSubnetAvailable.Status) == when, nil } diff --git a/pkg/analyze/host_subnetavailable_test.go b/pkg/analyze/host_subnetavailable_test.go new file mode 100644 index 000000000..95df98ffd --- /dev/null +++ b/pkg/analyze/host_subnetavailable_test.go @@ -0,0 +1,107 @@ +package analyzer + +import ( + "encoding/json" + "testing" + + troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2" + "github.com/replicatedhq/troubleshoot/pkg/collect" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestAnalyzeSubnetAvailable(t *testing.T) { + tests := []struct { + name string + info *collect.SubnetAvailableResult + hostAnalyzer *troubleshootv1beta2.SubnetAvailableAnalyze + result []*AnalyzeResult + expectedErr bool + }{ + { + name: "subnet available", + info: &collect.SubnetAvailableResult{ + CIDRRangeAlloc: "10.0.0.0/8", + DesiredCIDR: 22, + Status: collect.SubnetStatusAvailable, + }, + hostAnalyzer: &troubleshootv1beta2.SubnetAvailableAnalyze{ + Outcomes: []*troubleshootv1beta2.Outcome{ + { + Pass: &troubleshootv1beta2.SingleOutcome{ + When: "a-subnet-is-available", + Message: "available /22 subnet found", + }, + }, + { + Fail: &troubleshootv1beta2.SingleOutcome{ + When: "no-subnet-available", + Message: "failed to find available subnet", + }, + }, + }, + }, + result: []*AnalyzeResult{ + { + Title: "Subnet Available", + IsPass: true, + Message: "available /22 subnet found", + }, + }, + }, + { + name: "subnet not available", + info: &collect.SubnetAvailableResult{ + CIDRRangeAlloc: "10.0.0.0/8", + DesiredCIDR: 22, + Status: collect.SubnetStatusNoneAvailable, + }, + hostAnalyzer: &troubleshootv1beta2.SubnetAvailableAnalyze{ + Outcomes: []*troubleshootv1beta2.Outcome{ + { + Pass: &troubleshootv1beta2.SingleOutcome{ + When: "a-subnet-is-available", + Message: "available /22 subnet found", + }, + }, + { + Fail: &troubleshootv1beta2.SingleOutcome{ + When: "no-subnet-available", + Message: "failed to find available subnet", + }, + }, + }, + }, + result: []*AnalyzeResult{ + { + Title: "Subnet Available", + IsFail: true, + Message: "failed to find available subnet", + }, + }, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + req := require.New(t) + b, err := json.Marshal(test.info) + if err != nil { + t.Fatal(err) + } + + getCollectedFileContents := func(filename string) ([]byte, error) { + return b, nil + } + + result, err := (&AnalyzeHostSubnetAvailable{test.hostAnalyzer}).Analyze(getCollectedFileContents, nil) + if test.expectedErr { + req.Error(err) + } else { + req.NoError(err) + } + + assert.Equal(t, test.result, result) + }) + } +} diff --git a/pkg/analyze/host_system_packages.go b/pkg/analyze/host_system_packages.go index f10610691..2ef9a86bd 100644 --- a/pkg/analyze/host_system_packages.go +++ b/pkg/analyze/host_system_packages.go @@ -32,93 +32,40 @@ func (a *AnalyzeHostSystemPackages) Analyze( getCollectedFileContents func(string) ([]byte, error), findFiles getChildCollectedFileContents, ) ([]*AnalyzeResult, error) { hostAnalyzer := a.hostAnalyzer - - packagesFileName := "host-collectors/system/packages.json" - if a.hostAnalyzer.CollectorName != "" { - packagesFileName = fmt.Sprintf("host-collectors/system/%s-packages.json", a.hostAnalyzer.CollectorName) + collectorName := hostAnalyzer.CollectorName + if collectorName == "" { + collectorName = "packages" } - contents, err := getCollectedFileContents(packagesFileName) - if err != nil { - return nil, errors.Wrap(err, "failed to get collected file") - } + localPath := fmt.Sprintf("%s/%s-packages.json", collect.NodeInfoBaseDir, collectorName) + packagesFileName := fmt.Sprintf("%s-packages.json", collectorName) - var info collect.SystemPackagesInfo - if err := json.Unmarshal(contents, &info); err != nil { - return nil, errors.Wrap(err, "failed to unmarshal system packages info") + collectedContents, err := retrieveCollectedContents( + getCollectedFileContents, + localPath, + collect.NodeInfoBaseDir, + packagesFileName, + ) + if err != nil { + return []*AnalyzeResult{{Title: a.Title()}}, err } - allResults := []*AnalyzeResult{} - - for _, pkg := range info.Packages { - templateMap := getSystemPackageTemplateMap(pkg, info.OS, info.OSVersion) - - for _, outcome := range hostAnalyzer.Outcomes { - r := AnalyzeResult{} - when := "" - - if outcome.Fail != nil { - r.IsFail = true - r.Message = outcome.Fail.Message - r.URI = outcome.Fail.URI - when = outcome.Fail.When - } else if outcome.Warn != nil { - r.IsWarn = true - r.Message = outcome.Warn.Message - r.URI = outcome.Warn.URI - when = outcome.Warn.When - } else if outcome.Pass != nil { - r.IsPass = true - r.Message = outcome.Pass.Message - r.URI = outcome.Pass.URI - when = outcome.Pass.When - } else { - println("error: found an empty outcome in a systemPackages analyzer") // don't stop - continue - } - - match, err := compareSystemPackagesConditionalToActual(when, templateMap) - if err != nil { - return nil, errors.Wrapf(err, "failed to compare %s", when) - } - - if !match { - continue - } - - tmpl := template.New("package") - - // template the title - titleTmpl, err := tmpl.Parse(a.Title()) - if err != nil { - return nil, errors.Wrap(err, "failed to create new title template") - } - var t bytes.Buffer - err = titleTmpl.Execute(&t, templateMap) - if err != nil { - return nil, errors.Wrap(err, "failed to execute title template") - } - r.Title = t.String() - - // template the message - msgTmpl, err := tmpl.Parse(r.Message) - if err != nil { - return nil, errors.Wrap(err, "failed to create new message template") - } - var m bytes.Buffer - err = msgTmpl.Execute(&m, templateMap) - if err != nil { - return nil, errors.Wrap(err, "failed to execute message template") - } - r.Message = m.String() - - // add to results, break and check the next pod - allResults = append(allResults, &r) - break + var results []*AnalyzeResult + for _, content := range collectedContents { + currentTitle := a.Title() + if content.NodeName != "" { + currentTitle = fmt.Sprintf("%s - Node %s", a.Title(), content.NodeName) + } + result, err := a.analyzeSingleNode(content, currentTitle) + if err != nil { + return nil, errors.Wrapf(err, "failed to analyze system packages for %s", currentTitle) + } + if result != nil { + results = append(results, result...) } } - return allResults, nil + return results, nil } func getSystemPackageTemplateMap(pkg collect.SystemPackage, osName string, osVersion string) map[string]interface{} { @@ -231,3 +178,72 @@ func compareSystemPackagesConditionalToActual(conditional string, templateMap ma return t, nil } + +func (a *AnalyzeHostSystemPackages) analyzeSingleNode(content collectedContent, currentTitle string) ([]*AnalyzeResult, error) { + hostAnalyzer := a.hostAnalyzer + var info collect.SystemPackagesInfo + if err := json.Unmarshal(content.Data, &info); err != nil { + return nil, errors.Wrapf(err, "failed to unmarshal system packages info from %s", currentTitle) + } + + allResults := []*AnalyzeResult{} + + for _, pkg := range info.Packages { + templateMap := getSystemPackageTemplateMap(pkg, info.OS, info.OSVersion) + + for _, outcome := range hostAnalyzer.Outcomes { + r := AnalyzeResult{} + when := "" + switch { + case outcome.Fail != nil: + r.IsFail = true + r.Message = outcome.Fail.Message + r.URI = outcome.Fail.URI + when = outcome.Fail.When + case outcome.Warn != nil: + r.IsWarn = true + r.Message = outcome.Warn.Message + r.URI = outcome.Warn.URI + when = outcome.Warn.When + case outcome.Pass != nil: + r.IsPass = true + r.Message = outcome.Pass.Message + r.URI = outcome.Pass.URI + when = outcome.Pass.When + default: + return nil, errors.New("invalid outcome") + } + + match, err := compareSystemPackagesConditionalToActual(when, templateMap) + if err != nil { + return nil, errors.Wrapf(err, "failed to compare %s", when) + } + + if !match { + continue + } + + tmpl := template.New("package") + + r.Title = currentTitle + + // template the message + msgTmpl, err := tmpl.Parse(r.Message) + if err != nil { + return nil, errors.Wrap(err, "failed to create new message template") + } + var m bytes.Buffer + err = msgTmpl.Execute(&m, templateMap) + if err != nil { + return nil, errors.Wrap(err, "failed to execute message template") + } + r.Message = m.String() + + // add to results, break and check the next pod + allResults = append(allResults, &r) + break + } + } + + return allResults, nil +} diff --git a/pkg/analyze/host_tcp_connect.go b/pkg/analyze/host_tcp_connect.go index 4a405aab0..0fd992eb4 100644 --- a/pkg/analyze/host_tcp_connect.go +++ b/pkg/analyze/host_tcp_connect.go @@ -2,7 +2,7 @@ package analyzer import ( "encoding/json" - "path/filepath" + "fmt" "github.com/pkg/errors" troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2" @@ -24,84 +24,39 @@ func (a *AnalyzeHostTCPConnect) IsExcluded() (bool, error) { func (a *AnalyzeHostTCPConnect) Analyze( getCollectedFileContents func(string) ([]byte, error), findFiles getChildCollectedFileContents, ) ([]*AnalyzeResult, error) { - hostAnalyzer := a.hostAnalyzer - - collectorName := hostAnalyzer.CollectorName + collectorName := a.hostAnalyzer.CollectorName if collectorName == "" { collectorName = "connect" } - fullPath := filepath.Join("host-collectors/connect", collectorName+".json") - collected, err := getCollectedFileContents(fullPath) + const nodeBaseDir = "host-collectors/connect" + localPath := fmt.Sprintf("%s/%s.json", nodeBaseDir, collectorName) + fileName := fmt.Sprintf("%s.json", collectorName) + + collectedContents, err := retrieveCollectedContents( + getCollectedFileContents, + localPath, + nodeBaseDir, + fileName, + ) if err != nil { - return nil, errors.Wrapf(err, "failed to read collected file name: %s", fullPath) + return []*AnalyzeResult{{Title: a.Title()}}, err } - actual := collect.NetworkStatusResult{} - if err := json.Unmarshal(collected, &actual); err != nil { - return nil, errors.Wrap(err, "failed to unmarshal collected") - } - - var coll resultCollector - result := &AnalyzeResult{Title: a.Title()} - - for _, outcome := range hostAnalyzer.Outcomes { - - if outcome.Fail != nil { - if outcome.Fail.When == "" { - result.IsFail = true - result.Message = outcome.Fail.Message - result.URI = outcome.Fail.URI - - coll.push(result) - break - } - if string(actual.Status) == outcome.Fail.When { - result.IsFail = true - result.Message = outcome.Fail.Message - result.URI = outcome.Fail.URI - - coll.push(result) - break - } - } else if outcome.Warn != nil { - if outcome.Warn.When == "" { - result.IsWarn = true - result.Message = outcome.Warn.Message - result.URI = outcome.Warn.URI - - coll.push(result) - break - } - - if string(actual.Status) == outcome.Warn.When { - result.IsWarn = true - result.Message = outcome.Warn.Message - result.URI = outcome.Warn.URI - - coll.push(result) - break - } - } else if outcome.Pass != nil { - if outcome.Pass.When == "" { - result.IsPass = true - result.Message = outcome.Pass.Message - result.URI = outcome.Pass.URI + results, err := analyzeHostCollectorResults(collectedContents, a.hostAnalyzer.Outcomes, a.CheckCondition, a.Title()) + if err != nil { + return nil, errors.Wrap(err, "failed to analyze tcp connect") + } - coll.push(result) - break - } + return results, nil +} - if string(actual.Status) == outcome.Pass.When { - result.IsPass = true - result.Message = outcome.Pass.Message - result.URI = outcome.Pass.URI +func (a *AnalyzeHostTCPConnect) CheckCondition(when string, data []byte) (bool, error) { - coll.push(result) - break - } - } + var tcpConnect collect.NetworkStatusResult + if err := json.Unmarshal(data, &tcpConnect); err != nil { + return false, fmt.Errorf("failed to unmarshal data into NetworkStatusResult: %v", err) } - return coll.get(a.Title()), nil + return string(tcpConnect.Status) == when, nil } diff --git a/pkg/analyze/host_tcploadbalancer.go b/pkg/analyze/host_tcploadbalancer.go index 7a096ceb7..7477d5804 100644 --- a/pkg/analyze/host_tcploadbalancer.go +++ b/pkg/analyze/host_tcploadbalancer.go @@ -3,7 +3,6 @@ package analyzer import ( "encoding/json" "fmt" - "path" "github.com/pkg/errors" troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2" @@ -25,85 +24,45 @@ func (a *AnalyzeHostTCPLoadBalancer) IsExcluded() (bool, error) { func (a *AnalyzeHostTCPLoadBalancer) Analyze( getCollectedFileContents func(string) ([]byte, error), findFiles getChildCollectedFileContents, ) ([]*AnalyzeResult, error) { - hostAnalyzer := a.hostAnalyzer - collectorName := hostAnalyzer.CollectorName + collectorName := a.hostAnalyzer.CollectorName if collectorName == "" { collectorName = "tcpLoadBalancer" } - fullPath := path.Join("host-collectors/tcpLoadBalancer", fmt.Sprintf("%s.json", collectorName)) + const nodeBaseDir = "host-collectors/tcpLoadBalancer" + localPath := fmt.Sprintf("%s/%s.json", nodeBaseDir, collectorName) + fileName := fmt.Sprintf("%s.json", collectorName) - collected, err := getCollectedFileContents(fullPath) + collectedContents, err := retrieveCollectedContents( + getCollectedFileContents, + localPath, + nodeBaseDir, + fileName, + ) if err != nil { - return nil, errors.Wrapf(err, "failed to read collected file name: %s", fullPath) + return []*AnalyzeResult{{Title: a.Title()}}, err } - actual := collect.NetworkStatusResult{} - if err := json.Unmarshal(collected, &actual); err != nil { - return nil, errors.Wrap(err, "failed to unmarshal collected") + + results, err := analyzeHostCollectorResults(collectedContents, a.hostAnalyzer.Outcomes, a.CheckCondition, a.Title()) + if err != nil { + return nil, errors.Wrap(err, "failed to analyze TCP Load Balancer") + } + + return results, nil + +} + +func (a *AnalyzeHostTCPLoadBalancer) CheckCondition(when string, data []byte) (bool, error) { + + var tcpLoadBalancer collect.NetworkStatusResult + if err := json.Unmarshal(data, &tcpLoadBalancer); err != nil { + return false, fmt.Errorf("failed to unmarshal data into NetworkStatusResult: %v", err) } - var coll resultCollector - result := &AnalyzeResult{Title: a.Title()} - - for _, outcome := range hostAnalyzer.Outcomes { - - if outcome.Fail != nil { - if outcome.Fail.When == "" { - result.IsFail = true - result.Message = outcome.Fail.Message - result.URI = outcome.Fail.URI - - coll.push(result) - break - } - - if string(actual.Status) == outcome.Fail.When { - result.IsFail = true - result.Message = outcome.Fail.Message - result.URI = outcome.Fail.URI - - coll.push(result) - break - } - } else if outcome.Warn != nil { - if outcome.Warn.When == "" { - result.IsWarn = true - result.Message = outcome.Warn.Message - result.URI = outcome.Warn.URI - - coll.push(result) - break - } - - if string(actual.Status) == outcome.Warn.When { - result.IsWarn = true - result.Message = outcome.Warn.Message - result.URI = outcome.Warn.URI - - coll.push(result) - break - } - } else if outcome.Pass != nil { - if outcome.Pass.When == "" { - result.IsPass = true - result.Message = outcome.Pass.Message - result.URI = outcome.Pass.URI - - coll.push(result) - break - } - - if string(actual.Status) == outcome.Pass.When { - result.IsPass = true - result.Message = outcome.Pass.Message - result.URI = outcome.Pass.URI - - coll.push(result) - break - } - } + if string(tcpLoadBalancer.Status) == when { + return true, nil } - return coll.get(a.Title()), nil + return false, nil } diff --git a/pkg/analyze/host_tcpportstatus.go b/pkg/analyze/host_tcpportstatus.go index 4920ebdd5..d235552ad 100644 --- a/pkg/analyze/host_tcpportstatus.go +++ b/pkg/analyze/host_tcpportstatus.go @@ -3,7 +3,6 @@ package analyzer import ( "encoding/json" "fmt" - "path" "github.com/pkg/errors" troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2" @@ -27,76 +26,40 @@ func (a *AnalyzeHostTCPPortStatus) IsExcluded() (bool, error) { func (a *AnalyzeHostTCPPortStatus) Analyze( getCollectedFileContents func(string) ([]byte, error), findFiles getChildCollectedFileContents, ) ([]*AnalyzeResult, error) { - hostAnalyzer := a.hostAnalyzer - - fullPath := path.Join("host-collectors/tcpPortStatus", "tcpPortStatus.json") - if hostAnalyzer.CollectorName != "" { - fullPath = path.Join("host-collectors/tcpPortStatus", fmt.Sprintf("%s.json", hostAnalyzer.CollectorName)) + collectorName := a.hostAnalyzer.CollectorName + if collectorName == "" { + collectorName = "tcpPortStatus" } - collected, err := getCollectedFileContents(fullPath) + const nodeBaseDir = "host-collectors/tcpPortStatus" + localPath := fmt.Sprintf("%s/%s.json", nodeBaseDir, collectorName) + fileName := fmt.Sprintf("%s.json", collectorName) + + collectedContents, err := retrieveCollectedContents( + getCollectedFileContents, + localPath, + nodeBaseDir, + fileName, + ) if err != nil { - return nil, errors.Wrapf(err, "failed to read collected file name: %s", fullPath) + return []*AnalyzeResult{{Title: a.Title()}}, err } - actual := collect.NetworkStatusResult{} - if err := json.Unmarshal(collected, &actual); err != nil { - return nil, errors.Wrap(err, "failed to unmarshal collected") - } - - result := &AnalyzeResult{Title: a.Title()} - - for _, outcome := range hostAnalyzer.Outcomes { - - if outcome.Fail != nil { - if outcome.Fail.When == "" { - result.IsFail = true - result.Message = outcome.Fail.Message - result.URI = outcome.Fail.URI - - return []*AnalyzeResult{result}, nil - } - if string(actual.Status) == outcome.Fail.When { - result.IsFail = true - result.Message = outcome.Fail.Message - result.URI = outcome.Fail.URI - - return []*AnalyzeResult{result}, nil - } - } else if outcome.Warn != nil { - if outcome.Warn.When == "" { - result.IsWarn = true - result.Message = outcome.Warn.Message - result.URI = outcome.Warn.URI - - return []*AnalyzeResult{result}, nil - } - - if string(actual.Status) == outcome.Warn.When { - result.IsWarn = true - result.Message = outcome.Warn.Message - result.URI = outcome.Warn.URI + results, err := analyzeHostCollectorResults(collectedContents, a.hostAnalyzer.Outcomes, a.CheckCondition, a.Title()) + if err != nil { + return nil, errors.Wrap(err, "failed to analyze tcp port status") + } - return []*AnalyzeResult{result}, nil - } - } else if outcome.Pass != nil { - if outcome.Pass.When == "" { - result.IsPass = true - result.Message = outcome.Pass.Message - result.URI = outcome.Pass.URI + return results, nil - return []*AnalyzeResult{result}, nil - } +} - if string(actual.Status) == outcome.Pass.When { - result.IsPass = true - result.Message = outcome.Pass.Message - result.URI = outcome.Pass.URI +func (a *AnalyzeHostTCPPortStatus) CheckCondition(when string, data []byte) (bool, error) { - return []*AnalyzeResult{result}, nil - } - } + var tcpPortStatus collect.NetworkStatusResult + if err := json.Unmarshal(data, &tcpPortStatus); err != nil { + return false, fmt.Errorf("failed to unmarshal data into NetworkStatusResult: %v", err) } - return []*AnalyzeResult{result}, nil + return string(tcpPortStatus.Status) == when, nil } diff --git a/pkg/analyze/host_time.go b/pkg/analyze/host_time.go index 7fae39c78..735c39e79 100644 --- a/pkg/analyze/host_time.go +++ b/pkg/analyze/host_time.go @@ -32,95 +32,25 @@ func (a *AnalyzeHostTime) IsExcluded() (bool, error) { func (a *AnalyzeHostTime) Analyze( getCollectedFileContents func(string) ([]byte, error), findFiles getChildCollectedFileContents, ) ([]*AnalyzeResult, error) { - hostAnalyzer := a.hostAnalyzer - - contents, err := getCollectedFileContents(collect.HostTimePath) + result := AnalyzeResult{Title: a.Title()} + + collectedContents, err := retrieveCollectedContents( + getCollectedFileContents, + collect.HostTimePath, + collect.NodeInfoBaseDir, + collect.HostMemoryFileName, + ) if err != nil { - return nil, errors.Wrap(err, "failed to get collected file") + return []*AnalyzeResult{&result}, err } - timeInfo := collect.TimeInfo{} - if err := json.Unmarshal(contents, &timeInfo); err != nil { - return nil, errors.Wrap(err, "failed to unmarshal time info") + results, err := analyzeHostCollectorResults(collectedContents, a.hostAnalyzer.Outcomes, a.CheckCondition, a.Title()) + if err != nil { + return nil, errors.Wrap(err, "failed to analyze OS version") } - var coll resultCollector - - for _, outcome := range hostAnalyzer.Outcomes { - result := &AnalyzeResult{Title: a.Title()} - - if outcome.Fail != nil { - if outcome.Fail.When == "" { - result.IsFail = true - result.Message = outcome.Fail.Message - result.URI = outcome.Fail.URI - - coll.push(result) - continue - } - - isMatch, err := compareHostTimeStatusToActual(outcome.Fail.When, timeInfo) - if err != nil { - return nil, errors.Wrapf(err, "failed to compare %s", outcome.Fail.When) - } - - if isMatch { - result.IsFail = true - result.Message = outcome.Fail.Message - result.URI = outcome.Fail.URI - - coll.push(result) - } - } else if outcome.Warn != nil { - if outcome.Warn.When == "" { - result.IsWarn = true - result.Message = outcome.Warn.Message - result.URI = outcome.Warn.URI - - coll.push(result) - continue - } - - isMatch, err := compareHostTimeStatusToActual(outcome.Warn.When, timeInfo) - if err != nil { - return nil, errors.Wrapf(err, "failed to compare %s", outcome.Warn.When) - } - - if isMatch { - result.IsWarn = true - result.Message = outcome.Warn.Message - result.URI = outcome.Warn.URI - - coll.push(result) - } - - } else if outcome.Pass != nil { - if outcome.Pass.When == "" { - result.IsPass = true - result.Message = outcome.Pass.Message - result.URI = outcome.Pass.URI - - coll.push(result) - continue - } - - isMatch, err := compareHostTimeStatusToActual(outcome.Pass.When, timeInfo) - if err != nil { - return nil, errors.Wrapf(err, "failed to compare %s", outcome.Pass.When) - } - - if isMatch { - result.IsPass = true - result.Message = outcome.Pass.Message - result.URI = outcome.Pass.URI - - coll.push(result) - } - - } - } + return results, nil - return coll.get(a.Title()), nil } func compareHostTimeStatusToActual(status string, timeInfo collect.TimeInfo) (res bool, err error) { @@ -160,3 +90,13 @@ func compareHostTimeStatusToActual(status string, timeInfo collect.TimeInfo) (re return false, fmt.Errorf("Unknown keyword: %s", parts[0]) } + +func (a *AnalyzeHostTime) CheckCondition(when string, data []byte) (bool, error) { + + var timeInfo collect.TimeInfo + if err := json.Unmarshal(data, &timeInfo); err != nil { + return false, fmt.Errorf("failed to unmarshal data into TimeInfo: %v", err) + } + + return compareHostTimeStatusToActual(when, timeInfo) +} diff --git a/pkg/analyze/host_time_test.go b/pkg/analyze/host_time_test.go index 3b1a5265a..88505460f 100644 --- a/pkg/analyze/host_time_test.go +++ b/pkg/analyze/host_time_test.go @@ -222,54 +222,6 @@ func TestAnalyzeHostTime(t *testing.T) { }, }, }, - { - name: "multiple issues", - timeInfo: &collect.TimeInfo{ - Timezone: "PST", - NTPSynchronized: false, - NTPActive: true, - }, - hostAnalyzer: &troubleshootv1beta2.TimeAnalyze{ - Outcomes: []*troubleshootv1beta2.Outcome{ - { - Fail: &troubleshootv1beta2.SingleOutcome{ - When: "timezone != UTC", - Message: "timezone is not set to UTC", - }, - }, - { - Warn: &troubleshootv1beta2.SingleOutcome{ - When: "ntp == unsynchronized+active", - Message: "System clock not yet synchronized", - }, - }, - { - Pass: &troubleshootv1beta2.SingleOutcome{ - When: "timezone == UTC", - Message: "Timezone is set to UTC", - }, - }, - { - Pass: &troubleshootv1beta2.SingleOutcome{ - When: "ntp == synchronized+active", - Message: "System clock is synchronized", - }, - }, - }, - }, - result: []*AnalyzeResult{ - { - Title: "Time", - IsFail: true, - Message: "timezone is not set to UTC", - }, - { - Title: "Time", - IsWarn: true, - Message: "System clock not yet synchronized", - }, - }, - }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { diff --git a/pkg/analyze/host_udpportstatus.go b/pkg/analyze/host_udpportstatus.go index f73066ced..274d64838 100644 --- a/pkg/analyze/host_udpportstatus.go +++ b/pkg/analyze/host_udpportstatus.go @@ -3,7 +3,6 @@ package analyzer import ( "encoding/json" "fmt" - "path" "github.com/pkg/errors" troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2" @@ -27,76 +26,40 @@ func (a *AnalyzeHostUDPPortStatus) IsExcluded() (bool, error) { func (a *AnalyzeHostUDPPortStatus) Analyze( getCollectedFileContents func(string) ([]byte, error), findFiles getChildCollectedFileContents, ) ([]*AnalyzeResult, error) { - hostAnalyzer := a.hostAnalyzer - - fullPath := path.Join("host-collectors/udpPortStatus", "udpPortStatus.json") - if hostAnalyzer.CollectorName != "" { - fullPath = path.Join("host-collectors/udpPortStatus", fmt.Sprintf("%s.json", hostAnalyzer.CollectorName)) + collectorName := a.hostAnalyzer.CollectorName + if collectorName == "" { + collectorName = "udpPortStatus" } - collected, err := getCollectedFileContents(fullPath) + const nodeBaseDir = "host-collectors/udpPortStatus" + localPath := fmt.Sprintf("%s/%s.json", nodeBaseDir, collectorName) + fileName := fmt.Sprintf("%s.json", collectorName) + + collectedContents, err := retrieveCollectedContents( + getCollectedFileContents, + localPath, + nodeBaseDir, + fileName, + ) if err != nil { - return nil, errors.Wrapf(err, "failed to read collected file name: %s", fullPath) + return []*AnalyzeResult{{Title: a.Title()}}, err } - actual := collect.NetworkStatusResult{} - if err := json.Unmarshal(collected, &actual); err != nil { - return nil, errors.Wrap(err, "failed to unmarshal collected") - } - - result := &AnalyzeResult{Title: a.Title()} - - for _, outcome := range hostAnalyzer.Outcomes { - - if outcome.Fail != nil { - if outcome.Fail.When == "" { - result.IsFail = true - result.Message = outcome.Fail.Message - result.URI = outcome.Fail.URI - - return []*AnalyzeResult{result}, nil - } - if string(actual.Status) == outcome.Fail.When { - result.IsFail = true - result.Message = outcome.Fail.Message - result.URI = outcome.Fail.URI - - return []*AnalyzeResult{result}, nil - } - } else if outcome.Warn != nil { - if outcome.Warn.When == "" { - result.IsWarn = true - result.Message = outcome.Warn.Message - result.URI = outcome.Warn.URI - - return []*AnalyzeResult{result}, nil - } - - if string(actual.Status) == outcome.Warn.When { - result.IsWarn = true - result.Message = outcome.Warn.Message - result.URI = outcome.Warn.URI + results, err := analyzeHostCollectorResults(collectedContents, a.hostAnalyzer.Outcomes, a.CheckCondition, a.Title()) + if err != nil { + return nil, errors.Wrap(err, "failed to analyze udp port status") + } - return []*AnalyzeResult{result}, nil - } - } else if outcome.Pass != nil { - if outcome.Pass.When == "" { - result.IsPass = true - result.Message = outcome.Pass.Message - result.URI = outcome.Pass.URI + return results, nil - return []*AnalyzeResult{result}, nil - } +} - if string(actual.Status) == outcome.Pass.When { - result.IsPass = true - result.Message = outcome.Pass.Message - result.URI = outcome.Pass.URI +func (a *AnalyzeHostUDPPortStatus) CheckCondition(when string, data []byte) (bool, error) { - return []*AnalyzeResult{result}, nil - } - } + var udpPort collect.NetworkStatusResult + if err := json.Unmarshal(data, &udpPort); err != nil { + return false, fmt.Errorf("failed to unmarshal data into NetworkStatusResult: %v", err) } - return []*AnalyzeResult{result}, nil + return string(udpPort.Status) == when, nil } diff --git a/pkg/collect/host_block_device.go b/pkg/collect/host_block_device.go index 86c1bee86..b2feccbb4 100644 --- a/pkg/collect/host_block_device.go +++ b/pkg/collect/host_block_device.go @@ -30,6 +30,7 @@ type BlockDeviceInfo struct { const lsblkColumns = "NAME,KNAME,PKNAME,TYPE,MAJ:MIN,SIZE,FSTYPE,MOUNTPOINT,SERIAL,RO,RM" const lsblkFormat = `NAME=%q KNAME=%q PKNAME=%q TYPE=%q MAJ:MIN="%d:%d" SIZE="%d" FSTYPE=%q MOUNTPOINT=%q SERIAL=%q RO="%d" RM="%d0"` const HostBlockDevicesPath = `host-collectors/system/block_devices.json` +const HostBlockDevicesFileName = `block_devices.json` type CollectHostBlockDevices struct { hostCollector *troubleshootv1beta2.HostBlockDevices diff --git a/pkg/collect/host_ipv4interfaces.go b/pkg/collect/host_ipv4interfaces.go index c2133dc4f..e417a8955 100644 --- a/pkg/collect/host_ipv4interfaces.go +++ b/pkg/collect/host_ipv4interfaces.go @@ -10,6 +10,7 @@ import ( ) const HostIPV4InterfacesPath = `host-collectors/system/ipv4Interfaces.json` +const HostIPV4FileName = `ipv4Interfaces.json` type CollectHostIPV4Interfaces struct { hostCollector *troubleshootv1beta2.IPV4Interfaces diff --git a/pkg/collect/host_kernel_configs.go b/pkg/collect/host_kernel_configs.go index d671ce01b..5260bc5e0 100644 --- a/pkg/collect/host_kernel_configs.go +++ b/pkg/collect/host_kernel_configs.go @@ -25,6 +25,7 @@ type CollectHostKernelConfigs struct { type KConfigs map[string]string const HostKernelConfigsPath = `host-collectors/system/kernel-configs.json` +const HostKernelConfigsFileName = `kernel-configs.json` const ( kConfigBuiltIn string = "y" diff --git a/pkg/collect/host_services.go b/pkg/collect/host_services.go index 033f9067f..01a29e53d 100644 --- a/pkg/collect/host_services.go +++ b/pkg/collect/host_services.go @@ -20,6 +20,7 @@ type ServiceInfo struct { const systemctlFormat = `%s %s %s %s` // this leaves off the description const HostServicesPath = `host-collectors/system/systemctl_services.json` +const HostServicesFileName = `systemctl_services.json` type CollectHostServices struct { hostCollector *troubleshootv1beta2.HostServices diff --git a/pkg/collect/host_time.go b/pkg/collect/host_time.go index 10c21545a..7ecff185e 100644 --- a/pkg/collect/host_time.go +++ b/pkg/collect/host_time.go @@ -21,6 +21,7 @@ type TimeInfo struct { } const HostTimePath = `host-collectors/system/time.json` +const HostTimeFileName = `time.json` type CollectHostTime struct { hostCollector *troubleshootv1beta2.HostTime