github.com/djenriquez/nomad-1@v0.8.1/command/deployment_status.go (about) 1 package command 2 3 import ( 4 "fmt" 5 "sort" 6 "strings" 7 8 "github.com/hashicorp/nomad/api" 9 "github.com/hashicorp/nomad/api/contexts" 10 "github.com/posener/complete" 11 ) 12 13 type DeploymentStatusCommand struct { 14 Meta 15 } 16 17 func (c *DeploymentStatusCommand) Help() string { 18 helpText := ` 19 Usage: nomad deployment status [options] <deployment id> 20 21 Status is used to display the status of a deployment. The status will display 22 the number of desired changes as well as the currently applied changes. 23 24 General Options: 25 26 ` + generalOptionsUsage() + ` 27 28 Status Options: 29 30 -verbose 31 Display full information. 32 33 -json 34 Output the deployment in its JSON format. 35 36 -t 37 Format and display deployment using a Go template. 38 ` 39 return strings.TrimSpace(helpText) 40 } 41 42 func (c *DeploymentStatusCommand) Synopsis() string { 43 return "Display the status of a deployment" 44 } 45 46 func (c *DeploymentStatusCommand) AutocompleteFlags() complete.Flags { 47 return mergeAutocompleteFlags(c.Meta.AutocompleteFlags(FlagSetClient), 48 complete.Flags{ 49 "-verbose": complete.PredictNothing, 50 "-json": complete.PredictNothing, 51 "-t": complete.PredictAnything, 52 }) 53 } 54 55 func (c *DeploymentStatusCommand) AutocompleteArgs() complete.Predictor { 56 return complete.PredictFunc(func(a complete.Args) []string { 57 client, err := c.Meta.Client() 58 if err != nil { 59 return nil 60 } 61 62 resp, _, err := client.Search().PrefixSearch(a.Last, contexts.Deployments, nil) 63 if err != nil { 64 return []string{} 65 } 66 return resp.Matches[contexts.Deployments] 67 }) 68 } 69 70 func (c *DeploymentStatusCommand) Run(args []string) int { 71 var json, verbose bool 72 var tmpl string 73 74 flags := c.Meta.FlagSet("deployment status", FlagSetClient) 75 flags.Usage = func() { c.Ui.Output(c.Help()) } 76 flags.BoolVar(&verbose, "verbose", false, "") 77 flags.BoolVar(&json, "json", false, "") 78 flags.StringVar(&tmpl, "t", "", "") 79 80 if err := flags.Parse(args); err != nil { 81 return 1 82 } 83 84 // Check that we got no arguments 85 args = flags.Args() 86 if l := len(args); l != 1 { 87 c.Ui.Error(c.Help()) 88 return 1 89 } 90 91 dID := args[0] 92 93 // Truncate the id unless full length is requested 94 length := shortId 95 if verbose { 96 length = fullId 97 } 98 99 // Get the HTTP client 100 client, err := c.Meta.Client() 101 if err != nil { 102 c.Ui.Error(fmt.Sprintf("Error initializing client: %s", err)) 103 return 1 104 } 105 106 // Do a prefix lookup 107 deploy, possible, err := getDeployment(client.Deployments(), dID) 108 if err != nil { 109 c.Ui.Error(fmt.Sprintf("Error retrieving deployment: %s", err)) 110 return 1 111 } 112 113 if len(possible) != 0 { 114 c.Ui.Error(fmt.Sprintf("Prefix matched multiple deployments\n\n%s", formatDeployments(possible, length))) 115 return 1 116 } 117 118 if json || len(tmpl) > 0 { 119 out, err := Format(json, tmpl, deploy) 120 if err != nil { 121 c.Ui.Error(err.Error()) 122 return 1 123 } 124 125 c.Ui.Output(out) 126 return 0 127 } 128 129 c.Ui.Output(c.Colorize().Color(formatDeployment(deploy, length))) 130 return 0 131 } 132 133 func getDeployment(client *api.Deployments, dID string) (match *api.Deployment, possible []*api.Deployment, err error) { 134 // First attempt an immediate lookup if we have a proper length 135 if len(dID) == 36 { 136 d, _, err := client.Info(dID, nil) 137 if err != nil { 138 return nil, nil, err 139 } 140 141 return d, nil, nil 142 } 143 144 dID = strings.Replace(dID, "-", "", -1) 145 if len(dID) == 1 { 146 return nil, nil, fmt.Errorf("Identifier must contain at least two characters.") 147 } 148 if len(dID)%2 == 1 { 149 // Identifiers must be of even length, so we strip off the last byte 150 // to provide a consistent user experience. 151 dID = dID[:len(dID)-1] 152 } 153 154 // Have to do a prefix lookup 155 deploys, _, err := client.PrefixList(dID) 156 if err != nil { 157 return nil, nil, err 158 } 159 160 l := len(deploys) 161 switch { 162 case l == 0: 163 return nil, nil, fmt.Errorf("Deployment ID %q matched no deployments", dID) 164 case l == 1: 165 return deploys[0], nil, nil 166 default: 167 return nil, deploys, nil 168 } 169 } 170 171 func formatDeployment(d *api.Deployment, uuidLength int) string { 172 if d == nil { 173 return "No deployment found" 174 } 175 // Format the high-level elements 176 high := []string{ 177 fmt.Sprintf("ID|%s", limit(d.ID, uuidLength)), 178 fmt.Sprintf("Job ID|%s", d.JobID), 179 fmt.Sprintf("Job Version|%d", d.JobVersion), 180 fmt.Sprintf("Status|%s", d.Status), 181 fmt.Sprintf("Description|%s", d.StatusDescription), 182 } 183 184 base := formatKV(high) 185 if len(d.TaskGroups) == 0 { 186 return base 187 } 188 base += "\n\n[bold]Deployed[reset]\n" 189 base += formatDeploymentGroups(d, uuidLength) 190 return base 191 } 192 193 func formatDeploymentGroups(d *api.Deployment, uuidLength int) string { 194 // Detect if we need to add these columns 195 canaries, autorevert := false, false 196 tgNames := make([]string, 0, len(d.TaskGroups)) 197 for name, state := range d.TaskGroups { 198 tgNames = append(tgNames, name) 199 if state.AutoRevert { 200 autorevert = true 201 } 202 if state.DesiredCanaries > 0 { 203 canaries = true 204 } 205 } 206 207 // Sort the task group names to get a reliable ordering 208 sort.Strings(tgNames) 209 210 // Build the row string 211 rowString := "Task Group|" 212 if autorevert { 213 rowString += "Auto Revert|" 214 } 215 if canaries { 216 rowString += "Promoted|" 217 } 218 rowString += "Desired|" 219 if canaries { 220 rowString += "Canaries|" 221 } 222 rowString += "Placed|Healthy|Unhealthy" 223 224 rows := make([]string, len(d.TaskGroups)+1) 225 rows[0] = rowString 226 i := 1 227 for _, tg := range tgNames { 228 state := d.TaskGroups[tg] 229 row := fmt.Sprintf("%s|", tg) 230 if autorevert { 231 row += fmt.Sprintf("%v|", state.AutoRevert) 232 } 233 if canaries { 234 if state.DesiredCanaries > 0 { 235 row += fmt.Sprintf("%v|", state.Promoted) 236 } else { 237 row += fmt.Sprintf("%v|", "N/A") 238 } 239 } 240 row += fmt.Sprintf("%d|", state.DesiredTotal) 241 if canaries { 242 row += fmt.Sprintf("%d|", state.DesiredCanaries) 243 } 244 row += fmt.Sprintf("%d|%d|%d", state.PlacedAllocs, state.HealthyAllocs, state.UnhealthyAllocs) 245 rows[i] = row 246 i++ 247 } 248 249 return formatList(rows) 250 }