github.com/olljanat/moby@v1.13.1/cli/command/task/print.go (about)

     1  package task
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  	"sort"
     7  	"strings"
     8  	"text/tabwriter"
     9  	"time"
    10  
    11  	"golang.org/x/net/context"
    12  
    13  	distreference "github.com/docker/distribution/reference"
    14  	"github.com/docker/docker/api/types/swarm"
    15  	"github.com/docker/docker/cli/command"
    16  	"github.com/docker/docker/cli/command/idresolver"
    17  	"github.com/docker/docker/pkg/stringid"
    18  	"github.com/docker/go-units"
    19  )
    20  
    21  const (
    22  	psTaskItemFmt = "%s\t%s\t%s\t%s\t%s\t%s %s ago\t%s\t%s\n"
    23  	maxErrLength  = 30
    24  )
    25  
    26  type portStatus swarm.PortStatus
    27  
    28  func (ps portStatus) String() string {
    29  	if len(ps.Ports) == 0 {
    30  		return ""
    31  	}
    32  
    33  	str := fmt.Sprintf("*:%d->%d/%s", ps.Ports[0].PublishedPort, ps.Ports[0].TargetPort, ps.Ports[0].Protocol)
    34  	for _, pConfig := range ps.Ports[1:] {
    35  		str += fmt.Sprintf(",*:%d->%d/%s", pConfig.PublishedPort, pConfig.TargetPort, pConfig.Protocol)
    36  	}
    37  
    38  	return str
    39  }
    40  
    41  type tasksBySlot []swarm.Task
    42  
    43  func (t tasksBySlot) Len() int {
    44  	return len(t)
    45  }
    46  
    47  func (t tasksBySlot) Swap(i, j int) {
    48  	t[i], t[j] = t[j], t[i]
    49  }
    50  
    51  func (t tasksBySlot) Less(i, j int) bool {
    52  	// Sort by slot.
    53  	if t[i].Slot != t[j].Slot {
    54  		return t[i].Slot < t[j].Slot
    55  	}
    56  
    57  	// If same slot, sort by most recent.
    58  	return t[j].Meta.CreatedAt.Before(t[i].CreatedAt)
    59  }
    60  
    61  // Print task information in a table format.
    62  // Besides this, command `docker node ps <node>`
    63  // and `docker stack ps` will call this, too.
    64  func Print(dockerCli *command.DockerCli, ctx context.Context, tasks []swarm.Task, resolver *idresolver.IDResolver, noTrunc bool) error {
    65  	sort.Stable(tasksBySlot(tasks))
    66  
    67  	writer := tabwriter.NewWriter(dockerCli.Out(), 0, 4, 2, ' ', 0)
    68  
    69  	// Ignore flushing errors
    70  	defer writer.Flush()
    71  	fmt.Fprintln(writer, strings.Join([]string{"ID", "NAME", "IMAGE", "NODE", "DESIRED STATE", "CURRENT STATE", "ERROR", "PORTS"}, "\t"))
    72  
    73  	if err := print(writer, ctx, tasks, resolver, noTrunc); err != nil {
    74  		return err
    75  	}
    76  
    77  	return nil
    78  }
    79  
    80  // PrintQuiet shows task list in a quiet way.
    81  func PrintQuiet(dockerCli *command.DockerCli, tasks []swarm.Task) error {
    82  	sort.Stable(tasksBySlot(tasks))
    83  
    84  	out := dockerCli.Out()
    85  
    86  	for _, task := range tasks {
    87  		fmt.Fprintln(out, task.ID)
    88  	}
    89  
    90  	return nil
    91  }
    92  
    93  func print(out io.Writer, ctx context.Context, tasks []swarm.Task, resolver *idresolver.IDResolver, noTrunc bool) error {
    94  	prevName := ""
    95  	for _, task := range tasks {
    96  		id := task.ID
    97  		if !noTrunc {
    98  			id = stringid.TruncateID(id)
    99  		}
   100  
   101  		serviceName, err := resolver.Resolve(ctx, swarm.Service{}, task.ServiceID)
   102  		if err != nil {
   103  			return err
   104  		}
   105  
   106  		nodeValue, err := resolver.Resolve(ctx, swarm.Node{}, task.NodeID)
   107  		if err != nil {
   108  			return err
   109  		}
   110  
   111  		name := ""
   112  		if task.Slot != 0 {
   113  			name = fmt.Sprintf("%v.%v", serviceName, task.Slot)
   114  		} else {
   115  			name = fmt.Sprintf("%v.%v", serviceName, task.NodeID)
   116  		}
   117  
   118  		// Indent the name if necessary
   119  		indentedName := name
   120  		if name == prevName {
   121  			indentedName = fmt.Sprintf(" \\_ %s", indentedName)
   122  		}
   123  		prevName = name
   124  
   125  		// Trim and quote the error message.
   126  		taskErr := task.Status.Err
   127  		if !noTrunc && len(taskErr) > maxErrLength {
   128  			taskErr = fmt.Sprintf("%s…", taskErr[:maxErrLength-1])
   129  		}
   130  		if len(taskErr) > 0 {
   131  			taskErr = fmt.Sprintf("\"%s\"", taskErr)
   132  		}
   133  
   134  		image := task.Spec.ContainerSpec.Image
   135  		if !noTrunc {
   136  			ref, err := distreference.ParseNamed(image)
   137  			if err == nil {
   138  				// update image string for display
   139  				namedTagged, ok := ref.(distreference.NamedTagged)
   140  				if ok {
   141  					image = namedTagged.Name() + ":" + namedTagged.Tag()
   142  				}
   143  			}
   144  		}
   145  
   146  		fmt.Fprintf(
   147  			out,
   148  			psTaskItemFmt,
   149  			id,
   150  			indentedName,
   151  			image,
   152  			nodeValue,
   153  			command.PrettyPrint(task.DesiredState),
   154  			command.PrettyPrint(task.Status.State),
   155  			strings.ToLower(units.HumanDuration(time.Since(task.Status.Timestamp))),
   156  			taskErr,
   157  			portStatus(task.Status.PortStatus),
   158  		)
   159  	}
   160  	return nil
   161  }