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