From afb159c476719cb11198455616977abd751dad23 Mon Sep 17 00:00:00 2001 From: Pierre-Emmanuel Jacquier <15922119+pierre-emmanuelJ@users.noreply.github.com> Date: Fri, 19 Jan 2024 13:58:00 +0000 Subject: [PATCH] CSI: Implement Expand Volume Signed-off-by: Pierre-Emmanuel Jacquier <15922119+pierre-emmanuelJ@users.noreply.github.com> --- driver/controller.go | 50 ++++++++++++++++++++++++++++++++++++++++++- driver/helpers.go | 51 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 100 insertions(+), 1 deletion(-) diff --git a/driver/controller.go b/driver/controller.go index 54432eac..96f09a8a 100644 --- a/driver/controller.go +++ b/driver/controller.go @@ -73,7 +73,9 @@ var ( ) const ( + // TODO: The API should return it. MinimalVolumeSizeBytes = 100 * 1024 * 1024 * 1024 + MaximumVolumeSizeBytes = 1000 * 1024 * 1024 * 1024 ) type controllerService struct { @@ -605,8 +607,54 @@ func (d *controllerService) ListSnapshots(ctx context.Context, req *csi.ListSnap // ControllerExpandVolume resizes/updates the volume (not supported yet on Exoscale Public API) func (d *controllerService) ControllerExpandVolume(ctx context.Context, req *csi.ControllerExpandVolumeRequest) (*csi.ControllerExpandVolumeResponse, error) { klog.V(4).Infof("ControllerExpandVolume") + zone, volumeID, err := getExoscaleID(req.GetVolumeId()) + if err != nil { + return nil, err + } + client := d.client.WithURL(zone) + + volume, err := client.GetBlockStorageVolume(ctx, volumeID) + if err != nil { + if errors.Is(err, v3.ErrNotFound) { + return nil, status.Errorf(codes.NotFound, "volume %s not found", volumeID) + } + + return nil, err + } + + newSize, err := getNewVolumeSize(req.GetCapacityRange()) + if err != nil { + return nil, status.Errorf(codes.OutOfRange, "invalid capacity range: %v", err) + } + + if newSize < volume.Size { + return nil, status.Error(codes.InvalidArgument, "new size must be bigger than actual volume size") + } + + _, err = client.ResizeBlockStorageVolume(ctx, volumeID, v3.ResizeBlockStorageVolumeRequest{ + Size: newSize, + }) + if err != nil { + return nil, err + } - return nil, status.Error(codes.Unimplemented, "") + nodeExpansionRequired := true + volumeCapability := req.GetVolumeCapability() + if volumeCapability != nil { + err := validateVolumeCapability(volumeCapability) + if err != nil { + return nil, status.Errorf(codes.InvalidArgument, "volumeCapabilities not supported: %s", err) + } + + if _, ok := volumeCapability.GetAccessType().(*csi.VolumeCapability_Block); ok { + nodeExpansionRequired = false + } + } + + return &csi.ControllerExpandVolumeResponse{ + CapacityBytes: newSize, + NodeExpansionRequired: nodeExpansionRequired, + }, nil } // ControllerGetVolume gets a volume and return it. diff --git a/driver/helpers.go b/driver/helpers.go index c29739f0..bccec8cc 100644 --- a/driver/helpers.go +++ b/driver/helpers.go @@ -1,6 +1,7 @@ package driver import ( + "errors" "fmt" "os" "path/filepath" @@ -94,3 +95,53 @@ func createMountPoint(path string, file bool) error { func convertBytesToGibiBytes(nBytes int64) int64 { return nBytes / (1024 * 1024 * 1024) } + +func getNewVolumeSize(capacityRange *csi.CapacityRange) (int64, error) { + if capacityRange == nil { + return MinimalVolumeSizeBytes, nil + } + + requiredBytes := capacityRange.GetRequiredBytes() + requiredSet := requiredBytes > 0 + + limitBytes := capacityRange.GetLimitBytes() + limitSet := limitBytes > 0 + + if !requiredSet && !limitSet { + return MinimalVolumeSizeBytes, nil + } + + if requiredSet && limitSet && limitBytes < requiredBytes { + return 0, errors.New("limit size is less than required size") + } + + if requiredSet && !limitSet && requiredBytes < MinimalVolumeSizeBytes { + return 0, errors.New("required size is less than the minimun size") + } + + if limitSet && limitBytes < MinimalVolumeSizeBytes { + return 0, errors.New("limit size is less than the minimun size") + } + + if requiredSet && requiredBytes > MaximumVolumeSizeBytes { + return 0, errors.New("required size is greater than the maximum size") + } + + if !requiredSet && limitSet && limitBytes > MaximumVolumeSizeBytes { + return 0, errors.New("limit size is greater than the maximum size") + } + + if requiredSet && limitSet && requiredBytes == limitBytes { + return requiredBytes, nil + } + + if requiredSet { + return requiredBytes, nil + } + + if limitSet { + return limitBytes, nil + } + + return MinimalVolumeSizeBytes, nil +}