github.com/argoproj-labs/argocd-operator@v0.10.0/controllers/argocd/notifications.go (about)

     1  package argocd
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"reflect"
     7  	"time"
     8  
     9  	monitoringv1 "github.com/coreos/prometheus-operator/pkg/apis/monitoring/v1"
    10  	appsv1 "k8s.io/api/apps/v1"
    11  	corev1 "k8s.io/api/core/v1"
    12  	rbacv1 "k8s.io/api/rbac/v1"
    13  	"k8s.io/apimachinery/pkg/api/errors"
    14  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    15  	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    16  	"k8s.io/apimachinery/pkg/types"
    17  	"k8s.io/apimachinery/pkg/util/intstr"
    18  	"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
    19  
    20  	"github.com/argoproj-labs/argocd-operator/api/v1alpha1"
    21  	argoproj "github.com/argoproj-labs/argocd-operator/api/v1beta1"
    22  	"github.com/argoproj-labs/argocd-operator/common"
    23  	"github.com/argoproj-labs/argocd-operator/controllers/argoutil"
    24  )
    25  
    26  const (
    27  	DefaultNotificationsConfigurationInstanceName = "default-notifications-configuration"
    28  )
    29  
    30  func (r *ReconcileArgoCD) reconcileNotificationsController(cr *argoproj.ArgoCD) error {
    31  
    32  	log.Info("reconciling notifications serviceaccount")
    33  	sa, err := r.reconcileNotificationsServiceAccount(cr)
    34  	if err != nil {
    35  		return err
    36  	}
    37  
    38  	log.Info("reconciling notifications role")
    39  	role, err := r.reconcileNotificationsRole(cr)
    40  	if err != nil {
    41  		return err
    42  	}
    43  
    44  	log.Info("reconciling notifications role binding")
    45  	if err := r.reconcileNotificationsRoleBinding(cr, role, sa); err != nil {
    46  		return err
    47  	}
    48  
    49  	log.Info("reconciling NotificationsConfiguration")
    50  	if err := r.reconcileNotificationsConfigurationCR(cr); err != nil {
    51  		return err
    52  	}
    53  
    54  	log.Info("reconciling notifications secret")
    55  	if err := r.reconcileNotificationsSecret(cr); err != nil {
    56  		return err
    57  	}
    58  
    59  	log.Info("reconciling notifications deployment")
    60  	if err := r.reconcileNotificationsDeployment(cr, sa); err != nil {
    61  		return err
    62  	}
    63  
    64  	log.Info("reconciling notifications metrics service")
    65  	if err := r.reconcileNotificationsMetricsService(cr); err != nil {
    66  		return err
    67  	}
    68  
    69  	if prometheusAPIFound {
    70  		log.Info("reconciling notifications metrics service monitor")
    71  		if err := r.reconcileNotificationsServiceMonitor(cr); err != nil {
    72  			return err
    73  		}
    74  	}
    75  
    76  	return nil
    77  }
    78  
    79  func (r *ReconcileArgoCD) reconcileNotificationsConfigurationCR(cr *argoproj.ArgoCD) error {
    80  
    81  	defaultNotificationsConfigurationCR := &v1alpha1.NotificationsConfiguration{
    82  		ObjectMeta: metav1.ObjectMeta{
    83  			Name:      DefaultNotificationsConfigurationInstanceName,
    84  			Namespace: cr.Namespace,
    85  		},
    86  		Spec: v1alpha1.NotificationsConfigurationSpec{
    87  			Triggers:  getDefaultNotificationsTriggers(),
    88  			Templates: getDefaultNotificationsTemplates(),
    89  		},
    90  	}
    91  
    92  	if !cr.Spec.Notifications.Enabled {
    93  		log.Info("Deleting NotificationsConfiguration as notifications is disabled")
    94  		return r.Client.Delete(context.TODO(), defaultNotificationsConfigurationCR)
    95  	}
    96  
    97  	if err := argoutil.FetchObject(r.Client, cr.Namespace, DefaultNotificationsConfigurationInstanceName,
    98  		defaultNotificationsConfigurationCR); err != nil {
    99  
   100  		if !errors.IsNotFound(err) {
   101  			return fmt.Errorf("failed to get the NotificationsConfiguration associated with %s : %s",
   102  				cr.Name, err)
   103  		}
   104  
   105  		// NotificationsConfiguration doesn't exist and shouldn't, nothing to do here
   106  		if !cr.Spec.Notifications.Enabled {
   107  			return nil
   108  		}
   109  
   110  		err := r.Client.Create(context.TODO(), defaultNotificationsConfigurationCR)
   111  		if err != nil {
   112  			return err
   113  		}
   114  	}
   115  
   116  	return nil
   117  }
   118  
   119  // The code to create/delete notifications resources is written within the reconciliation logic itself. However, these functions must be called
   120  // in the right order depending on whether resources are getting created or deleted. During creation we must create the role and sa first.
   121  // RoleBinding and deployment are dependent on these resouces. During deletion the order is reversed.
   122  // Deployment and RoleBinding must be deleted before the role and sa. deleteNotificationsResources will only be called during
   123  // delete events, so we don't need to worry about duplicate, recurring reconciliation calls
   124  func (r *ReconcileArgoCD) deleteNotificationsResources(cr *argoproj.ArgoCD) error {
   125  
   126  	sa := &corev1.ServiceAccount{}
   127  	role := &rbacv1.Role{}
   128  
   129  	if err := argoutil.FetchObject(r.Client, cr.Namespace, fmt.Sprintf("%s-%s", cr.Name, common.ArgoCDNotificationsControllerComponent), sa); err != nil {
   130  		if !errors.IsNotFound(err) {
   131  			return err
   132  		}
   133  	}
   134  	if err := argoutil.FetchObject(r.Client, cr.Namespace, fmt.Sprintf("%s-%s", cr.Name, common.ArgoCDNotificationsControllerComponent), role); err != nil {
   135  		if !errors.IsNotFound(err) {
   136  			return err
   137  		}
   138  	}
   139  
   140  	log.Info("reconciling notifications deployment")
   141  	if err := r.reconcileNotificationsDeployment(cr, sa); err != nil {
   142  		return err
   143  	}
   144  
   145  	log.Info("reconciling notifications service")
   146  	if err := r.reconcileNotificationsMetricsService(cr); err != nil {
   147  		return err
   148  	}
   149  
   150  	log.Info("reconciling notifications service monitor")
   151  	if err := r.reconcileNotificationsServiceMonitor(cr); err != nil {
   152  		return err
   153  	}
   154  
   155  	log.Info("reconciling notifications secret")
   156  	if err := r.reconcileNotificationsSecret(cr); err != nil {
   157  		return err
   158  	}
   159  
   160  	log.Info("reconciling notifications role binding")
   161  	if err := r.reconcileNotificationsRoleBinding(cr, role, sa); err != nil {
   162  		return err
   163  	}
   164  
   165  	log.Info("reconciling notifications role")
   166  	_, err := r.reconcileNotificationsRole(cr)
   167  	if err != nil {
   168  		return err
   169  	}
   170  
   171  	log.Info("reconciling notifications serviceaccount")
   172  	_, err = r.reconcileNotificationsServiceAccount(cr)
   173  	if err != nil {
   174  		return err
   175  	}
   176  
   177  	log.Info("reconciling notificationsconfiguration")
   178  	err = r.reconcileNotificationsConfigurationCR(cr)
   179  	if err != nil {
   180  		return err
   181  	}
   182  
   183  	return nil
   184  }
   185  
   186  func (r *ReconcileArgoCD) reconcileNotificationsServiceAccount(cr *argoproj.ArgoCD) (*corev1.ServiceAccount, error) {
   187  
   188  	sa := newServiceAccountWithName(common.ArgoCDNotificationsControllerComponent, cr)
   189  
   190  	if err := argoutil.FetchObject(r.Client, cr.Namespace, sa.Name, sa); err != nil {
   191  		if !errors.IsNotFound(err) {
   192  			return nil, fmt.Errorf("failed to get the serviceAccount associated with %s : %s", sa.Name, err)
   193  		}
   194  
   195  		// SA doesn't exist and shouldn't, nothing to do here
   196  		if !cr.Spec.Notifications.Enabled {
   197  			return nil, nil
   198  		}
   199  
   200  		// SA doesn't exist but should, so it should be created
   201  		if err := controllerutil.SetControllerReference(cr, sa, r.Scheme); err != nil {
   202  			return nil, err
   203  		}
   204  
   205  		log.Info(fmt.Sprintf("Creating serviceaccount %s", sa.Name))
   206  		err := r.Client.Create(context.TODO(), sa)
   207  		if err != nil {
   208  			return nil, err
   209  		}
   210  	}
   211  
   212  	// SA exists but shouldn't, so it should be deleted
   213  	if !cr.Spec.Notifications.Enabled {
   214  		log.Info(fmt.Sprintf("Deleting serviceaccount %s as notifications is disabled", sa.Name))
   215  		return nil, r.Client.Delete(context.TODO(), sa)
   216  	}
   217  
   218  	return sa, nil
   219  }
   220  
   221  func (r *ReconcileArgoCD) reconcileNotificationsRole(cr *argoproj.ArgoCD) (*rbacv1.Role, error) {
   222  
   223  	policyRules := policyRuleForNotificationsController()
   224  	desiredRole := newRole(common.ArgoCDNotificationsControllerComponent, policyRules, cr)
   225  
   226  	existingRole := &rbacv1.Role{}
   227  	if err := argoutil.FetchObject(r.Client, cr.Namespace, desiredRole.Name, existingRole); err != nil {
   228  		if !errors.IsNotFound(err) {
   229  			return nil, fmt.Errorf("failed to get the role associated with %s : %s", desiredRole.Name, err)
   230  		}
   231  
   232  		// role does not exist and shouldn't, nothing to do here
   233  		if !cr.Spec.Notifications.Enabled {
   234  			return nil, nil
   235  		}
   236  
   237  		// role does not exist but should, so it should be created
   238  		if err := controllerutil.SetControllerReference(cr, desiredRole, r.Scheme); err != nil {
   239  			return nil, err
   240  		}
   241  
   242  		log.Info(fmt.Sprintf("Creating role %s", desiredRole.Name))
   243  		err := r.Client.Create(context.TODO(), desiredRole)
   244  		if err != nil {
   245  			return nil, err
   246  		}
   247  		return desiredRole, nil
   248  	}
   249  
   250  	// role exists but shouldn't, so it should be deleted
   251  	if !cr.Spec.Notifications.Enabled {
   252  		log.Info(fmt.Sprintf("Deleting role %s as notifications is disabled", existingRole.Name))
   253  		return nil, r.Client.Delete(context.TODO(), existingRole)
   254  	}
   255  
   256  	// role exists and should. Reconcile role if changed
   257  	if !reflect.DeepEqual(existingRole.Rules, desiredRole.Rules) {
   258  		existingRole.Rules = desiredRole.Rules
   259  		if err := controllerutil.SetControllerReference(cr, existingRole, r.Scheme); err != nil {
   260  			return nil, err
   261  		}
   262  		return existingRole, r.Client.Update(context.TODO(), existingRole)
   263  	}
   264  
   265  	return desiredRole, nil
   266  }
   267  
   268  func (r *ReconcileArgoCD) reconcileNotificationsRoleBinding(cr *argoproj.ArgoCD, role *rbacv1.Role, sa *corev1.ServiceAccount) error {
   269  
   270  	desiredRoleBinding := newRoleBindingWithname(common.ArgoCDNotificationsControllerComponent, cr)
   271  	desiredRoleBinding.RoleRef = rbacv1.RoleRef{
   272  		APIGroup: rbacv1.GroupName,
   273  		Kind:     "Role",
   274  		Name:     role.Name,
   275  	}
   276  
   277  	desiredRoleBinding.Subjects = []rbacv1.Subject{
   278  		{
   279  			Kind:      rbacv1.ServiceAccountKind,
   280  			Name:      sa.Name,
   281  			Namespace: sa.Namespace,
   282  		},
   283  	}
   284  
   285  	// fetch existing rolebinding by name
   286  	existingRoleBinding := &rbacv1.RoleBinding{}
   287  	if err := r.Client.Get(context.TODO(), types.NamespacedName{Name: desiredRoleBinding.Name, Namespace: cr.Namespace}, existingRoleBinding); err != nil {
   288  		if !errors.IsNotFound(err) {
   289  			return fmt.Errorf("failed to get the rolebinding associated with %s : %s", desiredRoleBinding.Name, err)
   290  		}
   291  
   292  		// roleBinding does not exist and shouldn't, nothing to do here
   293  		if !cr.Spec.Notifications.Enabled {
   294  			return nil
   295  		}
   296  
   297  		// roleBinding does not exist but should, so it should be created
   298  		if err := controllerutil.SetControllerReference(cr, desiredRoleBinding, r.Scheme); err != nil {
   299  			return err
   300  		}
   301  
   302  		log.Info(fmt.Sprintf("Creating roleBinding %s", desiredRoleBinding.Name))
   303  		return r.Client.Create(context.TODO(), desiredRoleBinding)
   304  	}
   305  
   306  	// roleBinding exists but shouldn't, so it should be deleted
   307  	if !cr.Spec.Notifications.Enabled {
   308  		log.Info(fmt.Sprintf("Deleting roleBinding %s as notifications is disabled", existingRoleBinding.Name))
   309  		return r.Client.Delete(context.TODO(), existingRoleBinding)
   310  	}
   311  
   312  	// roleBinding exists and should. Reconcile roleBinding if changed
   313  	if !reflect.DeepEqual(existingRoleBinding.RoleRef, desiredRoleBinding.RoleRef) {
   314  		// if the RoleRef changes, delete the existing role binding and create a new one
   315  		if err := r.Client.Delete(context.TODO(), existingRoleBinding); err != nil {
   316  			return err
   317  		}
   318  	} else if !reflect.DeepEqual(existingRoleBinding.Subjects, desiredRoleBinding.Subjects) {
   319  		existingRoleBinding.Subjects = desiredRoleBinding.Subjects
   320  		if err := controllerutil.SetControllerReference(cr, existingRoleBinding, r.Scheme); err != nil {
   321  			return err
   322  		}
   323  		return r.Client.Update(context.TODO(), existingRoleBinding)
   324  	}
   325  
   326  	return nil
   327  }
   328  
   329  func (r *ReconcileArgoCD) reconcileNotificationsDeployment(cr *argoproj.ArgoCD, sa *corev1.ServiceAccount) error {
   330  
   331  	desiredDeployment := newDeploymentWithSuffix("notifications-controller", "controller", cr)
   332  
   333  	desiredDeployment.Spec.Strategy = appsv1.DeploymentStrategy{
   334  		Type: appsv1.RecreateDeploymentStrategyType,
   335  	}
   336  
   337  	if replicas := getArgoCDNotificationsControllerReplicas(cr); replicas != nil {
   338  		desiredDeployment.Spec.Replicas = replicas
   339  	}
   340  
   341  	notificationEnv := cr.Spec.Notifications.Env
   342  	// Let user specify their own environment first
   343  	notificationEnv = argoutil.EnvMerge(notificationEnv, proxyEnvVars(), false)
   344  
   345  	podSpec := &desiredDeployment.Spec.Template.Spec
   346  	podSpec.SecurityContext = &corev1.PodSecurityContext{
   347  		RunAsNonRoot: boolPtr(true),
   348  	}
   349  	AddSeccompProfileForOpenShift(r.Client, podSpec)
   350  	podSpec.ServiceAccountName = sa.ObjectMeta.Name
   351  	podSpec.Volumes = []corev1.Volume{
   352  		{
   353  			Name: "tls-certs",
   354  			VolumeSource: corev1.VolumeSource{
   355  				ConfigMap: &corev1.ConfigMapVolumeSource{
   356  					LocalObjectReference: corev1.LocalObjectReference{
   357  						Name: common.ArgoCDTLSCertsConfigMapName,
   358  					},
   359  				},
   360  			},
   361  		},
   362  		{
   363  			Name: "argocd-repo-server-tls",
   364  			VolumeSource: corev1.VolumeSource{
   365  				Secret: &corev1.SecretVolumeSource{
   366  					SecretName: common.ArgoCDRepoServerTLSSecretName,
   367  					Optional:   boolPtr(true),
   368  				},
   369  			},
   370  		},
   371  	}
   372  
   373  	podSpec.Containers = []corev1.Container{{
   374  		Command:         getNotificationsCommand(cr),
   375  		Image:           getArgoContainerImage(cr),
   376  		ImagePullPolicy: corev1.PullAlways,
   377  		Name:            common.ArgoCDNotificationsControllerComponent,
   378  		Env:             notificationEnv,
   379  		Resources:       getNotificationsResources(cr),
   380  		LivenessProbe: &corev1.Probe{
   381  			ProbeHandler: corev1.ProbeHandler{
   382  				TCPSocket: &corev1.TCPSocketAction{
   383  					Port: intstr.IntOrString{
   384  						IntVal: int32(9001),
   385  					},
   386  				},
   387  			},
   388  		},
   389  		SecurityContext: &corev1.SecurityContext{
   390  			AllowPrivilegeEscalation: boolPtr(false),
   391  			Capabilities: &corev1.Capabilities{
   392  				Drop: []corev1.Capability{
   393  					"ALL",
   394  				},
   395  			},
   396  		},
   397  		VolumeMounts: []corev1.VolumeMount{
   398  			{
   399  				Name:      "tls-certs",
   400  				MountPath: "/app/config/tls",
   401  			},
   402  			{
   403  				Name:      "argocd-repo-server-tls",
   404  				MountPath: "/app/config/reposerver/tls",
   405  			},
   406  		},
   407  		WorkingDir: "/app",
   408  	}}
   409  
   410  	// fetch existing deployment by name
   411  	deploymentChanged := false
   412  	existingDeployment := &appsv1.Deployment{}
   413  	if err := r.Client.Get(context.TODO(), types.NamespacedName{Name: desiredDeployment.Name, Namespace: cr.Namespace}, existingDeployment); err != nil {
   414  		if !errors.IsNotFound(err) {
   415  			return fmt.Errorf("failed to get the deployment associated with %s : %s", existingDeployment.Name, err)
   416  		}
   417  
   418  		// deployment does not exist and shouldn't, nothing to do here
   419  		if !cr.Spec.Notifications.Enabled {
   420  			return nil
   421  		}
   422  
   423  		// deployment does not exist but should, so it should be created
   424  		if err := controllerutil.SetControllerReference(cr, desiredDeployment, r.Scheme); err != nil {
   425  			return err
   426  		}
   427  
   428  		log.Info(fmt.Sprintf("Creating deployment %s", desiredDeployment.Name))
   429  		return r.Client.Create(context.TODO(), desiredDeployment)
   430  	}
   431  
   432  	// deployment exists but shouldn't, so it should be deleted
   433  	if !cr.Spec.Notifications.Enabled {
   434  		log.Info(fmt.Sprintf("Deleting deployment %s as notifications is disabled", existingDeployment.Name))
   435  		return r.Client.Delete(context.TODO(), existingDeployment)
   436  	}
   437  
   438  	// deployment exists and should. Reconcile deployment if changed
   439  	updateNodePlacement(existingDeployment, desiredDeployment, &deploymentChanged)
   440  
   441  	if existingDeployment.Spec.Template.Spec.Containers[0].Image != desiredDeployment.Spec.Template.Spec.Containers[0].Image {
   442  		existingDeployment.Spec.Template.Spec.Containers[0].Image = desiredDeployment.Spec.Template.Spec.Containers[0].Image
   443  		existingDeployment.Spec.Template.ObjectMeta.Labels["image.upgraded"] = time.Now().UTC().Format("01022006-150406-MST")
   444  		deploymentChanged = true
   445  	}
   446  
   447  	if !reflect.DeepEqual(existingDeployment.Spec.Template.Spec.Containers[0].Command, desiredDeployment.Spec.Template.Spec.Containers[0].Command) {
   448  		existingDeployment.Spec.Template.Spec.Containers[0].Command = desiredDeployment.Spec.Template.Spec.Containers[0].Command
   449  		deploymentChanged = true
   450  	}
   451  
   452  	if !reflect.DeepEqual(existingDeployment.Spec.Template.Spec.Containers[0].Env,
   453  		desiredDeployment.Spec.Template.Spec.Containers[0].Env) {
   454  		existingDeployment.Spec.Template.Spec.Containers[0].Env = desiredDeployment.Spec.Template.Spec.Containers[0].Env
   455  		deploymentChanged = true
   456  	}
   457  
   458  	if !reflect.DeepEqual(existingDeployment.Spec.Template.Spec.Volumes, desiredDeployment.Spec.Template.Spec.Volumes) {
   459  		existingDeployment.Spec.Template.Spec.Volumes = desiredDeployment.Spec.Template.Spec.Volumes
   460  		deploymentChanged = true
   461  	}
   462  
   463  	if !reflect.DeepEqual(existingDeployment.Spec.Replicas, desiredDeployment.Spec.Replicas) {
   464  		existingDeployment.Spec.Replicas = desiredDeployment.Spec.Replicas
   465  		deploymentChanged = true
   466  	}
   467  
   468  	if !reflect.DeepEqual(existingDeployment.Spec.Template.Spec.Containers[0].VolumeMounts, desiredDeployment.Spec.Template.Spec.Containers[0].VolumeMounts) {
   469  		existingDeployment.Spec.Template.Spec.Containers[0].VolumeMounts = desiredDeployment.Spec.Template.Spec.Containers[0].VolumeMounts
   470  		deploymentChanged = true
   471  	}
   472  
   473  	if !reflect.DeepEqual(existingDeployment.Spec.Template.Spec.Containers[0].Resources, desiredDeployment.Spec.Template.Spec.Containers[0].Resources) {
   474  		existingDeployment.Spec.Template.Spec.Containers[0].Resources = desiredDeployment.Spec.Template.Spec.Containers[0].Resources
   475  		deploymentChanged = true
   476  	}
   477  
   478  	if !reflect.DeepEqual(existingDeployment.Spec.Template.Spec.ServiceAccountName, desiredDeployment.Spec.Template.Spec.ServiceAccountName) {
   479  		existingDeployment.Spec.Template.Spec.ServiceAccountName = desiredDeployment.Spec.Template.Spec.ServiceAccountName
   480  		deploymentChanged = true
   481  	}
   482  
   483  	if !reflect.DeepEqual(existingDeployment.Labels, desiredDeployment.Labels) {
   484  		existingDeployment.Labels = desiredDeployment.Labels
   485  		deploymentChanged = true
   486  	}
   487  
   488  	if !reflect.DeepEqual(existingDeployment.Spec.Template.Labels, desiredDeployment.Spec.Template.Labels) {
   489  		existingDeployment.Spec.Template.Labels = desiredDeployment.Spec.Template.Labels
   490  		deploymentChanged = true
   491  	}
   492  
   493  	if !reflect.DeepEqual(existingDeployment.Spec.Selector, desiredDeployment.Spec.Selector) {
   494  		existingDeployment.Spec.Selector = desiredDeployment.Spec.Selector
   495  		deploymentChanged = true
   496  	}
   497  
   498  	if deploymentChanged {
   499  		return r.Client.Update(context.TODO(), existingDeployment)
   500  	}
   501  
   502  	return nil
   503  
   504  }
   505  
   506  // reconcileNotificationsService will ensure that the Service for the Notifications controller metrics is present.
   507  func (r *ReconcileArgoCD) reconcileNotificationsMetricsService(cr *argoproj.ArgoCD) error {
   508  
   509  	var component = "notifications-controller"
   510  	var suffix = "notifications-controller-metrics"
   511  
   512  	svc := newServiceWithSuffix(suffix, component, cr)
   513  	if argoutil.IsObjectFound(r.Client, cr.Namespace, svc.Name, svc) {
   514  		// Service found, do nothing
   515  		return nil
   516  	}
   517  
   518  	svc.Spec.Selector = map[string]string{
   519  		common.ArgoCDKeyName: nameWithSuffix(component, cr),
   520  	}
   521  
   522  	svc.Spec.Ports = []corev1.ServicePort{
   523  		{
   524  			Name:       "metrics",
   525  			Port:       common.NotificationsControllerMetricsPort,
   526  			Protocol:   corev1.ProtocolTCP,
   527  			TargetPort: intstr.FromInt(common.NotificationsControllerMetricsPort),
   528  		},
   529  	}
   530  
   531  	if err := controllerutil.SetControllerReference(cr, svc, r.Scheme); err != nil {
   532  		return err
   533  	}
   534  	return r.Client.Create(context.TODO(), svc)
   535  }
   536  
   537  // reconcileNotificationsServiceMonitor will ensure that the ServiceMonitor for the Notifications controller metrics is present.
   538  func (r *ReconcileArgoCD) reconcileNotificationsServiceMonitor(cr *argoproj.ArgoCD) error {
   539  
   540  	name := fmt.Sprintf("%s-%s", cr.Name, "notifications-controller-metrics")
   541  	serviceMonitor := newServiceMonitorWithName(name, cr)
   542  	if argoutil.IsObjectFound(r.Client, cr.Namespace, serviceMonitor.Name, serviceMonitor) {
   543  		// Service found, do nothing
   544  		return nil
   545  	}
   546  
   547  	serviceMonitor.Spec.Selector = v1.LabelSelector{
   548  		MatchLabels: map[string]string{
   549  			common.ArgoCDKeyName: name,
   550  		},
   551  	}
   552  
   553  	serviceMonitor.Spec.Endpoints = []monitoringv1.Endpoint{
   554  		{
   555  			Port:     "metrics",
   556  			Scheme:   "http",
   557  			Interval: "30s",
   558  		},
   559  	}
   560  
   561  	return r.Client.Create(context.TODO(), serviceMonitor)
   562  }
   563  
   564  // reconcileNotificationsSecret only creates/deletes the argocd-notifications-secret based on whether notifications is enabled/disabled in the CR
   565  // It does not reconcile/overwrite any fields or information in the secret itself
   566  func (r *ReconcileArgoCD) reconcileNotificationsSecret(cr *argoproj.ArgoCD) error {
   567  
   568  	desiredSecret := argoutil.NewSecretWithName(cr, "argocd-notifications-secret")
   569  
   570  	secretExists := true
   571  	existingSecret := &corev1.Secret{}
   572  	if err := argoutil.FetchObject(r.Client, cr.Namespace, desiredSecret.Name, existingSecret); err != nil {
   573  		if !errors.IsNotFound(err) {
   574  			return fmt.Errorf("failed to get the secret associated with %s : %s", desiredSecret.Name, err)
   575  		}
   576  		secretExists = false
   577  	}
   578  
   579  	if secretExists {
   580  		// secret exists but shouldn't, so it should be deleted
   581  		if !cr.Spec.Notifications.Enabled {
   582  			log.Info(fmt.Sprintf("Deleting secret %s as notifications is disabled", existingSecret.Name))
   583  			return r.Client.Delete(context.TODO(), existingSecret)
   584  		}
   585  
   586  		// secret exists and should, nothing to do here
   587  		return nil
   588  	}
   589  
   590  	// secret doesn't exist and shouldn't, nothing to do here
   591  	if !cr.Spec.Notifications.Enabled {
   592  		return nil
   593  	}
   594  
   595  	// secret doesn't exist but should, so it should be created
   596  	if err := controllerutil.SetControllerReference(cr, desiredSecret, r.Scheme); err != nil {
   597  		return err
   598  	}
   599  
   600  	log.Info(fmt.Sprintf("Creating secret %s", desiredSecret.Name))
   601  	err := r.Client.Create(context.TODO(), desiredSecret)
   602  	if err != nil {
   603  		return err
   604  	}
   605  
   606  	return nil
   607  }
   608  
   609  func getNotificationsCommand(cr *argoproj.ArgoCD) []string {
   610  
   611  	cmd := make([]string, 0)
   612  	cmd = append(cmd, "argocd-notifications")
   613  
   614  	cmd = append(cmd, "--loglevel")
   615  	cmd = append(cmd, getLogLevel(cr.Spec.Notifications.LogLevel))
   616  
   617  	if cr.Spec.Repo.IsEnabled() {
   618  		cmd = append(cmd, "--argocd-repo-server", getRepoServerAddress(cr))
   619  	} else {
   620  		log.Info("Repo Server is disabled. This would affect the functioning of Notification Controller.")
   621  	}
   622  
   623  	return cmd
   624  }
   625  
   626  // getNotificationsResources will return the ResourceRequirements for the Notifications container.
   627  func getNotificationsResources(cr *argoproj.ArgoCD) corev1.ResourceRequirements {
   628  	resources := corev1.ResourceRequirements{}
   629  
   630  	// Allow override of resource requirements from CR
   631  	if cr.Spec.Notifications.Resources != nil {
   632  		resources = *cr.Spec.Notifications.Resources
   633  	}
   634  
   635  	return resources
   636  }