diff --git a/config.yaml b/config.yaml index 968deefd..de0e4686 100644 --- a/config.yaml +++ b/config.yaml @@ -1,13 +1,12 @@ services: - name: twinnation url: https://twinnation.org/actuator/health - interval: 10 + interval: 15 failure-threshold: 3 conditions: - "$STATUS == 200" - name: github url: https://github.com - interval: 10 - failure-threshold: 3 conditions: - - "$STATUS == 200" \ No newline at end of file + - "$STATUS != 400" + - "$STATUS != 500" \ No newline at end of file diff --git a/config/config.go b/config/config.go index 0d77a193..f248ae86 100644 --- a/config/config.go +++ b/config/config.go @@ -1,24 +1,15 @@ package config import ( + "github.com/TwinProduction/gatus/core" "gopkg.in/yaml.v2" "io/ioutil" ) type Config struct { - Services []Service `yaml:"services"` + Services []*core.Service `yaml:"services"` } -type Service struct { - Name string `yaml:"name"` - Url string `yaml:"url"` - Interval uint `yaml:"interval"` - FailureThreshold uint `yaml:"failure-threshold"` - Conditions []Condition `yaml:"conditions"` -} - -type Condition string - var config *Config func Get() *Config { @@ -41,5 +32,13 @@ func ReadConfigurationFile(fileName string) *Config { func ParseConfigBytes(yamlBytes []byte) *Config { config = &Config{} yaml.Unmarshal(yamlBytes, config) + for _, service := range config.Services { + if service.FailureThreshold == 0 { + service.FailureThreshold = 1 + } + if service.Interval == 0 { + service.Interval = 10 + } + } return config } diff --git a/config/config_test.go b/config/config_test.go index 9888dcf0..e923c2dd 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -7,16 +7,14 @@ func TestParseConfigBytes(t *testing.T) { services: - name: twinnation url: https://twinnation.org/actuator/health - interval: 10 + interval: 15 failure-threshold: 3 conditions: - "$STATUS == 200" - name: github url: https://github.com - interval: 9 - failure-threshold: 2 conditions: - - "$STATUS != 405" + - "$STATUS != 400" - "$STATUS != 500" `)) if len(config.Services) != 2 { @@ -28,17 +26,17 @@ services: if config.Services[1].Url != "https://github.com" { t.Errorf("URL should have been %s", "https://github.com") } - if config.Services[0].Interval != 10 { - t.Errorf("Interval should have been %d", 10) + if config.Services[0].Interval != 15 { + t.Errorf("Interval should have been %d", 15) } - if config.Services[1].Interval != 9 { - t.Errorf("Interval should have been %d", 9) + if config.Services[1].Interval != 10 { + t.Errorf("Interval should have been %d, because it is the default value", 10) } if config.Services[0].FailureThreshold != 3 { t.Errorf("FailureThreshold should have been %d", 3) } - if config.Services[1].FailureThreshold != 2 { - t.Errorf("FailureThreshold should have been %d", 2) + if config.Services[1].FailureThreshold != 1 { + t.Errorf("FailureThreshold should have been %d, because it is the default value", 1) } if len(config.Services[0].Conditions) != 1 { t.Errorf("There should have been %d conditions", 1) diff --git a/core/types.go b/core/types.go new file mode 100644 index 00000000..c92addd9 --- /dev/null +++ b/core/types.go @@ -0,0 +1,126 @@ +package core + +import ( + "fmt" + "net" + "net/http" + "net/url" + "strconv" + "strings" + "time" +) + +type Result struct { + HttpStatus int + Hostname string + Ip string + Duration time.Duration + Errors []error + ConditionResult []*ConditionResult +} + +type Service struct { + Name string `yaml:"name"` + Url string `yaml:"url"` + Interval int `yaml:"interval,omitempty"` + FailureThreshold int `yaml:"failure-threshold,omitempty"` + Conditions []*Condition `yaml:"conditions"` +} + +func (service *Service) getIp(result *Result) { + urlObject, err := url.Parse(service.Url) + if err != nil { + result.Errors = append(result.Errors, err) + return + } + result.Hostname = urlObject.Hostname() + ips, err := net.LookupIP(urlObject.Hostname()) + if err != nil { + result.Errors = append(result.Errors, err) + return + } + result.Ip = ips[0].String() +} + +func (service *Service) getStatus(result *Result) { + client := &http.Client{ + Timeout: time.Second * 10, + } + startTime := time.Now() + response, err := client.Get(service.Url) + if err != nil { + result.Errors = append(result.Errors, err) + return + } + result.Duration = time.Now().Sub(startTime) + result.HttpStatus = response.StatusCode +} + +func (service *Service) EvaluateConditions() *Result { + result := &Result{} + service.getStatus(result) + service.getIp(result) + for _, condition := range service.Conditions { + condition.Evaluate(result) + } + return result +} + +type ConditionResult struct { + Condition *Condition + Success bool + Explanation string +} + +type Condition string + +func (c *Condition) Evaluate(result *Result) { + condition := string(*c) + if strings.Contains(condition, "==") { + parts := sanitizeAndResolve(strings.Split(condition, "=="), result) + if parts[0] == parts[1] { + result.ConditionResult = append(result.ConditionResult, &ConditionResult{ + Condition: c, + Success: true, + Explanation: fmt.Sprintf("%s is equal to %s", parts[0], parts[1]), + }) + } else { + result.ConditionResult = append(result.ConditionResult, &ConditionResult{ + Condition: c, + Success: false, + Explanation: fmt.Sprintf("%s is not equal to %s", parts[0], parts[1]), + }) + } + } else if strings.Contains(condition, "!=") { + parts := sanitizeAndResolve(strings.Split(condition, "!="), result) + if parts[0] != parts[1] { + result.ConditionResult = append(result.ConditionResult, &ConditionResult{ + Condition: c, + Success: true, + Explanation: fmt.Sprintf("%s is not equal to %s", parts[0], parts[1]), + }) + } else { + result.ConditionResult = append(result.ConditionResult, &ConditionResult{ + Condition: c, + Success: false, + Explanation: fmt.Sprintf("%s is equal to %s", parts[0], parts[1]), + }) + } + } +} + +func sanitizeAndResolve(list []string, result *Result) []string { + var sanitizedList []string + for _, element := range list { + element = strings.TrimSpace(element) + switch strings.ToUpper(element) { + case "$STATUS": + element = strconv.Itoa(result.HttpStatus) + case "$IP": + element = result.Ip + default: + } + sanitizedList = append(sanitizedList, element) + } + return sanitizedList +} diff --git a/main.go b/main.go index 65327194..309a1804 100644 --- a/main.go +++ b/main.go @@ -3,15 +3,13 @@ package main import ( "fmt" "github.com/TwinProduction/gatus/config" - "github.com/TwinProduction/gatus/watchdog" ) func main() { - request := watchdog.Request{Url: "https://twinnation.org/actuator/health"} - result := &watchdog.Result{} - request.GetIp(result) - request.GetStatus(result) - fmt.Println(result) - - fmt.Println(config.Get()) + for _, service := range config.Get().Services { + result := service.EvaluateConditions() + for _, conditionResult := range result.ConditionResult { + fmt.Printf("%v\n", *conditionResult) + } + } } diff --git a/watchdog/watchdog.go b/watchdog/watchdog.go index 8ae4c024..03fb525e 100644 --- a/watchdog/watchdog.go +++ b/watchdog/watchdog.go @@ -1,49 +1 @@ package watchdog - -import ( - "net" - "net/http" - "net/url" - "time" -) - -type Request struct { - Url string -} - -type Result struct { - HttpStatus int - Hostname string - Ip string - Duration time.Duration - Errors []error -} - -func (request *Request) GetIp(result *Result) { - urlObject, err := url.Parse(request.Url) - if err != nil { - result.Errors = append(result.Errors, err) - return - } - result.Hostname = urlObject.Hostname() - ips, err := net.LookupIP(urlObject.Hostname()) - if err != nil { - result.Errors = append(result.Errors, err) - return - } - result.Ip = ips[0].String() -} - -func (request *Request) GetStatus(result *Result) { - client := &http.Client{ - Timeout: time.Second * 10, - } - startTime := time.Now() - response, err := client.Get(request.Url) - if err != nil { - result.Errors = append(result.Errors, err) - return - } - result.Duration = time.Now().Sub(startTime) - result.HttpStatus = response.StatusCode -}