// Calls Run() // Verifies there are no calls to attach or detach. func Test_Run_Positive_DoNothing(t *testing.T) { // Arrange volumePluginMgr, fakePlugin := volumetesting.GetTestVolumePluginMgr(t) dsw := cache.NewDesiredStateOfWorld(volumePluginMgr) asw := cache.NewActualStateOfWorld(volumePluginMgr) fakeKubeClient := controllervolumetesting.CreateTestClient() fakeRecorder := &record.FakeRecorder{} ad := operationexecutor.NewOperationExecutor( fakeKubeClient, volumePluginMgr, fakeRecorder, false) nodeInformer := informers.NewNodeInformer( fakeKubeClient, resyncPeriod) nsu := statusupdater.NewNodeStatusUpdater( fakeKubeClient, nodeInformer, asw) reconciler := NewReconciler( reconcilerLoopPeriod, maxWaitForUnmountDuration, syncLoopPeriod, dsw, asw, ad, nsu) // Act ch := make(chan struct{}) go reconciler.Run(ch) defer close(ch) // Assert waitForNewAttacherCallCount(t, 0 /* expectedCallCount */, fakePlugin) verifyNewAttacherCallCount(t, true /* expectZeroNewAttacherCallCount */, fakePlugin) verifyNewDetacherCallCount(t, true /* expectZeroNewDetacherCallCount */, fakePlugin) waitForAttachCallCount(t, 0 /* expectedAttachCallCount */, fakePlugin) waitForDetachCallCount(t, 0 /* expectedDetachCallCount */, fakePlugin) }
// NewAttachDetachController returns a new instance of AttachDetachController. func NewAttachDetachController( kubeClient internalclientset.Interface, podInformer framework.SharedInformer, nodeInformer framework.SharedInformer, pvcInformer framework.SharedInformer, pvInformer framework.SharedInformer, cloud cloudprovider.Interface, plugins []volume.VolumePlugin, recorder record.EventRecorder) (AttachDetachController, error) { // TODO: The default resyncPeriod for shared informers is 12 hours, this is // unacceptable for the attach/detach controller. For example, if a pod is // skipped because the node it is scheduled to didn't set its annotation in // time, we don't want to have to wait 12hrs before processing the pod // again. // Luckily https://github.com/kubernetes/kubernetes/issues/23394 is being // worked on and will split resync in to resync and relist. Once that // happens the resync period can be set to something much faster (30 // seconds). // If that issue is not resolved in time, then this controller will have to // consider some unappealing alternate options: use a non-shared informer // and set a faster resync period even if it causes relist, or requeue // dropped pods so they are continuously processed until it is accepted or // deleted (probably can't do this with sharedInformer), etc. adc := &attachDetachController{ kubeClient: kubeClient, pvcInformer: pvcInformer, pvInformer: pvInformer, cloud: cloud, } podInformer.AddEventHandler(framework.ResourceEventHandlerFuncs{ AddFunc: adc.podAdd, UpdateFunc: adc.podUpdate, DeleteFunc: adc.podDelete, }) nodeInformer.AddEventHandler(framework.ResourceEventHandlerFuncs{ AddFunc: adc.nodeAdd, UpdateFunc: adc.nodeUpdate, DeleteFunc: adc.nodeDelete, }) if err := adc.volumePluginMgr.InitPlugins(plugins, adc); err != nil { return nil, fmt.Errorf("Could not initialize volume plugins for Attach/Detach Controller: %+v", err) } adc.desiredStateOfWorld = cache.NewDesiredStateOfWorld(&adc.volumePluginMgr) adc.actualStateOfWorld = cache.NewActualStateOfWorld(&adc.volumePluginMgr) adc.attacherDetacher = operationexecutor.NewOperationExecutor( kubeClient, &adc.volumePluginMgr, recorder) adc.nodeStatusUpdater = statusupdater.NewNodeStatusUpdater( kubeClient, nodeInformer, adc.actualStateOfWorld) adc.reconciler = reconciler.NewReconciler( reconcilerLoopPeriod, reconcilerMaxWaitForUnmountDuration, adc.desiredStateOfWorld, adc.actualStateOfWorld, adc.attacherDetacher, adc.nodeStatusUpdater) adc.desiredStateOfWorldPopulator = populator.NewDesiredStateOfWorldPopulator( desiredStateOfWorldPopulatorLoopSleepPeriod, podInformer, adc.desiredStateOfWorld) return adc, nil }