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  }