github.com/xeptore/docker-cli@v20.10.14+incompatible/cli/command/task/print.go (about)

     1  package task
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"sort"
     7  
     8  	"github.com/docker/cli/cli/command"
     9  	"github.com/docker/cli/cli/command/formatter"
    10  	"github.com/docker/cli/cli/command/idresolver"
    11  	"github.com/docker/cli/cli/config/configfile"
    12  	"github.com/docker/docker/api/types/swarm"
    13  	"github.com/fvbommel/sortorder"
    14  )
    15  
    16  type tasksSortable []swarm.Task
    17  
    18  func (t tasksSortable) Len() int {
    19  	return len(t)
    20  }
    21  
    22  func (t tasksSortable) Swap(i, j int) {
    23  	t[i], t[j] = t[j], t[i]
    24  }
    25  
    26  func (t tasksSortable) Less(i, j int) bool {
    27  	if t[i].Name != t[j].Name {
    28  		return sortorder.NaturalLess(t[i].Name, t[j].Name)
    29  	}
    30  	// Sort tasks for the same service and slot by most recent.
    31  	return t[j].Meta.CreatedAt.Before(t[i].CreatedAt)
    32  }
    33  
    34  // Print task information in a format.
    35  // Besides this, command `docker node ps <node>`
    36  // and `docker stack ps` will call this, too.
    37  func Print(ctx context.Context, dockerCli command.Cli, tasks []swarm.Task, resolver *idresolver.IDResolver, trunc, quiet bool, format string) error {
    38  	tasks, err := generateTaskNames(ctx, tasks, resolver)
    39  	if err != nil {
    40  		return err
    41  	}
    42  
    43  	// First sort tasks, so that all tasks (including previous ones) of the same
    44  	// service and slot are together. This must be done first, to print "previous"
    45  	// tasks indented
    46  	sort.Stable(tasksSortable(tasks))
    47  
    48  	names := map[string]string{}
    49  	nodes := map[string]string{}
    50  
    51  	tasksCtx := formatter.Context{
    52  		Output: dockerCli.Out(),
    53  		Format: NewTaskFormat(format, quiet),
    54  		Trunc:  trunc,
    55  	}
    56  
    57  	var indent string
    58  	if tasksCtx.Format.IsTable() {
    59  		indent = ` \_ `
    60  	}
    61  	prevName := ""
    62  	for _, task := range tasks {
    63  		if task.Name == prevName {
    64  			// Indent previous tasks of the same slot
    65  			names[task.ID] = indent + task.Name
    66  		} else {
    67  			names[task.ID] = task.Name
    68  		}
    69  		prevName = task.Name
    70  
    71  		nodeValue, err := resolver.Resolve(ctx, swarm.Node{}, task.NodeID)
    72  		if err != nil {
    73  			return err
    74  		}
    75  		nodes[task.ID] = nodeValue
    76  	}
    77  
    78  	return FormatWrite(tasksCtx, tasks, names, nodes)
    79  }
    80  
    81  // generateTaskNames generates names for the given tasks, and returns a copy of
    82  // the slice with the 'Name' field set.
    83  //
    84  // Depending if the "--no-resolve" option is set, names have the following pattern:
    85  //
    86  // - ServiceName.Slot or ServiceID.Slot for tasks that are part of a replicated service
    87  // - ServiceName.NodeName or ServiceID.NodeID for tasks that are part of a global service
    88  //
    89  // Task-names are not unique in cases where "tasks" contains previous/rotated tasks.
    90  func generateTaskNames(ctx context.Context, tasks []swarm.Task, resolver *idresolver.IDResolver) ([]swarm.Task, error) {
    91  	// Use a copy of the tasks list, to not modify the original slice
    92  	t := append(tasks[:0:0], tasks...)
    93  
    94  	for i, task := range t {
    95  		serviceName, err := resolver.Resolve(ctx, swarm.Service{}, task.ServiceID)
    96  		if err != nil {
    97  			return nil, err
    98  		}
    99  		if task.Slot != 0 {
   100  			t[i].Name = fmt.Sprintf("%v.%v", serviceName, task.Slot)
   101  		} else {
   102  			t[i].Name = fmt.Sprintf("%v.%v", serviceName, task.NodeID)
   103  		}
   104  	}
   105  	return t, nil
   106  }
   107  
   108  // DefaultFormat returns the default format from the config file, or table
   109  // format if nothing is set in the config.
   110  func DefaultFormat(configFile *configfile.ConfigFile, quiet bool) string {
   111  	if len(configFile.TasksFormat) > 0 && !quiet {
   112  		return configFile.TasksFormat
   113  	}
   114  	return formatter.TableFormatKey
   115  }