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  }