2021-05-14 13:38:50 -04:00
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 {
2022-07-08 09:00:10 -04:00
"volume1" : {
2021-05-14 13:38:50 -04:00
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 {
2022-07-08 09:00:10 -04:00
"volume1" : {
2021-05-14 13:38:50 -04:00
Name : "volume1" ,
} ,
} ,
} ,
}
_ , err := callGetVolume ( v , "volume1" )
assert . NilError ( t , err )
}
func TestListVolumes ( t * testing . T ) {
v := & volumeRouter {
backend : & fakeVolumeBackend {
volumes : map [ string ] * volume . Volume {
2022-07-08 09:00:10 -04:00
"v1" : { Name : "v1" } ,
"v2" : { Name : "v2" } ,
2021-05-14 13:38:50 -04:00
} ,
} ,
cluster : & fakeClusterBackend {
swarm : true ,
manager : true ,
volumes : map [ string ] * volume . Volume {
2022-07-08 09:00:10 -04:00
"v3" : { Name : "v3" } ,
"v4" : { Name : "v4" } ,
2021-05-14 13:38:50 -04:00
} ,
} ,
}
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 {
2022-07-08 09:00:10 -04:00
"v1" : { Name : "v1" } ,
"v2" : { Name : "v2" } ,
2021-05-14 13:38:50 -04:00
} ,
} ,
cluster : & fakeClusterBackend { } ,
}
_ , err := callListVolumes ( v )
assert . NilError ( t , err )
}
func TestListVolumesNoManager ( t * testing . T ) {
v := & volumeRouter {
backend : & fakeVolumeBackend {
volumes : map [ string ] * volume . Volume {
2022-07-08 09:00:10 -04:00
"v1" : { Name : "v1" } ,
"v2" : { Name : "v2" } ,
2021-05-14 13:38:50 -04:00
} ,
} ,
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 {
2022-07-08 09:00:10 -04:00
"vol1" : {
2021-05-14 13:38:50 -04:00
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 {
2022-07-08 09:00:10 -04:00
"vol1" : {
2021-05-14 13:38:50 -04:00
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 {
2022-07-08 09:00:10 -04:00
"vol1" : {
2021-05-14 13:38:50 -04:00
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 {
2022-07-08 09:00:10 -04:00
"vol1" : {
2021-05-14 13:38:50 -04:00
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 {
2022-07-08 09:00:10 -04:00
"inuse" : {
2021-05-14 13:38:50 -04:00
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 {
2022-07-08 09:00:10 -04:00
"vol1" : {
2021-05-14 13:38:50 -04:00
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
}