@@ -45,8 +45,10 @@ type expansionStatus int
45
45
46
46
const (
47
47
expansionSuccess = iota
48
- expansionFailedOnController
49
- expansionFailedOnNode
48
+ expansionFailedOnControllerWithInfeasibleError
49
+ expansionFailedOnControllerWithFinalError
50
+ expansionFailedOnNodeWithInfeasibleError
51
+ expansionFailedOnNodeWithFinalError
50
52
expansionFailedMissingStagingPath
51
53
)
52
54
@@ -61,12 +63,13 @@ var (
61
63
)
62
64
63
65
type recoveryTest struct {
64
- name string
65
- pvcRequestSize string
66
- allocatedResource string
67
- simulatedCSIDriverError expansionStatus
68
- expectedResizeStatus v1.ClaimResourceStatus
69
- recoverySize resource.Quantity
66
+ name string
67
+ pvcRequestSize string
68
+ allocatedResource string
69
+ simulatedCSIDriverError expansionStatus
70
+ disableControllerExpansion bool
71
+ expectedResizeStatus v1.ClaimResourceStatus
72
+ recoverySize resource.Quantity
70
73
}
71
74
72
75
var _ = utils .SIGDescribe ("CSI Mock volume expansion" , func () {
@@ -400,35 +403,65 @@ var _ = utils.SIGDescribe("CSI Mock volume expansion", func() {
400
403
f .Context ("Expansion with recovery" , feature .RecoverVolumeExpansionFailure , func () {
401
404
tests := []recoveryTest {
402
405
{
403
- name : "should record target size in allocated resources" ,
404
- pvcRequestSize : "4Gi" ,
405
- allocatedResource : "4Gi" ,
406
- simulatedCSIDriverError : expansionSuccess ,
407
- expectedResizeStatus : "" ,
406
+ name : "should record target size in allocated resources" ,
407
+ pvcRequestSize : "4Gi" ,
408
+ allocatedResource : "4Gi" ,
409
+ disableControllerExpansion : false ,
410
+ simulatedCSIDriverError : expansionSuccess ,
411
+ expectedResizeStatus : "" ,
412
+ },
413
+ {
414
+ name : "should allow recovery if controller expansion fails with infeasible error" ,
415
+ pvcRequestSize : "11Gi" , // expansion to 11Gi will cause expansion to fail on controller
416
+ allocatedResource : "11Gi" ,
417
+ disableControllerExpansion : false ,
418
+ simulatedCSIDriverError : expansionFailedOnControllerWithInfeasibleError ,
419
+ expectedResizeStatus : v1 .PersistentVolumeClaimControllerResizeInfeasible ,
420
+ recoverySize : resource .MustParse ("4Gi" ),
408
421
},
409
422
{
410
- name : "should allow recovery if controller expansion fails with final error" ,
411
- pvcRequestSize : "11Gi" , // expansion to 11Gi will cause expansion to fail on controller
412
- allocatedResource : "11Gi" ,
413
- simulatedCSIDriverError : expansionFailedOnController ,
414
- expectedResizeStatus : v1 .PersistentVolumeClaimControllerResizeInfeasible ,
415
- recoverySize : resource .MustParse ("4Gi" ),
423
+ name : "should allow recovery if controller expansion fails with final error" ,
424
+ pvcRequestSize : "11Gi" , // expansion to 11Gi will cause expansion to fail on controller
425
+ allocatedResource : "11Gi" ,
426
+ disableControllerExpansion : false ,
427
+ simulatedCSIDriverError : expansionFailedOnControllerWithFinalError ,
428
+ expectedResizeStatus : v1 .PersistentVolumeClaimControllerResizeInProgress ,
429
+ recoverySize : resource .MustParse ("4Gi" ),
416
430
},
417
431
{
418
- name : "recovery should not be possible in partially expanded volumes" ,
419
- pvcRequestSize : "9Gi" , // expansion to 9Gi will cause expansion to fail on node
420
- allocatedResource : "9Gi" ,
421
- simulatedCSIDriverError : expansionFailedOnNode ,
422
- expectedResizeStatus : v1 .PersistentVolumeClaimNodeResizeInfeasible ,
423
- recoverySize : resource .MustParse ("5Gi" ),
432
+ name : "recovery should not be possible in partially expanded volumes" ,
433
+ pvcRequestSize : "9Gi" , // expansion to 9Gi will cause expansion to fail on node
434
+ allocatedResource : "9Gi" ,
435
+ disableControllerExpansion : false ,
436
+ simulatedCSIDriverError : expansionFailedOnNodeWithInfeasibleError ,
437
+ expectedResizeStatus : v1 .PersistentVolumeClaimNodeResizeInfeasible ,
438
+ recoverySize : resource .MustParse ("5Gi" ),
439
+ },
440
+ {
441
+ name : "recovery should be possible for node-only expanded volumes with infeasible error" ,
442
+ pvcRequestSize : "9Gi" , // expansion to 9Gi will cause expansion to fail on node
443
+ allocatedResource : "9Gi" ,
444
+ disableControllerExpansion : true ,
445
+ simulatedCSIDriverError : expansionFailedOnNodeWithInfeasibleError ,
446
+ expectedResizeStatus : v1 .PersistentVolumeClaimNodeResizeInfeasible ,
447
+ recoverySize : resource .MustParse ("5Gi" ),
448
+ },
449
+ {
450
+ name : "recovery should be possible for node-only expanded volumes with final error" ,
451
+ pvcRequestSize : "9Gi" , // expansion to 9Gi will cause expansion to fail on node
452
+ allocatedResource : "9Gi" ,
453
+ disableControllerExpansion : true ,
454
+ simulatedCSIDriverError : expansionFailedOnNodeWithFinalError ,
455
+ expectedResizeStatus : v1 .PersistentVolumeClaimNodeResizeInProgress ,
456
+ recoverySize : resource .MustParse ("5Gi" ),
424
457
},
425
458
}
426
459
427
460
for _ , t := range tests {
428
461
test := t
429
462
ginkgo .It (test .name , func (ctx context.Context ) {
430
463
var err error
431
- params := testParameters {enableResizing : true , enableNodeExpansion : true , enableRecoverExpansionFailure : true }
464
+ params := testParameters {enableResizing : true , enableNodeExpansion : true , enableRecoverExpansionFailure : true , disableControllerExpansion : test . disableControllerExpansion }
432
465
433
466
if test .simulatedCSIDriverError != expansionSuccess {
434
467
params .hooks = createExpansionHook (test .simulatedCSIDriverError )
@@ -476,9 +509,15 @@ func validateRecoveryBehaviour(ctx context.Context, pvc *v1.PersistentVolumeClai
476
509
err = waitForAllocatedResource (ctx , pvc , m , test .allocatedResource )
477
510
framework .ExpectNoError (err , "While waiting for allocated resource to be updated" )
478
511
479
- ginkgo .By ("Waiting for resizer to set resize status" )
480
- err = waitForResizeStatus (ctx , pvc , m .cs , test .expectedResizeStatus )
481
- framework .ExpectNoError (err , "While waiting for resize status to be set" )
512
+ if test .expectedResizeStatus == v1 .PersistentVolumeClaimNodeResizeInfeasible {
513
+ ginkgo .By ("Waiting for kubelet to fail expansion on the node" )
514
+ err = waitForResizeToFailOnNode (ctx , pvc , m .cs )
515
+ framework .ExpectNoError (err , "While waiting for resize status to be set" )
516
+ } else {
517
+ ginkgo .By ("Waiting for resizer to set resize status" )
518
+ err = waitForResizeStatus (ctx , pvc , m .cs , test .expectedResizeStatus )
519
+ framework .ExpectNoError (err , "While waiting for resize status to be set" )
520
+ }
482
521
483
522
ginkgo .By ("Recover pvc size" )
484
523
newPVC , err := testsuites .ExpandPVCSize (ctx , pvc , test .recoverySize , m .cs )
@@ -492,16 +531,25 @@ func validateRecoveryBehaviour(ctx context.Context, pvc *v1.PersistentVolumeClai
492
531
}
493
532
494
533
// if expansion failed on controller with final error, then recovery should be possible
495
- if test .simulatedCSIDriverError == expansionFailedOnController {
534
+ if test .simulatedCSIDriverError == expansionFailedOnControllerWithInfeasibleError {
535
+ validateExpansionSuccess (ctx , pvc , m , test , test .recoverySize .String ())
536
+ return
537
+ }
538
+
539
+ // if expansion failed on node with final error but volume was only expanded on the node
540
+ // then recovery should be possible
541
+ if test .disableControllerExpansion &&
542
+ (test .simulatedCSIDriverError == expansionFailedOnNodeWithInfeasibleError ||
543
+ test .simulatedCSIDriverError == expansionFailedOnNodeWithFinalError ) {
496
544
validateExpansionSuccess (ctx , pvc , m , test , test .recoverySize .String ())
497
545
return
498
546
}
499
547
500
548
// if expansion succeeded on controller but failed on the node
501
- if test .simulatedCSIDriverError == expansionFailedOnNode {
549
+ if test .simulatedCSIDriverError == expansionFailedOnNodeWithInfeasibleError {
502
550
ginkgo .By ("Wait for expansion to fail on node again" )
503
- err = waitForResizeStatus (ctx , pvc , m .cs , v1 . PersistentVolumeClaimNodeResizeInfeasible )
504
- framework .ExpectNoError (err , "While waiting for resize status to be set to expansion-failed-on-node " )
551
+ err = waitForResizeToFailOnNode (ctx , pvc , m .cs )
552
+ framework .ExpectNoError (err , "While waiting for resize status to be set" )
505
553
506
554
ginkgo .By ("verify allocated resources after recovery" )
507
555
pvc , err = m .cs .CoreV1 ().PersistentVolumeClaims (pvc .Namespace ).Get (context .TODO (), pvc .Name , metav1.GetOptions {})
@@ -520,11 +568,11 @@ func validateRecoveryBehaviour(ctx context.Context, pvc *v1.PersistentVolumeClai
520
568
521
569
func validateExpansionSuccess (ctx context.Context , pvc * v1.PersistentVolumeClaim , m * mockDriverSetup , test recoveryTest , expectedAllocatedSize string ) {
522
570
var err error
523
- ginkgo .By ("Waiting for persistent volume resize to finish" )
524
- err = testsuites .WaitForControllerVolumeResize ( ctx , pvc , m .cs , csiResizeWaitPeriod )
571
+ ginkgo .By (fmt . Sprintf ( "Waiting for PV %s to be expanded to %s" , pvc . Spec . VolumeName , test . recoverySize . String ()) )
572
+ err = testsuites .WaitForRecoveryPVSize ( pvc , m .cs , csiResizeWaitPeriod )
525
573
framework .ExpectNoError (err , "While waiting for PV resize to finish" )
526
574
527
- ginkgo .By ("Waiting for PVC resize to finish" )
575
+ ginkgo .By (fmt . Sprintf ( "Waiting for PVC %s to be expanded to %s" , pvc . Name , test . recoverySize . String ()) )
528
576
pvc , err = testsuites .WaitForFSResize (ctx , pvc , m .cs )
529
577
framework .ExpectNoError (err , "while waiting for PVC to finish" )
530
578
@@ -561,6 +609,31 @@ func waitForResizeStatus(ctx context.Context, pvc *v1.PersistentVolumeClaim, c c
561
609
return nil
562
610
}
563
611
612
+ func waitForResizeToFailOnNode (ctx context.Context , pvc * v1.PersistentVolumeClaim , c clientset.Interface ) error {
613
+ var finalConditions []v1.PersistentVolumeClaimCondition
614
+ waitErr := wait .PollUntilContextTimeout (ctx , resizePollInterval , csiResizeWaitPeriod , true , func (pollContext context.Context ) (bool , error ) {
615
+ var err error
616
+ updatedPVC , err := c .CoreV1 ().PersistentVolumeClaims (pvc .Namespace ).Get (pollContext , pvc .Name , metav1.GetOptions {})
617
+
618
+ if err != nil {
619
+ return false , fmt .Errorf ("error fetching pvc %q for checking for resize status: %w" , pvc .Name , err )
620
+ }
621
+ pvcConditions := updatedPVC .Status .Conditions
622
+ for _ , cond := range pvcConditions {
623
+ if cond .Type == v1 .PersistentVolumeClaimNodeResizeError {
624
+ return true , nil
625
+ }
626
+ }
627
+ finalConditions = pvcConditions
628
+ return false , nil
629
+ })
630
+
631
+ if waitErr != nil {
632
+ return fmt .Errorf ("error while waiting for resize condition sync to NodeResizeError, actualStatus %+v: %w" , finalConditions , waitErr )
633
+ }
634
+ return nil
635
+ }
636
+
564
637
func waitForAllocatedResource (ctx context.Context , pvc * v1.PersistentVolumeClaim , m * mockDriverSetup , expectedSize string ) error {
565
638
expectedQuantity := resource .MustParse (expectedSize )
566
639
waitErr := wait .PollUntilContextTimeout (ctx , resizePollInterval , csiResizeWaitPeriod , true , func (pollContext context.Context ) (bool , error ) {
@@ -596,15 +669,24 @@ func createExpansionHook(expectedExpansionStatus expansionStatus) *drivers.Hooks
596
669
}
597
670
598
671
}
599
- case expansionFailedOnController :
672
+ case expansionFailedOnControllerWithInfeasibleError :
600
673
expansionRequest , ok := request .(* csipbv1.ControllerExpandVolumeRequest )
601
674
if ok {
602
675
requestedSize := resource .NewQuantity (expansionRequest .CapacityRange .RequiredBytes , resource .BinarySI )
603
676
if requestedSize .Cmp (maxControllerSizeLimit ) > 0 {
604
677
return nil , status .Error (codes .InvalidArgument , "invalid expansion request" )
605
678
}
606
679
}
607
- case expansionFailedOnNode :
680
+ case expansionFailedOnControllerWithFinalError :
681
+ // This simulates a condition that a final, but not infeasible error is returned when expansion fails in the controller.
682
+ expansionRequest , ok := request .(* csipbv1.ControllerExpandVolumeRequest )
683
+ if ok {
684
+ requestedSize := resource .NewQuantity (expansionRequest .CapacityRange .RequiredBytes , resource .BinarySI )
685
+ if requestedSize .Cmp (maxControllerSizeLimit ) > 0 {
686
+ return nil , status .Error (codes .PermissionDenied , "permission denied for expansion" )
687
+ }
688
+ }
689
+ case expansionFailedOnNodeWithInfeasibleError :
608
690
expansionRequest , ok := request .(* csipbv1.NodeExpandVolumeRequest )
609
691
if ok {
610
692
requestedSize := resource .NewQuantity (expansionRequest .CapacityRange .RequiredBytes , resource .BinarySI )
@@ -613,6 +695,14 @@ func createExpansionHook(expectedExpansionStatus expansionStatus) *drivers.Hooks
613
695
}
614
696
615
697
}
698
+ case expansionFailedOnNodeWithFinalError :
699
+ expansionRequest , ok := request .(* csipbv1.NodeExpandVolumeRequest )
700
+ if ok {
701
+ requestedSize := resource .NewQuantity (expansionRequest .CapacityRange .RequiredBytes , resource .BinarySI )
702
+ if requestedSize .Cmp (maxNodeExpansionLimit ) > 0 {
703
+ return nil , status .Error (codes .PermissionDenied , "permission denied for expansion" )
704
+ }
705
+ }
616
706
}
617
707
618
708
return nil , nil
0 commit comments