mirror of
https://github.com/TwiN/gatus.git
synced 2024-11-21 23:43:27 +01:00
Work on #16: Support patterns
This commit is contained in:
parent
18d3236586
commit
8101646ba5
@ -2,23 +2,25 @@ package core
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/TwinProduction/gatus/pattern"
|
||||||
"log"
|
"log"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Condition string
|
type Condition string
|
||||||
|
|
||||||
|
// evaluate the Condition with the Result of the health check
|
||||||
func (c *Condition) evaluate(result *Result) bool {
|
func (c *Condition) evaluate(result *Result) bool {
|
||||||
condition := string(*c)
|
condition := string(*c)
|
||||||
success := false
|
success := false
|
||||||
var resolvedCondition string
|
var resolvedCondition string
|
||||||
if strings.Contains(condition, "==") {
|
if strings.Contains(condition, "==") {
|
||||||
parts := sanitizeAndResolve(strings.Split(condition, "=="), result)
|
parts := sanitizeAndResolve(strings.Split(condition, "=="), result)
|
||||||
success = parts[0] == parts[1]
|
success = isEqual(parts[0], parts[1])
|
||||||
resolvedCondition = fmt.Sprintf("%v == %v", parts[0], parts[1])
|
resolvedCondition = fmt.Sprintf("%v == %v", parts[0], parts[1])
|
||||||
} else if strings.Contains(condition, "!=") {
|
} else if strings.Contains(condition, "!=") {
|
||||||
parts := sanitizeAndResolve(strings.Split(condition, "!="), result)
|
parts := sanitizeAndResolve(strings.Split(condition, "!="), result)
|
||||||
success = parts[0] != parts[1]
|
success = !isEqual(parts[0], parts[1])
|
||||||
resolvedCondition = fmt.Sprintf("%v != %v", parts[0], parts[1])
|
resolvedCondition = fmt.Sprintf("%v != %v", parts[0], parts[1])
|
||||||
} else if strings.Contains(condition, "<=") {
|
} else if strings.Contains(condition, "<=") {
|
||||||
parts := sanitizeAndResolveNumerical(strings.Split(condition, "<="), result)
|
parts := sanitizeAndResolveNumerical(strings.Split(condition, "<="), result)
|
||||||
@ -49,3 +51,26 @@ func (c *Condition) evaluate(result *Result) bool {
|
|||||||
result.ConditionResults = append(result.ConditionResults, &ConditionResult{Condition: conditionToDisplay, Success: success})
|
result.ConditionResults = append(result.ConditionResults, &ConditionResult{Condition: conditionToDisplay, Success: success})
|
||||||
return success
|
return success
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// isEqual compares two strings.
|
||||||
|
//
|
||||||
|
// It also supports the pattern function. That is to say, if one of the strings starts with PatternFunctionPrefix
|
||||||
|
// and ends with FunctionSuffix, it will be treated like a pattern.
|
||||||
|
func isEqual(first, second string) bool {
|
||||||
|
var isFirstPattern, isSecondPattern bool
|
||||||
|
if strings.HasPrefix(first, PatternFunctionPrefix) && strings.HasSuffix(first, FunctionSuffix) {
|
||||||
|
isFirstPattern = true
|
||||||
|
first = strings.TrimSuffix(strings.TrimPrefix(first, PatternFunctionPrefix), FunctionSuffix)
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(second, PatternFunctionPrefix) && strings.HasSuffix(second, FunctionSuffix) {
|
||||||
|
isSecondPattern = true
|
||||||
|
second = strings.TrimSuffix(strings.TrimPrefix(second, PatternFunctionPrefix), FunctionSuffix)
|
||||||
|
}
|
||||||
|
if isFirstPattern && !isSecondPattern {
|
||||||
|
return pattern.Match(first, second)
|
||||||
|
} else if !isFirstPattern && isSecondPattern {
|
||||||
|
return pattern.Match(second, first)
|
||||||
|
} else {
|
||||||
|
return first == second
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -202,3 +202,66 @@ func TestCondition_evaluateWithBodyStringLength(t *testing.T) {
|
|||||||
t.Errorf("Condition '%s' should have been a success", condition)
|
t.Errorf("Condition '%s' should have been a success", condition)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCondition_evaluateWithBodyStringPattern(t *testing.T) {
|
||||||
|
condition := Condition("[BODY].name == pat(*ohn*)")
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCondition_evaluateWithBodyStringPatternFailure(t *testing.T) {
|
||||||
|
condition := Condition("[BODY].name == pat(bob*)")
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCondition_evaluateWithBodyPatternFailure(t *testing.T) {
|
||||||
|
condition := Condition("[BODY] == pat(*john*)")
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCondition_evaluateWithIPPattern(t *testing.T) {
|
||||||
|
condition := Condition("[IP] == pat(10.*)")
|
||||||
|
result := &Result{Ip: "10.0.0.0"}
|
||||||
|
condition.evaluate(result)
|
||||||
|
if !result.ConditionResults[0].Success {
|
||||||
|
t.Errorf("Condition '%s' should have been a success", condition)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCondition_evaluateWithIPPatternFailure(t *testing.T) {
|
||||||
|
condition := Condition("[IP] == pat(10.*)")
|
||||||
|
result := &Result{Ip: "255.255.255.255"}
|
||||||
|
condition.evaluate(result)
|
||||||
|
if result.ConditionResults[0].Success {
|
||||||
|
t.Errorf("Condition '%s' should have been a failure", condition)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCondition_evaluateWithStatusPattern(t *testing.T) {
|
||||||
|
condition := Condition("[STATUS] == pat(4*)")
|
||||||
|
result := &Result{HttpStatus: 404}
|
||||||
|
condition.evaluate(result)
|
||||||
|
if !result.ConditionResults[0].Success {
|
||||||
|
t.Errorf("Condition '%s' should have been a success", condition)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCondition_evaluateWithStatusPatternFailure(t *testing.T) {
|
||||||
|
condition := Condition("[STATUS] != pat(4*)")
|
||||||
|
result := &Result{HttpStatus: 404}
|
||||||
|
condition.evaluate(result)
|
||||||
|
if result.ConditionResults[0].Success {
|
||||||
|
t.Errorf("Condition '%s' should have been a failure", condition)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -13,8 +13,9 @@ const (
|
|||||||
ResponseTimePlaceHolder = "[RESPONSE_TIME]"
|
ResponseTimePlaceHolder = "[RESPONSE_TIME]"
|
||||||
BodyPlaceHolder = "[BODY]"
|
BodyPlaceHolder = "[BODY]"
|
||||||
|
|
||||||
LengthFunctionPrefix = "len("
|
LengthFunctionPrefix = "len("
|
||||||
FunctionSuffix = ")"
|
PatternFunctionPrefix = "pat("
|
||||||
|
FunctionSuffix = ")"
|
||||||
|
|
||||||
InvalidConditionElementSuffix = "(INVALID)"
|
InvalidConditionElementSuffix = "(INVALID)"
|
||||||
)
|
)
|
||||||
|
12
pattern/pattern.go
Normal file
12
pattern/pattern.go
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
package pattern
|
||||||
|
|
||||||
|
import "path/filepath"
|
||||||
|
|
||||||
|
// Match checks whether a string matches a pattern
|
||||||
|
func Match(pattern, s string) bool {
|
||||||
|
if pattern == "*" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
matched, _ := filepath.Match(pattern, s)
|
||||||
|
return matched
|
||||||
|
}
|
37
pattern/pattern_test.go
Normal file
37
pattern/pattern_test.go
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
package pattern
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestMatch(t *testing.T) {
|
||||||
|
testMatch(t, "*", "livingroom_123", true)
|
||||||
|
testMatch(t, "**", "livingroom_123", true)
|
||||||
|
testMatch(t, "living*", "livingroom_123", true)
|
||||||
|
testMatch(t, "*living*", "livingroom_123", true)
|
||||||
|
testMatch(t, "*123", "livingroom_123", true)
|
||||||
|
testMatch(t, "*_*", "livingroom_123", true)
|
||||||
|
testMatch(t, "living*_*3", "livingroom_123", true)
|
||||||
|
testMatch(t, "living*room_*3", "livingroom_123", true)
|
||||||
|
testMatch(t, "living*room_*3", "livingroom_123", true)
|
||||||
|
testMatch(t, "*vin*om*2*", "livingroom_123", true)
|
||||||
|
testMatch(t, "livingroom_123", "livingroom_123", true)
|
||||||
|
testMatch(t, "*livingroom_123*", "livingroom_123", true)
|
||||||
|
testMatch(t, "livingroom", "livingroom_123", false)
|
||||||
|
testMatch(t, "livingroom123", "livingroom_123", false)
|
||||||
|
testMatch(t, "what", "livingroom_123", false)
|
||||||
|
testMatch(t, "*what*", "livingroom_123", false)
|
||||||
|
testMatch(t, "*.*", "livingroom_123", false)
|
||||||
|
testMatch(t, "room*123", "livingroom_123", false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testMatch(t *testing.T, pattern, key string, expectedToMatch bool) {
|
||||||
|
matched := Match(pattern, key)
|
||||||
|
if expectedToMatch {
|
||||||
|
if !matched {
|
||||||
|
t.Errorf("%s should've matched pattern '%s'", key, pattern)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if matched {
|
||||||
|
t.Errorf("%s shouldn't have matched pattern '%s'", key, pattern)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user