service.go 6.8 KB

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