k8s.io/kubernetes@v1.29.3/pkg/kubelet/volumemanager/reconciler/reconciler.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 implements interfaces that attempt to reconcile the 18 // desired state of the world with the actual state of the world by triggering 19 // relevant actions (attach, detach, mount, unmount). 20 package reconciler 21 22 import ( 23 "k8s.io/apimachinery/pkg/types" 24 "k8s.io/apimachinery/pkg/util/wait" 25 "k8s.io/klog/v2" 26 "k8s.io/kubernetes/pkg/volume/util/operationexecutor" 27 ) 28 29 func (rc *reconciler) runOld(stopCh <-chan struct{}) { 30 wait.Until(rc.reconciliationLoopFunc(), rc.loopSleepDuration, stopCh) 31 } 32 33 func (rc *reconciler) reconciliationLoopFunc() func() { 34 return func() { 35 rc.reconcile() 36 37 // Sync the state with the reality once after all existing pods are added to the desired state from all sources. 38 // Otherwise, the reconstruct process may clean up pods' volumes that are still in use because 39 // desired state of world does not contain a complete list of pods. 40 if rc.populatorHasAddedPods() && !rc.StatesHasBeenSynced() { 41 klog.InfoS("Reconciler: start to sync state") 42 rc.sync() 43 } 44 } 45 } 46 47 func (rc *reconciler) reconcile() { 48 // Unmounts are triggered before mounts so that a volume that was 49 // referenced by a pod that was deleted and is now referenced by another 50 // pod is unmounted from the first pod before being mounted to the new 51 // pod. 52 rc.unmountVolumes() 53 54 // Next we mount required volumes. This function could also trigger 55 // attach if kubelet is responsible for attaching volumes. 56 // If underlying PVC was resized while in-use then this function also handles volume 57 // resizing. 58 rc.mountOrAttachVolumes() 59 60 // Ensure devices that should be detached/unmounted are detached/unmounted. 61 rc.unmountDetachDevices() 62 63 // After running the above operations if skippedDuringReconstruction is not empty 64 // then ensure that all volumes which were discovered and skipped during reconstruction 65 // are added to actualStateOfWorld in uncertain state. 66 if len(rc.skippedDuringReconstruction) > 0 { 67 rc.processReconstructedVolumes() 68 } 69 } 70 71 // processReconstructedVolumes checks volumes which were skipped during the reconstruction 72 // process because it was assumed that since these volumes were present in DSOW they would get 73 // mounted correctly and make it into ASOW. 74 // But if mount operation fails for some reason then we still need to mark the volume as uncertain 75 // and wait for the next reconciliation loop to deal with it. 76 func (rc *reconciler) processReconstructedVolumes() { 77 for volumeName, glblVolumeInfo := range rc.skippedDuringReconstruction { 78 // check if volume is marked as attached to the node 79 // for now lets only process volumes which are at least known as attached to the node 80 // this should help with most volume types (including secret, configmap etc) 81 if !rc.actualStateOfWorld.VolumeExists(volumeName) { 82 klog.V(4).InfoS("Volume is not marked as attached to the node. Skipping processing of the volume", "volumeName", volumeName) 83 continue 84 } 85 uncertainVolumeCount := 0 86 // only delete volumes which were marked as attached here. 87 // This should ensure that - we will wait for volumes which were not marked as attached 88 // before adding them in uncertain state during reconstruction. 89 delete(rc.skippedDuringReconstruction, volumeName) 90 91 for podName, volume := range glblVolumeInfo.podVolumes { 92 markVolumeOpts := operationexecutor.MarkVolumeOpts{ 93 PodName: volume.podName, 94 PodUID: types.UID(podName), 95 VolumeName: volume.volumeName, 96 Mounter: volume.mounter, 97 BlockVolumeMapper: volume.blockVolumeMapper, 98 OuterVolumeSpecName: volume.outerVolumeSpecName, 99 VolumeGidVolume: volume.volumeGidValue, 100 VolumeSpec: volume.volumeSpec, 101 VolumeMountState: operationexecutor.VolumeMountUncertain, 102 } 103 104 volumeAdded, err := rc.actualStateOfWorld.CheckAndMarkVolumeAsUncertainViaReconstruction(markVolumeOpts) 105 106 // if volume is not mounted then lets mark volume mounted in uncertain state in ASOW 107 if volumeAdded { 108 uncertainVolumeCount += 1 109 if err != nil { 110 klog.ErrorS(err, "Could not add pod to volume information to actual state of world", "pod", klog.KObj(volume.pod)) 111 continue 112 } 113 klog.V(4).InfoS("Volume is marked as mounted in uncertain state and added to the actual state", "pod", klog.KObj(volume.pod), "podName", volume.podName, "volumeName", volume.volumeName) 114 } 115 } 116 117 if uncertainVolumeCount > 0 { 118 // If the volume has device to mount, we mark its device as uncertain 119 if glblVolumeInfo.deviceMounter != nil || glblVolumeInfo.blockVolumeMapper != nil { 120 deviceMountPath, err := getDeviceMountPath(glblVolumeInfo) 121 if err != nil { 122 klog.ErrorS(err, "Could not find device mount path for volume", "volumeName", glblVolumeInfo.volumeName) 123 continue 124 } 125 deviceMounted := rc.actualStateOfWorld.CheckAndMarkDeviceUncertainViaReconstruction(glblVolumeInfo.volumeName, deviceMountPath) 126 if !deviceMounted { 127 klog.V(3).InfoS("Could not mark device as mounted in uncertain state", "volumeName", glblVolumeInfo.volumeName) 128 } 129 } 130 } 131 } 132 }