From 122b83b6cc9b48f63c0da4892ac4cb127aa57927 Mon Sep 17 00:00:00 2001 From: osherdp Date: Mon, 2 Sep 2019 20:06:20 +0300 Subject: [PATCH] fix number transformation from json files in heartbeat (#13348) Prior to this patch heartbeat could not check JSON responses for numeric values correctly because they were incorrectly being cast to the wrong type for comparison. (cherry picked from commit fb62c669e737abe4157410843da1e818783cd57c) --- CHANGELOG.next.asciidoc | 1 + heartbeat/monitors/active/http/check.go | 7 ++- heartbeat/monitors/active/http/check_test.go | 66 ++++++++++++++++++++ heartbeat/tests/system/test_monitor.py | 37 +++++++++++ 4 files changed, 110 insertions(+), 1 deletion(-) 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