From 7a9c71183212a40fdd10369d009a9f97b56fe359 Mon Sep 17 00:00:00 2001 From: Brian Olsen Date: Fri, 30 Aug 2013 02:29:35 +0200 Subject: [PATCH] Copies content from image to volumne if non-empty. Fixes #1582. --- container.go | 33 +++++++++++++++++++++++------ container_test.go | 54 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 81 insertions(+), 6 deletions(-) diff --git a/container.go b/container.go index 5799fa4e12..6b52983b0d 100644 --- a/container.go +++ b/container.go @@ -642,11 +642,13 @@ func (container *Container) Start(hostConfig *HostConfig) error { if _, exists := container.Volumes[volPath]; exists { continue } + var srcPath string + srcRW := false // If an external bind is defined for this volume, use that as a source if bindMap, exists := binds[volPath]; exists { - container.Volumes[volPath] = bindMap.SrcPath + srcPath = bindMap.SrcPath if strings.ToLower(bindMap.Mode) == "rw" { - container.VolumesRW[volPath] = true + srcRW = true } // Otherwise create an directory in $ROOT/volumes/ and use that } else { @@ -654,17 +656,36 @@ func (container *Container) Start(hostConfig *HostConfig) error { if err != nil { return err } - srcPath, err := c.layer() + srcPath, err = c.layer() if err != nil { return err } - container.Volumes[volPath] = srcPath - container.VolumesRW[volPath] = true // RW by default + srcRW = true // RW by default } + container.Volumes[volPath] = srcPath + container.VolumesRW[volPath] = srcRW // Create the mountpoint - if err := os.MkdirAll(path.Join(container.RootfsPath(), volPath), 0755); err != nil { + rootVolPath := path.Join(container.RootfsPath(), volPath) + if err := os.MkdirAll(rootVolPath, 0755); err != nil { return nil } + if srcRW { + volList, err := ioutil.ReadDir(rootVolPath) + if err != nil { + return err + } + if len(volList) > 0 { + srcList, err := ioutil.ReadDir(srcPath) + if err != nil { + return err + } + if len(srcList) == 0 { + if err := CopyWithTar(rootVolPath, srcPath); err != nil { + return err + } + } + } + } } if err := container.generateLXCConfig(hostConfig); err != nil { diff --git a/container_test.go b/container_test.go index ba48ceb47a..9b6b563048 100644 --- a/container_test.go +++ b/container_test.go @@ -1193,6 +1193,60 @@ func tempDir(t *testing.T) string { return tmpDir } +// Test for #1582 +func TestCopyVolumeContent(t *testing.T) { + r := mkRuntime(t) + defer nuke(r) + + // Put some content in a directory of a container and commit it + container1, _, _ := mkContainer(r, []string{"_", "/bin/sh", "-c", "mkdir -p /hello/local && echo hello > /hello/local/world"}, t) + defer r.Destroy(container1) + + if container1.State.Running { + t.Errorf("Container shouldn't be running") + } + if err := container1.Run(); err != nil { + t.Fatal(err) + } + if container1.State.Running { + t.Errorf("Container shouldn't be running") + } + + rwTar, err := container1.ExportRw() + if err != nil { + t.Error(err) + } + img, err := r.graph.Create(rwTar, container1, "unit test commited image", "", nil) + if err != nil { + t.Error(err) + } + + // Test that the content is copied from the image to the volume + tmpDir1 := tempDir(t) + defer os.RemoveAll(tmpDir1) + stdout1, _ := runContainer(r, []string{"-v", fmt.Sprintf("%s:/hello", tmpDir1), img.ID, "find", "/hello"}, t) + if !(strings.Contains(stdout1, "/hello/local/world") && strings.Contains(stdout1, "/hello/local")) { + t.Fatal("Container failed to transfer content to volume") + } + + // Test that the content is not copied when the volume is readonly + tmpDir2 := tempDir(t) + defer os.RemoveAll(tmpDir2) + stdout2, _ := runContainer(r, []string{"-v", fmt.Sprintf("%s:/hello:ro", tmpDir2), img.ID, "find", "/hello"}, t) + if strings.Contains(stdout2, "/hello/local/world") || strings.Contains(stdout2, "/hello/local") { + t.Fatal("Container transfered content to readonly volume") + } + + // Test that the content is not copied when the volume is non-empty + tmpDir3 := tempDir(t) + defer os.RemoveAll(tmpDir3) + writeFile(path.Join(tmpDir3, "touch-me"), "", t) + stdout3, _ := runContainer(r, []string{"-v", fmt.Sprintf("%s:/hello:rw", tmpDir3), img.ID, "find", "/hello"}, t) + if strings.Contains(stdout3, "/hello/local/world") || strings.Contains(stdout3, "/hello/local") || !strings.Contains(stdout3, "/hello/touch-me") { + t.Fatal("Container transfered content to non-empty volume") + } +} + func TestBindMounts(t *testing.T) { r := mkRuntime(t) defer nuke(r)