diff --git a/README.md b/README.md index ad13dd0..5a919f8 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ #### Requirements - [Golang](https://golang.org/) >= 1 - - [Sensu](http://sensuapp.org/) >= 0.13 + - [Sensu](http://sensuapp.org/) >= 0.19 #### Documents @@ -60,6 +60,7 @@ If a datacenter is not specified, use first of `datacenters`. checks List locally defined checks and request executions request Issues a check execution request events List and resolve current events + results List current check results resolve Resolves an event silence Create, list, and delete silence stashes health Check the status of the API's transport & Redis connections, and query the transport's status diff --git a/main.go b/main.go index ec68f50..09aa9cc 100644 --- a/main.go +++ b/main.go @@ -157,6 +157,22 @@ func main() { }, }) + rootCmd.AddCommand(&cobra.Command{ + Use: "results [client] [check]", + Short: "List current check results", + Long: "results Returns a list of current check results for all clients\nresults [client] Returns a list of current check results for a given client\nresults [client] [check] Returns a check result for a given client & check name", + Run: func(cmd *cobra.Command, args []string) { + switch len(args) { + case 0: + fmt.Print(ohgi.GetResults(sensu.DefaultAPI)) + case 1: + fmt.Print(ohgi.GetResultsClient(sensu.DefaultAPI, args[0])) + case 2: + fmt.Print(ohgi.GetResultsClientCheck(sensu.DefaultAPI, args[0], args[1])) + } + }, + }) + silenceCmd := &cobra.Command{ Use: "silence [client] [check]", Short: "Create, list, and delete silence stashes", diff --git a/ohgi/checks.go b/ohgi/checks.go index edf36b8..29ae187 100644 --- a/ohgi/checks.go +++ b/ohgi/checks.go @@ -18,13 +18,13 @@ func GetChecks(api *sensu.API) string { return "No checks\n" } - result := []byte(bold("NAME COMMAND INTERVAL\n")) + print := []byte(bold("NAME COMMAND INTERVAL\n")) for _, check := range checks { line = fillSpace(check.Name, 30) + fillSpace(check.Command, 60) + strconv.Itoa(check.Interval) + "\n" - result = append(result, line...) + print = append(print, line...) } - return string(result) + return string(print) } func GetChecksWildcard(api *sensu.API, pattern string) string { @@ -47,27 +47,27 @@ func GetChecksWildcard(api *sensu.API, pattern string) string { return "No checks\n" } - result := []byte(bold("NAME COMMAND INTERVAL\n")) + print := []byte(bold("NAME COMMAND INTERVAL\n")) for _, i := range matches { check := checks[i] line = fillSpace(check.Name, 30) + fillSpace(check.Command, 60) + strconv.Itoa(check.Interval) + "\n" - result = append(result, line...) + print = append(print, line...) } - return string(result) + return string(print) } func GetChecksCheck(api *sensu.API, name string) string { - var result []byte + var print []byte check, err := api.GetChecksCheck(name) checkError(err) - result = append(result, (bold("NAME ") + check.Name + "\n")...) - result = append(result, (bold("COMMAND ") + check.Command + "\n")...) - result = append(result, (bold("SUBSCRIBERS ") + strings.Join(check.Subscribers, ", ") + "\n")...) - result = append(result, (bold("INTERVAL ") + strconv.Itoa(check.Interval) + "\n")...) - result = append(result, (bold("HANDLERS ") + strings.Join(check.Handlers, ", ") + "\n")...) + print = append(print, (bold("NAME ") + check.Name + "\n")...) + print = append(print, (bold("COMMAND ") + check.Command + "\n")...) + print = append(print, (bold("SUBSCRIBERS ") + strings.Join(check.Subscribers, ", ") + "\n")...) + print = append(print, (bold("INTERVAL ") + strconv.Itoa(check.Interval) + "\n")...) + print = append(print, (bold("HANDLERS ") + strings.Join(check.Handlers, ", ") + "\n")...) - return string(result) + return string(print) } diff --git a/ohgi/clients.go b/ohgi/clients.go index 412859c..228d1b2 100644 --- a/ohgi/clients.go +++ b/ohgi/clients.go @@ -17,13 +17,13 @@ func GetClients(api *sensu.API, limit int, offset int) string { return "No clients\n" } - result := []byte(bold("NAME ADDRESS TIMESTAMP\n")) + print := []byte(bold("NAME ADDRESS TIMESTAMP\n")) for _, client := range clients { line = fillSpace(client.Name, 40) + fillSpace(client.Address, 40) + utoa(client.Timestamp) + "\n" - result = append(result, line...) + print = append(print, line...) } - return string(result) + return string(print) } func GetClientsWildcard(api *sensu.API, pattern string) string { @@ -46,29 +46,29 @@ func GetClientsWildcard(api *sensu.API, pattern string) string { return "No clients\n" } - result := []byte(bold("NAME ADDRESS TIMESTAMP\n")) + print := []byte(bold("NAME ADDRESS TIMESTAMP\n")) for _, i := range matches { client := clients[i] line = fillSpace(client.Name, 40) + fillSpace(client.Address, 40) + utoa(client.Timestamp) + "\n" - result = append(result, line...) + print = append(print, line...) } - return string(result) + return string(print) } func GetClientsClient(api *sensu.API, name string) string { - var result []byte + var print []byte client, err := api.GetClientsClient(name) checkError(err) - result = append(result, (bold("NAME ") + client.Name + "\n")...) - result = append(result, (bold("ADDRESS ") + client.Address + "\n")...) - result = append(result, (bold("SUBSCRIPTIONS ") + strings.Join(client.Subscriptions, ", ") + "\n")...) - result = append(result, (bold("TIMESTAMP ") + utoa(client.Timestamp) + "\n")...) - result = append(result, (bold("VERSION ") + client.Version + "\n")...) + print = append(print, (bold("NAME ") + client.Name + "\n")...) + print = append(print, (bold("ADDRESS ") + client.Address + "\n")...) + print = append(print, (bold("SUBSCRIPTIONS ") + strings.Join(client.Subscriptions, ", ") + "\n")...) + print = append(print, (bold("TIMESTAMP ") + utoa(client.Timestamp) + "\n")...) + print = append(print, (bold("VERSION ") + client.Version + "\n")...) - return string(result) + return string(print) } func PostClients(api *sensu.API, name string, address string, subscriptions []string) string { diff --git a/ohgi/events.go b/ohgi/events.go index c735271..0fddd84 100644 --- a/ohgi/events.go +++ b/ohgi/events.go @@ -17,60 +17,60 @@ func GetEvents(api *sensu.API) string { return "No current events\n" } - result := []byte(bold(" CLIENT CHECK # EXECUTED\n")) + print := []byte(bold(" CLIENT CHECK # EXECUTED\n")) for _, event := range events { line = indicateStatus(event.Check.Status) + fillSpace(event.Client.Name, 40) + fillSpace(event.Check.Name, 30) + fillSpace(strconv.Itoa(event.Occurrences), 10) + utoa(event.Check.Executed) + "\n" - result = append(result, line...) + print = append(print, line...) } - return string(result) + return string(print) } func GetEventsClient(api *sensu.API, client string) string { var line, output string - events, err := api.GetEvents() + events, err := api.GetEventsClient(client) checkError(err) if len(events) == 0 { return "No current events for " + client + "\n" } - result := []byte(bold(" CHECK OUTPUT EXECUTED\n")) + print := []byte(bold(" CHECK OUTPUT EXECUTED\n")) for _, event := range events { output = strings.Replace(event.Check.Output, "\n", " ", -1) line = indicateStatus(event.Check.Status) + fillSpace(event.Check.Name, 30) + fillSpace(output, 50) + utoa(event.Check.Executed) + "\n" - result = append(result, line...) + print = append(print, line...) } - return string(result) + return string(print) } func GetEventsClientCheck(api *sensu.API, client string, check string) string { - var result []byte + var print []byte event, err := api.GetEventsClientCheck(client, check) checkError(err) - result = append(result, (bold("CLIENT ") + event.Client.Name + "\n")...) - result = append(result, (bold("ADDRESS ") + event.Client.Address + "\n")...) - result = append(result, (bold("SUBSCRIPTIONS ") + strings.Join(event.Client.Subscriptions, ", ") + "\n")...) - result = append(result, (bold("TIMESTAMP ") + utoa(event.Client.Timestamp) + "\n")...) - result = append(result, (bold("CHECK ") + event.Check.Name + "\n")...) - result = append(result, (bold("COMMAND ") + event.Check.Command + "\n")...) - result = append(result, (bold("SUBSCRIBERS ") + strings.Join(event.Check.Subscribers, ", ") + "\n")...) - result = append(result, (bold("INTERVAL ") + strconv.Itoa(event.Check.Interval) + "\n")...) - result = append(result, (bold("HANDLERS ") + strings.Join(event.Check.Handlers, ", ") + "\n")...) - result = append(result, (bold("ISSUED ") + utoa(event.Check.Issued) + "\n")...) - result = append(result, (bold("EXECUTED ") + utoa(event.Check.Executed) + "\n")...) - result = append(result, (bold("OUTPUT ") + strings.Replace(event.Check.Output, "\n", " ", -1) + "\n")...) - result = append(result, (bold("STATUS ") + paintStatus(event.Check.Status) + "\n")...) - result = append(result, (bold("DURATION ") + strconv.FormatFloat(event.Check.Duration, 'f', 3, 64) + "\n")...) - result = append(result, (bold("HISTORY ") + paintHistory(strings.Join(event.Check.History, ", ")) + "\n")...) - result = append(result, (bold("OCCURRENCES ") + strconv.Itoa(event.Occurrences) + "\n")...) - result = append(result, (bold("ACTION ") + event.Action + "\n")...) - - return string(result) + print = append(print, (bold("CLIENT ") + event.Client.Name + "\n")...) + print = append(print, (bold("ADDRESS ") + event.Client.Address + "\n")...) + print = append(print, (bold("SUBSCRIPTIONS ") + strings.Join(event.Client.Subscriptions, ", ") + "\n")...) + print = append(print, (bold("TIMESTAMP ") + utoa(event.Client.Timestamp) + "\n")...) + print = append(print, (bold("CHECK ") + event.Check.Name + "\n")...) + print = append(print, (bold("COMMAND ") + event.Check.Command + "\n")...) + print = append(print, (bold("SUBSCRIBERS ") + strings.Join(event.Check.Subscribers, ", ") + "\n")...) + print = append(print, (bold("INTERVAL ") + strconv.Itoa(event.Check.Interval) + "\n")...) + print = append(print, (bold("HANDLERS ") + strings.Join(event.Check.Handlers, ", ") + "\n")...) + print = append(print, (bold("ISSUED ") + utoa(event.Check.Issued) + "\n")...) + print = append(print, (bold("EXECUTED ") + utoa(event.Check.Executed) + "\n")...) + print = append(print, (bold("OUTPUT ") + strings.Replace(event.Check.Output, "\n", " ", -1) + "\n")...) + print = append(print, (bold("STATUS ") + paintStatus(event.Check.Status) + "\n")...) + print = append(print, (bold("DURATION ") + strconv.FormatFloat(event.Check.Duration, 'f', 3, 64) + "\n")...) + print = append(print, (bold("HISTORY ") + paintHistory(strings.Join(event.Check.History, ", ")) + "\n")...) + print = append(print, (bold("OCCURRENCES ") + strconv.Itoa(event.Occurrences) + "\n")...) + print = append(print, (bold("ACTION ") + event.Action + "\n")...) + + return string(print) } func DeleteEventsClientCheck(api *sensu.API, client string, check string) string { diff --git a/ohgi/history.go b/ohgi/history.go index f3b57ca..fac5e69 100644 --- a/ohgi/history.go +++ b/ohgi/history.go @@ -12,11 +12,11 @@ func GetClientsHistory(api *sensu.API, client string) string { return "No histories\n" } - result := []byte(bold("CHECK HISTORY TIMESTAMP\n")) + print := []byte(bold("CHECK HISTORY TIMESTAMP\n")) for _, history := range histories { line = fillSpace(history.Check, 30) + paintHistory(fillSpace(stoa(history.History, ", "), 48)) + utoa(history.LastExecution) + "\n" - result = append(result, line...) + print = append(print, line...) } - return string(result) + return string(print) } diff --git a/ohgi/info.go b/ohgi/info.go index b88cd19..7cdef93 100644 --- a/ohgi/info.go +++ b/ohgi/info.go @@ -7,14 +7,14 @@ import ( ) func GetInfo(api *sensu.API) string { - var result []byte + var print []byte info, err := api.GetInfo() checkError(err) - result = append(result, (bold("VERSION ") + info.Sensu.Version + "\n")...) - result = append(result, (bold("TRANSPORT ") + strconv.FormatBool(info.Transport.Connected) + "\n")...) - result = append(result, (bold("REDIS ") + strconv.FormatBool(info.Redis.Connected) + "\n")...) + print = append(print, (bold("VERSION ") + info.Sensu.Version + "\n")...) + print = append(print, (bold("TRANSPORT ") + strconv.FormatBool(info.Transport.Connected) + "\n")...) + print = append(print, (bold("REDIS ") + strconv.FormatBool(info.Redis.Connected) + "\n")...) - return string(result) + return string(print) } diff --git a/ohgi/misc.go b/ohgi/misc.go index 299fd72..2fdbfa5 100644 --- a/ohgi/misc.go +++ b/ohgi/misc.go @@ -23,13 +23,13 @@ func utoa(timestamp int64) string { } func stoa(arr []int, sep string) string { - var result []byte + var print []byte for _, i := range arr { - result = append(result, (strconv.Itoa(i) + sep)...) + print = append(print, (strconv.Itoa(i) + sep)...) } - return string(result) + return string(print) } func stoe(expiration string) int64 { diff --git a/ohgi/results.go b/ohgi/results.go new file mode 100644 index 0000000..7e219ef --- /dev/null +++ b/ohgi/results.go @@ -0,0 +1,68 @@ +package ohgi + +import ( + "strconv" + "strings" + + "../sensu" +) + +func GetResults(api *sensu.API) string { + var line string + + results, err := api.GetResults() + checkError(err) + + if len(results) == 0 { + return "No current check results\n" + } + + print := []byte(bold(" CLIENT CHECK EXECUTED\n")) + for _, result := range results { + line = indicateStatus(result.Check.Status) + fillSpace(result.Client, 40) + fillSpace(result.Check.Name, 30) + utoa(result.Check.Executed) + "\n" + print = append(print, line...) + } + + return string(print) +} + +func GetResultsClient(api *sensu.API, client string) string { + var line, output string + + results, err := api.GetResultsClient(client) + checkError(err) + + if len(results) == 0 { + return "No current check results for " + client + "\n" + } + + print := []byte(bold(" CHECK OUTPUT EXECUTED\n")) + for _, result := range results { + output = strings.Replace(result.Check.Output, "\n", " ", -1) + line = indicateStatus(result.Check.Status) + fillSpace(result.Check.Name, 30) + fillSpace(output, 50) + utoa(result.Check.Executed) + "\n" + print = append(print, line...) + } + + return string(print) +} + +func GetResultsClientCheck(api *sensu.API, client string, check string) string { + var print []byte + + result, err := api.GetResultsClientCheck(client, check) + checkError(err) + + print = append(print, (bold("CLIENT ") + result.Client + "\n")...) + print = append(print, (bold("CHECK ") + result.Check.Name + "\n")...) + print = append(print, (bold("COMMAND ") + result.Check.Command + "\n")...) + print = append(print, (bold("SUBSCRIBERS ") + strings.Join(result.Check.Subscribers, ", ") + "\n")...) + print = append(print, (bold("INTERVAL ") + strconv.Itoa(result.Check.Interval) + "\n")...) + print = append(print, (bold("HANDLERS ") + strings.Join(result.Check.Handlers, ", ") + "\n")...) + print = append(print, (bold("ISSUED ") + utoa(result.Check.Issued) + "\n")...) + print = append(print, (bold("EXECUTED ") + utoa(result.Check.Executed) + "\n")...) + print = append(print, (bold("OUTPUT ") + strings.Replace(result.Check.Output, "\n", " ", -1) + "\n")...) + print = append(print, (bold("STATUS ") + paintStatus(result.Check.Status) + "\n")...) + print = append(print, (bold("DURATION ") + strconv.FormatFloat(result.Check.Duration, 'f', 3, 64) + "\n")...) + + return string(print) +} diff --git a/ohgi/silence.go b/ohgi/silence.go index 35d036f..3e0cc45 100644 --- a/ohgi/silence.go +++ b/ohgi/silence.go @@ -31,7 +31,7 @@ func GetSilence(api *sensu.API) string { return "No silence stashes\n" } - result := []byte(bold("CLIENT CHECK REASON EXPIRATION\n")) + print := []byte(bold("CLIENT CHECK REASON EXPIRATION\n")) for _, silence := range silences { path = strings.Split(silence.Path, "/") if path[0] != "silence" { @@ -47,10 +47,10 @@ func GetSilence(api *sensu.API) string { } line = fillSpace(path[1], 40) + fillSpace(path[2], 30) + fillSpace(silence.Content.Reason, 30) + expire + "\n" - result = append(result, line...) + print = append(print, line...) } - return string(result) + return string(print) } func PostSilence(api *sensu.API, client string, check string, expiration string, reason string) string { diff --git a/ohgi/version.go b/ohgi/version.go index bb17c1d..5398345 100644 --- a/ohgi/version.go +++ b/ohgi/version.go @@ -28,19 +28,19 @@ func verCheck(version string) <-chan *latest.CheckResponse { } func Version(version string) string { - var result []byte - result = append(result, fmt.Sprintf("ohgi version %s\n", version)...) + var print []byte + print = append(print, fmt.Sprintf("ohgi version %s\n", version)...) verCheckCh := verCheck(version) for { select { case res := <-verCheckCh: if res != nil && res.Outdated { - result = append(result, fmt.Sprintf("Latest version of ohgi is %s, please update it\n", res.Current)...) + print = append(print, fmt.Sprintf("Latest version of ohgi is %s, please update it\n", res.Current)...) } - return string(result) + return string(print) case <-time.After(TIMEOUT_SEC * time.Second): - return string(result) + return string(print) } } } diff --git a/sensu/results.go b/sensu/results.go new file mode 100644 index 0000000..a6b7a68 --- /dev/null +++ b/sensu/results.go @@ -0,0 +1,68 @@ +package sensu + +import ( + "encoding/json" + "errors" +) + +type ResultStruct struct { + Client string `json:"client"` + Check CheckStruct `json:"check"` +} + +// Returns a list of current check results for all clients. +func (api API) GetResults() ([]ResultStruct, error) { + var results []ResultStruct + + response, err := api.get("/results") + if err != nil { + return results, err + } else if response.StatusCode != 200 { + return results, errors.New("sensu: " + StatusCodeToString(response.StatusCode)) + } + + err = json.Unmarshal([]byte(response.Body), &results) + if err != nil { + return results, err + } + + return results, nil +} + +// Returns a list of current check results for a given client. +func (api API) GetResultsClient(client string) ([]ResultStruct, error) { + var results []ResultStruct + + response, err := api.get("/results/" + client) + if err != nil { + return results, err + } else if response.StatusCode != 200 { + return results, errors.New("sensu: " + StatusCodeToString(response.StatusCode)) + } + + err = json.Unmarshal([]byte(response.Body), &results) + if err != nil { + return results, err + } + + return results, nil +} + +// Returns a check result for a given client & check name. +func (api API) GetResultsClientCheck(client string, check string) (ResultStruct, error) { + var result ResultStruct + + response, err := api.get("/result/" + client + "/" + check) + if err != nil { + return result, err + } else if response.StatusCode != 200 { + return result, errors.New("sensu: " + StatusCodeToString(response.StatusCode)) + } + + err = json.Unmarshal([]byte(response.Body), &result) + if err != nil { + return result, err + } + + return result, nil +}