api/types/volume: change ListResponse.Volumes to a non-pointer slice

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
This commit is contained in:
Sebastiaan van Stijn
2025-11-10 13:10:38 +01:00
parent a0e4debdfc
commit 18000fe371
13 changed files with 34 additions and 52 deletions

View File

@@ -2149,6 +2149,7 @@ definitions:
Volume: Volume:
type: "object" type: "object"
required: [Name, Driver, Mountpoint, Labels, Scope, Options] required: [Name, Driver, Mountpoint, Labels, Scope, Options]
x-nullable: false
properties: properties:
Name: Name:
type: "string" type: "string"

View File

@@ -2149,6 +2149,7 @@ definitions:
Volume: Volume:
type: "object" type: "object"
required: [Name, Driver, Mountpoint, Labels, Scope, Options] required: [Name, Driver, Mountpoint, Labels, Scope, Options]
x-nullable: false
properties: properties:
Name: Name:
type: "string" type: "string"

View File

@@ -13,7 +13,7 @@ package volume
type ListResponse struct { type ListResponse struct {
// List of volumes // List of volumes
Volumes []*Volume `json:"Volumes"` Volumes []Volume `json:"Volumes"`
// Warnings that occurred when fetching the list of volumes. // Warnings that occurred when fetching the list of volumes.
// //

View File

@@ -4,7 +4,6 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"net/url" "net/url"
"slices"
"github.com/moby/moby/api/types/volume" "github.com/moby/moby/api/types/volume"
) )
@@ -40,16 +39,8 @@ func (cli *Client) VolumeList(ctx context.Context, options VolumeListOptions) (V
return VolumeListResult{}, err return VolumeListResult{}, err
} }
res := VolumeListResult{ return VolumeListResult{
Items: make([]volume.Volume, 0, len(apiResp.Volumes)), Items: apiResp.Volumes,
Warnings: slices.Clone(apiResp.Warnings), Warnings: apiResp.Warnings,
} }, nil
for _, vol := range apiResp.Volumes {
if vol != nil {
res.Items = append(res.Items, *vol)
}
}
return res, nil
} }

View File

