mirror of
https://github.com/containers/podman.git
synced 2025-05-17 23:26:08 +08:00
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:
@ -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*).
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -1474,6 +1474,7 @@ func WithImageVolumes(volumes []*ContainerImageVolume) CtrCreateOption {
|
||||
Dest: vol.Dest,
|
||||
Source: vol.Source,
|
||||
ReadWrite: vol.ReadWrite,
|
||||
SubPath: vol.SubPath,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -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))
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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()))
|
||||
})
|
||||
})
|
||||
|
Reference in New Issue
Block a user