Browse code

hack: support $DOCKER_ROOTLESS for testing rootless

```
$ DOCKER_EXPERIMENTAL=1 DOCKER_ROOTLESS=1 TEST_SKIP_INTEGRATION_CLI=1 \
make test-integration
```

test-integration-cli is unsupported currently.
Also, tests that spawn custom daemon (testutil/daemon) are skipped.

Signed-off-by: Akihiro Suda <akihiro.suda.cz@hco.ntt.co.jp>

Akihiro Suda authored on 2020/02/18 18:43:56
Showing 17 changed files
... ...
@@ -261,7 +261,9 @@ FROM djs55/vpnkit@sha256:${VPNKIT_DIGEST} AS vpnkit
261 261
 FROM runtime-dev AS dev-systemd-false
262 262
 ARG DEBIAN_FRONTEND
263 263
 RUN groupadd -r docker
264
-RUN useradd --create-home --gid docker unprivilegeduser
264
+RUN useradd --create-home --gid docker unprivilegeduser \
265
+ && mkdir -p /home/unprivilegeduser/.local/share/docker \
266
+ && chown -R unprivilegeduser /home/unprivilegeduser
265 267
 # Let us use a .bashrc file
266 268
 RUN ln -sfv /go/src/github.com/docker/docker/.bashrc ~/.bashrc
267 269
 # Activate bash completion and include Docker's completion if mounted with DOCKER_BASH_COMPLETION_PATH
... ...
@@ -288,7 +290,9 @@ RUN --mount=type=cache,sharing=locked,id=moby-dev-aptlib,target=/var/lib/apt \
288 288
             python3-pip \
289 289
             python3-setuptools \
290 290
             python3-wheel \
291
+            sudo \
291 292
             thin-provisioning-tools \
293
+            uidmap \
292 294
             vim \
293 295
             vim-common \
294 296
             xfsprogs \
... ...
@@ -325,6 +329,7 @@ ARG DOCKER_BUILDTAGS
325 325
 ENV DOCKER_BUILDTAGS="${DOCKER_BUILDTAGS}"
326 326
 WORKDIR /go/src/github.com/docker/docker
327 327
 VOLUME /var/lib/docker
328
+VOLUME /home/unprivilegeduser/.local/share/docker
328 329
 # Wrap all commands in the "docker-in-docker" script to allow nested containers
329 330
 ENTRYPOINT ["hack/dind"]
330 331
 
... ...
@@ -11,6 +11,7 @@ pipeline {
11 11
         booleanParam(name: 'unit_validate', defaultValue: true, description: 'amd64 (x86_64) unit tests and vendor check')
12 12
         booleanParam(name: 'validate_force', defaultValue: false, description: 'force validation steps to be run, even if no changes were detected')
13 13
         booleanParam(name: 'amd64', defaultValue: true, description: 'amd64 (x86_64) Build/Test')
14
+        booleanParam(name: 'rootless', defaultValue: true, description: 'amd64 (x86_64) Build/Test (Rootless mode)')
14 15
         booleanParam(name: 'arm64', defaultValue: true, description: 'ARM (arm64) Build/Test')
15 16
         booleanParam(name: 's390x', defaultValue: true, description: 'IBM Z (s390x) Build/Test')
16 17
         booleanParam(name: 'ppc64le', defaultValue: true, description: 'PowerPC (ppc64le) Build/Test')
... ...
@@ -380,6 +381,94 @@ pipeline {
380 380
                         }
381 381
                     }
382 382
                 }
