gopkg.in/docker/docker.v20@v20.10.27/integration-cli/docker_cli_service_create_test.go (about) 1 //go:build !windows 2 // +build !windows 3 4 package main 5 6 import ( 7 "encoding/json" 8 "fmt" 9 "path/filepath" 10 "strings" 11 "testing" 12 13 "github.com/docker/docker/api/types" 14 "github.com/docker/docker/api/types/mount" 15 "github.com/docker/docker/api/types/swarm" 16 "github.com/docker/docker/integration-cli/checker" 17 "gotest.tools/v3/assert" 18 "gotest.tools/v3/poll" 19 ) 20 21 func (s *DockerSwarmSuite) TestServiceCreateMountVolume(c *testing.T) { 22 d := s.AddDaemon(c, true, true) 23 out, err := d.Cmd("service", "create", "--no-resolve-image", "--detach=true", "--mount", "type=volume,source=foo,target=/foo,volume-nocopy", "busybox", "top") 24 assert.NilError(c, err, out) 25 id := strings.TrimSpace(out) 26 27 var tasks []swarm.Task 28 poll.WaitOn(c, pollCheck(c, func(c *testing.T) (interface{}, string) { 29 tasks = d.GetServiceTasks(c, id) 30 return len(tasks) > 0, "" 31 }, checker.Equals(true)), poll.WithTimeout(defaultReconciliationTimeout)) 32 33 task := tasks[0] 34 poll.WaitOn(c, pollCheck(c, func(c *testing.T) (interface{}, string) { 35 if task.NodeID == "" || task.Status.ContainerStatus == nil { 36 task = d.GetTask(c, task.ID) 37 } 38 return task.NodeID != "" && task.Status.ContainerStatus != nil, "" 39 }, checker.Equals(true)), poll.WithTimeout(defaultReconciliationTimeout)) 40 41 // check container mount config 42 out, err = s.nodeCmd(c, task.NodeID, "inspect", "--format", "{{json .HostConfig.Mounts}}", task.Status.ContainerStatus.ContainerID) 43 assert.NilError(c, err, out) 44 45 var mountConfig []mount.Mount 46 assert.Assert(c, json.Unmarshal([]byte(out), &mountConfig) == nil) 47 assert.Equal(c, len(mountConfig), 1) 48 49 assert.Equal(c, mountConfig[0].Source, "foo") 50 assert.Equal(c, mountConfig[0].Target, "/foo") 51 assert.Equal(c, mountConfig[0].Type, mount.TypeVolume) 52 assert.Assert(c, mountConfig[0].VolumeOptions != nil) 53 assert.Assert(c, mountConfig[0].VolumeOptions.NoCopy) 54 55 // check container mounts actual 56 out, err = s.nodeCmd(c, task.NodeID, "inspect", "--format", "{{json .Mounts}}", task.Status.ContainerStatus.ContainerID) 57 assert.NilError(c, err, out) 58 59 var mounts []types.MountPoint 60 assert.Assert(c, json.Unmarshal([]byte(out), &mounts) == nil) 61 assert.Equal(c, len(mounts), 1) 62 63 assert.Equal(c, mounts[0].Type, mount.TypeVolume) 64 assert.Equal(c, mounts[0].Name, "foo") 65 assert.Equal(c, mounts[0].Destination, "/foo") 66 assert.Equal(c, mounts[0].RW, true) 67 } 68 69 func (s *DockerSwarmSuite) TestServiceCreateWithSecretSimple(c *testing.T) { 70 d := s.AddDaemon(c, true, true) 71 72 serviceName := "test-service-secret" 73 testName := "test_secret" 74 id := d.CreateSecret(c, swarm.SecretSpec{ 75 Annotations: swarm.Annotations{ 76 Name: testName, 77 }, 78 Data: []byte("TESTINGDATA"), 79 }) 80 assert.Assert(c, id != "", "secrets: %s", id) 81 82 out, err := d.Cmd("service", "create", "--detach", "--no-resolve-image", "--name", serviceName, "--secret", testName, "busybox", "top") 83 assert.NilError(c, err, out) 84 85 out, err = d.Cmd("service", "inspect", "--format", "{{ json .Spec.TaskTemplate.ContainerSpec.Secrets }}", serviceName) 86 assert.NilError(c, err) 87 88 var refs []swarm.SecretReference 89 assert.Assert(c, json.Unmarshal([]byte(out), &refs) == nil) 90 assert.Equal(c, len(refs), 1) 91 92 assert.Equal(c, refs[0].SecretName, testName) 93 assert.Assert(c, refs[0].File != nil) 94 assert.Equal(c, refs[0].File.Name, testName) 95 assert.Equal(c, refs[0].File.UID, "0") 96 assert.Equal(c, refs[0].File.GID, "0") 97 98 out, err = d.Cmd("service", "rm", serviceName) 99 assert.NilError(c, err, out) 100 d.DeleteSecret(c, testName) 101 } 102 103 func (s *DockerSwarmSuite) TestServiceCreateWithSecretSourceTargetPaths(c *testing.T) { 104 d := s.AddDaemon(c, true, true) 105 106 testPaths := map[string]string{ 107 "app": "/etc/secret", 108 "test_secret": "test_secret", 109 "relative_secret": "relative/secret", 110 "escapes_in_container": "../secret", 111 } 112 113 var secretFlags []string 114 115 for testName, testTarget := range testPaths { 116 id := d.CreateSecret(c, swarm.SecretSpec{ 117 Annotations: swarm.Annotations{ 118 Name: testName, 119 }, 120 Data: []byte("TESTINGDATA " + testName + " " + testTarget), 121 }) 122 assert.Assert(c, id != "", "secrets: %s", id) 123 124 secretFlags = append(secretFlags, "--secret", fmt.Sprintf("source=%s,target=%s", testName, testTarget)) 125 } 126 127 serviceName := "svc" 128 serviceCmd := []string{"service", "create", "--detach", "--no-resolve-image", "--name", serviceName} 129 serviceCmd = append(serviceCmd, secretFlags...) 130 serviceCmd = append(serviceCmd, "busybox", "top") 131 out, err := d.Cmd(serviceCmd...) 132 assert.NilError(c, err, out) 133 134 out, err = d.Cmd("service", "inspect", "--format", "{{ json .Spec.TaskTemplate.ContainerSpec.Secrets }}", serviceName) 135 assert.NilError(c, err) 136 137 var refs []swarm.SecretReference 138 assert.Assert(c, json.Unmarshal([]byte(out), &refs) == nil) 139 assert.Equal(c, len(refs), len(testPaths)) 140 141 var tasks []swarm.Task 142 poll.WaitOn(c, pollCheck(c, func(c *testing.T) (interface{}, string) { 143 tasks = d.GetServiceTasks(c, serviceName) 144 return len(tasks) > 0, "" 145 }, checker.Equals(true)), poll.WithTimeout(defaultReconciliationTimeout)) 146 147 task := tasks[0] 148 poll.WaitOn(c, pollCheck(c, func(c *testing.T) (interface{}, string) { 149 if task.NodeID == "" || task.Status.ContainerStatus == nil { 150 task = d.GetTask(c, task.ID) 151 } 152 return task.NodeID != "" && task.Status.ContainerStatus != nil, "" 153 }, checker.Equals(true)), poll.WithTimeout(defaultReconciliationTimeout)) 154 155 for testName, testTarget := range testPaths { 156 path := testTarget 157 if !filepath.IsAbs(path) { 158 path = filepath.Join("/run/secrets", path) 159 } 160 out, err := d.Cmd("exec", task.Status.ContainerStatus.ContainerID, "cat", path) 161 assert.NilError(c, err) 162 assert.Equal(c, out, "TESTINGDATA "+testName+" "+testTarget) 163 } 164 165 out, err = d.Cmd("service", "rm", serviceName) 166 assert.NilError(c, err, out) 167 } 168 169 func (s *DockerSwarmSuite) TestServiceCreateWithSecretReferencedTwice(c *testing.T) { 170 d := s.AddDaemon(c, true, true) 171 172 id := d.CreateSecret(c, swarm.SecretSpec{ 173 Annotations: swarm.Annotations{ 174 Name: "mysecret", 175 }, 176 Data: []byte("TESTINGDATA"), 177 }) 178 assert.Assert(c, id != "", "secrets: %s", id) 179 180 serviceName := "svc" 181 out, err := d.Cmd("service", "create", "--detach", "--no-resolve-image", "--name", serviceName, "--secret", "source=mysecret,target=target1", "--secret", "source=mysecret,target=target2", "busybox", "top") 182 assert.NilError(c, err, out) 183 184 out, err = d.Cmd("service", "inspect", "--format", "{{ json .Spec.TaskTemplate.ContainerSpec.Secrets }}", serviceName) 185 assert.NilError(c, err) 186 187 var refs []swarm.SecretReference 188 assert.Assert(c, json.Unmarshal([]byte(out), &refs) == nil) 189 assert.Equal(c, len(refs), 2) 190 191 var tasks []swarm.Task 192 poll.WaitOn(c, pollCheck(c, func(c *testing.T) (interface{}, string) { 193 tasks = d.GetServiceTasks(c, serviceName) 194 return len(tasks) > 0, "" 195 }, checker.Equals(true)), poll.WithTimeout(defaultReconciliationTimeout)) 196 197 task := tasks[0] 198 poll.WaitOn(c, pollCheck(c, func(c *testing.T) (interface{}, string) { 199 if task.NodeID == "" || task.Status.ContainerStatus == nil { 200 task = d.GetTask(c, task.ID) 201 } 202 return task.NodeID != "" && task.Status.ContainerStatus != nil, "" 203 }, checker.Equals(true)), poll.WithTimeout(defaultReconciliationTimeout)) 204 205 for _, target := range []string{"target1", "target2"} { 206 assert.NilError(c, err, out) 207 path := filepath.Join("/run/secrets", target) 208 out, err := d.Cmd("exec", task.Status.ContainerStatus.ContainerID, "cat", path) 209 assert.NilError(c, err) 210 assert.Equal(c, out, "TESTINGDATA") 211 } 212 213 out, err = d.Cmd("service", "rm", serviceName) 214 assert.NilError(c, err, out) 215 } 216 217 func (s *DockerSwarmSuite) TestServiceCreateWithConfigSimple(c *testing.T) { 218 d := s.AddDaemon(c, true, true) 219 220 serviceName := "test-service-config" 221 testName := "test_config" 222 id := d.CreateConfig(c, swarm.ConfigSpec{ 223 Annotations: swarm.Annotations{ 224 Name: testName, 225 }, 226 Data: []byte("TESTINGDATA"), 227 }) 228 assert.Assert(c, id != "", "configs: %s", id) 229 230 out, err := d.Cmd("service", "create", "--detach", "--no-resolve-image", "--name", serviceName, "--config", testName, "busybox", "top") 231 assert.NilError(c, err, out) 232 233 out, err = d.Cmd("service", "inspect", "--format", "{{ json .Spec.TaskTemplate.ContainerSpec.Configs }}", serviceName) 234 assert.NilError(c, err) 235 236 var refs []swarm.ConfigReference 237 assert.Assert(c, json.Unmarshal([]byte(out), &refs) == nil) 238 assert.Equal(c, len(refs), 1) 239 240 assert.Equal(c, refs[0].ConfigName, testName) 241 assert.Assert(c, refs[0].File != nil) 242 assert.Equal(c, refs[0].File.Name, testName) 243 assert.Equal(c, refs[0].File.UID, "0") 244 assert.Equal(c, refs[0].File.GID, "0") 245 246 out, err = d.Cmd("service", "rm", serviceName) 247 assert.NilError(c, err, out) 248 d.DeleteConfig(c, testName) 249 } 250 251 func (s *DockerSwarmSuite) TestServiceCreateWithConfigSourceTargetPaths(c *testing.T) { 252 d := s.AddDaemon(c, true, true) 253 254 testPaths := map[string]string{ 255 "app": "/etc/config", 256 "test_config": "test_config", 257 "relative_config": "relative/config", 258 } 259 260 var configFlags []string 261 262 for testName, testTarget := range testPaths { 263 id := d.CreateConfig(c, swarm.ConfigSpec{ 264 Annotations: swarm.Annotations{ 265 Name: testName, 266 }, 267 Data: []byte("TESTINGDATA " + testName + " " + testTarget), 268 }) 269 assert.Assert(c, id != "", "configs: %s", id) 270 271 configFlags = append(configFlags, "--config", fmt.Sprintf("source=%s,target=%s", testName, testTarget)) 272 } 273 274 serviceName := "svc" 275 serviceCmd := []string{"service", "create", "--detach", "--no-resolve-image", "--name", serviceName} 276 serviceCmd = append(serviceCmd, configFlags...) 277 serviceCmd = append(serviceCmd, "busybox", "top") 278 out, err := d.Cmd(serviceCmd...) 279 assert.NilError(c, err, out) 280 281 out, err = d.Cmd("service", "inspect", "--format", "{{ json .Spec.TaskTemplate.ContainerSpec.Configs }}", serviceName) 282 assert.NilError(c, err) 283 284 var refs []swarm.ConfigReference 285 assert.Assert(c, json.Unmarshal([]byte(out), &refs) == nil) 286 assert.Equal(c, len(refs), len(testPaths)) 287 288 var tasks []swarm.Task 289 poll.WaitOn(c, pollCheck(c, func(c *testing.T) (interface{}, string) { 290 tasks = d.GetServiceTasks(c, serviceName) 291 return len(tasks) > 0, "" 292 }, checker.Equals(true)), poll.WithTimeout(defaultReconciliationTimeout)) 293 294 task := tasks[0] 295 poll.WaitOn(c, pollCheck(c, func(c *testing.T) (interface{}, string) { 296 if task.NodeID == "" || task.Status.ContainerStatus == nil { 297 task = d.GetTask(c, task.ID) 298 } 299 return task.NodeID != "" && task.Status.ContainerStatus != nil, "" 300 }, checker.Equals(true)), poll.WithTimeout(defaultReconciliationTimeout)) 301 302 for testName, testTarget := range testPaths { 303 path := testTarget 304 if !filepath.IsAbs(path) { 305 path = filepath.Join("/", path) 306 } 307 out, err := d.Cmd("exec", task.Status.ContainerStatus.ContainerID, "cat", path) 308 assert.NilError(c, err) 309 assert.Equal(c, out, "TESTINGDATA "+testName+" "+testTarget) 310 } 311 312 out, err = d.Cmd("service", "rm", serviceName) 313 assert.NilError(c, err, out) 314 } 315 316 func (s *DockerSwarmSuite) TestServiceCreateWithConfigReferencedTwice(c *testing.T) { 317 d := s.AddDaemon(c, true, true) 318 319 id := d.CreateConfig(c, swarm.ConfigSpec{ 320 Annotations: swarm.Annotations{ 321 Name: "myconfig", 322 }, 323 Data: []byte("TESTINGDATA"), 324 }) 325 assert.Assert(c, id != "", "configs: %s", id) 326 327 serviceName := "svc" 328 out, err := d.Cmd("service", "create", "--detach", "--no-resolve-image", "--name", serviceName, "--config", "source=myconfig,target=target1", "--config", "source=myconfig,target=target2", "busybox", "top") 329 assert.NilError(c, err, out) 330 331 out, err = d.Cmd("service", "inspect", "--format", "{{ json .Spec.TaskTemplate.ContainerSpec.Configs }}", serviceName) 332 assert.NilError(c, err) 333 334 var refs []swarm.ConfigReference 335 assert.Assert(c, json.Unmarshal([]byte(out), &refs) == nil) 336 assert.Equal(c, len(refs), 2) 337 338 var tasks []swarm.Task 339 poll.WaitOn(c, pollCheck(c, func(c *testing.T) (interface{}, string) { 340 tasks = d.GetServiceTasks(c, serviceName) 341 return len(tasks) > 0, "" 342 }, checker.Equals(true)), poll.WithTimeout(defaultReconciliationTimeout)) 343 344 task := tasks[0] 345 poll.WaitOn(c, pollCheck(c, func(c *testing.T) (interface{}, string) { 346 if task.NodeID == "" || task.Status.ContainerStatus == nil { 347 task = d.GetTask(c, task.ID) 348 } 349 return task.NodeID != "" && task.Status.ContainerStatus != nil, "" 350 }, checker.Equals(true)), poll.WithTimeout(defaultReconciliationTimeout)) 351 352 for _, target := range []string{"target1", "target2"} { 353 assert.NilError(c, err, out) 354 path := filepath.Join("/", target) 355 out, err := d.Cmd("exec", task.Status.ContainerStatus.ContainerID, "cat", path) 356 assert.NilError(c, err) 357 assert.Equal(c, out, "TESTINGDATA") 358 } 359 360 out, err = d.Cmd("service", "rm", serviceName) 361 assert.NilError(c, err, out) 362 } 363 364 func (s *DockerSwarmSuite) TestServiceCreateMountTmpfs(c *testing.T) { 365 d := s.AddDaemon(c, true, true) 366 out, err := d.Cmd("service", "create", "--no-resolve-image", "--detach=true", "--mount", "type=tmpfs,target=/foo,tmpfs-size=1MB", "busybox", "sh", "-c", "mount | grep foo; exec tail -f /dev/null") 367 assert.NilError(c, err, out) 368 id := strings.TrimSpace(out) 369 370 var tasks []swarm.Task 371 poll.WaitOn(c, pollCheck(c, func(c *testing.T) (interface{}, string) { 372 tasks = d.GetServiceTasks(c, id) 373 return len(tasks) > 0, "" 374 }, checker.Equals(true)), poll.WithTimeout(defaultReconciliationTimeout)) 375 376 task := tasks[0] 377 poll.WaitOn(c, pollCheck(c, func(c *testing.T) (interface{}, string) { 378 if task.NodeID == "" || task.Status.ContainerStatus == nil { 379 task = d.GetTask(c, task.ID) 380 } 381 return task.NodeID != "" && task.Status.ContainerStatus != nil, "" 382 }, checker.Equals(true)), poll.WithTimeout(defaultReconciliationTimeout)) 383 384 // check container mount config 385 out, err = s.nodeCmd(c, task.NodeID, "inspect", "--format", "{{json .HostConfig.Mounts}}", task.Status.ContainerStatus.ContainerID) 386 assert.NilError(c, err, out) 387 388 var mountConfig []mount.Mount 389 assert.Assert(c, json.Unmarshal([]byte(out), &mountConfig) == nil) 390 assert.Equal(c, len(mountConfig), 1) 391 392 assert.Equal(c, mountConfig[0].Source, "") 393 assert.Equal(c, mountConfig[0].Target, "/foo") 394 assert.Equal(c, mountConfig[0].Type, mount.TypeTmpfs) 395 assert.Assert(c, mountConfig[0].TmpfsOptions != nil) 396 assert.Equal(c, mountConfig[0].TmpfsOptions.SizeBytes, int64(1048576)) 397 398 // check container mounts actual 399 out, err = s.nodeCmd(c, task.NodeID, "inspect", "--format", "{{json .Mounts}}", task.Status.ContainerStatus.ContainerID) 400 assert.NilError(c, err, out) 401 402 var mounts []types.MountPoint 403 assert.Assert(c, json.Unmarshal([]byte(out), &mounts) == nil) 404 assert.Equal(c, len(mounts), 1) 405 406 assert.Equal(c, mounts[0].Type, mount.TypeTmpfs) 407 assert.Equal(c, mounts[0].Name, "") 408 assert.Equal(c, mounts[0].Destination, "/foo") 409 assert.Equal(c, mounts[0].RW, true) 410 411 out, err = s.nodeCmd(c, task.NodeID, "logs", task.Status.ContainerStatus.ContainerID) 412 assert.NilError(c, err, out) 413 assert.Assert(c, strings.HasPrefix(strings.TrimSpace(out), "tmpfs on /foo type tmpfs")) 414 assert.Assert(c, strings.Contains(strings.TrimSpace(out), "size=1024k")) 415 } 416 417 func (s *DockerSwarmSuite) TestServiceCreateWithNetworkAlias(c *testing.T) { 418 d := s.AddDaemon(c, true, true) 419 out, err := d.Cmd("network", "create", "--scope=swarm", "test_swarm_br") 420 assert.NilError(c, err, out) 421 422 out, err = d.Cmd("service", "create", "--no-resolve-image", "--detach=true", "--network=name=test_swarm_br,alias=srv_alias", "--name=alias_tst_container", "busybox", "top") 423 assert.NilError(c, err, out) 424 id := strings.TrimSpace(out) 425 426 var tasks []swarm.Task 427 poll.WaitOn(c, pollCheck(c, func(c *testing.T) (interface{}, string) { 428 tasks = d.GetServiceTasks(c, id) 429 return len(tasks) > 0, "" 430 }, checker.Equals(true)), poll.WithTimeout(defaultReconciliationTimeout)) 431 432 task := tasks[0] 433 poll.WaitOn(c, pollCheck(c, func(c *testing.T) (interface{}, string) { 434 if task.NodeID == "" || task.Status.ContainerStatus == nil { 435 task = d.GetTask(c, task.ID) 436 } 437 return task.NodeID != "" && task.Status.ContainerStatus != nil, "" 438 }, checker.Equals(true)), poll.WithTimeout(defaultReconciliationTimeout)) 439 440 // check container alias config 441 out, err = s.nodeCmd(c, task.NodeID, "inspect", "--format", "{{json .NetworkSettings.Networks.test_swarm_br.Aliases}}", task.Status.ContainerStatus.ContainerID) 442 assert.NilError(c, err, out) 443 444 // Make sure the only alias seen is the container-id 445 var aliases []string 446 assert.Assert(c, json.Unmarshal([]byte(out), &aliases) == nil) 447 assert.Equal(c, len(aliases), 1) 448 449 assert.Assert(c, strings.Contains(task.Status.ContainerStatus.ContainerID, aliases[0])) 450 }