191 lines
4.5 KiB
Go
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
|
|
}
|