Browse code

Add ability to mount volumes in readonly mode using -volumes-from

Daniel Garcia authored on 2013/10/14 05:58:54
Showing 3 changed files
... ...
@@ -199,7 +199,7 @@ func ParseRun(args []string, capabilities *Capabilities) (*Config, *HostConfig,
199 199
 	cmd.Var(flVolumes, "v", "Bind mount a volume (e.g. from the host: -v /host:/container, from docker: -v /container)")
200 200
 
201 201
 	var flVolumesFrom utils.ListOpts
202
-	cmd.Var(&flVolumesFrom, "volumes-from", "Mount volumes from the specified container")
202
+	cmd.Var(&flVolumesFrom, "volumes-from", "Mount volumes from the specified container(s)")
203 203
 
204 204
 	flEntrypoint := cmd.String("entrypoint", "", "Overwrite the default entrypoint of the image")
205 205
 
... ...
@@ -749,9 +749,23 @@ func (container *Container) Start() (err error) {
749 749
 
750 750
 	// Apply volumes from another container if requested
751 751
 	if container.Config.VolumesFrom != "" {
752
-		volumes := strings.Split(container.Config.VolumesFrom, ",")
753
-		for _, v := range volumes {
754
-			c := container.runtime.Get(v)
752
+		containerSpecs := strings.Split(container.Config.VolumesFrom, ",")
753
+		for _, containerSpec := range containerSpecs {
754
+			mountRW := true
755
+			specParts := strings.SplitN(containerSpec, ":", 2)
756
+			switch len(specParts) {
757
+			case 0:
758
+				return fmt.Errorf("Malformed volumes-from specification: %s", container.Config.VolumesFrom)
759
+			case 2:
760
+				switch specParts[1] {
761
+				case "ro":
762
+					mountRW = false
763
+				case "rw": // mountRW is already true
764
+				default:
765
+					return fmt.Errorf("Malformed volumes-from speficication: %s", containerSpec)
766
+				}
767
+			}
768
+			c := container.runtime.Get(specParts[0])
755 769
 			if c == nil {
756 770
 				return fmt.Errorf("Container %s not found. Impossible to mount its volumes", container.ID)
757 771
 			}
... ...
@@ -764,7 +778,7 @@ func (container *Container) Start() (err error) {
764 764
 				}
765 765
 				container.Volumes[volPath] = id
766 766
 				if isRW, exists := c.VolumesRW[volPath]; exists {
767
-					container.VolumesRW[volPath] = isRW
767
+					container.VolumesRW[volPath] = isRW && mountRW
768 768
 				}
769 769
 			}
770 770
 
... ...
@@ -1338,6 +1338,68 @@ func TestBindMounts(t *testing.T) {
1338 1338
 	}
1339 1339
 }
1340 1340
 
1341
+// Test that -volumes-from supports both read-only mounts
1342
+func TestFromVolumesInReadonlyMode(t *testing.T) {
1343
+	runtime := mkRuntime(t)
1344
+	defer nuke(runtime)
1345
+	container, _, err := runtime.Create(
1346
+		&Config{
1347
+			Image:     GetTestImage(runtime).ID,
1348
+			Cmd:       []string{"/bin/echo", "-n", "foobar"},
1349
+			Volumes:   map[string]struct{}{"/test": {}},
1350
+		},
1351
+		"",
1352
+	)
1353
+	if err != nil {
1354
+		t.Fatal(err)
1355
+	}
1356
+	defer runtime.Destroy(container)
1357
+	_, err = container.Output()
1358
+	if err != nil {
1359
+		t.Fatal(err)
1360
+	}
1361
+	if !container.VolumesRW["/test"] {
1362
+		t.Fail()
1363
+	}
1364
+
1365
+	container2, _, err := runtime.Create(
1366
+		&Config{
1367
+			Image:       GetTestImage(runtime).ID,
1368
+			Cmd:         []string{"/bin/echo", "-n", "foobar"},
1369
+			VolumesFrom: container.ID + ":ro",
1370
+		},
1371
+		"",
1372
+	)
1373
+	if err != nil {
1374
+		t.Fatal(err)
1375
+	}
1376
+	defer runtime.Destroy(container2)
1377
+
1378
+	_, err = container2.Output()
1379
+	if err != nil {
1380
+		t.Fatal(err)
1381
+	}
1382
+
1383
+	if container.Volumes["/test"] != container2.Volumes["/test"] {
1384
+		t.Logf("container volumes do not match: %s | %s ", 
1385
+			container.Volumes["/test"],
1386
+			container2.Volumes["/test"])
1387
+		t.Fail()
1388
+	}
1389
+
1390
+	_, exists := container2.VolumesRW["/test"]
1391
+	if !exists {
1392
+		t.Logf("container2 is missing '/test' volume: %s", container2.VolumesRW)
1393
+		t.Fail()
1394
+	}
1395
+
1396
+	if container2.VolumesRW["/test"] != false {
1397
+		t.Log("'/test' volume mounted in read-write mode, expected read-only")
1398
+		t.Fail()
1399
+	}
1400
+}
1401
+
1402
+
1341 1403
 // Test that VolumesRW values are copied to the new container.  Regression test for #1201
1342 1404
 func TestVolumesFromReadonlyMount(t *testing.T) {
1343 1405
 	runtime := mkRuntime(t)
... ...
@@ -576,7 +576,7 @@ network communication.
576 576
       -u="": Username or UID
577 577
       -dns=[]: Set custom dns servers for the container
578 578
       -v=[]: Create a bind mount with: [host-dir]:[container-dir]:[rw|ro]. If "container-dir" is missing, then docker creates a new volume.
579
-      -volumes-from="": Mount all volumes from the given container
579
+      -volumes-from="": Mount all volumes from the given container(s)
580 580
       -entrypoint="": Overwrite the default entrypoint set by the image
581 581
       -w="": Working directory inside the container
582 582
       -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
668 668
 environment variables.  The ``-name`` flag will assign the name ``console``
669 669
 to the newly created container.
670 670
 
671
+.. code-block:: bash
672
+
673
+   docker run -volumes-from 777f7dc92da7,ba8c0c54f0f2:ro -i -t ubuntu pwd
674
+
675
+The ``-volumes-from`` flag mounts all the defined volumes from the
676
+refrence containers. Containers can be specified by a comma seperated
677
+list or by repetitions of the ``-volumes-from`` argument. The container
678
+id may be optionally suffixed with ``:ro`` or ``:rw`` to mount the volumes in
679
+read-only or read-write mode, respectively. By default, the volumes are mounted
680
+in the same mode (rw or ro) as the reference container.
681
+
671 682
 .. _cli_search:
672 683
 
673 684
 ``search``