| ... | ... |
@@ -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, |
| ... | ... |
@@ -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 |
+} |