فهرست منبع

Add --opt arguments for drivers

In order to handle special configuration for different drivers we
make the Config field a map to string array. This lets
us use it for lxc, by using the "lxc" key for those, and we can
later extend it easily for other backend-specific options.

Docker-DCO-1.1-Signed-off-by: Alexander Larsson <alexl@redhat.com> (github: alexlarsson)
Alexander Larsson 11 سال پیش
والد
کامیت
7a3070a600

+ 4 - 6
runconfig/hostconfig.go

@@ -3,21 +3,18 @@ package runconfig
 import (
 	"github.com/dotcloud/docker/engine"
 	"github.com/dotcloud/docker/nat"
+	"github.com/dotcloud/docker/utils"
 )
 
 type HostConfig struct {
 	Binds           []string
 	ContainerIDFile string
-	LxcConf         []KeyValuePair
+	LxcConf         []utils.KeyValuePair
 	Privileged      bool
 	PortBindings    nat.PortMap
 	Links           []string
 	PublishAllPorts bool
-}
-
-type KeyValuePair struct {
-	Key   string
-	Value string
+	DriverOptions   map[string][]string
 }
 
 func ContainerHostConfigFromJob(job *engine.Job) *HostConfig {
@@ -28,6 +25,7 @@ func ContainerHostConfigFromJob(job *engine.Job) *HostConfig {
 	}
 	job.GetenvJson("LxcConf", &hostConfig.LxcConf)
 	job.GetenvJson("PortBindings", &hostConfig.PortBindings)
+	job.GetenvJson("DriverOptions", &hostConfig.DriverOptions)
 	if Binds := job.GetenvList("Binds"); Binds != nil {
 		hostConfig.Binds = Binds
 	}

+ 31 - 14
runconfig/parse.go

@@ -51,6 +51,7 @@ func parseRun(cmd *flag.FlagSet, args []string, sysInfo *sysinfo.SysInfo) (*Conf
 		flDnsSearch   = opts.NewListOpts(opts.ValidateDomain)
 		flVolumesFrom opts.ListOpts
 		flLxcOpts     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")
@@ -83,7 +84,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"}, "(lxc exec-driver only) Add custom lxc options --lxc-conf=\"lxc.cgroup.cpuset.cpus = 0,1\"")
+	cmd.Var(&flLxcOpts, []string{"#lxc-conf", "#-lxc-conf"}, "(lxc exec-driver only) 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
@@ -166,7 +168,7 @@ func parseRun(cmd *flag.FlagSet, args []string, sysInfo *sysinfo.SysInfo) (*Conf
 		mountLabel = mLabel
 	}
 
-	lxcConf, err := parseLxcConfOpts(flLxcOpts)
+	lxcConf, err := parseKeyValueOpts(flLxcOpts)
 	if err != nil {
 		return nil, nil, cmd, err
 	}
@@ -226,6 +228,11 @@ func parseRun(cmd *flag.FlagSet, args []string, sysInfo *sysinfo.SysInfo) (*Conf
 		},
 	}
 
+	driverOptions, err := parseDriverOpts(flDriverOpts)
+	if err != nil {
+		return nil, nil, cmd, err
+	}
+
 	hostConfig := &HostConfig{
 		Binds:           binds,
 		ContainerIDFile: *flContainerIDFile,
@@ -234,6 +241,7 @@ func parseRun(cmd *flag.FlagSet, args []string, sysInfo *sysinfo.SysInfo) (*Conf
 		PortBindings:    portBindings,
 		Links:           flLinks.GetAll(),
 		PublishAllPorts: *flPublishAll,
+		DriverOptions:   driverOptions,
 	}
 
 	if sysInfo != nil && flMemory > 0 && !sysInfo.SwapLimit {
@@ -248,22 +256,31 @@ func parseRun(cmd *flag.FlagSet, args []string, sysInfo *sysinfo.SysInfo) (*Conf
 	return config, hostConfig, cmd, nil
 }
 
-func parseLxcConfOpts(opts opts.ListOpts) ([]KeyValuePair, error) {
-	out := make([]KeyValuePair, opts.Len())
-	for i, o := range opts.GetAll() {
-		k, v, err := parseLxcOpt(o)
-		if err != nil {
-			return nil, err
+// options will come in the format of name.key=value or name.option
+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)
 		}
-		out[i] = KeyValuePair{Key: k, Value: v}
+		values, exists := out[parts[0]]
+		if !exists {
+			values = []string{}
+		}
+		out[parts[0]] = append(values, parts[1])
 	}
 	return out, nil
 }
 
-func parseLxcOpt(opt string) (string, string, error) {
-	parts := strings.SplitN(opt, "=", 2)
-	if len(parts) != 2 {
-		return "", "", fmt.Errorf("Unable to parse lxc conf option: %s", opt)
+func parseKeyValueOpts(opts opts.ListOpts) ([]utils.KeyValuePair, error) {
+	out := make([]utils.KeyValuePair, opts.Len())
+	for i, o := range opts.GetAll() {
+		k, v, err := utils.ParseKeyValueOpt(o)
+		if err != nil {
+			return nil, err
+		}
+		out[i] = utils.KeyValuePair{Key: k, Value: v}
 	}
-	return strings.TrimSpace(parts[0]), strings.TrimSpace(parts[1]), nil
+	return out, nil
 }

+ 2 - 1
runconfig/parse_test.go

@@ -1,6 +1,7 @@
 package runconfig
 
 import (
+	"github.com/dotcloud/docker/utils"
 	"testing"
 )
 
@@ -8,7 +9,7 @@ func TestParseLxcConfOpt(t *testing.T) {
 	opts := []string{"lxc.utsname=docker", "lxc.utsname = docker "}
 
 	for _, o := range opts {
-		k, v, err := parseLxcOpt(o)
+		k, v, err := utils.ParseKeyValueOpt(o)
 		if err != nil {
 			t.FailNow()
 		}

+ 8 - 6
runtime/container.go

@@ -361,9 +361,13 @@ func (container *Container) Attach(stdin io.ReadCloser, stdinCloser io.Closer, s
 func populateCommand(c *Container) {
 	var (
 		en           *execdriver.Network
-		driverConfig []string
+		driverConfig = c.hostConfig.DriverOptions
 	)
 
+	if driverConfig == nil {
+		driverConfig = make(map[string][]string)
+	}
+
 	en = &execdriver.Network{
 		Mtu:       c.runtime.config.Mtu,
 		Interface: nil,
@@ -379,11 +383,9 @@ func populateCommand(c *Container) {
 		}
 	}
 
-	if lxcConf := c.hostConfig.LxcConf; lxcConf != nil {
-		for _, pair := range lxcConf {
-			driverConfig = append(driverConfig, fmt.Sprintf("%s = %s", pair.Key, pair.Value))
-		}
-	}
+	// TODO: this can be removed after lxc-conf is fully deprecated
+	mergeLxcConfIntoOptions(c.hostConfig, driverConfig)
+
 	resources := &execdriver.Resources{
 		Memory:     c.Config.Memory,
 		MemorySwap: c.Config.MemorySwap,

+ 15 - 15
runtime/execdriver/driver.go

@@ -116,21 +116,21 @@ 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
-	Context    Context    `json:"context"`     // generic context for specific options (apparmor, selinux)
-	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
+	Context    Context             `json:"context"`     // generic context for specific options (apparmor, selinux)
+	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

+ 2 - 2
runtime/execdriver/lxc/lxc_template.go

@@ -123,8 +123,8 @@ lxc.cgroup.cpu.shares = {{.Resources.CpuShares}}
 {{end}}
 {{end}}
 
-{{if .Config}}
-{{range $value := .Config}}
+{{if .Config.lxc}}
+{{range $value := .Config.lxc}}
 {{$value}}
 {{end}}
 {{end}}

+ 5 - 3
runtime/execdriver/lxc/lxc_template_unit_test.go

@@ -75,9 +75,11 @@ func TestCustomLxcConfig(t *testing.T) {
 	command := &execdriver.Command{
 		ID:         "1",
 		Privileged: false,
-		Config: []string{
-			"lxc.utsname = docker",
-			"lxc.cgroup.cpuset.cpus = 0,1",
+		Config: map[string][]string{
+			"lxc": {
+				"lxc.utsname = docker",
+				"lxc.cgroup.cpuset.cpus = 0,1",
+			},
 		},
 		Network: &execdriver.Network{
 			Mtu:       1500,

+ 1 - 0
runtime/execdriver/native/default_template.go

@@ -58,6 +58,7 @@ func createContainer(c *execdriver.Command) *libcontainer.Container {
 		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") != ""
 

+ 3 - 4
runtime/execdriver/native/driver.go

@@ -184,10 +184,9 @@ 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)
-		}
+	lxc := c.Config["lxc"]
+	if lxc != nil && len(lxc) > 0 {
+		return fmt.Errorf("lxc config options are not supported by the native driver")
 	}
 	return nil
 }

+ 20 - 0
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
 }

+ 13 - 0
utils/utils.go

@@ -25,6 +25,11 @@ import (
 	"time"
 )
 
+type KeyValuePair struct {
+	Key   string
+	Value string
+}
+
 // A common interface to access the Fatal method of
 // both testing.B and testing.T.
 type Fataler interface {
@@ -1071,3 +1076,11 @@ func ReadSymlinkedDirectory(path string) (string, error) {
 	}
 	return realPath, nil
 }
+
+func ParseKeyValueOpt(opt string) (string, string, error) {
+	parts := strings.SplitN(opt, "=", 2)
+	if len(parts) != 2 {
+		return "", "", fmt.Errorf("Unable to parse key/value option: %s", opt)
+	}
+	return strings.TrimSpace(parts[0]), strings.TrimSpace(parts[1]), nil
+}