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 }