k8s.io/kubernetes@v1.29.3/pkg/controller/volume/attachdetach/util/util_test.go (about) 1 /* 2 Copyright 2017 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 util 18 19 import ( 20 "os" 21 "reflect" 22 "strings" 23 "testing" 24 "time" 25 26 "github.com/google/go-cmp/cmp" 27 28 v1 "k8s.io/api/core/v1" 29 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 30 kubetypes "k8s.io/apimachinery/pkg/types" 31 "k8s.io/client-go/informers" 32 csitrans "k8s.io/csi-translation-lib" 33 "k8s.io/klog/v2/ktesting" 34 tf "k8s.io/kubernetes/pkg/scheduler/testing/framework" 35 "k8s.io/kubernetes/pkg/volume/csimigration" 36 "k8s.io/kubernetes/pkg/volume/fc" 37 38 utilfeature "k8s.io/apiserver/pkg/util/feature" 39 "k8s.io/client-go/kubernetes/fake" 40 utiltesting "k8s.io/client-go/util/testing" 41 "k8s.io/kubernetes/pkg/volume" 42 volumetest "k8s.io/kubernetes/pkg/volume/testing" 43 ) 44 45 const ( 46 testHostName = "test-hostname" 47 socketPath = "/var/run/kmsplugin" 48 migratedVolume = "migrated-volume-name" 49 nonMigratedVolume = "non-migrated-volume-name" 50 testNodeName = "test-node-name" 51 ) 52 53 var ( 54 dirOrCreate = v1.HostPathType(v1.HostPathDirectoryOrCreate) 55 nodeName = kubetypes.NodeName(testNodeName) 56 hostPath = &v1.HostPathVolumeSource{ 57 Path: socketPath, 58 Type: &dirOrCreate, 59 } 60 migratedObjectReference = v1.ObjectReference{Namespace: "default", Name: "migrated-pvc"} 61 nonMigratedObjectReference = v1.ObjectReference{Namespace: "default", Name: "non-migrated-pvc"} 62 fsVolumeMode = new(v1.PersistentVolumeMode) 63 ) 64 65 type vaTest struct { 66 desc string 67 createNodeName kubetypes.NodeName 68 pod *v1.Pod 69 wantVolume *v1.Volume 70 wantPersistentVolume *v1.PersistentVolume 71 wantErrorMessage string 72 } 73 74 func Test_CreateVolumeSpec(t *testing.T) { 75 for _, test := range []vaTest{ 76 { 77 desc: "inline volume type that does not support csi migration", 78 createNodeName: nodeName, 79 pod: &v1.Pod{ 80 ObjectMeta: metav1.ObjectMeta{ 81 Name: "kube-apiserver", 82 Namespace: "default", 83 }, 84 Spec: v1.PodSpec{ 85 Volumes: []v1.Volume{ 86 { 87 Name: migratedVolume, 88 VolumeSource: v1.VolumeSource{ 89 HostPath: hostPath, 90 }, 91 }, 92 }, 93 }, 94 }, 95 wantVolume: &v1.Volume{ 96 Name: migratedVolume, 97 VolumeSource: v1.VolumeSource{ 98 HostPath: hostPath, 99 }, 100 }, 101 }, 102 { 103 desc: "inline volume type that supports csi migration", 104 createNodeName: nodeName, 105 pod: &v1.Pod{ 106 ObjectMeta: metav1.ObjectMeta{ 107 Name: "kube-apiserver", 108 Namespace: "default", 109 }, 110 Spec: v1.PodSpec{ 111 Volumes: []v1.Volume{ 112 { 113 Name: migratedVolume, 114 VolumeSource: v1.VolumeSource{ 115 GCEPersistentDisk: &v1.GCEPersistentDiskVolumeSource{ 116 PDName: "test-disk", 117 FSType: "ext4", 118 Partition: 0, 119 ReadOnly: false, 120 }, 121 }, 122 }, 123 }, 124 }, 125 }, 126 wantPersistentVolume: &v1.PersistentVolume{ 127 ObjectMeta: metav1.ObjectMeta{ 128 Name: "pd.csi.storage.gke.io-test-disk", 129 }, 130 Spec: v1.PersistentVolumeSpec{ 131 PersistentVolumeSource: v1.PersistentVolumeSource{ 132 CSI: &v1.CSIPersistentVolumeSource{ 133 Driver: "pd.csi.storage.gke.io", 134 VolumeHandle: "projects/UNSPECIFIED/zones/UNSPECIFIED/disks/test-disk", 135 FSType: "ext4", 136 ReadOnly: false, 137 VolumeAttributes: map[string]string{"partition": ""}, 138 }, 139 }, 140 AccessModes: []v1.PersistentVolumeAccessMode{"ReadWriteOnce"}, 141 VolumeMode: fsVolumeMode, 142 }, 143 }, 144 }, 145 { 146 desc: "pv type that does not support csi migration", 147 createNodeName: nodeName, 148 pod: &v1.Pod{ 149 ObjectMeta: metav1.ObjectMeta{ 150 Name: "kube-apiserver", 151 Namespace: "default", 152 }, 153 Spec: v1.PodSpec{ 154 Volumes: []v1.Volume{ 155 { 156 Name: nonMigratedVolume, 157 VolumeSource: v1.VolumeSource{ 158 PersistentVolumeClaim: &v1.PersistentVolumeClaimVolumeSource{ 159 ClaimName: "non-migrated-pvc", 160 ReadOnly: false, 161 }, 162 }, 163 }, 164 }, 165 }, 166 }, 167 wantPersistentVolume: &v1.PersistentVolume{ 168 ObjectMeta: metav1.ObjectMeta{ 169 Name: nonMigratedVolume, 170 }, 171 Spec: v1.PersistentVolumeSpec{ 172 PersistentVolumeSource: v1.PersistentVolumeSource{ 173 ScaleIO: &v1.ScaleIOPersistentVolumeSource{}, 174 }, 175 ClaimRef: &nonMigratedObjectReference, 176 }, 177 }, 178 }, 179 { 180 desc: "pv type that supports csi migration", 181 createNodeName: nodeName, 182 pod: &v1.Pod{ 183 ObjectMeta: metav1.ObjectMeta{ 184 Name: "kube-apiserver", 185 Namespace: "default", 186 }, 187 Spec: v1.PodSpec{ 188 Volumes: []v1.Volume{ 189 { 190 Name: migratedVolume, 191 VolumeSource: v1.VolumeSource{ 192 PersistentVolumeClaim: &v1.PersistentVolumeClaimVolumeSource{ 193 ClaimName: "migrated-pvc", 194 ReadOnly: false, 195 }, 196 }, 197 }, 198 }, 199 }, 200 }, 201 wantPersistentVolume: &v1.PersistentVolume{ 202 ObjectMeta: metav1.ObjectMeta{ 203 Name: migratedVolume, 204 }, 205 Spec: v1.PersistentVolumeSpec{ 206 PersistentVolumeSource: v1.PersistentVolumeSource{ 207 CSI: &v1.CSIPersistentVolumeSource{ 208 Driver: "pd.csi.storage.gke.io", 209 VolumeHandle: "projects/UNSPECIFIED/zones/UNSPECIFIED/disks/test-disk", 210 FSType: "ext4", 211 ReadOnly: false, 212 VolumeAttributes: map[string]string{"partition": ""}, 213 }, 214 }, 215 ClaimRef: &migratedObjectReference, 216 }, 217 }, 218 }, 219 { 220 desc: "CSINode not found for a volume type that supports csi migration", 221 createNodeName: kubetypes.NodeName("another-node"), 222 pod: &v1.Pod{ 223 ObjectMeta: metav1.ObjectMeta{ 224 Name: "kube-apiserver", 225 Namespace: "default", 226 }, 227 Spec: v1.PodSpec{ 228 Volumes: []v1.Volume{ 229 { 230 Name: migratedVolume, 231 VolumeSource: v1.VolumeSource{ 232 PersistentVolumeClaim: &v1.PersistentVolumeClaimVolumeSource{ 233 ClaimName: "migrated-pvc", 234 ReadOnly: false, 235 }, 236 }, 237 }, 238 }, 239 }, 240 }, 241 wantErrorMessage: "csiNode \"another-node\" not found", 242 }, 243 } { 244 t.Run(test.desc, func(t *testing.T) { 245 logger, _ := ktesting.NewTestContext(t) 246 plugMgr, intreeToCSITranslator, csiTranslator, pvLister, pvcLister := setup(testNodeName, t) 247 actualSpec, err := CreateVolumeSpec(logger, test.pod.Spec.Volumes[0], test.pod, test.createNodeName, plugMgr, pvcLister, pvLister, intreeToCSITranslator, csiTranslator) 248 249 if actualSpec == nil && (test.wantPersistentVolume != nil || test.wantVolume != nil) { 250 t.Errorf("got volume spec is nil") 251 } 252 253 if (len(test.wantErrorMessage) > 0 && err == nil) || (err != nil && !strings.Contains(err.Error(), test.wantErrorMessage)) { 254 t.Errorf("got err %v, want err with message %v", err, test.wantErrorMessage) 255 } 256 257 if test.wantPersistentVolume != nil { 258 if actualSpec.PersistentVolume == nil { 259 t.Errorf("gotVolumeWithCSIMigration is nil") 260 } 261 262 gotVolumeWithCSIMigration := *actualSpec.PersistentVolume 263 if gotVolumeWithCSIMigration.Name != test.wantPersistentVolume.Name { 264 t.Errorf("got volume name is %v, want volume name is %v", gotVolumeWithCSIMigration.Name, test.wantPersistentVolume.Name) 265 266 } 267 if !reflect.DeepEqual(gotVolumeWithCSIMigration.Spec, test.wantPersistentVolume.Spec) { 268 t.Errorf("got volume.Spec and want.Spec diff is %s", cmp.Diff(gotVolumeWithCSIMigration.Spec, test.wantPersistentVolume.Spec)) 269 } 270 } 271 if test.wantVolume != nil { 272 if actualSpec.Volume == nil { 273 t.Errorf("gotVolume is nil") 274 } 275 276 gotVolume := *actualSpec.Volume 277 if !reflect.DeepEqual(gotVolume, *test.wantVolume) { 278 t.Errorf("got volume and want diff is %s", cmp.Diff(gotVolume, test.wantVolume)) 279 } 280 } 281 }) 282 } 283 } 284 285 func setup(nodeName string, t *testing.T) (*volume.VolumePluginMgr, csimigration.PluginManager, csitrans.CSITranslator, tf.PersistentVolumeLister, tf.PersistentVolumeClaimLister) { 286 tmpDir, err := utiltesting.MkTmpdir("csi-test") 287 if err != nil { 288 t.Fatalf("can't make a temp dir: %v", err) 289 } 290 defer os.RemoveAll(tmpDir) 291 292 *fsVolumeMode = v1.PersistentVolumeFilesystem 293 294 csiTranslator := csitrans.New() 295 intreeToCSITranslator := csimigration.NewPluginManager(csiTranslator, utilfeature.DefaultFeatureGate) 296 kubeClient := fake.NewSimpleClientset() 297 298 factory := informers.NewSharedInformerFactory(kubeClient, time.Minute) 299 csiDriverInformer := factory.Storage().V1().CSIDrivers() 300 csiDriverLister := csiDriverInformer.Lister() 301 volumeAttachmentInformer := factory.Storage().V1().VolumeAttachments() 302 volumeAttachmentLister := volumeAttachmentInformer.Lister() 303 304 plugMgr := &volume.VolumePluginMgr{} 305 fakeAttachDetachVolumeHost := volumetest.NewFakeAttachDetachVolumeHostWithCSINodeName(t, 306 tmpDir, 307 kubeClient, 308 fc.ProbeVolumePlugins(), 309 nodeName, 310 csiDriverLister, 311 volumeAttachmentLister, 312 ) 313 314 plugMgr.Host = fakeAttachDetachVolumeHost 315 316 pvLister := tf.PersistentVolumeLister{ 317 { 318 ObjectMeta: metav1.ObjectMeta{Name: migratedVolume}, 319 Spec: v1.PersistentVolumeSpec{ 320 PersistentVolumeSource: v1.PersistentVolumeSource{ 321 GCEPersistentDisk: &v1.GCEPersistentDiskVolumeSource{ 322 PDName: "test-disk", 323 FSType: "ext4", 324 Partition: 0, 325 ReadOnly: false, 326 }, 327 }, 328 ClaimRef: &migratedObjectReference, 329 }, 330 }, 331 { 332 ObjectMeta: metav1.ObjectMeta{Name: nonMigratedVolume}, 333 Spec: v1.PersistentVolumeSpec{ 334 PersistentVolumeSource: v1.PersistentVolumeSource{ 335 ScaleIO: &v1.ScaleIOPersistentVolumeSource{}, 336 }, 337 ClaimRef: &nonMigratedObjectReference, 338 }, 339 }, 340 } 341 342 pvcLister := tf.PersistentVolumeClaimLister{ 343 { 344 ObjectMeta: metav1.ObjectMeta{Name: "migrated-pvc", Namespace: "default"}, 345 Spec: v1.PersistentVolumeClaimSpec{VolumeName: migratedVolume}, 346 Status: v1.PersistentVolumeClaimStatus{ 347 Phase: v1.ClaimBound, 348 }, 349 }, 350 { 351 ObjectMeta: metav1.ObjectMeta{Name: "non-migrated-pvc", Namespace: "default"}, 352 Spec: v1.PersistentVolumeClaimSpec{VolumeName: nonMigratedVolume}, 353 Status: v1.PersistentVolumeClaimStatus{ 354 Phase: v1.ClaimBound, 355 }, 356 }, 357 } 358 359 return plugMgr, intreeToCSITranslator, csiTranslator, pvLister, pvcLister 360 }