Skip to content

Commit 693ae0e

Browse files
committed
Add support for image volume subpaths
Image volumes (the `--mount type=image,...` kind, not the `podman volume create --driver image ...` kind - it's strange that we have two) are needed for our automount scheme, but the request is that we mount only specific subpaths from the image into the container. To do that, we need image volume subpath support. Not that difficult code-wise, mostly just plumbing. Also, add support to the CLI; not strictly necessary, but it doesn't hurt anything and will make testing easier. Signed-off-by: Matt Heon <[email protected]>
1 parent 2730201 commit 693ae0e

File tree

8 files changed

+47
-2
lines changed

8 files changed

+47
-2
lines changed

docs/source/markdown/options/mount.md

+2
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ Options specific to type=**image**:
4141

4242
- *rw*, *readwrite*: *true* or *false* (default if unspecified: *false*).
4343

44+
- *subpath*: Mount only a specific path within the image, instead of the whole image.
45+
4446
Options specific to **bind** and **glob**:
4547

4648
- *ro*, *readonly*: *true* or *false* (default if unspecified: *false*).

libpod/container.go

+2
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,8 @@ type ContainerImageVolume struct {
275275
Dest string `json:"dest"`
276276
// ReadWrite sets the volume writable.
277277
ReadWrite bool `json:"rw"`
278+
// SubPath determines which part of the image will be mounted into the container.
279+
SubPath string `json:"subPath,omitempty"`
278280
}
279281

280282
// ContainerSecret is a secret that is mounted in a container

libpod/container_internal_common.go

+14-2
Original file line numberDiff line numberDiff line change
@@ -459,11 +459,23 @@ func (c *Container) generateSpec(ctx context.Context) (s *spec.Spec, cleanupFunc
459459
return nil, nil, fmt.Errorf("failed to create TempDir in the %s directory: %w", c.config.StaticDir, err)
460460
}
461461

462+
imagePath := mountPoint
463+
if volume.SubPath != "" {
464+
safeMount, err := c.safeMountSubPath(mountPoint, volume.SubPath)
465+
if err != nil {
466+
return nil, nil, err
467+
}
468+
469+
safeMounts = append(safeMounts, safeMount)
470+
471+
imagePath = safeMount.mountPoint
472+
}
473+
462474
var overlayMount spec.Mount
463475
if volume.ReadWrite {
464-
overlayMount, err = overlay.Mount(contentDir, mountPoint, volume.Dest, c.RootUID(), c.RootGID(), c.runtime.store.GraphOptions())
476+
overlayMount, err = overlay.Mount(contentDir, imagePath, volume.Dest, c.RootUID(), c.RootGID(), c.runtime.store.GraphOptions())
465477
} else {
466-
overlayMount, err = overlay.MountReadOnly(contentDir, mountPoint, volume.Dest, c.RootUID(), c.RootGID(), c.runtime.store.GraphOptions())
478+
overlayMount, err = overlay.MountReadOnly(contentDir, imagePath, volume.Dest, c.RootUID(), c.RootGID(), c.runtime.store.GraphOptions())
467479
}
468480
if err != nil {
469481
return nil, nil, fmt.Errorf("creating overlay mount for image %q failed: %w", volume.Source, err)

libpod/options.go

+1
Original file line numberDiff line numberDiff line change
@@ -1474,6 +1474,7 @@ func WithImageVolumes(volumes []*ContainerImageVolume) CtrCreateOption {
14741474
Dest: vol.Dest,
14751475
Source: vol.Source,
14761476
ReadWrite: vol.ReadWrite,
1477+
SubPath: vol.SubPath,
14771478
})
14781479
}
14791480

pkg/specgen/generate/container_create.go

+1
Original file line numberDiff line numberDiff line change
@@ -501,6 +501,7 @@ func createContainerOptions(rt *libpod.Runtime, s *specgen.SpecGenerator, pod *l
501501
Dest: v.Destination,
502502
Source: v.Source,
503503
ReadWrite: v.ReadWrite,
504+
SubPath: v.SubPath,
504505
})
505506
}
506507
options = append(options, libpod.WithImageVolumes(vols))

pkg/specgen/volumes.go

+3
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,9 @@ type ImageVolume struct {
5353
Destination string
5454
// ReadWrite sets the volume writable.
5555
ReadWrite bool
56+
// SubPath mounts a particular path within the image.
57+
// If empty, the whole image is mounted.
58+
SubPath string `json:"subPath,omitempty"`
5659
}
5760

5861
// GenVolumeMounts parses user input into mounts, volumes and overlay volumes

pkg/specgenutil/volumes.go

+8
Original file line numberDiff line numberDiff line change
@@ -611,6 +611,14 @@ func getImageVolume(args []string) (*specgen.ImageVolume, error) {
611611
default:
612612
return nil, fmt.Errorf("invalid rw value %q: %w", value, util.ErrBadMntOption)
613613
}
614+
case "subpath":
615+
if !hasValue {
616+
return nil, fmt.Errorf("%v: %w", name, errOptionArg)
617+
}
618+
if !filepath.IsAbs(value) {
619+
return nil, fmt.Errorf("volume subpath %q must be an absolute path", value)
620+
}
621+
newVolume.SubPath = value
614622
case "consistency":
615623
// Often used on MACs and mistakenly on Linux platforms.
616624
// Since Docker ignores this option so shall we.

test/e2e/run_volume_test.go

+16
Original file line numberDiff line numberDiff line change
@@ -934,4 +934,20 @@ USER testuser`, CITEST_IMAGE)
934934
Expect(run).Should(ExitCleanly())
935935
Expect(run.OutputToString()).Should(ContainSubstring(strings.TrimLeft("/vol/", f.Name())))
936936
})
937+
938+
It("podman run --mount type=image with subpath", func() {
939+
ctrCommand := []string{"run", "--mount", fmt.Sprintf("type=image,source=%s,dest=/mnt,subpath=/etc", ALPINE), ALPINE, "ls"}
940+
941+
run1Cmd := append(ctrCommand, "/etc")
942+
run1 := podmanTest.Podman(run1Cmd)
943+
run1.WaitWithDefaultTimeout()
944+
Expect(run1).Should(ExitCleanly())
945+
946+
run2Cmd := append(ctrCommand, "/mnt")
947+
run2 := podmanTest.Podman(run2Cmd)
948+
run2.WaitWithDefaultTimeout()
949+
Expect(run2).Should(ExitCleanly())
950+
951+
Expect(run1.OutputToString()).Should(Equal(run2.OutputToString()))
952+
})
937953
})

0 commit comments

Comments
 (0)