github.com/operator-framework/operator-lifecycle-manager@v0.30.0/pkg/controller/operators/operator_controller.go (about)

     1  package operators
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  
     7  	"github.com/go-logr/logr"
     8  	appsv1 "k8s.io/api/apps/v1"
     9  	corev1 "k8s.io/api/core/v1"
    10  	rbacv1 "k8s.io/api/rbac/v1"
    11  	apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
    12  	"k8s.io/apimachinery/pkg/api/equality"
    13  	apierrors "k8s.io/apimachinery/pkg/api/errors"
    14  	"k8s.io/apimachinery/pkg/api/meta"
    15  	"k8s.io/apimachinery/pkg/labels"
    16  	"k8s.io/apimachinery/pkg/runtime"
    17  	kscheme "k8s.io/client-go/kubernetes/scheme"
    18  	apiregistrationv1 "k8s.io/kube-aggregator/pkg/apis/apiregistration/v1"
    19  	ctrl "sigs.k8s.io/controller-runtime"
    20  	"sigs.k8s.io/controller-runtime/pkg/builder"
    21  	"sigs.k8s.io/controller-runtime/pkg/client"
    22  	"sigs.k8s.io/controller-runtime/pkg/handler"
    23  	"sigs.k8s.io/controller-runtime/pkg/reconcile"
    24  
    25  	operatorsv1 "github.com/operator-framework/api/pkg/operators/v1"
    26  	operatorsv1alpha1 "github.com/operator-framework/api/pkg/operators/v1alpha1"
    27  	operatorsv2 "github.com/operator-framework/api/pkg/operators/v2"
    28  	"github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators/decorators"
    29  	"github.com/operator-framework/operator-lifecycle-manager/pkg/metrics"
    30  )
    31  
    32  var (
    33  	localSchemeBuilder = runtime.NewSchemeBuilder(
    34  		kscheme.AddToScheme,
    35  		apiextensionsv1.AddToScheme,
    36  		apiregistrationv1.AddToScheme,
    37  		operatorsv1alpha1.AddToScheme,
    38  		operatorsv1.AddToScheme,
    39  		operatorsv2.AddToScheme,
    40  	)
    41  
    42  	// AddToScheme adds all types necessary for the controller to operate.
    43  	AddToScheme = localSchemeBuilder.AddToScheme
    44  )
    45  
    46  // OperatorReconciler reconciles a Operator object.
    47  type OperatorReconciler struct {
    48  	client.Client
    49  
    50  	log     logr.Logger
    51  	factory decorators.OperatorFactory
    52  }
    53  
    54  // +kubebuilder:rbac:groups=operators.coreos.com,resources=operators,verbs=create;update;patch;delete
    55  // +kubebuilder:rbac:groups=operators.coreos.com,resources=operators/status,verbs=update;patch
    56  // +kubebuilder:rbac:groups=*,resources=*,verbs=get;list;watch
    57  
    58  // SetupWithManager adds the operator reconciler to the given controller manager.
    59  func (r *OperatorReconciler) SetupWithManager(mgr ctrl.Manager) error {
    60  	// Trigger operator events from the events of their compoenents.
    61  	enqueueOperator := handler.EnqueueRequestsFromMapFunc(r.mapComponentRequests)
    62  	// Note: If we want to support resources composed of custom resources, we need to figure out how
    63  	// to dynamically add resource types to watch.
    64  	return ctrl.NewControllerManagedBy(mgr).
    65  		For(&operatorsv1.Operator{}).
    66  		Watches(&appsv1.Deployment{}, enqueueOperator).
    67  		Watches(&corev1.Namespace{}, enqueueOperator).
    68  		Watches(&apiextensionsv1.CustomResourceDefinition{}, enqueueOperator).
    69  		Watches(&apiregistrationv1.APIService{}, enqueueOperator).
    70  		Watches(&operatorsv1alpha1.Subscription{}, enqueueOperator).
    71  		Watches(&operatorsv1alpha1.InstallPlan{}, enqueueOperator).
    72  		Watches(&operatorsv1alpha1.ClusterServiceVersion{}, enqueueOperator).
    73  		Watches(&operatorsv2.OperatorCondition{}, enqueueOperator).
    74  		// Metadata is sufficient to build component refs for
    75  		// GVKs that don't have a .status.conditions field.
    76  		Watches(&corev1.ServiceAccount{}, enqueueOperator, builder.OnlyMetadata).
    77  		Watches(&corev1.Secret{}, enqueueOperator, builder.OnlyMetadata).
    78  		Watches(&corev1.ConfigMap{}, enqueueOperator, builder.OnlyMetadata).
    79  		Watches(&rbacv1.Role{}, enqueueOperator, builder.OnlyMetadata).
    80  		Watches(&rbacv1.RoleBinding{}, enqueueOperator, builder.OnlyMetadata).
    81  		Watches(&rbacv1.ClusterRole{}, enqueueOperator, builder.OnlyMetadata).
    82  		Watches(&rbacv1.ClusterRoleBinding{}, enqueueOperator, builder.OnlyMetadata).
    83  		// TODO(njhale): Add WebhookConfigurations
    84  		Complete(r)
    85  }
    86  
    87  // NewOperatorReconciler constructs and returns an OperatorReconciler.
    88  // As a side effect, the given scheme has operator discovery types added to it.
    89  func NewOperatorReconciler(cli client.Client, log logr.Logger, scheme *runtime.Scheme) (*OperatorReconciler, error) {
    90  	// Add watched types to scheme.
    91  	if err := AddToScheme(scheme); err != nil {
    92  		return nil, err
    93  	}
    94  
    95  	factory, err := decorators.NewSchemedOperatorFactory(scheme)
    96  	if err != nil {
    97  		return nil, err
    98  	}
    99  
   100  	return &OperatorReconciler{
   101  		Client: cli,
   102  
   103  		log:     log,
   104  		factory: factory,
   105  	}, nil
   106  }
   107  
   108  // Implement reconcile.Reconciler so the controller can reconcile objects
   109  var _ reconcile.Reconciler = &OperatorReconciler{}
   110  
   111  func (r *OperatorReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
   112  	// Set up a convenient log object so we don't have to type request over and over again
   113  	log := r.log.WithValues("request", req)
   114  	log.V(1).Info("reconciling operator")
   115  	metrics.EmitOperatorReconcile(req.Namespace, req.Name)
   116  
   117  	// Get the Operator
   118  	create := false
   119  	name := req.NamespacedName.Name
   120  	in := &operatorsv1.Operator{}
   121  	if err := r.Get(ctx, req.NamespacedName, in); err != nil {
   122  		if apierrors.IsNotFound(err) {
   123  			// If the Operator instance is not found, we're likely reconciling because
   124  			// of a DELETE event. Only recreate the Operator if any of its components
   125  			// still exist.
   126  			if exists, err := r.hasExistingComponents(ctx, name); err != nil || !exists {
   127  				return reconcile.Result{}, err
   128  			}
   129  			create = true
   130  			in.SetName(name)
   131  		} else {
   132  			log.Error(err, "Error requesting Operator")
   133  			return reconcile.Result{Requeue: true}, nil
   134  		}
   135  	}
   136  
   137  	// Wrap with convenience decorator
   138  	operator, err := r.factory.NewOperator(in)
   139  	if err != nil {
   140  		log.Error(err, "Could not wrap Operator with convenience decorator")
   141  		return reconcile.Result{Requeue: true}, nil
   142  	}
   143  
   144  	if err = r.updateComponents(ctx, operator); err != nil {
   145  		log.Error(err, "Could not update components")
   146  		return reconcile.Result{Requeue: true}, nil
   147  	}
   148  
   149  	if create {
   150  		if err := r.Create(context.Background(), operator.Operator); err != nil && !apierrors.IsAlreadyExists(err) {
   151  			r.log.Error(err, "Could not create Operator", "operator", name)
   152  			return ctrl.Result{Requeue: true}, nil
   153  		}
   154  	} else {
   155  		if !equality.Semantic.DeepEqual(in.Status, operator.Operator.Status) {
   156  			if err := r.Status().Update(ctx, operator.Operator); err != nil {
   157  				log.Error(err, "Could not update Operator status")
   158  				return ctrl.Result{Requeue: true}, nil
   159  			}
   160  		}
   161  	}
   162  
   163  	return ctrl.Result{}, nil
   164  }
   165  
   166  func (r *OperatorReconciler) updateComponents(ctx context.Context, operator *decorators.Operator) error {
   167  	selector, err := operator.ComponentSelector()
   168  	if err != nil {
   169  		return err
   170  	}
   171  
   172  	components, err := r.listComponents(ctx, selector)
   173  	if err != nil {
   174  		return err
   175  	}
   176  
   177  	return operator.SetComponents(components...)
   178  }
   179  
   180  func (r *OperatorReconciler) listComponents(ctx context.Context, selector labels.Selector) ([]runtime.Object, error) {
   181  	// Note: We need to figure out how to dynamically add new list types here (or some equivalent) in
   182  	// order to support operators composed of custom resources.
   183  	componentLists := componentLists()
   184  
   185  	opt := client.MatchingLabelsSelector{Selector: selector}
   186  	for _, list := range componentLists {
   187  		cList, ok := list.(client.ObjectList)
   188  		if !ok {
   189  			return nil, fmt.Errorf("unable to typecast runtime.Object to client.ObjectList")
   190  		}
   191  		if err := r.List(ctx, cList, opt); err != nil {
   192  			return nil, err
   193  		}
   194  	}
   195  
   196  	return componentLists, nil
   197  }
   198  
   199  func (r *OperatorReconciler) hasExistingComponents(ctx context.Context, name string) (bool, error) {
   200  	op := &operatorsv1.Operator{}
   201  	op.SetName(name)
   202  	operator := decorators.Operator{Operator: op}
   203  
   204  	selector, err := operator.ComponentSelector()
   205  	if err != nil {
   206  		return false, err
   207  	}
   208  
   209  	components, err := r.listComponents(ctx, selector)
   210  	if err != nil {
   211  		return false, err
   212  	}
   213  
   214  	for _, list := range components {
   215  		items, err := meta.ExtractList(list)
   216  		if err != nil {
   217  			return false, fmt.Errorf("unable to extract list from runtime.Object")
   218  		}
   219  		if len(items) > 0 {
   220  			return true, nil
   221  		}
   222  	}
   223  	return false, nil
   224  }
   225  
   226  func (r *OperatorReconciler) mapComponentRequests(_ context.Context, obj client.Object) []reconcile.Request {
   227  	var requests []reconcile.Request
   228  	if obj == nil {
   229  		return requests
   230  	}
   231  
   232  	labels := decorators.OperatorNames(obj.GetLabels())
   233  	for _, name := range labels {
   234  		requests = append(requests, reconcile.Request{NamespacedName: name})
   235  	}
   236  
   237  	return requests
   238  }