run_cgroupns_linux_test.go 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. package container // import "github.com/docker/docker/integration/container"
  2. import (
  3. "context"
  4. "strings"
  5. "testing"
  6. "time"
  7. "github.com/docker/docker/client"
  8. "github.com/docker/docker/integration/internal/container"
  9. "github.com/docker/docker/integration/internal/requirement"
  10. "github.com/docker/docker/testutil/daemon"
  11. "gotest.tools/assert"
  12. is "gotest.tools/assert/cmp"
  13. "gotest.tools/poll"
  14. "gotest.tools/skip"
  15. )
  16. // Gets the value of the cgroup namespace for pid 1 of a container
  17. func containerCgroupNamespace(ctx context.Context, t *testing.T, client *client.Client, cID string) string {
  18. res, err := container.Exec(ctx, client, cID, []string{"readlink", "/proc/1/ns/cgroup"})
  19. assert.NilError(t, err)
  20. assert.Assert(t, is.Len(res.Stderr(), 0))
  21. assert.Equal(t, 0, res.ExitCode)
  22. return strings.TrimSpace(res.Stdout())
  23. }
  24. // Bring up a daemon with the specified default cgroup namespace mode, and then create a container with the container options
  25. func testRunWithCgroupNs(t *testing.T, daemonNsMode string, containerOpts ...func(*container.TestContainerConfig)) (string, string) {
  26. d := daemon.New(t, daemon.WithDefaultCgroupNamespaceMode(daemonNsMode))
  27. client := d.NewClientT(t)
  28. ctx := context.Background()
  29. d.StartWithBusybox(t)
  30. defer d.Stop(t)
  31. cID := container.Run(ctx, t, client, containerOpts...)
  32. poll.WaitOn(t, container.IsInState(ctx, client, cID, "running"), poll.WithDelay(100*time.Millisecond))
  33. daemonCgroup := d.CgroupNamespace(t)
  34. containerCgroup := containerCgroupNamespace(ctx, t, client, cID)
  35. return containerCgroup, daemonCgroup
  36. }
  37. // Bring up a daemon with the specified default cgroup namespace mode. Create a container with the container options,
  38. // expecting an error with the specified string
  39. func testCreateFailureWithCgroupNs(t *testing.T, daemonNsMode string, errStr string, containerOpts ...func(*container.TestContainerConfig)) {
  40. d := daemon.New(t, daemon.WithDefaultCgroupNamespaceMode(daemonNsMode))
  41. client := d.NewClientT(t)
  42. ctx := context.Background()
  43. d.StartWithBusybox(t)
  44. defer d.Stop(t)
  45. container.CreateExpectingErr(ctx, t, client, errStr, containerOpts...)
  46. }
  47. func TestCgroupNamespacesRun(t *testing.T) {
  48. skip.If(t, testEnv.DaemonInfo.OSType != "linux")
  49. skip.If(t, testEnv.IsRemoteDaemon())
  50. skip.If(t, !requirement.CgroupNamespacesEnabled())
  51. // When the daemon defaults to private cgroup namespaces, containers launched
  52. // should be in their own private cgroup namespace by default
  53. containerCgroup, daemonCgroup := testRunWithCgroupNs(t, "private")
  54. assert.Assert(t, daemonCgroup != containerCgroup)
  55. }
  56. func TestCgroupNamespacesRunPrivileged(t *testing.T) {
  57. skip.If(t, testEnv.DaemonInfo.OSType != "linux")
  58. skip.If(t, testEnv.IsRemoteDaemon())
  59. skip.If(t, !requirement.CgroupNamespacesEnabled())
  60. // When the daemon defaults to private cgroup namespaces, privileged containers
  61. // launched should not be inside their own cgroup namespaces
  62. containerCgroup, daemonCgroup := testRunWithCgroupNs(t, "private", container.WithPrivileged(true))
  63. assert.Assert(t, daemonCgroup == containerCgroup)
  64. }
  65. func TestCgroupNamespacesRunDaemonHostMode(t *testing.T) {
  66. skip.If(t, testEnv.DaemonInfo.OSType != "linux")
  67. skip.If(t, testEnv.IsRemoteDaemon())
  68. skip.If(t, !requirement.CgroupNamespacesEnabled())
  69. // When the daemon defaults to host cgroup namespaces, containers
  70. // launched should not be inside their own cgroup namespaces
  71. containerCgroup, daemonCgroup := testRunWithCgroupNs(t, "host")
  72. assert.Assert(t, daemonCgroup == containerCgroup)
  73. }
  74. func TestCgroupNamespacesRunHostMode(t *testing.T) {
  75. skip.If(t, testEnv.DaemonInfo.OSType != "linux")
  76. skip.If(t, testEnv.IsRemoteDaemon())
  77. skip.If(t, !requirement.CgroupNamespacesEnabled())
  78. // When the daemon defaults to private cgroup namespaces, containers launched
  79. // with a cgroup ns mode of "host" should not be inside their own cgroup namespaces
  80. containerCgroup, daemonCgroup := testRunWithCgroupNs(t, "private", container.WithCgroupnsMode("host"))
  81. assert.Assert(t, daemonCgroup == containerCgroup)
  82. }
  83. func TestCgroupNamespacesRunPrivateMode(t *testing.T) {
  84. skip.If(t, testEnv.DaemonInfo.OSType != "linux")
  85. skip.If(t, testEnv.IsRemoteDaemon())
  86. skip.If(t, !requirement.CgroupNamespacesEnabled())
  87. // When the daemon defaults to private cgroup namespaces, containers launched
  88. // with a cgroup ns mode of "private" should be inside their own cgroup namespaces
  89. containerCgroup, daemonCgroup := testRunWithCgroupNs(t, "private", container.WithCgroupnsMode("private"))
  90. assert.Assert(t, daemonCgroup != containerCgroup)
  91. }
  92. func TestCgroupNamespacesRunPrivilegedAndPrivate(t *testing.T) {
  93. skip.If(t, testEnv.DaemonInfo.OSType != "linux")
  94. skip.If(t, testEnv.IsRemoteDaemon())
  95. skip.If(t, !requirement.CgroupNamespacesEnabled())
  96. // Running with both privileged and cgroupns=private is not allowed
  97. 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"
  98. testCreateFailureWithCgroupNs(t, "private", errStr, container.WithPrivileged(true), container.WithCgroupnsMode("private"))
  99. }
  100. func TestCgroupNamespacesRunInvalidMode(t *testing.T) {
  101. skip.If(t, testEnv.DaemonInfo.OSType != "linux")
  102. skip.If(t, testEnv.IsRemoteDaemon())
  103. skip.If(t, !requirement.CgroupNamespacesEnabled())
  104. // An invalid cgroup namespace mode should return an error on container creation
  105. errStr := "invalid cgroup namespace mode: invalid"
  106. testCreateFailureWithCgroupNs(t, "private", errStr, container.WithCgroupnsMode("invalid"))
  107. }
  108. // Clients before 1.40 expect containers to be created in the host cgroup namespace,
  109. // regardless of the default setting of the daemon
  110. func TestCgroupNamespacesRunOlderClient(t *testing.T) {
  111. skip.If(t, testEnv.DaemonInfo.OSType != "linux")
  112. skip.If(t, testEnv.IsRemoteDaemon())
  113. skip.If(t, !requirement.CgroupNamespacesEnabled())
  114. d := daemon.New(t, daemon.WithDefaultCgroupNamespaceMode("private"))
  115. client := d.NewClientT(t, client.WithVersion("1.39"))
  116. ctx := context.Background()
  117. d.StartWithBusybox(t)
  118. defer d.Stop(t)
  119. cID := container.Run(ctx, t, client)
  120. poll.WaitOn(t, container.IsInState(ctx, client, cID, "running"), poll.WithDelay(100*time.Millisecond))
  121. daemonCgroup := d.CgroupNamespace(t)
  122. containerCgroup := containerCgroupNamespace(ctx, t, client, cID)
  123. assert.Assert(t, daemonCgroup == containerCgroup)
  124. }