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 }