Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

support deletion of snapshots after restore from backup #212

Merged
merged 1 commit into from
Oct 6, 2023
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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added

- Support to delete downloaded backups after restore operation (k8s only).

## [1.2.3] - 2023-08-31

### Changed
Expand Down
1 change: 1 addition & 0 deletions cmd/linstor-csi/linstor-csi.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ func main() {
driver.Expander(linstorClient),
driver.NodeInformer(linstorClient),
driver.TopologyPrefix(*propNs),
driver.ConfigureKubernetesIfAvailable(),
)
if err != nil {
log.Fatal(err)
Expand Down
16 changes: 15 additions & 1 deletion pkg/client/linstor.go
Original file line number Diff line number Diff line change
Expand Up @@ -910,7 +910,7 @@ func (s *Linstor) SnapDelete(ctx context.Context, snap *volume.Snapshot) error {
}

// VolFromSnap creates the volume using the data contained within the snapshot.
func (s *Linstor) VolFromSnap(ctx context.Context, snap *volume.Snapshot, vol *volume.Info, params *volume.Parameters, topologies *csi.TopologyRequirement) error {
func (s *Linstor) VolFromSnap(ctx context.Context, snap *volume.Snapshot, vol *volume.Info, params *volume.Parameters, snapParams *volume.SnapshotParameters, topologies *csi.TopologyRequirement) error {
logger := s.log.WithFields(logrus.Fields{
"volume": fmt.Sprintf("%+v", vol),
"snapshot": fmt.Sprintf("%+v", snap),
Expand Down Expand Up @@ -987,6 +987,20 @@ func (s *Linstor) VolFromSnap(ctx context.Context, snap *volume.Snapshot, vol *v
return err
}

if snap.Remote != "" && snapParams != nil && snapParams.DeleteLocal {
logger.Info("deleting local copy of backup")

err := s.client.Resources.DeleteSnapshot(ctx, snap.SourceVolumeId, snap.SnapshotId)
if err != nil {
logger.WithError(err).Warn("deleting local copy of backup failed")
}

err = s.deleteResourceDefinitionAndGroupIfUnused(ctx, snap.GetSourceVolumeId())
if err != nil {
logger.WithError(err).Warn("deleting local RD of backup failed")
}
}

logger.Debug("success")
return nil
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/client/mock.go
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ func (s *MockStorage) FindSnapsBySource(ctx context.Context, sourceVol *volume.I
return results[start:end], nil
}

func (s *MockStorage) VolFromSnap(ctx context.Context, snap *volume.Snapshot, vol *volume.Info, parameters *volume.Parameters, topologies *csi.TopologyRequirement) error {
func (s *MockStorage) VolFromSnap(ctx context.Context, snap *volume.Snapshot, vol *volume.Info, parameters *volume.Parameters, snapParams *volume.SnapshotParameters, topologies *csi.TopologyRequirement) error {
s.createdVolumes = append(s.createdVolumes, vol)
return nil
}
Expand Down
86 changes: 84 additions & 2 deletions pkg/driver/driver.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@ import (
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/dynamic"
"k8s.io/client-go/rest"

"github.com/piraeusdatastore/linstor-csi/pkg/client"
"github.com/piraeusdatastore/linstor-csi/pkg/linstor"
Expand All @@ -57,6 +62,7 @@ type Driver struct {
VolumeStatter volume.VolumeStatter
Expander volume.Expander
NodeInformer volume.NodeInformer
kubeClient dynamic.Interface
srv *grpc.Server
log *logrus.Entry
version string
Expand Down Expand Up @@ -236,6 +242,20 @@ func LogLevel(s string) func(*Driver) error {
}
}

func ConfigureKubernetesIfAvailable() func(*Driver) error {
return func(d *Driver) error {
cfg, err := rest.InClusterConfig()
if err != nil {
// Not running in kubernetes
return nil
}

d.kubeClient, err = dynamic.NewForConfig(cfg)

return err
}
}

// GetPluginInfo https://github.com/container-storage-interface/spec/blob/v1.6.0/spec.md#getplugininfo
func (d Driver) GetPluginInfo(ctx context.Context, req *csi.GetPluginInfoRequest) (*csi.GetPluginInfoResponse, error) {
return &csi.GetPluginInfoResponse{
Expand Down Expand Up @@ -1259,7 +1279,14 @@ func (d Driver) createNewVolume(ctx context.Context, info *volume.Info, params *
"CreateVolume failed for %s: snapshot not found in storage backend", req.GetName())
}

if err := d.Snapshots.VolFromSnap(ctx, snap, info, params, req.GetAccessibilityRequirements()); err != nil {
snapParams, err := d.maybeGetSnapshotParameters(ctx, snap)
if err != nil {
logger.WithError(err).Warn("failed to fetch snapshot parameters, continuing without it")

snapParams = nil
}

if err := d.Snapshots.VolFromSnap(ctx, snap, info, params, snapParams, req.GetAccessibilityRequirements()); err != nil {
d.failpathDelete(ctx, info.ID)
return nil, status.Errorf(codes.Internal,
"CreateVolume failed for %s: %v", req.GetName(), err)
Expand Down Expand Up @@ -1301,7 +1328,7 @@ func (d Driver) createNewVolume(ctx context.Context, info *volume.Info, params *
}
}()

err = d.Snapshots.VolFromSnap(ctx, snap, info, params, req.GetAccessibilityRequirements())
err = d.Snapshots.VolFromSnap(ctx, snap, info, params, nil, req.GetAccessibilityRequirements())
if err != nil {
d.failpathDelete(ctx, info.ID)

Expand Down Expand Up @@ -1345,6 +1372,61 @@ func (d Driver) createNewVolume(ctx context.Context, info *volume.Info, params *
}, nil
}

func findMatchingSnapshotClassName(snap *volume.Snapshot, contents ...unstructured.Unstructured) string {
for i := range contents {
content := contents[i].Object
if driver, _, _ := unstructured.NestedString(content, "spec", "driver"); driver != linstor.DriverName {
continue
}

if handle, _, _ := unstructured.NestedString(content, "status", "snapshotHandle"); handle != snap.SnapshotId {
continue
}

if readyToUse, _, _ := unstructured.NestedBool(content, "status", "readyToUse"); !readyToUse {
continue
}

snapshotClass, _, _ := unstructured.NestedString(content, "spec", "volumeSnapshotClassName")

return snapshotClass
}

return ""
}

func (d Driver) maybeGetSnapshotParameters(ctx context.Context, snap *volume.Snapshot) (*volume.SnapshotParameters, error) {
if d.kubeClient == nil {
return nil, nil
}

gv := schema.GroupVersion{Group: "snapshot.storage.k8s.io", Version: "v1"}
contentGvr := gv.WithResource("volumesnapshotcontents")
classGvr := gv.WithResource("volumesnapshotclasses")

result, err := d.kubeClient.Resource(contentGvr).List(ctx, metav1.ListOptions{})
if err != nil {
return nil, fmt.Errorf("failed to fetch list of snapshot contents")
}

snapshotClassName := findMatchingSnapshotClassName(snap, result.Items...)
if snapshotClassName == "" {
return nil, fmt.Errorf("failed to determine snapshot class name")
}

class, err := d.kubeClient.Resource(classGvr).Get(ctx, snapshotClassName, metav1.GetOptions{})
if err != nil {
return nil, fmt.Errorf("failed to fetch snapshot class: %w", err)
}

rawParams, _, err := unstructured.NestedStringMap(class.Object, "parameters")
if err != nil {
return nil, fmt.Errorf("failed to parse snapshot class: %w", err)
}

return volume.NewSnapshotParameters(rawParams, nil)
}

// maybeDeleteLocalSnapshot deletes the local portion of a snapshot according to their volume.SnapshotParameters.
// It will not delete a snapshot that is not ready, does not have a remote target, or where local deletion is disabled.
func (d Driver) maybeDeleteLocalSnapshot(ctx context.Context, snap *volume.Snapshot, params *volume.SnapshotParameters) error {
Expand Down
2 changes: 1 addition & 1 deletion pkg/volume/volume.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ type SnapshotCreateDeleter interface {
// List Snapshots should return a sorted list of snapshots.
ListSnaps(ctx context.Context, start, limit int) ([]*Snapshot, error)
// VolFromSnap creates a new volume based on the provided snapshot.
VolFromSnap(ctx context.Context, snap *Snapshot, vol *Info, params *Parameters, topologies *csi.TopologyRequirement) error
VolFromSnap(ctx context.Context, snap *Snapshot, vol *Info, params *Parameters, snapParams *SnapshotParameters, topologies *csi.TopologyRequirement) error
}

// AttacherDettacher handles operations relating to volume accessiblity on nodes.
Expand Down
Loading