summaryrefslogtreecommitdiff
path: root/libpod/runtime_volume_linux.go
blob: 5cc0938f03fcbd746cc1e9e66a30262849f3be8e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
// +build linux

package libpod

import (
	"context"
	"os"
	"path/filepath"
	"strings"

	"github.com/containers/storage"
	"github.com/containers/storage/pkg/stringid"
	"github.com/opencontainers/selinux/go-selinux/label"
	"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
	fullVolPath := filepath.Join(r.config.VolumePath, volume.config.Name, "_data")
	if err := os.MkdirAll(fullVolPath, 0755); err != nil {
		return nil, errors.Wrapf(err, "error creating volume directory %q", fullVolPath)
	}
	_, mountLabel, err := label.InitLabels([]string{})
	if err != nil {
		return nil, errors.Wrapf(err, "error getting default mountlabels")
	}
	if err := label.ReleaseLabel(mountLabel); err != nil {
		return nil, errors.Wrapf(err, "error releasing label %q", mountLabel)
	}
	if err := label.Relabel(fullVolPath, mountLabel, true); err != nil {
		return nil, errors.Wrapf(err, "error setting selinux label to %q", fullVolPath)
	}
	volume.config.MountPoint = fullVolPath

	// Path our lock file will reside at
	lockPath := filepath.Join(r.lockDir, volume.config.Name)
	// Grab a lockfile at the given path
	lock, err := storage.GetLockfile(lockPath)
	if err != nil {
		return nil, errors.Wrapf(err, "error creating lockfile for new volume")
	}
	volume.lock = lock

	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")
	}

	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, prune bool) error {
	if !v.valid {
		return ErrNoSuchVolume
	}

	deps, err := r.state.VolumeInUse(v)
	if err != nil {
		return err
	}
	if len(deps) != 0 {
		if prune {
			return ErrVolumeBeingUsed
		}
		depsStr := strings.Join(deps, ", ")
		if !force {
			return errors.Wrapf(ErrVolumeBeingUsed, "volume %s is being used by the following container(s): %s", v.Name(), depsStr)
		}
		// If using force, log the warning that the volume is being used by at least one container
		logrus.Warnf("volume %s is being used by the following container(s): %s", v.Name(), depsStr)
		// Remove the container dependencies so we can go ahead and delete the volume
		for _, dep := range deps {
			if err := r.state.RemoveVolCtrDep(v, dep); err != nil {
				return errors.Wrapf(err, "unable to remove container dependency %q from volume %q while trying to delete volume by force", dep, 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())
	}

	// Remove the volume from the state
	if err := r.state.RemoveVolume(v); err != nil {
		return errors.Wrapf(err, "error removing volume %s", v.Name())
	}

	// Set volume as invalid so it can no longer be used
	v.valid = false

	return nil
}