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  }