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 <mheon@redhat.com>
This commit is contained in:
Matt Heon
2024-04-18 14:56:19 -04:00
parent 273020160c
commit 693ae0ebc6
8 changed files with 47 additions and 2 deletions

View File

@ -41,6 +41,8 @@ Options specific to type=**image**:
- *rw*, *readwrite*: *true* or *false* (default if unspecified: *false*).
- *subpath*: Mount only a specific path within the image, instead of the whole image.
Options specific to **bind** and **glob**:
- *ro*, *readonly*: *true* or *false* (default if unspecified: *false*).

View File

@ -275,6 +275,8 @@ type ContainerImageVolume struct {
Dest string `json:"dest"`
// ReadWrite sets the volume writable.
ReadWrite bool `json:"rw"`
// SubPath determines which part of the image will be mounted into the container.
SubPath string `json:"subPath,omitempty"`
}
// ContainerSecret is a secret that is mounted in a container

View File

@ -459,11 +459,23 @@ func (c *Container) generateSpec(ctx context.Context) (s *spec.Spec, cleanupFunc
return nil, nil, fmt.Errorf("failed to create TempDir in the %s directory: %w", c.config.StaticDir, err)
}
imagePath := mountPoint
if volume.SubPath != "" {
safeMount, err := c.safeMountSubPath(mountPoint, volume.SubPath)
if err != nil {
return nil, nil, err
}
safeMounts = append(safeMounts, safeMount)
imagePath = safeMount.mountPoint
}
var overlayMount spec.Mount
if volume.ReadWrite {
overlayMount, err = overlay.Mount(contentDir, mountPoint, volume.Dest, c.RootUID(), c.RootGID(), c.runtime.store.GraphOptions())
overlayMount, err = overlay.Mount(contentDir, imagePath, volume.Dest, c.RootUID(), c.RootGID(), c.runtime.store.GraphOptions())
} else {
overlayMount, err = overlay.MountReadOnly(contentDir, mountPoint, volume.Dest, c.RootUID(), c.RootGID(), c.runtime.store.GraphOptions())
overlayMount, err = overlay.MountReadOnly(contentDir, imagePath, volume.Dest, c.RootUID(), c.RootGID(), c.runtime.store.GraphOptions())
}
if err != nil {
return nil, nil, fmt.Errorf("creating overlay mount for image %q failed: %w", volume.Source, err)

View File

@ -1474,6 +1474,7 @@ func WithImageVolumes(volumes []*ContainerImageVolume) CtrCreateOption {
Dest: vol.Dest,
Source: vol.Source,
ReadWrite: vol.ReadWrite,
SubPath: vol.SubPath,
})
}

View File

@ -501,6 +501,7 @@ func createContainerOptions(rt *libpod.Runtime, s *specgen.SpecGenerator, pod *l
Dest: v.Destination,
Source: v.Source,
ReadWrite: v.ReadWrite,
SubPath: v.SubPath,
})
}
options = append(options, libpod.WithImageVolumes(vols))

View File

@ -53,6 +53,9 @@ type ImageVolume struct {
Destination string
// ReadWrite sets the volume writable.
ReadWrite bool
// SubPath mounts a particular path within the image.
// If empty, the whole image is mounted.
SubPath string `json:"subPath,omitempty"`
}
// GenVolumeMounts parses user input into mounts, volumes and overlay volumes

View File

@ -611,6 +611,14 @@ func getImageVolume(args []string) (*specgen.ImageVolume, error) {
default:
return nil, fmt.Errorf("invalid rw value %q: %w", value, util.ErrBadMntOption)
}
case "subpath":
if !hasValue {
return nil, fmt.Errorf("%v: %w", name, errOptionArg)
}
if !filepath.IsAbs(value) {
return nil, fmt.Errorf("volume subpath %q must be an absolute path", value)
}
newVolume.SubPath = value
case "consistency":
// Often used on MACs and mistakenly on Linux platforms.
// Since Docker ignores this option so shall we.

View File

@ -934,4 +934,20 @@ USER testuser`, CITEST_IMAGE)
Expect(run).Should(ExitCleanly())
Expect(run.OutputToString()).Should(ContainSubstring(strings.TrimLeft("/vol/", f.Name())))
})
It("podman run --mount type=image with subpath", func() {
ctrCommand := []string{"run", "--mount", fmt.Sprintf("type=image,source=%s,dest=/mnt,subpath=/etc", ALPINE), ALPINE, "ls"}
run1Cmd := append(ctrCommand, "/etc")
run1 := podmanTest.Podman(run1Cmd)
run1.WaitWithDefaultTimeout()
Expect(run1).Should(ExitCleanly())
run2Cmd := append(ctrCommand, "/mnt")
run2 := podmanTest.Podman(run2Cmd)
run2.WaitWithDefaultTimeout()
Expect(run2).Should(ExitCleanly())
Expect(run1.OutputToString()).Should(Equal(run2.OutputToString()))
})
})