diff --git a/CHANGELOG.next.asciidoc b/CHANGELOG.next.asciidoc index d0d796d87ec9..4ac2de731259 100644 --- a/CHANGELOG.next.asciidoc +++ b/CHANGELOG.next.asciidoc @@ -147,6 +147,7 @@ https://github.com/elastic/beats/compare/v7.0.0-alpha2...master[Check the HEAD d - Fix NPEs / resource leaks when executing config checks. {pull}11165[11165] - Fix duplicated IPs on `mode: all` monitors. {pull}12458[12458] +- Fix integer comparison on JSON responses. {pull}13348[13348] *Journalbeat* diff --git a/heartbeat/monitors/active/http/check.go b/heartbeat/monitors/active/http/check.go index 9563747c2d8a..db667e3dbc5e 100644 --- a/heartbeat/monitors/active/http/check.go +++ b/heartbeat/monitors/active/http/check.go @@ -28,6 +28,7 @@ import ( pkgerrors "github.com/pkg/errors" "github.com/elastic/beats/libbeat/common" + "github.com/elastic/beats/libbeat/common/jsontransform" "github.com/elastic/beats/libbeat/common/match" "github.com/elastic/beats/libbeat/conditions" ) @@ -148,13 +149,17 @@ func checkJSON(checks []*jsonResponseCheck) (RespCheck, error) { return func(r *http.Response) error { decoded := &common.MapStr{} - err := json.NewDecoder(r.Body).Decode(decoded) + decoder := json.NewDecoder(r.Body) + decoder.UseNumber() + err := decoder.Decode(decoded) if err != nil { body, _ := ioutil.ReadAll(r.Body) return pkgerrors.Wrapf(err, "could not parse JSON for body check with condition. Source: %s", body) } + jsontransform.TransformNumbers(*decoded) + var errorDescs []string for _, compiledCheck := range compiledChecks { ok := compiledCheck.condition.Check(decoded) diff --git a/heartbeat/monitors/active/http/check_test.go b/heartbeat/monitors/active/http/check_test.go index f6f8f94a89c5..be0b8cc7b92d 100644 --- a/heartbeat/monitors/active/http/check_test.go +++ b/heartbeat/monitors/active/http/check_test.go @@ -196,3 +196,69 @@ func TestCheckJson(t *testing.T) { } } + +func TestCheckJsonWithIntegerComparison(t *testing.T) { + fooBazEqualsBar := common.MustNewConfigFrom(map[string]interface{}{"equals": map[string]interface{}{"foo": 1}}) + fooBazEqualsBarConf := &conditions.Config{} + err := fooBazEqualsBar.Unpack(fooBazEqualsBarConf) + require.NoError(t, err) + + fooBazEqualsBarDesc := "foo equals 1" + + var tests = []struct { + description string + body string + condDesc string + condConf *conditions.Config + result bool + }{ + { + "positive match", + "{\"foo\": 1}", + fooBazEqualsBarDesc, + fooBazEqualsBarConf, + true, + }, + { + "Negative match", + "{\"foo\": 2}", + fooBazEqualsBarDesc, + fooBazEqualsBarConf, + false, + }, + { + "Negative match", + "{\"foo\": \"some string\"}", + fooBazEqualsBarDesc, + fooBazEqualsBarConf, + false, + }, + } + + for _, test := range tests { + t.Run(test.description, func(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + fmt.Fprintln(w, test.body) + })) + defer ts.Close() + + res, err := http.Get(ts.URL) + if err != nil { + log.Fatal(err) + } + + checker, err := checkJSON([]*jsonResponseCheck{{test.condDesc, test.condConf}}) + require.NoError(t, err) + checkRes := checker(res) + + if result := checkRes == nil; result != test.result { + if test.result { + t.Fatalf("Expected condition: '%s' to match body: %s. got: %s", test.condDesc, test.body, checkRes) + } else { + t.Fatalf("Did not expect condition: '%s' to match body: %s. got: %s", test.condDesc, test.body, checkRes) + } + } + }) + } + +} diff --git a/heartbeat/tests/system/test_monitor.py b/heartbeat/tests/system/test_monitor.py index 4ca229c820ac..4eecc3c6623a 100644 --- a/heartbeat/tests/system/test_monitor.py +++ b/heartbeat/tests/system/test_monitor.py @@ -97,6 +97,43 @@ def test_http_json(self, expected_status, body): finally: server.shutdown() + @parameterized.expand([ + ('{"foo": "bar"}', {"foo": "bar"}), + ('{"foo": true}', {"foo": True},), + ('{"foo": 3}', {"foo": 3},), + ]) + def test_json_simple_comparisons(self, body, comparison): + """ + Test JSON response with simple straight-forward comparisons + """ + server = self.start_server(body, 200) + try: + self.render_config_template( + monitors=[{ + "type": "http", + "urls": ["http://localhost:{}".format(server.server_port)], + "check_response_json": [{ + "description": body, + "condition": { + "equals": comparison + } + }] + }] + ) + + try: + proc = self.start_beat() + self.wait_until(lambda: self.log_contains("heartbeat is running")) + + self.wait_until( + lambda: self.output_has(lines=1)) + finally: + proc.check_kill_and_wait() + + self.assert_last_status("up") + finally: + server.shutdown() + @parameterized.expand([ (lambda server: "localhost:{}".format(server.server_port), "up"), # This IP is reserved in IPv4