github.com/demonoid81/moby@v0.0.0-20200517203328-62dd8e17c460/integration/internal/swarm/service.go (about) 1 package swarm 2 3 import ( 4 "context" 5 "runtime" 6 "testing" 7 "time" 8 9 "github.com/demonoid81/moby/api/types" 10 "github.com/demonoid81/moby/api/types/filters" 11 swarmtypes "github.com/demonoid81/moby/api/types/swarm" 12 "github.com/demonoid81/moby/client" 13 "github.com/demonoid81/moby/testutil/daemon" 14 "github.com/demonoid81/moby/testutil/environment" 15 "gotest.tools/v3/assert" 16 "gotest.tools/v3/poll" 17 "gotest.tools/v3/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 = 15 * 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 ...daemon.Option) *daemon.Daemon { 53 t.Helper() 54 skip.If(t, testEnv.IsRemoteDaemon) 55 skip.If(t, testEnv.DaemonInfo.OSType == "windows") 56 skip.If(t, testEnv.IsRootless, "rootless mode doesn't support Swarm-mode") 57 if testEnv.DaemonInfo.ExperimentalBuild { 58 ops = append(ops, daemon.WithExperimental()) 59 } 60 d := daemon.New(t, ops...) 61 d.StartAndSwarmInit(t) 62 return d 63 } 64 65 // ServiceSpecOpt is used with `CreateService` to pass in service spec modifiers 66 type ServiceSpecOpt func(*swarmtypes.ServiceSpec) 67 68 // CreateService creates a service on the passed in swarm daemon. 69 func CreateService(t *testing.T, d *daemon.Daemon, opts ...ServiceSpecOpt) string { 70 t.Helper() 71 72 client := d.NewClientT(t) 73 defer client.Close() 74 75 spec := CreateServiceSpec(t, opts...) 76 resp, err := client.ServiceCreate(context.Background(), spec, types.ServiceCreateOptions{}) 77 assert.NilError(t, err, "error creating service") 78 return resp.ID 79 } 80 81 // CreateServiceSpec creates a default service-spec, and applies the provided options 82 func CreateServiceSpec(t *testing.T, opts ...ServiceSpecOpt) swarmtypes.ServiceSpec { 83 t.Helper() 84 var spec swarmtypes.ServiceSpec 85 ServiceWithImage("busybox:latest")(&spec) 86 ServiceWithCommand([]string{"/bin/top"})(&spec) 87 ServiceWithReplicas(1)(&spec) 88 89 for _, o := range opts { 90 o(&spec) 91 } 92 return spec 93 } 94 95 // ServiceWithMode sets the mode of the service to the provided mode. 96 func ServiceWithMode(mode swarmtypes.ServiceMode) func(*swarmtypes.ServiceSpec) { 97 return func(spec *swarmtypes.ServiceSpec) { 98 spec.Mode = mode 99 } 100 } 101 102 // ServiceWithInit sets whether the service should use init or not 103 func ServiceWithInit(b *bool) func(*swarmtypes.ServiceSpec) { 104 return func(spec *swarmtypes.ServiceSpec) { 105 ensureContainerSpec(spec) 106 spec.TaskTemplate.ContainerSpec.Init = b 107 } 108 } 109 110 // ServiceWithImage sets the image to use for the service 111 func ServiceWithImage(image string) func(*swarmtypes.ServiceSpec) { 112 return func(spec *swarmtypes.ServiceSpec) { 113 ensureContainerSpec(spec) 114 spec.TaskTemplate.ContainerSpec.Image = image 115 } 116 } 117 118 // ServiceWithCommand sets the command to use for the service 119 func ServiceWithCommand(cmd []string) ServiceSpecOpt { 120 return func(spec *swarmtypes.ServiceSpec) { 121 ensureContainerSpec(spec) 122 spec.TaskTemplate.ContainerSpec.Command = cmd 123 } 124 } 125 126 // ServiceWithConfig adds the config reference to the service 127 func ServiceWithConfig(configRef *swarmtypes.ConfigReference) ServiceSpecOpt { 128 return func(spec *swarmtypes.ServiceSpec) { 129 ensureContainerSpec(spec) 130 spec.TaskTemplate.ContainerSpec.Configs = append(spec.TaskTemplate.ContainerSpec.Configs, configRef) 131 } 132 } 133 134 // ServiceWithSecret adds the secret reference to the service 135 func ServiceWithSecret(secretRef *swarmtypes.SecretReference) ServiceSpecOpt { 136 return func(spec *swarmtypes.ServiceSpec) { 137 ensureContainerSpec(spec) 138 spec.TaskTemplate.ContainerSpec.Secrets = append(spec.TaskTemplate.ContainerSpec.Secrets, secretRef) 139 } 140 } 141 142 // ServiceWithReplicas sets the replicas for the service 143 func ServiceWithReplicas(n uint64) ServiceSpecOpt { 144 return func(spec *swarmtypes.ServiceSpec) { 145 spec.Mode = swarmtypes.ServiceMode{ 146 Replicated: &swarmtypes.ReplicatedService{ 147 Replicas: &n, 148 }, 149 } 150 } 151 } 152 153 // ServiceWithMaxReplicas sets the max replicas for the service 154 func ServiceWithMaxReplicas(n uint64) ServiceSpecOpt { 155 return func(spec *swarmtypes.ServiceSpec) { 156 ensurePlacement(spec) 157 spec.TaskTemplate.Placement.MaxReplicas = n 158 } 159 } 160 161 // ServiceWithName sets the name of the service 162 func ServiceWithName(name string) ServiceSpecOpt { 163 return func(spec *swarmtypes.ServiceSpec) { 164 spec.Annotations.Name = name 165 } 166 } 167 168 // ServiceWithNetwork sets the network of the service 169 func ServiceWithNetwork(network string) ServiceSpecOpt { 170 return func(spec *swarmtypes.ServiceSpec) { 171 spec.TaskTemplate.Networks = append(spec.TaskTemplate.Networks, 172 swarmtypes.NetworkAttachmentConfig{Target: network}) 173 } 174 } 175 176 // ServiceWithEndpoint sets the Endpoint of the service 177 func ServiceWithEndpoint(endpoint *swarmtypes.EndpointSpec) ServiceSpecOpt { 178 return func(spec *swarmtypes.ServiceSpec) { 179 spec.EndpointSpec = endpoint 180 } 181 } 182 183 // ServiceWithSysctls sets the Sysctls option of the service's ContainerSpec. 184 func ServiceWithSysctls(sysctls map[string]string) ServiceSpecOpt { 185 return func(spec *swarmtypes.ServiceSpec) { 186 ensureContainerSpec(spec) 187 spec.TaskTemplate.ContainerSpec.Sysctls = sysctls 188 } 189 } 190 191 // ServiceWithCapabilities sets the Capabilities option of the service's ContainerSpec. 192 func ServiceWithCapabilities(Capabilities []string) ServiceSpecOpt { 193 return func(spec *swarmtypes.ServiceSpec) { 194 ensureContainerSpec(spec) 195 spec.TaskTemplate.ContainerSpec.Capabilities = Capabilities 196 } 197 } 198 199 // ServiceWithPidsLimit sets the PidsLimit option of the service's ContainerSpec. 200 func ServiceWithPidsLimit(limit int64) ServiceSpecOpt { 201 return func(spec *swarmtypes.ServiceSpec) { 202 ensureContainerSpec(spec) 203 spec.TaskTemplate.ContainerSpec.PidsLimit = limit 204 } 205 } 206 207 // GetRunningTasks gets the list of running tasks for a service 208 func GetRunningTasks(t *testing.T, c client.ServiceAPIClient, serviceID string) []swarmtypes.Task { 209 t.Helper() 210 211 tasks, err := c.TaskList(context.Background(), types.TaskListOptions{ 212 Filters: filters.NewArgs( 213 filters.Arg("service", serviceID), 214 filters.Arg("desired-state", "running"), 215 ), 216 }) 217 218 assert.NilError(t, err) 219 return tasks 220 } 221 222 // ExecTask runs the passed in exec config on the given task 223 func ExecTask(t *testing.T, d *daemon.Daemon, task swarmtypes.Task, config types.ExecConfig) types.HijackedResponse { 224 t.Helper() 225 client := d.NewClientT(t) 226 defer client.Close() 227 228 ctx := context.Background() 229 resp, err := client.ContainerExecCreate(ctx, task.Status.ContainerStatus.ContainerID, config) 230 assert.NilError(t, err, "error creating exec") 231 232 startCheck := types.ExecStartCheck{} 233 attach, err := client.ContainerExecAttach(ctx, resp.ID, startCheck) 234 assert.NilError(t, err, "error attaching to exec") 235 return attach 236 } 237 238 func ensureContainerSpec(spec *swarmtypes.ServiceSpec) { 239 if spec.TaskTemplate.ContainerSpec == nil { 240 spec.TaskTemplate.ContainerSpec = &swarmtypes.ContainerSpec{} 241 } 242 } 243 244 func ensurePlacement(spec *swarmtypes.ServiceSpec) { 245 if spec.TaskTemplate.Placement == nil { 246 spec.TaskTemplate.Placement = &swarmtypes.Placement{} 247 } 248 }