From faee204c02a14343254b368d37676292a5fb0840 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Tue, 28 Oct 2025 15:28:23 +0100 Subject: [PATCH] client: VolumeRemove: add output struct Signed-off-by: Sebastiaan van Stijn --- client/client_interfaces.go | 2 +- client/volume_remove.go | 16 ++++++++++++---- client/volume_remove_test.go | 11 +++++------ integration/daemon/daemon_test.go | 6 +++--- integration/plugin/authz/authz_plugin_v2_test.go | 2 +- integration/volume/mount_test.go | 2 +- integration/volume/volume_test.go | 16 ++++++++-------- internal/testutil/environment/clean.go | 2 +- .../moby/moby/client/client_interfaces.go | 2 +- .../github.com/moby/moby/client/volume_remove.go | 16 ++++++++++++---- 10 files changed, 45 insertions(+), 30 deletions(-) diff --git a/client/client_interfaces.go b/client/client_interfaces.go index 9c69782e55..94ebd52c53 100644 --- a/client/client_interfaces.go +++ b/client/client_interfaces.go @@ -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 } diff --git a/client/volume_remove.go b/client/volume_remove.go index 1df9a0ca89..0449e08d4a 100644 --- a/client/volume_remove.go +++ b/client/volume_remove.go @@ -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 } diff --git a/client/volume_remove_test.go b/client/volume_remove_test.go index 433e2911e3..6366add2a6 100644 --- a/client/volume_remove_test.go +++ b/client/volume_remove_test.go @@ -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) } diff --git a/integration/daemon/daemon_test.go b/integration/daemon/daemon_test.go index 4d2980e1d1..d1358e8e5f 100644 --- a/integration/daemon/daemon_test.go +++ b/integration/daemon/daemon_test.go @@ -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) }) diff --git a/integration/plugin/authz/authz_plugin_v2_test.go b/integration/plugin/authz/authz_plugin_v2_test.go index c3b48a1b36..acdd1db075 100644 --- a/integration/plugin/authz/authz_plugin_v2_test.go +++ b/integration/plugin/authz/authz_plugin_v2_test.go @@ -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)) diff --git a/integration/volume/mount_test.go b/integration/volume/mount_test.go index b3bdf1617e..c8be83784b 100644 --- a/integration/volume/mount_test.go +++ b/integration/volume/mount_test.go @@ -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{ diff --git a/integration/volume/volume_test.go b/integration/volume/volume_test.go index b54621bad8..519a34c34c 100644 --- a/integration/volume/volume_test.go +++ b/integration/volume/volume_test.go @@ -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) }) } diff --git a/internal/testutil/environment/clean.go b/internal/testutil/environment/clean.go index fcdd8bd9ff..483ab79103 100644 --- a/internal/testutil/environment/clean.go +++ b/internal/testutil/environment/clean.go @@ -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) diff --git a/vendor/github.com/moby/moby/client/client_interfaces.go b/vendor/github.com/moby/moby/client/client_interfaces.go index 9c69782e55..94ebd52c53 100644 --- a/vendor/github.com/moby/moby/client/client_interfaces.go +++ b/vendor/github.com/moby/moby/client/client_interfaces.go @@ -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 } diff --git a/vendor/github.com/moby/moby/client/volume_remove.go b/vendor/github.com/moby/moby/client/volume_remove.go index 1df9a0ca89..0449e08d4a 100644 --- a/vendor/github.com/moby/moby/client/volume_remove.go +++ b/vendor/github.com/moby/moby/client/volume_remove.go @@ -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 }