Browse code

Strongly type exec driver context

This also removes dead code in the native driver for a past feature that
was never fully implemented.

Signed-off-by: Michael Crosby <crosbymichael@gmail.com>

Michael Crosby authored on 2014/09/30 07:40:26
Showing 10 changed files
... ...
@@ -195,14 +195,7 @@ func (container *Container) getRootResourcePath(path string) (string, error) {
195 195
 }
196 196
 
197 197
 func populateCommand(c *Container, env []string) error {
198
-	var (
199
-		en      *execdriver.Network
200
-		context = make(map[string][]string)
201
-	)
202
-	context["process_label"] = []string{c.GetProcessLabel()}
203
-	context["mount_label"] = []string{c.GetMountLabel()}
204
-
205
-	en = &execdriver.Network{
198
+	en := &execdriver.Network{
206 199
 		Mtu:       c.daemon.config.Mtu,
207 200
 		Interface: nil,
208 201
 	}
... ...
@@ -247,7 +240,7 @@ func populateCommand(c *Container, env []string) error {
247 247
 	autoCreatedDevices := append(devices.DefaultAutoCreatedDevices, userSpecifiedDevices...)
248 248
 
249 249
 	// TODO: this can be removed after lxc-conf is fully deprecated
250
-	mergeLxcConfIntoOptions(c.hostConfig, context)
250
+	lxcConfig := mergeLxcConfIntoOptions(c.hostConfig)
251 251
 
252 252
 	resources := &execdriver.Resources{
253 253
 		Memory:     c.Config.Memory,
... ...
@@ -263,21 +256,25 @@ func populateCommand(c *Container, env []string) error {
263 263
 		Tty:        c.Config.Tty,
264 264
 		User:       c.Config.User,
265 265
 	}
266
+
266 267
 	processConfig.SysProcAttr = &syscall.SysProcAttr{Setsid: true}
267 268
 	processConfig.Env = env
269
+
268 270
 	c.command = &execdriver.Command{
269 271
 		ID:                 c.ID,
270 272
 		Rootfs:             c.RootfsPath(),
271 273
 		InitPath:           "/.dockerinit",
272 274
 		WorkingDir:         c.Config.WorkingDir,
273 275
 		Network:            en,
274
-		Config:             context,
275 276
 		Resources:          resources,
276 277
 		AllowedDevices:     allowedDevices,
277 278
 		AutoCreatedDevices: autoCreatedDevices,
278 279
 		CapAdd:             c.hostConfig.CapAdd,
279 280
 		CapDrop:            c.hostConfig.CapDrop,
280 281
 		ProcessConfig:      processConfig,
282
+		ProcessLabel:       c.GetProcessLabel(),
283
+		MountLabel:         c.GetMountLabel(),
284
+		LxcConfig:          lxcConfig,
281 285
 	}
282 286
 
283 287
 	return nil
... ...
@@ -99,19 +99,21 @@ type ProcessConfig struct {
99 99
 
100 100
 // Process wrapps an os/exec.Cmd to add more metadata
101 101
 type Command struct {
102
-	ID                 string              `json:"id"`
103
-	Rootfs             string              `json:"rootfs"`   // root fs of the container
104
-	InitPath           string              `json:"initpath"` // dockerinit
105
-	WorkingDir         string              `json:"working_dir"`
106
-	ConfigPath         string              `json:"config_path"` // this should be able to be removed when the lxc template is moved into the driver
107
-	Network            *Network            `json:"network"`
108
-	Config             map[string][]string `json:"config"` //  generic values that specific drivers can consume
109
-	Resources          *Resources          `json:"resources"`
110
-	Mounts             []Mount             `json:"mounts"`
111
-	AllowedDevices     []*devices.Device   `json:"allowed_devices"`
112
-	AutoCreatedDevices []*devices.Device   `json:"autocreated_devices"`
113
-	CapAdd             []string            `json:"cap_add"`
114
-	CapDrop            []string            `json:"cap_drop"`
115
-	ContainerPid       int                 `json:"container_pid"`  // the pid for the process inside a container
116
-	ProcessConfig      ProcessConfig       `json:"process_config"` // Describes the init process of the container.
102
+	ID                 string            `json:"id"`
103
+	Rootfs             string            `json:"rootfs"`   // root fs of the container
104
+	InitPath           string            `json:"initpath"` // dockerinit
105
+	WorkingDir         string            `json:"working_dir"`
106
+	ConfigPath         string            `json:"config_path"` // this should be able to be removed when the lxc template is moved into the driver
107
+	Network            *Network          `json:"network"`
108
+	Resources          *Resources        `json:"resources"`
109
+	Mounts             []Mount           `json:"mounts"`
110
+	AllowedDevices     []*devices.Device `json:"allowed_devices"`
111
+	AutoCreatedDevices []*devices.Device `json:"autocreated_devices"`
112
+	CapAdd             []string          `json:"cap_add"`
113
+	CapDrop            []string          `json:"cap_drop"`
114
+	ContainerPid       int               `json:"container_pid"`  // the pid for the process inside a container
115
+	ProcessConfig      ProcessConfig     `json:"process_config"` // Describes the init process of the container.
116
+	ProcessLabel       string            `json:"process_label"`
117
+	MountLabel         string            `json:"mount_label"`
118
+	LxcConfig          []string          `json:"lxc_config"`
117 119
 }
... ...
@@ -22,7 +22,6 @@ import (
22 22
 	"github.com/docker/docker/pkg/term"
23 23
 	"github.com/docker/docker/utils"
24 24
 	"github.com/docker/libcontainer/cgroups"
25
-	"github.com/docker/libcontainer/label"
26 25
 	"github.com/docker/libcontainer/mount/nodes"
27 26
 )
28 27
 
... ...
@@ -410,37 +409,24 @@ func rootIsShared() bool {
410 410
 }
411 411
 
412 412
 func (d *driver) generateLXCConfig(c *execdriver.Command) (string, error) {
413
-	var (
414
-		process, mount string
415
-		root           = path.Join(d.root, "containers", c.ID, "config.lxc")
416
-		labels         = c.Config["label"]
417
-	)
413
+	root := path.Join(d.root, "containers", c.ID, "config.lxc")
414
+
418 415
 	fo, err := os.Create(root)
419 416
 	if err != nil {
420 417
 		return "", err
421 418
 	}
422 419
 	defer fo.Close()
423 420
 
424
-	if len(labels) > 0 {
425
-		process, mount, err = label.GenLabels(labels[0])
426
-		if err != nil {
427
-			return "", err
428
-		}
429
-	}
430
-
431 421
 	if err := LxcTemplateCompiled.Execute(fo, struct {
432 422
 		*execdriver.Command
433
-		AppArmor     bool
434
-		ProcessLabel string
435
-		MountLabel   string
423
+		AppArmor bool
436 424
 	}{
437
-		Command:      c,
438
-		AppArmor:     d.apparmor,
439
-		ProcessLabel: process,
440
-		MountLabel:   mount,
425
+		Command:  c,
426
+		AppArmor: d.apparmor,
441 427
 	}); err != nil {
442 428
 		return "", err
443 429
 	}
430
+
444 431
 	return root, nil
445 432
 }
446 433
 
... ...
@@ -34,10 +34,6 @@ lxc.pts = 1024
34 34
 
35 35
 # disable the main console
36 36
 lxc.console = none
37
-{{if .ProcessLabel}}
38
-lxc.se_context = {{ .ProcessLabel}}
39
-{{end}}
40
-{{$MOUNTLABEL := .MountLabel}}
41 37
 
42 38
 # no controlling tty at all
43 39
 lxc.tty = 1
... ...
@@ -70,8 +66,8 @@ lxc.mount.entry = sysfs {{escapeFstabSpaces $ROOTFS}}/sys sysfs nosuid,nodev,noe
70 70
 lxc.mount.entry = {{.ProcessConfig.Console}} {{escapeFstabSpaces $ROOTFS}}/dev/console none bind,rw 0 0
71 71
 {{end}}
72 72
 
73
-lxc.mount.entry = devpts {{escapeFstabSpaces $ROOTFS}}/dev/pts devpts {{formatMountLabel "newinstance,ptmxmode=0666,nosuid,noexec" $MOUNTLABEL}} 0 0
74
-lxc.mount.entry = shm {{escapeFstabSpaces $ROOTFS}}/dev/shm tmpfs {{formatMountLabel "size=65536k,nosuid,nodev,noexec" $MOUNTLABEL}} 0 0
73
+lxc.mount.entry = devpts {{escapeFstabSpaces $ROOTFS}}/dev/pts devpts {{formatMountLabel "newinstance,ptmxmode=0666,nosuid,noexec" ""}} 0 0
74
+lxc.mount.entry = shm {{escapeFstabSpaces $ROOTFS}}/dev/shm tmpfs {{formatMountLabel "size=65536k,nosuid,nodev,noexec" ""}} 0 0
75 75
 
76 76
 {{range $value := .Mounts}}
77 77
 {{if $value.Writable}}
... ...
@@ -106,8 +102,8 @@ lxc.cgroup.cpuset.cpus = {{.Resources.Cpuset}}
106 106
 {{end}}
107 107
 {{end}}
108 108
 
109
-{{if .Config.lxc}}
110
-{{range $value := .Config.lxc}}
109
+{{if .LxcConfig}}
110
+{{range $value := .LxcConfig}}
111 111
 lxc.{{$value}}
112 112
 {{end}}
113 113
 {{end}}
... ...
@@ -83,11 +83,9 @@ func TestCustomLxcConfig(t *testing.T) {
83 83
 	}
84 84
 	command := &execdriver.Command{
85 85
 		ID: "1",
86
-		Config: map[string][]string{
87
-			"lxc": {
88
-				"lxc.utsname = docker",
89
-				"lxc.cgroup.cpuset.cpus = 0,1",
90
-			},
86
+		LxcConfig: []string{
87
+			"lxc.utsname = docker",
88
+			"lxc.cgroup.cpuset.cpus = 0,1",
91 89
 		},
92 90
 		Network: &execdriver.Network{
93 91
 			Mtu:       1500,
94 92
deleted file mode 100644
... ...
@@ -1,188 +0,0 @@
1
-package configuration
2
-
3
-import (
4
-	"fmt"
5
-	"os/exec"
6
-	"path/filepath"
7
-	"strconv"
8
-	"strings"
9
-
10
-	"github.com/docker/docker/pkg/units"
11
-	"github.com/docker/libcontainer"
12
-)
13
-
14
-type Action func(*libcontainer.Config, interface{}, string) error
15
-
16
-var actions = map[string]Action{
17
-	"cap.add":  addCap,  // add a cap
18
-	"cap.drop": dropCap, // drop a cap
19
-
20
-	"ns.add":  addNamespace,  // add a namespace
21
-	"ns.drop": dropNamespace, // drop a namespace when cloning
22
-
23
-	"net.join": joinNetNamespace, // join another containers net namespace
24
-
25
-	"cgroups.cpu_shares":         cpuShares,         // set the cpu shares
26
-	"cgroups.memory":             memory,            // set the memory limit
27
-	"cgroups.memory_reservation": memoryReservation, // set the memory reservation
28
-	"cgroups.memory_swap":        memorySwap,        // set the memory swap limit
29
-	"cgroups.cpuset.cpus":        cpusetCpus,        // set the cpus used
30
-
31
-	"systemd.slice": systemdSlice, // set parent Slice used for systemd unit
32
-
33
-	"apparmor_profile": apparmorProfile, // set the apparmor profile to apply
34
-
35
-	"fs.readonly": readonlyFs, // make the rootfs of the container read only
36
-}
37
-
38
-func cpusetCpus(container *libcontainer.Config, context interface{}, value string) error {
39
-	if container.Cgroups == nil {
40
-		return fmt.Errorf("cannot set cgroups when they are disabled")
41
-	}
42
-	container.Cgroups.CpusetCpus = value
43
-
44
-	return nil
45
-}
46
-
47
-func systemdSlice(container *libcontainer.Config, context interface{}, value string) error {
48
-	if container.Cgroups == nil {
49
-		return fmt.Errorf("cannot set slice when cgroups are disabled")
50
-	}
51
-	container.Cgroups.Slice = value
52
-
53
-	return nil
54
-}
55
-
56
-func apparmorProfile(container *libcontainer.Config, context interface{}, value string) error {
57
-	container.AppArmorProfile = value
58
-	return nil
59
-}
60
-
61
-func cpuShares(container *libcontainer.Config, context interface{}, value string) error {
62
-	if container.Cgroups == nil {
63
-		return fmt.Errorf("cannot set cgroups when they are disabled")
64
-	}
65
-	v, err := strconv.ParseInt(value, 10, 0)
66
-	if err != nil {
67
-		return err
68
-	}
69
-	container.Cgroups.CpuShares = v
70
-	return nil
71
-}
72
-
73
-func memory(container *libcontainer.Config, context interface{}, value string) error {
74
-	if container.Cgroups == nil {
75
-		return fmt.Errorf("cannot set cgroups when they are disabled")
76
-	}
77
-
78
-	v, err := units.RAMInBytes(value)
79
-	if err != nil {
80
-		return err
81
-	}
82
-	container.Cgroups.Memory = v
83
-	return nil
84
-}
85
-
86
-func memoryReservation(container *libcontainer.Config, context interface{}, value string) error {
87
-	if container.Cgroups == nil {
88
-		return fmt.Errorf("cannot set cgroups when they are disabled")
89
-	}
90
-
91
-	v, err := units.RAMInBytes(value)
92
-	if err != nil {
93
-		return err
94
-	}
95
-	container.Cgroups.MemoryReservation = v
96
-	return nil
97
-}
98
-
99
-func memorySwap(container *libcontainer.Config, context interface{}, value string) error {
100
-	if container.Cgroups == nil {
101
-		return fmt.Errorf("cannot set cgroups when they are disabled")
102
-	}
103
-	v, err := strconv.ParseInt(value, 0, 64)
104
-	if err != nil {
105
-		return err
106
-	}
107
-	container.Cgroups.MemorySwap = v
108
-	return nil
109
-}
110
-
111
-func addCap(container *libcontainer.Config, context interface{}, value string) error {
112
-	container.Capabilities = append(container.Capabilities, value)
113
-	return nil
114
-}
115
-
116
-func dropCap(container *libcontainer.Config, context interface{}, value string) error {
117
-	// If the capability is specified multiple times, remove all instances.
118
-	for i, capability := range container.Capabilities {
119
-		if capability == value {
120
-			container.Capabilities = append(container.Capabilities[:i], container.Capabilities[i+1:]...)
121
-		}
122
-	}
123
-
124
-	// The capability wasn't found so we will drop it anyways.
125
-	return nil
126
-}
127
-
128
-func addNamespace(container *libcontainer.Config, context interface{}, value string) error {
129
-	container.Namespaces[value] = true
130
-	return nil
131
-}
132
-
133
-func dropNamespace(container *libcontainer.Config, context interface{}, value string) error {
134
-	container.Namespaces[value] = false
135
-	return nil
136
-}
137
-
138
-func readonlyFs(container *libcontainer.Config, context interface{}, value string) error {
139
-	switch value {
140
-	case "1", "true":
141
-		container.MountConfig.ReadonlyFs = true
142
-	default:
143
-		container.MountConfig.ReadonlyFs = false
144
-	}
145
-	return nil
146
-}
147
-
148
-func joinNetNamespace(container *libcontainer.Config, context interface{}, value string) error {
149
-	var (
150
-		running = context.(map[string]*exec.Cmd)
151
-		cmd     = running[value]
152
-	)
153
-
154
-	if cmd == nil || cmd.Process == nil {
155
-		return fmt.Errorf("%s is not a valid running container to join", value)
156
-	}
157
-
158
-	nspath := filepath.Join("/proc", fmt.Sprint(cmd.Process.Pid), "ns", "net")
159
-	container.Networks = append(container.Networks, &libcontainer.Network{
160
-		Type:   "netns",
161
-		NsPath: nspath,
162
-	})
163
-
164
-	return nil
165
-}
166
-
167
-// configureCustomOptions takes string commands from the user and allows modification of the
168
-// container's default configuration.
169
-//
170
-// TODO: this can be moved to a general utils or parser in pkg
171
-func ParseConfiguration(container *libcontainer.Config, running map[string]*exec.Cmd, opts []string) error {
172
-	for _, opt := range opts {
173
-		kv := strings.SplitN(opt, "=", 2)
174
-		if len(kv) < 2 {
175
-			return fmt.Errorf("invalid format for %s", opt)
176
-		}
177
-
178
-		action, exists := actions[kv[0]]
179
-		if !exists {
180
-			return fmt.Errorf("%s is not a valid option for the native driver", kv[0])
181
-		}
182
-
183
-		if err := action(container, running, kv[1]); err != nil {
184
-			return err
185
-		}
186
-	}
187
-	return nil
188
-}
189 1
deleted file mode 100644
... ...
@@ -1,193 +0,0 @@
1
-package configuration
2
-
3
-import (
4
-	"testing"
5
-
6
-	"github.com/docker/docker/daemon/execdriver/native/template"
7
-	"github.com/docker/libcontainer/security/capabilities"
8
-)
9
-
10
-// Checks whether the expected capability is specified in the capabilities.
11
-func hasCapability(expected string, capabilities []string) bool {
12
-	for _, capability := range capabilities {
13
-		if capability == expected {
14
-			return true
15
-		}
16
-	}
17
-	return false
18
-}
19
-
20
-func TestSetReadonlyRootFs(t *testing.T) {
21
-	var (
22
-		container = template.New()
23
-		opts      = []string{
24
-			"fs.readonly=true",
25
-		}
26
-	)
27
-
28
-	if container.MountConfig.ReadonlyFs {
29
-		t.Fatal("container should not have a readonly rootfs by default")
30
-	}
31
-	if err := ParseConfiguration(container, nil, opts); err != nil {
32
-		t.Fatal(err)
33
-	}
34
-
35
-	if !container.MountConfig.ReadonlyFs {
36
-		t.Fatal("container should have a readonly rootfs")
37
-	}
38
-}
39
-
40
-func TestConfigurationsDoNotConflict(t *testing.T) {
41
-	var (
42
-		container1 = template.New()
43
-		container2 = template.New()
44
-		opts       = []string{
45
-			"cap.add=NET_ADMIN",
46
-		}
47
-	)
48
-
49
-	if err := ParseConfiguration(container1, nil, opts); err != nil {
50
-		t.Fatal(err)
51
-	}
52
-
53
-	if !hasCapability("NET_ADMIN", container1.Capabilities) {
54
-		t.Fatal("container one should have NET_ADMIN enabled")
55
-	}
56
-	if hasCapability("NET_ADMIN", container2.Capabilities) {
57
-		t.Fatal("container two should not have NET_ADMIN enabled")
58
-	}
59
-}
60
-
61
-func TestCpusetCpus(t *testing.T) {
62
-	var (
63
-		container = template.New()
64
-		opts      = []string{
65
-			"cgroups.cpuset.cpus=1,2",
66
-		}
67
-	)
68
-	if err := ParseConfiguration(container, nil, opts); err != nil {
69
-		t.Fatal(err)
70
-	}
71
-
72
-	if expected := "1,2"; container.Cgroups.CpusetCpus != expected {
73
-		t.Fatalf("expected %s got %s for cpuset.cpus", expected, container.Cgroups.CpusetCpus)
74
-	}
75
-}
76
-
77
-func TestAppArmorProfile(t *testing.T) {
78
-	var (
79
-		container = template.New()
80
-		opts      = []string{
81
-			"apparmor_profile=koye-the-protector",
82
-		}
83
-	)
84
-	if err := ParseConfiguration(container, nil, opts); err != nil {
85
-		t.Fatal(err)
86
-	}
87
-
88
-	if expected := "koye-the-protector"; container.AppArmorProfile != expected {
89
-		t.Fatalf("expected profile %s got %s", expected, container.AppArmorProfile)
90
-	}
91
-}
92
-
93
-func TestCpuShares(t *testing.T) {
94
-	var (
95
-		container = template.New()
96
-		opts      = []string{
97
-			"cgroups.cpu_shares=1048",
98
-		}
99
-	)
100
-	if err := ParseConfiguration(container, nil, opts); err != nil {
101
-		t.Fatal(err)
102
-	}
103
-
104
-	if expected := int64(1048); container.Cgroups.CpuShares != expected {
105
-		t.Fatalf("expected cpu shares %d got %d", expected, container.Cgroups.CpuShares)
106
-	}
107
-}
108
-
109
-func TestMemory(t *testing.T) {
110
-	var (
111
-		container = template.New()
112
-		opts      = []string{
113
-			"cgroups.memory=500m",
114
-		}
115
-	)
116
-	if err := ParseConfiguration(container, nil, opts); err != nil {
117
-		t.Fatal(err)
118
-	}
119
-
120
-	if expected := int64(500 * 1024 * 1024); container.Cgroups.Memory != expected {
121
-		t.Fatalf("expected memory %d got %d", expected, container.Cgroups.Memory)
122
-	}
123
-}
124
-
125
-func TestMemoryReservation(t *testing.T) {
126
-	var (
127
-		container = template.New()
128
-		opts      = []string{
129
-			"cgroups.memory_reservation=500m",
130
-		}
131
-	)
132
-	if err := ParseConfiguration(container, nil, opts); err != nil {
133
-		t.Fatal(err)
134
-	}
135
-
136
-	if expected := int64(500 * 1024 * 1024); container.Cgroups.MemoryReservation != expected {
137
-		t.Fatalf("expected memory reservation %d got %d", expected, container.Cgroups.MemoryReservation)
138
-	}
139
-}
140
-
141
-func TestAddCap(t *testing.T) {
142
-	var (
143
-		container = template.New()
144
-		opts      = []string{
145
-			"cap.add=MKNOD",
146
-			"cap.add=SYS_ADMIN",
147
-		}
148
-	)
149
-	if err := ParseConfiguration(container, nil, opts); err != nil {
150
-		t.Fatal(err)
151
-	}
152
-
153
-	if !hasCapability("MKNOD", container.Capabilities) {
154
-		t.Fatal("container should have MKNOD enabled")
155
-	}
156
-	if !hasCapability("SYS_ADMIN", container.Capabilities) {
157
-		t.Fatal("container should have SYS_ADMIN enabled")
158
-	}
159
-}
160
-
161
-func TestDropCap(t *testing.T) {
162
-	var (
163
-		container = template.New()
164
-		opts      = []string{
165
-			"cap.drop=MKNOD",
166
-		}
167
-	)
168
-	// enabled all caps like in privileged mode
169
-	container.Capabilities = capabilities.GetAllCapabilities()
170
-	if err := ParseConfiguration(container, nil, opts); err != nil {
171
-		t.Fatal(err)
172
-	}
173
-
174
-	if hasCapability("MKNOD", container.Capabilities) {
175
-		t.Fatal("container should not have MKNOD enabled")
176
-	}
177
-}
178
-
179
-func TestDropNamespace(t *testing.T) {
180
-	var (
181
-		container = template.New()
182
-		opts      = []string{
183
-			"ns.drop=NEWNET",
184
-		}
185
-	)
186
-	if err := ParseConfiguration(container, nil, opts); err != nil {
187
-		t.Fatal(err)
188
-	}
189
-
190
-	if container.Namespaces["NEWNET"] {
191
-		t.Fatal("container should not have NEWNET enabled")
192
-	}
193
-}
... ...
@@ -9,7 +9,6 @@ import (
9 9
 	"path/filepath"
10 10
 
11 11
 	"github.com/docker/docker/daemon/execdriver"
12
-	"github.com/docker/docker/daemon/execdriver/native/configuration"
13 12
 	"github.com/docker/docker/daemon/execdriver/native/template"
14 13
 	"github.com/docker/libcontainer"
15 14
 	"github.com/docker/libcontainer/apparmor"
... ...
@@ -69,10 +68,6 @@ func (d *driver) createContainer(c *execdriver.Command) (*libcontainer.Config, e
69 69
 	}
70 70
 	d.Unlock()
71 71
 
72
-	if err := configuration.ParseConfiguration(container, cmds, c.Config["native"]); err != nil {
73
-		return nil, err
74
-	}
75
-
76 72
 	return container, nil
77 73
 }
78 74
 
... ...
@@ -175,8 +170,8 @@ func (d *driver) setupMounts(container *libcontainer.Config, c *execdriver.Comma
175 175
 }
176 176
 
177 177
 func (d *driver) setupLabels(container *libcontainer.Config, c *execdriver.Command) error {
178
-	container.ProcessLabel = c.Config["process_label"][0]
179
-	container.MountConfig.MountLabel = c.Config["mount_label"][0]
178
+	container.ProcessLabel = c.ProcessLabel
179
+	container.MountConfig.MountLabel = c.MountLabel
180 180
 
181 181
 	return nil
182 182
 }
... ...
@@ -32,20 +32,22 @@ func migratePortMappings(config *runconfig.Config, hostConfig *runconfig.HostCon
32 32
 	return nil
33 33
 }
34 34
 
35
-func mergeLxcConfIntoOptions(hostConfig *runconfig.HostConfig, driverConfig map[string][]string) {
35
+func mergeLxcConfIntoOptions(hostConfig *runconfig.HostConfig) []string {
36 36
 	if hostConfig == nil {
37
-		return
37
+		return nil
38 38
 	}
39 39
 
40
+	out := []string{}
41
+
40 42
 	// merge in the lxc conf options into the generic config map
41 43
 	if lxcConf := hostConfig.LxcConf; lxcConf != nil {
42
-		lxc := driverConfig["lxc"]
43 44
 		for _, pair := range lxcConf {
44 45
 			// because lxc conf gets the driver name lxc.XXXX we need to trim it off
45 46
 			// and let the lxc driver add it back later if needed
46 47
 			parts := strings.SplitN(pair.Key, ".", 2)
47
-			lxc = append(lxc, fmt.Sprintf("%s=%s", parts[1], pair.Value))
48
+			out = append(out, fmt.Sprintf("%s=%s", parts[1], pair.Value))
48 49
 		}
49
-		driverConfig["lxc"] = lxc
50 50
 	}
51
+
52
+	return out
51 53
 }
... ...
@@ -8,21 +8,15 @@ import (
8 8
 )
9 9
 
10 10
 func TestMergeLxcConfig(t *testing.T) {
11
-	var (
12
-		hostConfig = &runconfig.HostConfig{
13
-			LxcConf: []utils.KeyValuePair{
14
-				{Key: "lxc.cgroups.cpuset", Value: "1,2"},
15
-			},
16
-		}
17
-		driverConfig = make(map[string][]string)
18
-	)
19
-
20
-	mergeLxcConfIntoOptions(hostConfig, driverConfig)
21
-	if l := len(driverConfig["lxc"]); l > 1 {
22
-		t.Fatalf("expected lxc options len of 1 got %d", l)
11
+	hostConfig := &runconfig.HostConfig{
12
+		LxcConf: []utils.KeyValuePair{
13
+			{Key: "lxc.cgroups.cpuset", Value: "1,2"},
14
+		},
23 15
 	}
24 16
 
25
-	cpuset := driverConfig["lxc"][0]
17
+	out := mergeLxcConfIntoOptions(hostConfig)
18
+
19
+	cpuset := out[0]
26 20
 	if expected := "cgroups.cpuset=1,2"; cpuset != expected {
27 21
 		t.Fatalf("expected %s got %s", expected, cpuset)
28 22
 	}