mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
6668801d40
result of: gofmt -s -w $(find . -type f -name '*.go' | grep -v "/vendor/") Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
752 lines
19 KiB
Go
752 lines
19 KiB
Go
package volume
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/http/httptest"
|
|
"testing"
|
|
|
|
"gotest.tools/v3/assert"
|
|
|
|
"github.com/docker/docker/api/server/httputils"
|
|
"github.com/docker/docker/api/types"
|
|
"github.com/docker/docker/api/types/filters"
|
|
"github.com/docker/docker/api/types/volume"
|
|
"github.com/docker/docker/errdefs"
|
|
"github.com/docker/docker/volume/service/opts"
|
|
)
|
|
|
|
func callGetVolume(v *volumeRouter, name string) (*httptest.ResponseRecorder, error) {
|
|
ctx := context.WithValue(context.Background(), httputils.APIVersionKey{}, clusterVolumesVersion)
|
|
vars := map[string]string{"name": name}
|
|
req := httptest.NewRequest("GET", fmt.Sprintf("/volumes/%s", name), nil)
|
|
resp := httptest.NewRecorder()
|
|
|
|
err := v.getVolumeByName(ctx, resp, req, vars)
|
|
return resp, err
|
|
}
|
|
|
|
func callListVolumes(v *volumeRouter) (*httptest.ResponseRecorder, error) {
|
|
ctx := context.WithValue(context.Background(), httputils.APIVersionKey{}, clusterVolumesVersion)
|
|
vars := map[string]string{}
|
|
req := httptest.NewRequest("GET", "/volumes", nil)
|
|
resp := httptest.NewRecorder()
|
|
|
|
err := v.getVolumesList(ctx, resp, req, vars)
|
|
return resp, err
|
|
}
|
|
|
|
func TestGetVolumeByNameNotFoundNoSwarm(t *testing.T) {
|
|
v := &volumeRouter{
|
|
backend: &fakeVolumeBackend{},
|
|
cluster: &fakeClusterBackend{},
|
|
}
|
|
|
|
_, err := callGetVolume(v, "notReal")
|
|
|
|
assert.Assert(t, err != nil)
|
|
assert.Assert(t, errdefs.IsNotFound(err))
|
|
}
|
|
|
|
func TestGetVolumeByNameNotFoundNotManager(t *testing.T) {
|
|
v := &volumeRouter{
|
|
backend: &fakeVolumeBackend{},
|
|
cluster: &fakeClusterBackend{swarm: true},
|
|
}
|
|
|
|
_, err := callGetVolume(v, "notReal")
|
|
|
|
assert.Assert(t, err != nil)
|
|
assert.Assert(t, errdefs.IsNotFound(err))
|
|
}
|
|
|
|
func TestGetVolumeByNameNotFound(t *testing.T) {
|
|
v := &volumeRouter{
|
|
backend: &fakeVolumeBackend{},
|
|
cluster: &fakeClusterBackend{swarm: true, manager: true},
|
|
}
|
|
|
|
_, err := callGetVolume(v, "notReal")
|
|
|
|
assert.Assert(t, err != nil)
|
|
assert.Assert(t, errdefs.IsNotFound(err))
|
|
}
|
|
|
|
func TestGetVolumeByNameFoundRegular(t *testing.T) {
|
|
v := &volumeRouter{
|
|
backend: &fakeVolumeBackend{
|
|
volumes: map[string]*volume.Volume{
|
|
|
|
"volume1": {
|
|
Name: "volume1",
|
|
},
|
|
},
|
|
},
|
|
cluster: &fakeClusterBackend{swarm: true, manager: true},
|
|
}
|
|
|
|
_, err := callGetVolume(v, "volume1")
|
|
assert.NilError(t, err)
|
|
}
|
|
|
|
func TestGetVolumeByNameFoundSwarm(t *testing.T) {
|
|
v := &volumeRouter{
|
|
backend: &fakeVolumeBackend{},
|
|
cluster: &fakeClusterBackend{
|
|
swarm: true,
|
|
manager: true,
|
|
volumes: map[string]*volume.Volume{
|
|
"volume1": {
|
|
Name: "volume1",
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
_, err := callGetVolume(v, "volume1")
|
|
assert.NilError(t, err)
|
|
}
|
|
func TestListVolumes(t *testing.T) {
|
|
v := &volumeRouter{
|
|
backend: &fakeVolumeBackend{
|
|
volumes: map[string]*volume.Volume{
|
|
"v1": {Name: "v1"},
|
|
"v2": {Name: "v2"},
|
|
},
|
|
},
|
|
cluster: &fakeClusterBackend{
|
|
swarm: true,
|
|
manager: true,
|
|
volumes: map[string]*volume.Volume{
|
|
"v3": {Name: "v3"},
|
|
"v4": {Name: "v4"},
|
|
},
|
|
},
|
|
}
|
|
|
|
resp, err := callListVolumes(v)
|
|
assert.NilError(t, err)
|
|
d := json.NewDecoder(resp.Result().Body)
|
|
respVols := volume.ListResponse{}
|
|
assert.NilError(t, d.Decode(&respVols))
|
|
|
|
assert.Assert(t, respVols.Volumes != nil)
|
|
assert.Equal(t, len(respVols.Volumes), 4, "volumes %v", respVols.Volumes)
|
|
}
|
|
|
|
func TestListVolumesNoSwarm(t *testing.T) {
|
|
v := &volumeRouter{
|
|
backend: &fakeVolumeBackend{
|
|
volumes: map[string]*volume.Volume{
|
|
"v1": {Name: "v1"},
|
|
"v2": {Name: "v2"},
|
|
},
|
|
},
|
|
cluster: &fakeClusterBackend{},
|
|
}
|
|
|
|
_, err := callListVolumes(v)
|
|
assert.NilError(t, err)
|
|
}
|
|
|
|
func TestListVolumesNoManager(t *testing.T) {
|
|
v := &volumeRouter{
|
|
backend: &fakeVolumeBackend{
|
|
volumes: map[string]*volume.Volume{
|
|
"v1": {Name: "v1"},
|
|
"v2": {Name: "v2"},
|
|
},
|
|
},
|
|
cluster: &fakeClusterBackend{swarm: true},
|
|
}
|
|
|
|
resp, err := callListVolumes(v)
|
|
assert.NilError(t, err)
|
|
|
|
d := json.NewDecoder(resp.Result().Body)
|
|
respVols := volume.ListResponse{}
|
|
assert.NilError(t, d.Decode(&respVols))
|
|
|
|
assert.Equal(t, len(respVols.Volumes), 2)
|
|
assert.Equal(t, len(respVols.Warnings), 0)
|
|
}
|
|
|
|
func TestCreateRegularVolume(t *testing.T) {
|
|
b := &fakeVolumeBackend{}
|
|
c := &fakeClusterBackend{
|
|
swarm: true,
|
|
manager: true,
|
|
}
|
|
v := &volumeRouter{
|
|
backend: b,
|
|
cluster: c,
|
|
}
|
|
|
|
volumeCreate := volume.CreateOptions{
|
|
Name: "vol1",
|
|
Driver: "foodriver",
|
|
}
|
|
|
|
buf := bytes.Buffer{}
|
|
e := json.NewEncoder(&buf)
|
|
e.Encode(volumeCreate)
|
|
|
|
ctx := context.WithValue(context.Background(), httputils.APIVersionKey{}, clusterVolumesVersion)
|
|
req := httptest.NewRequest("POST", "/volumes/create", &buf)
|
|
req.Header.Add("Content-Type", "application/json")
|
|
|
|
resp := httptest.NewRecorder()
|
|
err := v.postVolumesCreate(ctx, resp, req, nil)
|
|
|
|
assert.NilError(t, err)
|
|
|
|
respVolume := volume.Volume{}
|
|
|
|
assert.NilError(t, json.NewDecoder(resp.Result().Body).Decode(&respVolume))
|
|
|
|
assert.Equal(t, respVolume.Name, "vol1")
|
|
assert.Equal(t, respVolume.Driver, "foodriver")
|
|
|
|
assert.Equal(t, 1, len(b.volumes))
|
|
assert.Equal(t, 0, len(c.volumes))
|
|
}
|
|
|
|
func TestCreateSwarmVolumeNoSwarm(t *testing.T) {
|
|
b := &fakeVolumeBackend{}
|
|
c := &fakeClusterBackend{}
|
|
|
|
v := &volumeRouter{
|
|
backend: b,
|
|
cluster: c,
|
|
}
|
|
|
|
volumeCreate := volume.CreateOptions{
|
|
ClusterVolumeSpec: &volume.ClusterVolumeSpec{},
|
|
Name: "volCluster",
|
|
Driver: "someCSI",
|
|
}
|
|
|
|
buf := bytes.Buffer{}
|
|
json.NewEncoder(&buf).Encode(volumeCreate)
|
|
|
|
ctx := context.WithValue(context.Background(), httputils.APIVersionKey{}, clusterVolumesVersion)
|
|
req := httptest.NewRequest("POST", "/volumes/create", &buf)
|
|
req.Header.Add("Content-Type", "application/json")
|
|
|
|
resp := httptest.NewRecorder()
|
|
err := v.postVolumesCreate(ctx, resp, req, nil)
|
|
|
|
assert.Assert(t, err != nil)
|
|
assert.Assert(t, errdefs.IsUnavailable(err))
|
|
}
|
|
|
|
func TestCreateSwarmVolumeNotManager(t *testing.T) {
|
|
b := &fakeVolumeBackend{}
|
|
c := &fakeClusterBackend{swarm: true}
|
|
|
|
v := &volumeRouter{
|
|
backend: b,
|
|
cluster: c,
|
|
}
|
|
|
|
volumeCreate := volume.CreateOptions{
|
|
ClusterVolumeSpec: &volume.ClusterVolumeSpec{},
|
|
Name: "volCluster",
|
|
Driver: "someCSI",
|
|
}
|
|
|
|
buf := bytes.Buffer{}
|
|
json.NewEncoder(&buf).Encode(volumeCreate)
|
|
|
|
ctx := context.WithValue(context.Background(), httputils.APIVersionKey{}, clusterVolumesVersion)
|
|
req := httptest.NewRequest("POST", "/volumes/create", &buf)
|
|
req.Header.Add("Content-Type", "application/json")
|
|
|
|
resp := httptest.NewRecorder()
|
|
err := v.postVolumesCreate(ctx, resp, req, nil)
|
|
|
|
assert.Assert(t, err != nil)
|
|
assert.Assert(t, errdefs.IsUnavailable(err))
|
|
}
|
|
|
|
func TestCreateVolumeCluster(t *testing.T) {
|
|
b := &fakeVolumeBackend{}
|
|
c := &fakeClusterBackend{
|
|
swarm: true,
|
|
manager: true,
|
|
}
|
|
|
|
v := &volumeRouter{
|
|
backend: b,
|
|
cluster: c,
|
|
}
|
|
|
|
volumeCreate := volume.CreateOptions{
|
|
ClusterVolumeSpec: &volume.ClusterVolumeSpec{},
|
|
Name: "volCluster",
|
|
Driver: "someCSI",
|
|
}
|
|
|
|
buf := bytes.Buffer{}
|
|
json.NewEncoder(&buf).Encode(volumeCreate)
|
|
|
|
ctx := context.WithValue(context.Background(), httputils.APIVersionKey{}, clusterVolumesVersion)
|
|
req := httptest.NewRequest("POST", "/volumes/create", &buf)
|
|
req.Header.Add("Content-Type", "application/json")
|
|
|
|
resp := httptest.NewRecorder()
|
|
err := v.postVolumesCreate(ctx, resp, req, nil)
|
|
|
|
assert.NilError(t, err)
|
|
|
|
respVolume := volume.Volume{}
|
|
|
|
assert.NilError(t, json.NewDecoder(resp.Result().Body).Decode(&respVolume))
|
|
|
|
assert.Equal(t, respVolume.Name, "volCluster")
|
|
assert.Equal(t, respVolume.Driver, "someCSI")
|
|
|
|
assert.Equal(t, 0, len(b.volumes))
|
|
assert.Equal(t, 1, len(c.volumes))
|
|
}
|
|
|
|
func TestUpdateVolume(t *testing.T) {
|
|
b := &fakeVolumeBackend{}
|
|
c := &fakeClusterBackend{
|
|
swarm: true,
|
|
manager: true,
|
|
volumes: map[string]*volume.Volume{
|
|
"vol1": {
|
|
Name: "vo1",
|
|
ClusterVolume: &volume.ClusterVolume{
|
|
ID: "vol1",
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
v := &volumeRouter{
|
|
backend: b,
|
|
cluster: c,
|
|
}
|
|
|
|
volumeUpdate := volume.UpdateOptions{
|
|
Spec: &volume.ClusterVolumeSpec{},
|
|
}
|
|
|
|
buf := bytes.Buffer{}
|
|
json.NewEncoder(&buf).Encode(volumeUpdate)
|
|
ctx := context.WithValue(context.Background(), httputils.APIVersionKey{}, clusterVolumesVersion)
|
|
req := httptest.NewRequest("POST", "/volumes/vol1/update?version=0", &buf)
|
|
req.Header.Add("Content-Type", "application/json")
|
|
|
|
resp := httptest.NewRecorder()
|
|
|
|
err := v.putVolumesUpdate(ctx, resp, req, map[string]string{"name": "vol1"})
|
|
assert.NilError(t, err)
|
|
|
|
assert.Equal(t, c.volumes["vol1"].ClusterVolume.Meta.Version.Index, uint64(1))
|
|
}
|
|
|
|
func TestUpdateVolumeNoSwarm(t *testing.T) {
|
|
b := &fakeVolumeBackend{}
|
|
c := &fakeClusterBackend{}
|
|
|
|
v := &volumeRouter{
|
|
backend: b,
|
|
cluster: c,
|
|
}
|
|
|
|
volumeUpdate := volume.UpdateOptions{
|
|
Spec: &volume.ClusterVolumeSpec{},
|
|
}
|
|
|
|
buf := bytes.Buffer{}
|
|
json.NewEncoder(&buf).Encode(volumeUpdate)
|
|
ctx := context.WithValue(context.Background(), httputils.APIVersionKey{}, clusterVolumesVersion)
|
|
req := httptest.NewRequest("POST", "/volumes/vol1/update?version=0", &buf)
|
|
req.Header.Add("Content-Type", "application/json")
|
|
|
|
resp := httptest.NewRecorder()
|
|
|
|
err := v.putVolumesUpdate(ctx, resp, req, map[string]string{"name": "vol1"})
|
|
assert.Assert(t, err != nil)
|
|
assert.Assert(t, errdefs.IsUnavailable(err))
|
|
}
|
|
|
|
func TestUpdateVolumeNotFound(t *testing.T) {
|
|
b := &fakeVolumeBackend{}
|
|
c := &fakeClusterBackend{
|
|
swarm: true,
|
|
manager: true,
|
|
volumes: map[string]*volume.Volume{},
|
|
}
|
|
|
|
v := &volumeRouter{
|
|
backend: b,
|
|
cluster: c,
|
|
}
|
|
|
|
volumeUpdate := volume.UpdateOptions{
|
|
Spec: &volume.ClusterVolumeSpec{},
|
|
}
|
|
|
|
buf := bytes.Buffer{}
|
|
json.NewEncoder(&buf).Encode(volumeUpdate)
|
|
ctx := context.WithValue(context.Background(), httputils.APIVersionKey{}, clusterVolumesVersion)
|
|
req := httptest.NewRequest("POST", "/volumes/vol1/update?version=0", &buf)
|
|
req.Header.Add("Content-Type", "application/json")
|
|
|
|
resp := httptest.NewRecorder()
|
|
|
|
err := v.putVolumesUpdate(ctx, resp, req, map[string]string{"name": "vol1"})
|
|
assert.Assert(t, err != nil)
|
|
assert.Assert(t, errdefs.IsNotFound(err))
|
|
}
|
|
|
|
func TestVolumeRemove(t *testing.T) {
|
|
b := &fakeVolumeBackend{
|
|
volumes: map[string]*volume.Volume{
|
|
"vol1": {
|
|
Name: "vol1",
|
|
},
|
|
},
|
|
}
|
|
c := &fakeClusterBackend{swarm: true, manager: true}
|
|
|
|
v := &volumeRouter{
|
|
backend: b,
|
|
cluster: c,
|
|
}
|
|
|
|
ctx := context.WithValue(context.Background(), httputils.APIVersionKey{}, clusterVolumesVersion)
|
|
req := httptest.NewRequest("DELETE", "/volumes/vol1", nil)
|
|
resp := httptest.NewRecorder()
|
|
|
|
err := v.deleteVolumes(ctx, resp, req, map[string]string{"name": "vol1"})
|
|
assert.NilError(t, err)
|
|
assert.Equal(t, len(b.volumes), 0)
|
|
}
|
|
|
|
func TestVolumeRemoveSwarm(t *testing.T) {
|
|
b := &fakeVolumeBackend{}
|
|
c := &fakeClusterBackend{
|
|
swarm: true,
|
|
manager: true,
|
|
volumes: map[string]*volume.Volume{
|
|
"vol1": {
|
|
Name: "vol1",
|
|
ClusterVolume: &volume.ClusterVolume{},
|
|
},
|
|
},
|
|
}
|
|
|
|
v := &volumeRouter{
|
|
backend: b,
|
|
cluster: c,
|
|
}
|
|
|
|
ctx := context.WithValue(context.Background(), httputils.APIVersionKey{}, clusterVolumesVersion)
|
|
req := httptest.NewRequest("DELETE", "/volumes/vol1", nil)
|
|
resp := httptest.NewRecorder()
|
|
|
|
err := v.deleteVolumes(ctx, resp, req, map[string]string{"name": "vol1"})
|
|
assert.NilError(t, err)
|
|
assert.Equal(t, len(c.volumes), 0)
|
|
}
|
|
|
|
func TestVolumeRemoveNotFoundNoSwarm(t *testing.T) {
|
|
b := &fakeVolumeBackend{}
|
|
c := &fakeClusterBackend{}
|
|
v := &volumeRouter{
|
|
backend: b,
|
|
cluster: c,
|
|
}
|
|
|
|
ctx := context.WithValue(context.Background(), httputils.APIVersionKey{}, clusterVolumesVersion)
|
|
req := httptest.NewRequest("DELETE", "/volumes/vol1", nil)
|
|
resp := httptest.NewRecorder()
|
|
|
|
err := v.deleteVolumes(ctx, resp, req, map[string]string{"name": "vol1"})
|
|
assert.Assert(t, err != nil)
|
|
assert.Assert(t, errdefs.IsNotFound(err), err.Error())
|
|
}
|
|
|
|
func TestVolumeRemoveNotFoundNoManager(t *testing.T) {
|
|
b := &fakeVolumeBackend{}
|
|
c := &fakeClusterBackend{swarm: true}
|
|
v := &volumeRouter{
|
|
backend: b,
|
|
cluster: c,
|
|
}
|
|
|
|
ctx := context.WithValue(context.Background(), httputils.APIVersionKey{}, clusterVolumesVersion)
|
|
req := httptest.NewRequest("DELETE", "/volumes/vol1", nil)
|
|
resp := httptest.NewRecorder()
|
|
|
|
err := v.deleteVolumes(ctx, resp, req, map[string]string{"name": "vol1"})
|
|
assert.Assert(t, err != nil)
|
|
assert.Assert(t, errdefs.IsNotFound(err))
|
|
}
|
|
|
|
func TestVolumeRemoveFoundNoSwarm(t *testing.T) {
|
|
b := &fakeVolumeBackend{
|
|
volumes: map[string]*volume.Volume{
|
|
"vol1": {
|
|
Name: "vol1",
|
|
},
|
|
},
|
|
}
|
|
c := &fakeClusterBackend{}
|
|
|
|
v := &volumeRouter{
|
|
backend: b,
|
|
cluster: c,
|
|
}
|
|
|
|
ctx := context.WithValue(context.Background(), httputils.APIVersionKey{}, clusterVolumesVersion)
|
|
req := httptest.NewRequest("DELETE", "/volumes/vol1", nil)
|
|
resp := httptest.NewRecorder()
|
|
|
|
err := v.deleteVolumes(ctx, resp, req, map[string]string{"name": "vol1"})
|
|
assert.NilError(t, err)
|
|
assert.Equal(t, len(b.volumes), 0)
|
|
}
|
|
|
|
func TestVolumeRemoveNoSwarmInUse(t *testing.T) {
|
|
b := &fakeVolumeBackend{
|
|
volumes: map[string]*volume.Volume{
|
|
"inuse": {
|
|
Name: "inuse",
|
|
},
|
|
},
|
|
}
|
|
c := &fakeClusterBackend{}
|
|
v := &volumeRouter{
|
|
backend: b,
|
|
cluster: c,
|
|
}
|
|
|
|
ctx := context.WithValue(context.Background(), httputils.APIVersionKey{}, clusterVolumesVersion)
|
|
req := httptest.NewRequest("DELETE", "/volumes/inuse", nil)
|
|
resp := httptest.NewRecorder()
|
|
|
|
err := v.deleteVolumes(ctx, resp, req, map[string]string{"name": "inuse"})
|
|
assert.Assert(t, err != nil)
|
|
assert.Assert(t, errdefs.IsConflict(err))
|
|
}
|
|
|
|
func TestVolumeRemoveSwarmForce(t *testing.T) {
|
|
b := &fakeVolumeBackend{}
|
|
c := &fakeClusterBackend{
|
|
swarm: true,
|
|
manager: true,
|
|
volumes: map[string]*volume.Volume{
|
|
"vol1": {
|
|
Name: "vol1",
|
|
ClusterVolume: &volume.ClusterVolume{},
|
|
Options: map[string]string{"mustforce": "yes"},
|
|
},
|
|
},
|
|
}
|
|
|
|
v := &volumeRouter{
|
|
backend: b,
|
|
cluster: c,
|
|
}
|
|
|
|
ctx := context.WithValue(context.Background(), httputils.APIVersionKey{}, clusterVolumesVersion)
|
|
req := httptest.NewRequest("DELETE", "/volumes/vol1", nil)
|
|
resp := httptest.NewRecorder()
|
|
|
|
err := v.deleteVolumes(ctx, resp, req, map[string]string{"name": "vol1"})
|
|
|
|
assert.Assert(t, err != nil)
|
|
assert.Assert(t, errdefs.IsConflict(err))
|
|
|
|
ctx = context.WithValue(context.Background(), httputils.APIVersionKey{}, clusterVolumesVersion)
|
|
req = httptest.NewRequest("DELETE", "/volumes/vol1?force=1", nil)
|
|
resp = httptest.NewRecorder()
|
|
|
|
err = v.deleteVolumes(ctx, resp, req, map[string]string{"name": "vol1"})
|
|
|
|
assert.NilError(t, err)
|
|
assert.Equal(t, len(b.volumes), 0)
|
|
}
|
|
|
|
type fakeVolumeBackend struct {
|
|
volumes map[string]*volume.Volume
|
|
}
|
|
|
|
func (b *fakeVolumeBackend) List(_ context.Context, _ filters.Args) ([]*volume.Volume, []string, error) {
|
|
volumes := []*volume.Volume{}
|
|
for _, v := range b.volumes {
|
|
volumes = append(volumes, v)
|
|
}
|
|
return volumes, nil, nil
|
|
}
|
|
|
|
func (b *fakeVolumeBackend) Get(_ context.Context, name string, _ ...opts.GetOption) (*volume.Volume, error) {
|
|
if v, ok := b.volumes[name]; ok {
|
|
return v, nil
|
|
}
|
|
return nil, errdefs.NotFound(fmt.Errorf("volume %s not found", name))
|
|
}
|
|
|
|
func (b *fakeVolumeBackend) Create(_ context.Context, name, driverName string, _ ...opts.CreateOption) (*volume.Volume, error) {
|
|
if _, ok := b.volumes[name]; ok {
|
|
// TODO(dperny): return appropriate error type
|
|
return nil, fmt.Errorf("already exists")
|
|
}
|
|
|
|
v := &volume.Volume{
|
|
Name: name,
|
|
Driver: driverName,
|
|
}
|
|
if b.volumes == nil {
|
|
b.volumes = map[string]*volume.Volume{
|
|
name: v,
|
|
}
|
|
} else {
|
|
b.volumes[name] = v
|
|
}
|
|
|
|
return v, nil
|
|
}
|
|
|
|
func (b *fakeVolumeBackend) Remove(_ context.Context, name string, _ ...opts.RemoveOption) error {
|
|
if v, ok := b.volumes[name]; !ok {
|
|
return errdefs.NotFound(fmt.Errorf("volume %s not found", name))
|
|
} else if v.Name == "inuse" {
|
|
return errdefs.Conflict(fmt.Errorf("volume in use"))
|
|
}
|
|
|
|
delete(b.volumes, name)
|
|
|
|
return nil
|
|
}
|
|
|
|
func (b *fakeVolumeBackend) Prune(_ context.Context, _ filters.Args) (*types.VolumesPruneReport, error) {
|
|
return nil, nil
|
|
}
|
|
|
|
type fakeClusterBackend struct {
|
|
swarm bool
|
|
manager bool
|
|
idCount int
|
|
volumes map[string]*volume.Volume
|
|
}
|
|
|
|
func (c *fakeClusterBackend) checkSwarm() error {
|
|
if !c.swarm {
|
|
return errdefs.Unavailable(fmt.Errorf("this node is not a swarm manager. Use \"docker swarm init\" or \"docker swarm join\" to connect this node to swarm and try again"))
|
|
} else if !c.manager {
|
|
return errdefs.Unavailable(fmt.Errorf("this node is not a swarm manager. Worker nodes can't be used to view or modify cluster state. Please run this command on a manager node or promote the current node to a manager"))
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (c *fakeClusterBackend) IsManager() bool {
|
|
return c.swarm && c.manager
|
|
}
|
|
|
|
func (c *fakeClusterBackend) GetVolume(nameOrID string) (volume.Volume, error) {
|
|
if err := c.checkSwarm(); err != nil {
|
|
return volume.Volume{}, err
|
|
}
|
|
|
|
if v, ok := c.volumes[nameOrID]; ok {
|
|
return *v, nil
|
|
}
|
|
return volume.Volume{}, errdefs.NotFound(fmt.Errorf("volume %s not found", nameOrID))
|
|
}
|
|
|
|
func (c *fakeClusterBackend) GetVolumes(options volume.ListOptions) ([]*volume.Volume, error) {
|
|
if err := c.checkSwarm(); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
volumes := []*volume.Volume{}
|
|
|
|
for _, v := range c.volumes {
|
|
volumes = append(volumes, v)
|
|
}
|
|
return volumes, nil
|
|
}
|
|
|
|
func (c *fakeClusterBackend) CreateVolume(volumeCreate volume.CreateOptions) (*volume.Volume, error) {
|
|
if err := c.checkSwarm(); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if _, ok := c.volumes[volumeCreate.Name]; ok {
|
|
// TODO(dperny): return appropriate already exists error
|
|
return nil, fmt.Errorf("already exists")
|
|
}
|
|
|
|
v := &volume.Volume{
|
|
Name: volumeCreate.Name,
|
|
Driver: volumeCreate.Driver,
|
|
Labels: volumeCreate.Labels,
|
|
Options: volumeCreate.DriverOpts,
|
|
Scope: "global",
|
|
}
|
|
|
|
v.ClusterVolume = &volume.ClusterVolume{
|
|
ID: fmt.Sprintf("cluster_%d", c.idCount),
|
|
Spec: *volumeCreate.ClusterVolumeSpec,
|
|
}
|
|
|
|
c.idCount = c.idCount + 1
|
|
if c.volumes == nil {
|
|
c.volumes = map[string]*volume.Volume{
|
|
v.Name: v,
|
|
}
|
|
} else {
|
|
c.volumes[v.Name] = v
|
|
}
|
|
|
|
return v, nil
|
|
}
|
|
|
|
func (c *fakeClusterBackend) RemoveVolume(nameOrID string, force bool) error {
|
|
if err := c.checkSwarm(); err != nil {
|
|
return err
|
|
}
|
|
|
|
v, ok := c.volumes[nameOrID]
|
|
if !ok {
|
|
return errdefs.NotFound(fmt.Errorf("volume %s not found", nameOrID))
|
|
}
|
|
|
|
if _, mustforce := v.Options["mustforce"]; mustforce && !force {
|
|
return errdefs.Conflict(fmt.Errorf("volume %s must be force removed", nameOrID))
|
|
}
|
|
|
|
delete(c.volumes, nameOrID)
|
|
|
|
return nil
|
|
}
|
|
|
|
func (c *fakeClusterBackend) UpdateVolume(nameOrID string, version uint64, _ volume.UpdateOptions) error {
|
|
if err := c.checkSwarm(); err != nil {
|
|
return err
|
|
}
|
|
|
|
if v, ok := c.volumes[nameOrID]; ok {
|
|
if v.ClusterVolume.Meta.Version.Index != version {
|
|
return fmt.Errorf("wrong version")
|
|
}
|
|
v.ClusterVolume.Meta.Version.Index = v.ClusterVolume.Meta.Version.Index + 1
|
|
// for testing, we don't actually need to change anything about the
|
|
// volume object. let's just increment the version so we can see the
|
|
// call happened.
|
|
} else {
|
|
return errdefs.NotFound(fmt.Errorf("volume %q not found", nameOrID))
|
|
}
|
|
|
|
return nil
|
|
}
|