k8s.io/kubernetes@v1.29.3/pkg/controller/controller_ref_manager.go (about) 1 /* 2 Copyright 2016 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package controller 18 19 import ( 20 "context" 21 "encoding/json" 22 "fmt" 23 "sync" 24 25 apps "k8s.io/api/apps/v1" 26 v1 "k8s.io/api/core/v1" 27 "k8s.io/apimachinery/pkg/api/errors" 28 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 29 "k8s.io/apimachinery/pkg/labels" 30 "k8s.io/apimachinery/pkg/runtime/schema" 31 "k8s.io/apimachinery/pkg/types" 32 utilerrors "k8s.io/apimachinery/pkg/util/errors" 33 "k8s.io/klog/v2" 34 ) 35 36 type BaseControllerRefManager struct { 37 Controller metav1.Object 38 Selector labels.Selector 39 40 canAdoptErr error 41 canAdoptOnce sync.Once 42 CanAdoptFunc func(ctx context.Context) error 43 } 44 45 func (m *BaseControllerRefManager) CanAdopt(ctx context.Context) error { 46 m.canAdoptOnce.Do(func() { 47 if m.CanAdoptFunc != nil { 48 m.canAdoptErr = m.CanAdoptFunc(ctx) 49 } 50 }) 51 return m.canAdoptErr 52 } 53 54 // ClaimObject tries to take ownership of an object for this controller. 55 // 56 // It will reconcile the following: 57 // - Adopt orphans if the match function returns true. 58 // - Release owned objects if the match function returns false. 59 // 60 // A non-nil error is returned if some form of reconciliation was attempted and 61 // failed. Usually, controllers should try again later in case reconciliation 62 // is still needed. 63 // 64 // If the error is nil, either the reconciliation succeeded, or no 65 // reconciliation was necessary. The returned boolean indicates whether you now 66 // own the object. 67 // 68 // No reconciliation will be attempted if the controller is being deleted. 69 func (m *BaseControllerRefManager) ClaimObject(ctx context.Context, obj metav1.Object, match func(metav1.Object) bool, adopt, release func(context.Context, metav1.Object) error) (bool, error) { 70 controllerRef := metav1.GetControllerOfNoCopy(obj) 71 if controllerRef != nil { 72 if controllerRef.UID != m.Controller.GetUID() { 73 // Owned by someone else. Ignore. 74 return false, nil 75 } 76 if match(obj) { 77 // We already own it and the selector matches. 78 // Return true (successfully claimed) before checking deletion timestamp. 79 // We're still allowed to claim things we already own while being deleted 80 // because doing so requires taking no actions. 81 return true, nil 82 } 83 // Owned by us but selector doesn't match. 84 // Try to release, unless we're being deleted. 85 if m.Controller.GetDeletionTimestamp() != nil { 86 return false, nil 87 } 88 if err := release(ctx, obj); err != nil { 89 // If the pod no longer exists, ignore the error. 90 if errors.IsNotFound(err) { 91 return false, nil 92 } 93 // Either someone else released it, or there was a transient error. 94 // The controller should requeue and try again if it's still stale. 95 return false, err 96 } 97 // Successfully released. 98 return false, nil 99 } 100 101 // It's an orphan. 102 if m.Controller.GetDeletionTimestamp() != nil || !match(obj) { 103 // Ignore if we're being deleted or selector doesn't match. 104 return false, nil 105 } 106 if obj.GetDeletionTimestamp() != nil { 107 // Ignore if the object is being deleted 108 return false, nil 109 } 110 111 if len(m.Controller.GetNamespace()) > 0 && m.Controller.GetNamespace() != obj.GetNamespace() { 112 // Ignore if namespace not match 113 return false, nil 114 } 115 116 // Selector matches. Try to adopt. 117 if err := adopt(ctx, obj); err != nil { 118 // If the pod no longer exists, ignore the error. 119 if errors.IsNotFound(err) { 120 return false, nil 121 } 122 // Either someone else claimed it first, or there was a transient error. 123 // The controller should requeue and try again if it's still orphaned. 124 return false, err 125 } 126 // Successfully adopted. 127 return true, nil 128 } 129 130 type PodControllerRefManager struct { 131 BaseControllerRefManager 132 controllerKind schema.GroupVersionKind 133 podControl PodControlInterface 134 finalizers []string 135 } 136 137 // NewPodControllerRefManager returns a PodControllerRefManager that exposes 138 // methods to manage the controllerRef of pods. 139 // 140 // The CanAdopt() function can be used to perform a potentially expensive check 141 // (such as a live GET from the API server) prior to the first adoption. 142 // It will only be called (at most once) if an adoption is actually attempted. 143 // If CanAdopt() returns a non-nil error, all adoptions will fail. 144 // 145 // NOTE: Once CanAdopt() is called, it will not be called again by the same 146 // PodControllerRefManager instance. Create a new instance if it makes 147 // sense to check CanAdopt() again (e.g. in a different sync pass). 148 func NewPodControllerRefManager( 149 podControl PodControlInterface, 150 controller metav1.Object, 151 selector labels.Selector, 152 controllerKind schema.GroupVersionKind, 153 canAdopt func(ctx context.Context) error, 154 finalizers ...string, 155 ) *PodControllerRefManager { 156 return &PodControllerRefManager{ 157 BaseControllerRefManager: BaseControllerRefManager{ 158 Controller: controller, 159 Selector: selector, 160 CanAdoptFunc: canAdopt, 161 }, 162 controllerKind: controllerKind, 163 podControl: podControl, 164 finalizers: finalizers, 165 } 166 } 167 168 // ClaimPods tries to take ownership of a list of Pods. 169 // 170 // It will reconcile the following: 171 // - Adopt orphans if the selector matches. 172 // - Release owned objects if the selector no longer matches. 173 // 174 // Optional: If one or more filters are specified, a Pod will only be claimed if 175 // all filters return true. 176 // 177 // A non-nil error is returned if some form of reconciliation was attempted and 178 // failed. Usually, controllers should try again later in case reconciliation 179 // is still needed. 180 // 181 // If the error is nil, either the reconciliation succeeded, or no 182 // reconciliation was necessary. The list of Pods that you now own is returned. 183 func (m *PodControllerRefManager) ClaimPods(ctx context.Context, pods []*v1.Pod, filters ...func(*v1.Pod) bool) ([]*v1.Pod, error) { 184 var claimed []*v1.Pod 185 var errlist []error 186 187 match := func(obj metav1.Object) bool { 188 pod := obj.(*v1.Pod) 189 // Check selector first so filters only run on potentially matching Pods. 190 if !m.Selector.Matches(labels.Set(pod.Labels)) { 191 return false 192 } 193 for _, filter := range filters { 194 if !filter(pod) { 195 return false 196 } 197 } 198 return true 199 } 200 adopt := func(ctx context.Context, obj metav1.Object) error { 201 return m.AdoptPod(ctx, obj.(*v1.Pod)) 202 } 203 release := func(ctx context.Context, obj metav1.Object) error { 204 return m.ReleasePod(ctx, obj.(*v1.Pod)) 205 } 206 207 for _, pod := range pods { 208 ok, err := m.ClaimObject(ctx, pod, match, adopt, release) 209 if err != nil { 210 errlist = append(errlist, err) 211 continue 212 } 213 if ok { 214 claimed = append(claimed, pod) 215 } 216 } 217 return claimed, utilerrors.NewAggregate(errlist) 218 } 219 220 // AdoptPod sends a patch to take control of the pod. It returns the error if 221 // the patching fails. 222 func (m *PodControllerRefManager) AdoptPod(ctx context.Context, pod *v1.Pod) error { 223 if err := m.CanAdopt(ctx); err != nil { 224 return fmt.Errorf("can't adopt Pod %v/%v (%v): %v", pod.Namespace, pod.Name, pod.UID, err) 225 } 226 // Note that ValidateOwnerReferences() will reject this patch if another 227 // OwnerReference exists with controller=true. 228 229 patchBytes, err := ownerRefControllerPatch(m.Controller, m.controllerKind, pod.UID, m.finalizers...) 230 if err != nil { 231 return err 232 } 233 return m.podControl.PatchPod(ctx, pod.Namespace, pod.Name, patchBytes) 234 } 235 236 // ReleasePod sends a patch to free the pod from the control of the controller. 237 // It returns the error if the patching fails. 404 and 422 errors are ignored. 238 func (m *PodControllerRefManager) ReleasePod(ctx context.Context, pod *v1.Pod) error { 239 logger := klog.FromContext(ctx) 240 logger.V(2).Info("Patching pod to remove its controllerRef", "pod", klog.KObj(pod), "gvk", m.controllerKind, "controller", m.Controller.GetName()) 241 patchBytes, err := GenerateDeleteOwnerRefStrategicMergeBytes(pod.UID, []types.UID{m.Controller.GetUID()}, m.finalizers...) 242 if err != nil { 243 return err 244 } 245 err = m.podControl.PatchPod(ctx, pod.Namespace, pod.Name, patchBytes) 246 if err != nil { 247 if errors.IsNotFound(err) { 248 // If the pod no longer exists, ignore it. 249 return nil 250 } 251 if errors.IsInvalid(err) { 252 // Invalid error will be returned in two cases: 1. the pod 253 // has no owner reference, 2. the uid of the pod doesn't 254 // match, which means the pod is deleted and then recreated. 255 // In both cases, the error can be ignored. 256 257 // TODO: If the pod has owner references, but none of them 258 // has the owner.UID, server will silently ignore the patch. 259 // Investigate why. 260 return nil 261 } 262 } 263 return err 264 } 265 266 // ReplicaSetControllerRefManager is used to manage controllerRef of ReplicaSets. 267 // Three methods are defined on this object 1: Classify 2: AdoptReplicaSet and 268 // 3: ReleaseReplicaSet which are used to classify the ReplicaSets into appropriate 269 // categories and accordingly adopt or release them. See comments on these functions 270 // for more details. 271 type ReplicaSetControllerRefManager struct { 272 BaseControllerRefManager 273 controllerKind schema.GroupVersionKind 274 rsControl RSControlInterface 275 } 276 277 // NewReplicaSetControllerRefManager returns a ReplicaSetControllerRefManager that exposes 278 // methods to manage the controllerRef of ReplicaSets. 279 // 280 // The CanAdopt() function can be used to perform a potentially expensive check 281 // (such as a live GET from the API server) prior to the first adoption. 282 // It will only be called (at most once) if an adoption is actually attempted. 283 // If CanAdopt() returns a non-nil error, all adoptions will fail. 284 // 285 // NOTE: Once CanAdopt() is called, it will not be called again by the same 286 // ReplicaSetControllerRefManager instance. Create a new instance if it 287 // makes sense to check CanAdopt() again (e.g. in a different sync pass). 288 func NewReplicaSetControllerRefManager( 289 rsControl RSControlInterface, 290 controller metav1.Object, 291 selector labels.Selector, 292 controllerKind schema.GroupVersionKind, 293 canAdopt func(ctx context.Context) error, 294 ) *ReplicaSetControllerRefManager { 295 return &ReplicaSetControllerRefManager{ 296 BaseControllerRefManager: BaseControllerRefManager{ 297 Controller: controller, 298 Selector: selector, 299 CanAdoptFunc: canAdopt, 300 }, 301 controllerKind: controllerKind, 302 rsControl: rsControl, 303 } 304 } 305 306 // ClaimReplicaSets tries to take ownership of a list of ReplicaSets. 307 // 308 // It will reconcile the following: 309 // - Adopt orphans if the selector matches. 310 // - Release owned objects if the selector no longer matches. 311 // 312 // A non-nil error is returned if some form of reconciliation was attempted and 313 // failed. Usually, controllers should try again later in case reconciliation 314 // is still needed. 315 // 316 // If the error is nil, either the reconciliation succeeded, or no 317 // reconciliation was necessary. The list of ReplicaSets that you now own is 318 // returned. 319 func (m *ReplicaSetControllerRefManager) ClaimReplicaSets(ctx context.Context, sets []*apps.ReplicaSet) ([]*apps.ReplicaSet, error) { 320 var claimed []*apps.ReplicaSet 321 var errlist []error 322 323 match := func(obj metav1.Object) bool { 324 return m.Selector.Matches(labels.Set(obj.GetLabels())) 325 } 326 adopt := func(ctx context.Context, obj metav1.Object) error { 327 return m.AdoptReplicaSet(ctx, obj.(*apps.ReplicaSet)) 328 } 329 release := func(ctx context.Context, obj metav1.Object) error { 330 return m.ReleaseReplicaSet(ctx, obj.(*apps.ReplicaSet)) 331 } 332 333 for _, rs := range sets { 334 ok, err := m.ClaimObject(ctx, rs, match, adopt, release) 335 if err != nil { 336 errlist = append(errlist, err) 337 continue 338 } 339 if ok { 340 claimed = append(claimed, rs) 341 } 342 } 343 return claimed, utilerrors.NewAggregate(errlist) 344 } 345 346 // AdoptReplicaSet sends a patch to take control of the ReplicaSet. It returns 347 // the error if the patching fails. 348 func (m *ReplicaSetControllerRefManager) AdoptReplicaSet(ctx context.Context, rs *apps.ReplicaSet) error { 349 if err := m.CanAdopt(ctx); err != nil { 350 return fmt.Errorf("can't adopt ReplicaSet %v/%v (%v): %v", rs.Namespace, rs.Name, rs.UID, err) 351 } 352 // Note that ValidateOwnerReferences() will reject this patch if another 353 // OwnerReference exists with controller=true. 354 patchBytes, err := ownerRefControllerPatch(m.Controller, m.controllerKind, rs.UID) 355 if err != nil { 356 return err 357 } 358 return m.rsControl.PatchReplicaSet(ctx, rs.Namespace, rs.Name, patchBytes) 359 } 360 361 // ReleaseReplicaSet sends a patch to free the ReplicaSet from the control of the Deployment controller. 362 // It returns the error if the patching fails. 404 and 422 errors are ignored. 363 func (m *ReplicaSetControllerRefManager) ReleaseReplicaSet(ctx context.Context, replicaSet *apps.ReplicaSet) error { 364 logger := klog.FromContext(ctx) 365 logger.V(2).Info("Patching ReplicaSet to remove its controllerRef", "replicaSet", klog.KObj(replicaSet), "gvk", m.controllerKind, "controller", m.Controller.GetName()) 366 patchBytes, err := GenerateDeleteOwnerRefStrategicMergeBytes(replicaSet.UID, []types.UID{m.Controller.GetUID()}) 367 if err != nil { 368 return err 369 } 370 err = m.rsControl.PatchReplicaSet(ctx, replicaSet.Namespace, replicaSet.Name, patchBytes) 371 if err != nil { 372 if errors.IsNotFound(err) { 373 // If the ReplicaSet no longer exists, ignore it. 374 return nil 375 } 376 if errors.IsInvalid(err) { 377 // Invalid error will be returned in two cases: 1. the ReplicaSet 378 // has no owner reference, 2. the uid of the ReplicaSet doesn't 379 // match, which means the ReplicaSet is deleted and then recreated. 380 // In both cases, the error can be ignored. 381 return nil 382 } 383 } 384 return err 385 } 386 387 // RecheckDeletionTimestamp returns a CanAdopt() function to recheck deletion. 388 // 389 // The CanAdopt() function calls getObject() to fetch the latest value, 390 // and denies adoption attempts if that object has a non-nil DeletionTimestamp. 391 func RecheckDeletionTimestamp(getObject func(context.Context) (metav1.Object, error)) func(context.Context) error { 392 return func(ctx context.Context) error { 393 obj, err := getObject(ctx) 394 if err != nil { 395 return fmt.Errorf("can't recheck DeletionTimestamp: %v", err) 396 } 397 if obj.GetDeletionTimestamp() != nil { 398 return fmt.Errorf("%v/%v has just been deleted at %v", obj.GetNamespace(), obj.GetName(), obj.GetDeletionTimestamp()) 399 } 400 return nil 401 } 402 } 403 404 // ControllerRevisionControllerRefManager is used to manage controllerRef of ControllerRevisions. 405 // Three methods are defined on this object 1: Classify 2: AdoptControllerRevision and 406 // 3: ReleaseControllerRevision which are used to classify the ControllerRevisions into appropriate 407 // categories and accordingly adopt or release them. See comments on these functions 408 // for more details. 409 type ControllerRevisionControllerRefManager struct { 410 BaseControllerRefManager 411 controllerKind schema.GroupVersionKind 412 crControl ControllerRevisionControlInterface 413 } 414 415 // NewControllerRevisionControllerRefManager returns a ControllerRevisionControllerRefManager that exposes 416 // methods to manage the controllerRef of ControllerRevisions. 417 // 418 // The canAdopt() function can be used to perform a potentially expensive check 419 // (such as a live GET from the API server) prior to the first adoption. 420 // It will only be called (at most once) if an adoption is actually attempted. 421 // If canAdopt() returns a non-nil error, all adoptions will fail. 422 // 423 // NOTE: Once canAdopt() is called, it will not be called again by the same 424 // ControllerRevisionControllerRefManager instance. Create a new instance if it 425 // makes sense to check canAdopt() again (e.g. in a different sync pass). 426 func NewControllerRevisionControllerRefManager( 427 crControl ControllerRevisionControlInterface, 428 controller metav1.Object, 429 selector labels.Selector, 430 controllerKind schema.GroupVersionKind, 431 canAdopt func(ctx context.Context) error, 432 ) *ControllerRevisionControllerRefManager { 433 return &ControllerRevisionControllerRefManager{ 434 BaseControllerRefManager: BaseControllerRefManager{ 435 Controller: controller, 436 Selector: selector, 437 CanAdoptFunc: canAdopt, 438 }, 439 controllerKind: controllerKind, 440 crControl: crControl, 441 } 442 } 443 444 // ClaimControllerRevisions tries to take ownership of a list of ControllerRevisions. 445 // 446 // It will reconcile the following: 447 // - Adopt orphans if the selector matches. 448 // - Release owned objects if the selector no longer matches. 449 // 450 // A non-nil error is returned if some form of reconciliation was attempted and 451 // failed. Usually, controllers should try again later in case reconciliation 452 // is still needed. 453 // 454 // If the error is nil, either the reconciliation succeeded, or no 455 // reconciliation was necessary. The list of ControllerRevisions that you now own is 456 // returned. 457 func (m *ControllerRevisionControllerRefManager) ClaimControllerRevisions(ctx context.Context, histories []*apps.ControllerRevision) ([]*apps.ControllerRevision, error) { 458 var claimed []*apps.ControllerRevision 459 var errlist []error 460 461 match := func(obj metav1.Object) bool { 462 return m.Selector.Matches(labels.Set(obj.GetLabels())) 463 } 464 adopt := func(ctx context.Context, obj metav1.Object) error { 465 return m.AdoptControllerRevision(ctx, obj.(*apps.ControllerRevision)) 466 } 467 release := func(ctx context.Context, obj metav1.Object) error { 468 return m.ReleaseControllerRevision(ctx, obj.(*apps.ControllerRevision)) 469 } 470 471 for _, h := range histories { 472 ok, err := m.ClaimObject(ctx, h, match, adopt, release) 473 if err != nil { 474 errlist = append(errlist, err) 475 continue 476 } 477 if ok { 478 claimed = append(claimed, h) 479 } 480 } 481 return claimed, utilerrors.NewAggregate(errlist) 482 } 483 484 // AdoptControllerRevision sends a patch to take control of the ControllerRevision. It returns the error if 485 // the patching fails. 486 func (m *ControllerRevisionControllerRefManager) AdoptControllerRevision(ctx context.Context, history *apps.ControllerRevision) error { 487 if err := m.CanAdopt(ctx); err != nil { 488 return fmt.Errorf("can't adopt ControllerRevision %v/%v (%v): %v", history.Namespace, history.Name, history.UID, err) 489 } 490 // Note that ValidateOwnerReferences() will reject this patch if another 491 // OwnerReference exists with controller=true. 492 patchBytes, err := ownerRefControllerPatch(m.Controller, m.controllerKind, history.UID) 493 if err != nil { 494 return err 495 } 496 return m.crControl.PatchControllerRevision(ctx, history.Namespace, history.Name, patchBytes) 497 } 498 499 // ReleaseControllerRevision sends a patch to free the ControllerRevision from the control of its controller. 500 // It returns the error if the patching fails. 404 and 422 errors are ignored. 501 func (m *ControllerRevisionControllerRefManager) ReleaseControllerRevision(ctx context.Context, history *apps.ControllerRevision) error { 502 logger := klog.FromContext(ctx) 503 logger.V(2).Info("Patching ControllerRevision to remove its controllerRef", "controllerRevision", klog.KObj(history), "gvk", m.controllerKind, "controller", m.Controller.GetName()) 504 patchBytes, err := GenerateDeleteOwnerRefStrategicMergeBytes(history.UID, []types.UID{m.Controller.GetUID()}) 505 if err != nil { 506 return err 507 } 508 509 err = m.crControl.PatchControllerRevision(ctx, history.Namespace, history.Name, patchBytes) 510 if err != nil { 511 if errors.IsNotFound(err) { 512 // If the ControllerRevision no longer exists, ignore it. 513 return nil 514 } 515 if errors.IsInvalid(err) { 516 // Invalid error will be returned in two cases: 1. the ControllerRevision 517 // has no owner reference, 2. the uid of the ControllerRevision doesn't 518 // match, which means the ControllerRevision is deleted and then recreated. 519 // In both cases, the error can be ignored. 520 return nil 521 } 522 } 523 return err 524 } 525 526 type objectForAddOwnerRefPatch struct { 527 Metadata objectMetaForPatch `json:"metadata"` 528 } 529 530 type objectMetaForPatch struct { 531 OwnerReferences []metav1.OwnerReference `json:"ownerReferences"` 532 UID types.UID `json:"uid"` 533 Finalizers []string `json:"finalizers,omitempty"` 534 } 535 536 func ownerRefControllerPatch(controller metav1.Object, controllerKind schema.GroupVersionKind, uid types.UID, finalizers ...string) ([]byte, error) { 537 blockOwnerDeletion := true 538 isController := true 539 addControllerPatch := objectForAddOwnerRefPatch{ 540 Metadata: objectMetaForPatch{ 541 UID: uid, 542 OwnerReferences: []metav1.OwnerReference{ 543 { 544 APIVersion: controllerKind.GroupVersion().String(), 545 Kind: controllerKind.Kind, 546 Name: controller.GetName(), 547 UID: controller.GetUID(), 548 Controller: &isController, 549 BlockOwnerDeletion: &blockOwnerDeletion, 550 }, 551 }, 552 Finalizers: finalizers, 553 }, 554 } 555 patchBytes, err := json.Marshal(&addControllerPatch) 556 if err != nil { 557 return nil, err 558 } 559 return patchBytes, nil 560 } 561 562 type objectForDeleteOwnerRefStrategicMergePatch struct { 563 Metadata objectMetaForMergePatch `json:"metadata"` 564 } 565 566 type objectMetaForMergePatch struct { 567 UID types.UID `json:"uid"` 568 OwnerReferences []map[string]string `json:"ownerReferences"` 569 DeleteFinalizers []string `json:"$deleteFromPrimitiveList/finalizers,omitempty"` 570 } 571 572 func GenerateDeleteOwnerRefStrategicMergeBytes(dependentUID types.UID, ownerUIDs []types.UID, finalizers ...string) ([]byte, error) { 573 var ownerReferences []map[string]string 574 for _, ownerUID := range ownerUIDs { 575 ownerReferences = append(ownerReferences, ownerReference(ownerUID, "delete")) 576 } 577 patch := objectForDeleteOwnerRefStrategicMergePatch{ 578 Metadata: objectMetaForMergePatch{ 579 UID: dependentUID, 580 OwnerReferences: ownerReferences, 581 DeleteFinalizers: finalizers, 582 }, 583 } 584 patchBytes, err := json.Marshal(&patch) 585 if err != nil { 586 return nil, err 587 } 588 return patchBytes, nil 589 } 590 591 func ownerReference(uid types.UID, patchType string) map[string]string { 592 return map[string]string{ 593 "$patch": patchType, 594 "uid": string(uid), 595 } 596 }