mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
f8c7c921d9
This fix tries to address the issue raised in 27189 where it is not possible to support configured formatting stored in config.json. Since `--format` was not supported in `docker service ps`, the flag `--format` has also been added in this fix. This fix 1. Add `--format` to `docker service ps` 2. Add `tasksFormat` to config.json 3. Add `--format` to `docker stack ps` 4. Add `--format` to `docker node ps` The related docs has been updated. An integration test has been added. This fix fixes 27189. Signed-off-by: Yong Tang <yong.tang.github@outlook.com>
145 lines
3.5 KiB
Go
145 lines
3.5 KiB
Go
package formatter
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/docker/distribution/reference"
|
|
"github.com/docker/docker/api/types/swarm"
|
|
"github.com/docker/docker/cli/command"
|
|
"github.com/docker/docker/pkg/stringid"
|
|
"github.com/docker/go-units"
|
|
)
|
|
|
|
const (
|
|
defaultTaskTableFormat = "table {{.ID}}\t{{.Name}}\t{{.Image}}\t{{.Node}}\t{{.DesiredState}}\t{{.CurrentState}}\t{{.Error}}\t{{.Ports}}"
|
|
|
|
nodeHeader = "NODE"
|
|
taskIDHeader = "ID"
|
|
desiredStateHeader = "DESIRED STATE"
|
|
currentStateHeader = "CURRENT STATE"
|
|
errorHeader = "ERROR"
|
|
|
|
maxErrLength = 30
|
|
)
|
|
|
|
// NewTaskFormat returns a Format for rendering using a task Context
|
|
func NewTaskFormat(source string, quiet bool) Format {
|
|
switch source {
|
|
case TableFormatKey:
|
|
if quiet {
|
|
return defaultQuietFormat
|
|
}
|
|
return defaultTaskTableFormat
|
|
case RawFormatKey:
|
|
if quiet {
|
|
return `id: {{.ID}}`
|
|
}
|
|
return `id: {{.ID}}\nname: {{.Name}}\nimage: {{.Image}}\nnode: {{.Node}}\ndesired_state: {{.DesiredState}}\ncurrent_state: {{.CurrentState}}\nerror: {{.Error}}\nports: {{.Ports}}\n`
|
|
}
|
|
return Format(source)
|
|
}
|
|
|
|
// TaskWrite writes the context
|
|
func TaskWrite(ctx Context, tasks []swarm.Task, names map[string]string, nodes map[string]string) error {
|
|
render := func(format func(subContext subContext) error) error {
|
|
for _, task := range tasks {
|
|
taskCtx := &taskContext{trunc: ctx.Trunc, task: task, name: names[task.ID], node: nodes[task.ID]}
|
|
if err := format(taskCtx); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
return ctx.Write(&taskContext{}, render)
|
|
}
|
|
|
|
type taskContext struct {
|
|
HeaderContext
|
|
trunc bool
|
|
task swarm.Task
|
|
name string
|
|
node string
|
|
}
|
|
|
|
func (c *taskContext) MarshalJSON() ([]byte, error) {
|
|
return marshalJSON(c)
|
|
}
|
|
|
|
func (c *taskContext) ID() string {
|
|
c.AddHeader(taskIDHeader)
|
|
if c.trunc {
|
|
return stringid.TruncateID(c.task.ID)
|
|
}
|
|
return c.task.ID
|
|
}
|
|
|
|
func (c *taskContext) Name() string {
|
|
c.AddHeader(nameHeader)
|
|
return c.name
|
|
}
|
|
|
|
func (c *taskContext) Image() string {
|
|
c.AddHeader(imageHeader)
|
|
image := c.task.Spec.ContainerSpec.Image
|
|
if c.trunc {
|
|
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)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return image
|
|
}
|
|
|
|
func (c *taskContext) Node() string {
|
|
c.AddHeader(nodeHeader)
|
|
return c.node
|
|
}
|
|
|
|
func (c *taskContext) DesiredState() string {
|
|
c.AddHeader(desiredStateHeader)
|
|
return command.PrettyPrint(c.task.DesiredState)
|
|
}
|
|
|
|
func (c *taskContext) CurrentState() string {
|
|
c.AddHeader(currentStateHeader)
|
|
return fmt.Sprintf("%s %s ago",
|
|
command.PrettyPrint(c.task.Status.State),
|
|
strings.ToLower(units.HumanDuration(time.Since(c.task.Status.Timestamp))),
|
|
)
|
|
}
|
|
|
|
func (c *taskContext) Error() string {
|
|
c.AddHeader(errorHeader)
|
|
// Trim and quote the error message.
|
|
taskErr := c.task.Status.Err
|
|
if c.trunc && len(taskErr) > maxErrLength {
|
|
taskErr = fmt.Sprintf("%s…", taskErr[:maxErrLength-1])
|
|
}
|
|
if len(taskErr) > 0 {
|
|
taskErr = fmt.Sprintf("\"%s\"", taskErr)
|
|
}
|
|
return taskErr
|
|
}
|
|
|
|
func (c *taskContext) Ports() string {
|
|
c.AddHeader(portsHeader)
|
|
if len(c.task.Status.PortStatus.Ports) == 0 {
|
|
return ""
|
|
}
|
|
ports := []string{}
|
|
for _, pConfig := range c.task.Status.PortStatus.Ports {
|
|
ports = append(ports, fmt.Sprintf("*:%d->%d/%s",
|
|
pConfig.PublishedPort,
|
|
pConfig.TargetPort,
|
|
pConfig.Protocol,
|
|
))
|
|
}
|
|
return strings.Join(ports, ",")
|
|
}
|