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

     1  // Copyright 2019 ArgoCD Operator Developers
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  // 	http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package argocd
    16  
    17  import (
    18  	"context"
    19  	"errors"
    20  	"fmt"
    21  	"os"
    22  	"reflect"
    23  	"strings"
    24  	"time"
    25  
    26  	argoprojv1alpha1 "github.com/argoproj-labs/argocd-operator/api/v1alpha1"
    27  	argoproj "github.com/argoproj-labs/argocd-operator/api/v1beta1"
    28  	"github.com/argoproj-labs/argocd-operator/common"
    29  	"github.com/argoproj-labs/argocd-operator/controllers/argoutil"
    30  
    31  	appsv1 "k8s.io/api/apps/v1"
    32  	corev1 "k8s.io/api/core/v1"
    33  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    34  	"k8s.io/apimachinery/pkg/util/intstr"
    35  	"sigs.k8s.io/controller-runtime/pkg/client"
    36  	"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
    37  )
    38  
    39  // getArgoCDRepoServerReplicas will return the size value for the argocd-repo-server replica count if it
    40  // has been set in argocd CR. Otherwise, nil is returned if the replicas is not set in the argocd CR or
    41  // replicas value is < 0.
    42  func getArgoCDRepoServerReplicas(cr *argoproj.ArgoCD) *int32 {
    43  	if cr.Spec.Repo.Replicas != nil && *cr.Spec.Repo.Replicas >= 0 {
    44  		return cr.Spec.Repo.Replicas
    45  	}
    46  
    47  	return nil
    48  }
    49  
    50  // getArgoCDServerReplicas will return the size value for the argocd-server replica count if it
    51  // has been set in argocd CR. Otherwise, nil is returned if the replicas is not set in the argocd CR or
    52  // replicas value is < 0. If Autoscale is enabled, the value for replicas in the argocd CR will be ignored.
    53  func getArgoCDServerReplicas(cr *argoproj.ArgoCD) *int32 {
    54  	if !cr.Spec.Server.Autoscale.Enabled && cr.Spec.Server.Replicas != nil && *cr.Spec.Server.Replicas >= 0 {
    55  		return cr.Spec.Server.Replicas
    56  	}
    57  	return nil
    58  }
    59  
    60  func (r *ReconcileArgoCD) getArgoCDExport(cr *argoproj.ArgoCD) *argoprojv1alpha1.ArgoCDExport {
    61  	if cr.Spec.Import == nil {
    62  		return nil
    63  	}
    64  
    65  	namespace := cr.ObjectMeta.Namespace
    66  	if cr.Spec.Import.Namespace != nil && len(*cr.Spec.Import.Namespace) > 0 {
    67  		namespace = *cr.Spec.Import.Namespace
    68  	}
    69  
    70  	export := &argoprojv1alpha1.ArgoCDExport{}
    71  	if argoutil.IsObjectFound(r.Client, namespace, cr.Spec.Import.Name, export) {
    72  		return export
    73  	}
    74  	return nil
    75  }
    76  
    77  func getArgoExportSecretName(export *argoprojv1alpha1.ArgoCDExport) string {
    78  	name := argoutil.NameWithSuffix(export.ObjectMeta, "export")
    79  	if export.Spec.Storage != nil && len(export.Spec.Storage.SecretName) > 0 {
    80  		name = export.Spec.Storage.SecretName
    81  	}
    82  	return name
    83  }
    84  
    85  func getArgoImportBackend(client client.Client, cr *argoproj.ArgoCD) string {
    86  	backend := common.ArgoCDExportStorageBackendLocal
    87  	namespace := cr.ObjectMeta.Namespace
    88  	if cr.Spec.Import != nil && cr.Spec.Import.Namespace != nil && len(*cr.Spec.Import.Namespace) > 0 {
    89  		namespace = *cr.Spec.Import.Namespace
    90  	}
    91  
    92  	export := &argoprojv1alpha1.ArgoCDExport{}
    93  	if argoutil.IsObjectFound(client, namespace, cr.Spec.Import.Name, export) {
    94  		if export.Spec.Storage != nil && len(export.Spec.Storage.Backend) > 0 {
    95  			backend = export.Spec.Storage.Backend
    96  		}
    97  	}
    98  	return backend
    99  }
   100  
   101  // getArgoImportCommand will return the command for the ArgoCD import process.
   102  func getArgoImportCommand(client client.Client, cr *argoproj.ArgoCD) []string {
   103  	cmd := make([]string, 0)
   104  	cmd = append(cmd, "uid_entrypoint.sh")
   105  	cmd = append(cmd, "argocd-operator-util")
   106  	cmd = append(cmd, "import")
   107  	cmd = append(cmd, getArgoImportBackend(client, cr))
   108  	return cmd
   109  }
   110  
   111  func getArgoImportContainerEnv(cr *argoprojv1alpha1.ArgoCDExport) []corev1.EnvVar {
   112  	env := make([]corev1.EnvVar, 0)
   113  
   114  	switch cr.Spec.Storage.Backend {
   115  	case common.ArgoCDExportStorageBackendAWS:
   116  		env = append(env, corev1.EnvVar{
   117  			Name: "AWS_ACCESS_KEY_ID",
   118  			ValueFrom: &corev1.EnvVarSource{
   119  				SecretKeyRef: &corev1.SecretKeySelector{
   120  					LocalObjectReference: corev1.LocalObjectReference{
   121  						Name: argoutil.FetchStorageSecretName(cr),
   122  					},
   123  					Key: "aws.access.key.id",
   124  				},
   125  			},
   126  		})
   127  
   128  		env = append(env, corev1.EnvVar{
   129  			Name: "AWS_SECRET_ACCESS_KEY",
   130  			ValueFrom: &corev1.EnvVarSource{
   131  				SecretKeyRef: &corev1.SecretKeySelector{
   132  					LocalObjectReference: corev1.LocalObjectReference{
   133  						Name: argoutil.FetchStorageSecretName(cr),
   134  					},
   135  					Key: "aws.secret.access.key",
   136  				},
   137  			},
   138  		})
   139  	}
   140  
   141  	return env
   142  }
   143  
   144  // getArgoImportContainerImage will return the container image for the Argo CD import process.
   145  func getArgoImportContainerImage(cr *argoprojv1alpha1.ArgoCDExport) string {
   146  	img := common.ArgoCDDefaultExportJobImage
   147  	if len(cr.Spec.Image) > 0 {
   148  		img = cr.Spec.Image
   149  	}
   150  
   151  	tag := common.ArgoCDDefaultExportJobVersion
   152  	if len(cr.Spec.Version) > 0 {
   153  		tag = cr.Spec.Version
   154  	}
   155  
   156  	return argoutil.CombineImageTag(img, tag)
   157  }
   158  
   159  // getArgoImportVolumeMounts will return the VolumneMounts for the given ArgoCDExport.
   160  func getArgoImportVolumeMounts() []corev1.VolumeMount {
   161  	mounts := make([]corev1.VolumeMount, 0)
   162  
   163  	mounts = append(mounts, corev1.VolumeMount{
   164  		Name:      "backup-storage",
   165  		MountPath: "/backups",
   166  	})
   167  
   168  	mounts = append(mounts, corev1.VolumeMount{
   169  		Name:      "secret-storage",
   170  		MountPath: "/secrets",
   171  	})
   172  
   173  	return mounts
   174  }
   175  
   176  // getArgoImportVolumes will return the Volumes for the given ArgoCDExport.
   177  func getArgoImportVolumes(cr *argoprojv1alpha1.ArgoCDExport) []corev1.Volume {
   178  	volumes := make([]corev1.Volume, 0)
   179  
   180  	if cr.Spec.Storage != nil && cr.Spec.Storage.Backend == common.ArgoCDExportStorageBackendLocal {
   181  		volumes = append(volumes, corev1.Volume{
   182  			Name: "backup-storage",
   183  			VolumeSource: corev1.VolumeSource{
   184  				PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{
   185  					ClaimName: cr.Name,
   186  				},
   187  			},
   188  		})
   189  	} else {
   190  		volumes = append(volumes, corev1.Volume{
   191  			Name: "backup-storage",
   192  			VolumeSource: corev1.VolumeSource{
   193  				EmptyDir: &corev1.EmptyDirVolumeSource{},
   194  			},
   195  		})
   196  	}
   197  
   198  	volumes = append(volumes, corev1.Volume{
   199  		Name: "secret-storage",
   200  		VolumeSource: corev1.VolumeSource{
   201  			Secret: &corev1.SecretVolumeSource{
   202  				SecretName: getArgoExportSecretName(cr),
   203  			},
   204  		},
   205  	})
   206  
   207  	return volumes
   208  }
   209  
   210  func getArgoRedisArgs(useTLS bool) []string {
   211  	args := make([]string, 0)
   212  
   213  	args = append(args, "--save", "")
   214  	args = append(args, "--appendonly", "no")
   215  
   216  	if useTLS {
   217  		args = append(args, "--tls-port", "6379")
   218  		args = append(args, "--port", "0")
   219  
   220  		args = append(args, "--tls-cert-file", "/app/config/redis/tls/tls.crt")
   221  		args = append(args, "--tls-key-file", "/app/config/redis/tls/tls.key")
   222  		args = append(args, "--tls-auth-clients", "no")
   223  	}
   224  
   225  	return args
   226  }
   227  
   228  // getArgoRepoCommand will return the command for the ArgoCD Repo component.
   229  func getArgoRepoCommand(cr *argoproj.ArgoCD, useTLSForRedis bool) []string {
   230  	cmd := make([]string, 0)
   231  
   232  	cmd = append(cmd, "uid_entrypoint.sh")
   233  	cmd = append(cmd, "argocd-repo-server")
   234  
   235  	if cr.Spec.Redis.IsEnabled() {
   236  		cmd = append(cmd, "--redis", getRedisServerAddress(cr))
   237  	} else {
   238  		log.Info("Redis is Disabled. Skipping adding Redis configuration to Repo Server.")
   239  	}
   240  	if useTLSForRedis {
   241  		cmd = append(cmd, "--redis-use-tls")
   242  		if isRedisTLSVerificationDisabled(cr) {
   243  			cmd = append(cmd, "--redis-insecure-skip-tls-verify")
   244  		} else {
   245  			cmd = append(cmd, "--redis-ca-certificate", "/app/config/reposerver/tls/redis/tls.crt")
   246  		}
   247  	}
   248  
   249  	cmd = append(cmd, "--loglevel")
   250  	cmd = append(cmd, getLogLevel(cr.Spec.Repo.LogLevel))
   251  
   252  	cmd = append(cmd, "--logformat")
   253  	cmd = append(cmd, getLogFormat(cr.Spec.Repo.LogFormat))
   254  
   255  	// *** NOTE ***
   256  	// Do Not add any new default command line arguments below this.
   257  	extraArgs := cr.Spec.Repo.ExtraRepoCommandArgs
   258  	err := isMergable(extraArgs, cmd)
   259  	if err != nil {
   260  		return cmd
   261  	}
   262  
   263  	cmd = append(cmd, extraArgs...)
   264  	return cmd
   265  }
   266  
   267  // getArgoCmpServerInitCommand will return the command for the ArgoCD CMP Server init container
   268  func getArgoCmpServerInitCommand() []string {
   269  	cmd := make([]string, 0)
   270  	cmd = append(cmd, "cp")
   271  	cmd = append(cmd, "-n")
   272  	cmd = append(cmd, "/usr/local/bin/argocd")
   273  	cmd = append(cmd, "/var/run/argocd/argocd-cmp-server")
   274  	return cmd
   275  }
   276  
   277  // getArgoServerCommand will return the command for the ArgoCD server component.
   278  func getArgoServerCommand(cr *argoproj.ArgoCD, useTLSForRedis bool) []string {
   279  	cmd := make([]string, 0)
   280  	cmd = append(cmd, "argocd-server")
   281  
   282  	if getArgoServerInsecure(cr) {
   283  		cmd = append(cmd, "--insecure")
   284  	}
   285  
   286  	if isRepoServerTLSVerificationRequested(cr) {
   287  		cmd = append(cmd, "--repo-server-strict-tls")
   288  	}
   289  
   290  	cmd = append(cmd, "--staticassets")
   291  	cmd = append(cmd, "/shared/app")
   292  
   293  	cmd = append(cmd, "--dex-server")
   294  	cmd = append(cmd, getDexServerAddress(cr))
   295  
   296  	if cr.Spec.Repo.IsEnabled() {
   297  		cmd = append(cmd, "--repo-server", getRepoServerAddress(cr))
   298  	} else {
   299  		log.Info("Repo Server is disabled. This would affect the functioning of ArgoCD Server.")
   300  	}
   301  
   302  	if cr.Spec.Redis.IsEnabled() {
   303  		cmd = append(cmd, "--redis", getRedisServerAddress(cr))
   304  	} else {
   305  		log.Info("Redis is Disabled. Skipping adding Redis configuration to ArgoCD Server.")
   306  	}
   307  
   308  	if useTLSForRedis {
   309  		cmd = append(cmd, "--redis-use-tls")
   310  		if isRedisTLSVerificationDisabled(cr) {
   311  			cmd = append(cmd, "--redis-insecure-skip-tls-verify")
   312  		} else {
   313  			cmd = append(cmd, "--redis-ca-certificate", "/app/config/server/tls/redis/tls.crt")
   314  		}
   315  	}
   316  
   317  	cmd = append(cmd, "--loglevel")
   318  	cmd = append(cmd, getLogLevel(cr.Spec.Server.LogLevel))
   319  
   320  	cmd = append(cmd, "--logformat")
   321  	cmd = append(cmd, getLogFormat(cr.Spec.Server.LogFormat))
   322  
   323  	extraArgs := cr.Spec.Server.ExtraCommandArgs
   324  	err := isMergable(extraArgs, cmd)
   325  	if err != nil {
   326  		return cmd
   327  	}
   328  	if cr.Spec.SourceNamespaces != nil && len(cr.Spec.SourceNamespaces) > 0 {
   329  		cmd = append(cmd, "--application-namespaces", fmt.Sprint(strings.Join(cr.Spec.SourceNamespaces, ",")))
   330  	}
   331  
   332  	cmd = append(cmd, extraArgs...)
   333  	return cmd
   334  }
   335  
   336  // isMergable returns error if any of the extraArgs is already part of the default command Arguments.
   337  func isMergable(extraArgs []string, cmd []string) error {
   338  	if len(extraArgs) > 0 {
   339  		for _, arg := range extraArgs {
   340  			if len(arg) > 2 && arg[:2] == "--" {
   341  				if ok := contains(cmd, arg); ok {
   342  					err := errors.New("duplicate argument error")
   343  					log.Error(err, fmt.Sprintf("Arg %s is already part of the default command arguments", arg))
   344  					return err
   345  				}
   346  			}
   347  		}
   348  	}
   349  	return nil
   350  }
   351  
   352  // getDexServerAddress will return the Dex server address.
   353  func getDexServerAddress(cr *argoproj.ArgoCD) string {
   354  	return fmt.Sprintf("https://%s", fqdnServiceRef("dex-server", common.ArgoCDDefaultDexHTTPPort, cr))
   355  }
   356  
   357  // getRepoServerAddress will return the Argo CD repo server address.
   358  func getRepoServerAddress(cr *argoproj.ArgoCD) string {
   359  	if cr.Spec.Repo.Remote != nil && *cr.Spec.Repo.Remote != "" {
   360  		return *cr.Spec.Repo.Remote
   361  	}
   362  	return fqdnServiceRef("repo-server", common.ArgoCDDefaultRepoServerPort, cr)
   363  }
   364  
   365  // newDeployment returns a new Deployment instance for the given ArgoCD.
   366  func newDeployment(cr *argoproj.ArgoCD) *appsv1.Deployment {
   367  	return &appsv1.Deployment{
   368  		ObjectMeta: metav1.ObjectMeta{
   369  			Name:      cr.Name,
   370  			Namespace: cr.Namespace,
   371  			Labels:    argoutil.LabelsForCluster(cr),
   372  		},
   373  	}
   374  }
   375  
   376  // newDeploymentWithName returns a new Deployment instance for the given ArgoCD using the given name.
   377  func newDeploymentWithName(name string, component string, cr *argoproj.ArgoCD) *appsv1.Deployment {
   378  	deploy := newDeployment(cr)
   379  	deploy.ObjectMeta.Name = name
   380  
   381  	lbls := deploy.ObjectMeta.Labels
   382  	lbls[common.ArgoCDKeyName] = name
   383  	lbls[common.ArgoCDKeyComponent] = component
   384  	deploy.ObjectMeta.Labels = lbls
   385  
   386  	deploy.Spec = appsv1.DeploymentSpec{
   387  		Selector: &metav1.LabelSelector{
   388  			MatchLabels: map[string]string{
   389  				common.ArgoCDKeyName: name,
   390  			},
   391  		},
   392  		Template: corev1.PodTemplateSpec{
   393  			ObjectMeta: metav1.ObjectMeta{
   394  				Labels: map[string]string{
   395  					common.ArgoCDKeyName: name,
   396  				},
   397  			},
   398  			Spec: corev1.PodSpec{
   399  				NodeSelector: common.DefaultNodeSelector(),
   400  			},
   401  		},
   402  	}
   403  
   404  	if cr.Spec.NodePlacement != nil {
   405  		deploy.Spec.Template.Spec.NodeSelector = argoutil.AppendStringMap(deploy.Spec.Template.Spec.NodeSelector, cr.Spec.NodePlacement.NodeSelector)
   406  		deploy.Spec.Template.Spec.Tolerations = cr.Spec.NodePlacement.Tolerations
   407  	}
   408  	return deploy
   409  }
   410  
   411  // newDeploymentWithSuffix returns a new Deployment instance for the given ArgoCD using the given suffix.
   412  func newDeploymentWithSuffix(suffix string, component string, cr *argoproj.ArgoCD) *appsv1.Deployment {
   413  	return newDeploymentWithName(fmt.Sprintf("%s-%s", cr.Name, suffix), component, cr)
   414  }
   415  
   416  // reconcileDeployments will ensure that all Deployment resources are present for the given ArgoCD.
   417  func (r *ReconcileArgoCD) reconcileDeployments(cr *argoproj.ArgoCD, useTLSForRedis bool) error {
   418  
   419  	if err := r.reconcileDexDeployment(cr); err != nil {
   420  		log.Error(err, "error reconciling dex deployment")
   421  	}
   422  
   423  	err := r.reconcileRedisDeployment(cr, useTLSForRedis)
   424  	if err != nil {
   425  		return err
   426  	}
   427  
   428  	err = r.reconcileRedisHAProxyDeployment(cr)
   429  	if err != nil {
   430  		return err
   431  	}
   432  
   433  	err = r.reconcileRepoDeployment(cr, useTLSForRedis)
   434  	if err != nil {
   435  		return err
   436  	}
   437  
   438  	err = r.reconcileServerDeployment(cr, useTLSForRedis)
   439  	if err != nil {
   440  		return err
   441  	}
   442  
   443  	err = r.reconcileGrafanaDeployment(cr)
   444  	if err != nil {
   445  		return err
   446  	}
   447  
   448  	return nil
   449  }
   450  
   451  // reconcileGrafanaDeployment will ensure the Deployment resource is present for the ArgoCD Grafana component.
   452  func (r *ReconcileArgoCD) reconcileGrafanaDeployment(cr *argoproj.ArgoCD) error {
   453  	if !cr.Spec.Grafana.Enabled {
   454  		return nil // Grafana not enabled, do nothing.
   455  	}
   456  	log.Info(grafanaDeprecatedWarning)
   457  	return nil
   458  }
   459  
   460  // reconcileRedisDeployment will ensure the Deployment resource is present for the ArgoCD Redis component.
   461  func (r *ReconcileArgoCD) reconcileRedisDeployment(cr *argoproj.ArgoCD, useTLS bool) error {
   462  	deploy := newDeploymentWithSuffix("redis", "redis", cr)
   463  
   464  	AddSeccompProfileForOpenShift(r.Client, &deploy.Spec.Template.Spec)
   465  
   466  	deploy.Spec.Template.Spec.Containers = []corev1.Container{{
   467  		Args:            getArgoRedisArgs(useTLS),
   468  		Image:           getRedisContainerImage(cr),
   469  		ImagePullPolicy: corev1.PullAlways,
   470  		Name:            "redis",
   471  		Ports: []corev1.ContainerPort{
   472  			{
   473  				ContainerPort: common.ArgoCDDefaultRedisPort,
   474  			},
   475  		},
   476  		Resources: getRedisResources(cr),
   477  		Env:       proxyEnvVars(),
   478  		SecurityContext: &corev1.SecurityContext{
   479  			AllowPrivilegeEscalation: boolPtr(false),
   480  			Capabilities: &corev1.Capabilities{
   481  				Drop: []corev1.Capability{
   482  					"ALL",
   483  				},
   484  			},
   485  			RunAsNonRoot: boolPtr(true),
   486  			RunAsUser:    int64Ptr(999),
   487  		},
   488  		VolumeMounts: []corev1.VolumeMount{
   489  			{
   490  				Name:      common.ArgoCDRedisServerTLSSecretName,
   491  				MountPath: "/app/config/redis/tls",
   492  			},
   493  		},
   494  	}}
   495  
   496  	deploy.Spec.Template.Spec.ServiceAccountName = fmt.Sprintf("%s-%s", cr.Name, "argocd-redis")
   497  	deploy.Spec.Template.Spec.Volumes = []corev1.Volume{
   498  		{
   499  			Name: common.ArgoCDRedisServerTLSSecretName,
   500  			VolumeSource: corev1.VolumeSource{
   501  				Secret: &corev1.SecretVolumeSource{
   502  					SecretName: common.ArgoCDRedisServerTLSSecretName,
   503  					Optional:   boolPtr(true),
   504  				},
   505  			},
   506  		},
   507  	}
   508  
   509  	if err := applyReconcilerHook(cr, deploy, ""); err != nil {
   510  		return err
   511  	}
   512  
   513  	existing := newDeploymentWithSuffix("redis", "redis", cr)
   514  	if argoutil.IsObjectFound(r.Client, cr.Namespace, existing.Name, existing) {
   515  		if !cr.Spec.Redis.IsEnabled() {
   516  			// Deployment exists but component enabled flag has been set to false, delete the Deployment
   517  			log.Info("Redis exists but should be disabled. Deleting existing redis.")
   518  			return r.Client.Delete(context.TODO(), deploy)
   519  		}
   520  		if cr.Spec.HA.Enabled {
   521  			// Deployment exists but HA enabled flag has been set to true, delete the Deployment
   522  			return r.Client.Delete(context.TODO(), deploy)
   523  		}
   524  		changed := false
   525  		actualImage := existing.Spec.Template.Spec.Containers[0].Image
   526  		desiredImage := getRedisContainerImage(cr)
   527  		if actualImage != desiredImage {
   528  			existing.Spec.Template.Spec.Containers[0].Image = desiredImage
   529  			existing.Spec.Template.ObjectMeta.Labels["image.upgraded"] = time.Now().UTC().Format("01022006-150406-MST")
   530  			changed = true
   531  		}
   532  		updateNodePlacement(existing, deploy, &changed)
   533  
   534  		if !reflect.DeepEqual(deploy.Spec.Template.Spec.Containers[0].Args, existing.Spec.Template.Spec.Containers[0].Args) {
   535  			existing.Spec.Template.Spec.Containers[0].Args = deploy.Spec.Template.Spec.Containers[0].Args
   536  			changed = true
   537  		}
   538  
   539  		if !reflect.DeepEqual(existing.Spec.Template.Spec.Containers[0].Env,
   540  			deploy.Spec.Template.Spec.Containers[0].Env) {
   541  			existing.Spec.Template.Spec.Containers[0].Env = deploy.Spec.Template.Spec.Containers[0].Env
   542  			changed = true
   543  		}
   544  
   545  		if !reflect.DeepEqual(deploy.Spec.Template.Spec.Containers[0].Resources, existing.Spec.Template.Spec.Containers[0].Resources) {
   546  			existing.Spec.Template.Spec.Containers[0].Resources = deploy.Spec.Template.Spec.Containers[0].Resources
   547  			changed = true
   548  		}
   549  
   550  		if changed {
   551  			return r.Client.Update(context.TODO(), existing)
   552  		}
   553  		return nil // Deployment found with nothing to do, move along...
   554  	}
   555  
   556  	if !cr.Spec.Redis.IsEnabled() {
   557  		log.Info("Redis disabled. Skipping starting redis.")
   558  		return nil
   559  	}
   560  
   561  	if cr.Spec.HA.Enabled {
   562  		return nil // HA enabled, do nothing.
   563  	}
   564  	if err := controllerutil.SetControllerReference(cr, deploy, r.Scheme); err != nil {
   565  		return err
   566  	}
   567  	return r.Client.Create(context.TODO(), deploy)
   568  }
   569  
   570  // reconcileRedisHAProxyDeployment will ensure the Deployment resource is present for the Redis HA Proxy component.
   571  func (r *ReconcileArgoCD) reconcileRedisHAProxyDeployment(cr *argoproj.ArgoCD) error {
   572  	deploy := newDeploymentWithSuffix("redis-ha-haproxy", "redis", cr)
   573  
   574  	deploy.Spec.Template.Spec.Affinity = &corev1.Affinity{
   575  		PodAntiAffinity: &corev1.PodAntiAffinity{
   576  			PreferredDuringSchedulingIgnoredDuringExecution: []corev1.WeightedPodAffinityTerm{
   577  				{
   578  					PodAffinityTerm: corev1.PodAffinityTerm{
   579  						LabelSelector: &metav1.LabelSelector{
   580  							MatchLabels: map[string]string{
   581  								common.ArgoCDKeyName: nameWithSuffix("redis-ha-haproxy", cr),
   582  							},
   583  						},
   584  						TopologyKey: common.ArgoCDKeyFailureDomainZone,
   585  					},
   586  					Weight: int32(100),
   587  				},
   588  			},
   589  			RequiredDuringSchedulingIgnoredDuringExecution: []corev1.PodAffinityTerm{
   590  				{
   591  					LabelSelector: &metav1.LabelSelector{
   592  						MatchLabels: map[string]string{
   593  							common.ArgoCDKeyName: nameWithSuffix("redis-ha-haproxy", cr),
   594  						},
   595  					},
   596  					TopologyKey: common.ArgoCDKeyHostname,
   597  				},
   598  			},
   599  		},
   600  	}
   601  
   602  	deploy.Spec.Template.Spec.Containers = []corev1.Container{{
   603  		Image:           getRedisHAProxyContainerImage(cr),
   604  		ImagePullPolicy: corev1.PullIfNotPresent,
   605  		Name:            "haproxy",
   606  		Env:             proxyEnvVars(),
   607  		LivenessProbe: &corev1.Probe{
   608  			ProbeHandler: corev1.ProbeHandler{
   609  				HTTPGet: &corev1.HTTPGetAction{
   610  					Path: "/healthz",
   611  					Port: intstr.FromInt(8888),
   612  				},
   613  			},
   614  			InitialDelaySeconds: int32(5),
   615  			PeriodSeconds:       int32(3),
   616  		},
   617  		Ports: []corev1.ContainerPort{
   618  			{
   619  				ContainerPort: common.ArgoCDDefaultRedisPort,
   620  				Name:          "redis",
   621  			},
   622  		},
   623  		Resources: getRedisHAResources(cr),
   624  		SecurityContext: &corev1.SecurityContext{
   625  			AllowPrivilegeEscalation: boolPtr(false),
   626  			Capabilities: &corev1.Capabilities{
   627  				Drop: []corev1.Capability{
   628  					"ALL",
   629  				},
   630  			},
   631  			RunAsNonRoot: boolPtr(true),
   632  		},
   633  		VolumeMounts: []corev1.VolumeMount{
   634  			{
   635  				Name:      "data",
   636  				MountPath: "/usr/local/etc/haproxy",
   637  			},
   638  			{
   639  				Name:      "shared-socket",
   640  				MountPath: "/run/haproxy",
   641  			},
   642  			{
   643  				Name:      common.ArgoCDRedisServerTLSSecretName,
   644  				MountPath: "/app/config/redis/tls",
   645  			},
   646  		},
   647  	}}
   648  
   649  	deploy.Spec.Template.Spec.InitContainers = []corev1.Container{{
   650  		Args: []string{
   651  			"/readonly/haproxy_init.sh",
   652  		},
   653  		Command: []string{
   654  			"sh",
   655  		},
   656  		Image:           getRedisHAProxyContainerImage(cr),
   657  		ImagePullPolicy: corev1.PullIfNotPresent,
   658  		Name:            "config-init",
   659  		Env:             proxyEnvVars(),
   660  		Resources:       getRedisHAResources(cr),
   661  		SecurityContext: &corev1.SecurityContext{
   662  			AllowPrivilegeEscalation: boolPtr(false),
   663  			Capabilities: &corev1.Capabilities{
   664  				Drop: []corev1.Capability{
   665  					"ALL",
   666  				},
   667  			},
   668  		},
   669  		VolumeMounts: []corev1.VolumeMount{
   670  			{
   671  				Name:      "config-volume",
   672  				MountPath: "/readonly",
   673  				ReadOnly:  true,
   674  			},
   675  			{
   676  				Name:      "data",
   677  				MountPath: "/data",
   678  			},
   679  		},
   680  	}}
   681  
   682  	deploy.Spec.Template.Spec.Volumes = []corev1.Volume{
   683  		{
   684  			Name: "config-volume",
   685  			VolumeSource: corev1.VolumeSource{
   686  				ConfigMap: &corev1.ConfigMapVolumeSource{
   687  					LocalObjectReference: corev1.LocalObjectReference{
   688  						Name: common.ArgoCDRedisHAConfigMapName,
   689  					},
   690  				},
   691  			},
   692  		},
   693  		{
   694  			Name: "shared-socket",
   695  			VolumeSource: corev1.VolumeSource{
   696  				EmptyDir: &corev1.EmptyDirVolumeSource{},
   697  			},
   698  		},
   699  		{
   700  			Name: "data",
   701  			VolumeSource: corev1.VolumeSource{
   702  				EmptyDir: &corev1.EmptyDirVolumeSource{},
   703  			},
   704  		},
   705  		{
   706  			Name: common.ArgoCDRedisServerTLSSecretName,
   707  			VolumeSource: corev1.VolumeSource{
   708  				Secret: &corev1.SecretVolumeSource{
   709  					SecretName: common.ArgoCDRedisServerTLSSecretName,
   710  					Optional:   boolPtr(true),
   711  				},
   712  			},
   713  		},
   714  	}
   715  
   716  	deploy.Spec.Template.Spec.SecurityContext = &corev1.PodSecurityContext{
   717  		RunAsNonRoot: boolPtr(true),
   718  		RunAsUser:    int64Ptr(1000),
   719  		FSGroup:      int64Ptr(1000),
   720  	}
   721  	AddSeccompProfileForOpenShift(r.Client, &deploy.Spec.Template.Spec)
   722  
   723  	deploy.Spec.Template.Spec.ServiceAccountName = fmt.Sprintf("%s-%s", cr.Name, "argocd-redis-ha")
   724  
   725  	version, err := getClusterVersion(r.Client)
   726  	if err != nil {
   727  		log.Error(err, "error getting cluster version")
   728  	}
   729  	if err := applyReconcilerHook(cr, deploy, version); err != nil {
   730  		return err
   731  	}
   732  
   733  	existing := newDeploymentWithSuffix("redis-ha-haproxy", "redis", cr)
   734  	if argoutil.IsObjectFound(r.Client, cr.Namespace, existing.Name, existing) {
   735  		if !cr.Spec.HA.Enabled {
   736  			// Deployment exists but HA enabled flag has been set to false, delete the Deployment
   737  			return r.Client.Delete(context.TODO(), existing)
   738  		}
   739  		changed := false
   740  		actualImage := existing.Spec.Template.Spec.Containers[0].Image
   741  		desiredImage := getRedisHAProxyContainerImage(cr)
   742  
   743  		if actualImage != desiredImage {
   744  			existing.Spec.Template.Spec.Containers[0].Image = desiredImage
   745  			existing.Spec.Template.ObjectMeta.Labels["image.upgraded"] = time.Now().UTC().Format("01022006-150406-MST")
   746  			changed = true
   747  		}
   748  		updateNodePlacement(existing, deploy, &changed)
   749  
   750  		if !reflect.DeepEqual(deploy.Spec.Template.Spec.Containers[0].Resources, existing.Spec.Template.Spec.Containers[0].Resources) {
   751  			existing.Spec.Template.Spec.Containers[0].Resources = deploy.Spec.Template.Spec.Containers[0].Resources
   752  			changed = true
   753  		}
   754  
   755  		if !reflect.DeepEqual(deploy.Spec.Template.Spec.InitContainers[0].Resources, existing.Spec.Template.Spec.InitContainers[0].Resources) {
   756  			existing.Spec.Template.Spec.InitContainers[0].Resources = deploy.Spec.Template.Spec.InitContainers[0].Resources
   757  			changed = true
   758  		}
   759  
   760  		if changed {
   761  			return r.Client.Update(context.TODO(), existing)
   762  		}
   763  		return nil // Deployment found, do nothing
   764  	}
   765  
   766  	if !cr.Spec.HA.Enabled {
   767  		return nil // HA not enabled, do nothing.
   768  	}
   769  
   770  	if err := controllerutil.SetControllerReference(cr, deploy, r.Scheme); err != nil {
   771  		return err
   772  	}
   773  	return r.Client.Create(context.TODO(), deploy)
   774  }
   775  
   776  // reconcileRepoDeployment will ensure the Deployment resource is present for the ArgoCD Repo component.
   777  func (r *ReconcileArgoCD) reconcileRepoDeployment(cr *argoproj.ArgoCD, useTLSForRedis bool) error {
   778  	deploy := newDeploymentWithSuffix("repo-server", "repo-server", cr)
   779  	automountToken := false
   780  	if cr.Spec.Repo.MountSAToken {
   781  		automountToken = cr.Spec.Repo.MountSAToken
   782  	}
   783  
   784  	deploy.Spec.Template.Spec.AutomountServiceAccountToken = &automountToken
   785  
   786  	if cr.Spec.Repo.ServiceAccount != "" {
   787  		deploy.Spec.Template.Spec.ServiceAccountName = cr.Spec.Repo.ServiceAccount
   788  	}
   789  
   790  	// Global proxy env vars go first
   791  	repoEnv := cr.Spec.Repo.Env
   792  	// Environment specified in the CR take precedence over everything else
   793  	repoEnv = argoutil.EnvMerge(repoEnv, proxyEnvVars(), false)
   794  	if cr.Spec.Repo.ExecTimeout != nil {
   795  		repoEnv = argoutil.EnvMerge(repoEnv, []corev1.EnvVar{{Name: "ARGOCD_EXEC_TIMEOUT", Value: fmt.Sprintf("%ds", *cr.Spec.Repo.ExecTimeout)}}, true)
   796  	}
   797  
   798  	AddSeccompProfileForOpenShift(r.Client, &deploy.Spec.Template.Spec)
   799  
   800  	deploy.Spec.Template.Spec.InitContainers = []corev1.Container{{
   801  		Name:            "copyutil",
   802  		Image:           getArgoContainerImage(cr),
   803  		Command:         getArgoCmpServerInitCommand(),
   804  		ImagePullPolicy: corev1.PullAlways,
   805  		Resources:       getArgoRepoResources(cr),
   806  		Env:             proxyEnvVars(),
   807  		SecurityContext: &corev1.SecurityContext{
   808  			AllowPrivilegeEscalation: boolPtr(false),
   809  			Capabilities: &corev1.Capabilities{
   810  				Drop: []corev1.Capability{
   811  					"ALL",
   812  				},
   813  			},
   814  			RunAsNonRoot: boolPtr(true),
   815  		},
   816  		VolumeMounts: []corev1.VolumeMount{
   817  			{
   818  				Name:      "var-files",
   819  				MountPath: "/var/run/argocd",
   820  			},
   821  		},
   822  	}}
   823  
   824  	if cr.Spec.Repo.InitContainers != nil {
   825  		deploy.Spec.Template.Spec.InitContainers = append(deploy.Spec.Template.Spec.InitContainers, cr.Spec.Repo.InitContainers...)
   826  	}
   827  
   828  	repoServerVolumeMounts := []corev1.VolumeMount{
   829  		{
   830  			Name:      "ssh-known-hosts",
   831  			MountPath: "/app/config/ssh",
   832  		},
   833  		{
   834  			Name:      "tls-certs",
   835  			MountPath: "/app/config/tls",
   836  		},
   837  		{
   838  			Name:      "gpg-keys",
   839  			MountPath: "/app/config/gpg/source",
   840  		},
   841  		{
   842  			Name:      "gpg-keyring",
   843  			MountPath: "/app/config/gpg/keys",
   844  		},
   845  		{
   846  			Name:      "tmp",
   847  			MountPath: "/tmp",
   848  		},
   849  		{
   850  			Name:      "argocd-repo-server-tls",
   851  			MountPath: "/app/config/reposerver/tls",
   852  		},
   853  		{
   854  			Name:      common.ArgoCDRedisServerTLSSecretName,
   855  			MountPath: "/app/config/reposerver/tls/redis",
   856  		},
   857  		{
   858  			Name:      "plugins",
   859  			MountPath: "/home/argocd/cmp-server/plugins",
   860  		},
   861  	}
   862  
   863  	if cr.Spec.Repo.VolumeMounts != nil {
   864  		repoServerVolumeMounts = append(repoServerVolumeMounts, cr.Spec.Repo.VolumeMounts...)
   865  	}
   866  
   867  	deploy.Spec.Template.Spec.Containers = []corev1.Container{{
   868  		Command:         getArgoRepoCommand(cr, useTLSForRedis),
   869  		Image:           getRepoServerContainerImage(cr),
   870  		ImagePullPolicy: corev1.PullAlways,
   871  		LivenessProbe: &corev1.Probe{
   872  			ProbeHandler: corev1.ProbeHandler{
   873  				TCPSocket: &corev1.TCPSocketAction{
   874  					Port: intstr.FromInt(common.ArgoCDDefaultRepoServerPort),
   875  				},
   876  			},
   877  			InitialDelaySeconds: 5,
   878  			PeriodSeconds:       10,
   879  		},
   880  		Env:  repoEnv,
   881  		Name: "argocd-repo-server",
   882  		Ports: []corev1.ContainerPort{
   883  			{
   884  				ContainerPort: common.ArgoCDDefaultRepoServerPort,
   885  				Name:          "server",
   886  			}, {
   887  				ContainerPort: common.ArgoCDDefaultRepoMetricsPort,
   888  				Name:          "metrics",
   889  			},
   890  		},
   891  		ReadinessProbe: &corev1.Probe{
   892  			ProbeHandler: corev1.ProbeHandler{
   893  				TCPSocket: &corev1.TCPSocketAction{
   894  					Port: intstr.FromInt(common.ArgoCDDefaultRepoServerPort),
   895  				},
   896  			},
   897  			InitialDelaySeconds: 5,
   898  			PeriodSeconds:       10,
   899  		},
   900  		Resources: getArgoRepoResources(cr),
   901  		SecurityContext: &corev1.SecurityContext{
   902  			AllowPrivilegeEscalation: boolPtr(false),
   903  			Capabilities: &corev1.Capabilities{
   904  				Drop: []corev1.Capability{
   905  					"ALL",
   906  				},
   907  			},
   908  			RunAsNonRoot: boolPtr(true),
   909  		},
   910  		VolumeMounts: repoServerVolumeMounts,
   911  	}}
   912  
   913  	if cr.Spec.Repo.SidecarContainers != nil {
   914  		deploy.Spec.Template.Spec.Containers = append(deploy.Spec.Template.Spec.Containers, cr.Spec.Repo.SidecarContainers...)
   915  	}
   916  
   917  	repoServerVolumes := []corev1.Volume{
   918  		{
   919  			Name: "ssh-known-hosts",
   920  			VolumeSource: corev1.VolumeSource{
   921  				ConfigMap: &corev1.ConfigMapVolumeSource{
   922  					LocalObjectReference: corev1.LocalObjectReference{
   923  						Name: common.ArgoCDKnownHostsConfigMapName,
   924  					},
   925  				},
   926  			},
   927  		},
   928  		{
   929  			Name: "tls-certs",
   930  			VolumeSource: corev1.VolumeSource{
   931  				ConfigMap: &corev1.ConfigMapVolumeSource{
   932  					LocalObjectReference: corev1.LocalObjectReference{
   933  						Name: common.ArgoCDTLSCertsConfigMapName,
   934  					},
   935  				},
   936  			},
   937  		},
   938  		{
   939  			Name: "gpg-keys",
   940  			VolumeSource: corev1.VolumeSource{
   941  				ConfigMap: &corev1.ConfigMapVolumeSource{
   942  					LocalObjectReference: corev1.LocalObjectReference{
   943  						Name: common.ArgoCDGPGKeysConfigMapName,
   944  					},
   945  				},
   946  			},
   947  		},
   948  		{
   949  			Name: "gpg-keyring",
   950  			VolumeSource: corev1.VolumeSource{
   951  				EmptyDir: &corev1.EmptyDirVolumeSource{},
   952  			},
   953  		},
   954  		{
   955  			Name: "tmp",
   956  			VolumeSource: corev1.VolumeSource{
   957  				EmptyDir: &corev1.EmptyDirVolumeSource{},
   958  			},
   959  		},
   960  		{
   961  			Name: "argocd-repo-server-tls",
   962  			VolumeSource: corev1.VolumeSource{
   963  				Secret: &corev1.SecretVolumeSource{
   964  					SecretName: common.ArgoCDRepoServerTLSSecretName,
   965  					Optional:   boolPtr(true),
   966  				},
   967  			},
   968  		},
   969  		{
   970  			Name: common.ArgoCDRedisServerTLSSecretName,
   971  			VolumeSource: corev1.VolumeSource{
   972  				Secret: &corev1.SecretVolumeSource{
   973  					SecretName: common.ArgoCDRedisServerTLSSecretName,
   974  					Optional:   boolPtr(true),
   975  				},
   976  			},
   977  		},
   978  		{
   979  			Name: "var-files",
   980  			VolumeSource: corev1.VolumeSource{
   981  				EmptyDir: &corev1.EmptyDirVolumeSource{},
   982  			},
   983  		},
   984  		{
   985  			Name: "plugins",
   986  			VolumeSource: corev1.VolumeSource{
   987  				EmptyDir: &corev1.EmptyDirVolumeSource{},
   988  			},
   989  		},
   990  	}
   991  
   992  	if cr.Spec.Repo.Volumes != nil {
   993  		repoServerVolumes = append(repoServerVolumes, cr.Spec.Repo.Volumes...)
   994  	}
   995  
   996  	deploy.Spec.Template.Spec.Volumes = repoServerVolumes
   997  
   998  	if replicas := getArgoCDRepoServerReplicas(cr); replicas != nil {
   999  		deploy.Spec.Replicas = replicas
  1000  	}
  1001  
  1002  	existing := newDeploymentWithSuffix("repo-server", "repo-server", cr)
  1003  	if argoutil.IsObjectFound(r.Client, cr.Namespace, existing.Name, existing) {
  1004  
  1005  		if !cr.Spec.Repo.IsEnabled() {
  1006  			log.Info("Existing ArgoCD Repo Server found but should be disabled. Deleting Repo Server")
  1007  			// Delete existing deployment for ArgoCD Repo Server, if any ..
  1008  			return r.Client.Delete(context.TODO(), existing)
  1009  		}
  1010  
  1011  		changed := false
  1012  		actualImage := existing.Spec.Template.Spec.Containers[0].Image
  1013  		desiredImage := getRepoServerContainerImage(cr)
  1014  		if actualImage != desiredImage {
  1015  			existing.Spec.Template.Spec.Containers[0].Image = desiredImage
  1016  			if existing.Spec.Template.ObjectMeta.Labels == nil {
  1017  				existing.Spec.Template.ObjectMeta.Labels = map[string]string{
  1018  					"image.upgraded": time.Now().UTC().Format("01022006-150406-MST"),
  1019  				}
  1020  			}
  1021  			existing.Spec.Template.ObjectMeta.Labels["image.upgraded"] = time.Now().UTC().Format("01022006-150406-MST")
  1022  			changed = true
  1023  		}
  1024  		updateNodePlacement(existing, deploy, &changed)
  1025  		if !reflect.DeepEqual(deploy.Spec.Template.Spec.Volumes, existing.Spec.Template.Spec.Volumes) {
  1026  			existing.Spec.Template.Spec.Volumes = deploy.Spec.Template.Spec.Volumes
  1027  			changed = true
  1028  		}
  1029  		if !reflect.DeepEqual(deploy.Spec.Template.Spec.Containers[0].VolumeMounts,
  1030  			existing.Spec.Template.Spec.Containers[0].VolumeMounts) {
  1031  			existing.Spec.Template.Spec.Containers[0].VolumeMounts = deploy.Spec.Template.Spec.Containers[0].VolumeMounts
  1032  			changed = true
  1033  		}
  1034  		if !reflect.DeepEqual(deploy.Spec.Template.Spec.Containers[0].Env,
  1035  			existing.Spec.Template.Spec.Containers[0].Env) {
  1036  			existing.Spec.Template.Spec.Containers[0].Env = deploy.Spec.Template.Spec.Containers[0].Env
  1037  			changed = true
  1038  		}
  1039  		if !reflect.DeepEqual(deploy.Spec.Template.Spec.Containers[0].Resources, existing.Spec.Template.Spec.Containers[0].Resources) {
  1040  			existing.Spec.Template.Spec.Containers[0].Resources = deploy.Spec.Template.Spec.Containers[0].Resources
  1041  			changed = true
  1042  		}
  1043  		if !reflect.DeepEqual(deploy.Spec.Template.Spec.Containers[0].Command, existing.Spec.Template.Spec.Containers[0].Command) {
  1044  			existing.Spec.Template.Spec.Containers[0].Command = deploy.Spec.Template.Spec.Containers[0].Command
  1045  			changed = true
  1046  		}
  1047  		if !reflect.DeepEqual(deploy.Spec.Template.Spec.Containers[1:],
  1048  			existing.Spec.Template.Spec.Containers[1:]) {
  1049  			existing.Spec.Template.Spec.Containers = append(existing.Spec.Template.Spec.Containers[0:1],
  1050  				deploy.Spec.Template.Spec.Containers[1:]...)
  1051  			changed = true
  1052  		}
  1053  		if !reflect.DeepEqual(deploy.Spec.Template.Spec.InitContainers, existing.Spec.Template.Spec.InitContainers) {
  1054  			existing.Spec.Template.Spec.InitContainers = deploy.Spec.Template.Spec.InitContainers
  1055  			changed = true
  1056  		}
  1057  
  1058  		if !reflect.DeepEqual(deploy.Spec.Replicas, existing.Spec.Replicas) {
  1059  			existing.Spec.Replicas = deploy.Spec.Replicas
  1060  			changed = true
  1061  		}
  1062  
  1063  		if deploy.Spec.Template.Spec.AutomountServiceAccountToken != existing.Spec.Template.Spec.AutomountServiceAccountToken {
  1064  			existing.Spec.Template.Spec.AutomountServiceAccountToken = deploy.Spec.Template.Spec.AutomountServiceAccountToken
  1065  			changed = true
  1066  		}
  1067  
  1068  		if deploy.Spec.Template.Spec.ServiceAccountName != existing.Spec.Template.Spec.ServiceAccountName {
  1069  			existing.Spec.Template.Spec.ServiceAccountName = deploy.Spec.Template.Spec.ServiceAccountName
  1070  			existing.Spec.Template.Spec.DeprecatedServiceAccount = deploy.Spec.Template.Spec.ServiceAccountName
  1071  			changed = true
  1072  		}
  1073  
  1074  		if changed {
  1075  			return r.Client.Update(context.TODO(), existing)
  1076  		}
  1077  		return nil // Deployment found with nothing to do, move along...
  1078  	}
  1079  
  1080  	if !cr.Spec.Repo.IsEnabled() {
  1081  		log.Info("ArgoCD Repo Server disabled. Skipping starting ArgoCD Repo Server.")
  1082  		return nil
  1083  	}
  1084  
  1085  	if err := controllerutil.SetControllerReference(cr, deploy, r.Scheme); err != nil {
  1086  		return err
  1087  	}
  1088  	return r.Client.Create(context.TODO(), deploy)
  1089  }
  1090  
  1091  // reconcileServerDeployment will ensure the Deployment resource is present for the ArgoCD Server component.
  1092  func (r *ReconcileArgoCD) reconcileServerDeployment(cr *argoproj.ArgoCD, useTLSForRedis bool) error {
  1093  	deploy := newDeploymentWithSuffix("server", "server", cr)
  1094  	serverEnv := cr.Spec.Server.Env
  1095  	serverEnv = argoutil.EnvMerge(serverEnv, proxyEnvVars(), false)
  1096  	AddSeccompProfileForOpenShift(r.Client, &deploy.Spec.Template.Spec)
  1097  	deploy.Spec.Template.Spec.Containers = []corev1.Container{{
  1098  		Command:         getArgoServerCommand(cr, useTLSForRedis),
  1099  		Image:           getArgoContainerImage(cr),
  1100  		ImagePullPolicy: corev1.PullAlways,
  1101  		Env:             serverEnv,
  1102  		LivenessProbe: &corev1.Probe{
  1103  			ProbeHandler: corev1.ProbeHandler{
  1104  				HTTPGet: &corev1.HTTPGetAction{
  1105  					Path: "/healthz",
  1106  					Port: intstr.FromInt(8080),
  1107  				},
  1108  			},
  1109  			InitialDelaySeconds: 3,
  1110  			PeriodSeconds:       30,
  1111  		},
  1112  		Name: "argocd-server",
  1113  		Ports: []corev1.ContainerPort{
  1114  			{
  1115  				ContainerPort: 8080,
  1116  			}, {
  1117  				ContainerPort: 8083,
  1118  			},
  1119  		},
  1120  		ReadinessProbe: &corev1.Probe{
  1121  			ProbeHandler: corev1.ProbeHandler{
  1122  				HTTPGet: &corev1.HTTPGetAction{
  1123  					Path: "/healthz",
  1124  					Port: intstr.FromInt(8080),
  1125  				},
  1126  			},
  1127  			InitialDelaySeconds: 3,
  1128  			PeriodSeconds:       30,
  1129  		},
  1130  		Resources: getArgoServerResources(cr),
  1131  		SecurityContext: &corev1.SecurityContext{
  1132  			AllowPrivilegeEscalation: boolPtr(false),
  1133  			Capabilities: &corev1.Capabilities{
  1134  				Drop: []corev1.Capability{
  1135  					"ALL",
  1136  				},
  1137  			},
  1138  			RunAsNonRoot: boolPtr(true),
  1139  		},
  1140  		VolumeMounts: []corev1.VolumeMount{
  1141  			{
  1142  				Name:      "ssh-known-hosts",
  1143  				MountPath: "/app/config/ssh",
  1144  			}, {
  1145  				Name:      "tls-certs",
  1146  				MountPath: "/app/config/tls",
  1147  			},
  1148  			{
  1149  				Name:      "argocd-repo-server-tls",
  1150  				MountPath: "/app/config/server/tls",
  1151  			},
  1152  			{
  1153  				Name:      common.ArgoCDRedisServerTLSSecretName,
  1154  				MountPath: "/app/config/server/tls/redis",
  1155  			},
  1156  		},
  1157  	}}
  1158  	deploy.Spec.Template.Spec.ServiceAccountName = fmt.Sprintf("%s-%s", cr.Name, "argocd-server")
  1159  	deploy.Spec.Template.Spec.Volumes = []corev1.Volume{
  1160  		{
  1161  			Name: "ssh-known-hosts",
  1162  			VolumeSource: corev1.VolumeSource{
  1163  				ConfigMap: &corev1.ConfigMapVolumeSource{
  1164  					LocalObjectReference: corev1.LocalObjectReference{
  1165  						Name: common.ArgoCDKnownHostsConfigMapName,
  1166  					},
  1167  				},
  1168  			},
  1169  		},
  1170  		{
  1171  			Name: "tls-certs",
  1172  			VolumeSource: corev1.VolumeSource{
  1173  				ConfigMap: &corev1.ConfigMapVolumeSource{
  1174  					LocalObjectReference: corev1.LocalObjectReference{
  1175  						Name: common.ArgoCDTLSCertsConfigMapName,
  1176  					},
  1177  				},
  1178  			},
  1179  		},
  1180  		{
  1181  			Name: "argocd-repo-server-tls",
  1182  			VolumeSource: corev1.VolumeSource{
  1183  				Secret: &corev1.SecretVolumeSource{
  1184  					SecretName: common.ArgoCDRepoServerTLSSecretName,
  1185  					Optional:   boolPtr(true),
  1186  				},
  1187  			},
  1188  		},
  1189  		{
  1190  			Name: common.ArgoCDRedisServerTLSSecretName,
  1191  			VolumeSource: corev1.VolumeSource{
  1192  				Secret: &corev1.SecretVolumeSource{
  1193  					SecretName: common.ArgoCDRedisServerTLSSecretName,
  1194  					Optional:   boolPtr(true),
  1195  				},
  1196  			},
  1197  		},
  1198  	}
  1199  
  1200  	if replicas := getArgoCDServerReplicas(cr); replicas != nil {
  1201  		deploy.Spec.Replicas = replicas
  1202  	}
  1203  
  1204  	existing := newDeploymentWithSuffix("server", "server", cr)
  1205  	if argoutil.IsObjectFound(r.Client, cr.Namespace, existing.Name, existing) {
  1206  		if !cr.Spec.Server.IsEnabled() {
  1207  			log.Info("Existing ArgoCD Server found but should be disabled. Deleting ArgoCD Server")
  1208  			// Delete existing deployment for ArgoCD Server, if any ..
  1209  			return r.Client.Delete(context.TODO(), existing)
  1210  		}
  1211  		actualImage := existing.Spec.Template.Spec.Containers[0].Image
  1212  		desiredImage := getArgoContainerImage(cr)
  1213  		changed := false
  1214  		if actualImage != desiredImage {
  1215  			existing.Spec.Template.Spec.Containers[0].Image = desiredImage
  1216  			existing.Spec.Template.ObjectMeta.Labels["image.upgraded"] = time.Now().UTC().Format("01022006-150406-MST")
  1217  			changed = true
  1218  		}
  1219  		updateNodePlacement(existing, deploy, &changed)
  1220  		if !reflect.DeepEqual(existing.Spec.Template.Spec.Containers[0].Env,
  1221  			deploy.Spec.Template.Spec.Containers[0].Env) {
  1222  			existing.Spec.Template.Spec.Containers[0].Env = deploy.Spec.Template.Spec.Containers[0].Env
  1223  			changed = true
  1224  		}
  1225  		if !reflect.DeepEqual(existing.Spec.Template.Spec.Containers[0].Command,
  1226  			deploy.Spec.Template.Spec.Containers[0].Command) {
  1227  			existing.Spec.Template.Spec.Containers[0].Command = deploy.Spec.Template.Spec.Containers[0].Command
  1228  			changed = true
  1229  		}
  1230  		if !reflect.DeepEqual(deploy.Spec.Template.Spec.Volumes, existing.Spec.Template.Spec.Volumes) {
  1231  			existing.Spec.Template.Spec.Volumes = deploy.Spec.Template.Spec.Volumes
  1232  			changed = true
  1233  		}
  1234  		if !reflect.DeepEqual(deploy.Spec.Template.Spec.Containers[0].VolumeMounts,
  1235  			existing.Spec.Template.Spec.Containers[0].VolumeMounts) {
  1236  			existing.Spec.Template.Spec.Containers[0].VolumeMounts = deploy.Spec.Template.Spec.Containers[0].VolumeMounts
  1237  			changed = true
  1238  		}
  1239  		if !reflect.DeepEqual(deploy.Spec.Template.Spec.Containers[0].Resources,
  1240  			existing.Spec.Template.Spec.Containers[0].Resources) {
  1241  			existing.Spec.Template.Spec.Containers[0].Resources = deploy.Spec.Template.Spec.Containers[0].Resources
  1242  			changed = true
  1243  		}
  1244  		if !reflect.DeepEqual(deploy.Spec.Replicas, existing.Spec.Replicas) {
  1245  			if !cr.Spec.Server.Autoscale.Enabled {
  1246  				existing.Spec.Replicas = deploy.Spec.Replicas
  1247  				changed = true
  1248  			}
  1249  		}
  1250  		if changed {
  1251  			return r.Client.Update(context.TODO(), existing)
  1252  		}
  1253  		return nil // Deployment found with nothing to do, move along...
  1254  	}
  1255  
  1256  	if !cr.Spec.Server.IsEnabled() {
  1257  		log.Info("ArgoCD Server disabled. Skipping starting argocd server.")
  1258  		return nil
  1259  	}
  1260  
  1261  	if err := controllerutil.SetControllerReference(cr, deploy, r.Scheme); err != nil {
  1262  		return err
  1263  	}
  1264  	return r.Client.Create(context.TODO(), deploy)
  1265  }
  1266  
  1267  // triggerDeploymentRollout will update the label with the given key to trigger a new rollout of the Deployment.
  1268  func (r *ReconcileArgoCD) triggerDeploymentRollout(deployment *appsv1.Deployment, key string) error {
  1269  	if !argoutil.IsObjectFound(r.Client, deployment.Namespace, deployment.Name, deployment) {
  1270  		log.Info(fmt.Sprintf("unable to locate deployment with name: %s", deployment.Name))
  1271  		return nil
  1272  	}
  1273  
  1274  	deployment.Spec.Template.ObjectMeta.Labels[key] = nowNano()
  1275  	return r.Client.Update(context.TODO(), deployment)
  1276  }
  1277  
  1278  func proxyEnvVars(vars ...corev1.EnvVar) []corev1.EnvVar {
  1279  	result := []corev1.EnvVar{}
  1280  	result = append(result, vars...)
  1281  	proxyKeys := []string{"HTTP_PROXY", "HTTPS_PROXY", "NO_PROXY"}
  1282  	for _, p := range proxyKeys {
  1283  		if k, v := caseInsensitiveGetenv(p); k != "" {
  1284  			result = append(result, corev1.EnvVar{Name: k, Value: v})
  1285  		}
  1286  	}
  1287  	return result
  1288  }
  1289  
  1290  func caseInsensitiveGetenv(s string) (string, string) {
  1291  	if v := os.Getenv(s); v != "" {
  1292  		return s, v
  1293  	}
  1294  	ls := strings.ToLower(s)
  1295  	if v := os.Getenv(ls); v != "" {
  1296  		return ls, v
  1297  	}
  1298  	return "", ""
  1299  }
  1300  
  1301  func isRemoveManagedByLabelOnArgoCDDeletion() bool {
  1302  	if v := os.Getenv("REMOVE_MANAGED_BY_LABEL_ON_ARGOCD_DELETION"); v != "" {
  1303  		return strings.ToLower(v) == "true"
  1304  	}
  1305  	return false
  1306  }
  1307  
  1308  // to update nodeSelector and tolerations in reconciler
  1309  func updateNodePlacement(existing *appsv1.Deployment, deploy *appsv1.Deployment, changed *bool) {
  1310  	if !reflect.DeepEqual(existing.Spec.Template.Spec.NodeSelector, deploy.Spec.Template.Spec.NodeSelector) {
  1311  		existing.Spec.Template.Spec.NodeSelector = deploy.Spec.Template.Spec.NodeSelector
  1312  		*changed = true
  1313  	}
  1314  	if !reflect.DeepEqual(existing.Spec.Template.Spec.Tolerations, deploy.Spec.Template.Spec.Tolerations) {
  1315  		existing.Spec.Template.Spec.Tolerations = deploy.Spec.Template.Spec.Tolerations
  1316  		*changed = true
  1317  	}
  1318  }