From 7038ddbc8c8c21491d1a88a25b120505997c4a36 Mon Sep 17 00:00:00 2001 From: Andrey Vagin Date: Wed, 17 Dec 2014 12:12:23 +0300 Subject: [PATCH 1/8] libcontainer: move Config in a separate package We are going to import the namespaces package into libcontainer, so libcontainer should not be imported into namespaces. Signed-off-by: Andrey Vagin --- config.go => configs/config.go | 2 +- config_test.go => configs/config_test.go | 4 ++-- container.go | 6 +++++- factory.go | 6 +++++- integration/exec_test.go | 4 ++-- integration/template_test.go | 14 +++++++------- integration/utils_test.go | 10 +++++----- linux_container.go | 5 +++-- linux_container_test.go | 5 +++-- linux_factory.go | 8 +++++--- linux_factory_test.go | 4 +++- namespaces/create.go | 4 ++-- namespaces/exec.go | 9 +++++---- namespaces/execin.go | 5 +++-- namespaces/init.go | 16 ++++++++-------- namespaces/utils.go | 4 ++-- nsinit/exec.go | 9 +++++---- nsinit/nsenter.go | 8 ++++---- nsinit/utils.go | 12 ++++++------ 19 files changed, 76 insertions(+), 59 deletions(-) rename config.go => configs/config.go (99%) rename config_test.go => configs/config_test.go (97%) diff --git a/config.go b/configs/config.go similarity index 99% rename from config.go rename to configs/config.go index 94c2bd98..ab40b2b4 100644 --- a/config.go +++ b/configs/config.go @@ -1,4 +1,4 @@ -package libcontainer +package configs import ( "github.com/docker/libcontainer/cgroups" diff --git a/config_test.go b/configs/config_test.go similarity index 97% rename from config_test.go rename to configs/config_test.go index b4e16bf0..f698e3d9 100644 --- a/config_test.go +++ b/configs/config_test.go @@ -1,4 +1,4 @@ -package libcontainer +package configs import ( "encoding/json" @@ -34,7 +34,7 @@ func containsDevice(expected *devices.Device, values []*devices.Device) bool { } func loadConfig(name string) (*Config, error) { - f, err := os.Open(filepath.Join("sample_configs", name)) + f, err := os.Open(filepath.Join("../sample_configs", name)) if err != nil { return nil, err } diff --git a/container.go b/container.go index e5376902..c0bafa5d 100644 --- a/container.go +++ b/container.go @@ -3,6 +3,10 @@ NOTE: The API is in flux and mainly not implemented. Proceed with caution until */ package libcontainer +import ( + "github.com/docker/libcontainer/configs" +) + // A libcontainer container object. // // Each container is thread-safe within the same process. Since a container can @@ -20,7 +24,7 @@ type Container interface { RunState() (RunState, error) // Returns the current config of the container. - Config() *Config + Config() *configs.Config // Returns the PIDs inside this container. The PIDs are in the namespace of the calling process. // diff --git a/factory.go b/factory.go index 4959ff1e..37e62964 100644 --- a/factory.go +++ b/factory.go @@ -1,5 +1,9 @@ package libcontainer +import ( + "github.com/docker/libcontainer/configs" +) + type Factory interface { // Creates a new container with the given id and starts the initial process inside it. // id must be a string containing only letters, digits and underscores and must contain @@ -17,7 +21,7 @@ type Factory interface { // Systemerror - System error // // On error, any partially created container parts are cleaned up (the operation is atomic). - Create(id string, config *Config) (Container, error) + Create(id string, config *configs.Config) (Container, error) // Load takes an ID for an existing container and returns the container information // from the state. This presents a read only view of the container. diff --git a/integration/exec_test.go b/integration/exec_test.go index cf749efb..993ca25c 100644 --- a/integration/exec_test.go +++ b/integration/exec_test.go @@ -5,7 +5,7 @@ import ( "strings" "testing" - "github.com/docker/libcontainer" + "github.com/docker/libcontainer/configs" ) func TestExecPS(t *testing.T) { @@ -180,7 +180,7 @@ func TestRlimit(t *testing.T) { } } -func getNamespaceIndex(config *libcontainer.Config, name string) int { +func getNamespaceIndex(config *configs.Config, name string) int { for i, v := range config.Namespaces { if v.Name == name { return i diff --git a/integration/template_test.go b/integration/template_test.go index f37070ff..d58bb613 100644 --- a/integration/template_test.go +++ b/integration/template_test.go @@ -3,8 +3,8 @@ package integration import ( "syscall" - "github.com/docker/libcontainer" "github.com/docker/libcontainer/cgroups" + "github.com/docker/libcontainer/configs" "github.com/docker/libcontainer/devices" ) @@ -12,8 +12,8 @@ import ( // // it uses a network strategy of just setting a loopback interface // and the default setup for devices -func newTemplateConfig(rootfs string) *libcontainer.Config { - return &libcontainer.Config{ +func newTemplateConfig(rootfs string) *configs.Config { + return &configs.Config{ RootFs: rootfs, Tty: false, Capabilities: []string{ @@ -32,7 +32,7 @@ func newTemplateConfig(rootfs string) *libcontainer.Config { "KILL", "AUDIT_WRITE", }, - Namespaces: []libcontainer.Namespace{ + Namespaces: []configs.Namespace{ {Name: "NEWNS"}, {Name: "NEWUTS"}, {Name: "NEWIPC"}, @@ -45,7 +45,7 @@ func newTemplateConfig(rootfs string) *libcontainer.Config { AllowedDevices: devices.DefaultAllowedDevices, }, - MountConfig: &libcontainer.MountConfig{ + MountConfig: &configs.MountConfig{ DeviceNodes: devices.DefaultAutoCreatedDevices, }, Hostname: "integration", @@ -55,14 +55,14 @@ func newTemplateConfig(rootfs string) *libcontainer.Config { "HOSTNAME=integration", "TERM=xterm", }, - Networks: []*libcontainer.Network{ + Networks: []*configs.Network{ { Type: "loopback", Address: "127.0.0.1/0", Gateway: "localhost", }, }, - Rlimits: []libcontainer.Rlimit{ + Rlimits: []configs.Rlimit{ { Type: syscall.RLIMIT_NOFILE, Hard: uint64(1024), diff --git a/integration/utils_test.go b/integration/utils_test.go index 6393fb99..051252fc 100644 --- a/integration/utils_test.go +++ b/integration/utils_test.go @@ -9,7 +9,7 @@ import ( "os/exec" "path/filepath" - "github.com/docker/libcontainer" + "github.com/docker/libcontainer/configs" "github.com/docker/libcontainer/namespaces" ) @@ -27,7 +27,7 @@ type stdBuffers struct { Stderr *bytes.Buffer } -func writeConfig(config *libcontainer.Config) error { +func writeConfig(config *configs.Config) error { f, err := os.OpenFile(filepath.Join(config.RootFs, "container.json"), os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0700) if err != nil { return err @@ -36,14 +36,14 @@ func writeConfig(config *libcontainer.Config) error { return json.NewEncoder(f).Encode(config) } -func loadConfig() (*libcontainer.Config, error) { +func loadConfig() (*configs.Config, error) { f, err := os.Open(filepath.Join(os.Getenv("data_path"), "container.json")) if err != nil { return nil, err } defer f.Close() - var container *libcontainer.Config + var container *configs.Config if err := json.NewDecoder(f).Decode(&container); err != nil { return nil, err } @@ -83,7 +83,7 @@ func copyBusybox(dest string) error { // // buffers are returned containing the STDOUT and STDERR output for the run // along with the exit code and any go error -func runContainer(config *libcontainer.Config, console string, args ...string) (buffers *stdBuffers, exitCode int, err error) { +func runContainer(config *configs.Config, console string, args ...string) (buffers *stdBuffers, exitCode int, err error) { if err := writeConfig(config); err != nil { return nil, -1, err } diff --git a/linux_container.go b/linux_container.go index 10d66bfe..d733cb79 100644 --- a/linux_container.go +++ b/linux_container.go @@ -10,6 +10,7 @@ import ( "path/filepath" "syscall" + "github.com/docker/libcontainer/configs" "github.com/docker/libcontainer/network" "github.com/golang/glog" ) @@ -17,7 +18,7 @@ import ( type linuxContainer struct { id string root string - config *Config + config *configs.Config state *State cgroupManager CgroupManager initArgs []string @@ -27,7 +28,7 @@ func (c *linuxContainer) ID() string { return c.id } -func (c *linuxContainer) Config() *Config { +func (c *linuxContainer) Config() *configs.Config { return c.config } diff --git a/linux_container_test.go b/linux_container_test.go index cd8d33d0..6771a824 100644 --- a/linux_container_test.go +++ b/linux_container_test.go @@ -6,6 +6,7 @@ import ( "testing" "github.com/docker/libcontainer/cgroups" + "github.com/docker/libcontainer/configs" ) type mockCgroupManager struct { @@ -24,7 +25,7 @@ func (m *mockCgroupManager) GetStats() (*cgroups.Stats, error) { func TestGetContainerPids(t *testing.T) { container := &linuxContainer{ id: "myid", - config: &Config{}, + config: &configs.Config{}, cgroupManager: &mockCgroupManager{pids: []int{1, 2, 3}}, } @@ -43,7 +44,7 @@ func TestGetContainerPids(t *testing.T) { func TestGetContainerStats(t *testing.T) { container := &linuxContainer{ id: "myid", - config: &Config{}, + config: &configs.Config{}, cgroupManager: &mockCgroupManager{ pids: []int{1, 2, 3}, stats: &cgroups.Stats{ diff --git a/linux_factory.go b/linux_factory.go index 30c63566..5d5d097c 100644 --- a/linux_factory.go +++ b/linux_factory.go @@ -10,6 +10,8 @@ import ( "regexp" "github.com/golang/glog" + + "github.com/docker/libcontainer/configs" ) const ( @@ -43,7 +45,7 @@ type linuxFactory struct { initArgs []string } -func (l *linuxFactory) Create(id string, config *Config) (Container, error) { +func (l *linuxFactory) Create(id string, config *configs.Config) (Container, error) { if l.root == "" { return nil, newGenericError(fmt.Errorf("invalid root"), ConfigInvalid) } @@ -125,7 +127,7 @@ func (l *linuxFactory) Load(id string) (Container, error) { }, nil } -func (l *linuxFactory) loadContainerConfig(root string) (*Config, error) { +func (l *linuxFactory) loadContainerConfig(root string) (*configs.Config, error) { f, err := os.Open(filepath.Join(root, configFilename)) if err != nil { if os.IsNotExist(err) { @@ -135,7 +137,7 @@ func (l *linuxFactory) loadContainerConfig(root string) (*Config, error) { } defer f.Close() - var config *Config + var config *configs.Config if err := json.NewDecoder(f).Decode(&config); err != nil { return nil, newGenericError(err, ConfigInvalid) } diff --git a/linux_factory_test.go b/linux_factory_test.go index 168be1be..51986d39 100644 --- a/linux_factory_test.go +++ b/linux_factory_test.go @@ -8,6 +8,8 @@ import ( "os" "path/filepath" "testing" + + "github.com/docker/libcontainer/configs" ) func newTestRoot() (string, error) { @@ -83,7 +85,7 @@ func TestFactoryLoadContainer(t *testing.T) { // setup default container config and state for mocking var ( id = "1" - expectedConfig = &Config{ + expectedConfig = &configs.Config{ RootFs: "/mycontainer/root", } expectedState = &State{ diff --git a/namespaces/create.go b/namespaces/create.go index b6418b6e..30de84ce 100644 --- a/namespaces/create.go +++ b/namespaces/create.go @@ -4,7 +4,7 @@ import ( "os" "os/exec" - "github.com/docker/libcontainer" + "github.com/docker/libcontainer/configs" ) -type CreateCommand func(container *libcontainer.Config, console, dataPath, init string, childPipe *os.File, args []string) *exec.Cmd +type CreateCommand func(container *configs.Config, console, dataPath, init string, childPipe *os.File, args []string) *exec.Cmd diff --git a/namespaces/exec.go b/namespaces/exec.go index b7873edd..63ade916 100644 --- a/namespaces/exec.go +++ b/namespaces/exec.go @@ -13,6 +13,7 @@ import ( "github.com/docker/libcontainer/cgroups" "github.com/docker/libcontainer/cgroups/fs" "github.com/docker/libcontainer/cgroups/systemd" + "github.com/docker/libcontainer/configs" "github.com/docker/libcontainer/network" "github.com/docker/libcontainer/system" ) @@ -21,7 +22,7 @@ import ( // Move this to libcontainer package. // Exec performs setup outside of a namespace so that a container can be // executed. Exec is a high level function for working with container namespaces. -func Exec(container *libcontainer.Config, stdin io.Reader, stdout, stderr io.Writer, console, dataPath string, args []string, createCommand CreateCommand, startCallback func()) (int, error) { +func Exec(container *configs.Config, stdin io.Reader, stdout, stderr io.Writer, console, dataPath string, args []string, createCommand CreateCommand, startCallback func()) (int, error) { var err error // create a pipe so that we can syncronize with the namespaced process and @@ -122,7 +123,7 @@ func Exec(container *libcontainer.Config, stdin io.Reader, stdout, stderr io.Wri // root: the path to the container json file and information // pipe: sync pipe to synchronize the parent and child processes // args: the arguments to pass to the container to run as the user's program -func DefaultCreateCommand(container *libcontainer.Config, console, dataPath, init string, pipe *os.File, args []string) *exec.Cmd { +func DefaultCreateCommand(container *configs.Config, console, dataPath, init string, pipe *os.File, args []string) *exec.Cmd { // get our binary name from arg0 so we can always reexec ourself env := []string{ "console=" + console, @@ -148,7 +149,7 @@ func DefaultCreateCommand(container *libcontainer.Config, console, dataPath, ini // SetupCgroups applies the cgroup restrictions to the process running in the container based // on the container's configuration -func SetupCgroups(container *libcontainer.Config, nspid int) (map[string]string, error) { +func SetupCgroups(container *configs.Config, nspid int) (map[string]string, error) { if container.Cgroups != nil { c := container.Cgroups if systemd.UseSystemd() { @@ -161,7 +162,7 @@ func SetupCgroups(container *libcontainer.Config, nspid int) (map[string]string, // InitializeNetworking creates the container's network stack outside of the namespace and moves // interfaces into the container's net namespaces if necessary -func InitializeNetworking(container *libcontainer.Config, nspid int, networkState *network.NetworkState) error { +func InitializeNetworking(container *configs.Config, nspid int, networkState *network.NetworkState) error { for _, config := range container.Networks { strategy, err := network.GetStrategy(config.Type) if err != nil { diff --git a/namespaces/execin.go b/namespaces/execin.go index 430dc72f..8b642fd3 100644 --- a/namespaces/execin.go +++ b/namespaces/execin.go @@ -15,13 +15,14 @@ import ( "github.com/docker/libcontainer" "github.com/docker/libcontainer/apparmor" "github.com/docker/libcontainer/cgroups" + "github.com/docker/libcontainer/configs" "github.com/docker/libcontainer/label" "github.com/docker/libcontainer/system" ) // ExecIn reexec's the initPath with the argv 0 rewrite to "nsenter" so that it is able to run the // setns code in a single threaded environment joining the existing containers' namespaces. -func ExecIn(container *libcontainer.Config, state *libcontainer.State, userArgs []string, initPath, action string, +func ExecIn(container *configs.Config, state *libcontainer.State, userArgs []string, initPath, action string, stdin io.Reader, stdout, stderr io.Writer, console string, startCallback func(*exec.Cmd)) (int, error) { args := []string{fmt.Sprintf("nsenter-%s", action), "--nspid", strconv.Itoa(state.InitPid)} @@ -91,7 +92,7 @@ func ExecIn(container *libcontainer.Config, state *libcontainer.State, userArgs // Finalize expects that the setns calls have been setup and that is has joined an // existing namespace -func FinalizeSetns(container *libcontainer.Config, args []string) error { +func FinalizeSetns(container *configs.Config, args []string) error { // clear the current processes env and replace it with the environment defined on the container if err := LoadContainerEnvironment(container); err != nil { return err diff --git a/namespaces/init.go b/namespaces/init.go index 5c7e1a71..a6d49f61 100644 --- a/namespaces/init.go +++ b/namespaces/init.go @@ -10,8 +10,8 @@ import ( "strings" "syscall" - "github.com/docker/libcontainer" "github.com/docker/libcontainer/apparmor" + "github.com/docker/libcontainer/configs" "github.com/docker/libcontainer/console" "github.com/docker/libcontainer/label" "github.com/docker/libcontainer/mount" @@ -30,7 +30,7 @@ import ( // and other options required for the new container. // The caller of Init function has to ensure that the go runtime is locked to an OS thread // (using runtime.LockOSThread) else system calls like setns called within Init may not work as intended. -func Init(container *libcontainer.Config, uncleanRootfs, consolePath string, pipe *os.File, args []string) (err error) { +func Init(container *configs.Config, uncleanRootfs, consolePath string, pipe *os.File, args []string) (err error) { defer func() { // if we have an error during the initialization of the container's init then send it back to the // parent process in the form of an initError. @@ -218,7 +218,7 @@ func SetupUser(u string) error { // setupVethNetwork uses the Network config if it is not nil to initialize // the new veth interface inside the container for use by changing the name to eth0 // setting the MTU and IP address along with the default gateway -func setupNetwork(container *libcontainer.Config, networkState *network.NetworkState) error { +func setupNetwork(container *configs.Config, networkState *network.NetworkState) error { for _, config := range container.Networks { strategy, err := network.GetStrategy(config.Type) if err != nil { @@ -233,7 +233,7 @@ func setupNetwork(container *libcontainer.Config, networkState *network.NetworkS return nil } -func setupRoute(container *libcontainer.Config) error { +func setupRoute(container *configs.Config) error { for _, config := range container.Routes { if err := netlink.AddRoute(config.Destination, config.Source, config.Gateway, config.InterfaceName); err != nil { return err @@ -242,7 +242,7 @@ func setupRoute(container *libcontainer.Config) error { return nil } -func setupRlimits(container *libcontainer.Config) error { +func setupRlimits(container *configs.Config) error { for _, rlimit := range container.Rlimits { l := &syscall.Rlimit{Max: rlimit.Hard, Cur: rlimit.Soft} if err := syscall.Setrlimit(rlimit.Type, l); err != nil { @@ -255,7 +255,7 @@ func setupRlimits(container *libcontainer.Config) error { // FinalizeNamespace drops the caps, sets the correct user // and working dir, and closes any leaky file descriptors // before execing the command inside the namespace -func FinalizeNamespace(container *libcontainer.Config) error { +func FinalizeNamespace(container *configs.Config) error { // Ensure that all non-standard fds we may have accidentally // inherited are marked close-on-exec so they stay out of the // container @@ -295,7 +295,7 @@ func FinalizeNamespace(container *libcontainer.Config) error { return nil } -func LoadContainerEnvironment(container *libcontainer.Config) error { +func LoadContainerEnvironment(container *configs.Config) error { os.Clearenv() for _, pair := range container.Env { p := strings.SplitN(pair, "=", 2) @@ -311,7 +311,7 @@ func LoadContainerEnvironment(container *libcontainer.Config) error { // joinExistingNamespaces gets all the namespace paths specified for the container and // does a setns on the namespace fd so that the current process joins the namespace. -func joinExistingNamespaces(namespaces []libcontainer.Namespace) error { +func joinExistingNamespaces(namespaces []configs.Namespace) error { for _, ns := range namespaces { if ns.Path != "" { f, err := os.OpenFile(ns.Path, os.O_RDONLY, 0) diff --git a/namespaces/utils.go b/namespaces/utils.go index 556ea669..4aa590fd 100644 --- a/namespaces/utils.go +++ b/namespaces/utils.go @@ -6,7 +6,7 @@ import ( "os" "syscall" - "github.com/docker/libcontainer" + "github.com/docker/libcontainer/configs" ) type initError struct { @@ -37,7 +37,7 @@ func newInitPipe() (parent *os.File, child *os.File, err error) { // GetNamespaceFlags parses the container's Namespaces options to set the correct // flags on clone, unshare, and setns -func GetNamespaceFlags(namespaces []libcontainer.Namespace) (flag int) { +func GetNamespaceFlags(namespaces []configs.Namespace) (flag int) { for _, v := range namespaces { flag |= namespaceInfo[v.Name] } diff --git a/nsinit/exec.go b/nsinit/exec.go index 4c7d6316..a5fd29cb 100644 --- a/nsinit/exec.go +++ b/nsinit/exec.go @@ -14,6 +14,7 @@ import ( "github.com/codegangsta/cli" "github.com/docker/docker/pkg/term" "github.com/docker/libcontainer" + "github.com/docker/libcontainer/configs" consolepkg "github.com/docker/libcontainer/console" "github.com/docker/libcontainer/namespaces" ) @@ -66,7 +67,7 @@ func execAction(context *cli.Context) { id := fmt.Sprintf("%x", md5.Sum([]byte(dataPath))) container, err := factory.Load(id) if err != nil && !os.IsNotExist(err) { - var config *libcontainer.Config + var config *configs.Config config, err = loadConfig() if err != nil { @@ -110,7 +111,7 @@ func execAction(context *cli.Context) { // with the nsenter argument so that the C code can setns an the namespaces that we require. Then that // code path will drop us into the path that we can do the final setup of the namespace and exec the users // application. -func startInExistingContainer(config *libcontainer.Config, state *libcontainer.State, action string, context *cli.Context) (int, error) { +func startInExistingContainer(config *configs.Config, state *libcontainer.State, action string, context *cli.Context) (int, error) { var ( master *os.File console string @@ -167,7 +168,7 @@ func startInExistingContainer(config *libcontainer.Config, state *libcontainer.S // error. // // Signals sent to the current process will be forwarded to container. -func startContainer(container *libcontainer.Config, dataPath string, args []string) (int, error) { +func startContainer(container *configs.Config, dataPath string, args []string) (int, error) { var ( cmd *exec.Cmd sigc = make(chan os.Signal, 10) @@ -175,7 +176,7 @@ func startContainer(container *libcontainer.Config, dataPath string, args []stri signal.Notify(sigc) - createCommand := func(container *libcontainer.Config, console, dataPath, init string, pipe *os.File, args []string) *exec.Cmd { + createCommand := func(container *configs.Config, console, dataPath, init string, pipe *os.File, args []string) *exec.Cmd { cmd = namespaces.DefaultCreateCommand(container, console, dataPath, init, pipe, args) if logPath != "" { cmd.Env = append(cmd.Env, fmt.Sprintf("log=%s", logPath)) diff --git a/nsinit/nsenter.go b/nsinit/nsenter.go index 8dc149f4..8365215e 100644 --- a/nsinit/nsenter.go +++ b/nsinit/nsenter.go @@ -9,7 +9,7 @@ import ( "strings" "text/tabwriter" - "github.com/docker/libcontainer" + "github.com/docker/libcontainer/configs" "github.com/docker/libcontainer/devices" "github.com/docker/libcontainer/mount/nodes" "github.com/docker/libcontainer/namespaces" @@ -17,7 +17,7 @@ import ( ) // nsenterExec exec's a process inside an existing container -func nsenterExec(config *libcontainer.Config, args []string) { +func nsenterExec(config *configs.Config, args []string) { if err := namespaces.FinalizeSetns(config, args); err != nil { log.Fatalf("failed to nsenter: %s", err) } @@ -26,7 +26,7 @@ func nsenterExec(config *libcontainer.Config, args []string) { // nsenterMknod runs mknod inside an existing container // // mknod -func nsenterMknod(config *libcontainer.Config, args []string) { +func nsenterMknod(config *configs.Config, args []string) { if len(args) != 4 { log.Fatalf("expected mknod to have 4 arguments not %d", len(args)) } @@ -56,7 +56,7 @@ func nsenterMknod(config *libcontainer.Config, args []string) { } // nsenterIp displays the network interfaces inside a container's net namespace -func nsenterIp(config *libcontainer.Config, args []string) { +func nsenterIp(config *configs.Config, args []string) { interfaces, err := net.Interfaces() if err != nil { log.Fatal(err) diff --git a/nsinit/utils.go b/nsinit/utils.go index 6a8aafbf..e02a1b3a 100644 --- a/nsinit/utils.go +++ b/nsinit/utils.go @@ -7,23 +7,23 @@ import ( "path/filepath" "github.com/codegangsta/cli" - "github.com/docker/libcontainer" + "github.com/docker/libcontainer/configs" ) // rFunc is a function registration for calling after an execin type rFunc struct { Usage string - Action func(*libcontainer.Config, []string) + Action func(*configs.Config, []string) } -func loadConfig() (*libcontainer.Config, error) { +func loadConfig() (*configs.Config, error) { f, err := os.Open(filepath.Join(dataPath, "container.json")) if err != nil { return nil, err } defer f.Close() - var container *libcontainer.Config + var container *configs.Config if err := json.NewDecoder(f).Decode(&container); err != nil { return nil, err } @@ -57,11 +57,11 @@ func findUserArgs() []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) { +func loadConfigFromFd() (*configs.Config, error) { pipe := os.NewFile(3, "pipe") defer pipe.Close() - var config *libcontainer.Config + var config *configs.Config if err := json.NewDecoder(pipe).Decode(&config); err != nil { return nil, err } From ce9d63376fea897f98c127f0cd8da528be4c6c23 Mon Sep 17 00:00:00 2001 From: Andrey Vagin Date: Wed, 17 Dec 2014 12:30:52 +0300 Subject: [PATCH 2/8] libcontainer: move State in the configs package We are going to import the namespaces package into libcontainer, so libcontainer should not be imported into namespaces. Signed-off-by: Andrey Vagin --- state.go => configs/state.go | 2 +- container.go | 2 +- linux_container.go | 14 +++++++------- linux_container_test.go | 2 +- linux_factory.go | 6 +++--- linux_factory_test.go | 2 +- namespaces/exec.go | 7 +++---- namespaces/execin.go | 5 ++--- nsinit/exec.go | 2 +- 9 files changed, 20 insertions(+), 22 deletions(-) rename state.go => configs/state.go (98%) diff --git a/state.go b/configs/state.go similarity index 98% rename from state.go rename to configs/state.go index 4ab47ad7..9dc77006 100644 --- a/state.go +++ b/configs/state.go @@ -1,4 +1,4 @@ -package libcontainer +package configs import ( "encoding/json" diff --git a/container.go b/container.go index c0bafa5d..e04a43df 100644 --- a/container.go +++ b/container.go @@ -21,7 +21,7 @@ type Container interface { // errors: // ContainerDestroyed - Container no longer exists, // Systemerror - System error. - RunState() (RunState, error) + RunState() (configs.RunState, error) // Returns the current config of the container. Config() *configs.Config diff --git a/linux_container.go b/linux_container.go index d733cb79..99cea633 100644 --- a/linux_container.go +++ b/linux_container.go @@ -19,7 +19,7 @@ type linuxContainer struct { id string root string config *configs.Config - state *State + state *configs.State cgroupManager CgroupManager initArgs []string } @@ -32,8 +32,8 @@ func (c *linuxContainer) Config() *configs.Config { return c.config } -func (c *linuxContainer) RunState() (RunState, error) { - return Destroyed, nil // FIXME return a real state +func (c *linuxContainer) RunState() (configs.RunState, error) { + return configs.Destroyed, nil // FIXME return a real state } func (c *linuxContainer) Processes() ([]int, error) { @@ -61,18 +61,18 @@ func (c *linuxContainer) Stats() (*ContainerStats, error) { return stats, nil } -func (c *linuxContainer) StartProcess(config *ProcessConfig) (int, error) { +func (c *linuxContainer) StartProcess(pconfig *ProcessConfig) (int, error) { state, err := c.RunState() if err != nil { return -1, err } - if state != Destroyed { + if state != configs.Destroyed { glog.Info("start new container process") panic("not implemented") } - if err := c.startInitProcess(config); err != nil { + if err := c.startInitProcess(pconfig); err != nil { return -1, err } @@ -141,7 +141,7 @@ func (c *linuxContainer) Destroy() error { return err } - if state != Destroyed { + if state != configs.Destroyed { return newGenericError(nil, ContainerNotStopped) } diff --git a/linux_container_test.go b/linux_container_test.go index 6771a824..64d4fb8b 100644 --- a/linux_container_test.go +++ b/linux_container_test.go @@ -53,7 +53,7 @@ func TestGetContainerStats(t *testing.T) { }, }, }, - state: &State{}, + state: &configs.State{}, } stats, err := container.Stats() diff --git a/linux_factory.go b/linux_factory.go index 5d5d097c..5dc679bd 100644 --- a/linux_factory.go +++ b/linux_factory.go @@ -93,7 +93,7 @@ func (l *linuxFactory) Create(id string, config *configs.Config) (Container, err root: containerRoot, config: config, initArgs: l.initArgs, - state: &State{}, + state: &configs.State{}, cgroupManager: cgroupManager, }, nil } @@ -144,7 +144,7 @@ func (l *linuxFactory) loadContainerConfig(root string) (*configs.Config, error) return config, nil } -func (l *linuxFactory) loadContainerState(root string) (*State, error) { +func (l *linuxFactory) loadContainerState(root string) (*configs.State, error) { f, err := os.Open(filepath.Join(root, stateFilename)) if err != nil { if os.IsNotExist(err) { @@ -154,7 +154,7 @@ func (l *linuxFactory) loadContainerState(root string) (*State, error) { } defer f.Close() - var state *State + var state *configs.State if err := json.NewDecoder(f).Decode(&state); err != nil { return nil, newGenericError(err, SystemError) } diff --git a/linux_factory_test.go b/linux_factory_test.go index 51986d39..3c1e275c 100644 --- a/linux_factory_test.go +++ b/linux_factory_test.go @@ -88,7 +88,7 @@ func TestFactoryLoadContainer(t *testing.T) { expectedConfig = &configs.Config{ RootFs: "/mycontainer/root", } - expectedState = &State{ + expectedState = &configs.State{ InitPid: 1024, } ) diff --git a/namespaces/exec.go b/namespaces/exec.go index 63ade916..32b9ab12 100644 --- a/namespaces/exec.go +++ b/namespaces/exec.go @@ -9,7 +9,6 @@ import ( "os/exec" "syscall" - "github.com/docker/libcontainer" "github.com/docker/libcontainer/cgroups" "github.com/docker/libcontainer/cgroups/fs" "github.com/docker/libcontainer/cgroups/systemd" @@ -80,17 +79,17 @@ func Exec(container *configs.Config, stdin io.Reader, stdout, stderr io.Writer, return terminate(err) } - state := &libcontainer.State{ + state := &configs.State{ InitPid: command.Process.Pid, InitStartTime: started, NetworkState: networkState, CgroupPaths: cgroupPaths, } - if err := libcontainer.SaveState(dataPath, state); err != nil { + if err := configs.SaveState(dataPath, state); err != nil { return terminate(err) } - defer libcontainer.DeleteState(dataPath) + defer configs.DeleteState(dataPath) // wait for the child process to fully complete and receive an error message // if one was encoutered diff --git a/namespaces/execin.go b/namespaces/execin.go index 8b642fd3..2b63b8c6 100644 --- a/namespaces/execin.go +++ b/namespaces/execin.go @@ -12,7 +12,6 @@ import ( "strconv" "syscall" - "github.com/docker/libcontainer" "github.com/docker/libcontainer/apparmor" "github.com/docker/libcontainer/cgroups" "github.com/docker/libcontainer/configs" @@ -22,7 +21,7 @@ import ( // ExecIn reexec's the initPath with the argv 0 rewrite to "nsenter" so that it is able to run the // setns code in a single threaded environment joining the existing containers' namespaces. -func ExecIn(container *configs.Config, state *libcontainer.State, userArgs []string, initPath, action string, +func ExecIn(container *configs.Config, state *configs.State, userArgs []string, initPath, action string, stdin io.Reader, stdout, stderr io.Writer, console string, startCallback func(*exec.Cmd)) (int, error) { args := []string{fmt.Sprintf("nsenter-%s", action), "--nspid", strconv.Itoa(state.InitPid)} @@ -119,6 +118,6 @@ func FinalizeSetns(container *configs.Config, args []string) error { panic("unreachable") } -func EnterCgroups(state *libcontainer.State, pid int) error { +func EnterCgroups(state *configs.State, pid int) error { return cgroups.EnterPid(state.CgroupPaths, pid) } diff --git a/nsinit/exec.go b/nsinit/exec.go index a5fd29cb..8e0e734f 100644 --- a/nsinit/exec.go +++ b/nsinit/exec.go @@ -111,7 +111,7 @@ func execAction(context *cli.Context) { // with the nsenter argument so that the C code can setns an the namespaces that we require. Then that // code path will drop us into the path that we can do the final setup of the namespace and exec the users // application. -func startInExistingContainer(config *configs.Config, state *libcontainer.State, action string, context *cli.Context) (int, error) { +func startInExistingContainer(config *configs.Config, state *configs.State, action string, context *cli.Context) (int, error) { var ( master *os.File console string From c406a6b6e00b996d5d5f67aa61e58189f8650e79 Mon Sep 17 00:00:00 2001 From: Andrey Vagin Date: Wed, 17 Dec 2014 16:16:29 +0300 Subject: [PATCH 3/8] nsinit: clean up Signed-off-by: Andrey Vagin --- nsinit/exec.go | 147 ------------------------------------------------- 1 file changed, 147 deletions(-) diff --git a/nsinit/exec.go b/nsinit/exec.go index 8e0e734f..266f5935 100644 --- a/nsinit/exec.go +++ b/nsinit/exec.go @@ -3,20 +3,14 @@ package main import ( "crypto/md5" "fmt" - "io" "log" "os" - "os/exec" - "os/signal" "syscall" "text/tabwriter" "github.com/codegangsta/cli" - "github.com/docker/docker/pkg/term" "github.com/docker/libcontainer" "github.com/docker/libcontainer/configs" - consolepkg "github.com/docker/libcontainer/console" - "github.com/docker/libcontainer/namespaces" ) var ( @@ -106,144 +100,3 @@ func execAction(context *cli.Context) { os.Exit(exitCode) } - -// the process for execing a new process inside an existing container is that we have to exec ourself -// with the nsenter argument so that the C code can setns an the namespaces that we require. Then that -// code path will drop us into the path that we can do the final setup of the namespace and exec the users -// application. -func startInExistingContainer(config *configs.Config, state *configs.State, action string, context *cli.Context) (int, error) { - var ( - master *os.File - console string - err error - - sigc = make(chan os.Signal, 10) - - stdin = os.Stdin - stdout = os.Stdout - stderr = os.Stderr - ) - signal.Notify(sigc) - - if config.Tty { - stdin = nil - stdout = nil - stderr = nil - - master, console, err = consolepkg.CreateMasterAndConsole() - if err != nil { - return -1, err - } - - go io.Copy(master, os.Stdin) - go io.Copy(os.Stdout, master) - - state, err := term.SetRawTerminal(os.Stdin.Fd()) - if err != nil { - return -1, err - } - - defer term.RestoreTerminal(os.Stdin.Fd(), state) - } - - startCallback := func(cmd *exec.Cmd) { - go func() { - resizeTty(master) - - for sig := range sigc { - switch sig { - case syscall.SIGWINCH: - resizeTty(master) - default: - cmd.Process.Signal(sig) - } - } - }() - } - - return namespaces.ExecIn(config, state, context.Args(), os.Args[0], action, stdin, stdout, stderr, console, startCallback) -} - -// startContainer starts the container. Returns the exit status or -1 and an -// error. -// -// Signals sent to the current process will be forwarded to container. -func startContainer(container *configs.Config, dataPath string, args []string) (int, error) { - var ( - cmd *exec.Cmd - sigc = make(chan os.Signal, 10) - ) - - signal.Notify(sigc) - - createCommand := func(container *configs.Config, console, dataPath, init string, pipe *os.File, args []string) *exec.Cmd { - cmd = namespaces.DefaultCreateCommand(container, console, dataPath, init, pipe, args) - if logPath != "" { - cmd.Env = append(cmd.Env, fmt.Sprintf("log=%s", logPath)) - } - return cmd - } - - var ( - master *os.File - console string - err error - - stdin = os.Stdin - stdout = os.Stdout - stderr = os.Stderr - ) - - if container.Tty { - stdin = nil - stdout = nil - stderr = nil - - master, console, err = consolepkg.CreateMasterAndConsole() - if err != nil { - return -1, err - } - - go io.Copy(master, os.Stdin) - go io.Copy(os.Stdout, master) - - state, err := term.SetRawTerminal(os.Stdin.Fd()) - if err != nil { - return -1, err - } - - defer term.RestoreTerminal(os.Stdin.Fd(), state) - } - - startCallback := func() { - go func() { - resizeTty(master) - - for sig := range sigc { - switch sig { - case syscall.SIGWINCH: - resizeTty(master) - default: - cmd.Process.Signal(sig) - } - } - }() - } - - return namespaces.Exec(container, stdin, stdout, stderr, console, dataPath, args, createCommand, startCallback) -} - -func resizeTty(master *os.File) { - if master == nil { - return - } - - ws, err := term.GetWinsize(os.Stdin.Fd()) - if err != nil { - return - } - - if err := term.SetWinsize(master.Fd(), ws); err != nil { - return - } -} From 5ecd29c1f20fec04eece5fdb9c47ae0a736645dd Mon Sep 17 00:00:00 2001 From: Andrey Vagin Date: Fri, 19 Dec 2014 12:40:03 +0300 Subject: [PATCH 4/8] linux_container: fork an init process in a new set of namespaces Use namespace.Exec() and namespace.Init() to execute processes in CT. Now an init process is actually executed in a new container. This series doesn't change code about creating containers, it only reworks code according with new API. Signed-off-by: Andrey Vagin --- linux_container.go | 10 +++++--- linux_factory.go | 5 ++-- namespaces/exec.go | 61 ++++++++++++++++++++-------------------------- namespaces/init.go | 38 +++++++++++++++++++++++------ nsinit/init.go | 8 +----- 5 files changed, 68 insertions(+), 54 deletions(-) diff --git a/linux_container.go b/linux_container.go index 99cea633..0a3945b0 100644 --- a/linux_container.go +++ b/linux_container.go @@ -11,6 +11,7 @@ import ( "syscall" "github.com/docker/libcontainer/configs" + "github.com/docker/libcontainer/namespaces" "github.com/docker/libcontainer/network" "github.com/golang/glog" ) @@ -119,16 +120,17 @@ func (c *linuxContainer) startInitProcess(config *ProcessConfig) error { cmd.SysProcAttr = &syscall.SysProcAttr{} } + cmd.SysProcAttr.Cloneflags = uintptr(namespaces.GetNamespaceFlags(c.config.Namespaces)) cmd.SysProcAttr.Pdeathsig = syscall.SIGKILL - //FIXME call namespaces.Exec() - if err := cmd.Start(); err != nil { + err := namespaces.Exec(config.Args, config.Env, cmd, c.config, c.state) + if err != nil { return err } - c.state.InitPid = cmd.Process.Pid - err := c.updateStateFile() + err = c.updateStateFile() if err != nil { + // FIXME c.Kill() return err } diff --git a/linux_factory.go b/linux_factory.go index 5dc679bd..ecef9dcd 100644 --- a/linux_factory.go +++ b/linux_factory.go @@ -12,6 +12,7 @@ import ( "github.com/golang/glog" "github.com/docker/libcontainer/configs" + "github.com/docker/libcontainer/namespaces" ) const ( @@ -164,7 +165,7 @@ func (l *linuxFactory) loadContainerState(root string) (*configs.State, error) { // StartInitialization loads a container by opening the pipe fd from the parent to read the configuration and state // This is a low level implementation detail of the reexec and should not be consumed externally func (f *linuxFactory) StartInitialization(pipefd uintptr) (err error) { + pipe := os.NewFile(uintptr(pipefd), "pipe") - /* FIXME call namespaces.Init() */ - return nil + return namespaces.Init(pipe) } diff --git a/namespaces/exec.go b/namespaces/exec.go index 32b9ab12..337d5416 100644 --- a/namespaces/exec.go +++ b/namespaces/exec.go @@ -21,36 +21,45 @@ import ( // Move this to libcontainer package. // Exec performs setup outside of a namespace so that a container can be // executed. Exec is a high level function for working with container namespaces. -func Exec(container *configs.Config, stdin io.Reader, stdout, stderr io.Writer, console, dataPath string, args []string, createCommand CreateCommand, startCallback func()) (int, error) { +func Exec(args []string, env []string, command *exec.Cmd, container *configs.Config, state *configs.State) error { var err error // create a pipe so that we can syncronize with the namespaced process and // pass the state and configuration to the child process parent, child, err := newInitPipe() if err != nil { - return -1, err + return err } defer parent.Close() - command := createCommand(container, console, dataPath, os.Args[0], child, args) - // Note: these are only used in non-tty mode - // if there is a tty for the container it will be opened within the namespace and the - // fds will be duped to stdin, stdiout, and stderr - command.Stdin = stdin - command.Stdout = stdout - command.Stderr = stderr + command.ExtraFiles = []*os.File{child} + command.Dir = container.RootFs if err := command.Start(); err != nil { child.Close() - return -1, err + return err } child.Close() - terminate := func(terr error) (int, error) { + terminate := func(terr error) error { // TODO: log the errors for kill and wait command.Process.Kill() command.Wait() - return -1, terr + return terr + } + + encoder := json.NewEncoder(parent) + + if err := encoder.Encode(container); err != nil { + return terminate(err) + } + + process := processArgs{ + Env: env, + Args: args, + } + if err := encoder.Encode(process); err != nil { + return terminate(err) } started, err := system.GetProcessStartTime(command.Process.Pid) @@ -71,7 +80,7 @@ func Exec(container *configs.Config, stdin io.Reader, stdout, stderr io.Writer, return terminate(err) } // send the state to the container's init process then shutdown writes for the parent - if err := json.NewEncoder(parent).Encode(networkState); err != nil { + if err := encoder.Encode(networkState); err != nil { return terminate(err) } // shutdown writes for the parent side of the pipe @@ -79,18 +88,6 @@ func Exec(container *configs.Config, stdin io.Reader, stdout, stderr io.Writer, return terminate(err) } - state := &configs.State{ - InitPid: command.Process.Pid, - InitStartTime: started, - NetworkState: networkState, - CgroupPaths: cgroupPaths, - } - - if err := configs.SaveState(dataPath, state); err != nil { - return terminate(err) - } - defer configs.DeleteState(dataPath) - // wait for the child process to fully complete and receive an error message // if one was encoutered var ierr *initError @@ -101,16 +98,12 @@ func Exec(container *configs.Config, stdin io.Reader, stdout, stderr io.Writer, return terminate(ierr) } - if startCallback != nil { - startCallback() - } + state.InitPid = command.Process.Pid + state.InitStartTime = started + state.NetworkState = networkState + state.CgroupPaths = cgroupPaths - if err := command.Wait(); err != nil { - if _, ok := err.(*exec.ExitError); !ok { - return -1, err - } - } - return command.ProcessState.Sys().(syscall.WaitStatus).ExitStatus(), nil + return nil } // DefaultCreateCommand will return an exec.Cmd with the Cloneflags set to the proper namespaces diff --git a/namespaces/init.go b/namespaces/init.go index a6d49f61..441b3c34 100644 --- a/namespaces/init.go +++ b/namespaces/init.go @@ -24,13 +24,20 @@ import ( "github.com/docker/libcontainer/utils" ) +// Process is used for transferring parameters from Exec() to Init() +type processArgs struct { + Args []string `json:"args,omitempty"` + Env []string `json:"environment,omitempty"` + ConsolePath string `json:"console_path,omitempty"` +} + // TODO(vishh): This is part of the libcontainer API and it does much more than just namespaces related work. // Move this to libcontainer package. // Init is the init process that first runs inside a new namespace to setup mounts, users, networking, // and other options required for the new container. // The caller of Init function has to ensure that the go runtime is locked to an OS thread // (using runtime.LockOSThread) else system calls like setns called within Init may not work as intended. -func Init(container *configs.Config, uncleanRootfs, consolePath string, pipe *os.File, args []string) (err error) { +func Init(pipe *os.File) (err error) { defer func() { // if we have an error during the initialization of the container's init then send it back to the // parent process in the form of an initError. @@ -48,6 +55,23 @@ func Init(container *configs.Config, uncleanRootfs, consolePath string, pipe *os pipe.Close() }() + decoder := json.NewDecoder(pipe) + + var container *configs.Config + if err := decoder.Decode(&container); err != nil { + return err + } + + var process *processArgs + if err := decoder.Decode(&process); err != nil { + return err + } + + uncleanRootfs, err := os.Getwd() + if err != nil { + return err + } + rootfs, err := utils.ResolveRootfs(uncleanRootfs) if err != nil { return err @@ -61,22 +85,22 @@ func Init(container *configs.Config, uncleanRootfs, consolePath string, pipe *os // We always read this as it is a way to sync with the parent as well var networkState *network.NetworkState - if err := json.NewDecoder(pipe).Decode(&networkState); err != nil { + if err := decoder.Decode(&networkState); err != nil { return err } // join any namespaces via a path to the namespace fd if provided if err := joinExistingNamespaces(container.Namespaces); err != nil { return err } - if consolePath != "" { - if err := console.OpenAndDup(consolePath); err != nil { + if process.ConsolePath != "" { + if err := console.OpenAndDup(process.ConsolePath); err != nil { return err } } if _, err := syscall.Setsid(); err != nil { return fmt.Errorf("setsid %s", err) } - if consolePath != "" { + if process.ConsolePath != "" { if err := system.Setctty(); err != nil { return fmt.Errorf("setctty %s", err) } @@ -96,7 +120,7 @@ func Init(container *configs.Config, uncleanRootfs, consolePath string, pipe *os label.Init() if err := mount.InitializeMountNamespace(rootfs, - consolePath, + process.ConsolePath, container.RestrictSys, (*mount.MountConfig)(container.MountConfig)); err != nil { return fmt.Errorf("setup mount namespace %s", err) @@ -138,7 +162,7 @@ func Init(container *configs.Config, uncleanRootfs, consolePath string, pipe *os return fmt.Errorf("restore parent death signal %s", err) } - return system.Execv(args[0], args[0:], os.Environ()) + return system.Execv(process.Args[0], process.Args[0:], process.Env) } // RestoreParentDeathSignal sets the parent death signal to old. diff --git a/nsinit/init.go b/nsinit/init.go index 853c9e8e..08836139 100644 --- a/nsinit/init.go +++ b/nsinit/init.go @@ -1,9 +1,7 @@ package main import ( - "github.com/docker/libcontainer/system" "log" - "os" "github.com/codegangsta/cli" "github.com/docker/libcontainer" @@ -36,9 +34,5 @@ func initAction(context *cli.Context) { log.Fatal(err) } - args := []string(context.Args()) - - if err := system.Execv(args[0], args[0:], os.Environ()); err != nil { - log.Fatal(err) - } + panic("This line should never been executed") } From 7996829914e8877392a106574b00d86047530cd1 Mon Sep 17 00:00:00 2001 From: Andrey Vagin Date: Wed, 17 Dec 2014 18:05:39 +0300 Subject: [PATCH 5/8] libcontainer: optimize updateStateFile() Signed-off-by: Andrey Vagin --- linux_container.go | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/linux_container.go b/linux_container.go index 0a3945b0..bbd0cb1d 100644 --- a/linux_container.go +++ b/linux_container.go @@ -81,20 +81,16 @@ func (c *linuxContainer) StartProcess(pconfig *ProcessConfig) (int, error) { } func (c *linuxContainer) updateStateFile() error { - data, err := json.MarshalIndent(c.state, "", "\t") - if err != nil { - return newGenericError(err, SystemError) - } - fnew := filepath.Join(c.root, fmt.Sprintf("%s.new", stateFilename)) f, err := os.Create(fnew) if err != nil { return newGenericError(err, SystemError) } - _, err = f.Write(data) + err = json.NewEncoder(f).Encode(c.state) if err != nil { f.Close() + os.Remove(fnew) return newGenericError(err, SystemError) } f.Close() From 540f44d3b2d57d9a4ff5dcd64a686e64a90cf683 Mon Sep 17 00:00:00 2001 From: Andrey Vagin Date: Thu, 18 Dec 2014 00:14:49 +0300 Subject: [PATCH 6/8] process: use io.Reader instead of io.WriteCloser for standard fds Could someone explain why we should close this fds? Usually users cares about closing them or not. For example exec.Cmd declares them as io.Reader. Signed-off-by: Andrey Vagin --- process.go | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/process.go b/process.go index 489666a5..924de2ec 100644 --- a/process.go +++ b/process.go @@ -17,11 +17,8 @@ type ProcessConfig struct { // If a reader or writer is nil, the input stream is assumed to be empty and the output is // discarded. // - // The readers and writers, if supplied, are closed when the process terminates. Their Close - // methods should be idempotent. - // // Stdout and Stderr may refer to the same writer in which case the output is interspersed. - Stdin io.ReadCloser - Stdout io.WriteCloser - Stderr io.WriteCloser + Stdin io.Reader + Stdout io.Writer + Stderr io.Writer } From 86653c66a33053e73c38baa9b34bf09f6b059e6b Mon Sep 17 00:00:00 2001 From: Andrey Vagin Date: Thu, 18 Dec 2014 00:10:41 +0300 Subject: [PATCH 7/8] libcontinaer: use new API in integration tests Signed-off-by: Andrey Vagin --- integration/init_test.go | 12 +--------- integration/utils_test.go | 49 ++++++++++++++++++++++++++++++++++++--- 2 files changed, 47 insertions(+), 14 deletions(-) diff --git a/integration/init_test.go b/integration/init_test.go index 9954c0f8..09526376 100644 --- a/integration/init_test.go +++ b/integration/init_test.go @@ -16,17 +16,7 @@ func init() { } runtime.LockOSThread() - 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 { + if err := namespaces.Init(os.NewFile(3, "pipe")); err != nil { log.Fatalf("unable to initialize for container: %s", err) } os.Exit(1) diff --git a/integration/utils_test.go b/integration/utils_test.go index 051252fc..93fe3b4b 100644 --- a/integration/utils_test.go +++ b/integration/utils_test.go @@ -8,9 +8,10 @@ import ( "os" "os/exec" "path/filepath" + "syscall" + "github.com/docker/libcontainer" "github.com/docker/libcontainer/configs" - "github.com/docker/libcontainer/namespaces" ) func newStdBuffers() *stdBuffers { @@ -89,7 +90,49 @@ func runContainer(config *configs.Config, console string, args ...string) (buffe } buffers = newStdBuffers() - exitCode, err = namespaces.Exec(config, buffers.Stdin, buffers.Stdout, buffers.Stderr, - console, config.RootFs, args, namespaces.DefaultCreateCommand, nil) + + process := &libcontainer.ProcessConfig{ + Args: args, + Env: make([]string, 0), + Stdin: buffers.Stdin, + Stdout: buffers.Stdout, + Stderr: buffers.Stderr, + } + + factory, err := libcontainer.New(".", []string{os.Args[0], "init", "--"}) + if err != nil { + return nil, -1, err + } + + container, err := factory.Create("testCT", config) + if err != nil { + return nil, -1, err + } + defer container.Destroy() + + pid, err := container.StartProcess(process) + if err != nil { + return nil, -1, err + } + + p, err := os.FindProcess(pid) + if err != nil { + return nil, -1, err + } + + ps, err := p.Wait() + if err != nil { + return nil, -1, err + } + + status := ps.Sys().(syscall.WaitStatus) + if status.Exited() { + exitCode = status.ExitStatus() + } else if status.Signaled() { + exitCode = -int(status.Signal()) + } else { + return nil, -1, err + } + return } From c9122076364979861e9d9c5e0bcde53336aa2cf5 Mon Sep 17 00:00:00 2001 From: Andrey Vagin Date: Thu, 18 Dec 2014 14:59:29 +0300 Subject: [PATCH 8/8] namespaces: combine Process.Env and Container.Env Signed-off-by: Andrey Vagin --- namespaces/exec.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/namespaces/exec.go b/namespaces/exec.go index 337d5416..0822154d 100644 --- a/namespaces/exec.go +++ b/namespaces/exec.go @@ -55,7 +55,7 @@ func Exec(args []string, env []string, command *exec.Cmd, container *configs.Con } process := processArgs{ - Env: env, + Env: append(env[0:], container.Env...), Args: args, } if err := encoder.Encode(process); err != nil {