Browse code

Move volumesfrom to hostconfig

This also migrates the volumes from integration tests into the new cli
integration test framework.
Docker-DCO-1.1-Signed-off-by: Michael Crosby <michael@crosbymichael.com> (github: crosbymichael)

Michael Crosby authored on 2014/04/08 18:26:09
Showing 9 changed files
... ...
@@ -312,3 +312,75 @@ func TestVolumesMountedAsReadonly(t *testing.T) {
312 312
 
313 313
 	logDone("run - volumes as readonly mount")
314 314
 }
315
+
316
+func TestVolumesFromInReadonlyMode(t *testing.T) {
317
+	cmd := exec.Command(dockerBinary, "run", "--name", "parent", "-v", "/test", "busybox", "true")
318
+	if _, err := runCommand(cmd); err != nil {
319
+		t.Fatal(err)
320
+	}
321
+
322
+	cmd = exec.Command(dockerBinary, "run", "--volumes-from", "parent:ro", "busybox", "touch", "/test/file")
323
+	if code, err := runCommand(cmd); err == nil || code == 0 {
324
+		t.Fatalf("run should fail because volume is ro: exit code %d", code)
325
+	}
326
+
327
+	deleteAllContainers()
328
+
329
+	logDone("run - volumes from as readonly mount")
330
+}
331
+
332
+// Regression test for #1201
333
+func TestVolumesFromInReadWriteMode(t *testing.T) {
334
+	cmd := exec.Command(dockerBinary, "run", "--name", "parent", "-v", "/test", "busybox", "true")
335
+	if _, err := runCommand(cmd); err != nil {
336
+		t.Fatal(err)
337
+	}
338
+
339
+	cmd = exec.Command(dockerBinary, "run", "--volumes-from", "parent", "busybox", "touch", "/test/file")
340
+	if _, err := runCommand(cmd); err != nil {
341
+		t.Fatal(err)
342
+	}
343
+
344
+	deleteAllContainers()
345
+
346
+	logDone("run - volumes from as read write mount")
347
+}
348
+
349
+// Test for #1351
350
+func TestApplyVolumesFromBeforeVolumes(t *testing.T) {
351
+	cmd := exec.Command(dockerBinary, "run", "--name", "parent", "-v", "/test", "busybox", "touch", "/test/foo")
352
+	if _, err := runCommand(cmd); err != nil {
353
+		t.Fatal(err)
354
+	}
355
+
356
+	cmd = exec.Command(dockerBinary, "run", "--volumes-from", "parent", "-v", "/test", "busybox", "cat", "/test/foo")
357
+	if _, err := runCommand(cmd); err != nil {
358
+		t.Fatal(err)
359
+	}
360
+
361
+	deleteAllContainers()
362
+
363
+	logDone("run - volumes from mounted first")
364
+}
365
+
366
+func TestMultipleVolumesFrom(t *testing.T) {
367
+	cmd := exec.Command(dockerBinary, "run", "--name", "parent1", "-v", "/test", "busybox", "touch", "/test/foo")
368
+	if _, err := runCommand(cmd); err != nil {
369
+		t.Fatal(err)
370
+	}
371
+
372
+	cmd = exec.Command(dockerBinary, "run", "--name", "parent2", "-v", "/other", "busybox", "touch", "/other/bar")
373
+	if _, err := runCommand(cmd); err != nil {
374
+		t.Fatal(err)
375
+	}
376
+
377
+	cmd = exec.Command(dockerBinary, "run", "--volumes-from", "parent1", "--volumes-from", "parent2",
378
+		"busybox", "sh", "-c", "cat /test/foo && cat /other/bar")
379
+	if _, err := runCommand(cmd); err != nil {
380
+		t.Fatal(err)
381
+	}
382
+
383
+	deleteAllContainers()
384
+
385
+	logDone("run - multiple volumes from")
386
+}
... ...
@@ -1273,123 +1273,6 @@ func TestBindMounts(t *testing.T) {
1273 1273
 	}
