diff --git a/container.go b/container.go index a22484c2d6..db444e758c 100644 --- a/container.go +++ b/container.go @@ -199,7 +199,7 @@ func ParseRun(args []string, capabilities *Capabilities) (*Config, *HostConfig, cmd.Var(flVolumes, "v", "Bind mount a volume (e.g. from the host: -v /host:/container, from docker: -v /container)") var flVolumesFrom utils.ListOpts - cmd.Var(&flVolumesFrom, "volumes-from", "Mount volumes from the specified container") + cmd.Var(&flVolumesFrom, "volumes-from", "Mount volumes from the specified container(s)") flEntrypoint := cmd.String("entrypoint", "", "Overwrite the default entrypoint of the image") @@ -749,9 +749,23 @@ func (container *Container) Start() (err error) { // Apply volumes from another container if requested if container.Config.VolumesFrom != "" { - volumes := strings.Split(container.Config.VolumesFrom, ",") - for _, v := range volumes { - c := container.runtime.Get(v) + containerSpecs := strings.Split(container.Config.VolumesFrom, ",") + for _, containerSpec := range containerSpecs { + mountRW := true + specParts := strings.SplitN(containerSpec, ":", 2) + switch len(specParts) { + case 0: + return fmt.Errorf("Malformed volumes-from specification: %s", container.Config.VolumesFrom) + case 2: + switch specParts[1] { + case "ro": + mountRW = false + case "rw": // mountRW is already true + default: + return fmt.Errorf("Malformed volumes-from speficication: %s", containerSpec) + } + } + c := container.runtime.Get(specParts[0]) if c == nil { return fmt.Errorf("Container %s not found. Impossible to mount its volumes", container.ID) } @@ -764,7 +778,7 @@ func (container *Container) Start() (err error) { } container.Volumes[volPath] = id if isRW, exists := c.VolumesRW[volPath]; exists { - container.VolumesRW[volPath] = isRW + container.VolumesRW[volPath] = isRW && mountRW } } diff --git a/container_test.go b/container_test.go index cbabffc364..5540bb9c27 100644 --- a/container_test.go +++ b/container_test.go @@ -1338,6 +1338,68 @@ func TestBindMounts(t *testing.T) { } } +// Test that -volumes-from supports both read-only mounts +func TestFromVolumesInReadonlyMode(t *testing.T) { + runtime := mkRuntime(t) + defer nuke(runtime) + container, _, err := runtime.Create( + &Config{ + Image: GetTestImage(runtime).ID, + Cmd: []string{"/bin/echo", "-n", "foobar"}, + Volumes: map[string]struct{}{"/test": {}}, + }, + "", + ) + if err != nil { + t.Fatal(err) + } + defer runtime.Destroy(container) + _, err = container.Output() + if err != nil { + t.Fatal(err) + } + if !container.VolumesRW["/test"] { + t.Fail() + } + + container2, _, err := runtime.Create( + &Config{ + Image: GetTestImage(runtime).ID, + Cmd: []string{"/bin/echo", "-n", "foobar"}, + VolumesFrom: container.ID + ":ro", + }, + "", + ) + if err != nil { + t.Fatal(err) + } + defer runtime.Destroy(container2) + + _, err = container2.Output() + if err != nil { + t.Fatal(err) + } + + if container.Volumes["/test"] != container2.Volumes["/test"] { + t.Logf("container volumes do not match: %s | %s ", + container.Volumes["/test"], + container2.Volumes["/test"]) + t.Fail() + } + + _, exists := container2.VolumesRW["/test"] + if !exists { + t.Logf("container2 is missing '/test' volume: %s", container2.VolumesRW) + t.Fail() + } + + if container2.VolumesRW["/test"] != false { + t.Log("'/test' volume mounted in read-write mode, expected read-only") + t.Fail() + } +} + + // Test that VolumesRW values are copied to the new container. Regression test for #1201 func TestVolumesFromReadonlyMount(t *testing.T) { runtime := mkRuntime(t) diff --git a/docs/sources/commandline/cli.rst b/docs/sources/commandline/cli.rst index da0c262c4a..786fca6a6f 100644 --- a/docs/sources/commandline/cli.rst +++ b/docs/sources/commandline/cli.rst @@ -576,7 +576,7 @@ network communication. -u="": Username or UID -dns=[]: Set custom dns servers for the container -v=[]: Create a bind mount with: [host-dir]:[container-dir]:[rw|ro]. If "container-dir" is missing, then docker creates a new volume. - -volumes-from="": Mount all volumes from the given container + -volumes-from="": Mount all volumes from the given container(s) -entrypoint="": Overwrite the default entrypoint set by the image -w="": Working directory inside the container -lxc-conf=[]: Add custom lxc options -lxc-conf="lxc.cgroup.cpuset.cpus = 0,1" @@ -668,6 +668,17 @@ can access the network and environment of the redis container via environment variables. The ``-name`` flag will assign the name ``console`` to the newly created container. +.. code-block:: bash + + docker run -volumes-from 777f7dc92da7,ba8c0c54f0f2:ro -i -t ubuntu pwd + +The ``-volumes-from`` flag mounts all the defined volumes from the +refrence containers. Containers can be specified by a comma seperated +list or by repetitions of the ``-volumes-from`` argument. The container +id may be optionally suffixed with ``:ro`` or ``:rw`` to mount the volumes in +read-only or read-write mode, respectively. By default, the volumes are mounted +in the same mode (rw or ro) as the reference container. + .. _cli_search: ``search``