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 }