From 541e0264ab212916523460e9c52a1e8adfd25645 Mon Sep 17 00:00:00 2001 From: TwinProduction Date: Mon, 8 Mar 2021 21:30:11 -0500 Subject: [PATCH] Don't export, persist or retain result body after evaluation --- controller/controller_test.go | 2 - core/condition.go | 12 +++-- core/condition_bench_test.go | 12 ++--- core/condition_test.go | 70 +++++++++++++++++++---------- core/dns.go | 12 ++--- core/dns_test.go | 8 ++-- core/result.go | 12 +++-- core/service-status_bench_test.go | 4 +- core/service.go | 23 ++++++++-- core/service_test.go | 51 +++++++++++++++------ storage/store/memory/memory_test.go | 5 --- storage/store/store_bench_test.go | 2 - watchdog/watchdog.go | 13 ++---- 13 files changed, 143 insertions(+), 83 deletions(-) diff --git a/controller/controller_test.go b/controller/controller_test.go index f0a526f8..d6f3ea36 100644 --- a/controller/controller_test.go +++ b/controller/controller_test.go @@ -38,7 +38,6 @@ var ( Hostname: "example.org", IP: "127.0.0.1", HTTPStatus: 200, - Body: []byte("body"), Errors: nil, Connected: true, Success: true, @@ -64,7 +63,6 @@ var ( Hostname: "example.org", IP: "127.0.0.1", HTTPStatus: 200, - Body: []byte("body"), Errors: []string{"error-1", "error-2"}, Connected: true, Success: false, diff --git a/core/condition.go b/core/condition.go index c610c34e..7c957d32 100644 --- a/core/condition.go +++ b/core/condition.go @@ -23,7 +23,7 @@ const ( // DNSRCodePlaceholder is a place holder for DNS_RCODE // - // Values that could be NOERROR, FORMERR, SERVFAIL, NXDOMAIN, NOTIMP and REFUSED + // Values that could replace the placeholder: NOERROR, FORMERR, SERVFAIL, NXDOMAIN, NOTIMP, REFUSED DNSRCodePlaceholder = "[DNS_RCODE]" // ResponseTimePlaceholder is a placeholder for the request response time, in milliseconds. @@ -123,6 +123,12 @@ func (c Condition) evaluate(result *Result) bool { return success } +// hasBodyPlaceholder checks whether the condition has a BodyPlaceholder +// Used for determining whether the response body should be read or not +func (c Condition) hasBodyPlaceholder() bool { + return strings.Contains(string(c), BodyPlaceholder) +} + // isEqual compares two strings. // // Supports the pattern and the any functions. @@ -181,7 +187,7 @@ func isEqual(first, second string) bool { func sanitizeAndResolve(elements []string, result *Result) ([]string, []string) { parameters := make([]string, len(elements)) resolvedParameters := make([]string, len(elements)) - body := strings.TrimSpace(string(result.Body)) + body := strings.TrimSpace(string(result.body)) for i, element := range elements { element = strings.TrimSpace(element) parameters[i] = element @@ -208,7 +214,7 @@ func sanitizeAndResolve(elements []string, result *Result) ([]string, []string) wantLength = true element = strings.TrimSuffix(strings.TrimPrefix(element, LengthFunctionPrefix), FunctionSuffix) } - resolvedElement, resolvedElementLength, err := jsonpath.Eval(strings.TrimPrefix(element, BodyPlaceholder+"."), result.Body) + resolvedElement, resolvedElementLength, err := jsonpath.Eval(strings.TrimPrefix(element, BodyPlaceholder+"."), result.body) if err != nil { if err.Error() != "unexpected end of JSON input" { result.Errors = append(result.Errors, err.Error()) diff --git a/core/condition_bench_test.go b/core/condition_bench_test.go index ea846e54..dbc1e582 100644 --- a/core/condition_bench_test.go +++ b/core/condition_bench_test.go @@ -5,7 +5,7 @@ import "testing" func BenchmarkCondition_evaluateWithBodyStringAny(b *testing.B) { condition := Condition("[BODY].name == any(john.doe, jane.doe)") for n := 0; n < b.N; n++ { - result := &Result{Body: []byte("{\"name\": \"john.doe\"}")} + result := &Result{body: []byte("{\"name\": \"john.doe\"}")} condition.evaluate(result) } b.ReportAllocs() @@ -14,7 +14,7 @@ func BenchmarkCondition_evaluateWithBodyStringAny(b *testing.B) { func BenchmarkCondition_evaluateWithBodyStringAnyFailure(b *testing.B) { condition := Condition("[BODY].name == any(john.doe, jane.doe)") for n := 0; n < b.N; n++ { - result := &Result{Body: []byte("{\"name\": \"bob.doe\"}")} + result := &Result{body: []byte("{\"name\": \"bob.doe\"}")} condition.evaluate(result) } b.ReportAllocs() @@ -23,7 +23,7 @@ func BenchmarkCondition_evaluateWithBodyStringAnyFailure(b *testing.B) { func BenchmarkCondition_evaluateWithBodyString(b *testing.B) { condition := Condition("[BODY].name == john.doe") for n := 0; n < b.N; n++ { - result := &Result{Body: []byte("{\"name\": \"john.doe\"}")} + result := &Result{body: []byte("{\"name\": \"john.doe\"}")} condition.evaluate(result) } b.ReportAllocs() @@ -32,7 +32,7 @@ func BenchmarkCondition_evaluateWithBodyString(b *testing.B) { func BenchmarkCondition_evaluateWithBodyStringFailure(b *testing.B) { condition := Condition("[BODY].name == john.doe") for n := 0; n < b.N; n++ { - result := &Result{Body: []byte("{\"name\": \"bob.doe\"}")} + result := &Result{body: []byte("{\"name\": \"bob.doe\"}")} condition.evaluate(result) } b.ReportAllocs() @@ -41,7 +41,7 @@ func BenchmarkCondition_evaluateWithBodyStringFailure(b *testing.B) { func BenchmarkCondition_evaluateWithBodyStringLen(b *testing.B) { condition := Condition("len([BODY].name) == 8") for n := 0; n < b.N; n++ { - result := &Result{Body: []byte("{\"name\": \"john.doe\"}")} + result := &Result{body: []byte("{\"name\": \"john.doe\"}")} condition.evaluate(result) } b.ReportAllocs() @@ -50,7 +50,7 @@ func BenchmarkCondition_evaluateWithBodyStringLen(b *testing.B) { func BenchmarkCondition_evaluateWithBodyStringLenFailure(b *testing.B) { condition := Condition("len([BODY].name) == 8") for n := 0; n < b.N; n++ { - result := &Result{Body: []byte("{\"name\": \"bob.doe\"}")} + result := &Result{body: []byte("{\"name\": \"bob.doe\"}")} condition.evaluate(result) } b.ReportAllocs() diff --git a/core/condition_test.go b/core/condition_test.go index 3b18240b..2b4d5d99 100644 --- a/core/condition_test.go +++ b/core/condition_test.go @@ -162,7 +162,7 @@ func TestCondition_evaluateWithResponseTimeUsingLessThanOrEqualTo(t *testing.T) func TestCondition_evaluateWithBody(t *testing.T) { condition := Condition("[BODY] == test") - result := &Result{Body: []byte("test")} + result := &Result{body: []byte("test")} condition.evaluate(result) if !result.ConditionResults[0].Success { t.Errorf("Condition '%s' should have been a success", condition) @@ -175,7 +175,7 @@ func TestCondition_evaluateWithBody(t *testing.T) { func TestCondition_evaluateWithBodyJSONPath(t *testing.T) { condition := Condition("[BODY].status == UP") - result := &Result{Body: []byte("{\"status\":\"UP\"}")} + result := &Result{body: []byte("{\"status\":\"UP\"}")} condition.evaluate(result) if !result.ConditionResults[0].Success { t.Errorf("Condition '%s' should have been a success", condition) @@ -188,7 +188,7 @@ func TestCondition_evaluateWithBodyJSONPath(t *testing.T) { func TestCondition_evaluateWithBodyJSONPathComplex(t *testing.T) { condition := Condition("[BODY].data.name == john") - result := &Result{Body: []byte("{\"data\": {\"id\": 1, \"name\": \"john\"}}")} + result := &Result{body: []byte("{\"data\": {\"id\": 1, \"name\": \"john\"}}")} condition.evaluate(result) if !result.ConditionResults[0].Success { t.Errorf("Condition '%s' should have been a success", condition) @@ -201,7 +201,7 @@ func TestCondition_evaluateWithBodyJSONPathComplex(t *testing.T) { func TestCondition_evaluateWithInvalidBodyJSONPathComplex(t *testing.T) { condition := Condition("[BODY].data.name == john") - result := &Result{Body: []byte("{\"data\": {\"id\": 1}}")} + result := &Result{body: []byte("{\"data\": {\"id\": 1}}")} condition.evaluate(result) if result.ConditionResults[0].Success { t.Errorf("Condition '%s' should have been a failure, because the path was invalid", condition) @@ -214,7 +214,7 @@ func TestCondition_evaluateWithInvalidBodyJSONPathComplex(t *testing.T) { func TestCondition_evaluateWithInvalidBodyJSONPathComplexWithLengthFunction(t *testing.T) { condition := Condition("len([BODY].data.name) == john") - result := &Result{Body: []byte("{\"data\": {\"id\": 1}}")} + result := &Result{body: []byte("{\"data\": {\"id\": 1}}")} condition.evaluate(result) if result.ConditionResults[0].Success { t.Errorf("Condition '%s' should have been a failure, because the path was invalid", condition) @@ -227,7 +227,7 @@ func TestCondition_evaluateWithInvalidBodyJSONPathComplexWithLengthFunction(t *t func TestCondition_evaluateWithBodyJSONPathDoublePlaceholders(t *testing.T) { condition := Condition("[BODY].user.firstName != [BODY].user.lastName") - result := &Result{Body: []byte("{\"user\": {\"firstName\": \"john\", \"lastName\": \"doe\"}}")} + result := &Result{body: []byte("{\"user\": {\"firstName\": \"john\", \"lastName\": \"doe\"}}")} condition.evaluate(result) if !result.ConditionResults[0].Success { t.Errorf("Condition '%s' should have been a success", condition) @@ -240,7 +240,7 @@ func TestCondition_evaluateWithBodyJSONPathDoublePlaceholders(t *testing.T) { func TestCondition_evaluateWithBodyJSONPathDoublePlaceholdersFailure(t *testing.T) { condition := Condition("[BODY].user.firstName == [BODY].user.lastName") - result := &Result{Body: []byte("{\"user\": {\"firstName\": \"john\", \"lastName\": \"doe\"}}")} + result := &Result{body: []byte("{\"user\": {\"firstName\": \"john\", \"lastName\": \"doe\"}}")} condition.evaluate(result) if result.ConditionResults[0].Success { t.Errorf("Condition '%s' should have been a failure", condition) @@ -253,7 +253,7 @@ func TestCondition_evaluateWithBodyJSONPathDoublePlaceholdersFailure(t *testing. func TestCondition_evaluateWithBodyJSONPathLongInt(t *testing.T) { condition := Condition("[BODY].data.id == 1") - result := &Result{Body: []byte("{\"data\": {\"id\": 1}}")} + result := &Result{body: []byte("{\"data\": {\"id\": 1}}")} condition.evaluate(result) if !result.ConditionResults[0].Success { t.Errorf("Condition '%s' should have been a success", condition) @@ -266,7 +266,7 @@ func TestCondition_evaluateWithBodyJSONPathLongInt(t *testing.T) { func TestCondition_evaluateWithBodyJSONPathComplexInt(t *testing.T) { condition := Condition("[BODY].data[1].id == 2") - result := &Result{Body: []byte("{\"data\": [{\"id\": 1}, {\"id\": 2}, {\"id\": 3}]}")} + result := &Result{body: []byte("{\"data\": [{\"id\": 1}, {\"id\": 2}, {\"id\": 3}]}")} condition.evaluate(result) if !result.ConditionResults[0].Success { t.Errorf("Condition '%s' should have been a success", condition) @@ -279,7 +279,7 @@ func TestCondition_evaluateWithBodyJSONPathComplexInt(t *testing.T) { func TestCondition_evaluateWithBodyJSONPathComplexIntUsingGreaterThan(t *testing.T) { condition := Condition("[BODY].data.id > 0") - result := &Result{Body: []byte("{\"data\": {\"id\": 1}}")} + result := &Result{body: []byte("{\"data\": {\"id\": 1}}")} condition.evaluate(result) if !result.ConditionResults[0].Success { t.Errorf("Condition '%s' should have been a success", condition) @@ -292,7 +292,7 @@ func TestCondition_evaluateWithBodyJSONPathComplexIntUsingGreaterThan(t *testing func TestCondition_evaluateWithBodyJSONPathComplexIntFailureUsingGreaterThan(t *testing.T) { condition := Condition("[BODY].data.id > 5") - result := &Result{Body: []byte("{\"data\": {\"id\": 1}}")} + result := &Result{body: []byte("{\"data\": {\"id\": 1}}")} condition.evaluate(result) if result.ConditionResults[0].Success { t.Errorf("Condition '%s' should have been a failure", condition) @@ -305,7 +305,7 @@ func TestCondition_evaluateWithBodyJSONPathComplexIntFailureUsingGreaterThan(t * func TestCondition_evaluateWithBodyJSONPathComplexIntUsingLessThan(t *testing.T) { condition := Condition("[BODY].data.id < 5") - result := &Result{Body: []byte("{\"data\": {\"id\": 2}}")} + result := &Result{body: []byte("{\"data\": {\"id\": 2}}")} condition.evaluate(result) if !result.ConditionResults[0].Success { t.Errorf("Condition '%s' should have been a success", condition) @@ -318,7 +318,7 @@ func TestCondition_evaluateWithBodyJSONPathComplexIntUsingLessThan(t *testing.T) func TestCondition_evaluateWithBodyJSONPathComplexIntFailureUsingLessThan(t *testing.T) { condition := Condition("[BODY].data.id < 5") - result := &Result{Body: []byte("{\"data\": {\"id\": 10}}")} + result := &Result{body: []byte("{\"data\": {\"id\": 10}}")} condition.evaluate(result) if result.ConditionResults[0].Success { t.Errorf("Condition '%s' should have been a failure", condition) @@ -331,7 +331,7 @@ func TestCondition_evaluateWithBodyJSONPathComplexIntFailureUsingLessThan(t *tes func TestCondition_evaluateWithBodySliceLength(t *testing.T) { condition := Condition("len([BODY].data) == 3") - result := &Result{Body: []byte("{\"data\": [{\"id\": 1}, {\"id\": 2}, {\"id\": 3}]}")} + result := &Result{body: []byte("{\"data\": [{\"id\": 1}, {\"id\": 2}, {\"id\": 3}]}")} condition.evaluate(result) if !result.ConditionResults[0].Success { t.Errorf("Condition '%s' should have been a success", condition) @@ -344,7 +344,7 @@ func TestCondition_evaluateWithBodySliceLength(t *testing.T) { func TestCondition_evaluateWithBodyStringLength(t *testing.T) { condition := Condition("len([BODY].name) == 8") - result := &Result{Body: []byte("{\"name\": \"john.doe\"}")} + result := &Result{body: []byte("{\"name\": \"john.doe\"}")} condition.evaluate(result) if !result.ConditionResults[0].Success { t.Errorf("Condition '%s' should have been a success", condition) @@ -357,7 +357,7 @@ func TestCondition_evaluateWithBodyStringLength(t *testing.T) { func TestCondition_evaluateWithBodyPattern(t *testing.T) { condition := Condition("[BODY] == pat(*john*)") - result := &Result{Body: []byte("{\"name\": \"john.doe\"}")} + result := &Result{body: []byte("{\"name\": \"john.doe\"}")} condition.evaluate(result) if !result.ConditionResults[0].Success { t.Errorf("Condition '%s' should have been a success", condition) @@ -370,7 +370,7 @@ func TestCondition_evaluateWithBodyPattern(t *testing.T) { func TestCondition_evaluateWithReverseBodyPattern(t *testing.T) { condition := Condition("pat(*john*) == [BODY]") - result := &Result{Body: []byte("{\"name\": \"john.doe\"}")} + result := &Result{body: []byte("{\"name\": \"john.doe\"}")} condition.evaluate(result) if !result.ConditionResults[0].Success { t.Errorf("Condition '%s' should have been a success", condition) @@ -383,7 +383,7 @@ func TestCondition_evaluateWithReverseBodyPattern(t *testing.T) { func TestCondition_evaluateWithBodyStringPattern(t *testing.T) { condition := Condition("[BODY].name == pat(*ohn*)") - result := &Result{Body: []byte("{\"name\": \"john.doe\"}")} + result := &Result{body: []byte("{\"name\": \"john.doe\"}")} condition.evaluate(result) if !result.ConditionResults[0].Success { t.Errorf("Condition '%s' should have been a success", condition) @@ -397,7 +397,7 @@ func TestCondition_evaluateWithBodyStringPattern(t *testing.T) { func TestCondition_evaluateWithBodyHTMLPattern(t *testing.T) { var html = `
john.doe
` condition := Condition("[BODY] == pat(*
john.doe
*)") - result := &Result{Body: []byte(html)} + result := &Result{body: []byte(html)} condition.evaluate(result) if !result.ConditionResults[0].Success { t.Errorf("Condition '%s' should have been a success", condition) @@ -410,7 +410,7 @@ func TestCondition_evaluateWithBodyHTMLPattern(t *testing.T) { func TestCondition_evaluateWithBodyStringPatternFailure(t *testing.T) { condition := Condition("[BODY].name == pat(bob*)") - result := &Result{Body: []byte("{\"name\": \"john.doe\"}")} + result := &Result{body: []byte("{\"name\": \"john.doe\"}")} condition.evaluate(result) if result.ConditionResults[0].Success { t.Errorf("Condition '%s' should have been a failure", condition) @@ -477,8 +477,8 @@ func TestCondition_evaluateWithBodyStringAny(t *testing.T) { condition := Condition("[BODY].name == any(john.doe, jane.doe)") expectedConditionDisplayed := "[BODY].name == any(john.doe, jane.doe)" results := []*Result{ - {Body: []byte("{\"name\": \"john.doe\"}")}, - {Body: []byte("{\"name\": \"jane.doe\"}")}, + {body: []byte("{\"name\": \"john.doe\"}")}, + {body: []byte("{\"name\": \"jane.doe\"}")}, } for _, result := range results { success := condition.evaluate(result) @@ -493,7 +493,7 @@ func TestCondition_evaluateWithBodyStringAny(t *testing.T) { func TestCondition_evaluateWithBodyStringAnyFailure(t *testing.T) { condition := Condition("[BODY].name == any(john.doe, jane.doe)") - result := &Result{Body: []byte("{\"name\": \"bob.doe\"}")} + result := &Result{body: []byte("{\"name\": \"bob.doe\"}")} condition.evaluate(result) if result.ConditionResults[0].Success { t.Errorf("Condition '%s' should have been a failure", condition) @@ -660,3 +660,27 @@ func TestCondition_evaluateWithCertificateExpirationGreaterThanDurationFailure(t t.Errorf("Condition '%s' should have resolved to '%s', got '%s'", condition, expectedConditionDisplayed, result.ConditionResults[0].Condition) } } + +func TestCondition_evaluateWithInvalidOperator(t *testing.T) { + condition := Condition("[STATUS] ? 201") + result := &Result{HTTPStatus: 201} + condition.evaluate(result) + if result.Success { + t.Error("condition was invalid, result should've been a failure") + } + if len(result.Errors) != 1 { + t.Error("condition was invalid, result should've had an error") + } +} + +func TestCondition_evaluateWithNoPlaceholder(t *testing.T) { + condition := Condition("1 == 2") + result := &Result{} + condition.evaluate(result) + if result.ConditionResults[0].Success { + t.Errorf("Condition '%s' should have been a failure", condition) + } + if result.ConditionResults[0].Condition != string(condition) { + t.Errorf("Condition '%s' should have resolved to '%s', got '%s'", condition, condition, result.ConditionResults[0].Condition) + } +} diff --git a/core/dns.go b/core/dns.go index 6afcf5e6..8a77ba85 100644 --- a/core/dns.go +++ b/core/dns.go @@ -60,26 +60,26 @@ func (d *DNS) query(url string, result *Result) { switch rr.Header().Rrtype { case dns.TypeA: if a, ok := rr.(*dns.A); ok { - result.Body = []byte(a.A.String()) + result.body = []byte(a.A.String()) } case dns.TypeAAAA: if aaaa, ok := rr.(*dns.AAAA); ok { - result.Body = []byte(aaaa.AAAA.String()) + result.body = []byte(aaaa.AAAA.String()) } case dns.TypeCNAME: if cname, ok := rr.(*dns.CNAME); ok { - result.Body = []byte(cname.Target) + result.body = []byte(cname.Target) } case dns.TypeMX: if mx, ok := rr.(*dns.MX); ok { - result.Body = []byte(mx.Mx) + result.body = []byte(mx.Mx) } case dns.TypeNS: if ns, ok := rr.(*dns.NS); ok { - result.Body = []byte(ns.Ns) + result.body = []byte(ns.Ns) } default: - result.Body = []byte("query type is not supported yet") + result.body = []byte("query type is not supported yet") } } } diff --git a/core/dns_test.go b/core/dns_test.go index d5073463..c037d5b4 100644 --- a/core/dns_test.go +++ b/core/dns_test.go @@ -91,12 +91,12 @@ func TestIntegrationQuery(t *testing.T) { if test.inputDNS.QueryType == "NS" { // Because there are often multiple nameservers backing a single domain, we'll only look at the suffix - if !pattern.Match(test.expectedBody, string(result.Body)) { - t.Errorf("got %s, expected result %s,", string(result.Body), test.expectedBody) + if !pattern.Match(test.expectedBody, string(result.body)) { + t.Errorf("got %s, expected result %s,", string(result.body), test.expectedBody) } } else { - if string(result.Body) != test.expectedBody { - t.Errorf("got %s, expected result %s,", string(result.Body), test.expectedBody) + if string(result.body) != test.expectedBody { + t.Errorf("got %s, expected result %s,", string(result.body), test.expectedBody) } } }) diff --git a/core/result.go b/core/result.go index 032dcb81..ea08b899 100644 --- a/core/result.go +++ b/core/result.go @@ -12,10 +12,7 @@ type Result struct { // DNSRCode is the response code of a DNS query in a human readable format DNSRCode string `json:"-"` - // Body is the response body - Body []byte `json:"-"` - - // Hostname extracted from the Service URL + // Hostname extracted from Service.URL Hostname string `json:"hostname"` // IP resolved from the Service URL @@ -41,4 +38,11 @@ type Result struct { // CertificateExpiration is the duration before the certificate expires CertificateExpiration time.Duration `json:"-"` + + // body is the response body + // + // Note that this variable is only used during the evaluation of a service's health. + // This means that the call Service.EvaluateHealth both populates the body (if necessary) + // and sets it to nil after the evaluation has been completed. + body []byte } diff --git a/core/service-status_bench_test.go b/core/service-status_bench_test.go index fb981296..4f5e7c41 100644 --- a/core/service-status_bench_test.go +++ b/core/service-status_bench_test.go @@ -29,7 +29,7 @@ var ( Hostname: "example.org", IP: "127.0.0.1", HTTPStatus: 200, - Body: []byte("body"), + body: []byte("body"), Errors: nil, Connected: true, Success: true, @@ -55,7 +55,7 @@ var ( Hostname: "example.org", IP: "127.0.0.1", HTTPStatus: 200, - Body: []byte("body"), + body: []byte("body"), Errors: []string{"error-1", "error-2"}, Connected: true, Success: false, diff --git a/core/service.go b/core/service.go index 12f47381..4c24df92 100644 --- a/core/service.go +++ b/core/service.go @@ -80,7 +80,7 @@ type Service struct { // NumberOfFailuresInARow is the number of unsuccessful evaluations in a row NumberOfFailuresInARow int - // NumberOfFailuresInARow is the number of successful evaluations in a row + // NumberOfSuccessesInARow is the number of successful evaluations in a row NumberOfSuccessesInARow int } @@ -149,6 +149,8 @@ func (service *Service) EvaluateHealth() *Result { } } result.Timestamp = time.Now() + // No need to keep the body after the service has been evaluated + result.body = nil return result } @@ -220,9 +222,12 @@ func (service *Service) call(result *Result) { } result.HTTPStatus = response.StatusCode result.Connected = response.StatusCode > 0 - result.Body, err = ioutil.ReadAll(response.Body) - if err != nil { - result.Errors = append(result.Errors, err.Error()) + // Only read the body if there's a condition that uses the BodyPlaceholder + if service.needsToReadBody() { + result.body, err = ioutil.ReadAll(response.Body) + if err != nil { + result.Errors = append(result.Errors, err.Error()) + } } } } @@ -247,3 +252,13 @@ func (service *Service) buildHTTPRequest() *http.Request { } return request } + +// needsToReadBody checks if there's any conditions that requires the response body to be read +func (service *Service) needsToReadBody() bool { + for _, condition := range service.Conditions { + if condition.hasBodyPlaceholder() { + return true + } + } + return false +} diff --git a/core/service_test.go b/core/service_test.go index 6e9a0fc9..8eb55557 100644 --- a/core/service_test.go +++ b/core/service_test.go @@ -10,7 +10,7 @@ import ( func TestService_ValidateAndSetDefaults(t *testing.T) { condition := Condition("[STATUS] == 200") service := Service{ - Name: "TwiNNatioN", + Name: "twinnation-health", URL: "https://twinnation.org/health", Conditions: []*Condition{&condition}, Alerts: []*Alert{{Type: PagerDutyAlert}}, @@ -94,7 +94,7 @@ func TestService_ValidateAndSetDefaultsWithDNS(t *testing.T) { func TestService_GetAlertsTriggered(t *testing.T) { condition := Condition("[STATUS] == 200") service := Service{ - Name: "TwiNNatioN", + Name: "twinnation-health", URL: "https://twinnation.org/health", Conditions: []*Condition{&condition}, Alerts: []*Alert{{Type: PagerDutyAlert, Enabled: true}}, @@ -118,7 +118,7 @@ func TestService_GetAlertsTriggered(t *testing.T) { func TestService_buildHTTPRequest(t *testing.T) { condition := Condition("[STATUS] == 200") service := Service{ - Name: "TwiNNatioN", + Name: "twinnation-health", URL: "https://twinnation.org/health", Conditions: []*Condition{&condition}, } @@ -138,7 +138,7 @@ func TestService_buildHTTPRequest(t *testing.T) { func TestService_buildHTTPRequestWithCustomUserAgent(t *testing.T) { condition := Condition("[STATUS] == 200") service := Service{ - Name: "TwiNNatioN", + Name: "twinnation-health", URL: "https://twinnation.org/health", Conditions: []*Condition{&condition}, Headers: map[string]string{ @@ -161,7 +161,7 @@ func TestService_buildHTTPRequestWithCustomUserAgent(t *testing.T) { func TestService_buildHTTPRequestWithHostHeader(t *testing.T) { condition := Condition("[STATUS] == 200") service := Service{ - Name: "TwiNNatioN", + Name: "twinnation-health", URL: "https://twinnation.org/health", Method: "POST", Conditions: []*Condition{&condition}, @@ -182,7 +182,7 @@ func TestService_buildHTTPRequestWithHostHeader(t *testing.T) { func TestService_buildHTTPRequestWithGraphQLEnabled(t *testing.T) { condition := Condition("[STATUS] == 200") service := Service{ - Name: "TwiNNatioN", + Name: "twinnation-graphql", URL: "https://twinnation.org/graphql", Method: "POST", Conditions: []*Condition{&condition}, @@ -206,16 +206,17 @@ func TestService_buildHTTPRequestWithGraphQLEnabled(t *testing.T) { } body, _ := ioutil.ReadAll(request.Body) if !strings.HasPrefix(string(body), "{\"query\":") { - t.Error("request.Body should've started with '{\"query\":', but it didn't:", string(body)) + t.Error("request.body should've started with '{\"query\":', but it didn't:", string(body)) } } func TestIntegrationEvaluateHealth(t *testing.T) { condition := Condition("[STATUS] == 200") + bodyCondition := Condition("[BODY].status == UP") service := Service{ - Name: "TwiNNatioN", + Name: "twinnation-health", URL: "https://twinnation.org/health", - Conditions: []*Condition{&condition}, + Conditions: []*Condition{&condition, &bodyCondition}, } result := service.EvaluateHealth() if !result.ConditionResults[0].Success { @@ -232,7 +233,7 @@ func TestIntegrationEvaluateHealth(t *testing.T) { func TestIntegrationEvaluateHealthWithFailure(t *testing.T) { condition := Condition("[STATUS] == 500") service := Service{ - Name: "TwiNNatioN", + Name: "twinnation-health", URL: "https://twinnation.org/health", Conditions: []*Condition{&condition}, } @@ -252,7 +253,7 @@ func TestIntegrationEvaluateHealthForDNS(t *testing.T) { conditionSuccess := Condition("[DNS_RCODE] == NOERROR") conditionBody := Condition("[BODY] == 93.184.216.34") service := Service{ - Name: "TwiNNatioN", + Name: "example", URL: "8.8.8.8", DNS: &DNS{ QueryType: "A", @@ -275,7 +276,7 @@ func TestIntegrationEvaluateHealthForDNS(t *testing.T) { func TestIntegrationEvaluateHealthForICMP(t *testing.T) { conditionSuccess := Condition("[CONNECTED] == true") service := Service{ - Name: "ICMP test", + Name: "icmp-test", URL: "icmp://127.0.0.1", Conditions: []*Condition{&conditionSuccess}, } @@ -294,7 +295,7 @@ func TestIntegrationEvaluateHealthForICMP(t *testing.T) { func TestService_getIP(t *testing.T) { conditionSuccess := Condition("[CONNECTED] == true") service := Service{ - Name: "Invalid URL test", + Name: "invalid-url-test", URL: "", Conditions: []*Condition{&conditionSuccess}, } @@ -304,3 +305,27 @@ func TestService_getIP(t *testing.T) { t.Error("service.getIP(result) should've thrown an error because the URL is invalid, thus cannot be parsed") } } + +func TestService_NeedsToReadBody(t *testing.T) { + statusCondition := Condition("[STATUS] == 200") + bodyCondition := Condition("[BODY].status == UP") + bodyConditionWithLength := Condition("len([BODY].tags) > 0") + if (&Service{Conditions: []*Condition{&statusCondition}}).needsToReadBody() { + t.Error("expected false, got true") + } + if !(&Service{Conditions: []*Condition{&bodyCondition}}).needsToReadBody() { + t.Error("expected true, got false") + } + if !(&Service{Conditions: []*Condition{&bodyConditionWithLength}}).needsToReadBody() { + t.Error("expected true, got false") + } + if !(&Service{Conditions: []*Condition{&statusCondition, &bodyCondition}}).needsToReadBody() { + t.Error("expected true, got false") + } + if !(&Service{Conditions: []*Condition{&bodyCondition, &statusCondition}}).needsToReadBody() { + t.Error("expected true, got false") + } + if !(&Service{Conditions: []*Condition{&bodyConditionWithLength, &statusCondition}}).needsToReadBody() { + t.Error("expected true, got false") + } +} diff --git a/storage/store/memory/memory_test.go b/storage/store/memory/memory_test.go index d3162f9a..ecdb51e8 100644 --- a/storage/store/memory/memory_test.go +++ b/storage/store/memory/memory_test.go @@ -33,7 +33,6 @@ var ( Hostname: "example.org", IP: "127.0.0.1", HTTPStatus: 200, - Body: []byte("body"), Errors: nil, Connected: true, Success: true, @@ -59,7 +58,6 @@ var ( Hostname: "example.org", IP: "127.0.0.1", HTTPStatus: 200, - Body: []byte("body"), Errors: []string{"error-1", "error-2"}, Connected: true, Success: false, @@ -107,9 +105,6 @@ func TestStore_Insert(t *testing.T) { if r.DNSRCode != expectedResult.DNSRCode { t.Errorf("Result at index %d should've had a DNSRCode of %s, but was actually %s", i, expectedResult.DNSRCode, r.DNSRCode) } - if len(r.Body) != len(expectedResult.Body) { - t.Errorf("Result at index %d should've had a body of length %d, but was actually %d", i, len(expectedResult.Body), len(r.Body)) - } if r.Hostname != expectedResult.Hostname { t.Errorf("Result at index %d should've had a Hostname of %s, but was actually %s", i, expectedResult.Hostname, r.Hostname) } diff --git a/storage/store/store_bench_test.go b/storage/store/store_bench_test.go index 1637d59d..a6488ed5 100644 --- a/storage/store/store_bench_test.go +++ b/storage/store/store_bench_test.go @@ -32,7 +32,6 @@ var ( Hostname: "example.org", IP: "127.0.0.1", HTTPStatus: 200, - Body: []byte("body"), Errors: nil, Connected: true, Success: true, @@ -58,7 +57,6 @@ var ( Hostname: "example.org", IP: "127.0.0.1", HTTPStatus: 200, - Body: []byte("body"), Errors: []string{"error-1", "error-2"}, Connected: true, Success: false, diff --git a/watchdog/watchdog.go b/watchdog/watchdog.go index 255615e6..5781756c 100644 --- a/watchdog/watchdog.go +++ b/watchdog/watchdog.go @@ -1,7 +1,6 @@ package watchdog import ( - "fmt" "log" "sync" "time" @@ -37,26 +36,22 @@ func monitor(service *core.Service) { monitoringMutex.Lock() } if cfg.Debug { - log.Printf("[watchdog][monitor] Monitoring serviceName=%s", service.Name) + log.Printf("[watchdog][monitor] Monitoring group=%s; service=%s", service.Group, service.Name) } result := service.EvaluateHealth() metric.PublishMetricsForService(service, result) UpdateServiceStatuses(service, result) - var extra string - if !result.Success { - extra = fmt.Sprintf("responseBody=%s", result.Body) - } log.Printf( - "[watchdog][monitor] Monitored serviceName=%s; success=%v; errors=%d; requestDuration=%s; %s", + "[watchdog][monitor] Monitored group=%s; service=%s; success=%v; errors=%d; duration=%s", + service.Group, service.Name, result.Success, len(result.Errors), result.Duration.Round(time.Millisecond), - extra, ) HandleAlerting(service, result) if cfg.Debug { - log.Printf("[watchdog][monitor] Waiting for interval=%s before monitoring serviceName=%s again", service.Interval, service.Name) + log.Printf("[watchdog][monitor] Waiting for interval=%s before monitoring group=%s service=%s again", service.Interval, service.Group, service.Name) } if !cfg.DisableMonitoringLock { monitoringMutex.Unlock()