Signed-off-by: David Calavera <david.calavera@gmail.com>
| ... | ... |
@@ -33,6 +33,7 @@ import ( |
| 33 | 33 |
"github.com/docker/docker/pkg/streamformatter" |
| 34 | 34 |
"github.com/docker/docker/pkg/version" |
| 35 | 35 |
"github.com/docker/docker/registry" |
| 36 |
+ "github.com/docker/docker/runconfig" |
|
| 36 | 37 |
"github.com/docker/docker/utils" |
| 37 | 38 |
) |
| 38 | 39 |
|
| ... | ... |
@@ -811,14 +812,14 @@ func postContainersCreate(eng *engine.Engine, version version.Version, w http.Re |
| 811 | 811 |
var ( |
| 812 | 812 |
warnings []string |
| 813 | 813 |
name = r.Form.Get("name")
|
| 814 |
- env = new(engine.Env) |
|
| 815 | 814 |
) |
| 816 | 815 |
|
| 817 |
- if err := env.Decode(r.Body); err != nil {
|
|
| 816 |
+ config, hostConfig, err := runconfig.DecodeContainerConfig(r.Body) |
|
| 817 |
+ if err != nil {
|
|
| 818 | 818 |
return err |
| 819 | 819 |
} |
| 820 | 820 |
|
| 821 |
- containerId, warnings, err := getDaemon(eng).ContainerCreate(name, env) |
|
| 821 |
+ containerId, warnings, err := getDaemon(eng).ContainerCreate(name, config, hostConfig) |
|
| 822 | 822 |
if err != nil {
|
| 823 | 823 |
return err |
| 824 | 824 |
} |
| ... | ... |
@@ -917,10 +918,6 @@ func postContainersStart(eng *engine.Engine, version version.Version, w http.Res |
| 917 | 917 |
if vars == nil {
|
| 918 | 918 |
return fmt.Errorf("Missing parameter")
|
| 919 | 919 |
} |
| 920 |
- var ( |
|
| 921 |
- name = vars["name"] |
|
| 922 |
- env = new(engine.Env) |
|
| 923 |
- ) |
|
| 924 | 920 |
|
| 925 | 921 |
// If contentLength is -1, we can assumed chunked encoding |
| 926 | 922 |
// or more technically that the length is unknown |
| ... | ... |
@@ -928,17 +925,21 @@ func postContainersStart(eng *engine.Engine, version version.Version, w http.Res |
| 928 | 928 |
// net/http otherwise seems to swallow any headers related to chunked encoding |
| 929 | 929 |
// including r.TransferEncoding |
| 930 | 930 |
// allow a nil body for backwards compatibility |
| 931 |
+ var hostConfig *runconfig.HostConfig |
|
| 931 | 932 |
if r.Body != nil && (r.ContentLength > 0 || r.ContentLength == -1) {
|
| 932 | 933 |
if err := checkForJson(r); err != nil {
|
| 933 | 934 |
return err |
| 934 | 935 |
} |
| 935 | 936 |
|
| 936 |
- if err := env.Decode(r.Body); err != nil {
|
|
| 937 |
+ c, err := runconfig.DecodeHostConfig(r.Body) |
|
| 938 |
+ if err != nil {
|
|
| 937 | 939 |
return err |
| 938 | 940 |
} |
| 941 |
+ |
|
| 942 |
+ hostConfig = c |
|
| 939 | 943 |
} |
| 940 | 944 |
|
| 941 |
- if err := getDaemon(eng).ContainerStart(name, env); err != nil {
|
|
| 945 |
+ if err := getDaemon(eng).ContainerStart(vars["name"], hostConfig); err != nil {
|
|
| 942 | 946 |
if err.Error() == "Container already started" {
|
| 943 | 947 |
w.WriteHeader(http.StatusNotModified) |
| 944 | 948 |
return nil |
| ... | ... |
@@ -262,7 +262,7 @@ func run(b *Builder, args []string, attributes map[string]bool, original string) |
| 262 | 262 |
b.Config.Cmd = config.Cmd |
| 263 | 263 |
runconfig.Merge(b.Config, config) |
| 264 | 264 |
|
| 265 |
- defer func(cmd []string) { b.Config.Cmd = cmd }(cmd)
|
|
| 265 |
+ defer func(cmd *runconfig.Command) { b.Config.Cmd = cmd }(cmd)
|
|
| 266 | 266 |
|
| 267 | 267 |
logrus.Debugf("[BUILDER] Command to be executed: %v", b.Config.Cmd)
|
| 268 | 268 |
|
| ... | ... |
@@ -301,13 +301,15 @@ func run(b *Builder, args []string, attributes map[string]bool, original string) |
| 301 | 301 |
// Argument handling is the same as RUN. |
| 302 | 302 |
// |
| 303 | 303 |
func cmd(b *Builder, args []string, attributes map[string]bool, original string) error {
|
| 304 |
- b.Config.Cmd = handleJsonArgs(args, attributes) |
|
| 304 |
+ cmdSlice := handleJsonArgs(args, attributes) |
|
| 305 | 305 |
|
| 306 | 306 |
if !attributes["json"] {
|
| 307 |
- b.Config.Cmd = append([]string{"/bin/sh", "-c"}, b.Config.Cmd...)
|
|
| 307 |
+ cmdSlice = append([]string{"/bin/sh", "-c"}, cmdSlice...)
|
|
| 308 | 308 |
} |
| 309 | 309 |
|
| 310 |
- if err := b.commit("", b.Config.Cmd, fmt.Sprintf("CMD %q", b.Config.Cmd)); err != nil {
|
|
| 310 |
+ b.Config.Cmd = runconfig.NewCommand(cmdSlice...) |
|
| 311 |
+ |
|
| 312 |
+ if err := b.commit("", b.Config.Cmd, fmt.Sprintf("CMD %q", cmdSlice)); err != nil {
|
|
| 311 | 313 |
return err |
| 312 | 314 |
} |
| 313 | 315 |
|
| ... | ... |
@@ -332,13 +334,13 @@ func entrypoint(b *Builder, args []string, attributes map[string]bool, original |
| 332 | 332 |
switch {
|
| 333 | 333 |
case attributes["json"]: |
| 334 | 334 |
// ENTRYPOINT ["echo", "hi"] |
| 335 |
- b.Config.Entrypoint = parsed |
|
| 335 |
+ b.Config.Entrypoint = runconfig.NewEntrypoint(parsed...) |
|
| 336 | 336 |
case len(parsed) == 0: |
| 337 | 337 |
// ENTRYPOINT [] |
| 338 | 338 |
b.Config.Entrypoint = nil |
| 339 | 339 |
default: |
| 340 | 340 |
// ENTRYPOINT echo hi |
| 341 |
- b.Config.Entrypoint = []string{"/bin/sh", "-c", parsed[0]}
|
|
| 341 |
+ b.Config.Entrypoint = runconfig.NewEntrypoint("/bin/sh", "-c", parsed[0])
|
|
| 342 | 342 |
} |
| 343 | 343 |
|
| 344 | 344 |
// when setting the entrypoint if a CMD was not explicitly set then |
| ... | ... |
@@ -61,7 +61,7 @@ func (b *Builder) readContext(context io.Reader) error {
|
| 61 | 61 |
return nil |
| 62 | 62 |
} |
| 63 | 63 |
|
| 64 |
-func (b *Builder) commit(id string, autoCmd []string, comment string) error {
|
|
| 64 |
+func (b *Builder) commit(id string, autoCmd *runconfig.Command, comment string) error {
|
|
| 65 | 65 |
if b.disableCommit {
|
| 66 | 66 |
return nil |
| 67 | 67 |
} |
| ... | ... |
@@ -71,8 +71,8 @@ func (b *Builder) commit(id string, autoCmd []string, comment string) error {
|
| 71 | 71 |
b.Config.Image = b.image |
| 72 | 72 |
if id == "" {
|
| 73 | 73 |
cmd := b.Config.Cmd |
| 74 |
- b.Config.Cmd = []string{"/bin/sh", "-c", "#(nop) " + comment}
|
|
| 75 |
- defer func(cmd []string) { b.Config.Cmd = cmd }(cmd)
|
|
| 74 |
+ b.Config.Cmd = runconfig.NewCommand("/bin/sh", "-c", "#(nop) "+comment)
|
|
| 75 |
+ defer func(cmd *runconfig.Command) { b.Config.Cmd = cmd }(cmd)
|
|
| 76 | 76 |
|
| 77 | 77 |
hit, err := b.probeCache() |
| 78 | 78 |
if err != nil {
|
| ... | ... |
@@ -182,8 +182,8 @@ func (b *Builder) runContextCommand(args []string, allowRemote bool, allowDecomp |
| 182 | 182 |
} |
| 183 | 183 |
|
| 184 | 184 |
cmd := b.Config.Cmd |
| 185 |
- b.Config.Cmd = []string{"/bin/sh", "-c", fmt.Sprintf("#(nop) %s %s in %s", cmdName, srcHash, dest)}
|
|
| 186 |
- defer func(cmd []string) { b.Config.Cmd = cmd }(cmd)
|
|
| 185 |
+ b.Config.Cmd = runconfig.NewCommand("/bin/sh", "-c", fmt.Sprintf("#(nop) %s %s in %s", cmdName, srcHash, dest))
|
|
| 186 |
+ defer func(cmd *runconfig.Command) { b.Config.Cmd = cmd }(cmd)
|
|
| 187 | 187 |
|
| 188 | 188 |
hit, err := b.probeCache() |
| 189 | 189 |
if err != nil {
|
| ... | ... |
@@ -559,12 +559,13 @@ func (b *Builder) create() (*daemon.Container, error) {
|
| 559 | 559 |
b.TmpContainers[c.ID] = struct{}{}
|
| 560 | 560 |
fmt.Fprintf(b.OutStream, " ---> Running in %s\n", stringid.TruncateID(c.ID)) |
| 561 | 561 |
|
| 562 |
- if len(config.Cmd) > 0 {
|
|
| 562 |
+ if config.Cmd.Len() > 0 {
|
|
| 563 | 563 |
// override the entry point that may have been picked up from the base image |
| 564 |
- c.Path = config.Cmd[0] |
|
| 565 |
- c.Args = config.Cmd[1:] |
|
| 564 |
+ s := config.Cmd.Slice() |
|
| 565 |
+ c.Path = s[0] |
|
| 566 |
+ c.Args = s[1:] |
|
| 566 | 567 |
} else {
|
| 567 |
- config.Cmd = []string{}
|
|
| 568 |
+ config.Cmd = runconfig.NewCommand() |
|
| 568 | 569 |
} |
| 569 | 570 |
|
| 570 | 571 |
return c, nil |
| ... | ... |
@@ -4,7 +4,6 @@ import ( |
| 4 | 4 |
"fmt" |
| 5 | 5 |
"strings" |
| 6 | 6 |
|
| 7 |
- "github.com/docker/docker/engine" |
|
| 8 | 7 |
"github.com/docker/docker/graph" |
| 9 | 8 |
"github.com/docker/docker/image" |
| 10 | 9 |
"github.com/docker/docker/pkg/parsers" |
| ... | ... |
@@ -12,13 +11,10 @@ import ( |
| 12 | 12 |
"github.com/docker/libcontainer/label" |
| 13 | 13 |
) |
| 14 | 14 |
|
| 15 |
-func (daemon *Daemon) ContainerCreate(name string, env *engine.Env) (string, []string, error) {
|
|
| 15 |
+func (daemon *Daemon) ContainerCreate(name string, config *runconfig.Config, hostConfig *runconfig.HostConfig) (string, []string, error) {
|
|
| 16 | 16 |
var warnings []string |
| 17 | 17 |
|
| 18 |
- config := runconfig.ContainerConfigFromJob(env) |
|
| 19 |
- hostConfig := runconfig.ContainerHostConfigFromJob(env) |
|
| 20 |
- |
|
| 21 |
- if len(hostConfig.LxcConf) > 0 && !strings.Contains(daemon.ExecutionDriver().Name(), "lxc") {
|
|
| 18 |
+ if hostConfig.LxcConf.Len() > 0 && !strings.Contains(daemon.ExecutionDriver().Name(), "lxc") {
|
|
| 22 | 19 |
return "", warnings, fmt.Errorf("Cannot use --lxc-conf with execdriver: %s", daemon.ExecutionDriver().Name())
|
| 23 | 20 |
} |
| 24 | 21 |
if hostConfig.Memory != 0 && hostConfig.Memory < 4194304 {
|
| ... | ... |
@@ -119,12 +119,8 @@ type Daemon struct {
|
| 119 | 119 |
func (daemon *Daemon) Install(eng *engine.Engine) error {
|
| 120 | 120 |
for name, method := range map[string]engine.Handler{
|
| 121 | 121 |
"container_inspect": daemon.ContainerInspect, |
| 122 |
- "container_stats": daemon.ContainerStats, |
|
| 123 |
- "export": daemon.ContainerExport, |
|
| 124 | 122 |
"info": daemon.CmdInfo, |
| 125 | 123 |
"restart": daemon.ContainerRestart, |
| 126 |
- "stop": daemon.ContainerStop, |
|
| 127 |
- "wait": daemon.ContainerWait, |
|
| 128 | 124 |
"execCreate": daemon.ContainerExecCreate, |
| 129 | 125 |
"execStart": daemon.ContainerExecStart, |
| 130 | 126 |
} {
|
| ... | ... |
@@ -485,7 +481,7 @@ func (daemon *Daemon) mergeAndVerifyConfig(config *runconfig.Config, img *image. |
| 485 | 485 |
return nil, err |
| 486 | 486 |
} |
| 487 | 487 |
} |
| 488 |
- if len(config.Entrypoint) == 0 && len(config.Cmd) == 0 {
|
|
| 488 |
+ if config.Entrypoint.Len() == 0 && config.Cmd.Len() == 0 {
|
|
| 489 | 489 |
return nil, fmt.Errorf("No command specified")
|
| 490 | 490 |
} |
| 491 | 491 |
return warnings, nil |
| ... | ... |
@@ -577,17 +573,20 @@ func (daemon *Daemon) generateHostname(id string, config *runconfig.Config) {
|
| 577 | 577 |
} |
| 578 | 578 |
} |
| 579 | 579 |
|
| 580 |
-func (daemon *Daemon) getEntrypointAndArgs(configEntrypoint, configCmd []string) (string, []string) {
|
|
| 580 |
+func (daemon *Daemon) getEntrypointAndArgs(configEntrypoint *runconfig.Entrypoint, configCmd *runconfig.Command) (string, []string) {
|
|
| 581 | 581 |
var ( |
| 582 | 582 |
entrypoint string |
| 583 | 583 |
args []string |
| 584 | 584 |
) |
| 585 |
- if len(configEntrypoint) != 0 {
|
|
| 586 |
- entrypoint = configEntrypoint[0] |
|
| 587 |
- args = append(configEntrypoint[1:], configCmd...) |
|
| 585 |
+ |
|
| 586 |
+ cmdSlice := configCmd.Slice() |
|
| 587 |
+ if configEntrypoint.Len() != 0 {
|
|
| 588 |
+ eSlice := configEntrypoint.Slice() |
|
| 589 |
+ entrypoint = eSlice[0] |
|
| 590 |
+ args = append(eSlice[1:], cmdSlice...) |
|
| 588 | 591 |
} else {
|
| 589 |
- entrypoint = configCmd[0] |
|
| 590 |
- args = configCmd[1:] |
|
| 592 |
+ entrypoint = cmdSlice[0] |
|
| 593 |
+ args = cmdSlice[1:] |
|
| 591 | 594 |
} |
| 592 | 595 |
return entrypoint, args |
| 593 | 596 |
} |
| ... | ... |
@@ -132,7 +132,8 @@ func (d *Daemon) ContainerExecCreate(job *engine.Job) error {
|
| 132 | 132 |
return err |
| 133 | 133 |
} |
| 134 | 134 |
|
| 135 |
- entrypoint, args := d.getEntrypointAndArgs(nil, config.Cmd) |
|
| 135 |
+ cmd := runconfig.NewCommand(config.Cmd...) |
|
| 136 |
+ entrypoint, args := d.getEntrypointAndArgs(runconfig.NewEntrypoint(), cmd) |
|
| 136 | 137 |
|
| 137 | 138 |
processConfig := execdriver.ProcessConfig{
|
| 138 | 139 |
Tty: config.Tty, |
| ... | ... |
@@ -3,11 +3,10 @@ package daemon |
| 3 | 3 |
import ( |
| 4 | 4 |
"fmt" |
| 5 | 5 |
|
| 6 |
- "github.com/docker/docker/engine" |
|
| 7 | 6 |
"github.com/docker/docker/runconfig" |
| 8 | 7 |
) |
| 9 | 8 |
|
| 10 |
-func (daemon *Daemon) ContainerStart(name string, env *engine.Env) error {
|
|
| 9 |
+func (daemon *Daemon) ContainerStart(name string, hostConfig *runconfig.HostConfig) error {
|
|
| 11 | 10 |
container, err := daemon.Get(name) |
| 12 | 11 |
if err != nil {
|
| 13 | 12 |
return err |
| ... | ... |
@@ -21,15 +20,14 @@ func (daemon *Daemon) ContainerStart(name string, env *engine.Env) error {
|
| 21 | 21 |
return fmt.Errorf("Container already started")
|
| 22 | 22 |
} |
| 23 | 23 |
|
| 24 |
- // If no environment was set, then no hostconfig was passed. |
|
| 25 | 24 |
// This is kept for backward compatibility - hostconfig should be passed when |
| 26 | 25 |
// creating a container, not during start. |
| 27 |
- if len(env.Map()) > 0 {
|
|
| 28 |
- hostConfig := runconfig.ContainerHostConfigFromJob(env) |
|
| 26 |
+ if hostConfig != nil {
|
|
| 29 | 27 |
if err := daemon.setHostConfig(container, hostConfig); err != nil {
|
| 30 | 28 |
return err |
| 31 | 29 |
} |
| 32 | 30 |
} |
| 31 |
+ |
|
| 33 | 32 |
if err := container.Start(); err != nil {
|
| 34 | 33 |
container.LogEvent("die")
|
| 35 | 34 |
return fmt.Errorf("Cannot start container %s: %s", name, err)
|
| ... | ... |
@@ -42,7 +42,8 @@ func mergeLxcConfIntoOptions(hostConfig *runconfig.HostConfig) ([]string, error) |
| 42 | 42 |
|
| 43 | 43 |
// merge in the lxc conf options into the generic config map |
| 44 | 44 |
if lxcConf := hostConfig.LxcConf; lxcConf != nil {
|
| 45 |
- for _, pair := range lxcConf {
|
|
| 45 |
+ lxSlice := lxcConf.Slice() |
|
| 46 |
+ for _, pair := range lxSlice {
|
|
| 46 | 47 |
// because lxc conf gets the driver name lxc.XXXX we need to trim it off |
| 47 | 48 |
// and let the lxc driver add it back later if needed |
| 48 | 49 |
if !strings.Contains(pair.Key, ".") {
|
| ... | ... |
@@ -7,10 +7,11 @@ import ( |
| 7 | 7 |
) |
| 8 | 8 |
|
| 9 | 9 |
func TestMergeLxcConfig(t *testing.T) {
|
| 10 |
+ kv := []runconfig.KeyValuePair{
|
|
| 11 |
+ {"lxc.cgroups.cpuset", "1,2"},
|
|
| 12 |
+ } |
|
| 10 | 13 |
hostConfig := &runconfig.HostConfig{
|
| 11 |
- LxcConf: []runconfig.KeyValuePair{
|
|
| 12 |
- {Key: "lxc.cgroups.cpuset", Value: "1,2"},
|
|
| 13 |
- }, |
|
| 14 |
+ LxcConf: runconfig.NewLxcConfig(kv), |
|
| 14 | 15 |
} |
| 15 | 16 |
|
| 16 | 17 |
out, err := mergeLxcConfIntoOptions(hostConfig) |
| ... | ... |
@@ -31,7 +31,7 @@ func (s *TagStore) History(name string) ([]*types.ImageHistory, error) {
|
| 31 | 31 |
history = append(history, &types.ImageHistory{
|
| 32 | 32 |
ID: img.ID, |
| 33 | 33 |
Created: img.Created.Unix(), |
| 34 |
- CreatedBy: strings.Join(img.ContainerConfig.Cmd, " "), |
|
| 34 |
+ CreatedBy: strings.Join(img.ContainerConfig.Cmd.Slice(), " "), |
|
| 35 | 35 |
Tags: lookupMap[img.ID], |
| 36 | 36 |
Size: img.Size, |
| 37 | 37 |
Comment: img.Comment, |
| ... | ... |
@@ -91,7 +91,7 @@ func TestGetContainersTop(t *testing.T) {
|
| 91 | 91 |
containerID := createTestContainer(eng, |
| 92 | 92 |
&runconfig.Config{
|
| 93 | 93 |
Image: unitTestImageID, |
| 94 |
- Cmd: []string{"/bin/sh", "-c", "cat"},
|
|
| 94 |
+ Cmd: runconfig.NewCommand("/bin/sh", "-c", "cat"),
|
|
| 95 | 95 |
OpenStdin: true, |
| 96 | 96 |
}, |
| 97 | 97 |
t, |
| ... | ... |
@@ -168,7 +168,7 @@ func TestPostCommit(t *testing.T) {
|
| 168 | 168 |
containerID := createTestContainer(eng, |
| 169 | 169 |
&runconfig.Config{
|
| 170 | 170 |
Image: unitTestImageID, |
| 171 |
- Cmd: []string{"touch", "/test"},
|
|
| 171 |
+ Cmd: runconfig.NewCommand("touch", "/test"),
|
|
| 172 | 172 |
}, |
| 173 | 173 |
t, |
| 174 | 174 |
) |
| ... | ... |
@@ -201,9 +201,8 @@ func TestPostContainersCreate(t *testing.T) {
|
| 201 | 201 |
defer mkDaemonFromEngine(eng, t).Nuke() |
| 202 | 202 |
|
| 203 | 203 |
configJSON, err := json.Marshal(&runconfig.Config{
|
| 204 |
- Image: unitTestImageID, |
|
| 205 |
- Memory: 33554432, |
|
| 206 |
- Cmd: []string{"touch", "/test"},
|
|
| 204 |
+ Image: unitTestImageID, |
|
| 205 |
+ Cmd: runconfig.NewCommand("touch", "/test"),
|
|
| 207 | 206 |
}) |
| 208 | 207 |
if err != nil {
|
| 209 | 208 |
t.Fatal(err) |
| ... | ... |
@@ -242,9 +241,8 @@ func TestPostJsonVerify(t *testing.T) {
|
| 242 | 242 |
defer mkDaemonFromEngine(eng, t).Nuke() |
| 243 | 243 |
|
| 244 | 244 |
configJSON, err := json.Marshal(&runconfig.Config{
|
| 245 |
- Image: unitTestImageID, |
|
| 246 |
- Memory: 33554432, |
|
| 247 |
- Cmd: []string{"touch", "/test"},
|
|
| 245 |
+ Image: unitTestImageID, |
|
| 246 |
+ Cmd: runconfig.NewCommand("touch", "/test"),
|
|
| 248 | 247 |
}) |
| 249 | 248 |
if err != nil {
|
| 250 | 249 |
t.Fatal(err) |
| ... | ... |
@@ -330,8 +328,8 @@ func TestPostCreateNull(t *testing.T) {
|
| 330 | 330 |
containerAssertExists(eng, containerID, t) |
| 331 | 331 |
|
| 332 | 332 |
c, _ := daemon.Get(containerID) |
| 333 |
- if c.Config.Cpuset != "" {
|
|
| 334 |
- t.Fatalf("Cpuset should have been empty - instead its:" + c.Config.Cpuset)
|
|
| 333 |
+ if c.HostConfig().CpusetCpus != "" {
|
|
| 334 |
+ t.Fatalf("Cpuset should have been empty - instead its:" + c.HostConfig().CpusetCpus)
|
|
| 335 | 335 |
} |
| 336 | 336 |
} |
| 337 | 337 |
|
| ... | ... |
@@ -342,7 +340,7 @@ func TestPostContainersKill(t *testing.T) {
|
| 342 | 342 |
containerID := createTestContainer(eng, |
| 343 | 343 |
&runconfig.Config{
|
| 344 | 344 |
Image: unitTestImageID, |
| 345 |
- Cmd: []string{"/bin/cat"},
|
|
| 345 |
+ Cmd: runconfig.NewCommand("/bin/cat"),
|
|
| 346 | 346 |
OpenStdin: true, |
| 347 | 347 |
}, |
| 348 | 348 |
t, |
| ... | ... |
@@ -379,7 +377,7 @@ func TestPostContainersRestart(t *testing.T) {
|
| 379 | 379 |
containerID := createTestContainer(eng, |
| 380 | 380 |
&runconfig.Config{
|
| 381 | 381 |
Image: unitTestImageID, |
| 382 |
- Cmd: []string{"/bin/top"},
|
|
| 382 |
+ Cmd: runconfig.NewCommand("/bin/top"),
|
|
| 383 | 383 |
OpenStdin: true, |
| 384 | 384 |
}, |
| 385 | 385 |
t, |
| ... | ... |
@@ -423,7 +421,7 @@ func TestPostContainersStart(t *testing.T) {
|
| 423 | 423 |
eng, |
| 424 | 424 |
&runconfig.Config{
|
| 425 | 425 |
Image: unitTestImageID, |
| 426 |
- Cmd: []string{"/bin/cat"},
|
|
| 426 |
+ Cmd: runconfig.NewCommand("/bin/cat"),
|
|
| 427 | 427 |
OpenStdin: true, |
| 428 | 428 |
}, |
| 429 | 429 |
t, |
| ... | ... |
@@ -473,7 +471,7 @@ func TestPostContainersStop(t *testing.T) {
|
| 473 | 473 |
containerID := createTestContainer(eng, |
| 474 | 474 |
&runconfig.Config{
|
| 475 | 475 |
Image: unitTestImageID, |
| 476 |
- Cmd: []string{"/bin/top"},
|
|
| 476 |
+ Cmd: runconfig.NewCommand("/bin/top"),
|
|
| 477 | 477 |
OpenStdin: true, |
| 478 | 478 |
}, |
| 479 | 479 |
t, |
| ... | ... |
@@ -525,7 +523,7 @@ func TestPostContainersWait(t *testing.T) {
|
| 525 | 525 |
containerID := createTestContainer(eng, |
| 526 | 526 |
&runconfig.Config{
|
| 527 | 527 |
Image: unitTestImageID, |
| 528 |
- Cmd: []string{"/bin/sleep", "1"},
|
|
| 528 |
+ Cmd: runconfig.NewCommand("/bin/sleep", "1"),
|
|
| 529 | 529 |
OpenStdin: true, |
| 530 | 530 |
}, |
| 531 | 531 |
t, |
| ... | ... |
@@ -561,7 +559,7 @@ func TestPostContainersAttach(t *testing.T) {
|
| 561 | 561 |
containerID := createTestContainer(eng, |
| 562 | 562 |
&runconfig.Config{
|
| 563 | 563 |
Image: unitTestImageID, |
| 564 |
- Cmd: []string{"/bin/cat"},
|
|
| 564 |
+ Cmd: runconfig.NewCommand("/bin/cat"),
|
|
| 565 | 565 |
OpenStdin: true, |
| 566 | 566 |
}, |
| 567 | 567 |
t, |
| ... | ... |
@@ -637,7 +635,7 @@ func TestPostContainersAttachStderr(t *testing.T) {
|
| 637 | 637 |
containerID := createTestContainer(eng, |
| 638 | 638 |
&runconfig.Config{
|
| 639 | 639 |
Image: unitTestImageID, |
| 640 |
- Cmd: []string{"/bin/sh", "-c", "/bin/cat >&2"},
|
|
| 640 |
+ Cmd: runconfig.NewCommand("/bin/sh", "-c", "/bin/cat >&2"),
|
|
| 641 | 641 |
OpenStdin: true, |
| 642 | 642 |
}, |
| 643 | 643 |
t, |
| ... | ... |
@@ -818,7 +816,7 @@ func TestPostContainersCopy(t *testing.T) {
|
| 818 | 818 |
containerID := createTestContainer(eng, |
| 819 | 819 |
&runconfig.Config{
|
| 820 | 820 |
Image: unitTestImageID, |
| 821 |
- Cmd: []string{"touch", "/test.txt"},
|
|
| 821 |
+ Cmd: runconfig.NewCommand("touch", "/test.txt"),
|
|
| 822 | 822 |
}, |
| 823 | 823 |
t, |
| 824 | 824 |
) |
| ... | ... |
@@ -14,7 +14,7 @@ func TestRestartStdin(t *testing.T) {
|
| 14 | 14 |
defer nuke(daemon) |
| 15 | 15 |
container, _, err := daemon.Create(&runconfig.Config{
|
| 16 | 16 |
Image: GetTestImage(daemon).ID, |
| 17 |
- Cmd: []string{"cat"},
|
|
| 17 |
+ Cmd: runconfig.NewCommand("cat"),
|
|
| 18 | 18 |
|
| 19 | 19 |
OpenStdin: true, |
| 20 | 20 |
}, |
| ... | ... |
@@ -79,7 +79,7 @@ func TestStdin(t *testing.T) {
|
| 79 | 79 |
defer nuke(daemon) |
| 80 | 80 |
container, _, err := daemon.Create(&runconfig.Config{
|
| 81 | 81 |
Image: GetTestImage(daemon).ID, |
| 82 |
- Cmd: []string{"cat"},
|
|
| 82 |
+ Cmd: runconfig.NewCommand("cat"),
|
|
| 83 | 83 |
|
| 84 | 84 |
OpenStdin: true, |
| 85 | 85 |
}, |
| ... | ... |
@@ -119,7 +119,7 @@ func TestTty(t *testing.T) {
|
| 119 | 119 |
defer nuke(daemon) |
| 120 | 120 |
container, _, err := daemon.Create(&runconfig.Config{
|
| 121 | 121 |
Image: GetTestImage(daemon).ID, |
| 122 |
- Cmd: []string{"cat"},
|
|
| 122 |
+ Cmd: runconfig.NewCommand("cat"),
|
|
| 123 | 123 |
|
| 124 | 124 |
OpenStdin: true, |
| 125 | 125 |
}, |
| ... | ... |
@@ -160,7 +160,7 @@ func BenchmarkRunSequential(b *testing.B) {
|
| 160 | 160 |
for i := 0; i < b.N; i++ {
|
| 161 | 161 |
container, _, err := daemon.Create(&runconfig.Config{
|
| 162 | 162 |
Image: GetTestImage(daemon).ID, |
| 163 |
- Cmd: []string{"echo", "-n", "foo"},
|
|
| 163 |
+ Cmd: runconfig.NewCommand("echo", "-n", "foo"),
|
|
| 164 | 164 |
}, |
| 165 | 165 |
&runconfig.HostConfig{},
|
| 166 | 166 |
"", |
| ... | ... |
@@ -194,7 +194,7 @@ func BenchmarkRunParallel(b *testing.B) {
|
| 194 | 194 |
go func(i int, complete chan error) {
|
| 195 | 195 |
container, _, err := daemon.Create(&runconfig.Config{
|
| 196 | 196 |
Image: GetTestImage(daemon).ID, |
| 197 |
- Cmd: []string{"echo", "-n", "foo"},
|
|
| 197 |
+ Cmd: runconfig.NewCommand("echo", "-n", "foo"),
|
|
| 198 | 198 |
}, |
| 199 | 199 |
&runconfig.HostConfig{},
|
| 200 | 200 |
"", |
| ... | ... |
@@ -255,7 +255,7 @@ func TestDaemonCreate(t *testing.T) {
|
| 255 | 255 |
|
| 256 | 256 |
container, _, err := daemon.Create(&runconfig.Config{
|
| 257 | 257 |
Image: GetTestImage(daemon).ID, |
| 258 |
- Cmd: []string{"ls", "-al"},
|
|
| 258 |
+ Cmd: runconfig.NewCommand("ls", "-al"),
|
|
| 259 | 259 |
}, |
| 260 | 260 |
&runconfig.HostConfig{},
|
| 261 | 261 |
"", |
| ... | ... |
@@ -296,15 +296,16 @@ func TestDaemonCreate(t *testing.T) {
|
| 296 | 296 |
} |
| 297 | 297 |
|
| 298 | 298 |
// Test that conflict error displays correct details |
| 299 |
+ cmd := runconfig.NewCommand("ls", "-al")
|
|
| 299 | 300 |
testContainer, _, _ := daemon.Create( |
| 300 | 301 |
&runconfig.Config{
|
| 301 | 302 |
Image: GetTestImage(daemon).ID, |
| 302 |
- Cmd: []string{"ls", "-al"},
|
|
| 303 |
+ Cmd: cmd, |
|
| 303 | 304 |
}, |
| 304 | 305 |
&runconfig.HostConfig{},
|
| 305 | 306 |
"conflictname", |
| 306 | 307 |
) |
| 307 |
- if _, _, err := daemon.Create(&runconfig.Config{Image: GetTestImage(daemon).ID, Cmd: []string{"ls", "-al"}}, &runconfig.HostConfig{}, testContainer.Name); err == nil || !strings.Contains(err.Error(), stringid.TruncateID(testContainer.ID)) {
|
|
| 308 |
+ if _, _, err := daemon.Create(&runconfig.Config{Image: GetTestImage(daemon).ID, Cmd: cmd}, &runconfig.HostConfig{}, testContainer.Name); err == nil || !strings.Contains(err.Error(), stringid.TruncateID(testContainer.ID)) {
|
|
| 308 | 309 |
t.Fatalf("Name conflict error doesn't include the correct short id. Message was: %v", err)
|
| 309 | 310 |
} |
| 310 | 311 |
|
| ... | ... |
@@ -316,7 +317,7 @@ func TestDaemonCreate(t *testing.T) {
|
| 316 | 316 |
if _, _, err := daemon.Create( |
| 317 | 317 |
&runconfig.Config{
|
| 318 | 318 |
Image: GetTestImage(daemon).ID, |
| 319 |
- Cmd: []string{},
|
|
| 319 |
+ Cmd: runconfig.NewCommand(), |
|
| 320 | 320 |
}, |
| 321 | 321 |
&runconfig.HostConfig{},
|
| 322 | 322 |
"", |
| ... | ... |
@@ -326,7 +327,7 @@ func TestDaemonCreate(t *testing.T) {
|
| 326 | 326 |
|
| 327 | 327 |
config := &runconfig.Config{
|
| 328 | 328 |
Image: GetTestImage(daemon).ID, |
| 329 |
- Cmd: []string{"/bin/ls"},
|
|
| 329 |
+ Cmd: runconfig.NewCommand("/bin/ls"),
|
|
| 330 | 330 |
PortSpecs: []string{"80"},
|
| 331 | 331 |
} |
| 332 | 332 |
container, _, err = daemon.Create(config, &runconfig.HostConfig{}, "")
|
| ... | ... |
@@ -339,7 +340,7 @@ func TestDaemonCreate(t *testing.T) {
|
| 339 | 339 |
// test expose 80:8000 |
| 340 | 340 |
container, warnings, err := daemon.Create(&runconfig.Config{
|
| 341 | 341 |
Image: GetTestImage(daemon).ID, |
| 342 |
- Cmd: []string{"ls", "-al"},
|
|
| 342 |
+ Cmd: runconfig.NewCommand("ls", "-al"),
|
|
| 343 | 343 |
PortSpecs: []string{"80:8000"},
|
| 344 | 344 |
}, |
| 345 | 345 |
&runconfig.HostConfig{},
|
| ... | ... |
@@ -359,7 +360,7 @@ func TestDestroy(t *testing.T) {
|
| 359 | 359 |
|
| 360 | 360 |
container, _, err := daemon.Create(&runconfig.Config{
|
| 361 | 361 |
Image: GetTestImage(daemon).ID, |
| 362 |
- Cmd: []string{"ls", "-al"},
|
|
| 362 |
+ Cmd: runconfig.NewCommand("ls", "-al"),
|
|
| 363 | 363 |
}, |
| 364 | 364 |
&runconfig.HostConfig{},
|
| 365 | 365 |
"") |
| ... | ... |
@@ -451,13 +452,14 @@ func startEchoServerContainer(t *testing.T, proto string) (*daemon.Daemon, *daem |
| 451 | 451 |
p = nat.Port(fmt.Sprintf("%s/%s", strPort, proto))
|
| 452 | 452 |
ep[p] = struct{}{}
|
| 453 | 453 |
|
| 454 |
- env := new(engine.Env) |
|
| 455 |
- env.Set("Image", unitTestImageID)
|
|
| 456 |
- env.SetList("Cmd", []string{"sh", "-c", cmd})
|
|
| 457 |
- env.SetList("PortSpecs", []string{fmt.Sprintf("%s/%s", strPort, proto)})
|
|
| 458 |
- env.SetJson("ExposedPorts", ep)
|
|
| 454 |
+ c := &runconfig.Config{
|
|
| 455 |
+ Image: unitTestImageID, |
|
| 456 |
+ Cmd: runconfig.NewCommand("sh", "-c", cmd),
|
|
| 457 |
+ PortSpecs: []string{fmt.Sprintf("%s/%s", strPort, proto)},
|
|
| 458 |
+ ExposedPorts: ep, |
|
| 459 |
+ } |
|
| 459 | 460 |
|
| 460 |
- id, _, err = daemon.ContainerCreate(unitTestImageID, env) |
|
| 461 |
+ id, _, err = daemon.ContainerCreate(unitTestImageID, c, &runconfig.HostConfig{})
|
|
| 461 | 462 |
// FIXME: this relies on the undocumented behavior of daemon.Create |
| 462 | 463 |
// which will return a nil error AND container if the exposed ports |
| 463 | 464 |
// are invalid. That behavior should be fixed! |
| ... | ... |
@@ -468,16 +470,7 @@ func startEchoServerContainer(t *testing.T, proto string) (*daemon.Daemon, *daem |
| 468 | 468 |
|
| 469 | 469 |
} |
| 470 | 470 |
|
| 471 |
- portBindings := make(map[nat.Port][]nat.PortBinding) |
|
| 472 |
- portBindings[p] = []nat.PortBinding{
|
|
| 473 |
- {},
|
|
| 474 |
- } |
|
| 475 |
- |
|
| 476 |
- env := new(engine.Env) |
|
| 477 |
- if err := env.SetJson("PortsBindings", portBindings); err != nil {
|
|
| 478 |
- t.Fatal(err) |
|
| 479 |
- } |
|
| 480 |
- if err := daemon.ContainerStart(id, env); err != nil {
|
|
| 471 |
+ if err := daemon.ContainerStart(id, &runconfig.HostConfig{}); err != nil {
|
|
| 481 | 472 |
t.Fatal(err) |
| 482 | 473 |
} |
| 483 | 474 |
|
| ... | ... |
@@ -728,12 +721,7 @@ func TestContainerNameValidation(t *testing.T) {
|
| 728 | 728 |
t.Fatal(err) |
| 729 | 729 |
} |
| 730 | 730 |
|
| 731 |
- env := new(engine.Env) |
|
| 732 |
- if err := env.Import(config); err != nil {
|
|
| 733 |
- t.Fatal(err) |
|
| 734 |
- } |
|
| 735 |
- |
|
| 736 |
- containerId, _, err := daemon.ContainerCreate(test.Name, env) |
|
| 731 |
+ containerId, _, err := daemon.ContainerCreate(test.Name, config, &runconfig.HostConfig{})
|
|
| 737 | 732 |
if err != nil {
|
| 738 | 733 |
if !test.Valid {
|
| 739 | 734 |
continue |
| ... | ... |
@@ -872,7 +860,7 @@ func TestDestroyWithInitLayer(t *testing.T) {
|
| 872 | 872 |
|
| 873 | 873 |
container, _, err := daemon.Create(&runconfig.Config{
|
| 874 | 874 |
Image: GetTestImage(daemon).ID, |
| 875 |
- Cmd: []string{"ls", "-al"},
|
|
| 875 |
+ Cmd: runconfig.NewCommand("ls", "-al"),
|
|
| 876 | 876 |
}, |
| 877 | 877 |
&runconfig.HostConfig{},
|
| 878 | 878 |
"") |
| ... | ... |
@@ -44,11 +44,7 @@ func mkDaemon(f Fataler) *daemon.Daemon {
|
| 44 | 44 |
} |
| 45 | 45 |
|
| 46 | 46 |
func createNamedTestContainer(eng *engine.Engine, config *runconfig.Config, f Fataler, name string) (shortId string) {
|
| 47 |
- env := new(engine.Env) |
|
| 48 |
- if err := env.Import(config); err != nil {
|
|
| 49 |
- f.Fatal(err) |
|
| 50 |
- } |
|
| 51 |
- containerId, _, err := getDaemon(eng).ContainerCreate(name, env) |
|
| 47 |
+ containerId, _, err := getDaemon(eng).ContainerCreate(name, config, &runconfig.HostConfig{})
|
|
| 52 | 48 |
if err != nil {
|
| 53 | 49 |
f.Fatal(err) |
| 54 | 50 |
} |
| ... | ... |
@@ -60,8 +56,7 @@ func createTestContainer(eng *engine.Engine, config *runconfig.Config, f Fataler |
| 60 | 60 |
} |
| 61 | 61 |
|
| 62 | 62 |
func startContainer(eng *engine.Engine, id string, t Fataler) {
|
| 63 |
- env := new(engine.Env) |
|
| 64 |
- if err := getDaemon(eng).ContainerStart(id, env); err != nil {
|
|
| 63 |
+ if err := getDaemon(eng).ContainerStart(id, &runconfig.HostConfig{}); err != nil {
|
|
| 65 | 64 |
t.Fatal(err) |
| 66 | 65 |
} |
| 67 | 66 |
} |
| ... | ... |
@@ -10,25 +10,25 @@ func Compare(a, b *Config) bool {
|
| 10 | 10 |
if a.AttachStdout != b.AttachStdout || |
| 11 | 11 |
a.AttachStderr != b.AttachStderr || |
| 12 | 12 |
a.User != b.User || |
| 13 |
- a.Memory != b.Memory || |
|
| 14 |
- a.MemorySwap != b.MemorySwap || |
|
| 15 |
- a.CpuShares != b.CpuShares || |
|
| 16 | 13 |
a.OpenStdin != b.OpenStdin || |
| 17 | 14 |
a.Tty != b.Tty {
|
| 18 | 15 |
return false |
| 19 | 16 |
} |
| 20 |
- if len(a.Cmd) != len(b.Cmd) || |
|
| 17 |
+ |
|
| 18 |
+ if a.Cmd.Len() != b.Cmd.Len() || |
|
| 21 | 19 |
len(a.Env) != len(b.Env) || |
| 22 | 20 |
len(a.Labels) != len(b.Labels) || |
| 23 | 21 |
len(a.PortSpecs) != len(b.PortSpecs) || |
| 24 | 22 |
len(a.ExposedPorts) != len(b.ExposedPorts) || |
| 25 |
- len(a.Entrypoint) != len(b.Entrypoint) || |
|
| 23 |
+ a.Entrypoint.Len() != b.Entrypoint.Len() || |
|
| 26 | 24 |
len(a.Volumes) != len(b.Volumes) {
|
| 27 | 25 |
return false |
| 28 | 26 |
} |
| 29 | 27 |
|
| 30 |
- for i := 0; i < len(a.Cmd); i++ {
|
|
| 31 |
- if a.Cmd[i] != b.Cmd[i] {
|
|
| 28 |
+ aCmd := a.Cmd.Slice() |
|
| 29 |
+ bCmd := b.Cmd.Slice() |
|
| 30 |
+ for i := 0; i < len(aCmd); i++ {
|
|
| 31 |
+ if aCmd[i] != bCmd[i] {
|
|
| 32 | 32 |
return false |
| 33 | 33 |
} |
| 34 | 34 |
} |
| ... | ... |
@@ -52,8 +52,11 @@ func Compare(a, b *Config) bool {
|
| 52 | 52 |
return false |
| 53 | 53 |
} |
| 54 | 54 |
} |
| 55 |
- for i := 0; i < len(a.Entrypoint); i++ {
|
|
| 56 |
- if a.Entrypoint[i] != b.Entrypoint[i] {
|
|
| 55 |
+ |
|
| 56 |
+ aEntrypoint := a.Entrypoint.Slice() |
|
| 57 |
+ bEntrypoint := b.Entrypoint.Slice() |
|
| 58 |
+ for i := 0; i < len(aEntrypoint); i++ {
|
|
| 59 |
+ if aEntrypoint[i] != bEntrypoint[i] {
|
|
| 57 | 60 |
return false |
| 58 | 61 |
} |
| 59 | 62 |
} |
| ... | ... |
@@ -1,10 +1,103 @@ |
| 1 | 1 |
package runconfig |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 |
- "github.com/docker/docker/engine" |
|
| 4 |
+ "encoding/json" |
|
| 5 |
+ "io" |
|
| 6 |
+ |
|
| 5 | 7 |
"github.com/docker/docker/nat" |
| 6 | 8 |
) |
| 7 | 9 |
|
| 10 |
+// Entrypoint encapsulates the container entrypoint. |
|
| 11 |
+// It might be represented as a string or an array of strings. |
|
| 12 |
+// We need to override the json decoder to accept both options. |
|
| 13 |
+// The JSON decoder will fail if the api sends an string and |
|
| 14 |
+// we try to decode it into an array of string. |
|
| 15 |
+type Entrypoint struct {
|
|
| 16 |
+ parts []string |
|
| 17 |
+} |
|
| 18 |
+ |
|
| 19 |
+func (e *Entrypoint) MarshalJSON() ([]byte, error) {
|
|
| 20 |
+ if e == nil {
|
|
| 21 |
+ return []byte{}, nil
|
|
| 22 |
+ } |
|
| 23 |
+ return json.Marshal(e.Slice()) |
|
| 24 |
+} |
|
| 25 |
+ |
|
| 26 |
+// UnmarshalJSON decoded the entrypoint whether it's a string or an array of strings. |
|
| 27 |
+func (e *Entrypoint) UnmarshalJSON(b []byte) error {
|
|
| 28 |
+ if len(b) == 0 {
|
|
| 29 |
+ return nil |
|
| 30 |
+ } |
|
| 31 |
+ |
|
| 32 |
+ p := make([]string, 0, 1) |
|
| 33 |
+ if err := json.Unmarshal(b, &p); err != nil {
|
|
| 34 |
+ p = append(p, string(b)) |
|
| 35 |
+ } |
|
| 36 |
+ e.parts = p |
|
| 37 |
+ return nil |
|
| 38 |
+} |
|
| 39 |
+ |
|
| 40 |
+func (e *Entrypoint) Len() int {
|
|
| 41 |
+ if e == nil {
|
|
| 42 |
+ return 0 |
|
| 43 |
+ } |
|
| 44 |
+ return len(e.parts) |
|
| 45 |
+} |
|
| 46 |
+ |
|
| 47 |
+func (e *Entrypoint) Slice() []string {
|
|
| 48 |
+ if e == nil {
|
|
| 49 |
+ return nil |
|
| 50 |
+ } |
|
| 51 |
+ return e.parts |
|
| 52 |
+} |
|
| 53 |
+ |
|
| 54 |
+func NewEntrypoint(parts ...string) *Entrypoint {
|
|
| 55 |
+ return &Entrypoint{parts}
|
|
| 56 |
+} |
|
| 57 |
+ |
|
| 58 |
+type Command struct {
|
|
| 59 |
+ parts []string |
|
| 60 |
+} |
|
| 61 |
+ |
|
| 62 |
+func (e *Command) MarshalJSON() ([]byte, error) {
|
|
| 63 |
+ if e == nil {
|
|
| 64 |
+ return []byte{}, nil
|
|
| 65 |
+ } |
|
| 66 |
+ return json.Marshal(e.Slice()) |
|
| 67 |
+} |
|
| 68 |
+ |
|
| 69 |
+// UnmarshalJSON decoded the entrypoint whether it's a string or an array of strings. |
|
| 70 |
+func (e *Command) UnmarshalJSON(b []byte) error {
|
|
| 71 |
+ if len(b) == 0 {
|
|
| 72 |
+ return nil |
|
| 73 |
+ } |
|
| 74 |
+ |
|
| 75 |
+ p := make([]string, 0, 1) |
|
| 76 |
+ if err := json.Unmarshal(b, &p); err != nil {
|
|
| 77 |
+ p = append(p, string(b)) |
|
| 78 |
+ } |
|
| 79 |
+ e.parts = p |
|
| 80 |
+ return nil |
|
| 81 |
+} |
|
| 82 |
+ |
|
| 83 |
+func (e *Command) Len() int {
|
|
| 84 |
+ if e == nil {
|
|
| 85 |
+ return 0 |
|
| 86 |
+ } |
|
| 87 |
+ return len(e.parts) |
|
| 88 |
+} |
|
| 89 |
+ |
|
| 90 |
+func (e *Command) Slice() []string {
|
|
| 91 |
+ if e == nil {
|
|
| 92 |
+ return nil |
|
| 93 |
+ } |
|
| 94 |
+ return e.parts |
|
| 95 |
+} |
|
| 96 |
+ |
|
| 97 |
+func NewCommand(parts ...string) *Command {
|
|
| 98 |
+ return &Command{parts}
|
|
| 99 |
+} |
|
| 100 |
+ |
|
| 8 | 101 |
// Note: the Config structure should hold only portable information about the container. |
| 9 | 102 |
// Here, "portable" means "independent from the host we are running on". |
| 10 | 103 |
// Non-portable information *should* appear in HostConfig. |
| ... | ... |
@@ -12,10 +105,6 @@ type Config struct {
|
| 12 | 12 |
Hostname string |
| 13 | 13 |
Domainname string |
| 14 | 14 |
User string |
| 15 |
- Memory int64 // FIXME: we keep it for backward compatibility, it has been moved to hostConfig. |
|
| 16 |
- MemorySwap int64 // FIXME: it has been moved to hostConfig. |
|
| 17 |
- CpuShares int64 // FIXME: it has been moved to hostConfig. |
|
| 18 |
- Cpuset string // FIXME: it has been moved to hostConfig and renamed to CpusetCpus. |
|
| 19 | 15 |
AttachStdin bool |
| 20 | 16 |
AttachStdout bool |
| 21 | 17 |
AttachStderr bool |
| ... | ... |
@@ -25,53 +114,37 @@ type Config struct {
|
| 25 | 25 |
OpenStdin bool // Open stdin |
| 26 | 26 |
StdinOnce bool // If true, close stdin after the 1 attached client disconnects. |
| 27 | 27 |
Env []string |
| 28 |
- Cmd []string |
|
| 28 |
+ Cmd *Command |
|
| 29 | 29 |
Image string // Name of the image as it was passed by the operator (eg. could be symbolic) |
| 30 | 30 |
Volumes map[string]struct{}
|
| 31 | 31 |
WorkingDir string |
| 32 |
- Entrypoint []string |
|
| 32 |
+ Entrypoint *Entrypoint |
|
| 33 | 33 |
NetworkDisabled bool |
| 34 | 34 |
MacAddress string |
| 35 | 35 |
OnBuild []string |
| 36 | 36 |
Labels map[string]string |
| 37 | 37 |
} |
| 38 | 38 |
|
| 39 |
-func ContainerConfigFromJob(env *engine.Env) *Config {
|
|
| 40 |
- config := &Config{
|
|
| 41 |
- Hostname: env.Get("Hostname"),
|
|
| 42 |
- Domainname: env.Get("Domainname"),
|
|
| 43 |
- User: env.Get("User"),
|
|
| 44 |
- Memory: env.GetInt64("Memory"),
|
|
| 45 |
- MemorySwap: env.GetInt64("MemorySwap"),
|
|
| 46 |
- CpuShares: env.GetInt64("CpuShares"),
|
|
| 47 |
- Cpuset: env.Get("Cpuset"),
|
|
| 48 |
- AttachStdin: env.GetBool("AttachStdin"),
|
|
| 49 |
- AttachStdout: env.GetBool("AttachStdout"),
|
|
| 50 |
- AttachStderr: env.GetBool("AttachStderr"),
|
|
| 51 |
- Tty: env.GetBool("Tty"),
|
|
| 52 |
- OpenStdin: env.GetBool("OpenStdin"),
|
|
| 53 |
- StdinOnce: env.GetBool("StdinOnce"),
|
|
| 54 |
- Image: env.Get("Image"),
|
|
| 55 |
- WorkingDir: env.Get("WorkingDir"),
|
|
| 56 |
- NetworkDisabled: env.GetBool("NetworkDisabled"),
|
|
| 57 |
- MacAddress: env.Get("MacAddress"),
|
|
| 58 |
- } |
|
| 59 |
- env.GetJson("ExposedPorts", &config.ExposedPorts)
|
|
| 60 |
- env.GetJson("Volumes", &config.Volumes)
|
|
| 61 |
- if PortSpecs := env.GetList("PortSpecs"); PortSpecs != nil {
|
|
| 62 |
- config.PortSpecs = PortSpecs |
|
| 63 |
- } |
|
| 64 |
- if Env := env.GetList("Env"); Env != nil {
|
|
| 65 |
- config.Env = Env |
|
| 66 |
- } |
|
| 67 |
- if Cmd := env.GetList("Cmd"); Cmd != nil {
|
|
| 68 |
- config.Cmd = Cmd |
|
| 39 |
+type ContainerConfigWrapper struct {
|
|
| 40 |
+ *Config |
|
| 41 |
+ *hostConfigWrapper |
|
| 42 |
+} |
|
| 43 |
+ |
|
| 44 |
+func (c ContainerConfigWrapper) HostConfig() *HostConfig {
|
|
| 45 |
+ if c.hostConfigWrapper == nil {
|
|
| 46 |
+ return new(HostConfig) |
|
| 69 | 47 |
} |
| 70 | 48 |
|
| 71 |
- env.GetJson("Labels", &config.Labels)
|
|
| 49 |
+ return c.hostConfigWrapper.GetHostConfig() |
|
| 50 |
+} |
|
| 72 | 51 |
|
| 73 |
- if Entrypoint := env.GetList("Entrypoint"); Entrypoint != nil {
|
|
| 74 |
- config.Entrypoint = Entrypoint |
|
| 52 |
+func DecodeContainerConfig(src io.Reader) (*Config, *HostConfig, error) {
|
|
| 53 |
+ decoder := json.NewDecoder(src) |
|
| 54 |
+ |
|
| 55 |
+ var w ContainerConfigWrapper |
|
| 56 |
+ if err := decoder.Decode(&w); err != nil {
|
|
| 57 |
+ return nil, nil, err |
|
| 75 | 58 |
} |
| 76 |
- return config |
|
| 59 |
+ |
|
| 60 |
+ return w.Config, w.HostConfig(), nil |
|
| 77 | 61 |
} |
| ... | ... |
@@ -1,7 +1,9 @@ |
| 1 | 1 |
package runconfig |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 |
+ "bytes" |
|
| 4 | 5 |
"fmt" |
| 6 |
+ "io/ioutil" |
|
| 5 | 7 |
"strings" |
| 6 | 8 |
"testing" |
| 7 | 9 |
|
| ... | ... |
@@ -260,5 +262,39 @@ func TestMerge(t *testing.T) {
|
| 260 | 260 |
t.Fatalf("Expected %q or %q or %q or %q, found %s", 0, 1111, 2222, 3333, portSpecs)
|
| 261 | 261 |
} |
| 262 | 262 |
} |
| 263 |
+} |
|
| 264 |
+ |
|
| 265 |
+func TestDecodeContainerConfig(t *testing.T) {
|
|
| 266 |
+ fixtures := []struct {
|
|
| 267 |
+ file string |
|
| 268 |
+ entrypoint *Entrypoint |
|
| 269 |
+ }{
|
|
| 270 |
+ {"fixtures/container_config_1_14.json", NewEntrypoint()},
|
|
| 271 |
+ {"fixtures/container_config_1_17.json", NewEntrypoint("bash")},
|
|
| 272 |
+ {"fixtures/container_config_1_19.json", NewEntrypoint("bash")},
|
|
| 273 |
+ } |
|
| 274 |
+ |
|
| 275 |
+ for _, f := range fixtures {
|
|
| 276 |
+ b, err := ioutil.ReadFile(f.file) |
|
| 277 |
+ if err != nil {
|
|
| 278 |
+ t.Fatal(err) |
|
| 279 |
+ } |
|
| 280 |
+ |
|
| 281 |
+ c, h, err := DecodeContainerConfig(bytes.NewReader(b)) |
|
| 282 |
+ if err != nil {
|
|
| 283 |
+ t.Fatal(fmt.Errorf("Error parsing %s: %v", f, err))
|
|
| 284 |
+ } |
|
| 263 | 285 |
|
| 286 |
+ if c.Image != "ubuntu" {
|
|
| 287 |
+ t.Fatalf("Expected ubuntu image, found %s\n", c.Image)
|
|
| 288 |
+ } |
|
| 289 |
+ |
|
| 290 |
+ if c.Entrypoint.Len() != f.entrypoint.Len() {
|
|
| 291 |
+ t.Fatalf("Expected %v, found %v\n", f.entrypoint, c.Entrypoint)
|
|
| 292 |
+ } |
|
| 293 |
+ |
|
| 294 |
+ if h.Memory != 1000 {
|
|
| 295 |
+ t.Fatalf("Expected memory to be 1000, found %d\n", h.Memory)
|
|
| 296 |
+ } |
|
| 297 |
+ } |
|
| 264 | 298 |
} |
| 265 | 299 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,30 @@ |
| 0 |
+{
|
|
| 1 |
+ "Hostname":"", |
|
| 2 |
+ "Domainname": "", |
|
| 3 |
+ "User":"", |
|
| 4 |
+ "Memory": 1000, |
|
| 5 |
+ "MemorySwap":0, |
|
| 6 |
+ "CpuShares": 512, |
|
| 7 |
+ "Cpuset": "0,1", |
|
| 8 |
+ "AttachStdin":false, |
|
| 9 |
+ "AttachStdout":true, |
|
| 10 |
+ "AttachStderr":true, |
|
| 11 |
+ "PortSpecs":null, |
|
| 12 |
+ "Tty":false, |
|
| 13 |
+ "OpenStdin":false, |
|
| 14 |
+ "StdinOnce":false, |
|
| 15 |
+ "Env":null, |
|
| 16 |
+ "Cmd":[ |
|
| 17 |
+ "bash" |
|
| 18 |
+ ], |
|
| 19 |
+ "Image":"ubuntu", |
|
| 20 |
+ "Volumes":{
|
|
| 21 |
+ "/tmp": {}
|
|
| 22 |
+ }, |
|
| 23 |
+ "WorkingDir":"", |
|
| 24 |
+ "NetworkDisabled": false, |
|
| 25 |
+ "ExposedPorts":{
|
|
| 26 |
+ "22/tcp": {}
|
|
| 27 |
+ }, |
|
| 28 |
+ "RestartPolicy": { "Name": "always" }
|
|
| 29 |
+} |
| 0 | 30 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,49 @@ |
| 0 |
+{
|
|
| 1 |
+ "Hostname": "", |
|
| 2 |
+ "Domainname": "", |
|
| 3 |
+ "User": "", |
|
| 4 |
+ "Memory": 1000, |
|
| 5 |
+ "MemorySwap": 0, |
|
| 6 |
+ "CpuShares": 512, |
|
| 7 |
+ "Cpuset": "0,1", |
|
| 8 |
+ "AttachStdin": false, |
|
| 9 |
+ "AttachStdout": true, |
|
| 10 |
+ "AttachStderr": true, |
|
| 11 |
+ "Tty": false, |
|
| 12 |
+ "OpenStdin": false, |
|
| 13 |
+ "StdinOnce": false, |
|
| 14 |
+ "Env": null, |
|
| 15 |
+ "Cmd": [ |
|
| 16 |
+ "date" |
|
| 17 |
+ ], |
|
| 18 |
+ "Entrypoint": "bash", |
|
| 19 |
+ "Image": "ubuntu", |
|
| 20 |
+ "Volumes": {
|
|
| 21 |
+ "/tmp": {}
|
|
| 22 |
+ }, |
|
| 23 |
+ "WorkingDir": "", |
|
| 24 |
+ "NetworkDisabled": false, |
|
| 25 |
+ "MacAddress": "12:34:56:78:9a:bc", |
|
| 26 |
+ "ExposedPorts": {
|
|
| 27 |
+ "22/tcp": {}
|
|
| 28 |
+ }, |
|
| 29 |
+ "SecurityOpt": [""], |
|
| 30 |
+ "HostConfig": {
|
|
| 31 |
+ "Binds": ["/tmp:/tmp"], |
|
| 32 |
+ "Links": ["redis3:redis"], |
|
| 33 |
+ "LxcConf": {"lxc.utsname":"docker"},
|
|
| 34 |
+ "PortBindings": { "22/tcp": [{ "HostPort": "11022" }] },
|
|
| 35 |
+ "PublishAllPorts": false, |
|
| 36 |
+ "Privileged": false, |
|
| 37 |
+ "ReadonlyRootfs": false, |
|
| 38 |
+ "Dns": ["8.8.8.8"], |
|
| 39 |
+ "DnsSearch": [""], |
|
| 40 |
+ "ExtraHosts": null, |
|
| 41 |
+ "VolumesFrom": ["parent", "other:ro"], |
|
| 42 |
+ "CapAdd": ["NET_ADMIN"], |
|
| 43 |
+ "CapDrop": ["MKNOD"], |
|
| 44 |
+ "RestartPolicy": { "Name": "", "MaximumRetryCount": 0 },
|
|
| 45 |
+ "NetworkMode": "bridge", |
|
| 46 |
+ "Devices": [] |
|
| 47 |
+ } |
|
| 48 |
+} |
| 0 | 49 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,57 @@ |
| 0 |
+{
|
|
| 1 |
+ "Hostname": "", |
|
| 2 |
+ "Domainname": "", |
|
| 3 |
+ "User": "", |
|
| 4 |
+ "AttachStdin": false, |
|
| 5 |
+ "AttachStdout": true, |
|
| 6 |
+ "AttachStderr": true, |
|
| 7 |
+ "Tty": false, |
|
| 8 |
+ "OpenStdin": false, |
|
| 9 |
+ "StdinOnce": false, |
|
| 10 |
+ "Env": null, |
|
| 11 |
+ "Cmd": [ |
|
| 12 |
+ "date" |
|
| 13 |
+ ], |
|
| 14 |
+ "Entrypoint": "bash", |
|
| 15 |
+ "Image": "ubuntu", |
|
| 16 |
+ "Labels": {
|
|
| 17 |
+ "com.example.vendor": "Acme", |
|
| 18 |
+ "com.example.license": "GPL", |
|
| 19 |
+ "com.example.version": "1.0" |
|
| 20 |
+ }, |
|
| 21 |
+ "Volumes": {
|
|
| 22 |
+ "/tmp": {}
|
|
| 23 |
+ }, |
|
| 24 |
+ "WorkingDir": "", |
|
| 25 |
+ "NetworkDisabled": false, |
|
| 26 |
+ "MacAddress": "12:34:56:78:9a:bc", |
|
| 27 |
+ "ExposedPorts": {
|
|
| 28 |
+ "22/tcp": {}
|
|
| 29 |
+ }, |
|
| 30 |
+ "HostConfig": {
|
|
| 31 |
+ "Binds": ["/tmp:/tmp"], |
|
| 32 |
+ "Links": ["redis3:redis"], |
|
| 33 |
+ "LxcConf": {"lxc.utsname":"docker"},
|
|
| 34 |
+ "Memory": 1000, |
|
| 35 |
+ "MemorySwap": 0, |
|
| 36 |
+ "CpuShares": 512, |
|
| 37 |
+ "CpusetCpus": "0,1", |
|
| 38 |
+ "PortBindings": { "22/tcp": [{ "HostPort": "11022" }] },
|
|
| 39 |
+ "PublishAllPorts": false, |
|
| 40 |
+ "Privileged": false, |
|
| 41 |
+ "ReadonlyRootfs": false, |
|
| 42 |
+ "Dns": ["8.8.8.8"], |
|
| 43 |
+ "DnsSearch": [""], |
|
| 44 |
+ "ExtraHosts": null, |
|
| 45 |
+ "VolumesFrom": ["parent", "other:ro"], |
|
| 46 |
+ "CapAdd": ["NET_ADMIN"], |
|
| 47 |
+ "CapDrop": ["MKNOD"], |
|
| 48 |
+ "RestartPolicy": { "Name": "", "MaximumRetryCount": 0 },
|
|
| 49 |
+ "NetworkMode": "bridge", |
|
| 50 |
+ "Devices": [], |
|
| 51 |
+ "Ulimits": [{}],
|
|
| 52 |
+ "LogConfig": { "Type": "json-file", "Config": {} },
|
|
| 53 |
+ "SecurityOpt": [""], |
|
| 54 |
+ "CgroupParent": "" |
|
| 55 |
+ } |
|
| 56 |
+} |
| ... | ... |
@@ -1,9 +1,10 @@ |
| 1 | 1 |
package runconfig |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 |
+ "encoding/json" |
|
| 5 |
+ "io" |
|
| 4 | 6 |
"strings" |
| 5 | 7 |
|
| 6 |
- "github.com/docker/docker/engine" |
|
| 7 | 8 |
"github.com/docker/docker/nat" |
| 8 | 9 |
"github.com/docker/docker/pkg/ulimit" |
| 9 | 10 |
) |
| ... | ... |
@@ -108,10 +109,59 @@ type LogConfig struct {
|
| 108 | 108 |
Config map[string]string |
| 109 | 109 |
} |
| 110 | 110 |
|
| 111 |
+type LxcConfig struct {
|
|
| 112 |
+ values []KeyValuePair |
|
| 113 |
+} |
|
| 114 |
+ |
|
| 115 |
+func (c *LxcConfig) MarshalJSON() ([]byte, error) {
|
|
| 116 |
+ if c == nil {
|
|
| 117 |
+ return []byte{}, nil
|
|
| 118 |
+ } |
|
| 119 |
+ return json.Marshal(c.Slice()) |
|
| 120 |
+} |
|
| 121 |
+ |
|
| 122 |
+func (c *LxcConfig) UnmarshalJSON(b []byte) error {
|
|
| 123 |
+ if len(b) == 0 {
|
|
| 124 |
+ return nil |
|
| 125 |
+ } |
|
| 126 |
+ |
|
| 127 |
+ var kv []KeyValuePair |
|
| 128 |
+ if err := json.Unmarshal(b, &kv); err != nil {
|
|
| 129 |
+ var h map[string]string |
|
| 130 |
+ if err := json.Unmarshal(b, &h); err != nil {
|
|
| 131 |
+ return err |
|
| 132 |
+ } |
|
| 133 |
+ for k, v := range h {
|
|
| 134 |
+ kv = append(kv, KeyValuePair{k, v})
|
|
| 135 |
+ } |
|
| 136 |
+ } |
|
| 137 |
+ c.values = kv |
|
| 138 |
+ |
|
| 139 |
+ return nil |
|
| 140 |
+} |
|
| 141 |
+ |
|
| 142 |
+func (c *LxcConfig) Len() int {
|
|
| 143 |
+ if c == nil {
|
|
| 144 |
+ return 0 |
|
| 145 |
+ } |
|
| 146 |
+ return len(c.values) |
|
| 147 |
+} |
|
| 148 |
+ |
|
| 149 |
+func (c *LxcConfig) Slice() []KeyValuePair {
|
|
| 150 |
+ if c == nil {
|
|
| 151 |
+ return nil |
|
| 152 |
+ } |
|
| 153 |
+ return c.values |
|
| 154 |
+} |
|
| 155 |
+ |
|
| 156 |
+func NewLxcConfig(values []KeyValuePair) *LxcConfig {
|
|
| 157 |
+ return &LxcConfig{values}
|
|
| 158 |
+} |
|
| 159 |
+ |
|
| 111 | 160 |
type HostConfig struct {
|
| 112 | 161 |
Binds []string |
| 113 | 162 |
ContainerIDFile string |
| 114 |
- LxcConf []KeyValuePair |
|
| 163 |
+ LxcConf *LxcConfig |
|
| 115 | 164 |
Memory int64 // Memory limit (in bytes) |
| 116 | 165 |
MemorySwap int64 // Total memory usage (memory + swap); set `-1` to disable swap |
| 117 | 166 |
CpuShares int64 // CPU shares (relative weight vs. other containers) |
| ... | ... |
@@ -138,96 +188,55 @@ type HostConfig struct {
|
| 138 | 138 |
CgroupParent string // Parent cgroup. |
| 139 | 139 |
} |
| 140 | 140 |
|
| 141 |
-// This is used by the create command when you want to set both the |
|
| 142 |
-// Config and the HostConfig in the same call |
|
| 143 |
-type ConfigAndHostConfig struct {
|
|
| 144 |
- Config |
|
| 145 |
- HostConfig HostConfig |
|
| 141 |
+func MergeConfigs(config *Config, hostConfig *HostConfig) *ContainerConfigWrapper {
|
|
| 142 |
+ return &ContainerConfigWrapper{
|
|
| 143 |
+ config, |
|
| 144 |
+ &hostConfigWrapper{InnerHostConfig: hostConfig},
|
|
| 145 |
+ } |
|
| 146 | 146 |
} |
| 147 | 147 |
|
| 148 |
-func MergeConfigs(config *Config, hostConfig *HostConfig) *ConfigAndHostConfig {
|
|
| 149 |
- return &ConfigAndHostConfig{
|
|
| 150 |
- *config, |
|
| 151 |
- *hostConfig, |
|
| 152 |
- } |
|
| 148 |
+type hostConfigWrapper struct {
|
|
| 149 |
+ InnerHostConfig *HostConfig `json:"HostConfig,omitempty"` |
|
| 150 |
+ Cpuset string `json:",omitempty"` // Deprecated. Exported for backwards compatibility. |
|
| 151 |
+ |
|
| 152 |
+ *HostConfig // Deprecated. Exported to read attrubutes from json that are not in the inner host config structure. |
|
| 153 | 153 |
} |
| 154 | 154 |
|
| 155 |
-func ContainerHostConfigFromJob(env *engine.Env) *HostConfig {
|
|
| 156 |
- if env.Exists("HostConfig") {
|
|
| 157 |
- hostConfig := HostConfig{}
|
|
| 158 |
- env.GetJson("HostConfig", &hostConfig)
|
|
| 155 |
+func (w hostConfigWrapper) GetHostConfig() *HostConfig {
|
|
| 156 |
+ hc := w.HostConfig |
|
| 159 | 157 |
|
| 160 |
- // FIXME: These are for backward compatibility, if people use these |
|
| 161 |
- // options with `HostConfig`, we should still make them workable. |
|
| 162 |
- if env.Exists("Memory") && hostConfig.Memory == 0 {
|
|
| 163 |
- hostConfig.Memory = env.GetInt64("Memory")
|
|
| 164 |
- } |
|
| 165 |
- if env.Exists("MemorySwap") && hostConfig.MemorySwap == 0 {
|
|
| 166 |
- hostConfig.MemorySwap = env.GetInt64("MemorySwap")
|
|
| 158 |
+ if hc == nil && w.InnerHostConfig != nil {
|
|
| 159 |
+ hc = w.InnerHostConfig |
|
| 160 |
+ } else if w.InnerHostConfig != nil {
|
|
| 161 |
+ if hc.Memory != 0 && w.InnerHostConfig.Memory == 0 {
|
|
| 162 |
+ w.InnerHostConfig.Memory = hc.Memory |
|
| 167 | 163 |
} |
| 168 |
- if env.Exists("CpuShares") && hostConfig.CpuShares == 0 {
|
|
| 169 |
- hostConfig.CpuShares = env.GetInt64("CpuShares")
|
|
| 164 |
+ if hc.MemorySwap != 0 && w.InnerHostConfig.MemorySwap == 0 {
|
|
| 165 |
+ w.InnerHostConfig.MemorySwap = hc.MemorySwap |
|
| 170 | 166 |
} |
| 171 |
- if env.Exists("Cpuset") && hostConfig.CpusetCpus == "" {
|
|
| 172 |
- hostConfig.CpusetCpus = env.Get("Cpuset")
|
|
| 167 |
+ if hc.CpuShares != 0 && w.InnerHostConfig.CpuShares == 0 {
|
|
| 168 |
+ w.InnerHostConfig.CpuShares = hc.CpuShares |
|
| 173 | 169 |
} |
| 174 | 170 |
|
| 175 |
- return &hostConfig |
|
| 171 |
+ hc = w.InnerHostConfig |
|
| 176 | 172 |
} |
| 177 | 173 |
|
| 178 |
- hostConfig := &HostConfig{
|
|
| 179 |
- ContainerIDFile: env.Get("ContainerIDFile"),
|
|
| 180 |
- Memory: env.GetInt64("Memory"),
|
|
| 181 |
- MemorySwap: env.GetInt64("MemorySwap"),
|
|
| 182 |
- CpuShares: env.GetInt64("CpuShares"),
|
|
| 183 |
- CpusetCpus: env.Get("CpusetCpus"),
|
|
| 184 |
- Privileged: env.GetBool("Privileged"),
|
|
| 185 |
- PublishAllPorts: env.GetBool("PublishAllPorts"),
|
|
| 186 |
- NetworkMode: NetworkMode(env.Get("NetworkMode")),
|
|
| 187 |
- IpcMode: IpcMode(env.Get("IpcMode")),
|
|
| 188 |
- PidMode: PidMode(env.Get("PidMode")),
|
|
| 189 |
- ReadonlyRootfs: env.GetBool("ReadonlyRootfs"),
|
|
| 190 |
- CgroupParent: env.Get("CgroupParent"),
|
|
| 174 |
+ if hc != nil && w.Cpuset != "" && hc.CpusetCpus == "" {
|
|
| 175 |
+ hc.CpusetCpus = w.Cpuset |
|
| 191 | 176 |
} |
| 192 | 177 |
|
| 193 |
- // FIXME: This is for backward compatibility, if people use `Cpuset` |
|
| 194 |
- // in json, make it workable, we will only pass hostConfig.CpusetCpus |
|
| 195 |
- // to execDriver. |
|
| 196 |
- if env.Exists("Cpuset") && hostConfig.CpusetCpus == "" {
|
|
| 197 |
- hostConfig.CpusetCpus = env.Get("Cpuset")
|
|
| 198 |
- } |
|
| 178 |
+ return hc |
|
| 179 |
+} |
|
| 199 | 180 |
|
| 200 |
- env.GetJson("LxcConf", &hostConfig.LxcConf)
|
|
| 201 |
- env.GetJson("PortBindings", &hostConfig.PortBindings)
|
|
| 202 |
- env.GetJson("Devices", &hostConfig.Devices)
|
|
| 203 |
- env.GetJson("RestartPolicy", &hostConfig.RestartPolicy)
|
|
| 204 |
- env.GetJson("Ulimits", &hostConfig.Ulimits)
|
|
| 205 |
- env.GetJson("LogConfig", &hostConfig.LogConfig)
|
|
| 206 |
- hostConfig.SecurityOpt = env.GetList("SecurityOpt")
|
|
| 207 |
- if Binds := env.GetList("Binds"); Binds != nil {
|
|
| 208 |
- hostConfig.Binds = Binds |
|
| 209 |
- } |
|
| 210 |
- if Links := env.GetList("Links"); Links != nil {
|
|
| 211 |
- hostConfig.Links = Links |
|
| 212 |
- } |
|
| 213 |
- if Dns := env.GetList("Dns"); Dns != nil {
|
|
| 214 |
- hostConfig.Dns = Dns |
|
| 215 |
- } |
|
| 216 |
- if DnsSearch := env.GetList("DnsSearch"); DnsSearch != nil {
|
|
| 217 |
- hostConfig.DnsSearch = DnsSearch |
|
| 218 |
- } |
|
| 219 |
- if ExtraHosts := env.GetList("ExtraHosts"); ExtraHosts != nil {
|
|
| 220 |
- hostConfig.ExtraHosts = ExtraHosts |
|
| 221 |
- } |
|
| 222 |
- if VolumesFrom := env.GetList("VolumesFrom"); VolumesFrom != nil {
|
|
| 223 |
- hostConfig.VolumesFrom = VolumesFrom |
|
| 224 |
- } |
|
| 225 |
- if CapAdd := env.GetList("CapAdd"); CapAdd != nil {
|
|
| 226 |
- hostConfig.CapAdd = CapAdd |
|
| 227 |
- } |
|
| 228 |
- if CapDrop := env.GetList("CapDrop"); CapDrop != nil {
|
|
| 229 |
- hostConfig.CapDrop = CapDrop |
|
| 181 |
+func DecodeHostConfig(src io.Reader) (*HostConfig, error) {
|
|
| 182 |
+ decoder := json.NewDecoder(src) |
|
| 183 |
+ |
|
| 184 |
+ var w hostConfigWrapper |
|
| 185 |
+ if err := decoder.Decode(&w); err != nil {
|
|
| 186 |
+ return nil, err |
|
| 230 | 187 |
} |
| 231 | 188 |
|
| 232 |
- return hostConfig |
|
| 189 |
+ hc := w.GetHostConfig() |
|
| 190 |
+ |
|
| 191 |
+ return hc, nil |
|
| 233 | 192 |
} |
| ... | ... |
@@ -11,15 +11,6 @@ func Merge(userConf, imageConf *Config) error {
|
| 11 | 11 |
if userConf.User == "" {
|
| 12 | 12 |
userConf.User = imageConf.User |
| 13 | 13 |
} |
| 14 |
- if userConf.Memory == 0 {
|
|
| 15 |
- userConf.Memory = imageConf.Memory |
|
| 16 |
- } |
|
| 17 |
- if userConf.MemorySwap == 0 {
|
|
| 18 |
- userConf.MemorySwap = imageConf.MemorySwap |
|
| 19 |
- } |
|
| 20 |
- if userConf.CpuShares == 0 {
|
|
| 21 |
- userConf.CpuShares = imageConf.CpuShares |
|
| 22 |
- } |
|
| 23 | 14 |
if len(userConf.ExposedPorts) == 0 {
|
| 24 | 15 |
userConf.ExposedPorts = imageConf.ExposedPorts |
| 25 | 16 |
} else if imageConf.ExposedPorts != nil {
|
| ... | ... |
@@ -94,8 +85,8 @@ func Merge(userConf, imageConf *Config) error {
|
| 94 | 94 |
userConf.Labels = imageConf.Labels |
| 95 | 95 |
} |
| 96 | 96 |
|
| 97 |
- if len(userConf.Entrypoint) == 0 {
|
|
| 98 |
- if len(userConf.Cmd) == 0 {
|
|
| 97 |
+ if userConf.Entrypoint.Len() == 0 {
|
|
| 98 |
+ if userConf.Cmd.Len() == 0 {
|
|
| 99 | 99 |
userConf.Cmd = imageConf.Cmd |
| 100 | 100 |
} |
| 101 | 101 |
|
| ... | ... |
@@ -185,21 +185,22 @@ func Parse(cmd *flag.FlagSet, args []string) (*Config, *HostConfig, *flag.FlagSe |
| 185 | 185 |
|
| 186 | 186 |
var ( |
| 187 | 187 |
parsedArgs = cmd.Args() |
| 188 |
- runCmd []string |
|
| 189 |
- entrypoint []string |
|
| 188 |
+ runCmd *Command |
|
| 189 |
+ entrypoint *Entrypoint |
|
| 190 | 190 |
image = cmd.Arg(0) |
| 191 | 191 |
) |
| 192 | 192 |
if len(parsedArgs) > 1 {
|
| 193 |
- runCmd = parsedArgs[1:] |
|
| 193 |
+ runCmd = NewCommand(parsedArgs[1:]...) |
|
| 194 | 194 |
} |
| 195 | 195 |
if *flEntrypoint != "" {
|
| 196 |
- entrypoint = []string{*flEntrypoint}
|
|
| 196 |
+ entrypoint = NewEntrypoint(*flEntrypoint) |
|
| 197 | 197 |
} |
| 198 | 198 |
|
| 199 |
- lxcConf, err := parseKeyValueOpts(flLxcOpts) |
|
| 199 |
+ lc, err := parseKeyValueOpts(flLxcOpts) |
|
| 200 | 200 |
if err != nil {
|
| 201 | 201 |
return nil, nil, cmd, err |
| 202 | 202 |
} |
| 203 |
+ lxcConf := NewLxcConfig(lc) |
|
| 203 | 204 |
|
| 204 | 205 |
var ( |
| 205 | 206 |
domainname string |
| ... | ... |
@@ -288,10 +289,6 @@ func Parse(cmd *flag.FlagSet, args []string) (*Config, *HostConfig, *flag.FlagSe |
| 288 | 288 |
Tty: *flTty, |
| 289 | 289 |
NetworkDisabled: !*flNetwork, |
| 290 | 290 |
OpenStdin: *flStdin, |
| 291 |
- Memory: flMemory, // FIXME: for backward compatibility |
|
| 292 |
- MemorySwap: MemorySwap, // FIXME: for backward compatibility |
|
| 293 |
- CpuShares: *flCpuShares, // FIXME: for backward compatibility |
|
| 294 |
- Cpuset: *flCpusetCpus, // FIXME: for backward compatibility |
|
| 295 | 291 |
AttachStdin: attachStdin, |
| 296 | 292 |
AttachStdout: attachStdout, |
| 297 | 293 |
AttachStderr: attachStderr, |