k8s.io/kubernetes@v1.29.3/pkg/controller/volume/attachdetach/reconciler/reconciler_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 reconciler 18 19 import ( 20 "context" 21 "fmt" 22 "sync" 23 "testing" 24 "time" 25 26 v1 "k8s.io/api/core/v1" 27 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 28 k8stypes "k8s.io/apimachinery/pkg/types" 29 "k8s.io/apimachinery/pkg/util/wait" 30 "k8s.io/client-go/informers" 31 "k8s.io/client-go/tools/record" 32 "k8s.io/component-base/metrics/legacyregistry" 33 metricstestutil "k8s.io/component-base/metrics/testutil" 34 "k8s.io/klog/v2" 35 "k8s.io/klog/v2/ktesting" 36 "k8s.io/kubernetes/pkg/controller" 37 "k8s.io/kubernetes/pkg/controller/volume/attachdetach/cache" 38 "k8s.io/kubernetes/pkg/controller/volume/attachdetach/metrics" 39 "k8s.io/kubernetes/pkg/controller/volume/attachdetach/statusupdater" 40 controllervolumetesting "k8s.io/kubernetes/pkg/controller/volume/attachdetach/testing" 41 volumetesting "k8s.io/kubernetes/pkg/volume/testing" 42 "k8s.io/kubernetes/pkg/volume/util/operationexecutor" 43 "k8s.io/kubernetes/pkg/volume/util/types" 44 utilstrings "k8s.io/utils/strings" 45 ) 46 47 const ( 48 reconcilerLoopPeriod = 10 * time.Millisecond 49 syncLoopPeriod = 100 * time.Minute 50 maxWaitForUnmountDuration = 50 * time.Millisecond 51 maxLongWaitForUnmountDuration = 4200 * time.Second 52 volumeAttachedCheckTimeout = 5 * time.Second 53 ) 54 55 var registerMetrics sync.Once 56 57 // Calls Run() 58 // Verifies there are no calls to attach or detach. 59 func Test_Run_Positive_DoNothing(t *testing.T) { 60 // Arrange 61 volumePluginMgr, fakePlugin := volumetesting.GetTestVolumePluginMgr(t) 62 dsw := cache.NewDesiredStateOfWorld(volumePluginMgr) 63 asw := cache.NewActualStateOfWorld(volumePluginMgr) 64 65 fakeKubeClient := controllervolumetesting.CreateTestClient() 66 fakeRecorder := &record.FakeRecorder{} 67 fakeHandler := volumetesting.NewBlockVolumePathHandler() 68 ad := operationexecutor.NewOperationExecutor(operationexecutor.NewOperationGenerator( 69 fakeKubeClient, 70 volumePluginMgr, 71 fakeRecorder, 72 fakeHandler)) 73 informerFactory := informers.NewSharedInformerFactory(fakeKubeClient, controller.NoResyncPeriodFunc()) 74 nsu := statusupdater.NewNodeStatusUpdater( 75 fakeKubeClient, informerFactory.Core().V1().Nodes().Lister(), asw) 76 nodeLister := informerFactory.Core().V1().Nodes().Lister() 77 reconciler := NewReconciler( 78 reconcilerLoopPeriod, maxWaitForUnmountDuration, syncLoopPeriod, false, dsw, asw, ad, nsu, nodeLister, fakeRecorder) 79 80 // Act 81 _, ctx := ktesting.NewTestContext(t) 82 ctx, cancel := context.WithCancel(ctx) 83 defer cancel() 84 go reconciler.Run(ctx) 85 86 // Assert 87 waitForNewAttacherCallCount(t, 0 /* expectedCallCount */, fakePlugin) 88 verifyNewAttacherCallCount(t, true /* expectZeroNewAttacherCallCount */, fakePlugin) 89 verifyNewDetacherCallCount(t, true /* expectZeroNewDetacherCallCount */, fakePlugin) 90 waitForAttachCallCount(t, 0 /* expectedAttachCallCount */, fakePlugin) 91 waitForDetachCallCount(t, 0 /* expectedDetachCallCount */, fakePlugin) 92 } 93 94 // Populates desiredStateOfWorld cache with one node/volume/pod tuple. 95 // Calls Run() 96 // Verifies there is one attach call and no detach calls. 97 func Test_Run_Positive_OneDesiredVolumeAttach(t *testing.T) { 98 // Arrange 99 volumePluginMgr, fakePlugin := volumetesting.GetTestVolumePluginMgr(t) 100 dsw := cache.NewDesiredStateOfWorld(volumePluginMgr) 101 asw := cache.NewActualStateOfWorld(volumePluginMgr) 102 fakeKubeClient := controllervolumetesting.CreateTestClient() 103 fakeRecorder := &record.FakeRecorder{} 104 fakeHandler := volumetesting.NewBlockVolumePathHandler() 105 ad := operationexecutor.NewOperationExecutor(operationexecutor.NewOperationGenerator( 106 fakeKubeClient, 107 volumePluginMgr, 108 fakeRecorder, 109 fakeHandler)) 110 informerFactory := informers.NewSharedInformerFactory(fakeKubeClient, controller.NoResyncPeriodFunc()) 111 nsu := statusupdater.NewFakeNodeStatusUpdater(false /* returnError */) 112 nodeLister := informerFactory.Core().V1().Nodes().Lister() 113 reconciler := NewReconciler( 114 reconcilerLoopPeriod, maxWaitForUnmountDuration, syncLoopPeriod, false, dsw, asw, ad, nsu, nodeLister, fakeRecorder) 115 podName := "pod-uid" 116 volumeName := v1.UniqueVolumeName("volume-name") 117 volumeSpec := controllervolumetesting.GetTestVolumeSpec(string(volumeName), volumeName) 118 nodeName := k8stypes.NodeName("node-name") 119 dsw.AddNode(nodeName, false /*keepTerminatedPodVolumes*/) 120 volumeExists := dsw.VolumeExists(volumeName, nodeName) 121 if volumeExists { 122 t.Fatalf( 123 "Volume %q/node %q should not exist, but it does.", 124 volumeName, 125 nodeName) 126 } 127 128 _, podErr := dsw.AddPod(types.UniquePodName(podName), controllervolumetesting.NewPod(podName, podName), volumeSpec, nodeName) 129 if podErr != nil { 130 t.Fatalf("AddPod failed. Expected: <no error> Actual: <%v>", podErr) 131 } 132 133 // Act 134 _, ctx := ktesting.NewTestContext(t) 135 ctx, cancel := context.WithCancel(ctx) 136 defer cancel() 137 go reconciler.Run(ctx) 138 139 // Assert 140 waitForNewAttacherCallCount(t, 1 /* expectedCallCount */, fakePlugin) 141 waitForAttachCallCount(t, 1 /* expectedAttachCallCount */, fakePlugin) 142 verifyNewDetacherCallCount(t, true /* expectZeroNewDetacherCallCount */, fakePlugin) 143 } 144 145 // Populates desiredStateOfWorld cache with one node/volume/pod tuple. 146 // Calls Run() 147 // Verifies there is one attach call and no detach calls. 148 // Marks the node/volume as unmounted. 149 // Deletes the node/volume/pod tuple from desiredStateOfWorld cache. 150 // Verifies there is one detach call and no (new) attach calls. 151 func Test_Run_Positive_OneDesiredVolumeAttachThenDetachWithUnmountedVolume(t *testing.T) { 152 // Arrange 153 volumePluginMgr, fakePlugin := volumetesting.GetTestVolumePluginMgr(t) 154 dsw := cache.NewDesiredStateOfWorld(volumePluginMgr) 155 asw := cache.NewActualStateOfWorld(volumePluginMgr) 156 fakeKubeClient := controllervolumetesting.CreateTestClient() 157 fakeRecorder := &record.FakeRecorder{} 158 fakeHandler := volumetesting.NewBlockVolumePathHandler() 159 ad := operationexecutor.NewOperationExecutor(operationexecutor.NewOperationGenerator( 160 fakeKubeClient, 161 volumePluginMgr, 162 fakeRecorder, 163 fakeHandler)) 164 informerFactory := informers.NewSharedInformerFactory(fakeKubeClient, controller.NoResyncPeriodFunc()) 165 nsu := statusupdater.NewFakeNodeStatusUpdater(false /* returnError */) 166 nodeLister := informerFactory.Core().V1().Nodes().Lister() 167 reconciler := NewReconciler( 168 reconcilerLoopPeriod, maxWaitForUnmountDuration, syncLoopPeriod, false, dsw, asw, ad, nsu, nodeLister, fakeRecorder) 169 podName := "pod-uid" 170 volumeName := v1.UniqueVolumeName("volume-name") 171 volumeSpec := controllervolumetesting.GetTestVolumeSpec(string(volumeName), volumeName) 172 nodeName := k8stypes.NodeName("node-name") 173 dsw.AddNode(nodeName, false /*keepTerminatedPodVolumes*/) 174 volumeExists := dsw.VolumeExists(volumeName, nodeName) 175 if volumeExists { 176 t.Fatalf( 177 "Volume %q/node %q should not exist, but it does.", 178 volumeName, 179 nodeName) 180 } 181 182 generatedVolumeName, podAddErr := dsw.AddPod(types.UniquePodName(podName), controllervolumetesting.NewPod(podName, podName), volumeSpec, nodeName) 183 if podAddErr != nil { 184 t.Fatalf("AddPod failed. Expected: <no error> Actual: <%v>", podAddErr) 185 } 186 187 // Act 188 logger, ctx := ktesting.NewTestContext(t) 189 ctx, cancel := context.WithCancel(ctx) 190 defer cancel() 191 go reconciler.Run(ctx) 192 193 // Assert 194 waitForNewAttacherCallCount(t, 1 /* expectedCallCount */, fakePlugin) 195 verifyNewAttacherCallCount(t, false /* expectZeroNewAttacherCallCount */, fakePlugin) 196 waitForAttachCallCount(t, 1 /* expectedAttachCallCount */, fakePlugin) 197 verifyNewDetacherCallCount(t, true /* expectZeroNewDetacherCallCount */, fakePlugin) 198 waitForDetachCallCount(t, 0 /* expectedDetachCallCount */, fakePlugin) 199 200 // Act 201 dsw.DeletePod(types.UniquePodName(podName), generatedVolumeName, nodeName) 202 volumeExists = dsw.VolumeExists(generatedVolumeName, nodeName) 203 if volumeExists { 204 t.Fatalf( 205 "Deleted pod %q from volume %q/node %q. Volume should also be deleted but it still exists.", 206 podName, 207 generatedVolumeName, 208 nodeName) 209 } 210 asw.SetVolumeMountedByNode(logger, generatedVolumeName, nodeName, true /* mounted */) 211 asw.SetVolumeMountedByNode(logger, generatedVolumeName, nodeName, false /* mounted */) 212 213 // Assert 214 waitForNewDetacherCallCount(t, 1 /* expectedCallCount */, fakePlugin) 215 verifyNewAttacherCallCount(t, false /* expectZeroNewAttacherCallCount */, fakePlugin) 216 waitForAttachCallCount(t, 1 /* expectedAttachCallCount */, fakePlugin) 217 verifyNewDetacherCallCount(t, false /* expectZeroNewDetacherCallCount */, fakePlugin) 218 waitForDetachCallCount(t, 1 /* expectedDetachCallCount */, fakePlugin) 219 } 220 221 // Populates desiredStateOfWorld cache with one node/volume/pod tuple. 222 // Calls Run() 223 // Verifies there is one attach call and no detach calls. 224 // Deletes the node/volume/pod tuple from desiredStateOfWorld cache without first marking the node/volume as unmounted. 225 // Verifies there is one detach call and no (new) attach calls. 226 func Test_Run_Positive_OneDesiredVolumeAttachThenDetachWithMountedVolume(t *testing.T) { 227 registerMetrics.Do(func() { 228 legacyregistry.MustRegister(metrics.ForceDetachMetricCounter) 229 }) 230 // Arrange 231 volumePluginMgr, fakePlugin := volumetesting.GetTestVolumePluginMgr(t) 232 dsw := cache.NewDesiredStateOfWorld(volumePluginMgr) 233 asw := cache.NewActualStateOfWorld(volumePluginMgr) 234 fakeKubeClient := controllervolumetesting.CreateTestClient() 235 fakeRecorder := &record.FakeRecorder{} 236 fakeHandler := volumetesting.NewBlockVolumePathHandler() 237 ad := operationexecutor.NewOperationExecutor(operationexecutor.NewOperationGenerator( 238 fakeKubeClient, 239 volumePluginMgr, 240 fakeRecorder, 241 fakeHandler)) 242 informerFactory := informers.NewSharedInformerFactory(fakeKubeClient, controller.NoResyncPeriodFunc()) 243 nodeLister := informerFactory.Core().V1().Nodes().Lister() 244 nsu := statusupdater.NewFakeNodeStatusUpdater(false /* returnError */) 245 reconciler := NewReconciler( 246 reconcilerLoopPeriod, maxWaitForUnmountDuration, syncLoopPeriod, false, dsw, asw, ad, nsu, nodeLister, fakeRecorder) 247 podName := "pod-uid" 248 volumeName := v1.UniqueVolumeName("volume-name") 249 volumeSpec := controllervolumetesting.GetTestVolumeSpec(string(volumeName), volumeName) 250 nodeName := k8stypes.NodeName("node-name") 251 dsw.AddNode(nodeName, false /*keepTerminatedPodVolumes*/) 252 253 volumeExists := dsw.VolumeExists(volumeName, nodeName) 254 if volumeExists { 255 t.Fatalf( 256 "Volume %q/node %q should not exist, but it does.", 257 volumeName, 258 nodeName) 259 } 260 261 generatedVolumeName, podAddErr := dsw.AddPod(types.UniquePodName(podName), controllervolumetesting.NewPod(podName, podName), volumeSpec, nodeName) 262 if podAddErr != nil { 263 t.Fatalf("AddPod failed. Expected: <no error> Actual: <%v>", podAddErr) 264 } 265 266 // Act 267 _, ctx := ktesting.NewTestContext(t) 268 ctx, cancel := context.WithCancel(ctx) 269 defer cancel() 270 go reconciler.Run(ctx) 271 272 // Assert 273 waitForNewAttacherCallCount(t, 1 /* expectedCallCount */, fakePlugin) 274 verifyNewAttacherCallCount(t, false /* expectZeroNewAttacherCallCount */, fakePlugin) 275 waitForAttachCallCount(t, 1 /* expectedAttachCallCount */, fakePlugin) 276 verifyNewDetacherCallCount(t, true /* expectZeroNewDetacherCallCount */, fakePlugin) 277 waitForDetachCallCount(t, 0 /* expectedDetachCallCount */, fakePlugin) 278 279 // Act 280 dsw.DeletePod(types.UniquePodName(podName), generatedVolumeName, nodeName) 281 volumeExists = dsw.VolumeExists(generatedVolumeName, nodeName) 282 if volumeExists { 283 t.Fatalf( 284 "Deleted pod %q from volume %q/node %q. Volume should also be deleted but it still exists.", 285 podName, 286 generatedVolumeName, 287 nodeName) 288 } 289 290 // Assert -- Timer will trigger detach 291 waitForNewDetacherCallCount(t, 1 /* expectedCallCount */, fakePlugin) 292 verifyNewAttacherCallCount(t, false /* expectZeroNewAttacherCallCount */, fakePlugin) 293 waitForAttachCallCount(t, 1 /* expectedAttachCallCount */, fakePlugin) 294 verifyNewDetacherCallCount(t, false /* expectZeroNewDetacherCallCount */, fakePlugin) 295 waitForDetachCallCount(t, 1 /* expectedDetachCallCount */, fakePlugin) 296 297 // Force detach metric due to timeout 298 testForceDetachMetric(t, 1, metrics.ForceDetachReasonTimeout) 299 } 300 301 // Populates desiredStateOfWorld cache with one node/volume/pod tuple. 302 // Has node update fail 303 // Calls Run() 304 // Verifies there is one attach call and no detach calls. 305 // Marks the node/volume as unmounted. 306 // Deletes the node/volume/pod tuple from desiredStateOfWorld cache. 307 // Verifies there are NO detach call and no (new) attach calls. 308 func Test_Run_Negative_OneDesiredVolumeAttachThenDetachWithUnmountedVolumeUpdateStatusFail(t *testing.T) { 309 // Arrange 310 volumePluginMgr, fakePlugin := volumetesting.GetTestVolumePluginMgr(t) 311 dsw := cache.NewDesiredStateOfWorld(volumePluginMgr) 312 asw := cache.NewActualStateOfWorld(volumePluginMgr) 313 fakeKubeClient := controllervolumetesting.CreateTestClient() 314 fakeRecorder := &record.FakeRecorder{} 315 fakeHandler := volumetesting.NewBlockVolumePathHandler() 316 ad := operationexecutor.NewOperationExecutor(operationexecutor.NewOperationGenerator( 317 fakeKubeClient, 318 volumePluginMgr, 319 fakeRecorder, 320 fakeHandler)) 321 informerFactory := informers.NewSharedInformerFactory(fakeKubeClient, controller.NoResyncPeriodFunc()) 322 nodeLister := informerFactory.Core().V1().Nodes().Lister() 323 nsu := statusupdater.NewFakeNodeStatusUpdater(true /* returnError */) 324 reconciler := NewReconciler( 325 reconcilerLoopPeriod, maxWaitForUnmountDuration, syncLoopPeriod, false, dsw, asw, ad, nsu, nodeLister, fakeRecorder) 326 podName := "pod-uid" 327 volumeName := v1.UniqueVolumeName("volume-name") 328 volumeSpec := controllervolumetesting.GetTestVolumeSpec(string(volumeName), volumeName) 329 nodeName := k8stypes.NodeName("node-name") 330 dsw.AddNode(nodeName, false /*keepTerminatedPodVolumes*/) 331 volumeExists := dsw.VolumeExists(volumeName, nodeName) 332 if volumeExists { 333 t.Fatalf( 334 "Volume %q/node %q should not exist, but it does.", 335 volumeName, 336 nodeName) 337 } 338 339 generatedVolumeName, podAddErr := dsw.AddPod(types.UniquePodName(podName), controllervolumetesting.NewPod(podName, podName), volumeSpec, nodeName) 340 if podAddErr != nil { 341 t.Fatalf("AddPod failed. Expected: <no error> Actual: <%v>", podAddErr) 342 } 343 344 // Act 345 logger, ctx := ktesting.NewTestContext(t) 346 ctx, cancel := context.WithCancel(ctx) 347 defer cancel() 348 go reconciler.Run(ctx) 349 350 // Assert 351 waitForNewAttacherCallCount(t, 1 /* expectedCallCount */, fakePlugin) 352 verifyNewAttacherCallCount(t, false /* expectZeroNewAttacherCallCount */, fakePlugin) 353 waitForAttachCallCount(t, 1 /* expectedAttachCallCount */, fakePlugin) 354 verifyNewDetacherCallCount(t, true /* expectZeroNewDetacherCallCount */, fakePlugin) 355 waitForDetachCallCount(t, 0 /* expectedDetachCallCount */, fakePlugin) 356 357 // Act 358 dsw.DeletePod(types.UniquePodName(podName), generatedVolumeName, nodeName) 359 volumeExists = dsw.VolumeExists(generatedVolumeName, nodeName) 360 if volumeExists { 361 t.Fatalf( 362 "Deleted pod %q from volume %q/node %q. Volume should also be deleted but it still exists.", 363 podName, 364 generatedVolumeName, 365 nodeName) 366 } 367 asw.SetVolumeMountedByNode(logger, generatedVolumeName, nodeName, true /* mounted */) 368 asw.SetVolumeMountedByNode(logger, generatedVolumeName, nodeName, false /* mounted */) 369 370 // Assert 371 verifyNewDetacherCallCount(t, true /* expectZeroNewDetacherCallCount */, fakePlugin) 372 verifyNewAttacherCallCount(t, false /* expectZeroNewAttacherCallCount */, fakePlugin) 373 waitForAttachCallCount(t, 1 /* expectedAttachCallCount */, fakePlugin) 374 verifyNewDetacherCallCount(t, false /* expectZeroNewDetacherCallCount */, fakePlugin) 375 waitForDetachCallCount(t, 0 /* expectedDetachCallCount */, fakePlugin) 376 } 377 378 // Creates a volume with accessMode ReadWriteMany 379 // Populates desiredStateOfWorld cache with two node/volume/pod tuples pointing to the created volume 380 // Calls Run() 381 // Verifies there are two attach calls and no detach calls. 382 // Deletes the first node/volume/pod tuple from desiredStateOfWorld cache without first marking the node/volume as unmounted. 383 // Verifies there is one detach call and no (new) attach calls. 384 // Deletes the second node/volume/pod tuple from desiredStateOfWorld cache without first marking the node/volume as unmounted. 385 // Verifies there are two detach calls and no (new) attach calls. 386 func Test_Run_OneVolumeAttachAndDetachMultipleNodesWithReadWriteMany(t *testing.T) { 387 // Arrange 388 volumePluginMgr, fakePlugin := volumetesting.GetTestVolumePluginMgr(t) 389 dsw := cache.NewDesiredStateOfWorld(volumePluginMgr) 390 asw := cache.NewActualStateOfWorld(volumePluginMgr) 391 fakeKubeClient := controllervolumetesting.CreateTestClient() 392 fakeRecorder := &record.FakeRecorder{} 393 fakeHandler := volumetesting.NewBlockVolumePathHandler() 394 ad := operationexecutor.NewOperationExecutor(operationexecutor.NewOperationGenerator( 395 fakeKubeClient, 396 volumePluginMgr, 397 fakeRecorder, 398 fakeHandler)) 399 nsu := statusupdater.NewFakeNodeStatusUpdater(false /* returnError */) 400 informerFactory := informers.NewSharedInformerFactory(fakeKubeClient, controller.NoResyncPeriodFunc()) 401 nodeLister := informerFactory.Core().V1().Nodes().Lister() 402 reconciler := NewReconciler( 403 reconcilerLoopPeriod, maxWaitForUnmountDuration, syncLoopPeriod, false, dsw, asw, ad, nsu, nodeLister, fakeRecorder) 404 podName1 := "pod-uid1" 405 podName2 := "pod-uid2" 406 volumeName := v1.UniqueVolumeName("volume-name") 407 volumeSpec := controllervolumetesting.GetTestVolumeSpec(string(volumeName), volumeName) 408 volumeSpec.PersistentVolume.Spec.AccessModes = []v1.PersistentVolumeAccessMode{v1.ReadWriteMany} 409 nodeName1 := k8stypes.NodeName("node-name1") 410 nodeName2 := k8stypes.NodeName(volumetesting.MultiAttachNode) 411 dsw.AddNode(nodeName1, false /*keepTerminatedPodVolumes*/) 412 dsw.AddNode(nodeName2, false /*keepTerminatedPodVolumes*/) 413 414 generatedVolumeName, podAddErr := dsw.AddPod(types.UniquePodName(podName1), controllervolumetesting.NewPod(podName1, podName1), volumeSpec, nodeName1) 415 if podAddErr != nil { 416 t.Fatalf("AddPod failed. Expected: <no error> Actual: <%v>", podAddErr) 417 } 418 419 _, podAddErr = dsw.AddPod(types.UniquePodName(podName2), controllervolumetesting.NewPod(podName2, podName2), volumeSpec, nodeName2) 420 if podAddErr != nil { 421 t.Fatalf("AddPod failed. Expected: <no error> Actual: <%v>", podAddErr) 422 } 423 424 // Act 425 _, ctx := ktesting.NewTestContext(t) 426 ctx, cancel := context.WithCancel(ctx) 427 defer cancel() 428 go reconciler.Run(ctx) 429 430 // Assert 431 waitForNewAttacherCallCount(t, 2 /* expectedCallCount */, fakePlugin) 432 verifyNewAttacherCallCount(t, false /* expectZeroNewAttacherCallCount */, fakePlugin) 433 waitForTotalAttachCallCount(t, 2 /* expectedAttachCallCount */, fakePlugin) 434 verifyNewDetacherCallCount(t, true /* expectZeroNewDetacherCallCount */, fakePlugin) 435 waitForDetachCallCount(t, 0 /* expectedDetachCallCount */, fakePlugin) 436 waitForAttachedToNodesCount(t, 2 /* expectedNodeCount */, generatedVolumeName, asw) 437 438 // Act 439 dsw.DeletePod(types.UniquePodName(podName1), generatedVolumeName, nodeName1) 440 volumeExists := dsw.VolumeExists(generatedVolumeName, nodeName1) 441 if volumeExists { 442 t.Fatalf( 443 "Deleted pod %q from volume %q/node %q. Volume should also be deleted but it still exists.", 444 podName1, 445 generatedVolumeName, 446 nodeName1) 447 } 448 449 // Assert -- Timer will trigger detach 450 waitForNewDetacherCallCount(t, 1 /* expectedCallCount */, fakePlugin) 451 verifyNewAttacherCallCount(t, false /* expectZeroNewAttacherCallCount */, fakePlugin) 452 waitForTotalAttachCallCount(t, 2 /* expectedAttachCallCount */, fakePlugin) 453 verifyNewDetacherCallCount(t, false /* expectZeroNewDetacherCallCount */, fakePlugin) 454 waitForTotalDetachCallCount(t, 1 /* expectedDetachCallCount */, fakePlugin) 455 456 // Act 457 dsw.DeletePod(types.UniquePodName(podName2), generatedVolumeName, nodeName2) 458 volumeExists = dsw.VolumeExists(generatedVolumeName, nodeName2) 459 if volumeExists { 460 t.Fatalf( 461 "Deleted pod %q from volume %q/node %q. Volume should also be deleted but it still exists.", 462 podName2, 463 generatedVolumeName, 464 nodeName2) 465 } 466 467 // Assert -- Timer will trigger detach 468 waitForNewDetacherCallCount(t, 2 /* expectedCallCount */, fakePlugin) 469 verifyNewAttacherCallCount(t, false /* expectZeroNewAttacherCallCount */, fakePlugin) 470 waitForTotalAttachCallCount(t, 2 /* expectedAttachCallCount */, fakePlugin) 471 verifyNewDetacherCallCount(t, false /* expectZeroNewDetacherCallCount */, fakePlugin) 472 waitForTotalDetachCallCount(t, 2 /* expectedDetachCallCount */, fakePlugin) 473 } 474 475 // Creates a volume with accessMode ReadWriteOnce 476 // Populates desiredStateOfWorld cache with two ode/volume/pod tuples pointing to the created volume 477 // Calls Run() 478 // Verifies there is one attach call and no detach calls. 479 // Deletes the node/volume/pod tuple from desiredStateOfWorld which succeeded in attaching 480 // Verifies there are two attach call and one detach call. 481 func Test_Run_OneVolumeAttachAndDetachMultipleNodesWithReadWriteOnce(t *testing.T) { 482 // Arrange 483 volumePluginMgr, fakePlugin := volumetesting.GetTestVolumePluginMgr(t) 484 dsw := cache.NewDesiredStateOfWorld(volumePluginMgr) 485 asw := cache.NewActualStateOfWorld(volumePluginMgr) 486 fakeKubeClient := controllervolumetesting.CreateTestClient() 487 fakeRecorder := &record.FakeRecorder{} 488 fakeHandler := volumetesting.NewBlockVolumePathHandler() 489 ad := operationexecutor.NewOperationExecutor(operationexecutor.NewOperationGenerator( 490 fakeKubeClient, 491 volumePluginMgr, 492 fakeRecorder, 493 fakeHandler)) 494 informerFactory := informers.NewSharedInformerFactory(fakeKubeClient, controller.NoResyncPeriodFunc()) 495 nodeLister := informerFactory.Core().V1().Nodes().Lister() 496 nsu := statusupdater.NewFakeNodeStatusUpdater(false /* returnError */) 497 reconciler := NewReconciler( 498 reconcilerLoopPeriod, maxWaitForUnmountDuration, syncLoopPeriod, false, dsw, asw, ad, nsu, nodeLister, fakeRecorder) 499 podName1 := "pod-uid1" 500 podName2 := "pod-uid2" 501 volumeName := v1.UniqueVolumeName("volume-name") 502 volumeSpec := controllervolumetesting.GetTestVolumeSpec(string(volumeName), volumeName) 503 volumeSpec.PersistentVolume.Spec.AccessModes = []v1.PersistentVolumeAccessMode{v1.ReadWriteOnce} 504 nodeName1 := k8stypes.NodeName("node-name1") 505 nodeName2 := k8stypes.NodeName("node-name2") 506 dsw.AddNode(nodeName1, false /*keepTerminatedPodVolumes*/) 507 dsw.AddNode(nodeName2, false /*keepTerminatedPodVolumes*/) 508 509 // Add both pods at the same time to provoke a potential race condition in the reconciler 510 generatedVolumeName, podAddErr := dsw.AddPod(types.UniquePodName(podName1), controllervolumetesting.NewPod(podName1, podName1), volumeSpec, nodeName1) 511 if podAddErr != nil { 512 t.Fatalf("AddPod failed. Expected: <no error> Actual: <%v>", podAddErr) 513 } 514 _, podAddErr = dsw.AddPod(types.UniquePodName(podName2), controllervolumetesting.NewPod(podName2, podName2), volumeSpec, nodeName2) 515 if podAddErr != nil { 516 t.Fatalf("AddPod failed. Expected: <no error> Actual: <%v>", podAddErr) 517 } 518 519 // Act 520 _, ctx := ktesting.NewTestContext(t) 521 ctx, cancel := context.WithCancel(ctx) 522 defer cancel() 523 go reconciler.Run(ctx) 524 525 // Assert 526 waitForNewAttacherCallCount(t, 1 /* expectedCallCount */, fakePlugin) 527 verifyNewAttacherCallCount(t, false /* expectZeroNewAttacherCallCount */, fakePlugin) 528 waitForTotalAttachCallCount(t, 1 /* expectedAttachCallCount */, fakePlugin) 529 verifyNewDetacherCallCount(t, true /* expectZeroNewDetacherCallCount */, fakePlugin) 530 waitForDetachCallCount(t, 0 /* expectedDetachCallCount */, fakePlugin) 531 waitForAttachedToNodesCount(t, 1 /* expectedNodeCount */, generatedVolumeName, asw) 532 533 nodesForVolume := asw.GetNodesForAttachedVolume(generatedVolumeName) 534 535 // check if multiattach is marked 536 // at least one volume+node should be marked with multiattach error 537 nodeAttachedTo := nodesForVolume[0] 538 waitForMultiAttachErrorOnNode(t, nodeAttachedTo, dsw) 539 540 // Act 541 podToDelete := "" 542 if nodesForVolume[0] == nodeName1 { 543 podToDelete = podName1 544 } else if nodesForVolume[0] == nodeName2 { 545 podToDelete = podName2 546 } else { 547 t.Fatal("Volume attached to unexpected node") 548 } 549 550 dsw.DeletePod(types.UniquePodName(podToDelete), generatedVolumeName, nodesForVolume[0]) 551 volumeExists := dsw.VolumeExists(generatedVolumeName, nodesForVolume[0]) 552 if volumeExists { 553 t.Fatalf( 554 "Deleted pod %q from volume %q/node %q. Volume should also be deleted but it still exists.", 555 podToDelete, 556 generatedVolumeName, 557 nodesForVolume[0]) 558 } 559 560 // Assert 561 waitForNewDetacherCallCount(t, 1 /* expectedCallCount */, fakePlugin) 562 verifyNewDetacherCallCount(t, false /* expectZeroNewDetacherCallCount */, fakePlugin) 563 waitForTotalDetachCallCount(t, 1 /* expectedDetachCallCount */, fakePlugin) 564 waitForNewAttacherCallCount(t, 2 /* expectedCallCount */, fakePlugin) 565 verifyNewAttacherCallCount(t, false /* expectZeroNewAttacherCallCount */, fakePlugin) 566 waitForTotalAttachCallCount(t, 2 /* expectedAttachCallCount */, fakePlugin) 567 } 568 569 // Creates a volume with accessMode ReadWriteOnce 570 // First create a pod which will try to attach the volume to the a node named "uncertain-node". The attach call for this node will 571 // fail for timeout, but the volume will be actually attached to the node after the call. 572 // Secondly, delete this pod. 573 // Lastly, create a pod scheduled to a normal node which will trigger attach volume to the node. The attach should return successfully. 574 func Test_Run_OneVolumeAttachAndDetachUncertainNodesWithReadWriteOnce(t *testing.T) { 575 // Arrange 576 volumePluginMgr, _ := volumetesting.GetTestVolumePluginMgr(t) 577 dsw := cache.NewDesiredStateOfWorld(volumePluginMgr) 578 asw := cache.NewActualStateOfWorld(volumePluginMgr) 579 fakeKubeClient := controllervolumetesting.CreateTestClient() 580 fakeRecorder := &record.FakeRecorder{} 581 fakeHandler := volumetesting.NewBlockVolumePathHandler() 582 ad := operationexecutor.NewOperationExecutor(operationexecutor.NewOperationGenerator( 583 fakeKubeClient, 584 volumePluginMgr, 585 fakeRecorder, 586 fakeHandler)) 587 informerFactory := informers.NewSharedInformerFactory(fakeKubeClient, controller.NoResyncPeriodFunc()) 588 nodeLister := informerFactory.Core().V1().Nodes().Lister() 589 nsu := statusupdater.NewFakeNodeStatusUpdater(false /* returnError */) 590 reconciler := NewReconciler( 591 reconcilerLoopPeriod, maxWaitForUnmountDuration, syncLoopPeriod, false, dsw, asw, ad, nsu, nodeLister, fakeRecorder) 592 podName1 := "pod-uid1" 593 podName2 := "pod-uid2" 594 volumeName := v1.UniqueVolumeName("volume-name") 595 volumeSpec := controllervolumetesting.GetTestVolumeSpec(string(volumeName), volumeName) 596 volumeSpec.PersistentVolume.Spec.AccessModes = []v1.PersistentVolumeAccessMode{v1.ReadWriteOnce} 597 nodeName1 := k8stypes.NodeName(volumetesting.UncertainAttachNode) 598 nodeName2 := k8stypes.NodeName("node-name2") 599 dsw.AddNode(nodeName1, false /*keepTerminatedPodVolumes*/) 600 dsw.AddNode(nodeName2, false /*keepTerminatedPodVolumes*/) 601 602 // Act 603 logger, ctx := ktesting.NewTestContext(t) 604 ctx, cancel := context.WithCancel(ctx) 605 defer cancel() 606 go reconciler.Run(ctx) 607 608 // Add the pod in which the volume is attached to the uncertain node 609 generatedVolumeName, podAddErr := dsw.AddPod(types.UniquePodName(podName1), controllervolumetesting.NewPod(podName1, podName1), volumeSpec, nodeName1) 610 if podAddErr != nil { 611 t.Fatalf("AddPod failed. Expected: <no error> Actual: <%v>", podAddErr) 612 } 613 614 time.Sleep(1 * time.Second) 615 // Volume is added to asw. Because attach operation fails, volume should not be reported as attached to the node. 616 waitForVolumeAddedToNode(t, generatedVolumeName, nodeName1, asw) 617 verifyVolumeAttachedToNode(t, generatedVolumeName, nodeName1, cache.AttachStateAttached, asw) 618 verifyVolumeReportedAsAttachedToNode(t, logger, generatedVolumeName, nodeName1, true, asw, volumeAttachedCheckTimeout) 619 620 // When volume is added to the node, it is set to mounted by default. Then the status will be updated by checking node status VolumeInUse. 621 // Without this, the delete operation will be delayed due to mounted status 622 asw.SetVolumeMountedByNode(logger, generatedVolumeName, nodeName1, false /* mounted */) 623 624 dsw.DeletePod(types.UniquePodName(podName1), generatedVolumeName, nodeName1) 625 626 waitForVolumeRemovedFromNode(t, generatedVolumeName, nodeName1, asw) 627 628 // Add a second pod which tries to attach the volume to a different node. 629 generatedVolumeName, podAddErr = dsw.AddPod(types.UniquePodName(podName2), controllervolumetesting.NewPod(podName2, podName2), volumeSpec, nodeName2) 630 if podAddErr != nil { 631 t.Fatalf("AddPod failed. Expected: <no error> Actual: <%v>", podAddErr) 632 } 633 waitForVolumeAttachedToNode(t, generatedVolumeName, nodeName2, asw) 634 verifyVolumeAttachedToNode(t, generatedVolumeName, nodeName2, cache.AttachStateAttached, asw) 635 636 } 637 638 func Test_Run_UpdateNodeStatusFailBeforeOneVolumeDetachNodeWithReadWriteOnce(t *testing.T) { 639 // Arrange 640 volumePluginMgr, _ := volumetesting.GetTestVolumePluginMgr(t) 641 dsw := cache.NewDesiredStateOfWorld(volumePluginMgr) 642 asw := cache.NewActualStateOfWorld(volumePluginMgr) 643 fakeKubeClient := controllervolumetesting.CreateTestClient() 644 fakeRecorder := &record.FakeRecorder{} 645 fakeHandler := volumetesting.NewBlockVolumePathHandler() 646 ad := operationexecutor.NewOperationExecutor(operationexecutor.NewOperationGenerator( 647 fakeKubeClient, 648 volumePluginMgr, 649 fakeRecorder, 650 fakeHandler)) 651 informerFactory := informers.NewSharedInformerFactory(fakeKubeClient, controller.NoResyncPeriodFunc()) 652 nodeLister := informerFactory.Core().V1().Nodes().Lister() 653 nsu := statusupdater.NewFakeNodeStatusUpdater(false /* returnError */) 654 logger, ctx := ktesting.NewTestContext(t) 655 ctx, cancel := context.WithCancel(ctx) 656 defer cancel() 657 rc := NewReconciler( 658 reconcilerLoopPeriod, maxWaitForUnmountDuration, syncLoopPeriod, false, dsw, asw, ad, nsu, nodeLister, fakeRecorder) 659 reconciliationLoopFunc := rc.(*reconciler).reconciliationLoopFunc(ctx) 660 podName1 := "pod-uid1" 661 volumeName := v1.UniqueVolumeName("volume-name") 662 volumeSpec := controllervolumetesting.GetTestVolumeSpec(string(volumeName), volumeName) 663 volumeSpec.PersistentVolume.Spec.AccessModes = []v1.PersistentVolumeAccessMode{v1.ReadWriteOnce} 664 nodeName1 := k8stypes.NodeName("node-name1") 665 dsw.AddNode(nodeName1, false /*keepTerminatedPodVolumes*/) 666 667 // Add the pod in which the volume is attached to the FailDetachNode 668 generatedVolumeName, podAddErr := dsw.AddPod(types.UniquePodName(podName1), controllervolumetesting.NewPod(podName1, podName1), volumeSpec, nodeName1) 669 if podAddErr != nil { 670 t.Fatalf("AddPod failed. Expected: <no error> Actual: <%v>", podAddErr) 671 } 672 673 // Act 674 reconciliationLoopFunc(ctx) 675 676 // Volume is added to asw, volume should be reported as attached to the node. 677 waitForVolumeAddedToNode(t, generatedVolumeName, nodeName1, asw) 678 verifyVolumeAttachedToNode(t, generatedVolumeName, nodeName1, cache.AttachStateAttached, asw) 679 verifyVolumeReportedAsAttachedToNode(t, logger, generatedVolumeName, nodeName1, true, asw, volumeAttachedCheckTimeout) 680 681 // Delete the pod 682 dsw.DeletePod(types.UniquePodName(podName1), generatedVolumeName, nodeName1) 683 684 // Mock NodeStatusUpdate fail 685 rc.(*reconciler).nodeStatusUpdater = statusupdater.NewFakeNodeStatusUpdater(true /* returnError */) 686 reconciliationLoopFunc(ctx) 687 // The first detach will be triggered after at least 50ms (maxWaitForUnmountDuration in test). 688 time.Sleep(100 * time.Millisecond) 689 reconciliationLoopFunc(ctx) 690 // Right before detach operation is performed, the volume will be first removed from being reported 691 // as attached on node status (RemoveVolumeFromReportAsAttached). After UpdateNodeStatus operation which is expected to fail, 692 // controller then added the volume back as attached. 693 // verifyVolumeReportedAsAttachedToNode will check volume is in the list of volume attached that needs to be updated 694 // in node status. By calling this function (GetVolumesToReportAttached), node status should be updated, and the volume 695 // will not need to be updated until new changes are applied (detach is triggered again) 696 verifyVolumeAttachedToNode(t, generatedVolumeName, nodeName1, cache.AttachStateAttached, asw) 697 verifyVolumeReportedAsAttachedToNode(t, logger, generatedVolumeName, nodeName1, true, asw, volumeAttachedCheckTimeout) 698 699 } 700 701 func Test_Run_OneVolumeDetachFailNodeWithReadWriteOnce(t *testing.T) { 702 // Arrange 703 volumePluginMgr, _ := volumetesting.GetTestVolumePluginMgr(t) 704 dsw := cache.NewDesiredStateOfWorld(volumePluginMgr) 705 asw := cache.NewActualStateOfWorld(volumePluginMgr) 706 fakeKubeClient := controllervolumetesting.CreateTestClient() 707 fakeRecorder := &record.FakeRecorder{} 708 fakeHandler := volumetesting.NewBlockVolumePathHandler() 709 ad := operationexecutor.NewOperationExecutor(operationexecutor.NewOperationGenerator( 710 fakeKubeClient, 711 volumePluginMgr, 712 fakeRecorder, 713 fakeHandler)) 714 informerFactory := informers.NewSharedInformerFactory(fakeKubeClient, controller.NoResyncPeriodFunc()) 715 nodeLister := informerFactory.Core().V1().Nodes().Lister() 716 nsu := statusupdater.NewFakeNodeStatusUpdater(false /* returnError */) 717 reconciler := NewReconciler( 718 reconcilerLoopPeriod, maxWaitForUnmountDuration, syncLoopPeriod, false, dsw, asw, ad, nsu, nodeLister, fakeRecorder) 719 podName1 := "pod-uid1" 720 podName2 := "pod-uid2" 721 podName3 := "pod-uid3" 722 volumeName := v1.UniqueVolumeName("volume-name") 723 volumeSpec := controllervolumetesting.GetTestVolumeSpec(string(volumeName), volumeName) 724 volumeSpec.PersistentVolume.Spec.AccessModes = []v1.PersistentVolumeAccessMode{v1.ReadWriteOnce} 725 nodeName1 := k8stypes.NodeName(volumetesting.FailDetachNode) 726 nodeName2 := k8stypes.NodeName("node-name2") 727 dsw.AddNode(nodeName1, false /*keepTerminatedPodVolumes*/) 728 dsw.AddNode(nodeName2, false /*keepTerminatedPodVolumes*/) 729 730 // Act 731 logger, ctx := ktesting.NewTestContext(t) 732 ctx, cancel := context.WithCancel(ctx) 733 defer cancel() 734 go reconciler.Run(ctx) 735 736 // Add the pod in which the volume is attached to the FailDetachNode 737 generatedVolumeName, podAddErr := dsw.AddPod(types.UniquePodName(podName1), controllervolumetesting.NewPod(podName1, podName1), volumeSpec, nodeName1) 738 if podAddErr != nil { 739 t.Fatalf("AddPod failed. Expected: <no error> Actual: <%v>", podAddErr) 740 } 741 742 // Volume is added to asw, volume should be reported as attached to the node. 743 waitForVolumeAddedToNode(t, generatedVolumeName, nodeName1, asw) 744 verifyVolumeAttachedToNode(t, generatedVolumeName, nodeName1, cache.AttachStateAttached, asw) 745 verifyVolumeReportedAsAttachedToNode(t, logger, generatedVolumeName, nodeName1, true, asw, volumeAttachedCheckTimeout) 746 747 // Delete the pod, but detach will fail 748 dsw.DeletePod(types.UniquePodName(podName1), generatedVolumeName, nodeName1) 749 750 // The first detach will be triggered after at least 50ms (maxWaitForUnmountDuration in test). 751 // Right before detach operation is performed, the volume will be first removed from being reported 752 // as attached on node status (RemoveVolumeFromReportAsAttached). After detach operation which is expected to fail, 753 // controller then treats the attachment as Uncertain. 754 // Here it sleeps 100ms so that detach should be triggered already at this point. 755 time.Sleep(100 * time.Millisecond) 756 verifyVolumeAttachedToNode(t, generatedVolumeName, nodeName1, cache.AttachStateUncertain, asw) 757 verifyVolumeReportedAsAttachedToNode(t, logger, generatedVolumeName, nodeName1, false, asw, volumeAttachedCheckTimeout) 758 759 // Add a second pod which tries to attach the volume to the same node. 760 // After adding pod to the same node, detach will not be triggered any more, 761 // the volume gets attached and reported as attached to the node. 762 generatedVolumeName, podAddErr = dsw.AddPod(types.UniquePodName(podName2), controllervolumetesting.NewPod(podName2, podName2), volumeSpec, nodeName1) 763 if podAddErr != nil { 764 t.Fatalf("AddPod failed. Expected: <no error> Actual: <%v>", podAddErr) 765 } 766 // Sleep 1s to verify no detach are triggered after second pod is added in the future. 767 time.Sleep(1000 * time.Millisecond) 768 verifyVolumeAttachedToNode(t, generatedVolumeName, nodeName1, cache.AttachStateAttached, asw) 769 verifyVolumeReportedAsAttachedToNode(t, logger, generatedVolumeName, nodeName1, true, asw, volumeAttachedCheckTimeout) 770 // verifyVolumeNoStatusUpdateNeeded(t, logger, generatedVolumeName, nodeName1, asw) 771 772 // Add a third pod which tries to attach the volume to a different node. 773 // At this point, volume is still attached to first node. There are no status update for both nodes. 774 generatedVolumeName, podAddErr = dsw.AddPod(types.UniquePodName(podName3), controllervolumetesting.NewPod(podName3, podName3), volumeSpec, nodeName2) 775 if podAddErr != nil { 776 t.Fatalf("AddPod failed. Expected: <no error> Actual: <%v>", podAddErr) 777 } 778 verifyVolumeAttachedToNode(t, generatedVolumeName, nodeName1, cache.AttachStateAttached, asw) 779 verifyVolumeNoStatusUpdateNeeded(t, logger, generatedVolumeName, nodeName1, asw) 780 verifyVolumeNoStatusUpdateNeeded(t, logger, generatedVolumeName, nodeName2, asw) 781 } 782 783 // Creates a volume with accessMode ReadWriteOnce 784 // First create a pod which will try to attach the volume to the a node named "timeout-node". The attach call for this node will 785 // fail for timeout, but the volume will be actually attached to the node after the call. 786 // Secondly, delete the this pod. 787 // Lastly, create a pod scheduled to a normal node which will trigger attach volume to the node. The attach should return successfully. 788 func Test_Run_OneVolumeAttachAndDetachTimeoutNodesWithReadWriteOnce(t *testing.T) { 789 // Arrange 790 volumePluginMgr, _ := volumetesting.GetTestVolumePluginMgr(t) 791 dsw := cache.NewDesiredStateOfWorld(volumePluginMgr) 792 asw := cache.NewActualStateOfWorld(volumePluginMgr) 793 fakeKubeClient := controllervolumetesting.CreateTestClient() 794 fakeRecorder := &record.FakeRecorder{} 795 fakeHandler := volumetesting.NewBlockVolumePathHandler() 796 ad := operationexecutor.NewOperationExecutor(operationexecutor.NewOperationGenerator( 797 fakeKubeClient, 798 volumePluginMgr, 799 fakeRecorder, 800 fakeHandler)) 801 informerFactory := informers.NewSharedInformerFactory(fakeKubeClient, controller.NoResyncPeriodFunc()) 802 nodeLister := informerFactory.Core().V1().Nodes().Lister() 803 nsu := statusupdater.NewFakeNodeStatusUpdater(false /* returnError */) 804 reconciler := NewReconciler( 805 reconcilerLoopPeriod, maxWaitForUnmountDuration, syncLoopPeriod, false, dsw, asw, ad, nsu, nodeLister, fakeRecorder) 806 podName1 := "pod-uid1" 807 podName2 := "pod-uid2" 808 volumeName := v1.UniqueVolumeName("volume-name") 809 volumeSpec := controllervolumetesting.GetTestVolumeSpec(string(volumeName), volumeName) 810 volumeSpec.PersistentVolume.Spec.AccessModes = []v1.PersistentVolumeAccessMode{v1.ReadWriteOnce} 811 nodeName1 := k8stypes.NodeName(volumetesting.TimeoutAttachNode) 812 nodeName2 := k8stypes.NodeName("node-name2") 813 dsw.AddNode(nodeName1, false /*keepTerminatedPodVolumes*/) 814 dsw.AddNode(nodeName2, false /*keepTerminatedPodVolumes*/) 815 816 // Act 817 logger, ctx := ktesting.NewTestContext(t) 818 ctx, cancel := context.WithCancel(ctx) 819 defer cancel() 820 go reconciler.Run(ctx) 821 822 // Add the pod in which the volume is attached to the timeout node 823 generatedVolumeName, podAddErr := dsw.AddPod(types.UniquePodName(podName1), controllervolumetesting.NewPod(podName1, podName1), volumeSpec, nodeName1) 824 if podAddErr != nil { 825 t.Fatalf("AddPod failed. Expected: <no error> Actual: <%v>", podAddErr) 826 } 827 828 // Volume is added to asw. Because attach operation fails, volume should not be reported as attached to the node. 829 waitForVolumeAddedToNode(t, generatedVolumeName, nodeName1, asw) 830 verifyVolumeAttachedToNode(t, generatedVolumeName, nodeName1, cache.AttachStateUncertain, asw) 831 verifyVolumeReportedAsAttachedToNode(t, logger, generatedVolumeName, nodeName1, false, asw, volumeAttachedCheckTimeout) 832 833 // When volume is added to the node, it is set to mounted by default. Then the status will be updated by checking node status VolumeInUse. 834 // Without this, the delete operation will be delayed due to mounted status 835 asw.SetVolumeMountedByNode(logger, generatedVolumeName, nodeName1, false /* mounted */) 836 837 dsw.DeletePod(types.UniquePodName(podName1), generatedVolumeName, nodeName1) 838 839 waitForVolumeRemovedFromNode(t, generatedVolumeName, nodeName1, asw) 840 841 // Add a second pod which tries to attach the volume to a different node. 842 generatedVolumeName, podAddErr = dsw.AddPod(types.UniquePodName(podName2), controllervolumetesting.NewPod(podName2, podName2), volumeSpec, nodeName2) 843 if podAddErr != nil { 844 t.Fatalf("AddPod failed. Expected: <no error> Actual: <%v>", podAddErr) 845 } 846 waitForVolumeAttachedToNode(t, generatedVolumeName, nodeName2, asw) 847 verifyVolumeAttachedToNode(t, generatedVolumeName, nodeName2, cache.AttachStateAttached, asw) 848 849 } 850 851 // Populates desiredStateOfWorld cache with one node/volume/pod tuple. 852 // The node has node.kubernetes.io/out-of-service taint present. 853 // 854 // The maxWaitForUnmountDuration is longer (in this case it is 4200 * time.Second so that detach does not happen 855 // immediately due to timeout. 856 // 857 // Calls Run() 858 // Verifies there is one attach call and no detach calls. 859 // Deletes the pod from desiredStateOfWorld cache without first marking the node/volume as unmounted. 860 // Verifies there is one detach call and no (new) attach calls. 861 func Test_Run_OneVolumeDetachOnOutOfServiceTaintedNode(t *testing.T) { 862 registerMetrics.Do(func() { 863 legacyregistry.MustRegister(metrics.ForceDetachMetricCounter) 864 }) 865 // Arrange 866 volumePluginMgr, fakePlugin := volumetesting.GetTestVolumePluginMgr(t) 867 dsw := cache.NewDesiredStateOfWorld(volumePluginMgr) 868 asw := cache.NewActualStateOfWorld(volumePluginMgr) 869 fakeKubeClient := controllervolumetesting.CreateTestClient() 870 fakeRecorder := &record.FakeRecorder{} 871 fakeHandler := volumetesting.NewBlockVolumePathHandler() 872 ad := operationexecutor.NewOperationExecutor(operationexecutor.NewOperationGenerator( 873 fakeKubeClient, 874 volumePluginMgr, 875 fakeRecorder, 876 fakeHandler)) 877 informerFactory := informers.NewSharedInformerFactory(fakeKubeClient, controller.NoResyncPeriodFunc()) 878 nsu := statusupdater.NewFakeNodeStatusUpdater(false /* returnError */) 879 nodeLister := informerFactory.Core().V1().Nodes().Lister() 880 reconciler := NewReconciler( 881 reconcilerLoopPeriod, maxLongWaitForUnmountDuration, syncLoopPeriod, false, dsw, asw, ad, 882 nsu, nodeLister, fakeRecorder) 883 podName1 := "pod-uid1" 884 volumeName1 := v1.UniqueVolumeName("volume-name1") 885 volumeSpec1 := controllervolumetesting.GetTestVolumeSpec(string(volumeName1), volumeName1) 886 nodeName1 := k8stypes.NodeName("worker-0") 887 node1 := &v1.Node{ 888 ObjectMeta: metav1.ObjectMeta{Name: string(nodeName1)}, 889 Spec: v1.NodeSpec{ 890 Taints: []v1.Taint{{Key: v1.TaintNodeOutOfService, Effect: v1.TaintEffectNoExecute}}, 891 }, 892 } 893 informerFactory.Core().V1().Nodes().Informer().GetStore().Add(node1) 894 dsw.AddNode(nodeName1, false /*keepTerminatedPodVolumes*/) 895 volumeExists := dsw.VolumeExists(volumeName1, nodeName1) 896 if volumeExists { 897 t.Fatalf( 898 "Volume %q/node %q should not exist, but it does.", 899 volumeName1, 900 nodeName1) 901 } 902 903 generatedVolumeName, podErr := dsw.AddPod(types.UniquePodName(podName1), controllervolumetesting.NewPod(podName1, 904 podName1), volumeSpec1, nodeName1) 905 if podErr != nil { 906 t.Fatalf("AddPod failed. Expected: <no error> Actual: <%v>", podErr) 907 } 908 909 // Act 910 _, ctx := ktesting.NewTestContext(t) 911 ctx, cancel := context.WithCancel(ctx) 912 defer cancel() 913 go reconciler.Run(ctx) 914 915 // Assert 916 waitForNewAttacherCallCount(t, 1 /* expectedCallCount */, fakePlugin) 917 verifyNewAttacherCallCount(t, false /* expectZeroNewAttacherCallCount */, fakePlugin) 918 waitForAttachCallCount(t, 1 /* expectedAttachCallCount */, fakePlugin) 919 verifyNewDetacherCallCount(t, true /* expectZeroNewDetacherCallCount */, fakePlugin) 920 waitForDetachCallCount(t, 0 /* expectedDetachCallCount */, fakePlugin) 921 922 // Delete the pod and the volume will be detached only after the maxLongWaitForUnmountDuration expires as volume is 923 //not unmounted. Here maxLongWaitForUnmountDuration is used to mimic that node is out of service. 924 // But in this case the node has the node.kubernetes.io/out-of-service taint and hence it will not wait for 925 // maxLongWaitForUnmountDuration and will progress to detach immediately. 926 dsw.DeletePod(types.UniquePodName(podName1), generatedVolumeName, nodeName1) 927 // Assert -- Detach will be triggered if node has out of service taint 928 waitForNewDetacherCallCount(t, 1 /* expectedCallCount */, fakePlugin) 929 verifyNewAttacherCallCount(t, false /* expectZeroNewAttacherCallCount */, fakePlugin) 930 waitForAttachCallCount(t, 1 /* expectedAttachCallCount */, fakePlugin) 931 verifyNewDetacherCallCount(t, false /* expectZeroNewDetacherCallCount */, fakePlugin) 932 waitForDetachCallCount(t, 1 /* expectedDetachCallCount */, fakePlugin) 933 934 // Force detach metric due to out-of-service taint 935 testForceDetachMetric(t, 1, metrics.ForceDetachReasonOutOfService) 936 } 937 938 // Populates desiredStateOfWorld cache with one node/volume/pod tuple. 939 // The node does not have the node.kubernetes.io/out-of-service taint present. 940 // 941 // The maxWaitForUnmountDuration is longer (in this case it is 4200 * time.Second so that detach does not happen 942 // immediately due to timeout. 943 // 944 // Calls Run() 945 // Verifies there is one attach call and no detach calls. 946 // Deletes the pod from desiredStateOfWorld cache without first marking the node/volume as unmounted. 947 // Verifies there is no detach call and no (new) attach calls. 948 func Test_Run_OneVolumeDetachOnNoOutOfServiceTaintedNode(t *testing.T) { 949 // Arrange 950 volumePluginMgr, fakePlugin := volumetesting.GetTestVolumePluginMgr(t) 951 dsw := cache.NewDesiredStateOfWorld(volumePluginMgr) 952 asw := cache.NewActualStateOfWorld(volumePluginMgr) 953 fakeKubeClient := controllervolumetesting.CreateTestClient() 954 fakeRecorder := &record.FakeRecorder{} 955 fakeHandler := volumetesting.NewBlockVolumePathHandler() 956 ad := operationexecutor.NewOperationExecutor(operationexecutor.NewOperationGenerator( 957 fakeKubeClient, 958 volumePluginMgr, 959 fakeRecorder, 960 fakeHandler)) 961 informerFactory := informers.NewSharedInformerFactory(fakeKubeClient, controller.NoResyncPeriodFunc()) 962 nsu := statusupdater.NewFakeNodeStatusUpdater(false /* returnError */) 963 nodeLister := informerFactory.Core().V1().Nodes().Lister() 964 reconciler := NewReconciler( 965 reconcilerLoopPeriod, maxLongWaitForUnmountDuration, syncLoopPeriod, false, dsw, asw, ad, 966 nsu, nodeLister, fakeRecorder) 967 podName1 := "pod-uid1" 968 volumeName1 := v1.UniqueVolumeName("volume-name1") 969 volumeSpec1 := controllervolumetesting.GetTestVolumeSpec(string(volumeName1), volumeName1) 970 nodeName1 := k8stypes.NodeName("worker-0") 971 node1 := &v1.Node{ 972 ObjectMeta: metav1.ObjectMeta{Name: string(nodeName1)}, 973 } 974 informerFactory.Core().V1().Nodes().Informer().GetStore().Add(node1) 975 dsw.AddNode(nodeName1, false /*keepTerminatedPodVolumes*/) 976 volumeExists := dsw.VolumeExists(volumeName1, nodeName1) 977 if volumeExists { 978 t.Fatalf( 979 "Volume %q/node %q should not exist, but it does.", 980 volumeName1, 981 nodeName1) 982 } 983 984 generatedVolumeName, podErr := dsw.AddPod(types.UniquePodName(podName1), controllervolumetesting.NewPod(podName1, 985 podName1), volumeSpec1, nodeName1) 986 if podErr != nil { 987 t.Fatalf("AddPod failed. Expected: <no error> Actual: <%v>", podErr) 988 } 989 990 // Act 991 _, ctx := ktesting.NewTestContext(t) 992 ctx, cancel := context.WithCancel(ctx) 993 defer cancel() 994 go reconciler.Run(ctx) 995 996 // Assert 997 waitForNewAttacherCallCount(t, 1 /* expectedCallCount */, fakePlugin) 998 verifyNewAttacherCallCount(t, false /* expectZeroNewAttacherCallCount */, fakePlugin) 999 waitForAttachCallCount(t, 1 /* expectedAttachCallCount */, fakePlugin) 1000 verifyNewDetacherCallCount(t, true /* expectZeroNewDetacherCallCount */, fakePlugin) 1001 waitForDetachCallCount(t, 0 /* expectedDetachCallCount */, fakePlugin) 1002 1003 // Delete the pod and the volume will be detached only after the maxLongWaitForUnmountDuration expires as volume is 1004 // not unmounted. Here maxLongWaitForUnmountDuration is used to mimic that node is out of service. 1005 // But in this case the node does not have the node.kubernetes.io/out-of-service taint and hence it will wait for 1006 // maxLongWaitForUnmountDuration and will not be detached immediately. 1007 dsw.DeletePod(types.UniquePodName(podName1), generatedVolumeName, nodeName1) 1008 // Assert -- Detach will be triggered only after maxLongWaitForUnmountDuration expires 1009 waitForNewDetacherCallCount(t, 0 /* expectedCallCount */, fakePlugin) 1010 verifyNewAttacherCallCount(t, false /* expectZeroNewAttacherCallCount */, fakePlugin) 1011 waitForAttachCallCount(t, 1 /* expectedAttachCallCount */, fakePlugin) 1012 verifyNewDetacherCallCount(t, true /* expectZeroNewDetacherCallCount */, fakePlugin) 1013 waitForDetachCallCount(t, 0 /* expectedDetachCallCount */, fakePlugin) 1014 } 1015 1016 // Populates desiredStateOfWorld cache with one node/volume/pod tuple. 1017 // The node starts as healthy. 1018 // 1019 // Calls Run() 1020 // Verifies there is one attach call and no detach calls. 1021 // Deletes the pod from desiredStateOfWorld cache without first marking the node/volume as unmounted. 1022 // Verifies that the volume is NOT detached after maxWaitForUnmountDuration. 1023 // Marks the node as unhealthy. 1024 // Verifies that the volume is detached after maxWaitForUnmountDuration. 1025 func Test_Run_OneVolumeDetachOnUnhealthyNode(t *testing.T) { 1026 // Arrange 1027 volumePluginMgr, fakePlugin := volumetesting.GetTestVolumePluginMgr(t) 1028 dsw := cache.NewDesiredStateOfWorld(volumePluginMgr) 1029 asw := cache.NewActualStateOfWorld(volumePluginMgr) 1030 fakeKubeClient := controllervolumetesting.CreateTestClient() 1031 fakeRecorder := &record.FakeRecorder{} 1032 fakeHandler := volumetesting.NewBlockVolumePathHandler() 1033 ad := operationexecutor.NewOperationExecutor(operationexecutor.NewOperationGenerator( 1034 fakeKubeClient, 1035 volumePluginMgr, 1036 fakeRecorder, 1037 fakeHandler)) 1038 informerFactory := informers.NewSharedInformerFactory(fakeKubeClient, controller.NoResyncPeriodFunc()) 1039 nsu := statusupdater.NewFakeNodeStatusUpdater(false /* returnError */) 1040 nodeLister := informerFactory.Core().V1().Nodes().Lister() 1041 reconciler := NewReconciler( 1042 reconcilerLoopPeriod, maxWaitForUnmountDuration, syncLoopPeriod, false, dsw, asw, ad, 1043 nsu, nodeLister, fakeRecorder) 1044 podName1 := "pod-uid1" 1045 volumeName1 := v1.UniqueVolumeName("volume-name1") 1046 volumeSpec1 := controllervolumetesting.GetTestVolumeSpec(string(volumeName1), volumeName1) 1047 nodeName1 := k8stypes.NodeName("worker-0") 1048 node1 := &v1.Node{ 1049 ObjectMeta: metav1.ObjectMeta{Name: string(nodeName1)}, 1050 Status: v1.NodeStatus{ 1051 Conditions: []v1.NodeCondition{ 1052 { 1053 Type: v1.NodeReady, 1054 Status: v1.ConditionTrue, 1055 }, 1056 }, 1057 }, 1058 } 1059 informerFactory.Core().V1().Nodes().Informer().GetStore().Add(node1) 1060 dsw.AddNode(nodeName1, false /*keepTerminatedPodVolumes*/) 1061 volumeExists := dsw.VolumeExists(volumeName1, nodeName1) 1062 if volumeExists { 1063 t.Fatalf( 1064 "Volume %q/node %q should not exist, but it does.", 1065 volumeName1, 1066 nodeName1) 1067 } 1068 1069 generatedVolumeName, podErr := dsw.AddPod(types.UniquePodName(podName1), controllervolumetesting.NewPod(podName1, 1070 podName1), volumeSpec1, nodeName1) 1071 if podErr != nil { 1072 t.Fatalf("AddPod failed. Expected: <no error> Actual: <%v>", podErr) 1073 } 1074 1075 // Act 1076 _, ctx := ktesting.NewTestContext(t) 1077 ctx, cancel := context.WithCancel(ctx) 1078 defer cancel() 1079 go reconciler.Run(ctx) 1080 1081 // Assert 1082 waitForNewAttacherCallCount(t, 1 /* expectedCallCount */, fakePlugin) 1083 verifyNewAttacherCallCount(t, false /* expectZeroNewAttacherCallCount */, fakePlugin) 1084 waitForAttachCallCount(t, 1 /* expectedAttachCallCount */, fakePlugin) 1085 verifyNewDetacherCallCount(t, true /* expectZeroNewDetacherCallCount */, fakePlugin) 1086 waitForDetachCallCount(t, 0 /* expectedDetachCallCount */, fakePlugin) 1087 1088 // Act 1089 // Delete the pod and the volume will be detached even after the maxWaitForUnmountDuration expires as volume is 1090 // not unmounted and the node is healthy. 1091 dsw.DeletePod(types.UniquePodName(podName1), generatedVolumeName, nodeName1) 1092 time.Sleep(maxWaitForUnmountDuration * 5) 1093 // Assert 1094 waitForNewDetacherCallCount(t, 0 /* expectedCallCount */, fakePlugin) 1095 verifyNewAttacherCallCount(t, false /* expectZeroNewAttacherCallCount */, fakePlugin) 1096 waitForAttachCallCount(t, 1 /* expectedAttachCallCount */, fakePlugin) 1097 verifyNewDetacherCallCount(t, true /* expectZeroNewDetacherCallCount */, fakePlugin) 1098 waitForDetachCallCount(t, 0 /* expectedDetachCallCount */, fakePlugin) 1099 1100 // Act 1101 // Mark the node unhealthy 1102 node2 := node1.DeepCopy() 1103 node2.Status.Conditions[0].Status = v1.ConditionFalse 1104 informerFactory.Core().V1().Nodes().Informer().GetStore().Update(node2) 1105 // Assert -- Detach was triggered after maxWaitForUnmountDuration 1106 waitForNewDetacherCallCount(t, 1 /* expectedCallCount */, fakePlugin) 1107 verifyNewAttacherCallCount(t, false /* expectZeroNewAttacherCallCount */, fakePlugin) 1108 waitForAttachCallCount(t, 1 /* expectedAttachCallCount */, fakePlugin) 1109 verifyNewDetacherCallCount(t, false /* expectZeroNewDetacherCallCount */, fakePlugin) 1110 waitForDetachCallCount(t, 1 /* expectedDetachCallCount */, fakePlugin) 1111 } 1112 1113 func Test_ReportMultiAttachError(t *testing.T) { 1114 type nodeWithPods struct { 1115 name k8stypes.NodeName 1116 podNames []string 1117 } 1118 tests := []struct { 1119 name string 1120 nodes []nodeWithPods 1121 expectedEvents []string 1122 }{ 1123 { 1124 "no pods use the volume", 1125 []nodeWithPods{ 1126 {"node1", []string{"ns1/pod1"}}, 1127 }, 1128 []string{"Warning FailedAttachVolume Multi-Attach error for volume \"volume-name\" Volume is already exclusively attached to one node and can't be attached to another"}, 1129 }, 1130 { 1131 "pods in the same namespace use the volume", 1132 []nodeWithPods{ 1133 {"node1", []string{"ns1/pod1"}}, 1134 {"node2", []string{"ns1/pod2"}}, 1135 }, 1136 []string{"Warning FailedAttachVolume Multi-Attach error for volume \"volume-name\" Volume is already used by pod(s) pod2"}, 1137 }, 1138 { 1139 "pods in another namespace use the volume", 1140 []nodeWithPods{ 1141 {"node1", []string{"ns1/pod1"}}, 1142 {"node2", []string{"ns2/pod2"}}, 1143 }, 1144 []string{"Warning FailedAttachVolume Multi-Attach error for volume \"volume-name\" Volume is already used by 1 pod(s) in different namespaces"}, 1145 }, 1146 { 1147 "pods both in the same and another namespace use the volume", 1148 []nodeWithPods{ 1149 {"node1", []string{"ns1/pod1"}}, 1150 {"node2", []string{"ns2/pod2"}}, 1151 {"node3", []string{"ns1/pod3"}}, 1152 }, 1153 []string{"Warning FailedAttachVolume Multi-Attach error for volume \"volume-name\" Volume is already used by pod(s) pod3 and 1 pod(s) in different namespaces"}, 1154 }, 1155 } 1156 1157 for _, test := range tests { 1158 // Arrange 1159 t.Logf("Test %q starting", test.name) 1160 volumePluginMgr, _ := volumetesting.GetTestVolumePluginMgr(t) 1161 dsw := cache.NewDesiredStateOfWorld(volumePluginMgr) 1162 asw := cache.NewActualStateOfWorld(volumePluginMgr) 1163 fakeKubeClient := controllervolumetesting.CreateTestClient() 1164 fakeRecorder := record.NewFakeRecorder(100) 1165 fakeHandler := volumetesting.NewBlockVolumePathHandler() 1166 ad := operationexecutor.NewOperationExecutor(operationexecutor.NewOperationGenerator( 1167 fakeKubeClient, 1168 volumePluginMgr, 1169 fakeRecorder, 1170 fakeHandler)) 1171 informerFactory := informers.NewSharedInformerFactory(fakeKubeClient, controller.NoResyncPeriodFunc()) 1172 nodeLister := informerFactory.Core().V1().Nodes().Lister() 1173 nsu := statusupdater.NewFakeNodeStatusUpdater(false /* returnError */) 1174 rc := NewReconciler( 1175 reconcilerLoopPeriod, maxWaitForUnmountDuration, syncLoopPeriod, false, dsw, asw, ad, nsu, nodeLister, fakeRecorder) 1176 1177 nodes := []k8stypes.NodeName{} 1178 for _, n := range test.nodes { 1179 dsw.AddNode(n.name, false /*keepTerminatedPodVolumes*/) 1180 nodes = append(nodes, n.name) 1181 for _, podName := range n.podNames { 1182 volumeName := v1.UniqueVolumeName("volume-name") 1183 volumeSpec := controllervolumetesting.GetTestVolumeSpec(string(volumeName), volumeName) 1184 volumeSpec.PersistentVolume.Spec.AccessModes = []v1.PersistentVolumeAccessMode{v1.ReadWriteOnce} 1185 uid := string(n.name) + "-" + podName // unique UID 1186 namespace, name := utilstrings.SplitQualifiedName(podName) 1187 pod := controllervolumetesting.NewPod(uid, name) 1188 pod.Namespace = namespace 1189 _, err := dsw.AddPod(types.UniquePodName(uid), pod, volumeSpec, n.name) 1190 if err != nil { 1191 t.Fatalf("Error adding pod %s to DSW: %s", podName, err) 1192 } 1193 } 1194 } 1195 // Act 1196 logger, _ := ktesting.NewTestContext(t) 1197 volumes := dsw.GetVolumesToAttach() 1198 for _, vol := range volumes { 1199 if vol.NodeName == "node1" { 1200 rc.(*reconciler).reportMultiAttachError(logger, vol, nodes) 1201 } 1202 } 1203 1204 // Assert 1205 close(fakeRecorder.Events) 1206 index := 0 1207 for event := range fakeRecorder.Events { 1208 if len(test.expectedEvents) < index { 1209 t.Errorf("Test %q: unexpected event received: %s", test.name, event) 1210 } else { 1211 expectedEvent := test.expectedEvents[index] 1212 if expectedEvent != event { 1213 t.Errorf("Test %q: event %d: expected %q, got %q", test.name, index, expectedEvent, event) 1214 } 1215 } 1216 index++ 1217 } 1218 for i := index; i < len(test.expectedEvents); i++ { 1219 t.Errorf("Test %q: event %d: expected %q, got none", test.name, i, test.expectedEvents[i]) 1220 } 1221 } 1222 } 1223 1224 func waitForMultiAttachErrorOnNode( 1225 t *testing.T, 1226 attachedNode k8stypes.NodeName, 1227 dsow cache.DesiredStateOfWorld) { 1228 multAttachCheckFunc := func() (bool, error) { 1229 for _, volumeToAttach := range dsow.GetVolumesToAttach() { 1230 if volumeToAttach.NodeName != attachedNode { 1231 if volumeToAttach.MultiAttachErrorReported { 1232 return true, nil 1233 } 1234 } 1235 } 1236 t.Logf("Warning: MultiAttach error not yet set on Node. Will retry.") 1237 return false, nil 1238 } 1239 1240 err := retryWithExponentialBackOff(100*time.Millisecond, multAttachCheckFunc) 1241 if err != nil { 1242 t.Fatalf("Timed out waiting for MultiAttach Error to be set on non-attached node") 1243 } 1244 } 1245 1246 func waitForNewAttacherCallCount( 1247 t *testing.T, 1248 expectedCallCount int, 1249 fakePlugin *volumetesting.FakeVolumePlugin) { 1250 err := retryWithExponentialBackOff( 1251 time.Duration(5*time.Millisecond), 1252 func() (bool, error) { 1253 actualCallCount := fakePlugin.GetNewAttacherCallCount() 1254 if actualCallCount >= expectedCallCount { 1255 return true, nil 1256 } 1257 t.Logf( 1258 "Warning: Wrong NewAttacherCallCount. Expected: <%v> Actual: <%v>. Will retry.", 1259 expectedCallCount, 1260 actualCallCount) 1261 return false, nil 1262 }, 1263 ) 1264 1265 if err != nil { 1266 t.Fatalf( 1267 "Timed out waiting for NewAttacherCallCount. Expected: <%v> Actual: <%v>", 1268 expectedCallCount, 1269 fakePlugin.GetNewAttacherCallCount()) 1270 } 1271 } 1272 1273 func waitForNewDetacherCallCount( 1274 t *testing.T, 1275 expectedCallCount int, 1276 fakePlugin *volumetesting.FakeVolumePlugin) { 1277 err := retryWithExponentialBackOff( 1278 time.Duration(5*time.Millisecond), 1279 func() (bool, error) { 1280 actualCallCount := fakePlugin.GetNewDetacherCallCount() 1281 if actualCallCount >= expectedCallCount { 1282 return true, nil 1283 } 1284 t.Logf( 1285 "Warning: Wrong NewDetacherCallCount. Expected: <%v> Actual: <%v>. Will retry.", 1286 expectedCallCount, 1287 actualCallCount) 1288 return false, nil 1289 }, 1290 ) 1291 1292 if err != nil { 1293 t.Fatalf( 1294 "Timed out waiting for NewDetacherCallCount. Expected: <%v> Actual: <%v>", 1295 expectedCallCount, 1296 fakePlugin.GetNewDetacherCallCount()) 1297 } 1298 } 1299 1300 func waitForAttachCallCount( 1301 t *testing.T, 1302 expectedAttachCallCount int, 1303 fakePlugin *volumetesting.FakeVolumePlugin) { 1304 if len(fakePlugin.GetAttachers()) == 0 && expectedAttachCallCount == 0 { 1305 return 1306 } 1307 1308 err := retryWithExponentialBackOff( 1309 time.Duration(5*time.Millisecond), 1310 func() (bool, error) { 1311 for i, attacher := range fakePlugin.GetAttachers() { 1312 actualCallCount := attacher.GetAttachCallCount() 1313 if actualCallCount == expectedAttachCallCount { 1314 return true, nil 1315 } 1316 t.Logf( 1317 "Warning: Wrong attacher[%v].GetAttachCallCount(). Expected: <%v> Actual: <%v>. Will try next attacher.", 1318 i, 1319 expectedAttachCallCount, 1320 actualCallCount) 1321 } 1322 1323 t.Logf( 1324 "Warning: No attachers have expected AttachCallCount. Expected: <%v>. Will retry.", 1325 expectedAttachCallCount) 1326 return false, nil 1327 }, 1328 ) 1329 1330 if err != nil { 1331 t.Fatalf( 1332 "No attachers have expected AttachCallCount. Expected: <%v>", 1333 expectedAttachCallCount) 1334 } 1335 } 1336 1337 func waitForTotalAttachCallCount( 1338 t *testing.T, 1339 expectedAttachCallCount int, 1340 fakePlugin *volumetesting.FakeVolumePlugin) { 1341 if len(fakePlugin.GetAttachers()) == 0 && expectedAttachCallCount == 0 { 1342 return 1343 } 1344 1345 err := retryWithExponentialBackOff( 1346 time.Duration(5*time.Millisecond), 1347 func() (bool, error) { 1348 totalCount := 0 1349 for _, attacher := range fakePlugin.GetAttachers() { 1350 totalCount += attacher.GetAttachCallCount() 1351 } 1352 if totalCount == expectedAttachCallCount { 1353 return true, nil 1354 } 1355 t.Logf( 1356 "Warning: Wrong total GetAttachCallCount(). Expected: <%v> Actual: <%v>. Will retry.", 1357 expectedAttachCallCount, 1358 totalCount) 1359 1360 return false, nil 1361 }, 1362 ) 1363 1364 if err != nil { 1365 t.Fatalf( 1366 "Total AttachCallCount does not match expected value. Expected: <%v>", 1367 expectedAttachCallCount) 1368 } 1369 } 1370 1371 func waitForDetachCallCount( 1372 t *testing.T, 1373 expectedDetachCallCount int, 1374 fakePlugin *volumetesting.FakeVolumePlugin) { 1375 if len(fakePlugin.GetDetachers()) == 0 && expectedDetachCallCount == 0 { 1376 return 1377 } 1378 1379 err := retryWithExponentialBackOff( 1380 time.Duration(5*time.Millisecond), 1381 func() (bool, error) { 1382 for i, detacher := range fakePlugin.GetDetachers() { 1383 actualCallCount := detacher.GetDetachCallCount() 1384 if actualCallCount == expectedDetachCallCount { 1385 return true, nil 1386 } 1387 t.Logf( 1388 "Wrong detacher[%v].GetDetachCallCount(). Expected: <%v> Actual: <%v>. Will try next detacher.", 1389 i, 1390 expectedDetachCallCount, 1391 actualCallCount) 1392 } 1393 1394 t.Logf( 1395 "Warning: No detachers have expected DetachCallCount. Expected: <%v>. Will retry.", 1396 expectedDetachCallCount) 1397 return false, nil 1398 }, 1399 ) 1400 1401 if err != nil { 1402 t.Fatalf( 1403 "No detachers have expected DetachCallCount. Expected: <%v>", 1404 expectedDetachCallCount) 1405 } 1406 } 1407 1408 func waitForTotalDetachCallCount( 1409 t *testing.T, 1410 expectedDetachCallCount int, 1411 fakePlugin *volumetesting.FakeVolumePlugin) { 1412 if len(fakePlugin.GetDetachers()) == 0 && expectedDetachCallCount == 0 { 1413 return 1414 } 1415 1416 err := retryWithExponentialBackOff( 1417 time.Duration(5*time.Millisecond), 1418 func() (bool, error) { 1419 totalCount := 0 1420 for _, detacher := range fakePlugin.GetDetachers() { 1421 totalCount += detacher.GetDetachCallCount() 1422 } 1423 if totalCount == expectedDetachCallCount { 1424 return true, nil 1425 } 1426 t.Logf( 1427 "Warning: Wrong total GetDetachCallCount(). Expected: <%v> Actual: <%v>. Will retry.", 1428 expectedDetachCallCount, 1429 totalCount) 1430 1431 return false, nil 1432 }, 1433 ) 1434 1435 if err != nil { 1436 t.Fatalf( 1437 "Total DetachCallCount does not match expected value. Expected: <%v>", 1438 expectedDetachCallCount) 1439 } 1440 } 1441 1442 func waitForAttachedToNodesCount( 1443 t *testing.T, 1444 expectedNodeCount int, 1445 volumeName v1.UniqueVolumeName, 1446 asw cache.ActualStateOfWorld) { 1447 1448 err := retryWithExponentialBackOff( 1449 time.Duration(5*time.Millisecond), 1450 func() (bool, error) { 1451 count := len(asw.GetNodesForAttachedVolume(volumeName)) 1452 if count == expectedNodeCount { 1453 return true, nil 1454 } 1455 t.Logf( 1456 "Warning: Wrong number of nodes having <%v> attached. Expected: <%v> Actual: <%v>. Will retry.", 1457 volumeName, 1458 expectedNodeCount, 1459 count) 1460 1461 return false, nil 1462 }, 1463 ) 1464 1465 if err != nil { 1466 count := len(asw.GetNodesForAttachedVolume(volumeName)) 1467 t.Fatalf( 1468 "Wrong number of nodes having <%v> attached. Expected: <%v> Actual: <%v>", 1469 volumeName, 1470 expectedNodeCount, 1471 count) 1472 } 1473 } 1474 1475 func verifyNewAttacherCallCount( 1476 t *testing.T, 1477 expectZeroNewAttacherCallCount bool, 1478 fakePlugin *volumetesting.FakeVolumePlugin) { 1479 1480 if expectZeroNewAttacherCallCount && 1481 fakePlugin.GetNewAttacherCallCount() != 0 { 1482 t.Fatalf( 1483 "Wrong NewAttacherCallCount. Expected: <0> Actual: <%v>", 1484 fakePlugin.GetNewAttacherCallCount()) 1485 } 1486 } 1487 1488 func waitForVolumeAttachedToNode( 1489 t *testing.T, 1490 volumeName v1.UniqueVolumeName, 1491 nodeName k8stypes.NodeName, 1492 asw cache.ActualStateOfWorld) { 1493 1494 err := retryWithExponentialBackOff( 1495 time.Duration(500*time.Millisecond), 1496 func() (bool, error) { 1497 attachState := asw.GetAttachState(volumeName, nodeName) 1498 if attachState == cache.AttachStateAttached { 1499 return true, nil 1500 } 1501 t.Logf( 1502 "Warning: Volume <%v> is not attached to node <%v> yet. Will retry.", 1503 volumeName, 1504 nodeName) 1505 1506 return false, nil 1507 }, 1508 ) 1509 1510 attachState := asw.GetAttachState(volumeName, nodeName) 1511 if err != nil && attachState != cache.AttachStateAttached { 1512 t.Fatalf( 1513 "Volume <%v> is not attached to node <%v>.", 1514 volumeName, 1515 nodeName) 1516 } 1517 } 1518 1519 func waitForVolumeAddedToNode( 1520 t *testing.T, 1521 volumeName v1.UniqueVolumeName, 1522 nodeName k8stypes.NodeName, 1523 asw cache.ActualStateOfWorld) { 1524 1525 err := retryWithExponentialBackOff( 1526 time.Duration(500*time.Millisecond), 1527 func() (bool, error) { 1528 volumes := asw.GetAttachedVolumes() 1529 for _, volume := range volumes { 1530 if volume.VolumeName == volumeName && volume.NodeName == nodeName { 1531 return true, nil 1532 } 1533 } 1534 t.Logf( 1535 "Warning: Volume <%v> is not added to node <%v> yet. Will retry.", 1536 volumeName, 1537 nodeName) 1538 1539 return false, nil 1540 }, 1541 ) 1542 1543 if err != nil { 1544 t.Fatalf( 1545 "Volume <%v> is not added to node <%v>. %v", 1546 volumeName, 1547 nodeName, err) 1548 } 1549 } 1550 1551 func waitForVolumeRemovedFromNode( 1552 t *testing.T, 1553 volumeName v1.UniqueVolumeName, 1554 nodeName k8stypes.NodeName, 1555 asw cache.ActualStateOfWorld) { 1556 1557 err := retryWithExponentialBackOff( 1558 time.Duration(500*time.Millisecond), 1559 func() (bool, error) { 1560 volumes := asw.GetAttachedVolumes() 1561 exist := false 1562 for _, volume := range volumes { 1563 if volume.VolumeName == volumeName && volume.NodeName == nodeName { 1564 exist = true 1565 } 1566 } 1567 if exist { 1568 t.Logf( 1569 "Warning: Volume <%v> is not removed from the node <%v> yet. Will retry.", 1570 volumeName, 1571 nodeName) 1572 1573 return false, nil 1574 } 1575 return true, nil 1576 1577 }, 1578 ) 1579 1580 if err != nil { 1581 t.Fatalf( 1582 "Volume <%v> is not removed from node <%v>. %v", 1583 volumeName, 1584 nodeName, err) 1585 } 1586 } 1587 1588 func verifyVolumeAttachedToNode( 1589 t *testing.T, 1590 volumeName v1.UniqueVolumeName, 1591 nodeName k8stypes.NodeName, 1592 expectedAttachState cache.AttachState, 1593 asw cache.ActualStateOfWorld, 1594 ) { 1595 attachState := asw.GetAttachState(volumeName, nodeName) 1596 if attachState != expectedAttachState { 1597 t.Fatalf("Check volume <%v> is attached to node <%v>, got %v, expected %v", 1598 volumeName, 1599 nodeName, 1600 attachState, 1601 expectedAttachState) 1602 } 1603 t.Logf("Volume <%v> is attached to node <%v>: %v", volumeName, nodeName, attachState) 1604 } 1605 1606 func verifyVolumeReportedAsAttachedToNode( 1607 t *testing.T, 1608 logger klog.Logger, 1609 volumeName v1.UniqueVolumeName, 1610 nodeName k8stypes.NodeName, 1611 isAttached bool, 1612 asw cache.ActualStateOfWorld, 1613 timeout time.Duration, 1614 ) { 1615 var result bool 1616 var lastErr error 1617 err := wait.PollUntilContextTimeout(context.TODO(), 50*time.Millisecond, timeout, false, func(context.Context) (done bool, err error) { 1618 volumes := asw.GetVolumesToReportAttached(logger) 1619 for _, volume := range volumes[nodeName] { 1620 if volume.Name == volumeName { 1621 result = true 1622 } 1623 } 1624 1625 if result == isAttached { 1626 t.Logf("Volume <%v> is reported as attached to node <%v>: %v", volumeName, nodeName, result) 1627 return true, nil 1628 } 1629 lastErr = fmt.Errorf("Check volume <%v> is reported as attached to node <%v>, got %v, expected %v", 1630 volumeName, 1631 nodeName, 1632 result, 1633 isAttached) 1634 return false, nil 1635 }) 1636 if err != nil { 1637 t.Fatalf("last error: %q, wait timeout: %q", lastErr, err.Error()) 1638 } 1639 1640 } 1641 1642 func verifyVolumeNoStatusUpdateNeeded( 1643 t *testing.T, 1644 logger klog.Logger, 1645 volumeName v1.UniqueVolumeName, 1646 nodeName k8stypes.NodeName, 1647 asw cache.ActualStateOfWorld, 1648 ) { 1649 volumes := asw.GetVolumesToReportAttached(logger) 1650 for _, volume := range volumes[nodeName] { 1651 if volume.Name == volumeName { 1652 t.Fatalf("Check volume <%v> is reported as need to update status on node <%v>, expected false", 1653 volumeName, 1654 nodeName) 1655 } 1656 } 1657 t.Logf("Volume <%v> is not reported as need to update status on node <%v>", volumeName, nodeName) 1658 } 1659 1660 func verifyNewDetacherCallCount( 1661 t *testing.T, 1662 expectZeroNewDetacherCallCount bool, 1663 fakePlugin *volumetesting.FakeVolumePlugin) { 1664 1665 if expectZeroNewDetacherCallCount && 1666 fakePlugin.GetNewDetacherCallCount() != 0 { 1667 t.Fatalf("Wrong NewDetacherCallCount. Expected: <0> Actual: <%v>", 1668 fakePlugin.GetNewDetacherCallCount()) 1669 } 1670 } 1671 1672 func retryWithExponentialBackOff(initialDuration time.Duration, fn wait.ConditionFunc) error { 1673 backoff := wait.Backoff{ 1674 Duration: initialDuration, 1675 Factor: 3, 1676 Jitter: 0, 1677 Steps: 6, 1678 } 1679 return wait.ExponentialBackoff(backoff, fn) 1680 } 1681 1682 // verifies the force detach metric with reason 1683 func testForceDetachMetric(t *testing.T, inputForceDetachMetricCounter int, reason string) { 1684 t.Helper() 1685 1686 actualForceDetachMericCounter, err := metricstestutil.GetCounterMetricValue(metrics.ForceDetachMetricCounter.WithLabelValues(reason)) 1687 if err != nil { 1688 t.Errorf("Error getting actualForceDetachMericCounter") 1689 } 1690 if actualForceDetachMericCounter != float64(inputForceDetachMetricCounter) { 1691 t.Errorf("Expected desiredForceDetachMericCounter to be %d, got %v", inputForceDetachMetricCounter, actualForceDetachMericCounter) 1692 } 1693 }