diff --git a/libpod/container.go b/libpod/container.go index a3187056a4..5aae2c75e1 100644 --- a/libpod/container.go +++ b/libpod/container.go @@ -254,6 +254,8 @@ type ContainerNamedVolume struct { // IsAnonymous sets the named volume as anonymous even if it has a name // This is used for emptyDir volumes from a kube yaml IsAnonymous bool `json:"setAnonymous,omitempty"` + // SubPath determines which part of the Source will be mounted in the container + SubPath string } // ContainerOverlayVolume is an overlay volume that will be mounted into the @@ -440,6 +442,7 @@ func (c *Container) NamedVolumes() []*ContainerNamedVolume { newVol.Name = vol.Name newVol.Dest = vol.Dest newVol.Options = vol.Options + newVol.SubPath = vol.SubPath volumes = append(volumes, newVol) } diff --git a/libpod/container_internal_common.go b/libpod/container_internal_common.go index bfb5d233e4..b4a5e494d4 100644 --- a/libpod/container_internal_common.go +++ b/libpod/container_internal_common.go @@ -157,6 +157,10 @@ func (c *Container) generateSpec(ctx context.Context) (*spec.Spec, error) { return nil, err } + if len(namedVol.SubPath) > 0 { + mountPoint = filepath.Join(mountPoint, namedVol.SubPath) + } + overlayFlag := false upperDir := "" workDir := "" diff --git a/libpod/options.go b/libpod/options.go index 05bd6d7d1f..46dadb7331 100644 --- a/libpod/options.go +++ b/libpod/options.go @@ -1440,6 +1440,7 @@ func WithNamedVolumes(volumes []*ContainerNamedVolume) CtrCreateOption { Dest: vol.Dest, Options: mountOpts, IsAnonymous: vol.IsAnonymous, + SubPath: vol.SubPath, }) } diff --git a/pkg/specgen/generate/container_create.go b/pkg/specgen/generate/container_create.go index 1cb3731859..635a0820c8 100644 --- a/pkg/specgen/generate/container_create.go +++ b/pkg/specgen/generate/container_create.go @@ -404,6 +404,7 @@ func createContainerOptions(rt *libpod.Runtime, s *specgen.SpecGenerator, pod *l Dest: v.Dest, Options: v.Options, IsAnonymous: v.IsAnonymous, + SubPath: v.SubPath, }) } options = append(options, libpod.WithNamedVolumes(vols)) diff --git a/pkg/specgen/generate/kube/kube.go b/pkg/specgen/generate/kube/kube.go index 18bccdc05d..fa76b55438 100644 --- a/pkg/specgen/generate/kube/kube.go +++ b/pkg/specgen/generate/kube/kube.go @@ -395,6 +395,7 @@ func ToSpecGen(ctx context.Context, opts *CtrSpecGenOptions) (*specgen.SpecGener Dest: volume.MountPath, Name: volumeSource.Source, Options: options, + SubPath: volume.SubPath, } s.Volumes = append(s.Volumes, &namedVolume) case KubeVolumeTypeConfigMap: diff --git a/pkg/specgen/volumes.go b/pkg/specgen/volumes.go index 917da4fd1f..b8b2ece8bd 100644 --- a/pkg/specgen/volumes.go +++ b/pkg/specgen/volumes.go @@ -27,6 +27,8 @@ type NamedVolume struct { // IsAnonymous sets the named volume as anonymous even if it has a name // This is used for emptyDir volumes from a kube yaml IsAnonymous bool + // SubPath stores the sub directory of the named volume to be mounted in the container + SubPath string } // OverlayVolume holds information about an overlay volume that will be mounted into diff --git a/test/e2e/play_kube_test.go b/test/e2e/play_kube_test.go index 538b11a5e6..225fd02bb2 100644 --- a/test/e2e/play_kube_test.go +++ b/test/e2e/play_kube_test.go @@ -247,6 +247,28 @@ spec: - containerPort: 80 ` +var subpathTest = ` +apiVersion: v1 +kind: Pod +metadata: + name: testpod +spec: + containers: + - name: testctr + image: quay.io/libpod/alpine_nginx:latest + command: + - sleep + - inf + volumeMounts: + - mountPath: /var + name: testing + subPath: testing/onlythis + volumes: + - name: testing + persistentVolumeClaim: + claimName: testvol +` + var checkInfraImagePodYaml = ` apiVersion: v1 kind: Pod @@ -4460,4 +4482,39 @@ spec: Expect(ps.OutputToStringArray()).To(HaveLen(0)) }) + It("podman play kube with named volume subpaths", func() { + SkipIfRemote("volume export does not exist on remote") + volumeCreate := podmanTest.Podman([]string{"volume", "create", "testvol1"}) + volumeCreate.WaitWithDefaultTimeout() + Expect(volumeCreate).Should(Exit(0)) + + session := podmanTest.Podman([]string{"run", "--volume", "testvol1:/data", ALPINE, "sh", "-c", "mkdir -p /data/testing/onlythis && touch /data/testing/onlythis/123.txt && echo hi >> /data/testing/onlythis/123.txt"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + tar := filepath.Join(podmanTest.TempDir, "out.tar") + session = podmanTest.Podman([]string{"volume", "export", "--output", tar, "testvol1"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + volumeCreate = podmanTest.Podman([]string{"volume", "create", "testvol"}) + volumeCreate.WaitWithDefaultTimeout() + Expect(volumeCreate).Should(Exit(0)) + + volumeImp := podmanTest.Podman([]string{"volume", "import", "testvol", filepath.Join(podmanTest.TempDir, "out.tar")}) + volumeImp.WaitWithDefaultTimeout() + Expect(volumeImp).Should(Exit(0)) + + err = writeYaml(subpathTest, kubeYaml) + Expect(err).ToNot(HaveOccurred()) + + playKube := podmanTest.Podman([]string{"play", "kube", kubeYaml}) + playKube.WaitWithDefaultTimeout() + Expect(playKube).Should(Exit(0)) + + exec := podmanTest.Podman([]string{"exec", "-it", "testpod-testctr", "cat", "/var/123.txt"}) + exec.WaitWithDefaultTimeout() + Expect(exec).Should(Exit(0)) + Expect(exec.OutputToString()).Should(Equal("hi")) + }) })