diff options
-rw-r--r-- | libpod/runtime_ctr.go | 26 | ||||
-rw-r--r-- | pkg/specgen/generate/container_create.go | 10 | ||||
-rw-r--r-- | test/e2e/container_create_volume_test.go | 127 |
3 files changed, 162 insertions, 1 deletions
diff --git a/libpod/runtime_ctr.go b/libpod/runtime_ctr.go index 6c69d1b72..ce4c5d758 100644 --- a/libpod/runtime_ctr.go +++ b/libpod/runtime_ctr.go @@ -47,6 +47,32 @@ func (r *Runtime) NewContainer(ctx context.Context, rSpec *spec.Spec, options .. return r.newContainer(ctx, rSpec, options...) } +func (r *Runtime) PrepareVolumeOnCreateContainer(ctx context.Context, ctr *Container) error { + // Copy the content from the underlying image into the newly created + // volume if configured to do so. + if !r.config.Containers.PrepareVolumeOnCreate { + return nil + } + + defer func() { + if err := ctr.cleanupStorage(); err != nil { + logrus.Errorf("error cleaning up container storage %s: %v", ctr.ID(), err) + } + }() + + mountPoint, err := ctr.mountStorage() + if err == nil { + // Finish up mountStorage + ctr.state.Mounted = true + ctr.state.Mountpoint = mountPoint + if err = ctr.save(); err != nil { + logrus.Errorf("Error saving container %s state: %v", ctr.ID(), err) + } + } + + return err +} + // RestoreContainer re-creates a container from an imported checkpoint func (r *Runtime) RestoreContainer(ctx context.Context, rSpec *spec.Spec, config *ContainerConfig) (*Container, error) { r.lock.Lock() diff --git a/pkg/specgen/generate/container_create.go b/pkg/specgen/generate/container_create.go index b569f8390..4e3a86ae4 100644 --- a/pkg/specgen/generate/container_create.go +++ b/pkg/specgen/generate/container_create.go @@ -153,7 +153,15 @@ func MakeContainer(ctx context.Context, rt *libpod.Runtime, s *specgen.SpecGener if err != nil { return nil, err } - return rt.NewContainer(ctx, runtimeSpec, options...) + + ctr, err := rt.NewContainer(ctx, runtimeSpec, options...) + if err != nil { + return ctr, err + } + + // Copy the content from the underlying image into the newly created + // volume if configured to do so. + return ctr, rt.PrepareVolumeOnCreateContainer(ctx, ctr) } func extractCDIDevices(s *specgen.SpecGenerator) []libpod.CtrCreateOption { diff --git a/test/e2e/container_create_volume_test.go b/test/e2e/container_create_volume_test.go new file mode 100644 index 000000000..001698239 --- /dev/null +++ b/test/e2e/container_create_volume_test.go @@ -0,0 +1,127 @@ +package integration + +import ( + "fmt" + "io/ioutil" + "os" + "path/filepath" + + . "github.com/containers/podman/v3/test/utils" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + . "github.com/onsi/gomega/gexec" +) + +func buildDataVolumeImage(pTest *PodmanTestIntegration, image, data, dest string) { + // Create a dummy file for data volume + dummyFile := filepath.Join(pTest.TempDir, data) + err := ioutil.WriteFile(dummyFile, []byte(data), 0644) + Expect(err).To(BeNil()) + + // Create a data volume container image but no CMD binary in it + containerFile := fmt.Sprintf(`FROM scratch +CMD doesnotexist.sh +ADD %s %s/ +VOLUME %s/`, data, dest, dest) + pTest.BuildImage(containerFile, image, "false") +} + +func createContainersConfFile(pTest *PodmanTestIntegration) { + configPath := filepath.Join(pTest.TempDir, "containers.conf") + containersConf := []byte(fmt.Sprintf("[containers]\nprepare_volume_on_create = true\n")) + err := ioutil.WriteFile(configPath, containersConf, os.ModePerm) + Expect(err).To(BeNil()) + + // Set custom containers.conf file + os.Setenv("CONTAINERS_CONF", configPath) + if IsRemote() { + pTest.RestartRemoteService() + } +} + +func checkDataVolumeContainer(pTest *PodmanTestIntegration, image, cont, dest, data string) { + create := pTest.Podman([]string{"create", "--name", cont, image}) + create.WaitWithDefaultTimeout() + Expect(create).Should(Exit(0)) + + inspect := pTest.InspectContainer(cont) + Expect(len(inspect)).To(Equal(1)) + Expect(len(inspect[0].Mounts)).To(Equal(1)) + Expect(inspect[0].Mounts[0].Destination).To(Equal(dest)) + + mntName, mntSource := inspect[0].Mounts[0].Name, inspect[0].Mounts[0].Source + + volList := pTest.Podman([]string{"volume", "list", "--quiet"}) + volList.WaitWithDefaultTimeout() + Expect(volList).Should(Exit(0)) + Expect(len(volList.OutputToStringArray())).To(Equal(1)) + Expect(volList.OutputToStringArray()[0]).To(Equal(mntName)) + + // Check the mount source directory + files, err := ioutil.ReadDir(mntSource) + Expect(err).To(BeNil()) + + if data == "" { + Expect(len(files)).To(Equal(0)) + } else { + Expect(len(files)).To(Equal(1)) + Expect(files[0].Name()).To(Equal(data)) + } +} + +var _ = Describe("Podman create data volume", func() { + var ( + tempdir string + err error + podmanTest *PodmanTestIntegration + ) + + BeforeEach(func() { + tempdir, err = CreateTempDirInTempDir() + if err != nil { + os.Exit(1) + } + podmanTest = PodmanTestCreate(tempdir) + podmanTest.Setup() + podmanTest.SeedImages() + }) + + AfterEach(func() { + podmanTest.Cleanup() + f := CurrentGinkgoTestDescription() + processTestResult(f) + os.Unsetenv("CONTAINERS_CONF") + }) + + It("podman create with volume data copy turned off", func() { + imgName, volData, volDest := "dataimg", "dummy", "/test" + + buildDataVolumeImage(podmanTest, imgName, volData, volDest) + + // Create a container with the default containers.conf and + // check that the volume is not copied from the image. + checkDataVolumeContainer(podmanTest, imgName, "ctr-nocopy", volDest, "") + }) + + It("podman create with volume data copy turned on", func() { + imgName, volData, volDest := "dataimg", "dummy", "/test" + + buildDataVolumeImage(podmanTest, imgName, volData, volDest) + + // Create a container with the custom containers.conf and + // check that the volume is copied from the image. + createContainersConfFile(podmanTest) + + checkDataVolumeContainer(podmanTest, imgName, "ctr-copy", volDest, volData) + }) + + It("podman run with volume data copy turned on", func() { + // Create a container with the custom containers.conf and + // check that the container is run successfully + createContainersConfFile(podmanTest) + + session := podmanTest.Podman([]string{"run", "--rm", ALPINE, "echo"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + }) +}) |