mirror of
https://github.com/containers/podman.git
synced 2025-08-01 07:40:22 +08:00

This implements support for mounting and unmounting volumes backed by volume plugins. Support for actually retrieving plugins requires a pull request to land in containers.conf and then that to be vendored, and as such is not yet ready. Given this, this code is only compile tested. However, the code for everything past retrieving the plugin has been written - there is support for creating, removing, mounting, and unmounting volumes, which should allow full functionality once the c/common PR is merged. A major change is the signature of the MountPoint function for volumes, which now, by necessity, returns an error. Named volumes managed by a plugin do not have a mountpoint we control; instead, it is managed entirely by the plugin. As such, we need to cache the path in the DB, and calls to retrieve it now need to access the DB (and may fail as such). Notably absent is support for SELinux relabelling and chowning these volumes. Given that we don't manage the mountpoint for these volumes, I am extremely reluctant to try and modify it - we could easily break the plugin trying to chown or relabel it. Also, we had no less than *5* separate implementations of inspecting a volume floating around in pkg/infra/abi and pkg/api/handlers/libpod. And none of them used volume.Inspect(), the only correct way of inspecting volumes. Remove them all and consolidate to using the correct way. Compat API is likely still doing things the wrong way, but that is an issue for another day. Fixes #4304 Signed-off-by: Matthew Heon <matthew.heon@pm.me>
236 lines
6.7 KiB
Go
236 lines
6.7 KiB
Go
package libpod
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"time"
|
|
|
|
"github.com/containers/podman/v2/libpod/define"
|
|
"github.com/containers/podman/v2/libpod/lock"
|
|
"github.com/containers/podman/v2/libpod/plugin"
|
|
)
|
|
|
|
// Volume is a libpod named volume.
|
|
// Named volumes may be shared by multiple containers, and may be created using
|
|
// more complex options than normal bind mounts. They may be backed by a mounted
|
|
// filesystem on the host.
|
|
type Volume struct {
|
|
config *VolumeConfig
|
|
state *VolumeState
|
|
|
|
valid bool
|
|
plugin *plugin.VolumePlugin
|
|
runtime *Runtime
|
|
lock lock.Locker
|
|
}
|
|
|
|
// VolumeConfig holds the volume's immutable configuration.
|
|
type VolumeConfig struct {
|
|
// Name of the volume.
|
|
Name string `json:"name"`
|
|
// ID of the volume's lock.
|
|
LockID uint32 `json:"lockID"`
|
|
// Labels for the volume.
|
|
Labels map[string]string `json:"labels"`
|
|
// The volume driver. Empty string or local does not activate a volume
|
|
// driver, all other values will.
|
|
Driver string `json:"volumeDriver"`
|
|
// The location the volume is mounted at.
|
|
MountPoint string `json:"mountPoint"`
|
|
// Time the volume was created.
|
|
CreatedTime time.Time `json:"createdAt,omitempty"`
|
|
// Options to pass to the volume driver. For the local driver, this is
|
|
// a list of mount options. For other drivers, they are passed to the
|
|
// volume driver handling the volume.
|
|
Options map[string]string `json:"volumeOptions,omitempty"`
|
|
// Whether this volume is anonymous (will be removed on container exit)
|
|
IsAnon bool `json:"isAnon"`
|
|
// UID the volume will be created as.
|
|
UID int `json:"uid"`
|
|
// GID the volume will be created as.
|
|
GID int `json:"gid"`
|
|
}
|
|
|
|
// VolumeState holds the volume's mutable state.
|
|
// Volumes are not guaranteed to have a state. Only volumes using the Local
|
|
// driver that have mount options set will create a state.
|
|
type VolumeState struct {
|
|
// Mountpoint is the location where the volume was mounted.
|
|
// This is only used for volumes using a volume plugin, which will mount
|
|
// at non-standard locations.
|
|
MountPoint string `json:"mountPoint,omitempty"`
|
|
// MountCount is the number of times this volume has been requested to
|
|
// be mounted.
|
|
// It is incremented on mount() and decremented on unmount().
|
|
// On incrementing from 0, the volume will be mounted on the host.
|
|
// On decrementing to 0, the volume will be unmounted on the host.
|
|
MountCount uint `json:"mountCount"`
|
|
// NeedsCopyUp indicates that the next time the volume is mounted into
|
|
// a container, the container will "copy up" the contents of the
|
|
// mountpoint into the volume.
|
|
// This should only be done once. As such, this is set at container
|
|
// create time, then cleared after the copy up is done and never set
|
|
// again.
|
|
NeedsCopyUp bool `json:"notYetMounted,omitempty"`
|
|
// NeedsChown indicates that the next time the volume is mounted into
|
|
// a container, the container will chown the volume to the container process
|
|
// UID/GID.
|
|
NeedsChown bool `json:"notYetChowned,omitempty"`
|
|
// UIDChowned is the UID the volume was chowned to.
|
|
UIDChowned int `json:"uidChowned,omitempty"`
|
|
// GIDChowned is the GID the volume was chowned to.
|
|
GIDChowned int `json:"gidChowned,omitempty"`
|
|
}
|
|
|
|
// Name retrieves the volume's name
|
|
func (v *Volume) Name() string {
|
|
return v.config.Name
|
|
}
|
|
|
|
// Returns the size on disk of volume
|
|
func (v *Volume) Size() (uint64, error) {
|
|
var size uint64
|
|
err := filepath.Walk(v.config.MountPoint, func(path string, info os.FileInfo, err error) error {
|
|
if err == nil && !info.IsDir() {
|
|
size += (uint64)(info.Size())
|
|
}
|
|
return err
|
|
})
|
|
return size, err
|
|
}
|
|
|
|
// Driver retrieves the volume's driver.
|
|
func (v *Volume) Driver() string {
|
|
return v.config.Driver
|
|
}
|
|
|
|
// Scope retrieves the volume's scope.
|
|
// Libpod does not implement volume scoping, and this is provided solely for
|
|
// Docker compatibility. It returns only "local".
|
|
func (v *Volume) Scope() string {
|
|
return "local"
|
|
}
|
|
|
|
// Labels returns the volume's labels
|
|
func (v *Volume) Labels() map[string]string {
|
|
labels := make(map[string]string)
|
|
for key, value := range v.config.Labels {
|
|
labels[key] = value
|
|
}
|
|
return labels
|
|
}
|
|
|
|
// MountPoint returns the volume's mountpoint on the host
|
|
func (v *Volume) MountPoint() (string, error) {
|
|
// For the sake of performance, avoid locking unless we have to.
|
|
if v.UsesVolumeDriver() {
|
|
v.lock.Lock()
|
|
defer v.lock.Unlock()
|
|
|
|
if err := v.update(); err != nil {
|
|
return "", err
|
|
}
|
|
|
|
return v.state.MountPoint, nil
|
|
}
|
|
|
|
return v.config.MountPoint, nil
|
|
}
|
|
|
|
// Options return the volume's options
|
|
func (v *Volume) Options() map[string]string {
|
|
options := make(map[string]string)
|
|
for k, v := range v.config.Options {
|
|
options[k] = v
|
|
}
|
|
return options
|
|
}
|
|
|
|
// Anonymous returns whether this volume is anonymous. Anonymous volumes were
|
|
// created with a container, and will be removed when that container is removed.
|
|
func (v *Volume) Anonymous() bool {
|
|
return v.config.IsAnon
|
|
}
|
|
|
|
// UID returns the UID the volume will be created as.
|
|
func (v *Volume) UID() (int, error) {
|
|
v.lock.Lock()
|
|
defer v.lock.Unlock()
|
|
|
|
if err := v.update(); err != nil {
|
|
return -1, err
|
|
}
|
|
|
|
return v.uid(), nil
|
|
}
|
|
|
|
// Internal, unlocked accessor for UID.
|
|
func (v *Volume) uid() int {
|
|
if v.state.UIDChowned > 0 {
|
|
return v.state.UIDChowned
|
|
}
|
|
return v.config.UID
|
|
}
|
|
|
|
// GID returns the GID the volume will be created as.
|
|
func (v *Volume) GID() (int, error) {
|
|
v.lock.Lock()
|
|
defer v.lock.Unlock()
|
|
|
|
if err := v.update(); err != nil {
|
|
return -1, err
|
|
}
|
|
|
|
return v.gid(), nil
|
|
}
|
|
|
|
// Internal, unlocked accessor for GID.
|
|
func (v *Volume) gid() int {
|
|
if v.state.GIDChowned > 0 {
|
|
return v.state.GIDChowned
|
|
}
|
|
return v.config.GID
|
|
}
|
|
|
|
// CreatedTime returns the time the volume was created at. It was not tracked
|
|
// for some time, so older volumes may not contain one.
|
|
func (v *Volume) CreatedTime() time.Time {
|
|
return v.config.CreatedTime
|
|
}
|
|
|
|
// Config returns the volume's configuration.
|
|
func (v *Volume) Config() (*VolumeConfig, error) {
|
|
config := VolumeConfig{}
|
|
err := JSONDeepCopy(v.config, &config)
|
|
return &config, err
|
|
}
|
|
|
|
// VolumeInUse goes through the container dependencies of a volume
|
|
// and checks if the volume is being used by any container.
|
|
func (v *Volume) VolumeInUse() ([]string, error) {
|
|
v.lock.Lock()
|
|
defer v.lock.Unlock()
|
|
|
|
if !v.valid {
|
|
return nil, define.ErrVolumeRemoved
|
|
}
|
|
return v.runtime.state.VolumeInUse(v)
|
|
}
|
|
|
|
// IsDangling returns whether this volume is dangling (unused by any
|
|
// containers).
|
|
func (v *Volume) IsDangling() (bool, error) {
|
|
ctrs, err := v.VolumeInUse()
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
return len(ctrs) == 0, nil
|
|
}
|
|
|
|
// UsesVolumeDriver determines whether the volume uses a volume driver. Volume
|
|
// drivers are pluggable backends for volumes that will manage the storage and
|
|
// mounting.
|
|
func (v *Volume) UsesVolumeDriver() bool {
|
|
return !(v.config.Driver == define.VolumeDriverLocal || v.config.Driver == "")
|
|
}
|