Skip to content

Commit 5bf33ad

Browse files
committed
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 <[email protected]>
1 parent e67f42e commit 5bf33ad

17 files changed

Lines changed: 183 additions & 6 deletions

Dockerfile

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,9 @@ FROM djs55/vpnkit@sha256:${VPNKIT_DIGEST} AS vpnkit
261261
FROM runtime-dev AS dev-systemd-false
262262
ARG DEBIAN_FRONTEND
263263
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
265267
# Let us use a .bashrc file
266268
RUN ln -sfv /go/src/github.com/docker/docker/.bashrc ~/.bashrc
267269
# 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 \
288290
python3-pip \
289291
python3-setuptools \
290292
python3-wheel \
293+
sudo \
291294
thin-provisioning-tools \
295+
uidmap \
292296
vim \
293297
vim-common \
294298
xfsprogs \
@@ -325,6 +329,7 @@ ARG DOCKER_BUILDTAGS
325329
ENV DOCKER_BUILDTAGS="${DOCKER_BUILDTAGS}"
326330
WORKDIR /go/src/github.com/docker/docker
327331
VOLUME /var/lib/docker
332+
VOLUME /home/unprivilegeduser/.local/share/docker
328333
# Wrap all commands in the "docker-in-docker" script to allow nested containers
329334
ENTRYPOINT ["hack/dind"]
330335

