github.com/ryanslade/nomad@v0.2.4-0.20160128061903-fc95782f2089/command/alloc_status.go (about) 1 package command 2 3 import ( 4 "fmt" 5 "sort" 6 "strings" 7 "time" 8 9 "github.com/hashicorp/nomad/api" 10 ) 11 12 type AllocStatusCommand struct { 13 Meta 14 } 15 16 func (c *AllocStatusCommand) Help() string { 17 helpText := ` 18 Usage: nomad alloc-status [options] <allocation> 19 20 Display information about existing allocations and its tasks. This command can 21 be used to inspect the current status of all allocation, including its running 22 status, metadata, and verbose failure messages reported by internal 23 subsystems. 24 25 General Options: 26 27 ` + generalOptionsUsage() + ` 28 29 30 -short 31 Display short output. Shows only the most recent task event. 32 33 -verbose 34 Show full information. 35 ` 36 37 return strings.TrimSpace(helpText) 38 } 39 40 func (c *AllocStatusCommand) Synopsis() string { 41 return "Display allocation status information and metadata" 42 } 43 44 func (c *AllocStatusCommand) Run(args []string) int { 45 var short, verbose bool 46 47 flags := c.Meta.FlagSet("alloc-status", FlagSetClient) 48 flags.Usage = func() { c.Ui.Output(c.Help()) } 49 flags.BoolVar(&short, "short", false, "") 50 flags.BoolVar(&verbose, "verbose", false, "") 51 52 if err := flags.Parse(args); err != nil { 53 return 1 54 } 55 56 // Check that we got exactly one allocation ID 57 args = flags.Args() 58 if len(args) != 1 { 59 c.Ui.Error(c.Help()) 60 return 1 61 } 62 allocID := args[0] 63 64 // Get the HTTP client 65 client, err := c.Meta.Client() 66 if err != nil { 67 c.Ui.Error(fmt.Sprintf("Error initializing client: %s", err)) 68 return 1 69 } 70 71 // Truncate the id unless full length is requested 72 length := shortId 73 if verbose { 74 length = fullId 75 } 76 77 // Query the allocation info 78 alloc, _, err := client.Allocations().Info(allocID, nil) 79 if err != nil { 80 if len(allocID) == 1 { 81 c.Ui.Error(fmt.Sprintf("Identifier must contain at least two characters.")) 82 return 1 83 } 84 if len(allocID)%2 == 1 { 85 // Identifiers must be of even length, so we strip off the last byte 86 // to provide a consistent user experience. 87 allocID = allocID[:len(allocID)-1] 88 } 89 90 allocs, _, err := client.Allocations().PrefixList(allocID) 91 if err != nil { 92 c.Ui.Error(fmt.Sprintf("Error querying allocation: %v", err)) 93 return 1 94 } 95 if len(allocs) == 0 { 96 c.Ui.Error(fmt.Sprintf("No allocation(s) with prefix or id %q found", allocID)) 97 return 1 98 } 99 if len(allocs) > 1 { 100 // Format the allocs 101 out := make([]string, len(allocs)+1) 102 out[0] = "ID|Eval ID|Job ID|Task Group|Desired Status|Client Status" 103 for i, alloc := range allocs { 104 out[i+1] = fmt.Sprintf("%s|%s|%s|%s|%s|%s", 105 limit(alloc.ID, length), 106 limit(alloc.EvalID, length), 107 alloc.JobID, 108 alloc.TaskGroup, 109 alloc.DesiredStatus, 110 alloc.ClientStatus, 111 ) 112 } 113 c.Ui.Output(fmt.Sprintf("Prefix matched multiple allocations\n\n%s", formatList(out))) 114 return 0 115 } 116 // Prefix lookup matched a single allocation 117 alloc, _, err = client.Allocations().Info(allocs[0].ID, nil) 118 if err != nil { 119 c.Ui.Error(fmt.Sprintf("Error querying allocation: %s", err)) 120 return 1 121 } 122 } 123 124 // Format the allocation data 125 basic := []string{ 126 fmt.Sprintf("ID|%s", limit(alloc.ID, length)), 127 fmt.Sprintf("Eval ID|%s", limit(alloc.EvalID, length)), 128 fmt.Sprintf("Name|%s", alloc.Name), 129 fmt.Sprintf("Node ID|%s", limit(alloc.NodeID, length)), 130 fmt.Sprintf("Job ID|%s", alloc.JobID), 131 fmt.Sprintf("Client Status|%s", alloc.ClientStatus), 132 fmt.Sprintf("Evaluated Nodes|%d", alloc.Metrics.NodesEvaluated), 133 fmt.Sprintf("Filtered Nodes|%d", alloc.Metrics.NodesFiltered), 134 fmt.Sprintf("Exhausted Nodes|%d", alloc.Metrics.NodesExhausted), 135 fmt.Sprintf("Allocation Time|%s", alloc.Metrics.AllocationTime), 136 fmt.Sprintf("Failures|%d", alloc.Metrics.CoalescedFailures), 137 } 138 c.Ui.Output(formatKV(basic)) 139 140 // Print the state of each task. 141 if short { 142 c.shortTaskStatus(alloc) 143 } else { 144 c.taskStatus(alloc) 145 } 146 147 // Format the detailed status 148 c.Ui.Output("\n==> Status") 149 dumpAllocStatus(c.Ui, alloc, length) 150 151 return 0 152 } 153 154 // shortTaskStatus prints out the current state of each task. 155 func (c *AllocStatusCommand) shortTaskStatus(alloc *api.Allocation) { 156 tasks := make([]string, 0, len(alloc.TaskStates)+1) 157 tasks = append(tasks, "Name|State|Last Event|Time") 158 for task := range c.sortedTaskStateIterator(alloc.TaskStates) { 159 fmt.Println(task) 160 state := alloc.TaskStates[task] 161 lastState := state.State 162 var lastEvent, lastTime string 163 164 l := len(state.Events) 165 if l != 0 { 166 last := state.Events[l-1] 167 lastEvent = last.Type 168 lastTime = c.formatUnixNanoTime(last.Time) 169 } 170 171 tasks = append(tasks, fmt.Sprintf("%s|%s|%s|%s", 172 task, lastState, lastEvent, lastTime)) 173 } 174 175 c.Ui.Output("\n==> Tasks") 176 c.Ui.Output(formatList(tasks)) 177 } 178 179 // taskStatus prints out the most recent events for each task. 180 func (c *AllocStatusCommand) taskStatus(alloc *api.Allocation) { 181 for task := range c.sortedTaskStateIterator(alloc.TaskStates) { 182 state := alloc.TaskStates[task] 183 events := make([]string, len(state.Events)+1) 184 events[0] = "Time|Type|Description" 185 186 size := len(state.Events) 187 for i, event := range state.Events { 188 formatedTime := c.formatUnixNanoTime(event.Time) 189 190 // Build up the description based on the event type. 191 var desc string 192 switch event.Type { 193 case api.TaskDriverFailure: 194 desc = event.DriverError 195 case api.TaskKilled: 196 desc = event.KillError 197 case api.TaskTerminated: 198 var parts []string 199 parts = append(parts, fmt.Sprintf("Exit Code: %d", event.ExitCode)) 200 201 if event.Signal != 0 { 202 parts = append(parts, fmt.Sprintf("Signal: %d", event.Signal)) 203 } 204 205 if event.Message != "" { 206 parts = append(parts, fmt.Sprintf("Exit Message: %q", event.Message)) 207 } 208 desc = strings.Join(parts, ", ") 209 } 210 211 // Reverse order so we are sorted by time 212 events[size-i] = fmt.Sprintf("%s|%s|%s", formatedTime, event.Type, desc) 213 } 214 215 c.Ui.Output(fmt.Sprintf("\n==> Task %q is %q\nRecent Events:", task, state.State)) 216 c.Ui.Output(formatList(events)) 217 } 218 } 219 220 // formatUnixNanoTime is a helper for formating time for output. 221 func (c *AllocStatusCommand) formatUnixNanoTime(nano int64) string { 222 t := time.Unix(0, nano) 223 return formatTime(t) 224 } 225 226 // sortedTaskStateIterator is a helper that takes the task state map and returns a 227 // channel that returns the keys in a sorted order. 228 func (c *AllocStatusCommand) sortedTaskStateIterator(m map[string]*api.TaskState) <-chan string { 229 output := make(chan string, len(m)) 230 keys := make([]string, len(m)) 231 i := 0 232 for k := range m { 233 keys[i] = k 234 i++ 235 } 236 sort.Strings(keys) 237 238 for _, key := range keys { 239 output <- key 240 } 241 242 close(output) 243 return output 244 }