// 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] 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 }