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)
| ... | ... |
@@ -3,21 +3,18 @@ package runconfig |
| 3 | 3 |
import ( |
| 4 | 4 |
"github.com/dotcloud/docker/engine" |
| 5 | 5 |
"github.com/dotcloud/docker/nat" |
| 6 |
+ "github.com/dotcloud/docker/utils" |
|
| 6 | 7 |
) |
| 7 | 8 |
|
| 8 | 9 |
type HostConfig struct {
|
| 9 | 10 |
Binds []string |
| 10 | 11 |
ContainerIDFile string |
| 11 |
- LxcConf []KeyValuePair |
|
| 12 |
+ LxcConf []utils.KeyValuePair |
|
| 12 | 13 |
Privileged bool |
| 13 | 14 |
PortBindings nat.PortMap |
| 14 | 15 |
Links []string |
| 15 | 16 |
PublishAllPorts bool |
| 16 |
-} |
|
| 17 |
- |
|
| 18 |
-type KeyValuePair struct {
|
|
| 19 |
- Key string |
|
| 20 |
- Value string |
|
| 17 |
+ DriverOptions map[string][]string |
|
| 21 | 18 |
} |
| 22 | 19 |
|
| 23 | 20 |
func ContainerHostConfigFromJob(job *engine.Job) *HostConfig {
|
| ... | ... |
@@ -28,6 +25,7 @@ func ContainerHostConfigFromJob(job *engine.Job) *HostConfig {
|
| 28 | 28 |
} |
| 29 | 29 |
job.GetenvJson("LxcConf", &hostConfig.LxcConf)
|
| 30 | 30 |
job.GetenvJson("PortBindings", &hostConfig.PortBindings)
|
| 31 |
+ job.GetenvJson("DriverOptions", &hostConfig.DriverOptions)
|
|
| 31 | 32 |
if Binds := job.GetenvList("Binds"); Binds != nil {
|
| 32 | 33 |
hostConfig.Binds = Binds |
| 33 | 34 |
} |
| ... | ... |
@@ -51,6 +51,7 @@ func parseRun(cmd *flag.FlagSet, args []string, sysInfo *sysinfo.SysInfo) (*Conf |
| 51 | 51 |
flDnsSearch = opts.NewListOpts(opts.ValidateDomain) |
| 52 | 52 |
flVolumesFrom opts.ListOpts |
| 53 | 53 |
flLxcOpts opts.ListOpts |
| 54 |
+ flDriverOpts opts.ListOpts |
|
| 54 | 55 |
|
| 55 | 56 |
flAutoRemove = cmd.Bool([]string{"#rm", "-rm"}, false, "Automatically remove the container when it exits (incompatible with -d)")
|
| 56 | 57 |
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 |
| 83 | 83 |
cmd.Var(&flDns, []string{"#dns", "-dns"}, "Set custom dns servers")
|
| 84 | 84 |
cmd.Var(&flDnsSearch, []string{"-dns-search"}, "Set custom dns search domains")
|
| 85 | 85 |
cmd.Var(&flVolumesFrom, []string{"#volumes-from", "-volumes-from"}, "Mount volumes from the specified container(s)")
|
| 86 |
- cmd.Var(&flLxcOpts, []string{"#lxc-conf", "-lxc-conf"}, "(lxc exec-driver only) Add custom lxc options --lxc-conf=\"lxc.cgroup.cpuset.cpus = 0,1\"")
|
|
| 86 |
+ cmd.Var(&flLxcOpts, []string{"#lxc-conf", "#-lxc-conf"}, "(lxc exec-driver only) Add custom lxc options --lxc-conf=\"lxc.cgroup.cpuset.cpus = 0,1\"")
|
|
| 87 |
+ cmd.Var(&flDriverOpts, []string{"o", "-opt"}, "Add custom driver options")
|
|
| 87 | 88 |
|
| 88 | 89 |
if err := cmd.Parse(args); err != nil {
|
| 89 | 90 |
return nil, nil, cmd, err |
| ... | ... |
@@ -166,7 +168,7 @@ func parseRun(cmd *flag.FlagSet, args []string, sysInfo *sysinfo.SysInfo) (*Conf |
| 166 | 166 |
mountLabel = mLabel |
| 167 | 167 |
} |
| 168 | 168 |
|
| 169 |
- lxcConf, err := parseLxcConfOpts(flLxcOpts) |
|
| 169 |
+ lxcConf, err := parseKeyValueOpts(flLxcOpts) |
|
| 170 | 170 |
if err != nil {
|
| 171 | 171 |
return nil, nil, cmd, err |
| 172 | 172 |
} |
| ... | ... |
@@ -226,6 +228,11 @@ func parseRun(cmd *flag.FlagSet, args []string, sysInfo *sysinfo.SysInfo) (*Conf |
| 226 | 226 |
}, |
| 227 | 227 |
} |
| 228 | 228 |
|
| 229 |
+ driverOptions, err := parseDriverOpts(flDriverOpts) |
|
| 230 |
+ if err != nil {
|
|
| 231 |
+ return nil, nil, cmd, err |
|
| 232 |
+ } |
|
| 233 |
+ |
|
| 229 | 234 |
hostConfig := &HostConfig{
|
| 230 | 235 |
Binds: binds, |
| 231 | 236 |
ContainerIDFile: *flContainerIDFile, |
| ... | ... |
@@ -234,6 +241,7 @@ func parseRun(cmd *flag.FlagSet, args []string, sysInfo *sysinfo.SysInfo) (*Conf |
| 234 | 234 |
PortBindings: portBindings, |
| 235 | 235 |
Links: flLinks.GetAll(), |
| 236 | 236 |
PublishAllPorts: *flPublishAll, |
| 237 |
+ DriverOptions: driverOptions, |
|
| 237 | 238 |
} |
| 238 | 239 |
|
| 239 | 240 |
if sysInfo != nil && flMemory > 0 && !sysInfo.SwapLimit {
|
| ... | ... |
@@ -248,22 +256,31 @@ func parseRun(cmd *flag.FlagSet, args []string, sysInfo *sysinfo.SysInfo) (*Conf |
| 248 | 248 |
return config, hostConfig, cmd, nil |
| 249 | 249 |
} |
| 250 | 250 |
|
| 251 |
-func parseLxcConfOpts(opts opts.ListOpts) ([]KeyValuePair, error) {
|
|
| 252 |
- out := make([]KeyValuePair, opts.Len()) |
|
| 253 |
- for i, o := range opts.GetAll() {
|
|
| 254 |
- k, v, err := parseLxcOpt(o) |
|
| 255 |
- if err != nil {
|
|
| 256 |
- return nil, err |
|
| 251 |
+// options will come in the format of name.key=value or name.option |
|
| 252 |
+func parseDriverOpts(opts opts.ListOpts) (map[string][]string, error) {
|
|
| 253 |
+ out := make(map[string][]string, len(opts.GetAll())) |
|
| 254 |
+ for _, o := range opts.GetAll() {
|
|
| 255 |
+ parts := strings.SplitN(o, ".", 2) |
|
| 256 |
+ if len(parts) < 2 {
|
|
| 257 |
+ return nil, fmt.Errorf("invalid opt format %s", o)
|
|
| 257 | 258 |
} |
| 258 |
- out[i] = KeyValuePair{Key: k, Value: v}
|
|
| 259 |
+ values, exists := out[parts[0]] |
|
| 260 |
+ if !exists {
|
|
| 261 |
+ values = []string{}
|
|
| 262 |
+ } |
|
| 263 |
+ out[parts[0]] = append(values, parts[1]) |
|
| 259 | 264 |
} |
| 260 | 265 |
return out, nil |
| 261 | 266 |
} |
| 262 | 267 |
|
| 263 |
-func parseLxcOpt(opt string) (string, string, error) {
|
|
| 264 |
- parts := strings.SplitN(opt, "=", 2) |
|
| 265 |
- if len(parts) != 2 {
|
|
| 266 |
- return "", "", fmt.Errorf("Unable to parse lxc conf option: %s", opt)
|
|
| 268 |
+func parseKeyValueOpts(opts opts.ListOpts) ([]utils.KeyValuePair, error) {
|
|
| 269 |
+ out := make([]utils.KeyValuePair, opts.Len()) |
|
| 270 |
+ for i, o := range opts.GetAll() {
|
|
| 271 |
+ k, v, err := utils.ParseKeyValueOpt(o) |
|
| 272 |
+ if err != nil {
|
|
| 273 |
+ return nil, err |
|
| 274 |
+ } |
|
| 275 |
+ out[i] = utils.KeyValuePair{Key: k, Value: v}
|
|
| 267 | 276 |
} |
| 268 |
- return strings.TrimSpace(parts[0]), strings.TrimSpace(parts[1]), nil |
|
| 277 |
+ return out, nil |
|
| 269 | 278 |
} |
| ... | ... |
@@ -1,6 +1,7 @@ |
| 1 | 1 |
package runconfig |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 |
+ "github.com/dotcloud/docker/utils" |
|
| 4 | 5 |
"testing" |
| 5 | 6 |
) |
| 6 | 7 |
|
| ... | ... |
@@ -8,7 +9,7 @@ func TestParseLxcConfOpt(t *testing.T) {
|
| 8 | 8 |
opts := []string{"lxc.utsname=docker", "lxc.utsname = docker "}
|
| 9 | 9 |
|
| 10 | 10 |
for _, o := range opts {
|
| 11 |
- k, v, err := parseLxcOpt(o) |
|
| 11 |
+ k, v, err := utils.ParseKeyValueOpt(o) |
|
| 12 | 12 |
if err != nil {
|
| 13 | 13 |
t.FailNow() |
| 14 | 14 |
} |
| ... | ... |
@@ -361,9 +361,13 @@ func (container *Container) Attach(stdin io.ReadCloser, stdinCloser io.Closer, s |
| 361 | 361 |
func populateCommand(c *Container) {
|
| 362 | 362 |
var ( |
| 363 | 363 |
en *execdriver.Network |
| 364 |
- driverConfig []string |
|
| 364 |
+ driverConfig = c.hostConfig.DriverOptions |
|
| 365 | 365 |
) |
| 366 | 366 |
|
| 367 |
+ if driverConfig == nil {
|
|
| 368 |
+ driverConfig = make(map[string][]string) |
|
| 369 |
+ } |
|
| 370 |
+ |
|
| 367 | 371 |
en = &execdriver.Network{
|
| 368 | 372 |
Mtu: c.runtime.config.Mtu, |
| 369 | 373 |
Interface: nil, |
| ... | ... |
@@ -379,11 +383,9 @@ func populateCommand(c *Container) {
|
| 379 | 379 |
} |
| 380 | 380 |
} |
| 381 | 381 |
|
| 382 |
- if lxcConf := c.hostConfig.LxcConf; lxcConf != nil {
|
|
| 383 |
- for _, pair := range lxcConf {
|
|
| 384 |
- driverConfig = append(driverConfig, fmt.Sprintf("%s = %s", pair.Key, pair.Value))
|
|
| 385 |
- } |
|
| 386 |
- } |
|
| 382 |
+ // TODO: this can be removed after lxc-conf is fully deprecated |
|
| 383 |
+ mergeLxcConfIntoOptions(c.hostConfig, driverConfig) |
|
| 384 |
+ |
|
| 387 | 385 |
resources := &execdriver.Resources{
|
| 388 | 386 |
Memory: c.Config.Memory, |
| 389 | 387 |
MemorySwap: c.Config.MemorySwap, |
| ... | ... |
@@ -116,21 +116,21 @@ type Mount struct {
|
| 116 | 116 |
type Command struct {
|
| 117 | 117 |
exec.Cmd `json:"-"` |
| 118 | 118 |
|
| 119 |
- ID string `json:"id"` |
|
| 120 |
- Privileged bool `json:"privileged"` |
|
| 121 |
- User string `json:"user"` |
|
| 122 |
- Rootfs string `json:"rootfs"` // root fs of the container |
|
| 123 |
- InitPath string `json:"initpath"` // dockerinit |
|
| 124 |
- Entrypoint string `json:"entrypoint"` |
|
| 125 |
- Arguments []string `json:"arguments"` |
|
| 126 |
- WorkingDir string `json:"working_dir"` |
|
| 127 |
- ConfigPath string `json:"config_path"` // this should be able to be removed when the lxc template is moved into the driver |
|
| 128 |
- Context Context `json:"context"` // generic context for specific options (apparmor, selinux) |
|
| 129 |
- Tty bool `json:"tty"` |
|
| 130 |
- Network *Network `json:"network"` |
|
| 131 |
- Config []string `json:"config"` // generic values that specific drivers can consume |
|
| 132 |
- Resources *Resources `json:"resources"` |
|
| 133 |
- Mounts []Mount `json:"mounts"` |
|
| 119 |
+ ID string `json:"id"` |
|
| 120 |
+ Privileged bool `json:"privileged"` |
|
| 121 |
+ User string `json:"user"` |
|
| 122 |
+ Rootfs string `json:"rootfs"` // root fs of the container |
|
| 123 |
+ InitPath string `json:"initpath"` // dockerinit |
|
| 124 |
+ Entrypoint string `json:"entrypoint"` |
|
| 125 |
+ Arguments []string `json:"arguments"` |
|
| 126 |
+ WorkingDir string `json:"working_dir"` |
|
| 127 |
+ ConfigPath string `json:"config_path"` // this should be able to be removed when the lxc template is moved into the driver |
|
| 128 |
+ Context Context `json:"context"` // generic context for specific options (apparmor, selinux) |
|
| 129 |
+ Tty bool `json:"tty"` |
|
| 130 |
+ Network *Network `json:"network"` |
|
| 131 |
+ Config map[string][]string `json:"config"` // generic values that specific drivers can consume |
|
| 132 |
+ Resources *Resources `json:"resources"` |
|
| 133 |
+ Mounts []Mount `json:"mounts"` |
|
| 134 | 134 |
|
| 135 | 135 |
Terminal Terminal `json:"-"` // standard or tty terminal |
| 136 | 136 |
Console string `json:"-"` // dev/console path |
| ... | ... |
@@ -75,9 +75,11 @@ func TestCustomLxcConfig(t *testing.T) {
|
| 75 | 75 |
command := &execdriver.Command{
|
| 76 | 76 |
ID: "1", |
| 77 | 77 |
Privileged: false, |
| 78 |
- Config: []string{
|
|
| 79 |
- "lxc.utsname = docker", |
|
| 80 |
- "lxc.cgroup.cpuset.cpus = 0,1", |
|
| 78 |
+ Config: map[string][]string{
|
|
| 79 |
+ "lxc": {
|
|
| 80 |
+ "lxc.utsname = docker", |
|
| 81 |
+ "lxc.cgroup.cpuset.cpus = 0,1", |
|
| 82 |
+ }, |
|
| 81 | 83 |
}, |
| 82 | 84 |
Network: &execdriver.Network{
|
| 83 | 85 |
Mtu: 1500, |
| ... | ... |
@@ -58,6 +58,7 @@ func createContainer(c *execdriver.Command) *libcontainer.Container {
|
| 58 | 58 |
container.Cgroups.Memory = c.Resources.Memory |
| 59 | 59 |
container.Cgroups.MemorySwap = c.Resources.MemorySwap |
| 60 | 60 |
} |
| 61 |
+ |
|
| 61 | 62 |
// check to see if we are running in ramdisk to disable pivot root |
| 62 | 63 |
container.NoPivotRoot = os.Getenv("DOCKER_RAMDISK") != ""
|
| 63 | 64 |
|
| ... | ... |
@@ -184,10 +184,9 @@ func (d *driver) removeContainerRoot(id string) error {
|
| 184 | 184 |
func (d *driver) validateCommand(c *execdriver.Command) error {
|
| 185 | 185 |
// we need to check the Config of the command to make sure that we |
| 186 | 186 |
// do not have any of the lxc-conf variables |
| 187 |
- for _, conf := range c.Config {
|
|
| 188 |
- if strings.Contains(conf, "lxc") {
|
|
| 189 |
- return fmt.Errorf("%s is not supported by the native driver", conf)
|
|
| 190 |
- } |
|
| 187 |
+ lxc := c.Config["lxc"] |
|
| 188 |
+ if lxc != nil && len(lxc) > 0 {
|
|
| 189 |
+ return fmt.Errorf("lxc config options are not supported by the native driver")
|
|
| 191 | 190 |
} |
| 192 | 191 |
return nil |
| 193 | 192 |
} |
| ... | ... |
@@ -1,9 +1,11 @@ |
| 1 | 1 |
package runtime |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 |
+ "fmt" |
|
| 4 | 5 |
"github.com/dotcloud/docker/nat" |
| 5 | 6 |
"github.com/dotcloud/docker/pkg/namesgenerator" |
| 6 | 7 |
"github.com/dotcloud/docker/runconfig" |
| 8 |
+ "strings" |
|
| 7 | 9 |
) |
| 8 | 10 |
|
| 9 | 11 |
func migratePortMappings(config *runconfig.Config, hostConfig *runconfig.HostConfig) error {
|
| ... | ... |
@@ -30,6 +32,24 @@ func migratePortMappings(config *runconfig.Config, hostConfig *runconfig.HostCon |
| 30 | 30 |
return nil |
| 31 | 31 |
} |
| 32 | 32 |
|
| 33 |
+func mergeLxcConfIntoOptions(hostConfig *runconfig.HostConfig, driverConfig map[string][]string) {
|
|
| 34 |
+ if hostConfig == nil {
|
|
| 35 |
+ return |
|
| 36 |
+ } |
|
| 37 |
+ |
|
| 38 |
+ // merge in the lxc conf options into the generic config map |
|
| 39 |
+ if lxcConf := hostConfig.LxcConf; lxcConf != nil {
|
|
| 40 |
+ lxc := driverConfig["lxc"] |
|
| 41 |
+ for _, pair := range lxcConf {
|
|
| 42 |
+ // because lxc conf gets the driver name lxc.XXXX we need to trim it off |
|
| 43 |
+ // and let the lxc driver add it back later if needed |
|
| 44 |
+ parts := strings.SplitN(pair.Key, ".", 2) |
|
| 45 |
+ lxc = append(lxc, fmt.Sprintf("%s=%s", parts[1], pair.Value))
|
|
| 46 |
+ } |
|
| 47 |
+ driverConfig["lxc"] = lxc |
|
| 48 |
+ } |
|
| 49 |
+} |
|
| 50 |
+ |
|
| 33 | 51 |
type checker struct {
|
| 34 | 52 |
runtime *Runtime |
| 35 | 53 |
} |
| ... | ... |
@@ -25,6 +25,11 @@ import ( |
| 25 | 25 |
"time" |
| 26 | 26 |
) |
| 27 | 27 |
|
| 28 |
+type KeyValuePair struct {
|
|
| 29 |
+ Key string |
|
| 30 |
+ Value string |
|
| 31 |
+} |
|
| 32 |
+ |
|
| 28 | 33 |
// A common interface to access the Fatal method of |
| 29 | 34 |
// both testing.B and testing.T. |
| 30 | 35 |
type Fataler interface {
|
| ... | ... |
@@ -1071,3 +1076,11 @@ func ReadSymlinkedDirectory(path string) (string, error) {
|
| 1071 | 1071 |
} |
| 1072 | 1072 |
return realPath, nil |
| 1073 | 1073 |
} |
| 1074 |
+ |
|
| 1075 |
+func ParseKeyValueOpt(opt string) (string, string, error) {
|
|
| 1076 |
+ parts := strings.SplitN(opt, "=", 2) |
|
| 1077 |
+ if len(parts) != 2 {
|
|
| 1078 |
+ return "", "", fmt.Errorf("Unable to parse key/value option: %s", opt)
|
|
| 1079 |
+ } |
|
| 1080 |
+ return strings.TrimSpace(parts[0]), strings.TrimSpace(parts[1]), nil |
|
| 1081 |
+} |