mirror of
https://github.com/replicatedhq/troubleshoot.git
synced 2026-02-14 10:19:54 +00:00
feat: adds new yamlCompare and jsonCompare analyzers (#598)
* feat: adds new yamlCompare analyzer * feat: adds new jsonCompare analyzer * outcome when for yamlCompare and jsonCompare
This commit is contained in:
@@ -339,6 +339,36 @@ func Analyze(analyzer *troubleshootv1beta2.Analyze, getFile getCollectedFileCont
|
|||||||
}
|
}
|
||||||
return results, nil
|
return results, nil
|
||||||
}
|
}
|
||||||
|
if analyzer.YamlCompare != nil {
|
||||||
|
isExcluded, err := isExcluded(analyzer.YamlCompare.Exclude)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if isExcluded {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
result, err := analyzeYamlCompare(analyzer.YamlCompare, getFile)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
result.Strict = analyzer.YamlCompare.Strict.BoolOrDefaultFalse()
|
||||||
|
return []*AnalyzeResult{result}, nil
|
||||||
|
}
|
||||||
|
if analyzer.JsonCompare != nil {
|
||||||
|
isExcluded, err := isExcluded(analyzer.JsonCompare.Exclude)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if isExcluded {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
result, err := analyzeJsonCompare(analyzer.JsonCompare, getFile)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
result.Strict = analyzer.JsonCompare.Strict.BoolOrDefaultFalse()
|
||||||
|
return []*AnalyzeResult{result}, nil
|
||||||
|
}
|
||||||
if analyzer.Postgres != nil {
|
if analyzer.Postgres != nil {
|
||||||
isExcluded, err := isExcluded(analyzer.Postgres.Exclude)
|
isExcluded, err := isExcluded(analyzer.Postgres.Exclude)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
112
pkg/analyze/json_compare.go
Normal file
112
pkg/analyze/json_compare.go
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
package analyzer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"path/filepath"
|
||||||
|
"reflect"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
|
||||||
|
iutils "github.com/replicatedhq/troubleshoot/pkg/interfaceutils"
|
||||||
|
)
|
||||||
|
|
||||||
|
func analyzeJsonCompare(analyzer *troubleshootv1beta2.JsonCompare, getCollectedFileContents func(string) ([]byte, error)) (*AnalyzeResult, error) {
|
||||||
|
fullPath := filepath.Join(analyzer.CollectorName, analyzer.FileName)
|
||||||
|
collected, err := getCollectedFileContents(fullPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "failed to read collected file name: %s", fullPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
var actual interface{}
|
||||||
|
err = json.Unmarshal(collected, &actual)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "failed to parse collected data as json")
|
||||||
|
}
|
||||||
|
|
||||||
|
if analyzer.Path != "" {
|
||||||
|
actual, err = iutils.GetAtPath(actual, analyzer.Path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "failed to get object at path: %s", analyzer.Path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var expected interface{}
|
||||||
|
err = json.Unmarshal([]byte(analyzer.Value), &expected)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "failed to parse expected value as json")
|
||||||
|
}
|
||||||
|
|
||||||
|
title := analyzer.CheckName
|
||||||
|
if title == "" {
|
||||||
|
title = analyzer.CollectorName
|
||||||
|
}
|
||||||
|
|
||||||
|
result := &AnalyzeResult{
|
||||||
|
Title: title,
|
||||||
|
IconKey: "kubernetes_text_analyze",
|
||||||
|
IconURI: "https://troubleshoot.sh/images/analyzer-icons/text-analyze.svg",
|
||||||
|
}
|
||||||
|
|
||||||
|
equal := reflect.DeepEqual(actual, expected)
|
||||||
|
|
||||||
|
for _, outcome := range analyzer.Outcomes {
|
||||||
|
if outcome.Fail != nil {
|
||||||
|
when := false
|
||||||
|
if outcome.Fail.When != "" {
|
||||||
|
when, err = strconv.ParseBool(outcome.Fail.When)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "failed to process when statement: %s", outcome.Fail.When)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if when == equal {
|
||||||
|
result.IsFail = true
|
||||||
|
result.Message = outcome.Fail.Message
|
||||||
|
result.URI = outcome.Fail.URI
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
} else if outcome.Warn != nil {
|
||||||
|
when := false
|
||||||
|
if outcome.Warn.When != "" {
|
||||||
|
when, err = strconv.ParseBool(outcome.Warn.When)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "failed to process when statement: %s", outcome.Warn.When)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if when == equal {
|
||||||
|
result.IsWarn = true
|
||||||
|
result.Message = outcome.Warn.Message
|
||||||
|
result.URI = outcome.Warn.URI
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
} else if outcome.Pass != nil {
|
||||||
|
when := true // default to passing when values are equal
|
||||||
|
if outcome.Pass.When != "" {
|
||||||
|
when, err = strconv.ParseBool(outcome.Pass.When)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "failed to process when statement: %s", outcome.Pass.When)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if when == equal {
|
||||||
|
result.IsPass = true
|
||||||
|
result.Message = outcome.Pass.Message
|
||||||
|
result.URI = outcome.Pass.URI
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &AnalyzeResult{
|
||||||
|
Title: title,
|
||||||
|
IconKey: "kubernetes_text_analyze",
|
||||||
|
IconURI: "https://troubleshoot.sh/images/analyzer-icons/text-analyze.svg",
|
||||||
|
IsFail: true,
|
||||||
|
Message: "Invalid analyzer",
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
563
pkg/analyze/json_compare_test.go
Normal file
563
pkg/analyze/json_compare_test.go
Normal file
@@ -0,0 +1,563 @@
|
|||||||
|
package analyzer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_jsonCompare(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
isError bool
|
||||||
|
analyzer troubleshootv1beta2.JsonCompare
|
||||||
|
expectResult AnalyzeResult
|
||||||
|
fileContents []byte
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "basic comparison",
|
||||||
|
analyzer: troubleshootv1beta2.JsonCompare{
|
||||||
|
Outcomes: []*troubleshootv1beta2.Outcome{
|
||||||
|
{
|
||||||
|
Pass: &troubleshootv1beta2.SingleOutcome{
|
||||||
|
Message: "pass",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Fail: &troubleshootv1beta2.SingleOutcome{
|
||||||
|
Message: "fail",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
CollectorName: "json-compare-1",
|
||||||
|
FileName: "json-compare-1.json",
|
||||||
|
Value: `{
|
||||||
|
"foo": "bar",
|
||||||
|
"stuff": {
|
||||||
|
"foo": "bar",
|
||||||
|
"bar": true
|
||||||
|
},
|
||||||
|
"morestuff": [
|
||||||
|
{
|
||||||
|
"foo": {
|
||||||
|
"bar": 123
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}`,
|
||||||
|
},
|
||||||
|
expectResult: AnalyzeResult{
|
||||||
|
IsPass: true,
|
||||||
|
IsWarn: false,
|
||||||
|
IsFail: false,
|
||||||
|
Title: "json-compare-1",
|
||||||
|
Message: "pass",
|
||||||
|
IconKey: "kubernetes_text_analyze",
|
||||||
|
IconURI: "https://troubleshoot.sh/images/analyzer-icons/text-analyze.svg",
|
||||||
|
},
|
||||||
|
fileContents: []byte(`{
|
||||||
|
"foo": "bar",
|
||||||
|
"stuff": {
|
||||||
|
"foo": "bar",
|
||||||
|
"bar": true
|
||||||
|
},
|
||||||
|
"morestuff": [
|
||||||
|
{
|
||||||
|
"foo": {
|
||||||
|
"bar": 123
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}`),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "basic comparison, but fail on match",
|
||||||
|
analyzer: troubleshootv1beta2.JsonCompare{
|
||||||
|
Outcomes: []*troubleshootv1beta2.Outcome{
|
||||||
|
{
|
||||||
|
Pass: &troubleshootv1beta2.SingleOutcome{
|
||||||
|
Message: "pass",
|
||||||
|
When: "false",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Fail: &troubleshootv1beta2.SingleOutcome{
|
||||||
|
Message: "fail",
|
||||||
|
When: "true",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
CollectorName: "json-compare-1-1",
|
||||||
|
FileName: "json-compare-1-1.json",
|
||||||
|
Value: `{
|
||||||
|
"foo": "bar",
|
||||||
|
"stuff": {
|
||||||
|
"foo": "bar",
|
||||||
|
"bar": true
|
||||||
|
},
|
||||||
|
"morestuff": [
|
||||||
|
{
|
||||||
|
"foo": {
|
||||||
|
"bar": 123
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}`,
|
||||||
|
},
|
||||||
|
expectResult: AnalyzeResult{
|
||||||
|
IsPass: false,
|
||||||
|
IsWarn: false,
|
||||||
|
IsFail: true,
|
||||||
|
Title: "json-compare-1-1",
|
||||||
|
Message: "fail",
|
||||||
|
IconKey: "kubernetes_text_analyze",
|
||||||
|
IconURI: "https://troubleshoot.sh/images/analyzer-icons/text-analyze.svg",
|
||||||
|
},
|
||||||
|
fileContents: []byte(`{
|
||||||
|
"foo": "bar",
|
||||||
|
"stuff": {
|
||||||
|
"foo": "bar",
|
||||||
|
"bar": true
|
||||||
|
},
|
||||||
|
"morestuff": [
|
||||||
|
{
|
||||||
|
"foo": {
|
||||||
|
"bar": 123
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}`),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "comparison using path 1",
|
||||||
|
analyzer: troubleshootv1beta2.JsonCompare{
|
||||||
|
Outcomes: []*troubleshootv1beta2.Outcome{
|
||||||
|
{
|
||||||
|
Pass: &troubleshootv1beta2.SingleOutcome{
|
||||||
|
Message: "pass",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Fail: &troubleshootv1beta2.SingleOutcome{
|
||||||
|
Message: "fail",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
CollectorName: "json-compare-2",
|
||||||
|
FileName: "json-compare-2.json",
|
||||||
|
Path: "morestuff",
|
||||||
|
Value: `[
|
||||||
|
{
|
||||||
|
"foo": {
|
||||||
|
"bar": 123
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]`,
|
||||||
|
},
|
||||||
|
expectResult: AnalyzeResult{
|
||||||
|
IsPass: true,
|
||||||
|
IsWarn: false,
|
||||||
|
IsFail: false,
|
||||||
|
Title: "json-compare-2",
|
||||||
|
Message: "pass",
|
||||||
|
IconKey: "kubernetes_text_analyze",
|
||||||
|
IconURI: "https://troubleshoot.sh/images/analyzer-icons/text-analyze.svg",
|
||||||
|
},
|
||||||
|
fileContents: []byte(`{
|
||||||
|
"foo": "bar",
|
||||||
|
"stuff": {
|
||||||
|
"foo": "bar",
|
||||||
|
"bar": true
|
||||||
|
},
|
||||||
|
"morestuff": [
|
||||||
|
{
|
||||||
|
"foo": {
|
||||||
|
"bar": 123
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}`),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "comparison using path 2",
|
||||||
|
analyzer: troubleshootv1beta2.JsonCompare{
|
||||||
|
Outcomes: []*troubleshootv1beta2.Outcome{
|
||||||
|
{
|
||||||
|
Pass: &troubleshootv1beta2.SingleOutcome{
|
||||||
|
Message: "pass",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Fail: &troubleshootv1beta2.SingleOutcome{
|
||||||
|
Message: "fail",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
CollectorName: "json-compare-3",
|
||||||
|
FileName: "json-compare-3.json",
|
||||||
|
Path: "morestuff.[0].foo.bar",
|
||||||
|
Value: `123`,
|
||||||
|
},
|
||||||
|
expectResult: AnalyzeResult{
|
||||||
|
IsPass: true,
|
||||||
|
IsWarn: false,
|
||||||
|
IsFail: false,
|
||||||
|
Title: "json-compare-3",
|
||||||
|
Message: "pass",
|
||||||
|
IconKey: "kubernetes_text_analyze",
|
||||||
|
IconURI: "https://troubleshoot.sh/images/analyzer-icons/text-analyze.svg",
|
||||||
|
},
|
||||||
|
fileContents: []byte(`{
|
||||||
|
"foo": "bar",
|
||||||
|
"stuff": {
|
||||||
|
"foo": "bar",
|
||||||
|
"bar": true
|
||||||
|
},
|
||||||
|
"morestuff": [
|
||||||
|
{
|
||||||
|
"foo": {
|
||||||
|
"bar": 123
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}`),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "comparison using path 2, but warn on match",
|
||||||
|
analyzer: troubleshootv1beta2.JsonCompare{
|
||||||
|
Outcomes: []*troubleshootv1beta2.Outcome{
|
||||||
|
{
|
||||||
|
Pass: &troubleshootv1beta2.SingleOutcome{
|
||||||
|
Message: "pass",
|
||||||
|
When: "false",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Warn: &troubleshootv1beta2.SingleOutcome{
|
||||||
|
Message: "warn",
|
||||||
|
When: "true",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
CollectorName: "json-compare-3-1",
|
||||||
|
FileName: "json-compare-3-1.json",
|
||||||
|
Path: "morestuff.[0].foo.bar",
|
||||||
|
Value: `123`,
|
||||||
|
},
|
||||||
|
expectResult: AnalyzeResult{
|
||||||
|
IsPass: false,
|
||||||
|
IsWarn: true,
|
||||||
|
IsFail: false,
|
||||||
|
Title: "json-compare-3-1",
|
||||||
|
Message: "warn",
|
||||||
|
IconKey: "kubernetes_text_analyze",
|
||||||
|
IconURI: "https://troubleshoot.sh/images/analyzer-icons/text-analyze.svg",
|
||||||
|
},
|
||||||
|
fileContents: []byte(`{
|
||||||
|
"foo": "bar",
|
||||||
|
"stuff": {
|
||||||
|
"foo": "bar",
|
||||||
|
"bar": true
|
||||||
|
},
|
||||||
|
"morestuff": [
|
||||||
|
{
|
||||||
|
"foo": {
|
||||||
|
"bar": 123
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}`),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "basic comparison fail",
|
||||||
|
analyzer: troubleshootv1beta2.JsonCompare{
|
||||||
|
Outcomes: []*troubleshootv1beta2.Outcome{
|
||||||
|
{
|
||||||
|
Pass: &troubleshootv1beta2.SingleOutcome{
|
||||||
|
Message: "pass",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Fail: &troubleshootv1beta2.SingleOutcome{
|
||||||
|
Message: "fail",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
CollectorName: "json-compare-4",
|
||||||
|
FileName: "json-compare-4.json",
|
||||||
|
Value: `{
|
||||||
|
"foo": "bar",
|
||||||
|
"stuff": {
|
||||||
|
"foo": "bar",
|
||||||
|
"bar": true
|
||||||
|
},
|
||||||
|
"morestuff": [
|
||||||
|
{
|
||||||
|
"foo": {
|
||||||
|
"bar": 123
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}`,
|
||||||
|
},
|
||||||
|
expectResult: AnalyzeResult{
|
||||||
|
IsPass: false,
|
||||||
|
IsWarn: false,
|
||||||
|
IsFail: true,
|
||||||
|
Title: "json-compare-4",
|
||||||
|
Message: "fail",
|
||||||
|
IconKey: "kubernetes_text_analyze",
|
||||||
|
IconURI: "https://troubleshoot.sh/images/analyzer-icons/text-analyze.svg",
|
||||||
|
},
|
||||||
|
fileContents: []byte(`{
|
||||||
|
"foo": "bar",
|
||||||
|
"stuff": {
|
||||||
|
"foo": "bar",
|
||||||
|
"bar": true
|
||||||
|
},
|
||||||
|
"otherstuff": [
|
||||||
|
{
|
||||||
|
"foo": {
|
||||||
|
"bar": 123
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}`),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "comparison using path fail 1",
|
||||||
|
analyzer: troubleshootv1beta2.JsonCompare{
|
||||||
|
Outcomes: []*troubleshootv1beta2.Outcome{
|
||||||
|
{
|
||||||
|
Pass: &troubleshootv1beta2.SingleOutcome{
|
||||||
|
Message: "pass",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Fail: &troubleshootv1beta2.SingleOutcome{
|
||||||
|
Message: "fail",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
CollectorName: "json-compare-5",
|
||||||
|
FileName: "json-compare-5.json",
|
||||||
|
Path: "morestuff",
|
||||||
|
Value: `[
|
||||||
|
{
|
||||||
|
"foo": {
|
||||||
|
"bar": 321
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]`,
|
||||||
|
},
|
||||||
|
expectResult: AnalyzeResult{
|
||||||
|
IsPass: false,
|
||||||
|
IsWarn: false,
|
||||||
|
IsFail: true,
|
||||||
|
Title: "json-compare-5",
|
||||||
|
Message: "fail",
|
||||||
|
IconKey: "kubernetes_text_analyze",
|
||||||
|
IconURI: "https://troubleshoot.sh/images/analyzer-icons/text-analyze.svg",
|
||||||
|
},
|
||||||
|
fileContents: []byte(`{
|
||||||
|
"foo": "bar",
|
||||||
|
"stuff": {
|
||||||
|
"foo": "bar",
|
||||||
|
"bar": true
|
||||||
|
},
|
||||||
|
"morestuff": [
|
||||||
|
{
|
||||||
|
"foo": {
|
||||||
|
"bar": 123
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}`),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "comparison using path, but pass when not matching",
|
||||||
|
analyzer: troubleshootv1beta2.JsonCompare{
|
||||||
|
Outcomes: []*troubleshootv1beta2.Outcome{
|
||||||
|
{
|
||||||
|
Pass: &troubleshootv1beta2.SingleOutcome{
|
||||||
|
Message: "pass",
|
||||||
|
When: "false",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Fail: &troubleshootv1beta2.SingleOutcome{
|
||||||
|
Message: "fail",
|
||||||
|
When: "true",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
CollectorName: "json-compare-5-1",
|
||||||
|
FileName: "json-compare-5-1.json",
|
||||||
|
Path: "morestuff",
|
||||||
|
Value: `[
|
||||||
|
{
|
||||||
|
"foo": {
|
||||||
|
"bar": 321
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]`,
|
||||||
|
},
|
||||||
|
expectResult: AnalyzeResult{
|
||||||
|
IsPass: true,
|
||||||
|
IsWarn: false,
|
||||||
|
IsFail: false,
|
||||||
|
Title: "json-compare-5-1",
|
||||||
|
Message: "pass",
|
||||||
|
IconKey: "kubernetes_text_analyze",
|
||||||
|
IconURI: "https://troubleshoot.sh/images/analyzer-icons/text-analyze.svg",
|
||||||
|
},
|
||||||
|
fileContents: []byte(`{
|
||||||
|
"foo": "bar",
|
||||||
|
"stuff": {
|
||||||
|
"foo": "bar",
|
||||||
|
"bar": true
|
||||||
|
},
|
||||||
|
"morestuff": [
|
||||||
|
{
|
||||||
|
"foo": {
|
||||||
|
"bar": 123
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}`),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "basic comparison warn",
|
||||||
|
analyzer: troubleshootv1beta2.JsonCompare{
|
||||||
|
Outcomes: []*troubleshootv1beta2.Outcome{
|
||||||
|
{
|
||||||
|
Pass: &troubleshootv1beta2.SingleOutcome{
|
||||||
|
Message: "pass",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Warn: &troubleshootv1beta2.SingleOutcome{
|
||||||
|
Message: "warn",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
CollectorName: "json-compare-6",
|
||||||
|
FileName: "json-compare-6.json",
|
||||||
|
Value: `{
|
||||||
|
"foo": "bar",
|
||||||
|
"stuff": {
|
||||||
|
"foo": "bar",
|
||||||
|
"bar": true
|
||||||
|
},
|
||||||
|
"morestuff": [
|
||||||
|
{
|
||||||
|
"foo": {
|
||||||
|
"bar": 123
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}`,
|
||||||
|
},
|
||||||
|
expectResult: AnalyzeResult{
|
||||||
|
IsPass: false,
|
||||||
|
IsWarn: true,
|
||||||
|
IsFail: false,
|
||||||
|
Title: "json-compare-6",
|
||||||
|
Message: "warn",
|
||||||
|
IconKey: "kubernetes_text_analyze",
|
||||||
|
IconURI: "https://troubleshoot.sh/images/analyzer-icons/text-analyze.svg",
|
||||||
|
},
|
||||||
|
fileContents: []byte(`{
|
||||||
|
"foo": "bar",
|
||||||
|
"stuff": {
|
||||||
|
"foo": "bar",
|
||||||
|
"bar": true
|
||||||
|
},
|
||||||
|
"otherstuff": [
|
||||||
|
{
|
||||||
|
"foo": {
|
||||||
|
"bar": 123
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}`),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "invalid json error",
|
||||||
|
isError: true,
|
||||||
|
analyzer: troubleshootv1beta2.JsonCompare{
|
||||||
|
Outcomes: []*troubleshootv1beta2.Outcome{
|
||||||
|
{
|
||||||
|
Pass: &troubleshootv1beta2.SingleOutcome{
|
||||||
|
Message: "pass",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Fail: &troubleshootv1beta2.SingleOutcome{
|
||||||
|
Message: "fail",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
CollectorName: "json-compare-7",
|
||||||
|
FileName: "json-compare-7.json",
|
||||||
|
Path: "morestuff",
|
||||||
|
Value: `[
|
||||||
|
{
|
||||||
|
"foo": {
|
||||||
|
"bar": 123
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]`,
|
||||||
|
},
|
||||||
|
fileContents: []byte(`{ "this: - is-invalid: json }`),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "no json error",
|
||||||
|
isError: true,
|
||||||
|
analyzer: troubleshootv1beta2.JsonCompare{
|
||||||
|
Outcomes: []*troubleshootv1beta2.Outcome{
|
||||||
|
{
|
||||||
|
Pass: &troubleshootv1beta2.SingleOutcome{
|
||||||
|
Message: "pass",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Fail: &troubleshootv1beta2.SingleOutcome{
|
||||||
|
Message: "fail",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
CollectorName: "json-compare-8",
|
||||||
|
FileName: "json-compare-8.json",
|
||||||
|
Path: "morestuff",
|
||||||
|
Value: `[
|
||||||
|
{
|
||||||
|
"foo": {
|
||||||
|
"bar": 123
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]`,
|
||||||
|
},
|
||||||
|
fileContents: []byte(``),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
req := require.New(t)
|
||||||
|
|
||||||
|
getCollectedFileContents := func(n string) ([]byte, error) {
|
||||||
|
return test.fileContents, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
actual, err := analyzeJsonCompare(&test.analyzer, getCollectedFileContents)
|
||||||
|
if !test.isError {
|
||||||
|
req.NoError(err)
|
||||||
|
req.Equal(test.expectResult, *actual)
|
||||||
|
} else {
|
||||||
|
req.Error(err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
109
pkg/analyze/yaml_compare.go
Normal file
109
pkg/analyze/yaml_compare.go
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
package analyzer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"path/filepath"
|
||||||
|
"reflect"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
|
||||||
|
iutils "github.com/replicatedhq/troubleshoot/pkg/interfaceutils"
|
||||||
|
"gopkg.in/yaml.v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
func analyzeYamlCompare(analyzer *troubleshootv1beta2.YamlCompare, getCollectedFileContents func(string) ([]byte, error)) (*AnalyzeResult, error) {
|
||||||
|
fullPath := filepath.Join(analyzer.CollectorName, analyzer.FileName)
|
||||||
|
collected, err := getCollectedFileContents(fullPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "failed to read collected file name: %s", fullPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
var actual interface{}
|
||||||
|
err = yaml.Unmarshal(collected, &actual)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "failed to parse collected data as yaml doc")
|
||||||
|
}
|
||||||
|
|
||||||
|
if analyzer.Path != "" {
|
||||||
|
actual, err = iutils.GetAtPath(actual, analyzer.Path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "failed to get object at path: %s", analyzer.Path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var expected interface{}
|
||||||
|
err = yaml.Unmarshal([]byte(analyzer.Value), &expected)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "failed to parse expected value as yaml doc")
|
||||||
|
}
|
||||||
|
|
||||||
|
title := analyzer.CheckName
|
||||||
|
if title == "" {
|
||||||
|
title = analyzer.CollectorName
|
||||||
|
}
|
||||||
|
|
||||||
|
result := &AnalyzeResult{
|
||||||
|
Title: title,
|
||||||
|
IconKey: "kubernetes_text_analyze",
|
||||||
|
IconURI: "https://troubleshoot.sh/images/analyzer-icons/text-analyze.svg",
|
||||||
|
}
|
||||||
|
|
||||||
|
equal := reflect.DeepEqual(actual, expected)
|
||||||
|
|
||||||
|
for _, outcome := range analyzer.Outcomes {
|
||||||
|
if outcome.Fail != nil {
|
||||||
|
when := false
|
||||||
|
if outcome.Fail.When != "" {
|
||||||
|
when, err = strconv.ParseBool(outcome.Fail.When)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "failed to process when statement: %s", outcome.Fail.When)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if when == equal {
|
||||||
|
result.IsFail = true
|
||||||
|
result.Message = outcome.Fail.Message
|
||||||
|
result.URI = outcome.Fail.URI
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
} else if outcome.Warn != nil {
|
||||||
|
when := false
|
||||||
|
if outcome.Warn.When != "" {
|
||||||
|
when, err = strconv.ParseBool(outcome.Warn.When)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "failed to process when statement: %s", outcome.Warn.When)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if when == equal {
|
||||||
|
result.IsWarn = true
|
||||||
|
result.Message = outcome.Warn.Message
|
||||||
|
result.URI = outcome.Warn.URI
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
} else if outcome.Pass != nil {
|
||||||
|
when := true // default to passing when values are equal
|
||||||
|
if outcome.Pass.When != "" {
|
||||||
|
when, err = strconv.ParseBool(outcome.Pass.When)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "failed to process when statement: %s", outcome.Pass.When)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if when == equal {
|
||||||
|
result.IsPass = true
|
||||||
|
result.Message = outcome.Pass.Message
|
||||||
|
result.URI = outcome.Pass.URI
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &AnalyzeResult{
|
||||||
|
Title: title,
|
||||||
|
IconKey: "kubernetes_text_analyze",
|
||||||
|
IconURI: "https://troubleshoot.sh/images/analyzer-icons/text-analyze.svg",
|
||||||
|
IsFail: true,
|
||||||
|
Message: "Invalid analyzer",
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
452
pkg/analyze/yaml_compare_test.go
Normal file
452
pkg/analyze/yaml_compare_test.go
Normal file
@@ -0,0 +1,452 @@
|
|||||||
|
package analyzer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_yamlCompare(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
isError bool
|
||||||
|
analyzer troubleshootv1beta2.YamlCompare
|
||||||
|
expectResult AnalyzeResult
|
||||||
|
fileContents []byte
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "basic comparison",
|
||||||
|
analyzer: troubleshootv1beta2.YamlCompare{
|
||||||
|
Outcomes: []*troubleshootv1beta2.Outcome{
|
||||||
|
{
|
||||||
|
Pass: &troubleshootv1beta2.SingleOutcome{
|
||||||
|
Message: "pass",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Fail: &troubleshootv1beta2.SingleOutcome{
|
||||||
|
Message: "fail",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
CollectorName: "yaml-compare-1",
|
||||||
|
FileName: "yaml-compare-1.yaml",
|
||||||
|
Value: `foo: bar
|
||||||
|
stuff:
|
||||||
|
foo: bar
|
||||||
|
bar: foo
|
||||||
|
morestuff:
|
||||||
|
- foo:
|
||||||
|
bar: baz`,
|
||||||
|
},
|
||||||
|
expectResult: AnalyzeResult{
|
||||||
|
IsPass: true,
|
||||||
|
IsWarn: false,
|
||||||
|
IsFail: false,
|
||||||
|
Title: "yaml-compare-1",
|
||||||
|
Message: "pass",
|
||||||
|
IconKey: "kubernetes_text_analyze",
|
||||||
|
IconURI: "https://troubleshoot.sh/images/analyzer-icons/text-analyze.svg",
|
||||||
|
},
|
||||||
|
fileContents: []byte(`foo: bar
|
||||||
|
stuff:
|
||||||
|
foo: bar
|
||||||
|
bar: foo
|
||||||
|
morestuff:
|
||||||
|
- foo:
|
||||||
|
bar: baz`),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "basic comparison, but fail on match",
|
||||||
|
analyzer: troubleshootv1beta2.YamlCompare{
|
||||||
|
Outcomes: []*troubleshootv1beta2.Outcome{
|
||||||
|
{
|
||||||
|
Pass: &troubleshootv1beta2.SingleOutcome{
|
||||||
|
Message: "pass",
|
||||||
|
When: "false",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Fail: &troubleshootv1beta2.SingleOutcome{
|
||||||
|
Message: "fail",
|
||||||
|
When: "true",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
CollectorName: "yaml-compare-1-1",
|
||||||
|
FileName: "yaml-compare-1-1.yaml",
|
||||||
|
Value: `foo: bar
|
||||||
|
stuff:
|
||||||
|
foo: bar
|
||||||
|
bar: foo
|
||||||
|
morestuff:
|
||||||
|
- foo:
|
||||||
|
bar: baz`,
|
||||||
|
},
|
||||||
|
expectResult: AnalyzeResult{
|
||||||
|
IsPass: false,
|
||||||
|
IsWarn: false,
|
||||||
|
IsFail: true,
|
||||||
|
Title: "yaml-compare-1-1",
|
||||||
|
Message: "fail",
|
||||||
|
IconKey: "kubernetes_text_analyze",
|
||||||
|
IconURI: "https://troubleshoot.sh/images/analyzer-icons/text-analyze.svg",
|
||||||
|
},
|
||||||
|
fileContents: []byte(`foo: bar
|
||||||
|
stuff:
|
||||||
|
foo: bar
|
||||||
|
bar: foo
|
||||||
|
morestuff:
|
||||||
|
- foo:
|
||||||
|
bar: baz`),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "comparison using path 1",
|
||||||
|
analyzer: troubleshootv1beta2.YamlCompare{
|
||||||
|
Outcomes: []*troubleshootv1beta2.Outcome{
|
||||||
|
{
|
||||||
|
Pass: &troubleshootv1beta2.SingleOutcome{
|
||||||
|
Message: "pass",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Fail: &troubleshootv1beta2.SingleOutcome{
|
||||||
|
Message: "fail",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
CollectorName: "yaml-compare-2",
|
||||||
|
FileName: "yaml-compare-2.yaml",
|
||||||
|
Path: "morestuff",
|
||||||
|
Value: `- foo:
|
||||||
|
bar: baz`,
|
||||||
|
},
|
||||||
|
expectResult: AnalyzeResult{
|
||||||
|
IsPass: true,
|
||||||
|
IsWarn: false,
|
||||||
|
IsFail: false,
|
||||||
|
Title: "yaml-compare-2",
|
||||||
|
Message: "pass",
|
||||||
|
IconKey: "kubernetes_text_analyze",
|
||||||
|
IconURI: "https://troubleshoot.sh/images/analyzer-icons/text-analyze.svg",
|
||||||
|
},
|
||||||
|
fileContents: []byte(`foo: bar
|
||||||
|
stuff:
|
||||||
|
foo: bar
|
||||||
|
bar: foo
|
||||||
|
morestuff:
|
||||||
|
- foo:
|
||||||
|
bar: baz`),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "comparison using path, but warn when matching",
|
||||||
|
analyzer: troubleshootv1beta2.YamlCompare{
|
||||||
|
Outcomes: []*troubleshootv1beta2.Outcome{
|
||||||
|
{
|
||||||
|
Pass: &troubleshootv1beta2.SingleOutcome{
|
||||||
|
Message: "pass",
|
||||||
|
When: "false",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Warn: &troubleshootv1beta2.SingleOutcome{
|
||||||
|
Message: "warn",
|
||||||
|
When: "true",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
CollectorName: "yaml-compare-2-1",
|
||||||
|
FileName: "yaml-compare-2-1.yaml",
|
||||||
|
Path: "morestuff",
|
||||||
|
Value: `- foo:
|
||||||
|
bar: baz`,
|
||||||
|
},
|
||||||
|
expectResult: AnalyzeResult{
|
||||||
|
IsPass: false,
|
||||||
|
IsWarn: true,
|
||||||
|
IsFail: false,
|
||||||
|
Title: "yaml-compare-2-1",
|
||||||
|
Message: "warn",
|
||||||
|
IconKey: "kubernetes_text_analyze",
|
||||||
|
IconURI: "https://troubleshoot.sh/images/analyzer-icons/text-analyze.svg",
|
||||||
|
},
|
||||||
|
fileContents: []byte(`foo: bar
|
||||||
|
stuff:
|
||||||
|
foo: bar
|
||||||
|
bar: foo
|
||||||
|
morestuff:
|
||||||
|
- foo:
|
||||||
|
bar: baz`),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "comparison using path 2",
|
||||||
|
analyzer: troubleshootv1beta2.YamlCompare{
|
||||||
|
Outcomes: []*troubleshootv1beta2.Outcome{
|
||||||
|
{
|
||||||
|
Pass: &troubleshootv1beta2.SingleOutcome{
|
||||||
|
Message: "pass",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Fail: &troubleshootv1beta2.SingleOutcome{
|
||||||
|
Message: "fail",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
CollectorName: "yaml-compare-3",
|
||||||
|
FileName: "yaml-compare-3.yaml",
|
||||||
|
Path: "morestuff.[0].foo",
|
||||||
|
Value: `bar: baz`,
|
||||||
|
},
|
||||||
|
expectResult: AnalyzeResult{
|
||||||
|
IsPass: true,
|
||||||
|
IsWarn: false,
|
||||||
|
IsFail: false,
|
||||||
|
Title: "yaml-compare-3",
|
||||||
|
Message: "pass",
|
||||||
|
IconKey: "kubernetes_text_analyze",
|
||||||
|
IconURI: "https://troubleshoot.sh/images/analyzer-icons/text-analyze.svg",
|
||||||
|
},
|
||||||
|
fileContents: []byte(`foo: bar
|
||||||
|
stuff:
|
||||||
|
foo: bar
|
||||||
|
bar: foo
|
||||||
|
morestuff:
|
||||||
|
- foo:
|
||||||
|
bar: baz`),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "basic comparison fail",
|
||||||
|
analyzer: troubleshootv1beta2.YamlCompare{
|
||||||
|
Outcomes: []*troubleshootv1beta2.Outcome{
|
||||||
|
{
|
||||||
|
Pass: &troubleshootv1beta2.SingleOutcome{
|
||||||
|
Message: "pass",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Fail: &troubleshootv1beta2.SingleOutcome{
|
||||||
|
Message: "fail",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
CollectorName: "yaml-compare-4",
|
||||||
|
FileName: "yaml-compare-4.yaml",
|
||||||
|
Value: `foo: bar
|
||||||
|
stuff:
|
||||||
|
foo: bar
|
||||||
|
bar: foo
|
||||||
|
morestuff:
|
||||||
|
- foo:
|
||||||
|
bar: baz`,
|
||||||
|
},
|
||||||
|
expectResult: AnalyzeResult{
|
||||||
|
IsPass: false,
|
||||||
|
IsWarn: false,
|
||||||
|
IsFail: true,
|
||||||
|
Title: "yaml-compare-4",
|
||||||
|
Message: "fail",
|
||||||
|
IconKey: "kubernetes_text_analyze",
|
||||||
|
IconURI: "https://troubleshoot.sh/images/analyzer-icons/text-analyze.svg",
|
||||||
|
},
|
||||||
|
fileContents: []byte(`foo: bar
|
||||||
|
stuff:
|
||||||
|
foo: bar
|
||||||
|
bar: foo
|
||||||
|
otherstuff:
|
||||||
|
- foo:
|
||||||
|
bar: baz`),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "basic comparison pass when not matching",
|
||||||
|
analyzer: troubleshootv1beta2.YamlCompare{
|
||||||
|
Outcomes: []*troubleshootv1beta2.Outcome{
|
||||||
|
{
|
||||||
|
Pass: &troubleshootv1beta2.SingleOutcome{
|
||||||
|
Message: "pass",
|
||||||
|
When: "false",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Fail: &troubleshootv1beta2.SingleOutcome{
|
||||||
|
Message: "fail",
|
||||||
|
When: "true",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
CollectorName: "yaml-compare-4-1",
|
||||||
|
FileName: "yaml-compare-4-1.yaml",
|
||||||
|
Value: `foo: bar
|
||||||
|
stuff:
|
||||||
|
foo: bar
|
||||||
|
bar: foo
|
||||||
|
morestuff:
|
||||||
|
- foo:
|
||||||
|
bar: baz`,
|
||||||
|
},
|
||||||
|
expectResult: AnalyzeResult{
|
||||||
|
IsPass: true,
|
||||||
|
IsWarn: false,
|
||||||
|
IsFail: false,
|
||||||
|
Title: "yaml-compare-4-1",
|
||||||
|
Message: "pass",
|
||||||
|
IconKey: "kubernetes_text_analyze",
|
||||||
|
IconURI: "https://troubleshoot.sh/images/analyzer-icons/text-analyze.svg",
|
||||||
|
},
|
||||||
|
fileContents: []byte(`foo: bar
|
||||||
|
stuff:
|
||||||
|
foo: bar
|
||||||
|
bar: foo
|
||||||
|
otherstuff:
|
||||||
|
- foo:
|
||||||
|
bar: baz`),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "comparison using path fail 1",
|
||||||
|
analyzer: troubleshootv1beta2.YamlCompare{
|
||||||
|
Outcomes: []*troubleshootv1beta2.Outcome{
|
||||||
|
{
|
||||||
|
Pass: &troubleshootv1beta2.SingleOutcome{
|
||||||
|
Message: "pass",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Fail: &troubleshootv1beta2.SingleOutcome{
|
||||||
|
Message: "fail",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
CollectorName: "yaml-compare-5",
|
||||||
|
FileName: "yaml-compare-5.yaml",
|
||||||
|
Path: "morestuff",
|
||||||
|
Value: `- bar:
|
||||||
|
foo: baz`,
|
||||||
|
},
|
||||||
|
expectResult: AnalyzeResult{
|
||||||
|
IsPass: false,
|
||||||
|
IsWarn: false,
|
||||||
|
IsFail: true,
|
||||||
|
Title: "yaml-compare-5",
|
||||||
|
Message: "fail",
|
||||||
|
IconKey: "kubernetes_text_analyze",
|
||||||
|
IconURI: "https://troubleshoot.sh/images/analyzer-icons/text-analyze.svg",
|
||||||
|
},
|
||||||
|
fileContents: []byte(`foo: bar
|
||||||
|
stuff:
|
||||||
|
foo: bar
|
||||||
|
bar: foo
|
||||||
|
morestuff:
|
||||||
|
- foo:
|
||||||
|
bar: baz`),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "basic comparison warn",
|
||||||
|
analyzer: troubleshootv1beta2.YamlCompare{
|
||||||
|
Outcomes: []*troubleshootv1beta2.Outcome{
|
||||||
|
{
|
||||||
|
Pass: &troubleshootv1beta2.SingleOutcome{
|
||||||
|
Message: "pass",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Warn: &troubleshootv1beta2.SingleOutcome{
|
||||||
|
Message: "warn",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
CollectorName: "yaml-compare-6",
|
||||||
|
FileName: "yaml-compare-6.yaml",
|
||||||
|
Value: `foo: bar
|
||||||
|
stuff:
|
||||||
|
foo: bar
|
||||||
|
bar: foo
|
||||||
|
morestuff:
|
||||||
|
- foo:
|
||||||
|
bar: baz`,
|
||||||
|
},
|
||||||
|
expectResult: AnalyzeResult{
|
||||||
|
IsPass: false,
|
||||||
|
IsWarn: true,
|
||||||
|
IsFail: false,
|
||||||
|
Title: "yaml-compare-6",
|
||||||
|
Message: "warn",
|
||||||
|
IconKey: "kubernetes_text_analyze",
|
||||||
|
IconURI: "https://troubleshoot.sh/images/analyzer-icons/text-analyze.svg",
|
||||||
|
},
|
||||||
|
fileContents: []byte(`foo: bar
|
||||||
|
stuff:
|
||||||
|
foo: bar
|
||||||
|
bar: foo
|
||||||
|
otherstuff:
|
||||||
|
- foo:
|
||||||
|
bar: baz`),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "invalid yaml error",
|
||||||
|
isError: true,
|
||||||
|
analyzer: troubleshootv1beta2.YamlCompare{
|
||||||
|
Outcomes: []*troubleshootv1beta2.Outcome{
|
||||||
|
{
|
||||||
|
Pass: &troubleshootv1beta2.SingleOutcome{
|
||||||
|
Message: "pass",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Fail: &troubleshootv1beta2.SingleOutcome{
|
||||||
|
Message: "fail",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
CollectorName: "yaml-compare-7",
|
||||||
|
FileName: "yaml-compare-7.yaml",
|
||||||
|
Path: "morestuff",
|
||||||
|
Value: `- foo:
|
||||||
|
bar: baz`,
|
||||||
|
},
|
||||||
|
fileContents: []byte(`{ "this: - is-invalid: yaml }`),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "no yaml error",
|
||||||
|
isError: true,
|
||||||
|
analyzer: troubleshootv1beta2.YamlCompare{
|
||||||
|
Outcomes: []*troubleshootv1beta2.Outcome{
|
||||||
|
{
|
||||||
|
Pass: &troubleshootv1beta2.SingleOutcome{
|
||||||
|
Message: "pass",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Fail: &troubleshootv1beta2.SingleOutcome{
|
||||||
|
Message: "fail",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
CollectorName: "yaml-compare-8",
|
||||||
|
FileName: "yaml-compare-8.yaml",
|
||||||
|
Path: "morestuff",
|
||||||
|
Value: `- foo:
|
||||||
|
bar: baz`,
|
||||||
|
},
|
||||||
|
fileContents: []byte(``),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
req := require.New(t)
|
||||||
|
|
||||||
|
getCollectedFileContents := func(n string) ([]byte, error) {
|
||||||
|
return test.fileContents, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
actual, err := analyzeYamlCompare(&test.analyzer, getCollectedFileContents)
|
||||||
|
if !test.isError {
|
||||||
|
req.NoError(err)
|
||||||
|
req.Equal(test.expectResult, *actual)
|
||||||
|
} else {
|
||||||
|
req.Error(err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -131,6 +131,24 @@ type TextAnalyze struct {
|
|||||||
Outcomes []*Outcome `json:"outcomes" yaml:"outcomes"`
|
Outcomes []*Outcome `json:"outcomes" yaml:"outcomes"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type YamlCompare struct {
|
||||||
|
AnalyzeMeta `json:",inline" yaml:",inline"`
|
||||||
|
CollectorName string `json:"collectorName,omitempty" yaml:"collectorName,omitempty"`
|
||||||
|
FileName string `json:"fileName,omitempty" yaml:"fileName,omitempty"`
|
||||||
|
Path string `json:"path,omitempty" yaml:"path,omitempty"`
|
||||||
|
Value string `json:"value,omitempty" yaml:"value,omitempty"`
|
||||||
|
Outcomes []*Outcome `json:"outcomes" yaml:"outcomes"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type JsonCompare struct {
|
||||||
|
AnalyzeMeta `json:",inline" yaml:",inline"`
|
||||||
|
CollectorName string `json:"collectorName,omitempty" yaml:"collectorName,omitempty"`
|
||||||
|
FileName string `json:"fileName,omitempty" yaml:"fileName,omitempty"`
|
||||||
|
Path string `json:"path,omitempty" yaml:"path,omitempty"`
|
||||||
|
Value string `json:"value,omitempty" yaml:"value,omitempty"`
|
||||||
|
Outcomes []*Outcome `json:"outcomes" yaml:"outcomes"`
|
||||||
|
}
|
||||||
|
|
||||||
type DatabaseAnalyze struct {
|
type DatabaseAnalyze struct {
|
||||||
AnalyzeMeta `json:",inline" yaml:",inline"`
|
AnalyzeMeta `json:",inline" yaml:",inline"`
|
||||||
Outcomes []*Outcome `json:"outcomes" yaml:"outcomes"`
|
Outcomes []*Outcome `json:"outcomes" yaml:"outcomes"`
|
||||||
@@ -175,9 +193,10 @@ type SysctlAnalyze struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type AnalyzeMeta struct {
|
type AnalyzeMeta struct {
|
||||||
CheckName string `json:"checkName,omitempty" yaml:"checkName,omitempty"`
|
CheckName string `json:"checkName,omitempty" yaml:"checkName,omitempty"`
|
||||||
Exclude *multitype.BoolOrString `json:"exclude,omitempty" yaml:"exclude,omitempty"`
|
Exclude *multitype.BoolOrString `json:"exclude,omitempty" yaml:"exclude,omitempty"`
|
||||||
Strict *multitype.BoolOrString `json:"strict,omitempty" yaml:"strict,omitempty"`
|
Strict *multitype.BoolOrString `json:"strict,omitempty" yaml:"strict,omitempty"`
|
||||||
|
Annotations map[string]string `json:"annotations,omitempty" yaml:"annotations,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Analyze struct {
|
type Analyze struct {
|
||||||
@@ -197,6 +216,8 @@ type Analyze struct {
|
|||||||
Distribution *Distribution `json:"distribution,omitempty" yaml:"distribution,omitempty"`
|
Distribution *Distribution `json:"distribution,omitempty" yaml:"distribution,omitempty"`
|
||||||
NodeResources *NodeResources `json:"nodeResources,omitempty" yaml:"nodeResources,omitempty"`
|
NodeResources *NodeResources `json:"nodeResources,omitempty" yaml:"nodeResources,omitempty"`
|
||||||
TextAnalyze *TextAnalyze `json:"textAnalyze,omitempty" yaml:"textAnalyze,omitempty"`
|
TextAnalyze *TextAnalyze `json:"textAnalyze,omitempty" yaml:"textAnalyze,omitempty"`
|
||||||
|
YamlCompare *YamlCompare `json:"yamlCompare,omitempty" yaml:"yamlCompare,omitempty"`
|
||||||
|
JsonCompare *JsonCompare `json:"jsonCompare,omitempty" yaml:"jsonCompare,omitempty"`
|
||||||
Postgres *DatabaseAnalyze `json:"postgres,omitempty" yaml:"postgres,omitempty"`
|
Postgres *DatabaseAnalyze `json:"postgres,omitempty" yaml:"postgres,omitempty"`
|
||||||
Mysql *DatabaseAnalyze `json:"mysql,omitempty" yaml:"mysql,omitempty"`
|
Mysql *DatabaseAnalyze `json:"mysql,omitempty" yaml:"mysql,omitempty"`
|
||||||
Redis *DatabaseAnalyze `json:"redis,omitempty" yaml:"redis,omitempty"`
|
Redis *DatabaseAnalyze `json:"redis,omitempty" yaml:"redis,omitempty"`
|
||||||
|
|||||||
@@ -134,6 +134,16 @@ func (in *Analyze) DeepCopyInto(out *Analyze) {
|
|||||||
*out = new(TextAnalyze)
|
*out = new(TextAnalyze)
|
||||||
(*in).DeepCopyInto(*out)
|
(*in).DeepCopyInto(*out)
|
||||||
}
|
}
|
||||||
|
if in.YamlCompare != nil {
|
||||||
|
in, out := &in.YamlCompare, &out.YamlCompare
|
||||||
|
*out = new(YamlCompare)
|
||||||
|
(*in).DeepCopyInto(*out)
|
||||||
|
}
|
||||||
|
if in.JsonCompare != nil {
|
||||||
|
in, out := &in.JsonCompare, &out.JsonCompare
|
||||||
|
*out = new(JsonCompare)
|
||||||
|
(*in).DeepCopyInto(*out)
|
||||||
|
}
|
||||||
if in.Postgres != nil {
|
if in.Postgres != nil {
|
||||||
in, out := &in.Postgres, &out.Postgres
|
in, out := &in.Postgres, &out.Postgres
|
||||||
*out = new(DatabaseAnalyze)
|
*out = new(DatabaseAnalyze)
|
||||||
@@ -226,6 +236,13 @@ func (in *AnalyzeMeta) DeepCopyInto(out *AnalyzeMeta) {
|
|||||||
*out = new(multitype.BoolOrString)
|
*out = new(multitype.BoolOrString)
|
||||||
**out = **in
|
**out = **in
|
||||||
}
|
}
|
||||||
|
if in.Annotations != nil {
|
||||||
|
in, out := &in.Annotations, &out.Annotations
|
||||||
|
*out = make(map[string]string, len(*in))
|
||||||
|
for key, val := range *in {
|
||||||
|
(*out)[key] = val
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AnalyzeMeta.
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AnalyzeMeta.
|
||||||
@@ -2276,6 +2293,33 @@ func (in *JobStatus) DeepCopy() *JobStatus {
|
|||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *JsonCompare) DeepCopyInto(out *JsonCompare) {
|
||||||
|
*out = *in
|
||||||
|
in.AnalyzeMeta.DeepCopyInto(&out.AnalyzeMeta)
|
||||||
|
if in.Outcomes != nil {
|
||||||
|
in, out := &in.Outcomes, &out.Outcomes
|
||||||
|
*out = make([]*Outcome, len(*in))
|
||||||
|
for i := range *in {
|
||||||
|
if (*in)[i] != nil {
|
||||||
|
in, out := &(*in)[i], &(*out)[i]
|
||||||
|
*out = new(Outcome)
|
||||||
|
(*in).DeepCopyInto(*out)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new JsonCompare.
|
||||||
|
func (in *JsonCompare) DeepCopy() *JsonCompare {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(JsonCompare)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
func (in *KernelModulesAnalyze) DeepCopyInto(out *KernelModulesAnalyze) {
|
func (in *KernelModulesAnalyze) DeepCopyInto(out *KernelModulesAnalyze) {
|
||||||
*out = *in
|
*out = *in
|
||||||
@@ -4079,3 +4123,30 @@ func (in *WeaveReportAnalyze) DeepCopy() *WeaveReportAnalyze {
|
|||||||
in.DeepCopyInto(out)
|
in.DeepCopyInto(out)
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *YamlCompare) DeepCopyInto(out *YamlCompare) {
|
||||||
|
*out = *in
|
||||||
|
in.AnalyzeMeta.DeepCopyInto(&out.AnalyzeMeta)
|
||||||
|
if in.Outcomes != nil {
|
||||||
|
in, out := &in.Outcomes, &out.Outcomes
|
||||||
|
*out = make([]*Outcome, len(*in))
|
||||||
|
for i := range *in {
|
||||||
|
if (*in)[i] != nil {
|
||||||
|
in, out := &(*in)[i], &(*out)[i]
|
||||||
|
*out = new(Outcome)
|
||||||
|
(*in).DeepCopyInto(*out)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new YamlCompare.
|
||||||
|
func (in *YamlCompare) DeepCopy() *YamlCompare {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(YamlCompare)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|||||||
54
pkg/interfaceutils/interfaceutils.go
Normal file
54
pkg/interfaceutils/interfaceutils.go
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
package interfaceutils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetAtPath(input interface{}, path string) (interface{}, error) {
|
||||||
|
parts := strings.SplitN(path, ".", 2)
|
||||||
|
key := parts[0]
|
||||||
|
if isArrayIndex(key) {
|
||||||
|
i, err := getArrayIndexValue(key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "failed to get index value of %s", key)
|
||||||
|
}
|
||||||
|
obj, ok := input.([]interface{})
|
||||||
|
if !ok {
|
||||||
|
return nil, errors.New(fmt.Sprintf("input is not an array: %+v", input))
|
||||||
|
}
|
||||||
|
input = obj[i]
|
||||||
|
} else {
|
||||||
|
switch t := input.(type) {
|
||||||
|
case map[interface{}]interface{}:
|
||||||
|
input = input.(map[interface{}]interface{})[key]
|
||||||
|
case map[string]interface{}:
|
||||||
|
input = input.(map[string]interface{})[key]
|
||||||
|
default:
|
||||||
|
return nil, errors.New(fmt.Sprintf("input is not a map, but rather a %v: %+v", t, input))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(parts) > 1 {
|
||||||
|
return GetAtPath(input, parts[1])
|
||||||
|
}
|
||||||
|
|
||||||
|
return input, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func isArrayIndex(key string) bool {
|
||||||
|
return strings.HasPrefix(key, "[") && strings.HasSuffix(key, "]")
|
||||||
|
}
|
||||||
|
|
||||||
|
func getArrayIndexValue(key string) (int, error) {
|
||||||
|
key = strings.TrimPrefix(key, "[")
|
||||||
|
key = strings.TrimSuffix(key, "]")
|
||||||
|
i, err := strconv.Atoi(key)
|
||||||
|
if err != nil {
|
||||||
|
return -1, errors.Wrapf(err, "failed to parse index %s", key)
|
||||||
|
}
|
||||||
|
return i, nil
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user