k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/test/integration/volume/attach_detach_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 volume
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"testing"
    23  	"time"
    24  
    25  	v1 "k8s.io/api/core/v1"
    26  	"k8s.io/apimachinery/pkg/api/resource"
    27  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    28  	"k8s.io/apimachinery/pkg/util/wait"
    29  	clientgoinformers "k8s.io/client-go/informers"
    30  	clientset "k8s.io/client-go/kubernetes"
    31  	restclient "k8s.io/client-go/rest"
    32  	"k8s.io/client-go/tools/cache"
    33  	kubeapiservertesting "k8s.io/kubernetes/cmd/kube-apiserver/app/testing"
    34  	"k8s.io/kubernetes/pkg/controller/volume/attachdetach"
    35  	volumecache "k8s.io/kubernetes/pkg/controller/volume/attachdetach/cache"
    36  	"k8s.io/kubernetes/pkg/controller/volume/persistentvolume"
    37  	persistentvolumeoptions "k8s.io/kubernetes/pkg/controller/volume/persistentvolume/options"
    38  	"k8s.io/kubernetes/pkg/volume"
    39  	volumetest "k8s.io/kubernetes/pkg/volume/testing"
    40  	"k8s.io/kubernetes/pkg/volume/util"
    41  	"k8s.io/kubernetes/test/integration/framework"
    42  	"k8s.io/kubernetes/test/utils/ktesting"
    43  )
    44  
    45  func fakePodWithVol(namespace string) *v1.Pod {
    46  	fakePod := &v1.Pod{
    47  		ObjectMeta: metav1.ObjectMeta{
    48  			Namespace: namespace,
    49  			Name:      "fakepod",
    50  		},
    51  		Spec: v1.PodSpec{
    52  			Containers: []v1.Container{
    53  				{
    54  					Name:  "fake-container",
    55  					Image: "nginx",
    56  					VolumeMounts: []v1.VolumeMount{
    57  						{
    58  							Name:      "fake-mount",
    59  							MountPath: "/var/www/html",
    60  						},
    61  					},
    62  				},
    63  			},
    64  			Volumes: []v1.Volume{
    65  				{
    66  					Name: "fake-mount",
    67  					VolumeSource: v1.VolumeSource{
    68  						HostPath: &v1.HostPathVolumeSource{
    69  							Path: "/var/www/html",
    70  						},
    71  					},
    72  				},
    73  			},
    74  			NodeName: "node-sandbox",
    75  		},
    76  	}
    77  	return fakePod
    78  }
    79  
    80  func fakePodWithPVC(name, pvcName, namespace string) (*v1.Pod, *v1.PersistentVolumeClaim) {
    81  	fakePod := &v1.Pod{
    82  		ObjectMeta: metav1.ObjectMeta{
    83  			Namespace: namespace,
    84  			Name:      name,
    85  		},
    86  		Spec: v1.PodSpec{
    87  			Containers: []v1.Container{
    88  				{
    89  					Name:  "fake-container",
    90  					Image: "nginx",
    91  					VolumeMounts: []v1.VolumeMount{
    92  						{
    93  							Name:      "fake-mount",
    94  							MountPath: "/var/www/html",
    95  						},
    96  					},
    97  				},
    98  			},
    99  			Volumes: []v1.Volume{
   100  				{
   101  					Name: "fake-mount",
   102  					VolumeSource: v1.VolumeSource{
   103  						PersistentVolumeClaim: &v1.PersistentVolumeClaimVolumeSource{
   104  							ClaimName: pvcName,
   105  						},
   106  					},
   107  				},
   108  			},
   109  			NodeName: "node-sandbox",
   110  		},
   111  	}
   112  	class := "fake-sc"
   113  	fakePVC := &v1.PersistentVolumeClaim{
   114  		ObjectMeta: metav1.ObjectMeta{
   115  			Namespace: namespace,
   116  			Name:      pvcName,
   117  		},
   118  		Spec: v1.PersistentVolumeClaimSpec{
   119  			AccessModes: []v1.PersistentVolumeAccessMode{
   120  				v1.ReadWriteOnce,
   121  			},
   122  			Resources: v1.VolumeResourceRequirements{
   123  				Requests: v1.ResourceList{
   124  					v1.ResourceName(v1.ResourceStorage): resource.MustParse("5Gi"),
   125  				},
   126  			},
   127  			StorageClassName: &class,
   128  		},
   129  	}
   130  	return fakePod, fakePVC
   131  }
   132  
   133  var defaultTimerConfig = attachdetach.TimerConfig{
   134  	ReconcilerLoopPeriod:                              100 * time.Millisecond,
   135  	ReconcilerMaxWaitForUnmountDuration:               6 * time.Second,
   136  	DesiredStateOfWorldPopulatorLoopSleepPeriod:       1 * time.Second,
   137  	DesiredStateOfWorldPopulatorListPodsRetryDuration: 3 * time.Second,
   138  }
   139  
   140  // Via integration test we can verify that if pod delete
   141  // event is somehow missed by AttachDetach controller - it still
   142  // gets cleaned up by Desired State of World populator.
   143  func TestPodDeletionWithDswp(t *testing.T) {
   144  	// Disable ServiceAccount admission plugin as we don't have serviceaccount controller running.
   145  	server := kubeapiservertesting.StartTestServerOrDie(t, nil, []string{"--disable-admission-plugins=ServiceAccount"}, framework.SharedEtcd())
   146  	defer server.TearDownFn()
   147  
   148  	namespaceName := "test-pod-deletion"
   149  	node := &v1.Node{
   150  		ObjectMeta: metav1.ObjectMeta{
   151  			Name: "node-sandbox",
   152  			Annotations: map[string]string{
   153  				util.ControllerManagedAttachAnnotation: "true",
   154  			},
   155  		},
   156  	}
   157  
   158  	tCtx := ktesting.Init(t)
   159  	defer tCtx.Cancel("test has completed")
   160  	testClient, ctrl, pvCtrl, informers := createAdClients(tCtx, t, server, defaultSyncPeriod, defaultTimerConfig)
   161  
   162  	ns := framework.CreateNamespaceOrDie(testClient, namespaceName, t)
   163  	defer framework.DeleteNamespaceOrDie(testClient, ns, t)
   164  
   165  	pod := fakePodWithVol(namespaceName)
   166  
   167  	if _, err := testClient.CoreV1().Nodes().Create(tCtx, node, metav1.CreateOptions{}); err != nil {
   168  		t.Fatalf("Failed to created node : %v", err)
   169  	}
   170  
   171  	// start controller loop
   172  	go informers.Core().V1().Nodes().Informer().Run(tCtx.Done())
   173  	if _, err := testClient.CoreV1().Pods(ns.Name).Create(tCtx, pod, metav1.CreateOptions{}); err != nil {
   174  		t.Errorf("Failed to create pod : %v", err)
   175  	}
   176  
   177  	podInformer := informers.Core().V1().Pods().Informer()
   178  	go podInformer.Run(tCtx.Done())
   179  
   180  	go informers.Core().V1().PersistentVolumeClaims().Informer().Run(tCtx.Done())
   181  	go informers.Core().V1().PersistentVolumes().Informer().Run(tCtx.Done())
   182  	go informers.Storage().V1().VolumeAttachments().Informer().Run(tCtx.Done())
   183  	initCSIObjects(tCtx.Done(), informers)
   184  	go ctrl.Run(tCtx)
   185  	// Run pvCtrl to avoid leaking goroutines started during its creation.
   186  	go pvCtrl.Run(tCtx)
   187  
   188  	waitToObservePods(t, podInformer, 1)
   189  	podKey, err := cache.MetaNamespaceKeyFunc(pod)
   190  	if err != nil {
   191  		t.Fatalf("MetaNamespaceKeyFunc failed with : %v", err)
   192  	}
   193  
   194  	podInformerObj, _, err := podInformer.GetStore().GetByKey(podKey)
   195  
   196  	if err != nil {
   197  		t.Fatalf("Pod not found in Pod Informer cache : %v", err)
   198  	}
   199  
   200  	waitForPodsInDSWP(t, ctrl.GetDesiredStateOfWorld())
   201  	// let's stop pod events from getting triggered
   202  	err = podInformer.GetStore().Delete(podInformerObj)
   203  	if err != nil {
   204  		t.Fatalf("Error deleting pod : %v", err)
   205  	}
   206  
   207  	waitToObservePods(t, podInformer, 0)
   208  	// the populator loop turns every 1 minute
   209  	waitForPodFuncInDSWP(t, ctrl.GetDesiredStateOfWorld(), 80*time.Second, "expected 0 pods in dsw after pod delete", 0)
   210  }
   211  
   212  func initCSIObjects(stopCh <-chan struct{}, informers clientgoinformers.SharedInformerFactory) {
   213  	go informers.Storage().V1().CSINodes().Informer().Run(stopCh)
   214  	go informers.Storage().V1().CSIDrivers().Informer().Run(stopCh)
   215  }
   216  
   217  func TestPodUpdateWithWithADC(t *testing.T) {
   218  	// Disable ServiceAccount admission plugin as we don't have serviceaccount controller running.
   219  	server := kubeapiservertesting.StartTestServerOrDie(t, nil, []string{"--disable-admission-plugins=ServiceAccount"}, framework.SharedEtcd())
   220  	defer server.TearDownFn()
   221  	namespaceName := "test-pod-update"
   222  
   223  	node := &v1.Node{
   224  		ObjectMeta: metav1.ObjectMeta{
   225  			Name: "node-sandbox",
   226  			Annotations: map[string]string{
   227  				util.ControllerManagedAttachAnnotation: "true",
   228  			},
   229  		},
   230  	}
   231  
   232  	tCtx := ktesting.Init(t)
   233  	defer tCtx.Cancel("test has completed")
   234  	testClient, ctrl, pvCtrl, informers := createAdClients(tCtx, t, server, defaultSyncPeriod, defaultTimerConfig)
   235  
   236  	ns := framework.CreateNamespaceOrDie(testClient, namespaceName, t)
   237  	defer framework.DeleteNamespaceOrDie(testClient, ns, t)
   238  
   239  	pod := fakePodWithVol(namespaceName)
   240  	podStopCh := make(chan struct{})
   241  	defer close(podStopCh)
   242  
   243  	if _, err := testClient.CoreV1().Nodes().Create(context.TODO(), node, metav1.CreateOptions{}); err != nil {
   244  		t.Fatalf("Failed to created node : %v", err)
   245  	}
   246  
   247  	go informers.Core().V1().Nodes().Informer().Run(podStopCh)
   248  
   249  	if _, err := testClient.CoreV1().Pods(ns.Name).Create(context.TODO(), pod, metav1.CreateOptions{}); err != nil {
   250  		t.Errorf("Failed to create pod : %v", err)
   251  	}
   252  
   253  	podInformer := informers.Core().V1().Pods().Informer()
   254  	go podInformer.Run(podStopCh)
   255  
   256  	// start controller loop
   257  	go informers.Core().V1().PersistentVolumeClaims().Informer().Run(tCtx.Done())
   258  	go informers.Core().V1().PersistentVolumes().Informer().Run(tCtx.Done())
   259  	go informers.Storage().V1().VolumeAttachments().Informer().Run(tCtx.Done())
   260  	initCSIObjects(tCtx.Done(), informers)
   261  	go ctrl.Run(tCtx)
   262  	// Run pvCtrl to avoid leaking goroutines started during its creation.
   263  	go pvCtrl.Run(tCtx)
   264  
   265  	waitToObservePods(t, podInformer, 1)
   266  	podKey, err := cache.MetaNamespaceKeyFunc(pod)
   267  	if err != nil {
   268  		t.Fatalf("MetaNamespaceKeyFunc failed with : %v", err)
   269  	}
   270  
   271  	_, _, err = podInformer.GetStore().GetByKey(podKey)
   272  
   273  	if err != nil {
   274  		t.Fatalf("Pod not found in Pod Informer cache : %v", err)
   275  	}
   276  
   277  	waitForPodsInDSWP(t, ctrl.GetDesiredStateOfWorld())
   278  
   279  	pod.Status.Phase = v1.PodSucceeded
   280  
   281  	if _, err := testClient.CoreV1().Pods(ns.Name).UpdateStatus(context.TODO(), pod, metav1.UpdateOptions{}); err != nil {
   282  		t.Errorf("Failed to update pod : %v", err)
   283  	}
   284  
   285  	waitForPodFuncInDSWP(t, ctrl.GetDesiredStateOfWorld(), 20*time.Second, "expected 0 pods in dsw after pod completion", 0)
   286  }
   287  
   288  // wait for the podInformer to observe the pods. Call this function before
   289  // running the RC manager to prevent the rc manager from creating new pods
   290  // rather than adopting the existing ones.
   291  func waitToObservePods(t *testing.T, podInformer cache.SharedIndexInformer, podNum int) {
   292  	if err := wait.Poll(100*time.Millisecond, 60*time.Second, func() (bool, error) {
   293  		objects := podInformer.GetIndexer().List()
   294  		if len(objects) == podNum {
   295  			return true, nil
   296  		}
   297  		return false, nil
   298  	}); err != nil {
   299  		t.Fatal(err)
   300  	}
   301  }
   302  
   303  // wait for pods to be observed in desired state of world
   304  func waitForPodsInDSWP(t *testing.T, dswp volumecache.DesiredStateOfWorld) {
   305  	if err := wait.Poll(time.Millisecond*500, wait.ForeverTestTimeout, func() (bool, error) {
   306  		pods := dswp.GetPodToAdd()
   307  		if len(pods) > 0 {
   308  			return true, nil
   309  		}
   310  		return false, nil
   311  	}); err != nil {
   312  		t.Fatalf("Pod not added to desired state of world : %v", err)
   313  	}
   314  }
   315  
   316  // wait for pods to be observed in desired state of world
   317  func waitForPodFuncInDSWP(t *testing.T, dswp volumecache.DesiredStateOfWorld, checkTimeout time.Duration, failMessage string, podCount int) {
   318  	if err := wait.Poll(time.Millisecond*500, checkTimeout, func() (bool, error) {
   319  		pods := dswp.GetPodToAdd()
   320  		if len(pods) == podCount {
   321  			return true, nil
   322  		}
   323  		return false, nil
   324  	}); err != nil {
   325  		t.Fatalf("%s but got error %v", failMessage, err)
   326  	}
   327  }
   328  
   329  func createAdClients(ctx context.Context, t *testing.T, server *kubeapiservertesting.TestServer, syncPeriod time.Duration, timers attachdetach.TimerConfig) (*clientset.Clientset, attachdetach.AttachDetachController, *persistentvolume.PersistentVolumeController, clientgoinformers.SharedInformerFactory) {
   330  	config := restclient.CopyConfig(server.ClientConfig)
   331  	config.QPS = 1000000
   332  	config.Burst = 1000000
   333  	resyncPeriod := 12 * time.Hour
   334  	testClient := clientset.NewForConfigOrDie(server.ClientConfig)
   335  
   336  	host := volumetest.NewFakeVolumeHost(t, "/tmp/fake", nil, nil)
   337  	plugin := &volumetest.FakeVolumePlugin{
   338  		PluginName:             provisionerPluginName,
   339  		Host:                   host,
   340  		Config:                 volume.VolumeConfig{},
   341  		LastProvisionerOptions: volume.VolumeOptions{},
   342  		NewAttacherCallCount:   0,
   343  		NewDetacherCallCount:   0,
   344  		Mounters:               nil,
   345  		Unmounters:             nil,
   346  		Attachers:              nil,
   347  		Detachers:              nil,
   348  	}
   349  	plugins := []volume.VolumePlugin{plugin}
   350  	informers := clientgoinformers.NewSharedInformerFactory(testClient, resyncPeriod)
   351  	ctrl, err := attachdetach.NewAttachDetachController(
   352  		ctx,
   353  		testClient,
   354  		informers.Core().V1().Pods(),
   355  		informers.Core().V1().Nodes(),
   356  		informers.Core().V1().PersistentVolumeClaims(),
   357  		informers.Core().V1().PersistentVolumes(),
   358  		informers.Storage().V1().CSINodes(),
   359  		informers.Storage().V1().CSIDrivers(),
   360  		informers.Storage().V1().VolumeAttachments(),
   361  		plugins,
   362  		nil, /* prober */
   363  		false,
   364  		5*time.Second,
   365  		false,
   366  		timers,
   367  	)
   368  
   369  	if err != nil {
   370  		t.Fatalf("Error creating AttachDetach : %v", err)
   371  	}
   372  
   373  	// create pv controller
   374  	controllerOptions := persistentvolumeoptions.NewPersistentVolumeControllerOptions()
   375  	params := persistentvolume.ControllerParameters{
   376  		KubeClient:                testClient,
   377  		SyncPeriod:                controllerOptions.PVClaimBinderSyncPeriod,
   378  		VolumePlugins:             plugins,
   379  		VolumeInformer:            informers.Core().V1().PersistentVolumes(),
   380  		ClaimInformer:             informers.Core().V1().PersistentVolumeClaims(),
   381  		ClassInformer:             informers.Storage().V1().StorageClasses(),
   382  		PodInformer:               informers.Core().V1().Pods(),
   383  		NodeInformer:              informers.Core().V1().Nodes(),
   384  		EnableDynamicProvisioning: false,
   385  	}
   386  	pvCtrl, err := persistentvolume.NewController(ctx, params)
   387  	if err != nil {
   388  		t.Fatalf("Failed to create PV controller: %v", err)
   389  	}
   390  	return testClient, ctrl, pvCtrl, informers
   391  }
   392  
   393  // Via integration test we can verify that if pod add
   394  // event is somehow missed by AttachDetach controller - it still
   395  // gets added by Desired State of World populator.
   396  func TestPodAddedByDswp(t *testing.T) {
   397  	// Disable ServiceAccount admission plugin as we don't have serviceaccount controller running.
   398  	server := kubeapiservertesting.StartTestServerOrDie(t, nil, []string{"--disable-admission-plugins=ServiceAccount"}, framework.SharedEtcd())
   399  	defer server.TearDownFn()
   400  	namespaceName := "test-pod-deletion"
   401  
   402  	node := &v1.Node{
   403  		ObjectMeta: metav1.ObjectMeta{
   404  			Name: "node-sandbox",
   405  			Annotations: map[string]string{
   406  				util.ControllerManagedAttachAnnotation: "true",
   407  			},
   408  		},
   409  	}
   410  
   411  	tCtx := ktesting.Init(t)
   412  	defer tCtx.Cancel("test has completed")
   413  	testClient, ctrl, pvCtrl, informers := createAdClients(tCtx, t, server, defaultSyncPeriod, defaultTimerConfig)
   414  
   415  	ns := framework.CreateNamespaceOrDie(testClient, namespaceName, t)
   416  	defer framework.DeleteNamespaceOrDie(testClient, ns, t)
   417  
   418  	pod := fakePodWithVol(namespaceName)
   419  	podStopCh := make(chan struct{})
   420  
   421  	if _, err := testClient.CoreV1().Nodes().Create(tCtx, node, metav1.CreateOptions{}); err != nil {
   422  		t.Fatalf("Failed to created node : %v", err)
   423  	}
   424  
   425  	go informers.Core().V1().Nodes().Informer().Run(podStopCh)
   426  
   427  	if _, err := testClient.CoreV1().Pods(ns.Name).Create(tCtx, pod, metav1.CreateOptions{}); err != nil {
   428  		t.Errorf("Failed to create pod : %v", err)
   429  	}
   430  
   431  	podInformer := informers.Core().V1().Pods().Informer()
   432  	go podInformer.Run(podStopCh)
   433  
   434  	// start controller loop
   435  	go informers.Core().V1().PersistentVolumeClaims().Informer().Run(tCtx.Done())
   436  	go informers.Core().V1().PersistentVolumes().Informer().Run(tCtx.Done())
   437  	go informers.Storage().V1().VolumeAttachments().Informer().Run(tCtx.Done())
   438  	initCSIObjects(tCtx.Done(), informers)
   439  	go ctrl.Run(tCtx)
   440  	// Run pvCtrl to avoid leaking goroutines started during its creation.
   441  	go pvCtrl.Run(tCtx)
   442  
   443  	waitToObservePods(t, podInformer, 1)
   444  	podKey, err := cache.MetaNamespaceKeyFunc(pod)
   445  	if err != nil {
   446  		t.Fatalf("MetaNamespaceKeyFunc failed with : %v", err)
   447  	}
   448  
   449  	_, _, err = podInformer.GetStore().GetByKey(podKey)
   450  
   451  	if err != nil {
   452  		t.Fatalf("Pod not found in Pod Informer cache : %v", err)
   453  	}
   454  
   455  	waitForPodsInDSWP(t, ctrl.GetDesiredStateOfWorld())
   456  
   457  	// let's stop pod events from getting triggered
   458  	close(podStopCh)
   459  	podNew := pod.DeepCopy()
   460  	newPodName := "newFakepod"
   461  	podNew.SetName(newPodName)
   462  	err = podInformer.GetStore().Add(podNew)
   463  	if err != nil {
   464  		t.Fatalf("Error adding pod : %v", err)
   465  	}
   466  
   467  	waitToObservePods(t, podInformer, 2)
   468  
   469  	// the findAndAddActivePods loop turns every 3 minute
   470  	waitForPodFuncInDSWP(t, ctrl.GetDesiredStateOfWorld(), 200*time.Second, "expected 2 pods in dsw after pod addition", 2)
   471  }
   472  
   473  func TestPVCBoundWithADC(t *testing.T) {
   474  	// Disable ServiceAccount admission plugin as we don't have serviceaccount controller running.
   475  	server := kubeapiservertesting.StartTestServerOrDie(t, nil, []string{"--disable-admission-plugins=ServiceAccount"}, framework.SharedEtcd())
   476  	defer server.TearDownFn()
   477  
   478  	tCtx := ktesting.Init(t)
   479  	defer tCtx.Cancel("test has completed")
   480  
   481  	namespaceName := "test-pod-deletion"
   482  
   483  	testClient, ctrl, pvCtrl, informers := createAdClients(tCtx, t, server, defaultSyncPeriod, attachdetach.TimerConfig{
   484  		ReconcilerLoopPeriod:                        100 * time.Millisecond,
   485  		ReconcilerMaxWaitForUnmountDuration:         6 * time.Second,
   486  		DesiredStateOfWorldPopulatorLoopSleepPeriod: 24 * time.Hour,
   487  		// Use high duration to disable DesiredStateOfWorldPopulator.findAndAddActivePods loop in test.
   488  		DesiredStateOfWorldPopulatorListPodsRetryDuration: 24 * time.Hour,
   489  	})
   490  
   491  	ns := framework.CreateNamespaceOrDie(testClient, namespaceName, t)
   492  	defer framework.DeleteNamespaceOrDie(testClient, ns, t)
   493  
   494  	node := &v1.Node{
   495  		ObjectMeta: metav1.ObjectMeta{
   496  			Name: "node-sandbox",
   497  			Annotations: map[string]string{
   498  				util.ControllerManagedAttachAnnotation: "true",
   499  			},
   500  		},
   501  	}
   502  	if _, err := testClient.CoreV1().Nodes().Create(context.TODO(), node, metav1.CreateOptions{}); err != nil {
   503  		t.Fatalf("Failed to created node : %v", err)
   504  	}
   505  
   506  	// pods with pvc not bound
   507  	pvcs := []*v1.PersistentVolumeClaim{}
   508  	for i := 0; i < 3; i++ {
   509  		pod, pvc := fakePodWithPVC(fmt.Sprintf("fakepod-pvcnotbound-%d", i), fmt.Sprintf("fakepvc-%d", i), namespaceName)
   510  		if _, err := testClient.CoreV1().Pods(pod.Namespace).Create(context.TODO(), pod, metav1.CreateOptions{}); err != nil {
   511  			t.Errorf("Failed to create pod : %v", err)
   512  		}
   513  		if _, err := testClient.CoreV1().PersistentVolumeClaims(pvc.Namespace).Create(context.TODO(), pvc, metav1.CreateOptions{}); err != nil {
   514  			t.Errorf("Failed to create pvc : %v", err)
   515  		}
   516  		pvcs = append(pvcs, pvc)
   517  	}
   518  	// pod with no pvc
   519  	podNew := fakePodWithVol(namespaceName)
   520  	podNew.SetName("fakepod")
   521  	if _, err := testClient.CoreV1().Pods(podNew.Namespace).Create(context.TODO(), podNew, metav1.CreateOptions{}); err != nil {
   522  		t.Errorf("Failed to create pod : %v", err)
   523  	}
   524  
   525  	// start controller loop
   526  	informers.Start(tCtx.Done())
   527  	informers.WaitForCacheSync(tCtx.Done())
   528  	initCSIObjects(tCtx.Done(), informers)
   529  	go ctrl.Run(tCtx)
   530  	go pvCtrl.Run(tCtx)
   531  
   532  	waitToObservePods(t, informers.Core().V1().Pods().Informer(), 4)
   533  	// Give attachdetach controller enough time to populate pods into DSWP.
   534  	time.Sleep(10 * time.Second)
   535  	waitForPodFuncInDSWP(t, ctrl.GetDesiredStateOfWorld(), 60*time.Second, "expected 1 pod in dsw", 1)
   536  	for _, pvc := range pvcs {
   537  		createPVForPVC(t, testClient, pvc)
   538  	}
   539  	waitForPodFuncInDSWP(t, ctrl.GetDesiredStateOfWorld(), 60*time.Second, "expected 4 pods in dsw after PVCs are bound", 4)
   540  }
   541  
   542  // Create PV for PVC, pv controller will bind them together.
   543  func createPVForPVC(t *testing.T, testClient *clientset.Clientset, pvc *v1.PersistentVolumeClaim) {
   544  	pv := &v1.PersistentVolume{
   545  		ObjectMeta: metav1.ObjectMeta{
   546  			Name: fmt.Sprintf("fakepv-%s", pvc.Name),
   547  		},
   548  		Spec: v1.PersistentVolumeSpec{
   549  			Capacity:    pvc.Spec.Resources.Requests,
   550  			AccessModes: pvc.Spec.AccessModes,
   551  			PersistentVolumeSource: v1.PersistentVolumeSource{
   552  				HostPath: &v1.HostPathVolumeSource{
   553  					Path: "/var/www/html",
   554  				},
   555  			},
   556  			ClaimRef:         &v1.ObjectReference{Name: pvc.Name, Namespace: pvc.Namespace},
   557  			StorageClassName: *pvc.Spec.StorageClassName,
   558  		},
   559  	}
   560  	if _, err := testClient.CoreV1().PersistentVolumes().Create(context.TODO(), pv, metav1.CreateOptions{}); err != nil {
   561  		t.Errorf("Failed to create pv : %v", err)
   562  	}
   563  }