k8s.io/kubernetes@v1.29.3/pkg/controller/volume/attachdetach/attach_detach_controller.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 attachdetach implements a controller to manage volume attach and detach
    18  // operations.
    19  package attachdetach
    20  
    21  import (
    22  	"context"
    23  	"fmt"
    24  	"net"
    25  	"time"
    26  
    27  	"k8s.io/klog/v2"
    28  	"k8s.io/mount-utils"
    29  	utilexec "k8s.io/utils/exec"
    30  
    31  	authenticationv1 "k8s.io/api/authentication/v1"
    32  	v1 "k8s.io/api/core/v1"
    33  	apierrors "k8s.io/apimachinery/pkg/api/errors"
    34  	"k8s.io/apimachinery/pkg/labels"
    35  	"k8s.io/apimachinery/pkg/types"
    36  	"k8s.io/apimachinery/pkg/util/runtime"
    37  	"k8s.io/apimachinery/pkg/util/wait"
    38  	utilfeature "k8s.io/apiserver/pkg/util/feature"
    39  	coreinformers "k8s.io/client-go/informers/core/v1"
    40  	storageinformersv1 "k8s.io/client-go/informers/storage/v1"
    41  	clientset "k8s.io/client-go/kubernetes"
    42  	"k8s.io/client-go/kubernetes/scheme"
    43  	v1core "k8s.io/client-go/kubernetes/typed/core/v1"
    44  	corelisters "k8s.io/client-go/listers/core/v1"
    45  	storagelistersv1 "k8s.io/client-go/listers/storage/v1"
    46  	kcache "k8s.io/client-go/tools/cache"
    47  	"k8s.io/client-go/tools/record"
    48  	"k8s.io/client-go/util/workqueue"
    49  	cloudprovider "k8s.io/cloud-provider"
    50  	csitrans "k8s.io/csi-translation-lib"
    51  	"k8s.io/kubernetes/pkg/controller/volume/attachdetach/cache"
    52  	"k8s.io/kubernetes/pkg/controller/volume/attachdetach/metrics"
    53  	"k8s.io/kubernetes/pkg/controller/volume/attachdetach/populator"
    54  	"k8s.io/kubernetes/pkg/controller/volume/attachdetach/reconciler"
    55  	"k8s.io/kubernetes/pkg/controller/volume/attachdetach/statusupdater"
    56  	"k8s.io/kubernetes/pkg/controller/volume/attachdetach/util"
    57  	"k8s.io/kubernetes/pkg/controller/volume/common"
    58  	"k8s.io/kubernetes/pkg/volume"
    59  	"k8s.io/kubernetes/pkg/volume/csi"
    60  	"k8s.io/kubernetes/pkg/volume/csimigration"
    61  	volumeutil "k8s.io/kubernetes/pkg/volume/util"
    62  	"k8s.io/kubernetes/pkg/volume/util/operationexecutor"
    63  	"k8s.io/kubernetes/pkg/volume/util/subpath"
    64  	"k8s.io/kubernetes/pkg/volume/util/volumepathhandler"
    65  )
    66  
    67  // TimerConfig contains configuration of internal attach/detach timers and
    68  // should be used only to speed up tests. DefaultTimerConfig is the suggested
    69  // timer configuration for production.
    70  type TimerConfig struct {
    71  	// ReconcilerLoopPeriod is the amount of time the reconciler loop waits
    72  	// between successive executions
    73  	ReconcilerLoopPeriod time.Duration
    74  
    75  	// ReconcilerMaxWaitForUnmountDuration is the maximum amount of time the
    76  	// attach detach controller will wait for a volume to be safely unmounted
    77  	// from its node. Once this time has expired, the controller will assume the
    78  	// node or kubelet are unresponsive and will detach the volume anyway.
    79  	ReconcilerMaxWaitForUnmountDuration time.Duration
    80  
    81  	// DesiredStateOfWorldPopulatorLoopSleepPeriod is the amount of time the
    82  	// DesiredStateOfWorldPopulator loop waits between successive executions
    83  	DesiredStateOfWorldPopulatorLoopSleepPeriod time.Duration
    84  
    85  	// DesiredStateOfWorldPopulatorListPodsRetryDuration is the amount of
    86  	// time the DesiredStateOfWorldPopulator loop waits between list pods
    87  	// calls.
    88  	DesiredStateOfWorldPopulatorListPodsRetryDuration time.Duration
    89  }
    90  
    91  // DefaultTimerConfig is the default configuration of Attach/Detach controller
    92  // timers.
    93  var DefaultTimerConfig = TimerConfig{
    94  	ReconcilerLoopPeriod:                              100 * time.Millisecond,
    95  	ReconcilerMaxWaitForUnmountDuration:               6 * time.Minute,
    96  	DesiredStateOfWorldPopulatorLoopSleepPeriod:       1 * time.Minute,
    97  	DesiredStateOfWorldPopulatorListPodsRetryDuration: 3 * time.Minute,
    98  }
    99  
   100  // AttachDetachController defines the operations supported by this controller.
   101  type AttachDetachController interface {
   102  	Run(ctx context.Context)
   103  	GetDesiredStateOfWorld() cache.DesiredStateOfWorld
   104  }
   105  
   106  // NewAttachDetachController returns a new instance of AttachDetachController.
   107  func NewAttachDetachController(
   108  	logger klog.Logger,
   109  	kubeClient clientset.Interface,
   110  	podInformer coreinformers.PodInformer,
   111  	nodeInformer coreinformers.NodeInformer,
   112  	pvcInformer coreinformers.PersistentVolumeClaimInformer,
   113  	pvInformer coreinformers.PersistentVolumeInformer,
   114  	csiNodeInformer storageinformersv1.CSINodeInformer,
   115  	csiDriverInformer storageinformersv1.CSIDriverInformer,
   116  	volumeAttachmentInformer storageinformersv1.VolumeAttachmentInformer,
   117  	cloud cloudprovider.Interface,
   118  	plugins []volume.VolumePlugin,
   119  	prober volume.DynamicPluginProber,
   120  	disableReconciliationSync bool,
   121  	reconcilerSyncDuration time.Duration,
   122  	timerConfig TimerConfig) (AttachDetachController, error) {
   123  
   124  	adc := &attachDetachController{
   125  		kubeClient:  kubeClient,
   126  		pvcLister:   pvcInformer.Lister(),
   127  		pvcsSynced:  pvcInformer.Informer().HasSynced,
   128  		pvLister:    pvInformer.Lister(),
   129  		pvsSynced:   pvInformer.Informer().HasSynced,
   130  		podLister:   podInformer.Lister(),
   131  		podsSynced:  podInformer.Informer().HasSynced,
   132  		podIndexer:  podInformer.Informer().GetIndexer(),
   133  		nodeLister:  nodeInformer.Lister(),
   134  		nodesSynced: nodeInformer.Informer().HasSynced,
   135  		cloud:       cloud,
   136  		pvcQueue:    workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "pvcs"),
   137  	}
   138  
   139  	adc.csiNodeLister = csiNodeInformer.Lister()
   140  	adc.csiNodeSynced = csiNodeInformer.Informer().HasSynced
   141  
   142  	adc.csiDriverLister = csiDriverInformer.Lister()
   143  	adc.csiDriversSynced = csiDriverInformer.Informer().HasSynced
   144  
   145  	adc.volumeAttachmentLister = volumeAttachmentInformer.Lister()
   146  	adc.volumeAttachmentSynced = volumeAttachmentInformer.Informer().HasSynced
   147  
   148  	if err := adc.volumePluginMgr.InitPlugins(plugins, prober, adc); err != nil {
   149  		return nil, fmt.Errorf("could not initialize volume plugins for Attach/Detach Controller: %w", err)
   150  	}
   151  
   152  	adc.broadcaster = record.NewBroadcaster()
   153  	recorder := adc.broadcaster.NewRecorder(scheme.Scheme, v1.EventSource{Component: "attachdetach-controller"})
   154  	blkutil := volumepathhandler.NewBlockVolumePathHandler()
   155  
   156  	adc.desiredStateOfWorld = cache.NewDesiredStateOfWorld(&adc.volumePluginMgr)
   157  	adc.actualStateOfWorld = cache.NewActualStateOfWorld(&adc.volumePluginMgr)
   158  	adc.attacherDetacher =
   159  		operationexecutor.NewOperationExecutor(operationexecutor.NewOperationGenerator(
   160  			kubeClient,
   161  			&adc.volumePluginMgr,
   162  			recorder,
   163  			blkutil))
   164  	adc.nodeStatusUpdater = statusupdater.NewNodeStatusUpdater(
   165  		kubeClient, nodeInformer.Lister(), adc.actualStateOfWorld)
   166  
   167  	// Default these to values in options
   168  	adc.reconciler = reconciler.NewReconciler(
   169  		timerConfig.ReconcilerLoopPeriod,
   170  		timerConfig.ReconcilerMaxWaitForUnmountDuration,
   171  		reconcilerSyncDuration,
   172  		disableReconciliationSync,
   173  		adc.desiredStateOfWorld,
   174  		adc.actualStateOfWorld,
   175  		adc.attacherDetacher,
   176  		adc.nodeStatusUpdater,
   177  		adc.nodeLister,
   178  		recorder)
   179  
   180  	csiTranslator := csitrans.New()
   181  	adc.intreeToCSITranslator = csiTranslator
   182  	adc.csiMigratedPluginManager = csimigration.NewPluginManager(csiTranslator, utilfeature.DefaultFeatureGate)
   183  
   184  	adc.desiredStateOfWorldPopulator = populator.NewDesiredStateOfWorldPopulator(
   185  		timerConfig.DesiredStateOfWorldPopulatorLoopSleepPeriod,
   186  		timerConfig.DesiredStateOfWorldPopulatorListPodsRetryDuration,
   187  		podInformer.Lister(),
   188  		adc.desiredStateOfWorld,
   189  		&adc.volumePluginMgr,
   190  		pvcInformer.Lister(),
   191  		pvInformer.Lister(),
   192  		adc.csiMigratedPluginManager,
   193  		adc.intreeToCSITranslator)
   194  
   195  	podInformer.Informer().AddEventHandler(kcache.ResourceEventHandlerFuncs{
   196  		AddFunc: func(obj interface{}) {
   197  			adc.podAdd(logger, obj)
   198  		},
   199  		UpdateFunc: func(oldObj, newObj interface{}) {
   200  			adc.podUpdate(logger, oldObj, newObj)
   201  		},
   202  		DeleteFunc: func(obj interface{}) {
   203  			adc.podDelete(logger, obj)
   204  		},
   205  	})
   206  
   207  	// This custom indexer will index pods by its PVC keys. Then we don't need
   208  	// to iterate all pods every time to find pods which reference given PVC.
   209  	if err := common.AddPodPVCIndexerIfNotPresent(adc.podIndexer); err != nil {
   210  		return nil, fmt.Errorf("could not initialize attach detach controller: %w", err)
   211  	}
   212  
   213  	nodeInformer.Informer().AddEventHandler(kcache.ResourceEventHandlerFuncs{
   214  		AddFunc: func(obj interface{}) {
   215  			adc.nodeAdd(logger, obj)
   216  		},
   217  		UpdateFunc: func(oldObj, newObj interface{}) {
   218  			adc.nodeUpdate(logger, oldObj, newObj)
   219  		},
   220  		DeleteFunc: func(obj interface{}) {
   221  			adc.nodeDelete(logger, obj)
   222  		},
   223  	})
   224  
   225  	pvcInformer.Informer().AddEventHandler(kcache.ResourceEventHandlerFuncs{
   226  		AddFunc: func(obj interface{}) {
   227  			adc.enqueuePVC(obj)
   228  		},
   229  		UpdateFunc: func(old, new interface{}) {
   230  			adc.enqueuePVC(new)
   231  		},
   232  	})
   233  
   234  	return adc, nil
   235  }
   236  
   237  type attachDetachController struct {
   238  	// kubeClient is the kube API client used by volumehost to communicate with
   239  	// the API server.
   240  	kubeClient clientset.Interface
   241  
   242  	// pvcLister is the shared PVC lister used to fetch and store PVC
   243  	// objects from the API server. It is shared with other controllers and
   244  	// therefore the PVC objects in its store should be treated as immutable.
   245  	pvcLister  corelisters.PersistentVolumeClaimLister
   246  	pvcsSynced kcache.InformerSynced
   247  
   248  	// pvLister is the shared PV lister used to fetch and store PV objects
   249  	// from the API server. It is shared with other controllers and therefore
   250  	// the PV objects in its store should be treated as immutable.
   251  	pvLister  corelisters.PersistentVolumeLister
   252  	pvsSynced kcache.InformerSynced
   253  
   254  	podLister  corelisters.PodLister
   255  	podsSynced kcache.InformerSynced
   256  	podIndexer kcache.Indexer
   257  
   258  	nodeLister  corelisters.NodeLister
   259  	nodesSynced kcache.InformerSynced
   260  
   261  	csiNodeLister storagelistersv1.CSINodeLister
   262  	csiNodeSynced kcache.InformerSynced
   263  
   264  	// csiDriverLister is the shared CSIDriver lister used to fetch and store
   265  	// CSIDriver objects from the API server. It is shared with other controllers
   266  	// and therefore the CSIDriver objects in its store should be treated as immutable.
   267  	csiDriverLister  storagelistersv1.CSIDriverLister
   268  	csiDriversSynced kcache.InformerSynced
   269  
   270  	// volumeAttachmentLister is the shared volumeAttachment lister used to fetch and store
   271  	// VolumeAttachment objects from the API server. It is shared with other controllers
   272  	// and therefore the VolumeAttachment objects in its store should be treated as immutable.
   273  	volumeAttachmentLister storagelistersv1.VolumeAttachmentLister
   274  	volumeAttachmentSynced kcache.InformerSynced
   275  
   276  	// cloud provider used by volume host
   277  	cloud cloudprovider.Interface
   278  
   279  	// volumePluginMgr used to initialize and fetch volume plugins
   280  	volumePluginMgr volume.VolumePluginMgr
   281  
   282  	// desiredStateOfWorld is a data structure containing the desired state of
   283  	// the world according to this controller: i.e. what nodes the controller
   284  	// is managing, what volumes it wants be attached to these nodes, and which
   285  	// pods are scheduled to those nodes referencing the volumes.
   286  	// The data structure is populated by the controller using a stream of node
   287  	// and pod API server objects fetched by the informers.
   288  	desiredStateOfWorld cache.DesiredStateOfWorld
   289  
   290  	// actualStateOfWorld is a data structure containing the actual state of
   291  	// the world according to this controller: i.e. which volumes are attached
   292  	// to which nodes.
   293  	// The data structure is populated upon successful completion of attach and
   294  	// detach actions triggered by the controller and a periodic sync with
   295  	// storage providers for the "true" state of the world.
   296  	actualStateOfWorld cache.ActualStateOfWorld
   297  
   298  	// attacherDetacher is used to start asynchronous attach and operations
   299  	attacherDetacher operationexecutor.OperationExecutor
   300  
   301  	// reconciler is used to run an asynchronous periodic loop to reconcile the
   302  	// desiredStateOfWorld with the actualStateOfWorld by triggering attach
   303  	// detach operations using the attacherDetacher.
   304  	reconciler reconciler.Reconciler
   305  
   306  	// nodeStatusUpdater is used to update node status with the list of attached
   307  	// volumes
   308  	nodeStatusUpdater statusupdater.NodeStatusUpdater
   309  
   310  	// desiredStateOfWorldPopulator runs an asynchronous periodic loop to
   311  	// populate the current pods using podInformer.
   312  	desiredStateOfWorldPopulator populator.DesiredStateOfWorldPopulator
   313  
   314  	// broadcaster is broadcasting events
   315  	broadcaster record.EventBroadcaster
   316  
   317  	// pvcQueue is used to queue pvc objects
   318  	pvcQueue workqueue.RateLimitingInterface
   319  
   320  	// csiMigratedPluginManager detects in-tree plugins that have been migrated to CSI
   321  	csiMigratedPluginManager csimigration.PluginManager
   322  
   323  	// intreeToCSITranslator translates from in-tree volume specs to CSI
   324  	intreeToCSITranslator csimigration.InTreeToCSITranslator
   325  }
   326  
   327  func (adc *attachDetachController) Run(ctx context.Context) {
   328  	defer runtime.HandleCrash()
   329  	defer adc.pvcQueue.ShutDown()
   330  
   331  	// Start events processing pipeline.
   332  	adc.broadcaster.StartStructuredLogging(0)
   333  	adc.broadcaster.StartRecordingToSink(&v1core.EventSinkImpl{Interface: adc.kubeClient.CoreV1().Events("")})
   334  	defer adc.broadcaster.Shutdown()
   335  
   336  	logger := klog.FromContext(ctx)
   337  	logger.Info("Starting attach detach controller")
   338  	defer logger.Info("Shutting down attach detach controller")
   339  
   340  	synced := []kcache.InformerSynced{adc.podsSynced, adc.nodesSynced, adc.pvcsSynced, adc.pvsSynced}
   341  	if adc.csiNodeSynced != nil {
   342  		synced = append(synced, adc.csiNodeSynced)
   343  	}
   344  	if adc.csiDriversSynced != nil {
   345  		synced = append(synced, adc.csiDriversSynced)
   346  	}
   347  	if adc.volumeAttachmentSynced != nil {
   348  		synced = append(synced, adc.volumeAttachmentSynced)
   349  	}
   350  
   351  	if !kcache.WaitForNamedCacheSync("attach detach", ctx.Done(), synced...) {
   352  		return
   353  	}
   354  
   355  	err := adc.populateActualStateOfWorld(logger)
   356  	if err != nil {
   357  		logger.Error(err, "Error populating the actual state of world")
   358  	}
   359  	err = adc.populateDesiredStateOfWorld(logger)
   360  	if err != nil {
   361  		logger.Error(err, "Error populating the desired state of world")
   362  	}
   363  	go adc.reconciler.Run(ctx)
   364  	go adc.desiredStateOfWorldPopulator.Run(ctx)
   365  	go wait.UntilWithContext(ctx, adc.pvcWorker, time.Second)
   366  	metrics.Register(adc.pvcLister,
   367  		adc.pvLister,
   368  		adc.podLister,
   369  		adc.actualStateOfWorld,
   370  		adc.desiredStateOfWorld,
   371  		&adc.volumePluginMgr,
   372  		adc.csiMigratedPluginManager,
   373  		adc.intreeToCSITranslator)
   374  
   375  	<-ctx.Done()
   376  }
   377  
   378  func (adc *attachDetachController) populateActualStateOfWorld(logger klog.Logger) error {
   379  	logger.V(5).Info("Populating ActualStateOfworld")
   380  	nodes, err := adc.nodeLister.List(labels.Everything())
   381  	if err != nil {
   382  		return err
   383  	}
   384  
   385  	for _, node := range nodes {
   386  		nodeName := types.NodeName(node.Name)
   387  		for _, attachedVolume := range node.Status.VolumesAttached {
   388  			uniqueName := attachedVolume.Name
   389  			// The nil VolumeSpec is safe only in the case the volume is not in use by any pod.
   390  			// In such a case it should be detached in the first reconciliation cycle and the
   391  			// volume spec is not needed to detach a volume. If the volume is used by a pod, it
   392  			// its spec can be: this would happen during in the populateDesiredStateOfWorld which
   393  			// scans the pods and updates their volumes in the ActualStateOfWorld too.
   394  			err = adc.actualStateOfWorld.MarkVolumeAsAttached(logger, uniqueName, nil /* VolumeSpec */, nodeName, attachedVolume.DevicePath)
   395  			if err != nil {
   396  				logger.Error(err, "Failed to mark the volume as attached")
   397  				continue
   398  			}
   399  			adc.processVolumesInUse(logger, nodeName, node.Status.VolumesInUse)
   400  			adc.addNodeToDswp(node, types.NodeName(node.Name))
   401  		}
   402  	}
   403  	err = adc.processVolumeAttachments(logger)
   404  	if err != nil {
   405  		logger.Error(err, "Failed to process volume attachments")
   406  	}
   407  	return err
   408  }
   409  
   410  func (adc *attachDetachController) getNodeVolumeDevicePath(
   411  	volumeName v1.UniqueVolumeName, nodeName types.NodeName) (string, error) {
   412  	var devicePath string
   413  	var found bool
   414  	node, err := adc.nodeLister.Get(string(nodeName))
   415  	if err != nil {
   416  		return devicePath, err
   417  	}
   418  	for _, attachedVolume := range node.Status.VolumesAttached {
   419  		if volumeName == attachedVolume.Name {
   420  			devicePath = attachedVolume.DevicePath
   421  			found = true
   422  			break
   423  		}
   424  	}
   425  	if !found {
   426  		err = fmt.Errorf("Volume %s not found on node %s", volumeName, nodeName)
   427  	}
   428  
   429  	return devicePath, err
   430  }
   431  
   432  func (adc *attachDetachController) populateDesiredStateOfWorld(logger klog.Logger) error {
   433  	logger.V(5).Info("Populating DesiredStateOfworld")
   434  
   435  	pods, err := adc.podLister.List(labels.Everything())
   436  	if err != nil {
   437  		return err
   438  	}
   439  	for _, pod := range pods {
   440  		podToAdd := pod
   441  		adc.podAdd(logger, podToAdd)
   442  		for _, podVolume := range podToAdd.Spec.Volumes {
   443  			nodeName := types.NodeName(podToAdd.Spec.NodeName)
   444  			// The volume specs present in the ActualStateOfWorld are nil, let's replace those
   445  			// with the correct ones found on pods. The present in the ASW with no corresponding
   446  			// pod will be detached and the spec is irrelevant.
   447  			volumeSpec, err := util.CreateVolumeSpec(logger, podVolume, podToAdd, nodeName, &adc.volumePluginMgr, adc.pvcLister, adc.pvLister, adc.csiMigratedPluginManager, adc.intreeToCSITranslator)
   448  			if err != nil {
   449  				logger.Error(
   450  					err,
   451  					"Error creating spec for volume of pod",
   452  					"pod", klog.KObj(podToAdd),
   453  					"volumeName", podVolume.Name)
   454  				continue
   455  			}
   456  			plugin, err := adc.volumePluginMgr.FindAttachablePluginBySpec(volumeSpec)
   457  			if err != nil || plugin == nil {
   458  				logger.V(10).Info(
   459  					"Skipping volume for pod: it does not implement attacher interface",
   460  					"pod", klog.KObj(podToAdd),
   461  					"volumeName", podVolume.Name,
   462  					"err", err)
   463  				continue
   464  			}
   465  			volumeName, err := volumeutil.GetUniqueVolumeNameFromSpec(plugin, volumeSpec)
   466  			if err != nil {
   467  				logger.Error(
   468  					err,
   469  					"Failed to find unique name for volume of pod",
   470  					"pod", klog.KObj(podToAdd),
   471  					"volumeName", podVolume.Name)
   472  				continue
   473  			}
   474  			attachState := adc.actualStateOfWorld.GetAttachState(volumeName, nodeName)
   475  			if attachState == cache.AttachStateAttached {
   476  				logger.V(10).Info("Volume is attached to node. Marking as attached in ActualStateOfWorld",
   477  					"node", klog.KRef("", string(nodeName)),
   478  					"volumeName", volumeName)
   479  				devicePath, err := adc.getNodeVolumeDevicePath(volumeName, nodeName)
   480  				if err != nil {
   481  					logger.Error(err, "Failed to find device path")
   482  					continue
   483  				}
   484  				err = adc.actualStateOfWorld.MarkVolumeAsAttached(logger, volumeName, volumeSpec, nodeName, devicePath)
   485  				if err != nil {
   486  					logger.Error(err, "Failed to update volume spec for node", "node", klog.KRef("", string(nodeName)))
   487  				}
   488  			}
   489  		}
   490  	}
   491  
   492  	return nil
   493  }
   494  
   495  func (adc *attachDetachController) podAdd(logger klog.Logger, obj interface{}) {
   496  	pod, ok := obj.(*v1.Pod)
   497  	if pod == nil || !ok {
   498  		return
   499  	}
   500  	if pod.Spec.NodeName == "" {
   501  		// Ignore pods without NodeName, indicating they are not scheduled.
   502  		return
   503  	}
   504  
   505  	volumeActionFlag := util.DetermineVolumeAction(
   506  		pod,
   507  		adc.desiredStateOfWorld,
   508  		true /* default volume action */)
   509  
   510  	util.ProcessPodVolumes(logger, pod, volumeActionFlag, /* addVolumes */
   511  		adc.desiredStateOfWorld, &adc.volumePluginMgr, adc.pvcLister, adc.pvLister, adc.csiMigratedPluginManager, adc.intreeToCSITranslator)
   512  }
   513  
   514  // GetDesiredStateOfWorld returns desired state of world associated with controller
   515  func (adc *attachDetachController) GetDesiredStateOfWorld() cache.DesiredStateOfWorld {
   516  	return adc.desiredStateOfWorld
   517  }
   518  
   519  func (adc *attachDetachController) podUpdate(logger klog.Logger, oldObj, newObj interface{}) {
   520  	pod, ok := newObj.(*v1.Pod)
   521  	if pod == nil || !ok {
   522  		return
   523  	}
   524  	if pod.Spec.NodeName == "" {
   525  		// Ignore pods without NodeName, indicating they are not scheduled.
   526  		return
   527  	}
   528  
   529  	volumeActionFlag := util.DetermineVolumeAction(
   530  		pod,
   531  		adc.desiredStateOfWorld,
   532  		true /* default volume action */)
   533  
   534  	util.ProcessPodVolumes(logger, pod, volumeActionFlag, /* addVolumes */
   535  		adc.desiredStateOfWorld, &adc.volumePluginMgr, adc.pvcLister, adc.pvLister, adc.csiMigratedPluginManager, adc.intreeToCSITranslator)
   536  }
   537  
   538  func (adc *attachDetachController) podDelete(logger klog.Logger, obj interface{}) {
   539  	pod, ok := obj.(*v1.Pod)
   540  	if pod == nil || !ok {
   541  		return
   542  	}
   543  
   544  	util.ProcessPodVolumes(logger, pod, false, /* addVolumes */
   545  		adc.desiredStateOfWorld, &adc.volumePluginMgr, adc.pvcLister, adc.pvLister, adc.csiMigratedPluginManager, adc.intreeToCSITranslator)
   546  }
   547  
   548  func (adc *attachDetachController) nodeAdd(logger klog.Logger, obj interface{}) {
   549  	node, ok := obj.(*v1.Node)
   550  	// TODO: investigate if nodeName is empty then if we can return
   551  	// kubernetes/kubernetes/issues/37777
   552  	if node == nil || !ok {
   553  		return
   554  	}
   555  	nodeName := types.NodeName(node.Name)
   556  	adc.nodeUpdate(logger, nil, obj)
   557  	// kubernetes/kubernetes/issues/37586
   558  	// This is to workaround the case when a node add causes to wipe out
   559  	// the attached volumes field. This function ensures that we sync with
   560  	// the actual status.
   561  	adc.actualStateOfWorld.SetNodeStatusUpdateNeeded(logger, nodeName)
   562  }
   563  
   564  func (adc *attachDetachController) nodeUpdate(logger klog.Logger, oldObj, newObj interface{}) {
   565  	node, ok := newObj.(*v1.Node)
   566  	// TODO: investigate if nodeName is empty then if we can return
   567  	if node == nil || !ok {
   568  		return
   569  	}
   570  
   571  	nodeName := types.NodeName(node.Name)
   572  	adc.addNodeToDswp(node, nodeName)
   573  	adc.processVolumesInUse(logger, nodeName, node.Status.VolumesInUse)
   574  }
   575  
   576  func (adc *attachDetachController) nodeDelete(logger klog.Logger, obj interface{}) {
   577  	node, ok := obj.(*v1.Node)
   578  	if node == nil || !ok {
   579  		return
   580  	}
   581  
   582  	nodeName := types.NodeName(node.Name)
   583  	if err := adc.desiredStateOfWorld.DeleteNode(nodeName); err != nil {
   584  		// This might happen during drain, but we still want it to appear in our logs
   585  		logger.Info("Error removing node from desired-state-of-world", "node", klog.KObj(node), "err", err)
   586  	}
   587  
   588  	adc.processVolumesInUse(logger, nodeName, node.Status.VolumesInUse)
   589  }
   590  
   591  func (adc *attachDetachController) enqueuePVC(obj interface{}) {
   592  	key, err := kcache.DeletionHandlingMetaNamespaceKeyFunc(obj)
   593  	if err != nil {
   594  		runtime.HandleError(fmt.Errorf("Couldn't get key for object %+v: %v", obj, err))
   595  		return
   596  	}
   597  	adc.pvcQueue.Add(key)
   598  }
   599  
   600  // pvcWorker processes items from pvcQueue
   601  func (adc *attachDetachController) pvcWorker(ctx context.Context) {
   602  	for adc.processNextItem(klog.FromContext(ctx)) {
   603  	}
   604  }
   605  
   606  func (adc *attachDetachController) processNextItem(logger klog.Logger) bool {
   607  	keyObj, shutdown := adc.pvcQueue.Get()
   608  	if shutdown {
   609  		return false
   610  	}
   611  	defer adc.pvcQueue.Done(keyObj)
   612  
   613  	if err := adc.syncPVCByKey(logger, keyObj.(string)); err != nil {
   614  		// Rather than wait for a full resync, re-add the key to the
   615  		// queue to be processed.
   616  		adc.pvcQueue.AddRateLimited(keyObj)
   617  		runtime.HandleError(fmt.Errorf("Failed to sync pvc %q, will retry again: %v", keyObj.(string), err))
   618  		return true
   619  	}
   620  
   621  	// Finally, if no error occurs we Forget this item so it does not
   622  	// get queued again until another change happens.
   623  	adc.pvcQueue.Forget(keyObj)
   624  	return true
   625  }
   626  
   627  func (adc *attachDetachController) syncPVCByKey(logger klog.Logger, key string) error {
   628  	logger.V(5).Info("syncPVCByKey", "pvcKey", key)
   629  	namespace, name, err := kcache.SplitMetaNamespaceKey(key)
   630  	if err != nil {
   631  		logger.V(4).Info("Error getting namespace & name of pvc to get pvc from informer", "pvcKey", key, "err", err)
   632  		return nil
   633  	}
   634  	pvc, err := adc.pvcLister.PersistentVolumeClaims(namespace).Get(name)
   635  	if apierrors.IsNotFound(err) {
   636  		logger.V(4).Info("Error getting pvc from informer", "pvcKey", key, "err", err)
   637  		return nil
   638  	}
   639  	if err != nil {
   640  		return err
   641  	}
   642  
   643  	if pvc.Status.Phase != v1.ClaimBound || pvc.Spec.VolumeName == "" {
   644  		// Skip unbound PVCs.
   645  		return nil
   646  	}
   647  
   648  	objs, err := adc.podIndexer.ByIndex(common.PodPVCIndex, key)
   649  	if err != nil {
   650  		return err
   651  	}
   652  	for _, obj := range objs {
   653  		pod, ok := obj.(*v1.Pod)
   654  		if !ok {
   655  			continue
   656  		}
   657  		// we are only interested in active pods with nodeName set
   658  		if len(pod.Spec.NodeName) == 0 || volumeutil.IsPodTerminated(pod, pod.Status) {
   659  			continue
   660  		}
   661  		volumeActionFlag := util.DetermineVolumeAction(
   662  			pod,
   663  			adc.desiredStateOfWorld,
   664  			true /* default volume action */)
   665  
   666  		util.ProcessPodVolumes(logger, pod, volumeActionFlag, /* addVolumes */
   667  			adc.desiredStateOfWorld, &adc.volumePluginMgr, adc.pvcLister, adc.pvLister, adc.csiMigratedPluginManager, adc.intreeToCSITranslator)
   668  	}
   669  	return nil
   670  }
   671  
   672  // processVolumesInUse processes the list of volumes marked as "in-use"
   673  // according to the specified Node's Status.VolumesInUse and updates the
   674  // corresponding volume in the actual state of the world to indicate that it is
   675  // mounted.
   676  func (adc *attachDetachController) processVolumesInUse(
   677  	logger klog.Logger, nodeName types.NodeName, volumesInUse []v1.UniqueVolumeName) {
   678  	logger.V(4).Info("processVolumesInUse for node", "node", klog.KRef("", string(nodeName)))
   679  	for _, attachedVolume := range adc.actualStateOfWorld.GetAttachedVolumesForNode(nodeName) {
   680  		mounted := false
   681  		for _, volumeInUse := range volumesInUse {
   682  			if attachedVolume.VolumeName == volumeInUse {
   683  				mounted = true
   684  				break
   685  			}
   686  		}
   687  		err := adc.actualStateOfWorld.SetVolumeMountedByNode(logger, attachedVolume.VolumeName, nodeName, mounted)
   688  		if err != nil {
   689  			logger.Info(
   690  				"SetVolumeMountedByNode returned an error",
   691  				"node", klog.KRef("", string(nodeName)),
   692  				"volumeName", attachedVolume.VolumeName,
   693  				"mounted", mounted,
   694  				"err", err)
   695  		}
   696  	}
   697  }
   698  
   699  // Process Volume-Attachment objects.
   700  // Should be called only after populating attached volumes in the ASW.
   701  // For each VA object, this function checks if its present in the ASW.
   702  // If not, adds the volume to ASW as an "uncertain" attachment.
   703  // In the reconciler, the logic checks if the volume is present in the DSW;
   704  //
   705  //	if yes, the reconciler will attempt attach on the volume;
   706  //	if not (could be a dangling attachment), the reconciler will detach this volume.
   707  func (adc *attachDetachController) processVolumeAttachments(logger klog.Logger) error {
   708  	vas, err := adc.volumeAttachmentLister.List(labels.Everything())
   709  	if err != nil {
   710  		logger.Error(err, "Failed to list VolumeAttachment objects")
   711  		return err
   712  	}
   713  	for _, va := range vas {
   714  		nodeName := types.NodeName(va.Spec.NodeName)
   715  		pvName := va.Spec.Source.PersistentVolumeName
   716  		if pvName == nil {
   717  			// Currently VA objects are created for CSI volumes only. nil pvName is unexpected, generate a warning
   718  			logger.Info("Skipping the va as its pvName is nil", "node", klog.KRef("", string(nodeName)), "vaName", va.Name)
   719  			continue
   720  		}
   721  		pv, err := adc.pvLister.Get(*pvName)
   722  		if err != nil {
   723  			logger.Error(err, "Unable to lookup pv object", "PV", klog.KRef("", *pvName))
   724  			continue
   725  		}
   726  
   727  		var plugin volume.AttachableVolumePlugin
   728  		volumeSpec := volume.NewSpecFromPersistentVolume(pv, false)
   729  
   730  		// Consult csiMigratedPluginManager first before querying the plugins registered during runtime in volumePluginMgr.
   731  		// In-tree plugins that provisioned PVs will not be registered anymore after migration to CSI, once the respective
   732  		// feature gate is enabled.
   733  		if inTreePluginName, err := adc.csiMigratedPluginManager.GetInTreePluginNameFromSpec(pv, nil); err == nil {
   734  			if adc.csiMigratedPluginManager.IsMigrationEnabledForPlugin(inTreePluginName) {
   735  				// PV is migrated and should be handled by the CSI plugin instead of the in-tree one
   736  				plugin, _ = adc.volumePluginMgr.FindAttachablePluginByName(csi.CSIPluginName)
   737  				// podNamespace is not needed here for Azurefile as the volumeName generated will be the same with or without podNamespace
   738  				volumeSpec, err = csimigration.TranslateInTreeSpecToCSI(volumeSpec, "" /* podNamespace */, adc.intreeToCSITranslator)
   739  				if err != nil {
   740  					logger.Error(err, "Failed to translate intree volumeSpec to CSI volumeSpec for volume", "node", klog.KRef("", string(nodeName)), "inTreePluginName", inTreePluginName, "vaName", va.Name, "PV", klog.KRef("", *pvName))
   741  					continue
   742  				}
   743  			}
   744  		}
   745  
   746  		if plugin == nil {
   747  			plugin, err = adc.volumePluginMgr.FindAttachablePluginBySpec(volumeSpec)
   748  			if err != nil || plugin == nil {
   749  				// Currently VA objects are created for CSI volumes only. nil plugin is unexpected, generate a warning
   750  				logger.Info("Skipping processing the volume on node, no attacher interface found", "node", klog.KRef("", string(nodeName)), "PV", klog.KRef("", *pvName), "err", err)
   751  				continue
   752  			}
   753  		}
   754  
   755  		volumeName, err := volumeutil.GetUniqueVolumeNameFromSpec(plugin, volumeSpec)
   756  		if err != nil {
   757  			logger.Error(err, "Failed to find unique name for volume", "node", klog.KRef("", string(nodeName)), "vaName", va.Name, "PV", klog.KRef("", *pvName))
   758  			continue
   759  		}
   760  		attachState := adc.actualStateOfWorld.GetAttachState(volumeName, nodeName)
   761  		if attachState == cache.AttachStateDetached {
   762  			logger.V(1).Info("Marking volume attachment as uncertain as volume is not attached", "node", klog.KRef("", string(nodeName)), "volumeName", volumeName, "attachState", attachState)
   763  			err = adc.actualStateOfWorld.MarkVolumeAsUncertain(logger, volumeName, volumeSpec, nodeName)
   764  			if err != nil {
   765  				logger.Error(err, "MarkVolumeAsUncertain fail to add the volume to ASW", "node", klog.KRef("", string(nodeName)), "volumeName", volumeName)
   766  			}
   767  		}
   768  	}
   769  	return nil
   770  }
   771  
   772  var _ volume.VolumeHost = &attachDetachController{}
   773  var _ volume.AttachDetachVolumeHost = &attachDetachController{}
   774  
   775  func (adc *attachDetachController) CSINodeLister() storagelistersv1.CSINodeLister {
   776  	return adc.csiNodeLister
   777  }
   778  
   779  func (adc *attachDetachController) CSIDriverLister() storagelistersv1.CSIDriverLister {
   780  	return adc.csiDriverLister
   781  }
   782  
   783  func (adc *attachDetachController) IsAttachDetachController() bool {
   784  	return true
   785  }
   786  
   787  func (adc *attachDetachController) VolumeAttachmentLister() storagelistersv1.VolumeAttachmentLister {
   788  	return adc.volumeAttachmentLister
   789  }
   790  
   791  // VolumeHost implementation
   792  // This is an unfortunate requirement of the current factoring of volume plugin
   793  // initializing code. It requires kubelet specific methods used by the mounting
   794  // code to be implemented by all initializers even if the initializer does not
   795  // do mounting (like this attach/detach controller).
   796  // Issue kubernetes/kubernetes/issues/14217 to fix this.
   797  func (adc *attachDetachController) GetPluginDir(podUID string) string {
   798  	return ""
   799  }
   800  
   801  func (adc *attachDetachController) GetVolumeDevicePluginDir(podUID string) string {
   802  	return ""
   803  }
   804  
   805  func (adc *attachDetachController) GetPodsDir() string {
   806  	return ""
   807  }
   808  
   809  func (adc *attachDetachController) GetPodVolumeDir(podUID types.UID, pluginName, volumeName string) string {
   810  	return ""
   811  }
   812  
   813  func (adc *attachDetachController) GetPodPluginDir(podUID types.UID, pluginName string) string {
   814  	return ""
   815  }
   816  
   817  func (adc *attachDetachController) GetPodVolumeDeviceDir(podUID types.UID, pluginName string) string {
   818  	return ""
   819  }
   820  
   821  func (adc *attachDetachController) GetKubeClient() clientset.Interface {
   822  	return adc.kubeClient
   823  }
   824  
   825  func (adc *attachDetachController) NewWrapperMounter(volName string, spec volume.Spec, pod *v1.Pod, opts volume.VolumeOptions) (volume.Mounter, error) {
   826  	return nil, fmt.Errorf("NewWrapperMounter not supported by Attach/Detach controller's VolumeHost implementation")
   827  }
   828  
   829  func (adc *attachDetachController) NewWrapperUnmounter(volName string, spec volume.Spec, podUID types.UID) (volume.Unmounter, error) {
   830  	return nil, fmt.Errorf("NewWrapperUnmounter not supported by Attach/Detach controller's VolumeHost implementation")
   831  }
   832  
   833  func (adc *attachDetachController) GetCloudProvider() cloudprovider.Interface {
   834  	return adc.cloud
   835  }
   836  
   837  func (adc *attachDetachController) GetMounter(pluginName string) mount.Interface {
   838  	return nil
   839  }
   840  
   841  func (adc *attachDetachController) GetHostName() string {
   842  	return ""
   843  }
   844  
   845  func (adc *attachDetachController) GetHostIP() (net.IP, error) {
   846  	return nil, fmt.Errorf("GetHostIP() not supported by Attach/Detach controller's VolumeHost implementation")
   847  }
   848  
   849  func (adc *attachDetachController) GetNodeAllocatable() (v1.ResourceList, error) {
   850  	return v1.ResourceList{}, nil
   851  }
   852  
   853  func (adc *attachDetachController) GetAttachedVolumesFromNodeStatus() (map[v1.UniqueVolumeName]string, error) {
   854  	return map[v1.UniqueVolumeName]string{}, nil
   855  }
   856  
   857  func (adc *attachDetachController) GetSecretFunc() func(namespace, name string) (*v1.Secret, error) {
   858  	return func(_, _ string) (*v1.Secret, error) {
   859  		return nil, fmt.Errorf("GetSecret unsupported in attachDetachController")
   860  	}
   861  }
   862  
   863  func (adc *attachDetachController) GetConfigMapFunc() func(namespace, name string) (*v1.ConfigMap, error) {
   864  	return func(_, _ string) (*v1.ConfigMap, error) {
   865  		return nil, fmt.Errorf("GetConfigMap unsupported in attachDetachController")
   866  	}
   867  }
   868  
   869  func (adc *attachDetachController) GetServiceAccountTokenFunc() func(_, _ string, _ *authenticationv1.TokenRequest) (*authenticationv1.TokenRequest, error) {
   870  	return func(_, _ string, _ *authenticationv1.TokenRequest) (*authenticationv1.TokenRequest, error) {
   871  		return nil, fmt.Errorf("GetServiceAccountToken unsupported in attachDetachController")
   872  	}
   873  }
   874  
   875  func (adc *attachDetachController) DeleteServiceAccountTokenFunc() func(types.UID) {
   876  	return func(types.UID) {
   877  		// nolint:logcheck
   878  		klog.ErrorS(nil, "DeleteServiceAccountToken unsupported in attachDetachController")
   879  	}
   880  }
   881  
   882  func (adc *attachDetachController) GetExec(pluginName string) utilexec.Interface {
   883  	return utilexec.New()
   884  }
   885  
   886  func (adc *attachDetachController) addNodeToDswp(node *v1.Node, nodeName types.NodeName) {
   887  	if _, exists := node.Annotations[volumeutil.ControllerManagedAttachAnnotation]; exists {
   888  		keepTerminatedPodVolumes := false
   889  
   890  		if t, ok := node.Annotations[volumeutil.KeepTerminatedPodVolumesAnnotation]; ok {
   891  			keepTerminatedPodVolumes = t == "true"
   892  		}
   893  
   894  		// Node specifies annotation indicating it should be managed by attach
   895  		// detach controller. Add it to desired state of world.
   896  		adc.desiredStateOfWorld.AddNode(nodeName, keepTerminatedPodVolumes)
   897  	}
   898  }
   899  
   900  func (adc *attachDetachController) GetNodeLabels() (map[string]string, error) {
   901  	return nil, fmt.Errorf("GetNodeLabels() unsupported in Attach/Detach controller")
   902  }
   903  
   904  func (adc *attachDetachController) GetNodeName() types.NodeName {
   905  	return ""
   906  }
   907  
   908  func (adc *attachDetachController) GetEventRecorder() record.EventRecorder {
   909  	return nil
   910  }
   911  
   912  func (adc *attachDetachController) GetSubpather() subpath.Interface {
   913  	// Subpaths not needed in attachdetach controller
   914  	return nil
   915  }
   916  
   917  func (adc *attachDetachController) GetCSIDriverLister() storagelistersv1.CSIDriverLister {
   918  	return adc.csiDriverLister
   919  }