Browse Source

Merge pull request #4085 from shykes/separate-config-hostconfig

Move canonical run configuration objects to a sub-package
Michael Crosby 11 years ago
parent
commit
d3c084beee

+ 6 - 5
buildfile.go

@@ -9,6 +9,7 @@ import (
 	"github.com/dotcloud/docker/archive"
 	"github.com/dotcloud/docker/archive"
 	"github.com/dotcloud/docker/auth"
 	"github.com/dotcloud/docker/auth"
 	"github.com/dotcloud/docker/registry"
 	"github.com/dotcloud/docker/registry"
+	"github.com/dotcloud/docker/runconfig"
 	"github.com/dotcloud/docker/utils"
 	"github.com/dotcloud/docker/utils"
 	"io"
 	"io"
 	"io/ioutil"
 	"io/ioutil"
@@ -38,7 +39,7 @@ type buildFile struct {
 
 
 	image      string
 	image      string
 	maintainer string
 	maintainer string
-	config     *Config
+	config     *runconfig.Config
 
 
 	contextPath string
 	contextPath string
 	context     *utils.TarSum
 	context     *utils.TarSum
@@ -101,7 +102,7 @@ func (b *buildFile) CmdFrom(name string) error {
 		}
 		}
 	}
 	}
 	b.image = image.ID
 	b.image = image.ID
-	b.config = &Config{}
+	b.config = &runconfig.Config{}
 	if image.Config != nil {
 	if image.Config != nil {
 		b.config = image.Config
 		b.config = image.Config
 	}
 	}
