Browse code

Fix: Failed Start breaks VolumesFrom

Running parseVolumesFromSpec on all VolumesFrom specs before initialize
any mounts endures that we don't leave container.Volumes in an
inconsistent (partially initialized) if one of out mount groups is not
available (e.g. the container we're trying to mount from does not
exist).

Keeping container.Volumes in a consistent state ensures that next time
we Start() the container, it'll run prepareVolumes() again.

The attached test demonstrates that when a container fails to start due
to a missing container specified in VolumesFrom, it "remembers" a Volume
that worked.

Fixes: #8726

Signed-off-by: Thomas Orozco <thomas@orozco.fr>

Conflicts:
integration-cli/docker_cli_start_test.go
cli integration test

Thomas Orozco authored on 2014/10/23 21:50:06
Showing 2 changed files
... ...
@@ -191,15 +191,20 @@ func parseBindMountSpec(spec string) (string, string, bool, error) {
191 191
 func (container *Container) applyVolumesFrom() error {
192 192
 	volumesFrom := container.hostConfig.VolumesFrom
193 193
 
194
+	mountGroups := make([]map[string]*Mount, 0, len(volumesFrom))
195
+
194 196
 	for _, spec := range volumesFrom {
195
-		mounts, err := parseVolumesFromSpec(container.daemon, spec)
197
+		mountGroup, err := parseVolumesFromSpec(container.daemon, spec)
196 198
 		if err != nil {
197 199
 			return err
198 200
 		}
201
+		mountGroups = append(mountGroups, mountGroup)
202
+	}
199 203
 
204
+	for _, mounts := range mountGroups {
200 205
 		for _, mnt := range mounts {
201 206
 			mnt.container = container
202
-			if err = mnt.initialize(); err != nil {
207
+			if err := mnt.initialize(); err != nil {
203 208
 				return err
204 209
 			}
205 210
 		}
... ...
@@ -2,6 +2,7 @@ package main
2 2
 
3 3
 import (
4 4
 	"os/exec"
5
+	"strings"
5 6
 	"testing"
6 7
 	"time"
7 8
 )
... ...
@@ -36,3 +37,31 @@ func TestStartAttachReturnsOnError(t *testing.T) {
36 36
 
37 37
 	logDone("start - error on start with attach exits")
38 38
 }
39
+
40
+// gh#8726: a failed Start() breaks --volumes-from on subsequent Start()'s
41
+func TestStartVolumesFromFailsCleanly(t *testing.T) {
42
+	defer deleteAllContainers()
43
+
44
+	// Create the first data volume
45
+	cmd(t, "run", "-d", "--name", "data_before", "-v", "/foo", "busybox")
46
+
47
+	// Expect this to fail because the data test after contaienr doesn't exist yet
48
+	if _, err := runCommand(exec.Command(dockerBinary, "run", "-d", "--name", "consumer", "--volumes-from", "data_before", "--volumes-from", "data_after", "busybox")); err == nil {
49
+		t.Fatal("Expected error but got none")
50
+	}
51
+
52
+	// Create the second data volume
53
+	cmd(t, "run", "-d", "--name", "data_after", "-v", "/bar", "busybox")
54
+
55
+	// Now, all the volumes should be there
56
+	cmd(t, "start", "consumer")
57
+
58
+	// Check that we have the volumes we want
59
+	out, _, _ := cmd(t, "inspect", "--format='{{ len .Volumes }}'", "consumer")
60
+	n_volumes := strings.Trim(out, " \r\n'")
61
+	if n_volumes != "2" {
62
+		t.Fatalf("Missing volumes: expected 2, got %s", n_volumes)
63
+	}
64
+
65
+	logDone("start - missing containers in --volumes-from did not affect subsequent runs")
66
+}