Browse code

Move the canonical run configuration objects to a sub-package

* Config is now runconfig.Config
* HostConfig is now runconfig.HostConfig
* MergeConfig is now runconfig.Merge
* CompareConfig is now runconfig.Compare
* ParseRun is now runconfig.Parse
* ContainerConfigFromJob is now runconfig.ContainerConfigFromJob
* ContainerHostConfigFromJob is now runconfig.ContainerHostConfigFromJob

This facilitates refactoring commands.go and shrinks the core.

Docker-DCO-1.1-Signed-off-by: Solomon Hykes <solomon@docker.com> (github: shykes)

Solomon Hykes authored on 2014/02/12 13:04:39
Showing 24 changed files
... ...
@@ -9,6 +9,7 @@ import (
9 9
 	"github.com/dotcloud/docker/archive"
10 10
 	"github.com/dotcloud/docker/auth"
11 11
 	"github.com/dotcloud/docker/registry"
12
+	"github.com/dotcloud/docker/runconfig"
12 13
 	"github.com/dotcloud/docker/utils"
13 14
 	"io"
14 15
 	"io/ioutil"
... ...
@@ -38,7 +39,7 @@ type buildFile struct {
38 38
 
39 39
 	image      string
40 40
 	maintainer string
41
-	config     *Config
41
+	config     *runconfig.Config
42 42
 
43 43
 	contextPath string
44 44
 	context     *utils.TarSum
... ...
@@ -101,7 +102,7 @@ func (b *buildFile) CmdFrom(name string) error {
101 101
 		}
102 102
 	}
103 103
 	b.image = image.ID
104
-	b.config = &Config{}
104
+	b.config = &runconfig.Config{}
105 105
 	if image.Config != nil {
106 106
 		b.config = image.Config
107 107
 	}
... ...
@@ -158,14 +159,14 @@ func (b *buildFile) CmdRun(args string) error {
158 158
 	if b.image == "" {
159 159
 		return fmt.Errorf("Please provide a source image with `from` prior to run")
160 160
 	}
161
-	config, _, _, err := ParseRun(append([]string{b.image}, b.buildCmdFromJson(args)...), nil)
161
+	config, _, _, err := runconfig.Parse(append([]string{b.image}, b.buildCmdFromJson(args)...), nil)
162 162
 	if err != nil {
163 163
 		return err
164 164
 	}
165 165
 
166 166
 	cmd := b.config.Cmd
167 167
 	b.config.Cmd = nil
168
-	MergeConfig(b.config, config)
168
+	runconfig.Merge(b.config, config)
169 169
 
170 170
 	defer func(cmd []string) { b.config.Cmd = cmd }(cmd)
171 171
 
... ...
@@ -742,7 +743,7 @@ func NewBuildFile(srv *Server, outStream, errStream io.Writer, verbose, utilizeC
742 742
 	return &buildFile{
743 743
 		runtime:       srv.runtime,
744 744
 		srv:           srv,
745
-		config:        &Config{},
745
+		config:        &runconfig.Config{},
746 746
 		outStream:     outStream,
747 747
 		errStream:     errStream,
748 748
 		tmpContainers: make(map[string]struct{}),
... ...
@@ -15,10 +15,9 @@ import (
15 15
 	"github.com/dotcloud/docker/engine"
16 16
 	"github.com/dotcloud/docker/nat"
17 17
 	flag "github.com/dotcloud/docker/pkg/mflag"
18
-	"github.com/dotcloud/docker/pkg/opts"
19
-	"github.com/dotcloud/docker/pkg/sysinfo"
20 18
 	"github.com/dotcloud/docker/pkg/term"
21 19
 	"github.com/dotcloud/docker/registry"
20
+	"github.com/dotcloud/docker/runconfig"
22 21
 	"github.com/dotcloud/docker/utils"
23 22
 	"io"
24 23
 	"io/ioutil"
... ...
@@ -1449,11 +1448,11 @@ func (cli *DockerCli) CmdCommit(args ...string) error {
1449 1449
 	v.Set("comment", *flComment)
1450 1450
 	v.Set("author", *flAuthor)
1451 1451
 	var (
1452
-		config *Config
1452
+		config *runconfig.Config
1453 1453
 		env    engine.Env
1454 1454
 	)
1455 1455
 	if *flConfig != "" {
1456
-		config = &Config{}
1456
+		config = &runconfig.Config{}
1457 1457
 		if err := json.Unmarshal([]byte(*flConfig), config); err != nil {
1458 1458
 			return err
1459 1459
 		}
... ...
@@ -1743,210 +1742,9 @@ func (cli *DockerCli) CmdTag(args ...string) error {
1743 1743
 	return nil
1744 1744
 }
1745 1745
 
1746
-//FIXME Only used in tests
1747
-func ParseRun(args []string, sysInfo *sysinfo.SysInfo) (*Config, *HostConfig, *flag.FlagSet, error) {
1748
-	cmd := flag.NewFlagSet("run", flag.ContinueOnError)
1749
-	cmd.SetOutput(ioutil.Discard)
1750
-	cmd.Usage = nil
1751
-	return parseRun(cmd, args, sysInfo)
1752
-}
1753
-
1754
-func parseRun(cmd *flag.FlagSet, args []string, sysInfo *sysinfo.SysInfo) (*Config, *HostConfig, *flag.FlagSet, error) {
1755
-	var (
1756
-		// FIXME: use utils.ListOpts for attach and volumes?
1757
-		flAttach  = opts.NewListOpts(opts.ValidateAttach)
1758
-		flVolumes = opts.NewListOpts(opts.ValidatePath)
1759
-		flLinks   = opts.NewListOpts(opts.ValidateLink)
1760
-		flEnv     = opts.NewListOpts(opts.ValidateEnv)
1761
-
1762
-		flPublish     opts.ListOpts
1763
-		flExpose      opts.ListOpts
1764
-		flDns         opts.ListOpts
1765
-		flVolumesFrom opts.ListOpts
1766
-		flLxcOpts     opts.ListOpts
1767
-
1768
-		flAutoRemove      = cmd.Bool([]string{"#rm", "-rm"}, false, "Automatically remove the container when it exits (incompatible with -d)")
1769
-		flDetach          = cmd.Bool([]string{"d", "-detach"}, false, "Detached mode: Run container in the background, print new container id")
1770
-		flNetwork         = cmd.Bool([]string{"n", "-networking"}, true, "Enable networking for this container")
1771
-		flPrivileged      = cmd.Bool([]string{"#privileged", "-privileged"}, false, "Give extended privileges to this container")
1772
-		flPublishAll      = cmd.Bool([]string{"P", "-publish-all"}, false, "Publish all exposed ports to the host interfaces")
1773
-		flStdin           = cmd.Bool([]string{"i", "-interactive"}, false, "Keep stdin open even if not attached")
1774
-		flTty             = cmd.Bool([]string{"t", "-tty"}, false, "Allocate a pseudo-tty")
1775
-		flContainerIDFile = cmd.String([]string{"#cidfile", "-cidfile"}, "", "Write the container ID to the file")
1776
-		flEntrypoint      = cmd.String([]string{"#entrypoint", "-entrypoint"}, "", "Overwrite the default entrypoint of the image")
1777
-		flHostname        = cmd.String([]string{"h", "-hostname"}, "", "Container host name")
1778
-		flMemoryString    = cmd.String([]string{"m", "-memory"}, "", "Memory limit (format: <number><optional unit>, where unit = b, k, m or g)")
1779
-		flUser            = cmd.String([]string{"u", "-user"}, "", "Username or UID")
1780
-		flWorkingDir      = cmd.String([]string{"w", "-workdir"}, "", "Working directory inside the container")
1781
-		flCpuShares       = cmd.Int64([]string{"c", "-cpu-shares"}, 0, "CPU shares (relative weight)")
1782
-
1783
-		// For documentation purpose
1784
-		_ = cmd.Bool([]string{"#sig-proxy", "-sig-proxy"}, true, "Proxify all received signal to the process (even in non-tty mode)")
1785
-		_ = cmd.String([]string{"#name", "-name"}, "", "Assign a name to the container")
1786
-	)
1787
-
1788
-	cmd.Var(&flAttach, []string{"a", "-attach"}, "Attach to stdin, stdout or stderr.")
1789
-	cmd.Var(&flVolumes, []string{"v", "-volume"}, "Bind mount a volume (e.g. from the host: -v /host:/container, from docker: -v /container)")
1790
-	cmd.Var(&flLinks, []string{"#link", "-link"}, "Add link to another container (name:alias)")
1791
-	cmd.Var(&flEnv, []string{"e", "-env"}, "Set environment variables")
1792
-
1793
-	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))
1794
-	cmd.Var(&flExpose, []string{"#expose", "-expose"}, "Expose a port from the container without publishing it to your host")
1795
-	cmd.Var(&flDns, []string{"#dns", "-dns"}, "Set custom dns servers")
1796
-	cmd.Var(&flVolumesFrom, []string{"#volumes-from", "-volumes-from"}, "Mount volumes from the specified container(s)")
1797
-	cmd.Var(&flLxcOpts, []string{"#lxc-conf", "-lxc-conf"}, "Add custom lxc options -lxc-conf=\"lxc.cgroup.cpuset.cpus = 0,1\"")
1798
-
1799
-	if err := cmd.Parse(args); err != nil {
1800
-		return nil, nil, cmd, err
1801
-	}
1802
-
1803
-	// Check if the kernel supports memory limit cgroup.
1804
-	if sysInfo != nil && *flMemoryString != "" && !sysInfo.MemoryLimit {
1805
-		*flMemoryString = ""
1806
-	}
1807
-
1808
-	// Validate input params
1809
-	if *flDetach && flAttach.Len() > 0 {
1810
-		return nil, nil, cmd, ErrConflictAttachDetach
1811
-	}
1812
-	if *flWorkingDir != "" && !path.IsAbs(*flWorkingDir) {
1813
-		return nil, nil, cmd, ErrInvalidWorikingDirectory
1814
-	}
1815
-	if *flDetach && *flAutoRemove {
1816
-		return nil, nil, cmd, ErrConflictDetachAutoRemove
1817
-	}
1818
-
1819
-	// If neither -d or -a are set, attach to everything by default
1820
-	if flAttach.Len() == 0 && !*flDetach {
1821
-		if !*flDetach {
1822
-			flAttach.Set("stdout")
1823
-			flAttach.Set("stderr")
1824
-			if *flStdin {
1825
-				flAttach.Set("stdin")
1826
-			}
1827
-		}
1828
-	}
1829
-
1830
-	var flMemory int64
1831
-	if *flMemoryString != "" {
1832
-		parsedMemory, err := utils.RAMInBytes(*flMemoryString)
1833
-		if err != nil {
1834
-			return nil, nil, cmd, err
1835
-		}
1836
-		flMemory = parsedMemory
1837
-	}
1838
-
1839
-	var binds []string
1840
-	// add any bind targets to the list of container volumes
1841
-	for bind := range flVolumes.GetMap() {
1842
-		if arr := strings.Split(bind, ":"); len(arr) > 1 {
1843
-			if arr[0] == "/" {
1844
-				return nil, nil, cmd, fmt.Errorf("Invalid bind mount: source can't be '/'")
1845
-			}
1846
-			dstDir := arr[1]
1847
-			flVolumes.Set(dstDir)
1848
-			binds = append(binds, bind)
1849
-			flVolumes.Delete(bind)
1850
-		} else if bind == "/" {
1851
-			return nil, nil, cmd, fmt.Errorf("Invalid volume: path can't be '/'")
1852
-		}
1853
-	}
1854
-
1855
-	var (
1856
-		parsedArgs = cmd.Args()
1857
-		runCmd     []string
1858
-		entrypoint []string
1859
-		image      string
1860
-	)
1861
-	if len(parsedArgs) >= 1 {
1862
-		image = cmd.Arg(0)
1863
-	}
1864
-	if len(parsedArgs) > 1 {
1865
-		runCmd = parsedArgs[1:]
1866
-	}
1867
-	if *flEntrypoint != "" {
1868
-		entrypoint = []string{*flEntrypoint}
1869
-	}
1870
-
1871
-	lxcConf, err := parseLxcConfOpts(flLxcOpts)
1872
-	if err != nil {
1873
-		return nil, nil, cmd, err
1874
-	}
1875
-
1876
-	var (
1877
-		domainname string
1878
-		hostname   = *flHostname
1879
-		parts      = strings.SplitN(hostname, ".", 2)
1880
-	)
1881
-	if len(parts) > 1 {
1882
-		hostname = parts[0]
1883
-		domainname = parts[1]
1884
-	}
1885
-
1886
-	ports, portBindings, err := nat.ParsePortSpecs(flPublish.GetAll())
1887
-	if err != nil {
1888
-		return nil, nil, cmd, err
1889
-	}
1890
-
1891
-	// Merge in exposed ports to the map of published ports
1892
-	for _, e := range flExpose.GetAll() {
1893
-		if strings.Contains(e, ":") {
1894
-			return nil, nil, cmd, fmt.Errorf("Invalid port format for --expose: %s", e)
1895
-		}
1896
-		p := nat.NewPort(nat.SplitProtoPort(e))
1897
-		if _, exists := ports[p]; !exists {
1898
-			ports[p] = struct{}{}
1899
-		}
1900
-	}
1901
-
1902
-	config := &Config{
1903
-		Hostname:        hostname,
1904
-		Domainname:      domainname,
1905
-		PortSpecs:       nil, // Deprecated
1906
-		ExposedPorts:    ports,
1907
-		User:            *flUser,
1908
-		Tty:             *flTty,
1909
-		NetworkDisabled: !*flNetwork,
1910
-		OpenStdin:       *flStdin,
1911
-		Memory:          flMemory,
1912
-		CpuShares:       *flCpuShares,
1913
-		AttachStdin:     flAttach.Get("stdin"),
1914
-		AttachStdout:    flAttach.Get("stdout"),
1915
-		AttachStderr:    flAttach.Get("stderr"),
1916
-		Env:             flEnv.GetAll(),
1917
-		Cmd:             runCmd,
1918
-		Dns:             flDns.GetAll(),
1919
-		Image:           image,
1920
-		Volumes:         flVolumes.GetMap(),
1921
-		VolumesFrom:     strings.Join(flVolumesFrom.GetAll(), ","),
1922
-		Entrypoint:      entrypoint,
1923
-		WorkingDir:      *flWorkingDir,
1924
-	}
1925
-
1926
-	hostConfig := &HostConfig{
1927
-		Binds:           binds,
1928
-		ContainerIDFile: *flContainerIDFile,
1929
-		LxcConf:         lxcConf,
1930
-		Privileged:      *flPrivileged,
1931
-		PortBindings:    portBindings,
1932
-		Links:           flLinks.GetAll(),
1933
-		PublishAllPorts: *flPublishAll,
1934
-	}
1935
-
1936
-	if sysInfo != nil && flMemory > 0 && !sysInfo.SwapLimit {
1937
-		//fmt.Fprintf(stdout, "WARNING: Your kernel does not support swap limit capabilities. Limitation discarded.\n")
1938
-		config.MemorySwap = -1
1939
-	}
1940
-
1941
-	// When allocating stdin in attached mode, close stdin at client disconnect
1942
-	if config.OpenStdin && config.AttachStdin {
1943
-		config.StdinOnce = true
1944
-	}
1945
-	return config, hostConfig, cmd, nil
1946
-}
1947
-
1948 1746
 func (cli *DockerCli) CmdRun(args ...string) error {
1949
-	config, hostConfig, cmd, err := parseRun(cli.Subcmd("run", "[OPTIONS] IMAGE [COMMAND] [ARG...]", "Run a command in a new container"), args, nil)
1747
+	// FIXME: just use runconfig.Parse already
1748
+	config, hostConfig, cmd, err := runconfig.ParseSubcommand(cli.Subcmd("run", "[OPTIONS] IMAGE [COMMAND] [ARG...]", "Run a command in a new container"), args, nil)
1950 1749
 	if err != nil {
1951 1750
 		return err
1952 1751
 	}
... ...
@@ -1,16 +1,17 @@
1 1
 package docker
2 2
 
3 3
 import (
4
+	"github.com/dotcloud/docker/runconfig"
4 5
 	"strings"
5 6
 	"testing"
6 7
 )
7 8
 
8
-func parse(t *testing.T, args string) (*Config, *HostConfig, error) {
9
-	config, hostConfig, _, err := ParseRun(strings.Split(args+" ubuntu bash", " "), nil)
9
+func parse(t *testing.T, args string) (*runconfig.Config, *runconfig.HostConfig, error) {
10
+	config, hostConfig, _, err := runconfig.Parse(strings.Split(args+" ubuntu bash", " "), nil)
10 11
 	return config, hostConfig, err
11 12
 }
12 13
 
13
-func mustParse(t *testing.T, args string) (*Config, *HostConfig) {
14
+func mustParse(t *testing.T, args string) (*runconfig.Config, *runconfig.HostConfig) {
14 15
 	config, hostConfig, err := parse(t, args)
15 16
 	if err != nil {
16 17
 		t.Fatal(err)
17 18
deleted file mode 100644
... ...
@@ -1,150 +0,0 @@
1
-package docker
2
-
3
-import (
4
-	"github.com/dotcloud/docker/nat"
5
-	"testing"
6
-)
7
-
8
-func TestCompareConfig(t *testing.T) {
9
-	volumes1 := make(map[string]struct{})
10
-	volumes1["/test1"] = struct{}{}
11
-	config1 := Config{
12
-		Dns:         []string{"1.1.1.1", "2.2.2.2"},
13
-		PortSpecs:   []string{"1111:1111", "2222:2222"},
14
-		Env:         []string{"VAR1=1", "VAR2=2"},
15
-		VolumesFrom: "11111111",
16
-		Volumes:     volumes1,
17
-	}
18
-	config2 := Config{
19
-		Dns:         []string{"0.0.0.0", "2.2.2.2"},
20
-		PortSpecs:   []string{"1111:1111", "2222:2222"},
21
-		Env:         []string{"VAR1=1", "VAR2=2"},
22
-		VolumesFrom: "11111111",
23
-		Volumes:     volumes1,
24
-	}
25
-	config3 := Config{
26
-		Dns:         []string{"1.1.1.1", "2.2.2.2"},
27
-		PortSpecs:   []string{"0000:0000", "2222:2222"},
28
-		Env:         []string{"VAR1=1", "VAR2=2"},
29
-		VolumesFrom: "11111111",
30
-		Volumes:     volumes1,
31
-	}
32
-	config4 := Config{
33
-		Dns:         []string{"1.1.1.1", "2.2.2.2"},
34
-		PortSpecs:   []string{"0000:0000", "2222:2222"},
35
-		Env:         []string{"VAR1=1", "VAR2=2"},
36
-		VolumesFrom: "22222222",
37
-		Volumes:     volumes1,
38
-	}
39
-	volumes2 := make(map[string]struct{})
40
-	volumes2["/test2"] = struct{}{}
41
-	config5 := Config{
42
-		Dns:         []string{"1.1.1.1", "2.2.2.2"},
43
-		PortSpecs:   []string{"0000:0000", "2222:2222"},
44
-		Env:         []string{"VAR1=1", "VAR2=2"},
45
-		VolumesFrom: "11111111",
46
-		Volumes:     volumes2,
47
-	}
48
-	if CompareConfig(&config1, &config2) {
49
-		t.Fatalf("CompareConfig should return false, Dns are different")
50
-	}
51
-	if CompareConfig(&config1, &config3) {
52
-		t.Fatalf("CompareConfig should return false, PortSpecs are different")
53
-	}
54
-	if CompareConfig(&config1, &config4) {
55
-		t.Fatalf("CompareConfig should return false, VolumesFrom are different")
56
-	}
57
-	if CompareConfig(&config1, &config5) {
58
-		t.Fatalf("CompareConfig should return false, Volumes are different")
59
-	}
60
-	if !CompareConfig(&config1, &config1) {
61
-		t.Fatalf("CompareConfig should return true")
62
-	}
63
-}
64
-
65
-func TestMergeConfig(t *testing.T) {
66
-	volumesImage := make(map[string]struct{})
67
-	volumesImage["/test1"] = struct{}{}
68
-	volumesImage["/test2"] = struct{}{}
69
-	configImage := &Config{
70
-		Dns:         []string{"1.1.1.1", "2.2.2.2"},
71
-		PortSpecs:   []string{"1111:1111", "2222:2222"},
72
-		Env:         []string{"VAR1=1", "VAR2=2"},
73
-		VolumesFrom: "1111",
74
-		Volumes:     volumesImage,
75
-	}
76
-
77
-	volumesUser := make(map[string]struct{})
78
-	volumesUser["/test3"] = struct{}{}
79
-	configUser := &Config{
80
-		Dns:       []string{"3.3.3.3"},
81
-		PortSpecs: []string{"3333:2222", "3333:3333"},
82
-		Env:       []string{"VAR2=3", "VAR3=3"},
83
-		Volumes:   volumesUser,
84
-	}
85
-
86
-	if err := MergeConfig(configUser, configImage); err != nil {
87
-		t.Error(err)
88
-	}
89
-
90
-	if len(configUser.Dns) != 3 {
91
-		t.Fatalf("Expected 3 dns, 1.1.1.1, 2.2.2.2 and 3.3.3.3, found %d", len(configUser.Dns))
92
-	}
93
-	for _, dns := range configUser.Dns {
94
-		if dns != "1.1.1.1" && dns != "2.2.2.2" && dns != "3.3.3.3" {
95
-			t.Fatalf("Expected 1.1.1.1 or 2.2.2.2 or 3.3.3.3, found %s", dns)
96
-		}
97
-	}
98
-
99
-	if len(configUser.ExposedPorts) != 3 {
100
-		t.Fatalf("Expected 3 ExposedPorts, 1111, 2222 and 3333, found %d", len(configUser.ExposedPorts))
101
-	}
102
-	for portSpecs := range configUser.ExposedPorts {
103
-		if portSpecs.Port() != "1111" && portSpecs.Port() != "2222" && portSpecs.Port() != "3333" {
104
-			t.Fatalf("Expected 1111 or 2222 or 3333, found %s", portSpecs)
105
-		}
106
-	}
107
-	if len(configUser.Env) != 3 {
108
-		t.Fatalf("Expected 3 env var, VAR1=1, VAR2=3 and VAR3=3, found %d", len(configUser.Env))
109
-	}
110
-	for _, env := range configUser.Env {
111
-		if env != "VAR1=1" && env != "VAR2=3" && env != "VAR3=3" {
112
-			t.Fatalf("Expected VAR1=1 or VAR2=3 or VAR3=3, found %s", env)
113
-		}
114
-	}
115
-
116
-	if len(configUser.Volumes) != 3 {
117
-		t.Fatalf("Expected 3 volumes, /test1, /test2 and /test3, found %d", len(configUser.Volumes))
118
-	}
119
-	for v := range configUser.Volumes {
120
-		if v != "/test1" && v != "/test2" && v != "/test3" {
121
-			t.Fatalf("Expected /test1 or /test2 or /test3, found %s", v)
122
-		}
123
-	}
124
-
125
-	if configUser.VolumesFrom != "1111" {
126
-		t.Fatalf("Expected VolumesFrom to be 1111, found %s", configUser.VolumesFrom)
127
-	}
128
-
129
-	ports, _, err := nat.ParsePortSpecs([]string{"0000"})
130
-	if err != nil {
131
-		t.Error(err)
132
-	}
133
-	configImage2 := &Config{
134
-		ExposedPorts: ports,
135
-	}
136
-
137
-	if err := MergeConfig(configUser, configImage2); err != nil {
138
-		t.Error(err)
139
-	}
140
-
141
-	if len(configUser.ExposedPorts) != 4 {
142
-		t.Fatalf("Expected 4 ExposedPorts, 0000, 1111, 2222 and 3333, found %d", len(configUser.ExposedPorts))
143
-	}
144
-	for portSpecs := range configUser.ExposedPorts {
145
-		if portSpecs.Port() != "0000" && portSpecs.Port() != "1111" && portSpecs.Port() != "2222" && portSpecs.Port() != "3333" {
146
-			t.Fatalf("Expected 0000 or 1111 or 2222 or 3333, found %s", portSpecs)
147
-		}
148
-	}
149
-
150
-}
... ...
@@ -11,6 +11,7 @@ import (
11 11
 	"github.com/dotcloud/docker/nat"
12 12
 	"github.com/dotcloud/docker/pkg/mount"
13 13
 	"github.com/dotcloud/docker/pkg/term"
14
+	"github.com/dotcloud/docker/runconfig"
14 15
 	"github.com/dotcloud/docker/utils"
15 16
 	"github.com/kr/pty"
16 17
 	"io"
... ...
@@ -42,7 +43,7 @@ type Container struct {
42 42
 	Path string
43 43
 	Args []string
44 44
 
45
-	Config *Config
45
+	Config *runconfig.Config
46 46
 	State  State
47 47
 	Image  string
48 48
 
... ...
@@ -68,109 +69,11 @@ type Container struct {
68 68
 	// Store rw/ro in a separate structure to preserve reverse-compatibility on-disk.
69 69
 	// Easier than migrating older container configs :)
70 70
 	VolumesRW  map[string]bool
71
-	hostConfig *HostConfig
71
+	hostConfig *runconfig.HostConfig
72 72
 
73 73
 	activeLinks map[string]*Link
74 74
 }
75 75
 
76
-// Note: the Config structure should hold only portable information about the container.
77
-// Here, "portable" means "independent from the host we are running on".
78
-// Non-portable information *should* appear in HostConfig.
79
-type Config struct {
80
-	Hostname        string
81
-	Domainname      string
82
-	User            string
83
-	Memory          int64 // Memory limit (in bytes)
84
-	MemorySwap      int64 // Total memory usage (memory + swap); set `-1' to disable swap
85
-	CpuShares       int64 // CPU shares (relative weight vs. other containers)
86
-	AttachStdin     bool
87
-	AttachStdout    bool
88
-	AttachStderr    bool
89
-	PortSpecs       []string // Deprecated - Can be in the format of 8080/tcp
90
-	ExposedPorts    map[nat.Port]struct{}
91
-	Tty             bool // Attach standard streams to a tty, including stdin if it is not closed.
92
-	OpenStdin       bool // Open stdin
93
-	StdinOnce       bool // If true, close stdin after the 1 attached client disconnects.
94
-	Env             []string
95
-	Cmd             []string
96
-	Dns             []string
97
-	Image           string // Name of the image as it was passed by the operator (eg. could be symbolic)
98
-	Volumes         map[string]struct{}
99
-	VolumesFrom     string
100
-	WorkingDir      string
101
-	Entrypoint      []string
102
-	NetworkDisabled bool
103
-	OnBuild         []string
104
-}
105
-
106
-func ContainerConfigFromJob(job *engine.Job) *Config {
107
-	config := &Config{
108
-		Hostname:        job.Getenv("Hostname"),
109
-		Domainname:      job.Getenv("Domainname"),
110
-		User:            job.Getenv("User"),
111
-		Memory:          job.GetenvInt64("Memory"),
112
-		MemorySwap:      job.GetenvInt64("MemorySwap"),
113
-		CpuShares:       job.GetenvInt64("CpuShares"),
114
-		AttachStdin:     job.GetenvBool("AttachStdin"),
115
-		AttachStdout:    job.GetenvBool("AttachStdout"),
116
-		AttachStderr:    job.GetenvBool("AttachStderr"),
117
-		Tty:             job.GetenvBool("Tty"),
118
-		OpenStdin:       job.GetenvBool("OpenStdin"),
119
-		StdinOnce:       job.GetenvBool("StdinOnce"),
120
-		Image:           job.Getenv("Image"),
121
-		VolumesFrom:     job.Getenv("VolumesFrom"),
122
-		WorkingDir:      job.Getenv("WorkingDir"),
123
-		NetworkDisabled: job.GetenvBool("NetworkDisabled"),
124
-	}
125
-	job.GetenvJson("ExposedPorts", &config.ExposedPorts)
126
-	job.GetenvJson("Volumes", &config.Volumes)
127
-	if PortSpecs := job.GetenvList("PortSpecs"); PortSpecs != nil {
128
-		config.PortSpecs = PortSpecs
129
-	}
130
-	if Env := job.GetenvList("Env"); Env != nil {
131
-		config.Env = Env
132
-	}
133
-	if Cmd := job.GetenvList("Cmd"); Cmd != nil {
134
-		config.Cmd = Cmd
135
-	}
136
-	if Dns := job.GetenvList("Dns"); Dns != nil {
137
-		config.Dns = Dns
138
-	}
139
-	if Entrypoint := job.GetenvList("Entrypoint"); Entrypoint != nil {
140
-		config.Entrypoint = Entrypoint
141
-	}
142
-
143
-	return config
144
-}
145
-
146
-type HostConfig struct {
147
-	Binds           []string
148
-	ContainerIDFile string
149
-	LxcConf         []KeyValuePair
150
-	Privileged      bool
151
-	PortBindings    nat.PortMap
152
-	Links           []string
153
-	PublishAllPorts bool
154
-}
155
-
156
-func ContainerHostConfigFromJob(job *engine.Job) *HostConfig {
157
-	hostConfig := &HostConfig{
158
-		ContainerIDFile: job.Getenv("ContainerIDFile"),
159
-		Privileged:      job.GetenvBool("Privileged"),
160
-		PublishAllPorts: job.GetenvBool("PublishAllPorts"),
161
-	}
162
-	job.GetenvJson("LxcConf", &hostConfig.LxcConf)
163
-	job.GetenvJson("PortBindings", &hostConfig.PortBindings)
164
-	if Binds := job.GetenvList("Binds"); Binds != nil {
165
-		hostConfig.Binds = Binds
166
-	}
167
-	if Links := job.GetenvList("Links"); Links != nil {
168
-		hostConfig.Links = Links
169
-	}
170
-
171
-	return hostConfig
172
-}
173
-
174 76
 type BindMap struct {
175 77
 	SrcPath string
176 78
 	DstPath string
... ...
@@ -178,18 +81,10 @@ type BindMap struct {
178 178
 }
179 179
 
180 180
 var (
181
-	ErrContainerStart           = errors.New("The container failed to start. Unknown error")
182
-	ErrContainerStartTimeout    = errors.New("The container failed to start due to timed out.")
183
-	ErrInvalidWorikingDirectory = errors.New("The working directory is invalid. It needs to be an absolute path.")
184
-	ErrConflictAttachDetach     = errors.New("Conflicting options: -a and -d")
185
-	ErrConflictDetachAutoRemove = errors.New("Conflicting options: -rm and -d")
181
+	ErrContainerStart        = errors.New("The container failed to start. Unknown error")
182
+	ErrContainerStartTimeout = errors.New("The container failed to start due to timed out.")
186 183
 )
187 184
 
188
-type KeyValuePair struct {
189
-	Key   string
190
-	Value string
191
-}
192
-
193 185
 // FIXME: move deprecated port stuff to nat to clean up the core.
194 186
 type PortMapping map[string]string // Deprecated
195 187
 
... ...
@@ -292,7 +187,7 @@ func (container *Container) ToDisk() (err error) {
292 292
 }
293 293
 
294 294
 func (container *Container) readHostConfig() error {
295
-	container.hostConfig = &HostConfig{}
295
+	container.hostConfig = &runconfig.HostConfig{}
296 296
 	// If the hostconfig file does not exist, do not read it.
297 297
 	// (We still have to initialize container.hostConfig,
298 298
 	// but that's OK, since we just did that above.)
... ...
@@ -5,23 +5,6 @@ import (
5 5
 	"testing"
6 6
 )
7 7
 
8
-func TestParseLxcConfOpt(t *testing.T) {
9
-	opts := []string{"lxc.utsname=docker", "lxc.utsname = docker "}
10
-
11
-	for _, o := range opts {
12
-		k, v, err := parseLxcOpt(o)
13
-		if err != nil {
14
-			t.FailNow()
15
-		}
16
-		if k != "lxc.utsname" {
17
-			t.Fail()
18
-		}
19
-		if v != "docker" {
20
-			t.Fail()
21
-		}
22
-	}
23
-}
24
-
25 8
 func TestParseNetworkOptsPrivateOnly(t *testing.T) {
26 9
 	ports, bindings, err := nat.ParsePortSpecs([]string{"192.168.1.100::80"})
27 10
 	if err != nil {
... ...
@@ -5,6 +5,7 @@ import (
5 5
 	"github.com/dotcloud/docker/archive"
6 6
 	"github.com/dotcloud/docker/dockerversion"
7 7
 	"github.com/dotcloud/docker/graphdriver"
8
+	"github.com/dotcloud/docker/runconfig"
8 9
 	"github.com/dotcloud/docker/utils"
9 10
 	"io"
10 11
 	"io/ioutil"
... ...
@@ -126,7 +127,7 @@ func (graph *Graph) Get(name string) (*Image, error) {
126 126
 }
127 127
 
128 128
 // Create creates a new image and registers it in the graph.
129
-func (graph *Graph) Create(layerData archive.Archive, container *Container, comment, author string, config *Config) (*Image, error) {
129
+func (graph *Graph) Create(layerData archive.Archive, container *Container, comment, author string, config *runconfig.Config) (*Image, error) {
130 130
 	img := &Image{
131 131
 		ID:            GenerateID(),
132 132
 		Comment:       comment,
... ...
@@ -7,6 +7,7 @@ import (
7 7
 	"fmt"
8 8
 	"github.com/dotcloud/docker/archive"
9 9
 	"github.com/dotcloud/docker/graphdriver"
10
+	"github.com/dotcloud/docker/runconfig"
10 11
 	"github.com/dotcloud/docker/utils"
11 12
 	"io"
12 13
 	"io/ioutil"
... ...
@@ -18,17 +19,17 @@ import (
18 18
 )
19 19
 
20 20
 type Image struct {
21
-	ID              string    `json:"id"`
22
-	Parent          string    `json:"parent,omitempty"`
23
-	Comment         string    `json:"comment,omitempty"`
24
-	Created         time.Time `json:"created"`
25
-	Container       string    `json:"container,omitempty"`
26
-	ContainerConfig Config    `json:"container_config,omitempty"`
27
-	DockerVersion   string    `json:"docker_version,omitempty"`
28
-	Author          string    `json:"author,omitempty"`
29
-	Config          *Config   `json:"config,omitempty"`
30
-	Architecture    string    `json:"architecture,omitempty"`
31
-	OS              string    `json:"os,omitempty"`
21
+	ID              string            `json:"id"`
22
+	Parent          string            `json:"parent,omitempty"`
23
+	Comment         string            `json:"comment,omitempty"`
24
+	Created         time.Time         `json:"created"`
25
+	Container       string            `json:"container,omitempty"`
26
+	ContainerConfig runconfig.Config  `json:"container_config,omitempty"`
27
+	DockerVersion   string            `json:"docker_version,omitempty"`
28
+	Author          string            `json:"author,omitempty"`
29
+	Config          *runconfig.Config `json:"config,omitempty"`
30
+	Architecture    string            `json:"architecture,omitempty"`
31
+	OS              string            `json:"os,omitempty"`
32 32
 	graph           *Graph
33 33
 	Size            int64
34 34
 }
... ...
@@ -10,6 +10,7 @@ import (
10 10
 	"github.com/dotcloud/docker/api"
11 11
 	"github.com/dotcloud/docker/dockerversion"
12 12
 	"github.com/dotcloud/docker/engine"
13
+	"github.com/dotcloud/docker/runconfig"
13 14
 	"github.com/dotcloud/docker/utils"
14 15
 	"io"
15 16
 	"net"
... ...
@@ -309,7 +310,7 @@ func TestGetContainersJSON(t *testing.T) {
309 309
 	}
310 310
 	beginLen := len(outs.Data)
311 311
 
312
-	containerID := createTestContainer(eng, &docker.Config{
312
+	containerID := createTestContainer(eng, &runconfig.Config{
313 313
 		Image: unitTestImageID,
314 314
 		Cmd:   []string{"echo", "test"},
315 315
 	}, t)
... ...
@@ -346,7 +347,7 @@ func TestGetContainersExport(t *testing.T) {
346 346
 
347 347
 	// Create a container and remove a file
348 348
 	containerID := createTestContainer(eng,
349
-		&docker.Config{
349
+		&runconfig.Config{
350 350
 			Image: unitTestImageID,
351 351
 			Cmd:   []string{"touch", "/test"},
352 352
 		},
... ...
@@ -394,7 +395,7 @@ func TestGetContainersChanges(t *testing.T) {
394 394
 
395 395
 	// Create a container and remove a file
396 396
 	containerID := createTestContainer(eng,
397
-		&docker.Config{
397
+		&runconfig.Config{
398 398
 			Image: unitTestImageID,
399 399
 			Cmd:   []string{"/bin/rm", "/etc/passwd"},
400 400
 		},
... ...
@@ -433,7 +434,7 @@ func TestGetContainersTop(t *testing.T) {
433 433
 	defer mkRuntimeFromEngine(eng, t).Nuke()
434 434
 
435 435
 	containerID := createTestContainer(eng,
436
-		&docker.Config{
436
+		&runconfig.Config{
437 437
 			Image:     unitTestImageID,
438 438
 			Cmd:       []string{"/bin/sh", "-c", "cat"},
439 439
 			OpenStdin: true,
... ...
@@ -510,7 +511,7 @@ func TestGetContainersByName(t *testing.T) {
510 510
 
511 511
 	// Create a container and remove a file
512 512
 	containerID := createTestContainer(eng,
513
-		&docker.Config{
513
+		&runconfig.Config{
514 514
 			Image: unitTestImageID,
515 515
 			Cmd:   []string{"echo", "test"},
516 516
 		},
... ...
@@ -542,7 +543,7 @@ func TestPostCommit(t *testing.T) {
542 542
 
543 543
 	// Create a container and remove a file
544 544
 	containerID := createTestContainer(eng,
545
-		&docker.Config{
545
+		&runconfig.Config{
546 546
 			Image: unitTestImageID,
547 547
 			Cmd:   []string{"touch", "/test"},
548 548
 		},
... ...
@@ -578,7 +579,7 @@ func TestPostContainersCreate(t *testing.T) {
578 578
 	eng := NewTestEngine(t)
579 579
 	defer mkRuntimeFromEngine(eng, t).Nuke()
580 580
 
581
-	configJSON, err := json.Marshal(&docker.Config{
581
+	configJSON, err := json.Marshal(&runconfig.Config{
582 582
 		Image:  unitTestImageID,
583 583
 		Memory: 33554432,
584 584
 		Cmd:    []string{"touch", "/test"},
... ...
@@ -620,7 +621,7 @@ func TestPostContainersKill(t *testing.T) {
620 620
 	defer mkRuntimeFromEngine(eng, t).Nuke()
621 621
 
622 622
 	containerID := createTestContainer(eng,
623
-		&docker.Config{
623
+		&runconfig.Config{
624 624
 			Image:     unitTestImageID,
625 625
 			Cmd:       []string{"/bin/cat"},
626 626
 			OpenStdin: true,
... ...
@@ -659,7 +660,7 @@ func TestPostContainersRestart(t *testing.T) {
659 659
 	defer mkRuntimeFromEngine(eng, t).Nuke()
660 660
 
661 661
 	containerID := createTestContainer(eng,
662
-		&docker.Config{
662
+		&runconfig.Config{
663 663
 			Image:     unitTestImageID,
664 664
 			Cmd:       []string{"/bin/top"},
665 665
 			OpenStdin: true,
... ...
@@ -705,7 +706,7 @@ func TestPostContainersStart(t *testing.T) {
705 705
 
706 706
 	containerID := createTestContainer(
707 707
 		eng,
708
-		&docker.Config{
708
+		&runconfig.Config{
709 709
 			Image:     unitTestImageID,
710 710
 			Cmd:       []string{"/bin/cat"},
711 711
 			OpenStdin: true,
... ...
@@ -713,7 +714,7 @@ func TestPostContainersStart(t *testing.T) {
713 713
 		t,
714 714
 	)
715 715
 
716
-	hostConfigJSON, err := json.Marshal(&docker.HostConfig{})
716
+	hostConfigJSON, err := json.Marshal(&runconfig.HostConfig{})
717 717
 
718 718
 	req, err := http.NewRequest("POST", "/containers/"+containerID+"/start", bytes.NewReader(hostConfigJSON))
719 719
 	if err != nil {
... ...
@@ -758,7 +759,7 @@ func TestRunErrorBindMountRootSource(t *testing.T) {
758 758
 
759 759
 	containerID := createTestContainer(
760 760
 		eng,
761
-		&docker.Config{
761
+		&runconfig.Config{
762 762
 			Image:     unitTestImageID,
763 763
 			Cmd:       []string{"/bin/cat"},
764 764
 			OpenStdin: true,
... ...
@@ -766,7 +767,7 @@ func TestRunErrorBindMountRootSource(t *testing.T) {
766 766
 		t,
767 767
 	)
768 768
 
769
-	hostConfigJSON, err := json.Marshal(&docker.HostConfig{
769
+	hostConfigJSON, err := json.Marshal(&runconfig.HostConfig{
770 770
 		Binds: []string{"/:/tmp"},
771 771
 	})
772 772
 
... ...
@@ -792,7 +793,7 @@ func TestPostContainersStop(t *testing.T) {
792 792
 	defer mkRuntimeFromEngine(eng, t).Nuke()
793 793
 
794 794
 	containerID := createTestContainer(eng,
795
-		&docker.Config{
795
+		&runconfig.Config{
796 796
 			Image:     unitTestImageID,
797 797
 			Cmd:       []string{"/bin/top"},
798 798
 			OpenStdin: true,
... ...
@@ -832,7 +833,7 @@ func TestPostContainersWait(t *testing.T) {
832 832
 	defer mkRuntimeFromEngine(eng, t).Nuke()
833 833
 
834 834
 	containerID := createTestContainer(eng,
835
-		&docker.Config{
835
+		&runconfig.Config{
836 836
 			Image:     unitTestImageID,
837 837
 			Cmd:       []string{"/bin/sleep", "1"},
838 838
 			OpenStdin: true,
... ...
@@ -870,7 +871,7 @@ func TestPostContainersAttach(t *testing.T) {
870 870
 	defer mkRuntimeFromEngine(eng, t).Nuke()
871 871
 
872 872
 	containerID := createTestContainer(eng,
873
-		&docker.Config{
873
+		&runconfig.Config{
874 874
 			Image:     unitTestImageID,
875 875
 			Cmd:       []string{"/bin/cat"},
876 876
 			OpenStdin: true,
... ...
@@ -948,7 +949,7 @@ func TestPostContainersAttachStderr(t *testing.T) {
948 948
 	defer mkRuntimeFromEngine(eng, t).Nuke()
949 949
 
950 950
 	containerID := createTestContainer(eng,
951
-		&docker.Config{
951
+		&runconfig.Config{
952 952
 			Image:     unitTestImageID,
953 953
 			Cmd:       []string{"/bin/sh", "-c", "/bin/cat >&2"},
954 954
 			OpenStdin: true,
... ...
@@ -1029,7 +1030,7 @@ func TestDeleteContainers(t *testing.T) {
1029 1029
 	defer mkRuntimeFromEngine(eng, t).Nuke()
1030 1030
 
1031 1031
 	containerID := createTestContainer(eng,
1032
-		&docker.Config{
1032
+		&runconfig.Config{
1033 1033
 			Image: unitTestImageID,
1034 1034
 			Cmd:   []string{"touch", "/test"},
1035 1035
 		},
... ...
@@ -1164,7 +1165,7 @@ func TestPostContainersCopy(t *testing.T) {
1164 1164
 
1165 1165
 	// Create a container and remove a file
1166 1166
 	containerID := createTestContainer(eng,
1167
-		&docker.Config{
1167
+		&runconfig.Config{
1168 1168
 			Image: unitTestImageID,
1169 1169
 			Cmd:   []string{"touch", "/test.txt"},
1170 1170
 		},
... ...
@@ -3,7 +3,7 @@ package docker
3 3
 import (
4 4
 	"bufio"
5 5
 	"fmt"
6
-	"github.com/dotcloud/docker"
6
+	"github.com/dotcloud/docker/runconfig"
7 7
 	"github.com/dotcloud/docker/utils"
8 8
 	"io"
9 9
 	"io/ioutil"
... ...
@@ -20,7 +20,7 @@ func TestIDFormat(t *testing.T) {
20 20
 	runtime := mkRuntime(t)
21 21
 	defer nuke(runtime)
22 22
 	container1, _, err := runtime.Create(
23
-		&docker.Config{
23
+		&runconfig.Config{
24 24
 			Image: GetTestImage(runtime).ID,
25 25
 			Cmd:   []string{"/bin/sh", "-c", "echo hello world"},
26 26
 		},
... ...
@@ -234,7 +234,7 @@ func TestCommitAutoRun(t *testing.T) {
234 234
 		t.Errorf("Container shouldn't be running")
235 235
 	}
236 236
 
237
-	img, err := runtime.Commit(container1, "", "", "unit test commited image", "", &docker.Config{Cmd: []string{"cat", "/world"}})
237
+	img, err := runtime.Commit(container1, "", "", "unit test commited image", "", &runconfig.Config{Cmd: []string{"cat", "/world"}})
238 238
 	if err != nil {
239 239
 		t.Error(err)
240 240
 	}
... ...
@@ -415,7 +415,7 @@ func TestOutput(t *testing.T) {
415 415
 	runtime := mkRuntime(t)
416 416
 	defer nuke(runtime)
417 417
 	container, _, err := runtime.Create(
418
-		&docker.Config{
418
+		&runconfig.Config{
419 419
 			Image: GetTestImage(runtime).ID,
420 420
 			Cmd:   []string{"echo", "-n", "foobar"},
421 421
 		},
... ...
@@ -438,7 +438,7 @@ func TestContainerNetwork(t *testing.T) {
438 438
 	runtime := mkRuntime(t)
439 439
 	defer nuke(runtime)
440 440
 	container, _, err := runtime.Create(
441
-		&docker.Config{
441
+		&runconfig.Config{
442 442
 			Image: GetTestImage(runtime).ID,
443 443
 			Cmd:   []string{"ping", "-c", "1", "127.0.0.1"},
444 444
 		},
... ...
@@ -460,7 +460,7 @@ func TestKillDifferentUser(t *testing.T) {
460 460
 	runtime := mkRuntime(t)
461 461
 	defer nuke(runtime)
462 462
 
463
-	container, _, err := runtime.Create(&docker.Config{
463
+	container, _, err := runtime.Create(&runconfig.Config{
464 464
 		Image:     GetTestImage(runtime).ID,
465 465
 		Cmd:       []string{"cat"},
466 466
 		OpenStdin: true,
... ...
@@ -520,7 +520,7 @@ func TestCreateVolume(t *testing.T) {
520 520
 	runtime := mkRuntimeFromEngine(eng, t)
521 521
 	defer nuke(runtime)
522 522
 
523
-	config, hc, _, err := docker.ParseRun([]string{"-v", "/var/lib/data", unitTestImageID, "echo", "hello", "world"}, nil)
523
+	config, hc, _, err := runconfig.Parse([]string{"-v", "/var/lib/data", unitTestImageID, "echo", "hello", "world"}, nil)
524 524
 	if err != nil {
525 525
 		t.Fatal(err)
526 526
 	}
... ...
@@ -552,7 +552,7 @@ func TestCreateVolume(t *testing.T) {
552 552
 func TestKill(t *testing.T) {
553 553
 	runtime := mkRuntime(t)
554 554
 	defer nuke(runtime)
555
-	container, _, err := runtime.Create(&docker.Config{
555
+	container, _, err := runtime.Create(&runconfig.Config{
556 556
 		Image: GetTestImage(runtime).ID,
557 557
 		Cmd:   []string{"sleep", "2"},
558 558
 	},
... ...
@@ -596,7 +596,7 @@ func TestExitCode(t *testing.T) {
596 596
 	runtime := mkRuntime(t)
597 597
 	defer nuke(runtime)
598 598
 
599
-	trueContainer, _, err := runtime.Create(&docker.Config{
599
+	trueContainer, _, err := runtime.Create(&runconfig.Config{
600 600
 		Image: GetTestImage(runtime).ID,
601 601
 		Cmd:   []string{"/bin/true"},
602 602
 	}, "")
... ...
@@ -611,7 +611,7 @@ func TestExitCode(t *testing.T) {
611 611
 		t.Fatalf("Unexpected exit code %d (expected 0)", code)
612 612
 	}
613 613
 
614
-	falseContainer, _, err := runtime.Create(&docker.Config{
614
+	falseContainer, _, err := runtime.Create(&runconfig.Config{
615 615
 		Image: GetTestImage(runtime).ID,
616 616
 		Cmd:   []string{"/bin/false"},
617 617
 	}, "")
... ...
@@ -630,7 +630,7 @@ func TestExitCode(t *testing.T) {
630 630
 func TestRestart(t *testing.T) {
631 631
 	runtime := mkRuntime(t)
632 632
 	defer nuke(runtime)
633
-	container, _, err := runtime.Create(&docker.Config{
633
+	container, _, err := runtime.Create(&runconfig.Config{
634 634
 		Image: GetTestImage(runtime).ID,
635 635
 		Cmd:   []string{"echo", "-n", "foobar"},
636 636
 	},
... ...
@@ -661,7 +661,7 @@ func TestRestart(t *testing.T) {
661 661
 func TestRestartStdin(t *testing.T) {
662 662
 	runtime := mkRuntime(t)
663 663
 	defer nuke(runtime)
664
-	container, _, err := runtime.Create(&docker.Config{
664
+	container, _, err := runtime.Create(&runconfig.Config{
665 665
 		Image: GetTestImage(runtime).ID,
666 666
 		Cmd:   []string{"cat"},
667 667
 
... ...
@@ -739,7 +739,7 @@ func TestUser(t *testing.T) {
739 739
 	defer nuke(runtime)
740 740
 
741 741
 	// Default user must be root
742
-	container, _, err := runtime.Create(&docker.Config{
742
+	container, _, err := runtime.Create(&runconfig.Config{
743 743
 		Image: GetTestImage(runtime).ID,
744 744
 		Cmd:   []string{"id"},
745 745
 	},
... ...
@@ -758,7 +758,7 @@ func TestUser(t *testing.T) {
758 758
 	}
759 759
 
760 760
 	// Set a username
761
-	container, _, err = runtime.Create(&docker.Config{
761
+	container, _, err = runtime.Create(&runconfig.Config{
762 762
 		Image: GetTestImage(runtime).ID,
763 763
 		Cmd:   []string{"id"},
764 764
 
... ...
@@ -779,7 +779,7 @@ func TestUser(t *testing.T) {
779 779
 	}
780 780
 
781 781
 	// Set a UID
782
-	container, _, err = runtime.Create(&docker.Config{
782
+	container, _, err = runtime.Create(&runconfig.Config{
783 783
 		Image: GetTestImage(runtime).ID,
784 784
 		Cmd:   []string{"id"},
785 785
 
... ...
@@ -800,7 +800,7 @@ func TestUser(t *testing.T) {
800 800
 	}
801 801
 
802 802
 	// Set a different user by uid
803
-	container, _, err = runtime.Create(&docker.Config{
803
+	container, _, err = runtime.Create(&runconfig.Config{
804 804
 		Image: GetTestImage(runtime).ID,
805 805
 		Cmd:   []string{"id"},
806 806
 
... ...
@@ -823,7 +823,7 @@ func TestUser(t *testing.T) {
823 823
 	}
824 824
 
825 825
 	// Set a different user by username
826
-	container, _, err = runtime.Create(&docker.Config{
826
+	container, _, err = runtime.Create(&runconfig.Config{
827 827
 		Image: GetTestImage(runtime).ID,
828 828
 		Cmd:   []string{"id"},
829 829
 
... ...
@@ -844,7 +844,7 @@ func TestUser(t *testing.T) {
844 844
 	}
845 845
 
846 846
 	// Test an wrong username
847
-	container, _, err = runtime.Create(&docker.Config{
847
+	container, _, err = runtime.Create(&runconfig.Config{
848 848
 		Image: GetTestImage(runtime).ID,
849 849
 		Cmd:   []string{"id"},
850 850
 
... ...
@@ -866,7 +866,7 @@ func TestMultipleContainers(t *testing.T) {
866 866
 	runtime := mkRuntime(t)
867 867
 	defer nuke(runtime)
868 868
 
869
-	container1, _, err := runtime.Create(&docker.Config{
869
+	container1, _, err := runtime.Create(&runconfig.Config{
870 870
 		Image: GetTestImage(runtime).ID,
871 871
 		Cmd:   []string{"sleep", "2"},
872 872
 	},
... ...
@@ -877,7 +877,7 @@ func TestMultipleContainers(t *testing.T) {
877 877
 	}
878 878
 	defer runtime.Destroy(container1)
879 879
 
880
-	container2, _, err := runtime.Create(&docker.Config{
880
+	container2, _, err := runtime.Create(&runconfig.Config{
881 881
 		Image: GetTestImage(runtime).ID,
882 882
 		Cmd:   []string{"sleep", "2"},
883 883
 	},
... ...
@@ -921,7 +921,7 @@ func TestMultipleContainers(t *testing.T) {
921 921
 func TestStdin(t *testing.T) {
922 922
 	runtime := mkRuntime(t)
923 923
 	defer nuke(runtime)
924
-	container, _, err := runtime.Create(&docker.Config{
924
+	container, _, err := runtime.Create(&runconfig.Config{
925 925
 		Image: GetTestImage(runtime).ID,
926 926
 		Cmd:   []string{"cat"},
927 927
 
... ...
@@ -966,7 +966,7 @@ func TestStdin(t *testing.T) {
966 966
 func TestTty(t *testing.T) {
967 967
 	runtime := mkRuntime(t)
968 968
 	defer nuke(runtime)
969
-	container, _, err := runtime.Create(&docker.Config{
969
+	container, _, err := runtime.Create(&runconfig.Config{
970 970
 		Image: GetTestImage(runtime).ID,
971 971
 		Cmd:   []string{"cat"},
972 972
 
... ...
@@ -1013,7 +1013,7 @@ func TestEnv(t *testing.T) {
1013 1013
 	os.Setenv("TRICKY", "tri\ncky\n")
1014 1014
 	runtime := mkRuntime(t)
1015 1015
 	defer nuke(runtime)
1016
-	config, _, _, err := docker.ParseRun([]string{"-e=FALSE=true", "-e=TRUE", "-e=TRICKY", GetTestImage(runtime).ID, "env"}, nil)
1016
+	config, _, _, err := runconfig.Parse([]string{"-e=FALSE=true", "-e=TRUE", "-e=TRICKY", GetTestImage(runtime).ID, "env"}, nil)
1017 1017
 	if err != nil {
1018 1018
 		t.Fatal(err)
1019 1019
 	}
... ...
@@ -1067,7 +1067,7 @@ func TestEntrypoint(t *testing.T) {
1067 1067
 	runtime := mkRuntime(t)
1068 1068
 	defer nuke(runtime)
1069 1069
 	container, _, err := runtime.Create(
1070
-		&docker.Config{
1070
+		&runconfig.Config{
1071 1071
 			Image:      GetTestImage(runtime).ID,
1072 1072
 			Entrypoint: []string{"/bin/echo"},
1073 1073
 			Cmd:        []string{"-n", "foobar"},
... ...
@@ -1091,7 +1091,7 @@ func TestEntrypointNoCmd(t *testing.T) {
1091 1091
 	runtime := mkRuntime(t)
1092 1092
 	defer nuke(runtime)
1093 1093
 	container, _, err := runtime.Create(
1094
-		&docker.Config{
1094
+		&runconfig.Config{
1095 1095
 			Image:      GetTestImage(runtime).ID,
1096 1096
 			Entrypoint: []string{"/bin/echo", "foobar"},
1097 1097
 		},
... ...
@@ -1114,7 +1114,7 @@ func BenchmarkRunSequencial(b *testing.B) {
1114 1114
 	runtime := mkRuntime(b)
1115 1115
 	defer nuke(runtime)
1116 1116
 	for i := 0; i < b.N; i++ {
1117
-		container, _, err := runtime.Create(&docker.Config{
1117
+		container, _, err := runtime.Create(&runconfig.Config{
1118 1118
 			Image: GetTestImage(runtime).ID,
1119 1119
 			Cmd:   []string{"echo", "-n", "foo"},
1120 1120
 		},
... ...
@@ -1147,7 +1147,7 @@ func BenchmarkRunParallel(b *testing.B) {
1147 1147
 		complete := make(chan error)
1148 1148
 		tasks = append(tasks, complete)
1149 1149
 		go func(i int, complete chan error) {
1150
-			container, _, err := runtime.Create(&docker.Config{
1150
+			container, _, err := runtime.Create(&runconfig.Config{
1151 1151
 				Image: GetTestImage(runtime).ID,
1152 1152
 				Cmd:   []string{"echo", "-n", "foo"},
1153 1153
 			},
... ...
@@ -1301,7 +1301,7 @@ func TestFromVolumesInReadonlyMode(t *testing.T) {
1301 1301
 	runtime := mkRuntime(t)
1302 1302
 	defer nuke(runtime)
1303 1303
 	container, _, err := runtime.Create(
1304
-		&docker.Config{
1304
+		&runconfig.Config{
1305 1305
 			Image:   GetTestImage(runtime).ID,
1306 1306
 			Cmd:     []string{"/bin/echo", "-n", "foobar"},
1307 1307
 			Volumes: map[string]struct{}{"/test": {}},
... ...
@@ -1321,7 +1321,7 @@ func TestFromVolumesInReadonlyMode(t *testing.T) {
1321 1321
 	}
1322 1322
 
1323 1323
 	container2, _, err := runtime.Create(
1324
-		&docker.Config{
1324
+		&runconfig.Config{
1325 1325
 			Image:       GetTestImage(runtime).ID,
1326 1326
 			Cmd:         []string{"/bin/echo", "-n", "foobar"},
1327 1327
 			VolumesFrom: container.ID + ":ro",
... ...
@@ -1362,7 +1362,7 @@ func TestVolumesFromReadonlyMount(t *testing.T) {
1362 1362
 	runtime := mkRuntime(t)
1363 1363
 	defer nuke(runtime)
1364 1364
 	container, _, err := runtime.Create(
1365
-		&docker.Config{
1365
+		&runconfig.Config{
1366 1366
 			Image:   GetTestImage(runtime).ID,
1367 1367
 			Cmd:     []string{"/bin/echo", "-n", "foobar"},
1368 1368
 			Volumes: map[string]struct{}{"/test": {}},
... ...
@@ -1382,7 +1382,7 @@ func TestVolumesFromReadonlyMount(t *testing.T) {
1382 1382
 	}
1383 1383
 
1384 1384
 	container2, _, err := runtime.Create(
1385
-		&docker.Config{
1385
+		&runconfig.Config{
1386 1386
 			Image:       GetTestImage(runtime).ID,
1387 1387
 			Cmd:         []string{"/bin/echo", "-n", "foobar"},
1388 1388
 			VolumesFrom: container.ID,
... ...
@@ -1418,7 +1418,7 @@ func TestRestartWithVolumes(t *testing.T) {
1418 1418
 	runtime := mkRuntime(t)
1419 1419
 	defer nuke(runtime)
1420 1420
 
1421
-	container, _, err := runtime.Create(&docker.Config{
1421
+	container, _, err := runtime.Create(&runconfig.Config{
1422 1422
 		Image:   GetTestImage(runtime).ID,
1423 1423
 		Cmd:     []string{"echo", "-n", "foobar"},
1424 1424
 		Volumes: map[string]struct{}{"/test": {}},
... ...
@@ -1462,7 +1462,7 @@ func TestVolumesFromWithVolumes(t *testing.T) {
1462 1462
 	runtime := mkRuntime(t)
1463 1463
 	defer nuke(runtime)
1464 1464
 
1465
-	container, _, err := runtime.Create(&docker.Config{
1465
+	container, _, err := runtime.Create(&runconfig.Config{
1466 1466
 		Image:   GetTestImage(runtime).ID,
1467 1467
 		Cmd:     []string{"sh", "-c", "echo -n bar > /test/foo"},
1468 1468
 		Volumes: map[string]struct{}{"/test": {}},
... ...
@@ -1491,7 +1491,7 @@ func TestVolumesFromWithVolumes(t *testing.T) {
1491 1491
 	}
1492 1492
 
1493 1493
 	container2, _, err := runtime.Create(
1494
-		&docker.Config{
1494
+		&runconfig.Config{
1495 1495
 			Image:       GetTestImage(runtime).ID,
1496 1496
 			Cmd:         []string{"cat", "/test/foo"},
1497 1497
 			VolumesFrom: container.ID,
... ...
@@ -1529,7 +1529,7 @@ func TestOnlyLoopbackExistsWhenUsingDisableNetworkOption(t *testing.T) {
1529 1529
 	runtime := mkRuntimeFromEngine(eng, t)
1530 1530
 	defer nuke(runtime)
1531 1531
 
1532
-	config, hc, _, err := docker.ParseRun([]string{"-n=false", GetTestImage(runtime).ID, "ip", "addr", "show"}, nil)
1532
+	config, hc, _, err := runconfig.Parse([]string{"-n=false", GetTestImage(runtime).ID, "ip", "addr", "show"}, nil)
1533 1533
 	if err != nil {
1534 1534
 		t.Fatal(err)
1535 1535
 	}
... ...
@@ -1617,7 +1617,7 @@ func TestMultipleVolumesFrom(t *testing.T) {
1617 1617
 	runtime := mkRuntime(t)
1618 1618
 	defer nuke(runtime)
1619 1619
 
1620
-	container, _, err := runtime.Create(&docker.Config{
1620
+	container, _, err := runtime.Create(&runconfig.Config{
1621 1621
 		Image:   GetTestImage(runtime).ID,
1622 1622
 		Cmd:     []string{"sh", "-c", "echo -n bar > /test/foo"},
1623 1623
 		Volumes: map[string]struct{}{"/test": {}},
... ...
@@ -1646,7 +1646,7 @@ func TestMultipleVolumesFrom(t *testing.T) {
1646 1646
 	}
1647 1647
 
1648 1648
 	container2, _, err := runtime.Create(
1649
-		&docker.Config{
1649
+		&runconfig.Config{
1650 1650
 			Image:   GetTestImage(runtime).ID,
1651 1651
 			Cmd:     []string{"sh", "-c", "echo -n bar > /other/foo"},
1652 1652
 			Volumes: map[string]struct{}{"/other": {}},
... ...
@@ -1668,7 +1668,7 @@ func TestMultipleVolumesFrom(t *testing.T) {
1668 1668
 	}
1669 1669
 
1670 1670
 	container3, _, err := runtime.Create(
1671
-		&docker.Config{
1671
+		&runconfig.Config{
1672 1672
 			Image:       GetTestImage(runtime).ID,
1673 1673
 			Cmd:         []string{"/bin/echo", "-n", "foobar"},
1674 1674
 			VolumesFrom: strings.Join([]string{container.ID, container2.ID}, ","),
... ...
@@ -1696,7 +1696,7 @@ func TestRestartGhost(t *testing.T) {
1696 1696
 	defer nuke(runtime)
1697 1697
 
1698 1698
 	container, _, err := runtime.Create(
1699
-		&docker.Config{
1699
+		&runconfig.Config{
1700 1700
 			Image:   GetTestImage(runtime).ID,
1701 1701
 			Cmd:     []string{"sh", "-c", "echo -n bar > /test/foo"},
1702 1702
 			Volumes: map[string]struct{}{"/test": {}},
... ...
@@ -6,6 +6,7 @@ import (
6 6
 	"github.com/dotcloud/docker"
7 7
 	"github.com/dotcloud/docker/engine"
8 8
 	"github.com/dotcloud/docker/nat"
9
+	"github.com/dotcloud/docker/runconfig"
9 10
 	"github.com/dotcloud/docker/sysinit"
10 11
 	"github.com/dotcloud/docker/utils"
11 12
 	"io"
... ...
@@ -200,7 +201,7 @@ func TestRuntimeCreate(t *testing.T) {
200 200
 		t.Errorf("Expected 0 containers, %v found", len(runtime.List()))
201 201
 	}
202 202
 
203
-	container, _, err := runtime.Create(&docker.Config{
203
+	container, _, err := runtime.Create(&runconfig.Config{
204 204
 		Image: GetTestImage(runtime).ID,
205 205
 		Cmd:   []string{"ls", "-al"},
206 206
 	},
... ...
@@ -243,23 +244,23 @@ func TestRuntimeCreate(t *testing.T) {
243 243
 
244 244
 	// Test that conflict error displays correct details
245 245
 	testContainer, _, _ := runtime.Create(
246
-		&docker.Config{
246
+		&runconfig.Config{
247 247
 			Image: GetTestImage(runtime).ID,
248 248
 			Cmd:   []string{"ls", "-al"},
249 249
 		},
250 250
 		"conflictname",
251 251
 	)
252
-	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)) {
252
+	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)) {
253 253
 		t.Fatalf("Name conflict error doesn't include the correct short id. Message was: %s", err.Error())
254 254
 	}
255 255
 
256 256
 	// Make sure create with bad parameters returns an error
257
-	if _, _, err = runtime.Create(&docker.Config{Image: GetTestImage(runtime).ID}, ""); err == nil {
257
+	if _, _, err = runtime.Create(&runconfig.Config{Image: GetTestImage(runtime).ID}, ""); err == nil {
258 258
 		t.Fatal("Builder.Create should throw an error when Cmd is missing")
259 259
 	}
260 260
 
261 261
 	if _, _, err := runtime.Create(
262
-		&docker.Config{
262
+		&runconfig.Config{
263 263
 			Image: GetTestImage(runtime).ID,
264 264
 			Cmd:   []string{},
265 265
 		},
... ...
@@ -268,7 +269,7 @@ func TestRuntimeCreate(t *testing.T) {
268 268
 		t.Fatal("Builder.Create should throw an error when Cmd is empty")
269 269
 	}
270 270
 
271
-	config := &docker.Config{
271
+	config := &runconfig.Config{
272 272
 		Image:     GetTestImage(runtime).ID,
273 273
 		Cmd:       []string{"/bin/ls"},
274 274
 		PortSpecs: []string{"80"},
... ...
@@ -281,7 +282,7 @@ func TestRuntimeCreate(t *testing.T) {
281 281
 	}
282 282
 
283 283
 	// test expose 80:8000
284
-	container, warnings, err := runtime.Create(&docker.Config{
284
+	container, warnings, err := runtime.Create(&runconfig.Config{
285 285
 		Image:     GetTestImage(runtime).ID,
286 286
 		Cmd:       []string{"ls", "-al"},
287 287
 		PortSpecs: []string{"80:8000"},
... ...
@@ -300,7 +301,7 @@ func TestDestroy(t *testing.T) {
300 300
 	runtime := mkRuntime(t)
301 301
 	defer nuke(runtime)
302 302
 
303
-	container, _, err := runtime.Create(&docker.Config{
303
+	container, _, err := runtime.Create(&runconfig.Config{
304 304
 		Image: GetTestImage(runtime).ID,
305 305
 		Cmd:   []string{"ls", "-al"},
306 306
 	}, "")
... ...
@@ -712,7 +713,7 @@ func TestDefaultContainerName(t *testing.T) {
712 712
 	runtime := mkRuntimeFromEngine(eng, t)
713 713
 	defer nuke(runtime)
714 714
 
715
-	config, _, _, err := docker.ParseRun([]string{unitTestImageID, "echo test"}, nil)
715
+	config, _, _, err := runconfig.Parse([]string{unitTestImageID, "echo test"}, nil)
716 716
 	if err != nil {
717 717
 		t.Fatal(err)
718 718
 	}
... ...
@@ -736,7 +737,7 @@ func TestRandomContainerName(t *testing.T) {
736 736
 	runtime := mkRuntimeFromEngine(eng, t)
737 737
 	defer nuke(runtime)
738 738
 
739
-	config, _, _, err := docker.ParseRun([]string{GetTestImage(runtime).ID, "echo test"}, nil)
739
+	config, _, _, err := runconfig.Parse([]string{GetTestImage(runtime).ID, "echo test"}, nil)
740 740
 	if err != nil {
741 741
 		t.Fatal(err)
742 742
 	}
... ...
@@ -767,7 +768,7 @@ func TestContainerNameValidation(t *testing.T) {
767 767
 		{"abc-123_AAA.1", true},
768 768
 		{"\000asdf", false},
769 769
 	} {
770
-		config, _, _, err := docker.ParseRun([]string{unitTestImageID, "echo test"}, nil)
770
+		config, _, _, err := runconfig.Parse([]string{unitTestImageID, "echo test"}, nil)
771 771
 		if err != nil {
772 772
 			if !test.Valid {
773 773
 				continue
... ...
@@ -808,7 +809,7 @@ func TestLinkChildContainer(t *testing.T) {
808 808
 	runtime := mkRuntimeFromEngine(eng, t)
809 809
 	defer nuke(runtime)
810 810
 
811
-	config, _, _, err := docker.ParseRun([]string{unitTestImageID, "echo test"}, nil)
811
+	config, _, _, err := runconfig.Parse([]string{unitTestImageID, "echo test"}, nil)
812 812
 	if err != nil {
813 813
 		t.Fatal(err)
814 814
 	}
... ...
@@ -824,7 +825,7 @@ func TestLinkChildContainer(t *testing.T) {
824 824
 		t.Fatalf("Expect webapp id to match container id: %s != %s", webapp.ID, container.ID)
825 825
 	}
826 826
 
827
-	config, _, _, err = docker.ParseRun([]string{GetTestImage(runtime).ID, "echo test"}, nil)
827
+	config, _, _, err = runconfig.Parse([]string{GetTestImage(runtime).ID, "echo test"}, nil)
828 828
 	if err != nil {
829 829
 		t.Fatal(err)
830 830
 	}
... ...
@@ -850,7 +851,7 @@ func TestGetAllChildren(t *testing.T) {
850 850
 	runtime := mkRuntimeFromEngine(eng, t)
851 851
 	defer nuke(runtime)
852 852
 
853
-	config, _, _, err := docker.ParseRun([]string{unitTestImageID, "echo test"}, nil)
853
+	config, _, _, err := runconfig.Parse([]string{unitTestImageID, "echo test"}, nil)
854 854
 	if err != nil {
855 855
 		t.Fatal(err)
856 856
 	}
... ...
@@ -866,7 +867,7 @@ func TestGetAllChildren(t *testing.T) {
866 866
 		t.Fatalf("Expect webapp id to match container id: %s != %s", webapp.ID, container.ID)
867 867
 	}
868 868
 
869
-	config, _, _, err = docker.ParseRun([]string{unitTestImageID, "echo test"}, nil)
869
+	config, _, _, err = runconfig.Parse([]string{unitTestImageID, "echo test"}, nil)
870 870
 	if err != nil {
871 871
 		t.Fatal(err)
872 872
 	}
... ...
@@ -903,7 +904,7 @@ func TestDestroyWithInitLayer(t *testing.T) {
903 903
 	runtime := mkRuntime(t)
904 904
 	defer nuke(runtime)
905 905
 
906
-	container, _, err := runtime.Create(&docker.Config{
906
+	container, _, err := runtime.Create(&runconfig.Config{
907 907
 		Image: GetTestImage(runtime).ID,
908 908
 		Cmd:   []string{"ls", "-al"},
909 909
 	}, "")
... ...
@@ -3,6 +3,7 @@ package docker
3 3
 import (
4 4
 	"github.com/dotcloud/docker"
5 5
 	"github.com/dotcloud/docker/engine"
6
+	"github.com/dotcloud/docker/runconfig"
6 7
 	"strings"
7 8
 	"testing"
8 9
 	"time"
... ...
@@ -71,7 +72,7 @@ func TestCreateRm(t *testing.T) {
71 71
 	eng := NewTestEngine(t)
72 72
 	defer mkRuntimeFromEngine(eng, t).Nuke()
73 73
 
74
-	config, _, _, err := docker.ParseRun([]string{unitTestImageID, "echo test"}, nil)
74
+	config, _, _, err := runconfig.Parse([]string{unitTestImageID, "echo test"}, nil)
75 75
 	if err != nil {
76 76
 		t.Fatal(err)
77 77
 	}
... ...
@@ -118,7 +119,7 @@ func TestCreateNumberHostname(t *testing.T) {
118 118
 	eng := NewTestEngine(t)
119 119
 	defer mkRuntimeFromEngine(eng, t).Nuke()
120 120
 
121
-	config, _, _, err := docker.ParseRun([]string{"-h", "web.0", unitTestImageID, "echo test"}, nil)
121
+	config, _, _, err := runconfig.Parse([]string{"-h", "web.0", unitTestImageID, "echo test"}, nil)
122 122
 	if err != nil {
123 123
 		t.Fatal(err)
124 124
 	}
... ...
@@ -130,7 +131,7 @@ func TestCreateNumberUsername(t *testing.T) {
130 130
 	eng := NewTestEngine(t)
131 131
 	defer mkRuntimeFromEngine(eng, t).Nuke()
132 132
 
133
-	config, _, _, err := docker.ParseRun([]string{"-u", "1002", unitTestImageID, "echo test"}, nil)
133
+	config, _, _, err := runconfig.Parse([]string{"-u", "1002", unitTestImageID, "echo test"}, nil)
134 134
 	if err != nil {
135 135
 		t.Fatal(err)
136 136
 	}
... ...
@@ -142,7 +143,7 @@ func TestCreateRmVolumes(t *testing.T) {
142 142
 	eng := NewTestEngine(t)
143 143
 	defer mkRuntimeFromEngine(eng, t).Nuke()
144 144
 
145
-	config, hostConfig, _, err := docker.ParseRun([]string{"-v", "/srv", unitTestImageID, "echo", "test"}, nil)
145
+	config, hostConfig, _, err := runconfig.Parse([]string{"-v", "/srv", unitTestImageID, "echo", "test"}, nil)
146 146
 	if err != nil {
147 147
 		t.Fatal(err)
148 148
 	}
... ...
@@ -202,7 +203,7 @@ func TestCommit(t *testing.T) {
202 202
 	eng := NewTestEngine(t)
203 203
 	defer mkRuntimeFromEngine(eng, t).Nuke()
204 204
 
205
-	config, _, _, err := docker.ParseRun([]string{unitTestImageID, "/bin/cat"}, nil)
205
+	config, _, _, err := runconfig.Parse([]string{unitTestImageID, "/bin/cat"}, nil)
206 206
 	if err != nil {
207 207
 		t.Fatal(err)
208 208
 	}
... ...
@@ -224,7 +225,7 @@ func TestRestartKillWait(t *testing.T) {
224 224
 	runtime := mkRuntimeFromEngine(eng, t)
225 225
 	defer runtime.Nuke()
226 226
 
227
-	config, hostConfig, _, err := docker.ParseRun([]string{"-i", unitTestImageID, "/bin/cat"}, nil)
227
+	config, hostConfig, _, err := runconfig.Parse([]string{"-i", unitTestImageID, "/bin/cat"}, nil)
228 228
 	if err != nil {
229 229
 		t.Fatal(err)
230 230
 	}
... ...
@@ -302,7 +303,7 @@ func TestCreateStartRestartStopStartKillRm(t *testing.T) {
302 302
 	srv := mkServerFromEngine(eng, t)
303 303
 	defer mkRuntimeFromEngine(eng, t).Nuke()
304 304
 
305
-	config, hostConfig, _, err := docker.ParseRun([]string{"-i", unitTestImageID, "/bin/cat"}, nil)
305
+	config, hostConfig, _, err := runconfig.Parse([]string{"-i", unitTestImageID, "/bin/cat"}, nil)
306 306
 	if err != nil {
307 307
 		t.Fatal(err)
308 308
 	}
... ...
@@ -401,7 +402,7 @@ func TestRmi(t *testing.T) {
401 401
 
402 402
 	initialImages := getAllImages(eng, t)
403 403
 
404
-	config, hostConfig, _, err := docker.ParseRun([]string{unitTestImageID, "echo", "test"}, nil)
404
+	config, hostConfig, _, err := runconfig.Parse([]string{unitTestImageID, "echo", "test"}, nil)
405 405
 	if err != nil {
406 406
 		t.Fatal(err)
407 407
 	}
... ...
@@ -548,7 +549,7 @@ func TestListContainers(t *testing.T) {
548 548
 	srv := mkServerFromEngine(eng, t)
549 549
 	defer mkRuntimeFromEngine(eng, t).Nuke()
550 550
 
551
-	config := docker.Config{
551
+	config := runconfig.Config{
552 552
 		Image:     unitTestImageID,
553 553
 		Cmd:       []string{"/bin/sh", "-c", "cat"},
554 554
 		OpenStdin: true,
... ...
@@ -671,7 +672,7 @@ func TestDeleteTagWithExistingContainers(t *testing.T) {
671 671
 	}
672 672
 
673 673
 	// Create a container from the image
674
-	config, _, _, err := docker.ParseRun([]string{unitTestImageID, "echo test"}, nil)
674
+	config, _, _, err := runconfig.Parse([]string{unitTestImageID, "echo test"}, nil)
675 675
 	if err != nil {
676 676
 		t.Fatal(err)
677 677
 	}
... ...
@@ -16,6 +16,7 @@ import (
16 16
 
17 17
 	"github.com/dotcloud/docker"
18 18
 	"github.com/dotcloud/docker/engine"
19
+	"github.com/dotcloud/docker/runconfig"
19 20
 	"github.com/dotcloud/docker/utils"
20 21
 )
21 22
 
... ...
@@ -48,7 +49,7 @@ func mkRuntime(f utils.Fataler) *docker.Runtime {
48 48
 	return r
49 49
 }
50 50
 
51
-func createNamedTestContainer(eng *engine.Engine, config *docker.Config, f utils.Fataler, name string) (shortId string) {
51
+func createNamedTestContainer(eng *engine.Engine, config *runconfig.Config, f utils.Fataler, name string) (shortId string) {
52 52
 	job := eng.Job("create", name)
53 53
 	if err := job.ImportEnv(config); err != nil {
54 54
 		f.Fatal(err)
... ...
@@ -60,7 +61,7 @@ func createNamedTestContainer(eng *engine.Engine, config *docker.Config, f utils
60 60
 	return
61 61
 }
62 62
 
63
-func createTestContainer(eng *engine.Engine, config *docker.Config, f utils.Fataler) (shortId string) {
63
+func createTestContainer(eng *engine.Engine, config *runconfig.Config, f utils.Fataler) (shortId string) {
64 64
 	return createNamedTestContainer(eng, config, f, "")
65 65
 }
66 66
 
... ...
@@ -252,8 +253,8 @@ func readFile(src string, t *testing.T) (content string) {
252 252
 // dynamically replaced by the current test image.
253 253
 // The caller is responsible for destroying the container.
254 254
 // Call t.Fatal() at the first error.
255
-func mkContainer(r *docker.Runtime, args []string, t *testing.T) (*docker.Container, *docker.HostConfig, error) {
256
-	config, hc, _, err := docker.ParseRun(args, nil)
255
+func mkContainer(r *docker.Runtime, args []string, t *testing.T) (*docker.Container, *runconfig.HostConfig, error) {
256
+	config, hc, _, err := runconfig.Parse(args, nil)
257 257
 	defer func() {
258 258
 		if err != nil && t != nil {
259 259
 			t.Fatal(err)
... ...
@@ -2,13 +2,14 @@ package docker
2 2
 
3 3
 import (
4 4
 	"github.com/dotcloud/docker/nat"
5
+	"github.com/dotcloud/docker/runconfig"
5 6
 	"strings"
6 7
 	"testing"
7 8
 )
8 9
 
9 10
 func newMockLinkContainer(id string, ip string) *Container {
10 11
 	return &Container{
11
-		Config: &Config{},
12
+		Config: &runconfig.Config{},
12 13
 		ID:     id,
13 14
 		NetworkSettings: &NetworkSettings{
14 15
 			IPAddress: ip,
15 16
new file mode 100644
... ...
@@ -0,0 +1,67 @@
0
+package runconfig
1
+
2
+// Compare two Config struct. Do not compare the "Image" nor "Hostname" fields
3
+// If OpenStdin is set, then it differs
4
+func Compare(a, b *Config) bool {
5
+	if a == nil || b == nil ||
6
+		a.OpenStdin || b.OpenStdin {
7
+		return false
8
+	}
9
+	if a.AttachStdout != b.AttachStdout ||
10
+		a.AttachStderr != b.AttachStderr ||
11
+		a.User != b.User ||
12
+		a.Memory != b.Memory ||
13
+		a.MemorySwap != b.MemorySwap ||
14
+		a.CpuShares != b.CpuShares ||
15
+		a.OpenStdin != b.OpenStdin ||
16
+		a.Tty != b.Tty ||
17
+		a.VolumesFrom != b.VolumesFrom {
18
+		return false
19
+	}
20
+	if len(a.Cmd) != len(b.Cmd) ||
21
+		len(a.Dns) != len(b.Dns) ||
22
+		len(a.Env) != len(b.Env) ||
23
+		len(a.PortSpecs) != len(b.PortSpecs) ||
24
+		len(a.ExposedPorts) != len(b.ExposedPorts) ||
25
+		len(a.Entrypoint) != len(b.Entrypoint) ||
26
+		len(a.Volumes) != len(b.Volumes) {
27
+		return false
28
+	}
29
+
30
+	for i := 0; i < len(a.Cmd); i++ {
31
+		if a.Cmd[i] != b.Cmd[i] {
32
+			return false
33
+		}
34
+	}
35
+	for i := 0; i < len(a.Dns); i++ {
36
+		if a.Dns[i] != b.Dns[i] {
37
+			return false
38
+		}
39
+	}
40
+	for i := 0; i < len(a.Env); i++ {
41
+		if a.Env[i] != b.Env[i] {
42
+			return false
43
+		}
44
+	}
45
+	for i := 0; i < len(a.PortSpecs); i++ {
46
+		if a.PortSpecs[i] != b.PortSpecs[i] {
47
+			return false
48
+		}
49
+	}
50
+	for k := range a.ExposedPorts {
51
+		if _, exists := b.ExposedPorts[k]; !exists {
52
+			return false
53
+		}
54
+	}
55
+	for i := 0; i < len(a.Entrypoint); i++ {
56
+		if a.Entrypoint[i] != b.Entrypoint[i] {
57
+			return false
58
+		}
59
+	}
60
+	for key := range a.Volumes {
61
+		if _, exists := b.Volumes[key]; !exists {
62
+			return false
63
+		}
64
+	}
65
+	return true
66
+}
0 67
new file mode 100644
... ...
@@ -0,0 +1,76 @@
0
+package runconfig
1
+
2
+import (
3
+	"github.com/dotcloud/docker/engine"
4
+	"github.com/dotcloud/docker/nat"
5
+)
6
+
7
+// Note: the Config structure should hold only portable information about the container.
8
+// Here, "portable" means "independent from the host we are running on".
9
+// Non-portable information *should* appear in HostConfig.
10
+type Config struct {
11
+	Hostname        string
12
+	Domainname      string
13
+	User            string
14
+	Memory          int64 // Memory limit (in bytes)
15
+	MemorySwap      int64 // Total memory usage (memory + swap); set `-1' to disable swap
16
+	CpuShares       int64 // CPU shares (relative weight vs. other containers)
17
+	AttachStdin     bool
18
+	AttachStdout    bool
19
+	AttachStderr    bool
20
+	PortSpecs       []string // Deprecated - Can be in the format of 8080/tcp
21
+	ExposedPorts    map[nat.Port]struct{}
22
+	Tty             bool // Attach standard streams to a tty, including stdin if it is not closed.
23
+	OpenStdin       bool // Open stdin
24
+	StdinOnce       bool // If true, close stdin after the 1 attached client disconnects.
25
+	Env             []string
26
+	Cmd             []string
27
+	Dns             []string
28
+	Image           string // Name of the image as it was passed by the operator (eg. could be symbolic)
29
+	Volumes         map[string]struct{}
30
+	VolumesFrom     string
31
+	WorkingDir      string
32
+	Entrypoint      []string
33
+	NetworkDisabled bool
34
+	OnBuild         []string
35
+}
36
+
37
+func ContainerConfigFromJob(job *engine.Job) *Config {
38
+	config := &Config{
39
+		Hostname:        job.Getenv("Hostname"),
40
+		Domainname:      job.Getenv("Domainname"),
41
+		User:            job.Getenv("User"),
42
+		Memory:          job.GetenvInt64("Memory"),
43
+		MemorySwap:      job.GetenvInt64("MemorySwap"),
44
+		CpuShares:       job.GetenvInt64("CpuShares"),
45
+		AttachStdin:     job.GetenvBool("AttachStdin"),
46
+		AttachStdout:    job.GetenvBool("AttachStdout"),
47
+		AttachStderr:    job.GetenvBool("AttachStderr"),
48
+		Tty:             job.GetenvBool("Tty"),
49
+		OpenStdin:       job.GetenvBool("OpenStdin"),
50
+		StdinOnce:       job.GetenvBool("StdinOnce"),
51
+		Image:           job.Getenv("Image"),
52
+		VolumesFrom:     job.Getenv("VolumesFrom"),
53
+		WorkingDir:      job.Getenv("WorkingDir"),
54
+		NetworkDisabled: job.GetenvBool("NetworkDisabled"),
55
+	}
56
+	job.GetenvJson("ExposedPorts", &config.ExposedPorts)
57
+	job.GetenvJson("Volumes", &config.Volumes)
58
+	if PortSpecs := job.GetenvList("PortSpecs"); PortSpecs != nil {
59
+		config.PortSpecs = PortSpecs
60
+	}
61
+	if Env := job.GetenvList("Env"); Env != nil {
62
+		config.Env = Env
63
+	}
64
+	if Cmd := job.GetenvList("Cmd"); Cmd != nil {
65
+		config.Cmd = Cmd
66
+	}
67
+	if Dns := job.GetenvList("Dns"); Dns != nil {
68
+		config.Dns = Dns
69
+	}
70
+	if Entrypoint := job.GetenvList("Entrypoint"); Entrypoint != nil {
71
+		config.Entrypoint = Entrypoint
72
+	}
73
+
74
+	return config
75
+}
0 76
new file mode 100644
... ...
@@ -0,0 +1,150 @@
0
+package runconfig
1
+
2
+import (
3
+	"github.com/dotcloud/docker/nat"
4
+	"testing"
5
+)
6
+
7
+func TestCompare(t *testing.T) {
8
+	volumes1 := make(map[string]struct{})
9
+	volumes1["/test1"] = struct{}{}
10
+	config1 := Config{
11
+		Dns:         []string{"1.1.1.1", "2.2.2.2"},
12
+		PortSpecs:   []string{"1111:1111", "2222:2222"},
13
+		Env:         []string{"VAR1=1", "VAR2=2"},
14
+		VolumesFrom: "11111111",
15
+		Volumes:     volumes1,
16
+	}
17
+	config2 := Config{
18
+		Dns:         []string{"0.0.0.0", "2.2.2.2"},
19
+		PortSpecs:   []string{"1111:1111", "2222:2222"},
20
+		Env:         []string{"VAR1=1", "VAR2=2"},
21
+		VolumesFrom: "11111111",
22
+		Volumes:     volumes1,
23
+	}
24
+	config3 := Config{
25
+		Dns:         []string{"1.1.1.1", "2.2.2.2"},
26
+		PortSpecs:   []string{"0000:0000", "2222:2222"},
27
+		Env:         []string{"VAR1=1", "VAR2=2"},
28
+		VolumesFrom: "11111111",
29
+		Volumes:     volumes1,
30
+	}
31
+	config4 := Config{
32
+		Dns:         []string{"1.1.1.1", "2.2.2.2"},
33
+		PortSpecs:   []string{"0000:0000", "2222:2222"},
34
+		Env:         []string{"VAR1=1", "VAR2=2"},
35
+		VolumesFrom: "22222222",
36
+		Volumes:     volumes1,
37
+	}
38
+	volumes2 := make(map[string]struct{})
39
+	volumes2["/test2"] = struct{}{}
40
+	config5 := Config{
41
+		Dns:         []string{"1.1.1.1", "2.2.2.2"},
42
+		PortSpecs:   []string{"0000:0000", "2222:2222"},
43
+		Env:         []string{"VAR1=1", "VAR2=2"},
44
+		VolumesFrom: "11111111",
45
+		Volumes:     volumes2,
46
+	}
47
+	if Compare(&config1, &config2) {
48
+		t.Fatalf("Compare should return false, Dns are different")
49
+	}
50
+	if Compare(&config1, &config3) {
51
+		t.Fatalf("Compare should return false, PortSpecs are different")
52
+	}
53
+	if Compare(&config1, &config4) {
54
+		t.Fatalf("Compare should return false, VolumesFrom are different")
55
+	}
56
+	if Compare(&config1, &config5) {
57
+		t.Fatalf("Compare should return false, Volumes are different")
58
+	}
59
+	if !Compare(&config1, &config1) {
60
+		t.Fatalf("Compare should return true")
61
+	}
62
+}
63
+
64
+func TestMerge(t *testing.T) {
65
+	volumesImage := make(map[string]struct{})
66
+	volumesImage["/test1"] = struct{}{}
67
+	volumesImage["/test2"] = struct{}{}
68
+	configImage := &Config{
69
+		Dns:         []string{"1.1.1.1", "2.2.2.2"},
70
+		PortSpecs:   []string{"1111:1111", "2222:2222"},
71
+		Env:         []string{"VAR1=1", "VAR2=2"},
72
+		VolumesFrom: "1111",
73
+		Volumes:     volumesImage,
74
+	}
75
+
76
+	volumesUser := make(map[string]struct{})
77
+	volumesUser["/test3"] = struct{}{}
78
+	configUser := &Config{
79
+		Dns:       []string{"3.3.3.3"},
80
+		PortSpecs: []string{"3333:2222", "3333:3333"},
81
+		Env:       []string{"VAR2=3", "VAR3=3"},
82
+		Volumes:   volumesUser,
83
+	}
84
+
85
+	if err := Merge(configUser, configImage); err != nil {
86
+		t.Error(err)
87
+	}
88
+
89
+	if len(configUser.Dns) != 3 {
90
+		t.Fatalf("Expected 3 dns, 1.1.1.1, 2.2.2.2 and 3.3.3.3, found %d", len(configUser.Dns))
91
+	}
92
+	for _, dns := range configUser.Dns {
93
+		if dns != "1.1.1.1" && dns != "2.2.2.2" && dns != "3.3.3.3" {
94
+			t.Fatalf("Expected 1.1.1.1 or 2.2.2.2 or 3.3.3.3, found %s", dns)
95
+		}
96
+	}
97
+
98
+	if len(configUser.ExposedPorts) != 3 {
99
+		t.Fatalf("Expected 3 ExposedPorts, 1111, 2222 and 3333, found %d", len(configUser.ExposedPorts))
100
+	}
101
+	for portSpecs := range configUser.ExposedPorts {
102
+		if portSpecs.Port() != "1111" && portSpecs.Port() != "2222" && portSpecs.Port() != "3333" {
103
+			t.Fatalf("Expected 1111 or 2222 or 3333, found %s", portSpecs)
104
+		}
105
+	}
106
+	if len(configUser.Env) != 3 {
107
+		t.Fatalf("Expected 3 env var, VAR1=1, VAR2=3 and VAR3=3, found %d", len(configUser.Env))
108
+	}
109
+	for _, env := range configUser.Env {
110
+		if env != "VAR1=1" && env != "VAR2=3" && env != "VAR3=3" {
111
+			t.Fatalf("Expected VAR1=1 or VAR2=3 or VAR3=3, found %s", env)
112
+		}
113
+	}
114
+
115
+	if len(configUser.Volumes) != 3 {
116
+		t.Fatalf("Expected 3 volumes, /test1, /test2 and /test3, found %d", len(configUser.Volumes))
117
+	}
118
+	for v := range configUser.Volumes {
119
+		if v != "/test1" && v != "/test2" && v != "/test3" {
120
+			t.Fatalf("Expected /test1 or /test2 or /test3, found %s", v)
121
+		}
122
+	}
123
+
124
+	if configUser.VolumesFrom != "1111" {
125
+		t.Fatalf("Expected VolumesFrom to be 1111, found %s", configUser.VolumesFrom)
126
+	}
127
+
128
+	ports, _, err := nat.ParsePortSpecs([]string{"0000"})
129
+	if err != nil {
130
+		t.Error(err)
131
+	}
132
+	configImage2 := &Config{
133
+		ExposedPorts: ports,
134
+	}
135
+
136
+	if err := Merge(configUser, configImage2); err != nil {
137
+		t.Error(err)
138
+	}
139
+
140
+	if len(configUser.ExposedPorts) != 4 {
141
+		t.Fatalf("Expected 4 ExposedPorts, 0000, 1111, 2222 and 3333, found %d", len(configUser.ExposedPorts))
142
+	}
143
+	for portSpecs := range configUser.ExposedPorts {
144
+		if portSpecs.Port() != "0000" && portSpecs.Port() != "1111" && portSpecs.Port() != "2222" && portSpecs.Port() != "3333" {
145
+			t.Fatalf("Expected 0000 or 1111 or 2222 or 3333, found %s", portSpecs)
146
+		}
147
+	}
148
+
149
+}
0 150
new file mode 100644
... ...
@@ -0,0 +1,39 @@
0
+package runconfig
1
+
2
+import (
3
+	"github.com/dotcloud/docker/engine"
4
+	"github.com/dotcloud/docker/nat"
5
+)
6
+
7
+type HostConfig struct {
8
+	Binds           []string
9
+	ContainerIDFile string
10
+	LxcConf         []KeyValuePair
11
+	Privileged      bool
12
+	PortBindings    nat.PortMap
13
+	Links           []string
14
+	PublishAllPorts bool
15
+}
16
+
17
+type KeyValuePair struct {
18
+	Key   string
19
+	Value string
20
+}
21
+
22
+func ContainerHostConfigFromJob(job *engine.Job) *HostConfig {
23
+	hostConfig := &HostConfig{
24
+		ContainerIDFile: job.Getenv("ContainerIDFile"),
25
+		Privileged:      job.GetenvBool("Privileged"),
26
+		PublishAllPorts: job.GetenvBool("PublishAllPorts"),
27
+	}
28
+	job.GetenvJson("LxcConf", &hostConfig.LxcConf)
29
+	job.GetenvJson("PortBindings", &hostConfig.PortBindings)
30
+	if Binds := job.GetenvList("Binds"); Binds != nil {
31
+		hostConfig.Binds = Binds
32
+	}
33
+	if Links := job.GetenvList("Links"); Links != nil {
34
+		hostConfig.Links = Links
35
+	}
36
+
37
+	return hostConfig
38
+}
0 39
new file mode 100644
... ...
@@ -0,0 +1,119 @@
0
+package runconfig
1
+
2
+import (
3
+	"github.com/dotcloud/docker/nat"
4
+	"github.com/dotcloud/docker/utils"
5
+	"strings"
6
+)
7
+
8
+func Merge(userConf, imageConf *Config) error {
9
+	if userConf.User == "" {
10
+		userConf.User = imageConf.User
11
+	}
12
+	if userConf.Memory == 0 {
13
+		userConf.Memory = imageConf.Memory
14
+	}
15
+	if userConf.MemorySwap == 0 {
16
+		userConf.MemorySwap = imageConf.MemorySwap
17
+	}
18
+	if userConf.CpuShares == 0 {
19
+		userConf.CpuShares = imageConf.CpuShares
20
+	}
21
+	if userConf.ExposedPorts == nil || len(userConf.ExposedPorts) == 0 {
22
+		userConf.ExposedPorts = imageConf.ExposedPorts
23
+	} else if imageConf.ExposedPorts != nil {
24
+		if userConf.ExposedPorts == nil {
25
+			userConf.ExposedPorts = make(nat.PortSet)
26
+		}
27
+		for port := range imageConf.ExposedPorts {
28
+			if _, exists := userConf.ExposedPorts[port]; !exists {
29
+				userConf.ExposedPorts[port] = struct{}{}
30
+			}
31
+		}
32
+	}
33
+
34
+	if userConf.PortSpecs != nil && len(userConf.PortSpecs) > 0 {
35
+		if userConf.ExposedPorts == nil {
36
+			userConf.ExposedPorts = make(nat.PortSet)
37
+		}
38
+		ports, _, err := nat.ParsePortSpecs(userConf.PortSpecs)
39
+		if err != nil {
40
+			return err
41
+		}
42
+		for port := range ports {
43
+			if _, exists := userConf.ExposedPorts[port]; !exists {
44
+				userConf.ExposedPorts[port] = struct{}{}
45
+			}
46
+		}
47
+		userConf.PortSpecs = nil
48
+	}
49
+	if imageConf.PortSpecs != nil && len(imageConf.PortSpecs) > 0 {
50
+		// FIXME: I think we can safely remove this. Leaving it for now for the sake of reverse-compat paranoia.
51
+		utils.Debugf("Migrating image port specs to containter: %s", strings.Join(imageConf.PortSpecs, ", "))
52
+		if userConf.ExposedPorts == nil {
53
+			userConf.ExposedPorts = make(nat.PortSet)
54
+		}
55
+
56
+		ports, _, err := nat.ParsePortSpecs(imageConf.PortSpecs)
57
+		if err != nil {
58
+			return err
59
+		}
60
+		for port := range ports {
61
+			if _, exists := userConf.ExposedPorts[port]; !exists {
62
+				userConf.ExposedPorts[port] = struct{}{}
63
+			}
64
+		}
65
+	}
66
+	if !userConf.Tty {
67
+		userConf.Tty = imageConf.Tty
68
+	}
69
+	if !userConf.OpenStdin {
70
+		userConf.OpenStdin = imageConf.OpenStdin
71
+	}
72
+	if !userConf.StdinOnce {
73
+		userConf.StdinOnce = imageConf.StdinOnce
74
+	}
75
+	if userConf.Env == nil || len(userConf.Env) == 0 {
76
+		userConf.Env = imageConf.Env
77
+	} else {
78
+		for _, imageEnv := range imageConf.Env {
79
+			found := false
80
+			imageEnvKey := strings.Split(imageEnv, "=")[0]
81
+			for _, userEnv := range userConf.Env {
82
+				userEnvKey := strings.Split(userEnv, "=")[0]
83
+				if imageEnvKey == userEnvKey {
84
+					found = true
85
+				}
86
+			}
87
+			if !found {
88
+				userConf.Env = append(userConf.Env, imageEnv)
89
+			}
90
+		}
91
+	}
92
+	if userConf.Cmd == nil || len(userConf.Cmd) == 0 {
93
+		userConf.Cmd = imageConf.Cmd
94
+	}
95
+	if userConf.Dns == nil || len(userConf.Dns) == 0 {
96
+		userConf.Dns = imageConf.Dns
97
+	} else {
98
+		//duplicates aren't an issue here
99
+		userConf.Dns = append(userConf.Dns, imageConf.Dns...)
100
+	}
101
+	if userConf.Entrypoint == nil || len(userConf.Entrypoint) == 0 {
102
+		userConf.Entrypoint = imageConf.Entrypoint
103
+	}
104
+	if userConf.WorkingDir == "" {
105
+		userConf.WorkingDir = imageConf.WorkingDir
106
+	}
107
+	if userConf.VolumesFrom == "" {
108
+		userConf.VolumesFrom = imageConf.VolumesFrom
109
+	}
110
+	if userConf.Volumes == nil || len(userConf.Volumes) == 0 {
111
+		userConf.Volumes = imageConf.Volumes
112
+	} else {
113
+		for k, v := range imageConf.Volumes {
114
+			userConf.Volumes[k] = v
115
+		}
116
+	}
117
+	return nil
118
+}
0 119
new file mode 100644
... ...
@@ -0,0 +1,246 @@
0
+package runconfig
1
+
2
+import (
3
+	"fmt"
4
+	"github.com/dotcloud/docker/nat"
5
+	flag "github.com/dotcloud/docker/pkg/mflag"
6
+	"github.com/dotcloud/docker/pkg/opts"
7
+	"github.com/dotcloud/docker/pkg/sysinfo"
8
+	"github.com/dotcloud/docker/utils"
9
+	"io/ioutil"
10
+	"path"
11
+	"strings"
12
+)
13
+
14
+var (
15
+	ErrInvalidWorikingDirectory = fmt.Errorf("The working directory is invalid. It needs to be an absolute path.")
16
+	ErrConflictAttachDetach     = fmt.Errorf("Conflicting options: -a and -d")
17
+	ErrConflictDetachAutoRemove = fmt.Errorf("Conflicting options: -rm and -d")
18
+)
19
+
20
+//FIXME Only used in tests
21
+func Parse(args []string, sysInfo *sysinfo.SysInfo) (*Config, *HostConfig, *flag.FlagSet, error) {
22
+	cmd := flag.NewFlagSet("run", flag.ContinueOnError)
23
+	cmd.SetOutput(ioutil.Discard)
24
+	cmd.Usage = nil
25
+	return parseRun(cmd, args, sysInfo)
26
+}
27
+
28
+// FIXME: this maps the legacy commands.go code. It should be merged with Parse to only expose a single parse function.
29
+func ParseSubcommand(cmd *flag.FlagSet, args []string, sysInfo *sysinfo.SysInfo) (*Config, *HostConfig, *flag.FlagSet, error) {
30
+	return parseRun(cmd, args, sysInfo)
31
+}
32
+
33
+func parseRun(cmd *flag.FlagSet, args []string, sysInfo *sysinfo.SysInfo) (*Config, *HostConfig, *flag.FlagSet, error) {
34
+	var (
35
+		// FIXME: use utils.ListOpts for attach and volumes?
36
+		flAttach  = opts.NewListOpts(opts.ValidateAttach)
37
+		flVolumes = opts.NewListOpts(opts.ValidatePath)
38
+		flLinks   = opts.NewListOpts(opts.ValidateLink)
39
+		flEnv     = opts.NewListOpts(opts.ValidateEnv)
40
+
41
+		flPublish     opts.ListOpts
42
+		flExpose      opts.ListOpts
43
+		flDns         opts.ListOpts
44
+		flVolumesFrom opts.ListOpts
45
+		flLxcOpts     opts.ListOpts
46
+
47
+		flAutoRemove      = cmd.Bool([]string{"#rm", "-rm"}, false, "Automatically remove the container when it exits (incompatible with -d)")
48
+		flDetach          = cmd.Bool([]string{"d", "-detach"}, false, "Detached mode: Run container in the background, print new container id")
49
+		flNetwork         = cmd.Bool([]string{"n", "-networking"}, true, "Enable networking for this container")
50
+		flPrivileged      = cmd.Bool([]string{"#privileged", "-privileged"}, false, "Give extended privileges to this container")
51
+		flPublishAll      = cmd.Bool([]string{"P", "-publish-all"}, false, "Publish all exposed ports to the host interfaces")
52
+		flStdin           = cmd.Bool([]string{"i", "-interactive"}, false, "Keep stdin open even if not attached")
53
+		flTty             = cmd.Bool([]string{"t", "-tty"}, false, "Allocate a pseudo-tty")
54
+		flContainerIDFile = cmd.String([]string{"#cidfile", "-cidfile"}, "", "Write the container ID to the file")
55
+		flEntrypoint      = cmd.String([]string{"#entrypoint", "-entrypoint"}, "", "Overwrite the default entrypoint of the image")
56
+		flHostname        = cmd.String([]string{"h", "-hostname"}, "", "Container host name")
57
+		flMemoryString    = cmd.String([]string{"m", "-memory"}, "", "Memory limit (format: <number><optional unit>, where unit = b, k, m or g)")
58
+		flUser            = cmd.String([]string{"u", "-user"}, "", "Username or UID")
59
+		flWorkingDir      = cmd.String([]string{"w", "-workdir"}, "", "Working directory inside the container")
60
+		flCpuShares       = cmd.Int64([]string{"c", "-cpu-shares"}, 0, "CPU shares (relative weight)")
61
+
62
+		// For documentation purpose
63
+		_ = cmd.Bool([]string{"#sig-proxy", "-sig-proxy"}, true, "Proxify all received signal to the process (even in non-tty mode)")
64
+		_ = cmd.String([]string{"#name", "-name"}, "", "Assign a name to the container")
65
+	)
66
+
67
+	cmd.Var(&flAttach, []string{"a", "-attach"}, "Attach to stdin, stdout or stderr.")
68
+	cmd.Var(&flVolumes, []string{"v", "-volume"}, "Bind mount a volume (e.g. from the host: -v /host:/container, from docker: -v /container)")
69
+	cmd.Var(&flLinks, []string{"#link", "-link"}, "Add link to another container (name:alias)")
70
+	cmd.Var(&flEnv, []string{"e", "-env"}, "Set environment variables")
71
+
72
+	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))
73
+	cmd.Var(&flExpose, []string{"#expose", "-expose"}, "Expose a port from the container without publishing it to your host")
74
+	cmd.Var(&flDns, []string{"#dns", "-dns"}, "Set custom dns servers")
75
+	cmd.Var(&flVolumesFrom, []string{"#volumes-from", "-volumes-from"}, "Mount volumes from the specified container(s)")
76
+	cmd.Var(&flLxcOpts, []string{"#lxc-conf", "-lxc-conf"}, "Add custom lxc options -lxc-conf=\"lxc.cgroup.cpuset.cpus = 0,1\"")
77
+
78
+	if err := cmd.Parse(args); err != nil {
79
+		return nil, nil, cmd, err
80
+	}
81
+
82
+	// Check if the kernel supports memory limit cgroup.
83
+	if sysInfo != nil && *flMemoryString != "" && !sysInfo.MemoryLimit {
84
+		*flMemoryString = ""
85
+	}
86
+
87
+	// Validate input params
88
+	if *flDetach && flAttach.Len() > 0 {
89
+		return nil, nil, cmd, ErrConflictAttachDetach
90
+	}
91
+	if *flWorkingDir != "" && !path.IsAbs(*flWorkingDir) {
92
+		return nil, nil, cmd, ErrInvalidWorikingDirectory
93
+	}
94
+	if *flDetach && *flAutoRemove {
95
+		return nil, nil, cmd, ErrConflictDetachAutoRemove
96
+	}
97
+
98
+	// If neither -d or -a are set, attach to everything by default
99
+	if flAttach.Len() == 0 && !*flDetach {
100
+		if !*flDetach {
101
+			flAttach.Set("stdout")
102
+			flAttach.Set("stderr")
103
+			if *flStdin {
104
+				flAttach.Set("stdin")
105
+			}
106
+		}
107
+	}
108
+
109
+	var flMemory int64
110
+	if *flMemoryString != "" {
111
+		parsedMemory, err := utils.RAMInBytes(*flMemoryString)
112
+		if err != nil {
113
+			return nil, nil, cmd, err
114
+		}
115
+		flMemory = parsedMemory
116
+	}
117
+
118
+	var binds []string
119
+	// add any bind targets to the list of container volumes
120
+	for bind := range flVolumes.GetMap() {
121
+		if arr := strings.Split(bind, ":"); len(arr) > 1 {
122
+			if arr[0] == "/" {
123
+				return nil, nil, cmd, fmt.Errorf("Invalid bind mount: source can't be '/'")
124
+			}
125
+			dstDir := arr[1]
126
+			flVolumes.Set(dstDir)
127
+			binds = append(binds, bind)
128
+			flVolumes.Delete(bind)
129
+		} else if bind == "/" {
130
+			return nil, nil, cmd, fmt.Errorf("Invalid volume: path can't be '/'")
131
+		}
132
+	}
133
+
134
+	var (
135
+		parsedArgs = cmd.Args()
136
+		runCmd     []string
137
+		entrypoint []string
138
+		image      string
139
+	)
140
+	if len(parsedArgs) >= 1 {
141
+		image = cmd.Arg(0)
142
+	}
143
+	if len(parsedArgs) > 1 {
144
+		runCmd = parsedArgs[1:]
145
+	}
146
+	if *flEntrypoint != "" {
147
+		entrypoint = []string{*flEntrypoint}
148
+	}
149
+
150
+	lxcConf, err := parseLxcConfOpts(flLxcOpts)
151
+	if err != nil {
152
+		return nil, nil, cmd, err
153
+	}
154
+
155
+	var (
156
+		domainname string
157
+		hostname   = *flHostname
158
+		parts      = strings.SplitN(hostname, ".", 2)
159
+	)
160
+	if len(parts) > 1 {
161
+		hostname = parts[0]
162
+		domainname = parts[1]
163
+	}
164
+
165
+	ports, portBindings, err := nat.ParsePortSpecs(flPublish.GetAll())
166
+	if err != nil {
167
+		return nil, nil, cmd, err
168
+	}
169
+
170
+	// Merge in exposed ports to the map of published ports
171
+	for _, e := range flExpose.GetAll() {
172
+		if strings.Contains(e, ":") {
173
+			return nil, nil, cmd, fmt.Errorf("Invalid port format for --expose: %s", e)
174
+		}
175
+		p := nat.NewPort(nat.SplitProtoPort(e))
176
+		if _, exists := ports[p]; !exists {
177
+			ports[p] = struct{}{}
178
+		}
179
+	}
180
+
181
+	config := &Config{
182
+		Hostname:        hostname,
183
+		Domainname:      domainname,
184
+		PortSpecs:       nil, // Deprecated
185
+		ExposedPorts:    ports,
186
+		User:            *flUser,
187
+		Tty:             *flTty,
188
+		NetworkDisabled: !*flNetwork,
189
+		OpenStdin:       *flStdin,
190
+		Memory:          flMemory,
191
+		CpuShares:       *flCpuShares,
192
+		AttachStdin:     flAttach.Get("stdin"),
193
+		AttachStdout:    flAttach.Get("stdout"),
194
+		AttachStderr:    flAttach.Get("stderr"),
195
+		Env:             flEnv.GetAll(),
196
+		Cmd:             runCmd,
197
+		Dns:             flDns.GetAll(),
198
+		Image:           image,
199
+		Volumes:         flVolumes.GetMap(),
200
+		VolumesFrom:     strings.Join(flVolumesFrom.GetAll(), ","),
201
+		Entrypoint:      entrypoint,
202
+		WorkingDir:      *flWorkingDir,
203
+	}
204
+
205
+	hostConfig := &HostConfig{
206
+		Binds:           binds,
207
+		ContainerIDFile: *flContainerIDFile,
208
+		LxcConf:         lxcConf,
209
+		Privileged:      *flPrivileged,
210
+		PortBindings:    portBindings,
211
+		Links:           flLinks.GetAll(),
212
+		PublishAllPorts: *flPublishAll,
213
+	}
214
+
215
+	if sysInfo != nil && flMemory > 0 && !sysInfo.SwapLimit {
216
+		//fmt.Fprintf(stdout, "WARNING: Your kernel does not support swap limit capabilities. Limitation discarded.\n")
217
+		config.MemorySwap = -1
218
+	}
219
+
220
+	// When allocating stdin in attached mode, close stdin at client disconnect
221
+	if config.OpenStdin && config.AttachStdin {
222
+		config.StdinOnce = true
223
+	}
224
+	return config, hostConfig, cmd, nil
225
+}
226
+
227
+func parseLxcConfOpts(opts opts.ListOpts) ([]KeyValuePair, error) {
228
+	out := make([]KeyValuePair, opts.Len())
229
+	for i, o := range opts.GetAll() {
230
+		k, v, err := parseLxcOpt(o)
231
+		if err != nil {
232
+			return nil, err
233
+		}
234
+		out[i] = KeyValuePair{Key: k, Value: v}
235
+	}
236
+	return out, nil
237
+}
238
+
239
+func parseLxcOpt(opt string) (string, string, error) {
240
+	parts := strings.SplitN(opt, "=", 2)
241
+	if len(parts) != 2 {
242
+		return "", "", fmt.Errorf("Unable to parse lxc conf option: %s", opt)
243
+	}
244
+	return strings.TrimSpace(parts[0]), strings.TrimSpace(parts[1]), nil
245
+}
0 246
new file mode 100644
... ...
@@ -0,0 +1,22 @@
0
+package runconfig
1
+
2
+import (
3
+	"testing"
4
+)
5
+
6
+func TestParseLxcConfOpt(t *testing.T) {
7
+	opts := []string{"lxc.utsname=docker", "lxc.utsname = docker "}
8
+
9
+	for _, o := range opts {
10
+		k, v, err := parseLxcOpt(o)
11
+		if err != nil {
12
+			t.FailNow()
13
+		}
14
+		if k != "lxc.utsname" {
15
+			t.Fail()
16
+		}
17
+		if v != "docker" {
18
+			t.Fail()
19
+		}
20
+	}
21
+}
... ...
@@ -18,6 +18,7 @@ import (
18 18
 	"github.com/dotcloud/docker/networkdriver/portallocator"
19 19
 	"github.com/dotcloud/docker/pkg/graphdb"
20 20
 	"github.com/dotcloud/docker/pkg/sysinfo"
21
+	"github.com/dotcloud/docker/runconfig"
21 22
 	"github.com/dotcloud/docker/utils"
22 23
 	"io"
23 24
 	"io/ioutil"
... ...
@@ -329,7 +330,7 @@ func (runtime *Runtime) restore() error {
329 329
 }
330 330
 
331 331
 // Create creates a new container from the given configuration with a given name.
332
-func (runtime *Runtime) Create(config *Config, name string) (*Container, []string, error) {
332
+func (runtime *Runtime) Create(config *runconfig.Config, name string) (*Container, []string, error) {
333 333
 	// Lookup image
334 334
 	img, err := runtime.repositories.LookupImage(config.Image)
335 335
 	if err != nil {
... ...
@@ -347,7 +348,7 @@ func (runtime *Runtime) Create(config *Config, name string) (*Container, []strin
347 347
 		return nil, nil, fmt.Errorf("Cannot create container with more than %d parents", MaxImageDepth)
348 348
 	}
349 349
 
350
-	checkDeprecatedExpose := func(config *Config) bool {
350
+	checkDeprecatedExpose := func(config *runconfig.Config) bool {
351 351
 		if config != nil {
352 352
 			if config.PortSpecs != nil {
353 353
 				for _, p := range config.PortSpecs {
... ...
@@ -366,7 +367,7 @@ func (runtime *Runtime) Create(config *Config, name string) (*Container, []strin
366 366
 	}
367 367
 
368 368
 	if img.Config != nil {
369
-		if err := MergeConfig(config, img.Config); err != nil {
369
+		if err := runconfig.Merge(config, img.Config); err != nil {
370 370
 			return nil, nil, err
371 371
 		}
372 372
 	}
... ...
@@ -441,7 +442,7 @@ func (runtime *Runtime) Create(config *Config, name string) (*Container, []strin
441 441
 		Path:            entrypoint,
442 442
 		Args:            args, //FIXME: de-duplicate from config
443 443
 		Config:          config,
444
-		hostConfig:      &HostConfig{},
444
+		hostConfig:      &runconfig.HostConfig{},
445 445
 		Image:           img.ID, // Always use the resolved image id
446 446
 		NetworkSettings: &NetworkSettings{},
447 447
 		Name:            name,
... ...
@@ -518,7 +519,7 @@ func (runtime *Runtime) Create(config *Config, name string) (*Container, []strin
518 518
 
519 519
 // Commit creates a new filesystem image from the current state of a container.
520 520
 // The image can optionally be tagged into a repository
521
-func (runtime *Runtime) Commit(container *Container, repository, tag, comment, author string, config *Config) (*Image, error) {
521
+func (runtime *Runtime) Commit(container *Container, repository, tag, comment, author string, config *runconfig.Config) (*Image, error) {
522 522
 	// FIXME: freeze the container before copying it to avoid data corruption?
523 523
 	// FIXME: this shouldn't be in commands.
524 524
 	if err := container.Mount(); err != nil {
... ...
@@ -10,6 +10,7 @@ import (
10 10
 	"github.com/dotcloud/docker/engine"
11 11
 	"github.com/dotcloud/docker/pkg/graphdb"
12 12
 	"github.com/dotcloud/docker/registry"
13
+	"github.com/dotcloud/docker/runconfig"
13 14
 	"github.com/dotcloud/docker/utils"
14 15
 	"io"
15 16
 	"io/ioutil"
... ...
@@ -662,7 +663,7 @@ func (srv *Server) ImageInsert(job *engine.Job) engine.Status {
662 662
 	}
663 663
 	defer file.Body.Close()
664 664
 
665
-	config, _, _, err := ParseRun([]string{img.ID, "echo", "insert", url, path}, srv.runtime.sysInfo)
665
+	config, _, _, err := runconfig.Parse([]string{img.ID, "echo", "insert", url, path}, srv.runtime.sysInfo)
666 666
 	if err != nil {
667 667
 		return job.Error(err)
668 668
 	}
... ...
@@ -1043,7 +1044,7 @@ func (srv *Server) ContainerCommit(job *engine.Job) engine.Status {
1043 1043
 	if container == nil {
1044 1044
 		return job.Errorf("No such container: %s", name)
1045 1045
 	}
1046
-	var config Config
1046
+	var config runconfig.Config
1047 1047
 	if err := job.GetenvJson("config", &config); err != nil {
1048 1048
 		return job.Error(err)
1049 1049
 	}
... ...
@@ -1623,7 +1624,7 @@ func (srv *Server) ContainerCreate(job *engine.Job) engine.Status {
1623 1623
 	} else if len(job.Args) > 1 {
1624 1624
 		return job.Errorf("Usage: %s", job.Name)
1625 1625
 	}
1626
-	config := ContainerConfigFromJob(job)
1626
+	config := runconfig.ContainerConfigFromJob(job)
1627 1627
 	if config.Memory != 0 && config.Memory < 524288 {
1628 1628
 		return job.Errorf("Minimum memory limit allowed is 512k")
1629 1629
 	}
... ...
@@ -1989,7 +1990,7 @@ func (srv *Server) canDeleteImage(imgID string) error {
1989 1989
 	return nil
1990 1990
 }
1991 1991
 
1992
-func (srv *Server) ImageGetCached(imgID string, config *Config) (*Image, error) {
1992
+func (srv *Server) ImageGetCached(imgID string, config *runconfig.Config) (*Image, error) {
1993 1993
 
1994 1994
 	// Retrieve all images
1995 1995
 	images, err := srv.runtime.graph.Map()
... ...
@@ -2013,7 +2014,7 @@ func (srv *Server) ImageGetCached(imgID string, config *Config) (*Image, error)
2013 2013
 		if err != nil {
2014 2014
 			return nil, err
2015 2015
 		}
2016
-		if CompareConfig(&img.ContainerConfig, config) {
2016
+		if runconfig.Compare(&img.ContainerConfig, config) {
2017 2017
 			if match == nil || match.Created.Before(img.Created) {
2018 2018
 				match = img
2019 2019
 			}
... ...
@@ -2022,7 +2023,7 @@ func (srv *Server) ImageGetCached(imgID string, config *Config) (*Image, error)
2022 2022
 	return match, nil
2023 2023
 }
2024 2024
 
2025
-func (srv *Server) RegisterLinks(container *Container, hostConfig *HostConfig) error {
2025
+func (srv *Server) RegisterLinks(container *Container, hostConfig *runconfig.HostConfig) error {
2026 2026
 	runtime := srv.runtime
2027 2027
 
2028 2028
 	if hostConfig != nil && hostConfig.Links != nil {
... ...
@@ -2066,7 +2067,7 @@ func (srv *Server) ContainerStart(job *engine.Job) engine.Status {
2066 2066
 	}
2067 2067
 	// If no environment was set, then no hostconfig was passed.
2068 2068
 	if len(job.Environ()) > 0 {
2069
-		hostConfig := ContainerHostConfigFromJob(job)
2069
+		hostConfig := runconfig.ContainerHostConfigFromJob(job)
2070 2070
 		// Validate the HostConfig binds. Make sure that:
2071 2071
 		// 1) the source of a bind mount isn't /
2072 2072
 		//         The bind mount "/:/foo" isn't allowed.
... ...
@@ -2310,7 +2311,7 @@ func (srv *Server) JobInspect(job *engine.Job) engine.Status {
2310 2310
 		}
2311 2311
 		object = &struct {
2312 2312
 			*Container
2313
-			HostConfig *HostConfig
2313
+			HostConfig *runconfig.HostConfig
2314 2314
 		}{container, container.hostConfig}
2315 2315
 	default:
2316 2316
 		return job.Errorf("Unknown kind: %s", kind)
... ...
@@ -1,14 +1,12 @@
1 1
 package docker
2 2
 
3 3
 import (
4
-	"fmt"
5 4
 	"github.com/dotcloud/docker/archive"
6 5
 	"github.com/dotcloud/docker/nat"
7 6
 	"github.com/dotcloud/docker/pkg/namesgenerator"
8
-	"github.com/dotcloud/docker/pkg/opts"
7
+	"github.com/dotcloud/docker/runconfig"
9 8
 	"github.com/dotcloud/docker/utils"
10 9
 	"io"
11
-	"strings"
12 10
 	"sync/atomic"
13 11
 )
14 12
 
... ...
@@ -16,204 +14,7 @@ type Change struct {
16 16
 	archive.Change
17 17
 }
18 18
 
19
-// Compare two Config struct. Do not compare the "Image" nor "Hostname" fields
20
-// If OpenStdin is set, then it differs
21
-func CompareConfig(a, b *Config) bool {
22
-	if a == nil || b == nil ||
23
-		a.OpenStdin || b.OpenStdin {
24
-		return false
25
-	}
26
-	if a.AttachStdout != b.AttachStdout ||
27
-		a.AttachStderr != b.AttachStderr ||
28
-		a.User != b.User ||
29
-		a.Memory != b.Memory ||
30
-		a.MemorySwap != b.MemorySwap ||
31
-		a.CpuShares != b.CpuShares ||
32
-		a.OpenStdin != b.OpenStdin ||
33
-		a.Tty != b.Tty ||
34
-		a.VolumesFrom != b.VolumesFrom {
35
-		return false
36
-	}
37
-	if len(a.Cmd) != len(b.Cmd) ||
38
-		len(a.Dns) != len(b.Dns) ||
39
-		len(a.Env) != len(b.Env) ||
40
-		len(a.PortSpecs) != len(b.PortSpecs) ||
41
-		len(a.ExposedPorts) != len(b.ExposedPorts) ||
42
-		len(a.Entrypoint) != len(b.Entrypoint) ||
43
-		len(a.Volumes) != len(b.Volumes) {
44
-		return false
45
-	}
46
-
47
-	for i := 0; i < len(a.Cmd); i++ {
48
-		if a.Cmd[i] != b.Cmd[i] {
49
-			return false
50
-		}
51
-	}
52
-	for i := 0; i < len(a.Dns); i++ {
53
-		if a.Dns[i] != b.Dns[i] {
54
-			return false
55
-		}
56
-	}
57
-	for i := 0; i < len(a.Env); i++ {
58
-		if a.Env[i] != b.Env[i] {
59
-			return false
60
-		}
61
-	}
62
-	for i := 0; i < len(a.PortSpecs); i++ {
63
-		if a.PortSpecs[i] != b.PortSpecs[i] {
64
-			return false
65
-		}
66
-	}
67
-	for k := range a.ExposedPorts {
68
-		if _, exists := b.ExposedPorts[k]; !exists {
69
-			return false
70
-		}
71
-	}
72
-	for i := 0; i < len(a.Entrypoint); i++ {
73
-		if a.Entrypoint[i] != b.Entrypoint[i] {
74
-			return false
75
-		}
76
-	}
77
-	for key := range a.Volumes {
78
-		if _, exists := b.Volumes[key]; !exists {
79
-			return false
80
-		}
81
-	}
82
-	return true
83
-}
84
-
85
-func MergeConfig(userConf, imageConf *Config) error {
86
-	if userConf.User == "" {
87
-		userConf.User = imageConf.User
88
-	}
89
-	if userConf.Memory == 0 {
90
-		userConf.Memory = imageConf.Memory
91
-	}
92
-	if userConf.MemorySwap == 0 {
93
-		userConf.MemorySwap = imageConf.MemorySwap
94
-	}
95
-	if userConf.CpuShares == 0 {
96
-		userConf.CpuShares = imageConf.CpuShares
97
-	}
98
-	if userConf.ExposedPorts == nil || len(userConf.ExposedPorts) == 0 {
99
-		userConf.ExposedPorts = imageConf.ExposedPorts
100
-	} else if imageConf.ExposedPorts != nil {
101
-		if userConf.ExposedPorts == nil {
102
-			userConf.ExposedPorts = make(nat.PortSet)
103
-		}
104
-		for port := range imageConf.ExposedPorts {
105
-			if _, exists := userConf.ExposedPorts[port]; !exists {
106
-				userConf.ExposedPorts[port] = struct{}{}
107
-			}
108
-		}
109
-	}
110
-
111
-	if userConf.PortSpecs != nil && len(userConf.PortSpecs) > 0 {
112
-		if userConf.ExposedPorts == nil {
113
-			userConf.ExposedPorts = make(nat.PortSet)
114
-		}
115
-		ports, _, err := nat.ParsePortSpecs(userConf.PortSpecs)
116
-		if err != nil {
117
-			return err
118
-		}
119
-		for port := range ports {
120
-			if _, exists := userConf.ExposedPorts[port]; !exists {
121
-				userConf.ExposedPorts[port] = struct{}{}
122
-			}
123
-		}
124
-		userConf.PortSpecs = nil
125
-	}
126
-	if imageConf.PortSpecs != nil && len(imageConf.PortSpecs) > 0 {
127
-		utils.Debugf("Migrating image port specs to containter: %s", strings.Join(imageConf.PortSpecs, ", "))
128
-		if userConf.ExposedPorts == nil {
129
-			userConf.ExposedPorts = make(nat.PortSet)
130
-		}
131
-
132
-		ports, _, err := nat.ParsePortSpecs(imageConf.PortSpecs)
133
-		if err != nil {
134
-			return err
135
-		}
136
-		for port := range ports {
137
-			if _, exists := userConf.ExposedPorts[port]; !exists {
138
-				userConf.ExposedPorts[port] = struct{}{}
139
-			}
140
-		}
141
-	}
142
-	if !userConf.Tty {
143
-		userConf.Tty = imageConf.Tty
144
-	}
145
-	if !userConf.OpenStdin {
146
-		userConf.OpenStdin = imageConf.OpenStdin
147
-	}
148
-	if !userConf.StdinOnce {
149
-		userConf.StdinOnce = imageConf.StdinOnce
150
-	}
151
-	if userConf.Env == nil || len(userConf.Env) == 0 {
152
-		userConf.Env = imageConf.Env
153
-	} else {
154
-		for _, imageEnv := range imageConf.Env {
155
-			found := false
156
-			imageEnvKey := strings.Split(imageEnv, "=")[0]
157
-			for _, userEnv := range userConf.Env {
158
-				userEnvKey := strings.Split(userEnv, "=")[0]
159
-				if imageEnvKey == userEnvKey {
160
-					found = true
161
-				}
162
-			}
163
-			if !found {
164
-				userConf.Env = append(userConf.Env, imageEnv)
165
-			}
166
-		}
167
-	}
168
-	if userConf.Cmd == nil || len(userConf.Cmd) == 0 {
169
-		userConf.Cmd = imageConf.Cmd
170
-	}
171
-	if userConf.Dns == nil || len(userConf.Dns) == 0 {
172
-		userConf.Dns = imageConf.Dns
173
-	} else {
174
-		//duplicates aren't an issue here
175
-		userConf.Dns = append(userConf.Dns, imageConf.Dns...)
176
-	}
177
-	if userConf.Entrypoint == nil || len(userConf.Entrypoint) == 0 {
178
-		userConf.Entrypoint = imageConf.Entrypoint
179
-	}
180
-	if userConf.WorkingDir == "" {
181
-		userConf.WorkingDir = imageConf.WorkingDir
182
-	}
183
-	if userConf.VolumesFrom == "" {
184
-		userConf.VolumesFrom = imageConf.VolumesFrom
185
-	}
186
-	if userConf.Volumes == nil || len(userConf.Volumes) == 0 {
187
-		userConf.Volumes = imageConf.Volumes
188
-	} else {
189
-		for k, v := range imageConf.Volumes {
190
-			userConf.Volumes[k] = v
191
-		}
192
-	}
193
-	return nil
194
-}
195
-
196
-func parseLxcConfOpts(opts opts.ListOpts) ([]KeyValuePair, error) {
197
-	out := make([]KeyValuePair, opts.Len())
198
-	for i, o := range opts.GetAll() {
199
-		k, v, err := parseLxcOpt(o)
200
-		if err != nil {
201
-			return nil, err
202
-		}
203
-		out[i] = KeyValuePair{Key: k, Value: v}
204
-	}
205
-	return out, nil
206
-}
207
-
208
-func parseLxcOpt(opt string) (string, string, error) {
209
-	parts := strings.SplitN(opt, "=", 2)
210
-	if len(parts) != 2 {
211
-		return "", "", fmt.Errorf("Unable to parse lxc conf option: %s", opt)
212
-	}
213
-	return strings.TrimSpace(parts[0]), strings.TrimSpace(parts[1]), nil
214
-}
215
-
216
-func migratePortMappings(config *Config, hostConfig *HostConfig) error {
19
+func migratePortMappings(config *runconfig.Config, hostConfig *runconfig.HostConfig) error {
217 20
 	if config.PortSpecs != nil {
218 21
 		ports, bindings, err := nat.ParsePortSpecs(config.PortSpecs)
219 22
 		if err != nil {
... ...
@@ -222,7 +23,7 @@ func migratePortMappings(config *Config, hostConfig *HostConfig) error {
222 222
 		config.PortSpecs = nil
223 223
 		if len(bindings) > 0 {
224 224
 			if hostConfig == nil {
225
-				hostConfig = &HostConfig{}
225
+				hostConfig = &runconfig.HostConfig{}
226 226
 			}
227 227
 			hostConfig.PortBindings = bindings
228 228
 		}