@@ -1,7 +1,6 @@
package client package client
import ( import (
"context"
"fmt" "fmt"
"net/http" "net/http"
"testing" "testing"
@@ -16,7 +15,7 @@ func TestVolumeListError(t *testing.T) {
client, err := New(WithMockClient(errorMock(http.StatusInternalServerError, "Server error"))) client, err := New(WithMockClient(errorMock(http.StatusInternalServerError, "Server error")))
assert.NilError(t, err) assert.NilError(t, err)
_, err = client.VolumeList(context.Background(), VolumeListOptions{}) _, err = client.VolumeList(t.Context(), VolumeListOptions{})
assert.Check(t, is.ErrorType(err, cerrdefs.IsInternal)) assert.Check(t, is.ErrorType(err, cerrdefs.IsInternal))
} }
@@ -52,7 +51,7 @@ func TestVolumeList(t *testing.T) {
return nil, fmt.Errorf("filters not set in URL query properly. Expected '%s', got %s", listCase.expectedFilters, actualFilters) return nil, fmt.Errorf("filters not set in URL query properly. Expected '%s', got %s", listCase.expectedFilters, actualFilters)
} }
return mockJSONResponse(http.StatusOK, nil, volume.ListResponse{ return mockJSONResponse(http.StatusOK, nil, volume.ListResponse{
Volumes: []*volume.Volume{ Volumes: []volume.Volume{
{ {
Name: "volume", Name: "volume",
Driver: "local", Driver: "local",
@@ -62,7 +61,7 @@ func TestVolumeList(t *testing.T) {
})) }))
assert.NilError(t, err) assert.NilError(t, err)
result, err := client.VolumeList(context.Background(), VolumeListOptions{Filters: listCase.filters}) result, err := client.VolumeList(t.Context(), VolumeListOptions{Filters: listCase.filters})
assert.NilError(t, err) assert.NilError(t, err)
assert.Check(t, is.Len(result.Items, 1)) assert.Check(t, is.Len(result.Items, 1))
} }

View File

@@ -30,9 +30,9 @@ func (c *Cluster) GetVolume(nameOrID string) (volumetypes.Volume, error) {
return convert.VolumeFromGRPC(volume), nil return convert.VolumeFromGRPC(volume), nil
} }
// GetVolumes returns all of the volumes matching the given options from a swarm cluster. // GetVolumes returns all volumes matching the given options from a swarm cluster.
func (c *Cluster) GetVolumes(options volumebackend.ListOptions) ([]*volumetypes.Volume, error) { func (c *Cluster) GetVolumes(options volumebackend.ListOptions) ([]volumetypes.Volume, error) {
var volumes []*volumetypes.Volume var volumes []volumetypes.Volume
if err := c.lockedManagerAction(context.TODO(), func(ctx context.Context, state nodeState) error { if err := c.lockedManagerAction(context.TODO(), func(ctx context.Context, state nodeState) error {
r, err := state.controlClient.ListVolumes( r, err := state.controlClient.ListVolumes(
ctx, &swarmapi.ListVolumesRequest{}, ctx, &swarmapi.ListVolumesRequest{},
@@ -42,10 +42,9 @@ func (c *Cluster) GetVolumes(options volumebackend.ListOptions) ([]*volumetypes.
return err return err
} }
volumes = make([]*volumetypes.Volume, 0, len(r.Volumes)) volumes = make([]volumetypes.Volume, 0, len(r.Volumes))
for _, volume := range r.Volumes { for _, volume := range r.Volumes {
v := convert.VolumeFromGRPC(volume) volumes = append(volumes, convert.VolumeFromGRPC(volume))
volumes = append(volumes, &v)
} }
return nil return nil

View File

@@ -127,7 +127,7 @@ func (daemon *Daemon) localVolumesSize(ctx context.Context, verbose bool) (*back
} }
if verbose { if verbose {
du.Items = sliceutil.Deref(volumes) du.Items = volumes
} }
return du, nil return du, nil

View File

@@ -12,7 +12,7 @@ import (
// Backend is the methods that need to be implemented to provide // Backend is the methods that need to be implemented to provide
// volume specific functionality // volume specific functionality
type Backend interface { type Backend interface {
List(ctx context.Context, filter filters.Args) ([]*volume.Volume, []string, error) List(ctx context.Context, filter filters.Args) ([]volume.Volume, []string, error)
Get(ctx context.Context, name string, opts ...opts.GetOption) (*volume.Volume, error) Get(ctx context.Context, name string, opts ...opts.GetOption) (*volume.Volume, error)
Create(ctx context.Context, name, driverName string, opts ...opts.CreateOption) (*volume.Volume, error) Create(ctx context.Context, name, driverName string, opts ...opts.CreateOption) (*volume.Volume, error)
Remove(ctx context.Context, name string, opts ...opts.RemoveOption) error Remove(ctx context.Context, name string, opts ...opts.RemoveOption) error
@@ -25,7 +25,7 @@ type Backend interface {
// backends here. // backends here.
type ClusterBackend interface { type ClusterBackend interface {
GetVolume(nameOrID string) (volume.Volume, error) GetVolume(nameOrID string) (volume.Volume, error)
GetVolumes(options volumebackend.ListOptions) ([]*volume.Volume, error) GetVolumes(options volumebackend.ListOptions) ([]volume.Volume, error)
CreateVolume(volume volume.CreateRequest) (*volume.Volume, error) CreateVolume(volume volume.CreateRequest) (*volume.Volume, error)
RemoveVolume(nameOrID string, force bool) error RemoveVolume(nameOrID string, force bool) error
UpdateVolume(nameOrID string, version uint64, volume volumebackend.UpdateOptions) error UpdateVolume(nameOrID string, version uint64, volume volumebackend.UpdateOptions) error

View File

@@ -589,10 +589,10 @@ type fakeVolumeBackend struct {
volumes map[string]*volume.Volume volumes map[string]*volume.Volume
} }
func (b *fakeVolumeBackend) List(_ context.Context, _ filters.Args) ([]*volume.Volume, []string, error) { func (b *fakeVolumeBackend) List(_ context.Context, _ filters.Args) ([]volume.Volume, []string, error) {
var volumes []*volume.Volume var volumes []volume.Volume
for _, v := range b.volumes { for _, v := range b.volumes {
volumes = append(volumes, v) volumes = append(volumes, *v)
} }
return volumes, nil, nil return volumes, nil, nil
} }
@@ -680,14 +680,14 @@ func (c *fakeClusterBackend) GetVolume(nameOrID string) (volume.Volume, error) {
return volume.Volume{}, errdefs.NotFound(fmt.Errorf("volume %s not found", nameOrID)) return volume.Volume{}, errdefs.NotFound(fmt.Errorf("volume %s not found", nameOrID))
} }
func (c *fakeClusterBackend) GetVolumes(_ volumebackend.ListOptions) ([]*volume.Volume, error) { func (c *fakeClusterBackend) GetVolumes(_ volumebackend.ListOptions) ([]volume.Volume, error) {
if err := c.checkSwarm(); err != nil { if err := c.checkSwarm(); err != nil {
return nil, err return nil, err
} }
var volumes []*volume.Volume var volumes []volume.Volume
for _, v := range c.volumes { for _, v := range c.volumes {
volumes = append(volumes, v) volumes = append(volumes, *v)
} }
return volumes, nil return volumes, nil
} }

View File

@@ -31,9 +31,9 @@ type pathCacher interface {
CachedPath() string CachedPath() string
} }
func (s *VolumesService) volumesToAPI(ctx context.Context, volumes []volume.Volume, opts ...convertOpt) []*volumetypes.Volume { func (s *VolumesService) volumesToAPI(ctx context.Context, volumes []volume.Volume, opts ...convertOpt) []volumetypes.Volume {
var ( var (
out = make([]*volumetypes.Volume, 0, len(volumes)) out = make([]volumetypes.Volume, 0, len(volumes))
getSize bool getSize bool
cachedPath bool cachedPath bool
) )
@@ -75,7 +75,7 @@ func (s *VolumesService) volumesToAPI(ctx context.Context, volumes []volume.Volu
apiV.UsageData = &volumetypes.UsageData{Size: sz, RefCount: int64(s.vs.CountReferences(v))} apiV.UsageData = &volumetypes.UsageData{Size: sz, RefCount: int64(s.vs.CountReferences(v))}
} }
out = append(out, &apiV) out = append(out, apiV)
} }
return out return out
} }

View File

@@ -195,7 +195,7 @@ var acceptedListFilters = map[string]bool{
// Note that this intentionally skips volumes which have mount options. Typically // Note that this intentionally skips volumes which have mount options. Typically
// volumes with mount options are not really local even if they are using the // volumes with mount options are not really local even if they are using the
// local driver. // local driver.
func (s *VolumesService) LocalVolumesSize(ctx context.Context) ([]*volumetypes.Volume, error) { func (s *VolumesService) LocalVolumesSize(ctx context.Context) ([]volumetypes.Volume, error) {
ls, _, err := s.vs.Find(ctx, And(ByDriver(volume.DefaultDriverName), CustomFilter(func(v volume.Volume) bool { ls, _, err := s.vs.Find(ctx, And(ByDriver(volume.DefaultDriverName), CustomFilter(func(v volume.Volume) bool {
dv, ok := v.(volume.DetailedVolume) dv, ok := v.(volume.DetailedVolume)
return ok && len(dv.Options()) == 0 return ok && len(dv.Options()) == 0
@@ -262,7 +262,7 @@ func (s *VolumesService) Prune(ctx context.Context, filter filters.Args) (*volum
// List gets the list of volumes which match the past in filters // List gets the list of volumes which match the past in filters
// If filters is nil or empty all volumes are returned. // If filters is nil or empty all volumes are returned.
func (s *VolumesService) List(ctx context.Context, filter filters.Args) (volumes []*volumetypes.Volume, warnings []string, _ error) { func (s *VolumesService) List(ctx context.Context, filter filters.Args) (volumes []volumetypes.Volume, warnings []string, _ error) {
by, err := filtersToBy(filter, acceptedListFilters) by, err := filtersToBy(filter, acceptedListFilters)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err

View File

@@ -13,7 +13,7 @@ package volume
type ListResponse struct { type ListResponse struct {
// List of volumes // List of volumes
Volumes []*Volume `json:"Volumes"` Volumes []Volume `json:"Volumes"`
// Warnings that occurred when fetching the list of volumes. // Warnings that occurred when fetching the list of volumes.
// //

View File

@@ -4,7 +4,6 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"net/url" "net/url"
"slices"
"github.com/moby/moby/api/types/volume" "github.com/moby/moby/api/types/volume"
) )
@@ -40,16 +39,8 @@ func (cli *Client) VolumeList(ctx context.Context, options VolumeListOptions) (V
return VolumeListResult{}, err return VolumeListResult{}, err
} }
res := VolumeListResult{ return VolumeListResult{
Items: make([]volume.Volume, 0, len(apiResp.Volumes)), Items: apiResp.Volumes,
Warnings: slices.Clone(apiResp.Warnings), Warnings: apiResp.Warnings,
} }, nil
for _, vol := range apiResp.Volumes {
if vol != nil {
res.Items = append(res.Items, *vol)
}
}
return res, nil
} }