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  }