service.go 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. package swarm
  2. import (
  3. "context"
  4. "runtime"
  5. "testing"
  6. "time"
  7. "github.com/docker/docker/api/types"
  8. "github.com/docker/docker/api/types/filters"
  9. swarmtypes "github.com/docker/docker/api/types/swarm"
  10. "github.com/docker/docker/internal/test/daemon"
  11. "github.com/docker/docker/internal/test/environment"
  12. "gotest.tools/assert"
  13. "gotest.tools/poll"
  14. "gotest.tools/skip"
  15. )
  16. // ServicePoll tweaks the pollSettings for `service`
  17. func ServicePoll(config *poll.Settings) {
  18. // Override the default pollSettings for `service` resource here ...
  19. config.Timeout = 30 * time.Second
  20. config.Delay = 100 * time.Millisecond
  21. if runtime.GOARCH == "arm64" || runtime.GOARCH == "arm" {
  22. config.Timeout = 90 * time.Second
  23. }
  24. }
  25. // NetworkPoll tweaks the pollSettings for `network`
  26. func NetworkPoll(config *poll.Settings) {
  27. // Override the default pollSettings for `network` resource here ...
  28. config.Timeout = 30 * time.Second
  29. config.Delay = 100 * time.Millisecond
  30. if runtime.GOARCH == "arm64" || runtime.GOARCH == "arm" {
  31. config.Timeout = 50 * time.Second
  32. }
  33. }
  34. // ContainerPoll tweaks the pollSettings for `container`
  35. func ContainerPoll(config *poll.Settings) {
  36. // Override the default pollSettings for `container` resource here ...
  37. if runtime.GOARCH == "arm64" || runtime.GOARCH == "arm" {
  38. config.Timeout = 30 * time.Second
  39. config.Delay = 100 * time.Millisecond
  40. }
  41. }
  42. // NewSwarm creates a swarm daemon for testing
  43. func NewSwarm(t *testing.T, testEnv *environment.Execution, ops ...func(*daemon.Daemon)) *daemon.Daemon {
  44. t.Helper()
  45. skip.If(t, testEnv.IsRemoteDaemon)
  46. skip.If(t, testEnv.DaemonInfo.OSType == "windows")
  47. if testEnv.DaemonInfo.ExperimentalBuild {
  48. ops = append(ops, daemon.WithExperimental)
  49. }
  50. d := daemon.New(t, ops...)
  51. d.StartAndSwarmInit(t)
  52. return d
  53. }
  54. // ServiceSpecOpt is used with `CreateService` to pass in service spec modifiers
  55. type ServiceSpecOpt func(*swarmtypes.ServiceSpec)
  56. // CreateService creates a service on the passed in swarm daemon.
  57. func CreateService(t *testing.T, d *daemon.Daemon, opts ...ServiceSpecOpt) string {
  58. t.Helper()
  59. client := d.NewClientT(t)
  60. defer client.Close()
  61. spec := CreateServiceSpec(t, opts...)
  62. resp, err := client.ServiceCreate(context.Background(), spec, types.ServiceCreateOptions{})
  63. assert.NilError(t, err, "error creating service")
  64. return resp.ID
  65. }
  66. // CreateServiceSpec creates a default service-spec, and applies the provided options
  67. func CreateServiceSpec(t *testing.T, opts ...ServiceSpecOpt) swarmtypes.ServiceSpec {
  68. t.Helper()
  69. var spec swarmtypes.ServiceSpec
  70. ServiceWithImage("busybox:latest")(&spec)
  71. ServiceWithCommand([]string{"/bin/top"})(&spec)
  72. ServiceWithReplicas(1)(&spec)
  73. for _, o := range opts {
  74. o(&spec)
  75. }
  76. return spec
  77. }
  78. // ServiceWithInit sets whether the service should use init or not
  79. func ServiceWithInit(b *bool) func(*swarmtypes.ServiceSpec) {
  80. return func(spec *swarmtypes.ServiceSpec) {
  81. ensureContainerSpec(spec)
  82. spec.TaskTemplate.ContainerSpec.Init = b
  83. }
  84. }
  85. // ServiceWithImage sets the image to use for the service
  86. func ServiceWithImage(image string) func(*swarmtypes.ServiceSpec) {
  87. return func(spec *swarmtypes.ServiceSpec) {
  88. ensureContainerSpec(spec)
  89. spec.TaskTemplate.ContainerSpec.Image = image
  90. }
  91. }
  92. // ServiceWithCommand sets the command to use for the service
  93. func ServiceWithCommand(cmd []string) ServiceSpecOpt {
  94. return func(spec *swarmtypes.ServiceSpec) {
  95. ensureContainerSpec(spec)
  96. spec.TaskTemplate.ContainerSpec.Command = cmd
  97. }
  98. }
  99. // ServiceWithConfig adds the config reference to the service
  100. func ServiceWithConfig(configRef *swarmtypes.ConfigReference) ServiceSpecOpt {
  101. return func(spec *swarmtypes.ServiceSpec) {
  102. ensureContainerSpec(spec)
  103. spec.TaskTemplate.ContainerSpec.Configs = append(spec.TaskTemplate.ContainerSpec.Configs, configRef)
  104. }
  105. }
  106. // ServiceWithSecret adds the secret reference to the service
  107. func ServiceWithSecret(secretRef *swarmtypes.SecretReference) ServiceSpecOpt {
  108. return func(spec *swarmtypes.ServiceSpec) {
  109. ensureContainerSpec(spec)
  110. spec.TaskTemplate.ContainerSpec.Secrets = append(spec.TaskTemplate.ContainerSpec.Secrets, secretRef)
  111. }
  112. }
  113. // ServiceWithReplicas sets the replicas for the service
  114. func ServiceWithReplicas(n uint64) ServiceSpecOpt {
  115. return func(spec *swarmtypes.ServiceSpec) {
  116. spec.Mode = swarmtypes.ServiceMode{
  117. Replicated: &swarmtypes.ReplicatedService{
  118. Replicas: &n,
  119. },
  120. }
  121. }
  122. }
  123. // ServiceWithName sets the name of the service
  124. func ServiceWithName(name string) ServiceSpecOpt {
  125. return func(spec *swarmtypes.ServiceSpec) {
  126. spec.Annotations.Name = name
  127. }
  128. }
  129. // ServiceWithNetwork sets the network of the service
  130. func ServiceWithNetwork(network string) ServiceSpecOpt {
  131. return func(spec *swarmtypes.ServiceSpec) {
  132. spec.TaskTemplate.Networks = append(spec.TaskTemplate.Networks,
  133. swarmtypes.NetworkAttachmentConfig{Target: network})
  134. }
  135. }
  136. // ServiceWithEndpoint sets the Endpoint of the service
  137. func ServiceWithEndpoint(endpoint *swarmtypes.EndpointSpec) ServiceSpecOpt {
  138. return func(spec *swarmtypes.ServiceSpec) {
  139. spec.EndpointSpec = endpoint
  140. }
  141. }
  142. // ServiceWithSysctls sets the Sysctls option of the service's ContainerSpec.
  143. func ServiceWithSysctls(sysctls map[string]string) ServiceSpecOpt {
  144. return func(spec *swarmtypes.ServiceSpec) {
  145. ensureContainerSpec(spec)
  146. spec.TaskTemplate.ContainerSpec.Sysctls = sysctls
  147. }
  148. }
  149. // GetRunningTasks gets the list of running tasks for a service
  150. func GetRunningTasks(t *testing.T, d *daemon.Daemon, serviceID string) []swarmtypes.Task {
  151. t.Helper()
  152. client := d.NewClientT(t)
  153. defer client.Close()
  154. filterArgs := filters.NewArgs()
  155. filterArgs.Add("desired-state", "running")
  156. filterArgs.Add("service", serviceID)
  157. options := types.TaskListOptions{
  158. Filters: filterArgs,
  159. }
  160. tasks, err := client.TaskList(context.Background(), options)
  161. assert.NilError(t, err)
  162. return tasks
  163. }
  164. // ExecTask runs the passed in exec config on the given task
  165. func ExecTask(t *testing.T, d *daemon.Daemon, task swarmtypes.Task, config types.ExecConfig) types.HijackedResponse {
  166. t.Helper()
  167. client := d.NewClientT(t)
  168. defer client.Close()
  169. ctx := context.Background()
  170. resp, err := client.ContainerExecCreate(ctx, task.Status.ContainerStatus.ContainerID, config)
  171. assert.NilError(t, err, "error creating exec")
  172. startCheck := types.ExecStartCheck{}
  173. attach, err := client.ContainerExecAttach(ctx, resp.ID, startCheck)
  174. assert.NilError(t, err, "error attaching to exec")
  175. return attach
  176. }
  177. func ensureContainerSpec(spec *swarmtypes.ServiceSpec) {
  178. if spec.TaskTemplate.ContainerSpec == nil {
  179. spec.TaskTemplate.ContainerSpec = &swarmtypes.ContainerSpec{}
  180. }
  181. }