github.com/argoproj-labs/argocd-operator@v0.10.0/controllers/argocd/argocd_controller.go (about) 1 /* 2 Copyright 2019, 2021. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package argocd 18 19 import ( 20 "context" 21 "fmt" 22 "time" 23 24 "github.com/prometheus/client_golang/prometheus" 25 26 argoproj "github.com/argoproj-labs/argocd-operator/api/v1beta1" 27 28 corev1 "k8s.io/api/core/v1" 29 "k8s.io/apimachinery/pkg/api/errors" 30 "k8s.io/apimachinery/pkg/labels" 31 "k8s.io/apimachinery/pkg/runtime" 32 33 ctrl "sigs.k8s.io/controller-runtime" 34 "sigs.k8s.io/controller-runtime/pkg/client" 35 logr "sigs.k8s.io/controller-runtime/pkg/log" 36 "sigs.k8s.io/controller-runtime/pkg/reconcile" 37 ) 38 39 // blank assignment to verify that ReconcileArgoCD implements reconcile.Reconciler 40 var _ reconcile.Reconciler = &ReconcileArgoCD{} 41 42 // ArgoCDReconciler reconciles a ArgoCD object 43 // TODO(upgrade): rename to ArgoCDRecoonciler 44 type ReconcileArgoCD struct { 45 client.Client 46 Scheme *runtime.Scheme 47 ManagedNamespaces *corev1.NamespaceList 48 // Stores a list of ApplicationSourceNamespaces as keys 49 ManagedSourceNamespaces map[string]string 50 // Stores a list of ApplicationSetSourceNamespaces as keys 51 ManagedApplicationSetSourceNamespaces map[string]string 52 // Stores label selector used to reconcile a subset of ArgoCD 53 LabelSelector string 54 } 55 56 var log = logr.Log.WithName("controller_argocd") 57 58 // Map to keep track of running Argo CD instances using their namespaces as key and phase as value 59 // This map will be used for the performance metrics purposes 60 // Important note: This assumes that each instance only contains one Argo CD instance 61 // as, having multiple Argo CD instances in the same namespace is considered an anti-pattern 62 var ActiveInstanceMap = make(map[string]string) 63 64 //+kubebuilder:rbac:groups=rbac.authorization.k8s.io,resources=clusterroles;clusterrolebindings,verbs=* 65 //+kubebuilder:rbac:groups="",resources=configmaps;endpoints;events;persistentvolumeclaims;pods;namespaces;secrets;serviceaccounts;services;services/finalizers,verbs=* 66 //+kubebuilder:rbac:groups=apps.openshift.io,resources=deploymentconfigs,verbs=* 67 //+kubebuilder:rbac:groups=apps,resources=deployments;replicasets;daemonsets;statefulsets,verbs=* 68 //+kubebuilder:rbac:groups=apps,resourceNames=argocd-operator,resources=deployments/finalizers,verbs=update 69 //+kubebuilder:rbac:groups=argoproj.io,resources=argocds;argocds/finalizers;argocds/status,verbs=* 70 //+kubebuilder:rbac:groups=autoscaling,resources=horizontalpodautoscalers,verbs=* 71 //+kubebuilder:rbac:groups=batch,resources=cronjobs;jobs,verbs=* 72 //+kubebuilder:rbac:groups=config.openshift.io,resources=clusterversions,verbs=get;list;watch 73 //+kubebuilder:rbac:groups=networking.k8s.io,resources=ingresses,verbs=* 74 //+kubebuilder:rbac:groups=monitoring.coreos.com,resources=prometheuses;prometheusrules;servicemonitors,verbs=* 75 //+kubebuilder:rbac:groups=route.openshift.io,resources=routes;routes/custom-host,verbs=* 76 //+kubebuilder:rbac:groups=argoproj.io,resources=applications;appprojects,verbs=* 77 //+kubebuilder:rbac:groups=rbac.authorization.k8s.io,resources=*,verbs=* 78 //+kubebuilder:rbac:groups="",resources=pods;pods/log,verbs=get 79 //+kubebuilder:rbac:groups=template.openshift.io,resources=templates;templateinstances;templateconfigs,verbs=* 80 //+kubebuilder:rbac:groups="oauth.openshift.io",resources=oauthclients,verbs=get;list;watch;create;delete;patch;update 81 // +kubebuilder:rbac:groups=argoproj.io,resources=notificationsconfigurations;notificationsconfigurations/finalizers,verbs=* 82 83 // Reconcile is part of the main kubernetes reconciliation loop which aims to 84 // move the current state of the cluster closer to the desired state. 85 // the ArgoCD object against the actual cluster state, and then 86 // perform operations to make the cluster state reflect the state specified by 87 // the user. 88 // 89 // For more details, check Reconcile and its Result here: 90 // - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.9.2/pkg/reconcile 91 func (r *ReconcileArgoCD) Reconcile(ctx context.Context, request ctrl.Request) (ctrl.Result, error) { 92 93 reconcileStartTS := time.Now() 94 defer func() { 95 ReconcileTime.WithLabelValues(request.Namespace).Observe(time.Since(reconcileStartTS).Seconds()) 96 }() 97 98 reqLogger := logr.FromContext(ctx, "namespace", request.Namespace, "name", request.Name) 99 reqLogger.Info("Reconciling ArgoCD") 100 101 argocd := &argoproj.ArgoCD{} 102 err := r.Client.Get(ctx, request.NamespacedName, argocd) 103 if err != nil { 104 if errors.IsNotFound(err) { 105 // Request object not found, could have been deleted after reconcile request. 106 // Owned objects are automatically garbage collected. For additional cleanup logic use finalizers. 107 // Return and don't requeue 108 return reconcile.Result{}, nil 109 } 110 // Error reading the object - requeue the request. 111 return reconcile.Result{}, err 112 } 113 114 // Fetch labelSelector from r.LabelSelector (command-line option) 115 labelSelector, err := labels.Parse(r.LabelSelector) 116 if err != nil { 117 reqLogger.Info(fmt.Sprintf("error parsing the labelSelector '%s'.", labelSelector)) 118 return reconcile.Result{}, err 119 } 120 // Match the value of labelSelector from ReconcileArgoCD to labels from the argocd instance 121 if !labelSelector.Matches(labels.Set(argocd.Labels)) { 122 reqLogger.Info(fmt.Sprintf("the ArgoCD instance '%s' does not match the label selector '%s' and skipping for reconciliation", request.NamespacedName, r.LabelSelector)) 123 return reconcile.Result{}, fmt.Errorf("error: failed to reconcile ArgoCD instance: '%s'", request.NamespacedName) 124 } 125 126 newPhase := argocd.Status.Phase 127 // If we discover a new Argo CD instance in a previously un-seen namespace 128 // we add it to the map and increment active instance count by phase 129 // as well as total active instance count 130 if _, ok := ActiveInstanceMap[request.Namespace]; !ok { 131 if newPhase != "" { 132 ActiveInstanceMap[request.Namespace] = newPhase 133 ActiveInstancesByPhase.WithLabelValues(newPhase).Inc() 134 ActiveInstancesTotal.Inc() 135 } 136 } else { 137 // If we discover an existing instance's phase has changed since we last saw it 138 // increment instance count with new phase and decrement instance count with old phase 139 // update the phase in corresponding map entry 140 // total instance count remains the same 141 if oldPhase := ActiveInstanceMap[argocd.Namespace]; oldPhase != newPhase { 142 ActiveInstanceMap[argocd.Namespace] = newPhase 143 ActiveInstancesByPhase.WithLabelValues(newPhase).Inc() 144 ActiveInstancesByPhase.WithLabelValues(oldPhase).Dec() 145 } 146 } 147 148 ActiveInstanceReconciliationCount.WithLabelValues(argocd.Namespace).Inc() 149 150 if argocd.GetDeletionTimestamp() != nil { 151 152 // Argo CD instance marked for deletion; remove entry from activeInstances map and decrement active instance count 153 // by phase as well as total 154 delete(ActiveInstanceMap, argocd.Namespace) 155 ActiveInstancesByPhase.WithLabelValues(newPhase).Dec() 156 ActiveInstancesTotal.Dec() 157 ActiveInstanceReconciliationCount.DeleteLabelValues(argocd.Namespace) 158 ReconcileTime.DeletePartialMatch(prometheus.Labels{"namespace": argocd.Namespace}) 159 160 if argocd.IsDeletionFinalizerPresent() { 161 if err := r.deleteClusterResources(argocd); err != nil { 162 return reconcile.Result{}, fmt.Errorf("failed to delete ClusterResources: %w", err) 163 } 164 165 if isRemoveManagedByLabelOnArgoCDDeletion() { 166 if err := r.removeManagedByLabelFromNamespaces(argocd.Namespace); err != nil { 167 return reconcile.Result{}, fmt.Errorf("failed to remove label from namespace[%v], error: %w", argocd.Namespace, err) 168 } 169 } 170 171 if err := r.removeUnmanagedSourceNamespaceResources(argocd); err != nil { 172 return reconcile.Result{}, fmt.Errorf("failed to remove resources from sourceNamespaces, error: %w", err) 173 } 174 175 if err := r.removeUnmanagedApplicationSetSourceNamespaceResources(argocd); err != nil { 176 return reconcile.Result{}, fmt.Errorf("failed to remove resources from applicationSetSourceNamespaces, error: %w", err) 177 } 178 179 if err := r.removeDeletionFinalizer(argocd); err != nil { 180 return reconcile.Result{}, err 181 } 182 183 // remove namespace of deleted Argo CD instance from deprecationEventEmissionTracker (if exists) so that if another instance 184 // is created in the same namespace in the future, that instance is appropriately tracked 185 delete(DeprecationEventEmissionTracker, argocd.Namespace) 186 } 187 return reconcile.Result{}, nil 188 } 189 190 if !argocd.IsDeletionFinalizerPresent() { 191 if err := r.addDeletionFinalizer(argocd); err != nil { 192 return reconcile.Result{}, err 193 } 194 } 195 196 // get the latest version of argocd instance before reconciling 197 if err = r.Client.Get(ctx, request.NamespacedName, argocd); err != nil { 198 return reconcile.Result{}, err 199 } 200 201 if err = r.setManagedNamespaces(argocd); err != nil { 202 return reconcile.Result{}, err 203 } 204 205 if err = r.setManagedSourceNamespaces(argocd); err != nil { 206 return reconcile.Result{}, err 207 } 208 209 if err = r.setManagedApplicationSetSourceNamespaces(argocd); err != nil { 210 return reconcile.Result{}, err 211 } 212 213 if err := r.reconcileResources(argocd); err != nil { 214 // Error reconciling ArgoCD sub-resources - requeue the request. 215 return reconcile.Result{}, err 216 } 217 218 // Return and don't requeue 219 return reconcile.Result{}, nil 220 } 221 222 // SetupWithManager sets up the controller with the Manager. 223 func (r *ReconcileArgoCD) SetupWithManager(mgr ctrl.Manager) error { 224 bldr := ctrl.NewControllerManagedBy(mgr) 225 r.setResourceWatches(bldr, r.clusterResourceMapper, r.tlsSecretMapper, r.namespaceResourceMapper, r.clusterSecretResourceMapper, r.applicationSetSCMTLSConfigMapMapper) 226 return bldr.Complete(r) 227 }