Explorar el Código

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>
Thomas Orozco hace 10 años
padre
commit
fb62e18441
Se han modificado 2 ficheros con 35 adiciones y 2 borrados
  1. 7 2
      daemon/volumes.go
  2. 28 0
      integration-cli/docker_cli_start_test.go

+ 7 - 2
daemon/volumes.go

@@ -204,15 +204,20 @@ func parseBindMountSpec(spec string) (string, string, bool, error) {
 func (container *Container) applyVolumesFrom() error {
 func (container *Container) applyVolumesFrom() error {
 	volumesFrom := container.hostConfig.VolumesFrom
 	volumesFrom := container.hostConfig.VolumesFrom
 
 
+	mountGroups := make([]map[string]*Mount, 0, len(volumesFrom))
+
 	for _, spec := range volumesFrom {
 	for _, spec := range volumesFrom {
-		mounts, err := parseVolumesFromSpec(container.daemon, spec)
+		mountGroup, err := parseVolumesFromSpec(container.daemon, spec)
 		if err != nil {
 		if err != nil {
 			return err
 			return err
 		}
 		}
+		mountGroups = append(mountGroups, mountGroup)
+	}
 
 
+	for _, mounts := range mountGroups {
 		for _, mnt := range mounts {
 		for _, mnt := range mounts {
 			mnt.container = container
 			mnt.container = container
-			if err = mnt.initialize(); err != nil {
+			if err := mnt.initialize(); err != nil {
 				return err
 				return err
 			}
 			}
 		}
 		}

+ 28 - 0
integration-cli/docker_cli_start_test.go

@@ -109,3 +109,31 @@ func TestStartRecordError(t *testing.T) {
 
 
 	logDone("start - set state error when start fails")
 	logDone("start - set state error when start fails")
 }
 }
+
+// gh#8726: a failed Start() breaks --volumes-from on subsequent Start()'s
+func TestStartVolumesFromFailsCleanly(t *testing.T) {
+	defer deleteAllContainers()
+
+	// Create the first data volume
+	cmd(t, "run", "-d", "--name", "data_before", "-v", "/foo", "busybox")
+
+	// Expect this to fail because the data test after contaienr doesn't exist yet
+	if _, err := runCommand(exec.Command(dockerBinary, "run", "-d", "--name", "consumer", "--volumes-from", "data_before", "--volumes-from", "data_after", "busybox")); err == nil {
+		t.Fatal("Expected error but got none")
+	}
+
+	// Create the second data volume
+	cmd(t, "run", "-d", "--name", "data_after", "-v", "/bar", "busybox")
+
+	// Now, all the volumes should be there
+	cmd(t, "start", "consumer")
+
+	// Check that we have the volumes we want
+	out, _, _ := cmd(t, "inspect", "--format='{{ len .Volumes }}'", "consumer")
+	n_volumes := strings.Trim(out, " \r\n'")
+	if n_volumes != "2" {
+		t.Fatalf("Missing volumes: expected 2, got %s", n_volumes)
+	}
+
+	logDone("start - missing containers in --volumes-from did not affect subsequent runs")
+}