summaryrefslogtreecommitdiff
path: root/libpod
diff options
context:
space:
mode:
Diffstat (limited to 'libpod')
-rw-r--r--libpod/container.go13
-rw-r--r--libpod/container_config.go2
-rw-r--r--libpod/container_inspect.go15
-rw-r--r--libpod/container_internal.go19
-rw-r--r--libpod/container_internal_linux.go30
-rw-r--r--libpod/container_validate.go10
-rw-r--r--libpod/options.go19
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 {