Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Heartbeat] Support for multiple status codes #13595 [WIP] #15587

Merged
merged 1 commit into from
Jan 21, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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