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  }