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 }