k8s.io/kubernetes@v1.29.3/test/e2e/storage/csi_mock/csi_volume_expansion.go (about) 1 /* 2 Copyright 2022 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package csi_mock 18 19 import ( 20 "context" 21 "fmt" 22 "time" 23 24 csipbv1 "github.com/container-storage-interface/spec/lib/go/csi" 25 "github.com/onsi/ginkgo/v2" 26 "github.com/onsi/gomega" 27 "google.golang.org/grpc/codes" 28 "google.golang.org/grpc/status" 29 v1 "k8s.io/api/core/v1" 30 "k8s.io/apimachinery/pkg/api/resource" 31 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 32 "k8s.io/apimachinery/pkg/util/wait" 33 clientset "k8s.io/client-go/kubernetes" 34 "k8s.io/kubernetes/test/e2e/feature" 35 "k8s.io/kubernetes/test/e2e/framework" 36 e2epod "k8s.io/kubernetes/test/e2e/framework/pod" 37 "k8s.io/kubernetes/test/e2e/storage/drivers" 38 "k8s.io/kubernetes/test/e2e/storage/testsuites" 39 "k8s.io/kubernetes/test/e2e/storage/utils" 40 admissionapi "k8s.io/pod-security-admission/api" 41 ) 42 43 type expansionStatus int 44 45 const ( 46 expansionSuccess = iota 47 expansionFailedOnController 48 expansionFailedOnNode 49 expansionFailedMissingStagingPath 50 ) 51 52 const ( 53 resizePollInterval = 2 * time.Second 54 ) 55 56 var ( 57 maxControllerSizeLimit = resource.MustParse("10Gi") 58 59 maxNodeExpansionLimit = resource.MustParse("8Gi") 60 ) 61 62 type recoveryTest struct { 63 name string 64 pvcRequestSize string 65 allocatedResource string 66 simulatedCSIDriverError expansionStatus 67 expectedResizeStatus v1.ClaimResourceStatus 68 recoverySize resource.Quantity 69 } 70 71 var _ = utils.SIGDescribe("CSI Mock volume expansion", func() { 72 f := framework.NewDefaultFramework("csi-mock-volumes-expansion") 73 f.NamespacePodSecurityLevel = admissionapi.LevelPrivileged 74 m := newMockDriverSetup(f) 75 76 ginkgo.Context("CSI Volume expansion", func() { 77 tests := []struct { 78 name string 79 nodeExpansionRequired bool 80 disableAttach bool 81 disableResizingOnDriver bool 82 simulatedCSIDriverError expansionStatus 83 expectFailure bool 84 }{ 85 { 86 name: "should expand volume without restarting pod if nodeExpansion=off", 87 nodeExpansionRequired: false, 88 simulatedCSIDriverError: expansionSuccess, 89 }, 90 { 91 name: "should expand volume by restarting pod if attach=on, nodeExpansion=on", 92 nodeExpansionRequired: true, 93 simulatedCSIDriverError: expansionSuccess, 94 }, 95 { 96 name: "should not have staging_path missing in node expand volume pod if attach=on, nodeExpansion=on", 97 nodeExpansionRequired: true, 98 simulatedCSIDriverError: expansionFailedMissingStagingPath, 99 }, 100 { 101 name: "should expand volume by restarting pod if attach=off, nodeExpansion=on", 102 disableAttach: true, 103 nodeExpansionRequired: true, 104 simulatedCSIDriverError: expansionSuccess, 105 }, 106 { 107 name: "should not expand volume if resizingOnDriver=off, resizingOnSC=on", 108 disableResizingOnDriver: true, 109 expectFailure: true, 110 simulatedCSIDriverError: expansionSuccess, 111 }, 112 } 113 for _, t := range tests { 114 test := t 115 ginkgo.It(t.name, func(ctx context.Context) { 116 var err error 117 tp := testParameters{ 118 enableResizing: true, 119 enableNodeExpansion: test.nodeExpansionRequired, 120 disableResizingOnDriver: test.disableResizingOnDriver, 121 } 122 // disabling attach requires drive registration feature 123 if test.disableAttach { 124 tp.disableAttach = true 125 tp.registerDriver = true 126 } 127 tp.hooks = createExpansionHook(test.simulatedCSIDriverError) 128 129 m.init(ctx, tp) 130 ginkgo.DeferCleanup(m.cleanup) 131 132 sc, pvc, pod := m.createPod(ctx, pvcReference) 133 gomega.Expect(pod).NotTo(gomega.BeNil(), "while creating pod for resizing") 134 135 if !*sc.AllowVolumeExpansion { 136 framework.Fail("failed creating sc with allowed expansion") 137 } 138 139 err = e2epod.WaitForPodNameRunningInNamespace(ctx, m.cs, pod.Name, pod.Namespace) 140 framework.ExpectNoError(err, "Failed to start pod1: %v", err) 141 142 ginkgo.By("Expanding current pvc") 143 newSize := resource.MustParse("6Gi") 144 newPVC, err := testsuites.ExpandPVCSize(ctx, pvc, newSize, m.cs) 145 framework.ExpectNoError(err, "While updating pvc for more size") 146 pvc = newPVC 147 gomega.Expect(pvc).NotTo(gomega.BeNil()) 148 149 pvcSize := pvc.Spec.Resources.Requests[v1.ResourceStorage] 150 if pvcSize.Cmp(newSize) != 0 { 151 framework.Failf("error updating pvc size %q", pvc.Name) 152 } 153 if test.expectFailure { 154 err = testsuites.WaitForResizingCondition(ctx, pvc, m.cs, csiResizingConditionWait) 155 framework.ExpectError(err, "unexpected resizing condition on PVC") 156 return 157 } 158 159 ginkgo.By("Waiting for persistent volume resize to finish") 160 err = testsuites.WaitForControllerVolumeResize(ctx, pvc, m.cs, csiResizeWaitPeriod) 161 framework.ExpectNoError(err, "While waiting for CSI PV resize to finish") 162 163 checkPVCSize := func() { 164 ginkgo.By("Waiting for PVC resize to finish") 165 pvc, err = testsuites.WaitForFSResize(ctx, pvc, m.cs) 166 framework.ExpectNoError(err, "while waiting for PVC resize to finish") 167 168 pvcConditions := pvc.Status.Conditions 169 gomega.Expect(pvcConditions).To(gomega.BeEmpty(), "pvc should not have conditions") 170 } 171 172 // if node expansion is not required PVC should be resized as well 173 if !test.nodeExpansionRequired { 174 checkPVCSize() 175 } else { 176 ginkgo.By("Checking for conditions on pvc") 177 npvc, err := testsuites.WaitForPendingFSResizeCondition(ctx, pvc, m.cs) 178 framework.ExpectNoError(err, "While waiting for pvc to have fs resizing condition") 179 pvc = npvc 180 181 inProgressConditions := pvc.Status.Conditions 182 if len(inProgressConditions) > 0 { 183 gomega.Expect(inProgressConditions[0].Type).To(gomega.Equal(v1.PersistentVolumeClaimFileSystemResizePending), "pvc must have fs resizing condition") 184 } 185 186 ginkgo.By("Deleting the previously created pod") 187 if test.simulatedCSIDriverError == expansionFailedMissingStagingPath { 188 e2epod.DeletePodOrFail(ctx, m.cs, pod.Namespace, pod.Name) 189 } else { 190 err = e2epod.DeletePodWithWait(ctx, m.cs, pod) 191 framework.ExpectNoError(err, "while deleting pod for resizing") 192 } 193 194 ginkgo.By("Creating a new pod with same volume") 195 pod2, err := m.createPodWithPVC(pvc) 196 gomega.Expect(pod2).NotTo(gomega.BeNil(), "while creating pod for csi resizing") 197 framework.ExpectNoError(err, "while recreating pod for resizing") 198 199 checkPVCSize() 200 } 201 }) 202 } 203 }) 204 ginkgo.Context("CSI online volume expansion with secret", func() { 205 var stringSecret = map[string]string{ 206 "username": "admin", 207 "password": "t0p-Secret", 208 } 209 trackedCalls := []string{ 210 "NodeExpandVolume", 211 } 212 tests := []struct { 213 name string 214 disableAttach bool 215 expectedCalls []csiCall 216 217 // Called for each NodeExpandVolume calls, with counter incremented atomically before 218 // the invocation (i.e first value will be 1). 219 nodeExpandHook func(counter int64) error 220 }{ 221 { 222 name: "should expand volume without restarting pod if attach=on, nodeExpansion=on, csiNodeExpandSecret=on", 223 expectedCalls: []csiCall{ 224 {expectedMethod: "NodeExpandVolume", expectedError: codes.OK, expectedSecret: stringSecret}, 225 }, 226 }, 227 } 228 for _, t := range tests { 229 test := t 230 ginkgo.It(test.name, func(ctx context.Context) { 231 var ( 232 err error 233 hooks *drivers.Hooks 234 secretName = "test-secret" 235 secret = &v1.Secret{ 236 ObjectMeta: metav1.ObjectMeta{ 237 Namespace: f.Namespace.Name, 238 Name: secretName, 239 }, 240 StringData: stringSecret, 241 } 242 ) 243 if test.nodeExpandHook != nil { 244 hooks = createPreHook("NodeExpandVolume", test.nodeExpandHook) 245 } 246 params := testParameters{enableResizing: true, enableNodeExpansion: true, enableCSINodeExpandSecret: true, hooks: hooks} 247 if test.disableAttach { 248 params.disableAttach = true 249 params.registerDriver = true 250 } 251 252 m.init(ctx, params) 253 ginkgo.DeferCleanup(m.cleanup) 254 255 if secret, err := m.cs.CoreV1().Secrets(f.Namespace.Name).Create(context.TODO(), secret, metav1.CreateOptions{}); err != nil { 256 framework.Failf("unable to create test secret %s: %v", secret.Name, err) 257 } 258 259 sc, pvc, pod := m.createPod(ctx, pvcReference) 260 gomega.Expect(pod).NotTo(gomega.BeNil(), "while creating pod for resizing") 261 262 if !*sc.AllowVolumeExpansion { 263 framework.Fail("failed creating sc with allowed expansion") 264 } 265 if sc.Parameters == nil { 266 framework.Fail("failed creating sc with secret") 267 } 268 if _, ok := sc.Parameters[csiNodeExpandSecretKey]; !ok { 269 framework.Failf("creating sc without %s", csiNodeExpandSecretKey) 270 } 271 if _, ok := sc.Parameters[csiNodeExpandSecretNamespaceKey]; !ok { 272 framework.Failf("creating sc without %s", csiNodeExpandSecretNamespaceKey) 273 } 274 err = e2epod.WaitForPodNameRunningInNamespace(ctx, m.cs, pod.Name, pod.Namespace) 275 framework.ExpectNoError(err, "Failed to start pod1: %v", err) 276 277 pvc, err = m.cs.CoreV1().PersistentVolumeClaims(pvc.Namespace).Get(ctx, pvc.Name, metav1.GetOptions{}) 278 if err != nil { 279 framework.Failf("failed to get pvc %s, %v", pvc.Name, err) 280 } 281 gomega.Expect(pvc.Spec.VolumeName).ShouldNot(gomega.BeEquivalentTo(""), "while provisioning a volume for resizing") 282 pv, err := m.cs.CoreV1().PersistentVolumes().Get(ctx, pvc.Spec.VolumeName, metav1.GetOptions{}) 283 if err != nil { 284 framework.Failf("failed to get pv %s, %v", pvc.Spec.VolumeName, err) 285 } 286 if pv.Spec.CSI == nil || pv.Spec.CSI.NodeExpandSecretRef == nil { 287 framework.Fail("creating pv without 'NodeExpandSecretRef'") 288 } 289 if pv.Spec.CSI.NodeExpandSecretRef.Namespace != f.Namespace.Name || pv.Spec.CSI.NodeExpandSecretRef.Name != secretName { 290 framework.Failf("failed to set node expand secret ref, namespace: %s name: %s", pv.Spec.CSI.NodeExpandSecretRef.Namespace, pv.Spec.CSI.NodeExpandSecretRef.Name) 291 } 292 293 ginkgo.By("Expanding current pvc") 294 newSize := resource.MustParse("6Gi") 295 newPVC, err := testsuites.ExpandPVCSize(ctx, pvc, newSize, m.cs) 296 framework.ExpectNoError(err, "While updating pvc for more size") 297 pvc = newPVC 298 gomega.Expect(pvc).NotTo(gomega.BeNil()) 299 300 pvcSize := pvc.Spec.Resources.Requests[v1.ResourceStorage] 301 if pvcSize.Cmp(newSize) != 0 { 302 framework.Failf("error updating pvc size %q", pvc.Name) 303 } 304 305 ginkgo.By("Waiting for persistent volume resize to finish") 306 err = testsuites.WaitForControllerVolumeResize(ctx, pvc, m.cs, csiResizeWaitPeriod) 307 framework.ExpectNoError(err, "While waiting for PV resize to finish") 308 309 ginkgo.By("Waiting for PVC resize to finish") 310 pvc, err = testsuites.WaitForFSResize(ctx, pvc, m.cs) 311 framework.ExpectNoError(err, "while waiting for PVC to finish") 312 313 ginkgo.By("Waiting for all remaining expected CSI calls") 314 err = wait.Poll(time.Second, csiResizeWaitPeriod, func() (done bool, err error) { 315 var index int 316 _, index, err = compareCSICalls(ctx, trackedCalls, test.expectedCalls, m.driver.GetCalls) 317 if err != nil { 318 return true, err 319 } 320 if index == 0 { 321 // No CSI call received yet 322 return false, nil 323 } 324 if len(test.expectedCalls) == index { 325 // all calls received 326 return true, nil 327 } 328 return false, nil 329 }) 330 framework.ExpectNoError(err, "while waiting for all CSI calls") 331 332 pvcConditions := pvc.Status.Conditions 333 gomega.Expect(pvcConditions).To(gomega.BeEmpty(), "pvc should not have conditions") 334 }) 335 } 336 }) 337 ginkgo.Context("CSI online volume expansion", func() { 338 tests := []struct { 339 name string 340 disableAttach bool 341 }{ 342 { 343 name: "should expand volume without restarting pod if attach=on, nodeExpansion=on", 344 }, 345 { 346 name: "should expand volume without restarting pod if attach=off, nodeExpansion=on", 347 disableAttach: true, 348 }, 349 } 350 for _, t := range tests { 351 test := t 352 ginkgo.It(test.name, func(ctx context.Context) { 353 var err error 354 params := testParameters{enableResizing: true, enableNodeExpansion: true} 355 if test.disableAttach { 356 params.disableAttach = true 357 params.registerDriver = true 358 } 359 360 m.init(ctx, params) 361 ginkgo.DeferCleanup(m.cleanup) 362 363 sc, pvc, pod := m.createPod(ctx, pvcReference) 364 gomega.Expect(pod).NotTo(gomega.BeNil(), "while creating pod for resizing") 365 366 if !*sc.AllowVolumeExpansion { 367 framework.Fail("failed creating sc with allowed expansion") 368 } 369 370 err = e2epod.WaitForPodNameRunningInNamespace(ctx, m.cs, pod.Name, pod.Namespace) 371 framework.ExpectNoError(err, "Failed to start pod1: %v", err) 372 373 ginkgo.By("Expanding current pvc") 374 newSize := resource.MustParse("6Gi") 375 newPVC, err := testsuites.ExpandPVCSize(ctx, pvc, newSize, m.cs) 376 framework.ExpectNoError(err, "While updating pvc for more size") 377 pvc = newPVC 378 gomega.Expect(pvc).NotTo(gomega.BeNil()) 379 380 pvcSize := pvc.Spec.Resources.Requests[v1.ResourceStorage] 381 if pvcSize.Cmp(newSize) != 0 { 382 framework.Failf("error updating pvc size %q", pvc.Name) 383 } 384 385 ginkgo.By("Waiting for persistent volume resize to finish") 386 err = testsuites.WaitForControllerVolumeResize(ctx, pvc, m.cs, csiResizeWaitPeriod) 387 framework.ExpectNoError(err, "While waiting for PV resize to finish") 388 389 ginkgo.By("Waiting for PVC resize to finish") 390 pvc, err = testsuites.WaitForFSResize(ctx, pvc, m.cs) 391 framework.ExpectNoError(err, "while waiting for PVC to finish") 392 393 pvcConditions := pvc.Status.Conditions 394 gomega.Expect(pvcConditions).To(gomega.BeEmpty(), "pvc should not have conditions") 395 396 }) 397 } 398 }) 399 400 f.Context("Expansion with recovery", feature.RecoverVolumeExpansionFailure, func() { 401 tests := []recoveryTest{ 402 { 403 name: "should record target size in allocated resources", 404 pvcRequestSize: "4Gi", 405 allocatedResource: "4Gi", 406 simulatedCSIDriverError: expansionSuccess, 407 expectedResizeStatus: "", 408 }, 409 { 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.PersistentVolumeClaimControllerResizeFailed, 415 recoverySize: resource.MustParse("4Gi"), 416 }, 417 { 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.PersistentVolumeClaimNodeResizeFailed, 423 recoverySize: resource.MustParse("5Gi"), 424 }, 425 } 426 427 for _, t := range tests { 428 test := t 429 ginkgo.It(test.name, func(ctx context.Context) { 430 var err error 431 params := testParameters{enableResizing: true, enableNodeExpansion: true, enableRecoverExpansionFailure: true} 432 433 if test.simulatedCSIDriverError != expansionSuccess { 434 params.hooks = createExpansionHook(test.simulatedCSIDriverError) 435 } 436 437 m.init(ctx, params) 438 ginkgo.DeferCleanup(m.cleanup) 439 440 sc, pvc, pod := m.createPod(ctx, pvcReference) 441 gomega.Expect(pod).NotTo(gomega.BeNil(), "while creating pod for resizing") 442 443 if !*sc.AllowVolumeExpansion { 444 framework.Fail("failed creating sc with allowed expansion") 445 } 446 447 err = e2epod.WaitForPodNameRunningInNamespace(ctx, m.cs, pod.Name, pod.Namespace) 448 framework.ExpectNoError(err, "Failed to start pod1: %v", err) 449 450 ginkgo.By("Expanding current pvc") 451 newSize := resource.MustParse(test.pvcRequestSize) 452 newPVC, err := testsuites.ExpandPVCSize(ctx, pvc, newSize, m.cs) 453 framework.ExpectNoError(err, "While updating pvc for more size") 454 pvc = newPVC 455 gomega.Expect(pvc).NotTo(gomega.BeNil()) 456 457 pvcSize := pvc.Spec.Resources.Requests[v1.ResourceStorage] 458 if pvcSize.Cmp(newSize) != 0 { 459 framework.Failf("error updating pvc size %q", pvc.Name) 460 } 461 462 if test.simulatedCSIDriverError == expansionSuccess { 463 validateExpansionSuccess(ctx, pvc, m, test, test.allocatedResource) 464 } else { 465 validateRecoveryBehaviour(ctx, pvc, m, test) 466 } 467 }) 468 } 469 470 }) 471 }) 472 473 func validateRecoveryBehaviour(ctx context.Context, pvc *v1.PersistentVolumeClaim, m *mockDriverSetup, test recoveryTest) { 474 var err error 475 ginkgo.By("Waiting for resizer to set allocated resource") 476 err = waitForAllocatedResource(pvc, m, test.allocatedResource) 477 framework.ExpectNoError(err, "While waiting for allocated resource to be updated") 478 479 ginkgo.By("Waiting for resizer to set resize status") 480 err = waitForResizeStatus(pvc, m.cs, test.expectedResizeStatus) 481 framework.ExpectNoError(err, "While waiting for resize status to be set") 482 483 ginkgo.By("Recover pvc size") 484 newPVC, err := testsuites.ExpandPVCSize(ctx, pvc, test.recoverySize, m.cs) 485 framework.ExpectNoError(err, "While updating pvc for more size") 486 pvc = newPVC 487 gomega.Expect(pvc).NotTo(gomega.BeNil()) 488 489 pvcSize := pvc.Spec.Resources.Requests[v1.ResourceStorage] 490 if pvcSize.Cmp(test.recoverySize) != 0 { 491 framework.Failf("error updating pvc size %q", pvc.Name) 492 } 493 494 // if expansion failed on controller with final error, then recovery should be possible 495 if test.simulatedCSIDriverError == expansionFailedOnController { 496 validateExpansionSuccess(ctx, pvc, m, test, test.recoverySize.String()) 497 return 498 } 499 500 // if expansion succeeded on controller but failed on the node 501 if test.simulatedCSIDriverError == expansionFailedOnNode { 502 ginkgo.By("Wait for expansion to fail on node again") 503 err = waitForResizeStatus(pvc, m.cs, v1.PersistentVolumeClaimNodeResizeFailed) 504 framework.ExpectNoError(err, "While waiting for resize status to be set to expansion-failed-on-node") 505 506 ginkgo.By("verify allocated resources after recovery") 507 pvc, err = m.cs.CoreV1().PersistentVolumeClaims(pvc.Namespace).Get(context.TODO(), pvc.Name, metav1.GetOptions{}) 508 framework.ExpectNoError(err, "while fetching pvc") 509 actualAllocatedResource := pvc.Status.AllocatedResources.Storage() 510 511 if actualAllocatedResource.Equal(test.recoverySize) { 512 framework.Failf("unexpected allocated resource size %s after node expansion failure", actualAllocatedResource.String()) 513 } 514 515 if !actualAllocatedResource.Equal(resource.MustParse(test.allocatedResource)) { 516 framework.Failf("expected allocated resources to be %s got %s", test.allocatedResource, actualAllocatedResource.String()) 517 } 518 } 519 } 520 521 func validateExpansionSuccess(ctx context.Context, pvc *v1.PersistentVolumeClaim, m *mockDriverSetup, test recoveryTest, expectedAllocatedSize string) { 522 var err error 523 ginkgo.By("Waiting for persistent volume resize to finish") 524 err = testsuites.WaitForControllerVolumeResize(ctx, pvc, m.cs, csiResizeWaitPeriod) 525 framework.ExpectNoError(err, "While waiting for PV resize to finish") 526 527 ginkgo.By("Waiting for PVC resize to finish") 528 pvc, err = testsuites.WaitForFSResize(ctx, pvc, m.cs) 529 framework.ExpectNoError(err, "while waiting for PVC to finish") 530 531 pvcConditions := pvc.Status.Conditions 532 gomega.Expect(pvcConditions).To(gomega.BeEmpty(), "pvc should not have conditions") 533 allocatedResource := pvc.Status.AllocatedResources.Storage() 534 gomega.Expect(allocatedResource).NotTo(gomega.BeNil()) 535 expectedAllocatedResource := resource.MustParse(expectedAllocatedSize) 536 if allocatedResource.Cmp(expectedAllocatedResource) != 0 { 537 framework.Failf("expected allocated Resources to be %s got %s", expectedAllocatedResource.String(), allocatedResource.String()) 538 } 539 540 resizeStatus := pvc.Status.AllocatedResourceStatuses[v1.ResourceStorage] 541 gomega.Expect(resizeStatus).To(gomega.BeZero(), "resize status should be empty") 542 } 543 544 func waitForResizeStatus(pvc *v1.PersistentVolumeClaim, c clientset.Interface, expectedState v1.ClaimResourceStatus) error { 545 var actualResizeStatus *v1.ClaimResourceStatus 546 547 waitErr := wait.PollImmediate(resizePollInterval, csiResizeWaitPeriod, func() (bool, error) { 548 var err error 549 updatedPVC, err := c.CoreV1().PersistentVolumeClaims(pvc.Namespace).Get(context.TODO(), pvc.Name, metav1.GetOptions{}) 550 551 if err != nil { 552 return false, fmt.Errorf("error fetching pvc %q for checking for resize status: %w", pvc.Name, err) 553 } 554 555 actualResizeStatus := updatedPVC.Status.AllocatedResourceStatuses[v1.ResourceStorage] 556 return (actualResizeStatus == expectedState), nil 557 }) 558 if waitErr != nil { 559 return fmt.Errorf("error while waiting for resize status to sync to %v, actualStatus %s: %v", expectedState, *actualResizeStatus, waitErr) 560 } 561 return nil 562 } 563 564 func waitForAllocatedResource(pvc *v1.PersistentVolumeClaim, m *mockDriverSetup, expectedSize string) error { 565 expectedQuantity := resource.MustParse(expectedSize) 566 waitErr := wait.PollImmediate(resizePollInterval, csiResizeWaitPeriod, func() (bool, error) { 567 var err error 568 updatedPVC, err := m.cs.CoreV1().PersistentVolumeClaims(pvc.Namespace).Get(context.TODO(), pvc.Name, metav1.GetOptions{}) 569 570 if err != nil { 571 return false, fmt.Errorf("error fetching pvc %q for checking for resize status: %w", pvc.Name, err) 572 } 573 actualAllocatedSize := updatedPVC.Status.AllocatedResources.Storage() 574 if actualAllocatedSize != nil && actualAllocatedSize.Equal(expectedQuantity) { 575 return true, nil 576 } 577 return false, nil 578 579 }) 580 if waitErr != nil { 581 return fmt.Errorf("error while waiting for allocatedSize to sync to %s: %v", expectedSize, waitErr) 582 } 583 return nil 584 } 585 586 func createExpansionHook(expectedExpansionStatus expansionStatus) *drivers.Hooks { 587 return &drivers.Hooks{ 588 Pre: func(ctx context.Context, method string, request interface{}) (reply interface{}, err error) { 589 switch expectedExpansionStatus { 590 case expansionFailedMissingStagingPath: 591 expansionRequest, ok := request.(*csipbv1.NodeExpandVolumeRequest) 592 if ok { 593 stagingPath := expansionRequest.StagingTargetPath 594 if stagingPath == "" { 595 return nil, status.Error(codes.InvalidArgument, "invalid node expansion request, missing staging path") 596 } 597 598 } 599 case expansionFailedOnController: 600 expansionRequest, ok := request.(*csipbv1.ControllerExpandVolumeRequest) 601 if ok { 602 requestedSize := resource.NewQuantity(expansionRequest.CapacityRange.RequiredBytes, resource.BinarySI) 603 if requestedSize.Cmp(maxControllerSizeLimit) > 0 { 604 return nil, status.Error(codes.InvalidArgument, "invalid expansion request") 605 } 606 } 607 case expansionFailedOnNode: 608 expansionRequest, ok := request.(*csipbv1.NodeExpandVolumeRequest) 609 if ok { 610 requestedSize := resource.NewQuantity(expansionRequest.CapacityRange.RequiredBytes, resource.BinarySI) 611 if requestedSize.Cmp(maxNodeExpansionLimit) > 0 { 612 return nil, status.Error(codes.InvalidArgument, "invalid node expansion request") 613 } 614 615 } 616 } 617 618 return nil, nil 619 }, 620 } 621 }