Skip to content

Commit

Permalink
[7.x][Heartbeat] Support for multiple status codes #13595 (#15587) (#…
Browse files Browse the repository at this point in the history
…15720)

Allow for multiple status codes in config. Fixes #13595

Backport of #15587
  • Loading branch information
andrewvc authored Jan 22, 2020
1 parent f1c5c02 commit 75e5485
Show file tree
Hide file tree
Showing 5 changed files with 79 additions and 17 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.next.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,8 @@ https://github.com/elastic/beats/compare/v7.0.0-alpha2...master[Check the HEAD d
- New fileset googlecloud/firewall for ingesting Google Cloud Firewall logs. {pull}14553[14553]
- google-pubsub input: ACK pub/sub message when acknowledged by publisher. {issue}13346[13346] {pull}14715[14715]
- Remove Beta label from google-pubsub input. {issue}13346[13346] {pull}14715[14715]
- Allow a list of status codes for HTTP checks. {pull}15587[15587]


*Heartbeat*

Expand Down
22 changes: 11 additions & 11 deletions heartbeat/docs/heartbeat-options.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -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
----------------------------------------------------------------------
Expand Down Expand Up @@ -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]
Expand Down Expand Up @@ -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]
Expand Down Expand Up @@ -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]
-------------------------------------------------------------------------------


Expand Down Expand Up @@ -517,22 +517,22 @@ to the endpoint `/demo/add`
# urlencode the body:
body: "name=first&email=someemail%40someemailprovider.com"
check.response:
status: 200
status: [200]
body:
- Saved
- saved
-------------------------------------------------------------------------------

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]
-------------------------------------------------------------------------------
Expand All @@ -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
Expand All @@ -568,7 +568,7 @@ contains JSON:
headers:
'X-API-Key': '12345-mykey-67890'
check.response:
status: 200
status: [200]
json:
- description: check status
condition:
Expand All @@ -589,7 +589,7 @@ patterns:
headers:
'X-API-Key': '12345-mykey-67890'
check.response:
status: 200
status: [200]
body:
- hello
- world
Expand All @@ -608,7 +608,7 @@ regex:
headers:
'X-API-Key': '12345-mykey-67890'
check.response:
status: 200
status: [200]
body: '(?s)first.*second.*third'
-------------------------------------------------------------------------------

Expand Down
10 changes: 6 additions & 4 deletions heartbeat/monitors/active/http/check.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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)
}
Expand Down
59 changes: 59 additions & 0 deletions heartbeat/monitors/active/http/check_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
})
}
}
3 changes: 1 addition & 2 deletions heartbeat/monitors/active/http/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"`
Expand Down Expand Up @@ -105,7 +105,6 @@ var defaultConfig = Config{
SendBody: "",
},
Response: responseParameters{
Status: 0,
RecvHeaders: nil,
RecvBody: []match.Matcher{},
RecvJSON: nil,
Expand Down

0 comments on commit 75e5485

Please sign in to comment.