k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/pkg/controller/volume/attachdetach/attach_detach_controller_test.go (about) 1 /* 2 Copyright 2016 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 attachdetach 18 19 import ( 20 "context" 21 "fmt" 22 "runtime" 23 "testing" 24 "time" 25 26 v1 "k8s.io/api/core/v1" 27 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 28 "k8s.io/apimachinery/pkg/labels" 29 "k8s.io/apimachinery/pkg/types" 30 "k8s.io/apimachinery/pkg/util/sets" 31 "k8s.io/client-go/informers" 32 "k8s.io/client-go/kubernetes/fake" 33 kcache "k8s.io/client-go/tools/cache" 34 "k8s.io/kubernetes/pkg/controller" 35 "k8s.io/kubernetes/pkg/controller/volume/attachdetach/cache" 36 controllervolumetesting "k8s.io/kubernetes/pkg/controller/volume/attachdetach/testing" 37 "k8s.io/kubernetes/pkg/volume" 38 "k8s.io/kubernetes/pkg/volume/csi" 39 "k8s.io/kubernetes/pkg/volume/util" 40 "k8s.io/kubernetes/test/utils/ktesting" 41 ) 42 43 const ( 44 intreePDUniqueNamePrefix = "kubernetes.io/gce-pd/" 45 csiPDUniqueNamePrefix = "kubernetes.io/csi/pd.csi.storage.gke.io^projects/UNSPECIFIED/zones/UNSPECIFIED/disks/" 46 ) 47 48 func createADC(t testing.TB, tCtx ktesting.TContext, fakeKubeClient *fake.Clientset, 49 informerFactory informers.SharedInformerFactory, plugins []volume.VolumePlugin) *attachDetachController { 50 51 adcObj, err := NewAttachDetachController( 52 tCtx, 53 fakeKubeClient, 54 informerFactory.Core().V1().Pods(), 55 informerFactory.Core().V1().Nodes(), 56 informerFactory.Core().V1().PersistentVolumeClaims(), 57 informerFactory.Core().V1().PersistentVolumes(), 58 informerFactory.Storage().V1().CSINodes(), 59 informerFactory.Storage().V1().CSIDrivers(), 60 informerFactory.Storage().V1().VolumeAttachments(), 61 plugins, 62 nil, /* prober */ 63 false, 64 1*time.Second, 65 false, 66 DefaultTimerConfig, 67 ) 68 69 if err != nil { 70 t.Fatalf("Run failed with error. Expected: <no error> Actual: <%v>", err) 71 } 72 return adcObj.(*attachDetachController) 73 } 74 75 func Test_NewAttachDetachController_Positive(t *testing.T) { 76 // Arrange 77 fakeKubeClient := controllervolumetesting.CreateTestClient() 78 informerFactory := informers.NewSharedInformerFactory(fakeKubeClient, controller.NoResyncPeriodFunc()) 79 tCtx := ktesting.Init(t) 80 81 // Act 82 createADC(t, tCtx, fakeKubeClient, informerFactory, nil) 83 } 84 85 func Test_AttachDetachControllerStateOfWorldPopulators_Positive(t *testing.T) { 86 // Arrange 87 fakeKubeClient := controllervolumetesting.CreateTestClient() 88 informerFactory := informers.NewSharedInformerFactory(fakeKubeClient, controller.NoResyncPeriodFunc()) 89 90 logger, tCtx := ktesting.NewTestContext(t) 91 adc := createADC(t, tCtx, fakeKubeClient, informerFactory, controllervolumetesting.CreateTestPlugin()) 92 93 // Act 94 informerFactory.Start(tCtx.Done()) 95 informerFactory.WaitForCacheSync(tCtx.Done()) 96 97 err := adc.populateActualStateOfWorld(logger) 98 if err != nil { 99 t.Fatalf("Run failed with error. Expected: <no error> Actual: <%v>", err) 100 } 101 102 err = adc.populateDesiredStateOfWorld(logger) 103 if err != nil { 104 t.Fatalf("Run failed with error. Expected: <no error> Actual: %v", err) 105 } 106 107 // Test the ActualStateOfWorld contains all the node volumes 108 nodes, err := adc.nodeLister.List(labels.Everything()) 109 if err != nil { 110 t.Fatalf("Failed to list nodes in indexer. Expected: <no error> Actual: %v", err) 111 } 112 113 for _, node := range nodes { 114 nodeName := types.NodeName(node.Name) 115 inUseVolumes := sets.New(node.Status.VolumesInUse...) 116 allAttachedVolumes := map[v1.UniqueVolumeName]cache.AttachedVolume{} 117 for _, v := range adc.actualStateOfWorld.GetAttachedVolumesForNode(nodeName) { 118 allAttachedVolumes[v.VolumeName] = v 119 } 120 121 for _, attachedVolume := range node.Status.VolumesAttached { 122 attachedState := adc.actualStateOfWorld.GetAttachState(attachedVolume.Name, nodeName) 123 if attachedState != cache.AttachStateAttached { 124 t.Fatalf("Run failed with error. Node %s, volume %s not found", nodeName, attachedVolume.Name) 125 } 126 inUse := inUseVolumes.Has(attachedVolume.Name) 127 mounted := allAttachedVolumes[attachedVolume.Name].MountedByNode 128 if mounted != inUse { 129 t.Fatalf("Node %s, volume %s MountedByNode %v unexpected", nodeName, attachedVolume.Name, mounted) 130 } 131 } 132 } 133 134 pods, err := adc.podLister.List(labels.Everything()) 135 if err != nil { 136 t.Fatalf("Run failed with error. Expected: <no error> Actual: %v", err) 137 } 138 for _, pod := range pods { 139 uniqueName := fmt.Sprintf("%s/%s", controllervolumetesting.TestPluginName, pod.Spec.Volumes[0].Name) 140 nodeName := types.NodeName(pod.Spec.NodeName) 141 found := adc.desiredStateOfWorld.VolumeExists(v1.UniqueVolumeName(uniqueName), nodeName) 142 if !found { 143 t.Fatalf("Run failed with error. Volume %s, node %s not found in DesiredStateOfWorld", 144 pod.Spec.Volumes[0].Name, 145 pod.Spec.NodeName) 146 } 147 } 148 } 149 150 func largeClusterClient(t testing.TB, numNodes int) *fake.Clientset { 151 // Arrange 152 fakeKubeClient := fake.NewSimpleClientset() 153 154 // populate numNodes nodes, each with 100 volumes 155 for i := 0; i < numNodes; i++ { 156 nodeName := fmt.Sprintf("node-%d", i) 157 node := &v1.Node{ 158 ObjectMeta: metav1.ObjectMeta{ 159 Name: nodeName, 160 Labels: map[string]string{ 161 "name": nodeName, 162 }, 163 Annotations: map[string]string{ 164 util.ControllerManagedAttachAnnotation: "true", 165 }, 166 }, 167 } 168 for j := 0; j < 100; j++ { 169 volumeName := v1.UniqueVolumeName(fmt.Sprintf("test-volume/vol-%d-%d", i, j)) 170 node.Status.VolumesAttached = append(node.Status.VolumesAttached, v1.AttachedVolume{ 171 Name: volumeName, 172 DevicePath: fmt.Sprintf("/dev/disk/by-id/vol-%d-%d", i, j), 173 }) 174 node.Status.VolumesInUse = append(node.Status.VolumesInUse, volumeName) 175 _, err := fakeKubeClient.CoreV1().PersistentVolumes().Create(context.Background(), &v1.PersistentVolume{ 176 ObjectMeta: metav1.ObjectMeta{ 177 Name: fmt.Sprintf("vol-%d-%d", i, j), 178 }, 179 }, metav1.CreateOptions{}) 180 if err != nil { 181 t.Fatalf("failed to create PV: %v", err) 182 } 183 } 184 _, err := fakeKubeClient.CoreV1().Nodes().Create(context.Background(), node, metav1.CreateOptions{}) 185 if err != nil { 186 t.Fatalf("failed to create node: %v", err) 187 } 188 } 189 190 return fakeKubeClient 191 } 192 193 func BenchmarkPopulateActualStateOfWorld(b *testing.B) { 194 fakeKubeClient := largeClusterClient(b, 10000) 195 informerFactory := informers.NewSharedInformerFactory(fakeKubeClient, controller.NoResyncPeriodFunc()) 196 197 logger, tCtx := ktesting.NewTestContext(b) 198 adc := createADC(b, tCtx, fakeKubeClient, informerFactory, nil) 199 200 // Act 201 informerFactory.Start(tCtx.Done()) 202 informerFactory.WaitForCacheSync(tCtx.Done()) 203 204 b.ResetTimer() 205 for i := 0; i < b.N; i++ { 206 err := adc.populateActualStateOfWorld(logger) 207 if err != nil { 208 b.Fatalf("Run failed with error. Expected: <no error> Actual: <%v>", err) 209 } 210 } 211 } 212 213 func BenchmarkNodeUpdate(b *testing.B) { 214 fakeKubeClient := largeClusterClient(b, 3000) 215 informerFactory := informers.NewSharedInformerFactory(fakeKubeClient, controller.NoResyncPeriodFunc()) 216 217 logger, tCtx := ktesting.NewTestContext(b) 218 adc := createADC(b, tCtx, fakeKubeClient, informerFactory, nil) 219 220 informerFactory.Start(tCtx.Done()) 221 informerFactory.WaitForCacheSync(tCtx.Done()) 222 223 err := adc.populateActualStateOfWorld(logger.V(2)) 224 if err != nil { 225 b.Fatalf("Run failed with error. Expected: <no error> Actual: <%v>", err) 226 } 227 228 node, err := fakeKubeClient.CoreV1().Nodes().Get(context.Background(), "node-123", metav1.GetOptions{}) 229 if err != nil { 230 b.Fatalf("Run failed with error. Expected: <no error> Actual: <%v>", err) 231 } 232 // Act 233 runtime.GC() 234 b.ResetTimer() 235 for i := 0; i < b.N; i++ { 236 adc.nodeUpdate(logger, node, node) 237 } 238 } 239 240 func Test_AttachDetachControllerRecovery(t *testing.T) { 241 attachDetachRecoveryTestCase(t, []*v1.Pod{}, []*v1.Pod{}) 242 newPod1 := controllervolumetesting.NewPodWithVolume("newpod-1", "volumeName2", "mynode-1") 243 attachDetachRecoveryTestCase(t, []*v1.Pod{newPod1}, []*v1.Pod{}) 244 newPod1 = controllervolumetesting.NewPodWithVolume("newpod-1", "volumeName2", "mynode-1") 245 attachDetachRecoveryTestCase(t, []*v1.Pod{}, []*v1.Pod{newPod1}) 246 newPod1 = controllervolumetesting.NewPodWithVolume("newpod-1", "volumeName2", "mynode-1") 247 newPod2 := controllervolumetesting.NewPodWithVolume("newpod-2", "volumeName3", "mynode-1") 248 attachDetachRecoveryTestCase(t, []*v1.Pod{newPod1}, []*v1.Pod{newPod2}) 249 } 250 251 func attachDetachRecoveryTestCase(t *testing.T, extraPods1 []*v1.Pod, extraPods2 []*v1.Pod) { 252 fakeKubeClient := controllervolumetesting.CreateTestClient() 253 informerFactory := informers.NewSharedInformerFactory(fakeKubeClient, time.Second*1) 254 //informerFactory := informers.NewSharedInformerFactory(fakeKubeClient, time.Second*1) 255 plugins := controllervolumetesting.CreateTestPlugin() 256 var prober volume.DynamicPluginProber = nil // TODO (#51147) inject mock 257 nodeInformer := informerFactory.Core().V1().Nodes().Informer() 258 csiNodeInformer := informerFactory.Storage().V1().CSINodes().Informer() 259 podInformer := informerFactory.Core().V1().Pods().Informer() 260 var podsNum, extraPodsNum, nodesNum, i int 261 262 // Create the controller 263 logger, tCtx := ktesting.NewTestContext(t) 264 adcObj, err := NewAttachDetachController( 265 tCtx, 266 fakeKubeClient, 267 informerFactory.Core().V1().Pods(), 268 informerFactory.Core().V1().Nodes(), 269 informerFactory.Core().V1().PersistentVolumeClaims(), 270 informerFactory.Core().V1().PersistentVolumes(), 271 informerFactory.Storage().V1().CSINodes(), 272 informerFactory.Storage().V1().CSIDrivers(), 273 informerFactory.Storage().V1().VolumeAttachments(), 274 plugins, 275 prober, 276 false, 277 1*time.Second, 278 false, 279 DefaultTimerConfig, 280 ) 281 282 if err != nil { 283 t.Fatalf("Run failed with error. Expected: <no error> Actual: <%v>", err) 284 } 285 286 adc := adcObj.(*attachDetachController) 287 288 pods, err := fakeKubeClient.CoreV1().Pods(v1.NamespaceAll).List(tCtx, metav1.ListOptions{}) 289 if err != nil { 290 t.Fatalf("Run failed with error. Expected: <no error> Actual: %v", err) 291 } 292 293 for _, pod := range pods.Items { 294 podToAdd := pod 295 podInformer.GetIndexer().Add(&podToAdd) 296 podsNum++ 297 } 298 nodes, err := fakeKubeClient.CoreV1().Nodes().List(tCtx, metav1.ListOptions{}) 299 if err != nil { 300 t.Fatalf("Run failed with error. Expected: <no error> Actual: %v", err) 301 } 302 for _, node := range nodes.Items { 303 nodeToAdd := node 304 nodeInformer.GetIndexer().Add(&nodeToAdd) 305 nodesNum++ 306 } 307 308 csiNodes, err := fakeKubeClient.StorageV1().CSINodes().List(tCtx, metav1.ListOptions{}) 309 if err != nil { 310 t.Fatalf("Run failed with error. Expected: <no error> Actual: %v", err) 311 } 312 for _, csiNode := range csiNodes.Items { 313 csiNodeToAdd := csiNode 314 csiNodeInformer.GetIndexer().Add(&csiNodeToAdd) 315 } 316 317 informerFactory.Start(tCtx.Done()) 318 319 if !kcache.WaitForNamedCacheSync("attach detach", tCtx.Done(), 320 informerFactory.Core().V1().Pods().Informer().HasSynced, 321 informerFactory.Core().V1().Nodes().Informer().HasSynced, 322 informerFactory.Storage().V1().CSINodes().Informer().HasSynced) { 323 t.Fatalf("Error waiting for the informer caches to sync") 324 } 325 326 // Make sure the nodes and pods are in the informer cache 327 i = 0 328 nodeList, err := informerFactory.Core().V1().Nodes().Lister().List(labels.Everything()) 329 for len(nodeList) < nodesNum { 330 if err != nil { 331 t.Fatalf("Error getting list of nodes %v", err) 332 } 333 if i > 100 { 334 t.Fatalf("Time out while waiting for the node informer sync: found %d nodes, expected %d nodes", len(nodeList), nodesNum) 335 } 336 time.Sleep(100 * time.Millisecond) 337 nodeList, err = informerFactory.Core().V1().Nodes().Lister().List(labels.Everything()) 338 i++ 339 } 340 i = 0 341 podList, err := informerFactory.Core().V1().Pods().Lister().List(labels.Everything()) 342 for len(podList) < podsNum { 343 if err != nil { 344 t.Fatalf("Error getting list of nodes %v", err) 345 } 346 if i > 100 { 347 t.Fatalf("Time out while waiting for the pod informer sync: found %d pods, expected %d pods", len(podList), podsNum) 348 } 349 time.Sleep(100 * time.Millisecond) 350 podList, err = informerFactory.Core().V1().Pods().Lister().List(labels.Everything()) 351 i++ 352 } 353 i = 0 354 csiNodesList, err := informerFactory.Storage().V1().CSINodes().Lister().List(labels.Everything()) 355 for len(csiNodesList) < nodesNum { 356 if err != nil { 357 t.Fatalf("Error getting list of csi nodes %v", err) 358 } 359 if i > 100 { 360 t.Fatalf("Time out while waiting for the csinodes informer sync: found %d csinodes, expected %d csinodes", len(csiNodesList), nodesNum) 361 } 362 time.Sleep(100 * time.Millisecond) 363 csiNodesList, err = informerFactory.Storage().V1().CSINodes().Lister().List(labels.Everything()) 364 i++ 365 } 366 367 // Populate ASW 368 err = adc.populateActualStateOfWorld(logger) 369 if err != nil { 370 t.Fatalf("Run failed with error. Expected: <no error> Actual: <%v>", err) 371 } 372 373 for _, newPod := range extraPods1 { 374 // Add a new pod between ASW and DSW ppoulators 375 _, err = adc.kubeClient.CoreV1().Pods(newPod.ObjectMeta.Namespace).Create(tCtx, newPod, metav1.CreateOptions{}) 376 if err != nil { 377 t.Fatalf("Run failed with error. Failed to create a new pod: <%v>", err) 378 } 379 extraPodsNum++ 380 podInformer.GetIndexer().Add(newPod) 381 382 } 383 384 // Populate DSW 385 err = adc.populateDesiredStateOfWorld(logger) 386 if err != nil { 387 t.Fatalf("Run failed with error. Expected: <no error> Actual: %v", err) 388 } 389 390 for _, newPod := range extraPods2 { 391 // Add a new pod between DSW ppoulator and reconciler run 392 _, err = adc.kubeClient.CoreV1().Pods(newPod.ObjectMeta.Namespace).Create(tCtx, newPod, metav1.CreateOptions{}) 393 if err != nil { 394 t.Fatalf("Run failed with error. Failed to create a new pod: <%v>", err) 395 } 396 extraPodsNum++ 397 podInformer.GetIndexer().Add(newPod) 398 } 399 400 go adc.reconciler.Run(tCtx) 401 go adc.desiredStateOfWorldPopulator.Run(tCtx) 402 403 time.Sleep(time.Second * 1) // Wait so the reconciler calls sync at least once 404 405 testPlugin := plugins[0].(*controllervolumetesting.TestPlugin) 406 for i = 0; i <= 10; i++ { 407 var attachedVolumesNum int = 0 408 var detachedVolumesNum int = 0 409 410 time.Sleep(time.Second * 1) // Wait for a second 411 for _, volumeList := range testPlugin.GetAttachedVolumes() { 412 attachedVolumesNum += len(volumeList) 413 } 414 for _, volumeList := range testPlugin.GetDetachedVolumes() { 415 detachedVolumesNum += len(volumeList) 416 } 417 418 // All the "extra pods" should result in volume to be attached, the pods all share one volume 419 // which should be attached (+1), the volumes found only in the nodes status should be detached 420 if attachedVolumesNum == 1+extraPodsNum && detachedVolumesNum == nodesNum { 421 break 422 } 423 if i == 10 { // 10 seconds time out 424 t.Fatalf("Waiting for the volumes to attach/detach timed out: attached %d (expected %d); detached %d (%d)", 425 attachedVolumesNum, 1+extraPodsNum, detachedVolumesNum, nodesNum) 426 } 427 } 428 429 if testPlugin.GetErrorEncountered() { 430 t.Fatalf("Fatal error encountered in the testing volume plugin") 431 } 432 433 } 434 435 type vaTest struct { 436 testName string 437 volName string 438 podName string 439 podNodeName string 440 pvName string 441 vaName string 442 vaNodeName string 443 vaAttachStatus bool 444 csiMigration bool 445 expected_attaches map[string][]string 446 expected_detaches map[string][]string 447 expectedASWAttachState cache.AttachState 448 } 449 450 func Test_ADC_VolumeAttachmentRecovery(t *testing.T) { 451 for _, tc := range []vaTest{ 452 { // pod is scheduled 453 testName: "Scheduled pod", 454 volName: "vol1", 455 podName: "pod1", 456 podNodeName: "mynode-1", 457 pvName: "pv1", 458 vaName: "va1", 459 vaNodeName: "mynode-1", 460 vaAttachStatus: false, 461 expected_attaches: map[string][]string{"mynode-1": {"vol1"}}, 462 expected_detaches: map[string][]string{}, 463 }, 464 { // pod is deleted, attach status:true, verify dangling volume is detached 465 testName: "VA status is attached", 466 volName: "vol1", 467 pvName: "pv1", 468 vaName: "va1", 469 vaNodeName: "mynode-1", 470 vaAttachStatus: true, 471 expected_attaches: map[string][]string{}, 472 expected_detaches: map[string][]string{"mynode-1": {"vol1"}}, 473 }, 474 { // pod is deleted, attach status:false, verify dangling volume is detached 475 testName: "VA status is unattached", 476 volName: "vol1", 477 pvName: "pv1", 478 vaName: "va1", 479 vaNodeName: "mynode-1", 480 vaAttachStatus: false, 481 expected_attaches: map[string][]string{}, 482 expected_detaches: map[string][]string{"mynode-1": {"vol1"}}, 483 }, 484 { // pod is scheduled, volume is migrated, attach status:false, verify volume is marked as attached 485 testName: "Scheduled Pod with migrated PV", 486 volName: "vol1", 487 podNodeName: "mynode-1", 488 pvName: "pv1", 489 vaName: "va1", 490 vaNodeName: "mynode-1", 491 vaAttachStatus: false, 492 csiMigration: true, 493 expectedASWAttachState: cache.AttachStateAttached, 494 }, 495 { // pod is deleted, volume is migrated, attach status:false, verify volume is marked as uncertain 496 testName: "Deleted Pod with migrated PV", 497 volName: "vol1", 498 pvName: "pv1", 499 vaName: "va1", 500 vaNodeName: "mynode-1", 501 vaAttachStatus: false, 502 csiMigration: true, 503 expectedASWAttachState: cache.AttachStateUncertain, 504 }, 505 } { 506 t.Run(tc.testName, func(t *testing.T) { 507 volumeAttachmentRecoveryTestCase(t, tc) 508 }) 509 } 510 } 511 512 func volumeAttachmentRecoveryTestCase(t *testing.T, tc vaTest) { 513 fakeKubeClient := controllervolumetesting.CreateTestClient() 514 informerFactory := informers.NewSharedInformerFactory(fakeKubeClient, time.Second*1) 515 var plugins []volume.VolumePlugin 516 517 plugins = append(plugins, controllervolumetesting.CreateTestPlugin()...) 518 plugins = append(plugins, csi.ProbeVolumePlugins()...) 519 520 nodeInformer := informerFactory.Core().V1().Nodes().Informer() 521 podInformer := informerFactory.Core().V1().Pods().Informer() 522 pvInformer := informerFactory.Core().V1().PersistentVolumes().Informer() 523 vaInformer := informerFactory.Storage().V1().VolumeAttachments().Informer() 524 525 // Create the controller 526 logger, tCtx := ktesting.NewTestContext(t) 527 adc := createADC(t, tCtx, fakeKubeClient, informerFactory, plugins) 528 529 // Add existing objects (created by testplugin) to the respective informers 530 pods, err := fakeKubeClient.CoreV1().Pods(v1.NamespaceAll).List(tCtx, metav1.ListOptions{}) 531 if err != nil { 532 t.Fatalf("Run failed with error. Expected: <no error> Actual: %v", err) 533 } 534 for _, pod := range pods.Items { 535 podToAdd := pod 536 podInformer.GetIndexer().Add(&podToAdd) 537 } 538 nodes, err := fakeKubeClient.CoreV1().Nodes().List(tCtx, metav1.ListOptions{}) 539 if err != nil { 540 t.Fatalf("Run failed with error. Expected: <no error> Actual: %v", err) 541 } 542 for _, node := range nodes.Items { 543 nodeToAdd := node 544 nodeInformer.GetIndexer().Add(&nodeToAdd) 545 } 546 547 if tc.csiMigration { 548 newNode := &v1.Node{ 549 ObjectMeta: metav1.ObjectMeta{ 550 Name: tc.podNodeName, 551 Labels: map[string]string{ 552 "name": tc.podNodeName, 553 }, 554 Annotations: map[string]string{ 555 util.ControllerManagedAttachAnnotation: "true", 556 }, 557 }, 558 Status: v1.NodeStatus{ 559 VolumesAttached: []v1.AttachedVolume{ 560 { 561 Name: v1.UniqueVolumeName(csiPDUniqueNamePrefix + tc.volName), 562 DevicePath: "fake/path", 563 }, 564 }, 565 }, 566 } 567 _, err = adc.kubeClient.CoreV1().Nodes().Update(tCtx, newNode, metav1.UpdateOptions{}) 568 if err != nil { 569 t.Fatalf("Run failed with error. Failed to create a new pod: <%v>", err) 570 } 571 nodeInformer.GetIndexer().Add(&newNode) 572 } 573 // Create and add objects requested by the test 574 if tc.podName != "" { 575 newPod := controllervolumetesting.NewPodWithVolume(tc.podName, tc.volName, tc.podNodeName) 576 _, err = adc.kubeClient.CoreV1().Pods(newPod.ObjectMeta.Namespace).Create(tCtx, newPod, metav1.CreateOptions{}) 577 if err != nil { 578 t.Fatalf("Run failed with error. Failed to create a new pod: <%v>", err) 579 } 580 podInformer.GetIndexer().Add(newPod) 581 } 582 if tc.pvName != "" { 583 var newPv *v1.PersistentVolume 584 if tc.csiMigration { 585 // NewPV returns a GCEPersistentDisk volume, which is migrated. 586 newPv = controllervolumetesting.NewPV(tc.pvName, tc.volName) 587 } else { 588 // Otherwise use NFS, which is not subject to migration. 589 newPv = controllervolumetesting.NewNFSPV(tc.pvName, tc.volName) 590 } 591 _, err = adc.kubeClient.CoreV1().PersistentVolumes().Create(tCtx, newPv, metav1.CreateOptions{}) 592 if err != nil { 593 t.Fatalf("Run failed with error. Failed to create a new pv: <%v>", err) 594 } 595 pvInformer.GetIndexer().Add(newPv) 596 } 597 if tc.vaName != "" { 598 newVa := controllervolumetesting.NewVolumeAttachment(tc.vaName, tc.pvName, tc.vaNodeName, tc.vaAttachStatus) 599 _, err = adc.kubeClient.StorageV1().VolumeAttachments().Create(tCtx, newVa, metav1.CreateOptions{}) 600 if err != nil { 601 t.Fatalf("Run failed with error. Failed to create a new volumeAttachment: <%v>", err) 602 } 603 vaInformer.GetIndexer().Add(newVa) 604 } 605 606 // Makesure the informer cache is synced 607 informerFactory.Start(tCtx.Done()) 608 609 if !kcache.WaitForNamedCacheSync("attach detach", tCtx.Done(), 610 informerFactory.Core().V1().Pods().Informer().HasSynced, 611 informerFactory.Core().V1().Nodes().Informer().HasSynced, 612 informerFactory.Core().V1().PersistentVolumes().Informer().HasSynced, 613 informerFactory.Storage().V1().VolumeAttachments().Informer().HasSynced) { 614 t.Fatalf("Error waiting for the informer caches to sync") 615 } 616 617 // Populate ASW 618 err = adc.populateActualStateOfWorld(logger) 619 if err != nil { 620 t.Fatalf("Run failed with error. Expected: <no error> Actual: <%v>", err) 621 } 622 623 // Populate DSW 624 err = adc.populateDesiredStateOfWorld(logger) 625 if err != nil { 626 t.Fatalf("Run failed with error. Expected: <no error> Actual: %v", err) 627 } 628 // Run reconciler and DSW populator loops 629 go adc.reconciler.Run(tCtx) 630 go adc.desiredStateOfWorldPopulator.Run(tCtx) 631 if tc.csiMigration { 632 verifyExpectedVolumeState(t, adc, tc) 633 } else { 634 // Verify if expected attaches and detaches have happened 635 testPlugin := plugins[0].(*controllervolumetesting.TestPlugin) 636 verifyAttachDetachCalls(t, testPlugin, tc) 637 } 638 639 } 640 641 func verifyExpectedVolumeState(t *testing.T, adc *attachDetachController, tc vaTest) { 642 // Since csi migration is turned on, the attach state for the PV should be in CSI format. 643 attachedState := adc.actualStateOfWorld.GetAttachState( 644 v1.UniqueVolumeName(csiPDUniqueNamePrefix+tc.volName), types.NodeName(tc.vaNodeName)) 645 if attachedState != tc.expectedASWAttachState { 646 t.Fatalf("Expected attachedState %v, but it is %v", tc.expectedASWAttachState, attachedState) 647 } 648 649 // kubernetes.io/gce-pd/<volName> should not be marked when CSI Migration is on 650 // so it should be in detach status 651 attachedState = adc.actualStateOfWorld.GetAttachState( 652 v1.UniqueVolumeName(intreePDUniqueNamePrefix+tc.volName), types.NodeName(tc.vaNodeName)) 653 if attachedState != cache.AttachStateDetached { 654 t.Fatalf("Expected attachedState not to be %v, but it is %v", cache.AttachStateDetached, attachedState) 655 } 656 } 657 658 func verifyAttachDetachCalls(t *testing.T, testPlugin *controllervolumetesting.TestPlugin, tc vaTest) { 659 for tries := 0; tries <= 10; tries++ { // wait & try few times before failing the test 660 expected_op_map := tc.expected_attaches 661 plugin_map := testPlugin.GetAttachedVolumes() 662 verify_op := "attach" 663 volFound, nodeFound := false, false 664 for i := 0; i <= 1; i++ { // verify attaches and detaches 665 if i == 1 { 666 expected_op_map = tc.expected_detaches 667 plugin_map = testPlugin.GetDetachedVolumes() 668 verify_op = "detach" 669 } 670 // Verify every (node, volume) in the expected_op_map is in the 671 // plugin_map 672 for expectedNode, expectedVolumeList := range expected_op_map { 673 var volumeList []string 674 volumeList, nodeFound = plugin_map[expectedNode] 675 if !nodeFound && tries == 10 { 676 t.Fatalf("Expected node not found, node:%v, op: %v, tries: %d", 677 expectedNode, verify_op, tries) 678 } 679 for _, expectedVolume := range expectedVolumeList { 680 volFound = false 681 for _, volume := range volumeList { 682 if expectedVolume == volume { 683 volFound = true 684 break 685 } 686 } 687 if !volFound && tries == 10 { 688 t.Fatalf("Expected %v operation not found, node:%v, volume: %v, tries: %d", 689 verify_op, expectedNode, expectedVolume, tries) 690 } 691 } 692 } 693 } 694 if nodeFound && volFound { 695 break 696 } 697 time.Sleep(time.Second * 1) 698 } 699 700 if testPlugin.GetErrorEncountered() { 701 t.Fatalf("Fatal error encountered in the testing volume plugin") 702 } 703 }