@@ -158,14 +159,14 @@ func (b *buildFile) CmdRun(args string) error {
 	if b.image == "" {
 	if b.image == "" {
 		return fmt.Errorf("Please provide a source image with `from` prior to run")
 		return fmt.Errorf("Please provide a source image with `from` prior to run")
 	}
 	}
-	config, _, _, err := ParseRun(append([]string{b.image}, b.buildCmdFromJson(args)...), nil)
+	config, _, _, err := runconfig.Parse(append([]string{b.image}, b.buildCmdFromJson(args)...), nil)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
 
 
 	cmd := b.config.Cmd
 	cmd := b.config.Cmd
 	b.config.Cmd = nil
 	b.config.Cmd = nil
-	MergeConfig(b.config, config)
+	runconfig.Merge(b.config, config)
 
 
 	defer func(cmd []string) { b.config.Cmd = cmd }(cmd)
 	defer func(cmd []string) { b.config.Cmd = cmd }(cmd)
 
 
@@ -742,7 +743,7 @@ func NewBuildFile(srv *Server, outStream, errStream io.Writer, verbose, utilizeC
 	return &buildFile{
 	return &buildFile{
 		runtime:       srv.runtime,
 		runtime:       srv.runtime,
 		srv:           srv,
 		srv:           srv,
-		config:        &Config{},
+		config:        &runconfig.Config{},
 		outStream:     outStream,
 		outStream:     outStream,
 		errStream:     errStream,
 		errStream:     errStream,
 		tmpContainers: make(map[string]struct{}),
 		tmpContainers: make(map[string]struct{}),

+ 5 - 207
commands.go

@@ -15,10 +15,9 @@ import (
 	"github.com/dotcloud/docker/engine"
 	"github.com/dotcloud/docker/engine"
 	"github.com/dotcloud/docker/nat"
 	"github.com/dotcloud/docker/nat"
 	flag "github.com/dotcloud/docker/pkg/mflag"
 	flag "github.com/dotcloud/docker/pkg/mflag"
-	"github.com/dotcloud/docker/pkg/opts"
-	"github.com/dotcloud/docker/pkg/sysinfo"
 	"github.com/dotcloud/docker/pkg/term"
 	"github.com/dotcloud/docker/pkg/term"
 	"github.com/dotcloud/docker/registry"
 	"github.com/dotcloud/docker/registry"
+	"github.com/dotcloud/docker/runconfig"
 	"github.com/dotcloud/docker/utils"
 	"github.com/dotcloud/docker/utils"
 	"io"
 	"io"
 	"io/ioutil"
 	"io/ioutil"
@@ -1449,11 +1448,11 @@ func (cli *DockerCli) CmdCommit(args ...string) error {
 	v.Set("comment", *flComment)
 	v.Set("comment", *flComment)
 	v.Set("author", *flAuthor)
 	v.Set("author", *flAuthor)
 	var (
 	var (
-		config *Config
+		config *runconfig.Config
 		env    engine.Env
 		env    engine.Env
 	)
 	)
 	if *flConfig != "" {
 	if *flConfig != "" {
-		config = &Config{}
+		config = &runconfig.Config{}
 		if err := json.Unmarshal([]byte(*flConfig), config); err != nil {
 		if err := json.Unmarshal([]byte(*flConfig), config); err != nil {
 			return err
 			return err
 		}
 		}
@@ -1743,210 +1742,9 @@ func (cli *DockerCli) CmdTag(args ...string) error {
 	return nil
 	return nil
 }
 }
 
 
-//FIXME Only used in tests
-func ParseRun(args []string, sysInfo *sysinfo.SysInfo) (*Config, *HostConfig, *flag.FlagSet, error) {
-	cmd := flag.NewFlagSet("run", flag.ContinueOnError)
-	cmd.SetOutput(ioutil.Discard)
-	cmd.Usage = nil
-	return parseRun(cmd, args, sysInfo)
-}
-
-func parseRun(cmd *flag.FlagSet, args []string, sysInfo *sysinfo.SysInfo) (*Config, *HostConfig, *flag.FlagSet, error) {
-	var (
-		// FIXME: use utils.ListOpts for attach and volumes?
-		flAttach  = opts.NewListOpts(opts.ValidateAttach)
-		flVolumes = opts.NewListOpts(opts.ValidatePath)
-		flLinks   = opts.NewListOpts(opts.ValidateLink)
-		flEnv     = opts.NewListOpts(opts.ValidateEnv)
-
-		flPublish     opts.ListOpts
-		flExpose      opts.ListOpts
-		flDns         opts.ListOpts
-		flVolumesFrom opts.ListOpts
-		flLxcOpts     opts.ListOpts
-
-		flAutoRemove      = cmd.Bool([]string{"#rm", "-rm"}, false, "Automatically remove the container when it exits (incompatible with -d)")
-		flDetach          = cmd.Bool([]string{"d", "-detach"}, false, "Detached mode: Run container in the background, print new container id")
-		flNetwork         = cmd.Bool([]string{"n", "-networking"}, true, "Enable networking for this container")
-		flPrivileged      = cmd.Bool([]string{"#privileged", "-privileged"}, false, "Give extended privileges to this container")
-		flPublishAll      = cmd.Bool([]string{"P", "-publish-all"}, false, "Publish all exposed ports to the host interfaces")
-		flStdin           = cmd.Bool([]string{"i", "-interactive"}, false, "Keep stdin open even if not attached")
-		flTty             = cmd.Bool([]string{"t", "-tty"}, false, "Allocate a pseudo-tty")
-		flContainerIDFile = cmd.String([]string{"#cidfile", "-cidfile"}, "", "Write the container ID to the file")
-		flEntrypoint      = cmd.String([]string{"#entrypoint", "-entrypoint"}, "", "Overwrite the default entrypoint of the image")
-		flHostname        = cmd.String([]string{"h", "-hostname"}, "", "Container host name")
-		flMemoryString    = cmd.String([]string{"m", "-memory"}, "", "Memory limit (format: <number><optional unit>, where unit = b, k, m or g)")
-		flUser            = cmd.String([]string{"u", "-user"}, "", "Username or UID")
-		flWorkingDir      = cmd.String([]string{"w", "-workdir"}, "", "Working directory inside the container")
-		flCpuShares       = cmd.Int64([]string{"c", "-cpu-shares"}, 0, "CPU shares (relative weight)")
-
-		// For documentation purpose
-		_ = cmd.Bool([]string{"#sig-proxy", "-sig-proxy"}, true, "Proxify all received signal to the process (even in non-tty mode)")
-		_ = cmd.String([]string{"#name", "-name"}, "", "Assign a name to the container")
-	)
-
-	cmd.Var(&flAttach, []string{"a", "-attach"}, "Attach to stdin, stdout or stderr.")
-	cmd.Var(&flVolumes, []string{"v", "-volume"}, "Bind mount a volume (e.g. from the host: -v /host:/container, from docker: -v /container)")
-	cmd.Var(&flLinks, []string{"#link", "-link"}, "Add link to another container (name:alias)")
-	cmd.Var(&flEnv, []string{"e", "-env"}, "Set environment variables")
-
-	cmd.Var(&flPublish, []string{"p", "-publish"}, fmt.Sprintf("Publish a container's port to the host (format: %s) (use 'docker port' to see the actual mapping)", nat.PortSpecTemplateFormat))
-	cmd.Var(&flExpose, []string{"#expose", "-expose"}, "Expose a port from the container without publishing it to your host")
-	cmd.Var(&flDns, []string{"#dns", "-dns"}, "Set custom dns servers")
-	cmd.Var(&flVolumesFrom, []string{"#volumes-from", "-volumes-from"}, "Mount volumes from the specified container(s)")
-	cmd.Var(&flLxcOpts, []string{"#lxc-conf", "-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
-	}
-
-	// Check if the kernel supports memory limit cgroup.
-	if sysInfo != nil && *flMemoryString != "" && !sysInfo.MemoryLimit {
-		*flMemoryString = ""
-	}
-
-	// Validate input params
-	if *flDetach && flAttach.Len() > 0 {
-		return nil, nil, cmd, ErrConflictAttachDetach
-	}
-	if *flWorkingDir != "" && !path.IsAbs(*flWorkingDir) {
-		return nil, nil, cmd, ErrInvalidWorikingDirectory
-	}
-	if *flDetach && *flAutoRemove {
-		return nil, nil, cmd, ErrConflictDetachAutoRemove
-	}
-
-	// If neither -d or -a are set, attach to everything by default
-	if flAttach.Len() == 0 && !*flDetach {
-		if !*flDetach {
-			flAttach.Set("stdout")
-			flAttach.Set("stderr")
-			if *flStdin {
-				flAttach.Set("stdin")
-			}
-		}
-	}
-
-	var flMemory int64
-	if *flMemoryString != "" {
-		parsedMemory, err := utils.RAMInBytes(*flMemoryString)
-		if err != nil {
-			return nil, nil, cmd, err
-		}
-		flMemory = parsedMemory
-	}
-
-	var binds []string
-	// add any bind targets to the list of container volumes
-	for bind := range flVolumes.GetMap() {
-		if arr := strings.Split(bind, ":"); len(arr) > 1 {
-			if arr[0] == "/" {
-				return nil, nil, cmd, fmt.Errorf("Invalid bind mount: source can't be '/'")
-			}
-			dstDir := arr[1]
-			flVolumes.Set(dstDir)
-			binds = append(binds, bind)
-			flVolumes.Delete(bind)
-		} else if bind == "/" {
-			return nil, nil, cmd, fmt.Errorf("Invalid volume: path can't be '/'")
-		}
-	}
-
-	var (
-		parsedArgs = cmd.Args()
-		runCmd     []string
-		entrypoint []string
-		image      string
-	)
-	if len(parsedArgs) >= 1 {
-		image = cmd.Arg(0)
-	}
-	if len(parsedArgs) > 1 {
-		runCmd = parsedArgs[1:]
-	}
-	if *flEntrypoint != "" {
-		entrypoint = []string{*flEntrypoint}
-	}
-
-	lxcConf, err := parseLxcConfOpts(flLxcOpts)
-	if err != nil {
-		return nil, nil, cmd, err
-	}
-
-	var (
-		domainname string
-		hostname   = *flHostname
-		parts      = strings.SplitN(hostname, ".", 2)
-	)
-	if len(parts) > 1 {
-		hostname = parts[0]
-		domainname = parts[1]
-	}
-
-	ports, portBindings, err := nat.ParsePortSpecs(flPublish.GetAll())
-	if err != nil {
-		return nil, nil, cmd, err
-	}
-
-	// Merge in exposed ports to the map of published ports
-	for _, e := range flExpose.GetAll() {
-		if strings.Contains(e, ":") {
-			return nil, nil, cmd, fmt.Errorf("Invalid port format for --expose: %s", e)
-		}
-		p := nat.NewPort(nat.SplitProtoPort(e))
-		if _, exists := ports[p]; !exists {
-			ports[p] = struct{}{}
-		}
-	}
-
-	config := &Config{
-		Hostname:        hostname,
-		Domainname:      domainname,
-		PortSpecs:       nil, // Deprecated
-		ExposedPorts:    ports,
-		User:            *flUser,
-		Tty:             *flTty,
-		NetworkDisabled: !*flNetwork,
-		OpenStdin:       *flStdin,
-		Memory:          flMemory,
-		CpuShares:       *flCpuShares,
-		AttachStdin:     flAttach.Get("stdin"),
-		AttachStdout:    flAttach.Get("stdout"),
-		AttachStderr:    flAttach.Get("stderr"),
-		Env:             flEnv.GetAll(),
-		Cmd:             runCmd,
-		Dns:             flDns.GetAll(),
-		Image:           image,
-		Volumes:         flVolumes.GetMap(),
-		VolumesFrom:     strings.Join(flVolumesFrom.GetAll(), ","),
-		Entrypoint:      entrypoint,
-		WorkingDir:      *flWorkingDir,
-	}
-
-	hostConfig := &HostConfig{
-		Binds:           binds,
-		ContainerIDFile: *flContainerIDFile,
-		LxcConf:         lxcConf,
-		Privileged:      *flPrivileged,
-		PortBindings:    portBindings,
-		Links:           flLinks.GetAll(),
-		PublishAllPorts: *flPublishAll,
-	}
-
-	if sysInfo != nil && flMemory > 0 && !sysInfo.SwapLimit {
-		//fmt.Fprintf(stdout, "WARNING: Your kernel does not support swap limit capabilities. Limitation discarded.\n")
-		config.MemorySwap = -1
-	}
-
-	// When allocating stdin in attached mode, close stdin at client disconnect
-	if config.OpenStdin && config.AttachStdin {
-		config.StdinOnce = true
-	}
-	return config, hostConfig, cmd, nil
-}
-
 func (cli *DockerCli) CmdRun(args ...string) error {
 func (cli *DockerCli) CmdRun(args ...string) error {
-	config, hostConfig, cmd, err := parseRun(cli.Subcmd("run", "[OPTIONS] IMAGE [COMMAND] [ARG...]", "Run a command in a new container"), args, nil)
+	// FIXME: just use runconfig.Parse already
+	config, hostConfig, cmd, err := runconfig.ParseSubcommand(cli.Subcmd("run", "[OPTIONS] IMAGE [COMMAND] [ARG...]", "Run a command in a new container"), args, nil)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}

+ 4 - 3
commands_unit_test.go

@@ -1,16 +1,17 @@
 package docker
 package docker
 
 
 import (
 import (
+	"github.com/dotcloud/docker/runconfig"
 	"strings"
 	"strings"
 	"testing"
 	"testing"
 )
 )
 
 
-func parse(t *testing.T, args string) (*Config, *HostConfig, error) {
-	config, hostConfig, _, err := ParseRun(strings.Split(args+" ubuntu bash", " "), nil)
+func parse(t *testing.T, args string) (*runconfig.Config, *runconfig.HostConfig, error) {
+	config, hostConfig, _, err := runconfig.Parse(strings.Split(args+" ubuntu bash", " "), nil)
 	return config, hostConfig, err
 	return config, hostConfig, err
 }
 }
 
 
-func mustParse(t *testing.T, args string) (*Config, *HostConfig) {
+func mustParse(t *testing.T, args string) (*runconfig.Config, *runconfig.HostConfig) {
 	config, hostConfig, err := parse(t, args)
 	config, hostConfig, err := parse(t, args)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)

+ 6 - 111
container.go

@@ -11,6 +11,7 @@ import (
 	"github.com/dotcloud/docker/nat"
 	"github.com/dotcloud/docker/nat"
 	"github.com/dotcloud/docker/pkg/mount"
 	"github.com/dotcloud/docker/pkg/mount"
 	"github.com/dotcloud/docker/pkg/term"
 	"github.com/dotcloud/docker/pkg/term"
+	"github.com/dotcloud/docker/runconfig"
 	"github.com/dotcloud/docker/utils"
 	"github.com/dotcloud/docker/utils"
 	"github.com/kr/pty"
 	"github.com/kr/pty"
 	"io"
 	"io"
@@ -42,7 +43,7 @@ type Container struct {
 	Path string
 	Path string
 	Args []string
 	Args []string
 
 
-	Config *Config
+	Config *runconfig.Config
 	State  State
 	State  State
 	Image  string
 	Image  string
 
 
@@ -68,109 +69,11 @@ type Container struct {
 	// Store rw/ro in a separate structure to preserve reverse-compatibility on-disk.
 	// Store rw/ro in a separate structure to preserve reverse-compatibility on-disk.
 	// Easier than migrating older container configs :)
 	// Easier than migrating older container configs :)
 	VolumesRW  map[string]bool
 	VolumesRW  map[string]bool
-	hostConfig *HostConfig
+	hostConfig *runconfig.HostConfig
 
 
 	activeLinks map[string]*Link
 	activeLinks map[string]*Link
 }
 }
 
 
-// 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.
-type Config struct {
-	Hostname        string
-	Domainname      string
-	User            string
-	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)
-	AttachStdin     bool
-	AttachStdout    bool
-	AttachStderr    bool
-	PortSpecs       []string // Deprecated - Can be in the format of 8080/tcp
-	ExposedPorts    map[nat.Port]struct{}
-	Tty             bool // Attach standard streams to a tty, including stdin if it is not closed.
-	OpenStdin       bool // Open stdin
-	StdinOnce       bool // If true, close stdin after the 1 attached client disconnects.
-	Env             []string
-	Cmd             []string
-	Dns             []string
-	Image           string // Name of the image as it was passed by the operator (eg. could be symbolic)
-	Volumes         map[string]struct{}
-	VolumesFrom     string
-	WorkingDir      string
-	Entrypoint      []string
-	NetworkDisabled bool
-	OnBuild         []string
-}
-
-func ContainerConfigFromJob(job *engine.Job) *Config {
-	config := &Config{
-		Hostname:        job.Getenv("Hostname"),
-		Domainname:      job.Getenv("Domainname"),
-		User:            job.Getenv("User"),
-		Memory:          job.GetenvInt64("Memory"),
-		MemorySwap:      job.GetenvInt64("MemorySwap"),
-		CpuShares:       job.GetenvInt64("CpuShares"),
-		AttachStdin:     job.GetenvBool("AttachStdin"),
-		AttachStdout:    job.GetenvBool("AttachStdout"),
-		AttachStderr:    job.GetenvBool("AttachStderr"),
-		Tty:             job.GetenvBool("Tty"),
-		OpenStdin:       job.GetenvBool("OpenStdin"),
-		StdinOnce:       job.GetenvBool("StdinOnce"),
-		Image:           job.Getenv("Image"),
-		VolumesFrom:     job.Getenv("VolumesFrom"),
-		WorkingDir:      job.Getenv("WorkingDir"),
-		NetworkDisabled: job.GetenvBool("NetworkDisabled"),
-	}
-	job.GetenvJson("ExposedPorts", &config.ExposedPorts)
-	job.GetenvJson("Volumes", &config.Volumes)
-	if PortSpecs := job.GetenvList("PortSpecs"); PortSpecs != nil {
-		config.PortSpecs = PortSpecs
-	}
-	if Env := job.GetenvList("Env"); Env != nil {
-		config.Env = Env
-	}
-	if Cmd := job.GetenvList("Cmd"); Cmd != nil {
-		config.Cmd = Cmd
-	}
-	if Dns := job.GetenvList("Dns"); Dns != nil {
-		config.Dns = Dns
-	}
-	if Entrypoint := job.GetenvList("Entrypoint"); Entrypoint != nil {
-		config.Entrypoint = Entrypoint
-	}
-
-	return config
-}
-
-type HostConfig struct {
-	Binds           []string
-	ContainerIDFile string
-	LxcConf         []KeyValuePair
-	Privileged      bool
-	PortBindings    nat.PortMap
-	Links           []string
-	PublishAllPorts bool
-}
-
-func ContainerHostConfigFromJob(job *engine.Job) *HostConfig {
-	hostConfig := &HostConfig{
-		ContainerIDFile: job.Getenv("ContainerIDFile"),
-		Privileged:      job.GetenvBool("Privileged"),
-		PublishAllPorts: job.GetenvBool("PublishAllPorts"),
-	}
-	job.GetenvJson("LxcConf", &hostConfig.LxcConf)
-	job.GetenvJson("PortBindings", &hostConfig.PortBindings)
-	if Binds := job.GetenvList("Binds"); Binds != nil {
-		hostConfig.Binds = Binds
-	}
-	if Links := job.GetenvList("Links"); Links != nil {
-		hostConfig.Links = Links
-	}
-
-	return hostConfig
-}
-
 type BindMap struct {
 type BindMap struct {
 	SrcPath string
 	SrcPath string
 	DstPath string
 	DstPath string
@@ -178,18 +81,10 @@ type BindMap struct {
 }
 }
 
 
 var (
 var (
-	ErrContainerStart           = errors.New("The container failed to start. Unknown error")
-	ErrContainerStartTimeout    = errors.New("The container failed to start due to timed out.")
-	ErrInvalidWorikingDirectory = errors.New("The working directory is invalid. It needs to be an absolute path.")
-	ErrConflictAttachDetach     = errors.New("Conflicting options: -a and -d")
-	ErrConflictDetachAutoRemove = errors.New("Conflicting options: -rm and -d")
+	ErrContainerStart        = errors.New("The container failed to start. Unknown error")
+	ErrContainerStartTimeout = errors.New("The container failed to start due to timed out.")
 )
 )
 
 
-type KeyValuePair struct {
-	Key   string
-	Value string
-}
-
 // FIXME: move deprecated port stuff to nat to clean up the core.
 // FIXME: move deprecated port stuff to nat to clean up the core.
 type PortMapping map[string]string // Deprecated
 type PortMapping map[string]string // Deprecated
 
 
@@ -292,7 +187,7 @@ func (container *Container) ToDisk() (err error) {
 }
 }
 
 
 func (container *Container) readHostConfig() error {
 func (container *Container) readHostConfig() error {
-	container.hostConfig = &HostConfig{}
+	container.hostConfig = &runconfig.HostConfig{}
 	// If the hostconfig file does not exist, do not read it.
 	// If the hostconfig file does not exist, do not read it.
 	// (We still have to initialize container.hostConfig,
 	// (We still have to initialize container.hostConfig,
 	// but that's OK, since we just did that above.)
 	// but that's OK, since we just did that above.)

+ 0 - 17
container_unit_test.go

@@ -5,23 +5,6 @@ import (
 	"testing"
 	"testing"
 )
 )
 
 
-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()
-		}
-	}
-}
-
 func TestParseNetworkOptsPrivateOnly(t *testing.T) {
 func TestParseNetworkOptsPrivateOnly(t *testing.T) {
 	ports, bindings, err := nat.ParsePortSpecs([]string{"192.168.1.100::80"})
 	ports, bindings, err := nat.ParsePortSpecs([]string{"192.168.1.100::80"})
 	if err != nil {
 	if err != nil {

+ 2 - 1
graph.go

@@ -5,6 +5,7 @@ import (
 	"github.com/dotcloud/docker/archive"
 	"github.com/dotcloud/docker/archive"
 	"github.com/dotcloud/docker/dockerversion"
 	"github.com/dotcloud/docker/dockerversion"
 	"github.com/dotcloud/docker/graphdriver"
 	"github.com/dotcloud/docker/graphdriver"
+	"github.com/dotcloud/docker/runconfig"
 	"github.com/dotcloud/docker/utils"
 	"github.com/dotcloud/docker/utils"
 	"io"
 	"io"
 	"io/ioutil"
 	"io/ioutil"
@@ -126,7 +127,7 @@ func (graph *Graph) Get(name string) (*Image, error) {
 }
 }
 
 
 // Create creates a new image and registers it in the graph.
 // Create creates a new image and registers it in the graph.
-func (graph *Graph) Create(layerData archive.Archive, container *Container, comment, author string, config *Config) (*Image, error) {
+func (graph *Graph) Create(layerData archive.Archive, container *Container, comment, author string, config *runconfig.Config) (*Image, error) {
 	img := &Image{
 	img := &Image{
 		ID:            GenerateID(),
 		ID:            GenerateID(),
 		Comment:       comment,
 		Comment:       comment,

+ 12 - 11
image.go

@@ -7,6 +7,7 @@ import (
 	"fmt"
 	"fmt"
 	"github.com/dotcloud/docker/archive"
 	"github.com/dotcloud/docker/archive"
 	"github.com/dotcloud/docker/graphdriver"
 	"github.com/dotcloud/docker/graphdriver"
+	"github.com/dotcloud/docker/runconfig"
 	"github.com/dotcloud/docker/utils"
 	"github.com/dotcloud/docker/utils"
 	"io"
 	"io"
 	"io/ioutil"
 	"io/ioutil"
@@ -18,17 +19,17 @@ import (
 )
 )
 
 
 type Image struct {
 type Image struct {
-	ID              string    `json:"id"`
-	Parent          string    `json:"parent,omitempty"`
-	Comment         string    `json:"comment,omitempty"`
-	Created         time.Time `json:"created"`
-	Container       string    `json:"container,omitempty"`
-	ContainerConfig Config    `json:"container_config,omitempty"`
-	DockerVersion   string    `json:"docker_version,omitempty"`
-	Author          string    `json:"author,omitempty"`
-	Config          *Config   `json:"config,omitempty"`
-	Architecture    string    `json:"architecture,omitempty"`
-	OS              string    `json:"os,omitempty"`
+	ID              string            `json:"id"`
+	Parent          string            `json:"parent,omitempty"`
+	Comment         string            `json:"comment,omitempty"`
+	Created         time.Time         `json:"created"`
+	Container       string            `json:"container,omitempty"`
+	ContainerConfig runconfig.Config  `json:"container_config,omitempty"`
+	DockerVersion   string            `json:"docker_version,omitempty"`
+	Author          string            `json:"author,omitempty"`
+	Config          *runconfig.Config `json:"config,omitempty"`
+	Architecture    string            `json:"architecture,omitempty"`
+	OS              string            `json:"os,omitempty"`
 	graph           *Graph
 	graph           *Graph
 	Size            int64
 	Size            int64
 }
 }

+ 20 - 19
integration/api_test.go

@@ -10,6 +10,7 @@ import (
 	"github.com/dotcloud/docker/api"
 	"github.com/dotcloud/docker/api"
 	"github.com/dotcloud/docker/dockerversion"
 	"github.com/dotcloud/docker/dockerversion"
 	"github.com/dotcloud/docker/engine"
 	"github.com/dotcloud/docker/engine"
+	"github.com/dotcloud/docker/runconfig"
 	"github.com/dotcloud/docker/utils"
 	"github.com/dotcloud/docker/utils"
 	"io"
 	"io"
 	"net"
 	"net"
@@ -309,7 +310,7 @@ func TestGetContainersJSON(t *testing.T) {
 	}
 	}
 	beginLen := len(outs.Data)
 	beginLen := len(outs.Data)
 
 
-	containerID := createTestContainer(eng, &docker.Config{
+	containerID := createTestContainer(eng, &runconfig.Config{
 		Image: unitTestImageID,
 		Image: unitTestImageID,
 		Cmd:   []string{"echo", "test"},
 		Cmd:   []string{"echo", "test"},
 	}, t)
 	}, t)
@@ -346,7 +347,7 @@ func TestGetContainersExport(t *testing.T) {
 
 
 	// Create a container and remove a file
 	// Create a container and remove a file
 	containerID := createTestContainer(eng,
 	containerID := createTestContainer(eng,
-		&docker.Config{
+		&runconfig.Config{
 			Image: unitTestImageID,
 			Image: unitTestImageID,
 			Cmd:   []string{"touch", "/test"},
 			Cmd:   []string{"touch", "/test"},
 		},
 		},
@@ -394,7 +395,7 @@ func TestGetContainersChanges(t *testing.T) {
 
 
 	// Create a container and remove a file
 	// Create a container and remove a file
 	containerID := createTestContainer(eng,
 	containerID := createTestContainer(eng,
-		&docker.Config{
+		&runconfig.Config{
 			Image: unitTestImageID,
 			Image: unitTestImageID,
 			Cmd:   []string{"/bin/rm", "/etc/passwd"},
 			Cmd:   []string{"/bin/rm", "/etc/passwd"},
 		},
 		},
@@ -433,7 +434,7 @@ func TestGetContainersTop(t *testing.T) {
 	defer mkRuntimeFromEngine(eng, t).Nuke()
 	defer mkRuntimeFromEngine(eng, t).Nuke()
 
 
 	containerID := createTestContainer(eng,
 	containerID := createTestContainer(eng,
-		&docker.Config{
+		&runconfig.Config{
 			Image:     unitTestImageID,
 			Image:     unitTestImageID,
 			Cmd:       []string{"/bin/sh", "-c", "cat"},
 			Cmd:       []string{"/bin/sh", "-c", "cat"},
 			OpenStdin: true,
 			OpenStdin: true,
@@ -510,7 +511,7 @@ func TestGetContainersByName(t *testing.T) {
 
 
 	// Create a container and remove a file
 	// Create a container and remove a file
 	containerID := createTestContainer(eng,
 	containerID := createTestContainer(eng,
-		&docker.Config{
+		&runconfig.Config{
 			Image: unitTestImageID,
 			Image: unitTestImageID,
 			Cmd:   []string{"echo", "test"},
 			Cmd:   []string{"echo", "test"},
 		},
 		},
@@ -542,7 +543,7 @@ func TestPostCommit(t *testing.T) {
 
 
 	// Create a container and remove a file
 	// Create a container and remove a file
 	containerID := createTestContainer(eng,
 	containerID := createTestContainer(eng,
-		&docker.Config{
+		&runconfig.Config{
 			Image: unitTestImageID,
 			Image: unitTestImageID,
 			Cmd:   []string{"touch", "/test"},
 			Cmd:   []string{"touch", "/test"},
 		},
 		},
@@ -578,7 +579,7 @@ func TestPostContainersCreate(t *testing.T) {
 	eng := NewTestEngine(t)
 	eng := NewTestEngine(t)
 	defer mkRuntimeFromEngine(eng, t).Nuke()
 	defer mkRuntimeFromEngine(eng, t).Nuke()
 
 
-	configJSON, err := json.Marshal(&docker.Config{
+	configJSON, err := json.Marshal(&runconfig.Config{
 		Image:  unitTestImageID,
 		Image:  unitTestImageID,
 		Memory: 33554432,
 		Memory: 33554432,
 		Cmd:    []string{"touch", "/test"},
 		Cmd:    []string{"touch", "/test"},
@@ -620,7 +621,7 @@ func TestPostContainersKill(t *testing.T) {
 	defer mkRuntimeFromEngine(eng, t).Nuke()
 	defer mkRuntimeFromEngine(eng, t).Nuke()
 
 
 	containerID := createTestContainer(eng,
 	containerID := createTestContainer(eng,
-		&docker.Config{
+		&runconfig.Config{
 			Image:     unitTestImageID,
 			Image:     unitTestImageID,
 			Cmd:       []string{"/bin/cat"},
 			Cmd:       []string{"/bin/cat"},
 			OpenStdin: true,
 			OpenStdin: true,
@@ -659,7 +660,7 @@ func TestPostContainersRestart(t *testing.T) {
 	defer mkRuntimeFromEngine(eng, t).Nuke()
 	defer mkRuntimeFromEngine(eng, t).Nuke()
 
 
 	containerID := createTestContainer(eng,
 	containerID := createTestContainer(eng,
-		&docker.Config{
+		&runconfig.Config{
 			Image:     unitTestImageID,
 			Image:     unitTestImageID,
 			Cmd:       []string{"/bin/top"},
 			Cmd:       []string{"/bin/top"},
 			OpenStdin: true,
 			OpenStdin: true,
@@ -705,7 +706,7 @@ func TestPostContainersStart(t *testing.T) {
 
 
 	containerID := createTestContainer(
 	containerID := createTestContainer(
 		eng,
 		eng,
-		&docker.Config{
+		&runconfig.Config{
 			Image:     unitTestImageID,
 			Image:     unitTestImageID,
 			Cmd:       []string{"/bin/cat"},
 			Cmd:       []string{"/bin/cat"},
 			OpenStdin: true,
 			OpenStdin: true,
@@ -713,7 +714,7 @@ func TestPostContainersStart(t *testing.T) {
 		t,
 		t,
 	)
 	)
 
 
-	hostConfigJSON, err := json.Marshal(&docker.HostConfig{})
+	hostConfigJSON, err := json.Marshal(&runconfig.HostConfig{})
 
 
 	req, err := http.NewRequest("POST", "/containers/"+containerID+"/start", bytes.NewReader(hostConfigJSON))
 	req, err := http.NewRequest("POST", "/containers/"+containerID+"/start", bytes.NewReader(hostConfigJSON))
 	if err != nil {
 	if err != nil {
@@ -758,7 +759,7 @@ func TestRunErrorBindMountRootSource(t *testing.T) {
 
 
 	containerID := createTestContainer(
 	containerID := createTestContainer(
 		eng,
 		eng,
-		&docker.Config{
+		&runconfig.Config{
 			Image:     unitTestImageID,
 			Image:     unitTestImageID,
 			Cmd:       []string{"/bin/cat"},
 			Cmd:       []string{"/bin/cat"},
 			OpenStdin: true,
 			OpenStdin: true,
@@ -766,7 +767,7 @@ func TestRunErrorBindMountRootSource(t *testing.T) {
 		t,
 		t,
 	)
 	)
 
 
-	hostConfigJSON, err := json.Marshal(&docker.HostConfig{
+	hostConfigJSON, err := json.Marshal(&runconfig.HostConfig{
 		Binds: []string{"/:/tmp"},
 		Binds: []string{"/:/tmp"},
 	})
 	})
 
 
@@ -792,7 +793,7 @@ func TestPostContainersStop(t *testing.T) {
 	defer mkRuntimeFromEngine(eng, t).Nuke()
 	defer mkRuntimeFromEngine(eng, t).Nuke()
 
 
 	containerID := createTestContainer(eng,
 	containerID := createTestContainer(eng,
-		&docker.Config{
+		&runconfig.Config{
 			Image:     unitTestImageID,
 			Image:     unitTestImageID,
 			Cmd:       []string{"/bin/top"},
 			Cmd:       []string{"/bin/top"},
 			OpenStdin: true,
 			OpenStdin: true,
@@ -832,7 +833,7 @@ func TestPostContainersWait(t *testing.T) {
 	defer mkRuntimeFromEngine(eng, t).Nuke()
 	defer mkRuntimeFromEngine(eng, t).Nuke()
 
 
 	containerID := createTestContainer(eng,
 	containerID := createTestContainer(eng,
-		&docker.Config{
+		&runconfig.Config{
 			Image:     unitTestImageID,
 			Image:     unitTestImageID,
 			Cmd:       []string{"/bin/sleep", "1"},
 			Cmd:       []string{"/bin/sleep", "1"},
 			OpenStdin: true,
 			OpenStdin: true,
@@ -870,7 +871,7 @@ func TestPostContainersAttach(t *testing.T) {
 	defer mkRuntimeFromEngine(eng, t).Nuke()
 	defer mkRuntimeFromEngine(eng, t).Nuke()
 
 
 	containerID := createTestContainer(eng,
 	containerID := createTestContainer(eng,
-		&docker.Config{
+		&runconfig.Config{
 			Image:     unitTestImageID,
 			Image:     unitTestImageID,
 			Cmd:       []string{"/bin/cat"},
 			Cmd:       []string{"/bin/cat"},
 			OpenStdin: true,
 			OpenStdin: true,
@@ -948,7 +949,7 @@ func TestPostContainersAttachStderr(t *testing.T) {
 	defer mkRuntimeFromEngine(eng, t).Nuke()
 	defer mkRuntimeFromEngine(eng, t).Nuke()
 
 
 	containerID := createTestContainer(eng,
 	containerID := createTestContainer(eng,
-		&docker.Config{
+		&runconfig.Config{
 			Image:     unitTestImageID,
 			Image:     unitTestImageID,
 			Cmd:       []string{"/bin/sh", "-c", "/bin/cat >&2"},
 			Cmd:       []string{"/bin/sh", "-c", "/bin/cat >&2"},
 			OpenStdin: true,
 			OpenStdin: true,
@@ -1029,7 +1030,7 @@ func TestDeleteContainers(t *testing.T) {
 	defer mkRuntimeFromEngine(eng, t).Nuke()
 	defer mkRuntimeFromEngine(eng, t).Nuke()
 
 
 	containerID := createTestContainer(eng,
 	containerID := createTestContainer(eng,
-		&docker.Config{
+		&runconfig.Config{
 			Image: unitTestImageID,
 			Image: unitTestImageID,
 			Cmd:   []string{"touch", "/test"},
 			Cmd:   []string{"touch", "/test"},
 		},
 		},
@@ -1164,7 +1165,7 @@ func TestPostContainersCopy(t *testing.T) {
 
 
 	// Create a container and remove a file
 	// Create a container and remove a file
 	containerID := createTestContainer(eng,
 	containerID := createTestContainer(eng,
-		&docker.Config{
+		&runconfig.Config{
 			Image: unitTestImageID,
 			Image: unitTestImageID,
 			Cmd:   []string{"touch", "/test.txt"},
 			Cmd:   []string{"touch", "/test.txt"},
 		},
 		},

+ 39 - 39
integration/container_test.go

@@ -3,7 +3,7 @@ package docker
 import (
 import (
 	"bufio"
 	"bufio"
 	"fmt"
 	"fmt"
-	"github.com/dotcloud/docker"
+	"github.com/dotcloud/docker/runconfig"
 	"github.com/dotcloud/docker/utils"
 	"github.com/dotcloud/docker/utils"
 	"io"
 	"io"
 	"io/ioutil"
 	"io/ioutil"
@@ -20,7 +20,7 @@ func TestIDFormat(t *testing.T) {
 	runtime := mkRuntime(t)
 	runtime := mkRuntime(t)
 	defer nuke(runtime)
 	defer nuke(runtime)
 	container1, _, err := runtime.Create(
 	container1, _, err := runtime.Create(
-		&docker.Config{
+		&runconfig.Config{
 			Image: GetTestImage(runtime).ID,
 			Image: GetTestImage(runtime).ID,
 			Cmd:   []string{"/bin/sh", "-c", "echo hello world"},
 			Cmd:   []string{"/bin/sh", "-c", "echo hello world"},
 		},
 		},
@@ -234,7 +234,7 @@ func TestCommitAutoRun(t *testing.T) {
 		t.Errorf("Container shouldn't be running")
 		t.Errorf("Container shouldn't be running")
 	}
 	}
 
 
-	img, err := runtime.Commit(container1, "", "", "unit test commited image", "", &docker.Config{Cmd: []string{"cat", "/world"}})
+	img, err := runtime.Commit(container1, "", "", "unit test commited image", "", &runconfig.Config{Cmd: []string{"cat", "/world"}})
 	if err != nil {
 	if err != nil {
 		t.Error(err)
 		t.Error(err)
 	}
 	}
@@ -415,7 +415,7 @@ func TestOutput(t *testing.T) {
 	runtime := mkRuntime(t)
 	runtime := mkRuntime(t)
 	defer nuke(runtime)
 	defer nuke(runtime)
 	container, _, err := runtime.Create(
 	container, _, err := runtime.Create(
-		&docker.Config{
+		&runconfig.Config{
 			Image: GetTestImage(runtime).ID,
 			Image: GetTestImage(runtime).ID,
 			Cmd:   []string{"echo", "-n", "foobar"},
 			Cmd:   []string{"echo", "-n", "foobar"},
 		},
 		},
@@ -438,7 +438,7 @@ func TestContainerNetwork(t *testing.T) {
 	runtime := mkRuntime(t)
 	runtime := mkRuntime(t)
 	defer nuke(runtime)
 	defer nuke(runtime)
 	container, _, err := runtime.Create(
 	container, _, err := runtime.Create(
-		&docker.Config{
+		&runconfig.Config{
 			Image: GetTestImage(runtime).ID,
 			Image: GetTestImage(runtime).ID,
 			Cmd:   []string{"ping", "-c", "1", "127.0.0.1"},
 			Cmd:   []string{"ping", "-c", "1", "127.0.0.1"},
 		},
 		},
@@ -460,7 +460,7 @@ func TestKillDifferentUser(t *testing.T) {
 	runtime := mkRuntime(t)
 	runtime := mkRuntime(t)
 	defer nuke(runtime)
 	defer nuke(runtime)
 
 
-	container, _, err := runtime.Create(&docker.Config{
+	container, _, err := runtime.Create(&runconfig.Config{
 		Image:     GetTestImage(runtime).ID,
 		Image:     GetTestImage(runtime).ID,
 		Cmd:       []string{"cat"},
 		Cmd:       []string{"cat"},
 		OpenStdin: true,
 		OpenStdin: true,
@@ -520,7 +520,7 @@ func TestCreateVolume(t *testing.T) {
 	runtime := mkRuntimeFromEngine(eng, t)
 	runtime := mkRuntimeFromEngine(eng, t)
 	defer nuke(runtime)
 	defer nuke(runtime)
 
 
-	config, hc, _, err := docker.ParseRun([]string{"-v", "/var/lib/data", unitTestImageID, "echo", "hello", "world"}, nil)
+	config, hc, _, err := runconfig.Parse([]string{"-v", "/var/lib/data", unitTestImageID, "echo", "hello", "world"}, nil)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
@@ -552,7 +552,7 @@ func TestCreateVolume(t *testing.T) {
 func TestKill(t *testing.T) {
 func TestKill(t *testing.T) {
 	runtime := mkRuntime(t)
 	runtime := mkRuntime(t)
 	defer nuke(runtime)
 	defer nuke(runtime)
-	container, _, err := runtime.Create(&docker.Config{
+	container, _, err := runtime.Create(&runconfig.Config{
 		Image: GetTestImage(runtime).ID,
 		Image: GetTestImage(runtime).ID,
 		Cmd:   []string{"sleep", "2"},
 		Cmd:   []string{"sleep", "2"},
 	},
 	},
@@ -596,7 +596,7 @@ func TestExitCode(t *testing.T) {
 	runtime := mkRuntime(t)
 	runtime := mkRuntime(t)
 	defer nuke(runtime)
 	defer nuke(runtime)
 
 
-	trueContainer, _, err := runtime.Create(&docker.Config{
+	trueContainer, _, err := runtime.Create(&runconfig.Config{
 		Image: GetTestImage(runtime).ID,
 		Image: GetTestImage(runtime).ID,
 		Cmd:   []string{"/bin/true"},
 		Cmd:   []string{"/bin/true"},
 	}, "")
 	}, "")
@@ -611,7 +611,7 @@ func TestExitCode(t *testing.T) {
 		t.Fatalf("Unexpected exit code %d (expected 0)", code)
 		t.Fatalf("Unexpected exit code %d (expected 0)", code)
 	}
 	}
 
 
-	falseContainer, _, err := runtime.Create(&docker.Config{
+	falseContainer, _, err := runtime.Create(&runconfig.Config{
 		Image: GetTestImage(runtime).ID,
 		Image: GetTestImage(runtime).ID,
 		Cmd:   []string{"/bin/false"},
 		Cmd:   []string{"/bin/false"},
 	}, "")
 	}, "")
@@ -630,7 +630,7 @@ func TestExitCode(t *testing.T) {
 func TestRestart(t *testing.T) {
 func TestRestart(t *testing.T) {
 	runtime := mkRuntime(t)
 	runtime := mkRuntime(t)
 	defer nuke(runtime)
 	defer nuke(runtime)
-	container, _, err := runtime.Create(&docker.Config{
+	container, _, err := runtime.Create(&runconfig.Config{
 		Image: GetTestImage(runtime).ID,
 		Image: GetTestImage(runtime).ID,
 		Cmd:   []string{"echo", "-n", "foobar"},
 		Cmd:   []string{"echo", "-n", "foobar"},
 	},
 	},
@@ -661,7 +661,7 @@ func TestRestart(t *testing.T) {
 func TestRestartStdin(t *testing.T) {
 func TestRestartStdin(t *testing.T) {
 	runtime := mkRuntime(t)
 	runtime := mkRuntime(t)
 	defer nuke(runtime)
 	defer nuke(runtime)
-	container, _, err := runtime.Create(&docker.Config{
+	container, _, err := runtime.Create(&runconfig.Config{
 		Image: GetTestImage(runtime).ID,
 		Image: GetTestImage(runtime).ID,
 		Cmd:   []string{"cat"},
 		Cmd:   []string{"cat"},
 
 
@@ -739,7 +739,7 @@ func TestUser(t *testing.T) {
 	defer nuke(runtime)
 	defer nuke(runtime)
 
 
 	// Default user must be root
 	// Default user must be root
-	container, _, err := runtime.Create(&docker.Config{
+	container, _, err := runtime.Create(&runconfig.Config{
 		Image: GetTestImage(runtime).ID,
 		Image: GetTestImage(runtime).ID,
 		Cmd:   []string{"id"},
 		Cmd:   []string{"id"},
 	},
 	},
@@ -758,7 +758,7 @@ func TestUser(t *testing.T) {
 	}
 	}
 
 
 	// Set a username
 	// Set a username
-	container, _, err = runtime.Create(&docker.Config{
+	container, _, err = runtime.Create(&runconfig.Config{
 		Image: GetTestImage(runtime).ID,
 		Image: GetTestImage(runtime).ID,
 		Cmd:   []string{"id"},
 		Cmd:   []string{"id"},
 
 
@@ -779,7 +779,7 @@ func TestUser(t *testing.T) {
 	}
 	}
 
 
 	// Set a UID
 	// Set a UID
-	container, _, err = runtime.Create(&docker.Config{
+	container, _, err = runtime.Create(&runconfig.Config{
 		Image: GetTestImage(runtime).ID,
 		Image: GetTestImage(runtime).ID,
 		Cmd:   []string{"id"},
 		Cmd:   []string{"id"},
 
 
@@ -800,7 +800,7 @@ func TestUser(t *testing.T) {
 	}
 	}
 
 
 	// Set a different user by uid
 	// Set a different user by uid
-	container, _, err = runtime.Create(&docker.Config{
+	container, _, err = runtime.Create(&runconfig.Config{
 		Image: GetTestImage(runtime).ID,
 		Image: GetTestImage(runtime).ID,
 		Cmd:   []string{"id"},
 		Cmd:   []string{"id"},
 
 
@@ -823,7 +823,7 @@ func TestUser(t *testing.T) {
 	}
 	}
 
 
 	// Set a different user by username
 	// Set a different user by username
-	container, _, err = runtime.Create(&docker.Config{
+	container, _, err = runtime.Create(&runconfig.Config{
 		Image: GetTestImage(runtime).ID,
 		Image: GetTestImage(runtime).ID,
 		Cmd:   []string{"id"},
 		Cmd:   []string{"id"},
 
 
@@ -844,7 +844,7 @@ func TestUser(t *testing.T) {
 	}
 	}
 
 
 	// Test an wrong username
 	// Test an wrong username
-	container, _, err = runtime.Create(&docker.Config{
+	container, _, err = runtime.Create(&runconfig.Config{
 		Image: GetTestImage(runtime).ID,
 		Image: GetTestImage(runtime).ID,
 		Cmd:   []string{"id"},
 		Cmd:   []string{"id"},
 
 
@@ -866,7 +866,7 @@ func TestMultipleContainers(t *testing.T) {
 	runtime := mkRuntime(t)
 	runtime := mkRuntime(t)
 	defer nuke(runtime)
 	defer nuke(runtime)
 
 
-	container1, _, err := runtime.Create(&docker.Config{
+	container1, _, err := runtime.Create(&runconfig.Config{
 		Image: GetTestImage(runtime).ID,
 		Image: GetTestImage(runtime).ID,
 		Cmd:   []string{"sleep", "2"},
 		Cmd:   []string{"sleep", "2"},
 	},
 	},
@@ -877,7 +877,7 @@ func TestMultipleContainers(t *testing.T) {
 	}
 	}
 	defer runtime.Destroy(container1)
 	defer runtime.Destroy(container1)
 
 
-	container2, _, err := runtime.Create(&docker.Config{
+	container2, _, err := runtime.Create(&runconfig.Config{
 		Image: GetTestImage(runtime).ID,
 		Image: GetTestImage(runtime).ID,
 		Cmd:   []string{"sleep", "2"},
 		Cmd:   []string{"sleep", "2"},
 	},
 	},
@@ -921,7 +921,7 @@ func TestMultipleContainers(t *testing.T) {
 func TestStdin(t *testing.T) {
 func TestStdin(t *testing.T) {
 	runtime := mkRuntime(t)
 	runtime := mkRuntime(t)
 	defer nuke(runtime)
 	defer nuke(runtime)
-	container, _, err := runtime.Create(&docker.Config{
+	container, _, err := runtime.Create(&runconfig.Config{
 		Image: GetTestImage(runtime).ID,
 		Image: GetTestImage(runtime).ID,
 		Cmd:   []string{"cat"},
 		Cmd:   []string{"cat"},
 
 
@@ -966,7 +966,7 @@ func TestStdin(t *testing.T) {
 func TestTty(t *testing.T) {
 func TestTty(t *testing.T) {
 	runtime := mkRuntime(t)
 	runtime := mkRuntime(t)
 	defer nuke(runtime)
 	defer nuke(runtime)
-	container, _, err := runtime.Create(&docker.Config{
+	container, _, err := runtime.Create(&runconfig.Config{
 		Image: GetTestImage(runtime).ID,
 		Image: GetTestImage(runtime).ID,
 		Cmd:   []string{"cat"},
 		Cmd:   []string{"cat"},
 
 
@@ -1013,7 +1013,7 @@ func TestEnv(t *testing.T) {
 	os.Setenv("TRICKY", "tri\ncky\n")
 	os.Setenv("TRICKY", "tri\ncky\n")
 	runtime := mkRuntime(t)
 	runtime := mkRuntime(t)
 	defer nuke(runtime)
 	defer nuke(runtime)
-	config, _, _, err := docker.ParseRun([]string{"-e=FALSE=true", "-e=TRUE", "-e=TRICKY", GetTestImage(runtime).ID, "env"}, nil)
+	config, _, _, err := runconfig.Parse([]string{"-e=FALSE=true", "-e=TRUE", "-e=TRICKY", GetTestImage(runtime).ID, "env"}, nil)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
@@ -1067,7 +1067,7 @@ func TestEntrypoint(t *testing.T) {
 	runtime := mkRuntime(t)
 	runtime := mkRuntime(t)
 	defer nuke(runtime)
 	defer nuke(runtime)
 	container, _, err := runtime.Create(
 	container, _, err := runtime.Create(
-		&docker.Config{
+		&runconfig.Config{
 			Image:      GetTestImage(runtime).ID,
 			Image:      GetTestImage(runtime).ID,
 			Entrypoint: []string{"/bin/echo"},
 			Entrypoint: []string{"/bin/echo"},
 			Cmd:        []string{"-n", "foobar"},
 			Cmd:        []string{"-n", "foobar"},
@@ -1091,7 +1091,7 @@ func TestEntrypointNoCmd(t *testing.T) {
 	runtime := mkRuntime(t)
 	runtime := mkRuntime(t)
 	defer nuke(runtime)
 	defer nuke(runtime)
 	container, _, err := runtime.Create(
 	container, _, err := runtime.Create(
-		&docker.Config{
+		&runconfig.Config{
 			Image:      GetTestImage(runtime).ID,
 			Image:      GetTestImage(runtime).ID,
 			Entrypoint: []string{"/bin/echo", "foobar"},
 			Entrypoint: []string{"/bin/echo", "foobar"},
 		},
 		},
@@ -1114,7 +1114,7 @@ func BenchmarkRunSequencial(b *testing.B) {
 	runtime := mkRuntime(b)
 	runtime := mkRuntime(b)
 	defer nuke(runtime)
 	defer nuke(runtime)
 	for i := 0; i < b.N; i++ {
 	for i := 0; i < b.N; i++ {
-		container, _, err := runtime.Create(&docker.Config{
+		container, _, err := runtime.Create(&runconfig.Config{
 			Image: GetTestImage(runtime).ID,
 			Image: GetTestImage(runtime).ID,
 			Cmd:   []string{"echo", "-n", "foo"},
 			Cmd:   []string{"echo", "-n", "foo"},
 		},
 		},
@@ -1147,7 +1147,7 @@ func BenchmarkRunParallel(b *testing.B) {
 		complete := make(chan error)
 		complete := make(chan error)
 		tasks = append(tasks, complete)
 		tasks = append(tasks, complete)
 		go func(i int, complete chan error) {
 		go func(i int, complete chan error) {
-			container, _, err := runtime.Create(&docker.Config{
+			container, _, err := runtime.Create(&runconfig.Config{
 				Image: GetTestImage(runtime).ID,
 				Image: GetTestImage(runtime).ID,
 				Cmd:   []string{"echo", "-n", "foo"},
 				Cmd:   []string{"echo", "-n", "foo"},
 			},
 			},
@@ -1301,7 +1301,7 @@ func TestFromVolumesInReadonlyMode(t *testing.T) {
 	runtime := mkRuntime(t)
 	runtime := mkRuntime(t)
 	defer nuke(runtime)
 	defer nuke(runtime)
 	container, _, err := runtime.Create(
 	container, _, err := runtime.Create(
-		&docker.Config{
+		&runconfig.Config{
 			Image:   GetTestImage(runtime).ID,
 			Image:   GetTestImage(runtime).ID,
 			Cmd:     []string{"/bin/echo", "-n", "foobar"},
 			Cmd:     []string{"/bin/echo", "-n", "foobar"},
 			Volumes: map[string]struct{}{"/test": {}},
 			Volumes: map[string]struct{}{"/test": {}},
@@ -1321,7 +1321,7 @@ func TestFromVolumesInReadonlyMode(t *testing.T) {
 	}
 	}
 
 
 	container2, _, err := runtime.Create(
 	container2, _, err := runtime.Create(
-		&docker.Config{
+		&runconfig.Config{
 			Image:       GetTestImage(runtime).ID,
 			Image:       GetTestImage(runtime).ID,
 			Cmd:         []string{"/bin/echo", "-n", "foobar"},
 			Cmd:         []string{"/bin/echo", "-n", "foobar"},
 			VolumesFrom: container.ID + ":ro",
 			VolumesFrom: container.ID + ":ro",
@@ -1362,7 +1362,7 @@ func TestVolumesFromReadonlyMount(t *testing.T) {
 	runtime := mkRuntime(t)
 	runtime := mkRuntime(t)
 	defer nuke(runtime)
 	defer nuke(runtime)
 	container, _, err := runtime.Create(
 	container, _, err := runtime.Create(
-		&docker.Config{
+		&runconfig.Config{
 			Image:   GetTestImage(runtime).ID,
 			Image:   GetTestImage(runtime).ID,
 			Cmd:     []string{"/bin/echo", "-n", "foobar"},
 			Cmd:     []string{"/bin/echo", "-n", "foobar"},
 			Volumes: map[string]struct{}{"/test": {}},
 			Volumes: map[string]struct{}{"/test": {}},
@@ -1382,7 +1382,7 @@ func TestVolumesFromReadonlyMount(t *testing.T) {
 	}
 	}
 
 
 	container2, _, err := runtime.Create(
 	container2, _, err := runtime.Create(
-		&docker.Config{
+		&runconfig.Config{
 			Image:       GetTestImage(runtime).ID,
 			Image:       GetTestImage(runtime).ID,
 			Cmd:         []string{"/bin/echo", "-n", "foobar"},
 			Cmd:         []string{"/bin/echo", "-n", "foobar"},
 			VolumesFrom: container.ID,
 			VolumesFrom: container.ID,
@@ -1418,7 +1418,7 @@ func TestRestartWithVolumes(t *testing.T) {
 	runtime := mkRuntime(t)
 	runtime := mkRuntime(t)
 	defer nuke(runtime)
 	defer nuke(runtime)
 
 
-	container, _, err := runtime.Create(&docker.Config{
+	container, _, err := runtime.Create(&runconfig.Config{
 		Image:   GetTestImage(runtime).ID,
 		Image:   GetTestImage(runtime).ID,
 		Cmd:     []string{"echo", "-n", "foobar"},
 		Cmd:     []string{"echo", "-n", "foobar"},
 		Volumes: map[string]struct{}{"/test": {}},
 		Volumes: map[string]struct{}{"/test": {}},
@@ -1462,7 +1462,7 @@ func TestVolumesFromWithVolumes(t *testing.T) {
 	runtime := mkRuntime(t)
 	runtime := mkRuntime(t)
 	defer nuke(runtime)
 	defer nuke(runtime)
 
 
-	container, _, err := runtime.Create(&docker.Config{
+	container, _, err := runtime.Create(&runconfig.Config{
 		Image:   GetTestImage(runtime).ID,
 		Image:   GetTestImage(runtime).ID,
 		Cmd:     []string{"sh", "-c", "echo -n bar > /test/foo"},
 		Cmd:     []string{"sh", "-c", "echo -n bar > /test/foo"},
 		Volumes: map[string]struct{}{"/test": {}},
 		Volumes: map[string]struct{}{"/test": {}},
@@ -1491,7 +1491,7 @@ func TestVolumesFromWithVolumes(t *testing.T) {
 	}
 	}
 
 
 	container2, _, err := runtime.Create(
 	container2, _, err := runtime.Create(
-		&docker.Config{
+		&runconfig.Config{
 			Image:       GetTestImage(runtime).ID,
 			Image:       GetTestImage(runtime).ID,
 			Cmd:         []string{"cat", "/test/foo"},
 			Cmd:         []string{"cat", "/test/foo"},
 			VolumesFrom: container.ID,
 			VolumesFrom: container.ID,
@@ -1529,7 +1529,7 @@ func TestOnlyLoopbackExistsWhenUsingDisableNetworkOption(t *testing.T) {
 	runtime := mkRuntimeFromEngine(eng, t)
 	runtime := mkRuntimeFromEngine(eng, t)
 	defer nuke(runtime)
 	defer nuke(runtime)
 
 
-	config, hc, _, err := docker.ParseRun([]string{"-n=false", GetTestImage(runtime).ID, "ip", "addr", "show"}, nil)
+	config, hc, _, err := runconfig.Parse([]string{"-n=false", GetTestImage(runtime).ID, "ip", "addr", "show"}, nil)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
@@ -1617,7 +1617,7 @@ func TestMultipleVolumesFrom(t *testing.T) {
 	runtime := mkRuntime(t)
 	runtime := mkRuntime(t)
 	defer nuke(runtime)
 	defer nuke(runtime)
 
 
-	container, _, err := runtime.Create(&docker.Config{
+	container, _, err := runtime.Create(&runconfig.Config{
 		Image:   GetTestImage(runtime).ID,
 		Image:   GetTestImage(runtime).ID,
 		Cmd:     []string{"sh", "-c", "echo -n bar > /test/foo"},
 		Cmd:     []string{"sh", "-c", "echo -n bar > /test/foo"},
 		Volumes: map[string]struct{}{"/test": {}},
 		Volumes: map[string]struct{}{"/test": {}},
@@ -1646,7 +1646,7 @@ func TestMultipleVolumesFrom(t *testing.T) {
 	}
 	}
 
 
 	container2, _, err := runtime.Create(
 	container2, _, err := runtime.Create(
-		&docker.Config{
+		&runconfig.Config{
 			Image:   GetTestImage(runtime).ID,
 			Image:   GetTestImage(runtime).ID,
 			Cmd:     []string{"sh", "-c", "echo -n bar > /other/foo"},
 			Cmd:     []string{"sh", "-c", "echo -n bar > /other/foo"},
 			Volumes: map[string]struct{}{"/other": {}},
 			Volumes: map[string]struct{}{"/other": {}},
@@ -1668,7 +1668,7 @@ func TestMultipleVolumesFrom(t *testing.T) {
 	}
 	}
 
 
 	container3, _, err := runtime.Create(
 	container3, _, err := runtime.Create(
-		&docker.Config{
+		&runconfig.Config{
 			Image:       GetTestImage(runtime).ID,
 			Image:       GetTestImage(runtime).ID,
 			Cmd:         []string{"/bin/echo", "-n", "foobar"},
 			Cmd:         []string{"/bin/echo", "-n", "foobar"},
 			VolumesFrom: strings.Join([]string{container.ID, container2.ID}, ","),
 			VolumesFrom: strings.Join([]string{container.ID, container2.ID}, ","),
@@ -1696,7 +1696,7 @@ func TestRestartGhost(t *testing.T) {
 	defer nuke(runtime)
 	defer nuke(runtime)
 
 
 	container, _, err := runtime.Create(
 	container, _, err := runtime.Create(
-		&docker.Config{
+		&runconfig.Config{
 			Image:   GetTestImage(runtime).ID,
 			Image:   GetTestImage(runtime).ID,
 			Cmd:     []string{"sh", "-c", "echo -n bar > /test/foo"},
 			Cmd:     []string{"sh", "-c", "echo -n bar > /test/foo"},
 			Volumes: map[string]struct{}{"/test": {}},
 			Volumes: map[string]struct{}{"/test": {}},

+ 17 - 16
integration/runtime_test.go

@@ -6,6 +6,7 @@ import (
 	"github.com/dotcloud/docker"
 	"github.com/dotcloud/docker"
 	"github.com/dotcloud/docker/engine"
 	"github.com/dotcloud/docker/engine"
 	"github.com/dotcloud/docker/nat"
 	"github.com/dotcloud/docker/nat"
+	"github.com/dotcloud/docker/runconfig"
 	"github.com/dotcloud/docker/sysinit"
 	"github.com/dotcloud/docker/sysinit"
 	"github.com/dotcloud/docker/utils"
 	"github.com/dotcloud/docker/utils"
 	"io"
 	"io"
@@ -200,7 +201,7 @@ func TestRuntimeCreate(t *testing.T) {
 		t.Errorf("Expected 0 containers, %v found", len(runtime.List()))
 		t.Errorf("Expected 0 containers, %v found", len(runtime.List()))
 	}
 	}
 
 
-	container, _, err := runtime.Create(&docker.Config{
+	container, _, err := runtime.Create(&runconfig.Config{
 		Image: GetTestImage(runtime).ID,
 		Image: GetTestImage(runtime).ID,
 		Cmd:   []string{"ls", "-al"},
 		Cmd:   []string{"ls", "-al"},
 	},
 	},
@@ -243,23 +244,23 @@ func TestRuntimeCreate(t *testing.T) {
 
 
 	// Test that conflict error displays correct details
 	// Test that conflict error displays correct details
 	testContainer, _, _ := runtime.Create(
 	testContainer, _, _ := runtime.Create(
-		&docker.Config{
+		&runconfig.Config{
 			Image: GetTestImage(runtime).ID,
 			Image: GetTestImage(runtime).ID,
 			Cmd:   []string{"ls", "-al"},
 			Cmd:   []string{"ls", "-al"},
 		},
 		},
 		"conflictname",
 		"conflictname",
 	)
 	)
-	if _, _, err := runtime.Create(&docker.Config{Image: GetTestImage(runtime).ID, Cmd: []string{"ls", "-al"}}, testContainer.Name); err == nil || !strings.Contains(err.Error(), utils.TruncateID(testContainer.ID)) {
+	if _, _, err := runtime.Create(&runconfig.Config{Image: GetTestImage(runtime).ID, Cmd: []string{"ls", "-al"}}, testContainer.Name); err == nil || !strings.Contains(err.Error(), utils.TruncateID(testContainer.ID)) {
 		t.Fatalf("Name conflict error doesn't include the correct short id. Message was: %s", err.Error())
 		t.Fatalf("Name conflict error doesn't include the correct short id. Message was: %s", err.Error())
 	}
 	}
 
 
 	// Make sure create with bad parameters returns an error
 	// Make sure create with bad parameters returns an error
-	if _, _, err = runtime.Create(&docker.Config{Image: GetTestImage(runtime).ID}, ""); err == nil {
+	if _, _, err = runtime.Create(&runconfig.Config{Image: GetTestImage(runtime).ID}, ""); err == nil {
 		t.Fatal("Builder.Create should throw an error when Cmd is missing")
 		t.Fatal("Builder.Create should throw an error when Cmd is missing")
 	}
 	}
 
 
 	if _, _, err := runtime.Create(
 	if _, _, err := runtime.Create(
-		&docker.Config{
+		&runconfig.Config{
 			Image: GetTestImage(runtime).ID,
 			Image: GetTestImage(runtime).ID,
 			Cmd:   []string{},
 			Cmd:   []string{},
 		},
 		},
@@ -268,7 +269,7 @@ func TestRuntimeCreate(t *testing.T) {
 		t.Fatal("Builder.Create should throw an error when Cmd is empty")
 		t.Fatal("Builder.Create should throw an error when Cmd is empty")
 	}
 	}
 
 
-	config := &docker.Config{
+	config := &runconfig.Config{
 		Image:     GetTestImage(runtime).ID,
 		Image:     GetTestImage(runtime).ID,
 		Cmd:       []string{"/bin/ls"},
 		Cmd:       []string{"/bin/ls"},
 		PortSpecs: []string{"80"},
 		PortSpecs: []string{"80"},
@@ -281,7 +282,7 @@ func TestRuntimeCreate(t *testing.T) {
 	}
 	}
 
 
 	// test expose 80:8000
 	// test expose 80:8000
-	container, warnings, err := runtime.Create(&docker.Config{
+	container, warnings, err := runtime.Create(&runconfig.Config{
 		Image:     GetTestImage(runtime).ID,
 		Image:     GetTestImage(runtime).ID,
 		Cmd:       []string{"ls", "-al"},
 		Cmd:       []string{"ls", "-al"},
 		PortSpecs: []string{"80:8000"},
 		PortSpecs: []string{"80:8000"},
@@ -300,7 +301,7 @@ func TestDestroy(t *testing.T) {
 	runtime := mkRuntime(t)
 	runtime := mkRuntime(t)
 	defer nuke(runtime)
 	defer nuke(runtime)
 
 
-	container, _, err := runtime.Create(&docker.Config{
+	container, _, err := runtime.Create(&runconfig.Config{
 		Image: GetTestImage(runtime).ID,
 		Image: GetTestImage(runtime).ID,
 		Cmd:   []string{"ls", "-al"},
 		Cmd:   []string{"ls", "-al"},
 	}, "")
 	}, "")
@@ -712,7 +713,7 @@ func TestDefaultContainerName(t *testing.T) {
 	runtime := mkRuntimeFromEngine(eng, t)
 	runtime := mkRuntimeFromEngine(eng, t)
 	defer nuke(runtime)
 	defer nuke(runtime)
 
 
-	config, _, _, err := docker.ParseRun([]string{unitTestImageID, "echo test"}, nil)
+	config, _, _, err := runconfig.Parse([]string{unitTestImageID, "echo test"}, nil)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
@@ -736,7 +737,7 @@ func TestRandomContainerName(t *testing.T) {
 	runtime := mkRuntimeFromEngine(eng, t)
 	runtime := mkRuntimeFromEngine(eng, t)
 	defer nuke(runtime)
 	defer nuke(runtime)
 
 
-	config, _, _, err := docker.ParseRun([]string{GetTestImage(runtime).ID, "echo test"}, nil)
+	config, _, _, err := runconfig.Parse([]string{GetTestImage(runtime).ID, "echo test"}, nil)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
@@ -767,7 +768,7 @@ func TestContainerNameValidation(t *testing.T) {
 		{"abc-123_AAA.1", true},
 		{"abc-123_AAA.1", true},
 		{"\000asdf", false},
 		{"\000asdf", false},
 	} {
 	} {
-		config, _, _, err := docker.ParseRun([]string{unitTestImageID, "echo test"}, nil)
+		config, _, _, err := runconfig.Parse([]string{unitTestImageID, "echo test"}, nil)
 		if err != nil {
 		if err != nil {
 			if !test.Valid {
 			if !test.Valid {
 				continue
 				continue
@@ -808,7 +809,7 @@ func TestLinkChildContainer(t *testing.T) {
 	runtime := mkRuntimeFromEngine(eng, t)
 	runtime := mkRuntimeFromEngine(eng, t)
 	defer nuke(runtime)
 	defer nuke(runtime)
 
 
-	config, _, _, err := docker.ParseRun([]string{unitTestImageID, "echo test"}, nil)
+	config, _, _, err := runconfig.Parse([]string{unitTestImageID, "echo test"}, nil)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
@@ -824,7 +825,7 @@ func TestLinkChildContainer(t *testing.T) {
 		t.Fatalf("Expect webapp id to match container id: %s != %s", webapp.ID, container.ID)
 		t.Fatalf("Expect webapp id to match container id: %s != %s", webapp.ID, container.ID)
 	}
 	}
 
 
-	config, _, _, err = docker.ParseRun([]string{GetTestImage(runtime).ID, "echo test"}, nil)
+	config, _, _, err = runconfig.Parse([]string{GetTestImage(runtime).ID, "echo test"}, nil)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
@@ -850,7 +851,7 @@ func TestGetAllChildren(t *testing.T) {
 	runtime := mkRuntimeFromEngine(eng, t)
 	runtime := mkRuntimeFromEngine(eng, t)
 	defer nuke(runtime)
 	defer nuke(runtime)
 
 
-	config, _, _, err := docker.ParseRun([]string{unitTestImageID, "echo test"}, nil)
+	config, _, _, err := runconfig.Parse([]string{unitTestImageID, "echo test"}, nil)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
@@ -866,7 +867,7 @@ func TestGetAllChildren(t *testing.T) {
 		t.Fatalf("Expect webapp id to match container id: %s != %s", webapp.ID, container.ID)
 		t.Fatalf("Expect webapp id to match container id: %s != %s", webapp.ID, container.ID)
 	}
 	}
 
 
-	config, _, _, err = docker.ParseRun([]string{unitTestImageID, "echo test"}, nil)
+	config, _, _, err = runconfig.Parse([]string{unitTestImageID, "echo test"}, nil)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
@@ -903,7 +904,7 @@ func TestDestroyWithInitLayer(t *testing.T) {
 	runtime := mkRuntime(t)
 	runtime := mkRuntime(t)
 	defer nuke(runtime)
 	defer nuke(runtime)
 
 
-	container, _, err := runtime.Create(&docker.Config{
+	container, _, err := runtime.Create(&runconfig.Config{
 		Image: GetTestImage(runtime).ID,
 		Image: GetTestImage(runtime).ID,
 		Cmd:   []string{"ls", "-al"},
 		Cmd:   []string{"ls", "-al"},
 	}, "")
 	}, "")

+ 11 - 10
integration/server_test.go

@@ -3,6 +3,7 @@ package docker
 import (
 import (
 	"github.com/dotcloud/docker"
 	"github.com/dotcloud/docker"
 	"github.com/dotcloud/docker/engine"
 	"github.com/dotcloud/docker/engine"
+	"github.com/dotcloud/docker/runconfig"
 	"strings"
 	"strings"
 	"testing"
 	"testing"
 	"time"
 	"time"
@@ -71,7 +72,7 @@ func TestCreateRm(t *testing.T) {
 	eng := NewTestEngine(t)
 	eng := NewTestEngine(t)
 	defer mkRuntimeFromEngine(eng, t).Nuke()
 	defer mkRuntimeFromEngine(eng, t).Nuke()
 
 
-	config, _, _, err := docker.ParseRun([]string{unitTestImageID, "echo test"}, nil)
+	config, _, _, err := runconfig.Parse([]string{unitTestImageID, "echo test"}, nil)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
@@ -118,7 +119,7 @@ func TestCreateNumberHostname(t *testing.T) {
 	eng := NewTestEngine(t)
 	eng := NewTestEngine(t)
 	defer mkRuntimeFromEngine(eng, t).Nuke()
 	defer mkRuntimeFromEngine(eng, t).Nuke()
 
 
-	config, _, _, err := docker.ParseRun([]string{"-h", "web.0", unitTestImageID, "echo test"}, nil)
+	config, _, _, err := runconfig.Parse([]string{"-h", "web.0", unitTestImageID, "echo test"}, nil)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
@@ -130,7 +131,7 @@ func TestCreateNumberUsername(t *testing.T) {
 	eng := NewTestEngine(t)
 	eng := NewTestEngine(t)
 	defer mkRuntimeFromEngine(eng, t).Nuke()
 	defer mkRuntimeFromEngine(eng, t).Nuke()
 
 
-	config, _, _, err := docker.ParseRun([]string{"-u", "1002", unitTestImageID, "echo test"}, nil)
+	config, _, _, err := runconfig.Parse([]string{"-u", "1002", unitTestImageID, "echo test"}, nil)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
@@ -142,7 +143,7 @@ func TestCreateRmVolumes(t *testing.T) {
 	eng := NewTestEngine(t)
 	eng := NewTestEngine(t)
 	defer mkRuntimeFromEngine(eng, t).Nuke()
 	defer mkRuntimeFromEngine(eng, t).Nuke()
 
 
-	config, hostConfig, _, err := docker.ParseRun([]string{"-v", "/srv", unitTestImageID, "echo", "test"}, nil)
+	config, hostConfig, _, err := runconfig.Parse([]string{"-v", "/srv", unitTestImageID, "echo", "test"}, nil)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
@@ -202,7 +203,7 @@ func TestCommit(t *testing.T) {
 	eng := NewTestEngine(t)
 	eng := NewTestEngine(t)
 	defer mkRuntimeFromEngine(eng, t).Nuke()
 	defer mkRuntimeFromEngine(eng, t).Nuke()
 
 
-	config, _, _, err := docker.ParseRun([]string{unitTestImageID, "/bin/cat"}, nil)
+	config, _, _, err := runconfig.Parse([]string{unitTestImageID, "/bin/cat"}, nil)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
@@ -224,7 +225,7 @@ func TestRestartKillWait(t *testing.T) {
 	runtime := mkRuntimeFromEngine(eng, t)
 	runtime := mkRuntimeFromEngine(eng, t)
 	defer runtime.Nuke()
 	defer runtime.Nuke()
 
 
-	config, hostConfig, _, err := docker.ParseRun([]string{"-i", unitTestImageID, "/bin/cat"}, nil)
+	config, hostConfig, _, err := runconfig.Parse([]string{"-i", unitTestImageID, "/bin/cat"}, nil)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
@@ -302,7 +303,7 @@ func TestCreateStartRestartStopStartKillRm(t *testing.T) {
 	srv := mkServerFromEngine(eng, t)
 	srv := mkServerFromEngine(eng, t)
 	defer mkRuntimeFromEngine(eng, t).Nuke()
 	defer mkRuntimeFromEngine(eng, t).Nuke()
 
 
-	config, hostConfig, _, err := docker.ParseRun([]string{"-i", unitTestImageID, "/bin/cat"}, nil)
+	config, hostConfig, _, err := runconfig.Parse([]string{"-i", unitTestImageID, "/bin/cat"}, nil)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
@@ -401,7 +402,7 @@ func TestRmi(t *testing.T) {
 
 
 	initialImages := getAllImages(eng, t)
 	initialImages := getAllImages(eng, t)
 
 
-	config, hostConfig, _, err := docker.ParseRun([]string{unitTestImageID, "echo", "test"}, nil)
+	config, hostConfig, _, err := runconfig.Parse([]string{unitTestImageID, "echo", "test"}, nil)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
@@ -548,7 +549,7 @@ func TestListContainers(t *testing.T) {
 	srv := mkServerFromEngine(eng, t)
 	srv := mkServerFromEngine(eng, t)
 	defer mkRuntimeFromEngine(eng, t).Nuke()
 	defer mkRuntimeFromEngine(eng, t).Nuke()
 
 
-	config := docker.Config{
+	config := runconfig.Config{
 		Image:     unitTestImageID,
 		Image:     unitTestImageID,
 		Cmd:       []string{"/bin/sh", "-c", "cat"},
 		Cmd:       []string{"/bin/sh", "-c", "cat"},
 		OpenStdin: true,
 		OpenStdin: true,
@@ -671,7 +672,7 @@ func TestDeleteTagWithExistingContainers(t *testing.T) {
 	}
 	}
 
 
 	// Create a container from the image
 	// Create a container from the image
-	config, _, _, err := docker.ParseRun([]string{unitTestImageID, "echo test"}, nil)
+	config, _, _, err := runconfig.Parse([]string{unitTestImageID, "echo test"}, nil)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}

+ 5 - 4
integration/utils_test.go

@@ -16,6 +16,7 @@ import (
 
 
 	"github.com/dotcloud/docker"
 	"github.com/dotcloud/docker"
 	"github.com/dotcloud/docker/engine"
 	"github.com/dotcloud/docker/engine"
+	"github.com/dotcloud/docker/runconfig"
 	"github.com/dotcloud/docker/utils"
 	"github.com/dotcloud/docker/utils"
 )
 )
 
 
@@ -48,7 +49,7 @@ func mkRuntime(f utils.Fataler) *docker.Runtime {
 	return r
 	return r
 }
 }
 
 
-func createNamedTestContainer(eng *engine.Engine, config *docker.Config, f utils.Fataler, name string) (shortId string) {
+func createNamedTestContainer(eng *engine.Engine, config *runconfig.Config, f utils.Fataler, name string) (shortId string) {
 	job := eng.Job("create", name)
 	job := eng.Job("create", name)
 	if err := job.ImportEnv(config); err != nil {
 	if err := job.ImportEnv(config); err != nil {
 		f.Fatal(err)
 		f.Fatal(err)
@@ -60,7 +61,7 @@ func createNamedTestContainer(eng *engine.Engine, config *docker.Config, f utils
 	return
 	return
 }
 }
 
 
-func createTestContainer(eng *engine.Engine, config *docker.Config, f utils.Fataler) (shortId string) {
+func createTestContainer(eng *engine.Engine, config *runconfig.Config, f utils.Fataler) (shortId string) {
 	return createNamedTestContainer(eng, config, f, "")
 	return createNamedTestContainer(eng, config, f, "")
 }
 }
 
 
@@ -252,8 +253,8 @@ func readFile(src string, t *testing.T) (content string) {
 // dynamically replaced by the current test image.
 // dynamically replaced by the current test image.
 // The caller is responsible for destroying the container.
 // The caller is responsible for destroying the container.
 // Call t.Fatal() at the first error.
 // Call t.Fatal() at the first error.
-func mkContainer(r *docker.Runtime, args []string, t *testing.T) (*docker.Container, *docker.HostConfig, error) {
-	config, hc, _, err := docker.ParseRun(args, nil)
+func mkContainer(r *docker.Runtime, args []string, t *testing.T) (*docker.Container, *runconfig.HostConfig, error) {
+	config, hc, _, err := runconfig.Parse(args, nil)
 	defer func() {
 	defer func() {
 		if err != nil && t != nil {
 		if err != nil && t != nil {
 			t.Fatal(err)
 			t.Fatal(err)

+ 2 - 1
links_test.go

@@ -2,13 +2,14 @@ package docker
 
 
 import (
 import (
 	"github.com/dotcloud/docker/nat"
 	"github.com/dotcloud/docker/nat"
+	"github.com/dotcloud/docker/runconfig"
 	"strings"
 	"strings"
 	"testing"
 	"testing"
 )
 )
 
 
 func newMockLinkContainer(id string, ip string) *Container {
 func newMockLinkContainer(id string, ip string) *Container {
 	return &Container{
 	return &Container{
-		Config: &Config{},
+		Config: &runconfig.Config{},
 		ID:     id,
 		ID:     id,
 		NetworkSettings: &NetworkSettings{
 		NetworkSettings: &NetworkSettings{
 			IPAddress: ip,
 			IPAddress: ip,

+ 67 - 0
runconfig/compare.go

@@ -0,0 +1,67 @@
+package runconfig
+
+// Compare two Config struct. Do not compare the "Image" nor "Hostname" fields
+// If OpenStdin is set, then it differs
+func Compare(a, b *Config) bool {
+	if a == nil || b == nil ||
+		a.OpenStdin || b.OpenStdin {
+		return false
+	}
+	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 ||
+		a.VolumesFrom != b.VolumesFrom {
+		return false
+	}
+	if len(a.Cmd) != len(b.Cmd) ||
+		len(a.Dns) != len(b.Dns) ||
+		len(a.Env) != len(b.Env) ||
+		len(a.PortSpecs) != len(b.PortSpecs) ||
+		len(a.ExposedPorts) != len(b.ExposedPorts) ||
+		len(a.Entrypoint) != len(b.Entrypoint) ||
+		len(a.Volumes) != len(b.Volumes) {
+		return false
+	}
+
+	for i := 0; i < len(a.Cmd); i++ {
+		if a.Cmd[i] != b.Cmd[i] {
+			return false
+		}
+	}
+	for i := 0; i < len(a.Dns); i++ {
+		if a.Dns[i] != b.Dns[i] {
+			return false
+		}
+	}
+	for i := 0; i < len(a.Env); i++ {
+		if a.Env[i] != b.Env[i] {
+			return false
+		}
+	}
+	for i := 0; i < len(a.PortSpecs); i++ {
+		if a.PortSpecs[i] != b.PortSpecs[i] {
+			return false
+		}
+	}
+	for k := range a.ExposedPorts {
+		if _, exists := b.ExposedPorts[k]; !exists {
+			return false
+		}
+	}
+	for i := 0; i < len(a.Entrypoint); i++ {
+		if a.Entrypoint[i] != b.Entrypoint[i] {
+			return false
+		}
+	}
+	for key := range a.Volumes {
+		if _, exists := b.Volumes[key]; !exists {
+			return false
+		}
+	}
+	return true
+}

+ 76 - 0
runconfig/config.go

@@ -0,0 +1,76 @@
+package runconfig
+
+import (
+	"github.com/dotcloud/docker/engine"
+	"github.com/dotcloud/docker/nat"
+)
+
+// 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.
+type Config struct {
+	Hostname        string
+	Domainname      string
+	User            string
+	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)
+	AttachStdin     bool
+	AttachStdout    bool
+	AttachStderr    bool
+	PortSpecs       []string // Deprecated - Can be in the format of 8080/tcp
+	ExposedPorts    map[nat.Port]struct{}
+	Tty             bool // Attach standard streams to a tty, including stdin if it is not closed.
+	OpenStdin       bool // Open stdin
+	StdinOnce       bool // If true, close stdin after the 1 attached client disconnects.
+	Env             []string
+	Cmd             []string
+	Dns             []string
+	Image           string // Name of the image as it was passed by the operator (eg. could be symbolic)
+	Volumes         map[string]struct{}
+	VolumesFrom     string
+	WorkingDir      string
+	Entrypoint      []string
+	NetworkDisabled bool
+	OnBuild         []string
+}
+
+func ContainerConfigFromJob(job *engine.Job) *Config {
+	config := &Config{
+		Hostname:        job.Getenv("Hostname"),
+		Domainname:      job.Getenv("Domainname"),
+		User:            job.Getenv("User"),
+		Memory:          job.GetenvInt64("Memory"),
+		MemorySwap:      job.GetenvInt64("MemorySwap"),
+		CpuShares:       job.GetenvInt64("CpuShares"),
+		AttachStdin:     job.GetenvBool("AttachStdin"),
+		AttachStdout:    job.GetenvBool("AttachStdout"),
+		AttachStderr:    job.GetenvBool("AttachStderr"),
+		Tty:             job.GetenvBool("Tty"),
+		OpenStdin:       job.GetenvBool("OpenStdin"),
+		StdinOnce:       job.GetenvBool("StdinOnce"),
+		Image:           job.Getenv("Image"),
+		VolumesFrom:     job.Getenv("VolumesFrom"),
+		WorkingDir:      job.Getenv("WorkingDir"),
+		NetworkDisabled: job.GetenvBool("NetworkDisabled"),
+	}
+	job.GetenvJson("ExposedPorts", &config.ExposedPorts)
+	job.GetenvJson("Volumes", &config.Volumes)
+	if PortSpecs := job.GetenvList("PortSpecs"); PortSpecs != nil {
+		config.PortSpecs = PortSpecs
+	}
+	if Env := job.GetenvList("Env"); Env != nil {
+		config.Env = Env
+	}
+	if Cmd := job.GetenvList("Cmd"); Cmd != nil {
+		config.Cmd = Cmd
+	}
+	if Dns := job.GetenvList("Dns"); Dns != nil {
+		config.Dns = Dns
+	}
+	if Entrypoint := job.GetenvList("Entrypoint"); Entrypoint != nil {
+		config.Entrypoint = Entrypoint
+	}
+
+	return config
+}

+ 15 - 15
config_test.go → runconfig/config_test.go

@@ -1,11 +1,11 @@
-package docker
+package runconfig
 
 
 import (
 import (
 	"github.com/dotcloud/docker/nat"
 	"github.com/dotcloud/docker/nat"
 	"testing"
 	"testing"
 )
 )
 
 
-func TestCompareConfig(t *testing.T) {
+func TestCompare(t *testing.T) {
 	volumes1 := make(map[string]struct{})
 	volumes1 := make(map[string]struct{})
 	volumes1["/test1"] = struct{}{}
 	volumes1["/test1"] = struct{}{}
 	config1 := Config{
 	config1 := Config{
@@ -45,24 +45,24 @@ func TestCompareConfig(t *testing.T) {
 		VolumesFrom: "11111111",
 		VolumesFrom: "11111111",
 		Volumes:     volumes2,
 		Volumes:     volumes2,
 	}
 	}
-	if CompareConfig(&config1, &config2) {
-		t.Fatalf("CompareConfig should return false, Dns are different")
+	if Compare(&config1, &config2) {
+		t.Fatalf("Compare should return false, Dns are different")
 	}
 	}
-	if CompareConfig(&config1, &config3) {
-		t.Fatalf("CompareConfig should return false, PortSpecs are different")
+	if Compare(&config1, &config3) {
+		t.Fatalf("Compare should return false, PortSpecs are different")
 	}
 	}
-	if CompareConfig(&config1, &config4) {
-		t.Fatalf("CompareConfig should return false, VolumesFrom are different")
+	if Compare(&config1, &config4) {
+		t.Fatalf("Compare should return false, VolumesFrom are different")
 	}
 	}
-	if CompareConfig(&config1, &config5) {
-		t.Fatalf("CompareConfig should return false, Volumes are different")
+	if Compare(&config1, &config5) {
+		t.Fatalf("Compare should return false, Volumes are different")
 	}
 	}
-	if !CompareConfig(&config1, &config1) {
-		t.Fatalf("CompareConfig should return true")
+	if !Compare(&config1, &config1) {
+		t.Fatalf("Compare should return true")
 	}
 	}
 }
 }
 
 
-func TestMergeConfig(t *testing.T) {
+func TestMerge(t *testing.T) {
 	volumesImage := make(map[string]struct{})
 	volumesImage := make(map[string]struct{})
 	volumesImage["/test1"] = struct{}{}
 	volumesImage["/test1"] = struct{}{}
 	volumesImage["/test2"] = struct{}{}
 	volumesImage["/test2"] = struct{}{}
@@ -83,7 +83,7 @@ func TestMergeConfig(t *testing.T) {
 		Volumes:   volumesUser,
 		Volumes:   volumesUser,
 	}
 	}
 
 
-	if err := MergeConfig(configUser, configImage); err != nil {
+	if err := Merge(configUser, configImage); err != nil {
 		t.Error(err)
 		t.Error(err)
 	}
 	}
 
 
@@ -134,7 +134,7 @@ func TestMergeConfig(t *testing.T) {
 		ExposedPorts: ports,
 		ExposedPorts: ports,
 	}
 	}
 
 
-	if err := MergeConfig(configUser, configImage2); err != nil {
+	if err := Merge(configUser, configImage2); err != nil {
 		t.Error(err)
 		t.Error(err)
 	}
 	}
 
 

+ 39 - 0
runconfig/hostconfig.go

@@ -0,0 +1,39 @@
+package runconfig
+
+import (
+	"github.com/dotcloud/docker/engine"
+	"github.com/dotcloud/docker/nat"
+)
+
+type HostConfig struct {
+	Binds           []string
+	ContainerIDFile string
+	LxcConf         []KeyValuePair
+	Privileged      bool
+	PortBindings    nat.PortMap
+	Links           []string
+	PublishAllPorts bool
+}
+
+type KeyValuePair struct {
+	Key   string
+	Value string
+}
+
+func ContainerHostConfigFromJob(job *engine.Job) *HostConfig {
+	hostConfig := &HostConfig{
+		ContainerIDFile: job.Getenv("ContainerIDFile"),
+		Privileged:      job.GetenvBool("Privileged"),
+		PublishAllPorts: job.GetenvBool("PublishAllPorts"),
+	}
+	job.GetenvJson("LxcConf", &hostConfig.LxcConf)
+	job.GetenvJson("PortBindings", &hostConfig.PortBindings)
+	if Binds := job.GetenvList("Binds"); Binds != nil {
+		hostConfig.Binds = Binds
+	}
+	if Links := job.GetenvList("Links"); Links != nil {
+		hostConfig.Links = Links
+	}
+
+	return hostConfig
+}

+ 119 - 0
runconfig/merge.go

@@ -0,0 +1,119 @@
+package runconfig
+
+import (
+	"github.com/dotcloud/docker/nat"
+	"github.com/dotcloud/docker/utils"
+	"strings"
+)
+
+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 userConf.ExposedPorts == nil || len(userConf.ExposedPorts) == 0 {
+		userConf.ExposedPorts = imageConf.ExposedPorts
+	} else if imageConf.ExposedPorts != nil {
+		if userConf.ExposedPorts == nil {
+			userConf.ExposedPorts = make(nat.PortSet)
+		}
+		for port := range imageConf.ExposedPorts {
+			if _, exists := userConf.ExposedPorts[port]; !exists {
+				userConf.ExposedPorts[port] = struct{}{}
+			}
+		}
+	}
+
+	if userConf.PortSpecs != nil && len(userConf.PortSpecs) > 0 {
+		if userConf.ExposedPorts == nil {
+			userConf.ExposedPorts = make(nat.PortSet)
+		}
+		ports, _, err := nat.ParsePortSpecs(userConf.PortSpecs)
+		if err != nil {
+			return err
+		}
+		for port := range ports {
+			if _, exists := userConf.ExposedPorts[port]; !exists {
+				userConf.ExposedPorts[port] = struct{}{}
+			}
+		}
+		userConf.PortSpecs = nil
+	}
+	if imageConf.PortSpecs != nil && len(imageConf.PortSpecs) > 0 {
+		// FIXME: I think we can safely remove this. Leaving it for now for the sake of reverse-compat paranoia.
+		utils.Debugf("Migrating image port specs to containter: %s", strings.Join(imageConf.PortSpecs, ", "))
+		if userConf.ExposedPorts == nil {
+			userConf.ExposedPorts = make(nat.PortSet)
+		}
+
+		ports, _, err := nat.ParsePortSpecs(imageConf.PortSpecs)
+		if err != nil {
+			return err
+		}
+		for port := range ports {
+			if _, exists := userConf.ExposedPorts[port]; !exists {
+				userConf.ExposedPorts[port] = struct{}{}
+			}
+		}
+	}
+	if !userConf.Tty {
+		userConf.Tty = imageConf.Tty
+	}
+	if !userConf.OpenStdin {
+		userConf.OpenStdin = imageConf.OpenStdin
+	}
+	if !userConf.StdinOnce {
+		userConf.StdinOnce = imageConf.StdinOnce
+	}
+	if userConf.Env == nil || len(userConf.Env) == 0 {
+		userConf.Env = imageConf.Env
+	} else {
+		for _, imageEnv := range imageConf.Env {
+			found := false
+			imageEnvKey := strings.Split(imageEnv, "=")[0]
+			for _, userEnv := range userConf.Env {
+				userEnvKey := strings.Split(userEnv, "=")[0]
+				if imageEnvKey == userEnvKey {
+					found = true
+				}
+			}
+			if !found {
+				userConf.Env = append(userConf.Env, imageEnv)
+			}
+		}
+	}
+	if userConf.Cmd == nil || len(userConf.Cmd) == 0 {
+		userConf.Cmd = imageConf.Cmd
+	}
+	if userConf.Dns == nil || len(userConf.Dns) == 0 {
+		userConf.Dns = imageConf.Dns
+	} else {
+		//duplicates aren't an issue here
+		userConf.Dns = append(userConf.Dns, imageConf.Dns...)
+	}
+	if userConf.Entrypoint == nil || len(userConf.Entrypoint) == 0 {
+		userConf.Entrypoint = imageConf.Entrypoint
+	}
+	if userConf.WorkingDir == "" {
+		userConf.WorkingDir = imageConf.WorkingDir
+	}
+	if userConf.VolumesFrom == "" {
+		userConf.VolumesFrom = imageConf.VolumesFrom
+	}
+	if userConf.Volumes == nil || len(userConf.Volumes) == 0 {
+		userConf.Volumes = imageConf.Volumes
+	} else {
+		for k, v := range imageConf.Volumes {
+			userConf.Volumes[k] = v
+		}
+	}
+	return nil
+}

+ 246 - 0
runconfig/parse.go

@@ -0,0 +1,246 @@
+package runconfig
+
+import (
+	"fmt"
+	"github.com/dotcloud/docker/nat"
+	flag "github.com/dotcloud/docker/pkg/mflag"
+	"github.com/dotcloud/docker/pkg/opts"
+	"github.com/dotcloud/docker/pkg/sysinfo"
+	"github.com/dotcloud/docker/utils"
+	"io/ioutil"
+	"path"
+	"strings"
+)
+
+var (
+	ErrInvalidWorikingDirectory = fmt.Errorf("The working directory is invalid. It needs to be an absolute path.")
+	ErrConflictAttachDetach     = fmt.Errorf("Conflicting options: -a and -d")
+	ErrConflictDetachAutoRemove = fmt.Errorf("Conflicting options: -rm and -d")
+)
+
+//FIXME Only used in tests
+func Parse(args []string, sysInfo *sysinfo.SysInfo) (*Config, *HostConfig, *flag.FlagSet, error) {
+	cmd := flag.NewFlagSet("run", flag.ContinueOnError)
+	cmd.SetOutput(ioutil.Discard)
+	cmd.Usage = nil
+	return parseRun(cmd, args, sysInfo)
+}
+
+// FIXME: this maps the legacy commands.go code. It should be merged with Parse to only expose a single parse function.
+func ParseSubcommand(cmd *flag.FlagSet, args []string, sysInfo *sysinfo.SysInfo) (*Config, *HostConfig, *flag.FlagSet, error) {
+	return parseRun(cmd, args, sysInfo)
+}
+
+func parseRun(cmd *flag.FlagSet, args []string, sysInfo *sysinfo.SysInfo) (*Config, *HostConfig, *flag.FlagSet, error) {
+	var (
+		// FIXME: use utils.ListOpts for attach and volumes?
+		flAttach  = opts.NewListOpts(opts.ValidateAttach)
+		flVolumes = opts.NewListOpts(opts.ValidatePath)
+		flLinks   = opts.NewListOpts(opts.ValidateLink)
+		flEnv     = opts.NewListOpts(opts.ValidateEnv)
+
+		flPublish     opts.ListOpts
+		flExpose      opts.ListOpts
+		flDns         opts.ListOpts
+		flVolumesFrom opts.ListOpts
+		flLxcOpts     opts.ListOpts
+
+		flAutoRemove      = cmd.Bool([]string{"#rm", "-rm"}, false, "Automatically remove the container when it exits (incompatible with -d)")
+		flDetach          = cmd.Bool([]string{"d", "-detach"}, false, "Detached mode: Run container in the background, print new container id")
+		flNetwork         = cmd.Bool([]string{"n", "-networking"}, true, "Enable networking for this container")
+		flPrivileged      = cmd.Bool([]string{"#privileged", "-privileged"}, false, "Give extended privileges to this container")
+		flPublishAll      = cmd.Bool([]string{"P", "-publish-all"}, false, "Publish all exposed ports to the host interfaces")
+		flStdin           = cmd.Bool([]string{"i", "-interactive"}, false, "Keep stdin open even if not attached")
+		flTty             = cmd.Bool([]string{"t", "-tty"}, false, "Allocate a pseudo-tty")
+		flContainerIDFile = cmd.String([]string{"#cidfile", "-cidfile"}, "", "Write the container ID to the file")
+		flEntrypoint      = cmd.String([]string{"#entrypoint", "-entrypoint"}, "", "Overwrite the default entrypoint of the image")
+		flHostname        = cmd.String([]string{"h", "-hostname"}, "", "Container host name")
+		flMemoryString    = cmd.String([]string{"m", "-memory"}, "", "Memory limit (format: <number><optional unit>, where unit = b, k, m or g)")
+		flUser            = cmd.String([]string{"u", "-user"}, "", "Username or UID")
+		flWorkingDir      = cmd.String([]string{"w", "-workdir"}, "", "Working directory inside the container")
+		flCpuShares       = cmd.Int64([]string{"c", "-cpu-shares"}, 0, "CPU shares (relative weight)")
+
+		// For documentation purpose
+		_ = cmd.Bool([]string{"#sig-proxy", "-sig-proxy"}, true, "Proxify all received signal to the process (even in non-tty mode)")
+		_ = cmd.String([]string{"#name", "-name"}, "", "Assign a name to the container")
+	)
+
+	cmd.Var(&flAttach, []string{"a", "-attach"}, "Attach to stdin, stdout or stderr.")
+	cmd.Var(&flVolumes, []string{"v", "-volume"}, "Bind mount a volume (e.g. from the host: -v /host:/container, from docker: -v /container)")
+	cmd.Var(&flLinks, []string{"#link", "-link"}, "Add link to another container (name:alias)")
+	cmd.Var(&flEnv, []string{"e", "-env"}, "Set environment variables")
+
+	cmd.Var(&flPublish, []string{"p", "-publish"}, fmt.Sprintf("Publish a container's port to the host (format: %s) (use 'docker port' to see the actual mapping)", nat.PortSpecTemplateFormat))
+	cmd.Var(&flExpose, []string{"#expose", "-expose"}, "Expose a port from the container without publishing it to your host")
+	cmd.Var(&flDns, []string{"#dns", "-dns"}, "Set custom dns servers")
+	cmd.Var(&flVolumesFrom, []string{"#volumes-from", "-volumes-from"}, "Mount volumes from the specified container(s)")
+	cmd.Var(&flLxcOpts, []string{"#lxc-conf", "-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
+	}
+
+	// Check if the kernel supports memory limit cgroup.
+	if sysInfo != nil && *flMemoryString != "" && !sysInfo.MemoryLimit {
+		*flMemoryString = ""
+	}
+
+	// Validate input params
+	if *flDetach && flAttach.Len() > 0 {
+		return nil, nil, cmd, ErrConflictAttachDetach
+	}
+	if *flWorkingDir != "" && !path.IsAbs(*flWorkingDir) {
+		return nil, nil, cmd, ErrInvalidWorikingDirectory
+	}
+	if *flDetach && *flAutoRemove {
+		return nil, nil, cmd, ErrConflictDetachAutoRemove
+	}
+
+	// If neither -d or -a are set, attach to everything by default
+	if flAttach.Len() == 0 && !*flDetach {
+		if !*flDetach {
+			flAttach.Set("stdout")
+			flAttach.Set("stderr")
+			if *flStdin {
+				flAttach.Set("stdin")
+			}
+		}
+	}
+
+	var flMemory int64
+	if *flMemoryString != "" {
+		parsedMemory, err := utils.RAMInBytes(*flMemoryString)
+		if err != nil {
+			return nil, nil, cmd, err
+		}
+		flMemory = parsedMemory
+	}
+
+	var binds []string
+	// add any bind targets to the list of container volumes
+	for bind := range flVolumes.GetMap() {
+		if arr := strings.Split(bind, ":"); len(arr) > 1 {
+			if arr[0] == "/" {
+				return nil, nil, cmd, fmt.Errorf("Invalid bind mount: source can't be '/'")
+			}
+			dstDir := arr[1]
+			flVolumes.Set(dstDir)
+			binds = append(binds, bind)
+			flVolumes.Delete(bind)
+		} else if bind == "/" {
+			return nil, nil, cmd, fmt.Errorf("Invalid volume: path can't be '/'")
+		}
+	}
+
+	var (
+		parsedArgs = cmd.Args()
+		runCmd     []string
+		entrypoint []string
+		image      string
+	)
+	if len(parsedArgs) >= 1 {
+		image = cmd.Arg(0)
+	}
+	if len(parsedArgs) > 1 {
+		runCmd = parsedArgs[1:]
+	}
+	if *flEntrypoint != "" {
+		entrypoint = []string{*flEntrypoint}
+	}
+
+	lxcConf, err := parseLxcConfOpts(flLxcOpts)
+	if err != nil {
+		return nil, nil, cmd, err
+	}
+
+	var (
+		domainname string
+		hostname   = *flHostname
+		parts      = strings.SplitN(hostname, ".", 2)
+	)
+	if len(parts) > 1 {
+		hostname = parts[0]
+		domainname = parts[1]
+	}
+
+	ports, portBindings, err := nat.ParsePortSpecs(flPublish.GetAll())
+	if err != nil {
+		return nil, nil, cmd, err
+	}
+
+	// Merge in exposed ports to the map of published ports
+	for _, e := range flExpose.GetAll() {
+		if strings.Contains(e, ":") {
+			return nil, nil, cmd, fmt.Errorf("Invalid port format for --expose: %s", e)
+		}
+		p := nat.NewPort(nat.SplitProtoPort(e))
+		if _, exists := ports[p]; !exists {
+			ports[p] = struct{}{}
+		}
+	}
+
+	config := &Config{
+		Hostname:        hostname,
+		Domainname:      domainname,
+		PortSpecs:       nil, // Deprecated
+		ExposedPorts:    ports,
+		User:            *flUser,
+		Tty:             *flTty,
+		NetworkDisabled: !*flNetwork,
+		OpenStdin:       *flStdin,
+		Memory:          flMemory,
+		CpuShares:       *flCpuShares,
+		AttachStdin:     flAttach.Get("stdin"),
+		AttachStdout:    flAttach.Get("stdout"),
+		AttachStderr:    flAttach.Get("stderr"),
+		Env:             flEnv.GetAll(),
+		Cmd:             runCmd,
+		Dns:             flDns.GetAll(),
+		Image:           image,
+		Volumes:         flVolumes.GetMap(),
+		VolumesFrom:     strings.Join(flVolumesFrom.GetAll(), ","),
+		Entrypoint:      entrypoint,
+		WorkingDir:      *flWorkingDir,
+	}
+
+	hostConfig := &HostConfig{
+		Binds:           binds,
+		ContainerIDFile: *flContainerIDFile,
+		LxcConf:         lxcConf,
+		Privileged:      *flPrivileged,
+		PortBindings:    portBindings,
+		Links:           flLinks.GetAll(),
+		PublishAllPorts: *flPublishAll,
+	}
+
+	if sysInfo != nil && flMemory > 0 && !sysInfo.SwapLimit {
+		//fmt.Fprintf(stdout, "WARNING: Your kernel does not support swap limit capabilities. Limitation discarded.\n")
+		config.MemorySwap = -1
+	}
+
+	// When allocating stdin in attached mode, close stdin at client disconnect
+	if config.OpenStdin && config.AttachStdin {
+		config.StdinOnce = true
+	}
+	return config, hostConfig, cmd, nil
+}
+
+func parseLxcConfOpts(opts opts.ListOpts) ([]KeyValuePair, error) {
+	out := make([]KeyValuePair, opts.Len())
+	for i, o := range opts.GetAll() {
+		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
+}

+ 22 - 0
runconfig/parse_test.go

@@ -0,0 +1,22 @@
+package runconfig
+
+import (
+	"testing"
+)
+
+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()
+		}
+	}
+}

+ 6 - 5
runtime.go

@@ -18,6 +18,7 @@ import (
 	"github.com/dotcloud/docker/networkdriver/portallocator"
 	"github.com/dotcloud/docker/networkdriver/portallocator"
 	"github.com/dotcloud/docker/pkg/graphdb"
 	"github.com/dotcloud/docker/pkg/graphdb"
 	"github.com/dotcloud/docker/pkg/sysinfo"
 	"github.com/dotcloud/docker/pkg/sysinfo"
+	"github.com/dotcloud/docker/runconfig"
 	"github.com/dotcloud/docker/utils"
 	"github.com/dotcloud/docker/utils"
 	"io"
 	"io"
 	"io/ioutil"
 	"io/ioutil"
@@ -329,7 +330,7 @@ func (runtime *Runtime) restore() error {
 }
 }
 
 
 // Create creates a new container from the given configuration with a given name.
 // Create creates a new container from the given configuration with a given name.
-func (runtime *Runtime) Create(config *Config, name string) (*Container, []string, error) {
+func (runtime *Runtime) Create(config *runconfig.Config, name string) (*Container, []string, error) {
 	// Lookup image
 	// Lookup image
 	img, err := runtime.repositories.LookupImage(config.Image)
 	img, err := runtime.repositories.LookupImage(config.Image)
 	if err != nil {
 	if err != nil {
@@ -347,7 +348,7 @@ func (runtime *Runtime) Create(config *Config, name string) (*Container, []strin
 		return nil, nil, fmt.Errorf("Cannot create container with more than %d parents", MaxImageDepth)
 		return nil, nil, fmt.Errorf("Cannot create container with more than %d parents", MaxImageDepth)
 	}
 	}
 
 
-	checkDeprecatedExpose := func(config *Config) bool {
+	checkDeprecatedExpose := func(config *runconfig.Config) bool {
 		if config != nil {
 		if config != nil {
 			if config.PortSpecs != nil {
 			if config.PortSpecs != nil {
 				for _, p := range config.PortSpecs {
 				for _, p := range config.PortSpecs {
@@ -366,7 +367,7 @@ func (runtime *Runtime) Create(config *Config, name string) (*Container, []strin
 	}
 	}
 
 
 	if img.Config != nil {
 	if img.Config != nil {
-		if err := MergeConfig(config, img.Config); err != nil {
+		if err := runconfig.Merge(config, img.Config); err != nil {
 			return nil, nil, err
 			return nil, nil, err
 		}
 		}
 	}
 	}
@@ -441,7 +442,7 @@ func (runtime *Runtime) Create(config *Config, name string) (*Container, []strin
 		Path:            entrypoint,
 		Path:            entrypoint,
 		Args:            args, //FIXME: de-duplicate from config
 		Args:            args, //FIXME: de-duplicate from config
 		Config:          config,
 		Config:          config,
-		hostConfig:      &HostConfig{},
+		hostConfig:      &runconfig.HostConfig{},
 		Image:           img.ID, // Always use the resolved image id
 		Image:           img.ID, // Always use the resolved image id
 		NetworkSettings: &NetworkSettings{},
 		NetworkSettings: &NetworkSettings{},
 		Name:            name,
 		Name:            name,
@@ -518,7 +519,7 @@ func (runtime *Runtime) Create(config *Config, name string) (*Container, []strin
 
 
 // Commit creates a new filesystem image from the current state of a container.
 // Commit creates a new filesystem image from the current state of a container.
 // The image can optionally be tagged into a repository
 // The image can optionally be tagged into a repository
-func (runtime *Runtime) Commit(container *Container, repository, tag, comment, author string, config *Config) (*Image, error) {
+func (runtime *Runtime) Commit(container *Container, repository, tag, comment, author string, config *runconfig.Config) (*Image, error) {
 	// FIXME: freeze the container before copying it to avoid data corruption?
 	// FIXME: freeze the container before copying it to avoid data corruption?
 	// FIXME: this shouldn't be in commands.
 	// FIXME: this shouldn't be in commands.
 	if err := container.Mount(); err != nil {
 	if err := container.Mount(); err != nil {

+ 9 - 8
server.go

@@ -10,6 +10,7 @@ import (
 	"github.com/dotcloud/docker/engine"
 	"github.com/dotcloud/docker/engine"
 	"github.com/dotcloud/docker/pkg/graphdb"
 	"github.com/dotcloud/docker/pkg/graphdb"
 	"github.com/dotcloud/docker/registry"
 	"github.com/dotcloud/docker/registry"
+	"github.com/dotcloud/docker/runconfig"
 	"github.com/dotcloud/docker/utils"
 	"github.com/dotcloud/docker/utils"
 	"io"
 	"io"
 	"io/ioutil"
 	"io/ioutil"
@@ -662,7 +663,7 @@ func (srv *Server) ImageInsert(job *engine.Job) engine.Status {
 	}
 	}
 	defer file.Body.Close()
 	defer file.Body.Close()
 
 
-	config, _, _, err := ParseRun([]string{img.ID, "echo", "insert", url, path}, srv.runtime.sysInfo)
+	config, _, _, err := runconfig.Parse([]string{img.ID, "echo", "insert", url, path}, srv.runtime.sysInfo)
 	if err != nil {
 	if err != nil {
 		return job.Error(err)
 		return job.Error(err)
 	}
 	}
@@ -1043,7 +1044,7 @@ func (srv *Server) ContainerCommit(job *engine.Job) engine.Status {
 	if container == nil {
 	if container == nil {
 		return job.Errorf("No such container: %s", name)
 		return job.Errorf("No such container: %s", name)
 	}
 	}
-	var config Config
+	var config runconfig.Config
 	if err := job.GetenvJson("config", &config); err != nil {
 	if err := job.GetenvJson("config", &config); err != nil {
 		return job.Error(err)
 		return job.Error(err)
 	}
 	}
@@ -1623,7 +1624,7 @@ func (srv *Server) ContainerCreate(job *engine.Job) engine.Status {
 	} else if len(job.Args) > 1 {
 	} else if len(job.Args) > 1 {
 		return job.Errorf("Usage: %s", job.Name)
 		return job.Errorf("Usage: %s", job.Name)
 	}
 	}
-	config := ContainerConfigFromJob(job)
+	config := runconfig.ContainerConfigFromJob(job)
 	if config.Memory != 0 && config.Memory < 524288 {
 	if config.Memory != 0 && config.Memory < 524288 {
 		return job.Errorf("Minimum memory limit allowed is 512k")
 		return job.Errorf("Minimum memory limit allowed is 512k")
 	}
 	}
@@ -1989,7 +1990,7 @@ func (srv *Server) canDeleteImage(imgID string) error {
 	return nil
 	return nil
 }
 }
 
 
-func (srv *Server) ImageGetCached(imgID string, config *Config) (*Image, error) {
+func (srv *Server) ImageGetCached(imgID string, config *runconfig.Config) (*Image, error) {
 
 
 	// Retrieve all images
 	// Retrieve all images
 	images, err := srv.runtime.graph.Map()
 	images, err := srv.runtime.graph.Map()
@@ -2013,7 +2014,7 @@ func (srv *Server) ImageGetCached(imgID string, config *Config) (*Image, error)
 		if err != nil {
 		if err != nil {
 			return nil, err
 			return nil, err
 		}
 		}
-		if CompareConfig(&img.ContainerConfig, config) {
+		if runconfig.Compare(&img.ContainerConfig, config) {
 			if match == nil || match.Created.Before(img.Created) {
 			if match == nil || match.Created.Before(img.Created) {
 				match = img
 				match = img
 			}
 			}
@@ -2022,7 +2023,7 @@ func (srv *Server) ImageGetCached(imgID string, config *Config) (*Image, error)
 	return match, nil
 	return match, nil
 }
 }
 
 
-func (srv *Server) RegisterLinks(container *Container, hostConfig *HostConfig) error {
+func (srv *Server) RegisterLinks(container *Container, hostConfig *runconfig.HostConfig) error {
 	runtime := srv.runtime
 	runtime := srv.runtime
 
 
 	if hostConfig != nil && hostConfig.Links != nil {
 	if hostConfig != nil && hostConfig.Links != nil {
@@ -2066,7 +2067,7 @@ func (srv *Server) ContainerStart(job *engine.Job) engine.Status {
 	}
 	}
 	// If no environment was set, then no hostconfig was passed.
 	// If no environment was set, then no hostconfig was passed.
 	if len(job.Environ()) > 0 {
 	if len(job.Environ()) > 0 {
-		hostConfig := ContainerHostConfigFromJob(job)
+		hostConfig := runconfig.ContainerHostConfigFromJob(job)
 		// Validate the HostConfig binds. Make sure that:
 		// Validate the HostConfig binds. Make sure that:
 		// 1) the source of a bind mount isn't /
 		// 1) the source of a bind mount isn't /
 		//         The bind mount "/:/foo" isn't allowed.
 		//         The bind mount "/:/foo" isn't allowed.
@@ -2310,7 +2311,7 @@ func (srv *Server) JobInspect(job *engine.Job) engine.Status {
 		}
 		}
 		object = &struct {
 		object = &struct {
 			*Container
 			*Container
-			HostConfig *HostConfig
+			HostConfig *runconfig.HostConfig
 		}{container, container.hostConfig}
 		}{container, container.hostConfig}
 	default:
 	default:
 		return job.Errorf("Unknown kind: %s", kind)
 		return job.Errorf("Unknown kind: %s", kind)

+ 3 - 202
utils.go

@@ -1,14 +1,12 @@
 package docker
 package docker
 
 
 import (
 import (
-	"fmt"
 	"github.com/dotcloud/docker/archive"
 	"github.com/dotcloud/docker/archive"
 	"github.com/dotcloud/docker/nat"
 	"github.com/dotcloud/docker/nat"
 	"github.com/dotcloud/docker/pkg/namesgenerator"
 	"github.com/dotcloud/docker/pkg/namesgenerator"
-	"github.com/dotcloud/docker/pkg/opts"
+	"github.com/dotcloud/docker/runconfig"
 	"github.com/dotcloud/docker/utils"
 	"github.com/dotcloud/docker/utils"
 	"io"
 	"io"
-	"strings"
 	"sync/atomic"
 	"sync/atomic"
 )
 )
 
 
@@ -16,204 +14,7 @@ type Change struct {
 	archive.Change
 	archive.Change
 }
 }
 
 
-// Compare two Config struct. Do not compare the "Image" nor "Hostname" fields
-// If OpenStdin is set, then it differs
-func CompareConfig(a, b *Config) bool {
-	if a == nil || b == nil ||
-		a.OpenStdin || b.OpenStdin {
-		return false
-	}
-	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 ||
-		a.VolumesFrom != b.VolumesFrom {
-		return false
-	}
-	if len(a.Cmd) != len(b.Cmd) ||
-		len(a.Dns) != len(b.Dns) ||
-		len(a.Env) != len(b.Env) ||
-		len(a.PortSpecs) != len(b.PortSpecs) ||
-		len(a.ExposedPorts) != len(b.ExposedPorts) ||
-		len(a.Entrypoint) != len(b.Entrypoint) ||
-		len(a.Volumes) != len(b.Volumes) {
-		return false
-	}
-
-	for i := 0; i < len(a.Cmd); i++ {
-		if a.Cmd[i] != b.Cmd[i] {
-			return false
-		}
-	}
-	for i := 0; i < len(a.Dns); i++ {
-		if a.Dns[i] != b.Dns[i] {
-			return false
-		}
-	}
-	for i := 0; i < len(a.Env); i++ {
-		if a.Env[i] != b.Env[i] {
-			return false
-		}
-	}
-	for i := 0; i < len(a.PortSpecs); i++ {
-		if a.PortSpecs[i] != b.PortSpecs[i] {
-			return false
-		}
-	}
-	for k := range a.ExposedPorts {
-		if _, exists := b.ExposedPorts[k]; !exists {
-			return false
-		}
-	}
-	for i := 0; i < len(a.Entrypoint); i++ {
-		if a.Entrypoint[i] != b.Entrypoint[i] {
-			return false
-		}
-	}
-	for key := range a.Volumes {
-		if _, exists := b.Volumes[key]; !exists {
-			return false
-		}
-	}
-	return true
-}
-
-func MergeConfig(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 userConf.ExposedPorts == nil || len(userConf.ExposedPorts) == 0 {
-		userConf.ExposedPorts = imageConf.ExposedPorts
-	} else if imageConf.ExposedPorts != nil {
-		if userConf.ExposedPorts == nil {
-			userConf.ExposedPorts = make(nat.PortSet)
-		}
-		for port := range imageConf.ExposedPorts {
-			if _, exists := userConf.ExposedPorts[port]; !exists {
-				userConf.ExposedPorts[port] = struct{}{}
-			}
-		}
-	}
-
-	if userConf.PortSpecs != nil && len(userConf.PortSpecs) > 0 {
-		if userConf.ExposedPorts == nil {
-			userConf.ExposedPorts = make(nat.PortSet)
-		}
-		ports, _, err := nat.ParsePortSpecs(userConf.PortSpecs)
-		if err != nil {
-			return err
-		}
-		for port := range ports {
-			if _, exists := userConf.ExposedPorts[port]; !exists {
-				userConf.ExposedPorts[port] = struct{}{}
-			}
-		}
-		userConf.PortSpecs = nil
-	}
-	if imageConf.PortSpecs != nil && len(imageConf.PortSpecs) > 0 {
-		utils.Debugf("Migrating image port specs to containter: %s", strings.Join(imageConf.PortSpecs, ", "))
-		if userConf.ExposedPorts == nil {
-			userConf.ExposedPorts = make(nat.PortSet)
-		}
-
-		ports, _, err := nat.ParsePortSpecs(imageConf.PortSpecs)
-		if err != nil {
-			return err
-		}
-		for port := range ports {
-			if _, exists := userConf.ExposedPorts[port]; !exists {
-				userConf.ExposedPorts[port] = struct{}{}
-			}
-		}
-	}
-	if !userConf.Tty {
-		userConf.Tty = imageConf.Tty
-	}
-	if !userConf.OpenStdin {
-		userConf.OpenStdin = imageConf.OpenStdin
-	}
-	if !userConf.StdinOnce {
-		userConf.StdinOnce = imageConf.StdinOnce
-	}
-	if userConf.Env == nil || len(userConf.Env) == 0 {
-		userConf.Env = imageConf.Env
-	} else {
-		for _, imageEnv := range imageConf.Env {
-			found := false
-			imageEnvKey := strings.Split(imageEnv, "=")[0]
-			for _, userEnv := range userConf.Env {
-				userEnvKey := strings.Split(userEnv, "=")[0]
-				if imageEnvKey == userEnvKey {
-					found = true
-				}
-			}
-			if !found {
-				userConf.Env = append(userConf.Env, imageEnv)
-			}
-		}
-	}
-	if userConf.Cmd == nil || len(userConf.Cmd) == 0 {
-		userConf.Cmd = imageConf.Cmd
-	}
-	if userConf.Dns == nil || len(userConf.Dns) == 0 {
-		userConf.Dns = imageConf.Dns
-	} else {
-		//duplicates aren't an issue here
-		userConf.Dns = append(userConf.Dns, imageConf.Dns...)
-	}
-	if userConf.Entrypoint == nil || len(userConf.Entrypoint) == 0 {
-		userConf.Entrypoint = imageConf.Entrypoint
-	}
-	if userConf.WorkingDir == "" {
-		userConf.WorkingDir = imageConf.WorkingDir
-	}
-	if userConf.VolumesFrom == "" {
-		userConf.VolumesFrom = imageConf.VolumesFrom
-	}
-	if userConf.Volumes == nil || len(userConf.Volumes) == 0 {
-		userConf.Volumes = imageConf.Volumes
-	} else {
-		for k, v := range imageConf.Volumes {
-			userConf.Volumes[k] = v
-		}
-	}
-	return nil
-}
-
-func parseLxcConfOpts(opts opts.ListOpts) ([]KeyValuePair, error) {
-	out := make([]KeyValuePair, opts.Len())
-	for i, o := range opts.GetAll() {
-		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
-}
-
-func migratePortMappings(config *Config, hostConfig *HostConfig) error {
+func migratePortMappings(config *runconfig.Config, hostConfig *runconfig.HostConfig) error {
 	if config.PortSpecs != nil {
 	if config.PortSpecs != nil {
 		ports, bindings, err := nat.ParsePortSpecs(config.PortSpecs)
 		ports, bindings, err := nat.ParsePortSpecs(config.PortSpecs)
 		if err != nil {
 		if err != nil {
@@ -222,7 +23,7 @@ func migratePortMappings(config *Config, hostConfig *HostConfig) error {
 		config.PortSpecs = nil
 		config.PortSpecs = nil
 		if len(bindings) > 0 {
 		if len(bindings) > 0 {
 			if hostConfig == nil {
 			if hostConfig == nil {
-				hostConfig = &HostConfig{}
+				hostConfig = &runconfig.HostConfig{}
 			}
 			}
 			hostConfig.PortBindings = bindings
 			hostConfig.PortBindings = bindings
 		}
 		}