Explorar o código

Decode container configurations into typed structures.

Signed-off-by: David Calavera <david.calavera@gmail.com>
David Calavera %!s(int64=10) %!d(string=hai) anos
pai
achega
767df67e31

+ 10 - 9
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

+ 8 - 6
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

+ 10 - 9
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

+ 2 - 6
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 {

+ 10 - 11
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
 }

+ 2 - 1
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,

+ 3 - 5
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)

+ 2 - 1
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, ".") {

+ 4 - 3
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)

+ 1 - 1
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,

+ 16 - 18
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,
 	)

+ 5 - 5
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{},
 				"",

+ 18 - 30
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{},
 		"")

+ 2 - 7
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)
 	}
 }

+ 12 - 9
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
 		}
 	}

+ 114 - 41
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
+type ContainerConfigWrapper struct {
+	*Config
+	*hostConfigWrapper
+}
+
+func (c ContainerConfigWrapper) HostConfig() *HostConfig {
+	if c.hostConfigWrapper == nil {
+		return new(HostConfig)
 	}
 
-	env.GetJson("Labels", &config.Labels)
+	return c.hostConfigWrapper.GetHostConfig()
+}
 
-	if Entrypoint := env.GetList("Entrypoint"); Entrypoint != nil {
-		config.Entrypoint = Entrypoint
+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 config
+
+	return w.Config, w.HostConfig(), nil
 }

+ 36 - 0
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)
+		}
+	}
 }

+ 30 - 0
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" }
+}

+ 49 - 0
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": []
+    }
+}

+ 57 - 0
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": ""
+    }
+}

+ 87 - 78
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) *ContainerConfigWrapper {
+	return &ContainerConfigWrapper{
+		config,
+		&hostConfigWrapper{InnerHostConfig: hostConfig},
+	}
 }
 
-func MergeConfigs(config *Config, hostConfig *HostConfig) *ConfigAndHostConfig {
-	return &ConfigAndHostConfig{
-		*config,
-		*hostConfig,
-	}
+type hostConfigWrapper struct {
+	InnerHostConfig *HostConfig `json:"HostConfig,omitempty"`
+	Cpuset          string      `json:",omitempty"` // Deprecated. Exported for backwards compatibility.
+
+	*HostConfig // Deprecated. Exported to read attrubutes from json that are not in the inner host config structure.
 }
 
-func ContainerHostConfigFromJob(env *engine.Env) *HostConfig {
-	if env.Exists("HostConfig") {
-		hostConfig := HostConfig{}
-		env.GetJson("HostConfig", &hostConfig)
+func (w hostConfigWrapper) GetHostConfig() *HostConfig {
+	hc := w.HostConfig
 
-		// 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 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 env.Exists("CpuShares") && hostConfig.CpuShares == 0 {
-			hostConfig.CpuShares = env.GetInt64("CpuShares")
+		if hc.MemorySwap != 0 && w.InnerHostConfig.MemorySwap == 0 {
+			w.InnerHostConfig.MemorySwap = hc.MemorySwap
 		}
-		if env.Exists("Cpuset") && hostConfig.CpusetCpus == "" {
-			hostConfig.CpusetCpus = env.Get("Cpuset")
+		if hc.CpuShares != 0 && w.InnerHostConfig.CpuShares == 0 {
+			w.InnerHostConfig.CpuShares = hc.CpuShares
 		}
 
-		return &hostConfig
+		hc = w.InnerHostConfig
 	}
 
-	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"),
+	if hc != nil && w.Cpuset != "" && hc.CpusetCpus == "" {
+		hc.CpusetCpus = w.Cpuset
 	}
 
-	// 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")
-	}
+	return hc
+}
 
-	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
+func DecodeHostConfig(src io.Reader) (*HostConfig, error) {
+	decoder := json.NewDecoder(src)
+
+	var w hostConfigWrapper
+	if err := decoder.Decode(&w); err != nil {
+		return nil, err
 	}
 
-	return hostConfig
+	hc := w.GetHostConfig()
+
+	return hc, nil
 }

+ 2 - 11
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
 		}
 

+ 6 - 9
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,