From 767df67e3149b83255db0809f6543b449a4f652e Mon Sep 17 00:00:00 2001 From: David Calavera Date: Fri, 10 Apr 2015 17:05:21 -0700 Subject: [PATCH] Decode container configurations into typed structures. Signed-off-by: David Calavera --- api/server/server.go | 19 +- builder/dispatchers.go | 14 +- builder/internals.go | 19 +- daemon/create.go | 8 +- daemon/daemon.go | 21 +- daemon/exec.go | 3 +- daemon/start.go | 8 +- daemon/utils.go | 3 +- daemon/utils_test.go | 7 +- graph/history.go | 2 +- integration/api_test.go | 34 ++-- integration/container_test.go | 10 +- integration/runtime_test.go | 48 ++--- integration/utils_test.go | 9 +- runconfig/compare.go | 21 +- runconfig/config.go | 163 ++++++++++----- runconfig/config_test.go | 38 +++- runconfig/fixtures/container_config_1_14.json | 30 +++ runconfig/fixtures/container_config_1_17.json | 49 +++++ runconfig/fixtures/container_config_1_19.json | 57 ++++++ runconfig/hostconfig.go | 189 +++++++++--------- runconfig/merge.go | 13 +- runconfig/parse.go | 15 +- 23 files changed, 503 insertions(+), 277 deletions(-) create mode 100644 runconfig/fixtures/container_config_1_14.json create mode 100644 runconfig/fixtures/container_config_1_17.json create mode 100644 runconfig/fixtures/container_config_1_19.json diff --git a/api/server/server.go b/api/server/server.go index 2cc9662496..2abcbf91fc 100644 --- a/api/server/server.go +++ b/api/server/server.go @@ -33,6 +33,7 @@ import ( "github.com/docker/docker/pkg/streamformatter" "github.com/docker/docker/pkg/version" "github.com/docker/docker/registry" + "github.com/docker/docker/runconfig" "github.com/docker/docker/utils" ) @@ -811,14 +812,14 @@ func postContainersCreate(eng *engine.Engine, version version.Version, w http.Re var ( warnings []string name = r.Form.Get("name") - env = new(engine.Env) ) - if err := env.Decode(r.Body); err != nil { + config, hostConfig, err := runconfig.DecodeContainerConfig(r.Body) + if err != nil { return err } - containerId, warnings, err := getDaemon(eng).ContainerCreate(name, env) + containerId, warnings, err := getDaemon(eng).ContainerCreate(name, config, hostConfig) if err != nil { return err } @@ -917,10 +918,6 @@ func postContainersStart(eng *engine.Engine, version version.Version, w http.Res if vars == nil { return fmt.Errorf("Missing parameter") } - var ( - name = vars["name"] - env = new(engine.Env) - ) // If contentLength is -1, we can assumed chunked encoding // or more technically that the length is unknown @@ -928,17 +925,21 @@ func postContainersStart(eng *engine.Engine, version version.Version, w http.Res // net/http otherwise seems to swallow any headers related to chunked encoding // including r.TransferEncoding // allow a nil body for backwards compatibility + var hostConfig *runconfig.HostConfig if r.Body != nil && (r.ContentLength > 0 || r.ContentLength == -1) { if err := checkForJson(r); err != nil { return err } - if err := env.Decode(r.Body); err != nil { + c, err := runconfig.DecodeHostConfig(r.Body) + if err != nil { return err } + + hostConfig = c } - if err := getDaemon(eng).ContainerStart(name, env); err != nil { + if err := getDaemon(eng).ContainerStart(vars["name"], hostConfig); err != nil { if err.Error() == "Container already started" { w.WriteHeader(http.StatusNotModified) return nil diff --git a/builder/dispatchers.go b/builder/dispatchers.go index 820ac17e3b..e807f1aee1 100644 --- a/builder/dispatchers.go +++ b/builder/dispatchers.go @@ -262,7 +262,7 @@ func run(b *Builder, args []string, attributes map[string]bool, original string) b.Config.Cmd = config.Cmd runconfig.Merge(b.Config, config) - defer func(cmd []string) { b.Config.Cmd = cmd }(cmd) + defer func(cmd *runconfig.Command) { b.Config.Cmd = cmd }(cmd) logrus.Debugf("[BUILDER] Command to be executed: %v", b.Config.Cmd) @@ -301,13 +301,15 @@ func run(b *Builder, args []string, attributes map[string]bool, original string) // Argument handling is the same as RUN. // func cmd(b *Builder, args []string, attributes map[string]bool, original string) error { - b.Config.Cmd = handleJsonArgs(args, attributes) + cmdSlice := handleJsonArgs(args, attributes) if !attributes["json"] { - b.Config.Cmd = append([]string{"/bin/sh", "-c"}, b.Config.Cmd...) + cmdSlice = append([]string{"/bin/sh", "-c"}, cmdSlice...) } - if err := b.commit("", b.Config.Cmd, fmt.Sprintf("CMD %q", b.Config.Cmd)); err != nil { + b.Config.Cmd = runconfig.NewCommand(cmdSlice...) + + if err := b.commit("", b.Config.Cmd, fmt.Sprintf("CMD %q", cmdSlice)); err != nil { return err } @@ -332,13 +334,13 @@ func entrypoint(b *Builder, args []string, attributes map[string]bool, original switch { case attributes["json"]: // ENTRYPOINT ["echo", "hi"] - b.Config.Entrypoint = parsed + b.Config.Entrypoint = runconfig.NewEntrypoint(parsed...) case len(parsed) == 0: // ENTRYPOINT [] b.Config.Entrypoint = nil default: // ENTRYPOINT echo hi - b.Config.Entrypoint = []string{"/bin/sh", "-c", parsed[0]} + b.Config.Entrypoint = runconfig.NewEntrypoint("/bin/sh", "-c", parsed[0]) } // when setting the entrypoint if a CMD was not explicitly set then diff --git a/builder/internals.go b/builder/internals.go index 728ccde8ae..be980a265d 100644 --- a/builder/internals.go +++ b/builder/internals.go @@ -61,7 +61,7 @@ func (b *Builder) readContext(context io.Reader) error { return nil } -func (b *Builder) commit(id string, autoCmd []string, comment string) error { +func (b *Builder) commit(id string, autoCmd *runconfig.Command, comment string) error { if b.disableCommit { return nil } @@ -71,8 +71,8 @@ func (b *Builder) commit(id string, autoCmd []string, comment string) error { b.Config.Image = b.image if id == "" { cmd := b.Config.Cmd - b.Config.Cmd = []string{"/bin/sh", "-c", "#(nop) " + comment} - defer func(cmd []string) { b.Config.Cmd = cmd }(cmd) + b.Config.Cmd = runconfig.NewCommand("/bin/sh", "-c", "#(nop) "+comment) + defer func(cmd *runconfig.Command) { b.Config.Cmd = cmd }(cmd) hit, err := b.probeCache() if err != nil { @@ -182,8 +182,8 @@ func (b *Builder) runContextCommand(args []string, allowRemote bool, allowDecomp } cmd := b.Config.Cmd - b.Config.Cmd = []string{"/bin/sh", "-c", fmt.Sprintf("#(nop) %s %s in %s", cmdName, srcHash, dest)} - defer func(cmd []string) { b.Config.Cmd = cmd }(cmd) + b.Config.Cmd = runconfig.NewCommand("/bin/sh", "-c", fmt.Sprintf("#(nop) %s %s in %s", cmdName, srcHash, dest)) + defer func(cmd *runconfig.Command) { b.Config.Cmd = cmd }(cmd) hit, err := b.probeCache() if err != nil { @@ -559,12 +559,13 @@ func (b *Builder) create() (*daemon.Container, error) { b.TmpContainers[c.ID] = struct{}{} fmt.Fprintf(b.OutStream, " ---> Running in %s\n", stringid.TruncateID(c.ID)) - if len(config.Cmd) > 0 { + if config.Cmd.Len() > 0 { // override the entry point that may have been picked up from the base image - c.Path = config.Cmd[0] - c.Args = config.Cmd[1:] + s := config.Cmd.Slice() + c.Path = s[0] + c.Args = s[1:] } else { - config.Cmd = []string{} + config.Cmd = runconfig.NewCommand() } return c, nil diff --git a/daemon/create.go b/daemon/create.go index da271043f2..eb8a252756 100644 --- a/daemon/create.go +++ b/daemon/create.go @@ -4,7 +4,6 @@ import ( "fmt" "strings" - "github.com/docker/docker/engine" "github.com/docker/docker/graph" "github.com/docker/docker/image" "github.com/docker/docker/pkg/parsers" @@ -12,13 +11,10 @@ import ( "github.com/docker/libcontainer/label" ) -func (daemon *Daemon) ContainerCreate(name string, env *engine.Env) (string, []string, error) { +func (daemon *Daemon) ContainerCreate(name string, config *runconfig.Config, hostConfig *runconfig.HostConfig) (string, []string, error) { var warnings []string - config := runconfig.ContainerConfigFromJob(env) - hostConfig := runconfig.ContainerHostConfigFromJob(env) - - if len(hostConfig.LxcConf) > 0 && !strings.Contains(daemon.ExecutionDriver().Name(), "lxc") { + if hostConfig.LxcConf.Len() > 0 && !strings.Contains(daemon.ExecutionDriver().Name(), "lxc") { return "", warnings, fmt.Errorf("Cannot use --lxc-conf with execdriver: %s", daemon.ExecutionDriver().Name()) } if hostConfig.Memory != 0 && hostConfig.Memory < 4194304 { diff --git a/daemon/daemon.go b/daemon/daemon.go index 76ed5a5ddc..23647ecf93 100644 --- a/daemon/daemon.go +++ b/daemon/daemon.go @@ -119,12 +119,8 @@ type Daemon struct { func (daemon *Daemon) Install(eng *engine.Engine) error { for name, method := range map[string]engine.Handler{ "container_inspect": daemon.ContainerInspect, - "container_stats": daemon.ContainerStats, - "export": daemon.ContainerExport, "info": daemon.CmdInfo, "restart": daemon.ContainerRestart, - "stop": daemon.ContainerStop, - "wait": daemon.ContainerWait, "execCreate": daemon.ContainerExecCreate, "execStart": daemon.ContainerExecStart, } { @@ -485,7 +481,7 @@ func (daemon *Daemon) mergeAndVerifyConfig(config *runconfig.Config, img *image. return nil, err } } - if len(config.Entrypoint) == 0 && len(config.Cmd) == 0 { + if config.Entrypoint.Len() == 0 && config.Cmd.Len() == 0 { return nil, fmt.Errorf("No command specified") } return warnings, nil @@ -577,17 +573,20 @@ func (daemon *Daemon) generateHostname(id string, config *runconfig.Config) { } } -func (daemon *Daemon) getEntrypointAndArgs(configEntrypoint, configCmd []string) (string, []string) { +func (daemon *Daemon) getEntrypointAndArgs(configEntrypoint *runconfig.Entrypoint, configCmd *runconfig.Command) (string, []string) { var ( entrypoint string args []string ) - if len(configEntrypoint) != 0 { - entrypoint = configEntrypoint[0] - args = append(configEntrypoint[1:], configCmd...) + + cmdSlice := configCmd.Slice() + if configEntrypoint.Len() != 0 { + eSlice := configEntrypoint.Slice() + entrypoint = eSlice[0] + args = append(eSlice[1:], cmdSlice...) } else { - entrypoint = configCmd[0] - args = configCmd[1:] + entrypoint = cmdSlice[0] + args = cmdSlice[1:] } return entrypoint, args } diff --git a/daemon/exec.go b/daemon/exec.go index 46c255a7cf..4787189a77 100644 --- a/daemon/exec.go +++ b/daemon/exec.go @@ -132,7 +132,8 @@ func (d *Daemon) ContainerExecCreate(job *engine.Job) error { return err } - entrypoint, args := d.getEntrypointAndArgs(nil, config.Cmd) + cmd := runconfig.NewCommand(config.Cmd...) + entrypoint, args := d.getEntrypointAndArgs(runconfig.NewEntrypoint(), cmd) processConfig := execdriver.ProcessConfig{ Tty: config.Tty, diff --git a/daemon/start.go b/daemon/start.go index b0b6dc75c3..dbb3dd1810 100644 --- a/daemon/start.go +++ b/daemon/start.go @@ -3,11 +3,10 @@ package daemon import ( "fmt" - "github.com/docker/docker/engine" "github.com/docker/docker/runconfig" ) -func (daemon *Daemon) ContainerStart(name string, env *engine.Env) error { +func (daemon *Daemon) ContainerStart(name string, hostConfig *runconfig.HostConfig) error { container, err := daemon.Get(name) if err != nil { return err @@ -21,15 +20,14 @@ func (daemon *Daemon) ContainerStart(name string, env *engine.Env) error { return fmt.Errorf("Container already started") } - // If no environment was set, then no hostconfig was passed. // This is kept for backward compatibility - hostconfig should be passed when // creating a container, not during start. - if len(env.Map()) > 0 { - hostConfig := runconfig.ContainerHostConfigFromJob(env) + if hostConfig != nil { if err := daemon.setHostConfig(container, hostConfig); err != nil { return err } } + if err := container.Start(); err != nil { container.LogEvent("die") return fmt.Errorf("Cannot start container %s: %s", name, err) diff --git a/daemon/utils.go b/daemon/utils.go index 6202e6d961..ec001ca071 100644 --- a/daemon/utils.go +++ b/daemon/utils.go @@ -42,7 +42,8 @@ func mergeLxcConfIntoOptions(hostConfig *runconfig.HostConfig) ([]string, error) // merge in the lxc conf options into the generic config map if lxcConf := hostConfig.LxcConf; lxcConf != nil { - for _, pair := range lxcConf { + lxSlice := lxcConf.Slice() + for _, pair := range lxSlice { // because lxc conf gets the driver name lxc.XXXX we need to trim it off // and let the lxc driver add it back later if needed if !strings.Contains(pair.Key, ".") { diff --git a/daemon/utils_test.go b/daemon/utils_test.go index aabbeaf6fa..f81843847c 100644 --- a/daemon/utils_test.go +++ b/daemon/utils_test.go @@ -7,10 +7,11 @@ import ( ) func TestMergeLxcConfig(t *testing.T) { + kv := []runconfig.KeyValuePair{ + {"lxc.cgroups.cpuset", "1,2"}, + } hostConfig := &runconfig.HostConfig{ - LxcConf: []runconfig.KeyValuePair{ - {Key: "lxc.cgroups.cpuset", Value: "1,2"}, - }, + LxcConf: runconfig.NewLxcConfig(kv), } out, err := mergeLxcConfIntoOptions(hostConfig) diff --git a/graph/history.go b/graph/history.go index 6f8581b9f3..56e759a8eb 100644 --- a/graph/history.go +++ b/graph/history.go @@ -31,7 +31,7 @@ func (s *TagStore) History(name string) ([]*types.ImageHistory, error) { history = append(history, &types.ImageHistory{ ID: img.ID, Created: img.Created.Unix(), - CreatedBy: strings.Join(img.ContainerConfig.Cmd, " "), + CreatedBy: strings.Join(img.ContainerConfig.Cmd.Slice(), " "), Tags: lookupMap[img.ID], Size: img.Size, Comment: img.Comment, diff --git a/integration/api_test.go b/integration/api_test.go index c527bcb927..3a795f94f9 100644 --- a/integration/api_test.go +++ b/integration/api_test.go @@ -91,7 +91,7 @@ func TestGetContainersTop(t *testing.T) { containerID := createTestContainer(eng, &runconfig.Config{ Image: unitTestImageID, - Cmd: []string{"/bin/sh", "-c", "cat"}, + Cmd: runconfig.NewCommand("/bin/sh", "-c", "cat"), OpenStdin: true, }, t, @@ -168,7 +168,7 @@ func TestPostCommit(t *testing.T) { containerID := createTestContainer(eng, &runconfig.Config{ Image: unitTestImageID, - Cmd: []string{"touch", "/test"}, + Cmd: runconfig.NewCommand("touch", "/test"), }, t, ) @@ -201,9 +201,8 @@ func TestPostContainersCreate(t *testing.T) { defer mkDaemonFromEngine(eng, t).Nuke() configJSON, err := json.Marshal(&runconfig.Config{ - Image: unitTestImageID, - Memory: 33554432, - Cmd: []string{"touch", "/test"}, + Image: unitTestImageID, + Cmd: runconfig.NewCommand("touch", "/test"), }) if err != nil { t.Fatal(err) @@ -242,9 +241,8 @@ func TestPostJsonVerify(t *testing.T) { defer mkDaemonFromEngine(eng, t).Nuke() configJSON, err := json.Marshal(&runconfig.Config{ - Image: unitTestImageID, - Memory: 33554432, - Cmd: []string{"touch", "/test"}, + Image: unitTestImageID, + Cmd: runconfig.NewCommand("touch", "/test"), }) if err != nil { t.Fatal(err) @@ -330,8 +328,8 @@ func TestPostCreateNull(t *testing.T) { containerAssertExists(eng, containerID, t) c, _ := daemon.Get(containerID) - if c.Config.Cpuset != "" { - t.Fatalf("Cpuset should have been empty - instead its:" + c.Config.Cpuset) + if c.HostConfig().CpusetCpus != "" { + t.Fatalf("Cpuset should have been empty - instead its:" + c.HostConfig().CpusetCpus) } } @@ -342,7 +340,7 @@ func TestPostContainersKill(t *testing.T) { containerID := createTestContainer(eng, &runconfig.Config{ Image: unitTestImageID, - Cmd: []string{"/bin/cat"}, + Cmd: runconfig.NewCommand("/bin/cat"), OpenStdin: true, }, t, @@ -379,7 +377,7 @@ func TestPostContainersRestart(t *testing.T) { containerID := createTestContainer(eng, &runconfig.Config{ Image: unitTestImageID, - Cmd: []string{"/bin/top"}, + Cmd: runconfig.NewCommand("/bin/top"), OpenStdin: true, }, t, @@ -423,7 +421,7 @@ func TestPostContainersStart(t *testing.T) { eng, &runconfig.Config{ Image: unitTestImageID, - Cmd: []string{"/bin/cat"}, + Cmd: runconfig.NewCommand("/bin/cat"), OpenStdin: true, }, t, @@ -473,7 +471,7 @@ func TestPostContainersStop(t *testing.T) { containerID := createTestContainer(eng, &runconfig.Config{ Image: unitTestImageID, - Cmd: []string{"/bin/top"}, + Cmd: runconfig.NewCommand("/bin/top"), OpenStdin: true, }, t, @@ -525,7 +523,7 @@ func TestPostContainersWait(t *testing.T) { containerID := createTestContainer(eng, &runconfig.Config{ Image: unitTestImageID, - Cmd: []string{"/bin/sleep", "1"}, + Cmd: runconfig.NewCommand("/bin/sleep", "1"), OpenStdin: true, }, t, @@ -561,7 +559,7 @@ func TestPostContainersAttach(t *testing.T) { containerID := createTestContainer(eng, &runconfig.Config{ Image: unitTestImageID, - Cmd: []string{"/bin/cat"}, + Cmd: runconfig.NewCommand("/bin/cat"), OpenStdin: true, }, t, @@ -637,7 +635,7 @@ func TestPostContainersAttachStderr(t *testing.T) { containerID := createTestContainer(eng, &runconfig.Config{ Image: unitTestImageID, - Cmd: []string{"/bin/sh", "-c", "/bin/cat >&2"}, + Cmd: runconfig.NewCommand("/bin/sh", "-c", "/bin/cat >&2"), OpenStdin: true, }, t, @@ -818,7 +816,7 @@ func TestPostContainersCopy(t *testing.T) { containerID := createTestContainer(eng, &runconfig.Config{ Image: unitTestImageID, - Cmd: []string{"touch", "/test.txt"}, + Cmd: runconfig.NewCommand("touch", "/test.txt"), }, t, ) diff --git a/integration/container_test.go b/integration/container_test.go index b6cbfd0961..01078734cf 100644 --- a/integration/container_test.go +++ b/integration/container_test.go @@ -14,7 +14,7 @@ func TestRestartStdin(t *testing.T) { defer nuke(daemon) container, _, err := daemon.Create(&runconfig.Config{ Image: GetTestImage(daemon).ID, - Cmd: []string{"cat"}, + Cmd: runconfig.NewCommand("cat"), OpenStdin: true, }, @@ -79,7 +79,7 @@ func TestStdin(t *testing.T) { defer nuke(daemon) container, _, err := daemon.Create(&runconfig.Config{ Image: GetTestImage(daemon).ID, - Cmd: []string{"cat"}, + Cmd: runconfig.NewCommand("cat"), OpenStdin: true, }, @@ -119,7 +119,7 @@ func TestTty(t *testing.T) { defer nuke(daemon) container, _, err := daemon.Create(&runconfig.Config{ Image: GetTestImage(daemon).ID, - Cmd: []string{"cat"}, + Cmd: runconfig.NewCommand("cat"), OpenStdin: true, }, @@ -160,7 +160,7 @@ func BenchmarkRunSequential(b *testing.B) { for i := 0; i < b.N; i++ { container, _, err := daemon.Create(&runconfig.Config{ Image: GetTestImage(daemon).ID, - Cmd: []string{"echo", "-n", "foo"}, + Cmd: runconfig.NewCommand("echo", "-n", "foo"), }, &runconfig.HostConfig{}, "", @@ -194,7 +194,7 @@ func BenchmarkRunParallel(b *testing.B) { go func(i int, complete chan error) { container, _, err := daemon.Create(&runconfig.Config{ Image: GetTestImage(daemon).ID, - Cmd: []string{"echo", "-n", "foo"}, + Cmd: runconfig.NewCommand("echo", "-n", "foo"), }, &runconfig.HostConfig{}, "", diff --git a/integration/runtime_test.go b/integration/runtime_test.go index f3485b2b36..2e456eabfd 100644 --- a/integration/runtime_test.go +++ b/integration/runtime_test.go @@ -255,7 +255,7 @@ func TestDaemonCreate(t *testing.T) { container, _, err := daemon.Create(&runconfig.Config{ Image: GetTestImage(daemon).ID, - Cmd: []string{"ls", "-al"}, + Cmd: runconfig.NewCommand("ls", "-al"), }, &runconfig.HostConfig{}, "", @@ -296,15 +296,16 @@ func TestDaemonCreate(t *testing.T) { } // Test that conflict error displays correct details + cmd := runconfig.NewCommand("ls", "-al") testContainer, _, _ := daemon.Create( &runconfig.Config{ Image: GetTestImage(daemon).ID, - Cmd: []string{"ls", "-al"}, + Cmd: cmd, }, &runconfig.HostConfig{}, "conflictname", ) - if _, _, err := daemon.Create(&runconfig.Config{Image: GetTestImage(daemon).ID, Cmd: []string{"ls", "-al"}}, &runconfig.HostConfig{}, testContainer.Name); err == nil || !strings.Contains(err.Error(), stringid.TruncateID(testContainer.ID)) { + if _, _, err := daemon.Create(&runconfig.Config{Image: GetTestImage(daemon).ID, Cmd: cmd}, &runconfig.HostConfig{}, testContainer.Name); err == nil || !strings.Contains(err.Error(), stringid.TruncateID(testContainer.ID)) { t.Fatalf("Name conflict error doesn't include the correct short id. Message was: %v", err) } @@ -316,7 +317,7 @@ func TestDaemonCreate(t *testing.T) { if _, _, err := daemon.Create( &runconfig.Config{ Image: GetTestImage(daemon).ID, - Cmd: []string{}, + Cmd: runconfig.NewCommand(), }, &runconfig.HostConfig{}, "", @@ -326,7 +327,7 @@ func TestDaemonCreate(t *testing.T) { config := &runconfig.Config{ Image: GetTestImage(daemon).ID, - Cmd: []string{"/bin/ls"}, + Cmd: runconfig.NewCommand("/bin/ls"), PortSpecs: []string{"80"}, } container, _, err = daemon.Create(config, &runconfig.HostConfig{}, "") @@ -339,7 +340,7 @@ func TestDaemonCreate(t *testing.T) { // test expose 80:8000 container, warnings, err := daemon.Create(&runconfig.Config{ Image: GetTestImage(daemon).ID, - Cmd: []string{"ls", "-al"}, + Cmd: runconfig.NewCommand("ls", "-al"), PortSpecs: []string{"80:8000"}, }, &runconfig.HostConfig{}, @@ -359,7 +360,7 @@ func TestDestroy(t *testing.T) { container, _, err := daemon.Create(&runconfig.Config{ Image: GetTestImage(daemon).ID, - Cmd: []string{"ls", "-al"}, + Cmd: runconfig.NewCommand("ls", "-al"), }, &runconfig.HostConfig{}, "") @@ -451,13 +452,14 @@ func startEchoServerContainer(t *testing.T, proto string) (*daemon.Daemon, *daem p = nat.Port(fmt.Sprintf("%s/%s", strPort, proto)) ep[p] = struct{}{} - env := new(engine.Env) - env.Set("Image", unitTestImageID) - env.SetList("Cmd", []string{"sh", "-c", cmd}) - env.SetList("PortSpecs", []string{fmt.Sprintf("%s/%s", strPort, proto)}) - env.SetJson("ExposedPorts", ep) + c := &runconfig.Config{ + Image: unitTestImageID, + Cmd: runconfig.NewCommand("sh", "-c", cmd), + PortSpecs: []string{fmt.Sprintf("%s/%s", strPort, proto)}, + ExposedPorts: ep, + } - id, _, err = daemon.ContainerCreate(unitTestImageID, env) + id, _, err = daemon.ContainerCreate(unitTestImageID, c, &runconfig.HostConfig{}) // FIXME: this relies on the undocumented behavior of daemon.Create // which will return a nil error AND container if the exposed ports // are invalid. That behavior should be fixed! @@ -468,16 +470,7 @@ func startEchoServerContainer(t *testing.T, proto string) (*daemon.Daemon, *daem } - portBindings := make(map[nat.Port][]nat.PortBinding) - portBindings[p] = []nat.PortBinding{ - {}, - } - - env := new(engine.Env) - if err := env.SetJson("PortsBindings", portBindings); err != nil { - t.Fatal(err) - } - if err := daemon.ContainerStart(id, env); err != nil { + if err := daemon.ContainerStart(id, &runconfig.HostConfig{}); err != nil { t.Fatal(err) } @@ -728,12 +721,7 @@ func TestContainerNameValidation(t *testing.T) { t.Fatal(err) } - env := new(engine.Env) - if err := env.Import(config); err != nil { - t.Fatal(err) - } - - containerId, _, err := daemon.ContainerCreate(test.Name, env) + containerId, _, err := daemon.ContainerCreate(test.Name, config, &runconfig.HostConfig{}) if err != nil { if !test.Valid { continue @@ -872,7 +860,7 @@ func TestDestroyWithInitLayer(t *testing.T) { container, _, err := daemon.Create(&runconfig.Config{ Image: GetTestImage(daemon).ID, - Cmd: []string{"ls", "-al"}, + Cmd: runconfig.NewCommand("ls", "-al"), }, &runconfig.HostConfig{}, "") diff --git a/integration/utils_test.go b/integration/utils_test.go index 86b27fb735..befd924eaf 100644 --- a/integration/utils_test.go +++ b/integration/utils_test.go @@ -44,11 +44,7 @@ func mkDaemon(f Fataler) *daemon.Daemon { } func createNamedTestContainer(eng *engine.Engine, config *runconfig.Config, f Fataler, name string) (shortId string) { - env := new(engine.Env) - if err := env.Import(config); err != nil { - f.Fatal(err) - } - containerId, _, err := getDaemon(eng).ContainerCreate(name, env) + containerId, _, err := getDaemon(eng).ContainerCreate(name, config, &runconfig.HostConfig{}) if err != nil { f.Fatal(err) } @@ -60,8 +56,7 @@ func createTestContainer(eng *engine.Engine, config *runconfig.Config, f Fataler } func startContainer(eng *engine.Engine, id string, t Fataler) { - env := new(engine.Env) - if err := getDaemon(eng).ContainerStart(id, env); err != nil { + if err := getDaemon(eng).ContainerStart(id, &runconfig.HostConfig{}); err != nil { t.Fatal(err) } } diff --git a/runconfig/compare.go b/runconfig/compare.go index 60a21a79c0..1d969e9be8 100644 --- a/runconfig/compare.go +++ b/runconfig/compare.go @@ -10,25 +10,25 @@ func Compare(a, b *Config) bool { if a.AttachStdout != b.AttachStdout || a.AttachStderr != b.AttachStderr || a.User != b.User || - a.Memory != b.Memory || - a.MemorySwap != b.MemorySwap || - a.CpuShares != b.CpuShares || a.OpenStdin != b.OpenStdin || a.Tty != b.Tty { return false } - if len(a.Cmd) != len(b.Cmd) || + + if a.Cmd.Len() != b.Cmd.Len() || len(a.Env) != len(b.Env) || len(a.Labels) != len(b.Labels) || len(a.PortSpecs) != len(b.PortSpecs) || len(a.ExposedPorts) != len(b.ExposedPorts) || - len(a.Entrypoint) != len(b.Entrypoint) || + a.Entrypoint.Len() != b.Entrypoint.Len() || len(a.Volumes) != len(b.Volumes) { return false } - for i := 0; i < len(a.Cmd); i++ { - if a.Cmd[i] != b.Cmd[i] { + aCmd := a.Cmd.Slice() + bCmd := b.Cmd.Slice() + for i := 0; i < len(aCmd); i++ { + if aCmd[i] != bCmd[i] { return false } } @@ -52,8 +52,11 @@ func Compare(a, b *Config) bool { return false } } - for i := 0; i < len(a.Entrypoint); i++ { - if a.Entrypoint[i] != b.Entrypoint[i] { + + aEntrypoint := a.Entrypoint.Slice() + bEntrypoint := b.Entrypoint.Slice() + for i := 0; i < len(aEntrypoint); i++ { + if aEntrypoint[i] != bEntrypoint[i] { return false } } diff --git a/runconfig/config.go b/runconfig/config.go index 30dd1eb4cf..844958be2c 100644 --- a/runconfig/config.go +++ b/runconfig/config.go @@ -1,10 +1,103 @@ package runconfig import ( - "github.com/docker/docker/engine" + "encoding/json" + "io" + "github.com/docker/docker/nat" ) +// Entrypoint encapsulates the container entrypoint. +// It might be represented as a string or an array of strings. +// We need to override the json decoder to accept both options. +// The JSON decoder will fail if the api sends an string and +// we try to decode it into an array of string. +type Entrypoint struct { + parts []string +} + +func (e *Entrypoint) MarshalJSON() ([]byte, error) { + if e == nil { + return []byte{}, nil + } + return json.Marshal(e.Slice()) +} + +// UnmarshalJSON decoded the entrypoint whether it's a string or an array of strings. +func (e *Entrypoint) UnmarshalJSON(b []byte) error { + if len(b) == 0 { + return nil + } + + p := make([]string, 0, 1) + if err := json.Unmarshal(b, &p); err != nil { + p = append(p, string(b)) + } + e.parts = p + return nil +} + +func (e *Entrypoint) Len() int { + if e == nil { + return 0 + } + return len(e.parts) +} + +func (e *Entrypoint) Slice() []string { + if e == nil { + return nil + } + return e.parts +} + +func NewEntrypoint(parts ...string) *Entrypoint { + return &Entrypoint{parts} +} + +type Command struct { + parts []string +} + +func (e *Command) MarshalJSON() ([]byte, error) { + if e == nil { + return []byte{}, nil + } + return json.Marshal(e.Slice()) +} + +// UnmarshalJSON decoded the entrypoint whether it's a string or an array of strings. +func (e *Command) UnmarshalJSON(b []byte) error { + if len(b) == 0 { + return nil + } + + p := make([]string, 0, 1) + if err := json.Unmarshal(b, &p); err != nil { + p = append(p, string(b)) + } + e.parts = p + return nil +} + +func (e *Command) Len() int { + if e == nil { + return 0 + } + return len(e.parts) +} + +func (e *Command) Slice() []string { + if e == nil { + return nil + } + return e.parts +} + +func NewCommand(parts ...string) *Command { + return &Command{parts} +} + // Note: the Config structure should hold only portable information about the container. // Here, "portable" means "independent from the host we are running on". // Non-portable information *should* appear in HostConfig. @@ -12,10 +105,6 @@ type Config struct { Hostname string Domainname string User string - Memory int64 // FIXME: we keep it for backward compatibility, it has been moved to hostConfig. - MemorySwap int64 // FIXME: it has been moved to hostConfig. - CpuShares int64 // FIXME: it has been moved to hostConfig. - Cpuset string // FIXME: it has been moved to hostConfig and renamed to CpusetCpus. AttachStdin bool AttachStdout bool AttachStderr bool @@ -25,53 +114,37 @@ type Config struct { OpenStdin bool // Open stdin StdinOnce bool // If true, close stdin after the 1 attached client disconnects. Env []string - Cmd []string + Cmd *Command Image string // Name of the image as it was passed by the operator (eg. could be symbolic) Volumes map[string]struct{} WorkingDir string - Entrypoint []string + Entrypoint *Entrypoint NetworkDisabled bool MacAddress string OnBuild []string Labels map[string]string } -func ContainerConfigFromJob(env *engine.Env) *Config { - config := &Config{ - Hostname: env.Get("Hostname"), - Domainname: env.Get("Domainname"), - User: env.Get("User"), - Memory: env.GetInt64("Memory"), - MemorySwap: env.GetInt64("MemorySwap"), - CpuShares: env.GetInt64("CpuShares"), - Cpuset: env.Get("Cpuset"), - AttachStdin: env.GetBool("AttachStdin"), - AttachStdout: env.GetBool("AttachStdout"), - AttachStderr: env.GetBool("AttachStderr"), - Tty: env.GetBool("Tty"), - OpenStdin: env.GetBool("OpenStdin"), - StdinOnce: env.GetBool("StdinOnce"), - Image: env.Get("Image"), - WorkingDir: env.Get("WorkingDir"), - NetworkDisabled: env.GetBool("NetworkDisabled"), - MacAddress: env.Get("MacAddress"), - } - env.GetJson("ExposedPorts", &config.ExposedPorts) - env.GetJson("Volumes", &config.Volumes) - if PortSpecs := env.GetList("PortSpecs"); PortSpecs != nil { - config.PortSpecs = PortSpecs - } - if Env := env.GetList("Env"); Env != nil { - config.Env = Env - } - if Cmd := env.GetList("Cmd"); Cmd != nil { - config.Cmd = Cmd - } - - env.GetJson("Labels", &config.Labels) - - if Entrypoint := env.GetList("Entrypoint"); Entrypoint != nil { - config.Entrypoint = Entrypoint - } - return config +type ContainerConfigWrapper struct { + *Config + *hostConfigWrapper +} + +func (c ContainerConfigWrapper) HostConfig() *HostConfig { + if c.hostConfigWrapper == nil { + return new(HostConfig) + } + + return c.hostConfigWrapper.GetHostConfig() +} + +func DecodeContainerConfig(src io.Reader) (*Config, *HostConfig, error) { + decoder := json.NewDecoder(src) + + var w ContainerConfigWrapper + if err := decoder.Decode(&w); err != nil { + return nil, nil, err + } + + return w.Config, w.HostConfig(), nil } diff --git a/runconfig/config_test.go b/runconfig/config_test.go index accbd9107e..e36dacbf44 100644 --- a/runconfig/config_test.go +++ b/runconfig/config_test.go @@ -1,7 +1,9 @@ package runconfig import ( + "bytes" "fmt" + "io/ioutil" "strings" "testing" @@ -260,5 +262,39 @@ func TestMerge(t *testing.T) { t.Fatalf("Expected %q or %q or %q or %q, found %s", 0, 1111, 2222, 3333, portSpecs) } } - +} + +func TestDecodeContainerConfig(t *testing.T) { + fixtures := []struct { + file string + entrypoint *Entrypoint + }{ + {"fixtures/container_config_1_14.json", NewEntrypoint()}, + {"fixtures/container_config_1_17.json", NewEntrypoint("bash")}, + {"fixtures/container_config_1_19.json", NewEntrypoint("bash")}, + } + + for _, f := range fixtures { + b, err := ioutil.ReadFile(f.file) + if err != nil { + t.Fatal(err) + } + + c, h, err := DecodeContainerConfig(bytes.NewReader(b)) + if err != nil { + t.Fatal(fmt.Errorf("Error parsing %s: %v", f, err)) + } + + if c.Image != "ubuntu" { + t.Fatalf("Expected ubuntu image, found %s\n", c.Image) + } + + if c.Entrypoint.Len() != f.entrypoint.Len() { + t.Fatalf("Expected %v, found %v\n", f.entrypoint, c.Entrypoint) + } + + if h.Memory != 1000 { + t.Fatalf("Expected memory to be 1000, found %d\n", h.Memory) + } + } } diff --git a/runconfig/fixtures/container_config_1_14.json b/runconfig/fixtures/container_config_1_14.json new file mode 100644 index 0000000000..b08334c095 --- /dev/null +++ b/runconfig/fixtures/container_config_1_14.json @@ -0,0 +1,30 @@ +{ + "Hostname":"", + "Domainname": "", + "User":"", + "Memory": 1000, + "MemorySwap":0, + "CpuShares": 512, + "Cpuset": "0,1", + "AttachStdin":false, + "AttachStdout":true, + "AttachStderr":true, + "PortSpecs":null, + "Tty":false, + "OpenStdin":false, + "StdinOnce":false, + "Env":null, + "Cmd":[ + "bash" + ], + "Image":"ubuntu", + "Volumes":{ + "/tmp": {} + }, + "WorkingDir":"", + "NetworkDisabled": false, + "ExposedPorts":{ + "22/tcp": {} + }, + "RestartPolicy": { "Name": "always" } +} diff --git a/runconfig/fixtures/container_config_1_17.json b/runconfig/fixtures/container_config_1_17.json new file mode 100644 index 0000000000..60fc6e25e2 --- /dev/null +++ b/runconfig/fixtures/container_config_1_17.json @@ -0,0 +1,49 @@ +{ + "Hostname": "", + "Domainname": "", + "User": "", + "Memory": 1000, + "MemorySwap": 0, + "CpuShares": 512, + "Cpuset": "0,1", + "AttachStdin": false, + "AttachStdout": true, + "AttachStderr": true, + "Tty": false, + "OpenStdin": false, + "StdinOnce": false, + "Env": null, + "Cmd": [ + "date" + ], + "Entrypoint": "bash", + "Image": "ubuntu", + "Volumes": { + "/tmp": {} + }, + "WorkingDir": "", + "NetworkDisabled": false, + "MacAddress": "12:34:56:78:9a:bc", + "ExposedPorts": { + "22/tcp": {} + }, + "SecurityOpt": [""], + "HostConfig": { + "Binds": ["/tmp:/tmp"], + "Links": ["redis3:redis"], + "LxcConf": {"lxc.utsname":"docker"}, + "PortBindings": { "22/tcp": [{ "HostPort": "11022" }] }, + "PublishAllPorts": false, + "Privileged": false, + "ReadonlyRootfs": false, + "Dns": ["8.8.8.8"], + "DnsSearch": [""], + "ExtraHosts": null, + "VolumesFrom": ["parent", "other:ro"], + "CapAdd": ["NET_ADMIN"], + "CapDrop": ["MKNOD"], + "RestartPolicy": { "Name": "", "MaximumRetryCount": 0 }, + "NetworkMode": "bridge", + "Devices": [] + } +} diff --git a/runconfig/fixtures/container_config_1_19.json b/runconfig/fixtures/container_config_1_19.json new file mode 100644 index 0000000000..9a3ce205b3 --- /dev/null +++ b/runconfig/fixtures/container_config_1_19.json @@ -0,0 +1,57 @@ +{ + "Hostname": "", + "Domainname": "", + "User": "", + "AttachStdin": false, + "AttachStdout": true, + "AttachStderr": true, + "Tty": false, + "OpenStdin": false, + "StdinOnce": false, + "Env": null, + "Cmd": [ + "date" + ], + "Entrypoint": "bash", + "Image": "ubuntu", + "Labels": { + "com.example.vendor": "Acme", + "com.example.license": "GPL", + "com.example.version": "1.0" + }, + "Volumes": { + "/tmp": {} + }, + "WorkingDir": "", + "NetworkDisabled": false, + "MacAddress": "12:34:56:78:9a:bc", + "ExposedPorts": { + "22/tcp": {} + }, + "HostConfig": { + "Binds": ["/tmp:/tmp"], + "Links": ["redis3:redis"], + "LxcConf": {"lxc.utsname":"docker"}, + "Memory": 1000, + "MemorySwap": 0, + "CpuShares": 512, + "CpusetCpus": "0,1", + "PortBindings": { "22/tcp": [{ "HostPort": "11022" }] }, + "PublishAllPorts": false, + "Privileged": false, + "ReadonlyRootfs": false, + "Dns": ["8.8.8.8"], + "DnsSearch": [""], + "ExtraHosts": null, + "VolumesFrom": ["parent", "other:ro"], + "CapAdd": ["NET_ADMIN"], + "CapDrop": ["MKNOD"], + "RestartPolicy": { "Name": "", "MaximumRetryCount": 0 }, + "NetworkMode": "bridge", + "Devices": [], + "Ulimits": [{}], + "LogConfig": { "Type": "json-file", "Config": {} }, + "SecurityOpt": [""], + "CgroupParent": "" + } +} diff --git a/runconfig/hostconfig.go b/runconfig/hostconfig.go index 470588a092..a25ae18355 100644 --- a/runconfig/hostconfig.go +++ b/runconfig/hostconfig.go @@ -1,9 +1,10 @@ package runconfig import ( + "encoding/json" + "io" "strings" - "github.com/docker/docker/engine" "github.com/docker/docker/nat" "github.com/docker/docker/pkg/ulimit" ) @@ -108,10 +109,59 @@ type LogConfig struct { Config map[string]string } +type LxcConfig struct { + values []KeyValuePair +} + +func (c *LxcConfig) MarshalJSON() ([]byte, error) { + if c == nil { + return []byte{}, nil + } + return json.Marshal(c.Slice()) +} + +func (c *LxcConfig) UnmarshalJSON(b []byte) error { + if len(b) == 0 { + return nil + } + + var kv []KeyValuePair + if err := json.Unmarshal(b, &kv); err != nil { + var h map[string]string + if err := json.Unmarshal(b, &h); err != nil { + return err + } + for k, v := range h { + kv = append(kv, KeyValuePair{k, v}) + } + } + c.values = kv + + return nil +} + +func (c *LxcConfig) Len() int { + if c == nil { + return 0 + } + return len(c.values) +} + +func (c *LxcConfig) Slice() []KeyValuePair { + if c == nil { + return nil + } + return c.values +} + +func NewLxcConfig(values []KeyValuePair) *LxcConfig { + return &LxcConfig{values} +} + type HostConfig struct { Binds []string ContainerIDFile string - LxcConf []KeyValuePair + LxcConf *LxcConfig Memory int64 // Memory limit (in bytes) MemorySwap int64 // Total memory usage (memory + swap); set `-1` to disable swap CpuShares int64 // CPU shares (relative weight vs. other containers) @@ -138,96 +188,55 @@ type HostConfig struct { CgroupParent string // Parent cgroup. } -// This is used by the create command when you want to set both the -// Config and the HostConfig in the same call -type ConfigAndHostConfig struct { - Config - HostConfig HostConfig -} - -func MergeConfigs(config *Config, hostConfig *HostConfig) *ConfigAndHostConfig { - return &ConfigAndHostConfig{ - *config, - *hostConfig, +func MergeConfigs(config *Config, hostConfig *HostConfig) *ContainerConfigWrapper { + return &ContainerConfigWrapper{ + config, + &hostConfigWrapper{InnerHostConfig: hostConfig}, } } -func ContainerHostConfigFromJob(env *engine.Env) *HostConfig { - if env.Exists("HostConfig") { - hostConfig := HostConfig{} - env.GetJson("HostConfig", &hostConfig) +type hostConfigWrapper struct { + InnerHostConfig *HostConfig `json:"HostConfig,omitempty"` + Cpuset string `json:",omitempty"` // Deprecated. Exported for backwards compatibility. - // FIXME: These are for backward compatibility, if people use these - // options with `HostConfig`, we should still make them workable. - if env.Exists("Memory") && hostConfig.Memory == 0 { - hostConfig.Memory = env.GetInt64("Memory") - } - if env.Exists("MemorySwap") && hostConfig.MemorySwap == 0 { - hostConfig.MemorySwap = env.GetInt64("MemorySwap") - } - if env.Exists("CpuShares") && hostConfig.CpuShares == 0 { - hostConfig.CpuShares = env.GetInt64("CpuShares") - } - if env.Exists("Cpuset") && hostConfig.CpusetCpus == "" { - hostConfig.CpusetCpus = env.Get("Cpuset") - } - - return &hostConfig - } - - hostConfig := &HostConfig{ - ContainerIDFile: env.Get("ContainerIDFile"), - Memory: env.GetInt64("Memory"), - MemorySwap: env.GetInt64("MemorySwap"), - CpuShares: env.GetInt64("CpuShares"), - CpusetCpus: env.Get("CpusetCpus"), - Privileged: env.GetBool("Privileged"), - PublishAllPorts: env.GetBool("PublishAllPorts"), - NetworkMode: NetworkMode(env.Get("NetworkMode")), - IpcMode: IpcMode(env.Get("IpcMode")), - PidMode: PidMode(env.Get("PidMode")), - ReadonlyRootfs: env.GetBool("ReadonlyRootfs"), - CgroupParent: env.Get("CgroupParent"), - } - - // FIXME: This is for backward compatibility, if people use `Cpuset` - // in json, make it workable, we will only pass hostConfig.CpusetCpus - // to execDriver. - if env.Exists("Cpuset") && hostConfig.CpusetCpus == "" { - hostConfig.CpusetCpus = env.Get("Cpuset") - } - - env.GetJson("LxcConf", &hostConfig.LxcConf) - env.GetJson("PortBindings", &hostConfig.PortBindings) - env.GetJson("Devices", &hostConfig.Devices) - env.GetJson("RestartPolicy", &hostConfig.RestartPolicy) - env.GetJson("Ulimits", &hostConfig.Ulimits) - env.GetJson("LogConfig", &hostConfig.LogConfig) - hostConfig.SecurityOpt = env.GetList("SecurityOpt") - if Binds := env.GetList("Binds"); Binds != nil { - hostConfig.Binds = Binds - } - if Links := env.GetList("Links"); Links != nil { - hostConfig.Links = Links - } - if Dns := env.GetList("Dns"); Dns != nil { - hostConfig.Dns = Dns - } - if DnsSearch := env.GetList("DnsSearch"); DnsSearch != nil { - hostConfig.DnsSearch = DnsSearch - } - if ExtraHosts := env.GetList("ExtraHosts"); ExtraHosts != nil { - hostConfig.ExtraHosts = ExtraHosts - } - if VolumesFrom := env.GetList("VolumesFrom"); VolumesFrom != nil { - hostConfig.VolumesFrom = VolumesFrom - } - if CapAdd := env.GetList("CapAdd"); CapAdd != nil { - hostConfig.CapAdd = CapAdd - } - if CapDrop := env.GetList("CapDrop"); CapDrop != nil { - hostConfig.CapDrop = CapDrop - } - - return hostConfig + *HostConfig // Deprecated. Exported to read attrubutes from json that are not in the inner host config structure. +} + +func (w hostConfigWrapper) GetHostConfig() *HostConfig { + hc := w.HostConfig + + if hc == nil && w.InnerHostConfig != nil { + hc = w.InnerHostConfig + } else if w.InnerHostConfig != nil { + if hc.Memory != 0 && w.InnerHostConfig.Memory == 0 { + w.InnerHostConfig.Memory = hc.Memory + } + if hc.MemorySwap != 0 && w.InnerHostConfig.MemorySwap == 0 { + w.InnerHostConfig.MemorySwap = hc.MemorySwap + } + if hc.CpuShares != 0 && w.InnerHostConfig.CpuShares == 0 { + w.InnerHostConfig.CpuShares = hc.CpuShares + } + + hc = w.InnerHostConfig + } + + if hc != nil && w.Cpuset != "" && hc.CpusetCpus == "" { + hc.CpusetCpus = w.Cpuset + } + + return hc +} + +func DecodeHostConfig(src io.Reader) (*HostConfig, error) { + decoder := json.NewDecoder(src) + + var w hostConfigWrapper + if err := decoder.Decode(&w); err != nil { + return nil, err + } + + hc := w.GetHostConfig() + + return hc, nil } diff --git a/runconfig/merge.go b/runconfig/merge.go index 68d3d6ee12..ce6697dbfc 100644 --- a/runconfig/merge.go +++ b/runconfig/merge.go @@ -11,15 +11,6 @@ func Merge(userConf, imageConf *Config) error { if userConf.User == "" { userConf.User = imageConf.User } - if userConf.Memory == 0 { - userConf.Memory = imageConf.Memory - } - if userConf.MemorySwap == 0 { - userConf.MemorySwap = imageConf.MemorySwap - } - if userConf.CpuShares == 0 { - userConf.CpuShares = imageConf.CpuShares - } if len(userConf.ExposedPorts) == 0 { userConf.ExposedPorts = imageConf.ExposedPorts } else if imageConf.ExposedPorts != nil { @@ -94,8 +85,8 @@ func Merge(userConf, imageConf *Config) error { userConf.Labels = imageConf.Labels } - if len(userConf.Entrypoint) == 0 { - if len(userConf.Cmd) == 0 { + if userConf.Entrypoint.Len() == 0 { + if userConf.Cmd.Len() == 0 { userConf.Cmd = imageConf.Cmd } diff --git a/runconfig/parse.go b/runconfig/parse.go index d302330c82..973fbbfc3f 100644 --- a/runconfig/parse.go +++ b/runconfig/parse.go @@ -185,21 +185,22 @@ func Parse(cmd *flag.FlagSet, args []string) (*Config, *HostConfig, *flag.FlagSe var ( parsedArgs = cmd.Args() - runCmd []string - entrypoint []string + runCmd *Command + entrypoint *Entrypoint image = cmd.Arg(0) ) if len(parsedArgs) > 1 { - runCmd = parsedArgs[1:] + runCmd = NewCommand(parsedArgs[1:]...) } if *flEntrypoint != "" { - entrypoint = []string{*flEntrypoint} + entrypoint = NewEntrypoint(*flEntrypoint) } - lxcConf, err := parseKeyValueOpts(flLxcOpts) + lc, err := parseKeyValueOpts(flLxcOpts) if err != nil { return nil, nil, cmd, err } + lxcConf := NewLxcConfig(lc) var ( domainname string @@ -288,10 +289,6 @@ func Parse(cmd *flag.FlagSet, args []string) (*Config, *HostConfig, *flag.FlagSe Tty: *flTty, NetworkDisabled: !*flNetwork, OpenStdin: *flStdin, - Memory: flMemory, // FIXME: for backward compatibility - MemorySwap: MemorySwap, // FIXME: for backward compatibility - CpuShares: *flCpuShares, // FIXME: for backward compatibility - Cpuset: *flCpusetCpus, // FIXME: for backward compatibility AttachStdin: attachStdin, AttachStdout: attachStdout, AttachStderr: attachStderr,