github.com/flavio/docker@v0.1.3-0.20170117145210-f63d1a6eec47/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.Cli, 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  	return print(writer, ctx, tasks, resolver, noTrunc)
    74  }
    75  
    76  // PrintQuiet shows task list in a quiet way.
    77  func PrintQuiet(dockerCli command.Cli, tasks []swarm.Task) error {
    78  	sort.Stable(tasksBySlot(tasks))
    79  
    80  	out := dockerCli.Out()
    81  
    82  	for _, task := range tasks {
    83  		fmt.Fprintln(out, task.ID)
    84  	}
    85  
    86  	return nil
    87  }
    88  
    89  func print(out io.Writer, ctx context.Context, tasks []swarm.Task, resolver *idresolver.IDResolver, noTrunc bool) error {
    90  	prevName := ""
    91  	for _, task := range tasks {
    92  		id := task.ID
    93  		if !noTrunc {
    94  			id = stringid.TruncateID(id)
    95  		}
    96  
    97  		serviceName, err := resolver.Resolve(ctx, swarm.Service{}, task.ServiceID)
    98  		if err != nil {
    99  			return err
   100  		}
   101  
   102  		nodeValue, err := resolver.Resolve(ctx, swarm.Node{}, task.NodeID)
   103  		if err != nil {
   104  			return err
   105  		}
   106  
   107  		name := ""
   108  		if task.Slot != 0 {
   109  			name = fmt.Sprintf("%v.%v", serviceName, task.Slot)
   110  		} else {
   111  			name = fmt.Sprintf("%v.%v", serviceName, task.NodeID)
   112  		}
   113  
   114  		// Indent the name if necessary
   115  		indentedName := name
   116  		if name == prevName {
   117  			indentedName = fmt.Sprintf(" \\_ %s", indentedName)
   118  		}
   119  		prevName = name
   120  
   121  		// Trim and quote the error message.
   122  		taskErr := task.Status.Err
   123  		if !noTrunc && len(taskErr) > maxErrLength {
   124  			taskErr = fmt.Sprintf("%s…", taskErr[:maxErrLength-1])
   125  		}
   126  		if len(taskErr) > 0 {
   127  			taskErr = fmt.Sprintf("\"%s\"", taskErr)
   128  		}
   129  
   130  		image := task.Spec.ContainerSpec.Image
   131  		if !noTrunc {
   132  			ref, err := distreference.ParseNamed(image)
   133  			if err == nil {
   134  				// update image string for display
   135  				namedTagged, ok := ref.(distreference.NamedTagged)
   136  				if ok {
   137  					image = namedTagged.Name() + ":" + namedTagged.Tag()
   138  				}
   139  			}
   140  		}
   141  
   142  		fmt.Fprintf(
   143  			out,
   144  			psTaskItemFmt,
   145  			id,
   146  			indentedName,
   147  			image,
   148  			nodeValue,
   149  			command.PrettyPrint(task.DesiredState),
   150  			command.PrettyPrint(task.Status.State),
   151  			strings.ToLower(units.HumanDuration(time.Since(task.Status.Timestamp))),
   152  			taskErr,
   153  			portStatus(task.Status.PortStatus),
   154  		)
   155  	}
   156  	return nil
   157  }