mirror of
https://github.com/rclone/rclone.git
synced 2026-05-24 08:54:33 -04:00
docker serve: make Create idempotent to avoid "volume already exists" after restart
Docker may re-send Create requests for volumes that already exist, especially after a plugin restart. Previously this returned ErrVolumeExists which Docker surfaced as "volume name must be unique". Now if a volume with the same name already exists, Create returns success (no-op), matching the Docker volume plugin protocol's expectation of idempotent operations.
This commit is contained in:
@@ -94,7 +94,8 @@ func TestDockerPluginLogic(t *testing.T) {
|
||||
assert.NoError(t, drv.Create(volReq))
|
||||
path1 := filepath.Join(testDir, "vol1")
|
||||
|
||||
assert.ErrorIs(t, drv.Create(volReq), docker.ErrVolumeExists)
|
||||
// Create is idempotent - creating the same volume again should succeed
|
||||
assert.NoError(t, drv.Create(volReq))
|
||||
|
||||
getReq := &docker.GetRequest{Name: "vol1"}
|
||||
getRes, err := drv.Get(getReq)
|
||||
@@ -373,8 +374,9 @@ func testMountAPI(t *testing.T, sockAddr string) {
|
||||
}
|
||||
cli.request("Create", createReq, &res, false)
|
||||
assert.Equal(t, "{}", res)
|
||||
cli.request("Create", createReq, &res, true)
|
||||
assert.Contains(t, res, "volume already exists")
|
||||
// Create is idempotent - creating the same volume again should succeed
|
||||
cli.request("Create", createReq, &res, false)
|
||||
assert.Equal(t, "{}", res)
|
||||
|
||||
mountReq := docker.MountRequest{Name: "vol1", ID: "id1"}
|
||||
var mountRes docker.MountResponse
|
||||
|
||||
@@ -182,8 +182,12 @@ func reportErr(err error) {
|
||||
}
|
||||
}
|
||||
|
||||
// Create volume
|
||||
// To use subpath we are limited to defining a new volume definition via alias
|
||||
// Create volume.
|
||||
//
|
||||
// If the volume already exists, the request is treated as a no-op
|
||||
// (idempotent) so that Docker can safely re-send Create requests
|
||||
// after a plugin restart without getting "volume already exists".
|
||||
// To use subpath we are limited to defining a new volume definition via alias.
|
||||
func (drv *Driver) Create(req *CreateRequest) error {
|
||||
ctx := context.Background()
|
||||
drv.mu.Lock()
|
||||
@@ -193,7 +197,8 @@ func (drv *Driver) Create(req *CreateRequest) error {
|
||||
fs.Debugf(nil, "Create volume %q", name)
|
||||
|
||||
if vol, _ := drv.getVolume(name); vol != nil {
|
||||
return ErrVolumeExists
|
||||
fs.Debugf(nil, "Volume %q already exists, treating Create as no-op", name)
|
||||
return nil
|
||||
}
|
||||
|
||||
vol, err := newVolume(ctx, name, req.Options, drv)
|
||||
|
||||
@@ -20,7 +20,6 @@ import (
|
||||
// Errors
|
||||
var (
|
||||
ErrVolumeNotFound = errors.New("volume not found")
|
||||
ErrVolumeExists = errors.New("volume already exists")
|
||||
ErrMountpointExists = errors.New("non-empty mountpoint already exists")
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user