Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ ARG BUILD_DATE
RUN CGO_ENABLED=0 GOOS=${TARGETOS} GOARCH=${TARGETARCH} go build -a -ldflags "-w -s -X github.com/scaleway/scaleway-csi/driver.driverVersion=${TAG} -X github.com/scaleway/scaleway-csi/driver.buildDate=${BUILD_DATE} -X github.com/scaleway/scaleway-csi/driver.gitCommit=${COMMIT_SHA} " -o scaleway-csi ./cmd/scaleway-csi

FROM alpine:3.11
RUN apk update && apk add --no-cache e2fsprogs xfsprogs ca-certificates && update-ca-certificates
RUN apk update && apk add --no-cache e2fsprogs e2fsprogs-extra xfsprogs xfsprogs-extra ca-certificates && update-ca-certificates
WORKDIR /
COPY --from=builder /go/src/github.com/scaleway/scaleway-csi/scaleway-csi .
ENTRYPOINT ["/scaleway-csi"]
157 changes: 106 additions & 51 deletions driver/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ var (
csi.ControllerServiceCapability_RPC_LIST_VOLUMES,
csi.ControllerServiceCapability_RPC_CREATE_DELETE_SNAPSHOT,
csi.ControllerServiceCapability_RPC_LIST_SNAPSHOTS,
csi.ControllerServiceCapability_RPC_EXPAND_VOLUME,
}

// supportedAccessModes represents the supported access modes for the Scaleway Block Volumes
Expand Down Expand Up @@ -74,21 +75,26 @@ func (d *controllerService) CreateVolume(ctx context.Context, req *csi.CreateVol
return nil, status.Errorf(codes.InvalidArgument, "volumeCapabilities not supported: %s", err)
}

size, err := getVolumeRequestCapacity(req.GetCapacityRange())
if err != nil {
return nil, status.Errorf(codes.OutOfRange, "capacityRange invalid: %s", err)
}

volumeType := scaleway.DefaultVolumeType
for key, value := range req.GetParameters() {
switch strings.ToLower(key) {
case volumeTypeKey:
volumeType = instance.VolumeType(value)
volumeType = instance.VolumeVolumeType(value)
default:
return nil, status.Errorf(codes.InvalidArgument, "invalid parameter key %s", key)
}
}

minSize, maxSize, err := d.scaleway.GetVolumeLimits(string(volumeType))
if err != nil {
return nil, status.Error(codes.Internal, err.Error())
}

size, err := getVolumeRequestCapacity(minSize, maxSize, req.GetCapacityRange())
if err != nil {
return nil, status.Errorf(codes.OutOfRange, "capacityRange invalid: %s", err)
}

