client: VolumeRemove: add output struct

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
This commit is contained in:
Sebastiaan van Stijn
2025-10-28 15:28:23 +01:00
parent 90109a373d
commit faee204c02
10 changed files with 45 additions and 30 deletions

View File

@@ -191,7 +191,7 @@ type VolumeAPIClient interface {
VolumeCreate(ctx context.Context, options VolumeCreateOptions) (VolumeCreateResult, error)
VolumeInspect(ctx context.Context, volumeID string, options VolumeInspectOptions) (VolumeInspectResult, error)
VolumeList(ctx context.Context, options VolumeListOptions) (VolumeListResult, error)
VolumeRemove(ctx context.Context, volumeID string, options VolumeRemoveOptions) error
VolumeRemove(ctx context.Context, volumeID string, options VolumeRemoveOptions) (VolumeRemoveResult, error)
VolumesPrune(ctx context.Context, opts VolumePruneOptions) (VolumePruneResult, error)
VolumeUpdate(ctx context.Context, volumeID string, version swarm.Version, options VolumeUpdateOptions) error
}

View File

@@ -5,17 +5,22 @@ import (
"net/url"
)
// VolumeRemoveOptions holds optional parameters for volume removal.
// VolumeRemoveOptions holds options for [Client.VolumeRemove].
type VolumeRemoveOptions struct {
// Force the removal of the volume
Force bool
}
// VolumeRemoveResult holds the result of [Client.VolumeRemove],
type VolumeRemoveResult struct {
// Add future fields here.
}
// VolumeRemove removes a volume from the docker host.
func (cli *Client) VolumeRemove(ctx context.Context, volumeID string, options VolumeRemoveOptions) error {
func (cli *Client) VolumeRemove(ctx context.Context, volumeID string, options VolumeRemoveOptions) (VolumeRemoveResult, error) {
volumeID, err := trimID("volume", volumeID)
if err != nil {
return err
return VolumeRemoveResult{}, err
}
query := url.Values{}
@@ -24,5 +29,8 @@ func (cli *Client) VolumeRemove(ctx context.Context, volumeID string, options Vo
}
resp, err := cli.delete(ctx, "/volumes/"+volumeID, query, nil)
defer ensureReaderClosed(resp)
return err
if err != nil {
return VolumeRemoveResult{}, err
}
return VolumeRemoveResult{}, nil
}

View File

@@ -1,7 +1,6 @@
package client
import (
"context"
"fmt"
"net/http"
"testing"
@@ -15,14 +14,14 @@ func TestVolumeRemoveError(t *testing.T) {
client, err := NewClientWithOpts(WithMockClient(errorMock(http.StatusInternalServerError, "Server error")))
assert.NilError(t, err)
err = client.VolumeRemove(context.Background(), "volume_id", VolumeRemoveOptions{})
_, err = client.VolumeRemove(t.Context(), "volume_id", VolumeRemoveOptions{})
assert.Check(t, is.ErrorType(err, cerrdefs.IsInternal))
err = client.VolumeRemove(context.Background(), "", VolumeRemoveOptions{})
_, err = client.VolumeRemove(t.Context(), "", VolumeRemoveOptions{})
assert.Check(t, is.ErrorType(err, cerrdefs.IsInvalidArgument))
assert.Check(t, is.ErrorContains(err, "value is empty"))
err = client.VolumeRemove(context.Background(), " ", VolumeRemoveOptions{})
_, err = client.VolumeRemove(t.Context(), " ", VolumeRemoveOptions{})
assert.Check(t, is.ErrorType(err, cerrdefs.IsInvalidArgument))
assert.Check(t, is.ErrorContains(err, "value is empty"))
}
@@ -35,7 +34,7 @@ func TestVolumeRemoveConnectionError(t *testing.T) {
client, err := NewClientWithOpts(WithAPIVersionNegotiation(), WithHost("tcp://no-such-host.invalid"))
assert.NilError(t, err)
err = client.VolumeRemove(context.Background(), "volume_id", VolumeRemoveOptions{})
_, err = client.VolumeRemove(t.Context(), "volume_id", VolumeRemoveOptions{})
assert.Check(t, is.ErrorType(err, IsErrConnectionFailed))
}
@@ -54,6 +53,6 @@ func TestVolumeRemove(t *testing.T) {
}))
assert.NilError(t, err)
err = client.VolumeRemove(context.Background(), "volume_id", VolumeRemoveOptions{Force: true})
_, err = client.VolumeRemove(t.Context(), "volume_id", VolumeRemoveOptions{Force: true})
assert.NilError(t, err)
}

