Files
podman/libpod/runtime_volume_linux.go
Matthew Heon d245c6df29 Switch Libpod over to new explicit named volumes
This swaps the previous handling (parse all volume mounts on the
container and look for ones that might refer to named volumes)
for the new, explicit named volume lists stored per-container.

It also deprecates force-removing volumes that are in use. I
don't know how we want to handle this yet, but leaving containers
that depend on a volume that no longer exists is definitely not
correct.

Signed-off-by: Matthew Heon <matthew.heon@pm.me>
2019-04-04 12:26:29 -04:00

126 lines
3.9 KiB
Go

// +build linux
package libpod
import (
"context"
"os"
"path/filepath"
"strings"
"github.com/containers/libpod/libpod/events"
"github.com/containers/storage/pkg/stringid"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
// NewVolume creates a new empty volume
func (r *Runtime) NewVolume(ctx context.Context, options ...VolumeCreateOption) (*Volume, error) {
r.lock.Lock()
defer r.lock.Unlock()
if !r.valid {
return nil, ErrRuntimeStopped
}
return r.newVolume(ctx, options...)
}
// newVolume creates a new empty volume
func (r *Runtime) newVolume(ctx context.Context, options ...VolumeCreateOption) (*Volume, error) {
volume, err := newVolume(r)
if err != nil {
return nil, errors.Wrapf(err, "error creating volume")
}
for _, option := range options {
if err := option(volume); err != nil {
return nil, errors.Wrapf(err, "error running volume create option")
}
}
if volume.config.Name == "" {
volume.config.Name = stringid.GenerateNonCryptoID()
}
// TODO: support for other volume drivers
if volume.config.Driver == "" {
volume.config.Driver = "local"
}
// TODO: determine when the scope is global and set it to that
if volume.config.Scope == "" {
volume.config.Scope = "local"
}
// Create the mountpoint of this volume
volPathRoot := filepath.Join(r.config.VolumePath, volume.config.Name)
if err := os.MkdirAll(volPathRoot, 0700); err != nil {
return nil, errors.Wrapf(err, "error creating volume directory %q", volPathRoot)
}
if err := os.Chown(volPathRoot, volume.config.UID, volume.config.GID); err != nil {
return nil, errors.Wrapf(err, "error chowning volume directory %q to %d:%d", volPathRoot, volume.config.UID, volume.config.GID)
}
fullVolPath := filepath.Join(volPathRoot, "_data")
if err := os.Mkdir(fullVolPath, 0755); err != nil {
return nil, errors.Wrapf(err, "error creating volume directory %q", fullVolPath)
}
if err := os.Chown(fullVolPath, volume.config.UID, volume.config.GID); err != nil {
return nil, errors.Wrapf(err, "error chowning volume directory %q to %d:%d", fullVolPath, volume.config.UID, volume.config.GID)
}
if err := LabelVolumePath(fullVolPath, true); err != nil {
return nil, err
}
volume.config.MountPoint = fullVolPath
volume.valid = true
// Add the volume to state
if err := r.state.AddVolume(volume); err != nil {
return nil, errors.Wrapf(err, "error adding volume to state")
}
defer volume.newVolumeEvent(events.Create)
return volume, nil
}
// removeVolume removes the specified volume from state as well tears down its mountpoint and storage
func (r *Runtime) removeVolume(ctx context.Context, v *Volume, force bool) error {
if !v.valid {
if ok, _ := r.state.HasVolume(v.Name()); !ok {
return nil
}
return ErrVolumeRemoved
}
deps, err := r.state.VolumeInUse(v)
if err != nil {
return err
}
if len(deps) != 0 {
depsStr := strings.Join(deps, ", ")
if !force {
return errors.Wrapf(ErrVolumeBeingUsed, "volume %s is being used by the following container(s): %s", v.Name(), depsStr)
}
// TODO: force-removing a volume makes *no sense*
// I do not believe we should be allowing this at all.
// For now, return angry errors.
// TODO: need to do something more sane in this case
return errors.Wrapf(ErrVolumeBeingUsed, "TODO: FIXME - still refusing to remove because force-removing an in-use volume is not good.")
}
// Set volume as invalid so it can no longer be used
v.valid = false
// Remove the volume from the state
if err := r.state.RemoveVolume(v); err != nil {
return errors.Wrapf(err, "error removing volume %s", v.Name())
}
// Delete the mountpoint path of the volume, that is delete the volume from /var/lib/containers/storage/volumes
if err := v.teardownStorage(); err != nil {
return errors.Wrapf(err, "error cleaning up volume storage for %q", v.Name())
}
defer v.newVolumeEvent(events.Remove)
logrus.Debugf("Removed volume %s", v.Name())
return nil
}