summaryrefslogtreecommitdiff
path: root/libpod/runtime_volume_linux.go
diff options
context:
space:
mode:
authorMatthew Heon <matthew.heon@pm.me>2022-09-16 15:00:37 -0400
committerMatthew Heon <mheon@redhat.com>2022-09-22 13:07:40 -0400
commitfc6dcd12b3430f2d1ee495ef19d184a088f3bb34 (patch)
tree0037ce64d14f971fec5cd29e16a63e7a0f331dc1 /libpod/runtime_volume_linux.go
parent08993516a939576fa009db6e7ed32524026a822d (diff)
downloadpodman-fc6dcd12b3430f2d1ee495ef19d184a088f3bb34.tar.gz
podman-fc6dcd12b3430f2d1ee495ef19d184a088f3bb34.tar.bz2
podman-fc6dcd12b3430f2d1ee495ef19d184a088f3bb34.zip
Add support for 'image' volume driver
We added the concept of image volumes in 2.2.0, to support inspecting an image from within a container. However, this is a strictly read-only mount, with no modification allowed. By contrast, the new `image` volume driver creates a c/storage container as its underlying storage, so we have a read/write layer. This, in and of itself, is not especially interesting, but what it will enable in the future is. If we add a new command to allow these image volumes to be committed, we can now distribute volumes - and changes to them - via a standard OCI image registry (which is rather new and quite exciting). Future work in this area: - Add support for `podman volume push` (commit volume changes and push resulting image to OCI registry). - Add support for `podman volume pull` (currently, we require that the image a volume is created from be already pulled; it would be simpler if we had a dedicated command that did the pull and made a volume from it) - Add support for scratch images (make an empty image on demand to use as the base of the volume) - Add UOR support to `podman volume push` and `podman volume pull` to enable both with non-image volume drivers Signed-off-by: Matthew Heon <matthew.heon@pm.me>
Diffstat (limited to 'libpod/runtime_volume_linux.go')
-rw-r--r--libpod/runtime_volume_linux.go61
1 files changed, 58 insertions, 3 deletions
diff --git a/libpod/runtime_volume_linux.go b/libpod/runtime_volume_linux.go
index 08fdbf977..c59417979 100644
--- a/libpod/runtime_volume_linux.go
+++ b/libpod/runtime_volume_linux.go
@@ -15,6 +15,7 @@ import (
"github.com/containers/podman/v4/libpod/define"
"github.com/containers/podman/v4/libpod/events"
volplugin "github.com/containers/podman/v4/libpod/plugin"
+ "github.com/containers/storage"
"github.com/containers/storage/drivers/quota"
"github.com/containers/storage/pkg/idtools"
"github.com/containers/storage/pkg/stringid"
@@ -22,18 +23,20 @@ import (
"github.com/sirupsen/logrus"
)
+const volumeSuffix = "+volume"
+
// NewVolume creates a new empty volume
func (r *Runtime) NewVolume(ctx context.Context, options ...VolumeCreateOption) (*Volume, error) {
if !r.valid {
return nil, define.ErrRuntimeStopped
}
- return r.newVolume(false, options...)
+ return r.newVolume(ctx, false, options...)
}
// newVolume creates a new empty volume with the given options.
// The createPluginVolume can be set to true to make it not create the volume in the volume plugin,
// this is required for the UpdateVolumePlugins() function. If you are not sure, set this to false.
-func (r *Runtime) newVolume(noCreatePluginVolume bool, options ...VolumeCreateOption) (_ *Volume, deferredErr error) {
+func (r *Runtime) newVolume(ctx context.Context, noCreatePluginVolume bool, options ...VolumeCreateOption) (_ *Volume, deferredErr error) {
volume := newVolume(r)
for _, option := range options {
if err := option(volume); err != nil {
@@ -83,6 +86,50 @@ func (r *Runtime) newVolume(noCreatePluginVolume bool, options ...VolumeCreateOp
return nil, fmt.Errorf("invalid mount option %s for driver 'local': %w", key, define.ErrInvalidArg)
}
}
+ } else if volume.config.Driver == define.VolumeDriverImage && !volume.UsesVolumeDriver() {
+ logrus.Debugf("Creating image-based volume")
+ var imgString string
+ // Validate options
+ for key, val := range volume.config.Options {
+ switch strings.ToLower(key) {
+ case "image":
+ imgString = val
+ default:
+ return nil, fmt.Errorf("invalid mount option %s for driver 'image': %w", key, define.ErrInvalidArg)
+ }
+ }
+
+ if imgString == "" {
+ return nil, fmt.Errorf("must provide an image name when creating a volume with the image driver: %w", define.ErrInvalidArg)
+ }
+
+ // Look up the image
+ image, _, err := r.libimageRuntime.LookupImage(imgString, nil)
+ if err != nil {
+ return nil, fmt.Errorf("looking up image %s to create volume failed: %w", imgString, err)
+ }
+
+ // Generate a c/storage name and ID for the volume.
+ // Use characters Podman does not allow for the name, to ensure
+ // no collision with containers.
+ volume.config.StorageID = stringid.GenerateRandomID()
+ volume.config.StorageName = volume.config.Name + volumeSuffix
+ volume.config.StorageImageID = image.ID()
+
+ // Create a backing container in c/storage.
+ storageConfig := storage.ContainerOptions{
+ LabelOpts: []string{"filetype:container_file_t:s0"},
+ }
+ if _, err := r.storageService.CreateContainerStorage(ctx, r.imageContext, imgString, image.ID(), volume.config.StorageName, volume.config.StorageID, storageConfig); err != nil {
+ return nil, fmt.Errorf("creating backing storage for image driver: %w", err)
+ }
+ defer func() {
+ if deferredErr != nil {
+ if err := r.storageService.DeleteContainer(volume.config.StorageID); err != nil {
+ logrus.Errorf("Error removing volume %s backing storage: %v", volume.config.Name, err)
+ }
+ }
+ }()
}
// Now we get conditional: we either need to make the volume in the
@@ -196,7 +243,7 @@ func (r *Runtime) UpdateVolumePlugins(ctx context.Context) *define.VolumeReload
}
for _, vol := range vols {
allPluginVolumes[vol.Name] = struct{}{}
- if _, err := r.newVolume(true, WithVolumeName(vol.Name), WithVolumeDriver(driverName)); err != nil {
+ if _, err := r.newVolume(ctx, true, WithVolumeName(vol.Name), WithVolumeDriver(driverName)); err != nil {
// If the volume exists this is not an error, just ignore it and log. It is very likely
// that the volume from the plugin was already in our db.
if !errors.Is(err, define.ErrVolumeExists) {
@@ -375,6 +422,14 @@ func (r *Runtime) removeVolume(ctx context.Context, v *Volume, force bool, timeo
return fmt.Errorf("volume %s could not be removed from plugin %s: %w", v.Name(), v.Driver(), err)
}
}
+ } else if v.config.Driver == define.VolumeDriverImage {
+ if err := v.runtime.storageService.DeleteContainer(v.config.StorageID); err != nil {
+ // Storage container is already gone, no problem.
+ if !(errors.Is(err, storage.ErrNotAContainer) || errors.Is(err, storage.ErrContainerUnknown)) {
+ return fmt.Errorf("removing volume %s storage: %w", v.Name(), err)
+ }
+ logrus.Infof("Storage for volume %s already removed", v.Name())
+ }
}
// Remove the volume from the state