service.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422
  1. package formatter
  2. import (
  3. "fmt"
  4. "strings"
  5. "time"
  6. "github.com/docker/distribution/reference"
  7. mounttypes "github.com/docker/docker/api/types/mount"
  8. "github.com/docker/docker/api/types/swarm"
  9. "github.com/docker/docker/cli/command/inspect"
  10. "github.com/docker/docker/pkg/stringid"
  11. units "github.com/docker/go-units"
  12. )
  13. const serviceInspectPrettyTemplate Format = `
  14. ID: {{.ID}}
  15. Name: {{.Name}}
  16. {{- if .Labels }}
  17. Labels:
  18. {{- range $k, $v := .Labels }}
  19. {{ $k }}{{if $v }}={{ $v }}{{ end }}
  20. {{- end }}{{ end }}
  21. Service Mode:
  22. {{- if .IsModeGlobal }} Global
  23. {{- else if .IsModeReplicated }} Replicated
  24. {{- if .ModeReplicatedReplicas }}
  25. Replicas: {{ .ModeReplicatedReplicas }}
  26. {{- end }}{{ end }}
  27. {{- if .HasUpdateStatus }}
  28. UpdateStatus:
  29. State: {{ .UpdateStatusState }}
  30. {{- if .HasUpdateStatusStarted }}
  31. Started: {{ .UpdateStatusStarted }}
  32. {{- end }}
  33. {{- if .UpdateIsCompleted }}
  34. Completed: {{ .UpdateStatusCompleted }}
  35. {{- end }}
  36. Message: {{ .UpdateStatusMessage }}
  37. {{- end }}
  38. Placement:
  39. {{- if .TaskPlacementConstraints -}}
  40. Contraints: {{ .TaskPlacementConstraints }}
  41. {{- end }}
  42. {{- if .HasUpdateConfig }}
  43. UpdateConfig:
  44. Parallelism: {{ .UpdateParallelism }}
  45. {{- if .HasUpdateDelay}}
  46. Delay: {{ .UpdateDelay }}
  47. {{- end }}
  48. On failure: {{ .UpdateOnFailure }}
  49. {{- if .HasUpdateMonitor}}
  50. Monitoring Period: {{ .UpdateMonitor }}
  51. {{- end }}
  52. Max failure ratio: {{ .UpdateMaxFailureRatio }}
  53. {{- end }}
  54. ContainerSpec:
  55. Image: {{ .ContainerImage }}
  56. {{- if .ContainerArgs }}
  57. Args: {{ range $arg := .ContainerArgs }}{{ $arg }} {{ end }}
  58. {{- end -}}
  59. {{- if .ContainerEnv }}
  60. Env: {{ range $env := .ContainerEnv }}{{ $env }} {{ end }}
  61. {{- end -}}
  62. {{- if .ContainerWorkDir }}
  63. Dir: {{ .ContainerWorkDir }}
  64. {{- end -}}
  65. {{- if .ContainerUser }}
  66. User: {{ .ContainerUser }}
  67. {{- end }}
  68. {{- if .ContainerMounts }}
  69. Mounts:
  70. {{- end }}
  71. {{- range $mount := .ContainerMounts }}
  72. Target = {{ $mount.Target }}
  73. Source = {{ $mount.Source }}
  74. ReadOnly = {{ $mount.ReadOnly }}
  75. Type = {{ $mount.Type }}
  76. {{- end -}}
  77. {{- if .HasResources }}
  78. Resources:
  79. {{- if .HasResourceReservations }}
  80. Reservations:
  81. {{- if gt .ResourceReservationNanoCPUs 0.0 }}
  82. CPU: {{ .ResourceReservationNanoCPUs }}
  83. {{- end }}
  84. {{- if .ResourceReservationMemory }}
  85. Memory: {{ .ResourceReservationMemory }}
  86. {{- end }}{{ end }}
  87. {{- if .HasResourceLimits }}
  88. Limits:
  89. {{- if gt .ResourceLimitsNanoCPUs 0.0 }}
  90. CPU: {{ .ResourceLimitsNanoCPUs }}
  91. {{- end }}
  92. {{- if .ResourceLimitMemory }}
  93. Memory: {{ .ResourceLimitMemory }}
  94. {{- end }}{{ end }}{{ end }}
  95. {{- if .Networks }}
  96. Networks:
  97. {{- range $network := .Networks }} {{ $network }}{{ end }} {{ end }}
  98. Endpoint Mode: {{ .EndpointMode }}
  99. {{- if .Ports }}
  100. Ports:
  101. {{- range $port := .Ports }}
  102. PublishedPort {{ $port.PublishedPort }}
  103. Protocol = {{ $port.Protocol }}
  104. TargetPort = {{ $port.TargetPort }}
  105. PublishMode = {{ $port.PublishMode }}
  106. {{- end }} {{ end -}}
  107. `
  108. // NewServiceFormat returns a Format for rendering using a Context
  109. func NewServiceFormat(source string) Format {
  110. switch source {
  111. case PrettyFormatKey:
  112. return serviceInspectPrettyTemplate
  113. default:
  114. return Format(strings.TrimPrefix(source, RawFormatKey))
  115. }
  116. }
  117. // ServiceInspectWrite renders the context for a list of services
  118. func ServiceInspectWrite(ctx Context, refs []string, getRef inspect.GetRefFunc) error {
  119. if ctx.Format != serviceInspectPrettyTemplate {
  120. return inspect.Inspect(ctx.Output, refs, string(ctx.Format), getRef)
  121. }
  122. render := func(format func(subContext subContext) error) error {
  123. for _, ref := range refs {
  124. serviceI, _, err := getRef(ref)
  125. if err != nil {
  126. return err
  127. }
  128. service, ok := serviceI.(swarm.Service)
  129. if !ok {
  130. return fmt.Errorf("got wrong object to inspect")
  131. }
  132. if err := format(&serviceInspectContext{Service: service}); err != nil {
  133. return err
  134. }
  135. }
  136. return nil
  137. }
  138. return ctx.Write(&serviceInspectContext{}, render)
  139. }
  140. type serviceInspectContext struct {
  141. swarm.Service
  142. subContext
  143. }
  144. func (ctx *serviceInspectContext) MarshalJSON() ([]byte, error) {
  145. return marshalJSON(ctx)
  146. }
  147. func (ctx *serviceInspectContext) ID() string {
  148. return ctx.Service.ID
  149. }
  150. func (ctx *serviceInspectContext) Name() string {
  151. return ctx.Service.Spec.Name
  152. }
  153. func (ctx *serviceInspectContext) Labels() map[string]string {
  154. return ctx.Service.Spec.Labels
  155. }
  156. func (ctx *serviceInspectContext) IsModeGlobal() bool {
  157. return ctx.Service.Spec.Mode.Global != nil
  158. }
  159. func (ctx *serviceInspectContext) IsModeReplicated() bool {
  160. return ctx.Service.Spec.Mode.Replicated != nil
  161. }
  162. func (ctx *serviceInspectContext) ModeReplicatedReplicas() *uint64 {
  163. return ctx.Service.Spec.Mode.Replicated.Replicas
  164. }
  165. func (ctx *serviceInspectContext) HasUpdateStatus() bool {
  166. return ctx.Service.UpdateStatus != nil && ctx.Service.UpdateStatus.State != ""
  167. }
  168. func (ctx *serviceInspectContext) UpdateStatusState() swarm.UpdateState {
  169. return ctx.Service.UpdateStatus.State
  170. }
  171. func (ctx *serviceInspectContext) HasUpdateStatusStarted() bool {
  172. return ctx.Service.UpdateStatus.StartedAt != nil
  173. }
  174. func (ctx *serviceInspectContext) UpdateStatusStarted() string {
  175. return units.HumanDuration(time.Since(*ctx.Service.UpdateStatus.StartedAt))
  176. }
  177. func (ctx *serviceInspectContext) UpdateIsCompleted() bool {
  178. return ctx.Service.UpdateStatus.State == swarm.UpdateStateCompleted && ctx.Service.UpdateStatus.CompletedAt != nil
  179. }
  180. func (ctx *serviceInspectContext) UpdateStatusCompleted() string {
  181. return units.HumanDuration(time.Since(*ctx.Service.UpdateStatus.CompletedAt))
  182. }
  183. func (ctx *serviceInspectContext) UpdateStatusMessage() string {
  184. return ctx.Service.UpdateStatus.Message
  185. }
  186. func (ctx *serviceInspectContext) TaskPlacementConstraints() []string {
  187. if ctx.Service.Spec.TaskTemplate.Placement != nil {
  188. return ctx.Service.Spec.TaskTemplate.Placement.Constraints
  189. }
  190. return nil
  191. }
  192. func (ctx *serviceInspectContext) HasUpdateConfig() bool {
  193. return ctx.Service.Spec.UpdateConfig != nil
  194. }
  195. func (ctx *serviceInspectContext) UpdateParallelism() uint64 {
  196. return ctx.Service.Spec.UpdateConfig.Parallelism
  197. }
  198. func (ctx *serviceInspectContext) HasUpdateDelay() bool {
  199. return ctx.Service.Spec.UpdateConfig.Delay.Nanoseconds() > 0
  200. }
  201. func (ctx *serviceInspectContext) UpdateDelay() time.Duration {
  202. return ctx.Service.Spec.UpdateConfig.Delay
  203. }
  204. func (ctx *serviceInspectContext) UpdateOnFailure() string {
  205. return ctx.Service.Spec.UpdateConfig.FailureAction
  206. }
  207. func (ctx *serviceInspectContext) HasUpdateMonitor() bool {
  208. return ctx.Service.Spec.UpdateConfig.Monitor.Nanoseconds() > 0
  209. }
  210. func (ctx *serviceInspectContext) UpdateMonitor() time.Duration {
  211. return ctx.Service.Spec.UpdateConfig.Monitor
  212. }
  213. func (ctx *serviceInspectContext) UpdateMaxFailureRatio() float32 {
  214. return ctx.Service.Spec.UpdateConfig.MaxFailureRatio
  215. }
  216. func (ctx *serviceInspectContext) ContainerImage() string {
  217. return ctx.Service.Spec.TaskTemplate.ContainerSpec.Image
  218. }
  219. func (ctx *serviceInspectContext) ContainerArgs() []string {
  220. return ctx.Service.Spec.TaskTemplate.ContainerSpec.Args
  221. }
  222. func (ctx *serviceInspectContext) ContainerEnv() []string {
  223. return ctx.Service.Spec.TaskTemplate.ContainerSpec.Env
  224. }
  225. func (ctx *serviceInspectContext) ContainerWorkDir() string {
  226. return ctx.Service.Spec.TaskTemplate.ContainerSpec.Dir
  227. }
  228. func (ctx *serviceInspectContext) ContainerUser() string {
  229. return ctx.Service.Spec.TaskTemplate.ContainerSpec.User
  230. }
  231. func (ctx *serviceInspectContext) ContainerMounts() []mounttypes.Mount {
  232. return ctx.Service.Spec.TaskTemplate.ContainerSpec.Mounts
  233. }
  234. func (ctx *serviceInspectContext) HasResources() bool {
  235. return ctx.Service.Spec.TaskTemplate.Resources != nil
  236. }
  237. func (ctx *serviceInspectContext) HasResourceReservations() bool {
  238. if ctx.Service.Spec.TaskTemplate.Resources == nil || ctx.Service.Spec.TaskTemplate.Resources.Reservations == nil {
  239. return false
  240. }
  241. return ctx.Service.Spec.TaskTemplate.Resources.Reservations.NanoCPUs > 0 || ctx.Service.Spec.TaskTemplate.Resources.Reservations.MemoryBytes > 0
  242. }
  243. func (ctx *serviceInspectContext) ResourceReservationNanoCPUs() float64 {
  244. if ctx.Service.Spec.TaskTemplate.Resources.Reservations.NanoCPUs == 0 {
  245. return float64(0)
  246. }
  247. return float64(ctx.Service.Spec.TaskTemplate.Resources.Reservations.NanoCPUs) / 1e9
  248. }
  249. func (ctx *serviceInspectContext) ResourceReservationMemory() string {
  250. if ctx.Service.Spec.TaskTemplate.Resources.Reservations.MemoryBytes == 0 {
  251. return ""
  252. }
  253. return units.BytesSize(float64(ctx.Service.Spec.TaskTemplate.Resources.Reservations.MemoryBytes))
  254. }
  255. func (ctx *serviceInspectContext) HasResourceLimits() bool {
  256. if ctx.Service.Spec.TaskTemplate.Resources == nil || ctx.Service.Spec.TaskTemplate.Resources.Limits == nil {
  257. return false
  258. }
  259. return ctx.Service.Spec.TaskTemplate.Resources.Limits.NanoCPUs > 0 || ctx.Service.Spec.TaskTemplate.Resources.Limits.MemoryBytes > 0
  260. }
  261. func (ctx *serviceInspectContext) ResourceLimitsNanoCPUs() float64 {
  262. return float64(ctx.Service.Spec.TaskTemplate.Resources.Limits.NanoCPUs) / 1e9
  263. }
  264. func (ctx *serviceInspectContext) ResourceLimitMemory() string {
  265. if ctx.Service.Spec.TaskTemplate.Resources.Limits.MemoryBytes == 0 {
  266. return ""
  267. }
  268. return units.BytesSize(float64(ctx.Service.Spec.TaskTemplate.Resources.Limits.MemoryBytes))
  269. }
  270. func (ctx *serviceInspectContext) Networks() []string {
  271. var out []string
  272. for _, n := range ctx.Service.Spec.Networks {
  273. out = append(out, n.Target)
  274. }
  275. return out
  276. }
  277. func (ctx *serviceInspectContext) EndpointMode() string {
  278. if ctx.Service.Spec.EndpointSpec == nil {
  279. return ""
  280. }
  281. return string(ctx.Service.Spec.EndpointSpec.Mode)
  282. }
  283. func (ctx *serviceInspectContext) Ports() []swarm.PortConfig {
  284. return ctx.Service.Endpoint.Ports
  285. }
  286. const (
  287. defaultServiceTableFormat = "table {{.ID}}\t{{.Name}}\t{{.Mode}}\t{{.Replicas}}\t{{.Image}}"
  288. serviceIDHeader = "ID"
  289. modeHeader = "MODE"
  290. replicasHeader = "REPLICAS"
  291. )
  292. // NewServiceListFormat returns a Format for rendering using a service Context
  293. func NewServiceListFormat(source string, quiet bool) Format {
  294. switch source {
  295. case TableFormatKey:
  296. if quiet {
  297. return defaultQuietFormat
  298. }
  299. return defaultServiceTableFormat
  300. case RawFormatKey:
  301. if quiet {
  302. return `id: {{.ID}}`
  303. }
  304. return `id: {{.ID}}\nname: {{.Name}}\nmode: {{.Mode}}\nreplicas: {{.Replicas}}\nimage: {{.Image}}\n`
  305. }
  306. return Format(source)
  307. }
  308. // ServiceListInfo stores the information about mode and replicas to be used by template
  309. type ServiceListInfo struct {
  310. Mode string
  311. Replicas string
  312. }
  313. // ServiceListWrite writes the context
  314. func ServiceListWrite(ctx Context, services []swarm.Service, info map[string]ServiceListInfo) error {
  315. render := func(format func(subContext subContext) error) error {
  316. for _, service := range services {
  317. serviceCtx := &serviceContext{service: service, mode: info[service.ID].Mode, replicas: info[service.ID].Replicas}
  318. if err := format(serviceCtx); err != nil {
  319. return err
  320. }
  321. }
  322. return nil
  323. }
  324. return ctx.Write(&serviceContext{}, render)
  325. }
  326. type serviceContext struct {
  327. HeaderContext
  328. service swarm.Service
  329. mode string
  330. replicas string
  331. }
  332. func (c *serviceContext) MarshalJSON() ([]byte, error) {
  333. return marshalJSON(c)
  334. }
  335. func (c *serviceContext) ID() string {
  336. c.AddHeader(serviceIDHeader)
  337. return stringid.TruncateID(c.service.ID)
  338. }
  339. func (c *serviceContext) Name() string {
  340. c.AddHeader(nameHeader)
  341. return c.service.Spec.Name
  342. }
  343. func (c *serviceContext) Mode() string {
  344. c.AddHeader(modeHeader)
  345. return c.mode
  346. }
  347. func (c *serviceContext) Replicas() string {
  348. c.AddHeader(replicasHeader)
  349. return c.replicas
  350. }
  351. func (c *serviceContext) Image() string {
  352. c.AddHeader(imageHeader)
  353. image := c.service.Spec.TaskTemplate.ContainerSpec.Image
  354. if ref, err := reference.ParseNormalizedNamed(image); err == nil {
  355. // update image string for display, (strips any digest)
  356. if nt, ok := ref.(reference.NamedTagged); ok {
  357. if namedTagged, err := reference.WithTag(reference.TrimNamed(nt), nt.Tag()); err == nil {
  358. image = reference.FamiliarString(namedTagged)
  359. }
  360. }
  361. }
  362. return image
  363. }