-
Notifications
You must be signed in to change notification settings - Fork 18.7k
/
volumes.go
141 lines (125 loc) · 4.18 KB
/
volumes.go
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
package cluster // import "github.com/docker/docker/daemon/cluster"
import (
"context"
"fmt"
volumetypes "github.com/docker/docker/api/types/volume"
"github.com/docker/docker/daemon/cluster/convert"
"github.com/docker/docker/errdefs"
swarmapi "github.com/moby/swarmkit/v2/api"
"google.golang.org/grpc"
)
// GetVolume returns a volume from the swarm cluster.
func (c *Cluster) GetVolume(nameOrID string) (volumetypes.Volume, error) {
var volume *swarmapi.Volume
if err := c.lockedManagerAction(func(ctx context.Context, state nodeState) error {
v, err := getVolume(ctx, state.controlClient, nameOrID)
if err != nil {
return err
}
volume = v
return nil
}); err != nil {
return volumetypes.Volume{}, err
}
return convert.VolumeFromGRPC(volume), nil
}
// GetVolumes returns all of the volumes matching the given options from a swarm cluster.
func (c *Cluster) GetVolumes(options volumetypes.ListOptions) ([]*volumetypes.Volume, error) {
var volumes []*volumetypes.Volume
if err := c.lockedManagerAction(func(ctx context.Context, state nodeState) error {
r, err := state.controlClient.ListVolumes(
ctx, &swarmapi.ListVolumesRequest{},
grpc.MaxCallRecvMsgSize(defaultRecvSizeForListResponse),
)
if err != nil {
return err
}
volumes = make([]*volumetypes.Volume, 0, len(r.Volumes))
for _, volume := range r.Volumes {
v := convert.VolumeFromGRPC(volume)
volumes = append(volumes, &v)
}
return nil
}); err != nil {
return nil, err
}
return volumes, nil
}
// CreateVolume creates a new cluster volume in the swarm cluster.
//
// Returns the volume ID if creation is successful, or an error if not.
func (c *Cluster) CreateVolume(v volumetypes.CreateOptions) (*volumetypes.Volume, error) {
var resp *swarmapi.CreateVolumeResponse
if err := c.lockedManagerAction(func(ctx context.Context, state nodeState) error {
volumeSpec := convert.VolumeCreateToGRPC(&v)
r, err := state.controlClient.CreateVolume(
ctx, &swarmapi.CreateVolumeRequest{Spec: volumeSpec},
)
if err != nil {
return err
}
resp = r
return nil
}); err != nil {
return nil, err
}
createdVol, err := c.GetVolume(resp.Volume.ID)
if err != nil {
// If there's a failure of some sort in this operation the user would
// get a very unhelpful "not found" error on a create, which is not
// very helpful at all. Instead, before returning the error, add some
// context, and change this to a system-type error, because it's
// nothing the user did wrong.
return nil, errdefs.System(fmt.Errorf("unable to retrieve created volume: %w", err))
}
return &createdVol, nil
}
// RemoveVolume removes a volume from the swarm cluster.
func (c *Cluster) RemoveVolume(nameOrID string, force bool) error {
return c.lockedManagerAction(func(ctx context.Context, state nodeState) error {
volume, err := getVolume(ctx, state.controlClient, nameOrID)
if err != nil {
if force && errdefs.IsNotFound(err) {
return nil
}
return err
}
_, err = state.controlClient.RemoveVolume(ctx, &swarmapi.RemoveVolumeRequest{
VolumeID: volume.ID,
Force: force,
})
return err
})
}
// UpdateVolume updates a volume in the swarm cluster.
func (c *Cluster) UpdateVolume(nameOrID string, version uint64, volume volumetypes.UpdateOptions) error {
return c.lockedManagerAction(func(ctx context.Context, state nodeState) error {
v, err := getVolume(ctx, state.controlClient, nameOrID)
if err != nil {
return err
}
// For now, the only thing we can update is availability. Instead of
// converting the whole spec, just pluck out the availability if it has
// been set.
if volume.Spec != nil {
switch volume.Spec.Availability {
case volumetypes.AvailabilityActive:
v.Spec.Availability = swarmapi.VolumeAvailabilityActive
case volumetypes.AvailabilityPause:
v.Spec.Availability = swarmapi.VolumeAvailabilityPause
case volumetypes.AvailabilityDrain:
v.Spec.Availability = swarmapi.VolumeAvailabilityDrain
default:
// if default empty value, change nothing.
}
}
_, err = state.controlClient.UpdateVolume(ctx, &swarmapi.UpdateVolumeRequest{
VolumeID: nameOrID,
VolumeVersion: &swarmapi.Version{
Index: version,
},
Spec: &v.Spec,
})
return err
})
}