Browse code

Add unit test for lxc conf merge and native opts Docker-DCO-1.1-Signed-off-by: Michael Crosby <michael@crosbymichael.com> (github: crosbymichael)

Michael Crosby authored on 2014/03/24 16:16:40
Showing 9 changed files
... ...
@@ -383,14 +383,8 @@ func populateCommand(c *Container) {
383 383
 		}
384 384
 	}
385 385
 
386
-	// merge in the lxc conf options into the generic config map
387
-	if lxcConf := c.hostConfig.LxcConf; lxcConf != nil {
388
-		lxc := driverConfig["lxc"]
389
-		for _, pair := range lxcConf {
390
-			lxc = append(lxc, fmt.Sprintf("%s = %s", pair.Key, pair.Value))
391
-		}
392
-		driverConfig["lxc"] = lxc
393
-	}
386
+	// TODO: this can be removed after lxc-conf is fully deprecated
387
+	mergeLxcConfIntoOptions(c.hostConfig, driverConfig)
394 388
 
395 389
 	resources := &execdriver.Resources{
396 390
 		Memory:     c.Config.Memory,
... ...
@@ -120,7 +120,7 @@ lxc.cgroup.cpu.shares = {{.Resources.CpuShares}}
120 120
 
121 121
 {{if .Config.lxc}}
122 122
 {{range $value := .Config.lxc}}
123
-{{$value}}
123
+lxc.{{$value}}
124 124
 {{end}}
125 125
 {{end}}
126 126
 `
... ...
@@ -31,20 +31,6 @@ var actions = map[string]Action{
31 31
 	"fs.readonly": readonlyFs, // make the rootfs of the container read only
32 32
 }
33 33
 
34
-// GetSupportedActions returns a list of all the avaliable actions supported by the driver
35
-// TODO: this should return a description also
36
-func GetSupportedActions() []string {
37
-	var (
38
-		i   int
39
-		out = make([]string, len(actions))
40
-	)
41
-	for k := range actions {
42
-		out[i] = k
43
-		i++
44
-	}
45
-	return out
46
-}
47
-
48 34
 func cpusetCpus(container *libcontainer.Container, context interface{}, value string) error {
49 35
 	if container.Cgroups == nil {
50 36
 		return fmt.Errorf("cannot set cgroups when they are disabled")
51 37
new file mode 100644
... ...
@@ -0,0 +1,166 @@
0
+package configuration
1
+
2
+import (
3
+	"github.com/dotcloud/docker/runtime/execdriver/native/template"
4
+	"testing"
5
+)
6
+
7
+func TestSetReadonlyRootFs(t *testing.T) {
8
+	var (
9
+		container = template.New()
10
+		opts      = []string{
11
+			"fs.readonly=true",
12
+		}
13
+	)
14
+
15
+	if container.ReadonlyFs {
16
+		t.Fatal("container should not have a readonly rootfs by default")
17
+	}
18
+	if err := ParseConfiguration(container, nil, opts); err != nil {
19
+		t.Fatal(err)
20
+	}
21
+
22
+	if !container.ReadonlyFs {
23
+		t.Fatal("container should have a readonly rootfs")
24
+	}
25
+}
26
+
27
+func TestConfigurationsDoNotConflict(t *testing.T) {
28
+	var (
29
+		container1 = template.New()
30
+		container2 = template.New()
31
+		opts       = []string{
32
+			"cap.add=NET_ADMIN",
33
+		}
34
+	)
35
+
36
+	if err := ParseConfiguration(container1, nil, opts); err != nil {
37
+		t.Fatal(err)
38
+	}
39
+
40
+	if !container1.CapabilitiesMask.Get("NET_ADMIN").Enabled {
41
+		t.Fatal("container one should have NET_ADMIN enabled")
42
+	}
43
+	if container2.CapabilitiesMask.Get("NET_ADMIN").Enabled {
44
+		t.Fatal("container two should not have NET_ADMIN enabled")
45
+	}
46
+}
47
+
48
+func TestCpusetCpus(t *testing.T) {
49
+	var (
50
+		container = template.New()
51
+		opts      = []string{
52
+			"cgroups.cpuset.cpus=1,2",
53
+		}
54
+	)
55
+	if err := ParseConfiguration(container, nil, opts); err != nil {
56
+		t.Fatal(err)
57
+	}
58
+
59
+	if expected := "1,2"; container.Cgroups.CpusetCpus != expected {
60
+		t.Fatalf("expected %s got %s for cpuset.cpus", expected, container.Cgroups.CpusetCpus)
61
+	}
62
+}
63
+
64
+func TestAppArmorProfile(t *testing.T) {
65
+	var (
66
+		container = template.New()
67
+		opts      = []string{
68
+			"apparmor_profile=koye-the-protector",
69
+		}
70
+	)
71
+	if err := ParseConfiguration(container, nil, opts); err != nil {
72
+		t.Fatal(err)
73
+	}
74
+	if expected := "koye-the-protector"; container.Context["apparmor_profile"] != expected {
75
+		t.Fatalf("expected profile %s got %s", expected, container.Context["apparmor_profile"])
76
+	}
77
+}
78
+
79
+func TestCpuShares(t *testing.T) {
80
+	var (
81
+		container = template.New()
82
+		opts      = []string{
83
+			"cgroups.cpu_shares=1048",
84
+		}
85
+	)
86
+	if err := ParseConfiguration(container, nil, opts); err != nil {
87
+		t.Fatal(err)
88
+	}
89
+
90
+	if expected := int64(1048); container.Cgroups.CpuShares != expected {
91
+		t.Fatalf("expected cpu shares %d got %d", expected, container.Cgroups.CpuShares)
92
+	}
93
+}
94
+
95
+func TestCgroupMemory(t *testing.T) {
96
+	var (
97
+		container = template.New()
98
+		opts      = []string{
99
+			"cgroups.memory=500m",
100
+		}
101
+	)
102
+	if err := ParseConfiguration(container, nil, opts); err != nil {
103
+		t.Fatal(err)
104
+	}
105
+
106
+	if expected := int64(500 * 1024 * 1024); container.Cgroups.Memory != expected {
107
+		t.Fatalf("expected memory %d got %d", expected, container.Cgroups.Memory)
108
+	}
109
+}
110
+
111
+func TestAddCap(t *testing.T) {
112
+	var (
113
+		container = template.New()
114
+		opts      = []string{
115
+			"cap.add=MKNOD",
116
+			"cap.add=SYS_ADMIN",
117
+		}
118
+	)
119
+	if err := ParseConfiguration(container, nil, opts); err != nil {
120
+		t.Fatal(err)
121
+	}
122
+
123
+	if !container.CapabilitiesMask.Get("MKNOD").Enabled {
124
+		t.Fatal("container should have MKNOD enabled")
125
+	}
126
+	if !container.CapabilitiesMask.Get("SYS_ADMIN").Enabled {
127
+		t.Fatal("container should have SYS_ADMIN enabled")
128
+	}
129
+}
130
+
131
+func TestDropCap(t *testing.T) {
132
+	var (
133
+		container = template.New()
134
+		opts      = []string{
135
+			"cap.drop=MKNOD",
136
+		}
137
+	)
138
+	// enabled all caps like in privileged mode
139
+	for _, c := range container.CapabilitiesMask {
140
+		c.Enabled = true
141
+	}
142
+	if err := ParseConfiguration(container, nil, opts); err != nil {
143
+		t.Fatal(err)
144
+	}
145
+
146
+	if container.CapabilitiesMask.Get("MKNOD").Enabled {
147
+		t.Fatal("container should not have MKNOD enabled")
148
+	}
149
+}
150
+
151
+func TestDropNamespace(t *testing.T) {
152
+	var (
153
+		container = template.New()
154
+		opts      = []string{
155
+			"ns.drop=NEWNET",
156
+		}
157
+	)
158
+	if err := ParseConfiguration(container, nil, opts); err != nil {
159
+		t.Fatal(err)
160
+	}
161
+
162
+	if container.Namespaces.Get("NEWNET").Enabled {
163
+		t.Fatal("container should not have NEWNET enabled")
164
+	}
165
+}
... ...
@@ -5,30 +5,53 @@ import (
5 5
 	"github.com/dotcloud/docker/pkg/libcontainer"
6 6
 	"github.com/dotcloud/docker/runtime/execdriver"
7 7
 	"github.com/dotcloud/docker/runtime/execdriver/native/configuration"
8
+	"github.com/dotcloud/docker/runtime/execdriver/native/template"
8 9
 	"os"
9 10
 )
10 11
 
11 12
 // createContainer populates and configures the container type with the
12 13
 // data provided by the execdriver.Command
13 14
 func (d *driver) createContainer(c *execdriver.Command) (*libcontainer.Container, error) {
14
-	container := getDefaultTemplate()
15
+	container := template.New()
15 16
 
16 17
 	container.Hostname = getEnv("HOSTNAME", c.Env)
17 18
 	container.Tty = c.Tty
18 19
 	container.User = c.User
19 20
 	container.WorkingDir = c.WorkingDir
20 21
 	container.Env = c.Env
22
+	container.Cgroups.Name = c.ID
23
+	// check to see if we are running in ramdisk to disable pivot root
24
+	container.NoPivotRoot = os.Getenv("DOCKER_RAMDISK") != ""
21 25
 
22
-	loopbackNetwork := libcontainer.Network{
23
-		Mtu:     c.Network.Mtu,
24
-		Address: fmt.Sprintf("%s/%d", "127.0.0.1", 0),
25
-		Gateway: "localhost",
26
-		Type:    "loopback",
27
-		Context: libcontainer.Context{},
26
+	if err := d.createNetwork(container, c); err != nil {
27
+		return nil, err
28 28
 	}
29
+	if c.Privileged {
30
+		if err := d.setPrivileged(container); err != nil {
31
+			return nil, err
32
+		}
33
+	}
34
+	if err := d.setupCgroups(container, c); err != nil {
35
+		return nil, err
36
+	}
37
+	if err := d.setupMounts(container, c); err != nil {
38
+		return nil, err
39
+	}
40
+	if err := configuration.ParseConfiguration(container, d.activeContainers, c.Config["native"]); err != nil {
41
+		return nil, err
42
+	}
43
+	return container, nil
44
+}
29 45
 
46
+func (d *driver) createNetwork(container *libcontainer.Container, c *execdriver.Command) error {
30 47
 	container.Networks = []*libcontainer.Network{
31
-		&loopbackNetwork,
48
+		{
49
+			Mtu:     c.Network.Mtu,
50
+			Address: fmt.Sprintf("%s/%d", "127.0.0.1", 0),
51
+			Gateway: "localhost",
52
+			Type:    "loopback",
53
+			Context: libcontainer.Context{},
54
+		},
32 55
 	}
33 56
 
34 57
 	if c.Network.Interface != nil {
... ...
@@ -44,27 +67,30 @@ func (d *driver) createContainer(c *execdriver.Command) (*libcontainer.Container
44 44
 		}
45 45
 		container.Networks = append(container.Networks, &vethNetwork)
46 46
 	}
47
+	return nil
48
+}
47 49
 
48
-	container.Cgroups.Name = c.ID
49
-	if c.Privileged {
50
-		container.CapabilitiesMask = nil
51
-		container.Cgroups.DeviceAccess = true
52
-		container.Context["apparmor_profile"] = "unconfined"
50
+func (d *driver) setPrivileged(container *libcontainer.Container) error {
51
+	for _, c := range container.CapabilitiesMask {
52
+		c.Enabled = true
53 53
 	}
54
+	container.Cgroups.DeviceAccess = true
55
+	container.Context["apparmor_profile"] = "unconfined"
56
+	return nil
57
+}
58
+
59
+func (d *driver) setupCgroups(container *libcontainer.Container, c *execdriver.Command) error {
54 60
 	if c.Resources != nil {
55 61
 		container.Cgroups.CpuShares = c.Resources.CpuShares
56 62
 		container.Cgroups.Memory = c.Resources.Memory
57 63
 		container.Cgroups.MemorySwap = c.Resources.MemorySwap
58 64
 	}
59
-	// check to see if we are running in ramdisk to disable pivot root
60
-	container.NoPivotRoot = os.Getenv("DOCKER_RAMDISK") != ""
65
+	return nil
66
+}
61 67
 
68
+func (d *driver) setupMounts(container *libcontainer.Container, c *execdriver.Command) error {
62 69
 	for _, m := range c.Mounts {
63 70
 		container.Mounts = append(container.Mounts, libcontainer.Mount{m.Source, m.Destination, m.Writable, m.Private})
64 71
 	}
65
-
66
-	if err := configuration.ParseConfiguration(container, d.activeContainers, c.Config["native"]); err != nil {
67
-		return nil, err
68
-	}
69
-	return container, nil
72
+	return nil
70 73
 }
71 74
deleted file mode 100644
... ...
@@ -1,44 +0,0 @@
1
-package native
2
-
3
-import (
4
-	"github.com/dotcloud/docker/pkg/cgroups"
5
-	"github.com/dotcloud/docker/pkg/libcontainer"
6
-)
7
-
8
-// getDefaultTemplate returns the docker default for
9
-// the libcontainer configuration file
10
-func getDefaultTemplate() *libcontainer.Container {
11
-	return &libcontainer.Container{
12
-		CapabilitiesMask: libcontainer.Capabilities{
13
-			libcontainer.GetCapability("SETPCAP"),
14
-			libcontainer.GetCapability("SYS_MODULE"),
15
-			libcontainer.GetCapability("SYS_RAWIO"),
16
-			libcontainer.GetCapability("SYS_PACCT"),
17
-			libcontainer.GetCapability("SYS_ADMIN"),
18
-			libcontainer.GetCapability("SYS_NICE"),
19
-			libcontainer.GetCapability("SYS_RESOURCE"),
20
-			libcontainer.GetCapability("SYS_TIME"),
21
-			libcontainer.GetCapability("SYS_TTY_CONFIG"),
22
-			libcontainer.GetCapability("MKNOD"),
23
-			libcontainer.GetCapability("AUDIT_WRITE"),
24
-			libcontainer.GetCapability("AUDIT_CONTROL"),
25
-			libcontainer.GetCapability("MAC_OVERRIDE"),
26
-			libcontainer.GetCapability("MAC_ADMIN"),
27
-			libcontainer.GetCapability("NET_ADMIN"),
28
-		},
29
-		Namespaces: libcontainer.Namespaces{
30
-			libcontainer.GetNamespace("NEWNS"),
31
-			libcontainer.GetNamespace("NEWUTS"),
32
-			libcontainer.GetNamespace("NEWIPC"),
33
-			libcontainer.GetNamespace("NEWPID"),
34
-			libcontainer.GetNamespace("NEWNET"),
35
-		},
36
-		Cgroups: &cgroups.Cgroup{
37
-			Parent:       "docker",
38
-			DeviceAccess: false,
39
-		},
40
-		Context: libcontainer.Context{
41
-			"apparmor_profile": "docker-default",
42
-		},
43
-	}
44
-}
45 1
new file mode 100644
... ...
@@ -0,0 +1,43 @@
0
+package template
1
+
2
+import (
3
+	"github.com/dotcloud/docker/pkg/cgroups"
4
+	"github.com/dotcloud/docker/pkg/libcontainer"
5
+)
6
+
7
+// New returns the docker default configuration for libcontainer
8
+func New() *libcontainer.Container {
9
+	return &libcontainer.Container{
10
+		CapabilitiesMask: libcontainer.Capabilities{
11
+			libcontainer.GetCapability("SETPCAP"),
12
+			libcontainer.GetCapability("SYS_MODULE"),
13
+			libcontainer.GetCapability("SYS_RAWIO"),
14
+			libcontainer.GetCapability("SYS_PACCT"),
15
+			libcontainer.GetCapability("SYS_ADMIN"),
16
+			libcontainer.GetCapability("SYS_NICE"),
17
+			libcontainer.GetCapability("SYS_RESOURCE"),
18
+			libcontainer.GetCapability("SYS_TIME"),
19
+			libcontainer.GetCapability("SYS_TTY_CONFIG"),
20
+			libcontainer.GetCapability("MKNOD"),
21
+			libcontainer.GetCapability("AUDIT_WRITE"),
22
+			libcontainer.GetCapability("AUDIT_CONTROL"),
23
+			libcontainer.GetCapability("MAC_OVERRIDE"),
24
+			libcontainer.GetCapability("MAC_ADMIN"),
25
+			libcontainer.GetCapability("NET_ADMIN"),
26
+		},
27
+		Namespaces: libcontainer.Namespaces{
28
+			libcontainer.GetNamespace("NEWNS"),
29
+			libcontainer.GetNamespace("NEWUTS"),
30
+			libcontainer.GetNamespace("NEWIPC"),
31
+			libcontainer.GetNamespace("NEWPID"),
32
+			libcontainer.GetNamespace("NEWNET"),
33
+		},
34
+		Cgroups: &cgroups.Cgroup{
35
+			Parent:       "docker",
36
+			DeviceAccess: false,
37
+		},
38
+		Context: libcontainer.Context{
39
+			"apparmor_profile": "docker-default",
40
+		},
41
+	}
42
+}
... ...
@@ -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
 }
36 54
new file mode 100644
... ...
@@ -0,0 +1,27 @@
0
+package runtime
1
+
2
+import (
3
+	"github.com/dotcloud/docker/runconfig"
4
+	"testing"
5
+)
6
+
7
+func TestMergeLxcConfig(t *testing.T) {
8
+	var (
9
+		hostConfig = &runconfig.HostConfig{
10
+			LxcConf: []runconfig.KeyValuePair{
11
+				{Key: "lxc.cgroups.cpuset", Value: "1,2"},
12
+			},
13
+		}
14
+		driverConfig = make(map[string][]string)
15
+	)
16
+
17
+	mergeLxcConfIntoOptions(hostConfig, driverConfig)
18
+	if l := len(driverConfig["lxc"]); l > 1 {
19
+		t.Fatalf("expected lxc options len of 1 got %d", l)
20
+	}
21
+
22
+	cpuset := driverConfig["lxc"][0]
23
+	if expected := "cgroups.cpuset=1,2"; cpuset != expected {
24
+		t.Fatalf("expected %s got %s", expected, cpuset)
25
+	}
26
+}