diff --git a/internal/csi-addons/rbd/reclaimspace.go b/internal/csi-addons/rbd/reclaimspace.go new file mode 100644 index 000000000..7ef7e1716 --- /dev/null +++ b/internal/csi-addons/rbd/reclaimspace.go @@ -0,0 +1,74 @@ +/* +Copyright 2021 The Ceph-CSI Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package rbd + +import ( + "context" + + rbdutil "github.com/ceph/ceph-csi/internal/rbd" + "github.com/ceph/ceph-csi/internal/util" + + rs "github.com/csi-addons/spec/lib/go/reclaimspace" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +// ReclaimSpaceControllerServer struct of rbd CSI driver with supported methods +// of CSI-addons reclaimspace controller service spec. +type ReclaimSpaceControllerServer struct { + *rs.UnimplementedReclaimSpaceControllerServer +} + +// NewReclaimSpaceControllerServer creates a new IdentityServer which handles +// the Identity Service requests from the CSI-Addons specification. +func NewReclaimSpaceControllerServer() *ReclaimSpaceControllerServer { + return &ReclaimSpaceControllerServer{} +} + +func (rscs *ReclaimSpaceControllerServer) RegisterService(server grpc.ServiceRegistrar) { + rs.RegisterReclaimSpaceControllerServer(server, rscs) +} + +func (rscs *ReclaimSpaceControllerServer) ControllerReclaimSpace( + ctx context.Context, + req *rs.ControllerReclaimSpaceRequest) (*rs.ControllerReclaimSpaceResponse, error) { + volumeID := req.GetVolumeId() + if volumeID == "" { + return nil, status.Error(codes.InvalidArgument, "empty volume ID in request") + } + + cr, err := util.NewUserCredentials(req.GetSecrets()) + if err != nil { + return nil, status.Error(codes.Internal, err.Error()) + } + defer cr.DeleteCredentials() + + rbdVol, err := rbdutil.GenVolFromVolID(ctx, volumeID, cr, req.GetSecrets()) + if err != nil { + return nil, status.Errorf(codes.Aborted, "failed to find volume with ID %q: %s", volumeID, err.Error()) + } + defer rbdVol.Destroy() + + err = rbdVol.Sparsify() + if err != nil { + // TODO: check for different error codes? + return nil, status.Errorf(codes.Internal, "failed to sparsify volume %q: %s", rbdVol, err.Error()) + } + + return &rs.ControllerReclaimSpaceResponse{}, nil +} diff --git a/internal/csi-addons/rbd/reclaimspace_test.go b/internal/csi-addons/rbd/reclaimspace_test.go new file mode 100644 index 000000000..18c67c6db --- /dev/null +++ b/internal/csi-addons/rbd/reclaimspace_test.go @@ -0,0 +1,43 @@ +/* +Copyright 2021 The Ceph-CSI Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package rbd + +import ( + "context" + "testing" + + "github.com/stretchr/testify/assert" + + rs "github.com/csi-addons/spec/lib/go/reclaimspace" +) + +// TestControllerReclaimSpace is a minimal test for the +// ControllerReclaimSpace() procedure. During unit-testing, there is no Ceph +// cluster available, so actual operations can not be performed. +func TestControllerReclaimSpace(t *testing.T) { + t.Parallel() + + controller := NewReclaimSpaceControllerServer() + + req := &rs.ControllerReclaimSpaceRequest{ + VolumeId: "", + Secrets: nil, + } + + _, err := controller.ControllerReclaimSpace(context.TODO(), req) + assert.Error(t, err) +} diff --git a/internal/rbd/diskusage.go b/internal/rbd/diskusage.go new file mode 100644 index 000000000..efb12f37c --- /dev/null +++ b/internal/rbd/diskusage.go @@ -0,0 +1,44 @@ +/* +Copyright 2021 The Ceph-CSI Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package rbd + +import ( + "fmt" +) + +// Sparsify checks the size of the objects in the RBD image and calls +// rbd_sparify() to free zero-filled blocks and reduce the storage consumption +// of the image. +func (ri *rbdImage) Sparsify() error { + image, err := ri.open() + if err != nil { + return err + } + defer image.Close() + + imageInfo, err := image.Stat() + if err != nil { + return err + } + + err = image.Sparsify(1 << imageInfo.Order) + if err != nil { + return fmt.Errorf("failed to sparsify image: %w", err) + } + + return nil +}