Files
docker-cli/components/engine/cli/command/task/print.go
Vincent Demeester f6ad06ceb6 Add some unit tests to the node and swarm cli code
Start work on adding unit tests to our cli code in order to have to
write less costly integration test.

Signed-off-by: Vincent Demeester <vincent@sbr.pm>
Upstream-commit: f151c297eb268e22dc1eb36ded0e356885f40739
Component: engine
2017-01-09 18:30:15 +01:00

158 lines
3.6 KiB
Go

package task
import (
"fmt"
"io"
"sort"
"strings"
"text/tabwriter"
"time"
"golang.org/x/net/context"
distreference "github.com/docker/distribution/reference"
"github.com/docker/docker/api/types/swarm"
"github.com/docker/docker/cli/command"
"github.com/docker/docker/cli/command/idresolver"
"github.com/docker/docker/pkg/stringid"
"github.com/docker/go-units"
)
const (
psTaskItemFmt = "%s\t%s\t%s\t%s\t%s\t%s %s ago\t%s\t%s\n"
maxErrLength = 30
)
type portStatus swarm.PortStatus
func (ps portStatus) String() string {
if len(ps.Ports) == 0 {
return ""
}
str := fmt.Sprintf("*:%d->%d/%s", ps.Ports[0].PublishedPort, ps.Ports[0].TargetPort, ps.Ports[0].Protocol)
for _, pConfig := range ps.Ports[1:] {
str += fmt.Sprintf(",*:%d->%d/%s", pConfig.PublishedPort, pConfig.TargetPort, pConfig.Protocol)
}
return str
}
type tasksBySlot []swarm.Task
func (t tasksBySlot) Len() int {
return len(t)
}
func (t tasksBySlot) Swap(i, j int) {
t[i], t[j] = t[j], t[i]
}
func (t tasksBySlot) Less(i, j int) bool {
// Sort by slot.
if t[i].Slot != t[j].Slot {
return t[i].Slot < t[j].Slot
}
// If same slot, sort by most recent.
return t[j].Meta.CreatedAt.Before(t[i].CreatedAt)
}
// Print task information in a table format.
// Besides this, command `docker node ps <node>`
// and `docker stack ps` will call this, too.
func Print(dockerCli command.Cli, ctx context.Context, tasks []swarm.Task, resolver *idresolver.IDResolver, noTrunc bool) error {
sort.Stable(tasksBySlot(tasks))
writer := tabwriter.NewWriter(dockerCli.Out(), 0, 4, 2, ' ', 0)
// Ignore flushing errors
defer writer.Flush()
fmt.Fprintln(writer, strings.Join([]string{"ID", "NAME", "IMAGE", "NODE", "DESIRED STATE", "CURRENT STATE", "ERROR", "PORTS"}, "\t"))
return print(writer, ctx, tasks, resolver, noTrunc)
}
// PrintQuiet shows task list in a quiet way.
func PrintQuiet(dockerCli command.Cli, tasks []swarm.Task) error {
sort.Stable(tasksBySlot(tasks))
out := dockerCli.Out()
for _, task := range tasks {
fmt.Fprintln(out, task.ID)
}
return nil
}
func print(out io.Writer, ctx context.Context, tasks []swarm.Task, resolver *idresolver.IDResolver, noTrunc bool) error {
prevName := ""
for _, task := range tasks {
id := task.ID
if !noTrunc {
id = stringid.TruncateID(id)
}
serviceName, err := resolver.Resolve(ctx, swarm.Service{}, task.ServiceID)
if err != nil {
return err
}
nodeValue, err := resolver.Resolve(ctx, swarm.Node{}, task.NodeID)
if err != nil {
return err
}
name := ""
if task.Slot != 0 {
name = fmt.Sprintf("%v.%v", serviceName, task.Slot)
} else {
name = fmt.Sprintf("%v.%v", serviceName, task.NodeID)
}
// Indent the name if necessary
indentedName := name
if name == prevName {
indentedName = fmt.Sprintf(" \\_ %s", indentedName)
}
prevName = name
// Trim and quote the error message.
taskErr := task.Status.Err
if !noTrunc && len(taskErr) > maxErrLength {
taskErr = fmt.Sprintf("%s…", taskErr[:maxErrLength-1])
}
if len(taskErr) > 0 {
taskErr = fmt.Sprintf("\"%s\"", taskErr)
}
image := task.Spec.ContainerSpec.Image
if !noTrunc {
ref, err := distreference.ParseNamed(image)
if err == nil {
// update image string for display
namedTagged, ok := ref.(distreference.NamedTagged)
if ok {
image = namedTagged.Name() + ":" + namedTagged.Tag()
}
}
}
fmt.Fprintf(
out,
psTaskItemFmt,
id,
indentedName,
image,
nodeValue,
command.PrettyPrint(task.DesiredState),
command.PrettyPrint(task.Status.State),
strings.ToLower(units.HumanDuration(time.Since(task.Status.Timestamp))),
taskErr,
portStatus(task.Status.PortStatus),
)
}
return nil
}