github.com/chenbh/concourse/v6@v6.4.2/fly/commands/workers.go (about) 1 package commands 2 3 import ( 4 "fmt" 5 "os" 6 "sort" 7 "strconv" 8 "strings" 9 "time" 10 11 "github.com/chenbh/concourse/v6/atc" 12 "github.com/chenbh/concourse/v6/fly/commands/internal/displayhelpers" 13 "github.com/chenbh/concourse/v6/fly/rc" 14 "github.com/chenbh/concourse/v6/fly/ui" 15 "github.com/fatih/color" 16 ) 17 18 type WorkersCommand struct { 19 Details bool `short:"d" long:"details" description:"Print additional information for each worker"` 20 Json bool `long:"json" description:"Print command result as JSON"` 21 } 22 23 func (command *WorkersCommand) Execute([]string) error { 24 target, err := rc.LoadTarget(Fly.Target, Fly.Verbose) 25 if err != nil { 26 return err 27 } 28 29 err = target.Validate() 30 if err != nil { 31 return err 32 } 33 34 workers, err := target.Client().ListWorkers() 35 if err != nil { 36 return err 37 } 38 39 if command.Json { 40 err = displayhelpers.JsonPrint(workers) 41 if err != nil { 42 return err 43 } 44 return nil 45 } 46 47 sort.Sort(byWorkerName(workers)) 48 49 var runningWorkers []worker 50 var stalledWorkers []worker 51 var outdatedWorkers []worker 52 for _, w := range workers { 53 if w.State == "stalled" { 54 stalledWorkers = append(stalledWorkers, worker{w, false}) 55 } else { 56 workerVersionCompatible, err := target.IsWorkerVersionCompatible(w.Version) 57 if err != nil { 58 return err 59 } 60 61 if !workerVersionCompatible { 62 outdatedWorkers = append(outdatedWorkers, worker{w, true}) 63 } else { 64 runningWorkers = append(runningWorkers, worker{w, false}) 65 } 66 } 67 } 68 69 dst, isTTY := ui.ForTTY(os.Stdout) 70 if !isTTY { 71 return command.tableFor(append(append(runningWorkers, outdatedWorkers...), stalledWorkers...)).Render(os.Stdout, Fly.PrintTableHeaders) 72 } 73 74 err = command.tableFor(runningWorkers).Render(os.Stdout, Fly.PrintTableHeaders) 75 if err != nil { 76 return err 77 } 78 79 if len(outdatedWorkers) > 0 { 80 requiredWorkerVersion, err := target.WorkerVersion() 81 if err != nil { 82 return err 83 } 84 85 fmt.Fprintln(dst, "") 86 fmt.Fprintln(dst, "") 87 fmt.Fprintln(dst, "the following workers need to be updated to version "+ui.Embolden(requiredWorkerVersion)+":") 88 fmt.Fprintln(dst, "") 89 90 err = command.tableFor(outdatedWorkers).Render(os.Stdout, Fly.PrintTableHeaders) 91 if err != nil { 92 return err 93 } 94 } 95 96 if len(stalledWorkers) > 0 { 97 fmt.Fprintln(dst, "") 98 fmt.Fprintln(dst, "") 99 fmt.Fprintln(dst, "the following workers have not checked in recently:") 100 fmt.Fprintln(dst, "") 101 102 err = command.tableFor(stalledWorkers).Render(os.Stdout, Fly.PrintTableHeaders) 103 if err != nil { 104 return err 105 } 106 107 fmt.Fprintln(dst, "") 108 fmt.Fprintln(dst, "these stalled workers can be cleaned up by running:") 109 fmt.Fprintln(dst, "") 110 fmt.Fprintln(dst, " "+ui.Embolden("fly -t %s prune-worker -w (name)", Fly.Target)) 111 fmt.Fprintln(dst, "") 112 } 113 114 return nil 115 } 116 117 func (command *WorkersCommand) tableFor(workers []worker) ui.Table { 118 headers := ui.TableRow{ 119 {Contents: "name", Color: color.New(color.Bold)}, 120 {Contents: "containers", Color: color.New(color.Bold)}, 121 {Contents: "platform", Color: color.New(color.Bold)}, 122 {Contents: "tags", Color: color.New(color.Bold)}, 123 {Contents: "team", Color: color.New(color.Bold)}, 124 {Contents: "state", Color: color.New(color.Bold)}, 125 {Contents: "version", Color: color.New(color.Bold)}, 126 {Contents: "age", Color: color.New(color.Bold)}, 127 } 128 129 if command.Details { 130 headers = append(headers, 131 ui.TableCell{Contents: "garden address", Color: color.New(color.Bold)}, 132 ui.TableCell{Contents: "baggageclaim url", Color: color.New(color.Bold)}, 133 ui.TableCell{Contents: "active tasks", Color: color.New(color.Bold)}, 134 ui.TableCell{Contents: "resource types", Color: color.New(color.Bold)}, 135 ) 136 } 137 138 table := ui.Table{Headers: headers} 139 140 for _, w := range workers { 141 row := ui.TableRow{ 142 {Contents: w.Name}, 143 {Contents: strconv.Itoa(w.ActiveContainers)}, 144 {Contents: w.Platform}, 145 stringOrDefault(strings.Join(w.Tags, ", ")), 146 stringOrDefault(w.Team), 147 {Contents: w.State}, 148 w.versionCell(), 149 w.ageCell(), 150 } 151 152 if command.Details { 153 var resourceTypes []string 154 for _, t := range w.ResourceTypes { 155 resourceTypes = append(resourceTypes, t.Type) 156 } 157 158 row = append(row, stringOrDefault(w.GardenAddr)) 159 row = append(row, stringOrDefault(w.BaggageclaimURL)) 160 row = append(row, stringOrDefault(strconv.Itoa(w.ActiveTasks))) 161 row = append(row, stringOrDefault(strings.Join(resourceTypes, ", "))) 162 } 163 164 table.Data = append(table.Data, row) 165 } 166 167 return table 168 } 169 170 type byWorkerName []atc.Worker 171 172 func (ws byWorkerName) Len() int { return len(ws) } 173 func (ws byWorkerName) Swap(i int, j int) { ws[i], ws[j] = ws[j], ws[i] } 174 func (ws byWorkerName) Less(i int, j int) bool { return ws[i].Name < ws[j].Name } 175 176 type worker struct { 177 atc.Worker 178 179 outdated bool 180 } 181 182 func (w *worker) versionCell() ui.TableCell { 183 var column ui.TableCell 184 if w.Version != "" { 185 column.Contents = w.Version 186 } else { 187 column.Contents = "none" 188 column.Color = color.New(color.Faint) 189 } 190 191 if w.outdated { 192 column.Color = color.New(color.FgRed) 193 } 194 195 return column 196 } 197 198 func (w *worker) ageCell() ui.TableCell { 199 var column ui.TableCell 200 201 const minute = 60 202 const hour = minute * 60 203 const day = hour * 24 204 205 age := time.Now().Unix() - w.StartTime 206 if w.StartTime <= 0 || age < 0 { 207 column.Contents = "n/a" 208 column.Color = color.New(color.Faint) 209 } else { 210 if age/day > 0 { 211 column.Contents = fmt.Sprintf("%dd", age/day) 212 } else if age/hour > 0 { 213 column.Contents = fmt.Sprintf("%dh%dm", age/hour, (age%hour)/minute) 214 } else if age/minute > 0 { 215 column.Contents = fmt.Sprintf("%dm%ds", age/minute, age%minute) 216 } else { 217 column.Contents = fmt.Sprintf("%ds", age) 218 } 219 } 220 221 return column 222 }