From 2f1b2ce204490854938fab57142b557caa4ab66d Mon Sep 17 00:00:00 2001 From: "Daniel, Dao Quang Minh" Date: Wed, 10 Dec 2014 03:39:00 -0500 Subject: [PATCH] add support for testing execin when the test binary starts, it detects whether it should run the container initialization code or the execin initialization code based on the suppplied arguments. The execin initialization code is taken from docker. also added a sample test for execin process. Docker-DCO-1.1-Signed-off-by: Daniel, Dao Quang Minh (github: dqminh) --- integration/execin_test.go | 83 ++++++++++++++++++++++++++++++++++++++ integration/init_test.go | 67 ++++++++++++++++++++++++------ 2 files changed, 138 insertions(+), 12 deletions(-) create mode 100644 integration/execin_test.go diff --git a/integration/execin_test.go b/integration/execin_test.go new file mode 100644 index 00000000..cf8f548e --- /dev/null +++ b/integration/execin_test.go @@ -0,0 +1,83 @@ +package integration + +import ( + "os" + "os/exec" + "strings" + "sync" + "testing" + + "github.com/docker/libcontainer" + "github.com/docker/libcontainer/namespaces" +) + +func TestExecIn(t *testing.T) { + if testing.Short() { + return + } + + rootfs, err := newRootFs() + if err != nil { + t.Fatal(err) + } + defer remove(rootfs) + + config := newTemplateConfig(rootfs) + if err := writeConfig(config); err != nil { + t.Fatalf("failed to write config %s", err) + } + + // start the container + containerErr := make(chan error, 1) + containerCmd := &exec.Cmd{} + var statePath string + createCmd := func(container *libcontainer.Config, console, dataPath, init string, + pipe *os.File, args []string) *exec.Cmd { + containerCmd = namespaces.DefaultCreateCommand(container, console, dataPath, init, pipe, args) + statePath = dataPath + return containerCmd + } + var containerStart sync.WaitGroup + containerStart.Add(1) + go func() { + buffers := newStdBuffers() + _, err := namespaces.Exec(config, + buffers.Stdin, buffers.Stdout, buffers.Stderr, + "", config.RootFs, []string{"sleep", "10"}, + createCmd, containerStart.Done) + containerErr <- err + }() + containerStart.Wait() + + defer func() { + // kill the container + if containerCmd.Process != nil { + containerCmd.Process.Kill() + } + if err := <-containerErr; err != nil { + t.Fatal(err) + } + }() + + // start the exec process + state, err := libcontainer.GetState(statePath) + if err != nil { + t.Fatalf("failed to get state %s", err) + } + buffers := newStdBuffers() + execErr := make(chan error, 1) + go func() { + _, err := namespaces.ExecIn(config, state, []string{"ps"}, + os.Args[0], "exec", buffers.Stdin, buffers.Stdout, buffers.Stderr, + "", nil) + execErr <- err + }() + if err := <-execErr; err != nil { + t.Fatalf("exec finished with error %s", err) + } + + out := buffers.Stdout.String() + if !strings.Contains(out, "sleep 10") || !strings.Contains(out, "ps") { + t.Fatalf("unexpected running process, output %q", out) + } +} diff --git a/integration/init_test.go b/integration/init_test.go index 9954c0f8..3106a5fb 100644 --- a/integration/init_test.go +++ b/integration/init_test.go @@ -1,33 +1,76 @@ package integration import ( + "encoding/json" "log" "os" "runtime" + "github.com/docker/libcontainer" "github.com/docker/libcontainer/namespaces" + _ "github.com/docker/libcontainer/namespaces/nsenter" ) // init runs the libcontainer initialization code because of the busybox style needs // to work around the go runtime and the issues with forking func init() { - if len(os.Args) < 2 || os.Args[1] != "init" { + if len(os.Args) < 2 { return } - runtime.LockOSThread() + // handle init + if len(os.Args) >= 2 && os.Args[1] == "init" { + runtime.LockOSThread() - container, err := loadConfig() - if err != nil { - log.Fatal(err) + container, err := loadConfig() + if err != nil { + log.Fatal(err) + } + + rootfs, err := os.Getwd() + if err != nil { + log.Fatal(err) + } + + if err := namespaces.Init(container, rootfs, "", os.NewFile(3, "pipe"), os.Args[3:]); err != nil { + log.Fatalf("unable to initialize for container: %s", err) + } + os.Exit(1) } - rootfs, err := os.Getwd() - if err != nil { - log.Fatal(err) - } + // handle execin + if len(os.Args) >= 2 && os.Args[0] == "nsenter-exec" { + runtime.LockOSThread() - if err := namespaces.Init(container, rootfs, "", os.NewFile(3, "pipe"), os.Args[3:]); err != nil { - log.Fatalf("unable to initialize for container: %s", err) + // User args are passed after '--' in the command line. + userArgs := findUserArgs() + + config, err := loadConfigFromFd() + if err != nil { + log.Fatalf("docker-exec: unable to receive config from sync pipe: %s", err) + } + + if err := namespaces.FinalizeSetns(config, userArgs); err != nil { + log.Fatalf("docker-exec: failed to exec: %s", err) + } + os.Exit(1) } - os.Exit(1) +} + +func findUserArgs() []string { + for i, a := range os.Args { + if a == "--" { + return os.Args[i+1:] + } + } + return []string{} +} + +// loadConfigFromFd loads a container's config from the sync pipe that is provided by +// fd 3 when running a process +func loadConfigFromFd() (*libcontainer.Config, error) { + var config *libcontainer.Config + if err := json.NewDecoder(os.NewFile(3, "child")).Decode(&config); err != nil { + return nil, err + } + return config, nil }