From 2f853b94931057d8ebcd6e21f7656d3a04e2acf4 Mon Sep 17 00:00:00 2001 From: Alexander Morozov Date: Tue, 7 Apr 2015 15:10:44 -0700 Subject: [PATCH 1/2] Update libcontainer to bd8ec36106086f72b66e1be85a81202b93503e44 Fix #12130 Signed-off-by: Alexander Morozov --- hack/vendor.sh | 2 +- .../github.com/docker/libcontainer/.gitignore | 1 + .../github.com/docker/libcontainer/Makefile | 2 + .../github.com/docker/libcontainer/README.md | 3 + .../cgroups/systemd/apply_systemd.go | 64 ++++++------- .../docker/libcontainer/configs/namespaces.go | 11 +++ .../docker/libcontainer/container_linux.go | 6 ++ .../libcontainer/container_linux_test.go | 3 +- .../libcontainer/integration/exec_test.go | 89 +++++++++++++++++++ .../libcontainer/integration/utils_test.go | 7 +- .../docker/libcontainer/nsinit/main.go | 2 +- 11 files changed, 149 insertions(+), 41 deletions(-) diff --git a/hack/vendor.sh b/hack/vendor.sh index 6552cab23b..8b42ad2783 100755 --- a/hack/vendor.sh +++ b/hack/vendor.sh @@ -75,7 +75,7 @@ rm -rf src/github.com/docker/distribution mkdir -p src/github.com/docker/distribution mv tmp-digest src/github.com/docker/distribution/digest -clone git github.com/docker/libcontainer d00b8369852285d6a830a8d3b966608b2ed89705 +clone git github.com/docker/libcontainer bd8ec36106086f72b66e1be85a81202b93503e44 # see src/github.com/docker/libcontainer/update-vendor.sh which is the "source of truth" for libcontainer deps (just like this file) rm -rf src/github.com/docker/libcontainer/vendor eval "$(grep '^clone ' src/github.com/docker/libcontainer/update-vendor.sh | grep -v 'github.com/codegangsta/cli' | grep -v 'github.com/Sirupsen/logrus')" diff --git a/vendor/src/github.com/docker/libcontainer/.gitignore b/vendor/src/github.com/docker/libcontainer/.gitignore index 4c2914fc7c..bf6a664db6 100644 --- a/vendor/src/github.com/docker/libcontainer/.gitignore +++ b/vendor/src/github.com/docker/libcontainer/.gitignore @@ -1 +1,2 @@ +bundles nsinit/nsinit diff --git a/vendor/src/github.com/docker/libcontainer/Makefile b/vendor/src/github.com/docker/libcontainer/Makefile index c2c9a98d35..1a2e23e048 100644 --- a/vendor/src/github.com/docker/libcontainer/Makefile +++ b/vendor/src/github.com/docker/libcontainer/Makefile @@ -29,3 +29,5 @@ local: validate: hack/validate.sh +binary: all + docker run --rm --privileged -v $(CURDIR)/bundles:/go/bin dockercore/libcontainer make direct-install diff --git a/vendor/src/github.com/docker/libcontainer/README.md b/vendor/src/github.com/docker/libcontainer/README.md index 984f2c5238..6257f9c78f 100644 --- a/vendor/src/github.com/docker/libcontainer/README.md +++ b/vendor/src/github.com/docker/libcontainer/README.md @@ -141,6 +141,9 @@ container.Resume() It is able to spawn new containers or join existing containers. A root filesystem must be provided for use along with a container configuration file. +To build `nsinit`, run `make binary`. It will save the binary into +`bundles/nsinit`. + To use `nsinit`, cd into a Linux rootfs and copy a `container.json` file into the directory with your specified configuration. Environment, networking, and different capabilities for the container are specified in this file. diff --git a/vendor/src/github.com/docker/libcontainer/cgroups/systemd/apply_systemd.go b/vendor/src/github.com/docker/libcontainer/cgroups/systemd/apply_systemd.go index dea196bd06..3609bccae6 100644 --- a/vendor/src/github.com/docker/libcontainer/cgroups/systemd/apply_systemd.go +++ b/vendor/src/github.com/docker/libcontainer/cgroups/systemd/apply_systemd.go @@ -3,7 +3,6 @@ package systemd import ( - "bytes" "fmt" "io/ioutil" "os" @@ -247,6 +246,21 @@ func writeFile(dir, file, data string) error { return ioutil.WriteFile(filepath.Join(dir, file), []byte(data), 0700) } +func join(c *configs.Cgroup, subsystem string, pid int) (string, error) { + path, err := getSubsystemPath(c, subsystem) + if err != nil { + return "", err + } + if err := os.MkdirAll(path, 0755); err != nil && !os.IsExist(err) { + return "", err + } + if err := writeFile(path, "cgroup.procs", strconv.Itoa(pid)); err != nil { + return "", err + } + + return path, nil +} + func joinCpu(c *configs.Cgroup, pid int) error { path, err := getSubsystemPath(c, "cpu") if err != nil { @@ -266,16 +280,11 @@ func joinCpu(c *configs.Cgroup, pid int) error { } func joinFreezer(c *configs.Cgroup, pid int) error { - path, err := getSubsystemPath(c, "freezer") - if err != nil { + if _, err := join(c, "freezer", pid); err != nil { return err } - if err := os.MkdirAll(path, 0755); err != nil && !os.IsExist(err) { - return err - } - - return ioutil.WriteFile(filepath.Join(path, "cgroup.procs"), []byte(strconv.Itoa(pid)), 0700) + return nil } func getSubsystemPath(c *configs.Cgroup, subsystem string) (string, error) { @@ -303,21 +312,15 @@ func (m *Manager) Freeze(state configs.FreezerState) error { return err } - if err := ioutil.WriteFile(filepath.Join(path, "freezer.state"), []byte(state), 0); err != nil { + prevState := m.Cgroups.Freezer + m.Cgroups.Freezer = state + + freezer := subsystems["freezer"] + err = freezer.Set(path, m.Cgroups) + if err != nil { + m.Cgroups.Freezer = prevState return err } - for { - state_, err := ioutil.ReadFile(filepath.Join(path, "freezer.state")) - if err != nil { - return err - } - if string(state) == string(bytes.TrimSpace(state_)) { - break - } - time.Sleep(1 * time.Millisecond) - } - - m.Cgroups.Freezer = state return nil } @@ -366,29 +369,16 @@ func getUnitName(c *configs.Cgroup) string { // because systemd will re-write the device settings if it needs to re-apply the cgroup context. // This happens at least for v208 when any sibling unit is started. func joinDevices(c *configs.Cgroup, pid int) error { - path, err := getSubsystemPath(c, "devices") + path, err := join(c, "devices", pid) if err != nil { return err } - if err := os.MkdirAll(path, 0755); err != nil && !os.IsExist(err) { + devices := subsystems["devices"] + if err := devices.Set(path, c); err != nil { return err } - if err := ioutil.WriteFile(filepath.Join(path, "cgroup.procs"), []byte(strconv.Itoa(pid)), 0700); err != nil { - return err - } - - if !c.AllowAllDevices { - if err := writeFile(path, "devices.deny", "a"); err != nil { - return err - } - } - for _, dev := range c.AllowedDevices { - if err := writeFile(path, "devices.allow", dev.CgroupString()); err != nil { - return err - } - } return nil } diff --git a/vendor/src/github.com/docker/libcontainer/configs/namespaces.go b/vendor/src/github.com/docker/libcontainer/configs/namespaces.go index 9078e6abfc..ac6a7fa2cd 100644 --- a/vendor/src/github.com/docker/libcontainer/configs/namespaces.go +++ b/vendor/src/github.com/docker/libcontainer/configs/namespaces.go @@ -16,6 +16,17 @@ const ( NEWUSER NamespaceType = "NEWUSER" ) +func NamespaceTypes() []NamespaceType { + return []NamespaceType{ + NEWNET, + NEWPID, + NEWNS, + NEWUTS, + NEWIPC, + NEWUSER, + } +} + // Namespace defines configuration for each namespace. It specifies an // alternate path that is able to be joined via setns. type Namespace struct { diff --git a/vendor/src/github.com/docker/libcontainer/container_linux.go b/vendor/src/github.com/docker/libcontainer/container_linux.go index 3c077afbd9..d52610f073 100644 --- a/vendor/src/github.com/docker/libcontainer/container_linux.go +++ b/vendor/src/github.com/docker/libcontainer/container_linux.go @@ -306,5 +306,11 @@ func (c *linuxContainer) currentState() (*State, error) { for _, ns := range c.config.Namespaces { state.NamespacePaths[ns.Type] = ns.GetPath(c.initProcess.pid()) } + for _, nsType := range configs.NamespaceTypes() { + if _, ok := state.NamespacePaths[nsType]; !ok { + ns := configs.Namespace{Type: nsType} + state.NamespacePaths[ns.Type] = ns.GetPath(c.initProcess.pid()) + } + } return state, nil } diff --git a/vendor/src/github.com/docker/libcontainer/container_linux_test.go b/vendor/src/github.com/docker/libcontainer/container_linux_test.go index 5ee46ab140..b05733e585 100644 --- a/vendor/src/github.com/docker/libcontainer/container_linux_test.go +++ b/vendor/src/github.com/docker/libcontainer/container_linux_test.go @@ -130,7 +130,8 @@ func TestGetContainerState(t *testing.T) { {Type: configs.NEWNS}, {Type: configs.NEWNET, Path: expectedNetworkPath}, {Type: configs.NEWUTS}, - {Type: configs.NEWIPC}, + // emulate host for IPC + //{Type: configs.NEWIPC}, }, }, initProcess: &mockProcess{ diff --git a/vendor/src/github.com/docker/libcontainer/integration/exec_test.go b/vendor/src/github.com/docker/libcontainer/integration/exec_test.go index 4afff77ded..12457ba1a2 100644 --- a/vendor/src/github.com/docker/libcontainer/integration/exec_test.go +++ b/vendor/src/github.com/docker/libcontainer/integration/exec_test.go @@ -9,6 +9,7 @@ import ( "testing" "github.com/docker/libcontainer" + "github.com/docker/libcontainer/cgroups/systemd" "github.com/docker/libcontainer/configs" ) @@ -481,6 +482,17 @@ func TestProcessCaps(t *testing.T) { } func TestFreeze(t *testing.T) { + testFreeze(t, false) +} + +func TestSystemdFreeze(t *testing.T) { + if !systemd.UseSystemd() { + t.Skip("Systemd is unsupported") + } + testFreeze(t, true) +} + +func testFreeze(t *testing.T, systemd bool) { if testing.Short() { return } @@ -497,6 +509,9 @@ func TestFreeze(t *testing.T) { defer remove(rootfs) config := newTemplateConfig(rootfs) + if systemd { + config.Cgroups.Slice = "system.slice" + } factory, err := libcontainer.New(root, libcontainer.Cgroupfs) if err != nil { @@ -559,3 +574,77 @@ func TestFreeze(t *testing.T) { t.Fatal(s.String()) } } + +func TestContainerState(t *testing.T) { + if testing.Short() { + return + } + root, err := newTestRoot() + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(root) + + rootfs, err := newRootfs() + if err != nil { + t.Fatal(err) + } + defer remove(rootfs) + + l, err := os.Readlink("/proc/1/ns/ipc") + if err != nil { + t.Fatal(err) + } + + config := newTemplateConfig(rootfs) + config.Namespaces = configs.Namespaces([]configs.Namespace{ + {Type: configs.NEWNS}, + {Type: configs.NEWUTS}, + // host for IPC + //{Type: configs.NEWIPC}, + {Type: configs.NEWPID}, + {Type: configs.NEWNET}, + }) + + factory, err := libcontainer.New(root, libcontainer.Cgroupfs) + if err != nil { + t.Fatal(err) + } + + container, err := factory.Create("test", config) + if err != nil { + t.Fatal(err) + } + defer container.Destroy() + + stdinR, stdinW, err := os.Pipe() + if err != nil { + t.Fatal(err) + } + p := &libcontainer.Process{ + Args: []string{"cat"}, + Env: standardEnvironment, + Stdin: stdinR, + } + err = container.Start(p) + if err != nil { + t.Fatal(err) + } + stdinR.Close() + defer p.Signal(os.Kill) + + st, err := container.State() + if err != nil { + t.Fatal(err) + } + + l1, err := os.Readlink(st.NamespacePaths[configs.NEWIPC]) + if err != nil { + t.Fatal(err) + } + if l1 != l { + t.Fatal("Container using non-host ipc namespace") + } + stdinW.Close() + p.Wait() +} diff --git a/vendor/src/github.com/docker/libcontainer/integration/utils_test.go b/vendor/src/github.com/docker/libcontainer/integration/utils_test.go index c444eecfc5..cf4596864e 100644 --- a/vendor/src/github.com/docker/libcontainer/integration/utils_test.go +++ b/vendor/src/github.com/docker/libcontainer/integration/utils_test.go @@ -68,9 +68,14 @@ func copyBusybox(dest string) error { } func newContainer(config *configs.Config) (libcontainer.Container, error) { + cgm := libcontainer.Cgroupfs + if config.Cgroups != nil && config.Cgroups.Slice == "system.slice" { + cgm = libcontainer.SystemdCgroups + } + factory, err := libcontainer.New(".", libcontainer.InitArgs(os.Args[0], "init", "--"), - libcontainer.Cgroupfs, + cgm, ) if err != nil { return nil, err diff --git a/vendor/src/github.com/docker/libcontainer/nsinit/main.go b/vendor/src/github.com/docker/libcontainer/nsinit/main.go index 922d74ccbb..eec064c2c4 100644 --- a/vendor/src/github.com/docker/libcontainer/nsinit/main.go +++ b/vendor/src/github.com/docker/libcontainer/nsinit/main.go @@ -13,7 +13,7 @@ func main() { app.Version = "2" app.Author = "libcontainer maintainers" app.Flags = []cli.Flag{ - cli.StringFlag{Name: "root", Value: ".", Usage: "root directory for containers"}, + cli.StringFlag{Name: "root", Value: "/var/run/nsinit", Usage: "root directory for containers"}, cli.StringFlag{Name: "log-file", Value: "", Usage: "set the log file to output logs to"}, cli.BoolFlag{Name: "debug", Usage: "enable debug output in the logs"}, } From ce69dafe4d0424ddb5df53d9aee7728f0734cce7 Mon Sep 17 00:00:00 2001 From: Alexander Morozov Date: Tue, 7 Apr 2015 15:20:39 -0700 Subject: [PATCH 2/2] Test case for network mode chain container -> container -> host Issue #12130 Signed-off-by: Alexander Morozov --- integration-cli/docker_cli_run_test.go | 29 ++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/integration-cli/docker_cli_run_test.go b/integration-cli/docker_cli_run_test.go index ef7a9dee8f..1be512d533 100644 --- a/integration-cli/docker_cli_run_test.go +++ b/integration-cli/docker_cli_run_test.go @@ -3249,6 +3249,35 @@ func TestRunNetHost(t *testing.T) { logDone("run - net host mode") } +func TestRunNetContainerWhichHost(t *testing.T) { + testRequires(t, SameHostDaemon) + defer deleteAllContainers() + + hostNet, err := os.Readlink("/proc/1/ns/net") + if err != nil { + t.Fatal(err) + } + + cmd := exec.Command(dockerBinary, "run", "-d", "--net=host", "--name=test", "busybox", "top") + out, _, err := runCommandWithOutput(cmd) + if err != nil { + t.Fatal(err, out) + } + + cmd = exec.Command(dockerBinary, "run", "--net=container:test", "busybox", "readlink", "/proc/self/ns/net") + out, _, err = runCommandWithOutput(cmd) + if err != nil { + t.Fatal(err, out) + } + + out = strings.Trim(out, "\n") + if hostNet != out { + t.Fatalf("Container should have host network namespace") + } + + logDone("run - net container mode, where container in host mode") +} + func TestRunAllowPortRangeThroughPublish(t *testing.T) { defer deleteAllContainers()