diff --git a/commands.go b/commands.go index b581590bc2..ac1c88055f 100644 --- a/commands.go +++ b/commands.go @@ -1249,10 +1249,22 @@ func (opts PathOpts) String() string { } func (opts PathOpts) Set(val string) error { - if !filepath.IsAbs(val) { - return fmt.Errorf("%s is not an absolute path", val) + var containerPath string + + splited := strings.SplitN(val, ":", 2) + if len(splited) == 1 { + containerPath = splited[0] + val = filepath.Clean(splited[0]) + } else { + containerPath = splited[1] + val = fmt.Sprintf("%s:%s", splited[0], filepath.Clean(splited[1])) } - opts[filepath.Clean(val)] = struct{}{} + + if !filepath.IsAbs(containerPath) { + utils.Debugf("%s is not an absolute path", containerPath) + return fmt.Errorf("%s is not an absolute path", containerPath) + } + opts[val] = struct{}{} return nil } diff --git a/container.go b/container.go index c714e9f0e1..3772cf29d2 100644 --- a/container.go +++ b/container.go @@ -121,14 +121,11 @@ func ParseRun(args []string, capabilities *Capabilities) (*Config, *HostConfig, cmd.Var(&flDns, "dns", "Set custom dns servers") flVolumes := NewPathOpts() - cmd.Var(flVolumes, "v", "Attach a data volume") + cmd.Var(flVolumes, "v", "Bind mount a volume (e.g. from the host: -v /host:/container, from docker: -v /container)") flVolumesFrom := cmd.String("volumes-from", "", "Mount volumes from the specified container") flEntrypoint := cmd.String("entrypoint", "", "Overwrite the default entrypoint of the image") - var flBinds ListOpts - cmd.Var(&flBinds, "b", "Bind mount a volume from the host (e.g. -b /host:/container)") - if err := cmd.Parse(args); err != nil { return nil, nil, cmd, err } @@ -146,11 +143,17 @@ func ParseRun(args []string, capabilities *Capabilities) (*Config, *HostConfig, } } + var binds []string + // add any bind targets to the list of container volumes - for _, bind := range flBinds { + for bind := range flVolumes { arr := strings.Split(bind, ":") - dstDir := arr[1] - flVolumes[dstDir] = struct{}{} + if len(arr) > 1 { + dstDir := arr[1] + flVolumes[dstDir] = struct{}{} + binds = append(binds, bind) + delete(flVolumes, bind) + } } parsedArgs := cmd.Args() @@ -187,7 +190,7 @@ func ParseRun(args []string, capabilities *Capabilities) (*Config, *HostConfig, Entrypoint: entrypoint, } hostConfig := &HostConfig{ - Binds: flBinds, + Binds: binds, } if capabilities != nil && *flMemory > 0 && !capabilities.SwapLimit { @@ -493,6 +496,7 @@ func (container *Container) Attach(stdin io.ReadCloser, stdinCloser io.Closer, s func (container *Container) Start(hostConfig *HostConfig) error { container.State.Lock() defer container.State.Unlock() + if len(hostConfig.Binds) == 0 { hostConfig, _ = container.ReadHostConfig() } diff --git a/container_test.go b/container_test.go index f431c7dc9a..6e82dd5ebd 100644 --- a/container_test.go +++ b/container_test.go @@ -1231,19 +1231,18 @@ func TestBindMounts(t *testing.T) { writeFile(path.Join(tmpDir, "touch-me"), "", t) // Test reading from a read-only bind mount - stdout, _ := runContainer(r, []string{"-b", fmt.Sprintf("%s:/tmp:ro", tmpDir), "_", "ls", "/tmp"}, t) + stdout, _ := runContainer(r, []string{"-v", fmt.Sprintf("%s:/tmp:ro", tmpDir), "_", "ls", "/tmp"}, t) if !strings.Contains(stdout, "touch-me") { t.Fatal("Container failed to read from bind mount") } // test writing to bind mount - runContainer(r, []string{"-b", fmt.Sprintf("%s:/tmp:rw", tmpDir), "_", "touch", "/tmp/holla"}, t) + runContainer(r, []string{"-v", fmt.Sprintf("%s:/tmp:rw", tmpDir), "_", "touch", "/tmp/holla"}, t) readFile(path.Join(tmpDir, "holla"), t) // Will fail if the file doesn't exist // test mounting to an illegal destination directory - if _, err := runContainer(r, []string{"-b", fmt.Sprintf("%s:.", tmpDir), "ls", "."}, nil); err == nil { + if _, err := runContainer(r, []string{"-v", fmt.Sprintf("%s:.", tmpDir), "ls", "."}, nil); err == nil { t.Fatal("Container bind mounted illegal directory") - } } diff --git a/utils_test.go b/utils_test.go index 4951d3a02d..afa6a3a1a1 100644 --- a/utils_test.go +++ b/utils_test.go @@ -1,13 +1,13 @@ package docker import ( + "github.com/dotcloud/docker/utils" "io" "io/ioutil" "os" "path" "strings" "testing" - "github.com/dotcloud/docker/utils" ) // This file contains utility functions for docker's unit test suite. @@ -87,17 +87,18 @@ func readFile(src string, t *testing.T) (content string) { // The image name (eg. the XXX in []string{"-i", "-t", "XXX", "bash"}, is dynamically replaced by the current test image. // The caller is responsible for destroying the container. // Call t.Fatal() at the first error. -func mkContainer(r *Runtime, args []string, t *testing.T) (*Container, *HostConfig) { +func mkContainer(r *Runtime, args []string, t *testing.T) (*Container, *HostConfig, error) { config, hostConfig, _, err := ParseRun(args, nil) if err != nil { - t.Fatal(err) + return nil, nil, err } config.Image = GetTestImage(r).ID c, err := NewBuilder(r).Create(config) if err != nil { t.Fatal(err) + return nil, nil, err } - return c, hostConfig + return c, hostConfig, nil } // Create a test container, start it, wait for it to complete, destroy it, @@ -110,7 +111,10 @@ func runContainer(r *Runtime, args []string, t *testing.T) (output string, err e t.Fatal(err) } }() - container, hostConfig := mkContainer(r, args, t) + container, hostConfig, err := mkContainer(r, args, t) + if err != nil { + return "", err + } defer r.Destroy(container) stdout, err := container.StdoutPipe() if err != nil {