sigs.k8s.io/controller-runtime@v0.18.2/pkg/controller/controllerutil/controllerutil.go (about) 1 /* 2 Copyright 2018 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 controllerutil 18 19 import ( 20 "context" 21 "fmt" 22 "reflect" 23 24 "k8s.io/apimachinery/pkg/api/equality" 25 apierrors "k8s.io/apimachinery/pkg/api/errors" 26 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 27 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" 28 "k8s.io/apimachinery/pkg/runtime" 29 "k8s.io/apimachinery/pkg/runtime/schema" 30 "k8s.io/utils/ptr" 31 32 "sigs.k8s.io/controller-runtime/pkg/client" 33 "sigs.k8s.io/controller-runtime/pkg/client/apiutil" 34 ) 35 36 // AlreadyOwnedError is an error returned if the object you are trying to assign 37 // a controller reference is already owned by another controller Object is the 38 // subject and Owner is the reference for the current owner. 39 type AlreadyOwnedError struct { 40 Object metav1.Object 41 Owner metav1.OwnerReference 42 } 43 44 func (e *AlreadyOwnedError) Error() string { 45 return fmt.Sprintf("Object %s/%s is already owned by another %s controller %s", e.Object.GetNamespace(), e.Object.GetName(), e.Owner.Kind, e.Owner.Name) 46 } 47 48 func newAlreadyOwnedError(obj metav1.Object, owner metav1.OwnerReference) *AlreadyOwnedError { 49 return &AlreadyOwnedError{ 50 Object: obj, 51 Owner: owner, 52 } 53 } 54 55 // SetControllerReference sets owner as a Controller OwnerReference on controlled. 56 // This is used for garbage collection of the controlled object and for 57 // reconciling the owner object on changes to controlled (with a Watch + EnqueueRequestForOwner). 58 // Since only one OwnerReference can be a controller, it returns an error if 59 // there is another OwnerReference with Controller flag set. 60 func SetControllerReference(owner, controlled metav1.Object, scheme *runtime.Scheme) error { 61 // Validate the owner. 62 ro, ok := owner.(runtime.Object) 63 if !ok { 64 return fmt.Errorf("%T is not a runtime.Object, cannot call SetControllerReference", owner) 65 } 66 if err := validateOwner(owner, controlled); err != nil { 67 return err 68 } 69 70 // Create a new controller ref. 71 gvk, err := apiutil.GVKForObject(ro, scheme) 72 if err != nil { 73 return err 74 } 75 ref := metav1.OwnerReference{ 76 APIVersion: gvk.GroupVersion().String(), 77 Kind: gvk.Kind, 78 Name: owner.GetName(), 79 UID: owner.GetUID(), 80 BlockOwnerDeletion: ptr.To(true), 81 Controller: ptr.To(true), 82 } 83 84 // Return early with an error if the object is already controlled. 85 if existing := metav1.GetControllerOf(controlled); existing != nil && !referSameObject(*existing, ref) { 86 return newAlreadyOwnedError(controlled, *existing) 87 } 88 89 // Update owner references and return. 90 upsertOwnerRef(ref, controlled) 91 return nil 92 } 93 94 // SetOwnerReference is a helper method to make sure the given object contains an object reference to the object provided. 95 // This allows you to declare that owner has a dependency on the object without specifying it as a controller. 96 // If a reference to the same object already exists, it'll be overwritten with the newly provided version. 97 func SetOwnerReference(owner, object metav1.Object, scheme *runtime.Scheme) error { 98 // Validate the owner. 99 ro, ok := owner.(runtime.Object) 100 if !ok { 101 return fmt.Errorf("%T is not a runtime.Object, cannot call SetOwnerReference", owner) 102 } 103 if err := validateOwner(owner, object); err != nil { 104 return err 105 } 106 107 // Create a new owner ref. 108 gvk, err := apiutil.GVKForObject(ro, scheme) 109 if err != nil { 110 return err 111 } 112 ref := metav1.OwnerReference{ 113 APIVersion: gvk.GroupVersion().String(), 114 Kind: gvk.Kind, 115 UID: owner.GetUID(), 116 Name: owner.GetName(), 117 } 118 119 // Update owner references and return. 120 upsertOwnerRef(ref, object) 121 return nil 122 } 123 124 // RemoveOwnerReference is a helper method to make sure the given object removes an owner reference to the object provided. 125 // This allows you to remove the owner to establish a new owner of the object in a subsequent call. 126 func RemoveOwnerReference(owner, object metav1.Object, scheme *runtime.Scheme) error { 127 owners := object.GetOwnerReferences() 128 length := len(owners) 129 if length < 1 { 130 return fmt.Errorf("%T does not have any owner references", object) 131 } 132 ro, ok := owner.(runtime.Object) 133 if !ok { 134 return fmt.Errorf("%T is not a runtime.Object, cannot call RemoveOwnerReference", owner) 135 } 136 gvk, err := apiutil.GVKForObject(ro, scheme) 137 if err != nil { 138 return err 139 } 140 141 index := indexOwnerRef(owners, metav1.OwnerReference{ 142 APIVersion: gvk.GroupVersion().String(), 143 Name: owner.GetName(), 144 Kind: gvk.Kind, 145 }) 146 if index == -1 { 147 return fmt.Errorf("%T does not have an owner reference for %T", object, owner) 148 } 149 150 owners = append(owners[:index], owners[index+1:]...) 151 object.SetOwnerReferences(owners) 152 return nil 153 } 154 155 // HasControllerReference returns true if the object 156 // has an owner ref with controller equal to true 157 func HasControllerReference(object metav1.Object) bool { 158 owners := object.GetOwnerReferences() 159 for _, owner := range owners { 160 isTrue := owner.Controller 161 if owner.Controller != nil && *isTrue { 162 return true 163 } 164 } 165 return false 166 } 167 168 // RemoveControllerReference removes an owner reference where the controller 169 // equals true 170 func RemoveControllerReference(owner, object metav1.Object, scheme *runtime.Scheme) error { 171 if ok := HasControllerReference(object); !ok { 172 return fmt.Errorf("%T does not have a owner reference with controller equals true", object) 173 } 174 ro, ok := owner.(runtime.Object) 175 if !ok { 176 return fmt.Errorf("%T is not a runtime.Object, cannot call RemoveControllerReference", owner) 177 } 178 gvk, err := apiutil.GVKForObject(ro, scheme) 179 if err != nil { 180 return err 181 } 182 ownerRefs := object.GetOwnerReferences() 183 index := indexOwnerRef(ownerRefs, metav1.OwnerReference{ 184 APIVersion: gvk.GroupVersion().String(), 185 Name: owner.GetName(), 186 Kind: gvk.Kind, 187 }) 188 189 if index == -1 { 190 return fmt.Errorf("%T does not have an controller reference for %T", object, owner) 191 } 192 193 if ownerRefs[index].Controller == nil || !*ownerRefs[index].Controller { 194 return fmt.Errorf("%T owner is not the controller reference for %T", owner, object) 195 } 196 197 ownerRefs = append(ownerRefs[:index], ownerRefs[index+1:]...) 198 object.SetOwnerReferences(ownerRefs) 199 return nil 200 } 201 202 func upsertOwnerRef(ref metav1.OwnerReference, object metav1.Object) { 203 owners := object.GetOwnerReferences() 204 if idx := indexOwnerRef(owners, ref); idx == -1 { 205 owners = append(owners, ref) 206 } else { 207 owners[idx] = ref 208 } 209 object.SetOwnerReferences(owners) 210 } 211 212 // indexOwnerRef returns the index of the owner reference in the slice if found, or -1. 213 func indexOwnerRef(ownerReferences []metav1.OwnerReference, ref metav1.OwnerReference) int { 214 for index, r := range ownerReferences { 215 if referSameObject(r, ref) { 216 return index 217 } 218 } 219 return -1 220 } 221 222 func validateOwner(owner, object metav1.Object) error { 223 ownerNs := owner.GetNamespace() 224 if ownerNs != "" { 225 objNs := object.GetNamespace() 226 if objNs == "" { 227 return fmt.Errorf("cluster-scoped resource must not have a namespace-scoped owner, owner's namespace %s", ownerNs) 228 } 229 if ownerNs != objNs { 230 return fmt.Errorf("cross-namespace owner references are disallowed, owner's namespace %s, obj's namespace %s", owner.GetNamespace(), object.GetNamespace()) 231 } 232 } 233 return nil 234 } 235 236 // Returns true if a and b point to the same object. 237 func referSameObject(a, b metav1.OwnerReference) bool { 238 aGV, err := schema.ParseGroupVersion(a.APIVersion) 239 if err != nil { 240 return false 241 } 242 243 bGV, err := schema.ParseGroupVersion(b.APIVersion) 244 if err != nil { 245 return false 246 } 247 return aGV.Group == bGV.Group && a.Kind == b.Kind && a.Name == b.Name 248 } 249 250 // OperationResult is the action result of a CreateOrUpdate call. 251 type OperationResult string 252 253 const ( // They should complete the sentence "Deployment default/foo has been ..." 254 // OperationResultNone means that the resource has not been changed. 255 OperationResultNone OperationResult = "unchanged" 256 // OperationResultCreated means that a new resource is created. 257 OperationResultCreated OperationResult = "created" 258 // OperationResultUpdated means that an existing resource is updated. 259 OperationResultUpdated OperationResult = "updated" 260 // OperationResultUpdatedStatus means that an existing resource and its status is updated. 261 OperationResultUpdatedStatus OperationResult = "updatedStatus" 262 // OperationResultUpdatedStatusOnly means that only an existing status is updated. 263 OperationResultUpdatedStatusOnly OperationResult = "updatedStatusOnly" 264 ) 265 266 // CreateOrUpdate creates or updates the given object in the Kubernetes 267 // cluster. The object's desired state must be reconciled with the existing 268 // state inside the passed in callback MutateFn. 269 // 270 // The MutateFn is called regardless of creating or updating an object. 271 // 272 // It returns the executed operation and an error. 273 // 274 // Note: changes made by MutateFn to any sub-resource (status...), will be 275 // discarded. 276 func CreateOrUpdate(ctx context.Context, c client.Client, obj client.Object, f MutateFn) (OperationResult, error) { 277 key := client.ObjectKeyFromObject(obj) 278 if err := c.Get(ctx, key, obj); err != nil { 279 if !apierrors.IsNotFound(err) { 280 return OperationResultNone, err 281 } 282 if err := mutate(f, key, obj); err != nil { 283 return OperationResultNone, err 284 } 285 if err := c.Create(ctx, obj); err != nil { 286 return OperationResultNone, err 287 } 288 return OperationResultCreated, nil 289 } 290 291 existing := obj.DeepCopyObject() 292 if err := mutate(f, key, obj); err != nil { 293 return OperationResultNone, err 294 } 295 296 if equality.Semantic.DeepEqual(existing, obj) { 297 return OperationResultNone, nil 298 } 299 300 if err := c.Update(ctx, obj); err != nil { 301 return OperationResultNone, err 302 } 303 return OperationResultUpdated, nil 304 } 305 306 // CreateOrPatch creates or patches the given object in the Kubernetes 307 // cluster. The object's desired state must be reconciled with the before 308 // state inside the passed in callback MutateFn. 309 // 310 // The MutateFn is called regardless of creating or updating an object. 311 // 312 // It returns the executed operation and an error. 313 // 314 // Note: changes to any sub-resource other than status will be ignored. 315 // Changes to the status sub-resource will only be applied if the object 316 // already exist. To change the status on object creation, the easiest 317 // way is to requeue the object in the controller if OperationResult is 318 // OperationResultCreated 319 func CreateOrPatch(ctx context.Context, c client.Client, obj client.Object, f MutateFn) (OperationResult, error) { 320 key := client.ObjectKeyFromObject(obj) 321 if err := c.Get(ctx, key, obj); err != nil { 322 if !apierrors.IsNotFound(err) { 323 return OperationResultNone, err 324 } 325 if f != nil { 326 if err := mutate(f, key, obj); err != nil { 327 return OperationResultNone, err 328 } 329 } 330 if err := c.Create(ctx, obj); err != nil { 331 return OperationResultNone, err 332 } 333 return OperationResultCreated, nil 334 } 335 336 // Create patches for the object and its possible status. 337 objPatch := client.MergeFrom(obj.DeepCopyObject().(client.Object)) 338 statusPatch := client.MergeFrom(obj.DeepCopyObject().(client.Object)) 339 340 // Create a copy of the original object as well as converting that copy to 341 // unstructured data. 342 before, err := runtime.DefaultUnstructuredConverter.ToUnstructured(obj.DeepCopyObject()) 343 if err != nil { 344 return OperationResultNone, err 345 } 346 347 // Attempt to extract the status from the resource for easier comparison later 348 beforeStatus, hasBeforeStatus, err := unstructured.NestedFieldCopy(before, "status") 349 if err != nil { 350 return OperationResultNone, err 351 } 352 353 // If the resource contains a status then remove it from the unstructured 354 // copy to avoid unnecessary patching later. 355 if hasBeforeStatus { 356 unstructured.RemoveNestedField(before, "status") 357 } 358 359 // Mutate the original object. 360 if f != nil { 361 if err := mutate(f, key, obj); err != nil { 362 return OperationResultNone, err 363 } 364 } 365 366 // Convert the resource to unstructured to compare against our before copy. 367 after, err := runtime.DefaultUnstructuredConverter.ToUnstructured(obj) 368 if err != nil { 369 return OperationResultNone, err 370 } 371 372 // Attempt to extract the status from the resource for easier comparison later 373 afterStatus, hasAfterStatus, err := unstructured.NestedFieldCopy(after, "status") 374 if err != nil { 375 return OperationResultNone, err 376 } 377 378 // If the resource contains a status then remove it from the unstructured 379 // copy to avoid unnecessary patching later. 380 if hasAfterStatus { 381 unstructured.RemoveNestedField(after, "status") 382 } 383 384 result := OperationResultNone 385 386 if !reflect.DeepEqual(before, after) { 387 // Only issue a Patch if the before and after resources (minus status) differ 388 if err := c.Patch(ctx, obj, objPatch); err != nil { 389 return result, err 390 } 391 result = OperationResultUpdated 392 } 393 394 if (hasBeforeStatus || hasAfterStatus) && !reflect.DeepEqual(beforeStatus, afterStatus) { 395 // Only issue a Status Patch if the resource has a status and the beforeStatus 396 // and afterStatus copies differ 397 if result == OperationResultUpdated { 398 // If Status was replaced by Patch before, set it to afterStatus 399 objectAfterPatch, err := runtime.DefaultUnstructuredConverter.ToUnstructured(obj) 400 if err != nil { 401 return result, err 402 } 403 if err = unstructured.SetNestedField(objectAfterPatch, afterStatus, "status"); err != nil { 404 return result, err 405 } 406 // If Status was replaced by Patch before, restore patched structure to the obj 407 if err = runtime.DefaultUnstructuredConverter.FromUnstructured(objectAfterPatch, obj); err != nil { 408 return result, err 409 } 410 } 411 if err := c.Status().Patch(ctx, obj, statusPatch); err != nil { 412 return result, err 413 } 414 if result == OperationResultUpdated { 415 result = OperationResultUpdatedStatus 416 } else { 417 result = OperationResultUpdatedStatusOnly 418 } 419 } 420 421 return result, nil 422 } 423 424 // mutate wraps a MutateFn and applies validation to its result. 425 func mutate(f MutateFn, key client.ObjectKey, obj client.Object) error { 426 if err := f(); err != nil { 427 return err 428 } 429 if newKey := client.ObjectKeyFromObject(obj); key != newKey { 430 return fmt.Errorf("MutateFn cannot mutate object name and/or object namespace") 431 } 432 return nil 433 } 434 435 // MutateFn is a function which mutates the existing object into its desired state. 436 type MutateFn func() error 437 438 // AddFinalizer accepts an Object and adds the provided finalizer if not present. 439 // It returns an indication of whether it updated the object's list of finalizers. 440 func AddFinalizer(o client.Object, finalizer string) (finalizersUpdated bool) { 441 f := o.GetFinalizers() 442 for _, e := range f { 443 if e == finalizer { 444 return false 445 } 446 } 447 o.SetFinalizers(append(f, finalizer)) 448 return true 449 } 450 451 // RemoveFinalizer accepts an Object and removes the provided finalizer if present. 452 // It returns an indication of whether it updated the object's list of finalizers. 453 func RemoveFinalizer(o client.Object, finalizer string) (finalizersUpdated bool) { 454 f := o.GetFinalizers() 455 length := len(f) 456 457 index := 0 458 for i := 0; i < length; i++ { 459 if f[i] == finalizer { 460 continue 461 } 462 f[index] = f[i] 463 index++ 464 } 465 o.SetFinalizers(f[:index]) 466 return length != index 467 } 468 469 // ContainsFinalizer checks an Object that the provided finalizer is present. 470 func ContainsFinalizer(o client.Object, finalizer string) bool { 471 f := o.GetFinalizers() 472 for _, e := range f { 473 if e == finalizer { 474 return true 475 } 476 } 477 return false 478 }