summaryrefslogtreecommitdiff
path: root/integration/container/run_cgroupns_linux_test.go
blob: 9f17f40371a593924874e9f235ba5ac89ae31b7b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
package container // import "github.com/docker/docker/integration/container"

import (
	"context"
	"strings"
	"testing"
	"time"

	"github.com/docker/docker/client"
	"github.com/docker/docker/integration/internal/container"
	"github.com/docker/docker/integration/internal/requirement"
	"github.com/docker/docker/testutil/daemon"
	"gotest.tools/v3/assert"
	is "gotest.tools/v3/assert/cmp"
	"gotest.tools/v3/poll"
	"gotest.tools/v3/skip"
)

// Gets the value of the cgroup namespace for pid 1 of a container
func containerCgroupNamespace(ctx context.Context, t *testing.T, client *client.Client, cID string) string {
	res, err := container.Exec(ctx, client, cID, []string{"readlink", "/proc/1/ns/cgroup"})
	assert.NilError(t, err)
	assert.Assert(t, is.Len(res.Stderr(), 0))
	assert.Equal(t, 0, res.ExitCode)
	return strings.TrimSpace(res.Stdout())
}

// Bring up a daemon with the specified default cgroup namespace mode, and then create a container with the container options
func testRunWithCgroupNs(t *testing.T, daemonNsMode string, containerOpts ...func(*container.TestContainerConfig)) (string, string) {
	d := daemon.New(t, daemon.WithDefaultCgroupNamespaceMode(daemonNsMode))
	client := d.NewClientT(t)
	ctx := context.Background()

	d.StartWithBusybox(t)
	defer d.Stop(t)

	cID := container.Run(ctx, t, client, containerOpts...)
	poll.WaitOn(t, container.IsInState(ctx, client, cID, "running"), poll.WithDelay(100*time.Millisecond))

	daemonCgroup := d.CgroupNamespace(t)
	containerCgroup := containerCgroupNamespace(ctx, t, client, cID)
	return containerCgroup, daemonCgroup
}

// Bring up a daemon with the specified default cgroup namespace mode. Create a container with the container options,
// expecting an error with the specified string
func testCreateFailureWithCgroupNs(t *testing.T, daemonNsMode string, errStr string, containerOpts ...func(*container.TestContainerConfig)) {
	d := daemon.New(t, daemon.WithDefaultCgroupNamespaceMode(daemonNsMode))
	client := d.NewClientT(t)
	ctx := context.Background()

	d.StartWithBusybox(t)
	defer d.Stop(t)
	container.CreateExpectingErr(ctx, t, client, errStr, containerOpts...)
}

func TestCgroupNamespacesRun(t *testing.T) {
	skip.If(t, testEnv.DaemonInfo.OSType != "linux")
	skip.If(t, testEnv.IsRemoteDaemon())
	skip.If(t, !requirement.CgroupNamespacesEnabled())

	// When the daemon defaults to private cgroup namespaces, containers launched
	// should be in their own private cgroup namespace by default
	containerCgroup, daemonCgroup := testRunWithCgroupNs(t, "private")
	assert.Assert(t, daemonCgroup != containerCgroup)
}

func TestCgroupNamespacesRunPrivileged(t *testing.T) {
	skip.If(t, testEnv.DaemonInfo.OSType != "linux")
	skip.If(t, testEnv.IsRemoteDaemon())
	skip.If(t, !requirement.CgroupNamespacesEnabled())
	skip.If(t, testEnv.DaemonInfo.CgroupVersion == "2", "on cgroup v2, privileged containers use private cgroupns")

	// When the daemon defaults to private cgroup namespaces, privileged containers
	// launched should not be inside their own cgroup namespaces
	containerCgroup, daemonCgroup := testRunWithCgroupNs(t, "private", container.WithPrivileged(true))
	assert.Assert(t, daemonCgroup == containerCgroup)
}

