github.com/Prakhar-Agarwal-byte/moby@v0.0.0-20231027092010-a14e3e8ab87e/integration/container/create_test.go (about)

     1  package container // import "github.com/Prakhar-Agarwal-byte/moby/integration/container"
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	"fmt"
     7  	"strconv"
     8  	"testing"
     9  	"time"
    10  
    11  	"github.com/Prakhar-Agarwal-byte/moby/api/types/container"
    12  	"github.com/Prakhar-Agarwal-byte/moby/api/types/network"
    13  	"github.com/Prakhar-Agarwal-byte/moby/api/types/versions"
    14  	"github.com/Prakhar-Agarwal-byte/moby/client"
    15  	"github.com/Prakhar-Agarwal-byte/moby/errdefs"
    16  	ctr "github.com/Prakhar-Agarwal-byte/moby/integration/internal/container"
    17  	"github.com/Prakhar-Agarwal-byte/moby/oci"
    18  	"github.com/Prakhar-Agarwal-byte/moby/testutil"
    19  	ocispec "github.com/opencontainers/image-spec/specs-go/v1"
    20  	"gotest.tools/v3/assert"
    21  	is "gotest.tools/v3/assert/cmp"
    22  	"gotest.tools/v3/poll"
    23  	"gotest.tools/v3/skip"
    24  )
    25  
    26  func TestCreateFailsWhenIdentifierDoesNotExist(t *testing.T) {
    27  	ctx := setupTest(t)
    28  	client := testEnv.APIClient()
    29  
    30  	testCases := []struct {
    31  		doc           string
    32  		image         string
    33  		expectedError string
    34  	}{
    35  		{
    36  			doc:           "image and tag",
    37  			image:         "test456:v1",
    38  			expectedError: "No such image: test456:v1",
    39  		},
    40  		{
    41  			doc:           "image no tag",
    42  			image:         "test456",
    43  			expectedError: "No such image: test456",
    44  		},
    45  		{
    46  			doc:           "digest",
    47  			image:         "sha256:0cb40641836c461bc97c793971d84d758371ed682042457523e4ae701efeaaaa",
    48  			expectedError: "No such image: sha256:0cb40641836c461bc97c793971d84d758371ed682042457523e4ae701efeaaaa",
    49  		},
    50  	}
    51  
    52  	for _, tc := range testCases {
    53  		tc := tc
    54  		t.Run(tc.doc, func(t *testing.T) {
    55  			t.Parallel()
    56  			ctx := testutil.StartSpan(ctx, t)
    57  			_, err := client.ContainerCreate(ctx,
    58  				&container.Config{Image: tc.image},
    59  				&container.HostConfig{},
    60  				&network.NetworkingConfig{},
    61  				nil,
    62  				"",
    63  			)
    64  			assert.Check(t, is.ErrorContains(err, tc.expectedError))
    65  			assert.Check(t, errdefs.IsNotFound(err))
    66  		})
    67  	}
    68  }
    69  
    70  // TestCreateLinkToNonExistingContainer verifies that linking to a non-existing
    71  // container returns an "invalid parameter" (400) status, and not the underlying
    72  // "non exists" (404).
    73  func TestCreateLinkToNonExistingContainer(t *testing.T) {
    74  	skip.If(t, testEnv.DaemonInfo.OSType == "windows", "legacy links are not supported on windows")
    75  	ctx := setupTest(t)
    76  	c := testEnv.APIClient()
    77  
    78  	_, err := c.ContainerCreate(ctx,
    79  		&container.Config{
    80  			Image: "busybox",
    81  		},
    82  		&container.HostConfig{
    83  			Links: []string{"no-such-container"},
    84  		},
    85  		&network.NetworkingConfig{},
    86  		nil,
    87  		"",
    88  	)
    89  	assert.Check(t, is.ErrorContains(err, "could not get container for no-such-container"))
    90  	assert.Check(t, errdefs.IsInvalidParameter(err))
    91  }
    92  
    93  func TestCreateWithInvalidEnv(t *testing.T) {
    94  	ctx := setupTest(t)
    95  	client := testEnv.APIClient()
    96  
    97  	testCases := []struct {
    98  		env           string
    99  		expectedError string
   100  	}{
   101  		{
   102  			env:           "",
   103  			expectedError: "invalid environment variable:",
   104  		},
   105  		{
   106  			env:           "=",
   107  			expectedError: "invalid environment variable: =",
   108  		},
   109  		{
   110  			env:           "=foo",
   111  			expectedError: "invalid environment variable: =foo",
   112  		},
   113  	}
   114  
   115  	for index, tc := range testCases {
   116  		tc := tc
   117  		t.Run(strconv.Itoa(index), func(t *testing.T) {
   118  			t.Parallel()
   119  			ctx := testutil.StartSpan(ctx, t)
   120  			_, err := client.ContainerCreate(ctx,
   121  				&container.Config{
   122  					Image: "busybox",
   123  					Env:   []string{tc.env},
   124  				},
   125  				&container.HostConfig{},
   126  				&network.NetworkingConfig{},
   127  				nil,
   128  				"",
   129  			)
   130  			assert.Check(t, is.ErrorContains(err, tc.expectedError))
   131  			assert.Check(t, errdefs.IsInvalidParameter(err))
   132  		})
   133  	}
   134  }
   135  
   136  // Test case for #30166 (target was not validated)
   137  func TestCreateTmpfsMountsTarget(t *testing.T) {
   138  	skip.If(t, testEnv.DaemonInfo.OSType == "windows")
   139  	ctx := setupTest(t)
   140  
   141  	client := testEnv.APIClient()
   142  
   143  	testCases := []struct {
   144  		target        string
   145  		expectedError string
   146  	}{
   147  		{
   148  			target:        ".",
   149  			expectedError: "mount path must be absolute",
   150  		},
   151  		{
   152  			target:        "foo",
   153  			expectedError: "mount path must be absolute",
   154  		},
   155  		{
   156  			target:        "/",
   157  			expectedError: "destination can't be '/'",
   158  		},
   159  		{
   160  			target:        "//",
   161  			expectedError: "destination can't be '/'",
   162  		},
   163  	}
   164  
   165  	for _, tc := range testCases {
   166  		_, err := client.ContainerCreate(ctx,
   167  			&container.Config{
   168  				Image: "busybox",
   169  			},
   170  			&container.HostConfig{
   171  				Tmpfs: map[string]string{tc.target: ""},
   172  			},
   173  			&network.NetworkingConfig{},
   174  			nil,
   175  			"",
   176  		)
   177  		assert.Check(t, is.ErrorContains(err, tc.expectedError))
   178  		assert.Check(t, errdefs.IsInvalidParameter(err))
   179  	}
   180  }
   181  
   182  func TestCreateWithCustomMaskedPaths(t *testing.T) {
   183  	skip.If(t, testEnv.DaemonInfo.OSType != "linux")
   184  
   185  	ctx := setupTest(t)
   186  	apiClient := testEnv.APIClient()
   187  
   188  	testCases := []struct {
   189  		maskedPaths []string
   190  		expected    []string
   191  	}{
   192  		{
   193  			maskedPaths: []string{},
   194  			expected:    []string{},
   195  		},
   196  		{
   197  			maskedPaths: nil,
   198  			expected:    oci.DefaultSpec().Linux.MaskedPaths,
   199  		},
   200  		{
   201  			maskedPaths: []string{"/proc/kcore", "/proc/keys"},
   202  			expected:    []string{"/proc/kcore", "/proc/keys"},
   203  		},
   204  	}
   205  
   206  	checkInspect := func(t *testing.T, ctx context.Context, name string, expected []string) {
   207  		_, b, err := apiClient.ContainerInspectWithRaw(ctx, name, false)
   208  		assert.NilError(t, err)
   209  
   210  		var inspectJSON map[string]interface{}
   211  		err = json.Unmarshal(b, &inspectJSON)
   212  		assert.NilError(t, err)
   213  
   214  		cfg, ok := inspectJSON["HostConfig"].(map[string]interface{})
   215  		assert.Check(t, is.Equal(true, ok), name)
   216  
   217  		maskedPaths, ok := cfg["MaskedPaths"].([]interface{})
   218  		assert.Check(t, is.Equal(true, ok), name)
   219  
   220  		mps := []string{}
   221  		for _, mp := range maskedPaths {
   222  			mps = append(mps, mp.(string))
   223  		}
   224  
   225  		assert.DeepEqual(t, expected, mps)
   226  	}
   227  
   228  	// TODO: This should be using subtests
   229  
   230  	for i, tc := range testCases {
   231  		name := fmt.Sprintf("create-masked-paths-%d", i)
   232  		config := container.Config{
   233  			Image: "busybox",
   234  			Cmd:   []string{"true"},
   235  		}
   236  		hc := container.HostConfig{}
   237  		if tc.maskedPaths != nil {
   238  			hc.MaskedPaths = tc.maskedPaths
   239  		}
   240  
   241  		// Create the container.
   242  		c, err := apiClient.ContainerCreate(ctx,
   243  			&config,
   244  			&hc,
   245  			&network.NetworkingConfig{},
   246  			nil,
   247  			name,
   248  		)
   249  		assert.NilError(t, err)
   250  
   251  		checkInspect(t, ctx, name, tc.expected)
   252  
   253  		// Start the container.
   254  		err = apiClient.ContainerStart(ctx, c.ID, container.StartOptions{})
   255  		assert.NilError(t, err)
   256  
   257  		poll.WaitOn(t, ctr.IsInState(ctx, apiClient, c.ID, "exited"), poll.WithDelay(100*time.Millisecond))
   258  
   259  		checkInspect(t, ctx, name, tc.expected)
   260  	}
   261  }
   262  
   263  func TestCreateWithCustomReadonlyPaths(t *testing.T) {
   264  	skip.If(t, testEnv.DaemonInfo.OSType != "linux")
   265  
   266  	ctx := setupTest(t)
   267  	apiClient := testEnv.APIClient()
   268  
   269  	testCases := []struct {
   270  		readonlyPaths []string
   271  		expected      []string
   272  	}{
   273  		{
   274  			readonlyPaths: []string{},
   275  			expected:      []string{},
   276  		},
   277  		{
   278  			readonlyPaths: nil,
   279  			expected:      oci.DefaultSpec().Linux.ReadonlyPaths,
   280  		},
   281  		{
   282  			readonlyPaths: []string{"/proc/asound", "/proc/bus"},
   283  			expected:      []string{"/proc/asound", "/proc/bus"},
   284  		},
   285  	}
   286  
   287  	checkInspect := func(t *testing.T, ctx context.Context, name string, expected []string) {
   288  		_, b, err := apiClient.ContainerInspectWithRaw(ctx, name, false)
   289  		assert.NilError(t, err)
   290  
   291  		var inspectJSON map[string]interface{}
   292  		err = json.Unmarshal(b, &inspectJSON)
   293  		assert.NilError(t, err)
   294  
   295  		cfg, ok := inspectJSON["HostConfig"].(map[string]interface{})
   296  		assert.Check(t, is.Equal(true, ok), name)
   297  
   298  		readonlyPaths, ok := cfg["ReadonlyPaths"].([]interface{})
   299  		assert.Check(t, is.Equal(true, ok), name)
   300  
   301  		rops := []string{}
   302  		for _, rop := range readonlyPaths {
   303  			rops = append(rops, rop.(string))
   304  		}
   305  		assert.DeepEqual(t, expected, rops)
   306  	}
   307  
   308  	for i, tc := range testCases {
   309  		name := fmt.Sprintf("create-readonly-paths-%d", i)
   310  		config := container.Config{
   311  			Image: "busybox",
   312  			Cmd:   []string{"true"},
   313  		}
   314  		hc := container.HostConfig{}
   315  		if tc.readonlyPaths != nil {
   316  			hc.ReadonlyPaths = tc.readonlyPaths
   317  		}
   318  
   319  		// Create the container.
   320  		c, err := apiClient.ContainerCreate(ctx,
   321  			&config,
   322  			&hc,
   323  			&network.NetworkingConfig{},
   324  			nil,
   325  			name,
   326  		)
   327  		assert.NilError(t, err)
   328  
   329  		checkInspect(t, ctx, name, tc.expected)
   330  
   331  		// Start the container.
   332  		err = apiClient.ContainerStart(ctx, c.ID, container.StartOptions{})
   333  		assert.NilError(t, err)
   334  
   335  		poll.WaitOn(t, ctr.IsInState(ctx, apiClient, c.ID, "exited"), poll.WithDelay(100*time.Millisecond))
   336  
   337  		checkInspect(t, ctx, name, tc.expected)
   338  	}
   339  }
   340  
   341  func TestCreateWithInvalidHealthcheckParams(t *testing.T) {
   342  	ctx := setupTest(t)
   343  	apiClient := testEnv.APIClient()
   344  
   345  	testCases := []struct {
   346  		doc           string
   347  		interval      time.Duration
   348  		timeout       time.Duration
   349  		retries       int
   350  		startPeriod   time.Duration
   351  		startInterval time.Duration
   352  		expectedErr   string
   353  	}{
   354  		{
   355  			doc:         "test invalid Interval in Healthcheck: less than 0s",
   356  			interval:    -10 * time.Millisecond,
   357  			timeout:     time.Second,
   358  			retries:     1000,
   359  			expectedErr: fmt.Sprintf("Interval in Healthcheck cannot be less than %s", container.MinimumDuration),
   360  		},
   361  		{
   362  			doc:         "test invalid Interval in Healthcheck: larger than 0s but less than 1ms",
   363  			interval:    500 * time.Microsecond,
   364  			timeout:     time.Second,
   365  			retries:     1000,
   366  			expectedErr: fmt.Sprintf("Interval in Healthcheck cannot be less than %s", container.MinimumDuration),
   367  		},
   368  		{
   369  			doc:         "test invalid Timeout in Healthcheck: less than 1ms",
   370  			interval:    time.Second,
   371  			timeout:     -100 * time.Millisecond,
   372  			retries:     1000,
   373  			expectedErr: fmt.Sprintf("Timeout in Healthcheck cannot be less than %s", container.MinimumDuration),
   374  		},
   375  		{
   376  			doc:         "test invalid Retries in Healthcheck: less than 0",
   377  			interval:    time.Second,
   378  			timeout:     time.Second,
   379  			retries:     -10,
   380  			expectedErr: "Retries in Healthcheck cannot be negative",
   381  		},
   382  		{
   383  			doc:         "test invalid StartPeriod in Healthcheck: not 0 and less than 1ms",
   384  			interval:    time.Second,
   385  			timeout:     time.Second,
   386  			retries:     1000,
   387  			startPeriod: 100 * time.Microsecond,
   388  			expectedErr: fmt.Sprintf("StartPeriod in Healthcheck cannot be less than %s", container.MinimumDuration),
   389  		},
   390  		{
   391  			doc:           "test invalid StartInterval in Healthcheck: not 0 and less than 1ms",
   392  			interval:      time.Second,
   393  			timeout:       time.Second,
   394  			retries:       1000,
   395  			startPeriod:   time.Second,
   396  			startInterval: 100 * time.Microsecond,
   397  			expectedErr:   fmt.Sprintf("StartInterval in Healthcheck cannot be less than %s", container.MinimumDuration),
   398  		},
   399  	}
   400  
   401  	for _, tc := range testCases {
   402  		tc := tc
   403  		t.Run(tc.doc, func(t *testing.T) {
   404  			t.Parallel()
   405  			ctx := testutil.StartSpan(ctx, t)
   406  			cfg := container.Config{
   407  				Image: "busybox",
   408  				Healthcheck: &container.HealthConfig{
   409  					Interval:      tc.interval,
   410  					Timeout:       tc.timeout,
   411  					Retries:       tc.retries,
   412  					StartInterval: tc.startInterval,
   413  				},
   414  			}
   415  			if tc.startPeriod != 0 {
   416  				cfg.Healthcheck.StartPeriod = tc.startPeriod
   417  			}
   418  
   419  			resp, err := apiClient.ContainerCreate(ctx, &cfg, &container.HostConfig{}, nil, nil, "")
   420  			assert.Check(t, is.Equal(len(resp.Warnings), 0))
   421  
   422  			if versions.LessThan(testEnv.DaemonAPIVersion(), "1.32") {
   423  				assert.Check(t, errdefs.IsSystem(err))
   424  			} else {
   425  				assert.Check(t, errdefs.IsInvalidParameter(err))
   426  			}
   427  			assert.ErrorContains(t, err, tc.expectedErr)
   428  		})
   429  	}
   430  }
   431  
   432  // Make sure that anonymous volumes can be overritten by tmpfs
   433  // https://github.com/moby/moby/issues/40446
   434  func TestCreateTmpfsOverrideAnonymousVolume(t *testing.T) {
   435  	skip.If(t, testEnv.DaemonInfo.OSType == "windows", "windows does not support tmpfs")
   436  	ctx := setupTest(t)
   437  	apiClient := testEnv.APIClient()
   438  
   439  	id := ctr.Create(ctx, t, apiClient,
   440  		ctr.WithVolume("/foo"),
   441  		ctr.WithTmpfs("/foo"),
   442  		ctr.WithVolume("/bar"),
   443  		ctr.WithTmpfs("/bar:size=999"),
   444  		ctr.WithCmd("/bin/sh", "-c", "mount | grep '/foo' | grep tmpfs && mount | grep '/bar' | grep tmpfs"),
   445  	)
   446  
   447  	defer func() {
   448  		err := apiClient.ContainerRemove(ctx, id, container.RemoveOptions{Force: true})
   449  		assert.NilError(t, err)
   450  	}()
   451  
   452  	inspect, err := apiClient.ContainerInspect(ctx, id)
   453  	assert.NilError(t, err)
   454  	// tmpfs do not currently get added to inspect.Mounts
   455  	// Normally an anonymous volume would, except now tmpfs should prevent that.
   456  	assert.Assert(t, is.Len(inspect.Mounts, 0))
   457  
   458  	chWait, chErr := apiClient.ContainerWait(ctx, id, container.WaitConditionNextExit)
   459  	assert.NilError(t, apiClient.ContainerStart(ctx, id, container.StartOptions{}))
   460  
   461  	timeout := time.NewTimer(30 * time.Second)
   462  	defer timeout.Stop()
   463  
   464  	select {
   465  	case <-timeout.C:
   466  		t.Fatal("timeout waiting for container to exit")
   467  	case status := <-chWait:
   468  		var errMsg string
   469  		if status.Error != nil {
   470  			errMsg = status.Error.Message
   471  		}
   472  		assert.Equal(t, int(status.StatusCode), 0, errMsg)
   473  	case err := <-chErr:
   474  		assert.NilError(t, err)
   475  	}
   476  }
   477  
   478  // Test that if the referenced image platform does not match the requested platform on container create that we get an
   479  // error.
   480  func TestCreateDifferentPlatform(t *testing.T) {
   481  	ctx := setupTest(t)
   482  	apiClient := testEnv.APIClient()
   483  
   484  	img, _, err := apiClient.ImageInspectWithRaw(ctx, "busybox:latest")
   485  	assert.NilError(t, err)
   486  	assert.Assert(t, img.Architecture != "")
   487  
   488  	t.Run("different os", func(t *testing.T) {
   489  		ctx := testutil.StartSpan(ctx, t)
   490  		p := ocispec.Platform{
   491  			OS:           img.Os + "DifferentOS",
   492  			Architecture: img.Architecture,
   493  			Variant:      img.Variant,
   494  		}
   495  		_, err := apiClient.ContainerCreate(ctx, &container.Config{Image: "busybox:latest"}, &container.HostConfig{}, nil, &p, "")
   496  		assert.Check(t, is.ErrorType(err, errdefs.IsNotFound))
   497  	})
   498  	t.Run("different cpu arch", func(t *testing.T) {
   499  		ctx := testutil.StartSpan(ctx, t)
   500  		p := ocispec.Platform{
   501  			OS:           img.Os,
   502  			Architecture: img.Architecture + "DifferentArch",
   503  			Variant:      img.Variant,
   504  		}
   505  		_, err := apiClient.ContainerCreate(ctx, &container.Config{Image: "busybox:latest"}, &container.HostConfig{}, nil, &p, "")
   506  		assert.Check(t, is.ErrorType(err, errdefs.IsNotFound))
   507  	})
   508  }
   509  
   510  func TestCreateVolumesFromNonExistingContainer(t *testing.T) {
   511  	ctx := setupTest(t)
   512  	cli := testEnv.APIClient()
   513  
   514  	_, err := cli.ContainerCreate(
   515  		ctx,
   516  		&container.Config{Image: "busybox"},
   517  		&container.HostConfig{VolumesFrom: []string{"nosuchcontainer"}},
   518  		nil,
   519  		nil,
   520  		"",
   521  	)
   522  	assert.Check(t, errdefs.IsInvalidParameter(err))
   523  }
   524  
   525  // Test that we can create a container from an image that is for a different platform even if a platform was not specified
   526  // This is for the regression detailed here: https://github.com/moby/moby/issues/41552
   527  func TestCreatePlatformSpecificImageNoPlatform(t *testing.T) {
   528  	ctx := setupTest(t)
   529  
   530  	skip.If(t, testEnv.DaemonInfo.Architecture == "arm", "test only makes sense to run on non-arm systems")
   531  	skip.If(t, testEnv.DaemonInfo.OSType != "linux", "test image is only available on linux")
   532  	cli := testEnv.APIClient()
   533  
   534  	_, err := cli.ContainerCreate(
   535  		ctx,
   536  		&container.Config{Image: "arm32v7/hello-world"},
   537  		&container.HostConfig{},
   538  		nil,
   539  		nil,
   540  		"",
   541  	)
   542  	assert.NilError(t, err)
   543  }
   544  
   545  func TestCreateInvalidHostConfig(t *testing.T) {
   546  	skip.If(t, testEnv.DaemonInfo.OSType == "windows")
   547  
   548  	ctx := setupTest(t)
   549  	apiClient := testEnv.APIClient()
   550  
   551  	testCases := []struct {
   552  		doc         string
   553  		hc          container.HostConfig
   554  		expectedErr string
   555  	}{
   556  		{
   557  			doc:         "invalid IpcMode",
   558  			hc:          container.HostConfig{IpcMode: "invalid"},
   559  			expectedErr: "Error response from daemon: invalid IPC mode: invalid",
   560  		},
   561  		{
   562  			doc:         "invalid PidMode",
   563  			hc:          container.HostConfig{PidMode: "invalid"},
   564  			expectedErr: "Error response from daemon: invalid PID mode: invalid",
   565  		},
   566  		{
   567  			doc:         "invalid PidMode without container ID",
   568  			hc:          container.HostConfig{PidMode: "container"},
   569  			expectedErr: "Error response from daemon: invalid PID mode: container",
   570  		},
   571  		{
   572  			doc:         "invalid UTSMode",
   573  			hc:          container.HostConfig{UTSMode: "invalid"},
   574  			expectedErr: "Error response from daemon: invalid UTS mode: invalid",
   575  		},
   576  		{
   577  			doc:         "invalid Annotations",
   578  			hc:          container.HostConfig{Annotations: map[string]string{"": "a"}},
   579  			expectedErr: "Error response from daemon: invalid Annotations: the empty string is not permitted as an annotation key",
   580  		},
   581  	}
   582  
   583  	for _, tc := range testCases {
   584  		tc := tc
   585  		t.Run(tc.doc, func(t *testing.T) {
   586  			t.Parallel()
   587  			ctx := testutil.StartSpan(ctx, t)
   588  			cfg := container.Config{
   589  				Image: "busybox",
   590  			}
   591  			resp, err := apiClient.ContainerCreate(ctx, &cfg, &tc.hc, nil, nil, "")
   592  			assert.Check(t, is.Equal(len(resp.Warnings), 0))
   593  			assert.Check(t, errdefs.IsInvalidParameter(err), "got: %T", err)
   594  			assert.Error(t, err, tc.expectedErr)
   595  		})
   596  	}
   597  }
   598  
   599  func TestCreateWithMultipleEndpointSettings(t *testing.T) {
   600  	ctx := setupTest(t)
   601  
   602  	testcases := []struct {
   603  		apiVersion  string
   604  		expectedErr string
   605  	}{
   606  		{apiVersion: "1.44"},
   607  		{apiVersion: "1.43", expectedErr: "Container cannot be created with multiple network endpoints"},
   608  	}
   609  
   610  	for _, tc := range testcases {
   611  		t.Run("with API v"+tc.apiVersion, func(t *testing.T) {
   612  			apiClient, err := client.NewClientWithOpts(client.FromEnv, client.WithVersion(tc.apiVersion))
   613  			assert.NilError(t, err)
   614  
   615  			config := container.Config{
   616  				Image: "busybox",
   617  			}
   618  			networkingConfig := network.NetworkingConfig{
   619  				EndpointsConfig: map[string]*network.EndpointSettings{
   620  					"net1": {},
   621  					"net2": {},
   622  					"net3": {},
   623  				},
   624  			}
   625  			_, err = apiClient.ContainerCreate(ctx, &config, &container.HostConfig{}, &networkingConfig, nil, "")
   626  			if tc.expectedErr == "" {
   627  				assert.NilError(t, err)
   628  			} else {
   629  				assert.ErrorContains(t, err, tc.expectedErr)
   630  			}
   631  		})
   632  	}
   633  }