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:
Nick Craig-Wood
2026-03-05 17:57:09 +00:00
parent d43635d1d9
commit c0f0b82fa2
3 changed files with 13 additions and 7 deletions

View File

@@ -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

View File

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

View File

@@ -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")
)