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