container.go 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. /*
  2. Copyright 2020 Docker Compose CLI authors
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package formatter
  14. import (
  15. "time"
  16. "github.com/docker/cli/cli/command/formatter"
  17. "github.com/docker/compose/v2/pkg/api"
  18. "github.com/docker/docker/api/types"
  19. "github.com/docker/docker/pkg/stringid"
  20. "github.com/docker/go-units"
  21. )
  22. const (
  23. defaultContainerTableFormat = "table {{.Name}}\t{{.Image}}\t{{.Command}}\t{{.Service}}\t{{.RunningFor}}\t{{.Status}}\t{{.Ports}}"
  24. nameHeader = "NAME"
  25. serviceHeader = "SERVICE"
  26. commandHeader = "COMMAND"
  27. runningForHeader = "CREATED"
  28. mountsHeader = "MOUNTS"
  29. localVolumes = "LOCAL VOLUMES"
  30. networksHeader = "NETWORKS"
  31. )
  32. // NewContainerFormat returns a Format for rendering using a Context
  33. func NewContainerFormat(source string, quiet bool, size bool) formatter.Format {
  34. switch source {
  35. case formatter.TableFormatKey, "": // table formatting is the default if none is set.
  36. if quiet {
  37. return formatter.DefaultQuietFormat
  38. }
  39. format := defaultContainerTableFormat
  40. if size {
  41. format += `\t{{.Size}}`
  42. }
  43. return formatter.Format(format)
  44. case formatter.RawFormatKey:
  45. if quiet {
  46. return `container_id: {{.ID}}`
  47. }
  48. format := `container_id: {{.ID}}
  49. image: {{.Image}}
  50. command: {{.Command}}
  51. created_at: {{.CreatedAt}}
  52. state: {{- pad .State 1 0}}
  53. status: {{- pad .Status 1 0}}
  54. names: {{.Names}}
  55. labels: {{- pad .Labels 1 0}}
  56. ports: {{- pad .Ports 1 0}}
  57. `
  58. if size {
  59. format += `size: {{.Size}}\n`
  60. }
  61. return formatter.Format(format)
  62. default: // custom format
  63. if quiet {
  64. return formatter.DefaultQuietFormat
  65. }
  66. return formatter.Format(source)
  67. }
  68. }
  69. // ContainerWrite renders the context for a list of containers
  70. func ContainerWrite(ctx formatter.Context, containers []api.ContainerSummary) error {
  71. render := func(format func(subContext formatter.SubContext) error) error {
  72. for _, container := range containers {
  73. err := format(&ContainerContext{trunc: ctx.Trunc, c: container})
  74. if err != nil {
  75. return err
  76. }
  77. }
  78. return nil
  79. }
  80. return ctx.Write(NewContainerContext(), render)
  81. }
  82. // ContainerContext is a struct used for rendering a list of containers in a Go template.
  83. type ContainerContext struct {
  84. formatter.HeaderContext
  85. trunc bool
  86. c api.ContainerSummary
  87. // FieldsUsed is used in the pre-processing step to detect which fields are
  88. // used in the template. It's currently only used to detect use of the .Size
  89. // field which (if used) automatically sets the '--size' option when making
  90. // the API call.
  91. FieldsUsed map[string]interface{}
  92. }
  93. // NewContainerContext creates a new context for rendering containers
  94. func NewContainerContext() *ContainerContext {
  95. containerCtx := ContainerContext{}
  96. containerCtx.Header = formatter.SubHeaderContext{
  97. "ID": formatter.ContainerIDHeader,
  98. "Name": nameHeader,
  99. "Service": serviceHeader,
  100. "Image": formatter.ImageHeader,
  101. "Command": commandHeader,
  102. "CreatedAt": formatter.CreatedAtHeader,
  103. "RunningFor": runningForHeader,
  104. "Ports": formatter.PortsHeader,
  105. "State": formatter.StateHeader,
  106. "Status": formatter.StatusHeader,
  107. "Size": formatter.SizeHeader,
  108. "Labels": formatter.LabelsHeader,
  109. }
  110. return &containerCtx
  111. }
  112. // MarshalJSON makes ContainerContext implement json.Marshaler
  113. func (c *ContainerContext) MarshalJSON() ([]byte, error) {
  114. return formatter.MarshalJSON(c)
  115. }
  116. // ID returns the container's ID as a string. Depending on the `--no-trunc`
  117. // option being set, the full or truncated ID is returned.
  118. func (c *ContainerContext) ID() string {
  119. if c.trunc {
  120. return stringid.TruncateID(c.c.ID)
  121. }
  122. return c.c.ID
  123. }
  124. func (c *ContainerContext) Name() string {
  125. return c.c.Name
  126. }
  127. func (c *ContainerContext) Service() string {
  128. return c.c.Service
  129. }
  130. func (c *ContainerContext) Image() string {
  131. return c.c.Image
  132. }
  133. func (c *ContainerContext) Command() string {
  134. return c.c.Command
  135. }
  136. func (c *ContainerContext) CreatedAt() string {
  137. return time.Unix(c.c.Created, 0).String()
  138. }
  139. func (c *ContainerContext) RunningFor() string {
  140. createdAt := time.Unix(c.c.Created, 0)
  141. return units.HumanDuration(time.Now().UTC().Sub(createdAt)) + " ago"
  142. }
  143. func (c *ContainerContext) ExitCode() int {
  144. return c.c.ExitCode
  145. }
  146. func (c *ContainerContext) State() string {
  147. return c.c.State
  148. }
  149. func (c *ContainerContext) Status() string {
  150. return c.c.Status
  151. }
  152. func (c *ContainerContext) Health() string {
  153. return c.c.Health
  154. }
  155. func (c *ContainerContext) Publishers() api.PortPublishers {
  156. return c.c.Publishers
  157. }
  158. func (c *ContainerContext) Ports() string {
  159. var ports []types.Port
  160. for _, publisher := range c.c.Publishers {
  161. ports = append(ports, types.Port{
  162. IP: publisher.URL,
  163. PrivatePort: uint16(publisher.TargetPort),
  164. PublicPort: uint16(publisher.PublishedPort),
  165. Type: publisher.Protocol,
  166. })
  167. }
  168. return formatter.DisplayablePorts(ports)
  169. }