k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/pkg/controller/volume/attachdetach/cache/actual_state_of_world.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  /*
    18  Package cache implements data structures used by the attach/detach controller
    19  to keep track of volumes, the nodes they are attached to, and the pods that
    20  reference them.
    21  */
    22  package cache
    23  
    24  import (
    25  	"fmt"
    26  	"sync"
    27  	"time"
    28  
    29  	v1 "k8s.io/api/core/v1"
    30  	"k8s.io/apimachinery/pkg/api/resource"
    31  	"k8s.io/apimachinery/pkg/types"
    32  	"k8s.io/apimachinery/pkg/util/sets"
    33  	"k8s.io/klog/v2"
    34  	"k8s.io/kubernetes/pkg/volume"
    35  	"k8s.io/kubernetes/pkg/volume/util"
    36  	"k8s.io/kubernetes/pkg/volume/util/operationexecutor"
    37  )
    38  
    39  // ActualStateOfWorld defines a set of thread-safe operations supported on
    40  // the attach/detach controller's actual state of the world cache.
    41  // This cache contains volumes->nodes i.e. a set of all volumes and the nodes
    42  // the attach/detach controller believes are successfully attached.
    43  // Note: This is distinct from the ActualStateOfWorld implemented by the kubelet
    44  // volume manager. They both keep track of different objects. This contains
    45  // attach/detach controller specific state.
    46  type ActualStateOfWorld interface {
    47  	// ActualStateOfWorld must implement the methods required to allow
    48  	// operationexecutor to interact with it.
    49  	operationexecutor.ActualStateOfWorldAttacherUpdater
    50  
    51  	// AddVolumeNode adds the given volume and node to the underlying store.
    52  	// If attached is set to true, it indicates the specified volume is already
    53  	// attached to the specified node. If attached set to false, it means that
    54  	// the volume is not confirmed to be attached to the node yet.
    55  	// A unique volume name is generated from the volumeSpec and returned on
    56  	// success.
    57  	// If volumeSpec is not an attachable volume plugin, an error is returned.
    58  	// If no volume with the name volumeName exists in the store, the volume is
    59  	// added.
    60  	// If no node with the name nodeName exists in list of attached nodes for
    61  	// the specified volume, the node is added.
    62  	AddVolumeNode(logger klog.Logger, uniqueName v1.UniqueVolumeName, volumeSpec *volume.Spec, nodeName types.NodeName, devicePath string, attached bool) (v1.UniqueVolumeName, error)
    63  
    64  	// SetVolumesMountedByNode sets all the volumes mounted by the given node.
    65  	// These volumes should include attached volumes, not-yet-attached volumes,
    66  	// and may also include non-attachable volumes.
    67  	// When present in the volumeNames parameter, the volume
    68  	// is mounted by the given node, indicating it may not be safe to detach.
    69  	// Otherwise, the volume is not mounted by the given node.
    70  	SetVolumesMountedByNode(logger klog.Logger, volumeNames []v1.UniqueVolumeName, nodeName types.NodeName)
    71  
    72  	// SetNodeStatusUpdateNeeded sets statusUpdateNeeded for the specified
    73  	// node to true indicating the AttachedVolume field in the Node's Status
    74  	// object needs to be updated by the node updater again.
    75  	// If the specified node does not exist in the nodesToUpdateStatusFor list,
    76  	// log the error and return
    77  	SetNodeStatusUpdateNeeded(logger klog.Logger, nodeName types.NodeName)
    78  
    79  	// ResetDetachRequestTime resets the detachRequestTime to 0 which indicates there is no detach
    80  	// request any more for the volume
    81  	ResetDetachRequestTime(logger klog.Logger, volumeName v1.UniqueVolumeName, nodeName types.NodeName)
    82  
    83  	// SetDetachRequestTime sets the detachRequestedTime to current time if this is no
    84  	// previous request (the previous detachRequestedTime is zero) and return the time elapsed
    85  	// since last request
    86  	SetDetachRequestTime(logger klog.Logger, volumeName v1.UniqueVolumeName, nodeName types.NodeName) (time.Duration, error)
    87  
    88  	// DeleteVolumeNode removes the given volume and node from the underlying
    89  	// store indicating the specified volume is no longer attached to the
    90  	// specified node.
    91  	// If the volume/node combo does not exist, this is a no-op.
    92  	// If after deleting the node, the specified volume contains no other child
    93  	// nodes, the volume is also deleted.
    94  	DeleteVolumeNode(volumeName v1.UniqueVolumeName, nodeName types.NodeName)
    95  
    96  	// GetAttachState returns the attach state for the given volume-node
    97  	// combination.
    98  	// Returns AttachStateAttached if the specified volume/node combo exists in
    99  	// the underlying store indicating the specified volume is attached to the
   100  	// specified node, AttachStateDetached if the combo does not exist, or
   101  	// AttachStateUncertain if the attached state is marked as uncertain.
   102  	GetAttachState(volumeName v1.UniqueVolumeName, nodeName types.NodeName) AttachState
   103  
   104  	// GetAttachedVolumes generates and returns a list of volumes/node pairs
   105  	// reflecting which volumes might attached to which nodes based on the
   106  	// current actual state of the world. This list includes all the volumes which return successful
   107  	// attach and also the volumes which return errors during attach.
   108  	GetAttachedVolumes() []AttachedVolume
   109  
   110  	// GetAttachedVolumesForNode generates and returns a list of volumes that added to
   111  	// the specified node reflecting which volumes are/or might be attached to that node
   112  	// based on the current actual state of the world. This function is currently used by
   113  	// attach_detach_controller to process VolumeInUse
   114  	GetAttachedVolumesForNode(nodeName types.NodeName) []AttachedVolume
   115  
   116  	// GetAttachedVolumesPerNode generates and returns a map of nodes and volumes that added to
   117  	// the specified node reflecting which volumes are attached to that node
   118  	// based on the current actual state of the world. This function is currently used by
   119  	// reconciler to verify whether the volume is still attached to the node.
   120  	GetAttachedVolumesPerNode() map[types.NodeName][]operationexecutor.AttachedVolume
   121  
   122  	// GetNodesForAttachedVolume returns the nodes on which the volume is attached.
   123  	// This function is used by reconciler for multi-attach check.
   124  	GetNodesForAttachedVolume(volumeName v1.UniqueVolumeName) []types.NodeName
   125  
   126  	// GetVolumesToReportAttached returns a map containing the set of nodes for
   127  	// which the VolumesAttached Status field in the Node API object should be
   128  	// updated. The key in this map is the name of the node to update and the
   129  	// value is list of volumes that should be reported as attached (note that
   130  	// this may differ from the actual list of attached volumes for the node
   131  	// since volumes should be removed from this list as soon a detach operation
   132  	// is considered, before the detach operation is triggered).
   133  	GetVolumesToReportAttached(logger klog.Logger) map[types.NodeName][]v1.AttachedVolume
   134  
   135  	// GetVolumesToReportAttachedForNode returns the list of volumes that should be reported as
   136  	// attached for the given node. It reports a boolean indicating if there is an update for that
   137  	// node and the corresponding attachedVolumes list.
   138  	GetVolumesToReportAttachedForNode(logger klog.Logger, name types.NodeName) (bool, []v1.AttachedVolume)
   139  
   140  	// GetNodesToUpdateStatusFor returns the map of nodeNames to nodeToUpdateStatusFor
   141  	GetNodesToUpdateStatusFor() map[types.NodeName]nodeToUpdateStatusFor
   142  }
   143  
   144  // AttachedVolume represents a volume that is attached to a node.
   145  type AttachedVolume struct {
   146  	operationexecutor.AttachedVolume
   147  
   148  	// MountedByNode indicates that this volume has been mounted by the node and
   149  	// is unsafe to detach.
   150  	// The value is set and unset by SetVolumesMountedByNode(...).
   151  	MountedByNode bool
   152  
   153  	// DetachRequestedTime is used to capture the desire to detach this volume.
   154  	// When the volume is newly created this value is set to time zero.
   155  	// It is set to current time, when SetDetachRequestTime(...) is called, if it
   156  	// was previously set to zero (other wise its value remains the same).
   157  	// It is reset to zero on ResetDetachRequestTime(...) calls.
   158  	DetachRequestedTime time.Time
   159  }
   160  
   161  // AttachState represents the attach state of a volume to a node known to the
   162  // Actual State of World.
   163  // This type is used as external representation of attach state (specifically
   164  // as the return type of GetAttachState only); the state is represented
   165  // differently in the internal cache implementation.
   166  type AttachState int
   167  
   168  const (
   169  	// AttachStateAttached represents the state in which the volume is attached to
   170  	// the node.
   171  	AttachStateAttached AttachState = iota
   172  
   173  	// AttachStateUncertain represents the state in which the Actual State of World
   174  	// does not know whether the volume is attached to the node.
   175  	AttachStateUncertain
   176  
   177  	// AttachStateDetached represents the state in which the volume is not
   178  	// attached to the node.
   179  	AttachStateDetached
   180  )
   181  
   182  func (s AttachState) String() string {
   183  	return []string{"Attached", "Uncertain", "Detached"}[s]
   184  }
   185  
   186  // NewActualStateOfWorld returns a new instance of ActualStateOfWorld.
   187  func NewActualStateOfWorld(volumePluginMgr *volume.VolumePluginMgr) ActualStateOfWorld {
   188  	return &actualStateOfWorld{
   189  		attachedVolumes:        make(map[v1.UniqueVolumeName]attachedVolume),
   190  		nodesToUpdateStatusFor: make(map[types.NodeName]nodeToUpdateStatusFor),
   191  		inUseVolumes:           make(map[types.NodeName]sets.Set[v1.UniqueVolumeName]),
   192  		volumePluginMgr:        volumePluginMgr,
   193  	}
   194  }
   195  
   196  type actualStateOfWorld struct {
   197  	// attachedVolumes is a map containing the set of volumes the attach/detach
   198  	// controller believes to be successfully attached to the nodes it is
   199  	// managing. The key in this map is the name of the volume and the value is
   200  	// an object containing more information about the attached volume.
   201  	attachedVolumes map[v1.UniqueVolumeName]attachedVolume
   202  
   203  	// nodesToUpdateStatusFor is a map containing the set of nodes for which to
   204  	// update the VolumesAttached Status field. The key in this map is the name
   205  	// of the node and the value is an object containing more information about
   206  	// the node (including the list of volumes to report attached).
   207  	nodesToUpdateStatusFor map[types.NodeName]nodeToUpdateStatusFor
   208  
   209  	// inUseVolumes is a map containing the set of volumes that are reported as
   210  	// in use by the kubelet.
   211  	inUseVolumes map[types.NodeName]sets.Set[v1.UniqueVolumeName]
   212  
   213  	// volumePluginMgr is the volume plugin manager used to create volume
   214  	// plugin objects.
   215  	volumePluginMgr *volume.VolumePluginMgr
   216  
   217  	sync.RWMutex
   218  }
   219  
   220  // The volume object represents a volume the attach/detach controller
   221  // believes to be successfully attached to a node it is managing.
   222  type attachedVolume struct {
   223  	// volumeName contains the unique identifier for this volume.
   224  	volumeName v1.UniqueVolumeName
   225  
   226  	// spec is the volume spec containing the specification for this volume.
   227  	// Used to generate the volume plugin object, and passed to attach/detach
   228  	// methods.
   229  	spec *volume.Spec
   230  
   231  	// nodesAttachedTo is a map containing the set of nodes this volume has
   232  	// been attached to. The key in this map is the name of the
   233  	// node and the value is a node object containing more information about
   234  	// the node.
   235  	nodesAttachedTo map[types.NodeName]nodeAttachedTo
   236  
   237  	// devicePath contains the path on the node where the volume is attached
   238  	devicePath string
   239  }
   240  
   241  // The nodeAttachedTo object represents a node that has volumes attached to it
   242  // or trying to attach to it.
   243  type nodeAttachedTo struct {
   244  	// nodeName contains the name of this node.
   245  	nodeName types.NodeName
   246  
   247  	// attachConfirmed indicates that the storage system verified the volume has been attached to this node.
   248  	// This value is set to false when an attach  operation fails and the volume may be attached or not.
   249  	attachedConfirmed bool
   250  
   251  	// detachRequestedTime used to capture the desire to detach this volume
   252  	detachRequestedTime time.Time
   253  }
   254  
   255  // nodeToUpdateStatusFor is an object that reflects a node that has one or more
   256  // volume attached. It keeps track of the volumes that should be reported as
   257  // attached in the Node's Status API object.
   258  type nodeToUpdateStatusFor struct {
   259  	// nodeName contains the name of this node.
   260  	nodeName types.NodeName
   261  
   262  	// statusUpdateNeeded indicates that the value of the VolumesAttached field
   263  	// in the Node's Status API object should be updated. This should be set to
   264  	// true whenever a volume is added or deleted from
   265  	// volumesToReportAsAttached. It should be reset whenever the status is
   266  	// updated.
   267  	statusUpdateNeeded bool
   268  
   269  	// volumesToReportAsAttached is the list of volumes that should be reported
   270  	// as attached in the Node's status (note that this may differ from the
   271  	// actual list of attached volumes since volumes should be removed from this
   272  	// list as soon a detach operation is considered, before the detach
   273  	// operation is triggered).
   274  	volumesToReportAsAttached map[v1.UniqueVolumeName]v1.UniqueVolumeName
   275  }
   276  
   277  func (asw *actualStateOfWorld) MarkVolumeAsUncertain(
   278  	logger klog.Logger,
   279  	uniqueName v1.UniqueVolumeName, volumeSpec *volume.Spec, nodeName types.NodeName) error {
   280  
   281  	_, err := asw.AddVolumeNode(logger, uniqueName, volumeSpec, nodeName, "", false /* isAttached */)
   282  	return err
   283  }
   284  
   285  func (asw *actualStateOfWorld) MarkVolumeAsAttached(
   286  	logger klog.Logger,
   287  	uniqueName v1.UniqueVolumeName, volumeSpec *volume.Spec, nodeName types.NodeName, devicePath string) error {
   288  	_, err := asw.AddVolumeNode(logger, uniqueName, volumeSpec, nodeName, devicePath, true)
   289  	return err
   290  }
   291  
   292  func (asw *actualStateOfWorld) MarkVolumeAsDetached(
   293  	volumeName v1.UniqueVolumeName, nodeName types.NodeName) {
   294  	asw.DeleteVolumeNode(volumeName, nodeName)
   295  }
   296  
   297  func (asw *actualStateOfWorld) RemoveVolumeFromReportAsAttached(
   298  	volumeName v1.UniqueVolumeName, nodeName types.NodeName) error {
   299  	asw.Lock()
   300  	defer asw.Unlock()
   301  	return asw.removeVolumeFromReportAsAttached(volumeName, nodeName)
   302  }
   303  
   304  func (asw *actualStateOfWorld) AddVolumeToReportAsAttached(
   305  	logger klog.Logger,
   306  	volumeName v1.UniqueVolumeName, nodeName types.NodeName) {
   307  	asw.Lock()
   308  	defer asw.Unlock()
   309  	asw.addVolumeToReportAsAttached(logger, volumeName, nodeName)
   310  }
   311  
   312  func (asw *actualStateOfWorld) AddVolumeNode(
   313  	logger klog.Logger,
   314  	uniqueName v1.UniqueVolumeName, volumeSpec *volume.Spec, nodeName types.NodeName, devicePath string, isAttached bool) (v1.UniqueVolumeName, error) {
   315  	volumeName := uniqueName
   316  	if volumeName == "" {
   317  		if volumeSpec == nil {
   318  			return volumeName, fmt.Errorf("volumeSpec cannot be nil if volumeName is empty")
   319  		}
   320  		attachableVolumePlugin, err := asw.volumePluginMgr.FindAttachablePluginBySpec(volumeSpec)
   321  		if err != nil || attachableVolumePlugin == nil {
   322  			if attachableVolumePlugin == nil {
   323  				err = fmt.Errorf("plugin do not support attachment")
   324  			}
   325  			return "", fmt.Errorf(
   326  				"failed to get AttachablePlugin from volumeSpec for volume %q err=%v",
   327  				volumeSpec.Name(),
   328  				err)
   329  		}
   330  
   331  		volumeName, err = util.GetUniqueVolumeNameFromSpec(
   332  			attachableVolumePlugin, volumeSpec)
   333  		if err != nil {
   334  			return "", fmt.Errorf(
   335  				"failed to GetUniqueVolumeNameFromSpec for volumeSpec %q err=%v",
   336  				volumeSpec.Name(),
   337  				err)
   338  		}
   339  	}
   340  
   341  	asw.Lock()
   342  	defer asw.Unlock()
   343  
   344  	volumeObj, volumeExists := asw.attachedVolumes[volumeName]
   345  	if !volumeExists {
   346  		volumeObj = attachedVolume{
   347  			volumeName:      volumeName,
   348  			spec:            volumeSpec,
   349  			nodesAttachedTo: make(map[types.NodeName]nodeAttachedTo),
   350  			devicePath:      devicePath,
   351  		}
   352  	} else {
   353  		// If volume object already exists, it indicates that the information would be out of date.
   354  		// Update the fields for volume object except the nodes attached to the volumes.
   355  		volumeObj.devicePath = devicePath
   356  		volumeObj.spec = volumeSpec
   357  		logger.V(2).Info("Volume is already added to attachedVolume list to node, update device path",
   358  			"volumeName", volumeName,
   359  			"node", klog.KRef("", string(nodeName)),
   360  			"devicePath", devicePath)
   361  	}
   362  	node, nodeExists := volumeObj.nodesAttachedTo[nodeName]
   363  	if !nodeExists {
   364  		// Create object if it doesn't exist.
   365  		node = nodeAttachedTo{
   366  			nodeName:            nodeName,
   367  			attachedConfirmed:   isAttached,
   368  			detachRequestedTime: time.Time{},
   369  		}
   370  		// Assume mounted, until proven otherwise
   371  		if asw.inUseVolumes[nodeName] == nil {
   372  			asw.inUseVolumes[nodeName] = sets.New(volumeName)
   373  		} else {
   374  			asw.inUseVolumes[nodeName].Insert(volumeName)
   375  		}
   376  	} else {
   377  		node.attachedConfirmed = isAttached
   378  		logger.V(5).Info("Volume is already added to attachedVolume list to the node",
   379  			"volumeName", volumeName,
   380  			"node", klog.KRef("", string(nodeName)),
   381  			"currentAttachState", isAttached)
   382  	}
   383  
   384  	volumeObj.nodesAttachedTo[nodeName] = node
   385  	asw.attachedVolumes[volumeName] = volumeObj
   386  
   387  	if isAttached {
   388  		asw.addVolumeToReportAsAttached(logger, volumeName, nodeName)
   389  	}
   390  	return volumeName, nil
   391  }
   392  
   393  func (asw *actualStateOfWorld) SetVolumesMountedByNode(
   394  	logger klog.Logger, volumeNames []v1.UniqueVolumeName, nodeName types.NodeName) {
   395  	asw.Lock()
   396  	defer asw.Unlock()
   397  
   398  	asw.inUseVolumes[nodeName] = sets.New(volumeNames...)
   399  	logger.V(5).Info("SetVolumesMountedByNode volume to the node",
   400  		"node", klog.KRef("", string(nodeName)),
   401  		"volumeNames", volumeNames)
   402  }
   403  
   404  func (asw *actualStateOfWorld) ResetDetachRequestTime(
   405  	logger klog.Logger,
   406  	volumeName v1.UniqueVolumeName, nodeName types.NodeName) {
   407  	asw.Lock()
   408  	defer asw.Unlock()
   409  
   410  	volumeObj, nodeObj, err := asw.getNodeAndVolume(volumeName, nodeName)
   411  	if err != nil {
   412  		logger.Error(err, "Failed to ResetDetachRequestTime with error")
   413  		return
   414  	}
   415  	nodeObj.detachRequestedTime = time.Time{}
   416  	volumeObj.nodesAttachedTo[nodeName] = nodeObj
   417  }
   418  
   419  func (asw *actualStateOfWorld) SetDetachRequestTime(
   420  	logger klog.Logger,
   421  	volumeName v1.UniqueVolumeName, nodeName types.NodeName) (time.Duration, error) {
   422  	asw.Lock()
   423  	defer asw.Unlock()
   424  
   425  	volumeObj, nodeObj, err := asw.getNodeAndVolume(volumeName, nodeName)
   426  	if err != nil {
   427  		return 0, fmt.Errorf("failed to set detach request time with error: %v", err)
   428  	}
   429  	// If there is no previous detach request, set it to the current time
   430  	if nodeObj.detachRequestedTime.IsZero() {
   431  		nodeObj.detachRequestedTime = time.Now()
   432  		volumeObj.nodesAttachedTo[nodeName] = nodeObj
   433  		logger.V(4).Info("Set detach request time to current time for volume on node",
   434  			"node", klog.KRef("", string(nodeName)),
   435  			"volumeName", volumeName)
   436  	}
   437  	return time.Since(nodeObj.detachRequestedTime), nil
   438  }
   439  
   440  // Get the volume and node object from actual state of world
   441  // This is an internal function and caller should acquire and release the lock
   442  //
   443  // Note that this returns disconnected objects, so if you change the volume object you must set it back with
   444  // `asw.attachedVolumes[volumeName]=volumeObj`.
   445  //
   446  // If you change the node object you must use `volumeObj.nodesAttachedTo[nodeName] = nodeObj`
   447  // This is correct, because if volumeObj is empty this function returns an error, and nodesAttachedTo
   448  // map is a reference type, and thus mutating the copy changes the original map.
   449  func (asw *actualStateOfWorld) getNodeAndVolume(
   450  	volumeName v1.UniqueVolumeName, nodeName types.NodeName) (attachedVolume, nodeAttachedTo, error) {
   451  
   452  	volumeObj, volumeExists := asw.attachedVolumes[volumeName]
   453  	if volumeExists {
   454  		nodeObj, nodeExists := volumeObj.nodesAttachedTo[nodeName]
   455  		if nodeExists {
   456  			return volumeObj, nodeObj, nil
   457  		}
   458  	}
   459  
   460  	return attachedVolume{}, nodeAttachedTo{}, fmt.Errorf("volume %v is no longer attached to the node %q",
   461  		volumeName,
   462  		nodeName)
   463  }
   464  
   465  // Remove the volumeName from the node's volumesToReportAsAttached list
   466  // This is an internal function and caller should acquire and release the lock
   467  func (asw *actualStateOfWorld) removeVolumeFromReportAsAttached(
   468  	volumeName v1.UniqueVolumeName, nodeName types.NodeName) error {
   469  
   470  	nodeToUpdate, nodeToUpdateExists := asw.nodesToUpdateStatusFor[nodeName]
   471  	if nodeToUpdateExists {
   472  		_, nodeToUpdateVolumeExists :=
   473  			nodeToUpdate.volumesToReportAsAttached[volumeName]
   474  		if nodeToUpdateVolumeExists {
   475  			nodeToUpdate.statusUpdateNeeded = true
   476  			delete(nodeToUpdate.volumesToReportAsAttached, volumeName)
   477  			asw.nodesToUpdateStatusFor[nodeName] = nodeToUpdate
   478  			return nil
   479  		}
   480  	}
   481  	return fmt.Errorf("volume %q does not exist in volumesToReportAsAttached list or node %q does not exist in nodesToUpdateStatusFor list",
   482  		volumeName,
   483  		nodeName)
   484  
   485  }
   486  
   487  // Add the volumeName to the node's volumesToReportAsAttached list
   488  // This is an internal function and caller should acquire and release the lock
   489  func (asw *actualStateOfWorld) addVolumeToReportAsAttached(
   490  	logger klog.Logger, volumeName v1.UniqueVolumeName, nodeName types.NodeName) {
   491  	// In case the volume/node entry is no longer in attachedVolume list, skip the rest
   492  	if _, _, err := asw.getNodeAndVolume(volumeName, nodeName); err != nil {
   493  		logger.V(4).Info("Volume is no longer attached to node", "node", klog.KRef("", string(nodeName)), "volumeName", volumeName)
   494  		return
   495  	}
   496  	nodeToUpdate, nodeToUpdateExists := asw.nodesToUpdateStatusFor[nodeName]
   497  	if !nodeToUpdateExists {
   498  		// Create object if it doesn't exist
   499  		nodeToUpdate = nodeToUpdateStatusFor{
   500  			nodeName:                  nodeName,
   501  			statusUpdateNeeded:        true,
   502  			volumesToReportAsAttached: make(map[v1.UniqueVolumeName]v1.UniqueVolumeName),
   503  		}
   504  		asw.nodesToUpdateStatusFor[nodeName] = nodeToUpdate
   505  		logger.V(4).Info("Add new node to nodesToUpdateStatusFor", "node", klog.KRef("", string(nodeName)))
   506  	}
   507  	_, nodeToUpdateVolumeExists :=
   508  		nodeToUpdate.volumesToReportAsAttached[volumeName]
   509  	if !nodeToUpdateVolumeExists {
   510  		nodeToUpdate.statusUpdateNeeded = true
   511  		nodeToUpdate.volumesToReportAsAttached[volumeName] = volumeName
   512  		asw.nodesToUpdateStatusFor[nodeName] = nodeToUpdate
   513  		logger.V(4).Info("Report volume as attached to node", "node", klog.KRef("", string(nodeName)), "volumeName", volumeName)
   514  	}
   515  }
   516  
   517  // Update the flag statusUpdateNeeded to indicate whether node status is already updated or
   518  // needs to be updated again by the node status updater.
   519  // If the specified node does not exist in the nodesToUpdateStatusFor list, log the error and return
   520  // This is an internal function and caller should acquire and release the lock
   521  func (asw *actualStateOfWorld) updateNodeStatusUpdateNeeded(nodeName types.NodeName, needed bool) error {
   522  	nodeToUpdate, nodeToUpdateExists := asw.nodesToUpdateStatusFor[nodeName]
   523  	if !nodeToUpdateExists {
   524  		// should not happen
   525  		errMsg := fmt.Sprintf("Failed to set statusUpdateNeeded to needed %t, because nodeName=%q does not exist",
   526  			needed, nodeName)
   527  		return fmt.Errorf(errMsg)
   528  	}
   529  
   530  	nodeToUpdate.statusUpdateNeeded = needed
   531  	asw.nodesToUpdateStatusFor[nodeName] = nodeToUpdate
   532  
   533  	return nil
   534  }
   535  
   536  func (asw *actualStateOfWorld) SetNodeStatusUpdateNeeded(logger klog.Logger, nodeName types.NodeName) {
   537  	asw.Lock()
   538  	defer asw.Unlock()
   539  	if err := asw.updateNodeStatusUpdateNeeded(nodeName, true); err != nil {
   540  		logger.Info("Failed to update statusUpdateNeeded field in actual state of world", "err", err)
   541  	}
   542  }
   543  
   544  func (asw *actualStateOfWorld) DeleteVolumeNode(
   545  	volumeName v1.UniqueVolumeName, nodeName types.NodeName) {
   546  	asw.Lock()
   547  	defer asw.Unlock()
   548  
   549  	volumeObj, volumeExists := asw.attachedVolumes[volumeName]
   550  	if !volumeExists {
   551  		return
   552  	}
   553  
   554  	_, nodeExists := volumeObj.nodesAttachedTo[nodeName]
   555  	if nodeExists {
   556  		delete(asw.attachedVolumes[volumeName].nodesAttachedTo, nodeName)
   557  	}
   558  
   559  	if len(volumeObj.nodesAttachedTo) == 0 {
   560  		delete(asw.attachedVolumes, volumeName)
   561  	}
   562  
   563  	// Remove volume from volumes to report as attached
   564  	asw.removeVolumeFromReportAsAttached(volumeName, nodeName)
   565  }
   566  
   567  func (asw *actualStateOfWorld) GetAttachState(
   568  	volumeName v1.UniqueVolumeName, nodeName types.NodeName) AttachState {
   569  	asw.RLock()
   570  	defer asw.RUnlock()
   571  
   572  	volumeObj, volumeExists := asw.attachedVolumes[volumeName]
   573  	if volumeExists {
   574  		if node, nodeExists := volumeObj.nodesAttachedTo[nodeName]; nodeExists {
   575  			if node.attachedConfirmed {
   576  				return AttachStateAttached
   577  			}
   578  			return AttachStateUncertain
   579  		}
   580  	}
   581  
   582  	return AttachStateDetached
   583  }
   584  
   585  // SetVolumeClaimSize sets size of the volume. But this function should not be used from attach_detach controller.
   586  func (asw *actualStateOfWorld) InitializeClaimSize(logger klog.Logger, volumeName v1.UniqueVolumeName, claimSize *resource.Quantity) {
   587  	logger.V(5).Info("no-op InitializeClaimSize call in attach-detach controller")
   588  }
   589  
   590  func (asw *actualStateOfWorld) GetClaimSize(volumeName v1.UniqueVolumeName) *resource.Quantity {
   591  	// not needed in attach-detach controller
   592  	return nil
   593  }
   594  
   595  func (asw *actualStateOfWorld) GetAttachedVolumes() []AttachedVolume {
   596  	asw.RLock()
   597  	defer asw.RUnlock()
   598  
   599  	attachedVolumes := make([]AttachedVolume, 0 /* len */, len(asw.attachedVolumes) /* cap */)
   600  	for _, volumeObj := range asw.attachedVolumes {
   601  		for _, nodeObj := range volumeObj.nodesAttachedTo {
   602  			attachedVolumes = append(
   603  				attachedVolumes,
   604  				asw.getAttachedVolume(&volumeObj, &nodeObj))
   605  		}
   606  	}
   607  
   608  	return attachedVolumes
   609  }
   610  
   611  func (asw *actualStateOfWorld) GetAttachedVolumesForNode(
   612  	nodeName types.NodeName) []AttachedVolume {
   613  	asw.RLock()
   614  	defer asw.RUnlock()
   615  
   616  	attachedVolumes := make(
   617  		[]AttachedVolume, 0 /* len */, len(asw.attachedVolumes) /* cap */)
   618  	for _, volumeObj := range asw.attachedVolumes {
   619  		if nodeObj, nodeExists := volumeObj.nodesAttachedTo[nodeName]; nodeExists {
   620  			attachedVolumes = append(
   621  				attachedVolumes,
   622  				asw.getAttachedVolume(&volumeObj, &nodeObj))
   623  		}
   624  	}
   625  
   626  	return attachedVolumes
   627  }
   628  
   629  func (asw *actualStateOfWorld) GetAttachedVolumesPerNode() map[types.NodeName][]operationexecutor.AttachedVolume {
   630  	asw.RLock()
   631  	defer asw.RUnlock()
   632  
   633  	attachedVolumesPerNode := make(map[types.NodeName][]operationexecutor.AttachedVolume)
   634  	for _, volumeObj := range asw.attachedVolumes {
   635  		for nodeName, nodeObj := range volumeObj.nodesAttachedTo {
   636  			if nodeObj.attachedConfirmed {
   637  				volumes := attachedVolumesPerNode[nodeName]
   638  				volumes = append(volumes, asw.getAttachedVolume(&volumeObj, &nodeObj).AttachedVolume)
   639  				attachedVolumesPerNode[nodeName] = volumes
   640  			}
   641  		}
   642  	}
   643  
   644  	return attachedVolumesPerNode
   645  }
   646  
   647  func (asw *actualStateOfWorld) GetNodesForAttachedVolume(volumeName v1.UniqueVolumeName) []types.NodeName {
   648  	asw.RLock()
   649  	defer asw.RUnlock()
   650  
   651  	volumeObj, volumeExists := asw.attachedVolumes[volumeName]
   652  	if !volumeExists || len(volumeObj.nodesAttachedTo) == 0 {
   653  		return []types.NodeName{}
   654  	}
   655  
   656  	nodes := []types.NodeName{}
   657  	for nodeName, nodesAttached := range volumeObj.nodesAttachedTo {
   658  		if nodesAttached.attachedConfirmed {
   659  			nodes = append(nodes, nodeName)
   660  		}
   661  	}
   662  	return nodes
   663  }
   664  
   665  func (asw *actualStateOfWorld) GetVolumesToReportAttached(logger klog.Logger) map[types.NodeName][]v1.AttachedVolume {
   666  	asw.Lock()
   667  	defer asw.Unlock()
   668  
   669  	volumesToReportAttached := make(map[types.NodeName][]v1.AttachedVolume)
   670  	for nodeName, nodeToUpdateObj := range asw.nodesToUpdateStatusFor {
   671  		if nodeToUpdateObj.statusUpdateNeeded {
   672  			volumesToReportAttached[nodeToUpdateObj.nodeName] = asw.getAttachedVolumeFromUpdateObject(nodeToUpdateObj.volumesToReportAsAttached)
   673  		}
   674  		// When GetVolumesToReportAttached is called by node status updater, the current status
   675  		// of this node will be updated, so set the flag statusUpdateNeeded to false indicating
   676  		// the current status is already updated.
   677  		if err := asw.updateNodeStatusUpdateNeeded(nodeName, false); err != nil {
   678  			logger.Error(err, "Failed to update statusUpdateNeeded field when getting volumes")
   679  		}
   680  	}
   681  
   682  	return volumesToReportAttached
   683  }
   684  
   685  func (asw *actualStateOfWorld) GetVolumesToReportAttachedForNode(logger klog.Logger, nodeName types.NodeName) (bool, []v1.AttachedVolume) {
   686  	asw.Lock()
   687  	defer asw.Unlock()
   688  
   689  	nodeToUpdateObj, ok := asw.nodesToUpdateStatusFor[nodeName]
   690  	if !ok {
   691  		return false, nil
   692  	}
   693  	if !nodeToUpdateObj.statusUpdateNeeded {
   694  		return false, nil
   695  	}
   696  
   697  	volumesToReportAttached := asw.getAttachedVolumeFromUpdateObject(nodeToUpdateObj.volumesToReportAsAttached)
   698  	// When GetVolumesToReportAttached is called by node status updater, the current status
   699  	// of this node will be updated, so set the flag statusUpdateNeeded to false indicating
   700  	// the current status is already updated.
   701  	if err := asw.updateNodeStatusUpdateNeeded(nodeName, false); err != nil {
   702  		logger.Error(err, "Failed to update statusUpdateNeeded field when getting volumes")
   703  	}
   704  
   705  	return true, volumesToReportAttached
   706  }
   707  
   708  func (asw *actualStateOfWorld) GetNodesToUpdateStatusFor() map[types.NodeName]nodeToUpdateStatusFor {
   709  	return asw.nodesToUpdateStatusFor
   710  }
   711  
   712  func (asw *actualStateOfWorld) getAttachedVolumeFromUpdateObject(volumesToReportAsAttached map[v1.UniqueVolumeName]v1.UniqueVolumeName) []v1.AttachedVolume {
   713  	var attachedVolumes = make(
   714  		[]v1.AttachedVolume,
   715  		0,
   716  		len(volumesToReportAsAttached) /* len */)
   717  	for _, volume := range volumesToReportAsAttached {
   718  		attachedVolumes = append(attachedVolumes,
   719  			v1.AttachedVolume{
   720  				Name:       volume,
   721  				DevicePath: asw.attachedVolumes[volume].devicePath,
   722  			})
   723  	}
   724  	return attachedVolumes
   725  }
   726  
   727  func (asw *actualStateOfWorld) getAttachedVolume(
   728  	attachedVolume *attachedVolume,
   729  	nodeAttachedTo *nodeAttachedTo) AttachedVolume {
   730  	return AttachedVolume{
   731  		AttachedVolume: operationexecutor.AttachedVolume{
   732  			VolumeName:         attachedVolume.volumeName,
   733  			VolumeSpec:         attachedVolume.spec,
   734  			NodeName:           nodeAttachedTo.nodeName,
   735  			DevicePath:         attachedVolume.devicePath,
   736  			PluginIsAttachable: true,
   737  		},
   738  		MountedByNode:       asw.inUseVolumes[nodeAttachedTo.nodeName].Has(attachedVolume.volumeName),
   739  		DetachRequestedTime: nodeAttachedTo.detachRequestedTime}
   740  }