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