diff --git a/runtime/container.go b/runtime/container.go index 488d905f4b..1f0f82eebb 100644 --- a/runtime/container.go +++ b/runtime/container.go @@ -364,6 +364,10 @@ func populateCommand(c *Container) { driverConfig = c.hostConfig.PluginOptions ) + if driverConfig == nil { + driverConfig = make(map[string][]string) + } + en = &execdriver.Network{ Mtu: c.runtime.config.Mtu, Interface: nil, diff --git a/runtime/execdriver/native/configuration/caps.go b/runtime/execdriver/native/configuration/caps.go new file mode 100644 index 0000000000..f4de470684 --- /dev/null +++ b/runtime/execdriver/native/configuration/caps.go @@ -0,0 +1,27 @@ +package configuration + +import ( + "fmt" + "github.com/dotcloud/docker/pkg/libcontainer" + "strings" +) + +// i.e: cap +MKNOD cap -NET_ADMIN +func parseCapOpt(container *libcontainer.Container, opts []string) error { + var ( + value = strings.TrimSpace(opts[0]) + c = container.CapabilitiesMask.Get(value[1:]) + ) + if c == nil { + return fmt.Errorf("%s is not a valid capability", value[1:]) + } + switch value[0] { + case '-': + c.Enabled = false + case '+': + c.Enabled = true + default: + return fmt.Errorf("%c is not a valid modifier for capabilities", value[0]) + } + return nil +} diff --git a/runtime/execdriver/native/configuration/net.go b/runtime/execdriver/native/configuration/net.go new file mode 100644 index 0000000000..cac7f658ba --- /dev/null +++ b/runtime/execdriver/native/configuration/net.go @@ -0,0 +1,35 @@ +package configuration + +import ( + "fmt" + "github.com/dotcloud/docker/pkg/libcontainer" + "os/exec" + "path/filepath" + "strings" +) + +// i.e: net join +func parseNetOpt(container *libcontainer.Container, running map[string]*exec.Cmd, opts []string) error { + opt := strings.TrimSpace(opts[1]) + switch opt { + case "join": + var ( + id = strings.TrimSpace(opts[2]) + cmd = running[id] + ) + + if cmd == nil || cmd.Process == nil { + return fmt.Errorf("%s is not a valid running container to join", id) + } + nspath := filepath.Join("/proc", fmt.Sprint(cmd.Process.Pid), "ns", "net") + container.Networks = append(container.Networks, &libcontainer.Network{ + Type: "netns", + Context: libcontainer.Context{ + "nspath": nspath, + }, + }) + default: + return fmt.Errorf("%s is not a valid network option", opt) + } + return nil +} diff --git a/runtime/execdriver/native/configuration/ns.go b/runtime/execdriver/native/configuration/ns.go new file mode 100644 index 0000000000..ff7f367196 --- /dev/null +++ b/runtime/execdriver/native/configuration/ns.go @@ -0,0 +1,26 @@ +package configuration + +import ( + "fmt" + "github.com/dotcloud/docker/pkg/libcontainer" + "strings" +) + +func parseNsOpt(container *libcontainer.Container, opts []string) error { + var ( + value = strings.TrimSpace(opts[0]) + ns = container.Namespaces.Get(value[1:]) + ) + if ns == nil { + return fmt.Errorf("%s is not a valid namespace", value[1:]) + } + switch value[0] { + case '-': + ns.Enabled = false + case '+': + ns.Enabled = true + default: + return fmt.Errorf("%c is not a valid modifier for namespaces", value[0]) + } + return nil +} diff --git a/runtime/execdriver/native/configuration/parse.go b/runtime/execdriver/native/configuration/parse.go new file mode 100644 index 0000000000..08b98fbd12 --- /dev/null +++ b/runtime/execdriver/native/configuration/parse.go @@ -0,0 +1,37 @@ +package configuration + +import ( + "fmt" + "github.com/dotcloud/docker/pkg/libcontainer" + "os/exec" + "strings" +) + +// configureCustomOptions takes string commands from the user and allows modification of the +// container's default configuration. +// +// format: <...value> +// i.e: cgroup devices.allow *:* +func ParseConfiguration(container *libcontainer.Container, running map[string]*exec.Cmd, opts []string) error { + for _, opt := range opts { + var ( + err error + parts = strings.Split(strings.TrimSpace(opt), " ") + ) + + switch parts[0] { + case "cap": + err = parseCapOpt(container, parts[1:]) + case "ns": + err = parseNsOpt(container, parts[1:]) + case "net": + err = parseNetOpt(container, running, parts[1:]) + default: + return fmt.Errorf("%s is not a valid configuration option for the native driver", parts[0]) + } + if err != nil { + return err + } + } + return nil +} diff --git a/runtime/execdriver/native/create.go b/runtime/execdriver/native/create.go new file mode 100644 index 0000000000..7118edc91e --- /dev/null +++ b/runtime/execdriver/native/create.go @@ -0,0 +1,70 @@ +package native + +import ( + "fmt" + "github.com/dotcloud/docker/pkg/libcontainer" + "github.com/dotcloud/docker/runtime/execdriver" + "github.com/dotcloud/docker/runtime/execdriver/native/configuration" + "os" +) + +// createContainer populates and configures the container type with the +// data provided by the execdriver.Command +func (d *driver) createContainer(c *execdriver.Command) (*libcontainer.Container, error) { + container := getDefaultTemplate() + + container.Hostname = getEnv("HOSTNAME", c.Env) + container.Tty = c.Tty + container.User = c.User + container.WorkingDir = c.WorkingDir + container.Env = c.Env + + loopbackNetwork := libcontainer.Network{ + Mtu: c.Network.Mtu, + Address: fmt.Sprintf("%s/%d", "127.0.0.1", 0), + Gateway: "localhost", + Type: "loopback", + Context: libcontainer.Context{}, + } + + container.Networks = []*libcontainer.Network{ + &loopbackNetwork, + } + + if c.Network.Interface != nil { + vethNetwork := libcontainer.Network{ + Mtu: c.Network.Mtu, + Address: fmt.Sprintf("%s/%d", c.Network.Interface.IPAddress, c.Network.Interface.IPPrefixLen), + Gateway: c.Network.Interface.Gateway, + Type: "veth", + Context: libcontainer.Context{ + "prefix": "veth", + "bridge": c.Network.Interface.Bridge, + }, + } + container.Networks = append(container.Networks, &vethNetwork) + } + + container.Cgroups.Name = c.ID + if c.Privileged { + container.CapabilitiesMask = nil + container.Cgroups.DeviceAccess = true + container.Context["apparmor_profile"] = "unconfined" + } + if c.Resources != nil { + container.Cgroups.CpuShares = c.Resources.CpuShares + container.Cgroups.Memory = c.Resources.Memory + container.Cgroups.MemorySwap = c.Resources.MemorySwap + } + // check to see if we are running in ramdisk to disable pivot root + container.NoPivotRoot = os.Getenv("DOCKER_RAMDISK") != "" + + for _, m := range c.Mounts { + container.Mounts = append(container.Mounts, libcontainer.Mount{m.Source, m.Destination, m.Writable, m.Private}) + } + + if err := configuration.ParseConfiguration(container, d.activeContainers, c.Config["native"]); err != nil { + return nil, err + } + return container, nil +} diff --git a/runtime/execdriver/native/default_template.go b/runtime/execdriver/native/default_template.go index 890e260ad2..0dcd7db356 100644 --- a/runtime/execdriver/native/default_template.go +++ b/runtime/execdriver/native/default_template.go @@ -1,136 +1,10 @@ package native import ( - "fmt" "github.com/dotcloud/docker/pkg/cgroups" "github.com/dotcloud/docker/pkg/libcontainer" - "github.com/dotcloud/docker/runtime/execdriver" - "os" - "path/filepath" - "strings" ) -// createContainer populates and configures the container type with the -// data provided by the execdriver.Command -func (d *driver) createContainer(c *execdriver.Command) *libcontainer.Container { - container := getDefaultTemplate() - - container.Hostname = getEnv("HOSTNAME", c.Env) - container.Tty = c.Tty - container.User = c.User - container.WorkingDir = c.WorkingDir - container.Env = c.Env - - loopbackNetwork := libcontainer.Network{ - Mtu: c.Network.Mtu, - Address: fmt.Sprintf("%s/%d", "127.0.0.1", 0), - Gateway: "localhost", - Type: "loopback", - Context: libcontainer.Context{}, - } - - container.Networks = []*libcontainer.Network{ - &loopbackNetwork, - } - - if c.Network.Interface != nil { - vethNetwork := libcontainer.Network{ - Mtu: c.Network.Mtu, - Address: fmt.Sprintf("%s/%d", c.Network.Interface.IPAddress, c.Network.Interface.IPPrefixLen), - Gateway: c.Network.Interface.Gateway, - Type: "veth", - Context: libcontainer.Context{ - "prefix": "veth", - "bridge": c.Network.Interface.Bridge, - }, - } - container.Networks = append(container.Networks, &vethNetwork) - } - - container.Cgroups.Name = c.ID - if c.Privileged { - container.CapabilitiesMask = nil - container.Cgroups.DeviceAccess = true - container.Context["apparmor_profile"] = "unconfined" - } - if c.Resources != nil { - container.Cgroups.CpuShares = c.Resources.CpuShares - container.Cgroups.Memory = c.Resources.Memory - container.Cgroups.MemorySwap = c.Resources.MemorySwap - } - // check to see if we are running in ramdisk to disable pivot root - container.NoPivotRoot = os.Getenv("DOCKER_RAMDISK") != "" - - for _, m := range c.Mounts { - container.Mounts = append(container.Mounts, libcontainer.Mount{m.Source, m.Destination, m.Writable, m.Private}) - } - - d.configureCustomOptions(container, c.Config["native"]) - - return container -} - -// configureCustomOptions takes string commands from the user and allows modification of the -// container's default configuration. -// -// format: -// i.e: cap +MKNOD cap -NET_ADMIN -// i.e: cgroup devices.allow *:* -// i.e: net join -func (d *driver) configureCustomOptions(container *libcontainer.Container, opts []string) { - for _, opt := range opts { - var ( - parts = strings.Split(strings.TrimSpace(opt), " ") - value = strings.TrimSpace(parts[1]) - ) - switch parts[0] { - case "cap": - c := container.CapabilitiesMask.Get(value[1:]) - if c == nil { - continue - } - switch value[0] { - case '-': - c.Enabled = false - case '+': - c.Enabled = true - default: - // do error here - } - case "ns": - ns := container.Namespaces.Get(value[1:]) - switch value[0] { - case '-': - ns.Enabled = false - case '+': - ns.Enabled = true - default: - // error - } - case "net": - switch strings.TrimSpace(parts[1]) { - case "join": - var ( - id = strings.TrimSpace(parts[2]) - cmd = d.activeContainers[id] - nspath = filepath.Join("/proc", fmt.Sprint(cmd.Process.Pid), "ns", "net") - ) - - container.Networks = append(container.Networks, &libcontainer.Network{ - Type: "netns", - Context: libcontainer.Context{ - "nspath": nspath, - }, - }) - default: - // error - } - default: - // error not defined - } - } -} - // getDefaultTemplate returns the docker default for // the libcontainer configuration file func getDefaultTemplate() *libcontainer.Container { diff --git a/runtime/execdriver/native/driver.go b/runtime/execdriver/native/driver.go index b998db743d..4acc4b388c 100644 --- a/runtime/execdriver/native/driver.go +++ b/runtime/execdriver/native/driver.go @@ -59,7 +59,7 @@ func init() { type driver struct { root string initPath string - activeContainers map[string]*execdriver.Command + activeContainers map[string]*exec.Cmd } func NewDriver(root, initPath string) (*driver, error) { @@ -72,16 +72,20 @@ func NewDriver(root, initPath string) (*driver, error) { return &driver{ root: root, initPath: initPath, - activeContainers: make(map[string]*execdriver.Command), + activeContainers: make(map[string]*exec.Cmd), }, nil } func (d *driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallback execdriver.StartCallback) (int, error) { - d.activeContainers[c.ID] = c + // take the Command and populate the libcontainer.Container from it + container, err := d.createContainer(c) + if err != nil { + return -1, err + } + d.activeContainers[c.ID] = &c.Cmd var ( term nsinit.Terminal - container = d.createContainer(c) factory = &dockerCommandFactory{c: c, driver: d} stateWriter = &dockerStateWriter{ callback: startCallback,