From f52b2fdcbb29258c5b492fdb2479d473fcb42ca0 Mon Sep 17 00:00:00 2001 From: Johan Euphrosine Date: Mon, 3 Mar 2014 14:41:38 -0800 Subject: [PATCH 01/21] libcontainer/network: add netns strategy Docker-DCO-1.1-Signed-off-by: Johan Euphrosine (github: proppy) --- pkg/libcontainer/network/netns.go | 42 ++++++++++++++++++++++++++++ pkg/libcontainer/network/strategy.go | 2 ++ 2 files changed, 44 insertions(+) create mode 100644 pkg/libcontainer/network/netns.go diff --git a/pkg/libcontainer/network/netns.go b/pkg/libcontainer/network/netns.go new file mode 100644 index 0000000000..3eb8ee587a --- /dev/null +++ b/pkg/libcontainer/network/netns.go @@ -0,0 +1,42 @@ +package network + +import ( + "fmt" + "os" + "syscall" + + "github.com/dotcloud/docker/pkg/libcontainer" + "github.com/dotcloud/docker/pkg/system" +) + +// crosbymichael: could make a network strategy that instead of returning veth pair names it returns a pid to an existing network namespace +type NetNS struct { +} + +func (v *NetNS) Create(n *libcontainer.Network, nspid int, context libcontainer.Context) error { + nsname, exists := n.Context["nsname"] + + if !exists { + return fmt.Errorf("nspath does not exist in network context") + } + + context["nspath"] = fmt.Sprintf("/var/run/netns/%s", nsname) + return nil +} + +func (v *NetNS) Initialize(config *libcontainer.Network, context libcontainer.Context) error { + nspath, exists := context["nspath"] + if !exists { + return fmt.Errorf("nspath does not exist in network context") + } + + f, err := os.OpenFile(nspath, os.O_RDONLY, 0) + if err != nil { + return fmt.Errorf("failed get network namespace fd: %v", err) + } + + if err := system.Setns(f.Fd(), syscall.CLONE_NEWNET); err != nil { + return fmt.Errorf("failed to setns current network namespace: %v", err) + } + return nil +} diff --git a/pkg/libcontainer/network/strategy.go b/pkg/libcontainer/network/strategy.go index 693790d280..e41ecc3ea6 100644 --- a/pkg/libcontainer/network/strategy.go +++ b/pkg/libcontainer/network/strategy.go @@ -2,6 +2,7 @@ package network import ( "errors" + "github.com/dotcloud/docker/pkg/libcontainer" ) @@ -12,6 +13,7 @@ var ( var strategies = map[string]NetworkStrategy{ "veth": &Veth{}, "loopback": &Loopback{}, + "netns": &NetNS{}, } // NetworkStrategy represents a specific network configuration for From b10b950b110c93db34a399753706dc79c71b94d3 Mon Sep 17 00:00:00 2001 From: Johan Euphrosine Date: Mon, 3 Mar 2014 21:46:49 -0800 Subject: [PATCH 02/21] libcontainer/nsinit/init: move mount namespace after network Docker-DCO-1.1-Signed-off-by: Johan Euphrosine (github: proppy) --- pkg/libcontainer/nsinit/init.go | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/pkg/libcontainer/nsinit/init.go b/pkg/libcontainer/nsinit/init.go index 117ae875ed..c39928d459 100644 --- a/pkg/libcontainer/nsinit/init.go +++ b/pkg/libcontainer/nsinit/init.go @@ -4,6 +4,9 @@ package nsinit import ( "fmt" + "os" + "syscall" + "github.com/dotcloud/docker/pkg/libcontainer" "github.com/dotcloud/docker/pkg/libcontainer/apparmor" "github.com/dotcloud/docker/pkg/libcontainer/capabilities" @@ -11,8 +14,6 @@ import ( "github.com/dotcloud/docker/pkg/libcontainer/utils" "github.com/dotcloud/docker/pkg/system" "github.com/dotcloud/docker/pkg/user" - "os" - "syscall" ) // Init is the init process that first runs inside a new namespace to setup mounts, users, networking, @@ -56,13 +57,13 @@ func (ns *linuxNs) Init(container *libcontainer.Container, uncleanRootfs, consol if err := system.ParentDeathSignal(uintptr(syscall.SIGTERM)); err != nil { return fmt.Errorf("parent death signal %s", err) } + if err := setupNetwork(container, context); err != nil { + return fmt.Errorf("setup networking %s", err) + } ns.logger.Println("setup mount namespace") if err := setupNewMountNamespace(rootfs, container.Mounts, console, container.ReadonlyFs, container.NoPivotRoot); err != nil { return fmt.Errorf("setup mount namespace %s", err) } - if err := setupNetwork(container, context); err != nil { - return fmt.Errorf("setup networking %s", err) - } if err := system.Sethostname(container.Hostname); err != nil { return fmt.Errorf("sethostname %s", err) } From f58757a699cf47c3b8770e13f371e1bbf493b5b1 Mon Sep 17 00:00:00 2001 From: Johan Euphrosine Date: Mon, 3 Mar 2014 21:47:03 -0800 Subject: [PATCH 03/21] libcontainer: goimports Docker-DCO-1.1-Signed-off-by: Johan Euphrosine (github: proppy) --- pkg/libcontainer/nsinit/exec.go | 7 ++++--- pkg/libcontainer/nsinit/nsinit/main.go | 8 ++++++-- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/pkg/libcontainer/nsinit/exec.go b/pkg/libcontainer/nsinit/exec.go index 61286cc13c..6e902d1916 100644 --- a/pkg/libcontainer/nsinit/exec.go +++ b/pkg/libcontainer/nsinit/exec.go @@ -3,12 +3,13 @@ package nsinit import ( - "github.com/dotcloud/docker/pkg/libcontainer" - "github.com/dotcloud/docker/pkg/libcontainer/network" - "github.com/dotcloud/docker/pkg/system" "os" "os/exec" "syscall" + + "github.com/dotcloud/docker/pkg/libcontainer" + "github.com/dotcloud/docker/pkg/libcontainer/network" + "github.com/dotcloud/docker/pkg/system" ) // Exec performes setup outside of a namespace so that a container can be diff --git a/pkg/libcontainer/nsinit/nsinit/main.go b/pkg/libcontainer/nsinit/nsinit/main.go index df32d0b49e..3725fb5534 100644 --- a/pkg/libcontainer/nsinit/nsinit/main.go +++ b/pkg/libcontainer/nsinit/nsinit/main.go @@ -3,14 +3,18 @@ package main import ( "encoding/json" "flag" - "github.com/dotcloud/docker/pkg/libcontainer" - "github.com/dotcloud/docker/pkg/libcontainer/nsinit" "io" "io/ioutil" "log" "os" "path/filepath" "strconv" + + "github.com/dotcloud/docker/pkg/libcontainer" + "github.com/dotcloud/docker/pkg/libcontainer/nsinit" + + "github.com/dotcloud/docker/pkg/libcontainer" + "github.com/dotcloud/docker/pkg/libcontainer/nsinit" ) var ( From 5dbfe310fe624c66714f8c5017692f528af4c87f Mon Sep 17 00:00:00 2001 From: Johan Euphrosine Date: Tue, 18 Mar 2014 16:25:26 -0700 Subject: [PATCH 04/21] libcontainer: remove duplicate imports Docker-DCO-1.1-Signed-off-by: Johan Euphrosine (github: proppy) --- pkg/libcontainer/nsinit/nsinit/main.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/pkg/libcontainer/nsinit/nsinit/main.go b/pkg/libcontainer/nsinit/nsinit/main.go index 3725fb5534..37aa784981 100644 --- a/pkg/libcontainer/nsinit/nsinit/main.go +++ b/pkg/libcontainer/nsinit/nsinit/main.go @@ -12,9 +12,6 @@ import ( "github.com/dotcloud/docker/pkg/libcontainer" "github.com/dotcloud/docker/pkg/libcontainer/nsinit" - - "github.com/dotcloud/docker/pkg/libcontainer" - "github.com/dotcloud/docker/pkg/libcontainer/nsinit" ) var ( From f7b3e879fc3047ed93ac6b43cd9bf47a25f3d0fc Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Thu, 20 Mar 2014 22:58:02 +0000 Subject: [PATCH 05/21] Add initial plugin flag to pass lxc and native driver options Docker-DCO-1.1-Signed-off-by: Michael Crosby (github: crosbymichael) --- runconfig/hostconfig.go | 2 ++ runconfig/parse.go | 18 ++++++++++++ runtime/container.go | 8 ++++-- runtime/execdriver/driver.go | 28 +++++++++---------- runtime/execdriver/lxc/lxc_template.go | 4 +-- .../execdriver/lxc/lxc_template_unit_test.go | 3 +- runtime/execdriver/native/driver.go | 6 ++-- 7 files changed, 46 insertions(+), 23 deletions(-) diff --git a/runconfig/hostconfig.go b/runconfig/hostconfig.go index 6c8618ee81..8ee2288b4b 100644 --- a/runconfig/hostconfig.go +++ b/runconfig/hostconfig.go @@ -13,6 +13,7 @@ type HostConfig struct { PortBindings nat.PortMap Links []string PublishAllPorts bool + PluginOptions map[string][]string } type KeyValuePair struct { @@ -28,6 +29,7 @@ func ContainerHostConfigFromJob(job *engine.Job) *HostConfig { } job.GetenvJson("LxcConf", &hostConfig.LxcConf) job.GetenvJson("PortBindings", &hostConfig.PortBindings) + job.GetenvJson("PluginOptions", &hostConfig.PluginOptions) if Binds := job.GetenvList("Binds"); Binds != nil { hostConfig.Binds = Binds } diff --git a/runconfig/parse.go b/runconfig/parse.go index cc33188ad5..afcaec304f 100644 --- a/runconfig/parse.go +++ b/runconfig/parse.go @@ -45,6 +45,7 @@ func parseRun(cmd *flag.FlagSet, args []string, sysInfo *sysinfo.SysInfo) (*Conf flDnsSearch = opts.NewListOpts(opts.ValidateDomain) flVolumesFrom opts.ListOpts flLxcOpts opts.ListOpts + flPluginOpts opts.ListOpts flAutoRemove = cmd.Bool([]string{"#rm", "-rm"}, false, "Automatically remove the container when it exits (incompatible with -d)") flDetach = cmd.Bool([]string{"d", "-detach"}, false, "Detached mode: Run container in the background, print new container id") @@ -77,6 +78,7 @@ func parseRun(cmd *flag.FlagSet, args []string, sysInfo *sysinfo.SysInfo) (*Conf cmd.Var(&flDnsSearch, []string{"-dns-search"}, "Set custom dns search domains") cmd.Var(&flVolumesFrom, []string{"#volumes-from", "-volumes-from"}, "Mount volumes from the specified container(s)") cmd.Var(&flLxcOpts, []string{"#lxc-conf", "-lxc-conf"}, "Add custom lxc options --lxc-conf=\"lxc.cgroup.cpuset.cpus = 0,1\"") + cmd.Var(&flPluginOpts, []string{"-plugin"}, "Add custom plugin options") if err := cmd.Parse(args); err != nil { return nil, nil, cmd, err @@ -206,6 +208,8 @@ func parseRun(cmd *flag.FlagSet, args []string, sysInfo *sysinfo.SysInfo) (*Conf WorkingDir: *flWorkingDir, } + pluginOptions := parsePluginOpts(flPluginOpts) + hostConfig := &HostConfig{ Binds: binds, ContainerIDFile: *flContainerIDFile, @@ -214,6 +218,7 @@ func parseRun(cmd *flag.FlagSet, args []string, sysInfo *sysinfo.SysInfo) (*Conf PortBindings: portBindings, Links: flLinks.GetAll(), PublishAllPorts: *flPublishAll, + PluginOptions: pluginOptions, } if sysInfo != nil && flMemory > 0 && !sysInfo.SwapLimit { @@ -247,3 +252,16 @@ func parseLxcOpt(opt string) (string, string, error) { } return strings.TrimSpace(parts[0]), strings.TrimSpace(parts[1]), nil } + +func parsePluginOpts(opts opts.ListOpts) map[string][]string { + out := make(map[string][]string, len(opts.GetAll())) + for _, o := range opts.GetAll() { + parts := strings.SplitN(o, " ", 2) + values, exists := out[parts[0]] + if !exists { + values = []string{} + } + out[parts[0]] = append(values, parts[1]) + } + return out +} diff --git a/runtime/container.go b/runtime/container.go index 6194a19c8c..488d905f4b 100644 --- a/runtime/container.go +++ b/runtime/container.go @@ -361,7 +361,7 @@ func (container *Container) Attach(stdin io.ReadCloser, stdinCloser io.Closer, s func populateCommand(c *Container) { var ( en *execdriver.Network - driverConfig []string + driverConfig = c.hostConfig.PluginOptions ) en = &execdriver.Network{ @@ -379,11 +379,15 @@ func populateCommand(c *Container) { } } + // merge in the lxc conf options into the generic config map if lxcConf := c.hostConfig.LxcConf; lxcConf != nil { + lxc := driverConfig["lxc"] for _, pair := range lxcConf { - driverConfig = append(driverConfig, fmt.Sprintf("%s = %s", pair.Key, pair.Value)) + lxc = append(lxc, fmt.Sprintf("%s = %s", pair.Key, pair.Value)) } + driverConfig["lxc"] = lxc } + resources := &execdriver.Resources{ Memory: c.Config.Memory, MemorySwap: c.Config.MemorySwap, diff --git a/runtime/execdriver/driver.go b/runtime/execdriver/driver.go index 23e31ee8d9..2b7c367453 100644 --- a/runtime/execdriver/driver.go +++ b/runtime/execdriver/driver.go @@ -112,20 +112,20 @@ type Mount struct { type Command struct { exec.Cmd `json:"-"` - ID string `json:"id"` - Privileged bool `json:"privileged"` - User string `json:"user"` - Rootfs string `json:"rootfs"` // root fs of the container - InitPath string `json:"initpath"` // dockerinit - Entrypoint string `json:"entrypoint"` - Arguments []string `json:"arguments"` - WorkingDir string `json:"working_dir"` - ConfigPath string `json:"config_path"` // this should be able to be removed when the lxc template is moved into the driver - Tty bool `json:"tty"` - Network *Network `json:"network"` - Config []string `json:"config"` // generic values that specific drivers can consume - Resources *Resources `json:"resources"` - Mounts []Mount `json:"mounts"` + ID string `json:"id"` + Privileged bool `json:"privileged"` + User string `json:"user"` + Rootfs string `json:"rootfs"` // root fs of the container + InitPath string `json:"initpath"` // dockerinit + Entrypoint string `json:"entrypoint"` + Arguments []string `json:"arguments"` + WorkingDir string `json:"working_dir"` + ConfigPath string `json:"config_path"` // this should be able to be removed when the lxc template is moved into the driver + Tty bool `json:"tty"` + Network *Network `json:"network"` + Config map[string][]string `json:"config"` // generic values that specific drivers can consume + Resources *Resources `json:"resources"` + Mounts []Mount `json:"mounts"` Terminal Terminal `json:"-"` // standard or tty terminal Console string `json:"-"` // dev/console path diff --git a/runtime/execdriver/lxc/lxc_template.go b/runtime/execdriver/lxc/lxc_template.go index ce9d90469f..7979e4f284 100644 --- a/runtime/execdriver/lxc/lxc_template.go +++ b/runtime/execdriver/lxc/lxc_template.go @@ -118,8 +118,8 @@ lxc.cgroup.cpu.shares = {{.Resources.CpuShares}} {{end}} {{end}} -{{if .Config}} -{{range $value := .Config}} +{{if .Config.lxc}} +{{range $value := .Config.lxc}} {{$value}} {{end}} {{end}} diff --git a/runtime/execdriver/lxc/lxc_template_unit_test.go b/runtime/execdriver/lxc/lxc_template_unit_test.go index e613adf7a9..74cfd6229c 100644 --- a/runtime/execdriver/lxc/lxc_template_unit_test.go +++ b/runtime/execdriver/lxc/lxc_template_unit_test.go @@ -75,10 +75,11 @@ func TestCustomLxcConfig(t *testing.T) { command := &execdriver.Command{ ID: "1", Privileged: false, - Config: []string{ + Config: map[string][]string{"lxc": { "lxc.utsname = docker", "lxc.cgroup.cpuset.cpus = 0,1", }, + }, Network: &execdriver.Network{ Mtu: 1500, Interface: nil, diff --git a/runtime/execdriver/native/driver.go b/runtime/execdriver/native/driver.go index bf7e8ccdec..0a09d324db 100644 --- a/runtime/execdriver/native/driver.go +++ b/runtime/execdriver/native/driver.go @@ -184,10 +184,8 @@ func (d *driver) removeContainerRoot(id string) error { func (d *driver) validateCommand(c *execdriver.Command) error { // we need to check the Config of the command to make sure that we // do not have any of the lxc-conf variables - for _, conf := range c.Config { - if strings.Contains(conf, "lxc") { - return fmt.Errorf("%s is not supported by the native driver", conf) - } + for _, conf := range c.Config["native"] { + log.Println(conf) } return nil } From c5f9c4bd6933c806490e4f7cb52557cee154dbed Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Thu, 20 Mar 2014 23:09:01 +0000 Subject: [PATCH 06/21] Dont use custom marshaling for caps and namespaces This also adds an enabled field to the types so that they can be easily toggled. Docker-DCO-1.1-Signed-off-by: Michael Crosby (github: crosbymichael) --- pkg/libcontainer/types.go | 77 ++++++++++----------------------- pkg/libcontainer/types_linux.go | 12 ++--- 2 files changed, 28 insertions(+), 61 deletions(-) diff --git a/pkg/libcontainer/types.go b/pkg/libcontainer/types.go index 94fe876554..87346348bc 100644 --- a/pkg/libcontainer/types.go +++ b/pkg/libcontainer/types.go @@ -1,7 +1,6 @@ package libcontainer import ( - "encoding/json" "errors" "github.com/syndtr/gocapability/capability" ) @@ -19,29 +18,30 @@ var ( namespaceList = Namespaces{} capabilityList = Capabilities{ - {Key: "SETPCAP", Value: capability.CAP_SETPCAP}, - {Key: "SYS_MODULE", Value: capability.CAP_SYS_MODULE}, - {Key: "SYS_RAWIO", Value: capability.CAP_SYS_RAWIO}, - {Key: "SYS_PACCT", Value: capability.CAP_SYS_PACCT}, - {Key: "SYS_ADMIN", Value: capability.CAP_SYS_ADMIN}, - {Key: "SYS_NICE", Value: capability.CAP_SYS_NICE}, - {Key: "SYS_RESOURCE", Value: capability.CAP_SYS_RESOURCE}, - {Key: "SYS_TIME", Value: capability.CAP_SYS_TIME}, - {Key: "SYS_TTY_CONFIG", Value: capability.CAP_SYS_TTY_CONFIG}, - {Key: "MKNOD", Value: capability.CAP_MKNOD}, - {Key: "AUDIT_WRITE", Value: capability.CAP_AUDIT_WRITE}, - {Key: "AUDIT_CONTROL", Value: capability.CAP_AUDIT_CONTROL}, - {Key: "MAC_OVERRIDE", Value: capability.CAP_MAC_OVERRIDE}, - {Key: "MAC_ADMIN", Value: capability.CAP_MAC_ADMIN}, - {Key: "NET_ADMIN", Value: capability.CAP_NET_ADMIN}, + {Key: "SETPCAP", Value: capability.CAP_SETPCAP, Enabled: true}, + {Key: "SYS_MODULE", Value: capability.CAP_SYS_MODULE, Enabled: true}, + {Key: "SYS_RAWIO", Value: capability.CAP_SYS_RAWIO, Enabled: true}, + {Key: "SYS_PACCT", Value: capability.CAP_SYS_PACCT, Enabled: true}, + {Key: "SYS_ADMIN", Value: capability.CAP_SYS_ADMIN, Enabled: true}, + {Key: "SYS_NICE", Value: capability.CAP_SYS_NICE, Enabled: true}, + {Key: "SYS_RESOURCE", Value: capability.CAP_SYS_RESOURCE, Enabled: true}, + {Key: "SYS_TIME", Value: capability.CAP_SYS_TIME, Enabled: true}, + {Key: "SYS_TTY_CONFIG", Value: capability.CAP_SYS_TTY_CONFIG, Enabled: true}, + {Key: "MKNOD", Value: capability.CAP_MKNOD, Enabled: true}, + {Key: "AUDIT_WRITE", Value: capability.CAP_AUDIT_WRITE, Enabled: true}, + {Key: "AUDIT_CONTROL", Value: capability.CAP_AUDIT_CONTROL, Enabled: true}, + {Key: "MAC_OVERRIDE", Value: capability.CAP_MAC_OVERRIDE, Enabled: true}, + {Key: "MAC_ADMIN", Value: capability.CAP_MAC_ADMIN, Enabled: true}, + {Key: "NET_ADMIN", Value: capability.CAP_NET_ADMIN, Enabled: true}, } ) type ( Namespace struct { - Key string - Value int - File string + Key string `json:"key,omitempty"` + Enabled bool `json:"enabled,omitempty"` + Value int `json:"value,omitempty"` + File string `json:"file,omitempty"` } Namespaces []*Namespace ) @@ -50,23 +50,6 @@ func (ns *Namespace) String() string { return ns.Key } -func (ns *Namespace) MarshalJSON() ([]byte, error) { - return json.Marshal(ns.Key) -} - -func (ns *Namespace) UnmarshalJSON(src []byte) error { - var nsName string - if err := json.Unmarshal(src, &nsName); err != nil { - return err - } - ret := GetNamespace(nsName) - if ret == nil { - return ErrUnkownNamespace - } - *ns = *ret - return nil -} - func GetNamespace(key string) *Namespace { for _, ns := range namespaceList { if ns.Key == key { @@ -89,8 +72,9 @@ func (n Namespaces) Contains(ns string) bool { type ( Capability struct { - Key string - Value capability.Cap + Key string `json:"key,omitempty"` + Enabled bool `json:"enabled"` + Value capability.Cap `json:"value,omitempty"` } Capabilities []*Capability ) @@ -99,23 +83,6 @@ func (c *Capability) String() string { return c.Key } -func (c *Capability) MarshalJSON() ([]byte, error) { - return json.Marshal(c.Key) -} - -func (c *Capability) UnmarshalJSON(src []byte) error { - var capName string - if err := json.Unmarshal(src, &capName); err != nil { - return err - } - ret := GetCapability(capName) - if ret == nil { - return ErrUnkownCapability - } - *c = *ret - return nil -} - func GetCapability(key string) *Capability { for _, capp := range capabilityList { if capp.Key == key { diff --git a/pkg/libcontainer/types_linux.go b/pkg/libcontainer/types_linux.go index c14531df20..1f937e0c97 100644 --- a/pkg/libcontainer/types_linux.go +++ b/pkg/libcontainer/types_linux.go @@ -6,11 +6,11 @@ import ( func init() { namespaceList = Namespaces{ - {Key: "NEWNS", Value: syscall.CLONE_NEWNS, File: "mnt"}, - {Key: "NEWUTS", Value: syscall.CLONE_NEWUTS, File: "uts"}, - {Key: "NEWIPC", Value: syscall.CLONE_NEWIPC, File: "ipc"}, - {Key: "NEWUSER", Value: syscall.CLONE_NEWUSER, File: "user"}, - {Key: "NEWPID", Value: syscall.CLONE_NEWPID, File: "pid"}, - {Key: "NEWNET", Value: syscall.CLONE_NEWNET, File: "net"}, + {Key: "NEWNS", Value: syscall.CLONE_NEWNS, File: "mnt", Enabled: true}, + {Key: "NEWUTS", Value: syscall.CLONE_NEWUTS, File: "uts", Enabled: true}, + {Key: "NEWIPC", Value: syscall.CLONE_NEWIPC, File: "ipc", Enabled: true}, + {Key: "NEWUSER", Value: syscall.CLONE_NEWUSER, File: "user", Enabled: true}, + {Key: "NEWPID", Value: syscall.CLONE_NEWPID, File: "pid", Enabled: true}, + {Key: "NEWNET", Value: syscall.CLONE_NEWNET, File: "net", Enabled: true}, } } From 443a75d5f66e986e9d7740d3f2aaef080aef8ea0 Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Fri, 21 Mar 2014 00:10:24 +0000 Subject: [PATCH 07/21] Allow caps to be toggled in native driver with plugin flag Docker-DCO-1.1-Signed-off-by: Michael Crosby (github: crosbymichael) --- pkg/libcontainer/capabilities/capabilities.go | 4 +- pkg/libcontainer/types.go | 41 +++++++++++-------- runtime/execdriver/native/default_template.go | 31 ++++++++++++++ runtime/execdriver/native/driver.go | 12 ------ 4 files changed, 57 insertions(+), 31 deletions(-) diff --git a/pkg/libcontainer/capabilities/capabilities.go b/pkg/libcontainer/capabilities/capabilities.go index fbf73538e0..4b81e708c7 100644 --- a/pkg/libcontainer/capabilities/capabilities.go +++ b/pkg/libcontainer/capabilities/capabilities.go @@ -27,7 +27,9 @@ func DropCapabilities(container *libcontainer.Container) error { func getCapabilitiesMask(container *libcontainer.Container) []capability.Cap { drop := []capability.Cap{} for _, c := range container.CapabilitiesMask { - drop = append(drop, c.Value) + if !c.Enabled { + drop = append(drop, c.Value) + } } return drop } diff --git a/pkg/libcontainer/types.go b/pkg/libcontainer/types.go index 87346348bc..7751e850b6 100644 --- a/pkg/libcontainer/types.go +++ b/pkg/libcontainer/types.go @@ -18,21 +18,21 @@ var ( namespaceList = Namespaces{} capabilityList = Capabilities{ - {Key: "SETPCAP", Value: capability.CAP_SETPCAP, Enabled: true}, - {Key: "SYS_MODULE", Value: capability.CAP_SYS_MODULE, Enabled: true}, - {Key: "SYS_RAWIO", Value: capability.CAP_SYS_RAWIO, Enabled: true}, - {Key: "SYS_PACCT", Value: capability.CAP_SYS_PACCT, Enabled: true}, - {Key: "SYS_ADMIN", Value: capability.CAP_SYS_ADMIN, Enabled: true}, - {Key: "SYS_NICE", Value: capability.CAP_SYS_NICE, Enabled: true}, - {Key: "SYS_RESOURCE", Value: capability.CAP_SYS_RESOURCE, Enabled: true}, - {Key: "SYS_TIME", Value: capability.CAP_SYS_TIME, Enabled: true}, - {Key: "SYS_TTY_CONFIG", Value: capability.CAP_SYS_TTY_CONFIG, Enabled: true}, - {Key: "MKNOD", Value: capability.CAP_MKNOD, Enabled: true}, - {Key: "AUDIT_WRITE", Value: capability.CAP_AUDIT_WRITE, Enabled: true}, - {Key: "AUDIT_CONTROL", Value: capability.CAP_AUDIT_CONTROL, Enabled: true}, - {Key: "MAC_OVERRIDE", Value: capability.CAP_MAC_OVERRIDE, Enabled: true}, - {Key: "MAC_ADMIN", Value: capability.CAP_MAC_ADMIN, Enabled: true}, - {Key: "NET_ADMIN", Value: capability.CAP_NET_ADMIN, Enabled: true}, + {Key: "SETPCAP", Value: capability.CAP_SETPCAP, Enabled: false}, + {Key: "SYS_MODULE", Value: capability.CAP_SYS_MODULE, Enabled: false}, + {Key: "SYS_RAWIO", Value: capability.CAP_SYS_RAWIO, Enabled: false}, + {Key: "SYS_PACCT", Value: capability.CAP_SYS_PACCT, Enabled: false}, + {Key: "SYS_ADMIN", Value: capability.CAP_SYS_ADMIN, Enabled: false}, + {Key: "SYS_NICE", Value: capability.CAP_SYS_NICE, Enabled: false}, + {Key: "SYS_RESOURCE", Value: capability.CAP_SYS_RESOURCE, Enabled: false}, + {Key: "SYS_TIME", Value: capability.CAP_SYS_TIME, Enabled: false}, + {Key: "SYS_TTY_CONFIG", Value: capability.CAP_SYS_TTY_CONFIG, Enabled: false}, + {Key: "MKNOD", Value: capability.CAP_MKNOD, Enabled: false}, + {Key: "AUDIT_WRITE", Value: capability.CAP_AUDIT_WRITE, Enabled: false}, + {Key: "AUDIT_CONTROL", Value: capability.CAP_AUDIT_CONTROL, Enabled: false}, + {Key: "MAC_OVERRIDE", Value: capability.CAP_MAC_OVERRIDE, Enabled: false}, + {Key: "MAC_ADMIN", Value: capability.CAP_MAC_ADMIN, Enabled: false}, + {Key: "NET_ADMIN", Value: capability.CAP_NET_ADMIN, Enabled: false}, } ) @@ -86,7 +86,8 @@ func (c *Capability) String() string { func GetCapability(key string) *Capability { for _, capp := range capabilityList { if capp.Key == key { - return capp + cpy := *capp + return &cpy } } return nil @@ -95,10 +96,14 @@ func GetCapability(key string) *Capability { // Contains returns true if the specified Capability is // in the slice func (c Capabilities) Contains(capp string) bool { + return c.Get(capp) != nil +} + +func (c Capabilities) Get(capp string) *Capability { for _, cap := range c { if cap.Key == capp { - return true + return cap } } - return false + return nil } diff --git a/runtime/execdriver/native/default_template.go b/runtime/execdriver/native/default_template.go index d744ab382f..d47a5eb8cd 100644 --- a/runtime/execdriver/native/default_template.go +++ b/runtime/execdriver/native/default_template.go @@ -6,6 +6,7 @@ import ( "github.com/dotcloud/docker/pkg/libcontainer" "github.com/dotcloud/docker/runtime/execdriver" "os" + "strings" ) // createContainer populates and configures the container type with the @@ -63,9 +64,39 @@ func createContainer(c *execdriver.Command) *libcontainer.Container { container.Mounts = append(container.Mounts, libcontainer.Mount{m.Source, m.Destination, m.Writable, m.Private}) } + 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 *:* +func configureCustomOptions(container *libcontainer.Container, opts []string) { + for _, opt := range opts { + parts := strings.Split(strings.TrimSpace(opt), " ") + switch parts[0] { + case "cap": + value := strings.TrimSpace(parts[1]) + 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 + } + } + } +} + // 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 0a09d324db..0d9297191c 100644 --- a/runtime/execdriver/native/driver.go +++ b/runtime/execdriver/native/driver.go @@ -75,9 +75,6 @@ func NewDriver(root, initPath string) (*driver, error) { } func (d *driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallback execdriver.StartCallback) (int, error) { - if err := d.validateCommand(c); err != nil { - return -1, err - } var ( term nsinit.Terminal container = createContainer(c) @@ -181,15 +178,6 @@ func (d *driver) removeContainerRoot(id string) error { return os.RemoveAll(filepath.Join(d.root, id)) } -func (d *driver) validateCommand(c *execdriver.Command) error { - // we need to check the Config of the command to make sure that we - // do not have any of the lxc-conf variables - for _, conf := range c.Config["native"] { - log.Println(conf) - } - return nil -} - func getEnv(key string, env []string) string { for _, pair := range env { parts := strings.Split(pair, "=") From 70f3b9f4ce67ee54ec226814cdd26db01f69378d Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Fri, 21 Mar 2014 00:23:34 +0000 Subject: [PATCH 08/21] Add ability to work with individual namespaces Docker-DCO-1.1-Signed-off-by: Michael Crosby (github: crosbymichael) --- pkg/libcontainer/nsinit/command.go | 4 +++- pkg/libcontainer/types.go | 11 ++++++++--- runtime/execdriver/native/default_template.go | 16 ++++++++++++++-- 3 files changed, 25 insertions(+), 6 deletions(-) diff --git a/pkg/libcontainer/nsinit/command.go b/pkg/libcontainer/nsinit/command.go index 5546065b6d..153a48ab59 100644 --- a/pkg/libcontainer/nsinit/command.go +++ b/pkg/libcontainer/nsinit/command.go @@ -39,7 +39,9 @@ func (c *DefaultCommandFactory) Create(container *libcontainer.Container, consol // flags on clone, unshare, and setns func GetNamespaceFlags(namespaces libcontainer.Namespaces) (flag int) { for _, ns := range namespaces { - flag |= ns.Value + if ns.Enabled { + flag |= ns.Value + } } return flag } diff --git a/pkg/libcontainer/types.go b/pkg/libcontainer/types.go index 7751e850b6..ffeb55a022 100644 --- a/pkg/libcontainer/types.go +++ b/pkg/libcontainer/types.go @@ -53,7 +53,8 @@ func (ns *Namespace) String() string { func GetNamespace(key string) *Namespace { for _, ns := range namespaceList { if ns.Key == key { - return ns + cpy := *ns + return &cpy } } return nil @@ -62,12 +63,16 @@ func GetNamespace(key string) *Namespace { // Contains returns true if the specified Namespace is // in the slice func (n Namespaces) Contains(ns string) bool { + return n.Get(ns) != nil +} + +func (n Namespaces) Get(ns string) *Namespace { for _, nsp := range n { if nsp.Key == ns { - return true + return nsp } } - return false + return nil } type ( diff --git a/runtime/execdriver/native/default_template.go b/runtime/execdriver/native/default_template.go index d47a5eb8cd..dbb7a45ae7 100644 --- a/runtime/execdriver/native/default_template.go +++ b/runtime/execdriver/native/default_template.go @@ -77,10 +77,12 @@ func createContainer(c *execdriver.Command) *libcontainer.Container { // i.e: cgroup devices.allow *:* func configureCustomOptions(container *libcontainer.Container, opts []string) { for _, opt := range opts { - parts := strings.Split(strings.TrimSpace(opt), " ") + var ( + parts = strings.Split(strings.TrimSpace(opt), " ") + value = strings.TrimSpace(parts[1]) + ) switch parts[0] { case "cap": - value := strings.TrimSpace(parts[1]) c := container.CapabilitiesMask.Get(value[1:]) if c == nil { continue @@ -93,6 +95,16 @@ func configureCustomOptions(container *libcontainer.Container, opts []string) { 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 + } } } } From be5538d8a8820ac1192c7a5660e0d950927b42d0 Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Fri, 21 Mar 2014 00:48:17 +0000 Subject: [PATCH 09/21] Allow containers to join the net namespace of other conatiners Docker-DCO-1.1-Signed-off-by: Michael Crosby (github: crosbymichael) --- pkg/libcontainer/network/netns.go | 10 +------ runtime/execdriver/native/default_template.go | 28 +++++++++++++++++-- runtime/execdriver/native/driver.go | 14 ++++++---- 3 files changed, 35 insertions(+), 17 deletions(-) diff --git a/pkg/libcontainer/network/netns.go b/pkg/libcontainer/network/netns.go index 3eb8ee587a..7e311f22d8 100644 --- a/pkg/libcontainer/network/netns.go +++ b/pkg/libcontainer/network/netns.go @@ -14,13 +14,7 @@ type NetNS struct { } func (v *NetNS) Create(n *libcontainer.Network, nspid int, context libcontainer.Context) error { - nsname, exists := n.Context["nsname"] - - if !exists { - return fmt.Errorf("nspath does not exist in network context") - } - - context["nspath"] = fmt.Sprintf("/var/run/netns/%s", nsname) + context["nspath"] = n.Context["nspath"] return nil } @@ -29,12 +23,10 @@ func (v *NetNS) Initialize(config *libcontainer.Network, context libcontainer.Co if !exists { return fmt.Errorf("nspath does not exist in network context") } - f, err := os.OpenFile(nspath, os.O_RDONLY, 0) if err != nil { return fmt.Errorf("failed get network namespace fd: %v", err) } - if err := system.Setns(f.Fd(), syscall.CLONE_NEWNET); err != nil { return fmt.Errorf("failed to setns current network namespace: %v", err) } diff --git a/runtime/execdriver/native/default_template.go b/runtime/execdriver/native/default_template.go index dbb7a45ae7..890e260ad2 100644 --- a/runtime/execdriver/native/default_template.go +++ b/runtime/execdriver/native/default_template.go @@ -6,12 +6,13 @@ import ( "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 createContainer(c *execdriver.Command) *libcontainer.Container { +func (d *driver) createContainer(c *execdriver.Command) *libcontainer.Container { container := getDefaultTemplate() container.Hostname = getEnv("HOSTNAME", c.Env) @@ -64,7 +65,7 @@ func createContainer(c *execdriver.Command) *libcontainer.Container { container.Mounts = append(container.Mounts, libcontainer.Mount{m.Source, m.Destination, m.Writable, m.Private}) } - configureCustomOptions(container, c.Config["native"]) + d.configureCustomOptions(container, c.Config["native"]) return container } @@ -75,7 +76,8 @@ func createContainer(c *execdriver.Command) *libcontainer.Container { // format: // i.e: cap +MKNOD cap -NET_ADMIN // i.e: cgroup devices.allow *:* -func configureCustomOptions(container *libcontainer.Container, opts []string) { +// i.e: net join +func (d *driver) configureCustomOptions(container *libcontainer.Container, opts []string) { for _, opt := range opts { var ( parts = strings.Split(strings.TrimSpace(opt), " ") @@ -105,6 +107,26 @@ func configureCustomOptions(container *libcontainer.Container, opts []string) { 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 } } } diff --git a/runtime/execdriver/native/driver.go b/runtime/execdriver/native/driver.go index 0d9297191c..b998db743d 100644 --- a/runtime/execdriver/native/driver.go +++ b/runtime/execdriver/native/driver.go @@ -57,8 +57,9 @@ func init() { } type driver struct { - root string - initPath string + root string + initPath string + activeContainers map[string]*execdriver.Command } func NewDriver(root, initPath string) (*driver, error) { @@ -69,15 +70,18 @@ func NewDriver(root, initPath string) (*driver, error) { return nil, err } return &driver{ - root: root, - initPath: initPath, + root: root, + initPath: initPath, + activeContainers: make(map[string]*execdriver.Command), }, nil } func (d *driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallback execdriver.StartCallback) (int, error) { + d.activeContainers[c.ID] = c + var ( term nsinit.Terminal - container = createContainer(c) + container = d.createContainer(c) factory = &dockerCommandFactory{c: c, driver: d} stateWriter = &dockerStateWriter{ callback: startCallback, From 7c726669cbcc0cfde12c6a9f03974bb672839271 Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Fri, 21 Mar 2014 08:10:07 +0000 Subject: [PATCH 10/21] Factor out the native driver config options Docker-DCO-1.1-Signed-off-by: Michael Crosby (github: crosbymichael) --- runtime/container.go | 4 + .../execdriver/native/configuration/caps.go | 27 ++++ .../execdriver/native/configuration/net.go | 35 +++++ runtime/execdriver/native/configuration/ns.go | 26 ++++ .../execdriver/native/configuration/parse.go | 37 +++++ runtime/execdriver/native/create.go | 70 ++++++++++ runtime/execdriver/native/default_template.go | 126 ------------------ runtime/execdriver/native/driver.go | 12 +- 8 files changed, 207 insertions(+), 130 deletions(-) create mode 100644 runtime/execdriver/native/configuration/caps.go create mode 100644 runtime/execdriver/native/configuration/net.go create mode 100644 runtime/execdriver/native/configuration/ns.go create mode 100644 runtime/execdriver/native/configuration/parse.go create mode 100644 runtime/execdriver/native/create.go 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, From c9d7f858fd1c6e2d1287e28ee12952333b327c75 Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Fri, 21 Mar 2014 11:53:15 +0000 Subject: [PATCH 11/21] Change flag to -o and --opt Docker-DCO-1.1-Signed-off-by: Michael Crosby (github: crosbymichael) --- runconfig/hostconfig.go | 4 ++-- runconfig/parse.go | 20 ++++++++++++------- runtime/container.go | 2 +- runtime/execdriver/native/configuration/fs.go | 19 ++++++++++++++++++ .../execdriver/native/configuration/parse.go | 3 +++ 5 files changed, 38 insertions(+), 10 deletions(-) create mode 100644 runtime/execdriver/native/configuration/fs.go diff --git a/runconfig/hostconfig.go b/runconfig/hostconfig.go index 8ee2288b4b..b564f98cd3 100644 --- a/runconfig/hostconfig.go +++ b/runconfig/hostconfig.go @@ -13,7 +13,7 @@ type HostConfig struct { PortBindings nat.PortMap Links []string PublishAllPorts bool - PluginOptions map[string][]string + DriverOptions map[string][]string } type KeyValuePair struct { @@ -29,7 +29,7 @@ func ContainerHostConfigFromJob(job *engine.Job) *HostConfig { } job.GetenvJson("LxcConf", &hostConfig.LxcConf) job.GetenvJson("PortBindings", &hostConfig.PortBindings) - job.GetenvJson("PluginOptions", &hostConfig.PluginOptions) + job.GetenvJson("DriverOptions", &hostConfig.DriverOptions) if Binds := job.GetenvList("Binds"); Binds != nil { hostConfig.Binds = Binds } diff --git a/runconfig/parse.go b/runconfig/parse.go index afcaec304f..2f51dface2 100644 --- a/runconfig/parse.go +++ b/runconfig/parse.go @@ -45,7 +45,7 @@ func parseRun(cmd *flag.FlagSet, args []string, sysInfo *sysinfo.SysInfo) (*Conf flDnsSearch = opts.NewListOpts(opts.ValidateDomain) flVolumesFrom opts.ListOpts flLxcOpts opts.ListOpts - flPluginOpts opts.ListOpts + flDriverOpts opts.ListOpts flAutoRemove = cmd.Bool([]string{"#rm", "-rm"}, false, "Automatically remove the container when it exits (incompatible with -d)") flDetach = cmd.Bool([]string{"d", "-detach"}, false, "Detached mode: Run container in the background, print new container id") @@ -77,8 +77,8 @@ func parseRun(cmd *flag.FlagSet, args []string, sysInfo *sysinfo.SysInfo) (*Conf cmd.Var(&flDns, []string{"#dns", "-dns"}, "Set custom dns servers") cmd.Var(&flDnsSearch, []string{"-dns-search"}, "Set custom dns search domains") cmd.Var(&flVolumesFrom, []string{"#volumes-from", "-volumes-from"}, "Mount volumes from the specified container(s)") - cmd.Var(&flLxcOpts, []string{"#lxc-conf", "-lxc-conf"}, "Add custom lxc options --lxc-conf=\"lxc.cgroup.cpuset.cpus = 0,1\"") - cmd.Var(&flPluginOpts, []string{"-plugin"}, "Add custom plugin options") + cmd.Var(&flLxcOpts, []string{"#lxc-conf", "#-lxc-conf"}, "Add custom lxc options --lxc-conf=\"lxc.cgroup.cpuset.cpus = 0,1\"") + cmd.Var(&flDriverOpts, []string{"o", "-opt"}, "Add custom driver options") if err := cmd.Parse(args); err != nil { return nil, nil, cmd, err @@ -208,7 +208,10 @@ func parseRun(cmd *flag.FlagSet, args []string, sysInfo *sysinfo.SysInfo) (*Conf WorkingDir: *flWorkingDir, } - pluginOptions := parsePluginOpts(flPluginOpts) + pluginOptions, err := parseDriverOpts(flDriverOpts) + if err != nil { + return nil, nil, cmd, err + } hostConfig := &HostConfig{ Binds: binds, @@ -218,7 +221,7 @@ func parseRun(cmd *flag.FlagSet, args []string, sysInfo *sysinfo.SysInfo) (*Conf PortBindings: portBindings, Links: flLinks.GetAll(), PublishAllPorts: *flPublishAll, - PluginOptions: pluginOptions, + DriverOptions: pluginOptions, } if sysInfo != nil && flMemory > 0 && !sysInfo.SwapLimit { @@ -253,15 +256,18 @@ func parseLxcOpt(opt string) (string, string, error) { return strings.TrimSpace(parts[0]), strings.TrimSpace(parts[1]), nil } -func parsePluginOpts(opts opts.ListOpts) map[string][]string { +func parseDriverOpts(opts opts.ListOpts) (map[string][]string, error) { out := make(map[string][]string, len(opts.GetAll())) for _, o := range opts.GetAll() { parts := strings.SplitN(o, " ", 2) + if len(parts) < 2 { + return nil, fmt.Errorf("invalid opt format %s", o) + } values, exists := out[parts[0]] if !exists { values = []string{} } out[parts[0]] = append(values, parts[1]) } - return out + return out, nil } diff --git a/runtime/container.go b/runtime/container.go index 1f0f82eebb..ee5045e374 100644 --- a/runtime/container.go +++ b/runtime/container.go @@ -361,7 +361,7 @@ func (container *Container) Attach(stdin io.ReadCloser, stdinCloser io.Closer, s func populateCommand(c *Container) { var ( en *execdriver.Network - driverConfig = c.hostConfig.PluginOptions + driverConfig = c.hostConfig.DriverOptions ) if driverConfig == nil { diff --git a/runtime/execdriver/native/configuration/fs.go b/runtime/execdriver/native/configuration/fs.go new file mode 100644 index 0000000000..76fb2f08da --- /dev/null +++ b/runtime/execdriver/native/configuration/fs.go @@ -0,0 +1,19 @@ +package configuration + +import ( + "fmt" + "github.com/dotcloud/docker/pkg/libcontainer" + "strings" +) + +func parseFsOpts(container *libcontainer.Container, opts []string) error { + opt := strings.TrimSpace(opts[0]) + + switch opt { + case "readonly": + container.ReadonlyFs = true + default: + return fmt.Errorf("%s is not a valid filesystem option", opt) + } + return nil +} diff --git a/runtime/execdriver/native/configuration/parse.go b/runtime/execdriver/native/configuration/parse.go index 08b98fbd12..083fd43371 100644 --- a/runtime/execdriver/native/configuration/parse.go +++ b/runtime/execdriver/native/configuration/parse.go @@ -18,6 +18,9 @@ func ParseConfiguration(container *libcontainer.Container, running map[string]*e err error parts = strings.Split(strings.TrimSpace(opt), " ") ) + if len(parts) < 2 { + return fmt.Errorf("invalid native driver opt %s", opt) + } switch parts[0] { case "cap": From 146a212f71fe129f9d349c5c3e80ba4197e35850 Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Fri, 21 Mar 2014 12:38:50 +0000 Subject: [PATCH 12/21] Change syntax to use dots Docker-DCO-1.1-Signed-off-by: Michael Crosby (github: crosbymichael) --- runconfig/parse.go | 3 +- .../execdriver/native/configuration/caps.go | 27 ----- runtime/execdriver/native/configuration/fs.go | 19 ---- .../execdriver/native/configuration/net.go | 35 ------ runtime/execdriver/native/configuration/ns.go | 26 ----- .../execdriver/native/configuration/parse.go | 101 +++++++++++++++--- 6 files changed, 87 insertions(+), 124 deletions(-) delete mode 100644 runtime/execdriver/native/configuration/caps.go delete mode 100644 runtime/execdriver/native/configuration/fs.go delete mode 100644 runtime/execdriver/native/configuration/net.go delete mode 100644 runtime/execdriver/native/configuration/ns.go diff --git a/runconfig/parse.go b/runconfig/parse.go index 2f51dface2..b03f8732ee 100644 --- a/runconfig/parse.go +++ b/runconfig/parse.go @@ -256,10 +256,11 @@ func parseLxcOpt(opt string) (string, string, error) { return strings.TrimSpace(parts[0]), strings.TrimSpace(parts[1]), nil } +// options will come in the format of name.type=value func parseDriverOpts(opts opts.ListOpts) (map[string][]string, error) { out := make(map[string][]string, len(opts.GetAll())) for _, o := range opts.GetAll() { - parts := strings.SplitN(o, " ", 2) + parts := strings.SplitN(o, ".", 2) if len(parts) < 2 { return nil, fmt.Errorf("invalid opt format %s", o) } diff --git a/runtime/execdriver/native/configuration/caps.go b/runtime/execdriver/native/configuration/caps.go deleted file mode 100644 index f4de470684..0000000000 --- a/runtime/execdriver/native/configuration/caps.go +++ /dev/null @@ -1,27 +0,0 @@ -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/fs.go b/runtime/execdriver/native/configuration/fs.go deleted file mode 100644 index 76fb2f08da..0000000000 --- a/runtime/execdriver/native/configuration/fs.go +++ /dev/null @@ -1,19 +0,0 @@ -package configuration - -import ( - "fmt" - "github.com/dotcloud/docker/pkg/libcontainer" - "strings" -) - -func parseFsOpts(container *libcontainer.Container, opts []string) error { - opt := strings.TrimSpace(opts[0]) - - switch opt { - case "readonly": - container.ReadonlyFs = true - default: - return fmt.Errorf("%s is not a valid filesystem option", opt) - } - return nil -} diff --git a/runtime/execdriver/native/configuration/net.go b/runtime/execdriver/native/configuration/net.go deleted file mode 100644 index cac7f658ba..0000000000 --- a/runtime/execdriver/native/configuration/net.go +++ /dev/null @@ -1,35 +0,0 @@ -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 deleted file mode 100644 index ff7f367196..0000000000 --- a/runtime/execdriver/native/configuration/ns.go +++ /dev/null @@ -1,26 +0,0 @@ -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 index 083fd43371..0003d724b3 100644 --- a/runtime/execdriver/native/configuration/parse.go +++ b/runtime/execdriver/native/configuration/parse.go @@ -4,9 +4,86 @@ import ( "fmt" "github.com/dotcloud/docker/pkg/libcontainer" "os/exec" + "path/filepath" "strings" ) +type Action func(*libcontainer.Container, interface{}, string) error + +var actions = map[string]Action{ + "cap.add": addCap, + "cap.drop": dropCap, + "fs.readonly": readonlyFs, + "ns.add": addNamespace, + "ns.drop": dropNamespace, + "net.join": joinNetNamespace, +} + +func addCap(container *libcontainer.Container, context interface{}, value string) error { + c := container.CapabilitiesMask.Get(value) + if c == nil { + return fmt.Errorf("%s is not a valid capability", value) + } + c.Enabled = true + return nil +} + +func dropCap(container *libcontainer.Container, context interface{}, value string) error { + c := container.CapabilitiesMask.Get(value) + if c == nil { + return fmt.Errorf("%s is not a valid capability", value) + } + c.Enabled = false + return nil +} + +func addNamespace(container *libcontainer.Container, context interface{}, value string) error { + ns := container.Namespaces.Get(value) + if ns == nil { + return fmt.Errorf("%s is not a valid namespace", value[1:]) + } + ns.Enabled = true + return nil +} + +func dropNamespace(container *libcontainer.Container, context interface{}, value string) error { + ns := container.Namespaces.Get(value) + if ns == nil { + return fmt.Errorf("%s is not a valid namespace", value[1:]) + } + ns.Enabled = false + return nil +} + +func readonlyFs(container *libcontainer.Container, context interface{}, value string) error { + switch value { + case "1", "true": + container.ReadonlyFs = true + default: + container.ReadonlyFs = false + } + return nil +} + +func joinNetNamespace(container *libcontainer.Container, context interface{}, value string) error { + var ( + running = context.(map[string]*exec.Cmd) + cmd = running[value] + ) + + if cmd == nil || cmd.Process == nil { + return fmt.Errorf("%s is not a valid running container to join", value) + } + 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, + }, + }) + return nil +} + // configureCustomOptions takes string commands from the user and allows modification of the // container's default configuration. // @@ -14,25 +91,17 @@ import ( // 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), " ") - ) - if len(parts) < 2 { - return fmt.Errorf("invalid native driver opt %s", opt) + kv := strings.SplitN(opt, "=", 2) + if len(kv) < 2 { + return fmt.Errorf("invalid format for %s", 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]) + action, exists := actions[kv[0]] + if !exists { + return fmt.Errorf("%s is not a valid option for the native driver", kv[0]) } - if err != nil { + + if err := action(container, running, kv[1]); err != nil { return err } } From 83618c2b81c561cd77fd70eca90b2b251f61fcc1 Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Fri, 21 Mar 2014 14:07:16 +0000 Subject: [PATCH 13/21] Add more native driver options Docker-DCO-1.1-Signed-off-by: Michael Crosby (github: crosbymichael) --- .../execdriver/native/configuration/parse.go | 80 +++++++++++++++++-- 1 file changed, 74 insertions(+), 6 deletions(-) diff --git a/runtime/execdriver/native/configuration/parse.go b/runtime/execdriver/native/configuration/parse.go index 0003d724b3..1733b94426 100644 --- a/runtime/execdriver/native/configuration/parse.go +++ b/runtime/execdriver/native/configuration/parse.go @@ -5,18 +5,70 @@ import ( "github.com/dotcloud/docker/pkg/libcontainer" "os/exec" "path/filepath" + "strconv" "strings" ) type Action func(*libcontainer.Container, interface{}, string) error var actions = map[string]Action{ - "cap.add": addCap, - "cap.drop": dropCap, - "fs.readonly": readonlyFs, - "ns.add": addNamespace, - "ns.drop": dropNamespace, - "net.join": joinNetNamespace, + "cap.add": addCap, // add a cap + "cap.drop": dropCap, // drop a cap + + "ns.add": addNamespace, // add a namespace + "ns.drop": dropNamespace, // drop a namespace when cloning + + "net.join": joinNetNamespace, // join another containers net namespace + // "net.veth.mac": vethMacAddress, // set the mac address for the veth + + "cgroups.cpu_shares": cpuShares, // set the cpu shares + "cgroups.memory": memory, // set the memory limit + "cgroups.memory_swap": memorySwap, // set the memory swap limit + + "apparmor_profile": apparmorProfile, // set the apparmor profile to apply + + "fs.readonly": readonlyFs, // make the rootfs of the container read only +} + +func apparmorProfile(container *libcontainer.Container, context interface{}, value string) error { + container.Context["apparmor_profile"] = value + return nil +} + +func cpuShares(container *libcontainer.Container, context interface{}, value string) error { + if container.Cgroups == nil { + return fmt.Errorf("cannot set cgroups when they are disabled") + } + v, err := strconv.ParseInt(value, 0, 64) + if err != nil { + return err + } + container.Cgroups.CpuShares = v + return nil +} + +func memory(container *libcontainer.Container, context interface{}, value string) error { + if container.Cgroups == nil { + return fmt.Errorf("cannot set cgroups when they are disabled") + } + v, err := strconv.ParseInt(value, 0, 64) + if err != nil { + return err + } + container.Cgroups.Memory = v + return nil +} + +func memorySwap(container *libcontainer.Container, context interface{}, value string) error { + if container.Cgroups == nil { + return fmt.Errorf("cannot set cgroups when they are disabled") + } + v, err := strconv.ParseInt(value, 0, 64) + if err != nil { + return err + } + container.Cgroups.MemorySwap = v + return nil } func addCap(container *libcontainer.Container, context interface{}, value string) error { @@ -84,6 +136,22 @@ func joinNetNamespace(container *libcontainer.Container, context interface{}, va return nil } +func vethMacAddress(container *libcontainer.Container, context interface{}, value string) error { + var veth *libcontainer.Network + + for _, network := range container.Networks { + if network.Type == "veth" { + veth = network + break + } + } + if veth == nil { + return fmt.Errorf("not veth configured for container") + } + veth.Context["mac"] = value + return nil +} + // configureCustomOptions takes string commands from the user and allows modification of the // container's default configuration. // From 2c58a1e2886433a4266615b1f492f829e7a6f53f Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Fri, 21 Mar 2014 14:17:17 +0000 Subject: [PATCH 14/21] Change placement of readonly filesystem We need to change it to read only at the very end so that bound, copy dev nodes and other ops do not fail. Docker-DCO-1.1-Signed-off-by: Michael Crosby (github: crosbymichael) --- pkg/libcontainer/nsinit/mount.go | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/pkg/libcontainer/nsinit/mount.go b/pkg/libcontainer/nsinit/mount.go index 61a90125e0..19dacfaa17 100644 --- a/pkg/libcontainer/nsinit/mount.go +++ b/pkg/libcontainer/nsinit/mount.go @@ -31,11 +31,6 @@ func setupNewMountNamespace(rootfs string, bindMounts []libcontainer.Mount, cons if err := system.Mount(rootfs, rootfs, "bind", syscall.MS_BIND|syscall.MS_REC, ""); err != nil { return fmt.Errorf("mouting %s as bind %s", rootfs, err) } - if readonly { - if err := system.Mount(rootfs, rootfs, "bind", syscall.MS_BIND|syscall.MS_REMOUNT|syscall.MS_RDONLY|syscall.MS_REC, ""); err != nil { - return fmt.Errorf("mounting %s as readonly %s", rootfs, err) - } - } if err := mountSystem(rootfs); err != nil { return fmt.Errorf("mount system %s", err) } @@ -81,6 +76,12 @@ func setupNewMountNamespace(rootfs string, bindMounts []libcontainer.Mount, cons } } + if readonly { + if err := system.Mount("/", "/", "bind", syscall.MS_BIND|syscall.MS_REMOUNT|syscall.MS_RDONLY|syscall.MS_REC, ""); err != nil { + return fmt.Errorf("mounting %s as readonly %s", rootfs, err) + } + } + system.Umask(0022) return nil From 9a7be1b015a1ba79e5480d0ddddfa5954b994507 Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Fri, 21 Mar 2014 14:53:47 +0000 Subject: [PATCH 15/21] Add cpuset.cpus to cgroups and native driver options Docker-DCO-1.1-Signed-off-by: Michael Crosby (github: crosbymichael) --- pkg/cgroups/cgroups.go | 32 ++++++++++++++--- .../execdriver/native/configuration/parse.go | 35 +++++++++++++++---- 2 files changed, 57 insertions(+), 10 deletions(-) diff --git a/pkg/cgroups/cgroups.go b/pkg/cgroups/cgroups.go index b40e1a31fa..9d485d1080 100644 --- a/pkg/cgroups/cgroups.go +++ b/pkg/cgroups/cgroups.go @@ -16,10 +16,11 @@ type Cgroup struct { Name string `json:"name,omitempty"` Parent string `json:"parent,omitempty"` - DeviceAccess bool `json:"device_access,omitempty"` // name of parent cgroup or slice - Memory int64 `json:"memory,omitempty"` // Memory limit (in bytes) - MemorySwap int64 `json:"memory_swap,omitempty"` // Total memory usage (memory + swap); set `-1' to disable swap - CpuShares int64 `json:"cpu_shares,omitempty"` // CPU shares (relative weight vs. other containers) + DeviceAccess bool `json:"device_access,omitempty"` // name of parent cgroup or slice + Memory int64 `json:"memory,omitempty"` // Memory limit (in bytes) + MemorySwap int64 `json:"memory_swap,omitempty"` // Total memory usage (memory + swap); set `-1' to disable swap + CpuShares int64 `json:"cpu_shares,omitempty"` // CPU shares (relative weight vs. other containers) + CpusetCpus string `json:"cpuset_cpus,omitempty"` // CPU to use } // https://www.kernel.org/doc/Documentation/cgroups/cgroups.txt @@ -98,6 +99,7 @@ func (c *Cgroup) Cleanup(root string) error { get("memory"), get("devices"), get("cpu"), + get("cpuset"), } { os.RemoveAll(path) } @@ -150,6 +152,9 @@ func (c *Cgroup) Apply(pid int) error { if err := c.setupCpu(cgroupRoot, pid); err != nil { return err } + if err := c.setupCpuset(cgroupRoot, pid); err != nil { + return err + } return nil } @@ -248,3 +253,22 @@ func (c *Cgroup) setupCpu(cgroupRoot string, pid int) (err error) { } return nil } + +func (c *Cgroup) setupCpuset(cgroupRoot string, pid int) (err error) { + if c.CpusetCpus != "" { + dir, err := c.Join(cgroupRoot, "cpuset", pid) + if err != nil { + return err + } + defer func() { + if err != nil { + os.RemoveAll(dir) + } + }() + + if err := writeFile(dir, "cpuset.cpus", c.CpusetCpus); err != nil { + return err + } + } + return nil +} diff --git a/runtime/execdriver/native/configuration/parse.go b/runtime/execdriver/native/configuration/parse.go index 1733b94426..090cb29660 100644 --- a/runtime/execdriver/native/configuration/parse.go +++ b/runtime/execdriver/native/configuration/parse.go @@ -3,6 +3,7 @@ package configuration import ( "fmt" "github.com/dotcloud/docker/pkg/libcontainer" + "github.com/dotcloud/docker/utils" "os/exec" "path/filepath" "strconv" @@ -19,17 +20,40 @@ var actions = map[string]Action{ "ns.drop": dropNamespace, // drop a namespace when cloning "net.join": joinNetNamespace, // join another containers net namespace - // "net.veth.mac": vethMacAddress, // set the mac address for the veth "cgroups.cpu_shares": cpuShares, // set the cpu shares "cgroups.memory": memory, // set the memory limit "cgroups.memory_swap": memorySwap, // set the memory swap limit + "cgroups.cpuset.cpus": cpusetCpus, // set the cpus used "apparmor_profile": apparmorProfile, // set the apparmor profile to apply "fs.readonly": readonlyFs, // make the rootfs of the container read only } +// GetSupportedActions returns a list of all the avaliable actions supported by the driver +// TODO: this should return a description also +func GetSupportedActions() []string { + var ( + i int + out = make([]string, len(actions)) + ) + for k := range actions { + out[i] = k + i++ + } + return out +} + +func cpusetCpus(container *libcontainer.Container, context interface{}, value string) error { + if container.Cgroups == nil { + return fmt.Errorf("cannot set cgroups when they are disabled") + } + container.Cgroups.CpusetCpus = value + + return nil +} + func apparmorProfile(container *libcontainer.Container, context interface{}, value string) error { container.Context["apparmor_profile"] = value return nil @@ -39,7 +63,7 @@ func cpuShares(container *libcontainer.Container, context interface{}, value str if container.Cgroups == nil { return fmt.Errorf("cannot set cgroups when they are disabled") } - v, err := strconv.ParseInt(value, 0, 64) + v, err := strconv.ParseInt(value, 10, 0) if err != nil { return err } @@ -51,7 +75,8 @@ func memory(container *libcontainer.Container, context interface{}, value string if container.Cgroups == nil { return fmt.Errorf("cannot set cgroups when they are disabled") } - v, err := strconv.ParseInt(value, 0, 64) + + v, err := utils.RAMInBytes(value) if err != nil { return err } @@ -138,7 +163,6 @@ func joinNetNamespace(container *libcontainer.Container, context interface{}, va func vethMacAddress(container *libcontainer.Container, context interface{}, value string) error { var veth *libcontainer.Network - for _, network := range container.Networks { if network.Type == "veth" { veth = network @@ -155,8 +179,7 @@ func vethMacAddress(container *libcontainer.Container, context interface{}, valu // configureCustomOptions takes string commands from the user and allows modification of the // container's default configuration. // -// format: <...value> -// i.e: cgroup devices.allow *:* +// TODO: this can be moved to a general utils or parser in pkg func ParseConfiguration(container *libcontainer.Container, running map[string]*exec.Cmd, opts []string) error { for _, opt := range opts { kv := strings.SplitN(opt, "=", 2) From 10fdbc0467d1be6c7c731d3f35590d87ee42f96f Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Mon, 24 Mar 2014 07:16:40 +0000 Subject: [PATCH 16/21] Add unit test for lxc conf merge and native opts Docker-DCO-1.1-Signed-off-by: Michael Crosby (github: crosbymichael) --- runtime/container.go | 10 +- runtime/execdriver/lxc/lxc_template.go | 2 +- .../execdriver/native/configuration/parse.go | 14 -- .../native/configuration/parse_test.go | 166 ++++++++++++++++++ runtime/execdriver/native/create.go | 66 ++++--- .../native/{ => template}/default_template.go | 7 +- runtime/utils.go | 20 +++ runtime/utils_test.go | 27 +++ 8 files changed, 265 insertions(+), 47 deletions(-) create mode 100644 runtime/execdriver/native/configuration/parse_test.go rename runtime/execdriver/native/{ => template}/default_template.go (88%) create mode 100644 runtime/utils_test.go diff --git a/runtime/container.go b/runtime/container.go index ee5045e374..be162c7a21 100644 --- a/runtime/container.go +++ b/runtime/container.go @@ -383,14 +383,8 @@ func populateCommand(c *Container) { } } - // merge in the lxc conf options into the generic config map - if lxcConf := c.hostConfig.LxcConf; lxcConf != nil { - lxc := driverConfig["lxc"] - for _, pair := range lxcConf { - lxc = append(lxc, fmt.Sprintf("%s = %s", pair.Key, pair.Value)) - } - driverConfig["lxc"] = lxc - } + // TODO: this can be removed after lxc-conf is fully deprecated + mergeLxcConfIntoOptions(c.hostConfig, driverConfig) resources := &execdriver.Resources{ Memory: c.Config.Memory, diff --git a/runtime/execdriver/lxc/lxc_template.go b/runtime/execdriver/lxc/lxc_template.go index 7979e4f284..c5ba876dce 100644 --- a/runtime/execdriver/lxc/lxc_template.go +++ b/runtime/execdriver/lxc/lxc_template.go @@ -120,7 +120,7 @@ lxc.cgroup.cpu.shares = {{.Resources.CpuShares}} {{if .Config.lxc}} {{range $value := .Config.lxc}} -{{$value}} +lxc.{{$value}} {{end}} {{end}} ` diff --git a/runtime/execdriver/native/configuration/parse.go b/runtime/execdriver/native/configuration/parse.go index 090cb29660..6d6c643919 100644 --- a/runtime/execdriver/native/configuration/parse.go +++ b/runtime/execdriver/native/configuration/parse.go @@ -31,20 +31,6 @@ var actions = map[string]Action{ "fs.readonly": readonlyFs, // make the rootfs of the container read only } -// GetSupportedActions returns a list of all the avaliable actions supported by the driver -// TODO: this should return a description also -func GetSupportedActions() []string { - var ( - i int - out = make([]string, len(actions)) - ) - for k := range actions { - out[i] = k - i++ - } - return out -} - func cpusetCpus(container *libcontainer.Container, context interface{}, value string) error { if container.Cgroups == nil { return fmt.Errorf("cannot set cgroups when they are disabled") diff --git a/runtime/execdriver/native/configuration/parse_test.go b/runtime/execdriver/native/configuration/parse_test.go new file mode 100644 index 0000000000..8001358766 --- /dev/null +++ b/runtime/execdriver/native/configuration/parse_test.go @@ -0,0 +1,166 @@ +package configuration + +import ( + "github.com/dotcloud/docker/runtime/execdriver/native/template" + "testing" +) + +func TestSetReadonlyRootFs(t *testing.T) { + var ( + container = template.New() + opts = []string{ + "fs.readonly=true", + } + ) + + if container.ReadonlyFs { + t.Fatal("container should not have a readonly rootfs by default") + } + if err := ParseConfiguration(container, nil, opts); err != nil { + t.Fatal(err) + } + + if !container.ReadonlyFs { + t.Fatal("container should have a readonly rootfs") + } +} + +func TestConfigurationsDoNotConflict(t *testing.T) { + var ( + container1 = template.New() + container2 = template.New() + opts = []string{ + "cap.add=NET_ADMIN", + } + ) + + if err := ParseConfiguration(container1, nil, opts); err != nil { + t.Fatal(err) + } + + if !container1.CapabilitiesMask.Get("NET_ADMIN").Enabled { + t.Fatal("container one should have NET_ADMIN enabled") + } + if container2.CapabilitiesMask.Get("NET_ADMIN").Enabled { + t.Fatal("container two should not have NET_ADMIN enabled") + } +} + +func TestCpusetCpus(t *testing.T) { + var ( + container = template.New() + opts = []string{ + "cgroups.cpuset.cpus=1,2", + } + ) + if err := ParseConfiguration(container, nil, opts); err != nil { + t.Fatal(err) + } + + if expected := "1,2"; container.Cgroups.CpusetCpus != expected { + t.Fatalf("expected %s got %s for cpuset.cpus", expected, container.Cgroups.CpusetCpus) + } +} + +func TestAppArmorProfile(t *testing.T) { + var ( + container = template.New() + opts = []string{ + "apparmor_profile=koye-the-protector", + } + ) + if err := ParseConfiguration(container, nil, opts); err != nil { + t.Fatal(err) + } + if expected := "koye-the-protector"; container.Context["apparmor_profile"] != expected { + t.Fatalf("expected profile %s got %s", expected, container.Context["apparmor_profile"]) + } +} + +func TestCpuShares(t *testing.T) { + var ( + container = template.New() + opts = []string{ + "cgroups.cpu_shares=1048", + } + ) + if err := ParseConfiguration(container, nil, opts); err != nil { + t.Fatal(err) + } + + if expected := int64(1048); container.Cgroups.CpuShares != expected { + t.Fatalf("expected cpu shares %d got %d", expected, container.Cgroups.CpuShares) + } +} + +func TestCgroupMemory(t *testing.T) { + var ( + container = template.New() + opts = []string{ + "cgroups.memory=500m", + } + ) + if err := ParseConfiguration(container, nil, opts); err != nil { + t.Fatal(err) + } + + if expected := int64(500 * 1024 * 1024); container.Cgroups.Memory != expected { + t.Fatalf("expected memory %d got %d", expected, container.Cgroups.Memory) + } +} + +func TestAddCap(t *testing.T) { + var ( + container = template.New() + opts = []string{ + "cap.add=MKNOD", + "cap.add=SYS_ADMIN", + } + ) + if err := ParseConfiguration(container, nil, opts); err != nil { + t.Fatal(err) + } + + if !container.CapabilitiesMask.Get("MKNOD").Enabled { + t.Fatal("container should have MKNOD enabled") + } + if !container.CapabilitiesMask.Get("SYS_ADMIN").Enabled { + t.Fatal("container should have SYS_ADMIN enabled") + } +} + +func TestDropCap(t *testing.T) { + var ( + container = template.New() + opts = []string{ + "cap.drop=MKNOD", + } + ) + // enabled all caps like in privileged mode + for _, c := range container.CapabilitiesMask { + c.Enabled = true + } + if err := ParseConfiguration(container, nil, opts); err != nil { + t.Fatal(err) + } + + if container.CapabilitiesMask.Get("MKNOD").Enabled { + t.Fatal("container should not have MKNOD enabled") + } +} + +func TestDropNamespace(t *testing.T) { + var ( + container = template.New() + opts = []string{ + "ns.drop=NEWNET", + } + ) + if err := ParseConfiguration(container, nil, opts); err != nil { + t.Fatal(err) + } + + if container.Namespaces.Get("NEWNET").Enabled { + t.Fatal("container should not have NEWNET enabled") + } +} diff --git a/runtime/execdriver/native/create.go b/runtime/execdriver/native/create.go index 7118edc91e..7e663f0555 100644 --- a/runtime/execdriver/native/create.go +++ b/runtime/execdriver/native/create.go @@ -5,30 +5,53 @@ import ( "github.com/dotcloud/docker/pkg/libcontainer" "github.com/dotcloud/docker/runtime/execdriver" "github.com/dotcloud/docker/runtime/execdriver/native/configuration" + "github.com/dotcloud/docker/runtime/execdriver/native/template" "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 := template.New() container.Hostname = getEnv("HOSTNAME", c.Env) container.Tty = c.Tty container.User = c.User container.WorkingDir = c.WorkingDir container.Env = c.Env + container.Cgroups.Name = c.ID + // check to see if we are running in ramdisk to disable pivot root + container.NoPivotRoot = os.Getenv("DOCKER_RAMDISK") != "" - loopbackNetwork := libcontainer.Network{ - Mtu: c.Network.Mtu, - Address: fmt.Sprintf("%s/%d", "127.0.0.1", 0), - Gateway: "localhost", - Type: "loopback", - Context: libcontainer.Context{}, + if err := d.createNetwork(container, c); err != nil { + return nil, err } + if c.Privileged { + if err := d.setPrivileged(container); err != nil { + return nil, err + } + } + if err := d.setupCgroups(container, c); err != nil { + return nil, err + } + if err := d.setupMounts(container, c); err != nil { + return nil, err + } + if err := configuration.ParseConfiguration(container, d.activeContainers, c.Config["native"]); err != nil { + return nil, err + } + return container, nil +} +func (d *driver) createNetwork(container *libcontainer.Container, c *execdriver.Command) error { container.Networks = []*libcontainer.Network{ - &loopbackNetwork, + { + Mtu: c.Network.Mtu, + Address: fmt.Sprintf("%s/%d", "127.0.0.1", 0), + Gateway: "localhost", + Type: "loopback", + Context: libcontainer.Context{}, + }, } if c.Network.Interface != nil { @@ -44,27 +67,30 @@ func (d *driver) createContainer(c *execdriver.Command) (*libcontainer.Container } container.Networks = append(container.Networks, &vethNetwork) } + return nil +} - container.Cgroups.Name = c.ID - if c.Privileged { - container.CapabilitiesMask = nil - container.Cgroups.DeviceAccess = true - container.Context["apparmor_profile"] = "unconfined" +func (d *driver) setPrivileged(container *libcontainer.Container) error { + for _, c := range container.CapabilitiesMask { + c.Enabled = true } + container.Cgroups.DeviceAccess = true + container.Context["apparmor_profile"] = "unconfined" + return nil +} + +func (d *driver) setupCgroups(container *libcontainer.Container, c *execdriver.Command) error { 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") != "" + return nil +} +func (d *driver) setupMounts(container *libcontainer.Container, c *execdriver.Command) error { 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 + return nil } diff --git a/runtime/execdriver/native/default_template.go b/runtime/execdriver/native/template/default_template.go similarity index 88% rename from runtime/execdriver/native/default_template.go rename to runtime/execdriver/native/template/default_template.go index 0dcd7db356..b9eb87713e 100644 --- a/runtime/execdriver/native/default_template.go +++ b/runtime/execdriver/native/template/default_template.go @@ -1,13 +1,12 @@ -package native +package template import ( "github.com/dotcloud/docker/pkg/cgroups" "github.com/dotcloud/docker/pkg/libcontainer" ) -// getDefaultTemplate returns the docker default for -// the libcontainer configuration file -func getDefaultTemplate() *libcontainer.Container { +// New returns the docker default configuration for libcontainer +func New() *libcontainer.Container { return &libcontainer.Container{ CapabilitiesMask: libcontainer.Capabilities{ libcontainer.GetCapability("SETPCAP"), diff --git a/runtime/utils.go b/runtime/utils.go index b343b5b10e..b983e67d41 100644 --- a/runtime/utils.go +++ b/runtime/utils.go @@ -1,9 +1,11 @@ package runtime import ( + "fmt" "github.com/dotcloud/docker/nat" "github.com/dotcloud/docker/pkg/namesgenerator" "github.com/dotcloud/docker/runconfig" + "strings" ) func migratePortMappings(config *runconfig.Config, hostConfig *runconfig.HostConfig) error { @@ -30,6 +32,24 @@ func migratePortMappings(config *runconfig.Config, hostConfig *runconfig.HostCon return nil } +func mergeLxcConfIntoOptions(hostConfig *runconfig.HostConfig, driverConfig map[string][]string) { + if hostConfig == nil { + return + } + + // merge in the lxc conf options into the generic config map + if lxcConf := hostConfig.LxcConf; lxcConf != nil { + lxc := driverConfig["lxc"] + for _, pair := range lxcConf { + // because lxc conf gets the driver name lxc.XXXX we need to trim it off + // and let the lxc driver add it back later if needed + parts := strings.SplitN(pair.Key, ".", 2) + lxc = append(lxc, fmt.Sprintf("%s=%s", parts[1], pair.Value)) + } + driverConfig["lxc"] = lxc + } +} + type checker struct { runtime *Runtime } diff --git a/runtime/utils_test.go b/runtime/utils_test.go new file mode 100644 index 0000000000..81c745c0d5 --- /dev/null +++ b/runtime/utils_test.go @@ -0,0 +1,27 @@ +package runtime + +import ( + "github.com/dotcloud/docker/runconfig" + "testing" +) + +func TestMergeLxcConfig(t *testing.T) { + var ( + hostConfig = &runconfig.HostConfig{ + LxcConf: []runconfig.KeyValuePair{ + {Key: "lxc.cgroups.cpuset", Value: "1,2"}, + }, + } + driverConfig = make(map[string][]string) + ) + + mergeLxcConfIntoOptions(hostConfig, driverConfig) + if l := len(driverConfig["lxc"]); l > 1 { + t.Fatalf("expected lxc options len of 1 got %d", l) + } + + cpuset := driverConfig["lxc"][0] + if expected := "cgroups.cpuset=1,2"; cpuset != expected { + t.Fatalf("expected %s got %s", expected, cpuset) + } +} From 2d270c4f06dbc2ee1293e3f81f6922df248ef8eb Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Thu, 27 Mar 2014 08:25:01 +0000 Subject: [PATCH 17/21] Fix compile and unit test errors after merge Docker-DCO-1.1-Signed-off-by: Michael Crosby (github: crosbymichael) --- pkg/cgroups/apply_raw.go | 23 ++++++++++++++ pkg/cgroups/cgroups.go | 19 ------------ runconfig/hostconfig.go | 5 --- runconfig/parse.go | 42 -------------------------- runtime/container.go | 1 - runtime/execdriver/lxc/lxc_template.go | 25 ++++++++++----- runtime/utils_test.go | 3 +- 7 files changed, 43 insertions(+), 75 deletions(-) diff --git a/pkg/cgroups/apply_raw.go b/pkg/cgroups/apply_raw.go index 47a2a002b8..5fe317937a 100644 --- a/pkg/cgroups/apply_raw.go +++ b/pkg/cgroups/apply_raw.go @@ -49,6 +49,9 @@ func rawApply(c *Cgroup, pid int) (ActiveCgroup, error) { if err := raw.setupCpu(c, pid); err != nil { return nil, err } + if err := raw.setupCpuset(c, pid); err != nil { + return nil, err + } return raw, nil } @@ -170,6 +173,25 @@ func (raw *rawCgroup) setupCpu(c *Cgroup, pid int) (err error) { return nil } +func (raw *rawCgroup) setupCpuset(c *Cgroup, pid int) (err error) { + if c.CpusetCpus != "" { + dir, err := raw.join("cpuset", pid) + if err != nil { + return err + } + defer func() { + if err != nil { + os.RemoveAll(dir) + } + }() + + if err := writeFile(dir, "cpuset.cpus", c.CpusetCpus); err != nil { + return err + } + } + return nil +} + func (raw *rawCgroup) Cleanup() error { get := func(subsystem string) string { path, _ := raw.path(subsystem) @@ -180,6 +202,7 @@ func (raw *rawCgroup) Cleanup() error { get("memory"), get("devices"), get("cpu"), + get("cpuset"), } { if path != "" { os.RemoveAll(path) diff --git a/pkg/cgroups/cgroups.go b/pkg/cgroups/cgroups.go index cdf268711a..5fe10346df 100644 --- a/pkg/cgroups/cgroups.go +++ b/pkg/cgroups/cgroups.go @@ -101,22 +101,3 @@ func (c *Cgroup) Apply(pid int) (ActiveCgroup, error) { return rawApply(c, pid) } } - -func (c *Cgroup) setupCpuset(cgroupRoot string, pid int) (err error) { - if c.CpusetCpus != "" { - dir, err := c.Join(cgroupRoot, "cpuset", pid) - if err != nil { - return err - } - defer func() { - if err != nil { - os.RemoveAll(dir) - } - }() - - if err := writeFile(dir, "cpuset.cpus", c.CpusetCpus); err != nil { - return err - } - } - return nil -} diff --git a/runconfig/hostconfig.go b/runconfig/hostconfig.go index 1a9ffbada5..9a92258644 100644 --- a/runconfig/hostconfig.go +++ b/runconfig/hostconfig.go @@ -17,11 +17,6 @@ type HostConfig struct { DriverOptions map[string][]string } -type KeyValuePair struct { - Key string - Value string -} - func ContainerHostConfigFromJob(job *engine.Job) *HostConfig { hostConfig := &HostConfig{ ContainerIDFile: job.Getenv("ContainerIDFile"), diff --git a/runconfig/parse.go b/runconfig/parse.go index b89d6c4683..a330c6c869 100644 --- a/runconfig/parse.go +++ b/runconfig/parse.go @@ -4,10 +4,8 @@ import ( "fmt" "github.com/dotcloud/docker/nat" "github.com/dotcloud/docker/opts" - "github.com/dotcloud/docker/pkg/label" flag "github.com/dotcloud/docker/pkg/mflag" "github.com/dotcloud/docker/pkg/sysinfo" - "github.com/dotcloud/docker/runtime/execdriver" "github.com/dotcloud/docker/utils" "io/ioutil" "path" @@ -34,10 +32,6 @@ func ParseSubcommand(cmd *flag.FlagSet, args []string, sysInfo *sysinfo.SysInfo) } func parseRun(cmd *flag.FlagSet, args []string, sysInfo *sysinfo.SysInfo) (*Config, *HostConfig, *flag.FlagSet, error) { - var ( - processLabel string - mountLabel string - ) var ( // FIXME: use utils.ListOpts for attach and volumes? flAttach = opts.NewListOpts(opts.ValidateAttach) @@ -67,7 +61,6 @@ func parseRun(cmd *flag.FlagSet, args []string, sysInfo *sysinfo.SysInfo) (*Conf flUser = cmd.String([]string{"u", "-user"}, "", "Username or UID") flWorkingDir = cmd.String([]string{"w", "-workdir"}, "", "Working directory inside the container") flCpuShares = cmd.Int64([]string{"c", "-cpu-shares"}, 0, "CPU shares (relative weight)") - flLabelOptions = cmd.String([]string{"Z", "-label"}, "", "Options to pass to underlying labeling system") // For documentation purpose _ = cmd.Bool([]string{"#sig-proxy", "-sig-proxy"}, true, "Proxify all received signal to the process (even in non-tty mode)") @@ -159,15 +152,6 @@ func parseRun(cmd *flag.FlagSet, args []string, sysInfo *sysinfo.SysInfo) (*Conf entrypoint = []string{*flEntrypoint} } - if !*flPrivileged { - pLabel, mLabel, e := label.GenLabels(*flLabelOptions) - if e != nil { - return nil, nil, cmd, fmt.Errorf("Invalid security labels : %s", e) - } - processLabel = pLabel - mountLabel = mLabel - } - lxcConf, err := parseKeyValueOpts(flLxcOpts) if err != nil { return nil, nil, cmd, err @@ -222,10 +206,6 @@ func parseRun(cmd *flag.FlagSet, args []string, sysInfo *sysinfo.SysInfo) (*Conf VolumesFrom: strings.Join(flVolumesFrom.GetAll(), ","), Entrypoint: entrypoint, WorkingDir: *flWorkingDir, - Context: execdriver.Context{ - "mount_label": mountLabel, - "process_label": processLabel, - }, } driverOptions, err := parseDriverOpts(flDriverOpts) @@ -233,11 +213,6 @@ func parseRun(cmd *flag.FlagSet, args []string, sysInfo *sysinfo.SysInfo) (*Conf return nil, nil, cmd, err } - pluginOptions, err := parseDriverOpts(flDriverOpts) - if err != nil { - return nil, nil, cmd, err - } - hostConfig := &HostConfig{ Binds: binds, ContainerIDFile: *flContainerIDFile, @@ -289,20 +264,3 @@ func parseKeyValueOpts(opts opts.ListOpts) ([]utils.KeyValuePair, error) { } return out, nil } - -// options will come in the format of name.type=value -func parseDriverOpts(opts opts.ListOpts) (map[string][]string, error) { - out := make(map[string][]string, len(opts.GetAll())) - for _, o := range opts.GetAll() { - parts := strings.SplitN(o, ".", 2) - if len(parts) < 2 { - return nil, fmt.Errorf("invalid opt format %s", o) - } - values, exists := out[parts[0]] - if !exists { - values = []string{} - } - out[parts[0]] = append(values, parts[1]) - } - return out, nil -} diff --git a/runtime/container.go b/runtime/container.go index 656e9ae587..f37ffcd1e7 100644 --- a/runtime/container.go +++ b/runtime/container.go @@ -404,7 +404,6 @@ func populateCommand(c *Container) { User: c.Config.User, Config: driverConfig, Resources: resources, - Context: c.Config.Context, } c.command.SysProcAttr = &syscall.SysProcAttr{Setsid: true} } diff --git a/runtime/execdriver/lxc/lxc_template.go b/runtime/execdriver/lxc/lxc_template.go index 230518bd7f..67095383ec 100644 --- a/runtime/execdriver/lxc/lxc_template.go +++ b/runtime/execdriver/lxc/lxc_template.go @@ -30,9 +30,9 @@ lxc.pts = 1024 # disable the main console lxc.console = none -{{if getProcessLabel .Context}} -lxc.se_context = {{ getProcessLabel .Context}} -{{$MOUNTLABEL := getMountLabel .Context}} +{{if getProcessLabel .Config}} +lxc.se_context = {{ getProcessLabel .Config}} +{{$MOUNTLABEL := getMountLabel .Config}} {{end}} # no controlling tty at all @@ -147,12 +147,23 @@ func getMemorySwap(v *execdriver.Resources) int64 { return v.Memory * 2 } -func getProcessLabel(c execdriver.Context) string { - return c["process_label"] +func getProcessLabel(c map[string][]string) string { + return getLabel(c, "process") } -func getMountLabel(c execdriver.Context) string { - return c["mount_label"] +func getMountLabel(c map[string][]string) string { + return getLabel(c, "mount") +} + +func getLabel(c map[string][]string, name string) string { + label := c["label"] + for _, l := range label { + parts := strings.SplitN(l, "=", 2) + if parts[0] == name { + return parts[1] + } + } + return "" } func init() { diff --git a/runtime/utils_test.go b/runtime/utils_test.go index 81c745c0d5..833634cb47 100644 --- a/runtime/utils_test.go +++ b/runtime/utils_test.go @@ -2,13 +2,14 @@ package runtime import ( "github.com/dotcloud/docker/runconfig" + "github.com/dotcloud/docker/utils" "testing" ) func TestMergeLxcConfig(t *testing.T) { var ( hostConfig = &runconfig.HostConfig{ - LxcConf: []runconfig.KeyValuePair{ + LxcConf: []utils.KeyValuePair{ {Key: "lxc.cgroups.cpuset", Value: "1,2"}, }, } From bfa2141765c2a3866ca0f5237fc951f4c2db8b98 Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Thu, 27 Mar 2014 08:57:01 +0000 Subject: [PATCH 18/21] Update lxc to use opts for selinux labels Docker-DCO-1.1-Signed-off-by: Michael Crosby (github: crosbymichael) --- runtime/execdriver/lxc/driver.go | 24 ++++++++++++++++++++---- runtime/execdriver/lxc/lxc_template.go | 10 +++++----- 2 files changed, 25 insertions(+), 9 deletions(-) diff --git a/runtime/execdriver/lxc/driver.go b/runtime/execdriver/lxc/driver.go index 086e35f643..896f215366 100644 --- a/runtime/execdriver/lxc/driver.go +++ b/runtime/execdriver/lxc/driver.go @@ -3,6 +3,7 @@ package lxc import ( "fmt" "github.com/dotcloud/docker/pkg/cgroups" + "github.com/dotcloud/docker/pkg/label" "github.com/dotcloud/docker/runtime/execdriver" "github.com/dotcloud/docker/utils" "io/ioutil" @@ -378,19 +379,34 @@ func rootIsShared() bool { } func (d *driver) generateLXCConfig(c *execdriver.Command) (string, error) { - root := path.Join(d.root, "containers", c.ID, "config.lxc") + var ( + process, mount string + root = path.Join(d.root, "containers", c.ID, "config.lxc") + labels = c.Config["label"] + ) fo, err := os.Create(root) if err != nil { return "", err } defer fo.Close() + if len(labels) > 0 { + process, mount, err = label.GenLabels(labels[0]) + if err != nil { + return "", err + } + } + if err := LxcTemplateCompiled.Execute(fo, struct { *execdriver.Command - AppArmor bool + AppArmor bool + ProcessLabel string + MountLabel string }{ - Command: c, - AppArmor: d.apparmor, + Command: c, + AppArmor: d.apparmor, + ProcessLabel: process, + MountLabel: mount, }); err != nil { return "", err } diff --git a/runtime/execdriver/lxc/lxc_template.go b/runtime/execdriver/lxc/lxc_template.go index 67095383ec..e5248375a8 100644 --- a/runtime/execdriver/lxc/lxc_template.go +++ b/runtime/execdriver/lxc/lxc_template.go @@ -30,9 +30,9 @@ lxc.pts = 1024 # disable the main console lxc.console = none -{{if getProcessLabel .Config}} -lxc.se_context = {{ getProcessLabel .Config}} -{{$MOUNTLABEL := getMountLabel .Config}} +{{if .ProcessLabel}} +lxc.se_context = {{ .ProcessLabel}} +{{$MOUNTLABEL := .MountLabel}} {{end}} # no controlling tty at all @@ -159,8 +159,8 @@ func getLabel(c map[string][]string, name string) string { label := c["label"] for _, l := range label { parts := strings.SplitN(l, "=", 2) - if parts[0] == name { - return parts[1] + if strings.TrimSpace(parts[0]) == name { + return strings.TrimSpace(parts[1]) } } return "" From 6c9a47f01c583e9c22b831eb426192148d29d792 Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Thu, 27 Mar 2014 09:04:54 +0000 Subject: [PATCH 19/21] Update native driver to use labels from opts Docker-DCO-1.1-Signed-off-by: Michael Crosby (github: crosbymichael) --- runtime/execdriver/native/create.go | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/runtime/execdriver/native/create.go b/runtime/execdriver/native/create.go index 7e663f0555..976416a8ca 100644 --- a/runtime/execdriver/native/create.go +++ b/runtime/execdriver/native/create.go @@ -2,6 +2,7 @@ package native import ( "fmt" + "github.com/dotcloud/docker/pkg/label" "github.com/dotcloud/docker/pkg/libcontainer" "github.com/dotcloud/docker/runtime/execdriver" "github.com/dotcloud/docker/runtime/execdriver/native/configuration" @@ -37,6 +38,9 @@ func (d *driver) createContainer(c *execdriver.Command) (*libcontainer.Container if err := d.setupMounts(container, c); err != nil { return nil, err } + if err := d.setupLabels(container, c); err != nil { + return nil, err + } if err := configuration.ParseConfiguration(container, d.activeContainers, c.Config["native"]); err != nil { return nil, err } @@ -94,3 +98,16 @@ func (d *driver) setupMounts(container *libcontainer.Container, c *execdriver.Co } return nil } + +func (d *driver) setupLabels(container *libcontainer.Container, c *execdriver.Command) error { + labels := c.Config["label"] + if len(labels) > 0 { + process, mount, err := label.GenLabels(labels[0]) + if err != nil { + return err + } + container.Context["mount_label"] = mount + container.Context["process_label"] = process + } + return nil +} From 7a7f59210d5eb7a38a5fac8889010bd54576ea01 Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Mon, 31 Mar 2014 23:12:08 +0000 Subject: [PATCH 20/21] Ensure secound part of the key is provided Docker-DCO-1.1-Signed-off-by: Michael Crosby (github: crosbymichael) --- runconfig/parse.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/runconfig/parse.go b/runconfig/parse.go index a330c6c869..da4e045cd0 100644 --- a/runconfig/parse.go +++ b/runconfig/parse.go @@ -243,6 +243,8 @@ func parseDriverOpts(opts opts.ListOpts) (map[string][]string, error) { parts := strings.SplitN(o, ".", 2) if len(parts) < 2 { return nil, fmt.Errorf("invalid opt format %s", o) + } else if strings.TrimSpace(parts[0]) == "" { + return nil, fmt.Errorf("key cannot be empty %s", o) } values, exists := out[parts[0]] if !exists { From 40c6d00c97c737d9d3827f159518007803affcc7 Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Tue, 1 Apr 2014 07:07:42 +0000 Subject: [PATCH 21/21] Update imports to be consistent Docker-DCO-1.1-Signed-off-by: Michael Crosby (github: crosbymichael) --- runtime/execdriver/native/create.go | 3 ++- runtime/utils_test.go | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/runtime/execdriver/native/create.go b/runtime/execdriver/native/create.go index 976416a8ca..71fab3e064 100644 --- a/runtime/execdriver/native/create.go +++ b/runtime/execdriver/native/create.go @@ -2,12 +2,13 @@ package native import ( "fmt" + "os" + "github.com/dotcloud/docker/pkg/label" "github.com/dotcloud/docker/pkg/libcontainer" "github.com/dotcloud/docker/runtime/execdriver" "github.com/dotcloud/docker/runtime/execdriver/native/configuration" "github.com/dotcloud/docker/runtime/execdriver/native/template" - "os" ) // createContainer populates and configures the container type with the diff --git a/runtime/utils_test.go b/runtime/utils_test.go index 833634cb47..bdf3543a49 100644 --- a/runtime/utils_test.go +++ b/runtime/utils_test.go @@ -1,9 +1,10 @@ package runtime import ( + "testing" + "github.com/dotcloud/docker/runconfig" "github.com/dotcloud/docker/utils" - "testing" ) func TestMergeLxcConfig(t *testing.T) {