3a1279393f
Remove forked reference package. Use normalized named values everywhere and familiar functions to convert back to familiar strings for UX and storage compatibility. Enforce that the source repository in the distribution metadata is always a normalized string, ignore invalid values which are not. Update distribution tests to use normalized values. Signed-off-by: Derek McGowan <derek@mcgstyle.net> (github: dmcgowan)
159 lines
3.7 KiB
Go
159 lines
3.7 KiB
Go
package task
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"sort"
|
|
"strings"
|
|
"text/tabwriter"
|
|
"time"
|
|
|
|
"golang.org/x/net/context"
|
|
|
|
"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 := reference.ParseNormalizedNamed(image)
|
|
if err == nil {
|
|
// update image string for display, (strips any digest)
|
|
if nt, ok := ref.(reference.NamedTagged); ok {
|
|
if namedTagged, err := reference.WithTag(reference.TrimNamed(nt), nt.Tag()); err == nil {
|
|
image = reference.FamiliarString(namedTagged)
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
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
|
|
}
|