Merge pull request #1560 from dotcloud/439-allow-lxc-args

Add lxc-conf flag to allow custom lxc options
This commit is contained in:
Michael Crosby 2013-08-22 09:34:27 -07:00
commit a3510c99f1
7 changed files with 109 additions and 6 deletions

View file

@ -86,6 +86,7 @@ type Config struct {
type HostConfig struct {
Binds []string
ContainerIDFile string
LxcConf []KeyValuePair
}
type BindMap struct {
@ -98,6 +99,11 @@ var (
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) {
cmd := Subcmd("run", "[OPTIONS] IMAGE [COMMAND] [ARG...]", "Run a command in a new container")
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")
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 {
return nil, nil, cmd, err
}
@ -187,6 +196,12 @@ func ParseRun(args []string, capabilities *Capabilities) (*Config, *HostConfig,
entrypoint = []string{*flEntrypoint}
}
var lxcConf []KeyValuePair
lxcConf, err := parseLxcConfOpts(flLxcOpts)
if err != nil {
return nil, nil, cmd, err
}
config := &Config{
Hostname: *flHostname,
PortSpecs: flPorts,
@ -212,6 +227,7 @@ func ParseRun(args []string, capabilities *Capabilities) (*Config, *HostConfig,
hostConfig := &HostConfig{
Binds: binds,
ContainerIDFile: *flContainerIDFile,
LxcConf: lxcConf,
}
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)
}
func (container *Container) generateLXCConfig() error {
func (container *Container) generateLXCConfig(hostConfig *HostConfig) error {
fo, err := os.Create(container.lxcConfigPath())
if err != nil {
return err
@ -324,6 +340,11 @@ func (container *Container) generateLXCConfig() error {
if err := LxcTemplateCompiled.Execute(fo, container); err != nil {
return err
}
if hostConfig != nil {
if err := LxcHostConfigTemplateCompiled.Execute(fo, hostConfig); err != nil {
return err
}
}
return nil
}
@ -520,7 +541,7 @@ func (container *Container) Start(hostConfig *HostConfig) error {
container.State.Lock()
defer container.State.Unlock()
if len(hostConfig.Binds) == 0 {
if len(hostConfig.Binds) == 0 && len(hostConfig.LxcConf) == 0 {
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
}

View file

@ -1070,7 +1070,7 @@ func TestLXCConfig(t *testing.T) {
t.Fatal(err)
}
defer runtime.Destroy(container)
container.generateLXCConfig()
container.generateLXCConfig(nil)
grepFile(t, container.lxcConfigPath(), "lxc.utsname = foobar")
grepFile(t, container.lxcConfigPath(),
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))
}
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) {
runtime := mkRuntime(b)
defer nuke(runtime)

View file

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

View file

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

View file

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

View file

@ -1,6 +1,7 @@
package docker
import (
"fmt"
"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
}

View file

@ -301,3 +301,20 @@ func TestMergeConfigPublicPortNotHonored(t *testing.T) {
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()
}
}
}