github.com/vieux/docker@v0.6.3-0.20161004191708-e097c2a938c7/cli/command/task/print.go (about)

     1  package task
     2  
     3  import (
     4  	"fmt"
     5  	"sort"
     6  	"strings"
     7  	"text/tabwriter"
     8  	"time"
     9  
    10  	"golang.org/x/net/context"
    11  
    12  	"github.com/docker/docker/api/types/swarm"
    13  	"github.com/docker/docker/cli/command"
    14  	"github.com/docker/docker/cli/command/idresolver"
    15  	"github.com/docker/go-units"
    16  )
    17  
    18  const (
    19  	psTaskItemFmt = "%s\t%s\t%s\t%s\t%s %s ago\t%s\n"
    20  	maxErrLength  = 30
    21  )
    22  
    23  type tasksBySlot []swarm.Task
    24  
    25  func (t tasksBySlot) Len() int {
    26  	return len(t)
    27  }
    28  
    29  func (t tasksBySlot) Swap(i, j int) {
    30  	t[i], t[j] = t[j], t[i]
    31  }
    32  
    33  func (t tasksBySlot) Less(i, j int) bool {
    34  	// Sort by slot.
    35  	if t[i].Slot != t[j].Slot {
    36  		return t[i].Slot < t[j].Slot
    37  	}
    38  
    39  	// If same slot, sort by most recent.
    40  	return t[j].Meta.CreatedAt.Before(t[i].CreatedAt)
    41  }
    42  
    43  // Print task information in a table format
    44  func Print(dockerCli *command.DockerCli, ctx context.Context, tasks []swarm.Task, resolver *idresolver.IDResolver, noTrunc bool) error {
    45  	sort.Stable(tasksBySlot(tasks))
    46  
    47  	writer := tabwriter.NewWriter(dockerCli.Out(), 0, 4, 2, ' ', 0)
    48  
    49  	// Ignore flushing errors
    50  	defer writer.Flush()
    51  	fmt.Fprintln(writer, strings.Join([]string{"NAME", "IMAGE", "NODE", "DESIRED STATE", "CURRENT STATE", "ERROR"}, "\t"))
    52  
    53  	prevServiceName := ""
    54  	prevSlot := 0
    55  	for _, task := range tasks {
    56  		serviceName, err := resolver.Resolve(ctx, swarm.Service{}, task.ServiceID)
    57  		if err != nil {
    58  			return err
    59  		}
    60  		nodeValue, err := resolver.Resolve(ctx, swarm.Node{}, task.NodeID)
    61  		if err != nil {
    62  			return err
    63  		}
    64  
    65  		name := task.Annotations.Name
    66  		// TODO: This is the fallback <ServiceName>.<Slot>.<taskID> in case task name is not present in
    67  		// Annotations (upgraded from 1.12).
    68  		// We may be able to remove the following in the future.
    69  		if name == "" {
    70  			if task.Slot != 0 {
    71  				name = fmt.Sprintf("%v.%v.%v", serviceName, task.Slot, task.ID)
    72  			} else {
    73  				name = fmt.Sprintf("%v.%v.%v", serviceName, task.NodeID, task.ID)
    74  			}
    75  		}
    76  
    77  		// Indent the name if necessary
    78  		indentedName := name
    79  		// Since the new format of the task name is <ServiceName>.<Slot>.<taskID>, we should only compare
    80  		// <ServiceName> and <Slot> here.
    81  		if prevServiceName == serviceName && prevSlot == task.Slot {
    82  			indentedName = fmt.Sprintf(" \\_ %s", indentedName)
    83  		}
    84  		prevServiceName = serviceName
    85  		prevSlot = task.Slot
    86  
    87  		// Trim and quote the error message.
    88  		taskErr := task.Status.Err
    89  		if !noTrunc && len(taskErr) > maxErrLength {
    90  			taskErr = fmt.Sprintf("%s…", taskErr[:maxErrLength-1])
    91  		}
    92  		if len(taskErr) > 0 {
    93  			taskErr = fmt.Sprintf("\"%s\"", taskErr)
    94  		}
    95  
    96  		fmt.Fprintf(
    97  			writer,
    98  			psTaskItemFmt,
    99  			indentedName,
   100  			task.Spec.ContainerSpec.Image,
   101  			nodeValue,
   102  			command.PrettyPrint(task.DesiredState),
   103  			command.PrettyPrint(task.Status.State),
   104  			strings.ToLower(units.HumanDuration(time.Since(task.Status.Timestamp))),
   105  			taskErr,
   106  		)
   107  	}
   108  
   109  	return nil
   110  }