diff options
author | Valentin Rothberg <rothberg@redhat.com> | 2020-10-26 11:35:02 +0100 |
---|---|---|
committer | Valentin Rothberg <rothberg@redhat.com> | 2020-10-29 15:06:22 +0100 |
commit | 65a618886efc48562e5b9ff99ca630c83622419b (patch) | |
tree | 09d19a7f6fe596a1b9e19fec6e45288f2b76de5a /libpod | |
parent | cce6c6cd40137c460f173300b36c5868383870c5 (diff) | |
download | podman-65a618886efc48562e5b9ff99ca630c83622419b.tar.gz podman-65a618886efc48562e5b9ff99ca630c83622419b.tar.bz2 podman-65a618886efc48562e5b9ff99ca630c83622419b.zip |
new "image" mount type
Add a new "image" mount type to `--mount`. The source of the mount is
the name or ID of an image. The destination is the path inside the
container. Image mounts further support an optional `rw,readwrite`
parameter which if set to "true" will yield the mount writable inside
the container. Note that no changes are propagated to the image mount
on the host (which in any case is read only).
Mounts are overlay mounts. To support read-only overlay mounts, vendor
a non-release version of Buildah.
Signed-off-by: Valentin Rothberg <rothberg@redhat.com>
Diffstat (limited to 'libpod')
-rw-r--r-- | libpod/container.go | 13 | ||||
-rw-r--r-- | libpod/container_config.go | 2 | ||||
-rw-r--r-- | libpod/container_inspect.go | 15 | ||||
-rw-r--r-- | libpod/container_internal.go | 19 | ||||
-rw-r--r-- | libpod/container_internal_linux.go | 30 | ||||
-rw-r--r-- | libpod/container_validate.go | 10 | ||||
-rw-r--r-- | libpod/options.go | 19 |
7 files changed, 105 insertions, 3 deletions
diff --git a/libpod/container.go b/libpod/container.go index 01419500e..ea5a6e09c 100644 --- a/libpod/container.go +++ b/libpod/container.go @@ -235,6 +235,19 @@ type ContainerOverlayVolume struct { Source string `json:"source,omitempty"` } +// ContainerImageVolume is a volume based on a container image. The container +// image is first mounted on the host and is then bind-mounted into the +// container. +type ContainerImageVolume struct { + // Source is the source of the image volume. The image can be referred + // to by name and by ID. + Source string `json:"source"` + // Dest is the absolute path of the mount in the container. + Dest string `json:"dest"` + // ReadWrite sets the volume writable. + ReadWrite bool `json:"rw"` +} + // Config accessors // Unlocked diff --git a/libpod/container_config.go b/libpod/container_config.go index e264da4da..0006613bc 100644 --- a/libpod/container_config.go +++ b/libpod/container_config.go @@ -134,6 +134,8 @@ type ContainerRootFSConfig struct { NamedVolumes []*ContainerNamedVolume `json:"namedVolumes,omitempty"` // OverlayVolumes lists the overlay volumes to mount into the container. OverlayVolumes []*ContainerOverlayVolume `json:"overlayVolumes,omitempty"` + // ImageVolumes lists the image volumes to mount into the container. + ImageVolumes []*ContainerImageVolume `json:"imageVolumes,omitempty"` // CreateWorkingDir indicates that Libpod should create the container's // working directory if it does not exist. Some OCI runtimes do this by // default, but others do not. diff --git a/libpod/container_inspect.go b/libpod/container_inspect.go index b8bce1272..162f70326 100644 --- a/libpod/container_inspect.go +++ b/libpod/container_inspect.go @@ -90,7 +90,7 @@ func (c *Container) getContainerInspectData(size bool, driverData *driver.Data) } namedVolumes, mounts := c.sortUserVolumes(ctrSpec) - inspectMounts, err := c.getInspectMounts(namedVolumes, mounts) + inspectMounts, err := c.getInspectMounts(namedVolumes, c.config.ImageVolumes, mounts) if err != nil { return nil, err } @@ -192,7 +192,7 @@ func (c *Container) getContainerInspectData(size bool, driverData *driver.Data) // Get inspect-formatted mounts list. // Only includes user-specified mounts. Only includes bind mounts and named // volumes, not tmpfs volumes. -func (c *Container) getInspectMounts(namedVolumes []*ContainerNamedVolume, mounts []spec.Mount) ([]define.InspectMount, error) { +func (c *Container) getInspectMounts(namedVolumes []*ContainerNamedVolume, imageVolumes []*ContainerImageVolume, mounts []spec.Mount) ([]define.InspectMount, error) { inspectMounts := []define.InspectMount{} // No mounts, return early @@ -219,6 +219,17 @@ func (c *Container) getInspectMounts(namedVolumes []*ContainerNamedVolume, mount inspectMounts = append(inspectMounts, mountStruct) } + + for _, volume := range imageVolumes { + mountStruct := define.InspectMount{} + mountStruct.Type = "image" + mountStruct.Destination = volume.Dest + mountStruct.Source = volume.Source + mountStruct.RW = volume.ReadWrite + + inspectMounts = append(inspectMounts, mountStruct) + } + for _, mount := range mounts { // It's a mount. // Is it a tmpfs? If so, discard. diff --git a/libpod/container_internal.go b/libpod/container_internal.go index 4ae571de6..cafe70b80 100644 --- a/libpod/container_internal.go +++ b/libpod/container_internal.go @@ -1734,6 +1734,25 @@ func (c *Container) cleanup(ctx context.Context) error { } } + // Unmount image volumes + for _, v := range c.config.ImageVolumes { + img, err := c.runtime.ImageRuntime().NewFromLocal(v.Source) + if err != nil { + if lastError == nil { + lastError = err + continue + } + logrus.Errorf("error unmounting image volume %q:%q :%v", v.Source, v.Dest, err) + } + if err := img.Unmount(false); err != nil { + if lastError == nil { + lastError = err + continue + } + logrus.Errorf("error unmounting image volume %q:%q :%v", v.Source, v.Dest, err) + } + } + return lastError } diff --git a/libpod/container_internal_linux.go b/libpod/container_internal_linux.go index a1b4334fb..57d5100cf 100644 --- a/libpod/container_internal_linux.go +++ b/libpod/container_internal_linux.go @@ -39,6 +39,7 @@ import ( "github.com/containers/storage/pkg/idtools" securejoin "github.com/cyphar/filepath-securejoin" runcuser "github.com/opencontainers/runc/libcontainer/user" + "github.com/opencontainers/runtime-spec/specs-go" spec "github.com/opencontainers/runtime-spec/specs-go" "github.com/opencontainers/runtime-tools/generate" "github.com/opencontainers/selinux/go-selinux/label" @@ -368,6 +369,35 @@ func (c *Container) generateSpec(ctx context.Context) (*spec.Spec, error) { g.AddMount(overlayMount) } + // Add image volumes as overlay mounts + for _, volume := range c.config.ImageVolumes { + // Mount the specified image. + img, err := c.runtime.ImageRuntime().NewFromLocal(volume.Source) + if err != nil { + return nil, errors.Wrapf(err, "error creating image volume %q:%q", volume.Source, volume.Dest) + } + mountPoint, err := img.Mount(nil, "") + if err != nil { + return nil, errors.Wrapf(err, "error mounting image volume %q:%q", volume.Source, volume.Dest) + } + + contentDir, err := overlay.TempDir(c.config.StaticDir, c.RootUID(), c.RootGID()) + if err != nil { + return nil, errors.Wrapf(err, "failed to create TempDir in the %s directory", c.config.StaticDir) + } + + var overlayMount specs.Mount + if volume.ReadWrite { + overlayMount, err = overlay.Mount(contentDir, mountPoint, 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()) + } + if err != nil { + return nil, errors.Wrapf(err, "creating overlay mount for image %q failed", volume.Source) + } + g.AddMount(overlayMount) + } + hasHomeSet := false for _, s := range c.config.Spec.Process.Env { if strings.HasPrefix(s, "HOME=") { diff --git a/libpod/container_validate.go b/libpod/container_validate.go index b78168cd1..68cc095b7 100644 --- a/libpod/container_validate.go +++ b/libpod/container_validate.go @@ -88,7 +88,7 @@ func (c *Container) validate() error { return errors.Wrapf(define.ErrInvalidArg, "cannot add to /etc/hosts if using image's /etc/hosts") } - // Check named volume and overlay volumes destination conflits + // Check named volume, overlay volume and image volume destination conflits destinations := make(map[string]bool) for _, vol := range c.config.NamedVolumes { // Don't check if they already exist. @@ -106,6 +106,14 @@ func (c *Container) validate() error { } destinations[vol.Dest] = true } + for _, vol := range c.config.ImageVolumes { + // Don't check if they already exist. + // If they don't we will automatically create them. + if _, ok := destinations[vol.Dest]; ok { + return errors.Wrapf(define.ErrInvalidArg, "two volumes found with destination %s", vol.Dest) + } + destinations[vol.Dest] = true + } return nil } diff --git a/libpod/options.go b/libpod/options.go index 1ffb78da9..5d1ce8755 100644 --- a/libpod/options.go +++ b/libpod/options.go @@ -1439,6 +1439,25 @@ func WithOverlayVolumes(volumes []*ContainerOverlayVolume) CtrCreateOption { } } +// WithImageVolumes adds the given image volumes to the container. +func WithImageVolumes(volumes []*ContainerImageVolume) CtrCreateOption { + return func(ctr *Container) error { + if ctr.valid { + return define.ErrCtrFinalized + } + + for _, vol := range volumes { + ctr.config.ImageVolumes = append(ctr.config.ImageVolumes, &ContainerImageVolume{ + Dest: vol.Dest, + Source: vol.Source, + ReadWrite: vol.ReadWrite, + }) + } + + return nil + } +} + // WithHealthCheck adds the healthcheck to the container config func WithHealthCheck(healthCheck *manifest.Schema2HealthConfig) CtrCreateOption { return func(ctr *Container) error { |