Skip to content

Commit 15a2ed6

Browse files
committed
also fetch backups when fetching snapshots
Signed-off-by: Felix Breuer <f.breuer94@gmail.com>
1 parent f70d9e2 commit 15a2ed6

2 files changed

Lines changed: 285 additions & 54 deletions

File tree

pkg/csi/blockstorage/controllerserver.go

Lines changed: 97 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"context"
2121
"errors"
2222
"fmt"
23+
"sort"
2324
"strconv"
2425
"time"
2526

@@ -769,76 +770,127 @@ func (cs *controllerServer) ListSnapshots(ctx context.Context, req *csi.ListSnap
769770
if snapshotID != "" {
770771
snap, err := cloud.GetSnapshot(ctx, snapshotID)
771772
if err != nil {
772-
if stackiterrors.IsNotFound(err) {
773-
klog.V(3).Infof("Snapshot %s not found", snapshotID)
774-
return &csi.ListSnapshotsResponse{}, nil
773+
if !stackiterrors.IsNotFound(err) {
774+
return nil, status.Errorf(codes.Internal, "Failed to GetSnapshot %s: %v", snapshotID, err)
775775
}
776-
return nil, status.Errorf(codes.Internal, "Failed to GetSnapshot %s: %v", snapshotID, err)
777-
}
778776

779-
ctime := timestamppb.New(*snap.CreatedAt)
777+
backup, backupErr := cloud.GetBackup(ctx, snapshotID)
778+
if backupErr != nil {
779+
if stackiterrors.IsNotFound(backupErr) {
780+
klog.V(3).Infof("Snapshot or backup %s not found", snapshotID)
781+
return &csi.ListSnapshotsResponse{}, nil
782+
}
783+
return nil, status.Errorf(codes.Internal, "Failed to GetBackup %s: %v", snapshotID, backupErr)
784+
}
780785

781-
entry := &csi.ListSnapshotsResponse_Entry{
782-
Snapshot: &csi.Snapshot{
783-
SizeBytes: *snap.Size * util.GIBIBYTE,
784-
SnapshotId: *snap.Id,
785-
SourceVolumeId: snap.VolumeId,
786-
CreationTime: ctime,
787-
ReadyToUse: true,
788-
},
786+
entry := backupSnapshotEntry(*backup)
787+
return &csi.ListSnapshotsResponse{
788+
Entries: []*csi.ListSnapshotsResponse_Entry{entry},
789+
}, entry.Snapshot.CreationTime.CheckValid()
789790
}
790791

791-
entries := []*csi.ListSnapshotsResponse_Entry{entry}
792792
return &csi.ListSnapshotsResponse{
793-
Entries: entries,
794-
}, ctime.CheckValid()
793+
Entries: []*csi.ListSnapshotsResponse_Entry{snapshotEntry(*snap)},
794+
}, timestamppb.New(*snap.CreatedAt).CheckValid()
795795
}
796796

797-
filters := map[string]string{}
798-
799-
var slist []iaas.Snapshot
800-
var err error
801-
var nextPageToken string
797+
startOffset := 0
798+
if req.GetStartingToken() != "" {
799+
var err error
800+
startOffset, err = strconv.Atoi(req.GetStartingToken())
801+
if err != nil || startOffset < 0 {
802+
return nil, status.Error(codes.Aborted, "starting_token is invalid")
803+
}
804+
}
802805

803-
// Add the filters
806+
filters := map[string]string{
807+
"Status": stackitclient.SnapshotReadyStatus,
808+
}
804809
if req.GetSourceVolumeId() != "" {
805810
filters["VolumeID"] = req.GetSourceVolumeId()
806-
} else {
807-
filters["Limit"] = strconv.Itoa(int(req.MaxEntries))
808-
filters["Marker"] = req.StartingToken
809811
}
810812

811-
// Only retrieve snapshots that are available
812-
filters["Status"] = stackitclient.SnapshotReadyStatus
813-
slist, nextPageToken, err = cloud.ListSnapshots(ctx, filters)
813+
snapshotList, _, err := cloud.ListSnapshots(ctx, filters)
814814
if err != nil {
815815
klog.Errorf("Failed to ListSnapshots: %v", err)
816816
return nil, status.Errorf(codes.Internal, "ListSnapshots failed with error %v", err)
817817
}
818818

819-
sentries := make([]*csi.ListSnapshotsResponse_Entry, 0, len(slist))
820-
for _, v := range slist {
821-
ctime := timestamppb.New(*v.CreatedAt)
822-
if err := ctime.CheckValid(); err != nil {
823-
klog.Errorf("Error to convert time to timestamp: %v", err)
824-
}
825-
sentry := csi.ListSnapshotsResponse_Entry{
826-
Snapshot: &csi.Snapshot{
827-
SizeBytes: *v.Size * util.GIBIBYTE,
828-
SnapshotId: *v.Id,
829-
SourceVolumeId: v.VolumeId,
830-
CreationTime: ctime,
831-
ReadyToUse: true,
832-
},
819+
backupList, err := cloud.ListBackups(ctx, filters)
820+
if err != nil {
821+
klog.Errorf("Failed to ListBackups: %v", err)
822+
return nil, status.Errorf(codes.Internal, "ListBackups failed with error %v", err)
823+
}
824+
825+
entries := make([]*csi.ListSnapshotsResponse_Entry, 0, len(snapshotList)+len(backupList))
826+
for _, v := range snapshotList {
827+
entries = append(entries, snapshotEntry(v))
828+
}
829+
for _, v := range backupList {
830+
entries = append(entries, backupSnapshotEntry(v))
831+
}
832+
833+
// sort by creation time and by id (fallback)
834+
sort.Slice(entries, func(i, j int) bool {
835+
left := entries[i].Snapshot
836+
right := entries[j].Snapshot
837+
if left.CreationTime.AsTime().Equal(right.CreationTime.AsTime()) {
838+
return left.SnapshotId < right.SnapshotId
833839
}
834-
sentries = append(sentries, &sentry)
840+
return left.CreationTime.AsTime().Before(right.CreationTime.AsTime())
841+
})
842+
843+
if startOffset > len(entries) {
844+
return nil, status.Error(codes.Aborted, "starting_token is invalid")
845+
}
846+
847+
nextPageToken := ""
848+
entries = entries[startOffset:]
849+
if req.MaxEntries > 0 && int(req.MaxEntries) < len(entries) {
850+
nextPageToken = strconv.Itoa(startOffset + int(req.MaxEntries))
851+
entries = entries[:req.MaxEntries]
835852
}
853+
836854
return &csi.ListSnapshotsResponse{
837-
Entries: sentries,
855+
Entries: entries,
838856
NextToken: nextPageToken,
839857
}, nil
840858
}
841859

860+
func snapshotEntry(snapshot iaas.Snapshot) *csi.ListSnapshotsResponse_Entry {
861+
ctime := timestamppb.New(*snapshot.CreatedAt)
862+
if err := ctime.CheckValid(); err != nil {
863+
klog.Errorf("Error to convert time to timestamp: %v", err)
864+
}
865+
866+
return &csi.ListSnapshotsResponse_Entry{
867+
Snapshot: &csi.Snapshot{
868+
SizeBytes: *snapshot.Size * util.GIBIBYTE,
869+
SnapshotId: *snapshot.Id,
870+
SourceVolumeId: snapshot.VolumeId,
871+
CreationTime: ctime,
872+
ReadyToUse: true,
873+
},
874+
}
875+
}
876+
877+
func backupSnapshotEntry(backup iaas.Backup) *csi.ListSnapshotsResponse_Entry {
878+
ctime := timestamppb.New(*backup.CreatedAt)
879+
if err := ctime.CheckValid(); err != nil {
880+
klog.Errorf("Error to convert time to timestamp: %v", err)
881+
}
882+
883+
return &csi.ListSnapshotsResponse_Entry{
884+
Snapshot: &csi.Snapshot{
885+
SizeBytes: *backup.Size * util.GIBIBYTE,
886+
SnapshotId: *backup.Id,
887+
SourceVolumeId: *backup.VolumeId,
888+
CreationTime: ctime,
889+
ReadyToUse: true,
890+
},
891+
}
892+
}
893+
842894
// ControllerGetCapabilities implements the default GRPC callout.
843895
// Default supports all capabilities
844896
func (cs *controllerServer) ControllerGetCapabilities(_ context.Context, _ *csi.ControllerGetCapabilitiesRequest) (*csi.ControllerGetCapabilitiesResponse, error) {

0 commit comments

Comments
 (0)