1274 1274
 }
1275 1275
 
1276
-// Test that -volumes-from supports both read-only mounts
1277
-func TestFromVolumesInReadonlyMode(t *testing.T) {
1278
-	runtime := mkRuntime(t)
1279
-	defer nuke(runtime)
1280
-	container, _, err := runtime.Create(
1281
-		&runconfig.Config{
1282
-			Image:   GetTestImage(runtime).ID,
1283
-			Cmd:     []string{"/bin/echo", "-n", "foobar"},
1284
-			Volumes: map[string]struct{}{"/test": {}},
1285
-		},
1286
-		"",
1287
-	)
1288
-	if err != nil {
1289
-		t.Fatal(err)
1290
-	}
1291
-	defer runtime.Destroy(container)
1292
-	_, err = container.Output()
1293
-	if err != nil {
1294
-		t.Fatal(err)
1295
-	}
1296
-	if !container.VolumesRW["/test"] {
1297
-		t.Fail()
1298
-	}
1299
-
1300
-	container2, _, err := runtime.Create(
1301
-		&runconfig.Config{
1302
-			Image:       GetTestImage(runtime).ID,
1303
-			Cmd:         []string{"/bin/echo", "-n", "foobar"},
1304
-			VolumesFrom: container.ID + ":ro",
1305
-		},
1306
-		"",
1307
-	)
1308
-	if err != nil {
1309
-		t.Fatal(err)
1310
-	}
1311
-	defer runtime.Destroy(container2)
1312
-
1313
-	_, err = container2.Output()
1314
-	if err != nil {
1315
-		t.Fatal(err)
1316
-	}
1317
-
1318
-	if container.Volumes["/test"] != container2.Volumes["/test"] {
1319
-		t.Logf("container volumes do not match: %s | %s ",
1320
-			container.Volumes["/test"],
1321
-			container2.Volumes["/test"])
1322
-		t.Fail()
1323
-	}
1324
-
1325
-	_, exists := container2.VolumesRW["/test"]
1326
-	if !exists {
1327
-		t.Logf("container2 is missing '/test' volume: %s", container2.VolumesRW)
1328
-		t.Fail()
1329
-	}
1330
-
1331
-	if container2.VolumesRW["/test"] != false {
1332
-		t.Log("'/test' volume mounted in read-write mode, expected read-only")
1333
-		t.Fail()
1334
-	}
1335
-}
1336
-
1337
-// Test that VolumesRW values are copied to the new container.  Regression test for #1201
1338
-func TestVolumesFromReadonlyMount(t *testing.T) {
1339
-	runtime := mkRuntime(t)
1340
-	defer nuke(runtime)
1341
-	container, _, err := runtime.Create(
1342
-		&runconfig.Config{
1343
-			Image:   GetTestImage(runtime).ID,
1344
-			Cmd:     []string{"/bin/echo", "-n", "foobar"},
1345
-			Volumes: map[string]struct{}{"/test": {}},
1346
-		},
1347
-		"",
1348
-	)
1349
-	if err != nil {
1350
-		t.Fatal(err)
1351
-	}
1352
-	defer runtime.Destroy(container)
1353
-	_, err = container.Output()
1354
-	if err != nil {
1355
-		t.Fatal(err)
1356
-	}
1357
-	if !container.VolumesRW["/test"] {
1358
-		t.Fail()
1359
-	}
1360
-
1361
-	container2, _, err := runtime.Create(
1362
-		&runconfig.Config{
1363
-			Image:       GetTestImage(runtime).ID,
1364
-			Cmd:         []string{"/bin/echo", "-n", "foobar"},
1365
-			VolumesFrom: container.ID,
1366
-		},
1367
-		"",
1368
-	)
1369
-	if err != nil {
1370
-		t.Fatal(err)
1371
-	}
1372
-	defer runtime.Destroy(container2)
1373
-
1374
-	_, err = container2.Output()
1375
-	if err != nil {
1376
-		t.Fatal(err)
1377
-	}
1378
-
1379
-	if container.Volumes["/test"] != container2.Volumes["/test"] {
1380
-		t.Fail()
1381
-	}
1382
-
1383
-	actual, exists := container2.VolumesRW["/test"]
1384
-	if !exists {
1385
-		t.Fail()
1386
-	}
1387
-
1388
-	if container.VolumesRW["/test"] != actual {
1389
-		t.Fail()
1390
-	}
1391
-}
1392
-
1393 1276
 // Test that restarting a container with a volume does not create a new volume on restart. Regression test for #819.
