aboutsummaryrefslogtreecommitdiff
path: root/libpod/runtime_volume_linux.go
blob: ac6fd02c3a76f2a689075073f7e0a88c0fb356db (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
133
134
135
136
137
138
139
140
141
142
// +build linux

package libpod

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

	"github.com/containers/libpod/libpod/define"
	"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, define.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 define.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(define.ErrVolumeBeingUsed, "volume %s is being used by the following container(s): %s", v.Name(), depsStr)
		}

		// We need to remove all containers using the volume
		for _, dep := range deps {
			ctr, err := r.state.Container(dep)
			if err != nil {
				// If the container's removed, no point in
				// erroring.
				if errors.Cause(err) == define.ErrNoSuchCtr || errors.Cause(err) == define.ErrCtrRemoved {
					continue
				}

				return errors.Wrapf(err, "error removing container %s that depends on volume %s", dep, v.Name())
			}

			// TODO: do we want to set force here when removing
			// containers?
			// I'm inclined to say no, in case someone accidentally
			// wipes a container they're using...
			if err := r.removeContainer(ctx, ctr, false, false, false); err != nil {
				return errors.Wrapf(err, "error removing container %s that depends on volume %s", ctr.ID(), v.Name())
			}
		}
	}

	// 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
}