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 }