feat: multiple nodes analyzers (#1667)

* implement refactor for multiple node analyzers

---------

Co-authored-by: Diamon Wiggins <38189728+diamonwiggins@users.noreply.github.com>
This commit is contained in:
Gerard Nguyen
2024-11-04 14:17:39 +11:00
committed by GitHub
co-authored by Diamon Wiggins
parent 544a700062
commit f0b8de68ae
29 changed files with 872 additions and 1149 deletions
+1 -3
View File
@@ -10,11 +10,9 @@ import (
type collectedContent struct {
NodeName string
Data collectorData
Data []byte
}
type collectorData interface{}
type nodeNames struct {
Nodes []string `json:"nodes"`
}
+2 -2
View File
@@ -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 {
+29 -34
View File
@@ -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",
+23 -78
View File
@@ -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
result := AnalyzeResult{Title: a.Title()}
contents, err := getCollectedFileContents(collect.HostBlockDevicesPath)
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")
results, err := analyzeHostCollectorResults(collectedContents, a.hostAnalyzer.Outcomes, a.CheckCondition, a.Title())
if err != nil {
return nil, errors.Wrap(err, "failed to analyze block devices")
}
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
}
}
}
return []*AnalyzeResult{&result}, nil
return results, nil
}
// <regexp> <op> <count>
@@ -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)
}
+24 -42
View File
@@ -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)
const nodeBaseDir = "host-collectors/certificate"
localPath := fmt.Sprintf("%s/%s", nodeBaseDir, collectorName)
fileName := fmt.Sprintf("%s.json", collectorName)
collectedContents, err := retrieveCollectedContents(
getCollectedFileContents,
localPath,
nodeBaseDir,
fileName,
)
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
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
coll.push(result)
}
}
return []*AnalyzeResult{{Title: a.Title()}}, err
}
return coll.get(a.Title()), nil
results, err := analyzeHostCollectorResults(collectedContents, a.hostAnalyzer.Outcomes, a.CheckCondition, a.Title())
if err != nil {
return nil, errors.Wrap(err, "failed to analyze certificate")
}
return results, nil
}
func (a *AnalyzeHostCertificate) CheckCondition(when string, data []byte) (bool, error) {
return strings.TrimSpace(string(data)) == when, nil
}
+29 -85
View File
@@ -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")
collectorName := a.hostAnalyzer.CollectorName
if collectorName == "" {
collectorName = "diskUsage"
}
contents, err := getCollectedFileContents(name)
const nodeBaseDir = "host-collectors/diskUsage"
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 get collected file %s", name)
return []*AnalyzeResult{{Title: a.Title()}}, err
}
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)
results, err := analyzeHostCollectorResults(collectedContents, a.hostAnalyzer.Outcomes, a.CheckCondition, a.Title())
if err != nil {
return nil, errors.Wrap(err, "failed to analyze disk usage")
}
result := AnalyzeResult{
Title: a.Title(),
}
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
}
}
}
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)
}
+131 -103
View File
@@ -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", 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
}
}
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
}
return nil, errors.Wrapf(err, "failed to get collected file %s", localPath)
}
}
return []*AnalyzeResult{result}, nil
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 filesystem performance for %s", currentTitle)
}
if result != nil {
results = append(results, result...)
}
}
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
}
+32 -6
View File
@@ -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)
}
+30 -70
View File
@@ -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)
}
actual := collect.NetworkStatusResult{}
if err := json.Unmarshal(collected, &actual); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal collected")
return []*AnalyzeResult{{Title: a.Title()}}, 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
}
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
coll.push(result)
continue
}
if string(actual.Status) == outcome.Pass.When {
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 HTTP Load Balancer")
}
return coll.get(a.Title()), nil
return results, nil
}
func (a *AnalyzeHostHTTPLoadBalancer) CheckCondition(when string, data []byte) (bool, error) {
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 false, nil
}
+112
View File
@@ -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)
})
}
}
+22 -82
View File
@@ -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
result := AnalyzeResult{Title: a.Title()}
contents, err := getCollectedFileContents(collect.HostIPV4InterfacesPath)
collectedContents, err := retrieveCollectedContents(
getCollectedFileContents,
collect.HostIPV4InterfacesPath,
collect.NodeInfoBaseDir,
collect.HostIPV4FileName,
)
if err != nil {
return nil, errors.Wrap(err, "failed to get collected file")
return []*AnalyzeResult{&result}, err
}
var ipv4Interfaces []net.Interface
if err := json.Unmarshal(contents, &ipv4Interfaces); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal ipv4Interfaces")
results, err := analyzeHostCollectorResults(collectedContents, a.hostAnalyzer.Outcomes, a.CheckCondition, a.Title())
if err != nil {
return nil, errors.Wrap(err, "failed to analyze IPv4 interfaces")
}
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)
}
}
}
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)
}
+41 -15
View File
@@ -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, ", "))
}
+2 -7
View File
@@ -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)
}
+2 -7
View File
@@ -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)
}
+22 -82
View File
@@ -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
result := AnalyzeResult{Title: a.Title()}
contents, err := getCollectedFileContents(collect.HostServicesPath)
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")
results, err := analyzeHostCollectorResults(collectedContents, a.hostAnalyzer.Outcomes, a.CheckCondition, a.Title())
if err != nil {
return nil, errors.Wrap(err, "failed to analyze host services")
}
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)
}
}
}
return coll.get(a.Title()), nil
return results, nil
}
// <service> <op> <state>
@@ -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)
}
+26 -49
View File
@@ -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")
collectorName := a.hostAnalyzer.CollectorName
if collectorName == "" {
collectorName = "result"
}
contents, err := getCollectedFileContents(name)
const nodeBaseDir = "host-collectors/subnetAvailable"
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.Wrap(err, "failed to get collected file")
return []*AnalyzeResult{{Title: a.Title()}}, err
}
isSubnetAvailable := &collect.SubnetAvailableResult{}
if err := json.Unmarshal(contents, isSubnetAvailable); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal subnetAvailable result")
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")
}
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(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
}
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
}
+107
View File
@@ -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)
})
}
}
+95 -79
View File
@@ -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)
localPath := fmt.Sprintf("%s/%s-packages.json", collect.NodeInfoBaseDir, collectorName)
packagesFileName := fmt.Sprintf("%s-packages.json", collectorName)
collectedContents, err := retrieveCollectedContents(
getCollectedFileContents,
localPath,
collect.NodeInfoBaseDir,
packagesFileName,
)
if err != nil {
return nil, errors.Wrap(err, "failed to get collected file")
return []*AnalyzeResult{{Title: a.Title()}}, err
}
var info collect.SystemPackagesInfo
if err := json.Unmarshal(contents, &info); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal system packages info")
}
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
}
+27 -72
View File
@@ -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)
}
actual := collect.NetworkStatusResult{}
if err := json.Unmarshal(collected, &actual); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal collected")
return []*AnalyzeResult{{Title: a.Title()}}, 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
}
}
results, err := analyzeHostCollectorResults(collectedContents, a.hostAnalyzer.Outcomes, a.CheckCondition, a.Title())
if err != nil {
return nil, errors.Wrap(err, "failed to analyze tcp connect")
}
return coll.get(a.Title()), nil
return results, nil
}
func (a *AnalyzeHostTCPConnect) CheckCondition(when string, data []byte) (bool, error) {
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 string(tcpConnect.Status) == when, nil
}
+30 -71
View File
@@ -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)
}
actual := collect.NetworkStatusResult{}
if err := json.Unmarshal(collected, &actual); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal collected")
return []*AnalyzeResult{{Title: a.Title()}}, 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
}
}
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 coll.get(a.Title()), nil
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)
}
if string(tcpLoadBalancer.Status) == when {
return true, nil
}
return false, nil
}
+29 -66
View File
@@ -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)
}
actual := collect.NetworkStatusResult{}
if err := json.Unmarshal(collected, &actual); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal collected")
return []*AnalyzeResult{{Title: a.Title()}}, err
}
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
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
}
if string(actual.Status) == 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 tcp port status")
}
return []*AnalyzeResult{result}, nil
return results, nil
}
func (a *AnalyzeHostTCPPortStatus) CheckCondition(when string, data []byte) (bool, error) {
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 string(tcpPortStatus.Status) == when, nil
}
+22 -82
View File
@@ -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
result := AnalyzeResult{Title: a.Title()}
contents, err := getCollectedFileContents(collect.HostTimePath)
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
return results, nil
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 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)
}
-48
View File
@@ -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) {
+29 -66
View File
@@ -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)
}
actual := collect.NetworkStatusResult{}
if err := json.Unmarshal(collected, &actual); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal collected")
return []*AnalyzeResult{{Title: a.Title()}}, err
}
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
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
}
if string(actual.Status) == 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 udp port status")
}
return []*AnalyzeResult{result}, nil
return results, nil
}
func (a *AnalyzeHostUDPPortStatus) CheckCondition(when string, data []byte) (bool, error) {
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 string(udpPort.Status) == when, nil
}
+1
View File
@@ -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
+1
View File
@@ -10,6 +10,7 @@ import (
)
const HostIPV4InterfacesPath = `host-collectors/system/ipv4Interfaces.json`
const HostIPV4FileName = `ipv4Interfaces.json`
type CollectHostIPV4Interfaces struct {
hostCollector *troubleshootv1beta2.IPV4Interfaces
+1
View File
@@ -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"
+1
View File
@@ -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
+1
View File
@@ -21,6 +21,7 @@ type TimeInfo struct {
}
const HostTimePath = `host-collectors/system/time.json`
const HostTimeFileName = `time.json`
type CollectHostTime struct {
hostCollector *troubleshootv1beta2.HostTime