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