github.com/erdrix/operator-sdk@v0.8.2/pkg/handler/enqueue_annotation.go (about)

     1  // Copyright 2019 The Operator-SDK Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package handler
    16  
    17  import (
    18  	"strings"
    19  
    20  	"k8s.io/apimachinery/pkg/api/meta"
    21  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    22  	"k8s.io/apimachinery/pkg/types"
    23  	"k8s.io/client-go/util/workqueue"
    24  	"sigs.k8s.io/controller-runtime/pkg/event"
    25  	crtHandler "sigs.k8s.io/controller-runtime/pkg/handler"
    26  	"sigs.k8s.io/controller-runtime/pkg/reconcile"
    27  	logf "sigs.k8s.io/controller-runtime/pkg/runtime/log"
    28  )
    29  
    30  var log = logf.Log.WithName("event_handler")
    31  
    32  const (
    33  	// NamespacedNameAnnotation - annotation that will be used to get the primary resource namespaced name.
    34  	NamespacedNameAnnotation = "operator-sdk/primary-resource"
    35  	// TypeAnnotation - annotation that will be used to verify that the primary resource is the primary resource to use.
    36  	TypeAnnotation = "operator-sdk/primary-resource-type"
    37  )
    38  
    39  // EnqueueRequestForAnnotation enqueues Requests based on the presence of an annotation that contains the
    40  // namespaced name of the primary resource.
    41  //
    42  // The primary usecase for this, is to have a controller enqueue requests for the following scenarios
    43  // 1. namespaced primary object and dependent cluster scoped resource
    44  // 2. cluster scoped primary object.
    45  // 3. namespaced primary object and dependent namespaced scoped but in a different namespace object.
    46  type EnqueueRequestForAnnotation struct {
    47  	Type string
    48  
    49  	mapper meta.RESTMapper
    50  }
    51  
    52  var _ crtHandler.EventHandler = &EnqueueRequestForAnnotation{}
    53  
    54  // Create implements EventHandler
    55  func (e *EnqueueRequestForAnnotation) Create(evt event.CreateEvent, q workqueue.RateLimitingInterface) {
    56  	if ok, req := e.getAnnotationRequests(evt.Meta); ok {
    57  		q.Add(req)
    58  	}
    59  }
    60  
    61  // Update implements EventHandler
    62  func (e *EnqueueRequestForAnnotation) Update(evt event.UpdateEvent, q workqueue.RateLimitingInterface) {
    63  	if ok, req := e.getAnnotationRequests(evt.MetaOld); ok {
    64  		q.Add(req)
    65  	}
    66  	if ok, req := e.getAnnotationRequests(evt.MetaNew); ok {
    67  		q.Add(req)
    68  	}
    69  }
    70  
    71  // Delete implements EventHandler
    72  func (e *EnqueueRequestForAnnotation) Delete(evt event.DeleteEvent, q workqueue.RateLimitingInterface) {
    73  	if ok, req := e.getAnnotationRequests(evt.Meta); ok {
    74  		q.Add(req)
    75  	}
    76  }
    77  
    78  // Generic implements EventHandler
    79  func (e *EnqueueRequestForAnnotation) Generic(evt event.GenericEvent, q workqueue.RateLimitingInterface) {
    80  	if ok, req := e.getAnnotationRequests(evt.Meta); ok {
    81  		q.Add(req)
    82  	}
    83  }
    84  
    85  func (e *EnqueueRequestForAnnotation) getAnnotationRequests(object metav1.Object) (bool, reconcile.Request) {
    86  	if typeString, ok := object.GetAnnotations()[TypeAnnotation]; ok && typeString == e.Type {
    87  		namespacedNameString, ok := object.GetAnnotations()[NamespacedNameAnnotation]
    88  		if !ok {
    89  			log.Info("Unable to find namespaced name annotation for resource", "resource", object)
    90  		}
    91  		if namespacedNameString == "" {
    92  			return false, reconcile.Request{}
    93  		}
    94  		nsn := parseNamespacedName(namespacedNameString)
    95  		return true, reconcile.Request{NamespacedName: nsn}
    96  	}
    97  	return false, reconcile.Request{}
    98  }
    99  
   100  func parseNamespacedName(namespacedNameString string) types.NamespacedName {
   101  	values := strings.Split(namespacedNameString, "/")
   102  	if len(values) == 1 {
   103  		return types.NamespacedName{
   104  			Name:      values[0],
   105  			Namespace: "",
   106  		}
   107  	}
   108  	if len(values) >= 2 {
   109  		return types.NamespacedName{
   110  			Name:      values[1],
   111  			Namespace: values[0],
   112  		}
   113  	}
   114  	return types.NamespacedName{}
   115  }