k8s.io/kubernetes@v1.29.3/pkg/kubelet/volumemanager/reconciler/reconciler_new_test.go (about) 1 /* 2 Copyright 2023 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 "testing" 21 22 "github.com/stretchr/testify/assert" 23 v1 "k8s.io/api/core/v1" 24 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 25 "k8s.io/client-go/tools/record" 26 "k8s.io/kubernetes/pkg/kubelet/volumemanager/cache" 27 "k8s.io/kubernetes/pkg/volume" 28 volumetesting "k8s.io/kubernetes/pkg/volume/testing" 29 "k8s.io/kubernetes/pkg/volume/util" 30 "k8s.io/kubernetes/pkg/volume/util/hostutil" 31 "k8s.io/kubernetes/pkg/volume/util/operationexecutor" 32 "k8s.io/mount-utils" 33 ) 34 35 func TestReconcileWithUpdateReconstructedFromAPIServer(t *testing.T) { 36 // Calls Run() with two reconstructed volumes. 37 // Verifies the devicePaths + volume attachability are reconstructed from node.status. 38 39 // Arrange 40 node := &v1.Node{ 41 ObjectMeta: metav1.ObjectMeta{ 42 Name: string(nodeName), 43 }, 44 Status: v1.NodeStatus{ 45 VolumesAttached: []v1.AttachedVolume{ 46 { 47 Name: "fake-plugin/fake-device1", 48 DevicePath: "fake/path", 49 }, 50 }, 51 }, 52 } 53 volumePluginMgr, fakePlugin := volumetesting.GetTestKubeletVolumePluginMgrWithNode(t, node) 54 seLinuxTranslator := util.NewFakeSELinuxLabelTranslator() 55 dsw := cache.NewDesiredStateOfWorld(volumePluginMgr, seLinuxTranslator) 56 asw := cache.NewActualStateOfWorld(nodeName, volumePluginMgr) 57 kubeClient := createTestClient() 58 fakeRecorder := &record.FakeRecorder{} 59 fakeHandler := volumetesting.NewBlockVolumePathHandler() 60 oex := operationexecutor.NewOperationExecutor(operationexecutor.NewOperationGenerator( 61 kubeClient, 62 volumePluginMgr, 63 fakeRecorder, 64 fakeHandler)) 65 rc := NewReconciler( 66 kubeClient, 67 true, /* controllerAttachDetachEnabled */ 68 reconcilerLoopSleepDuration, 69 waitForAttachTimeout, 70 nodeName, 71 dsw, 72 asw, 73 hasAddedPods, 74 oex, 75 mount.NewFakeMounter(nil), 76 hostutil.NewFakeHostUtil(nil), 77 volumePluginMgr, 78 kubeletPodsDir) 79 reconciler := rc.(*reconciler) 80 81 // The pod has two volumes, fake-device1 is attachable, fake-device2 is not. 82 pod := &v1.Pod{ 83 ObjectMeta: metav1.ObjectMeta{ 84 Name: "pod1", 85 UID: "pod1uid", 86 }, 87 Spec: v1.PodSpec{ 88 Volumes: []v1.Volume{ 89 { 90 Name: "volume-name", 91 VolumeSource: v1.VolumeSource{ 92 GCEPersistentDisk: &v1.GCEPersistentDiskVolumeSource{ 93 PDName: "fake-device1", 94 }, 95 }, 96 }, 97 { 98 Name: "volume-name2", 99 VolumeSource: v1.VolumeSource{ 100 GCEPersistentDisk: &v1.GCEPersistentDiskVolumeSource{ 101 PDName: "fake-device2", 102 }, 103 }, 104 }, 105 }, 106 }, 107 } 108 109 volumeSpec1 := &volume.Spec{Volume: &pod.Spec.Volumes[0]} 110 volumeName1 := util.GetUniqueVolumeName(fakePlugin.GetPluginName(), "fake-device1") 111 volumeSpec2 := &volume.Spec{Volume: &pod.Spec.Volumes[1]} 112 volumeName2 := util.GetUniqueVolumeName(fakePlugin.GetPluginName(), "fake-device2") 113 114 assert.NoError(t, asw.AddAttachUncertainReconstructedVolume(volumeName1, volumeSpec1, nodeName, "")) 115 assert.NoError(t, asw.MarkDeviceAsUncertain(volumeName1, "/dev/badly/reconstructed", "/var/lib/kubelet/plugins/global1", "")) 116 assert.NoError(t, asw.AddAttachUncertainReconstructedVolume(volumeName2, volumeSpec2, nodeName, "")) 117 assert.NoError(t, asw.MarkDeviceAsUncertain(volumeName2, "/dev/reconstructed", "/var/lib/kubelet/plugins/global2", "")) 118 119 assert.False(t, reconciler.StatesHasBeenSynced()) 120 121 reconciler.volumesNeedUpdateFromNodeStatus = append(reconciler.volumesNeedUpdateFromNodeStatus, volumeName1, volumeName2) 122 // Act - run reconcile loop just once. 123 // "volumesNeedUpdateFromNodeStatus" is not empty, so no unmount will be triggered. 124 reconciler.reconcileNew() 125 126 // Assert 127 assert.True(t, reconciler.StatesHasBeenSynced()) 128 assert.Empty(t, reconciler.volumesNeedUpdateFromNodeStatus) 129 130 attachedVolumes := asw.GetAttachedVolumes() 131 assert.Equalf(t, len(attachedVolumes), 2, "two volumes in ASW expected") 132 for _, vol := range attachedVolumes { 133 if vol.VolumeName == volumeName1 { 134 // devicePath + attachability must have been updated from node.status 135 assert.True(t, vol.PluginIsAttachable) 136 assert.Equal(t, vol.DevicePath, "fake/path") 137 } 138 if vol.VolumeName == volumeName2 { 139 // only attachability was updated from node.status 140 assert.False(t, vol.PluginIsAttachable) 141 assert.Equal(t, vol.DevicePath, "/dev/reconstructed") 142 } 143 } 144 }