123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760 |
- 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)
- assert.Equal(t, len(c.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, o ...opts.RemoveOption) error {
- removeOpts := &opts.RemoveConfig{}
- for _, opt := range o {
- opt(removeOpts)
- }
- if v, ok := b.volumes[name]; !ok {
- if !removeOpts.PurgeOnError {
- 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
- }
|