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>
| ... | ... |
@@ -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 |
+} |