From 5c15dc79ae6495a38cdff5074760fb65c14e8c40 Mon Sep 17 00:00:00 2001 From: Lorenzo Setale Date: Sun, 15 Nov 2020 23:08:04 +0100 Subject: [PATCH] shows allocation status Signed-off-by: Lorenzo Setale --- cmd/status.go | 112 +++++++++++++++++++++++++++++++++++++++++++++ cmd/stop.go | 5 +- cmd/utils.go | 5 ++ conn/client.go | 7 +-- conn/client_job.go | 17 +++++++ 5 files changed, 142 insertions(+), 4 deletions(-) create mode 100644 cmd/status.go diff --git a/cmd/status.go b/cmd/status.go new file mode 100644 index 0000000..1ebc463 --- /dev/null +++ b/cmd/status.go @@ -0,0 +1,112 @@ +package cmd + +import ( + "fmt" + "io/ioutil" + "log" + "os" + "text/tabwriter" + "time" + + "github.com/hashicorp/nomad/api" + "github.com/spf13/cobra" + "gitlab.com/qm64/backpack/conn" + "gitlab.com/qm64/backpack/pkg" + "gitlab.com/qm64/backpack/templating" +) + +// statusCmd represents the status command +var statusCmd = &cobra.Command{ + Use: "status [path]", + Aliases: []string{"state", "alloc"}, + Args: cobra.ExactArgs(1), + Short: "Check the status of all the jobs in a pack", + Long: `Run this command to know the status of all the jobs in a pack. It will +check and provide useful information. +`, + Run: statusRun, +} + +func init() { + rootCmd.AddCommand(statusCmd) + statusCmd.Flags().BoolP("all", "a", false, "show all allocations") + statusCmd.Flags().BoolP("unpacked", "u", false, "instead of reading from a file, read from a directory") +} + +var showAllocationsWithStatus = map[string]bool{ + api.AllocClientStatusRunning: true, + api.AllocClientStatusPending: true, +} + +// This is the actual command.. +func statusRun(cmd *cobra.Command, args []string) { + b := getPackFromCLIInput(cmd, args) + var err error + + client, err := conn.NewClient() + if err != nil { + log.Fatalf("Error creating new Nomad Client: %s", err) + } + + showAllAlloc, err := cmd.Flags().GetBool("all") + if err != nil { + log.Fatalf("Error parsing CLI flags (all): %s", err) + } + + // Populate the template into job files 💪 + bts, err := templating.BuildHCL(&b, pkg.ValuesType{}) + if err != nil { + log.Fatalf("Error building the HCL files: %s", err) + } + + // Prepare a table for the output in a buffer. This is done so that we can + // have a table after outputting the Plans for each job + rt, wt, err := os.Pipe() + if err != nil { + log.Fatal("Error preparing the output table:", err) + } + + defer rt.Close() + w := tabwriter.NewWriter(wt, 3, 0, 4, ' ', 0) + fmt.Fprintf(w, "Status of the jobs' allocations from \"%s\" backpack:\n", b.Name) + fmt.Fprintln(w, "Job ID\tAlloc ID\tStatus/Desired\tModified At\tError") + + for name, hcl := range bts { + job, err := client.GetJobFromCode(string(hcl)) + if err != nil { + log.Fatalf("Error obtaining job %s: %s", name, err) + } + + jobResult, err := client.GetJobStatus(*job.ID) + if err != nil { + fmt.Fprintf(w, "%s\t\t%s\t\t%s\n", *job.ID, *job.Status, err) + continue + } + + allocations, err := client.GetJobAllocations(*job.ID) + if err != nil { + fmt.Fprintf(w, "%s\t\t%s\t\t%s\n", *job.ID, *jobResult.Status, err) + continue + } + + // fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t\n", *jobResult.ID, "(check allocations)", *jobResult.Status, "", "") + for i, alloc := range allocations { + _, ok := showAllocationsWithStatus[alloc.ClientStatus] + if !showAllAlloc && len(allocations) > 1 && i != 0 && !ok { + continue + } + lt := time.Unix(0, alloc.ModifyTime).Format(time.RFC3339) + allocID := sanitizeUUIDPrefix(alloc.ID) + fmt.Fprintf(w, "%s\t%s\t%s/%s\t%s\t\n", *jobResult.ID, allocID, alloc.ClientStatus, alloc.DesiredStatus, lt) + } + + } + // Flushes all the table output after all the plans output. + w.Flush() + wt.Close() + output, err := ioutil.ReadAll(rt) + if err != nil { + log.Fatal("Error reading the output table after operation completed:", err) + } + os.Stdout.Write(output) +} diff --git a/cmd/stop.go b/cmd/stop.go index 8c00287..bc80011 100644 --- a/cmd/stop.go +++ b/cmd/stop.go @@ -19,7 +19,10 @@ var stopCmd = &cobra.Command{ Aliases: []string{"uninstall", "delete"}, Args: cobra.ExactArgs(1), Short: "Stop all the jobs in a pack", - Long: ` + Long: `This command will stop all the jobs available in a pack. By default it +mimics nomad CLI, that keeps the job as "dead" until the garbage collector +deletes the job. If you want to delete the jobs entirely and lose all the setup +you can pass the option --purge (or -p). `, Run: stopRun, } diff --git a/cmd/utils.go b/cmd/utils.go index 66fb9e4..9cb0812 100644 --- a/cmd/utils.go +++ b/cmd/utils.go @@ -7,6 +7,7 @@ import ( "net/http" "net/url" "os" + "strings" "github.com/spf13/cobra" "gitlab.com/qm64/backpack/pkg" @@ -118,3 +119,7 @@ func getValuesFromCLIInput(cmd *cobra.Command) pkg.ValuesType { } return values } + +func sanitizeUUIDPrefix(s string) string { + return strings.Split(s, "-")[0] +} diff --git a/conn/client.go b/conn/client.go index 8c46cd0..f7ff03e 100644 --- a/conn/client.go +++ b/conn/client.go @@ -5,8 +5,9 @@ import ( ) type Client struct { - c *api.Client - jobs *api.Jobs + c *api.Client + jobs *api.Jobs + alloc *api.Allocations } // NewClient returns a new client configured with the default values for Nomad @@ -18,7 +19,7 @@ func NewClient() (co *Client, err error) { return nil, err } co.jobs = co.c.Jobs() - + co.alloc = co.c.Allocations() return } diff --git a/conn/client_job.go b/conn/client_job.go index 3b6dfcc..1beab31 100644 --- a/conn/client_job.go +++ b/conn/client_job.go @@ -13,3 +13,20 @@ func (co *Client) GetJobStatus(jobId string) (j *api.Job, err error) { j, _, err = co.jobs.Info(jobId, nil) return } + +func (co *Client) GetJobAllocations(jobId string) (alls []*api.Allocation, err error) { + allList, _, err := co.jobs.Allocations(jobId, false, nil) + if err != nil { + return + } + + alls = []*api.Allocation{} + for _, al := range allList { + alloc, _, err := co.alloc.Info(al.ID, nil) + if err != nil { + return []*api.Allocation{}, err + } + alls = append(alls, alloc) + } + return +}