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