github.com/operator-framework/operator-lifecycle-manager@v0.30.0/pkg/lib/ownerutil/util.go (about) 1 package ownerutil 2 3 import ( 4 "fmt" 5 6 log "github.com/sirupsen/logrus" 7 corev1 "k8s.io/api/core/v1" 8 rbac "k8s.io/api/rbac/v1" 9 apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" 10 apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" 11 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 12 metav1ac "k8s.io/client-go/applyconfigurations/meta/v1" 13 14 "k8s.io/apimachinery/pkg/labels" 15 "k8s.io/apimachinery/pkg/runtime" 16 "k8s.io/apimachinery/pkg/runtime/schema" 17 apiregistrationv1 "k8s.io/kube-aggregator/pkg/apis/apiregistration/v1" 18 19 operatorsv1 "github.com/operator-framework/api/pkg/operators/v1" 20 operatorsv1alpha1 "github.com/operator-framework/api/pkg/operators/v1alpha1" 21 operatorsv2 "github.com/operator-framework/api/pkg/operators/v2" 22 ) 23 24 const ( 25 OwnerKey = "olm.owner" 26 OwnerNamespaceKey = "olm.owner.namespace" 27 OwnerKind = "olm.owner.kind" 28 ) 29 30 var ( 31 NotController = false 32 DontBlockOwnerDeletion = false 33 ) 34 35 // Owner is used to build an OwnerReference, and we need type and object metadata 36 type Owner interface { 37 metav1.Object 38 runtime.Object 39 } 40 41 func IsOwnedBy(object metav1.Object, owner Owner) bool { 42 for _, oref := range object.GetOwnerReferences() { 43 if oref.UID == owner.GetUID() { 44 return true 45 } 46 } 47 return false 48 } 49 50 func IsOwnedByLabel(object metav1.Object, owner Owner) bool { 51 kind := owner.GetObjectKind().GroupVersionKind().Kind 52 name, namespace, ok := GetOwnerByKindLabel(object, kind) 53 if !ok { 54 return false 55 } 56 57 if namespace == owner.GetNamespace() && name == owner.GetName() { 58 return true 59 } 60 61 return false 62 } 63 64 func IsOwnedByKind(object metav1.Object, ownerKind string) bool { 65 for _, oref := range object.GetOwnerReferences() { 66 if oref.Kind == ownerKind { 67 return true 68 } 69 } 70 return false 71 } 72 73 func GetOwnerByKind(object metav1.Object, ownerKind string) *metav1.OwnerReference { 74 for _, oref := range object.GetOwnerReferences() { 75 if oref.Kind == ownerKind { 76 return &oref 77 } 78 } 79 return nil 80 } 81 82 func GetOwnerByKindLabel(object metav1.Object, ownerKind string) (name, namespace string, ok bool) { 83 if !IsOwnedByKindLabel(object, ownerKind) { 84 return 85 } 86 if object.GetLabels() == nil { 87 return 88 } 89 90 namespace, ok = object.GetLabels()[OwnerNamespaceKey] 91 if !ok { 92 return 93 } 94 ok = false 95 96 name, ok = object.GetLabels()[OwnerKey] 97 return 98 } 99 100 // GetOwnersByKind returns all OwnerReferences of the given kind listed by the given object 101 func GetOwnersByKind(object metav1.Object, ownerKind string) []metav1.OwnerReference { 102 var orefs []metav1.OwnerReference 103 for _, oref := range object.GetOwnerReferences() { 104 if oref.Kind == ownerKind { 105 orefs = append(orefs, oref) 106 } 107 } 108 return orefs 109 } 110 111 // HasOwnerConflict checks if the given list of OwnerReferences points to owners other than the target. 112 // This function returns true if the list of OwnerReferences is empty or contains elements of the same kind as 113 // the target but does not include the target OwnerReference itself. This function returns false if the list contains 114 // the target, or has no elements of the same kind as the target. 115 // 116 // Note: This is imporant when determining if a Role, RoleBinding, ClusterRole, or ClusterRoleBinding 117 // can be used to satisfy permissions of a CSV. If the target CSV is not a member of the RBAC resource's 118 // OwnerReferences, then we know the resource can be garbage collected by OLM independently of the target 119 // CSV 120 func HasOwnerConflict(target Owner, owners []metav1.OwnerReference) bool { 121 // Infer TypeMeta for the target 122 if err := InferGroupVersionKind(target); err != nil { 123 log.Warn(err.Error()) 124 } 125 126 conflicts := false 127 for _, owner := range owners { 128 gvk := target.GetObjectKind().GroupVersionKind() 129 if owner.Kind == gvk.Kind && owner.APIVersion == gvk.Version { 130 if owner.Name == target.GetName() && owner.UID == target.GetUID() { 131 return false 132 } 133 134 conflicts = true 135 } 136 } 137 138 return conflicts 139 } 140 141 // Adoptable checks whether a resource with the given set of OwnerReferences is "adoptable" by 142 // the target OwnerReference. This function returns true if there exists an element in owners 143 // referencing the same kind target does, otherwise it returns false. 144 func Adoptable(target Owner, owners []metav1.OwnerReference) bool { 145 if len(owners) == 0 { 146 // Resources with no owners are not adoptable 147 return false 148 } 149 150 // Infer TypeMeta for the target 151 if err := InferGroupVersionKind(target); err != nil { 152 log.Warn(err.Error()) 153 } 154 155 for _, owner := range owners { 156 gvk := target.GetObjectKind().GroupVersionKind() 157 if owner.Kind == gvk.Kind { 158 return true 159 } 160 } 161 162 return false 163 } 164 165 // AddNonBlockingOwner adds a nonblocking owner to the ownerref list. 166 func AddNonBlockingOwner(object metav1.Object, owner Owner) { 167 ownerRefs := object.GetOwnerReferences() 168 if ownerRefs == nil { 169 ownerRefs = []metav1.OwnerReference{} 170 } 171 172 nonBlockingOwner := NonBlockingOwner(owner) 173 for _, item := range ownerRefs { 174 if item.Kind == nonBlockingOwner.Kind && item.Name == nonBlockingOwner.Name && item.UID == nonBlockingOwner.UID { 175 return 176 } 177 } 178 ownerRefs = append(ownerRefs, nonBlockingOwner) 179 object.SetOwnerReferences(ownerRefs) 180 } 181 182 // NonBlockingOwner returns an ownerrefence to be added to an ownerref list 183 func NonBlockingOwner(owner Owner) metav1.OwnerReference { 184 // Most of the time we won't have TypeMeta on the object, so we infer it for types we know about 185 if err := InferGroupVersionKind(owner); err != nil { 186 log.Warn(err.Error()) 187 } 188 189 gvk := owner.GetObjectKind().GroupVersionKind() 190 apiVersion, kind := gvk.ToAPIVersionAndKind() 191 192 return metav1.OwnerReference{ 193 APIVersion: apiVersion, 194 Kind: kind, 195 Name: owner.GetName(), 196 UID: owner.GetUID(), 197 BlockOwnerDeletion: &DontBlockOwnerDeletion, 198 Controller: &NotController, 199 } 200 } 201 202 // NonBlockingOwnerApplyConfiguration returns an ownerrefence to be added to an ownerref list used in an SSA Configuration 203 func NonBlockingOwnerApplyConfiguration(owner Owner) *metav1ac.OwnerReferenceApplyConfiguration { 204 ownerRef := NonBlockingOwner(owner) 205 206 ownerRefAC := metav1ac.OwnerReference(). 207 WithAPIVersion(ownerRef.APIVersion). 208 WithKind(ownerRef.Kind). 209 WithUID(ownerRef.UID). 210 WithName(ownerRef.Name). 211 WithBlockOwnerDeletion(*ownerRef.BlockOwnerDeletion). 212 WithController(*ownerRef.Controller) 213 214 return ownerRefAC 215 } 216 217 // OwnerLabel returns a label added to generated objects for later querying 218 func OwnerLabel(owner Owner, kind string) map[string]string { 219 return map[string]string{ 220 OwnerKey: owner.GetName(), 221 OwnerNamespaceKey: owner.GetNamespace(), 222 OwnerKind: kind, 223 } 224 } 225 226 // AddOwnerLabels adds ownerref-like labels to an object by inferring the owner kind 227 func AddOwnerLabels(object metav1.Object, owner Owner) error { 228 err := InferGroupVersionKind(owner) 229 if err != nil { 230 return err 231 } 232 AddOwnerLabelsForKind(object, owner, owner.GetObjectKind().GroupVersionKind().Kind) 233 return nil 234 } 235 236 // AddOwnerLabels adds ownerref-like labels to an object, with no inference 237 func AddOwnerLabelsForKind(object metav1.Object, owner Owner, kind string) { 238 labels := object.GetLabels() 239 if labels == nil { 240 labels = map[string]string{} 241 } 242 for key, val := range OwnerLabel(owner, kind) { 243 labels[key] = val 244 } 245 object.SetLabels(labels) 246 } 247 248 // IsOwnedByKindLabel returns whether or not a label exists on the object pointing to an owner of a particular kind 249 func IsOwnedByKindLabel(object metav1.Object, ownerKind string) bool { 250 if object.GetLabels() == nil { 251 return false 252 } 253 return object.GetLabels()[OwnerKind] == ownerKind 254 } 255 256 // AdoptableLabels determines if an OLM managed resource is adoptable by any of the given targets based on its owner labels. 257 // The checkName perimeter enables an additional check for name equality with the `olm.owner` label. 258 // Generally used for cross-namespace ownership and for Cluster -> Namespace scope. 259 func AdoptableLabels(labels map[string]string, checkName bool, targets ...Owner) bool { 260 if len(labels) == 0 { 261 // Resources with no owners are not adoptable 262 return false 263 } 264 265 for _, target := range targets { 266 if err := InferGroupVersionKind(target); err != nil { 267 log.Warn(err.Error()) 268 } 269 if labels[OwnerKind] == target.GetObjectKind().GroupVersionKind().Kind && 270 labels[OwnerNamespaceKey] == target.GetNamespace() && 271 (!checkName || labels[OwnerKey] == target.GetName()) { 272 return true 273 } 274 } 275 276 return false 277 } 278 279 // CSVOwnerSelector returns a label selector to find generated objects owned by owner 280 func CSVOwnerSelector(owner *operatorsv1alpha1.ClusterServiceVersion) labels.Selector { 281 return labels.SelectorFromSet(OwnerLabel(owner, operatorsv1alpha1.ClusterServiceVersionKind)) 282 } 283 284 // AddOwner adds an owner to the ownerref list. 285 func AddOwner(object metav1.Object, owner Owner, blockOwnerDeletion, isController bool) { 286 // Most of the time we won't have TypeMeta on the object, so we infer it for types we know about 287 if err := InferGroupVersionKind(owner); err != nil { 288 log.Warn(err.Error()) 289 } 290 291 ownerRefs := object.GetOwnerReferences() 292 if ownerRefs == nil { 293 ownerRefs = []metav1.OwnerReference{} 294 } 295 gvk := owner.GetObjectKind().GroupVersionKind() 296 apiVersion, kind := gvk.ToAPIVersionAndKind() 297 ownerRef := metav1.OwnerReference{ 298 APIVersion: apiVersion, 299 Kind: kind, 300 Name: owner.GetName(), 301 UID: owner.GetUID(), 302 BlockOwnerDeletion: &blockOwnerDeletion, 303 Controller: &isController, 304 } 305 for _, ref := range ownerRefs { 306 if ref.Kind == ownerRef.Kind && ref.Name == ownerRef.Name && ref.UID == ownerRef.UID { 307 return 308 } 309 } 310 ownerRefs = append(ownerRefs, ownerRef) 311 object.SetOwnerReferences(ownerRefs) 312 } 313 314 // EnsureOwner adds a new owner if needed and returns whether the object already had the owner. 315 func EnsureOwner(object metav1.Object, owner Owner) bool { 316 if IsOwnedBy(object, owner) { 317 return true 318 } else { 319 AddNonBlockingOwner(object, owner) 320 return false 321 } 322 } 323 324 // InferGroupVersionKind adds TypeMeta to an owner so that it can be written to an ownerref. 325 // TypeMeta is generally only known at serialization time, so we often won't know what GVK an owner has. 326 // For the types we know about, we can add the GVK of the apis that we're using the interact with the object. 327 func InferGroupVersionKind(obj runtime.Object) error { 328 objectKind := obj.GetObjectKind() 329 if !objectKind.GroupVersionKind().Empty() { 330 // objectKind already has TypeMeta, no inference needed 331 return nil 332 } 333 334 switch obj.(type) { 335 case *corev1.Service: 336 objectKind.SetGroupVersionKind(schema.GroupVersionKind{ 337 Group: "", 338 Version: "v1", 339 Kind: "Service", 340 }) 341 case *corev1.ServiceAccount: 342 objectKind.SetGroupVersionKind(schema.GroupVersionKind{ 343 Group: "", 344 Version: "v1", 345 Kind: "ServiceAccount", 346 }) 347 case *corev1.ConfigMap: 348 objectKind.SetGroupVersionKind(schema.GroupVersionKind{ 349 Group: "", 350 Version: "v1", 351 Kind: "ConfigMap", 352 }) 353 case *corev1.Secret: 354 objectKind.SetGroupVersionKind(schema.GroupVersionKind{ 355 Group: "", 356 Version: "v1", 357 Kind: "Secret", 358 }) 359 case *rbac.ClusterRole: 360 objectKind.SetGroupVersionKind(schema.GroupVersionKind{ 361 Group: "rbac.authorization.k8s.io", 362 Version: "v1", 363 Kind: "ClusterRole", 364 }) 365 case *rbac.ClusterRoleBinding: 366 objectKind.SetGroupVersionKind(schema.GroupVersionKind{ 367 Group: "rbac.authorization.k8s.io", 368 Version: "v1", 369 Kind: "ClusterRoleBinding", 370 }) 371 case *rbac.Role: 372 objectKind.SetGroupVersionKind(schema.GroupVersionKind{ 373 Group: "rbac.authorization.k8s.io", 374 Version: "v1", 375 Kind: "Role", 376 }) 377 case *rbac.RoleBinding: 378 objectKind.SetGroupVersionKind(schema.GroupVersionKind{ 379 Group: "rbac.authorization.k8s.io", 380 Version: "v1", 381 Kind: "RoleBinding", 382 }) 383 case *operatorsv1alpha1.ClusterServiceVersion: 384 objectKind.SetGroupVersionKind(schema.GroupVersionKind{ 385 Group: operatorsv1alpha1.GroupName, 386 Version: operatorsv1alpha1.GroupVersion, 387 Kind: operatorsv1alpha1.ClusterServiceVersionKind, 388 }) 389 case *operatorsv1alpha1.InstallPlan: 390 objectKind.SetGroupVersionKind(schema.GroupVersionKind{ 391 Group: operatorsv1alpha1.GroupName, 392 Version: operatorsv1alpha1.GroupVersion, 393 Kind: operatorsv1alpha1.InstallPlanKind, 394 }) 395 case *operatorsv1alpha1.Subscription: 396 objectKind.SetGroupVersionKind(schema.GroupVersionKind{ 397 Group: operatorsv1alpha1.GroupName, 398 Version: operatorsv1alpha1.GroupVersion, 399 Kind: operatorsv1alpha1.SubscriptionKind, 400 }) 401 case *operatorsv1alpha1.CatalogSource: 402 objectKind.SetGroupVersionKind(schema.GroupVersionKind{ 403 Group: operatorsv1alpha1.GroupName, 404 Version: operatorsv1alpha1.GroupVersion, 405 Kind: operatorsv1alpha1.CatalogSourceKind, 406 }) 407 case *operatorsv1.OperatorGroup: 408 objectKind.SetGroupVersionKind(schema.GroupVersionKind{ 409 Group: operatorsv1.GroupVersion.Group, 410 Version: operatorsv1.GroupVersion.Version, 411 Kind: "OperatorGroup", 412 }) 413 case *operatorsv2.OperatorCondition: 414 objectKind.SetGroupVersionKind(schema.GroupVersionKind{ 415 Group: operatorsv2.GroupVersion.Group, 416 Version: operatorsv2.GroupVersion.Version, 417 Kind: "OperatorCondition", 418 }) 419 case *apiregistrationv1.APIService: 420 objectKind.SetGroupVersionKind(schema.GroupVersionKind{ 421 Group: apiregistrationv1.GroupName, 422 Version: apiregistrationv1.SchemeGroupVersion.Version, 423 Kind: "APIService", 424 }) 425 case *apiextensionsv1beta1.CustomResourceDefinition: 426 objectKind.SetGroupVersionKind(schema.GroupVersionKind{ 427 Group: apiextensionsv1beta1.GroupName, 428 Version: apiextensionsv1beta1.SchemeGroupVersion.Version, 429 Kind: "CustomResourceDefinition", 430 }) 431 case *apiextensionsv1.CustomResourceDefinition: 432 objectKind.SetGroupVersionKind(schema.GroupVersionKind{ 433 Group: apiextensionsv1.GroupName, 434 Version: apiextensionsv1.SchemeGroupVersion.Version, 435 Kind: "CustomResourceDefinition", 436 }) 437 default: 438 return fmt.Errorf("could not infer GVK for object: %#v, %#v", obj, objectKind) 439 } 440 return nil 441 }