k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/pkg/controller/volume/attachdetach/cache/desired_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 28 v1 "k8s.io/api/core/v1" 29 k8stypes "k8s.io/apimachinery/pkg/types" 30 "k8s.io/kubernetes/pkg/volume" 31 "k8s.io/kubernetes/pkg/volume/util" 32 "k8s.io/kubernetes/pkg/volume/util/operationexecutor" 33 "k8s.io/kubernetes/pkg/volume/util/types" 34 ) 35 36 // DesiredStateOfWorld defines a set of thread-safe operations supported on 37 // the attach/detach controller's desired state of the world cache. 38 // This cache contains nodes->volumes->pods where nodes are all the nodes 39 // managed by the attach/detach controller, volumes are all the volumes that 40 // should be attached to the specified node, and pods are the pods that 41 // reference the volume and are scheduled to that node. 42 // Note: This is distinct from the DesiredStateOfWorld implemented by the 43 // kubelet volume manager. They both keep track of different objects. This 44 // contains attach/detach controller specific state. 45 type DesiredStateOfWorld interface { 46 // AddNode adds the given node to the list of nodes managed by the attach/ 47 // detach controller. 48 // If the node already exists this is a no-op. 49 AddNode(nodeName k8stypes.NodeName) 50 51 // AddPod adds the given pod to the list of pods that reference the 52 // specified volume and is scheduled to the specified node. 53 // A unique volumeName is generated from the volumeSpec and returned on 54 // success. 55 // If the pod already exists under the specified volume, this is a no-op. 56 // If volumeSpec is not an attachable volume plugin, an error is returned. 57 // If no volume with the name volumeName exists in the list of volumes that 58 // should be attached to the specified node, the volume is implicitly added. 59 // If no node with the name nodeName exists in list of nodes managed by the 60 // attach/detach attached controller, an error is returned. 61 AddPod(podName types.UniquePodName, pod *v1.Pod, volumeSpec *volume.Spec, nodeName k8stypes.NodeName) (v1.UniqueVolumeName, error) 62 63 // DeleteNode removes the given node from the list of nodes managed by the 64 // attach/detach controller. 65 // If the node does not exist this is a no-op. 66 // If the node exists but has 1 or more child volumes, an error is returned. 67 DeleteNode(nodeName k8stypes.NodeName) error 68 69 // DeletePod removes the given pod from the list of pods that reference the 70 // specified volume and are scheduled to the specified node. 71 // If no pod exists in the list of pods that reference the specified volume 72 // and are scheduled to the specified node, this is a no-op. 73 // If a node with the name nodeName does not exist in the list of nodes 74 // managed by the attach/detach attached controller, this is a no-op. 75 // If no volume with the name volumeName exists in the list of managed 76 // volumes under the specified node, this is a no-op. 77 // If after deleting the pod, the specified volume contains no other child 78 // pods, the volume is also deleted. 79 DeletePod(podName types.UniquePodName, volumeName v1.UniqueVolumeName, nodeName k8stypes.NodeName) 80 81 // NodeExists returns true if the node with the specified name exists in 82 // the list of nodes managed by the attach/detach controller. 83 NodeExists(nodeName k8stypes.NodeName) bool 84 85 // VolumeExists returns true if the volume with the specified name exists 86 // in the list of volumes that should be attached to the specified node by 87 // the attach detach controller. 88 VolumeExists(volumeName v1.UniqueVolumeName, nodeName k8stypes.NodeName) bool 89 90 // GetVolumesToAttach generates and returns a list of volumes to attach 91 // and the nodes they should be attached to based on the current desired 92 // state of the world. 93 GetVolumesToAttach() []VolumeToAttach 94 95 // GetPodToAdd generates and returns a map of pods based on the current desired 96 // state of world 97 GetPodToAdd() map[types.UniquePodName]PodToAdd 98 99 // Mark multi-attach error as reported to prevent spamming multiple 100 // events for same error 101 SetMultiAttachError(v1.UniqueVolumeName, k8stypes.NodeName) 102 103 // GetPodsOnNodes returns list of pods ("namespace/name") that require 104 // given volume on given nodes. 105 GetVolumePodsOnNodes(nodes []k8stypes.NodeName, volumeName v1.UniqueVolumeName) []*v1.Pod 106 } 107 108 // VolumeToAttach represents a volume that should be attached to a node. 109 type VolumeToAttach struct { 110 operationexecutor.VolumeToAttach 111 } 112 113 // PodToAdd represents a pod that references the underlying volume and is 114 // scheduled to the underlying node. 115 type PodToAdd struct { 116 // pod contains the api object of pod 117 Pod *v1.Pod 118 119 // volumeName contains the unique identifier for this volume. 120 VolumeName v1.UniqueVolumeName 121 122 // nodeName contains the name of this node. 123 NodeName k8stypes.NodeName 124 } 125 126 // NewDesiredStateOfWorld returns a new instance of DesiredStateOfWorld. 127 func NewDesiredStateOfWorld(volumePluginMgr *volume.VolumePluginMgr) DesiredStateOfWorld { 128 return &desiredStateOfWorld{ 129 nodesManaged: make(map[k8stypes.NodeName]nodeManaged), 130 volumePluginMgr: volumePluginMgr, 131 } 132 } 133 134 type desiredStateOfWorld struct { 135 // nodesManaged is a map containing the set of nodes managed by the attach/ 136 // detach controller. The key in this map is the name of the node and the 137 // value is a node object containing more information about the node. 138 nodesManaged map[k8stypes.NodeName]nodeManaged 139 // volumePluginMgr is the volume plugin manager used to create volume 140 // plugin objects. 141 volumePluginMgr *volume.VolumePluginMgr 142 sync.RWMutex 143 } 144 145 // nodeManaged represents a node that is being managed by the attach/detach 146 // controller. 147 type nodeManaged struct { 148 // nodeName contains the name of this node. 149 nodeName k8stypes.NodeName 150 151 // volumesToAttach is a map containing the set of volumes that should be 152 // attached to this node. The key in the map is the name of the volume and 153 // the value is a volumeToAttach object containing more information about the volume. 154 volumesToAttach map[v1.UniqueVolumeName]volumeToAttach 155 } 156 157 // The volumeToAttach object represents a volume that should be attached to a node. 158 type volumeToAttach struct { 159 // multiAttachErrorReported indicates whether the multi-attach error has been reported for the given volume. 160 // It is used to prevent reporting the error from being reported more than once for a given volume. 161 multiAttachErrorReported bool 162 163 // volumeName contains the unique identifier for this volume. 164 volumeName v1.UniqueVolumeName 165 166 // spec is the volume spec containing the specification for this volume. 167 // Used to generate the volume plugin object, and passed to attach/detach 168 // methods. 169 spec *volume.Spec 170 171 // scheduledPods is a map containing the set of pods that reference this 172 // volume and are scheduled to the underlying node. The key in the map is 173 // the name of the pod and the value is a pod object containing more 174 // information about the pod. 175 scheduledPods map[types.UniquePodName]pod 176 } 177 178 // The pod represents a pod that references the underlying volume and is 179 // scheduled to the underlying node. 180 type pod struct { 181 // podName contains the unique identifier for this pod 182 podName types.UniquePodName 183 184 // pod object contains the api object of pod 185 podObj *v1.Pod 186 } 187 188 func (dsw *desiredStateOfWorld) AddNode(nodeName k8stypes.NodeName) { 189 dsw.Lock() 190 defer dsw.Unlock() 191 192 if _, nodeExists := dsw.nodesManaged[nodeName]; !nodeExists { 193 dsw.nodesManaged[nodeName] = nodeManaged{ 194 nodeName: nodeName, 195 volumesToAttach: make(map[v1.UniqueVolumeName]volumeToAttach), 196 } 197 } 198 } 199 200 func (dsw *desiredStateOfWorld) AddPod( 201 podName types.UniquePodName, 202 podToAdd *v1.Pod, 203 volumeSpec *volume.Spec, 204 nodeName k8stypes.NodeName) (v1.UniqueVolumeName, error) { 205 dsw.Lock() 206 defer dsw.Unlock() 207 208 nodeObj, nodeExists := dsw.nodesManaged[nodeName] 209 if !nodeExists { 210 return "", fmt.Errorf( 211 "no node with the name %q exists in the list of managed nodes", 212 nodeName) 213 } 214 215 attachableVolumePlugin, err := dsw.volumePluginMgr.FindAttachablePluginBySpec(volumeSpec) 216 if err != nil || attachableVolumePlugin == nil { 217 if attachableVolumePlugin == nil { 218 err = fmt.Errorf("plugin do not support attachment") 219 } 220 return "", fmt.Errorf( 221 "failed to get AttachablePlugin from volumeSpec for volume %q err=%v", 222 volumeSpec.Name(), 223 err) 224 } 225 226 volumeName, err := util.GetUniqueVolumeNameFromSpec( 227 attachableVolumePlugin, volumeSpec) 228 if err != nil { 229 return "", fmt.Errorf( 230 "failed to get UniqueVolumeName from volumeSpec for plugin=%q and volume=%q err=%v", 231 attachableVolumePlugin.GetPluginName(), 232 volumeSpec.Name(), 233 err) 234 } 235 236 volumeObj, volumeExists := nodeObj.volumesToAttach[volumeName] 237 if !volumeExists { 238 volumeObj = volumeToAttach{ 239 multiAttachErrorReported: false, 240 volumeName: volumeName, 241 spec: volumeSpec, 242 scheduledPods: make(map[types.UniquePodName]pod), 243 } 244 dsw.nodesManaged[nodeName].volumesToAttach[volumeName] = volumeObj 245 } 246 if _, podExists := volumeObj.scheduledPods[podName]; !podExists { 247 dsw.nodesManaged[nodeName].volumesToAttach[volumeName].scheduledPods[podName] = 248 pod{ 249 podName: podName, 250 podObj: podToAdd, 251 } 252 } 253 254 return volumeName, nil 255 } 256 257 func (dsw *desiredStateOfWorld) DeleteNode(nodeName k8stypes.NodeName) error { 258 dsw.Lock() 259 defer dsw.Unlock() 260 261 nodeObj, nodeExists := dsw.nodesManaged[nodeName] 262 if !nodeExists { 263 return nil 264 } 265 266 if len(nodeObj.volumesToAttach) > 0 { 267 return fmt.Errorf( 268 "failed to delete node %q from list of nodes managed by attach/detach controller--the node still contains %v volumes in its list of volumes to attach", 269 nodeName, 270 len(nodeObj.volumesToAttach)) 271 } 272 273 delete( 274 dsw.nodesManaged, 275 nodeName) 276 return nil 277 } 278 279 func (dsw *desiredStateOfWorld) DeletePod( 280 podName types.UniquePodName, 281 volumeName v1.UniqueVolumeName, 282 nodeName k8stypes.NodeName) { 283 dsw.Lock() 284 defer dsw.Unlock() 285 286 nodeObj, nodeExists := dsw.nodesManaged[nodeName] 287 if !nodeExists { 288 return 289 } 290 291 volumeObj, volumeExists := nodeObj.volumesToAttach[volumeName] 292 if !volumeExists { 293 return 294 } 295 if _, podExists := volumeObj.scheduledPods[podName]; !podExists { 296 return 297 } 298 299 delete( 300 dsw.nodesManaged[nodeName].volumesToAttach[volumeName].scheduledPods, 301 podName) 302 303 if len(volumeObj.scheduledPods) == 0 { 304 delete( 305 dsw.nodesManaged[nodeName].volumesToAttach, 306 volumeName) 307 } 308 } 309 310 func (dsw *desiredStateOfWorld) NodeExists(nodeName k8stypes.NodeName) bool { 311 dsw.RLock() 312 defer dsw.RUnlock() 313 314 _, nodeExists := dsw.nodesManaged[nodeName] 315 return nodeExists 316 } 317 318 func (dsw *desiredStateOfWorld) VolumeExists( 319 volumeName v1.UniqueVolumeName, nodeName k8stypes.NodeName) bool { 320 dsw.RLock() 321 defer dsw.RUnlock() 322 323 nodeObj, nodeExists := dsw.nodesManaged[nodeName] 324 if nodeExists { 325 if _, volumeExists := nodeObj.volumesToAttach[volumeName]; volumeExists { 326 return true 327 } 328 } 329 330 return false 331 } 332 333 func (dsw *desiredStateOfWorld) SetMultiAttachError( 334 volumeName v1.UniqueVolumeName, 335 nodeName k8stypes.NodeName) { 336 dsw.Lock() 337 defer dsw.Unlock() 338 339 nodeObj, nodeExists := dsw.nodesManaged[nodeName] 340 if nodeExists { 341 if volumeObj, volumeExists := nodeObj.volumesToAttach[volumeName]; volumeExists { 342 volumeObj.multiAttachErrorReported = true 343 dsw.nodesManaged[nodeName].volumesToAttach[volumeName] = volumeObj 344 } 345 } 346 } 347 348 func (dsw *desiredStateOfWorld) GetVolumesToAttach() []VolumeToAttach { 349 dsw.RLock() 350 defer dsw.RUnlock() 351 352 volumesToAttach := make([]VolumeToAttach, 0 /* len */, len(dsw.nodesManaged) /* cap */) 353 for nodeName, nodeObj := range dsw.nodesManaged { 354 for volumeName, volumeObj := range nodeObj.volumesToAttach { 355 volumesToAttach = append(volumesToAttach, 356 VolumeToAttach{ 357 VolumeToAttach: operationexecutor.VolumeToAttach{ 358 MultiAttachErrorReported: volumeObj.multiAttachErrorReported, 359 VolumeName: volumeName, 360 VolumeSpec: volumeObj.spec, 361 NodeName: nodeName, 362 ScheduledPods: getPodsFromMap(volumeObj.scheduledPods), 363 }}) 364 } 365 } 366 367 return volumesToAttach 368 } 369 370 // Construct a list of v1.Pod objects from the given pod map 371 func getPodsFromMap(podMap map[types.UniquePodName]pod) []*v1.Pod { 372 pods := make([]*v1.Pod, 0, len(podMap)) 373 for _, pod := range podMap { 374 pods = append(pods, pod.podObj) 375 } 376 return pods 377 } 378 379 func (dsw *desiredStateOfWorld) GetPodToAdd() map[types.UniquePodName]PodToAdd { 380 dsw.RLock() 381 defer dsw.RUnlock() 382 383 pods := make(map[types.UniquePodName]PodToAdd) 384 for nodeName, nodeObj := range dsw.nodesManaged { 385 for volumeName, volumeObj := range nodeObj.volumesToAttach { 386 for podUID, pod := range volumeObj.scheduledPods { 387 pods[podUID] = PodToAdd{ 388 Pod: pod.podObj, 389 VolumeName: volumeName, 390 NodeName: nodeName, 391 } 392 } 393 } 394 } 395 return pods 396 } 397 398 func (dsw *desiredStateOfWorld) GetVolumePodsOnNodes(nodes []k8stypes.NodeName, volumeName v1.UniqueVolumeName) []*v1.Pod { 399 dsw.RLock() 400 defer dsw.RUnlock() 401 402 pods := []*v1.Pod{} 403 for _, nodeName := range nodes { 404 node, ok := dsw.nodesManaged[nodeName] 405 if !ok { 406 continue 407 } 408 volume, ok := node.volumesToAttach[volumeName] 409 if !ok { 410 continue 411 } 412 for _, pod := range volume.scheduledPods { 413 pods = append(pods, pod.podObj) 414 } 415 } 416 return pods 417 }