Skip to content

Commit 3665d25

Browse files
jtuchschererlaverya
andauthoredMar 6, 2025··
Http comperators (#1753)
* Allowing more comperators for the http analyzer * test * Update pkg/analyze/host_http.go Co-authored-by: Andrew Lavery <[email protected]> --------- Co-authored-by: Andrew Lavery <[email protected]>
1 parent c770c32 commit 3665d25

File tree

4 files changed

+214
-14
lines changed

4 files changed

+214
-14
lines changed
 

‎go.mod

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ require (
99
github.com/ahmetalpbalkan/go-cursor v0.0.0-20131010032410-8136607ea412
1010
github.com/apparentlymart/go-cidr v1.1.0
1111
github.com/blang/semver/v4 v4.0.0
12+
github.com/casbin/govaluate v1.3.0
1213
github.com/cilium/ebpf v0.17.3
1314
github.com/containerd/cgroups/v3 v3.0.5
1415
github.com/containers/image/v5 v5.34.1

‎go.sum

+2
Original file line numberDiff line numberDiff line change
@@ -707,6 +707,8 @@ github.com/bshuster-repo/logrus-logstash-hook v1.0.0 h1:e+C0SB5R1pu//O4MQ3f9cFuP
707707
github.com/bshuster-repo/logrus-logstash-hook v1.0.0/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk=
708708
github.com/c9s/goprocinfo v0.0.0-20170724085704-0010a05ce49f h1:tRk+aBit+q3oqnj/1mF5HHhP2yxJM2lSa0afOJxQ3nE=
709709
github.com/c9s/goprocinfo v0.0.0-20170724085704-0010a05ce49f/go.mod h1:uEyr4WpAH4hio6LFriaPkL938XnrvLpNPmQHBdrmbIE=
710+
github.com/casbin/govaluate v1.3.0 h1:VA0eSY0M2lA86dYd5kPPuNZMUD9QkWnOCnavGrw9myc=
711+
github.com/casbin/govaluate v1.3.0/go.mod h1:G/UnbIjZk/0uMNaLwZZmFQrR72tYRZWQkO70si/iR7A=
710712
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
711713
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
712714
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=

‎pkg/analyze/host_http.go

+19-14
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@ package analyzer
33
import (
44
"encoding/json"
55
"fmt"
6-
"strconv"
76
"strings"
87

8+
"github.com/casbin/govaluate"
99
"github.com/pkg/errors"
1010
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
1111
"github.com/replicatedhq/troubleshoot/pkg/collect"
@@ -64,28 +64,33 @@ func compareHostHTTPConditionalToActual(conditional string, result *httpResult)
6464
return result.Error != nil, nil
6565
}
6666

67-
parts := strings.Split(conditional, " ")
68-
if len(parts) != 3 {
69-
return false, fmt.Errorf("Failed to parse conditional: got %d parts", len(parts))
70-
}
67+
conditional = strings.ReplaceAll(conditional, " = ", " == ")
68+
conditional = strings.ReplaceAll(conditional, " === ", " == ")
7169

72-
if parts[0] != "statusCode" {
73-
return false, errors.New(`Conditional must begin with keyword "statusCode"`)
70+
expression, err := govaluate.NewEvaluableExpression(conditional)
71+
if err != nil {
72+
return false, errors.Wrap(err, "failed to create evaluable expression")
7473
}
7574

76-
if parts[1] != "=" && parts[1] != "==" && parts[1] != "===" {
77-
return false, errors.New(`Only supported operator is "=="`)
75+
if result.Response == nil {
76+
return false, nil
7877
}
7978

80-
i, err := strconv.Atoi(parts[2])
79+
parameters := make(map[string]interface{}, 8)
80+
parameters["statusCode"] = result.Response.Status
81+
82+
comparisonResult, err := expression.Evaluate(parameters)
83+
8184
if err != nil {
82-
return false, err
85+
return false, errors.Wrap(err, "failed to evaluate expression")
8386
}
8487

85-
if result.Response == nil {
86-
return false, err
88+
boolResult, ok := comparisonResult.(bool)
89+
if !ok {
90+
return false, fmt.Errorf("expression did not evaluate to a boolean value")
8791
}
88-
return result.Response.Status == i, nil
92+
93+
return boolResult, nil
8994
}
9095

9196
func analyzeHTTPResult(analyzer *troubleshootv1beta2.HTTPAnalyze, fileName string, getCollectedFileContents getCollectedFileContents, title string) ([]*AnalyzeResult, error) {

‎pkg/analyze/host_http_test.go

+192
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,124 @@ func TestAnalyzeHostHTTP(t *testing.T) {
113113
},
114114
},
115115
},
116+
{
117+
name: "invalid compare operator",
118+
expectErr: true,
119+
httpResult: &httpResult{
120+
Response: &collect.HTTPResponse{
121+
Status: 200,
122+
},
123+
},
124+
hostAnalyzer: &troubleshootv1beta2.HTTPAnalyze{
125+
CollectorName: "collector",
126+
Outcomes: []*troubleshootv1beta2.Outcome{
127+
{
128+
Pass: &troubleshootv1beta2.SingleOutcome{
129+
When: "statusCode #$ 200",
130+
Message: "passed",
131+
},
132+
},
133+
{
134+
Warn: &troubleshootv1beta2.SingleOutcome{
135+
Message: "default",
136+
},
137+
},
138+
},
139+
},
140+
},
141+
{
142+
name: "!= compare operator",
143+
httpResult: &httpResult{
144+
Response: &collect.HTTPResponse{
145+
Status: 201,
146+
},
147+
},
148+
hostAnalyzer: &troubleshootv1beta2.HTTPAnalyze{
149+
CollectorName: "collector",
150+
Outcomes: []*troubleshootv1beta2.Outcome{
151+
{
152+
Pass: &troubleshootv1beta2.SingleOutcome{
153+
When: "statusCode != 200",
154+
Message: "passed",
155+
},
156+
},
157+
{
158+
Warn: &troubleshootv1beta2.SingleOutcome{
159+
Message: "default",
160+
},
161+
},
162+
},
163+
},
164+
result: []*AnalyzeResult{
165+
{
166+
Title: "HTTP Request",
167+
IsPass: true,
168+
Message: "passed",
169+
},
170+
},
171+
},
172+
{
173+
name: "Looking for 2xx status codes",
174+
httpResult: &httpResult{
175+
Response: &collect.HTTPResponse{
176+
Status: 201,
177+
},
178+
},
179+
hostAnalyzer: &troubleshootv1beta2.HTTPAnalyze{
180+
CollectorName: "collector",
181+
Outcomes: []*troubleshootv1beta2.Outcome{
182+
{
183+
Fail: &troubleshootv1beta2.SingleOutcome{
184+
When: "statusCode >= 300 || statusCode < 200",
185+
Message: "expected 2xx status code",
186+
},
187+
},
188+
{
189+
Pass: &troubleshootv1beta2.SingleOutcome{
190+
Message: "default",
191+
},
192+
},
193+
},
194+
},
195+
result: []*AnalyzeResult{
196+
{
197+
Title: "HTTP Request",
198+
IsPass: true,
199+
Message: "default",
200+
},
201+
},
202+
},
203+
{
204+
name: "Looking for 2xx status codes does not match",
205+
httpResult: &httpResult{
206+
Response: &collect.HTTPResponse{
207+
Status: 300,
208+
},
209+
},
210+
hostAnalyzer: &troubleshootv1beta2.HTTPAnalyze{
211+
CollectorName: "collector",
212+
Outcomes: []*troubleshootv1beta2.Outcome{
213+
{
214+
Fail: &troubleshootv1beta2.SingleOutcome{
215+
When: "statusCode >= 300 || statusCode < 200",
216+
Message: "expected 2xx status code",
217+
},
218+
},
219+
{
220+
Pass: &troubleshootv1beta2.SingleOutcome{
221+
Message: "default",
222+
},
223+
},
224+
},
225+
},
226+
result: []*AnalyzeResult{
227+
{
228+
Title: "HTTP Request",
229+
IsFail: true,
230+
Message: "expected 2xx status code",
231+
},
232+
},
233+
},
116234
}
117235
for _, test := range tests {
118236
t.Run(test.name, func(t *testing.T) {
@@ -137,3 +255,77 @@ func TestAnalyzeHostHTTP(t *testing.T) {
137255
})
138256
}
139257
}
258+
259+
func TestAnalyzeHostHTTPHTTPCodesAndCompareOperators(t *testing.T) {
260+
httpResult := &httpResult{
261+
Response: &collect.HTTPResponse{
262+
Status: 200,
263+
},
264+
}
265+
266+
tests := []struct {
267+
name string
268+
}{
269+
{
270+
name: "statusCode == 200",
271+
},
272+
{
273+
name: "statusCode === 200",
274+
},
275+
{
276+
name: "statusCode = 200",
277+
},
278+
{
279+
name: "statusCode != 201",
280+
},
281+
{
282+
name: "statusCode >= 200",
283+
},
284+
{
285+
name: "statusCode > 199",
286+
},
287+
{
288+
name: "statusCode <= 200",
289+
},
290+
{
291+
name: "statusCode <= 201",
292+
},
293+
{
294+
name: "statusCode < 201",
295+
},
296+
{
297+
name: "statusCode < 201 && statusCode > 199",
298+
},
299+
{
300+
name: "statusCode < 201 || statusCode > 199 && statusCode == 200",
301+
},
302+
}
303+
for _, test := range tests {
304+
t.Run(test.name, func(t *testing.T) {
305+
hostAnalyzer := &troubleshootv1beta2.HTTPAnalyze{
306+
CollectorName: "registry",
307+
Outcomes: []*troubleshootv1beta2.Outcome{
308+
{
309+
Pass: &troubleshootv1beta2.SingleOutcome{
310+
When: test.name},
311+
},
312+
},
313+
}
314+
315+
req := require.New(t)
316+
b, err := json.Marshal(httpResult)
317+
if err != nil {
318+
t.Fatal(err)
319+
}
320+
321+
getCollectedFileContents := func(filename string) ([]byte, error) {
322+
return b, nil
323+
}
324+
325+
result, err := (&AnalyzeHostHTTP{hostAnalyzer}).Analyze(getCollectedFileContents, nil)
326+
req.NoError(err)
327+
req.Len(result, 1)
328+
req.Equal(true, result[0].IsPass)
329+
})
330+
}
331+
}

0 commit comments

Comments
 (0)
Please sign in to comment.