Merge pull request #306 from avagin/api-linux-2
Use namespace.Exec() and namespace.Init() to execute processes in CT
This commit is contained in:
commit
e1b4ec3363
|
@ -1,4 +1,4 @@
|
||||||
package libcontainer
|
package configs
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/docker/libcontainer/cgroups"
|
"github.com/docker/libcontainer/cgroups"
|
|
@ -1,4 +1,4 @@
|
||||||
package libcontainer
|
package configs
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
@ -34,7 +34,7 @@ func containsDevice(expected *devices.Device, values []*devices.Device) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadConfig(name string) (*Config, error) {
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package libcontainer
|
package configs
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
|
@ -3,6 +3,10 @@ NOTE: The API is in flux and mainly not implemented. Proceed with caution until
|
||||||
*/
|
*/
|
||||||
package libcontainer
|
package libcontainer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/docker/libcontainer/configs"
|
||||||
|
)
|
||||||
|
|
||||||
// A libcontainer container object.
|
// A libcontainer container object.
|
||||||
//
|
//
|
||||||
// Each container is thread-safe within the same process. Since a container can
|
// Each container is thread-safe within the same process. Since a container can
|
||||||
|
@ -17,10 +21,10 @@ type Container interface {
|
||||||
// errors:
|
// errors:
|
||||||
// ContainerDestroyed - Container no longer exists,
|
// ContainerDestroyed - Container no longer exists,
|
||||||
// Systemerror - System error.
|
// Systemerror - System error.
|
||||||
RunState() (RunState, error)
|
RunState() (configs.RunState, error)
|
||||||
|
|
||||||
// Returns the current config of the container.
|
// 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.
|
// Returns the PIDs inside this container. The PIDs are in the namespace of the calling process.
|
||||||
//
|
//
|
||||||
|
|
|
@ -1,5 +1,9 @@
|
||||||
package libcontainer
|
package libcontainer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/docker/libcontainer/configs"
|
||||||
|
)
|
||||||
|
|
||||||
type Factory interface {
|
type Factory interface {
|
||||||
// Creates a new container with the given id and starts the initial process inside it.
|
// 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
|
// id must be a string containing only letters, digits and underscores and must contain
|
||||||
|
@ -17,7 +21,7 @@ type Factory interface {
|
||||||
// Systemerror - System error
|
// Systemerror - System error
|
||||||
//
|
//
|
||||||
// On error, any partially created container parts are cleaned up (the operation is atomic).
|
// 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
|
// 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.
|
// from the state. This presents a read only view of the container.
|
||||||
|
|
|
@ -5,7 +5,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/docker/libcontainer"
|
"github.com/docker/libcontainer/configs"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestExecPS(t *testing.T) {
|
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 {
|
for i, v := range config.Namespaces {
|
||||||
if v.Name == name {
|
if v.Name == name {
|
||||||
return i
|
return i
|
||||||
|
|
|
@ -16,17 +16,7 @@ func init() {
|
||||||
}
|
}
|
||||||
runtime.LockOSThread()
|
runtime.LockOSThread()
|
||||||
|
|
||||||
container, err := loadConfig()
|
if err := namespaces.Init(os.NewFile(3, "pipe")); err != nil {
|
||||||
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)
|
log.Fatalf("unable to initialize for container: %s", err)
|
||||||
}
|
}
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
|
|
|
@ -3,8 +3,8 @@ package integration
|
||||||
import (
|
import (
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"github.com/docker/libcontainer"
|
|
||||||
"github.com/docker/libcontainer/cgroups"
|
"github.com/docker/libcontainer/cgroups"
|
||||||
|
"github.com/docker/libcontainer/configs"
|
||||||
"github.com/docker/libcontainer/devices"
|
"github.com/docker/libcontainer/devices"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -12,8 +12,8 @@ import (
|
||||||
//
|
//
|
||||||
// it uses a network strategy of just setting a loopback interface
|
// it uses a network strategy of just setting a loopback interface
|
||||||
// and the default setup for devices
|
// and the default setup for devices
|
||||||
func newTemplateConfig(rootfs string) *libcontainer.Config {
|
func newTemplateConfig(rootfs string) *configs.Config {
|
||||||
return &libcontainer.Config{
|
return &configs.Config{
|
||||||
RootFs: rootfs,
|
RootFs: rootfs,
|
||||||
Tty: false,
|
Tty: false,
|
||||||
Capabilities: []string{
|
Capabilities: []string{
|
||||||
|
@ -32,7 +32,7 @@ func newTemplateConfig(rootfs string) *libcontainer.Config {
|
||||||
"KILL",
|
"KILL",
|
||||||
"AUDIT_WRITE",
|
"AUDIT_WRITE",
|
||||||
},
|
},
|
||||||
Namespaces: []libcontainer.Namespace{
|
Namespaces: []configs.Namespace{
|
||||||
{Name: "NEWNS"},
|
{Name: "NEWNS"},
|
||||||
{Name: "NEWUTS"},
|
{Name: "NEWUTS"},
|
||||||
{Name: "NEWIPC"},
|
{Name: "NEWIPC"},
|
||||||
|
@ -45,7 +45,7 @@ func newTemplateConfig(rootfs string) *libcontainer.Config {
|
||||||
AllowedDevices: devices.DefaultAllowedDevices,
|
AllowedDevices: devices.DefaultAllowedDevices,
|
||||||
},
|
},
|
||||||
|
|
||||||
MountConfig: &libcontainer.MountConfig{
|
MountConfig: &configs.MountConfig{
|
||||||
DeviceNodes: devices.DefaultAutoCreatedDevices,
|
DeviceNodes: devices.DefaultAutoCreatedDevices,
|
||||||
},
|
},
|
||||||
Hostname: "integration",
|
Hostname: "integration",
|
||||||
|
@ -55,14 +55,14 @@ func newTemplateConfig(rootfs string) *libcontainer.Config {
|
||||||
"HOSTNAME=integration",
|
"HOSTNAME=integration",
|
||||||
"TERM=xterm",
|
"TERM=xterm",
|
||||||
},
|
},
|
||||||
Networks: []*libcontainer.Network{
|
Networks: []*configs.Network{
|
||||||
{
|
{
|
||||||
Type: "loopback",
|
Type: "loopback",
|
||||||
Address: "127.0.0.1/0",
|
Address: "127.0.0.1/0",
|
||||||
Gateway: "localhost",
|
Gateway: "localhost",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Rlimits: []libcontainer.Rlimit{
|
Rlimits: []configs.Rlimit{
|
||||||
{
|
{
|
||||||
Type: syscall.RLIMIT_NOFILE,
|
Type: syscall.RLIMIT_NOFILE,
|
||||||
Hard: uint64(1024),
|
Hard: uint64(1024),
|
||||||
|
|
|
@ -8,9 +8,10 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
"github.com/docker/libcontainer"
|
"github.com/docker/libcontainer"
|
||||||
"github.com/docker/libcontainer/namespaces"
|
"github.com/docker/libcontainer/configs"
|
||||||
)
|
)
|
||||||
|
|
||||||
func newStdBuffers() *stdBuffers {
|
func newStdBuffers() *stdBuffers {
|
||||||
|
@ -27,7 +28,7 @@ type stdBuffers struct {
|
||||||
Stderr *bytes.Buffer
|
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)
|
f, err := os.OpenFile(filepath.Join(config.RootFs, "container.json"), os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0700)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -36,14 +37,14 @@ func writeConfig(config *libcontainer.Config) error {
|
||||||
return json.NewEncoder(f).Encode(config)
|
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"))
|
f, err := os.Open(filepath.Join(os.Getenv("data_path"), "container.json"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer f.Close()
|
defer f.Close()
|
||||||
|
|
||||||
var container *libcontainer.Config
|
var container *configs.Config
|
||||||
if err := json.NewDecoder(f).Decode(&container); err != nil {
|
if err := json.NewDecoder(f).Decode(&container); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -83,13 +84,55 @@ func copyBusybox(dest string) error {
|
||||||
//
|
//
|
||||||
// buffers are returned containing the STDOUT and STDERR output for the run
|
// buffers are returned containing the STDOUT and STDERR output for the run
|
||||||
// along with the exit code and any go error
|
// 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 {
|
if err := writeConfig(config); err != nil {
|
||||||
return nil, -1, err
|
return nil, -1, err
|
||||||
}
|
}
|
||||||
|
|
||||||
buffers = newStdBuffers()
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,8 @@ import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/docker/libcontainer/configs"
|
||||||
|
"github.com/docker/libcontainer/namespaces"
|
||||||
"github.com/docker/libcontainer/network"
|
"github.com/docker/libcontainer/network"
|
||||||
"github.com/golang/glog"
|
"github.com/golang/glog"
|
||||||
)
|
)
|
||||||
|
@ -17,8 +19,8 @@ import (
|
||||||
type linuxContainer struct {
|
type linuxContainer struct {
|
||||||
id string
|
id string
|
||||||
root string
|
root string
|
||||||
config *Config
|
config *configs.Config
|
||||||
state *State
|
state *configs.State
|
||||||
cgroupManager CgroupManager
|
cgroupManager CgroupManager
|
||||||
initArgs []string
|
initArgs []string
|
||||||
}
|
}
|
||||||
|
@ -27,12 +29,12 @@ func (c *linuxContainer) ID() string {
|
||||||
return c.id
|
return c.id
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *linuxContainer) Config() *Config {
|
func (c *linuxContainer) Config() *configs.Config {
|
||||||
return c.config
|
return c.config
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *linuxContainer) RunState() (RunState, error) {
|
func (c *linuxContainer) RunState() (configs.RunState, error) {
|
||||||
return Destroyed, nil // FIXME return a real state
|
return configs.Destroyed, nil // FIXME return a real state
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *linuxContainer) Processes() ([]int, error) {
|
func (c *linuxContainer) Processes() ([]int, error) {
|
||||||
|
@ -60,18 +62,18 @@ func (c *linuxContainer) Stats() (*ContainerStats, error) {
|
||||||
return stats, nil
|
return stats, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *linuxContainer) StartProcess(config *ProcessConfig) (int, error) {
|
func (c *linuxContainer) StartProcess(pconfig *ProcessConfig) (int, error) {
|
||||||
state, err := c.RunState()
|
state, err := c.RunState()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return -1, err
|
return -1, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if state != Destroyed {
|
if state != configs.Destroyed {
|
||||||
glog.Info("start new container process")
|
glog.Info("start new container process")
|
||||||
panic("not implemented")
|
panic("not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := c.startInitProcess(config); err != nil {
|
if err := c.startInitProcess(pconfig); err != nil {
|
||||||
return -1, err
|
return -1, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,20 +81,16 @@ func (c *linuxContainer) StartProcess(config *ProcessConfig) (int, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *linuxContainer) updateStateFile() 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))
|
fnew := filepath.Join(c.root, fmt.Sprintf("%s.new", stateFilename))
|
||||||
f, err := os.Create(fnew)
|
f, err := os.Create(fnew)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return newGenericError(err, SystemError)
|
return newGenericError(err, SystemError)
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = f.Write(data)
|
err = json.NewEncoder(f).Encode(c.state)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
f.Close()
|
f.Close()
|
||||||
|
os.Remove(fnew)
|
||||||
return newGenericError(err, SystemError)
|
return newGenericError(err, SystemError)
|
||||||
}
|
}
|
||||||
f.Close()
|
f.Close()
|
||||||
|
@ -118,16 +116,17 @@ func (c *linuxContainer) startInitProcess(config *ProcessConfig) error {
|
||||||
cmd.SysProcAttr = &syscall.SysProcAttr{}
|
cmd.SysProcAttr = &syscall.SysProcAttr{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cmd.SysProcAttr.Cloneflags = uintptr(namespaces.GetNamespaceFlags(c.config.Namespaces))
|
||||||
cmd.SysProcAttr.Pdeathsig = syscall.SIGKILL
|
cmd.SysProcAttr.Pdeathsig = syscall.SIGKILL
|
||||||
|
|
||||||
//FIXME call namespaces.Exec()
|
err := namespaces.Exec(config.Args, config.Env, cmd, c.config, c.state)
|
||||||
if err := cmd.Start(); err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
c.state.InitPid = cmd.Process.Pid
|
err = c.updateStateFile()
|
||||||
err := c.updateStateFile()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
// FIXME c.Kill()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -140,7 +139,7 @@ func (c *linuxContainer) Destroy() error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if state != Destroyed {
|
if state != configs.Destroyed {
|
||||||
return newGenericError(nil, ContainerNotStopped)
|
return newGenericError(nil, ContainerNotStopped)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/docker/libcontainer/cgroups"
|
"github.com/docker/libcontainer/cgroups"
|
||||||
|
"github.com/docker/libcontainer/configs"
|
||||||
)
|
)
|
||||||
|
|
||||||
type mockCgroupManager struct {
|
type mockCgroupManager struct {
|
||||||
|
@ -24,7 +25,7 @@ func (m *mockCgroupManager) GetStats() (*cgroups.Stats, error) {
|
||||||
func TestGetContainerPids(t *testing.T) {
|
func TestGetContainerPids(t *testing.T) {
|
||||||
container := &linuxContainer{
|
container := &linuxContainer{
|
||||||
id: "myid",
|
id: "myid",
|
||||||
config: &Config{},
|
config: &configs.Config{},
|
||||||
cgroupManager: &mockCgroupManager{pids: []int{1, 2, 3}},
|
cgroupManager: &mockCgroupManager{pids: []int{1, 2, 3}},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,7 +44,7 @@ func TestGetContainerPids(t *testing.T) {
|
||||||
func TestGetContainerStats(t *testing.T) {
|
func TestGetContainerStats(t *testing.T) {
|
||||||
container := &linuxContainer{
|
container := &linuxContainer{
|
||||||
id: "myid",
|
id: "myid",
|
||||||
config: &Config{},
|
config: &configs.Config{},
|
||||||
cgroupManager: &mockCgroupManager{
|
cgroupManager: &mockCgroupManager{
|
||||||
pids: []int{1, 2, 3},
|
pids: []int{1, 2, 3},
|
||||||
stats: &cgroups.Stats{
|
stats: &cgroups.Stats{
|
||||||
|
@ -52,7 +53,7 @@ func TestGetContainerStats(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
state: &State{},
|
state: &configs.State{},
|
||||||
}
|
}
|
||||||
|
|
||||||
stats, err := container.Stats()
|
stats, err := container.Stats()
|
||||||
|
|
|
@ -10,6 +10,9 @@ import (
|
||||||
"regexp"
|
"regexp"
|
||||||
|
|
||||||
"github.com/golang/glog"
|
"github.com/golang/glog"
|
||||||
|
|
||||||
|
"github.com/docker/libcontainer/configs"
|
||||||
|
"github.com/docker/libcontainer/namespaces"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -43,7 +46,7 @@ type linuxFactory struct {
|
||||||
initArgs []string
|
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 == "" {
|
if l.root == "" {
|
||||||
return nil, newGenericError(fmt.Errorf("invalid root"), ConfigInvalid)
|
return nil, newGenericError(fmt.Errorf("invalid root"), ConfigInvalid)
|
||||||
}
|
}
|
||||||
|
@ -91,7 +94,7 @@ func (l *linuxFactory) Create(id string, config *Config) (Container, error) {
|
||||||
root: containerRoot,
|
root: containerRoot,
|
||||||
config: config,
|
config: config,
|
||||||
initArgs: l.initArgs,
|
initArgs: l.initArgs,
|
||||||
state: &State{},
|
state: &configs.State{},
|
||||||
cgroupManager: cgroupManager,
|
cgroupManager: cgroupManager,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
@ -125,7 +128,7 @@ func (l *linuxFactory) Load(id string) (Container, error) {
|
||||||
}, nil
|
}, 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))
|
f, err := os.Open(filepath.Join(root, configFilename))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(err) {
|
||||||
|
@ -135,14 +138,14 @@ func (l *linuxFactory) loadContainerConfig(root string) (*Config, error) {
|
||||||
}
|
}
|
||||||
defer f.Close()
|
defer f.Close()
|
||||||
|
|
||||||
var config *Config
|
var config *configs.Config
|
||||||
if err := json.NewDecoder(f).Decode(&config); err != nil {
|
if err := json.NewDecoder(f).Decode(&config); err != nil {
|
||||||
return nil, newGenericError(err, ConfigInvalid)
|
return nil, newGenericError(err, ConfigInvalid)
|
||||||
}
|
}
|
||||||
return config, nil
|
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))
|
f, err := os.Open(filepath.Join(root, stateFilename))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(err) {
|
||||||
|
@ -152,7 +155,7 @@ func (l *linuxFactory) loadContainerState(root string) (*State, error) {
|
||||||
}
|
}
|
||||||
defer f.Close()
|
defer f.Close()
|
||||||
|
|
||||||
var state *State
|
var state *configs.State
|
||||||
if err := json.NewDecoder(f).Decode(&state); err != nil {
|
if err := json.NewDecoder(f).Decode(&state); err != nil {
|
||||||
return nil, newGenericError(err, SystemError)
|
return nil, newGenericError(err, SystemError)
|
||||||
}
|
}
|
||||||
|
@ -162,7 +165,7 @@ func (l *linuxFactory) loadContainerState(root string) (*State, error) {
|
||||||
// StartInitialization loads a container by opening the pipe fd from the parent to read the configuration and state
|
// 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
|
// This is a low level implementation detail of the reexec and should not be consumed externally
|
||||||
func (f *linuxFactory) StartInitialization(pipefd uintptr) (err error) {
|
func (f *linuxFactory) StartInitialization(pipefd uintptr) (err error) {
|
||||||
|
pipe := os.NewFile(uintptr(pipefd), "pipe")
|
||||||
|
|
||||||
/* FIXME call namespaces.Init() */
|
return namespaces.Init(pipe)
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,8 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/docker/libcontainer/configs"
|
||||||
)
|
)
|
||||||
|
|
||||||
func newTestRoot() (string, error) {
|
func newTestRoot() (string, error) {
|
||||||
|
@ -83,10 +85,10 @@ func TestFactoryLoadContainer(t *testing.T) {
|
||||||
// setup default container config and state for mocking
|
// setup default container config and state for mocking
|
||||||
var (
|
var (
|
||||||
id = "1"
|
id = "1"
|
||||||
expectedConfig = &Config{
|
expectedConfig = &configs.Config{
|
||||||
RootFs: "/mycontainer/root",
|
RootFs: "/mycontainer/root",
|
||||||
}
|
}
|
||||||
expectedState = &State{
|
expectedState = &configs.State{
|
||||||
InitPid: 1024,
|
InitPid: 1024,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
|
@ -4,7 +4,7 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"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
|
||||||
|
|
|
@ -9,10 +9,10 @@ import (
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"github.com/docker/libcontainer"
|
|
||||||
"github.com/docker/libcontainer/cgroups"
|
"github.com/docker/libcontainer/cgroups"
|
||||||
"github.com/docker/libcontainer/cgroups/fs"
|
"github.com/docker/libcontainer/cgroups/fs"
|
||||||
"github.com/docker/libcontainer/cgroups/systemd"
|
"github.com/docker/libcontainer/cgroups/systemd"
|
||||||
|
"github.com/docker/libcontainer/configs"
|
||||||
"github.com/docker/libcontainer/network"
|
"github.com/docker/libcontainer/network"
|
||||||
"github.com/docker/libcontainer/system"
|
"github.com/docker/libcontainer/system"
|
||||||
)
|
)
|
||||||
|
@ -21,36 +21,45 @@ import (
|
||||||
// Move this to libcontainer package.
|
// Move this to libcontainer package.
|
||||||
// Exec performs setup outside of a namespace so that a container can be
|
// 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.
|
// 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(args []string, env []string, command *exec.Cmd, container *configs.Config, state *configs.State) error {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
// create a pipe so that we can syncronize with the namespaced process and
|
// create a pipe so that we can syncronize with the namespaced process and
|
||||||
// pass the state and configuration to the child process
|
// pass the state and configuration to the child process
|
||||||
parent, child, err := newInitPipe()
|
parent, child, err := newInitPipe()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return -1, err
|
return err
|
||||||
}
|
}
|
||||||
defer parent.Close()
|
defer parent.Close()
|
||||||
|
|
||||||
command := createCommand(container, console, dataPath, os.Args[0], child, args)
|
command.ExtraFiles = []*os.File{child}
|
||||||
// Note: these are only used in non-tty mode
|
command.Dir = container.RootFs
|
||||||
// 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
|
|
||||||
|
|
||||||
if err := command.Start(); err != nil {
|
if err := command.Start(); err != nil {
|
||||||
child.Close()
|
child.Close()
|
||||||
return -1, err
|
return err
|
||||||
}
|
}
|
||||||
child.Close()
|
child.Close()
|
||||||
|
|
||||||
terminate := func(terr error) (int, error) {
|
terminate := func(terr error) error {
|
||||||
// TODO: log the errors for kill and wait
|
// TODO: log the errors for kill and wait
|
||||||
command.Process.Kill()
|
command.Process.Kill()
|
||||||
command.Wait()
|
command.Wait()
|
||||||
return -1, terr
|
return terr
|
||||||
|
}
|
||||||
|
|
||||||
|
encoder := json.NewEncoder(parent)
|
||||||
|
|
||||||
|
if err := encoder.Encode(container); err != nil {
|
||||||
|
return terminate(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
process := processArgs{
|
||||||
|
Env: append(env[0:], container.Env...),
|
||||||
|
Args: args,
|
||||||
|
}
|
||||||
|
if err := encoder.Encode(process); err != nil {
|
||||||
|
return terminate(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
started, err := system.GetProcessStartTime(command.Process.Pid)
|
started, err := system.GetProcessStartTime(command.Process.Pid)
|
||||||
|
@ -71,7 +80,7 @@ func Exec(container *libcontainer.Config, stdin io.Reader, stdout, stderr io.Wri
|
||||||
return terminate(err)
|
return terminate(err)
|
||||||
}
|
}
|
||||||
// send the state to the container's init process then shutdown writes for the parent
|
// 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)
|
return terminate(err)
|
||||||
}
|
}
|
||||||
// shutdown writes for the parent side of the pipe
|
// shutdown writes for the parent side of the pipe
|
||||||
|
@ -79,18 +88,6 @@ func Exec(container *libcontainer.Config, stdin io.Reader, stdout, stderr io.Wri
|
||||||
return terminate(err)
|
return terminate(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
state := &libcontainer.State{
|
|
||||||
InitPid: command.Process.Pid,
|
|
||||||
InitStartTime: started,
|
|
||||||
NetworkState: networkState,
|
|
||||||
CgroupPaths: cgroupPaths,
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := libcontainer.SaveState(dataPath, state); err != nil {
|
|
||||||
return terminate(err)
|
|
||||||
}
|
|
||||||
defer libcontainer.DeleteState(dataPath)
|
|
||||||
|
|
||||||
// wait for the child process to fully complete and receive an error message
|
// wait for the child process to fully complete and receive an error message
|
||||||
// if one was encoutered
|
// if one was encoutered
|
||||||
var ierr *initError
|
var ierr *initError
|
||||||
|
@ -101,16 +98,12 @@ func Exec(container *libcontainer.Config, stdin io.Reader, stdout, stderr io.Wri
|
||||||
return terminate(ierr)
|
return terminate(ierr)
|
||||||
}
|
}
|
||||||
|
|
||||||
if startCallback != nil {
|
state.InitPid = command.Process.Pid
|
||||||
startCallback()
|
state.InitStartTime = started
|
||||||
}
|
state.NetworkState = networkState
|
||||||
|
state.CgroupPaths = cgroupPaths
|
||||||
|
|
||||||
if err := command.Wait(); err != nil {
|
return nil
|
||||||
if _, ok := err.(*exec.ExitError); !ok {
|
|
||||||
return -1, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return command.ProcessState.Sys().(syscall.WaitStatus).ExitStatus(), nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// DefaultCreateCommand will return an exec.Cmd with the Cloneflags set to the proper namespaces
|
// DefaultCreateCommand will return an exec.Cmd with the Cloneflags set to the proper namespaces
|
||||||
|
@ -122,7 +115,7 @@ func Exec(container *libcontainer.Config, stdin io.Reader, stdout, stderr io.Wri
|
||||||
// root: the path to the container json file and information
|
// root: the path to the container json file and information
|
||||||
// pipe: sync pipe to synchronize the parent and child processes
|
// 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
|
// 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
|
// get our binary name from arg0 so we can always reexec ourself
|
||||||
env := []string{
|
env := []string{
|
||||||
"console=" + console,
|
"console=" + console,
|
||||||
|
@ -148,7 +141,7 @@ func DefaultCreateCommand(container *libcontainer.Config, console, dataPath, ini
|
||||||
|
|
||||||
// SetupCgroups applies the cgroup restrictions to the process running in the container based
|
// SetupCgroups applies the cgroup restrictions to the process running in the container based
|
||||||
// on the container's configuration
|
// 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 {
|
if container.Cgroups != nil {
|
||||||
c := container.Cgroups
|
c := container.Cgroups
|
||||||
if systemd.UseSystemd() {
|
if systemd.UseSystemd() {
|
||||||
|
@ -161,7 +154,7 @@ func SetupCgroups(container *libcontainer.Config, nspid int) (map[string]string,
|
||||||
|
|
||||||
// InitializeNetworking creates the container's network stack outside of the namespace and moves
|
// InitializeNetworking creates the container's network stack outside of the namespace and moves
|
||||||
// interfaces into the container's net namespaces if necessary
|
// 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 {
|
for _, config := range container.Networks {
|
||||||
strategy, err := network.GetStrategy(config.Type)
|
strategy, err := network.GetStrategy(config.Type)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -12,16 +12,16 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"github.com/docker/libcontainer"
|
|
||||||
"github.com/docker/libcontainer/apparmor"
|
"github.com/docker/libcontainer/apparmor"
|
||||||
"github.com/docker/libcontainer/cgroups"
|
"github.com/docker/libcontainer/cgroups"
|
||||||
|
"github.com/docker/libcontainer/configs"
|
||||||
"github.com/docker/libcontainer/label"
|
"github.com/docker/libcontainer/label"
|
||||||
"github.com/docker/libcontainer/system"
|
"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
|
// 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.
|
// 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 *configs.State, userArgs []string, initPath, action string,
|
||||||
stdin io.Reader, stdout, stderr io.Writer, console string, startCallback func(*exec.Cmd)) (int, error) {
|
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)}
|
args := []string{fmt.Sprintf("nsenter-%s", action), "--nspid", strconv.Itoa(state.InitPid)}
|
||||||
|
@ -91,7 +91,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
|
// Finalize expects that the setns calls have been setup and that is has joined an
|
||||||
// existing namespace
|
// 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
|
// clear the current processes env and replace it with the environment defined on the container
|
||||||
if err := LoadContainerEnvironment(container); err != nil {
|
if err := LoadContainerEnvironment(container); err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -118,6 +118,6 @@ func FinalizeSetns(container *libcontainer.Config, args []string) error {
|
||||||
panic("unreachable")
|
panic("unreachable")
|
||||||
}
|
}
|
||||||
|
|
||||||
func EnterCgroups(state *libcontainer.State, pid int) error {
|
func EnterCgroups(state *configs.State, pid int) error {
|
||||||
return cgroups.EnterPid(state.CgroupPaths, pid)
|
return cgroups.EnterPid(state.CgroupPaths, pid)
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,8 +10,8 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"github.com/docker/libcontainer"
|
|
||||||
"github.com/docker/libcontainer/apparmor"
|
"github.com/docker/libcontainer/apparmor"
|
||||||
|
"github.com/docker/libcontainer/configs"
|
||||||
"github.com/docker/libcontainer/console"
|
"github.com/docker/libcontainer/console"
|
||||||
"github.com/docker/libcontainer/label"
|
"github.com/docker/libcontainer/label"
|
||||||
"github.com/docker/libcontainer/mount"
|
"github.com/docker/libcontainer/mount"
|
||||||
|
@ -24,13 +24,20 @@ import (
|
||||||
"github.com/docker/libcontainer/utils"
|
"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.
|
// TODO(vishh): This is part of the libcontainer API and it does much more than just namespaces related work.
|
||||||
// Move this to libcontainer package.
|
// Move this to libcontainer package.
|
||||||
// Init is the init process that first runs inside a new namespace to setup mounts, users, networking,
|
// 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.
|
// 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
|
// 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.
|
// (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(pipe *os.File) (err error) {
|
||||||
defer func() {
|
defer func() {
|
||||||
// if we have an error during the initialization of the container's init then send it back to the
|
// 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.
|
// parent process in the form of an initError.
|
||||||
|
@ -48,6 +55,23 @@ func Init(container *libcontainer.Config, uncleanRootfs, consolePath string, pip
|
||||||
pipe.Close()
|
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)
|
rootfs, err := utils.ResolveRootfs(uncleanRootfs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -61,22 +85,22 @@ func Init(container *libcontainer.Config, uncleanRootfs, consolePath string, pip
|
||||||
|
|
||||||
// We always read this as it is a way to sync with the parent as well
|
// We always read this as it is a way to sync with the parent as well
|
||||||
var networkState *network.NetworkState
|
var networkState *network.NetworkState
|
||||||
if err := json.NewDecoder(pipe).Decode(&networkState); err != nil {
|
if err := decoder.Decode(&networkState); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// join any namespaces via a path to the namespace fd if provided
|
// join any namespaces via a path to the namespace fd if provided
|
||||||
if err := joinExistingNamespaces(container.Namespaces); err != nil {
|
if err := joinExistingNamespaces(container.Namespaces); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if consolePath != "" {
|
if process.ConsolePath != "" {
|
||||||
if err := console.OpenAndDup(consolePath); err != nil {
|
if err := console.OpenAndDup(process.ConsolePath); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if _, err := syscall.Setsid(); err != nil {
|
if _, err := syscall.Setsid(); err != nil {
|
||||||
return fmt.Errorf("setsid %s", err)
|
return fmt.Errorf("setsid %s", err)
|
||||||
}
|
}
|
||||||
if consolePath != "" {
|
if process.ConsolePath != "" {
|
||||||
if err := system.Setctty(); err != nil {
|
if err := system.Setctty(); err != nil {
|
||||||
return fmt.Errorf("setctty %s", err)
|
return fmt.Errorf("setctty %s", err)
|
||||||
}
|
}
|
||||||
|
@ -96,7 +120,7 @@ func Init(container *libcontainer.Config, uncleanRootfs, consolePath string, pip
|
||||||
label.Init()
|
label.Init()
|
||||||
|
|
||||||
if err := mount.InitializeMountNamespace(rootfs,
|
if err := mount.InitializeMountNamespace(rootfs,
|
||||||
consolePath,
|
process.ConsolePath,
|
||||||
container.RestrictSys,
|
container.RestrictSys,
|
||||||
(*mount.MountConfig)(container.MountConfig)); err != nil {
|
(*mount.MountConfig)(container.MountConfig)); err != nil {
|
||||||
return fmt.Errorf("setup mount namespace %s", err)
|
return fmt.Errorf("setup mount namespace %s", err)
|
||||||
|
@ -138,7 +162,7 @@ func Init(container *libcontainer.Config, uncleanRootfs, consolePath string, pip
|
||||||
return fmt.Errorf("restore parent death signal %s", err)
|
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.
|
// RestoreParentDeathSignal sets the parent death signal to old.
|
||||||
|
@ -218,7 +242,7 @@ func SetupUser(u string) error {
|
||||||
// setupVethNetwork uses the Network config if it is not nil to initialize
|
// 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
|
// 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
|
// 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 {
|
for _, config := range container.Networks {
|
||||||
strategy, err := network.GetStrategy(config.Type)
|
strategy, err := network.GetStrategy(config.Type)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -233,7 +257,7 @@ func setupNetwork(container *libcontainer.Config, networkState *network.NetworkS
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func setupRoute(container *libcontainer.Config) error {
|
func setupRoute(container *configs.Config) error {
|
||||||
for _, config := range container.Routes {
|
for _, config := range container.Routes {
|
||||||
if err := netlink.AddRoute(config.Destination, config.Source, config.Gateway, config.InterfaceName); err != nil {
|
if err := netlink.AddRoute(config.Destination, config.Source, config.Gateway, config.InterfaceName); err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -242,7 +266,7 @@ func setupRoute(container *libcontainer.Config) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func setupRlimits(container *libcontainer.Config) error {
|
func setupRlimits(container *configs.Config) error {
|
||||||
for _, rlimit := range container.Rlimits {
|
for _, rlimit := range container.Rlimits {
|
||||||
l := &syscall.Rlimit{Max: rlimit.Hard, Cur: rlimit.Soft}
|
l := &syscall.Rlimit{Max: rlimit.Hard, Cur: rlimit.Soft}
|
||||||
if err := syscall.Setrlimit(rlimit.Type, l); err != nil {
|
if err := syscall.Setrlimit(rlimit.Type, l); err != nil {
|
||||||
|
@ -255,7 +279,7 @@ func setupRlimits(container *libcontainer.Config) error {
|
||||||
// FinalizeNamespace drops the caps, sets the correct user
|
// FinalizeNamespace drops the caps, sets the correct user
|
||||||
// and working dir, and closes any leaky file descriptors
|
// and working dir, and closes any leaky file descriptors
|
||||||
// before execing the command inside the namespace
|
// 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
|
// Ensure that all non-standard fds we may have accidentally
|
||||||
// inherited are marked close-on-exec so they stay out of the
|
// inherited are marked close-on-exec so they stay out of the
|
||||||
// container
|
// container
|
||||||
|
@ -295,7 +319,7 @@ func FinalizeNamespace(container *libcontainer.Config) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func LoadContainerEnvironment(container *libcontainer.Config) error {
|
func LoadContainerEnvironment(container *configs.Config) error {
|
||||||
os.Clearenv()
|
os.Clearenv()
|
||||||
for _, pair := range container.Env {
|
for _, pair := range container.Env {
|
||||||
p := strings.SplitN(pair, "=", 2)
|
p := strings.SplitN(pair, "=", 2)
|
||||||
|
@ -311,7 +335,7 @@ func LoadContainerEnvironment(container *libcontainer.Config) error {
|
||||||
|
|
||||||
// joinExistingNamespaces gets all the namespace paths specified for the container and
|
// 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.
|
// 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 {
|
for _, ns := range namespaces {
|
||||||
if ns.Path != "" {
|
if ns.Path != "" {
|
||||||
f, err := os.OpenFile(ns.Path, os.O_RDONLY, 0)
|
f, err := os.OpenFile(ns.Path, os.O_RDONLY, 0)
|
||||||
|
|
|
@ -6,7 +6,7 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"github.com/docker/libcontainer"
|
"github.com/docker/libcontainer/configs"
|
||||||
)
|
)
|
||||||
|
|
||||||
type initError struct {
|
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
|
// GetNamespaceFlags parses the container's Namespaces options to set the correct
|
||||||
// flags on clone, unshare, and setns
|
// flags on clone, unshare, and setns
|
||||||
func GetNamespaceFlags(namespaces []libcontainer.Namespace) (flag int) {
|
func GetNamespaceFlags(namespaces []configs.Namespace) (flag int) {
|
||||||
for _, v := range namespaces {
|
for _, v := range namespaces {
|
||||||
flag |= namespaceInfo[v.Name]
|
flag |= namespaceInfo[v.Name]
|
||||||
}
|
}
|
||||||
|
|
150
nsinit/exec.go
150
nsinit/exec.go
|
@ -3,19 +3,14 @@ package main
|
||||||
import (
|
import (
|
||||||
"crypto/md5"
|
"crypto/md5"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
|
||||||
"os/signal"
|
|
||||||
"syscall"
|
"syscall"
|
||||||
"text/tabwriter"
|
"text/tabwriter"
|
||||||
|
|
||||||
"github.com/codegangsta/cli"
|
"github.com/codegangsta/cli"
|
||||||
"github.com/docker/docker/pkg/term"
|
|
||||||
"github.com/docker/libcontainer"
|
"github.com/docker/libcontainer"
|
||||||
consolepkg "github.com/docker/libcontainer/console"
|
"github.com/docker/libcontainer/configs"
|
||||||
"github.com/docker/libcontainer/namespaces"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -66,7 +61,7 @@ func execAction(context *cli.Context) {
|
||||||
id := fmt.Sprintf("%x", md5.Sum([]byte(dataPath)))
|
id := fmt.Sprintf("%x", md5.Sum([]byte(dataPath)))
|
||||||
container, err := factory.Load(id)
|
container, err := factory.Load(id)
|
||||||
if err != nil && !os.IsNotExist(err) {
|
if err != nil && !os.IsNotExist(err) {
|
||||||
var config *libcontainer.Config
|
var config *configs.Config
|
||||||
|
|
||||||
config, err = loadConfig()
|
config, err = loadConfig()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -105,144 +100,3 @@ func execAction(context *cli.Context) {
|
||||||
|
|
||||||
os.Exit(exitCode)
|
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 *libcontainer.Config, state *libcontainer.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 *libcontainer.Config, dataPath string, args []string) (int, error) {
|
|
||||||
var (
|
|
||||||
cmd *exec.Cmd
|
|
||||||
sigc = make(chan os.Signal, 10)
|
|
||||||
)
|
|
||||||
|
|
||||||
signal.Notify(sigc)
|
|
||||||
|
|
||||||
createCommand := func(container *libcontainer.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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/docker/libcontainer/system"
|
|
||||||
"log"
|
"log"
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/codegangsta/cli"
|
"github.com/codegangsta/cli"
|
||||||
"github.com/docker/libcontainer"
|
"github.com/docker/libcontainer"
|
||||||
|
@ -36,9 +34,5 @@ func initAction(context *cli.Context) {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
args := []string(context.Args())
|
panic("This line should never been executed")
|
||||||
|
|
||||||
if err := system.Execv(args[0], args[0:], os.Environ()); err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"text/tabwriter"
|
"text/tabwriter"
|
||||||
|
|
||||||
"github.com/docker/libcontainer"
|
"github.com/docker/libcontainer/configs"
|
||||||
"github.com/docker/libcontainer/devices"
|
"github.com/docker/libcontainer/devices"
|
||||||
"github.com/docker/libcontainer/mount/nodes"
|
"github.com/docker/libcontainer/mount/nodes"
|
||||||
"github.com/docker/libcontainer/namespaces"
|
"github.com/docker/libcontainer/namespaces"
|
||||||
|
@ -17,7 +17,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// nsenterExec exec's a process inside an existing container
|
// 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 {
|
if err := namespaces.FinalizeSetns(config, args); err != nil {
|
||||||
log.Fatalf("failed to nsenter: %s", err)
|
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
|
// nsenterMknod runs mknod inside an existing container
|
||||||
//
|
//
|
||||||
// mknod <path> <type> <major> <minor>
|
// mknod <path> <type> <major> <minor>
|
||||||
func nsenterMknod(config *libcontainer.Config, args []string) {
|
func nsenterMknod(config *configs.Config, args []string) {
|
||||||
if len(args) != 4 {
|
if len(args) != 4 {
|
||||||
log.Fatalf("expected mknod to have 4 arguments not %d", len(args))
|
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
|
// 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()
|
interfaces, err := net.Interfaces()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
|
|
|
@ -7,23 +7,23 @@ import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/codegangsta/cli"
|
"github.com/codegangsta/cli"
|
||||||
"github.com/docker/libcontainer"
|
"github.com/docker/libcontainer/configs"
|
||||||
)
|
)
|
||||||
|
|
||||||
// rFunc is a function registration for calling after an execin
|
// rFunc is a function registration for calling after an execin
|
||||||
type rFunc struct {
|
type rFunc struct {
|
||||||
Usage string
|
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"))
|
f, err := os.Open(filepath.Join(dataPath, "container.json"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer f.Close()
|
defer f.Close()
|
||||||
|
|
||||||
var container *libcontainer.Config
|
var container *configs.Config
|
||||||
if err := json.NewDecoder(f).Decode(&container); err != nil {
|
if err := json.NewDecoder(f).Decode(&container); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -57,11 +57,11 @@ func findUserArgs() []string {
|
||||||
|
|
||||||
// loadConfigFromFd loads a container's config from the sync pipe that is provided by
|
// loadConfigFromFd loads a container's config from the sync pipe that is provided by
|
||||||
// fd 3 when running a process
|
// fd 3 when running a process
|
||||||
func loadConfigFromFd() (*libcontainer.Config, error) {
|
func loadConfigFromFd() (*configs.Config, error) {
|
||||||
pipe := os.NewFile(3, "pipe")
|
pipe := os.NewFile(3, "pipe")
|
||||||
defer pipe.Close()
|
defer pipe.Close()
|
||||||
|
|
||||||
var config *libcontainer.Config
|
var config *configs.Config
|
||||||
if err := json.NewDecoder(pipe).Decode(&config); err != nil {
|
if err := json.NewDecoder(pipe).Decode(&config); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
// If a reader or writer is nil, the input stream is assumed to be empty and the output is
|
||||||
// discarded.
|
// 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.
|
// Stdout and Stderr may refer to the same writer in which case the output is interspersed.
|
||||||
Stdin io.ReadCloser
|
Stdin io.Reader
|
||||||
Stdout io.WriteCloser
|
Stdout io.Writer
|
||||||
Stderr io.WriteCloser
|
Stderr io.Writer
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue