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