github.com/jwhonce/docker@v0.6.7-0.20190327063223-da823cf3a5a3/integration/service/create_test.go (about) 1 package service // import "github.com/docker/docker/integration/service" 2 3 import ( 4 "context" 5 "fmt" 6 "io/ioutil" 7 "testing" 8 "time" 9 10 "github.com/docker/docker/api/types" 11 "github.com/docker/docker/api/types/filters" 12 swarmtypes "github.com/docker/docker/api/types/swarm" 13 "github.com/docker/docker/api/types/versions" 14 "github.com/docker/docker/client" 15 "github.com/docker/docker/errdefs" 16 "github.com/docker/docker/integration/internal/network" 17 "github.com/docker/docker/integration/internal/swarm" 18 "github.com/docker/docker/internal/test/daemon" 19 "gotest.tools/assert" 20 is "gotest.tools/assert/cmp" 21 "gotest.tools/poll" 22 "gotest.tools/skip" 23 ) 24 25 func TestServiceCreateInit(t *testing.T) { 26 defer setupTest(t)() 27 t.Run("daemonInitDisabled", testServiceCreateInit(false)) 28 t.Run("daemonInitEnabled", testServiceCreateInit(true)) 29 } 30 31 func testServiceCreateInit(daemonEnabled bool) func(t *testing.T) { 32 return func(t *testing.T) { 33 var ops = []func(*daemon.Daemon){} 34 35 if daemonEnabled { 36 ops = append(ops, daemon.WithInit) 37 } 38 d := swarm.NewSwarm(t, testEnv, ops...) 39 defer d.Stop(t) 40 client := d.NewClientT(t) 41 defer client.Close() 42 43 booleanTrue := true 44 booleanFalse := false 45 46 serviceID := swarm.CreateService(t, d) 47 poll.WaitOn(t, swarm.RunningTasksCount(client, serviceID, 1), swarm.ServicePoll) 48 i := inspectServiceContainer(t, client, serviceID) 49 // HostConfig.Init == nil means that it delegates to daemon configuration 50 assert.Check(t, i.HostConfig.Init == nil) 51 52 serviceID = swarm.CreateService(t, d, swarm.ServiceWithInit(&booleanTrue)) 53 poll.WaitOn(t, swarm.RunningTasksCount(client, serviceID, 1), swarm.ServicePoll) 54 i = inspectServiceContainer(t, client, serviceID) 55 assert.Check(t, is.Equal(true, *i.HostConfig.Init)) 56 57 serviceID = swarm.CreateService(t, d, swarm.ServiceWithInit(&booleanFalse)) 58 poll.WaitOn(t, swarm.RunningTasksCount(client, serviceID, 1), swarm.ServicePoll) 59 i = inspectServiceContainer(t, client, serviceID) 60 assert.Check(t, is.Equal(false, *i.HostConfig.Init)) 61 } 62 } 63 64 func inspectServiceContainer(t *testing.T, client client.APIClient, serviceID string) types.ContainerJSON { 65 t.Helper() 66 filter := filters.NewArgs() 67 filter.Add("label", fmt.Sprintf("com.docker.swarm.service.id=%s", serviceID)) 68 containers, err := client.ContainerList(context.Background(), types.ContainerListOptions{Filters: filter}) 69 assert.NilError(t, err) 70 assert.Check(t, is.Len(containers, 1)) 71 72 i, err := client.ContainerInspect(context.Background(), containers[0].ID) 73 assert.NilError(t, err) 74 return i 75 } 76 77 func TestCreateServiceMultipleTimes(t *testing.T) { 78 skip.If(t, testEnv.DaemonInfo.OSType == "windows") 79 defer setupTest(t)() 80 d := swarm.NewSwarm(t, testEnv) 81 defer d.Stop(t) 82 client := d.NewClientT(t) 83 defer client.Close() 84 ctx := context.Background() 85 86 overlayName := "overlay1_" + t.Name() 87 overlayID := network.CreateNoError(t, ctx, client, overlayName, 88 network.WithCheckDuplicate(), 89 network.WithDriver("overlay"), 90 ) 91 92 var instances uint64 = 4 93 94 serviceName := "TestService_" + t.Name() 95 serviceSpec := []swarm.ServiceSpecOpt{ 96 swarm.ServiceWithReplicas(instances), 97 swarm.ServiceWithName(serviceName), 98 swarm.ServiceWithNetwork(overlayName), 99 } 100 101 serviceID := swarm.CreateService(t, d, serviceSpec...) 102 poll.WaitOn(t, swarm.RunningTasksCount(client, serviceID, instances), swarm.ServicePoll) 103 104 _, _, err := client.ServiceInspectWithRaw(context.Background(), serviceID, types.ServiceInspectOptions{}) 105 assert.NilError(t, err) 106 107 err = client.ServiceRemove(context.Background(), serviceID) 108 assert.NilError(t, err) 109 110 poll.WaitOn(t, swarm.NoTasksForService(ctx, client, serviceID), swarm.ServicePoll) 111 112 serviceID2 := swarm.CreateService(t, d, serviceSpec...) 113 poll.WaitOn(t, swarm.RunningTasksCount(client, serviceID2, instances), swarm.ServicePoll) 114 115 err = client.ServiceRemove(context.Background(), serviceID2) 116 assert.NilError(t, err) 117 118 poll.WaitOn(t, swarm.NoTasksForService(ctx, client, serviceID2), swarm.ServicePoll) 119 120 err = client.NetworkRemove(context.Background(), overlayID) 121 assert.NilError(t, err) 122 123 poll.WaitOn(t, network.IsRemoved(context.Background(), client, overlayID), poll.WithTimeout(1*time.Minute), poll.WithDelay(10*time.Second)) 124 } 125 126 func TestCreateServiceConflict(t *testing.T) { 127 skip.If(t, testEnv.DaemonInfo.OSType == "windows") 128 defer setupTest(t)() 129 d := swarm.NewSwarm(t, testEnv) 130 defer d.Stop(t) 131 c := d.NewClientT(t) 132 defer c.Close() 133 ctx := context.Background() 134 135 serviceName := "TestService_" + t.Name() 136 serviceSpec := []swarm.ServiceSpecOpt{ 137 swarm.ServiceWithName(serviceName), 138 } 139 140 swarm.CreateService(t, d, serviceSpec...) 141 142 spec := swarm.CreateServiceSpec(t, serviceSpec...) 143 _, err := c.ServiceCreate(ctx, spec, types.ServiceCreateOptions{}) 144 assert.Check(t, errdefs.IsConflict(err)) 145 assert.ErrorContains(t, err, "service "+serviceName+" already exists") 146 } 147 148 func TestCreateServiceMaxReplicas(t *testing.T) { 149 defer setupTest(t)() 150 d := swarm.NewSwarm(t, testEnv) 151 defer d.Stop(t) 152 client := d.NewClientT(t) 153 defer client.Close() 154 155 var maxReplicas uint64 = 2 156 serviceSpec := []swarm.ServiceSpecOpt{ 157 swarm.ServiceWithReplicas(maxReplicas), 158 swarm.ServiceWithMaxReplicas(maxReplicas), 159 } 160 161 serviceID := swarm.CreateService(t, d, serviceSpec...) 162 poll.WaitOn(t, swarm.RunningTasksCount(client, serviceID, maxReplicas), swarm.ServicePoll) 163 164 _, _, err := client.ServiceInspectWithRaw(context.Background(), serviceID, types.ServiceInspectOptions{}) 165 assert.NilError(t, err) 166 } 167 168 func TestCreateWithDuplicateNetworkNames(t *testing.T) { 169 skip.If(t, testEnv.DaemonInfo.OSType == "windows") 170 defer setupTest(t)() 171 d := swarm.NewSwarm(t, testEnv) 172 defer d.Stop(t) 173 client := d.NewClientT(t) 174 defer client.Close() 175 ctx := context.Background() 176 177 name := "foo_" + t.Name() 178 n1 := network.CreateNoError(t, ctx, client, name, network.WithDriver("bridge")) 179 n2 := network.CreateNoError(t, ctx, client, name, network.WithDriver("bridge")) 180 181 // Duplicates with name but with different driver 182 n3 := network.CreateNoError(t, ctx, client, name, network.WithDriver("overlay")) 183 184 // Create Service with the same name 185 var instances uint64 = 1 186 187 serviceName := "top_" + t.Name() 188 serviceID := swarm.CreateService(t, d, 189 swarm.ServiceWithReplicas(instances), 190 swarm.ServiceWithName(serviceName), 191 swarm.ServiceWithNetwork(name), 192 ) 193 194 poll.WaitOn(t, swarm.RunningTasksCount(client, serviceID, instances), swarm.ServicePoll) 195 196 resp, _, err := client.ServiceInspectWithRaw(ctx, serviceID, types.ServiceInspectOptions{}) 197 assert.NilError(t, err) 198 assert.Check(t, is.Equal(n3, resp.Spec.TaskTemplate.Networks[0].Target)) 199 200 // Remove Service, and wait for its tasks to be removed 201 err = client.ServiceRemove(ctx, serviceID) 202 assert.NilError(t, err) 203 poll.WaitOn(t, swarm.NoTasksForService(ctx, client, serviceID), swarm.ServicePoll) 204 205 // Remove networks 206 err = client.NetworkRemove(context.Background(), n3) 207 assert.NilError(t, err) 208 209 err = client.NetworkRemove(context.Background(), n2) 210 assert.NilError(t, err) 211 212 err = client.NetworkRemove(context.Background(), n1) 213 assert.NilError(t, err) 214 215 // Make sure networks have been destroyed. 216 poll.WaitOn(t, network.IsRemoved(context.Background(), client, n3), poll.WithTimeout(1*time.Minute), poll.WithDelay(10*time.Second)) 217 poll.WaitOn(t, network.IsRemoved(context.Background(), client, n2), poll.WithTimeout(1*time.Minute), poll.WithDelay(10*time.Second)) 218 poll.WaitOn(t, network.IsRemoved(context.Background(), client, n1), poll.WithTimeout(1*time.Minute), poll.WithDelay(10*time.Second)) 219 } 220 221 func TestCreateServiceSecretFileMode(t *testing.T) { 222 skip.If(t, testEnv.DaemonInfo.OSType == "windows") 223 defer setupTest(t)() 224 d := swarm.NewSwarm(t, testEnv) 225 defer d.Stop(t) 226 client := d.NewClientT(t) 227 defer client.Close() 228 229 ctx := context.Background() 230 secretName := "TestSecret_" + t.Name() 231 secretResp, err := client.SecretCreate(ctx, swarmtypes.SecretSpec{ 232 Annotations: swarmtypes.Annotations{ 233 Name: secretName, 234 }, 235 Data: []byte("TESTSECRET"), 236 }) 237 assert.NilError(t, err) 238 239 var instances uint64 = 1 240 serviceName := "TestService_" + t.Name() 241 serviceID := swarm.CreateService(t, d, 242 swarm.ServiceWithReplicas(instances), 243 swarm.ServiceWithName(serviceName), 244 swarm.ServiceWithCommand([]string{"/bin/sh", "-c", "ls -l /etc/secret || /bin/top"}), 245 swarm.ServiceWithSecret(&swarmtypes.SecretReference{ 246 File: &swarmtypes.SecretReferenceFileTarget{ 247 Name: "/etc/secret", 248 UID: "0", 249 GID: "0", 250 Mode: 0777, 251 }, 252 SecretID: secretResp.ID, 253 SecretName: secretName, 254 }), 255 ) 256 257 poll.WaitOn(t, swarm.RunningTasksCount(client, serviceID, instances), swarm.ServicePoll) 258 259 filter := filters.NewArgs() 260 filter.Add("service", serviceID) 261 tasks, err := client.TaskList(ctx, types.TaskListOptions{ 262 Filters: filter, 263 }) 264 assert.NilError(t, err) 265 assert.Check(t, is.Equal(len(tasks), 1)) 266 267 body, err := client.ContainerLogs(ctx, tasks[0].Status.ContainerStatus.ContainerID, types.ContainerLogsOptions{ 268 ShowStdout: true, 269 }) 270 assert.NilError(t, err) 271 defer body.Close() 272 273 content, err := ioutil.ReadAll(body) 274 assert.NilError(t, err) 275 assert.Check(t, is.Contains(string(content), "-rwxrwxrwx")) 276 277 err = client.ServiceRemove(ctx, serviceID) 278 assert.NilError(t, err) 279 poll.WaitOn(t, swarm.NoTasksForService(ctx, client, serviceID), swarm.ServicePoll) 280 281 err = client.SecretRemove(ctx, secretName) 282 assert.NilError(t, err) 283 } 284 285 func TestCreateServiceConfigFileMode(t *testing.T) { 286 skip.If(t, testEnv.DaemonInfo.OSType == "windows") 287 defer setupTest(t)() 288 d := swarm.NewSwarm(t, testEnv) 289 defer d.Stop(t) 290 client := d.NewClientT(t) 291 defer client.Close() 292 293 ctx := context.Background() 294 configName := "TestConfig_" + t.Name() 295 configResp, err := client.ConfigCreate(ctx, swarmtypes.ConfigSpec{ 296 Annotations: swarmtypes.Annotations{ 297 Name: configName, 298 }, 299 Data: []byte("TESTCONFIG"), 300 }) 301 assert.NilError(t, err) 302 303 var instances uint64 = 1 304 serviceName := "TestService_" + t.Name() 305 serviceID := swarm.CreateService(t, d, 306 swarm.ServiceWithName(serviceName), 307 swarm.ServiceWithCommand([]string{"/bin/sh", "-c", "ls -l /etc/config || /bin/top"}), 308 swarm.ServiceWithReplicas(instances), 309 swarm.ServiceWithConfig(&swarmtypes.ConfigReference{ 310 File: &swarmtypes.ConfigReferenceFileTarget{ 311 Name: "/etc/config", 312 UID: "0", 313 GID: "0", 314 Mode: 0777, 315 }, 316 ConfigID: configResp.ID, 317 ConfigName: configName, 318 }), 319 ) 320 321 poll.WaitOn(t, swarm.RunningTasksCount(client, serviceID, instances)) 322 323 filter := filters.NewArgs() 324 filter.Add("service", serviceID) 325 tasks, err := client.TaskList(ctx, types.TaskListOptions{ 326 Filters: filter, 327 }) 328 assert.NilError(t, err) 329 assert.Check(t, is.Equal(len(tasks), 1)) 330 331 body, err := client.ContainerLogs(ctx, tasks[0].Status.ContainerStatus.ContainerID, types.ContainerLogsOptions{ 332 ShowStdout: true, 333 }) 334 assert.NilError(t, err) 335 defer body.Close() 336 337 content, err := ioutil.ReadAll(body) 338 assert.NilError(t, err) 339 assert.Check(t, is.Contains(string(content), "-rwxrwxrwx")) 340 341 err = client.ServiceRemove(ctx, serviceID) 342 assert.NilError(t, err) 343 poll.WaitOn(t, swarm.NoTasksForService(ctx, client, serviceID)) 344 345 err = client.ConfigRemove(ctx, configName) 346 assert.NilError(t, err) 347 } 348 349 // TestServiceCreateSysctls tests that a service created with sysctl options in 350 // the ContainerSpec correctly applies those options. 351 // 352 // To test this, we're going to create a service with the sysctl option 353 // 354 // {"net.ipv4.ip_nonlocal_bind": "0"} 355 // 356 // We'll get the service's tasks to get the container ID, and then we'll 357 // inspect the container. If the output of the container inspect contains the 358 // sysctl option with the correct value, we can assume that the sysctl has been 359 // plumbed correctly. 360 // 361 // Next, we'll remove that service and create a new service with that option 362 // set to 1. This means that no matter what the default is, we can be confident 363 // that the sysctl option is applying as intended. 364 // 365 // Additionally, we'll do service and task inspects to verify that the inspect 366 // output includes the desired sysctl option. 367 // 368 // We're using net.ipv4.ip_nonlocal_bind because it's something that I'm fairly 369 // confident won't be modified by the container runtime, and won't blow 370 // anything up in the test environment 371 func TestCreateServiceSysctls(t *testing.T) { 372 skip.If( 373 t, versions.LessThan(testEnv.DaemonAPIVersion(), "1.40"), 374 "setting service sysctls is unsupported before api v1.40", 375 ) 376 377 defer setupTest(t)() 378 d := swarm.NewSwarm(t, testEnv) 379 defer d.Stop(t) 380 client := d.NewClientT(t) 381 defer client.Close() 382 383 ctx := context.Background() 384 385 // run thie block twice, so that no matter what the default value of 386 // net.ipv4.ip_nonlocal_bind is, we can verify that setting the sysctl 387 // options works 388 for _, expected := range []string{"0", "1"} { 389 390 // store the map we're going to be using everywhere. 391 expectedSysctls := map[string]string{"net.ipv4.ip_nonlocal_bind": expected} 392 393 // Create the service with the sysctl options 394 var instances uint64 = 1 395 serviceID := swarm.CreateService(t, d, 396 swarm.ServiceWithSysctls(expectedSysctls), 397 ) 398 399 // wait for the service to converge to 1 running task as expected 400 poll.WaitOn(t, swarm.RunningTasksCount(client, serviceID, instances)) 401 402 // we're going to check 3 things: 403 // 404 // 1. Does the container, when inspected, have the sysctl option set? 405 // 2. Does the task have the sysctl in the spec? 406 // 3. Does the service have the sysctl in the spec? 407 // 408 // if all 3 of these things are true, we know that the sysctl has been 409 // plumbed correctly through the engine. 410 // 411 // We don't actually have to get inside the container and check its 412 // logs or anything. If we see the sysctl set on the container inspect, 413 // we know that the sysctl is plumbed correctly. everything below that 414 // level has been tested elsewhere. (thanks @thaJeztah, because an 415 // earlier version of this test had to get container logs and was much 416 // more complex) 417 418 // get all of the tasks of the service, so we can get the container 419 filter := filters.NewArgs() 420 filter.Add("service", serviceID) 421 tasks, err := client.TaskList(ctx, types.TaskListOptions{ 422 Filters: filter, 423 }) 424 assert.NilError(t, err) 425 assert.Check(t, is.Equal(len(tasks), 1)) 426 427 // verify that the container has the sysctl option set 428 ctnr, err := client.ContainerInspect(ctx, tasks[0].Status.ContainerStatus.ContainerID) 429 assert.NilError(t, err) 430 assert.DeepEqual(t, ctnr.HostConfig.Sysctls, expectedSysctls) 431 432 // verify that the task has the sysctl option set in the task object 433 assert.DeepEqual(t, tasks[0].Spec.ContainerSpec.Sysctls, expectedSysctls) 434 435 // verify that the service also has the sysctl set in the spec. 436 service, _, err := client.ServiceInspectWithRaw(ctx, serviceID, types.ServiceInspectOptions{}) 437 assert.NilError(t, err) 438 assert.DeepEqual(t, 439 service.Spec.TaskTemplate.ContainerSpec.Sysctls, expectedSysctls, 440 ) 441 } 442 }