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  }