scwVolumeName := d.config.Prefix + volumeName
// TODO check all zones
volume, err := d.scaleway.GetVolumeByName(scwVolumeName, size, volumeType)
Expand Down Expand Up @@ -135,10 +141,8 @@ func (d *controllerService) CreateVolume(ctx context.Context, req *csi.CreateVol
Zone: sourceSnapshotZone,
})
if err != nil {
if ferr, ok := err.(*scw.ResponseError); ok {
if ferr.StatusCode == 404 {
return nil, status.Error(codes.NotFound, err.Error())
}
if _, ok := err.(*scw.ResourceNotFoundError); ok {
return nil, status.Errorf(codes.NotFound, "snapshot %s not found", sourceSnapshotID)
}
return nil, status.Error(codes.Internal, err.Error())
}
Expand Down Expand Up @@ -178,10 +182,8 @@ func (d *controllerService) CreateVolume(ctx context.Context, req *csi.CreateVol
}
volumeResp, err := d.scaleway.CreateVolume(volumeRequest)
if err != nil {
if ferr, ok := err.(*scw.ResponseError); ok {
if ferr.StatusCode == 404 {
return nil, status.Error(codes.NotFound, err.Error())
}
if _, ok := err.(*scw.ResourceNotFoundError); ok {
return nil, status.Error(codes.NotFound, err.Error())
}
return nil, status.Error(codes.Internal, err.Error())
}
Expand Down Expand Up @@ -248,12 +250,11 @@ func (d *controllerService) DeleteVolume(ctx context.Context, req *csi.DeleteVol
Zone: volumeZone,
})
if err != nil {
if ferr, ok := err.(*scw.ResponseError); ok {
if ferr.StatusCode == 404 {
klog.V(4).Infof("volume with ID %s not found", volumeID)
return &csi.DeleteVolumeResponse{}, nil
}
if _, ok := err.(*scw.ResourceNotFoundError); ok {
klog.V(4).Infof("volume with ID %s not found", volumeID)
return &csi.DeleteVolumeResponse{}, nil
}

return nil, status.Error(codes.Internal, err.Error())
}
if volumeResp.Volume.Server != nil {
Expand All @@ -266,12 +267,11 @@ func (d *controllerService) DeleteVolume(ctx context.Context, req *csi.DeleteVol
Zone: volumeResp.Volume.Zone,
})
if err != nil {
if ferr, ok := err.(*scw.ResponseError); ok {
if ferr.StatusCode == 404 {
klog.V(4).Infof("volume with ID %s not found", volumeID)
return &csi.DeleteVolumeResponse{}, nil
}
if _, ok := err.(*scw.ResourceNotFoundError); ok {
klog.V(4).Infof("volume with ID %s not found", volumeID)
return &csi.DeleteVolumeResponse{}, nil
}

return nil, status.Error(codes.Internal, err.Error())
}
klog.V(4).Infof("volume with ID %s deleted", volumeID)
Expand Down Expand Up @@ -308,10 +308,8 @@ func (d *controllerService) ControllerPublishVolume(ctx context.Context, req *cs
Zone: volumeZone,
})
if err != nil {
if ferr, ok := err.(*scw.ResponseError); ok {
if ferr.StatusCode == 404 {
return nil, status.Errorf(codes.NotFound, "volume %s not found", volumeID)
}
if _, ok := err.(*scw.ResourceNotFoundError); ok {
return nil, status.Errorf(codes.NotFound, "volume %s not found", volumeID)
}
return nil, status.Error(codes.Internal, err.Error())
}
Expand All @@ -321,10 +319,8 @@ func (d *controllerService) ControllerPublishVolume(ctx context.Context, req *cs
Zone: nodeZone,
})
if err != nil {
if ferr, ok := err.(*scw.ResponseError); ok {
if ferr.StatusCode == 404 {
return nil, status.Errorf(codes.NotFound, "node %s not found", nodeID)
}
if _, ok := err.(*scw.ResourceNotFoundError); ok {
return nil, status.Errorf(codes.NotFound, "instance %s not found", volumeID)
}
return nil, status.Error(codes.Internal, err.Error())
}
Expand Down Expand Up @@ -391,10 +387,8 @@ func (d *controllerService) ControllerUnpublishVolume(ctx context.Context, req *
Zone: volumeZone,
})
if err != nil {
if ferr, ok := err.(*scw.ResponseError); ok {
if ferr.StatusCode == 404 {
return &csi.ControllerUnpublishVolumeResponse{}, nil
}
if _, ok := err.(*scw.ResourceNotFoundError); ok {
return &csi.ControllerUnpublishVolumeResponse{}, nil
}
return nil, status.Error(codes.Internal, err.Error())
}
Expand All @@ -408,10 +402,8 @@ func (d *controllerService) ControllerUnpublishVolume(ctx context.Context, req *
Zone: nodeZone,
})
if err != nil {
if ferr, ok := err.(*scw.ResponseError); ok {
if ferr.StatusCode == 404 {
return &csi.ControllerUnpublishVolumeResponse{}, nil
}
if _, ok := err.(*scw.ResourceNotFoundError); ok {
return &csi.ControllerUnpublishVolumeResponse{}, nil
}
return nil, status.Error(codes.Internal, err.Error())
}
Expand Down Expand Up @@ -450,11 +442,10 @@ func (d *controllerService) ValidateVolumeCapabilities(ctx context.Context, req
Zone: volumeZone,
})
if err != nil {
if ferr, ok := err.(*scw.ResponseError); ok {
if ferr.StatusCode == 404 {
return nil, status.Errorf(codes.NotFound, "volume %s not found", volumeID)
}
if _, ok := err.(*scw.ResourceNotFoundError); ok {
return nil, status.Errorf(codes.NotFound, "volume %s not found", volumeID)
}

return nil, status.Error(codes.Internal, err.Error())
}
// TODO check stuff
Expand Down Expand Up @@ -620,6 +611,7 @@ func (d *controllerService) CreateSnapshot(ctx context.Context, req *csi.CreateS

// DeleteSnapshot deletes the given snapshot
func (d *controllerService) DeleteSnapshot(ctx context.Context, req *csi.DeleteSnapshotRequest) (*csi.DeleteSnapshotResponse, error) {
klog.V(4).Infof("DeleteSnapshot called with %+v", *req)
snapshotID, snapshotZone, err := getSnapshotIDAndZone(req.GetSnapshotId())
if err != nil {
return nil, err
Expand All @@ -630,12 +622,11 @@ func (d *controllerService) DeleteSnapshot(ctx context.Context, req *csi.DeleteS
Zone: snapshotZone,
})
if err != nil {
if ferr, ok := err.(*scw.ResponseError); ok {
if ferr.StatusCode == 404 {
klog.V(4).Infof("snapshot with ID %s not found", snapshotID)
return &csi.DeleteSnapshotResponse{}, nil
}
if _, ok := err.(*scw.ResourceNotFoundError); ok {
klog.V(4).Infof("snapshot with ID %s not found", snapshotID)
return &csi.DeleteSnapshotResponse{}, nil
}

return nil, status.Error(codes.Internal, err.Error())
}
return &csi.DeleteSnapshotResponse{}, nil
Expand All @@ -646,6 +637,7 @@ func (d *controllerService) DeleteSnapshot(ctx context.Context, req *csi.DeleteS
// they were created. ListSnapshots SHALL NOT list a snapshot that
// is being created but has not been cut successfully yet.
func (d *controllerService) ListSnapshots(ctx context.Context, req *csi.ListSnapshotsRequest) (*csi.ListSnapshotsResponse, error) {
klog.V(4).Infof("ListSnapshots called with %+v", *req)
var numberResults int
var err error

Expand Down Expand Up @@ -722,6 +714,69 @@ func (d *controllerService) ListSnapshots(ctx context.Context, req *csi.ListSnap

// ControllerExpandVolume expands the given volume
func (d *controllerService) ControllerExpandVolume(ctx context.Context, req *csi.ControllerExpandVolumeRequest) (*csi.ControllerExpandVolumeResponse, error) {
klog.V(4).Infof("ControllerExpandVolume is not yet implemented")
return nil, status.Error(codes.Unimplemented, "ControllerExpandVolume is not yet implemented")
klog.V(4).Infof("ControllerExpandVolume called with %+v", *req)
volumeID, volumeZone, err := getVolumeIDAndZone(req.GetVolumeId())
if err != nil {
return nil, err
}

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)
}
switch volumeCapability.GetAccessType().(type) {
case *csi.VolumeCapability_Block:
nodeExpansionRequired = false
}
}

volumeResp, err := d.scaleway.GetVolume(&instance.GetVolumeRequest{
VolumeID: volumeID,
Zone: volumeZone,
})
if err != nil {
if _, ok := err.(*scw.ResourceNotFoundError); ok {
return nil, status.Errorf(codes.NotFound, "volume %s not found", volumeID)
}
return nil, status.Error(codes.Internal, err.Error())
}

minSize, maxSize, err := d.scaleway.GetVolumeLimits(string(volumeResp.Volume.VolumeType))
if err != nil {
return nil, status.Error(codes.Internal, err.Error())
}

newSize, err := getVolumeRequestCapacity(minSize, maxSize, req.GetCapacityRange())
if err != nil {
return nil, status.Errorf(codes.OutOfRange, "capacityRange invalid: %s", err)
}

if newSize < int64(volumeResp.Volume.Size) {
return nil, status.Error(codes.InvalidArgument, "the new size of the volume will be less than the actual size")
}

_, err = d.scaleway.UpdateVolume(&instance.UpdateVolumeRequest{
Zone: volumeZone,
VolumeID: volumeID,
Size: scw.SizePtr(scw.Size(newSize)),
})
if err != nil {
return nil, status.Error(codes.Internal, err.Error())
}

vol, err := d.scaleway.WaitForVolume(&instance.WaitForVolumeRequest{
VolumeID: volumeID,
})
if err != nil {
return nil, status.Error(codes.Internal, err.Error())
}
if vol.State != instance.VolumeStateAvailable {
return nil, status.Errorf(codes.Internal, "volume %s is in state %s", volumeID, vol.State)
}

return &csi.ControllerExpandVolumeResponse{CapacityBytes: newSize, NodeExpansionRequired: nodeExpansionRequired}, nil
}
29 changes: 29 additions & 0 deletions driver/diskutils.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ type DiskUtils interface {

// GetStatfs return the statfs struct for the given path
GetStatfs(path string) (*unix.Statfs_t, error)

// Resize resizes the given volumes
Resize(targetPath string, devicePath string) error
}

type diskUtils struct{}
Expand Down Expand Up @@ -311,3 +314,29 @@ func (d *diskUtils) GetStatfs(path string) (*unix.Statfs_t, error) {
err := unix.Statfs(path, fs)
return fs, err
}

func (d *diskUtils) Resize(targetPath string, devicePath string) error {
mountInfo, err := d.GetMountInfo(targetPath)
if err != nil {
return err
}

switch mountInfo.fsType {
case "ext3", "ext4":
resize2fsPath, err := exec.LookPath("resize2fs")
if err != nil {
return err
}
resize2fsArgs := []string{devicePath}
return exec.Command(resize2fsPath, resize2fsArgs...).Run()
case "xfs":
xfsGrowfsPath, err := exec.LookPath("xfs_growfs")
if err != nil {
return err
}
xfsGrowfsArgs := []string{"-d", targetPath}
return exec.Command(xfsGrowfsPath, xfsGrowfsArgs...).Run()
}

return fmt.Errorf("filesystem %s does not support resizing", mountInfo.fsType)
}
17 changes: 8 additions & 9 deletions driver/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
"strings"

"github.com/container-storage-interface/spec/lib/go/csi"
"github.com/scaleway/scaleway-csi/scaleway"
"github.com/scaleway/scaleway-sdk-go/scw"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
Expand Down Expand Up @@ -159,9 +158,9 @@ func validateVolumeCapability(volumeCapability *csi.VolumeCapability) error {
return errAccessModeNotSupported
}

func getVolumeRequestCapacity(capacityRange *csi.CapacityRange) (int64, error) {
func getVolumeRequestCapacity(minSize int64, maxSize int64, capacityRange *csi.CapacityRange) (int64, error) {
if capacityRange == nil {
return scaleway.MinimumVolumeSizeInBytes, nil
return minSize, nil
}

requiredBytes := capacityRange.GetRequiredBytes()
Expand All @@ -171,26 +170,26 @@ func getVolumeRequestCapacity(capacityRange *csi.CapacityRange) (int64, error) {
limitBytesSet := limitBytes > 0

if !requiredBytesSet && !limitBytesSet {
return scaleway.MinimumVolumeSizeInBytes, nil
return minSize, nil
}

if requiredBytesSet && limitBytesSet && limitBytes < requiredBytes {
return 0, errLimitBytesLessThanRequiredBytes
}

if requiredBytesSet && !limitBytesSet && requiredBytes < scaleway.MinimumVolumeSizeInBytes {
if requiredBytesSet && !limitBytesSet && requiredBytes < minSize {
return 0, errRequiredBytesLessThanMinimun
}

if limitBytesSet && limitBytes < scaleway.MinimumVolumeSizeInBytes {
if limitBytesSet && limitBytes < minSize {
return 0, errLimitBytesLessThanMinimum
}

if requiredBytesSet && requiredBytes > scaleway.MaximumVolumeSizeInBytes {
if requiredBytesSet && requiredBytes > maxSize {
return 0, errRequiredBytesGreaterThanMaximun
}

if !requiredBytesSet && limitBytesSet && limitBytes > scaleway.MaximumVolumeSizeInBytes {
if !requiredBytesSet && limitBytesSet && limitBytes > maxSize {
return 0, errLimitBytesGreaterThanMaximum
}

Expand All @@ -206,7 +205,7 @@ func getVolumeRequestCapacity(capacityRange *csi.CapacityRange) (int64, error) {
return limitBytes, nil
}

return scaleway.MinimumVolumeSizeInBytes, nil
return minSize, nil
}

func newAccessibleTopology(zone scw.Zone) []*csi.Topology {
Expand Down
Loading