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 }} Service Mode: {{- if .IsModeGlobal }} Global {{- else if .IsModeReplicated }} Replicated {{- if .ModeReplicatedReplicas }} Replicas: {{ .ModeReplicatedReplicas }} {{- end }}{{ end }} {{- if .HasUpdateStatus }} UpdateStatus: State: {{ .UpdateStatusState }} {{- if .HasUpdateStatusStarted }} Started: {{ .UpdateStatusStarted }} {{- end }} {{- 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 }} {{- if .HasUpdateMonitor}} Monitoring Period: {{ .UpdateMonitor }} {{- end }} Max failure ratio: {{ .UpdateMaxFailureRatio }} {{- 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: {{- if gt .ResourceReservationNanoCPUs 0.0 }} CPU: {{ .ResourceReservationNanoCPUs }} {{- end }} {{- if .ResourceReservationMemory }} Memory: {{ .ResourceReservationMemory }} {{- end }}{{ end }} {{- if .HasResourceLimits }} Limits: {{- if gt .ResourceLimitsNanoCPUs 0.0 }} CPU: {{ .ResourceLimitsNanoCPUs }} {{- end }} {{- if .ResourceLimitMemory }} Memory: {{ .ResourceLimitMemory }} {{- end }}{{ end }}{{ end }} {{- if .Networks }} Networks: {{- range $network := .Networks }} {{ $network }}{{ end }} {{ end }} Endpoint Mode: {{ .EndpointMode }} {{- 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) MarshalJSON() ([]byte, error) { return marshalJSON(ctx) } 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) IsModeReplicated() bool { return ctx.Service.Spec.Mode.Replicated != nil } func (ctx *serviceInspectContext) ModeReplicatedReplicas() *uint64 { return ctx.Service.Spec.Mode.Replicated.Replicas } func (ctx *serviceInspectContext) HasUpdateStatus() bool { return ctx.Service.UpdateStatus != nil && ctx.Service.UpdateStatus.State != "" } func (ctx *serviceInspectContext) UpdateStatusState() swarm.UpdateState { return ctx.Service.UpdateStatus.State } func (ctx *serviceInspectContext) HasUpdateStatusStarted() bool { return ctx.Service.UpdateStatus.StartedAt != nil } 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 && ctx.Service.UpdateStatus.CompletedAt != nil } 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) HasUpdateMonitor() bool { return ctx.Service.Spec.UpdateConfig.Monitor.Nanoseconds() > 0 } func (ctx *serviceInspectContext) UpdateMonitor() time.Duration { return ctx.Service.Spec.UpdateConfig.Monitor } func (ctx *serviceInspectContext) UpdateMaxFailureRatio() float32 { return ctx.Service.Spec.UpdateConfig.MaxFailureRatio } 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 { if ctx.Service.Spec.TaskTemplate.Resources == nil || ctx.Service.Spec.TaskTemplate.Resources.Reservations == nil { return false } 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 { if ctx.Service.Spec.TaskTemplate.Resources == nil || ctx.Service.Spec.TaskTemplate.Resources.Limits == nil { return false } 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) EndpointMode() string { if ctx.Service.Spec.EndpointSpec == nil { return "" } return string(ctx.Service.Spec.EndpointSpec.Mode) } func (ctx *serviceInspectContext) Ports() []swarm.PortConfig { return ctx.Service.Endpoint.Ports }