1394 1277
 func TestRestartWithVolumes(t *testing.T) {
1395 1278
 	runtime := mkRuntime(t)
... ...
@@ -1434,73 +1317,6 @@ func TestRestartWithVolumes(t *testing.T) {
1434 1434
 	}
1435 1435
 }
1436 1436
 
1437
-// Test for #1351
1438
-func TestVolumesFromWithVolumes(t *testing.T) {
1439
-	runtime := mkRuntime(t)
1440
-	defer nuke(runtime)
1441
-
1442
-	container, _, err := runtime.Create(&runconfig.Config{
1443
-		Image:   GetTestImage(runtime).ID,
1444
-		Cmd:     []string{"sh", "-c", "echo -n bar > /test/foo"},
1445
-		Volumes: map[string]struct{}{"/test": {}},
1446
-	},
1447
-		"",
1448
-	)
1449
-	if err != nil {
1450
-		t.Fatal(err)
1451
-	}
1452
-	defer runtime.Destroy(container)
1453
-
1454
-	for key := range container.Config.Volumes {
1455
-		if key != "/test" {
1456
-			t.Fail()
1457
-		}
1458
-	}
1459
-
1460
-	_, err = container.Output()
1461
-	if err != nil {
1462
-		t.Fatal(err)
1463
-	}
1464
-
1465
-	expected := container.Volumes["/test"]
1466
-	if expected == "" {
1467
-		t.Fail()
1468
-	}
1469
-
1470
-	container2, _, err := runtime.Create(
1471
-		&runconfig.Config{
1472
-			Image:       GetTestImage(runtime).ID,
1473
-			Cmd:         []string{"cat", "/test/foo"},
1474
-			VolumesFrom: container.ID,
1475
-			Volumes:     map[string]struct{}{"/test": {}},
1476
-		},
1477
-		"",
1478
-	)
1479
-	if err != nil {
1480
-		t.Fatal(err)
1481
-	}
1482
-	defer runtime.Destroy(container2)
1483
-
1484
-	output, err := container2.Output()
1485
-	if err != nil {
1486
-		t.Fatal(err)
1487
-	}
1488
-
1489
-	if string(output) != "bar" {
1490
-		t.Fail()
1491
-	}
1492
-
1493
-	if container.Volumes["/test"] != container2.Volumes["/test"] {
1494
-		t.Fail()
1495
-	}
1496
-
1497
-	// Ensure it restarts successfully
1498
-	_, err = container2.Output()
1499
-	if err != nil {
1500
-		t.Fatal(err)
1501
-	}
1502
-}
1503
-
1504 1437
 func TestContainerNetwork(t *testing.T) {
1505 1438
 	runtime := mkRuntime(t)
1506 1439
 	defer nuke(runtime)
... ...
@@ -1636,81 +1452,3 @@ func TestUnprivilegedCannotMount(t *testing.T) {
1636 1636
 		t.Fatal("Could mount into secure container")
1637 1637
 	}
1638 1638
 }
