6
6
"errors"
7
7
"fmt"
8
8
"math/rand"
9
+ "strconv"
9
10
"time"
10
11
11
12
"github.com/container-storage-interface/spec/lib/go/csi"
@@ -338,15 +339,6 @@ func (cs *controllerServer) CreateSnapshot(ctx context.Context, req *csi.CreateS
338
339
return nil , status .Error (codes .InvalidArgument , "SourceVolumeId missing in request" )
339
340
}
340
341
341
- // Check for existing snapshot with same name but different source volume ID
342
- if req .GetName () != "" {
343
- // check if the name matches and volumeID differs
344
- existingSnap , err := cs .connector .GetSnapshotByName (ctx , req .GetName ())
345
- if err == nil && existingSnap .VolumeID != volumeID {
346
- return nil , status .Errorf (codes .AlreadyExists , "Snapshot with name %s already exists for a different source volume" , req .GetName ())
347
- }
348
- }
349
-
350
342
volume , err := cs .connector .GetVolumeByID (ctx , volumeID )
351
343
if err != nil {
352
344
if err .Error () == "invalid volume ID: empty string" {
@@ -355,12 +347,13 @@ func (cs *controllerServer) CreateSnapshot(ctx context.Context, req *csi.CreateS
355
347
if errors .Is (err , cloud .ErrNotFound ) {
356
348
return nil , status .Errorf (codes .NotFound , "Volume %v not found" , volumeID )
357
349
}
358
- // Error with CloudStack
359
350
return nil , status .Errorf (codes .Internal , "Error %v" , err )
360
351
}
361
352
klog .V (4 ).Infof ("CreateSnapshot of volume: %s" , volume .ID )
362
- snapshot , err := cs .connector .CreateSnapshot (ctx , volume .ID )
363
- if err != nil {
353
+ snapshot , err := cs .connector .CreateSnapshot (ctx , volume .ID , req .GetName ())
354
+ if errors .Is (err , cloud .ErrAlreadyExists ) {
355
+ return nil , status .Errorf (codes .AlreadyExists , "Snapshot name conflict: already exists for a different source volume" )
356
+ } else if err != nil {
364
357
return nil , status .Errorf (codes .Internal , "Failed to create snapshot for volume %s: %v" , volume .ID , err .Error ())
365
358
}
366
359
@@ -369,7 +362,6 @@ func (cs *controllerServer) CreateSnapshot(ctx context.Context, req *csi.CreateS
369
362
return nil , status .Errorf (codes .Internal , "Failed to parse snapshot creation time: %v" , err )
370
363
}
371
364
372
- // Convert to Timestamp protobuf
373
365
ts := timestamppb .New (t )
374
366
375
367
resp := & csi.CreateSnapshotResponse {
@@ -378,37 +370,53 @@ func (cs *controllerServer) CreateSnapshot(ctx context.Context, req *csi.CreateS
378
370
SourceVolumeId : volume .ID ,
379
371
CreationTime : ts ,
380
372
ReadyToUse : true ,
381
- // We leave the optional SizeBytes field unset as the size of a block storage snapshot is the size of the difference to the volume or previous snapshots, k8s however expects the size to be the size of the restored volume.
382
373
},
383
374
}
384
375
return resp , nil
385
-
386
376
}
387
377
388
378
func (cs * controllerServer ) ListSnapshots (ctx context.Context , req * csi.ListSnapshotsRequest ) (* csi.ListSnapshotsResponse , error ) {
389
379
entries := []* csi.ListSnapshotsResponse_Entry {}
390
380
391
- if req .GetSnapshotId () != "" {
392
- snap , err := cs .connector .GetSnapshotByID (ctx , req .GetSnapshotId ())
393
- if err == nil && snap != nil {
394
- t , _ := time .Parse ("2006-01-02T15:04:05-0700" , snap .CreatedAt )
395
- ts := timestamppb .New (t )
396
- entry := & csi.ListSnapshotsResponse_Entry {
397
- Snapshot : & csi.Snapshot {
398
- SnapshotId : snap .ID ,
399
- SourceVolumeId : snap .VolumeID ,
400
- CreationTime : ts ,
401
- ReadyToUse : true ,
402
- },
403
- }
404
- entries = append (entries , entry )
405
- return & csi.ListSnapshotsResponse {Entries : entries }, nil
406
- }
407
- // If not found, return empty list
408
- return & csi.ListSnapshotsResponse {Entries : []* csi.ListSnapshotsResponse_Entry {}}, nil
381
+ snapshots , err := cs .connector .ListSnapshots (ctx , req .GetSourceVolumeId (), req .GetSnapshotId ())
382
+ if err != nil {
383
+ return nil , status .Errorf (codes .Internal , "Failed to list snapshots: %v" , err )
409
384
}
410
385
411
- return & csi.ListSnapshotsResponse {Entries : entries }, nil
386
+ // Pagination logic
387
+ start := 0
388
+ if req .StartingToken != "" {
389
+ var err error
390
+ start , err = strconv .Atoi (req .StartingToken )
391
+ if err != nil || start < 0 || start > len (snapshots ) {
392
+ return nil , status .Error (codes .Aborted , "Invalid startingToken" )
393
+ }
394
+ }
395
+ maxEntries := int (req .MaxEntries )
396
+ end := len (snapshots )
397
+ if maxEntries > 0 && start + maxEntries < end {
398
+ end = start + maxEntries
399
+ }
400
+ nextToken := ""
401
+ if end < len (snapshots ) {
402
+ nextToken = strconv .Itoa (end )
403
+ }
404
+
405
+ for i := start ; i < end ; i ++ {
406
+ snap := snapshots [i ]
407
+ t , _ := time .Parse ("2006-01-02T15:04:05-0700" , snap .CreatedAt )
408
+ ts := timestamppb .New (t )
409
+ entry := & csi.ListSnapshotsResponse_Entry {
410
+ Snapshot : & csi.Snapshot {
411
+ SnapshotId : snap .ID ,
412
+ SourceVolumeId : snap .VolumeID ,
413
+ CreationTime : ts ,
414
+ ReadyToUse : true ,
415
+ },
416
+ }
417
+ entries = append (entries , entry )
418
+ }
419
+ return & csi.ListSnapshotsResponse {Entries : entries , NextToken : nextToken }, nil
412
420
}
413
421
414
422
func (cs * controllerServer ) DeleteSnapshot (ctx context.Context , req * csi.DeleteSnapshotRequest ) (* csi.DeleteSnapshotResponse , error ) {
@@ -420,19 +428,14 @@ func (cs *controllerServer) DeleteSnapshot(ctx context.Context, req *csi.DeleteS
420
428
421
429
klog .V (4 ).Infof ("DeleteSnapshot for snapshotID: %s" , snapshotID )
422
430
423
- snapshot , err := cs .connector .GetSnapshotByID (ctx , snapshotID )
431
+ err := cs .connector .DeleteSnapshot (ctx , snapshotID )
424
432
if errors .Is (err , cloud .ErrNotFound ) {
425
- return nil , status .Errorf (codes .NotFound , "Snapshot %v not found" , snapshotID )
433
+ // Per CSI spec, return OK if snapshot does not exist
434
+ return & csi.DeleteSnapshotResponse {}, nil
426
435
} else if err != nil {
427
- // Error with CloudStack
428
436
return nil , status .Errorf (codes .Internal , "Error %v" , err )
429
437
}
430
438
431
- err = cs .connector .DeleteSnapshot (ctx , snapshot .ID )
432
- if err != nil && ! errors .Is (err , cloud .ErrNotFound ) {
433
- return nil , status .Errorf (codes .Internal , "Cannot delete snapshot %s: %s" , snapshotID , err .Error ())
434
- }
435
-
436
439
return & csi.DeleteSnapshotResponse {}, nil
437
440
}
438
441
0 commit comments