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

     1  package operators
     2  
     3  import (
     4  	"context"
     5  	"reflect"
     6  
     7  	"github.com/go-logr/logr"
     8  	apierrors "k8s.io/apimachinery/pkg/api/errors"
     9  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    10  	"k8s.io/apimachinery/pkg/runtime"
    11  	ctrl "sigs.k8s.io/controller-runtime"
    12  	"sigs.k8s.io/controller-runtime/pkg/builder"
    13  	"sigs.k8s.io/controller-runtime/pkg/client"
    14  	"sigs.k8s.io/controller-runtime/pkg/event"
    15  	"sigs.k8s.io/controller-runtime/pkg/handler"
    16  	"sigs.k8s.io/controller-runtime/pkg/predicate"
    17  	"sigs.k8s.io/controller-runtime/pkg/reconcile"
    18  
    19  	operatorsv1alpha1 "github.com/operator-framework/api/pkg/operators/v1alpha1"
    20  	operatorsv2 "github.com/operator-framework/api/pkg/operators/v2"
    21  	"github.com/operator-framework/operator-lifecycle-manager/pkg/lib/ownerutil"
    22  	"github.com/operator-framework/operator-lifecycle-manager/pkg/metrics"
    23  )
    24  
    25  // OperatorConditionGeneratorReconciler reconciles a ClusterServiceVersion object and creates an OperatorCondition.
    26  type OperatorConditionGeneratorReconciler struct {
    27  	Client client.Client
    28  	log    logr.Logger
    29  }
    30  
    31  // +kubebuilder:rbac:groups=operators.coreos.com,resources=operatorconditions,verbs=get;list;update;patch;delete
    32  // +kubebuilder:rbac:groups=operators.coreos.com,resources=operatorconditions/status,verbs=update;patch
    33  
    34  // SetupWithManager adds the OperatorCondition Reconciler reconciler to the given controller manager.
    35  func (r *OperatorConditionGeneratorReconciler) SetupWithManager(mgr ctrl.Manager) error {
    36  	handler := handler.EnqueueRequestForOwner(mgr.GetScheme(), mgr.GetRESTMapper(), &operatorsv1alpha1.ClusterServiceVersion{}, handler.OnlyControllerOwner())
    37  	p := predicate.Funcs{
    38  		CreateFunc: func(e event.CreateEvent) bool {
    39  			if _, ok := e.Object.GetLabels()[operatorsv1alpha1.CopiedLabelKey]; ok {
    40  				return false
    41  			}
    42  			return true
    43  		},
    44  		DeleteFunc: func(e event.DeleteEvent) bool {
    45  			if _, ok := e.Object.GetLabels()[operatorsv1alpha1.CopiedLabelKey]; ok {
    46  				return false
    47  			}
    48  			return true
    49  		},
    50  		UpdateFunc: func(e event.UpdateEvent) bool {
    51  			if _, ok := e.ObjectOld.GetLabels()[operatorsv1alpha1.CopiedLabelKey]; ok {
    52  				return false
    53  			}
    54  			return true
    55  		},
    56  		GenericFunc: func(e event.GenericEvent) bool {
    57  			if _, ok := e.Object.GetLabels()[operatorsv1alpha1.CopiedLabelKey]; ok {
    58  				return false
    59  			}
    60  			return true
    61  		},
    62  	}
    63  
    64  	return ctrl.NewControllerManagedBy(mgr).
    65  		Named("operator-condition-generator").
    66  		For(&operatorsv1alpha1.ClusterServiceVersion{}, builder.WithPredicates(p)).
    67  		Watches(&operatorsv2.OperatorCondition{}, handler).
    68  		Complete(r)
    69  }
    70  
    71  // NewOperatorConditionGeneratorReconciler constructs and returns an OperatorConditionGeneratorReconciler.
    72  // As a side effect, the given scheme has operator discovery types added to it.
    73  func NewOperatorConditionGeneratorReconciler(cli client.Client, log logr.Logger, scheme *runtime.Scheme) (*OperatorConditionGeneratorReconciler, error) {
    74  	// Add watched types to scheme.
    75  	if err := AddToScheme(scheme); err != nil {
    76  		return nil, err
    77  	}
    78  
    79  	return &OperatorConditionGeneratorReconciler{
    80  		Client: cli,
    81  		log:    log,
    82  	}, nil
    83  }
    84  
    85  // Implement reconcile.Reconciler so the controller can reconcile objects
    86  var _ reconcile.Reconciler = &OperatorConditionGeneratorReconciler{}
    87  
    88  func (r *OperatorConditionGeneratorReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
    89  	// Set up a convenient log object so we don't have to type request over and over again
    90  	log := r.log.WithValues("request", req).V(1)
    91  	metrics.EmitOperatorConditionGeneratorReconcile(req.Namespace, req.Name)
    92  
    93  	in := &operatorsv1alpha1.ClusterServiceVersion{}
    94  	if err := r.Client.Get(ctx, req.NamespacedName, in); err != nil {
    95  		log.Info("Unable to find ClusterServiceVersion")
    96  		return ctrl.Result{}, client.IgnoreNotFound(err)
    97  	}
    98  
    99  	operatorCondition := &operatorsv2.OperatorCondition{
   100  		ObjectMeta: metav1.ObjectMeta{
   101  			// For now, only generate an OperatorCondition with the same name as the csv.
   102  			Name:      in.GetName(),
   103  			Namespace: in.GetNamespace(),
   104  		},
   105  		Spec: operatorsv2.OperatorConditionSpec{
   106  			ServiceAccounts: getServiceAccountNames(*in),
   107  			Deployments:     getDeploymentNames(*in),
   108  		},
   109  	}
   110  	ownerutil.AddOwner(operatorCondition, in, false, true)
   111  
   112  	if err := r.ensureOperatorCondition(*operatorCondition); err != nil {
   113  		log.Info("Error ensuring  OperatorCondition")
   114  		return ctrl.Result{}, err
   115  	}
   116  
   117  	return ctrl.Result{}, nil
   118  }
   119  
   120  func getServiceAccountNames(csv operatorsv1alpha1.ClusterServiceVersion) []string {
   121  	result := []string{}
   122  	for _, clusterPermissions := range csv.Spec.InstallStrategy.StrategySpec.ClusterPermissions {
   123  		if clusterPermissions.ServiceAccountName != "" {
   124  			result = append(result, clusterPermissions.ServiceAccountName)
   125  		}
   126  	}
   127  
   128  	for _, permissions := range csv.Spec.InstallStrategy.StrategySpec.Permissions {
   129  		if permissions.ServiceAccountName != "" {
   130  			result = append(result, permissions.ServiceAccountName)
   131  		}
   132  	}
   133  
   134  	if len(result) == 0 {
   135  		result = []string{"default"}
   136  	}
   137  
   138  	return result
   139  }
   140  
   141  func getDeploymentNames(csv operatorsv1alpha1.ClusterServiceVersion) []string {
   142  	result := []string{}
   143  	for _, deploymentSpec := range csv.Spec.InstallStrategy.StrategySpec.DeploymentSpecs {
   144  		if deploymentSpec.Name != "" {
   145  			result = append(result, deploymentSpec.Name)
   146  		}
   147  	}
   148  
   149  	return result
   150  }
   151  
   152  func (r *OperatorConditionGeneratorReconciler) ensureOperatorCondition(operatorCondition operatorsv2.OperatorCondition) error {
   153  	existingOperatorCondition := &operatorsv2.OperatorCondition{}
   154  	err := r.Client.Get(context.TODO(), client.ObjectKey{Name: operatorCondition.GetName(), Namespace: operatorCondition.GetNamespace()}, existingOperatorCondition)
   155  	if err != nil {
   156  		if !apierrors.IsNotFound(err) {
   157  			return err
   158  		}
   159  		return r.Client.Create(context.TODO(), &operatorCondition)
   160  	}
   161  
   162  	if reflect.DeepEqual(operatorCondition.OwnerReferences, existingOperatorCondition.OwnerReferences) &&
   163  		reflect.DeepEqual(operatorCondition.Spec.Deployments, existingOperatorCondition.Spec.Deployments) &&
   164  		reflect.DeepEqual(operatorCondition.Spec.ServiceAccounts, existingOperatorCondition.Spec.ServiceAccounts) {
   165  		r.log.V(5).Info("Existing OperatorCondition does not need to be updated")
   166  		return nil
   167  	}
   168  	r.log.V(5).Info("Existing OperatorCondition needs to be updated")
   169  	existingOperatorCondition.OwnerReferences = operatorCondition.OwnerReferences
   170  	existingOperatorCondition.Spec.Deployments = operatorCondition.Spec.Deployments
   171  	existingOperatorCondition.Spec.ServiceAccounts = operatorCondition.Spec.ServiceAccounts
   172  	return r.Client.Update(context.TODO(), existingOperatorCondition)
   173  }