1639
-
1640
-func TestMultipleVolumesFrom(t *testing.T) {
1641
-	runtime := mkRuntime(t)
1642
-	defer nuke(runtime)
1643
-
1644
-	container, _, err := runtime.Create(&runconfig.Config{
1645
-		Image:   GetTestImage(runtime).ID,
1646
-		Cmd:     []string{"sh", "-c", "echo -n bar > /test/foo"},
1647
-		Volumes: map[string]struct{}{"/test": {}},
1648
-	},
1649
-		"",
1650
-	)
1651
-	if err != nil {
1652
-		t.Fatal(err)
1653
-	}
1654
-	defer runtime.Destroy(container)
1655
-
1656
-	for key := range container.Config.Volumes {
1657
-		if key != "/test" {
1658
-			t.Fail()
1659
-		}
1660
-	}
1661
-
1662
-	_, err = container.Output()
1663
-	if err != nil {
1664
-		t.Fatal(err)
1665
-	}
1666
-
1667
-	expected := container.Volumes["/test"]
1668
-	if expected == "" {
1669
-		t.Fail()
1670
-	}
1671
-
1672
-	container2, _, err := runtime.Create(
1673
-		&runconfig.Config{
1674
-			Image:   GetTestImage(runtime).ID,
1675
-			Cmd:     []string{"sh", "-c", "echo -n bar > /other/foo"},
1676
-			Volumes: map[string]struct{}{"/other": {}},
1677
-		},
1678
-		"",
1679
-	)
1680
-	if err != nil {
1681
-		t.Fatal(err)
1682
-	}
1683
-	defer runtime.Destroy(container2)
1684
-
1685
-	for key := range container2.Config.Volumes {
1686
-		if key != "/other" {
1687
-			t.FailNow()
1688
-		}
1689
-	}
1690
-	if _, err := container2.Output(); err != nil {
1691
-		t.Fatal(err)
1692
-	}
1693
-
1694
-	container3, _, err := runtime.Create(
1695
-		&runconfig.Config{
1696
-			Image:       GetTestImage(runtime).ID,
1697
-			Cmd:         []string{"/bin/echo", "-n", "foobar"},
1698
-			VolumesFrom: strings.Join([]string{container.ID, container2.ID}, ","),
1699
-		}, "")
1700
-
1701
-	if err != nil {
1702
-		t.Fatal(err)
1703
-	}
1704
-	defer runtime.Destroy(container3)
1705
-
1706
-	if _, err := container3.Output(); err != nil {
1707
-		t.Fatal(err)
1708
-	}
1709
-
1710
-	if container3.Volumes["/test"] != container.Volumes["/test"] {
1711
-		t.Fail()
1712
-	}
1713
-	if container3.Volumes["/other"] != container2.Volumes["/other"] {
1714
-		t.Fail()
1715
-	}
1716
-}
... ...
@@ -14,8 +14,7 @@ func Compare(a, b *Config) bool {
14 14
 		a.MemorySwap != b.MemorySwap ||
15 15
 		a.CpuShares != b.CpuShares ||
16 16
 		a.OpenStdin != b.OpenStdin ||
17
-		a.Tty != b.Tty ||
18
-		a.VolumesFrom != b.VolumesFrom {
17
+		a.Tty != b.Tty {
19 18
 		return false
20 19
 	}
21 20
 	if len(a.Cmd) != len(b.Cmd) ||
... ...
@@ -27,7 +27,6 @@ type Config struct {
27 27
 	Cmd             []string
28 28
 	Image           string // Name of the image as it was passed by the operator (eg. could be symbolic)
29 29
 	Volumes         map[string]struct{}
30
-	VolumesFrom     string
31 30
 	WorkingDir      string
32 31
 	Entrypoint      []string
33 32
 	NetworkDisabled bool
... ...
@@ -49,7 +48,6 @@ func ContainerConfigFromJob(job *engine.Job) *Config {
49 49
 		OpenStdin:       job.GetenvBool("OpenStdin"),
50 50
 		StdinOnce:       job.GetenvBool("StdinOnce"),
51 51
 		Image:           job.Getenv("Image"),
52
-		VolumesFrom:     job.Getenv("VolumesFrom"),
53 52
 		WorkingDir:      job.Getenv("WorkingDir"),
54 53
 		NetworkDisabled: job.GetenvBool("NetworkDisabled"),
55 54
 	}
... ...
@@ -163,37 +163,25 @@ func TestCompare(t *testing.T) {
163 163
 	volumes1 := make(map[string]struct{})
164 164
 	volumes1["/test1"] = struct{}{}
165 165
 	config1 := Config{
166
-		PortSpecs:   []string{"1111:1111", "2222:2222"},
167
-		Env:         []string{"VAR1=1", "VAR2=2"},
168
-		VolumesFrom: "11111111",
169
-		Volumes:     volumes1,
166
+		PortSpecs: []string{"1111:1111", "2222:2222"},
167
+		Env:       []string{"VAR1=1", "VAR2=2"},
168
+		Volumes:   volumes1,
170 169
 	}
171 170
 	config3 := Config{
172
-		PortSpecs:   []string{"0000:0000", "2222:2222"},
173
-		Env:         []string{"VAR1=1", "VAR2=2"},
174
-		VolumesFrom: "11111111",
175
-		Volumes:     volumes1,
176
-	}
177
-	config4 := Config{
178
-		PortSpecs:   []string{"0000:0000", "2222:2222"},
179
-		Env:         []string{"VAR1=1", "VAR2=2"},
180
-		VolumesFrom: "22222222",
181
-		Volumes:     volumes1,
171
+		PortSpecs: []string{"0000:0000", "2222:2222"},
172
+		Env:       []string{"VAR1=1", "VAR2=2"},
173
+		Volumes:   volumes1,
182 174
 	}
183 175
 	volumes2 := make(map[string]struct{})
184 176
 	volumes2["/test2"] = struct{}{}
185 177
 	config5 := Config{
186
-		PortSpecs:   []string{"0000:0000", "2222:2222"},
187
-		Env:         []string{"VAR1=1", "VAR2=2"},
188
-		VolumesFrom: "11111111",
189
-		Volumes:     volumes2,
178
+		PortSpecs: []string{"0000:0000", "2222:2222"},
179
+		Env:       []string{"VAR1=1", "VAR2=2"},
180
+		Volumes:   volumes2,
190 181
 	}
191 182
 	if Compare(&config1, &config3) {
192 183
 		t.Fatalf("Compare should return false, PortSpecs are different")
193 184
 	}
194
-	if Compare(&config1, &config4) {
195
-		t.Fatalf("Compare should return false, VolumesFrom are different")
196
-	}
197 185
 	if Compare(&config1, &config5) {
198 186
 		t.Fatalf("Compare should return false, Volumes are different")
199 187
 	}
... ...
@@ -207,10 +195,9 @@ func TestMerge(t *testing.T) {
207 207
 	volumesImage["/test1"] = struct{}{}
208 208
 	volumesImage["/test2"] = struct{}{}
209 209
 	configImage := &Config{
210
-		PortSpecs:   []string{"1111:1111", "2222:2222"},
211
-		Env:         []string{"VAR1=1", "VAR2=2"},
212
-		VolumesFrom: "1111",
213
-		Volumes:     volumesImage,
210
+		PortSpecs: []string{"1111:1111", "2222:2222"},
211
+		Env:       []string{"VAR1=1", "VAR2=2"},
212
+		Volumes:   volumesImage,
214 213
 	}
215 214
 
216 215
 	volumesUser := make(map[string]struct{})
... ...
@@ -251,10 +238,6 @@ func TestMerge(t *testing.T) {
251 251
 		}
252 252
 	}
253 253
 
254
-	if configUser.VolumesFrom != "1111" {
255
-		t.Fatalf("Expected VolumesFrom to be 1111, found %s", configUser.VolumesFrom)
256
-	}
257
-
258 254
 	ports, _, err := nat.ParsePortSpecs([]string{"0000"})
259 255
 	if err != nil {
260 256
 		t.Error(err)
... ...
@@ -16,6 +16,7 @@ type HostConfig struct {
16 16
 	PublishAllPorts bool
17 17
 	Dns             []string
18 18
 	DnsSearch       []string
19
+	VolumesFrom     string
19 20
 }
20 21
 
21 22
 func ContainerHostConfigFromJob(job *engine.Job) *HostConfig {
... ...
@@ -23,6 +24,7 @@ func ContainerHostConfigFromJob(job *engine.Job) *HostConfig {
23 23
 		ContainerIDFile: job.Getenv("ContainerIDFile"),
24 24
 		Privileged:      job.GetenvBool("Privileged"),
25 25
 		PublishAllPorts: job.GetenvBool("PublishAllPorts"),
26
+		VolumesFrom:     job.Getenv("VolumesFrom"),
26 27
 	}
27 28
 	job.GetenvJson("LxcConf", &hostConfig.LxcConf)
28 29
 	job.GetenvJson("PortBindings", &hostConfig.PortBindings)
... ...
@@ -100,9 +100,6 @@ func Merge(userConf, imageConf *Config) error {
100 100
 	if userConf.WorkingDir == "" {
101 101
 		userConf.WorkingDir = imageConf.WorkingDir
102 102
 	}
103
-	if userConf.VolumesFrom == "" {
104
-		userConf.VolumesFrom = imageConf.VolumesFrom
105
-	}
106 103
 	if userConf.Volumes == nil || len(userConf.Volumes) == 0 {
107 104
 		userConf.Volumes = imageConf.Volumes
108 105
 	} else {
... ...
@@ -215,7 +215,6 @@ func parseRun(cmd *flag.FlagSet, args []string, sysInfo *sysinfo.SysInfo) (*Conf
215 215
 		Cmd:             runCmd,
216 216
 		Image:           image,
217 217
 		Volumes:         flVolumes.GetMap(),
218
-		VolumesFrom:     strings.Join(flVolumesFrom.GetAll(), ","),
219 218
 		Entrypoint:      entrypoint,
220 219
 		WorkingDir:      *flWorkingDir,
221 220
 	}
... ...
@@ -230,6 +229,7 @@ func parseRun(cmd *flag.FlagSet, args []string, sysInfo *sysinfo.SysInfo) (*Conf
230 230
 		PublishAllPorts: *flPublishAll,
231 231
 		Dns:             flDns.GetAll(),
232 232
 		DnsSearch:       flDnsSearch.GetAll(),
233
+		VolumesFrom:     strings.Join(flVolumesFrom.GetAll(), ","),
233 234
 	}
234 235
 
235 236
 	if sysInfo != nil && flMemory > 0 && !sysInfo.SwapLimit {
... ...
@@ -59,8 +59,9 @@ func setupMountsForContainer(container *Container, envPath string) error {
59 59
 }
60 60
 
61 61
 func applyVolumesFrom(container *Container) error {
62
-	if container.Config.VolumesFrom != "" {
63
-		for _, containerSpec := range strings.Split(container.Config.VolumesFrom, ",") {
62
+	volumesFrom := container.hostConfig.VolumesFrom
63
+	if volumesFrom != "" {
64
+		for _, containerSpec := range strings.Split(volumesFrom, ",") {
64 65
 			var (
65 66
 				mountRW   = true
66 67
 				specParts = strings.SplitN(containerSpec, ":", 2)
... ...
@@ -68,7 +69,7 @@ func applyVolumesFrom(container *Container) error {
68 68
 
69 69
 			switch len(specParts) {
70 70
 			case 0:
71
-				return fmt.Errorf("Malformed volumes-from specification: %s", container.Config.VolumesFrom)
71
+				return fmt.Errorf("Malformed volumes-from specification: %s", volumesFrom)
72 72
 			case 2:
73 73
 				switch specParts[1] {
74 74
 				case "ro":