mirror of
https://github.com/replicatedhq/troubleshoot.git
synced 2026-08-27 00:37:20 +00:00
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:
co-authored by
Diamon Wiggins
parent
544a700062
commit
f0b8de68ae
@@ -10,11 +10,9 @@ import (
|
||||
|
||||
type collectedContent struct {
|
||||
NodeName string
|
||||
Data collectorData
|
||||
Data []byte
|
||||
}
|
||||
|
||||
type collectorData interface{}
|
||||
|
||||
type nodeNames struct {
|
||||
Nodes []string `json:"nodes"`
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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)
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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, ", "))
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
)
|
||||
|
||||
const HostIPV4InterfacesPath = `host-collectors/system/ipv4Interfaces.json`
|
||||
const HostIPV4FileName = `ipv4Interfaces.json`
|
||||
|
||||
type CollectHostIPV4Interfaces struct {
|
||||
hostCollector *troubleshootv1beta2.IPV4Interfaces
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -21,6 +21,7 @@ type TimeInfo struct {
|
||||
}
|
||||
|
||||
const HostTimePath = `host-collectors/system/time.json`
|
||||
const HostTimeFileName = `time.json`
|
||||
|
||||
type CollectHostTime struct {
|
||||
hostCollector *troubleshootv1beta2.HostTime
|
||||
|
||||
Reference in New Issue
Block a user