k8s.io/kubernetes@v1.29.3/pkg/kubelet/volumemanager/reconciler/reconstruct.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 22 v1 "k8s.io/api/core/v1" 23 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 24 "k8s.io/apimachinery/pkg/types" 25 "k8s.io/klog/v2" 26 "k8s.io/kubernetes/pkg/volume/util/nestedpendingoperations" 27 "k8s.io/kubernetes/pkg/volume/util/operationexecutor" 28 ) 29 30 // sync process tries to observe the real world by scanning all pods' volume directories from the disk. 31 // If the actual and desired state of worlds are not consistent with the observed world, it means that some 32 // mounted volumes are left out probably during kubelet restart. This process will reconstruct 33 // the volumes and update the actual and desired states. For the volumes that cannot support reconstruction, 34 // it will try to clean up the mount paths with operation executor. 35 func (rc *reconciler) sync() { 36 defer rc.updateLastSyncTime() 37 rc.syncStates(rc.kubeletPodsDir) 38 } 39 40 // syncStates scans the volume directories under the given pod directory. 41 // If the volume is not in desired state of world, this function will reconstruct 42 // the volume related information and put it in both the actual and desired state of worlds. 43 // For some volume plugins that cannot support reconstruction, it will clean up the existing 44 // mount points since the volume is no long needed (removed from desired state) 45 func (rc *reconciler) syncStates(kubeletPodDir string) { 46 // Get volumes information by reading the pod's directory 47 podVolumes, err := getVolumesFromPodDir(kubeletPodDir) 48 if err != nil { 49 klog.ErrorS(err, "Cannot get volumes from disk, skip sync states for volume reconstruction") 50 return 51 } 52 volumesNeedUpdate := make(map[v1.UniqueVolumeName]*globalVolumeInfo) 53 volumeNeedReport := []v1.UniqueVolumeName{} 54 for _, volume := range podVolumes { 55 if rc.actualStateOfWorld.VolumeExistsWithSpecName(volume.podName, volume.volumeSpecName) { 56 klog.V(4).InfoS("Volume exists in actual state, skip cleaning up mounts", "podName", volume.podName, "volumeSpecName", volume.volumeSpecName) 57 // There is nothing to reconstruct 58 continue 59 } 60 volumeInDSW := rc.desiredStateOfWorld.VolumeExistsWithSpecName(volume.podName, volume.volumeSpecName) 61 62 reconstructedVolume, err := rc.reconstructVolume(volume) 63 if err != nil { 64 if volumeInDSW { 65 // Some pod needs the volume, don't clean it up and hope that 66 // reconcile() calls SetUp and reconstructs the volume in ASW. 67 klog.V(4).InfoS("Volume exists in desired state, skip cleaning up mounts", "podName", volume.podName, "volumeSpecName", volume.volumeSpecName) 68 continue 69 } 70 // No pod needs the volume. 71 klog.InfoS("Could not construct volume information, cleaning up mounts", "podName", volume.podName, "volumeSpecName", volume.volumeSpecName, "err", err) 72 rc.cleanupMounts(volume) 73 continue 74 } 75 gvl := &globalVolumeInfo{ 76 volumeName: reconstructedVolume.volumeName, 77 volumeSpec: reconstructedVolume.volumeSpec, 78 devicePath: reconstructedVolume.devicePath, 79 deviceMounter: reconstructedVolume.deviceMounter, 80 blockVolumeMapper: reconstructedVolume.blockVolumeMapper, 81 mounter: reconstructedVolume.mounter, 82 } 83 if volumeInDSW { 84 // Some pod needs the volume. And it exists on disk. Some previous 85 // kubelet must have created the directory, therefore it must have 86 // reported the volume as in use. Mark the volume as in use also in 87 // this new kubelet so reconcile() calls SetUp and re-mounts the 88 // volume if it's necessary. 89 volumeNeedReport = append(volumeNeedReport, reconstructedVolume.volumeName) 90 if cachedInfo, ok := rc.skippedDuringReconstruction[reconstructedVolume.volumeName]; ok { 91 gvl = cachedInfo 92 } 93 gvl.addPodVolume(reconstructedVolume) 94 rc.skippedDuringReconstruction[reconstructedVolume.volumeName] = gvl 95 klog.V(4).InfoS("Volume exists in desired state, marking as InUse", "podName", volume.podName, "volumeSpecName", volume.volumeSpecName) 96 continue 97 } 98 // There is no pod that uses the volume. 99 if rc.operationExecutor.IsOperationPending(reconstructedVolume.volumeName, nestedpendingoperations.EmptyUniquePodName, nestedpendingoperations.EmptyNodeName) { 100 klog.InfoS("Volume is in pending operation, skip cleaning up mounts") 101 } 102 klog.V(2).InfoS("Reconciler sync states: could not find pod information in desired state, update it in actual state", "reconstructedVolume", reconstructedVolume) 103 if cachedInfo, ok := volumesNeedUpdate[reconstructedVolume.volumeName]; ok { 104 gvl = cachedInfo 105 } 106 gvl.addPodVolume(reconstructedVolume) 107 volumesNeedUpdate[reconstructedVolume.volumeName] = gvl 108 } 109 110 if len(volumesNeedUpdate) > 0 { 111 if err = rc.updateStates(volumesNeedUpdate); err != nil { 112 klog.ErrorS(err, "Error occurred during reconstruct volume from disk") 113 } 114 } 115 if len(volumeNeedReport) > 0 { 116 rc.desiredStateOfWorld.MarkVolumesReportedInUse(volumeNeedReport) 117 } 118 } 119 120 // updateDevicePath gets the node status to retrieve volume device path information. 121 func (rc *reconciler) updateDevicePath(volumesNeedUpdate map[v1.UniqueVolumeName]*globalVolumeInfo) { 122 node, fetchErr := rc.kubeClient.CoreV1().Nodes().Get(context.TODO(), string(rc.nodeName), metav1.GetOptions{}) 123 if fetchErr != nil { 124 klog.ErrorS(fetchErr, "UpdateStates in reconciler: could not get node status with error") 125 } else { 126 for _, attachedVolume := range node.Status.VolumesAttached { 127 if volume, exists := volumesNeedUpdate[attachedVolume.Name]; exists { 128 volume.devicePath = attachedVolume.DevicePath 129 volumesNeedUpdate[attachedVolume.Name] = volume 130 klog.V(4).InfoS("Update devicePath from node status for volume", "volumeName", attachedVolume.Name, "path", volume.devicePath) 131 } 132 } 133 } 134 } 135 136 func (rc *reconciler) updateStates(volumesNeedUpdate map[v1.UniqueVolumeName]*globalVolumeInfo) error { 137 // Get the node status to retrieve volume device path information. 138 // Skip reporting devicePath in node objects if kubeClient is nil. 139 // In standalone mode, kubelet is not expected to mount any attachable volume types or secret, configmaps etc. 140 if rc.kubeClient != nil { 141 rc.updateDevicePath(volumesNeedUpdate) 142 } 143 144 for _, gvl := range volumesNeedUpdate { 145 err := rc.actualStateOfWorld.MarkVolumeAsAttached( 146 //TODO: the devicePath might not be correct for some volume plugins: see issue #54108 147 klog.TODO(), gvl.volumeName, gvl.volumeSpec, rc.nodeName, gvl.devicePath) 148 if err != nil { 149 klog.ErrorS(err, "Could not add volume information to actual state of world", "volumeName", gvl.volumeName) 150 continue 151 } 152 for _, volume := range gvl.podVolumes { 153 err = rc.markVolumeState(volume, operationexecutor.VolumeMounted) 154 if err != nil { 155 klog.ErrorS(err, "Could not add pod to volume information to actual state of world", "pod", klog.KObj(volume.pod)) 156 continue 157 } 158 klog.V(2).InfoS("Volume is marked as mounted and added into the actual state", "pod", klog.KObj(volume.pod), "podName", volume.podName, "volumeName", volume.volumeName) 159 } 160 // If the volume has device to mount, we mark its device as mounted. 161 if gvl.deviceMounter != nil || gvl.blockVolumeMapper != nil { 162 deviceMountPath, err := getDeviceMountPath(gvl) 163 if err != nil { 164 klog.ErrorS(err, "Could not find device mount path for volume", "volumeName", gvl.volumeName) 165 continue 166 } 167 err = rc.actualStateOfWorld.MarkDeviceAsMounted(gvl.volumeName, gvl.devicePath, deviceMountPath, "") 168 if err != nil { 169 klog.ErrorS(err, "Could not mark device is mounted to actual state of world", "volume", gvl.volumeName) 170 continue 171 } 172 klog.V(2).InfoS("Volume is marked device as mounted and added into the actual state", "volumeName", gvl.volumeName) 173 } 174 } 175 return nil 176 } 177 178 func (rc *reconciler) markVolumeState(volume *reconstructedVolume, volumeState operationexecutor.VolumeMountState) error { 179 markVolumeOpts := operationexecutor.MarkVolumeOpts{ 180 PodName: volume.podName, 181 PodUID: types.UID(volume.podName), 182 VolumeName: volume.volumeName, 183 Mounter: volume.mounter, 184 BlockVolumeMapper: volume.blockVolumeMapper, 185 OuterVolumeSpecName: volume.outerVolumeSpecName, 186 VolumeGidVolume: volume.volumeGidValue, 187 VolumeSpec: volume.volumeSpec, 188 VolumeMountState: volumeState, 189 } 190 err := rc.actualStateOfWorld.MarkVolumeAsMounted(markVolumeOpts) 191 return err 192 }