Skip to content

Commit b02d12f

Browse files
JSONPath support for json compare analyzer (#1244)
This adds JSONPath support to the json compare analyzer using k8s.io/client-go/util/jsonpath implementation. To preserve backwards compatibility a new attribute, `JsonPath, is added to the compare analyzer as opposed to changing how `Path` works. Only one should be set, but preference is given to `Path`, again to maintain backwards compatibility. As a convience for users, if the result of running the JSONPath expression returns a single value, that value is unwrapped from its enclosing array and used as the comparison with `Value`. This isn't strictly compatible with how JSONPath works (all results are wrapped in an array), but it's easier for end users who are expecting a single result from their JSONPath expression.
1 parent 24822b7 commit b02d12f

9 files changed

+182
-0
lines changed

config/crds/troubleshoot.sh_analyzers.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -732,6 +732,8 @@ spec:
732732
type: BoolString
733733
fileName:
734734
type: string
735+
jsonPath:
736+
type: string
735737
outcomes:
736738
items:
737739
properties:

config/crds/troubleshoot.sh_preflights.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -732,6 +732,8 @@ spec:
732732
type: BoolString
733733
fileName:
734734
type: string
735+
jsonPath:
736+
type: string
735737
outcomes:
736738
items:
737739
properties:

config/crds/troubleshoot.sh_supportbundles.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -763,6 +763,8 @@ spec:
763763
type: BoolString
764764
fileName:
765765
type: string
766+
jsonPath:
767+
type: string
766768
outcomes:
767769
items:
768770
properties:

pkg/analyze/json_compare.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package analyzer
22

33
import (
4+
"bytes"
45
"encoding/json"
56
"path/filepath"
67
"reflect"
@@ -9,6 +10,7 @@ import (
910
"github.com/pkg/errors"
1011
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
1112
iutils "github.com/replicatedhq/troubleshoot/pkg/interfaceutils"
13+
"k8s.io/client-go/util/jsonpath"
1214
)
1315

1416
type AnalyzeJsonCompare struct {
@@ -55,6 +57,32 @@ func (a *AnalyzeJsonCompare) analyzeJsonCompare(analyzer *troubleshootv1beta2.Js
5557
if err != nil {
5658
return nil, errors.Wrapf(err, "failed to get object at path: %s", analyzer.Path)
5759
}
60+
} else if analyzer.JsonPath != "" {
61+
jsp := jsonpath.New(analyzer.CheckName)
62+
jsp.AllowMissingKeys(true).EnableJSONOutput(true)
63+
err = jsp.Parse(analyzer.JsonPath)
64+
if err != nil {
65+
return nil, errors.Wrapf(err, "failed to parse jsonpath: %s", analyzer.JsonPath)
66+
}
67+
68+
var data bytes.Buffer
69+
err = jsp.Execute(&data, actual)
70+
if err != nil {
71+
return nil, errors.Wrap(err, "failed to execute jsonpath")
72+
}
73+
74+
err = json.NewDecoder(&data).Decode(&actual)
75+
if err != nil {
76+
return nil, errors.Wrap(err, "failed to decode jsonpath result")
77+
}
78+
79+
// If we get back a single result in a slice unwrap it.
80+
// Technically this doesn't strictly follow jsonpath, but it makes
81+
// things easier downstream. Basically we don't want to require
82+
// users to wrap a single result with [].
83+
if a, ok := actual.([]interface{}); ok && len(a) == 1 {
84+
actual = a[0]
85+
}
5886
}
5987

6088
var expected interface{}

pkg/analyze/json_compare_test.go

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -541,6 +541,144 @@ func Test_jsonCompare(t *testing.T) {
541541
},
542542
fileContents: []byte(``),
543543
},
544+
{
545+
name: "jsonpath comparison",
546+
analyzer: troubleshootv1beta2.JsonCompare{
547+
Outcomes: []*troubleshootv1beta2.Outcome{
548+
{
549+
Pass: &troubleshootv1beta2.SingleOutcome{
550+
Message: "pass",
551+
},
552+
},
553+
{
554+
Fail: &troubleshootv1beta2.SingleOutcome{
555+
Message: "fail",
556+
},
557+
},
558+
},
559+
CollectorName: "jsonpath-compare-1",
560+
FileName: "jsonpath-compare-1.json",
561+
JsonPath: "{$.morestuff[0]}",
562+
Value: `{
563+
"foo": {
564+
"bar": 123
565+
}
566+
}`,
567+
},
568+
expectResult: AnalyzeResult{
569+
IsPass: true,
570+
IsWarn: false,
571+
IsFail: false,
572+
Title: "jsonpath-compare-1",
573+
Message: "pass",
574+
IconKey: "kubernetes_text_analyze",
575+
IconURI: "https://troubleshoot.sh/images/analyzer-icons/text-analyze.svg",
576+
},
577+
fileContents: []byte(`{
578+
"foo": "bar",
579+
"stuff": {
580+
"foo": "bar",
581+
"bar": true
582+
},
583+
"morestuff": [
584+
{
585+
"foo": {
586+
"bar": 123
587+
}
588+
}
589+
]
590+
}`),
591+
},
592+
{
593+
name: "jsonpath comparison, but fail on match",
594+
analyzer: troubleshootv1beta2.JsonCompare{
595+
Outcomes: []*troubleshootv1beta2.Outcome{
596+
{
597+
Pass: &troubleshootv1beta2.SingleOutcome{
598+
Message: "pass",
599+
When: "false",
600+
},
601+
},
602+
{
603+
Fail: &troubleshootv1beta2.SingleOutcome{
604+
Message: "fail",
605+
When: "true",
606+
},
607+
},
608+
},
609+
CollectorName: "jsonpath-compare-1-1",
610+
FileName: "jsonpath-compare-1-1.json",
611+
JsonPath: "{$.morestuff[0].foo.bar}",
612+
Value: `123`,
613+
},
614+
expectResult: AnalyzeResult{
615+
IsPass: false,
616+
IsWarn: false,
617+
IsFail: true,
618+
Title: "jsonpath-compare-1-1",
619+
Message: "fail",
620+
IconKey: "kubernetes_text_analyze",
621+
IconURI: "https://troubleshoot.sh/images/analyzer-icons/text-analyze.svg",
622+
},
623+
fileContents: []byte(`{
624+
"foo": "bar",
625+
"stuff": {
626+
"foo": "bar",
627+
"bar": true
628+
},
629+
"morestuff": [
630+
{
631+
"foo": {
632+
"bar": 123
633+
}
634+
}
635+
]
636+
}`),
637+
},
638+
{
639+
name: "jsonpath comparison, multiple values",
640+
analyzer: troubleshootv1beta2.JsonCompare{
641+
Outcomes: []*troubleshootv1beta2.Outcome{
642+
{
643+
Pass: &troubleshootv1beta2.SingleOutcome{
644+
Message: "pass",
645+
},
646+
},
647+
{
648+
Fail: &troubleshootv1beta2.SingleOutcome{
649+
Message: "fail",
650+
},
651+
},
652+
},
653+
CollectorName: "jsonpath-compare-2",
654+
FileName: "jsonpath-compare-2.json",
655+
JsonPath: "{$..bar}",
656+
Value: `[true, 123]`,
657+
},
658+
expectResult: AnalyzeResult{
659+
IsPass: true,
660+
IsWarn: false,
661+
IsFail: false,
662+
Title: "jsonpath-compare-2",
663+
Message: "pass",
664+
IconKey: "kubernetes_text_analyze",
665+
IconURI: "https://troubleshoot.sh/images/analyzer-icons/text-analyze.svg",
666+
},
667+
fileContents: []byte(`{
668+
"foo": "bar",
669+
"stuff": {
670+
"foo": "bar",
671+
"bar": true
672+
},
673+
"morestuff": [
674+
{
675+
"foo": {
676+
"bar": 123
677+
}
678+
}
679+
]
680+
}`),
681+
},
544682
}
545683

546684
for _, test := range tests {

pkg/apis/troubleshoot/v1beta2/analyzer_shared.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,7 @@ type JsonCompare struct {
161161
CollectorName string `json:"collectorName,omitempty" yaml:"collectorName,omitempty"`
162162
FileName string `json:"fileName,omitempty" yaml:"fileName,omitempty"`
163163
Path string `json:"path,omitempty" yaml:"path,omitempty"`
164+
JsonPath string `json:"jsonPath,omitempty" yaml:"jsonPath,omitempty"`
164165
Value string `json:"value,omitempty" yaml:"value,omitempty"`
165166
Outcomes []*Outcome `json:"outcomes" yaml:"outcomes"`
166167
}

schemas/analyzer-troubleshoot-v1beta2.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1092,6 +1092,9 @@
10921092
"fileName": {
10931093
"type": "string"
10941094
},
1095+
"jsonPath": {
1096+
"type": "string"
1097+
},
10951098
"outcomes": {
10961099
"type": "array",
10971100
"items": {

schemas/preflight-troubleshoot-v1beta2.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1092,6 +1092,9 @@
10921092
"fileName": {
10931093
"type": "string"
10941094
},
1095+
"jsonPath": {
1096+
"type": "string"
1097+
},
10951098
"outcomes": {
10961099
"type": "array",
10971100
"items": {

schemas/supportbundle-troubleshoot-v1beta2.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1138,6 +1138,9 @@
11381138
"fileName": {
11391139
"type": "string"
11401140
},
1141+
"jsonPath": {
1142+
"type": "string"
1143+
},
11411144
"outcomes": {
11421145
"type": "array",
11431146
"items": {

0 commit comments

Comments
 (0)