diff --git a/api/volume.go b/api/volume.go index 8bde8cbe..dd9427a8 100644 --- a/api/volume.go +++ b/api/volume.go @@ -24,6 +24,23 @@ func NewVolumeService(logger log.Logger, client *hcloud.Client) *VolumeService { } } +func (s *VolumeService) All(ctx context.Context) ([]*csi.Volume, error) { + hcloudVolumes, err := s.client.Volume.All(ctx) + if err != nil { + level.Info(s.logger).Log( + "msg", "failed to get volumes", + "err", err, + ) + return nil, err + } + + volumes := make([]*csi.Volume, 0, len(hcloudVolumes)) + for i, hcloudVolume := range hcloudVolumes { + volumes[i] = toDomainVolume(hcloudVolume) + } + return volumes, nil +} + func (s *VolumeService) Create(ctx context.Context, opts volumes.CreateOpts) (*csi.Volume, error) { level.Info(s.logger).Log( "msg", "creating volume", diff --git a/driver/controller.go b/driver/controller.go index 8b929dc3..c84cacfd 100644 --- a/driver/controller.go +++ b/driver/controller.go @@ -273,8 +273,31 @@ func (s *ControllerService) ValidateVolumeCapabilities(ctx context.Context, req return resp, nil } -func (s *ControllerService) ListVolumes(context.Context, *proto.ListVolumesRequest) (*proto.ListVolumesResponse, error) { - return nil, status.Error(codes.Unimplemented, "listing volumes is not supported") +func (s *ControllerService) ListVolumes(ctx context.Context, req *proto.ListVolumesRequest) (*proto.ListVolumesResponse, error) { + vols, err := s.volumeService.All(ctx) + + if err != nil { + return nil, status.Error(codes.Internal, err.Error()) + } + + resp := &proto.ListVolumesResponse{Entries: make([]*proto.ListVolumesResponse_Entry, 0, len(vols))} + for i, volume := range vols { + resp.Entries[i] = &proto.ListVolumesResponse_Entry{ + Volume: &proto.Volume{ + VolumeId: strconv.FormatUint(volume.ID, 10), + CapacityBytes: volume.SizeBytes(), + AccessibleTopology: []*proto.Topology{ + { + Segments: map[string]string{ + TopologySegmentLocation: volume.Location, + }, + }, + }, + }, + } + } + + return resp, nil } func (s *ControllerService) GetCapacity(context.Context, *proto.GetCapacityRequest) (*proto.GetCapacityResponse, error) { @@ -305,6 +328,13 @@ func (s *ControllerService) ControllerGetCapabilities(context.Context, *proto.Co }, }, }, + { + Type: &proto.ControllerServiceCapability_Rpc{ + Rpc: &proto.ControllerServiceCapability_RPC{ + Type: proto.ControllerServiceCapability_RPC_LIST_VOLUMES, + }, + }, + }, }, } return resp, nil diff --git a/driver/sanity_test.go b/driver/sanity_test.go index d3b5f3d7..acd3e540 100644 --- a/driver/sanity_test.go +++ b/driver/sanity_test.go @@ -86,6 +86,19 @@ type sanityVolumeService struct { volumes list.List } +func (s *sanityVolumeService) All(ctx context.Context) ([]*csi.Volume, error) { + s.mu.Lock() + defer s.mu.Unlock() + + vols := []*csi.Volume{} + for e := s.volumes.Front(); e != nil; e = e.Next() { + v := e.Value.(*csi.Volume) + vols = append(vols, v) + + } + return vols, nil +} + func (s *sanityVolumeService) Create(ctx context.Context, opts volumes.CreateOpts) (*csi.Volume, error) { s.mu.Lock() defer s.mu.Unlock() diff --git a/mock/volume.go b/mock/volume.go index 4d917415..c41f5daa 100644 --- a/mock/volume.go +++ b/mock/volume.go @@ -12,6 +12,7 @@ import ( type VolumeService struct { CreateFunc func(ctx context.Context, opts volumes.CreateOpts) (*csi.Volume, error) GetServerByIDFunc func(ctx context.Context, id int) (*hcloud.Server, error) + AllFunc func(ctx context.Context) ([]*csi.Volume, error) GetByIDFunc func(ctx context.Context, id uint64) (*csi.Volume, error) GetByNameFunc func(ctx context.Context, name string) (*csi.Volume, error) DeleteFunc func(ctx context.Context, volume *csi.Volume) error @@ -20,6 +21,13 @@ type VolumeService struct { ResizeFunc func(ctx context.Context, volume *csi.Volume, size int) error } +func (s *VolumeService) All(ctx context.Context) ([]*csi.Volume, error) { + if s.AllFunc == nil { + panic("not implemented") + } + return s.AllFunc(ctx) +} + func (s *VolumeService) Create(ctx context.Context, opts volumes.CreateOpts) (*csi.Volume, error) { if s.CreateFunc == nil { panic("not implemented") diff --git a/volumes/idempotency.go b/volumes/idempotency.go index 55fead49..d9f989cd 100644 --- a/volumes/idempotency.go +++ b/volumes/idempotency.go @@ -96,6 +96,10 @@ func (s *IdempotentService) Create(ctx context.Context, opts CreateOpts) (*csi.V return nil, err } +func (s *IdempotentService) All(ctx context.Context) ([]*csi.Volume, error) { + return s.volumeService.All(ctx) +} + func (s *IdempotentService) GetByID(ctx context.Context, id uint64) (*csi.Volume, error) { return s.volumeService.GetByID(ctx, id) } diff --git a/volumes/service.go b/volumes/service.go index 05f6e560..978a26b5 100644 --- a/volumes/service.go +++ b/volumes/service.go @@ -25,6 +25,7 @@ type Service interface { Attach(ctx context.Context, volume *csi.Volume, server *csi.Server) error Detach(ctx context.Context, volume *csi.Volume, server *csi.Server) error Resize(ctx context.Context, volume *csi.Volume, size int) error + All(ctx context.Context) ([]*csi.Volume, error) } // CreateOpts specifies the options for creating a volume.