Browse code

Allow normal volume to overwrite in start Binds

Fixes #9981
Allows a volume which was created by docker (ie, in
/var/lib/docker/vfs/dir) to be used as a Bind argument via the container
start API and overwrite an existing volume.

For example:

```bash
docker create -v /foo --name one
docker create -v /foo --name two
```

This allows the volume from `one` to be passed into the container start
API as a bind to `two`, and it will overwrite it.

This was possible before 7107898d5cf0f86dc1c6dab29e9dbdad3edc9411

Signed-off-by: Brian Goff <cpuguy83@gmail.com>

Brian Goff authored on 2015/01/27 07:31:30
Showing 2 changed files
... ...
@@ -25,6 +25,7 @@ type Mount struct {
25 25
 	Writable    bool
26 26
 	copyData    bool
27 27
 	from        *Container
28
+	isBind      bool
28 29
 }
29 30
 
30 31
 func (mnt *Mount) Export(resource string) (io.ReadCloser, error) {
... ...
@@ -80,7 +81,7 @@ func (m *Mount) initialize() error {
80 80
 	if hostPath, exists := m.container.Volumes[m.MountToPath]; exists {
81 81
 		// If this is a bind-mount/volumes-from, maybe it was passed in at start instead of create
82 82
 		// We need to make sure bind-mounts/volumes-from passed on start can override existing ones.
83
-		if !m.volume.IsBindMount && m.from == nil {
83
+		if (!m.volume.IsBindMount && !m.isBind) && m.from == nil {
84 84
 			return nil
85 85
 		}
86 86
 		if m.volume.Path == hostPath {
... ...
@@ -172,6 +173,7 @@ func (container *Container) parseVolumeMountConfig() (map[string]*Mount, error)
172 172
 			volume:      vol,
173 173
 			MountToPath: mountToPath,
174 174
 			Writable:    writable,
175
+			isBind:      true, // in case the volume itself is a normal volume, but is being mounted in as a bindmount here
175 176
 		}
176 177
 	}
177 178
 
... ...
@@ -403,3 +403,40 @@ func TestBuildApiDockerfileSymlink(t *testing.T) {
403 403
 
404 404
 	logDone("container REST API - check build w/bad Dockerfile symlink path")
405 405
 }
406
+
407
+// #9981 - Allow a docker created volume (ie, one in /var/lib/docker/volumes) to be used to overwrite (via passing in Binds on api start) an existing volume
408
+func TestPostContainerBindNormalVolume(t *testing.T) {
409
+	defer deleteAllContainers()
410
+
411
+	out, _, err := runCommandWithOutput(exec.Command(dockerBinary, "create", "-v", "/foo", "--name=one", "busybox"))
412
+	if err != nil {
413
+		t.Fatal(err, out)
414
+	}
415
+
416
+	fooDir, err := inspectFieldMap("one", "Volumes", "/foo")
417
+	if err != nil {
418
+		t.Fatal(err)
419
+	}
420
+
421
+	out, _, err = runCommandWithOutput(exec.Command(dockerBinary, "create", "-v", "/foo", "--name=two", "busybox"))
422
+	if err != nil {
423
+		t.Fatal(err, out)
424
+	}
425
+
426
+	bindSpec := map[string][]string{"Binds": {fooDir + ":/foo"}}
427
+	_, err = sockRequest("POST", "/containers/two/start", bindSpec)
428
+	if err != nil && !strings.Contains(err.Error(), "204 No Content") {
429
+		t.Fatal(err)
430
+	}
431
+
432
+	fooDir2, err := inspectFieldMap("two", "Volumes", "/foo")
433
+	if err != nil {
434
+		t.Fatal(err)
435
+	}
436
+
437
+	if fooDir2 != fooDir {
438
+		t.Fatal("expected volume path to be %s, got: %s", fooDir, fooDir2)
439
+	}
440
+
441
+	logDone("container REST API - can use path from normal volume as bind-mount to overwrite another volume")
442
+}