diff --git a/heartbeat/docs/heartbeat-options.asciidoc b/heartbeat/docs/heartbeat-options.asciidoc index f72e631781d..6becc27a7a9 100644 --- a/heartbeat/docs/heartbeat-options.asciidoc +++ b/heartbeat/docs/heartbeat-options.asciidoc @@ -34,7 +34,7 @@ heartbeat.monitors: - type: http schedule: '@every 5s' hosts: ["http://localhost:80/service/status"] - check.response.status: 200 + check.response.status: [200] heartbeat.scheduler: limit: 10 ---------------------------------------------------------------------- @@ -69,7 +69,7 @@ monitor definitions only, e.g. what is normally under the `heartbeat.monitors` s - type: http schedule: '@every 5s' hosts: ["http://localhost:80/service/status"] - check.response.status: 200 + check.response.status: [200] ---------------------------------------------------------------------- [float] @@ -429,7 +429,7 @@ The username for authenticating with the server. The credentials are passed with the request. This setting is optional. You need to specify credentials when your `check.response` settings require it. -For example, you can check for a 403 response (`check.response.status: 403`) +For example, you can check for a 403 response (`check.response.status: [403]`) without setting credentials. [float] @@ -489,7 +489,7 @@ Example configuration: schedule: '@every 5s' hosts: ["http://myhost:80"] check.request.method: HEAD - check.response.status: 200 + check.response.status: [200] ------------------------------------------------------------------------------- @@ -517,7 +517,7 @@ to the endpoint `/demo/add` # urlencode the body: body: "name=first&email=someemail%40someemailprovider.com" check.response: - status: 200 + status: [200] body: - Saved - saved @@ -525,14 +525,14 @@ to the endpoint `/demo/add` Under `check.response`, specify these options: -*`status`*:: The expected status code. 4xx and 5xx codes are considered `down` by default. Other codes are considered `up`. +*`status`*:: A list of expected status codes. 4xx and 5xx codes are considered `down` by default. Other codes are considered `up`. *`headers`*:: The required response headers. *`body`*:: A list of regular expressions to match the the body output. Only a single expression needs to match. HTTP response bodies of up to 100MiB are supported. Example configuration: This monitor examines the -response body for the strings `saved` or `Saved` +response body for the strings `saved` or `Saved` and expects 200 or 201 status codes [source,yaml] ------------------------------------------------------------------------------- @@ -546,7 +546,7 @@ response body for the strings `saved` or `Saved` # urlencode the body: body: "name=first&email=someemail%40someemailprovider.com" check.response: - status: 200 + status: [200, 201] body: - Saved - saved @@ -568,7 +568,7 @@ contains JSON: headers: 'X-API-Key': '12345-mykey-67890' check.response: - status: 200 + status: [200] json: - description: check status condition: @@ -589,7 +589,7 @@ patterns: headers: 'X-API-Key': '12345-mykey-67890' check.response: - status: 200 + status: [200] body: - hello - world @@ -608,7 +608,7 @@ regex: headers: 'X-API-Key': '12345-mykey-67890' check.response: - status: 200 + status: [200] body: '(?s)first.*second.*third' ------------------------------------------------------------------------------- diff --git a/heartbeat/monitors/active/http/check.go b/heartbeat/monitors/active/http/check.go index 8976cd56304..4ff5ab4a470 100644 --- a/heartbeat/monitors/active/http/check.go +++ b/heartbeat/monitors/active/http/check.go @@ -77,7 +77,7 @@ func makeValidateResponse(config *responseParameters) (multiValidator, error) { var respValidators []respValidator var bodyValidators []bodyValidator - if config.Status > 0 { + if len(config.Status) > 0 { respValidators = append(respValidators, checkStatus(config.Status)) } else { respValidators = append(respValidators, checkStatusOK) @@ -102,10 +102,12 @@ func makeValidateResponse(config *responseParameters) (multiValidator, error) { return multiValidator{respValidators, bodyValidators}, nil } -func checkStatus(status uint16) respValidator { +func checkStatus(status []uint16) respValidator { return func(r *http.Response) error { - if r.StatusCode == int(status) { - return nil + for _, v := range status { + if r.StatusCode == int(v) { + return nil + } } return fmt.Errorf("received status code %v expecting %v", r.StatusCode, status) } diff --git a/heartbeat/monitors/active/http/check_test.go b/heartbeat/monitors/active/http/check_test.go index a705ca34454..3c3e42caa06 100644 --- a/heartbeat/monitors/active/http/check_test.go +++ b/heartbeat/monitors/active/http/check_test.go @@ -268,3 +268,62 @@ func TestCheckJsonWithIntegerComparison(t *testing.T) { } } + +func TestCheckStatus(t *testing.T) { + + var matchTests = []struct { + description string + status []uint16 + statusRec int + result bool + }{ + { + "not match multiple values", + []uint16{200, 301, 302}, + 500, + false, + }, + { + "match multiple values", + []uint16{200, 301, 302}, + 200, + true, + }, + { + "not match single value", + []uint16{200}, + 201, + false, + }, + { + "match single value", + []uint16{200}, + 200, + true, + }, + } + + for _, test := range matchTests { + t.Run(test.description, func(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(test.statusRec) + })) + defer ts.Close() + + res, err := http.Get(ts.URL) + if err != nil { + log.Fatal(err) + } + + check := checkStatus(test.status)(res) + + if result := (check == nil); result != test.result { + if test.result { + t.Fatalf("Expected at least one of status: %d to match status: %d", test.status, test.statusRec) + } else { + t.Fatalf("Did not expect status: %d to match status: %d", test.status, test.statusRec) + } + } + }) + } +} diff --git a/heartbeat/monitors/active/http/config.go b/heartbeat/monitors/active/http/config.go index 033d8fab628..e195f4209e3 100644 --- a/heartbeat/monitors/active/http/config.go +++ b/heartbeat/monitors/active/http/config.go @@ -74,7 +74,7 @@ type requestParameters struct { type responseParameters struct { // expected HTTP response configuration - Status uint16 `config:"status" verify:"min=0, max=699"` + Status []uint16 `config:"status"` RecvHeaders map[string]string `config:"headers"` RecvBody []match.Matcher `config:"body"` RecvJSON []*jsonResponseCheck `config:"json"` @@ -105,7 +105,6 @@ var defaultConfig = Config{ SendBody: "", }, Response: responseParameters{ - Status: 0, RecvHeaders: nil, RecvBody: []match.Matcher{}, RecvJSON: nil,