Jelajahi Sumber

Add lxc-conf flag to allow custom lxc options

Michael Crosby 12 tahun lalu
induk
melakukan
551092f9c0

+ 24 - 3
container.go

@@ -86,6 +86,7 @@ type Config struct {
 type HostConfig struct {
 type HostConfig struct {
 	Binds           []string
 	Binds           []string
 	ContainerIDFile string
 	ContainerIDFile string
+	LxcConf         []KeyValuePair
 }
 }
 
 
 type BindMap struct {
 type BindMap struct {
@@ -98,6 +99,11 @@ var (
 	ErrInvaidWorikingDirectory = errors.New("The working directory is invalid. It needs to be an absolute path.")
 	ErrInvaidWorikingDirectory = errors.New("The working directory is invalid. It needs to be an absolute path.")
 )
 )
 
 
+type KeyValuePair struct {
+	Key   string
+	Value string
+}
+
 func ParseRun(args []string, capabilities *Capabilities) (*Config, *HostConfig, *flag.FlagSet, error) {
 func ParseRun(args []string, capabilities *Capabilities) (*Config, *HostConfig, *flag.FlagSet, error) {
 	cmd := Subcmd("run", "[OPTIONS] IMAGE [COMMAND] [ARG...]", "Run a command in a new container")
 	cmd := Subcmd("run", "[OPTIONS] IMAGE [COMMAND] [ARG...]", "Run a command in a new container")
 	if len(args) > 0 && args[0] != "--help" {
 	if len(args) > 0 && args[0] != "--help" {
@@ -140,6 +146,9 @@ func ParseRun(args []string, capabilities *Capabilities) (*Config, *HostConfig,
 	flVolumesFrom := cmd.String("volumes-from", "", "Mount volumes from the specified container")
 	flVolumesFrom := cmd.String("volumes-from", "", "Mount volumes from the specified container")
 	flEntrypoint := cmd.String("entrypoint", "", "Overwrite the default entrypoint of the image")
 	flEntrypoint := cmd.String("entrypoint", "", "Overwrite the default entrypoint of the image")
 
 
+	var flLxcOpts ListOpts
+	cmd.Var(&flLxcOpts, "lxc-conf", "Add custom lxc options -lxc-conf=\"lxc.cgroup.cpuset.cpus = 0,1\"")
+
 	if err := cmd.Parse(args); err != nil {
 	if err := cmd.Parse(args); err != nil {
 		return nil, nil, cmd, err
 		return nil, nil, cmd, err
 	}
 	}
@@ -187,6 +196,12 @@ func ParseRun(args []string, capabilities *Capabilities) (*Config, *HostConfig,
 		entrypoint = []string{*flEntrypoint}
 		entrypoint = []string{*flEntrypoint}
 	}
 	}
 
 
+	var lxcConf []KeyValuePair
+	lxcConf, err := parseLxcConfOpts(flLxcOpts)
+	if err != nil {
+		return nil, nil, cmd, err
+	}
+
 	config := &Config{
 	config := &Config{
 		Hostname:        *flHostname,
 		Hostname:        *flHostname,
 		PortSpecs:       flPorts,
 		PortSpecs:       flPorts,
@@ -212,6 +227,7 @@ func ParseRun(args []string, capabilities *Capabilities) (*Config, *HostConfig,
 	hostConfig := &HostConfig{
 	hostConfig := &HostConfig{
 		Binds:           binds,
 		Binds:           binds,
 		ContainerIDFile: *flContainerIDFile,
 		ContainerIDFile: *flContainerIDFile,
+		LxcConf:         lxcConf,
 	}
 	}
 
 
 	if capabilities != nil && *flMemory > 0 && !capabilities.SwapLimit {
 	if capabilities != nil && *flMemory > 0 && !capabilities.SwapLimit {
@@ -315,7 +331,7 @@ func (container *Container) SaveHostConfig(hostConfig *HostConfig) (err error) {
 	return ioutil.WriteFile(container.hostConfigPath(), data, 0666)
 	return ioutil.WriteFile(container.hostConfigPath(), data, 0666)
 }
 }
 
 
-func (container *Container) generateLXCConfig() error {
+func (container *Container) generateLXCConfig(hostConfig *HostConfig) error {
 	fo, err := os.Create(container.lxcConfigPath())
 	fo, err := os.Create(container.lxcConfigPath())
 	if err != nil {
 	if err != nil {
 		return err
 		return err
@@ -324,6 +340,11 @@ func (container *Container) generateLXCConfig() error {
 	if err := LxcTemplateCompiled.Execute(fo, container); err != nil {
 	if err := LxcTemplateCompiled.Execute(fo, container); err != nil {
 		return err
 		return err
 	}
 	}
+	if hostConfig != nil {
+		if err := LxcHostConfigTemplateCompiled.Execute(fo, hostConfig); err != nil {
+			return err
+		}
+	}
 	return nil
 	return nil
 }
 }
 
 
@@ -520,7 +541,7 @@ func (container *Container) Start(hostConfig *HostConfig) error {
 	container.State.Lock()
 	container.State.Lock()
 	defer container.State.Unlock()
 	defer container.State.Unlock()
 
 
-	if len(hostConfig.Binds) == 0 {
+	if len(hostConfig.Binds) == 0 && len(hostConfig.LxcConf) == 0 {
 		hostConfig, _ = container.ReadHostConfig()
 		hostConfig, _ = container.ReadHostConfig()
 	}
 	}
 
 
@@ -645,7 +666,7 @@ func (container *Container) Start(hostConfig *HostConfig) error {
 		}
 		}
 	}
 	}
 
 
-	if err := container.generateLXCConfig(); err != nil {
+	if err := container.generateLXCConfig(hostConfig); err != nil {
 		return err
 		return err
 	}
 	}
 
 

+ 31 - 1
container_test.go

@@ -1070,7 +1070,7 @@ func TestLXCConfig(t *testing.T) {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 	defer runtime.Destroy(container)
 	defer runtime.Destroy(container)
-	container.generateLXCConfig()
+	container.generateLXCConfig(nil)
 	grepFile(t, container.lxcConfigPath(), "lxc.utsname = foobar")
 	grepFile(t, container.lxcConfigPath(), "lxc.utsname = foobar")
 	grepFile(t, container.lxcConfigPath(),
 	grepFile(t, container.lxcConfigPath(),
 		fmt.Sprintf("lxc.cgroup.memory.limit_in_bytes = %d", mem))
 		fmt.Sprintf("lxc.cgroup.memory.limit_in_bytes = %d", mem))
@@ -1078,6 +1078,36 @@ func TestLXCConfig(t *testing.T) {
 		fmt.Sprintf("lxc.cgroup.memory.memsw.limit_in_bytes = %d", mem*2))
 		fmt.Sprintf("lxc.cgroup.memory.memsw.limit_in_bytes = %d", mem*2))
 }
 }
 
 
+func TestCustomLxcConfig(t *testing.T) {
+	runtime := mkRuntime(t)
+	defer nuke(runtime)
+	container, err := NewBuilder(runtime).Create(&Config{
+		Image: GetTestImage(runtime).ID,
+		Cmd:   []string{"/bin/true"},
+
+		Hostname: "foobar",
+	},
+	)
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer runtime.Destroy(container)
+	hostConfig := &HostConfig{LxcConf: []KeyValuePair{
+		{
+			Key:   "lxc.utsname",
+			Value: "docker",
+		},
+		{
+			Key:   "lxc.cgroup.cpuset.cpus",
+			Value: "0,1",
+		},
+	}}
+
+	container.generateLXCConfig(hostConfig)
+	grepFile(t, container.lxcConfigPath(), "lxc.utsname = docker")
+	grepFile(t, container.lxcConfigPath(), "lxc.cgroup.cpuset.cpus = 0,1")
+}
+
 func BenchmarkRunSequencial(b *testing.B) {
 func BenchmarkRunSequencial(b *testing.B) {
 	runtime := mkRuntime(b)
 	runtime := mkRuntime(b)
 	defer nuke(runtime)
 	defer nuke(runtime)

+ 2 - 1
docs/sources/api/docker_remote_api_v1.4.rst

@@ -356,7 +356,8 @@ Start a container
            Content-Type: application/json
            Content-Type: application/json
 
 
            {
            {
-                "Binds":["/tmp:/tmp"]
+                "Binds":["/tmp:/tmp"],
+                "LxcConf":{"lxc.utsname":"docker"}
            }
            }
 
 
         **Example response**:
         **Example response**:

+ 1 - 1
docs/sources/commandline/command/run.rst

@@ -30,7 +30,7 @@
       -volumes-from="": Mount all volumes from the given container.
       -volumes-from="": Mount all volumes from the given container.
       -entrypoint="": Overwrite the default entrypoint set by the image.
       -entrypoint="": Overwrite the default entrypoint set by the image.
       -w="": Working directory inside the container
       -w="": Working directory inside the container
-
+      -lxc-conf=[]: Add custom lxc options -lxc-conf="lxc.cgroup.cpuset.cpus = 0,1"
 
 
 Examples
 Examples
 --------
 --------

+ 13 - 0
lxc_template.go

@@ -121,7 +121,16 @@ lxc.cgroup.cpu.shares = {{.Config.CpuShares}}
 {{end}}
 {{end}}
 `
 `
 
 
+const LxcHostConfigTemplate = `
+{{if .LxcConf}}
+{{range $pair := .LxcConf}}
+{{$pair.Key}} = {{$pair.Value}}
+{{end}}
+{{end}}
+`
+
 var LxcTemplateCompiled *template.Template
 var LxcTemplateCompiled *template.Template
+var LxcHostConfigTemplateCompiled *template.Template
 
 
 func getMemorySwap(config *Config) int64 {
 func getMemorySwap(config *Config) int64 {
 	// By default, MemorySwap is set to twice the size of RAM.
 	// By default, MemorySwap is set to twice the size of RAM.
@@ -141,4 +150,8 @@ func init() {
 	if err != nil {
 	if err != nil {
 		panic(err)
 		panic(err)
 	}
 	}
+	LxcHostConfigTemplateCompiled, err = template.New("lxc-hostconfig").Funcs(funcMap).Parse(LxcHostConfigTemplate)
+	if err != nil {
+		panic(err)
+	}
 }
 }

+ 21 - 0
utils.go

@@ -1,6 +1,7 @@
 package docker
 package docker
 
 
 import (
 import (
+	"fmt"
 	"strings"
 	"strings"
 )
 )
 
 
@@ -146,3 +147,23 @@ func MergeConfig(userConf, imageConf *Config) {
 		}
 		}
 	}
 	}
 }
 }
+
+func parseLxcConfOpts(opts ListOpts) ([]KeyValuePair, error) {
+	out := make([]KeyValuePair, len(opts))
+	for i, o := range opts {
+		k, v, err := parseLxcOpt(o)
+		if err != nil {
+			return nil, err
+		}
+		out[i] = KeyValuePair{Key: k, Value: v}
+	}
+	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)
+	}
+	return strings.TrimSpace(parts[0]), strings.TrimSpace(parts[1]), nil
+}

+ 17 - 0
utils_test.go

@@ -301,3 +301,20 @@ func TestMergeConfigPublicPortNotHonored(t *testing.T) {
 		t.Fail()
 		t.Fail()
 	}
 	}
 }
 }
+
+func TestParseLxcConfOpt(t *testing.T) {
+	opts := []string{"lxc.utsname=docker", "lxc.utsname = docker "}
+
+	for _, o := range opts {
+		k, v, err := parseLxcOpt(o)
+		if err != nil {
+			t.FailNow()
+		}
+		if k != "lxc.utsname" {
+			t.Fail()
+		}
+		if v != "docker" {
+			t.Fail()
+		}
+	}
+}