View File

@@ -566,7 +566,7 @@ func testLiveRestoreVolumeReferences(t *testing.T) {
d.Restart(t, "--live-restore", "--iptables=false", "--ip6tables=false")
// Try to remove the volume
err = c.VolumeRemove(ctx, volName, client.VolumeRemoveOptions{})
_, err = c.VolumeRemove(ctx, volName, client.VolumeRemoveOptions{})
assert.ErrorContains(t, err, "volume is in use")
_, err = c.VolumeInspect(ctx, volName, client.VolumeInspectOptions{})
@@ -626,7 +626,7 @@ func testLiveRestoreVolumeReferences(t *testing.T) {
// Try to remove the volume
// This should fail since its used by a container
err = c.VolumeRemove(ctx, v.Name, client.VolumeRemoveOptions{})
_, err = c.VolumeRemove(ctx, v.Name, client.VolumeRemoveOptions{})
assert.ErrorContains(t, err, "volume is in use")
t.Run("volume still mounted", func(t *testing.T) {
@@ -660,7 +660,7 @@ func testLiveRestoreVolumeReferences(t *testing.T) {
assert.NilError(t, err)
// Now we should be able to remove the volume
err = c.VolumeRemove(ctx, v.Name, client.VolumeRemoveOptions{})
_, err = c.VolumeRemove(ctx, v.Name, client.VolumeRemoveOptions{})
assert.NilError(t, err)
})

View File

@@ -101,7 +101,7 @@ func TestAuthZPluginV2RejectVolumeRequests(t *testing.T) {
assert.ErrorContains(t, err, fmt.Sprintf("Error response from daemon: plugin %s failed with error:", authzPluginNameWithTag))
// The plugin will block the command before it can determine the volume does not exist
err = c.VolumeRemove(ctx, "test", client.VolumeRemoveOptions{})
_, err = c.VolumeRemove(ctx, "test", client.VolumeRemoveOptions{})
assert.Assert(t, err != nil)
assert.ErrorContains(t, err, fmt.Sprintf("Error response from daemon: plugin %s failed with error:", authzPluginNameWithTag))

View File

@@ -249,7 +249,7 @@ func setupTestVolume(t *testing.T, apiClient client.APIClient) string {
volumeName := t.Name() + "-volume"
err := apiClient.VolumeRemove(ctx, volumeName, client.VolumeRemoveOptions{Force: true})
_, err := apiClient.VolumeRemove(ctx, volumeName, client.VolumeRemoveOptions{Force: true})
assert.NilError(t, err, "failed to clean volume")
_, err = apiClient.VolumeCreate(ctx, client.VolumeCreateOptions{

View File

@@ -76,7 +76,7 @@ func TestVolumesRemove(t *testing.T) {
vname := inspect.Container.Mounts[0].Name
t.Run("volume in use", func(t *testing.T) {
err = apiClient.VolumeRemove(ctx, vname, client.VolumeRemoveOptions{})
_, err = apiClient.VolumeRemove(ctx, vname, client.VolumeRemoveOptions{})
assert.Check(t, is.ErrorType(err, cerrdefs.IsConflict))
assert.Check(t, is.ErrorContains(err, "volume is in use"))
})
@@ -87,17 +87,17 @@ func TestVolumesRemove(t *testing.T) {
})
assert.NilError(t, err)
err = apiClient.VolumeRemove(ctx, vname, client.VolumeRemoveOptions{})
_, err = apiClient.VolumeRemove(ctx, vname, client.VolumeRemoveOptions{})
assert.NilError(t, err)
})
t.Run("non-existing volume", func(t *testing.T) {
err = apiClient.VolumeRemove(ctx, "no_such_volume", client.VolumeRemoveOptions{})
_, err = apiClient.VolumeRemove(ctx, "no_such_volume", client.VolumeRemoveOptions{})
assert.Check(t, is.ErrorType(err, cerrdefs.IsNotFound))
})
t.Run("non-existing volume force", func(t *testing.T) {
err = apiClient.VolumeRemove(ctx, "no_such_volume", client.VolumeRemoveOptions{Force: true})
_, err = apiClient.VolumeRemove(ctx, "no_such_volume", client.VolumeRemoveOptions{Force: true})
assert.NilError(t, err)
})
}
@@ -128,7 +128,7 @@ func TestVolumesRemoveSwarmEnabled(t *testing.T) {
vname := inspect.Container.Mounts[0].Name
t.Run("volume in use", func(t *testing.T) {
err = apiClient.VolumeRemove(ctx, vname, client.VolumeRemoveOptions{})
_, err = apiClient.VolumeRemove(ctx, vname, client.VolumeRemoveOptions{})
assert.Check(t, is.ErrorType(err, cerrdefs.IsConflict))
assert.Check(t, is.ErrorContains(err, "volume is in use"))
})
@@ -139,17 +139,17 @@ func TestVolumesRemoveSwarmEnabled(t *testing.T) {
})
assert.NilError(t, err)
err = apiClient.VolumeRemove(ctx, vname, client.VolumeRemoveOptions{})
_, err = apiClient.VolumeRemove(ctx, vname, client.VolumeRemoveOptions{})
assert.NilError(t, err)
})
t.Run("non-existing volume", func(t *testing.T) {
err = apiClient.VolumeRemove(ctx, "no_such_volume", client.VolumeRemoveOptions{})
_, err = apiClient.VolumeRemove(ctx, "no_such_volume", client.VolumeRemoveOptions{})
assert.Check(t, is.ErrorType(err, cerrdefs.IsNotFound))
})
t.Run("non-existing volume force", func(t *testing.T) {
err = apiClient.VolumeRemove(ctx, "no_such_volume", client.VolumeRemoveOptions{Force: true})
_, err = apiClient.VolumeRemove(ctx, "no_such_volume", client.VolumeRemoveOptions{Force: true})
assert.NilError(t, err)
})
}

View File

@@ -134,7 +134,7 @@ func deleteAllVolumes(ctx context.Context, t testing.TB, c client.VolumeAPIClien
if _, ok := protectedVolumes[v.Name]; ok {
continue
}
err := c.VolumeRemove(ctx, v.Name, client.VolumeRemoveOptions{
_, err := c.VolumeRemove(ctx, v.Name, client.VolumeRemoveOptions{
Force: true,
})
assert.Check(t, err, "failed to remove volume %s", v.Name)

View File

@@ -191,7 +191,7 @@ type VolumeAPIClient interface {
VolumeCreate(ctx context.Context, options VolumeCreateOptions) (VolumeCreateResult, error)
VolumeInspect(ctx context.Context, volumeID string, options VolumeInspectOptions) (VolumeInspectResult, error)
VolumeList(ctx context.Context, options VolumeListOptions) (VolumeListResult, error)
VolumeRemove(ctx context.Context, volumeID string, options VolumeRemoveOptions) error
VolumeRemove(ctx context.Context, volumeID string, options VolumeRemoveOptions) (VolumeRemoveResult, error)
VolumesPrune(ctx context.Context, opts VolumePruneOptions) (VolumePruneResult, error)
VolumeUpdate(ctx context.Context, volumeID string, version swarm.Version, options VolumeUpdateOptions) error
}

View File

@@ -5,17 +5,22 @@ import (
"net/url"
)
// VolumeRemoveOptions holds optional parameters for volume removal.
// VolumeRemoveOptions holds options for [Client.VolumeRemove].
type VolumeRemoveOptions struct {
// Force the removal of the volume
Force bool
}
// VolumeRemoveResult holds the result of [Client.VolumeRemove],
type VolumeRemoveResult struct {
// Add future fields here.
}
// VolumeRemove removes a volume from the docker host.
func (cli *Client) VolumeRemove(ctx context.Context, volumeID string, options VolumeRemoveOptions) error {
func (cli *Client) VolumeRemove(ctx context.Context, volumeID string, options VolumeRemoveOptions) (VolumeRemoveResult, error) {
volumeID, err := trimID("volume", volumeID)
if err != nil {
return err
return VolumeRemoveResult{}, err
}
query := url.Values{}
@@ -24,5 +29,8 @@ func (cli *Client) VolumeRemove(ctx context.Context, volumeID string, options Vo
}
resp, err := cli.delete(ctx, "/volumes/"+volumeID, query, nil)
defer ensureReaderClosed(resp)
return err
if err != nil {
return VolumeRemoveResult{}, err
}
return VolumeRemoveResult{}, nil
}