k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/pkg/kubelet/volumemanager/volume_manager.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 volumemanager
    18  
    19  import (
    20  	"context"
    21  	"errors"
    22  	"fmt"
    23  	"sort"
    24  	"strconv"
    25  	"strings"
    26  	"time"
    27  
    28  	utilfeature "k8s.io/apiserver/pkg/util/feature"
    29  	"k8s.io/klog/v2"
    30  	"k8s.io/mount-utils"
    31  
    32  	v1 "k8s.io/api/core/v1"
    33  	k8stypes "k8s.io/apimachinery/pkg/types"
    34  	"k8s.io/apimachinery/pkg/util/runtime"
    35  	"k8s.io/apimachinery/pkg/util/sets"
    36  	"k8s.io/apimachinery/pkg/util/wait"
    37  	clientset "k8s.io/client-go/kubernetes"
    38  	"k8s.io/client-go/tools/record"
    39  	csitrans "k8s.io/csi-translation-lib"
    40  	"k8s.io/kubernetes/pkg/kubelet/config"
    41  	"k8s.io/kubernetes/pkg/kubelet/container"
    42  	"k8s.io/kubernetes/pkg/kubelet/volumemanager/cache"
    43  	"k8s.io/kubernetes/pkg/kubelet/volumemanager/metrics"
    44  	"k8s.io/kubernetes/pkg/kubelet/volumemanager/populator"
    45  	"k8s.io/kubernetes/pkg/kubelet/volumemanager/reconciler"
    46  	"k8s.io/kubernetes/pkg/volume"
    47  	"k8s.io/kubernetes/pkg/volume/csimigration"
    48  	"k8s.io/kubernetes/pkg/volume/util"
    49  	"k8s.io/kubernetes/pkg/volume/util/hostutil"
    50  	"k8s.io/kubernetes/pkg/volume/util/operationexecutor"
    51  	"k8s.io/kubernetes/pkg/volume/util/types"
    52  	"k8s.io/kubernetes/pkg/volume/util/volumepathhandler"
    53  )
    54  
    55  const (
    56  	// reconcilerLoopSleepPeriod is the amount of time the reconciler loop waits
    57  	// between successive executions
    58  	reconcilerLoopSleepPeriod = 100 * time.Millisecond
    59  
    60  	// desiredStateOfWorldPopulatorLoopSleepPeriod is the amount of time the
    61  	// DesiredStateOfWorldPopulator loop waits between successive executions
    62  	desiredStateOfWorldPopulatorLoopSleepPeriod = 100 * time.Millisecond
    63  
    64  	// podAttachAndMountTimeout is the maximum amount of time the
    65  	// WaitForAttachAndMount call will wait for all volumes in the specified pod
    66  	// to be attached and mounted. Even though cloud operations can take several
    67  	// minutes to complete, we set the timeout to 2 minutes because kubelet
    68  	// will retry in the next sync iteration. This frees the associated
    69  	// goroutine of the pod to process newer updates if needed (e.g., a delete
    70  	// request to the pod).
    71  	// Value is slightly offset from 2 minutes to make timeouts due to this
    72  	// constant recognizable.
    73  	podAttachAndMountTimeout = 2*time.Minute + 3*time.Second
    74  
    75  	// podAttachAndMountRetryInterval is the amount of time the GetVolumesForPod
    76  	// call waits before retrying
    77  	podAttachAndMountRetryInterval = 300 * time.Millisecond
    78  
    79  	// waitForAttachTimeout is the maximum amount of time a
    80  	// operationexecutor.Mount call will wait for a volume to be attached.
    81  	// Set to 10 minutes because we've seen attach operations take several
    82  	// minutes to complete for some volume plugins in some cases. While this
    83  	// operation is waiting it only blocks other operations on the same device,
    84  	// other devices are not affected.
    85  	waitForAttachTimeout = 10 * time.Minute
    86  )
    87  
    88  // VolumeManager runs a set of asynchronous loops that figure out which volumes
    89  // need to be attached/mounted/unmounted/detached based on the pods scheduled on
    90  // this node and makes it so.
    91  type VolumeManager interface {
    92  	// Starts the volume manager and all the asynchronous loops that it controls
    93  	Run(sourcesReady config.SourcesReady, stopCh <-chan struct{})
    94  
    95  	// WaitForAttachAndMount processes the volumes referenced in the specified
    96  	// pod and blocks until they are all attached and mounted (reflected in
    97  	// actual state of the world).
    98  	// An error is returned if all volumes are not attached and mounted within
    99  	// the duration defined in podAttachAndMountTimeout.
   100  	WaitForAttachAndMount(ctx context.Context, pod *v1.Pod) error
   101  
   102  	// WaitForUnmount processes the volumes referenced in the specified
   103  	// pod and blocks until they are all unmounted (reflected in the actual
   104  	// state of the world).
   105  	// An error is returned if all volumes are not unmounted within
   106  	// the duration defined in podAttachAndMountTimeout.
   107  	WaitForUnmount(ctx context.Context, pod *v1.Pod) error
   108  
   109  	// GetMountedVolumesForPod returns a VolumeMap containing the volumes
   110  	// referenced by the specified pod that are successfully attached and
   111  	// mounted. The key in the map is the OuterVolumeSpecName (i.e.
   112  	// pod.Spec.Volumes[x].Name). It returns an empty VolumeMap if pod has no
   113  	// volumes.
   114  	GetMountedVolumesForPod(podName types.UniquePodName) container.VolumeMap
   115  
   116  	// GetPossiblyMountedVolumesForPod returns a VolumeMap containing the volumes
   117  	// referenced by the specified pod that are either successfully attached
   118  	// and mounted or are "uncertain", i.e. a volume plugin may be mounting
   119  	// them right now. The key in the map is the OuterVolumeSpecName (i.e.
   120  	// pod.Spec.Volumes[x].Name). It returns an empty VolumeMap if pod has no
   121  	// volumes.
   122  	GetPossiblyMountedVolumesForPod(podName types.UniquePodName) container.VolumeMap
   123  
   124  	// GetExtraSupplementalGroupsForPod returns a list of the extra
   125  	// supplemental groups for the Pod. These extra supplemental groups come
   126  	// from annotations on persistent volumes that the pod depends on.
   127  	GetExtraSupplementalGroupsForPod(pod *v1.Pod) []int64
   128  
   129  	// GetVolumesInUse returns a list of all volumes that implement the volume.Attacher
   130  	// interface and are currently in use according to the actual and desired
   131  	// state of the world caches. A volume is considered "in use" as soon as it
   132  	// is added to the desired state of world, indicating it *should* be
   133  	// attached to this node and remains "in use" until it is removed from both
   134  	// the desired state of the world and the actual state of the world, or it
   135  	// has been unmounted (as indicated in actual state of world).
   136  	GetVolumesInUse() []v1.UniqueVolumeName
   137  
   138  	// ReconcilerStatesHasBeenSynced returns true only after the actual states in reconciler
   139  	// has been synced at least once after kubelet starts so that it is safe to update mounted
   140  	// volume list retrieved from actual state.
   141  	ReconcilerStatesHasBeenSynced() bool
   142  
   143  	// VolumeIsAttached returns true if the given volume is attached to this
   144  	// node.
   145  	VolumeIsAttached(volumeName v1.UniqueVolumeName) bool
   146  
   147  	// Marks the specified volume as having successfully been reported as "in
   148  	// use" in the nodes's volume status.
   149  	MarkVolumesAsReportedInUse(volumesReportedAsInUse []v1.UniqueVolumeName)
   150  }
   151  
   152  // podStateProvider can determine if a pod is going to be terminated
   153  type PodStateProvider interface {
   154  	ShouldPodContainersBeTerminating(k8stypes.UID) bool
   155  	ShouldPodRuntimeBeRemoved(k8stypes.UID) bool
   156  }
   157  
   158  // PodManager is the subset of methods the manager needs to observe the actual state of the kubelet.
   159  // See pkg/k8s.io/kubernetes/pkg/kubelet/pod.Manager for method godoc.
   160  type PodManager interface {
   161  	GetPodByUID(k8stypes.UID) (*v1.Pod, bool)
   162  	GetPods() []*v1.Pod
   163  }
   164  
   165  // NewVolumeManager returns a new concrete instance implementing the
   166  // VolumeManager interface.
   167  //
   168  // kubeClient - kubeClient is the kube API client used by DesiredStateOfWorldPopulator
   169  // to communicate with the API server to fetch PV and PVC objects
   170  //
   171  // volumePluginMgr - the volume plugin manager used to access volume plugins.
   172  // Must be pre-initialized.
   173  func NewVolumeManager(
   174  	controllerAttachDetachEnabled bool,
   175  	nodeName k8stypes.NodeName,
   176  	podManager PodManager,
   177  	podStateProvider PodStateProvider,
   178  	kubeClient clientset.Interface,
   179  	volumePluginMgr *volume.VolumePluginMgr,
   180  	kubeContainerRuntime container.Runtime,
   181  	mounter mount.Interface,
   182  	hostutil hostutil.HostUtils,
   183  	kubeletPodsDir string,
   184  	recorder record.EventRecorder,
   185  	blockVolumePathHandler volumepathhandler.BlockVolumePathHandler) VolumeManager {
   186  
   187  	seLinuxTranslator := util.NewSELinuxLabelTranslator()
   188  	vm := &volumeManager{
   189  		kubeClient:          kubeClient,
   190  		volumePluginMgr:     volumePluginMgr,
   191  		desiredStateOfWorld: cache.NewDesiredStateOfWorld(volumePluginMgr, seLinuxTranslator),
   192  		actualStateOfWorld:  cache.NewActualStateOfWorld(nodeName, volumePluginMgr),
   193  		operationExecutor: operationexecutor.NewOperationExecutor(operationexecutor.NewOperationGenerator(
   194  			kubeClient,
   195  			volumePluginMgr,
   196  			recorder,
   197  			blockVolumePathHandler)),
   198  	}
   199  
   200  	intreeToCSITranslator := csitrans.New()
   201  	csiMigratedPluginManager := csimigration.NewPluginManager(intreeToCSITranslator, utilfeature.DefaultFeatureGate)
   202  
   203  	vm.intreeToCSITranslator = intreeToCSITranslator
   204  	vm.csiMigratedPluginManager = csiMigratedPluginManager
   205  	vm.desiredStateOfWorldPopulator = populator.NewDesiredStateOfWorldPopulator(
   206  		kubeClient,
   207  		desiredStateOfWorldPopulatorLoopSleepPeriod,
   208  		podManager,
   209  		podStateProvider,
   210  		vm.desiredStateOfWorld,
   211  		vm.actualStateOfWorld,
   212  		kubeContainerRuntime,
   213  		csiMigratedPluginManager,
   214  		intreeToCSITranslator,
   215  		volumePluginMgr)
   216  	vm.reconciler = reconciler.NewReconciler(
   217  		kubeClient,
   218  		controllerAttachDetachEnabled,
   219  		reconcilerLoopSleepPeriod,
   220  		waitForAttachTimeout,
   221  		nodeName,
   222  		vm.desiredStateOfWorld,
   223  		vm.actualStateOfWorld,
   224  		vm.desiredStateOfWorldPopulator.HasAddedPods,
   225  		vm.operationExecutor,
   226  		mounter,
   227  		hostutil,
   228  		volumePluginMgr,
   229  		kubeletPodsDir)
   230  
   231  	return vm
   232  }
   233  
   234  // volumeManager implements the VolumeManager interface
   235  type volumeManager struct {
   236  	// kubeClient is the kube API client used by DesiredStateOfWorldPopulator to
   237  	// communicate with the API server to fetch PV and PVC objects
   238  	kubeClient clientset.Interface
   239  
   240  	// volumePluginMgr is the volume plugin manager used to access volume
   241  	// plugins. It must be pre-initialized.
   242  	volumePluginMgr *volume.VolumePluginMgr
   243  
   244  	// desiredStateOfWorld is a data structure containing the desired state of
   245  	// the world according to the volume manager: i.e. what volumes should be
   246  	// attached and which pods are referencing the volumes).
   247  	// The data structure is populated by the desired state of the world
   248  	// populator using the kubelet pod manager.
   249  	desiredStateOfWorld cache.DesiredStateOfWorld
   250  
   251  	// actualStateOfWorld is a data structure containing the actual state of
   252  	// the world according to the manager: i.e. which volumes are attached to
   253  	// this node and what pods the volumes are mounted to.
   254  	// The data structure is populated upon successful completion of attach,
   255  	// detach, mount, and unmount actions triggered by the reconciler.
   256  	actualStateOfWorld cache.ActualStateOfWorld
   257  
   258  	// operationExecutor is used to start asynchronous attach, detach, mount,
   259  	// and unmount operations.
   260  	operationExecutor operationexecutor.OperationExecutor
   261  
   262  	// reconciler runs an asynchronous periodic loop to reconcile the
   263  	// desiredStateOfWorld with the actualStateOfWorld by triggering attach,
   264  	// detach, mount, and unmount operations using the operationExecutor.
   265  	reconciler reconciler.Reconciler
   266  
   267  	// desiredStateOfWorldPopulator runs an asynchronous periodic loop to
   268  	// populate the desiredStateOfWorld using the kubelet PodManager.
   269  	desiredStateOfWorldPopulator populator.DesiredStateOfWorldPopulator
   270  
   271  	// csiMigratedPluginManager keeps track of CSI migration status of plugins
   272  	csiMigratedPluginManager csimigration.PluginManager
   273  
   274  	// intreeToCSITranslator translates in-tree volume specs to CSI
   275  	intreeToCSITranslator csimigration.InTreeToCSITranslator
   276  }
   277  
   278  func (vm *volumeManager) Run(sourcesReady config.SourcesReady, stopCh <-chan struct{}) {
   279  	defer runtime.HandleCrash()
   280  
   281  	if vm.kubeClient != nil {
   282  		// start informer for CSIDriver
   283  		go vm.volumePluginMgr.Run(stopCh)
   284  	}
   285  
   286  	go vm.desiredStateOfWorldPopulator.Run(sourcesReady, stopCh)
   287  	klog.V(2).InfoS("The desired_state_of_world populator starts")
   288  
   289  	klog.InfoS("Starting Kubelet Volume Manager")
   290  	go vm.reconciler.Run(stopCh)
   291  
   292  	metrics.Register(vm.actualStateOfWorld, vm.desiredStateOfWorld, vm.volumePluginMgr)
   293  
   294  	<-stopCh
   295  	klog.InfoS("Shutting down Kubelet Volume Manager")
   296  }
   297  
   298  func (vm *volumeManager) GetMountedVolumesForPod(podName types.UniquePodName) container.VolumeMap {
   299  	podVolumes := make(container.VolumeMap)
   300  	for _, mountedVolume := range vm.actualStateOfWorld.GetMountedVolumesForPod(podName) {
   301  		podVolumes[mountedVolume.OuterVolumeSpecName] = container.VolumeInfo{
   302  			Mounter:             mountedVolume.Mounter,
   303  			BlockVolumeMapper:   mountedVolume.BlockVolumeMapper,
   304  			ReadOnly:            mountedVolume.VolumeSpec.ReadOnly,
   305  			InnerVolumeSpecName: mountedVolume.InnerVolumeSpecName,
   306  		}
   307  	}
   308  	return podVolumes
   309  }
   310  
   311  func (vm *volumeManager) GetPossiblyMountedVolumesForPod(podName types.UniquePodName) container.VolumeMap {
   312  	podVolumes := make(container.VolumeMap)
   313  	for _, mountedVolume := range vm.actualStateOfWorld.GetPossiblyMountedVolumesForPod(podName) {
   314  		podVolumes[mountedVolume.OuterVolumeSpecName] = container.VolumeInfo{
   315  			Mounter:             mountedVolume.Mounter,
   316  			BlockVolumeMapper:   mountedVolume.BlockVolumeMapper,
   317  			ReadOnly:            mountedVolume.VolumeSpec.ReadOnly,
   318  			InnerVolumeSpecName: mountedVolume.InnerVolumeSpecName,
   319  		}
   320  	}
   321  	return podVolumes
   322  }
   323  
   324  func (vm *volumeManager) GetExtraSupplementalGroupsForPod(pod *v1.Pod) []int64 {
   325  	podName := util.GetUniquePodName(pod)
   326  	supplementalGroups := sets.NewString()
   327  
   328  	for _, mountedVolume := range vm.actualStateOfWorld.GetMountedVolumesForPod(podName) {
   329  		if mountedVolume.VolumeGidValue != "" {
   330  			supplementalGroups.Insert(mountedVolume.VolumeGidValue)
   331  		}
   332  	}
   333  
   334  	result := make([]int64, 0, supplementalGroups.Len())
   335  	for _, group := range supplementalGroups.List() {
   336  		iGroup, extra := getExtraSupplementalGid(group, pod)
   337  		if !extra {
   338  			continue
   339  		}
   340  
   341  		result = append(result, int64(iGroup))
   342  	}
   343  
   344  	return result
   345  }
   346  
   347  func (vm *volumeManager) GetVolumesInUse() []v1.UniqueVolumeName {
   348  	// Report volumes in desired state of world and actual state of world so
   349  	// that volumes are marked in use as soon as the decision is made that the
   350  	// volume *should* be attached to this node until it is safely unmounted.
   351  	desiredVolumes := vm.desiredStateOfWorld.GetVolumesToMount()
   352  	allAttachedVolumes := vm.actualStateOfWorld.GetAttachedVolumes()
   353  	volumesToReportInUse := make([]v1.UniqueVolumeName, 0, len(desiredVolumes)+len(allAttachedVolumes))
   354  	desiredVolumesMap := make(map[v1.UniqueVolumeName]bool, len(desiredVolumes)+len(allAttachedVolumes))
   355  
   356  	for _, volume := range desiredVolumes {
   357  		if volume.PluginIsAttachable {
   358  			if _, exists := desiredVolumesMap[volume.VolumeName]; !exists {
   359  				desiredVolumesMap[volume.VolumeName] = true
   360  				volumesToReportInUse = append(volumesToReportInUse, volume.VolumeName)
   361  			}
   362  		}
   363  	}
   364  
   365  	for _, volume := range allAttachedVolumes {
   366  		if volume.PluginIsAttachable {
   367  			if _, exists := desiredVolumesMap[volume.VolumeName]; !exists {
   368  				volumesToReportInUse = append(volumesToReportInUse, volume.VolumeName)
   369  			}
   370  		}
   371  	}
   372  
   373  	sort.Slice(volumesToReportInUse, func(i, j int) bool {
   374  		return string(volumesToReportInUse[i]) < string(volumesToReportInUse[j])
   375  	})
   376  	return volumesToReportInUse
   377  }
   378  
   379  func (vm *volumeManager) ReconcilerStatesHasBeenSynced() bool {
   380  	return vm.reconciler.StatesHasBeenSynced()
   381  }
   382  
   383  func (vm *volumeManager) VolumeIsAttached(
   384  	volumeName v1.UniqueVolumeName) bool {
   385  	return vm.actualStateOfWorld.VolumeExists(volumeName)
   386  }
   387  
   388  func (vm *volumeManager) MarkVolumesAsReportedInUse(
   389  	volumesReportedAsInUse []v1.UniqueVolumeName) {
   390  	vm.desiredStateOfWorld.MarkVolumesReportedInUse(volumesReportedAsInUse)
   391  }
   392  
   393  func (vm *volumeManager) WaitForAttachAndMount(ctx context.Context, pod *v1.Pod) error {
   394  	if pod == nil {
   395  		return nil
   396  	}
   397  
   398  	expectedVolumes := getExpectedVolumes(pod)
   399  	if len(expectedVolumes) == 0 {
   400  		// No volumes to verify
   401  		return nil
   402  	}
   403  
   404  	klog.V(3).InfoS("Waiting for volumes to attach and mount for pod", "pod", klog.KObj(pod))
   405  	uniquePodName := util.GetUniquePodName(pod)
   406  
   407  	// Some pods expect to have Setup called over and over again to update.
   408  	// Remount plugins for which this is true. (Atomically updating volumes,
   409  	// like Downward API, depend on this to update the contents of the volume).
   410  	vm.desiredStateOfWorldPopulator.ReprocessPod(uniquePodName)
   411  
   412  	err := wait.PollUntilContextTimeout(
   413  		ctx,
   414  		podAttachAndMountRetryInterval,
   415  		podAttachAndMountTimeout,
   416  		true,
   417  		vm.verifyVolumesMountedFunc(uniquePodName, expectedVolumes))
   418  
   419  	if err != nil {
   420  		unmountedVolumes :=
   421  			vm.getUnmountedVolumes(uniquePodName, expectedVolumes)
   422  		// Also get unattached volumes and volumes not in dsw for error message
   423  		unattachedVolumes :=
   424  			vm.getUnattachedVolumes(uniquePodName)
   425  		volumesNotInDSW :=
   426  			vm.getVolumesNotInDSW(uniquePodName, expectedVolumes)
   427  
   428  		if len(unmountedVolumes) == 0 {
   429  			return nil
   430  		}
   431  
   432  		return fmt.Errorf(
   433  			"unmounted volumes=%v, unattached volumes=%v, failed to process volumes=%v: %w",
   434  			unmountedVolumes,
   435  			unattachedVolumes,
   436  			volumesNotInDSW,
   437  			err)
   438  	}
   439  
   440  	klog.V(3).InfoS("All volumes are attached and mounted for pod", "pod", klog.KObj(pod))
   441  	return nil
   442  }
   443  
   444  func (vm *volumeManager) WaitForUnmount(ctx context.Context, pod *v1.Pod) error {
   445  	if pod == nil {
   446  		return nil
   447  	}
   448  
   449  	klog.V(3).InfoS("Waiting for volumes to unmount for pod", "pod", klog.KObj(pod))
   450  	uniquePodName := util.GetUniquePodName(pod)
   451  
   452  	vm.desiredStateOfWorldPopulator.ReprocessPod(uniquePodName)
   453  
   454  	err := wait.PollUntilContextTimeout(
   455  		ctx,
   456  		podAttachAndMountRetryInterval,
   457  		podAttachAndMountTimeout,
   458  		true,
   459  		vm.verifyVolumesUnmountedFunc(uniquePodName))
   460  
   461  	if err != nil {
   462  		var mountedVolumes []string
   463  		for _, v := range vm.actualStateOfWorld.GetMountedVolumesForPod(uniquePodName) {
   464  			mountedVolumes = append(mountedVolumes, v.OuterVolumeSpecName)
   465  		}
   466  		sort.Strings(mountedVolumes)
   467  
   468  		if len(mountedVolumes) == 0 {
   469  			return nil
   470  		}
   471  
   472  		return fmt.Errorf(
   473  			"mounted volumes=%v: %w",
   474  			mountedVolumes,
   475  			err)
   476  	}
   477  
   478  	klog.V(3).InfoS("All volumes are unmounted for pod", "pod", klog.KObj(pod))
   479  	return nil
   480  }
   481  
   482  func (vm *volumeManager) getVolumesNotInDSW(uniquePodName types.UniquePodName, expectedVolumes []string) []string {
   483  	volumesNotInDSW := sets.NewString(expectedVolumes...)
   484  
   485  	for _, volumeToMount := range vm.desiredStateOfWorld.GetVolumesToMount() {
   486  		if volumeToMount.PodName == uniquePodName {
   487  			volumesNotInDSW.Delete(volumeToMount.OuterVolumeSpecName)
   488  		}
   489  	}
   490  
   491  	return volumesNotInDSW.List()
   492  }
   493  
   494  // getUnattachedVolumes returns a list of the volumes that are expected to be attached but
   495  // are not currently attached to the node
   496  func (vm *volumeManager) getUnattachedVolumes(uniquePodName types.UniquePodName) []string {
   497  	unattachedVolumes := []string{}
   498  	for _, volumeToMount := range vm.desiredStateOfWorld.GetVolumesToMount() {
   499  		if volumeToMount.PodName == uniquePodName &&
   500  			volumeToMount.PluginIsAttachable &&
   501  			!vm.actualStateOfWorld.VolumeExists(volumeToMount.VolumeName) {
   502  			unattachedVolumes = append(unattachedVolumes, volumeToMount.OuterVolumeSpecName)
   503  		}
   504  	}
   505  	sort.Strings(unattachedVolumes)
   506  
   507  	return unattachedVolumes
   508  }
   509  
   510  // verifyVolumesMountedFunc returns a method that returns true when all expected
   511  // volumes are mounted.
   512  func (vm *volumeManager) verifyVolumesMountedFunc(podName types.UniquePodName, expectedVolumes []string) wait.ConditionWithContextFunc {
   513  	return func(_ context.Context) (done bool, err error) {
   514  		if errs := vm.desiredStateOfWorld.PopPodErrors(podName); len(errs) > 0 {
   515  			return true, errors.New(strings.Join(errs, "; "))
   516  		}
   517  		return len(vm.getUnmountedVolumes(podName, expectedVolumes)) == 0, nil
   518  	}
   519  }
   520  
   521  // verifyVolumesUnmountedFunc returns a method that is true when there are no mounted volumes for this
   522  // pod.
   523  func (vm *volumeManager) verifyVolumesUnmountedFunc(podName types.UniquePodName) wait.ConditionWithContextFunc {
   524  	return func(_ context.Context) (done bool, err error) {
   525  		if errs := vm.desiredStateOfWorld.PopPodErrors(podName); len(errs) > 0 {
   526  			return true, errors.New(strings.Join(errs, "; "))
   527  		}
   528  		return len(vm.actualStateOfWorld.GetMountedVolumesForPod(podName)) == 0, nil
   529  	}
   530  }
   531  
   532  // getUnmountedVolumes fetches the current list of mounted volumes from
   533  // the actual state of the world, and uses it to process the list of
   534  // expectedVolumes. It returns a list of unmounted volumes.
   535  // The list also includes volume that may be mounted in uncertain state.
   536  func (vm *volumeManager) getUnmountedVolumes(podName types.UniquePodName, expectedVolumes []string) []string {
   537  	mountedVolumes := sets.NewString()
   538  	for _, mountedVolume := range vm.actualStateOfWorld.GetMountedVolumesForPod(podName) {
   539  		mountedVolumes.Insert(mountedVolume.OuterVolumeSpecName)
   540  	}
   541  	return filterUnmountedVolumes(mountedVolumes, expectedVolumes)
   542  }
   543  
   544  // filterUnmountedVolumes adds each element of expectedVolumes that is not in
   545  // mountedVolumes to a list of unmountedVolumes and returns it.
   546  func filterUnmountedVolumes(mountedVolumes sets.String, expectedVolumes []string) []string {
   547  	unmountedVolumes := []string{}
   548  	for _, expectedVolume := range expectedVolumes {
   549  		if !mountedVolumes.Has(expectedVolume) {
   550  			unmountedVolumes = append(unmountedVolumes, expectedVolume)
   551  		}
   552  	}
   553  	sort.Strings(unmountedVolumes)
   554  
   555  	return unmountedVolumes
   556  }
   557  
   558  // getExpectedVolumes returns a list of volumes that must be mounted in order to
   559  // consider the volume setup step for this pod satisfied.
   560  func getExpectedVolumes(pod *v1.Pod) []string {
   561  	mounts, devices, _ := util.GetPodVolumeNames(pod)
   562  	return mounts.Union(devices).UnsortedList()
   563  }
   564  
   565  // getExtraSupplementalGid returns the value of an extra supplemental GID as
   566  // defined by an annotation on a volume and a boolean indicating whether the
   567  // volume defined a GID that the pod doesn't already request.
   568  func getExtraSupplementalGid(volumeGidValue string, pod *v1.Pod) (int64, bool) {
   569  	if volumeGidValue == "" {
   570  		return 0, false
   571  	}
   572  
   573  	gid, err := strconv.ParseInt(volumeGidValue, 10, 64)
   574  	if err != nil {
   575  		return 0, false
   576  	}
   577  
   578  	if pod.Spec.SecurityContext != nil {
   579  		for _, existingGid := range pod.Spec.SecurityContext.SupplementalGroups {
   580  			if gid == int64(existingGid) {
   581  				return 0, false
   582  			}
   583  		}
   584  	}
   585  
   586  	return gid, true
   587  }