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  }