383
+                stage('rootless') {
384
+                    when {
385
+                        beforeAgent true
386
+                        expression { params.rootless }
387
+                    }
388
+                    agent { label 'amd64 && ubuntu-1804 && overlay2' }
389
+                    stages {
390
+                        stage("Print info") {
391
+                            steps {
392
+                                sh 'docker version'
393
+                                sh 'docker info'
394
+                                sh '''
395
+                                echo "check-config.sh version: ${CHECK_CONFIG_COMMIT}"
396
+                                curl -fsSL -o ${WORKSPACE}/check-config.sh "https://raw.githubusercontent.com/moby/moby/${CHECK_CONFIG_COMMIT}/contrib/check-config.sh" \
397
+                                && bash ${WORKSPACE}/check-config.sh || true
398
+                                '''
399
+                            }
400
+                        }
401
+                        stage("Build dev image") {
402
+                            steps {
403
+                                sh '''
404
+                                docker build --force-rm --build-arg APT_MIRROR -t docker:${GIT_COMMIT} .
405
+                                '''
406
+                            }
407
+                        }
408
+                        stage("Integration tests") {
409
+                            environment {
410
+                                DOCKER_EXPERIMENTAL = '1'
411
+                                DOCKER_ROOTLESS = '1'
412
+                                TEST_SKIP_INTEGRATION_CLI = '1'
413
+                            }
414
+                            steps {
415
+                                sh '''
416
+                                docker run --rm -t --privileged \
417
+                                  -v "$WORKSPACE/bundles:/go/src/github.com/docker/docker/bundles" \
418
+                                  --name docker-pr$BUILD_NUMBER \
419
+                                  -e DOCKER_GITCOMMIT=${GIT_COMMIT} \
420
+                                  -e DOCKER_GRAPHDRIVER \
421
+                                  -e DOCKER_EXPERIMENTAL \
422
+                                  -e DOCKER_ROOTLESS \
423
+                                  -e TEST_SKIP_INTEGRATION_CLI \
424
+                                  -e TIMEOUT \
425
+                                  -e VALIDATE_REPO=${GIT_URL} \
426
+                                  -e VALIDATE_BRANCH=${CHANGE_TARGET} \
427
+                                  docker:${GIT_COMMIT} \
428
+                                  hack/make.sh \
429
+                                    dynbinary \
430
+                                    test-integration
431
+                                '''
432
+                            }
433
+                            post {
434
+                                always {
435
+                                    junit testResults: 'bundles/**/*-report.xml', allowEmptyResults: true
436
+                                }
437
+                            }
438
+                        }
439
+                    }
440
+
441
+                    post {
442
+                        always {
443
+                            sh '''
444
+                            echo "Ensuring container killed."
445
+                            docker rm -vf docker-pr$BUILD_NUMBER || true
446
+                            '''
447
+
448
+                            sh '''
449
+                            echo "Chowning /workspace to jenkins user"
450
+                            docker run --rm -v "$WORKSPACE:/workspace" busybox chown -R "$(id -u):$(id -g)" /workspace
451
+                            '''
452
+
453
+                            catchError(buildResult: 'SUCCESS', stageResult: 'FAILURE', message: 'Failed to create bundles.tar.gz') {
454
+                                sh '''
455
+                                bundleName=amd64-rootless
456
+                                echo "Creating ${bundleName}-bundles.tar.gz"
457
+                                # exclude overlay2 directories
458
+                                find bundles -path '*/root/*overlay2' -prune -o -type f \\( -name '*-report.json' -o -name '*.log' -o -name '*.prof' -o -name '*-report.xml' \\) -print | xargs tar -czf ${bundleName}-bundles.tar.gz
459
+                                '''
460
+
461
+                                archiveArtifacts artifacts: '*-bundles.tar.gz', allowEmptyArchive: true
462
+                            }
463
+                        }
464
+                        cleanup {
465
+                            sh 'make clean'
466
+                            deleteDir()
467
+                        }
468
+                    }
469
+                }
470
+
383 471
                 stage('s390x') {
384 472
                     when {
385 473
                         beforeAgent true
... ...
@@ -61,6 +61,7 @@ DOCKER_ENVS := \
61 61
 	-e DOCKER_LDFLAGS \
62 62
 	-e DOCKER_PORT \
63 63
 	-e DOCKER_REMAP_ROOT \
64
+	-e DOCKER_ROOTLESS \
64 65
 	-e DOCKER_STORAGE_OPTS \
65 66
 	-e DOCKER_TEST_HOST \
66 67
 	-e DOCKER_USERLANDPROXY \
... ...
@@ -63,6 +63,26 @@ if [ "$DOCKER_EXPERIMENTAL" ]; then
63 63
 	extra_params="$extra_params --experimental"
64 64
 fi
65 65
 
66
+dockerd="dockerd"
67
+if [ -n "$DOCKER_ROOTLESS" ]; then
68
+	if [ -z "$DOCKER_EXPERIMENTAL" ]; then
69
+		echo >&2 '# DOCKER_ROOTLESS requires DOCKER_EXPERIMENTAL to be set'
70
+		exit 1
71
+	fi
72
+	if [ -z "$TEST_SKIP_INTEGRATION_CLI" ]; then
73
+		echo >&2 '# DOCKER_ROOTLESS requires TEST_SKIP_INTEGRATION_CLI to be set'
74
+		exit 1
75
+	fi
76
+	ln -sf "$(command -v vpnkit."$(uname -m)")" /usr/local/bin/vpnkit
77
+	user="unprivilegeduser"
78
+	uid=$(id -u $user)
79
+	# shellcheck disable=SC2174
80
+	mkdir -p -m 700 "/tmp/docker-${uid}"
81
+	chown "$user" "/tmp/docker-${uid}"
82
+	chmod -R o+w "$DEST"
83
+	dockerd="sudo -u $user -E -E XDG_RUNTIME_DIR=/tmp/docker-${uid} -E HOME=/home/${user} -E PATH=$PATH -- dockerd-rootless.sh"
84
+fi
85
+
66 86
 if [ -z "$DOCKER_TEST_HOST" ]; then
67 87
 	# Start apparmor if it is enabled
68 88
 	if [ -e "/sys/module/apparmor/parameters/enabled" ] && [ "$(cat /sys/module/apparmor/parameters/enabled)" == "Y" ]; then
... ...
@@ -81,7 +101,7 @@ if [ -z "$DOCKER_TEST_HOST" ]; then
81 81
 		echo "Starting dockerd"
82 82
 		[ -n "$TESTDEBUG" ] && set -x
83 83
 		exec \
84
-			dockerd --debug \
84
+			${dockerd} --debug \
85 85
 			--host "$DOCKER_HOST" \
86 86
 			--storage-driver "$DOCKER_GRAPHDRIVER" \
87 87
 			--pidfile "$DEST/docker.pid" \
... ...
@@ -147,6 +147,7 @@ test_env() {
147 147
 			DOCKER_HOST="$DOCKER_HOST" \
148 148
 			DOCKER_REMAP_ROOT="$DOCKER_REMAP_ROOT" \
149 149
 			DOCKER_REMOTE_DAEMON="$DOCKER_REMOTE_DAEMON" \
150
+			DOCKER_ROOTLESS="$DOCKER_ROOTLESS" \
150 151
 			DOCKERFILE="$DOCKERFILE" \
151 152
 			GOPATH="$GOPATH" \
152 153
 			GOTRACEBACK=all \
... ...
@@ -32,12 +32,33 @@ if [ "$DOCKER_REMAP_ROOT" ]; then
32 32
 	extra_params="$extra_params --userns-remap $DOCKER_REMAP_ROOT"
33 33
 fi
34 34
 
35
+if [ -n "$DOCKER_EXPERIMENTAL" ]; then
36
+	extra_params="$extra_params --experimental"
37
+fi
38
+
39
+dockerd="dockerd"
40
+socket=/var/run/docker.sock
41
+if [ -n "$DOCKER_ROOTLESS" ]; then
42
+	if [ -z "$DOCKER_EXPERIMENTAL" ]; then
43
+		echo >&2 '# DOCKER_ROOTLESS requires DOCKER_EXPERIMENTAL to be set'
44
+		exit 1
45
+	fi
46
+	user="unprivilegeduser"
47
+	uid=$(id -u $user)
48
+	# shellcheck disable=SC2174
49
+	mkdir -p -m 700 "/tmp/docker-${uid}"
50
+	chown $user "/tmp/docker-${uid}"
51
+	dockerd="sudo -u $user -E XDG_RUNTIME_DIR=/tmp/docker-${uid} -E HOME=/home/${user} -- dockerd-rootless.sh"
52
+	socket=/tmp/docker-${uid}/docker.sock
53
+fi
54
+
35 55
 args="--debug \
36
-	--host tcp://0.0.0.0:${listen_port} --host unix:///var/run/docker.sock \
56
+	--host "tcp://0.0.0.0:${listen_port}" --host "unix://${socket}" \
37 57
 	--storage-driver "${DOCKER_GRAPHDRIVER}" \
38 58
 	--userland-proxy="${DOCKER_USERLANDPROXY}" \
39 59
 	$storage_params \
40 60
 	$extra_params"
41 61
 
42
-echo dockerd ${args}
43
-exec dockerd ${args}
62
+echo "${dockerd} ${args}"
63
+# shellcheck disable=SC2086
64
+exec "${dockerd}" ${args}
... ...
@@ -116,6 +116,7 @@ func TestIpcModePrivate(t *testing.T) {
116 116
 // also exists on the host.
117 117
 func TestIpcModeShareable(t *testing.T) {
118 118
 	skip.If(t, testEnv.IsRemoteDaemon)
119
+	skip.If(t, testEnv.IsRootless, "cannot test /dev/shm in rootless")
119 120
 
120 121
 	testIpcNonePrivateShareable(t, "shareable", true, true)
121 122
 }
... ...
@@ -191,6 +192,7 @@ func TestAPIIpcModeShareableAndContainer(t *testing.T) {
191 191
 func TestAPIIpcModeHost(t *testing.T) {
192 192
 	skip.If(t, testEnv.IsRemoteDaemon)
193 193
 	skip.If(t, testEnv.IsUserNamespace)
194
+	skip.If(t, testEnv.IsRootless, "cannot test /dev/shm in rootless")
194 195
 
195 196
 	cfg := containertypes.Config{
196 197
 		Image: "busybox",
... ...
@@ -262,6 +264,7 @@ func testDaemonIpcPrivateShareable(t *testing.T, mustBeShared bool, arg ...strin
262 262
 // TestDaemonIpcModeShareable checks that --default-ipc-mode shareable works as intended.
263 263
 func TestDaemonIpcModeShareable(t *testing.T) {
264 264
 	skip.If(t, testEnv.IsRemoteDaemon)
265
+	skip.If(t, testEnv.IsRootless, "cannot test /dev/shm in rootless")
265 266
 
266 267
 	testDaemonIpcPrivateShareable(t, true, "--default-ipc-mode", "shareable")
267 268
 }
... ...
@@ -148,7 +148,9 @@ func TestKillDifferentUserContainer(t *testing.T) {
148 148
 }
149 149
 
150 150
 func TestInspectOomKilledTrue(t *testing.T) {
151
-	skip.If(t, testEnv.DaemonInfo.OSType == "windows" || !testEnv.DaemonInfo.MemoryLimit || !testEnv.DaemonInfo.SwapLimit)
151
+	skip.If(t, testEnv.DaemonInfo.OSType == "windows")
152
+	skip.If(t, testEnv.DaemonInfo.CgroupDriver == "none")
153
+	skip.If(t, !testEnv.DaemonInfo.MemoryLimit || !testEnv.DaemonInfo.SwapLimit)
152 154
 
153 155
 	defer setupTest(t)()
154 156
 	ctx := context.Background()
... ...
@@ -16,6 +16,7 @@ import (
16 16
 
17 17
 func TestLinksEtcHostsContentMatch(t *testing.T) {
18 18
 	skip.If(t, testEnv.IsRemoteDaemon)
19
+	skip.If(t, testEnv.IsRootless, "rootless mode has different view of /etc/hosts")
19 20
 
20 21
 	hosts, err := ioutil.ReadFile("/etc/hosts")
21 22
 	skip.If(t, os.IsNotExist(err))
... ...
@@ -214,6 +214,7 @@ func TestMountDaemonRoot(t *testing.T) {
214 214
 func TestContainerBindMountNonRecursive(t *testing.T) {
215 215
 	skip.If(t, testEnv.IsRemoteDaemon)
216 216
 	skip.If(t, versions.LessThan(testEnv.DaemonAPIVersion(), "1.40"), "BindOptions.NonRecursive requires API v1.40")
217
+	skip.If(t, testEnv.IsRootless, "cannot be tested because RootlessKit executes the daemon in private mount namespace (https://github.com/rootless-containers/rootlesskit/issues/97)")
217 218
 
218 219
 	defer setupTest(t)()
219 220
 
... ...
@@ -20,6 +20,7 @@ import (
20 20
 
21 21
 func TestPause(t *testing.T) {
22 22
 	skip.If(t, testEnv.DaemonInfo.OSType == "windows" && testEnv.DaemonInfo.Isolation == "process")
23
+	skip.If(t, testEnv.DaemonInfo.CgroupDriver == "none")
23 24
 
24 25
 	defer setupTest(t)()
25 26
 	client := testEnv.APIClient()
... ...
@@ -67,6 +68,7 @@ func TestPauseFailsOnWindowsServerContainers(t *testing.T) {
67 67
 func TestPauseStopPausedContainer(t *testing.T) {
68 68
 	skip.If(t, testEnv.DaemonInfo.OSType == "windows")
69 69
 	skip.If(t, versions.LessThan(testEnv.DaemonAPIVersion(), "1.31"), "broken in earlier versions")
70
+	skip.If(t, testEnv.DaemonInfo.CgroupDriver == "none")
70 71
 	defer setupTest(t)()
71 72
 	client := testEnv.APIClient()
72 73
 	ctx := context.Background()
... ...
@@ -20,6 +20,7 @@ import (
20 20
 func TestKernelTCPMemory(t *testing.T) {
21 21
 	skip.If(t, testEnv.DaemonInfo.OSType != "linux")
22 22
 	skip.If(t, versions.LessThan(testEnv.DaemonAPIVersion(), "1.40"), "skip test from new feature")
23
+	skip.If(t, testEnv.DaemonInfo.CgroupDriver == "none")
23 24
 	skip.If(t, !testEnv.DaemonInfo.KernelMemoryTCP)
24 25
 
25 26
 	defer setupTest(t)()
... ...
@@ -57,6 +58,11 @@ func TestNISDomainname(t *testing.T) {
57 57
 	skip.If(t, versions.LessThan(testEnv.DaemonAPIVersion(), "1.40"), "skip test from new feature")
58 58
 	skip.If(t, testEnv.DaemonInfo.OSType != "linux")
59 59
 
60
+	// Rootless supports custom Hostname but doesn't support custom Domainname
61
+	//  OCI runtime create failed: container_linux.go:349: starting container process caused "process_linux.go:449: container init caused \
62
+	//  "write sysctl key kernel.domainname: open /proc/sys/kernel/domainname: permission denied\"": unknown.
63
+	skip.If(t, testEnv.IsRootless, "rootless mode doesn't support setting Domainname (TODO: https://github.com/moby/moby/issues/40632)")
64
+
60 65
 	defer setupTest(t)()
61 66
 	client := testEnv.APIClient()
62 67
 	ctx := context.Background()
... ...
@@ -16,6 +16,7 @@ import (
16 16
 )
17 17
 
18 18
 func TestStats(t *testing.T) {
19
+	skip.If(t, testEnv.DaemonInfo.CgroupDriver == "none")
19 20
 	skip.If(t, !testEnv.DaemonInfo.MemoryLimit)
20 21
 
21 22
 	defer setupTest(t)()
... ...
@@ -19,6 +19,7 @@ import (
19 19
 
20 20
 func TestUpdateMemory(t *testing.T) {
21 21
 	skip.If(t, testEnv.DaemonInfo.OSType == "windows")
22
+	skip.If(t, testEnv.DaemonInfo.CgroupDriver == "none")
22 23
 	skip.If(t, !testEnv.DaemonInfo.MemoryLimit)
23 24
 	skip.If(t, !testEnv.DaemonInfo.SwapLimit)
24 25
 
... ...
@@ -68,6 +69,7 @@ func TestUpdateMemory(t *testing.T) {
68 68
 }
69 69
 
70 70
 func TestUpdateCPUQuota(t *testing.T) {
71
+	skip.If(t, testEnv.DaemonInfo.CgroupDriver == "none")
71 72
 	defer setupTest(t)()
72 73
 	client := testEnv.APIClient()
73 74
 	ctx := context.Background()
... ...
@@ -106,6 +108,7 @@ func TestUpdateCPUQuota(t *testing.T) {
106 106
 
107 107
 func TestUpdatePidsLimit(t *testing.T) {
108 108
 	skip.If(t, testEnv.DaemonInfo.OSType == "windows")
109
+	skip.If(t, testEnv.DaemonInfo.CgroupDriver == "none")
109 110
 	skip.If(t, !testEnv.DaemonInfo.PidsLimit)
110 111
 
111 112
 	defer setupTest(t)()
... ...
@@ -29,6 +29,7 @@ func TestDaemonRestartWithLiveRestore(t *testing.T) {
29 29
 	skip.If(t, testEnv.OSType == "windows")
30 30
 	skip.If(t, testEnv.IsRemoteDaemon)
31 31
 	skip.If(t, versions.LessThan(testEnv.DaemonAPIVersion(), "1.38"), "skip test from new feature")
32
+	skip.If(t, testEnv.IsRootless, "rootless mode has different view of network")
32 33
 	d := daemon.New(t)
33 34
 	defer d.Stop(t)
34 35
 	d.Start(t)
... ...
@@ -52,6 +53,7 @@ func TestDaemonDefaultNetworkPools(t *testing.T) {
52 52
 	// Remove docker0 bridge and the start daemon defining the predefined address pools
53 53
 	skip.If(t, testEnv.IsRemoteDaemon)
54 54
 	skip.If(t, versions.LessThan(testEnv.DaemonAPIVersion(), "1.38"), "skip test from new feature")
55
+	skip.If(t, testEnv.IsRootless, "rootless mode has different view of network")
55 56
 	defaultNetworkBridge := "docker0"
56 57
 	delInterface(t, defaultNetworkBridge)
57 58
 	d := daemon.New(t)
... ...
@@ -94,6 +96,7 @@ func TestDaemonRestartWithExistingNetwork(t *testing.T) {
94 94
 	skip.If(t, testEnv.OSType == "windows")
95 95
 	skip.If(t, testEnv.IsRemoteDaemon)
96 96
 	skip.If(t, versions.LessThan(testEnv.DaemonAPIVersion(), "1.38"), "skip test from new feature")
97
+	skip.If(t, testEnv.IsRootless, "rootless mode has different view of network")
97 98
 	defaultNetworkBridge := "docker0"
98 99
 	d := daemon.New(t)
99 100
 	d.Start(t)
... ...
@@ -127,6 +130,7 @@ func TestDaemonRestartWithExistingNetworkWithDefaultPoolRange(t *testing.T) {
127 127
 	skip.If(t, testEnv.OSType == "windows")
128 128
 	skip.If(t, testEnv.IsRemoteDaemon)
129 129
 	skip.If(t, versions.LessThan(testEnv.DaemonAPIVersion(), "1.38"), "skip test from new feature")
130
+	skip.If(t, testEnv.IsRootless, "rootless mode has different view of network")
130 131
 	defaultNetworkBridge := "docker0"
131 132
 	d := daemon.New(t)
132 133
 	d.Start(t)
... ...
@@ -177,6 +181,7 @@ func TestDaemonWithBipAndDefaultNetworkPool(t *testing.T) {
177 177
 	skip.If(t, testEnv.OSType == "windows")
178 178
 	skip.If(t, testEnv.IsRemoteDaemon)
179 179
 	skip.If(t, versions.LessThan(testEnv.DaemonAPIVersion(), "1.38"), "skip test from new feature")
180
+	skip.If(t, testEnv.IsRootless, "rootless mode has different view of network")
180 181
 	defaultNetworkBridge := "docker0"
181 182
 	d := daemon.New(t)
182 183
 	defer d.Stop(t)
... ...
@@ -199,6 +204,7 @@ func TestDaemonWithBipAndDefaultNetworkPool(t *testing.T) {
199 199
 
200 200
 func TestServiceWithPredefinedNetwork(t *testing.T) {
201 201
 	skip.If(t, testEnv.OSType == "windows")
202
+	skip.If(t, testEnv.IsRootless, "rootless mode doesn't support Swarm-mode")
202 203
 	defer setupTest(t)()
203 204
 	d := swarm.NewSwarm(t, testEnv)
204 205
 	defer d.Stop(t)
... ...
@@ -228,6 +234,7 @@ const ingressNet = "ingress"
228 228
 
229 229
 func TestServiceRemoveKeepsIngressNetwork(t *testing.T) {
230 230
 	t.Skip("FLAKY_TEST")
231
+	skip.If(t, testEnv.IsRootless, "rootless mode doesn't support Swarm-mode")
231 232
 
232 233
 	skip.If(t, testEnv.OSType == "windows")
233 234
 	defer setupTest(t)()
... ...
@@ -318,6 +325,7 @@ func noServices(ctx context.Context, client client.ServiceAPIClient) func(log po
318 318
 func TestServiceWithDataPathPortInit(t *testing.T) {
319 319
 	skip.If(t, testEnv.OSType == "windows")
320 320
 	skip.If(t, versions.LessThan(testEnv.DaemonAPIVersion(), "1.40"), "DataPathPort was added in API v1.40")
321
+	skip.If(t, testEnv.IsRootless, "rootless mode doesn't support Swarm-mode")
321 322
 	defer setupTest(t)()
322 323
 	var datapathPort uint32 = 7777
323 324
 	d := swarm.NewSwarm(t, testEnv, daemon.WithSwarmDataPathPort(datapathPort))
... ...
@@ -384,6 +392,7 @@ func TestServiceWithDataPathPortInit(t *testing.T) {
384 384
 
385 385
 func TestServiceWithDefaultAddressPoolInit(t *testing.T) {
386 386
 	skip.If(t, testEnv.OSType == "windows")
387
+	skip.If(t, testEnv.IsRootless, "rootless mode doesn't support Swarm-mode")
387 388
 	defer setupTest(t)()
388 389
 	d := swarm.NewSwarm(t, testEnv,
389 390
 		daemon.WithSwarmDefaultAddrPool([]string{"20.20.0.0/16"}),
... ...
@@ -151,6 +151,10 @@ func New(t testing.TB, ops ...Option) *Daemon {
151 151
 
152 152
 	assert.Check(t, dest != "", "Please set the DOCKER_INTEGRATION_DAEMON_DEST or the DEST environment variable")
153 153
 
154
+	if os.Getenv("DOCKER_ROOTLESS") != "" {
155
+		t.Skip("github.com/docker/docker/testutil/daemon.Daemon doesn't support DOCKER_ROOTLESS")
156
+	}
157
+
154 158
 	d, err := NewDaemon(dest, ops...)
155 159
 	assert.NilError(t, err, "could not create daemon at %q", dest)
156 160
 
... ...
@@ -227,6 +231,9 @@ func (d *Daemon) Cleanup(t testing.TB) {
227 227
 // Start starts the daemon and return once it is ready to receive requests.
228 228
 func (d *Daemon) Start(t testing.TB, args ...string) {
229 229
 	t.Helper()
230
+	if os.Getenv("DOCKER_ROOTLESS") != "" {
231
+		t.Skip("github.com/docker/docker/testutil/daemon.Daemon doesn't support DOCKER_ROOTLESS")
232
+	}
230 233
 	if err := d.StartWithError(args...); err != nil {
231 234
 		t.Fatalf("[%s] failed to start daemon with arguments %v : %v", d.id, d.args, err)
232 235
 	}
... ...
@@ -162,6 +162,11 @@ func (e *Execution) IsUserNamespace() bool {
162 162
 	return root != ""
163 163
 }
164 164
 
165
+// IsRootless returns whether the rootless mode is enabled
166
+func (e *Execution) IsRootless() bool {
167
+	return os.Getenv("DOCKER_ROOTLESS") != ""
168
+}
169
+
165 170
 // HasExistingImage checks whether there is an image with the given reference.
166 171
 // Note that this is done by filtering and then checking whether there were any
167 172
 // results -- so ambiguous references might result in false-positives.