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