service.go 15 KB

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