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