inspect.go 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. package service
  2. import (
  3. "fmt"
  4. "io"
  5. "strings"
  6. "time"
  7. "golang.org/x/net/context"
  8. "github.com/docker/docker/api/client"
  9. "github.com/docker/docker/api/client/inspect"
  10. "github.com/docker/docker/cli"
  11. "github.com/docker/docker/pkg/ioutils"
  12. apiclient "github.com/docker/engine-api/client"
  13. "github.com/docker/engine-api/types/swarm"
  14. "github.com/docker/go-units"
  15. "github.com/spf13/cobra"
  16. )
  17. type inspectOptions struct {
  18. refs []string
  19. format string
  20. pretty bool
  21. }
  22. func newInspectCommand(dockerCli *client.DockerCli) *cobra.Command {
  23. var opts inspectOptions
  24. cmd := &cobra.Command{
  25. Use: "inspect [OPTIONS] SERVICE [SERVICE...]",
  26. Short: "Display detailed information on one or more services",
  27. Args: cli.RequiresMinArgs(1),
  28. RunE: func(cmd *cobra.Command, args []string) error {
  29. opts.refs = args
  30. if opts.pretty && len(opts.format) > 0 {
  31. return fmt.Errorf("--format is incompatible with human friendly format")
  32. }
  33. return runInspect(dockerCli, opts)
  34. },
  35. }
  36. flags := cmd.Flags()
  37. flags.StringVarP(&opts.format, "format", "f", "", "Format the output using the given go template")
  38. flags.BoolVar(&opts.pretty, "pretty", false, "Print the information in a human friendly format.")
  39. return cmd
  40. }
  41. func runInspect(dockerCli *client.DockerCli, opts inspectOptions) error {
  42. client := dockerCli.Client()
  43. ctx := context.Background()
  44. getRef := func(ref string) (interface{}, []byte, error) {
  45. service, _, err := client.ServiceInspectWithRaw(ctx, ref)
  46. if err == nil || !apiclient.IsErrServiceNotFound(err) {
  47. return service, nil, err
  48. }
  49. return nil, nil, fmt.Errorf("Error: no such service: %s", ref)
  50. }
  51. if !opts.pretty {
  52. return inspect.Inspect(dockerCli.Out(), opts.refs, opts.format, getRef)
  53. }
  54. return printHumanFriendly(dockerCli.Out(), opts.refs, getRef)
  55. }
  56. func printHumanFriendly(out io.Writer, refs []string, getRef inspect.GetRefFunc) error {
  57. for idx, ref := range refs {
  58. obj, _, err := getRef(ref)
  59. if err != nil {
  60. return err
  61. }
  62. printService(out, obj.(swarm.Service))
  63. // TODO: better way to do this?
  64. // print extra space between objects, but not after the last one
  65. if idx+1 != len(refs) {
  66. fmt.Fprintf(out, "\n\n")
  67. }
  68. }
  69. return nil
  70. }
  71. // TODO: use a template
  72. func printService(out io.Writer, service swarm.Service) {
  73. fmt.Fprintf(out, "ID:\t\t%s\n", service.ID)
  74. fmt.Fprintf(out, "Name:\t\t%s\n", service.Spec.Name)
  75. if service.Spec.Labels != nil {
  76. fmt.Fprintln(out, "Labels:")
  77. for k, v := range service.Spec.Labels {
  78. fmt.Fprintf(out, " - %s=%s\n", k, v)
  79. }
  80. }
  81. if service.Spec.Mode.Global != nil {
  82. fmt.Fprintln(out, "Mode:\t\tGlobal")
  83. } else {
  84. fmt.Fprintln(out, "Mode:\t\tReplicated")
  85. if service.Spec.Mode.Replicated.Replicas != nil {
  86. fmt.Fprintf(out, " Replicas:\t%d\n", *service.Spec.Mode.Replicated.Replicas)
  87. }
  88. }
  89. if service.UpdateStatus.State != "" {
  90. fmt.Fprintln(out, "Update status:")
  91. fmt.Fprintf(out, " State:\t\t%s\n", service.UpdateStatus.State)
  92. fmt.Fprintf(out, " Started:\t%s ago\n", strings.ToLower(units.HumanDuration(time.Since(service.UpdateStatus.StartedAt))))
  93. if service.UpdateStatus.State == swarm.UpdateStateCompleted {
  94. fmt.Fprintf(out, " Completed:\t%s ago\n", strings.ToLower(units.HumanDuration(time.Since(service.UpdateStatus.CompletedAt))))
  95. }
  96. fmt.Fprintf(out, " Message:\t%s\n", service.UpdateStatus.Message)
  97. }
  98. fmt.Fprintln(out, "Placement:")
  99. if service.Spec.TaskTemplate.Placement != nil && len(service.Spec.TaskTemplate.Placement.Constraints) > 0 {
  100. ioutils.FprintfIfNotEmpty(out, " Constraints\t: %s\n", strings.Join(service.Spec.TaskTemplate.Placement.Constraints, ", "))
  101. }
  102. fmt.Fprintf(out, "UpdateConfig:\n")
  103. fmt.Fprintf(out, " Parallelism:\t%d\n", service.Spec.UpdateConfig.Parallelism)
  104. if service.Spec.UpdateConfig.Delay.Nanoseconds() > 0 {
  105. fmt.Fprintf(out, " Delay:\t\t%s\n", service.Spec.UpdateConfig.Delay)
  106. }
  107. fmt.Fprintf(out, " On failure:\t%s\n", service.Spec.UpdateConfig.FailureAction)
  108. fmt.Fprintf(out, "ContainerSpec:\n")
  109. printContainerSpec(out, service.Spec.TaskTemplate.ContainerSpec)
  110. resources := service.Spec.TaskTemplate.Resources
  111. if resources != nil {
  112. fmt.Fprintln(out, "Resources:")
  113. printResources := func(out io.Writer, requirement string, r *swarm.Resources) {
  114. if r == nil || (r.MemoryBytes == 0 && r.NanoCPUs == 0) {
  115. return
  116. }
  117. fmt.Fprintf(out, " %s:\n", requirement)
  118. if r.NanoCPUs != 0 {
  119. fmt.Fprintf(out, " CPU:\t\t%g\n", float64(r.NanoCPUs)/1e9)
  120. }
  121. if r.MemoryBytes != 0 {
  122. fmt.Fprintf(out, " Memory:\t%s\n", units.BytesSize(float64(r.MemoryBytes)))
  123. }
  124. }
  125. printResources(out, "Reservations", resources.Reservations)
  126. printResources(out, "Limits", resources.Limits)
  127. }
  128. if len(service.Spec.Networks) > 0 {
  129. fmt.Fprintf(out, "Networks:")
  130. for _, n := range service.Spec.Networks {
  131. fmt.Fprintf(out, " %s", n.Target)
  132. }
  133. fmt.Fprintln(out, "")
  134. }
  135. if len(service.Endpoint.Ports) > 0 {
  136. fmt.Fprintln(out, "Ports:")
  137. for _, port := range service.Endpoint.Ports {
  138. ioutils.FprintfIfNotEmpty(out, " Name = %s\n", port.Name)
  139. fmt.Fprintf(out, " Protocol = %s\n", port.Protocol)
  140. fmt.Fprintf(out, " TargetPort = %d\n", port.TargetPort)
  141. fmt.Fprintf(out, " PublishedPort = %d\n", port.PublishedPort)
  142. }
  143. }
  144. }
  145. func printContainerSpec(out io.Writer, containerSpec swarm.ContainerSpec) {
  146. fmt.Fprintf(out, " Image:\t\t%s\n", containerSpec.Image)
  147. if len(containerSpec.Args) > 0 {
  148. fmt.Fprintf(out, " Args:\t\t%s\n", strings.Join(containerSpec.Args, " "))
  149. }
  150. if len(containerSpec.Env) > 0 {
  151. fmt.Fprintf(out, " Env:\t\t%s\n", strings.Join(containerSpec.Env, " "))
  152. }
  153. ioutils.FprintfIfNotEmpty(out, " Dir\t\t%s\n", containerSpec.Dir)
  154. ioutils.FprintfIfNotEmpty(out, " User\t\t%s\n", containerSpec.User)
  155. if len(containerSpec.Mounts) > 0 {
  156. fmt.Fprintln(out, " Mounts:")
  157. for _, v := range containerSpec.Mounts {
  158. fmt.Fprintf(out, " Target = %s\n", v.Target)
  159. fmt.Fprintf(out, " Source = %s\n", v.Source)
  160. fmt.Fprintf(out, " ReadOnly = %v\n", v.ReadOnly)
  161. fmt.Fprintf(out, " Type = %v\n", v.Type)
  162. }
  163. }
  164. }