k8s.io/kubernetes@v1.29.3/pkg/volume/util/operationexecutor/node_expander_test.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 operationexecutor 18 19 import ( 20 "testing" 21 22 v1 "k8s.io/api/core/v1" 23 "k8s.io/apimachinery/pkg/api/resource" 24 utilfeature "k8s.io/apiserver/pkg/util/feature" 25 featuregatetesting "k8s.io/component-base/featuregate/testing" 26 "k8s.io/kubernetes/pkg/features" 27 "k8s.io/kubernetes/pkg/volume" 28 volumetesting "k8s.io/kubernetes/pkg/volume/testing" 29 ) 30 31 func TestNodeExpander(t *testing.T) { 32 nodeResizeFailed := v1.PersistentVolumeClaimNodeResizeFailed 33 34 nodeResizePending := v1.PersistentVolumeClaimNodeResizePending 35 var tests = []struct { 36 name string 37 pvc *v1.PersistentVolumeClaim 38 pv *v1.PersistentVolume 39 40 // desired size, defaults to pv.Spec.Capacity 41 desiredSize *resource.Quantity 42 // actualSize, defaults to pvc.Status.Capacity 43 actualSize *resource.Quantity 44 45 // expectations of test 46 expectedResizeStatus v1.ClaimResourceStatus 47 expectedStatusSize resource.Quantity 48 expectResizeCall bool 49 assumeResizeOpAsFinished bool 50 expectError bool 51 }{ 52 { 53 name: "pv.spec.cap > pvc.status.cap, resizeStatus=node_expansion_failed", 54 pvc: getTestPVC("test-vol0", "2G", "1G", "", &nodeResizeFailed), 55 pv: getTestPV("test-vol0", "2G"), 56 57 expectedResizeStatus: nodeResizeFailed, 58 expectResizeCall: false, 59 assumeResizeOpAsFinished: true, 60 expectedStatusSize: resource.MustParse("1G"), 61 }, 62 { 63 name: "pv.spec.cap > pvc.status.cap, resizeStatus=node_expansion_pending", 64 pvc: getTestPVC("test-vol0", "2G", "1G", "2G", &nodeResizePending), 65 pv: getTestPV("test-vol0", "2G"), 66 expectedResizeStatus: "", 67 expectResizeCall: true, 68 assumeResizeOpAsFinished: true, 69 expectedStatusSize: resource.MustParse("2G"), 70 }, 71 { 72 name: "pv.spec.cap > pvc.status.cap, resizeStatus=node_expansion_pending, reize_op=failing", 73 pvc: getTestPVC(volumetesting.AlwaysFailNodeExpansion, "2G", "1G", "2G", &nodeResizePending), 74 pv: getTestPV(volumetesting.AlwaysFailNodeExpansion, "2G"), 75 expectError: true, 76 expectedResizeStatus: nodeResizeFailed, 77 expectResizeCall: true, 78 assumeResizeOpAsFinished: true, 79 expectedStatusSize: resource.MustParse("1G"), 80 }, 81 { 82 name: "pv.spec.cap = pvc.status.cap, resizeStatus='', desiredSize > actualSize", 83 pvc: getTestPVC("test-vol0", "2G", "2G", "2G", nil), 84 pv: getTestPV("test-vol0", "2G"), 85 86 expectedResizeStatus: "", 87 expectResizeCall: true, 88 assumeResizeOpAsFinished: true, 89 expectedStatusSize: resource.MustParse("2G"), 90 }, 91 } 92 93 for i := range tests { 94 test := tests[i] 95 t.Run(test.name, func(t *testing.T) { 96 defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.RecoverVolumeExpansionFailure, true)() 97 volumePluginMgr, fakePlugin := volumetesting.GetTestKubeletVolumePluginMgr(t) 98 99 pvc := test.pvc 100 pv := test.pv 101 pod := getTestPod("test-pod", pvc.Name) 102 og := getTestOperationGenerator(volumePluginMgr, pvc, pv) 103 104 vmt := VolumeToMount{ 105 Pod: pod, 106 VolumeName: v1.UniqueVolumeName(pv.Name), 107 VolumeSpec: volume.NewSpecFromPersistentVolume(pv, false), 108 } 109 desiredSize := test.desiredSize 110 if desiredSize == nil { 111 desiredSize = pv.Spec.Capacity.Storage() 112 } 113 actualSize := test.actualSize 114 if actualSize == nil { 115 actualSize = pvc.Status.Capacity.Storage() 116 } 117 resizeOp := nodeResizeOperationOpts{ 118 pvc: pvc, 119 pv: pv, 120 volumePlugin: fakePlugin, 121 vmt: vmt, 122 actualStateOfWorld: nil, 123 pluginResizeOpts: volume.NodeResizeOptions{ 124 VolumeSpec: vmt.VolumeSpec, 125 NewSize: *desiredSize, 126 OldSize: *actualSize, 127 }, 128 } 129 ogInstance, _ := og.(*operationGenerator) 130 nodeExpander := newNodeExpander(resizeOp, ogInstance.kubeClient, ogInstance.recorder) 131 132 _, err, expansionResponse := nodeExpander.expandOnPlugin() 133 134 pvc = nodeExpander.pvc 135 pvcStatusCap := pvc.Status.Capacity[v1.ResourceStorage] 136 137 if !test.expectError && err != nil { 138 t.Errorf("For test %s, expected no error got: %v", test.name, err) 139 } 140 if test.expectError && err == nil { 141 t.Errorf("For test %s, expected error but got none", test.name) 142 } 143 144 if test.expectResizeCall != expansionResponse.resizeCalledOnPlugin { 145 t.Errorf("For test %s, expected resize called %t, got %t", test.name, test.expectResizeCall, expansionResponse.resizeCalledOnPlugin) 146 } 147 if test.assumeResizeOpAsFinished != expansionResponse.assumeResizeFinished { 148 t.Errorf("For test %s, expected assumeResizeOpAsFinished %t, got %t", test.name, test.assumeResizeOpAsFinished, expansionResponse.assumeResizeFinished) 149 } 150 allocatedResourceStatus := pvc.Status.AllocatedResourceStatuses 151 resizeStatus := allocatedResourceStatus[v1.ResourceStorage] 152 153 if test.expectedResizeStatus != resizeStatus { 154 t.Errorf("For test %s, expected resizeStatus %v, got %v", test.name, test.expectedResizeStatus, resizeStatus) 155 } 156 if pvcStatusCap.Cmp(test.expectedStatusSize) != 0 { 157 t.Errorf("For test %s, expected status size %s, got %s", test.name, test.expectedStatusSize.String(), pvcStatusCap.String()) 158 } 159 }) 160 } 161 }