mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
Add formatter for service inspect
Allows the user to use `pretty` as the format string. This enables users to put custom format options into their CLI config just like is supported for `docker ps` and `docker images` Signed-off-by: Brian Goff <cpuguy83@gmail.com>
This commit is contained in:
parent
45a8f68026
commit
54ba82beab
7 changed files with 344 additions and 140 deletions
|
@ -11,11 +11,11 @@ import (
|
||||||
"github.com/docker/docker/utils/templates"
|
"github.com/docker/docker/utils/templates"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Format keys used to specify certain kinds of output formats
|
||||||
const (
|
const (
|
||||||
// TableFormatKey is the key used to format as a table
|
TableFormatKey = "table"
|
||||||
TableFormatKey = "table"
|
RawFormatKey = "raw"
|
||||||
// RawFormatKey is the key used to format as raw JSON
|
PrettyFormatKey = "pretty"
|
||||||
RawFormatKey = "raw"
|
|
||||||
|
|
||||||
defaultQuietFormat = "{{.ID}}"
|
defaultQuietFormat = "{{.ID}}"
|
||||||
)
|
)
|
||||||
|
|
285
cli/command/formatter/service.go
Normal file
285
cli/command/formatter/service.go
Normal file
|
@ -0,0 +1,285 @@
|
||||||
|
package formatter
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
mounttypes "github.com/docker/docker/api/types/mount"
|
||||||
|
"github.com/docker/docker/api/types/swarm"
|
||||||
|
"github.com/docker/docker/cli/command/inspect"
|
||||||
|
units "github.com/docker/go-units"
|
||||||
|
)
|
||||||
|
|
||||||
|
const serviceInspectPrettyTemplate Format = `
|
||||||
|
ID: {{.ID}}
|
||||||
|
Name: {{.Name}}
|
||||||
|
{{- if .Labels }}
|
||||||
|
Labels:
|
||||||
|
{{- range $k, $v := .Labels }}
|
||||||
|
{{ $k }}{{if $v }}={{ $v }}{{ end }}
|
||||||
|
{{- end }}{{ end }}
|
||||||
|
Mode:
|
||||||
|
{{- if .IsModeGlobal }} Global
|
||||||
|
{{- else }} Replicated
|
||||||
|
{{- if .ModeReplicatedReplicas }}
|
||||||
|
Replicas: {{ .ModeReplicatedReplicas }}
|
||||||
|
{{- end }}{{ end }}
|
||||||
|
{{- if .HasUpdateStatus }}
|
||||||
|
UpdateStatus:
|
||||||
|
State: {{ .UpdateStatusState }}
|
||||||
|
Started: {{ .UpdateStatusStarted }}
|
||||||
|
{{- if .UpdateIsCompleted }}
|
||||||
|
Completed: {{ .UpdateStatusCompleted }}
|
||||||
|
{{- end }}
|
||||||
|
Message: {{ .UpdateStatusMessage }}
|
||||||
|
{{- end }}
|
||||||
|
Placement:
|
||||||
|
{{- if .TaskPlacementConstraints -}}
|
||||||
|
Contraints: {{ .TaskPlacementConstraints }}
|
||||||
|
{{- end }}
|
||||||
|
{{- if .HasUpdateConfig }}
|
||||||
|
UpdateConfig:
|
||||||
|
Parallelism: {{ .UpdateParallelism }}
|
||||||
|
{{- if .HasUpdateDelay -}}
|
||||||
|
Delay: {{ .UpdateDelay }}
|
||||||
|
{{- end }}
|
||||||
|
On failure: {{ .UpdateOnFailure }}
|
||||||
|
{{- end }}
|
||||||
|
ContainerSpec:
|
||||||
|
Image: {{ .ContainerImage }}
|
||||||
|
{{- if .ContainerArgs }}
|
||||||
|
Args: {{ range $arg := .ContainerArgs }}{{ $arg }} {{ end }}
|
||||||
|
{{- end -}}
|
||||||
|
{{- if .ContainerEnv }}
|
||||||
|
Env: {{ range $env := .ContainerEnv }}{{ $env }} {{ end }}
|
||||||
|
{{- end -}}
|
||||||
|
{{- if .ContainerWorkDir }}
|
||||||
|
Dir: {{ .ContainerWorkDir }}
|
||||||
|
{{- end -}}
|
||||||
|
{{- if .ContainerUser }}
|
||||||
|
User: {{ .ContainerUser }}
|
||||||
|
{{- end }}
|
||||||
|
{{- if .ContainerMounts }}
|
||||||
|
Mounts:
|
||||||
|
{{- end }}
|
||||||
|
{{- range $mount := .ContainerMounts }}
|
||||||
|
Target = {{ $mount.Target }}
|
||||||
|
Source = {{ $mount.Source }}
|
||||||
|
ReadOnly = {{ $mount.ReadOnly }}
|
||||||
|
Type = {{ $mount.Type }}
|
||||||
|
{{- end -}}
|
||||||
|
{{- if .HasResources }}
|
||||||
|
Resources:
|
||||||
|
{{- if .HasResourceReservations }}
|
||||||
|
Reservations:
|
||||||
|
{{- end }}
|
||||||
|
{{- if gt .ResourceReservationNanoCPUs 0.0 }}
|
||||||
|
CPU: {{ .ResourceReservationNanoCPUs }}
|
||||||
|
{{- end }}
|
||||||
|
{{- if .ResourceReservationMemory }}
|
||||||
|
Memory: {{ .ResourceReservationMemory }}
|
||||||
|
{{- end }}
|
||||||
|
{{- if .HasResourceLimits }}
|
||||||
|
Limits:
|
||||||
|
{{- end }}
|
||||||
|
{{- if gt .ResourceLimitsNanoCPUs 0.0 }}
|
||||||
|
CPU: {{ .ResourceLimitsNanoCPUs }}
|
||||||
|
{{- end }}
|
||||||
|
{{- if .ResourceLimitMemory }}
|
||||||
|
Memory: {{ .ResourceLimitMemory }}
|
||||||
|
{{- end }}{{ end }}
|
||||||
|
{{- if .Networks }}
|
||||||
|
Networks:
|
||||||
|
{{- range $network := .Networks }} {{ $network }}{{ end }} {{ end }}
|
||||||
|
{{- if .Ports }}
|
||||||
|
Ports:
|
||||||
|
{{- range $port := .Ports }}
|
||||||
|
PublishedPort {{ $port.PublishedPort }}
|
||||||
|
Protocol = {{ $port.Protocol }}
|
||||||
|
TargetPort = {{ $port.TargetPort }}
|
||||||
|
{{- end }} {{ end -}}
|
||||||
|
`
|
||||||
|
|
||||||
|
// NewServiceFormat returns a Format for rendering using a Context
|
||||||
|
func NewServiceFormat(source string) Format {
|
||||||
|
switch source {
|
||||||
|
case PrettyFormatKey:
|
||||||
|
return serviceInspectPrettyTemplate
|
||||||
|
default:
|
||||||
|
return Format(strings.TrimPrefix(source, RawFormatKey))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServiceInspectWrite renders the context for a list of services
|
||||||
|
func ServiceInspectWrite(ctx Context, refs []string, getRef inspect.GetRefFunc) error {
|
||||||
|
if ctx.Format != serviceInspectPrettyTemplate {
|
||||||
|
return inspect.Inspect(ctx.Output, refs, string(ctx.Format), getRef)
|
||||||
|
}
|
||||||
|
render := func(format func(subContext subContext) error) error {
|
||||||
|
for _, ref := range refs {
|
||||||
|
serviceI, _, err := getRef(ref)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
service, ok := serviceI.(swarm.Service)
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("got wrong object to inspect")
|
||||||
|
}
|
||||||
|
if err := format(&serviceInspectContext{Service: service}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return ctx.Write(&serviceInspectContext{}, render)
|
||||||
|
}
|
||||||
|
|
||||||
|
type serviceInspectContext struct {
|
||||||
|
swarm.Service
|
||||||
|
subContext
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ctx *serviceInspectContext) ID() string {
|
||||||
|
return ctx.Service.ID
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ctx *serviceInspectContext) Name() string {
|
||||||
|
return ctx.Service.Spec.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ctx *serviceInspectContext) Labels() map[string]string {
|
||||||
|
return ctx.Service.Spec.Labels
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ctx *serviceInspectContext) IsModeGlobal() bool {
|
||||||
|
return ctx.Service.Spec.Mode.Global != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ctx *serviceInspectContext) ModeReplicatedReplicas() *uint64 {
|
||||||
|
return ctx.Service.Spec.Mode.Replicated.Replicas
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ctx *serviceInspectContext) HasUpdateStatus() bool {
|
||||||
|
return ctx.Service.UpdateStatus.State != ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ctx *serviceInspectContext) UpdateStatusState() swarm.UpdateState {
|
||||||
|
return ctx.Service.UpdateStatus.State
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ctx *serviceInspectContext) UpdateStatusStarted() string {
|
||||||
|
return units.HumanDuration(time.Since(ctx.Service.UpdateStatus.StartedAt))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ctx *serviceInspectContext) UpdateIsCompleted() bool {
|
||||||
|
return ctx.Service.UpdateStatus.State == swarm.UpdateStateCompleted
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ctx *serviceInspectContext) UpdateStatusCompleted() string {
|
||||||
|
return units.HumanDuration(time.Since(ctx.Service.UpdateStatus.CompletedAt))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ctx *serviceInspectContext) UpdateStatusMessage() string {
|
||||||
|
return ctx.Service.UpdateStatus.Message
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ctx *serviceInspectContext) TaskPlacementConstraints() []string {
|
||||||
|
if ctx.Service.Spec.TaskTemplate.Placement != nil {
|
||||||
|
return ctx.Service.Spec.TaskTemplate.Placement.Constraints
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ctx *serviceInspectContext) HasUpdateConfig() bool {
|
||||||
|
return ctx.Service.Spec.UpdateConfig != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ctx *serviceInspectContext) UpdateParallelism() uint64 {
|
||||||
|
return ctx.Service.Spec.UpdateConfig.Parallelism
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ctx *serviceInspectContext) HasUpdateDelay() bool {
|
||||||
|
return ctx.Service.Spec.UpdateConfig.Delay.Nanoseconds() > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ctx *serviceInspectContext) UpdateDelay() time.Duration {
|
||||||
|
return ctx.Service.Spec.UpdateConfig.Delay
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ctx *serviceInspectContext) UpdateOnFailure() string {
|
||||||
|
return ctx.Service.Spec.UpdateConfig.FailureAction
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ctx *serviceInspectContext) ContainerImage() string {
|
||||||
|
return ctx.Service.Spec.TaskTemplate.ContainerSpec.Image
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ctx *serviceInspectContext) ContainerArgs() []string {
|
||||||
|
return ctx.Service.Spec.TaskTemplate.ContainerSpec.Args
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ctx *serviceInspectContext) ContainerEnv() []string {
|
||||||
|
return ctx.Service.Spec.TaskTemplate.ContainerSpec.Env
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ctx *serviceInspectContext) ContainerWorkDir() string {
|
||||||
|
return ctx.Service.Spec.TaskTemplate.ContainerSpec.Dir
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ctx *serviceInspectContext) ContainerUser() string {
|
||||||
|
return ctx.Service.Spec.TaskTemplate.ContainerSpec.User
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ctx *serviceInspectContext) ContainerMounts() []mounttypes.Mount {
|
||||||
|
return ctx.Service.Spec.TaskTemplate.ContainerSpec.Mounts
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ctx *serviceInspectContext) HasResources() bool {
|
||||||
|
return ctx.Service.Spec.TaskTemplate.Resources != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ctx *serviceInspectContext) HasResourceReservations() bool {
|
||||||
|
return ctx.Service.Spec.TaskTemplate.Resources.Reservations.NanoCPUs > 0 || ctx.Service.Spec.TaskTemplate.Resources.Reservations.MemoryBytes > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ctx *serviceInspectContext) ResourceReservationNanoCPUs() float64 {
|
||||||
|
if ctx.Service.Spec.TaskTemplate.Resources.Reservations.NanoCPUs == 0 {
|
||||||
|
return float64(0)
|
||||||
|
}
|
||||||
|
return float64(ctx.Service.Spec.TaskTemplate.Resources.Reservations.NanoCPUs) / 1e9
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ctx *serviceInspectContext) ResourceReservationMemory() string {
|
||||||
|
if ctx.Service.Spec.TaskTemplate.Resources.Reservations.MemoryBytes == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return units.BytesSize(float64(ctx.Service.Spec.TaskTemplate.Resources.Reservations.MemoryBytes))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ctx *serviceInspectContext) HasResourceLimits() bool {
|
||||||
|
return ctx.Service.Spec.TaskTemplate.Resources.Limits.NanoCPUs > 0 || ctx.Service.Spec.TaskTemplate.Resources.Limits.MemoryBytes > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ctx *serviceInspectContext) ResourceLimitsNanoCPUs() float64 {
|
||||||
|
return float64(ctx.Service.Spec.TaskTemplate.Resources.Limits.NanoCPUs) / 1e9
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ctx *serviceInspectContext) ResourceLimitMemory() string {
|
||||||
|
if ctx.Service.Spec.TaskTemplate.Resources.Limits.MemoryBytes == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return units.BytesSize(float64(ctx.Service.Spec.TaskTemplate.Resources.Limits.MemoryBytes))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ctx *serviceInspectContext) Networks() []string {
|
||||||
|
var out []string
|
||||||
|
for _, n := range ctx.Service.Spec.Networks {
|
||||||
|
out = append(out, n.Target)
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ctx *serviceInspectContext) Ports() []swarm.PortConfig {
|
||||||
|
return ctx.Service.Endpoint.Ports
|
||||||
|
}
|
|
@ -2,19 +2,14 @@ package service
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
|
||||||
|
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
|
|
||||||
"github.com/docker/docker/api/types/swarm"
|
|
||||||
"github.com/docker/docker/cli"
|
"github.com/docker/docker/cli"
|
||||||
"github.com/docker/docker/cli/command"
|
"github.com/docker/docker/cli/command"
|
||||||
"github.com/docker/docker/cli/command/inspect"
|
"github.com/docker/docker/cli/command/formatter"
|
||||||
apiclient "github.com/docker/docker/client"
|
apiclient "github.com/docker/docker/client"
|
||||||
"github.com/docker/docker/pkg/ioutils"
|
|
||||||
"github.com/docker/go-units"
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -51,6 +46,10 @@ func runInspect(dockerCli *command.DockerCli, opts inspectOptions) error {
|
||||||
client := dockerCli.Client()
|
client := dockerCli.Client()
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
|
if opts.pretty {
|
||||||
|
opts.format = "pretty"
|
||||||
|
}
|
||||||
|
|
||||||
getRef := func(ref string) (interface{}, []byte, error) {
|
getRef := func(ref string) (interface{}, []byte, error) {
|
||||||
service, _, err := client.ServiceInspectWithRaw(ctx, ref)
|
service, _, err := client.ServiceInspectWithRaw(ctx, ref)
|
||||||
if err == nil || !apiclient.IsErrServiceNotFound(err) {
|
if err == nil || !apiclient.IsErrServiceNotFound(err) {
|
||||||
|
@ -59,130 +58,27 @@ func runInspect(dockerCli *command.DockerCli, opts inspectOptions) error {
|
||||||
return nil, nil, fmt.Errorf("Error: no such service: %s", ref)
|
return nil, nil, fmt.Errorf("Error: no such service: %s", ref)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !opts.pretty {
|
f := opts.format
|
||||||
return inspect.Inspect(dockerCli.Out(), opts.refs, opts.format, getRef)
|
if len(f) == 0 {
|
||||||
|
f = "raw"
|
||||||
|
if len(dockerCli.ConfigFile().ServiceInspectFormat) > 0 {
|
||||||
|
f = dockerCli.ConfigFile().ServiceInspectFormat
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return printHumanFriendly(dockerCli.Out(), opts.refs, getRef)
|
// check if the user is trying to apply a template to the pretty format, which
|
||||||
}
|
// is not supported
|
||||||
|
if strings.HasPrefix(f, "pretty") && f != "pretty" {
|
||||||
|
return fmt.Errorf("Cannot supply extra formatting options to the pretty template")
|
||||||
|
}
|
||||||
|
|
||||||
func printHumanFriendly(out io.Writer, refs []string, getRef inspect.GetRefFunc) error {
|
serviceCtx := formatter.Context{
|
||||||
for idx, ref := range refs {
|
Output: dockerCli.Out(),
|
||||||
obj, _, err := getRef(ref)
|
Format: formatter.NewServiceFormat(f),
|
||||||
if err != nil {
|
}
|
||||||
return err
|
|
||||||
}
|
|
||||||
printService(out, obj.(swarm.Service))
|
|
||||||
|
|
||||||
// TODO: better way to do this?
|
if err := formatter.ServiceInspectWrite(serviceCtx, opts.refs, getRef); err != nil {
|
||||||
// print extra space between objects, but not after the last one
|
return cli.StatusError{StatusCode: 1, Status: err.Error()}
|
||||||
if idx+1 != len(refs) {
|
|
||||||
fmt.Fprintf(out, "\n\n")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: use a template
|
|
||||||
func printService(out io.Writer, service swarm.Service) {
|
|
||||||
fmt.Fprintf(out, "ID:\t\t%s\n", service.ID)
|
|
||||||
fmt.Fprintf(out, "Name:\t\t%s\n", service.Spec.Name)
|
|
||||||
if service.Spec.Labels != nil {
|
|
||||||
fmt.Fprintln(out, "Labels:")
|
|
||||||
for k, v := range service.Spec.Labels {
|
|
||||||
fmt.Fprintf(out, " - %s=%s\n", k, v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if service.Spec.Mode.Global != nil {
|
|
||||||
fmt.Fprintln(out, "Mode:\t\tGlobal")
|
|
||||||
} else {
|
|
||||||
fmt.Fprintln(out, "Mode:\t\tReplicated")
|
|
||||||
if service.Spec.Mode.Replicated.Replicas != nil {
|
|
||||||
fmt.Fprintf(out, " Replicas:\t%d\n", *service.Spec.Mode.Replicated.Replicas)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if service.UpdateStatus.State != "" {
|
|
||||||
fmt.Fprintln(out, "Update status:")
|
|
||||||
fmt.Fprintf(out, " State:\t\t%s\n", service.UpdateStatus.State)
|
|
||||||
fmt.Fprintf(out, " Started:\t%s ago\n", strings.ToLower(units.HumanDuration(time.Since(service.UpdateStatus.StartedAt))))
|
|
||||||
if service.UpdateStatus.State == swarm.UpdateStateCompleted {
|
|
||||||
fmt.Fprintf(out, " Completed:\t%s ago\n", strings.ToLower(units.HumanDuration(time.Since(service.UpdateStatus.CompletedAt))))
|
|
||||||
}
|
|
||||||
fmt.Fprintf(out, " Message:\t%s\n", service.UpdateStatus.Message)
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Fprintln(out, "Placement:")
|
|
||||||
if service.Spec.TaskTemplate.Placement != nil && len(service.Spec.TaskTemplate.Placement.Constraints) > 0 {
|
|
||||||
ioutils.FprintfIfNotEmpty(out, " Constraints\t: %s\n", strings.Join(service.Spec.TaskTemplate.Placement.Constraints, ", "))
|
|
||||||
}
|
|
||||||
if service.Spec.UpdateConfig != nil {
|
|
||||||
fmt.Fprintf(out, "UpdateConfig:\n")
|
|
||||||
fmt.Fprintf(out, " Parallelism:\t%d\n", service.Spec.UpdateConfig.Parallelism)
|
|
||||||
if service.Spec.UpdateConfig.Delay.Nanoseconds() > 0 {
|
|
||||||
fmt.Fprintf(out, " Delay:\t\t%s\n", service.Spec.UpdateConfig.Delay)
|
|
||||||
}
|
|
||||||
fmt.Fprintf(out, " On failure:\t%s\n", service.Spec.UpdateConfig.FailureAction)
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Fprintf(out, "ContainerSpec:\n")
|
|
||||||
printContainerSpec(out, service.Spec.TaskTemplate.ContainerSpec)
|
|
||||||
|
|
||||||
resources := service.Spec.TaskTemplate.Resources
|
|
||||||
if resources != nil {
|
|
||||||
fmt.Fprintln(out, "Resources:")
|
|
||||||
printResources := func(out io.Writer, requirement string, r *swarm.Resources) {
|
|
||||||
if r == nil || (r.MemoryBytes == 0 && r.NanoCPUs == 0) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
fmt.Fprintf(out, " %s:\n", requirement)
|
|
||||||
if r.NanoCPUs != 0 {
|
|
||||||
fmt.Fprintf(out, " CPU:\t\t%g\n", float64(r.NanoCPUs)/1e9)
|
|
||||||
}
|
|
||||||
if r.MemoryBytes != 0 {
|
|
||||||
fmt.Fprintf(out, " Memory:\t%s\n", units.BytesSize(float64(r.MemoryBytes)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
printResources(out, "Reservations", resources.Reservations)
|
|
||||||
printResources(out, "Limits", resources.Limits)
|
|
||||||
}
|
|
||||||
if len(service.Spec.Networks) > 0 {
|
|
||||||
fmt.Fprintf(out, "Networks:")
|
|
||||||
for _, n := range service.Spec.Networks {
|
|
||||||
fmt.Fprintf(out, " %s", n.Target)
|
|
||||||
}
|
|
||||||
fmt.Fprintln(out, "")
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(service.Endpoint.Ports) > 0 {
|
|
||||||
fmt.Fprintln(out, "Ports:")
|
|
||||||
for _, port := range service.Endpoint.Ports {
|
|
||||||
ioutils.FprintfIfNotEmpty(out, " Name = %s\n", port.Name)
|
|
||||||
fmt.Fprintf(out, " Protocol = %s\n", port.Protocol)
|
|
||||||
fmt.Fprintf(out, " TargetPort = %d\n", port.TargetPort)
|
|
||||||
fmt.Fprintf(out, " PublishedPort = %d\n", port.PublishedPort)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func printContainerSpec(out io.Writer, containerSpec swarm.ContainerSpec) {
|
|
||||||
fmt.Fprintf(out, " Image:\t\t%s\n", containerSpec.Image)
|
|
||||||
if len(containerSpec.Args) > 0 {
|
|
||||||
fmt.Fprintf(out, " Args:\t\t%s\n", strings.Join(containerSpec.Args, " "))
|
|
||||||
}
|
|
||||||
if len(containerSpec.Env) > 0 {
|
|
||||||
fmt.Fprintf(out, " Env:\t\t%s\n", strings.Join(containerSpec.Env, " "))
|
|
||||||
}
|
|
||||||
ioutils.FprintfIfNotEmpty(out, " Dir\t\t%s\n", containerSpec.Dir)
|
|
||||||
ioutils.FprintfIfNotEmpty(out, " User\t\t%s\n", containerSpec.User)
|
|
||||||
if len(containerSpec.Mounts) > 0 {
|
|
||||||
fmt.Fprintln(out, " Mounts:")
|
|
||||||
for _, v := range containerSpec.Mounts {
|
|
||||||
fmt.Fprintf(out, " Target = %s\n", v.Target)
|
|
||||||
fmt.Fprintf(out, " Source = %s\n", v.Source)
|
|
||||||
fmt.Fprintf(out, " ReadOnly = %v\n", v.ReadOnly)
|
|
||||||
fmt.Fprintf(out, " Type = %v\n", v.Type)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/docker/docker/api/types/swarm"
|
"github.com/docker/docker/api/types/swarm"
|
||||||
|
"github.com/docker/docker/cli/command/formatter"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestPrettyPrintWithNoUpdateConfig(t *testing.T) {
|
func TestPrettyPrintWithNoUpdateConfig(t *testing.T) {
|
||||||
|
@ -77,7 +78,18 @@ func TestPrettyPrintWithNoUpdateConfig(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
printService(b, s)
|
ctx := formatter.Context{
|
||||||
|
Output: b,
|
||||||
|
Format: formatter.NewServiceFormat("pretty"),
|
||||||
|
}
|
||||||
|
|
||||||
|
err := formatter.ServiceInspectWrite(ctx, []string{"de179gar9d0o7ltdybungplod"}, func(ref string) (interface{}, []byte, error) {
|
||||||
|
return s, nil, nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
if strings.Contains(b.String(), "UpdateStatus") {
|
if strings.Contains(b.String(), "UpdateStatus") {
|
||||||
t.Fatal("Pretty print failed before parsing UpdateStatus")
|
t.Fatal("Pretty print failed before parsing UpdateStatus")
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,15 +22,16 @@ const (
|
||||||
|
|
||||||
// ConfigFile ~/.docker/config.json file info
|
// ConfigFile ~/.docker/config.json file info
|
||||||
type ConfigFile struct {
|
type ConfigFile struct {
|
||||||
AuthConfigs map[string]types.AuthConfig `json:"auths"`
|
AuthConfigs map[string]types.AuthConfig `json:"auths"`
|
||||||
HTTPHeaders map[string]string `json:"HttpHeaders,omitempty"`
|
HTTPHeaders map[string]string `json:"HttpHeaders,omitempty"`
|
||||||
PsFormat string `json:"psFormat,omitempty"`
|
PsFormat string `json:"psFormat,omitempty"`
|
||||||
ImagesFormat string `json:"imagesFormat,omitempty"`
|
ImagesFormat string `json:"imagesFormat,omitempty"`
|
||||||
NetworksFormat string `json:"networksFormat,omitempty"`
|
NetworksFormat string `json:"networksFormat,omitempty"`
|
||||||
VolumesFormat string `json:"volumesFormat,omitempty"`
|
VolumesFormat string `json:"volumesFormat,omitempty"`
|
||||||
DetachKeys string `json:"detachKeys,omitempty"`
|
DetachKeys string `json:"detachKeys,omitempty"`
|
||||||
CredentialsStore string `json:"credsStore,omitempty"`
|
CredentialsStore string `json:"credsStore,omitempty"`
|
||||||
Filename string `json:"-"` // Note: for internal use only
|
Filename string `json:"-"` // Note: for internal use only
|
||||||
|
ServiceInspectFormat string `json:"serviceInspectFormat,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// LegacyLoadFromReader reads the non-nested configuration data given and sets up the
|
// LegacyLoadFromReader reads the non-nested configuration data given and sets up the
|
||||||
|
|
|
@ -143,6 +143,13 @@ Docker's client uses this property. If this property is not set, the client
|
||||||
falls back to the default table format. For a list of supported formatting
|
falls back to the default table format. For a list of supported formatting
|
||||||
directives, see the [**Formatting** section in the `docker images` documentation](images.md)
|
directives, see the [**Formatting** section in the `docker images` documentation](images.md)
|
||||||
|
|
||||||
|
The property `serviceInspectFormat` specifies the default format for `docker
|
||||||
|
service inspect` output. When the `--format` flag is not provided with the
|
||||||
|
`docker service inspect` command, Docker's client uses this property. If this
|
||||||
|
property is not set, the client falls back to the default json format. For a
|
||||||
|
list of supported formatting directives, see the
|
||||||
|
[**Formatting** section in the `docker service inspect` documentation](service_inspect.md)
|
||||||
|
|
||||||
Following is a sample `config.json` file:
|
Following is a sample `config.json` file:
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -151,6 +158,7 @@ Following is a sample `config.json` file:
|
||||||
},
|
},
|
||||||
"psFormat": "table {{.ID}}\\t{{.Image}}\\t{{.Command}}\\t{{.Labels}}",
|
"psFormat": "table {{.ID}}\\t{{.Image}}\\t{{.Command}}\\t{{.Labels}}",
|
||||||
"imagesFormat": "table {{.ID}}\\t{{.Repository}}\\t{{.Tag}}\\t{{.CreatedAt}}",
|
"imagesFormat": "table {{.ID}}\\t{{.Repository}}\\t{{.Tag}}\\t{{.CreatedAt}}",
|
||||||
|
"serviceInspectFormat": "pretty",
|
||||||
"detachKeys": "ctrl-e,e"
|
"detachKeys": "ctrl-e,e"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -130,6 +130,8 @@ Ports:
|
||||||
PublishedPort = 4443
|
PublishedPort = 4443
|
||||||
```
|
```
|
||||||
|
|
||||||
|
You can also use `--format pretty` for the same effect.
|
||||||
|
|
||||||
|
|
||||||
### Finding the number of tasks running as part of a service
|
### Finding the number of tasks running as part of a service
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue