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/internal/test/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(t, ctx, 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(t, ctx, 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. 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(t, ctx, 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. }