func TestCgroupNamespacesRunDaemonHostMode(t *testing.T) {
	skip.If(t, testEnv.DaemonInfo.OSType != "linux")
	skip.If(t, testEnv.IsRemoteDaemon())
	skip.If(t, !requirement.CgroupNamespacesEnabled())

	// When the daemon defaults to host cgroup namespaces, containers
	// launched should not be inside their own cgroup namespaces
	containerCgroup, daemonCgroup := testRunWithCgroupNs(t, "host")
	assert.Assert(t, daemonCgroup == containerCgroup)
}

func TestCgroupNamespacesRunHostMode(t *testing.T) {
	skip.If(t, testEnv.DaemonInfo.OSType != "linux")
	skip.If(t, testEnv.IsRemoteDaemon())
	skip.If(t, !requirement.CgroupNamespacesEnabled())

	// When the daemon defaults to private cgroup namespaces, containers launched
	// with a cgroup ns mode of "host" should not be inside their own cgroup namespaces
	containerCgroup, daemonCgroup := testRunWithCgroupNs(t, "private", container.WithCgroupnsMode("host"))
	assert.Assert(t, daemonCgroup == containerCgroup)
}

func TestCgroupNamespacesRunPrivateMode(t *testing.T) {
	skip.If(t, testEnv.DaemonInfo.OSType != "linux")
	skip.If(t, testEnv.IsRemoteDaemon())
	skip.If(t, !requirement.CgroupNamespacesEnabled())

	// When the daemon defaults to private cgroup namespaces, containers launched
	// with a cgroup ns mode of "private" should be inside their own cgroup namespaces
	containerCgroup, daemonCgroup := testRunWithCgroupNs(t, "private", container.WithCgroupnsMode("private"))
	assert.Assert(t, daemonCgroup != containerCgroup)
}

func TestCgroupNamespacesRunPrivilegedAndPrivate(t *testing.T) {
	skip.If(t, testEnv.DaemonInfo.OSType != "linux")
	skip.If(t, testEnv.IsRemoteDaemon())
	skip.If(t, !requirement.CgroupNamespacesEnabled())

	containerCgroup, daemonCgroup := testRunWithCgroupNs(t, "private", container.WithPrivileged(true), container.WithCgroupnsMode("private"))
	assert.Assert(t, daemonCgroup != containerCgroup)
}

func TestCgroupNamespacesRunInvalidMode(t *testing.T) {
	skip.If(t, testEnv.DaemonInfo.OSType != "linux")
	skip.If(t, testEnv.IsRemoteDaemon())
	skip.If(t, !requirement.CgroupNamespacesEnabled())

	// An invalid cgroup namespace mode should return an error on container creation
	errStr := "invalid cgroup namespace mode: invalid"
	testCreateFailureWithCgroupNs(t, "private", errStr, container.WithCgroupnsMode("invalid"))
}

// Clients before 1.40 expect containers to be created in the host cgroup namespace,
// regardless of the default setting of the daemon
func TestCgroupNamespacesRunOlderClient(t *testing.T) {
	skip.If(t, testEnv.DaemonInfo.OSType != "linux")
	skip.If(t, testEnv.IsRemoteDaemon())
	skip.If(t, !requirement.CgroupNamespacesEnabled())

	d := daemon.New(t, daemon.WithDefaultCgroupNamespaceMode("private"))
	client := d.NewClientT(t, client.WithVersion("1.39"))

	ctx := context.Background()
	d.StartWithBusybox(t)
	defer d.Stop(t)

	cID := container.Run(ctx, t, client)
	poll.WaitOn(t, container.IsInState(ctx, client, cID, "running"), poll.WithDelay(100*time.Millisecond))

	daemonCgroup := d.CgroupNamespace(t)
	containerCgroup := containerCgroupNamespace(ctx, t, client, cID)
	assert.Assert(t, daemonCgroup == containerCgroup)
}