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/assert" is "gotest.tools/assert/cmp" "gotest.tools/poll" "gotest.tools/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()) // 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()) // Running with both privileged and cgroupns=private is not allowed errStr := "privileged mode is incompatible with private cgroup namespaces on cgroup v1 host. You must run the container in the host cgroup namespace when running privileged mode" testCreateFailureWithCgroupNs(t, "private", errStr, container.WithPrivileged(true), container.WithCgroupnsMode("private")) } 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) }