diff --git a/cmd/health.go b/cmd/health.go index 5f41dd0..f2be6ec 100644 --- a/cmd/health.go +++ b/cmd/health.go @@ -180,6 +180,16 @@ var healthCmd = &cobra.Command{ check.ExitError(err) } + // Enable some backwards compatibility + // Can be changed to a switch statement in the future, + // when more versions need special cases + // For Logstash 6, we assume a parsed JSON response + // is enough to declare the instance running, since there + // is no status field. + if stat.MajorVersion == 6 { + stat.Status = "green" + } + // Logstash Health Status switch stat.Status { default: diff --git a/cmd/health_test.go b/cmd/health_test.go index 732bc29..71f5b95 100644 --- a/cmd/health_test.go +++ b/cmd/health_test.go @@ -29,6 +29,47 @@ type HealthTest struct { expected string } +func TestHealthCmd_Logstash6(t *testing.T) { + tests := []HealthTest{ + { + name: "version-error", + server: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + w.Write([]byte(`{"host":"logstash","version":"foo"}`)) + })), + args: []string{"run", "../main.go", "health"}, + expected: "UNKNOWN - Could not determine version", + }, + { + name: "health-ok", + server: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + w.Write([]byte(`{"host":"logstash","version":"6.8.23","http_address":"0.0.0.0:9600","id":"123","name":"logstash","jvm":{"threads":{"count":1,"peak_count":2},"mem":{},"gc":{},"uptime_in_millis":123},"process":{},"events":{},"pipelines":{"main":{}},"reloads":{"failures":0,"successes":0},"os":{}}`)) + })), + args: []string{"run", "../main.go", "health"}, + expected: "OK - Logstash is healthy", + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + defer test.server.Close() + + // We need the random Port extracted + u, _ := url.Parse(test.server.URL) + cmd := exec.Command("go", append(test.args, "--port", u.Port())...) + out, _ := cmd.CombinedOutput() + + actual := string(out) + + if !strings.Contains(actual, test.expected) { + t.Error("\nActual: ", actual, "\nExpected: ", test.expected) + } + + }) + } +} + func TestHealthCmd_Logstash7(t *testing.T) { tests := []HealthTest{ { diff --git a/internal/logstash/api.go b/internal/logstash/api.go index b15c691..e6e861e 100644 --- a/internal/logstash/api.go +++ b/internal/logstash/api.go @@ -1,5 +1,12 @@ package logstash +import ( + "encoding/json" + "fmt" + "strconv" + "strings" +) + // https://www.elastic.co/guide/en/logstash/current/node-stats-api.html type Pipeline struct { @@ -44,9 +51,40 @@ type JVM struct { } type Stat struct { - Host string `json:"host"` - Version string `json:"version"` - Status string `json:"status"` - Process Process `json:"process"` - Jvm JVM `json:"jvm"` + Host string `json:"host"` + Version string `json:"version"` + Status string `json:"status"` + Process Process `json:"process"` + Jvm JVM `json:"jvm"` + MajorVersion int +} + +// Custom Unmarshal since we might want to add or parse +// further fields in the future. This is simpler to extend and +// to test here than during the CheckPlugin logic. +func (s *Stat) UnmarshalJSON(b []byte) error { + type Temp Stat + + t := (*Temp)(s) + + err := json.Unmarshal(b, t) + + if err != nil { + return err + } + + // Could also use some semver package, + // but decided against the depedency + if s.Version != "" { + v := strings.Split(s.Version, ".") + majorVersion, convErr := strconv.Atoi(v[0]) + + if convErr != nil { + return fmt.Errorf("Could not determine version") + } + + s.MajorVersion = majorVersion + } + + return nil }