github.com/tonistiigi/docker@v0.10.1-0.20240229224939-974013b0dc6a/integration-cli/docker_cli_service_create_test.go (about)

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