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  }