Jenkinsfile

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ pipeline {
1111
booleanParam(name: 'unit_validate', defaultValue: true, description: 'amd64 (x86_64) unit tests and vendor check')
1212
booleanParam(name: 'validate_force', defaultValue: false, description: 'force validation steps to be run, even if no changes were detected')
1313
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)')
1415
booleanParam(name: 'arm64', defaultValue: true, description: 'ARM (arm64) Build/Test')
1516
booleanParam(name: 's390x', defaultValue: true, description: 'IBM Z (s390x) Build/Test')
1617
booleanParam(name: 'ppc64le', defaultValue: true, description: 'PowerPC (ppc64le) Build/Test')
@@ -380,6 +381,94 @@ pipeline {
380381
}
381382
}
382383
}
384+
stage('rootless') {
385+
when {
386+
beforeAgent true
387+
expression { params.rootless }
388+
}
389+
agent { label 'amd64 && ubuntu-1804 && overlay2' }
390+
stages {
391+
stage("Print info") {
392+
steps {
393+
sh 'docker version'
394+
sh 'docker info'
395+
sh '''
396+
echo "check-config.sh version: ${CHECK_CONFIG_COMMIT}"
397+
curl -fsSL -o ${WORKSPACE}/check-config.sh "https://raw.githubusercontent.com/moby/moby/${CHECK_CONFIG_COMMIT}/contrib/check-config.sh" \
398+
&& bash ${WORKSPACE}/check-config.sh || true
399+
'''
400+
}
401+
}
402+
stage("Build dev image") {
403+
steps {
404+
sh '''
405+
docker build --force-rm --build-arg APT_MIRROR -t docker:${GIT_COMMIT} .
406+
'''
407+
}
408+
}
409+
stage("Integration tests") {
410+
environment {
411+
DOCKER_EXPERIMENTAL = '1'
412+
DOCKER_ROOTLESS = '1'
413+
TEST_SKIP_INTEGRATION_CLI = '1'
414+
}
415+
steps {
416+
sh '''
417+
docker run --rm -t --privileged \
418+
-v "$WORKSPACE/bundles:/go/src/github.com/docker/docker/bundles" \
419+
--name docker-pr$BUILD_NUMBER \
420+
-e DOCKER_GITCOMMIT=${GIT_COMMIT} \
421+
-e DOCKER_GRAPHDRIVER \
422+
-e DOCKER_EXPERIMENTAL \
423+
-e DOCKER_ROOTLESS \
424+
-e TEST_SKIP_INTEGRATION_CLI \
425+
-e TIMEOUT \
426+
-e VALIDATE_REPO=${GIT_URL} \
427+
-e VALIDATE_BRANCH=${CHANGE_TARGET} \
428+
docker:${GIT_COMMIT} \
429+
hack/make.sh \
430+
dynbinary \
431+
test-integration
432+
'''
433+
}
434+
post {
435+
always {
436+
junit testResults: 'bundles/**/*-report.xml', allowEmptyResults: true
437+
}
438+
}
439+
}
440+
}
441+
442+
post {
443+
always {
444+
sh '''
445+
echo "Ensuring container killed."
446+
docker rm -vf docker-pr$BUILD_NUMBER || true
447+
'''
448+
449+
sh '''
450+
echo "Chowning /workspace to jenkins user"
451+
docker run --rm -v "$WORKSPACE:/workspace" busybox chown -R "$(id -u):$(id -g)" /workspace
452+
'''
453+
454+
catchError(buildResult: 'SUCCESS', stageResult: 'FAILURE', message: 'Failed to create bundles.tar.gz') {
455+
sh '''
456+
bundleName=amd64-rootless
457+
echo "Creating ${bundleName}-bundles.tar.gz"
458+
# exclude overlay2 directories
459+
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
460+
'''
461+
462+
archiveArtifacts artifacts: '*-bundles.tar.gz', allowEmptyArchive: true
463+
}
464+
}
465+
cleanup {
466+
sh 'make clean'
467+
deleteDir()
468+
}
469+
}
470+
}
471+
383472
stage('s390x') {
384473
when {
385474
beforeAgent true

Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ DOCKER_ENVS := \
6161
-e DOCKER_LDFLAGS \
6262
-e DOCKER_PORT \
6363
-e DOCKER_REMAP_ROOT \
64+
-e DOCKER_ROOTLESS \
6465
-e DOCKER_STORAGE_OPTS \
6566
-e DOCKER_TEST_HOST \
6667
-e DOCKER_USERLANDPROXY \

hack/make/.integration-daemon-start

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,26 @@ if [ "$DOCKER_EXPERIMENTAL" ]; then
6363
extra_params="$extra_params --experimental"
6464
fi
6565

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+
6686
if [ -z "$DOCKER_TEST_HOST" ]; then
6787
# Start apparmor if it is enabled
6888
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
81101
echo "Starting dockerd"
82102
[ -n "$TESTDEBUG" ] && set -x
83103
exec \
84-
dockerd --debug \
104+
${dockerd} --debug \
85105
--host "$DOCKER_HOST" \
86106
--storage-driver "$DOCKER_GRAPHDRIVER" \
87107
--pidfile "$DEST/docker.pid" \

hack/make/.integration-test-helpers

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,7 @@ test_env() {
147147
DOCKER_HOST="$DOCKER_HOST" \
148148
DOCKER_REMAP_ROOT="$DOCKER_REMAP_ROOT" \
149149
DOCKER_REMOTE_DAEMON="$DOCKER_REMOTE_DAEMON" \
150+
DOCKER_ROOTLESS="$DOCKER_ROOTLESS" \
150151
DOCKERFILE="$DOCKERFILE" \
151152
GOPATH="$GOPATH" \
152153
GOTRACEBACK=all \

hack/make/run

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,33 @@ if [ "$DOCKER_REMAP_ROOT" ]; then
3232
extra_params="$extra_params --userns-remap $DOCKER_REMAP_ROOT"
3333
fi
3434

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+
3555
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}" \
3757
--storage-driver "${DOCKER_GRAPHDRIVER}" \
3858
--userland-proxy="${DOCKER_USERLANDPROXY}" \
3959
$storage_params \
4060
$extra_params"
4161

42-
echo dockerd ${args}
43-
exec dockerd ${args}
62+
echo "${dockerd} ${args}"
63+
# shellcheck disable=SC2086
64+
exec "${dockerd}" ${args}

integration/container/ipcmode_linux_test.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ func TestIpcModePrivate(t *testing.T) {
116116
// also exists on the host.
117117
func TestIpcModeShareable(t *testing.T) {
118118
skip.If(t, testEnv.IsRemoteDaemon)
119+
skip.If(t, testEnv.IsRootless, "cannot test /dev/shm in rootless")
119120

120121
testIpcNonePrivateShareable(t, "shareable", true, true)
121122
}
@@ -191,6 +192,7 @@ func TestAPIIpcModeShareableAndContainer(t *testing.T) {
191192
func TestAPIIpcModeHost(t *testing.T) {
192193
skip.If(t, testEnv.IsRemoteDaemon)
193194
skip.If(t, testEnv.IsUserNamespace)
195+
skip.If(t, testEnv.IsRootless, "cannot test /dev/shm in rootless")
194196

195197
cfg := containertypes.Config{
196198
Image: "busybox",
@@ -262,6 +264,7 @@ func testDaemonIpcPrivateShareable(t *testing.T, mustBeShared bool, arg ...strin
262264
// TestDaemonIpcModeShareable checks that --default-ipc-mode shareable works as intended.
263265
func TestDaemonIpcModeShareable(t *testing.T) {
264266
skip.If(t, testEnv.IsRemoteDaemon)
267+
skip.If(t, testEnv.IsRootless, "cannot test /dev/shm in rootless")
265268

266269
testDaemonIpcPrivateShareable(t, true, "--default-ipc-mode", "shareable")
267270
}

integration/container/kill_test.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,9 @@ func TestKillDifferentUserContainer(t *testing.T) {
148148
}
149149

150150
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)
152154

153155
defer setupTest(t)()
154156
ctx := context.Background()

integration/container/links_linux_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import (
1616

1717
func TestLinksEtcHostsContentMatch(t *testing.T) {
1818
skip.If(t, testEnv.IsRemoteDaemon)
19+
skip.If(t, testEnv.IsRootless, "rootless mode has different view of /etc/hosts")
1920

2021
hosts, err := ioutil.ReadFile("/etc/hosts")
2122
skip.If(t, os.IsNotExist(err))

integration/container/mounts_linux_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,7 @@ func TestMountDaemonRoot(t *testing.T) {
214214
func TestContainerBindMountNonRecursive(t *testing.T) {
215215
skip.If(t, testEnv.IsRemoteDaemon)
216216
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)")
217218

218219
defer setupTest(t)()
219220

0 commit comments

Comments
 (0)