open-nomad/command/alloc_checks.go
2023-04-10 15:36:59 +00:00

191 lines
4.5 KiB
Go

// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package command
import (
"fmt"
"strings"
"time"
"github.com/hashicorp/nomad/api"
"github.com/hashicorp/nomad/api/contexts"
"github.com/posener/complete"
)
type AllocChecksCommand struct {
Meta
}
func (c *AllocChecksCommand) Help() string {
helpText := `
Usage: nomad alloc checks [options] <allocation>
Alias: nomad checks
Outputs the latest health check status information for services in the allocation
using the Nomad service discovery provider.
General Options:
` + generalOptionsUsage(usageOptsDefault) + `
Checks Specific Options:
-verbose
Show full information.
-json
Output the latest health check status information in a JSON format.
-t
Format and display latest health check status information using a Go template.
`
return strings.TrimSpace(helpText)
}
func (c *AllocChecksCommand) Synopsis() string {
return "Outputs service health check status information."
}
func (c *AllocChecksCommand) AutocompleteFlags() complete.Flags {
return mergeAutocompleteFlags(c.Meta.AutocompleteFlags(FlagSetClient),
complete.Flags{
"-verbose": complete.PredictNothing,
"-json": complete.PredictNothing,
"-t": complete.PredictAnything,
})
}
func (c *AllocChecksCommand) AutocompleteArgs() complete.Predictor {
return complete.PredictFunc(func(a complete.Args) []string {
client, err := c.Meta.Client()
if err != nil {
return nil
}
resp, _, err := client.Search().PrefixSearch(a.Last, contexts.Allocs, nil)
if err != nil {
return nil
}
return resp.Matches[contexts.Allocs]
})
}
func (c *AllocChecksCommand) Name() string {
return "alloc checks"
}
func (c *AllocChecksCommand) Run(args []string) int {
var json, verbose bool
var tmpl string
flags := c.Meta.FlagSet(c.Name(), FlagSetClient)
flags.Usage = func() { c.Ui.Output(c.Help()) }
flags.BoolVar(&verbose, "verbose", false, "")
flags.BoolVar(&json, "json", false, "")
flags.StringVar(&tmpl, "t", "", "")
if err := flags.Parse(args); err != nil {
return 1
}
// Check that we got only one argument
args = flags.Args()
if numArgs := len(args); numArgs < 1 {
c.Ui.Error("An allocation ID is required")
c.Ui.Error(commandErrorText(c))
return 1
} else if numArgs > 1 {
c.Ui.Error("This command takes one argument (allocation ID)")
c.Ui.Error(commandErrorText(c))
return 1
}
client, err := c.Meta.Client()
if err != nil {
c.Ui.Error(fmt.Sprintf("Error initializing client: %v", err))
return 1
}
allocID := args[0]
// Truncate the id unless full length is requested
length := shortId
if verbose {
length = fullId
}
// Query the allocation info
if len(allocID) == 1 {
c.Ui.Error("Alloc ID must contain at least two characters.")
return 1
}
allocID = sanitizeUUIDPrefix(allocID)
allocations, _, err := client.Allocations().PrefixList(allocID)
if err != nil {
c.Ui.Error(fmt.Sprintf("Error querying allocation: %v", err))
return 1
}
if len(allocations) == 0 {
c.Ui.Error(fmt.Sprintf("No allocation(s) with prefix or id %q found", allocID))
return 1
}
if len(allocations) > 1 {
out := formatAllocListStubs(allocations, verbose, length)
c.Ui.Error(fmt.Sprintf("Prefix matched multiple allocations\n\n%s", out))
return 1
}
// prefix lookup matched single allocation (happy path), lookup the checks
q := &api.QueryOptions{Namespace: allocations[0].Namespace}
checks, err := client.Allocations().Checks(allocations[0].ID, q)
if err != nil {
c.Ui.Error(fmt.Sprintf("Error querying allocation checks: %s", err))
return 1
}
if json || len(tmpl) > 0 {
out, err := Format(json, tmpl, checks)
if err != nil {
c.Ui.Error(err.Error())
return 1
}
c.Ui.Output(out)
return 0
}
c.Ui.Output(fmt.Sprintf("Status of %d Nomad Service Checks", len(checks)))
c.Ui.Output("")
pair := func(key, value string) string { return fmt.Sprintf("%s|=|%s", key, value) }
taskFmt := func(s string) string {
if s == "" {
return "(group)"
}
return s
}
for _, check := range checks {
list := []string{
pair("ID", check.ID),
pair("Name", check.Check),
pair("Group", check.Group),
pair("Task", taskFmt(check.Task)),
pair("Service", check.Service),
pair("Status", check.Status),
}
if check.StatusCode > 0 {
list = append(list, pair("StatusCode", fmt.Sprintf("%d", check.StatusCode)))
}
list = append(list,
pair("Mode", check.Mode),
pair("Timestamp", formatTaskTimes(time.Unix(check.Timestamp, 0))),
pair("Output", check.Output),
)
c.Ui.Output(formatList(list))
c.Ui.Output("")
}
return 0
}