github.com/argoproj-labs/argocd-operator@v0.10.0/controllers/argocd/secret.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  	"crypto/rsa"
    20  	"crypto/sha256"
    21  	"crypto/x509"
    22  	"encoding/json"
    23  	"fmt"
    24  	"os"
    25  	"sort"
    26  	"strings"
    27  	"time"
    28  
    29  	argopass "github.com/argoproj/argo-cd/v2/util/password"
    30  	tlsutil "github.com/operator-framework/operator-sdk/pkg/tls"
    31  
    32  	argoproj "github.com/argoproj-labs/argocd-operator/api/v1beta1"
    33  	"github.com/argoproj-labs/argocd-operator/common"
    34  	"github.com/argoproj-labs/argocd-operator/controllers/argoutil"
    35  
    36  	corev1 "k8s.io/api/core/v1"
    37  	apierrors "k8s.io/apimachinery/pkg/api/errors"
    38  	"k8s.io/apimachinery/pkg/labels"
    39  	"k8s.io/apimachinery/pkg/types"
    40  	"sigs.k8s.io/controller-runtime/pkg/client"
    41  	"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
    42  )
    43  
    44  // hasArgoAdminPasswordChanged will return true if the Argo admin password has changed.
    45  func hasArgoAdminPasswordChanged(actual *corev1.Secret, expected *corev1.Secret) bool {
    46  	actualPwd := string(actual.Data[common.ArgoCDKeyAdminPassword])
    47  	expectedPwd := string(expected.Data[common.ArgoCDKeyAdminPassword])
    48  
    49  	validPwd, _ := argopass.VerifyPassword(expectedPwd, actualPwd)
    50  	if !validPwd {
    51  		log.Info("admin password has changed")
    52  		return true
    53  	}
    54  	return false
    55  }
    56  
    57  // hasArgoTLSChanged will return true if the Argo TLS certificate or key have changed.
    58  func hasArgoTLSChanged(actual *corev1.Secret, expected *corev1.Secret) bool {
    59  	actualCert := string(actual.Data[common.ArgoCDKeyTLSCert])
    60  	actualKey := string(actual.Data[common.ArgoCDKeyTLSPrivateKey])
    61  	expectedCert := string(expected.Data[common.ArgoCDKeyTLSCert])
    62  	expectedKey := string(expected.Data[common.ArgoCDKeyTLSPrivateKey])
    63  
    64  	if actualCert != expectedCert || actualKey != expectedKey {
    65  		log.Info("tls secret has changed")
    66  		return true
    67  	}
    68  	return false
    69  }
    70  
    71  // nowBytes is a shortcut function to return the current date/time in RFC3339 format.
    72  func nowBytes() []byte {
    73  	return []byte(time.Now().UTC().Format(time.RFC3339))
    74  }
    75  
    76  // nowNano returns a string with the current UTC time as epoch in nanoseconds
    77  func nowNano() string {
    78  	return fmt.Sprintf("%d", time.Now().UTC().UnixNano())
    79  }
    80  
    81  // newCASecret creates a new CA secret with the given suffix for the given ArgoCD.
    82  func newCASecret(cr *argoproj.ArgoCD) (*corev1.Secret, error) {
    83  	secret := argoutil.NewTLSSecret(cr, "ca")
    84  
    85  	key, err := argoutil.NewPrivateKey()
    86  	if err != nil {
    87  		return nil, err
    88  	}
    89  
    90  	cert, err := argoutil.NewSelfSignedCACertificate(cr.Name, key)
    91  	if err != nil {
    92  		return nil, err
    93  	}
    94  
    95  	// This puts both ca.crt and tls.crt into the secret.
    96  	secret.Data = map[string][]byte{
    97  		corev1.TLSCertKey:              argoutil.EncodeCertificatePEM(cert),
    98  		corev1.ServiceAccountRootCAKey: argoutil.EncodeCertificatePEM(cert),
    99  		corev1.TLSPrivateKeyKey:        argoutil.EncodePrivateKeyPEM(key),
   100  	}
   101  
   102  	return secret, nil
   103  }
   104  
   105  // newCertificateSecret creates a new secret using the given name suffix for the given TLS certificate.
   106  func newCertificateSecret(suffix string, caCert *x509.Certificate, caKey *rsa.PrivateKey, cr *argoproj.ArgoCD) (*corev1.Secret, error) {
   107  	secret := argoutil.NewTLSSecret(cr, suffix)
   108  
   109  	key, err := argoutil.NewPrivateKey()
   110  	if err != nil {
   111  		return nil, err
   112  	}
   113  
   114  	cfg := &tlsutil.CertConfig{
   115  		CertName:     secret.Name,
   116  		CertType:     tlsutil.ClientAndServingCert,
   117  		CommonName:   secret.Name,
   118  		Organization: []string{cr.ObjectMeta.Namespace},
   119  	}
   120  
   121  	dnsNames := []string{
   122  		cr.ObjectMeta.Name,
   123  		nameWithSuffix("grpc", cr),
   124  		fmt.Sprintf("%s.%s.svc.cluster.local", cr.ObjectMeta.Name, cr.ObjectMeta.Namespace),
   125  	}
   126  
   127  	if cr.Spec.Grafana.Enabled {
   128  		log.Info(grafanaDeprecatedWarning)
   129  	}
   130  	if cr.Spec.Prometheus.Enabled {
   131  		dnsNames = append(dnsNames, getPrometheusHost(cr))
   132  	}
   133  
   134  	cert, err := argoutil.NewSignedCertificate(cfg, dnsNames, key, caCert, caKey)
   135  	if err != nil {
   136  		return nil, err
   137  	}
   138  
   139  	secret.Data = map[string][]byte{
   140  		corev1.TLSCertKey:       argoutil.EncodeCertificatePEM(cert),
   141  		corev1.TLSPrivateKeyKey: argoutil.EncodePrivateKeyPEM(key),
   142  	}
   143  
   144  	return secret, nil
   145  }
   146  
   147  // reconcileArgoSecret will ensure that the Argo CD Secret is present.
   148  func (r *ReconcileArgoCD) reconcileArgoSecret(cr *argoproj.ArgoCD) error {
   149  	clusterSecret := argoutil.NewSecretWithSuffix(cr, "cluster")
   150  	secret := argoutil.NewSecretWithName(cr, common.ArgoCDSecretName)
   151  
   152  	if !argoutil.IsObjectFound(r.Client, cr.Namespace, clusterSecret.Name, clusterSecret) {
   153  		log.Info(fmt.Sprintf("cluster secret [%s] not found, waiting to reconcile argo secret [%s]", clusterSecret.Name, secret.Name))
   154  		return nil
   155  	}
   156  
   157  	tlsSecret := argoutil.NewSecretWithSuffix(cr, "tls")
   158  	if !argoutil.IsObjectFound(r.Client, cr.Namespace, tlsSecret.Name, tlsSecret) {
   159  		log.Info(fmt.Sprintf("tls secret [%s] not found, waiting to reconcile argo secret [%s]", tlsSecret.Name, secret.Name))
   160  		return nil
   161  	}
   162  
   163  	if argoutil.IsObjectFound(r.Client, cr.Namespace, secret.Name, secret) {
   164  		return r.reconcileExistingArgoSecret(cr, secret, clusterSecret, tlsSecret)
   165  	}
   166  
   167  	// Secret not found, create it...
   168  	hashedPassword, err := argopass.HashPassword(string(clusterSecret.Data[common.ArgoCDKeyAdminPassword]))
   169  	if err != nil {
   170  		return err
   171  	}
   172  
   173  	sessionKey, err := generateArgoServerSessionKey()
   174  	if err != nil {
   175  		return err
   176  	}
   177  
   178  	secret.Data = map[string][]byte{
   179  		common.ArgoCDKeyAdminPassword:      []byte(hashedPassword),
   180  		common.ArgoCDKeyAdminPasswordMTime: nowBytes(),
   181  		common.ArgoCDKeyServerSecretKey:    sessionKey,
   182  		common.ArgoCDKeyTLSCert:            tlsSecret.Data[common.ArgoCDKeyTLSCert],
   183  		common.ArgoCDKeyTLSPrivateKey:      tlsSecret.Data[common.ArgoCDKeyTLSPrivateKey],
   184  	}
   185  
   186  	if cr.Spec.SSO != nil && cr.Spec.SSO.Provider.ToLower() == argoproj.SSOProviderTypeDex {
   187  		dexOIDCClientSecret, err := r.getDexOAuthClientSecret(cr)
   188  		if err != nil {
   189  			return nil
   190  		}
   191  		secret.Data[common.ArgoCDDexSecretKey] = []byte(*dexOIDCClientSecret)
   192  	}
   193  
   194  	if err := controllerutil.SetControllerReference(cr, secret, r.Scheme); err != nil {
   195  		return err
   196  	}
   197  	return r.Client.Create(context.TODO(), secret)
   198  }
   199  
   200  // reconcileClusterMainSecret will ensure that the main Secret is present for the Argo CD cluster.
   201  func (r *ReconcileArgoCD) reconcileClusterMainSecret(cr *argoproj.ArgoCD) error {
   202  	secret := argoutil.NewSecretWithSuffix(cr, "cluster")
   203  	if argoutil.IsObjectFound(r.Client, cr.Namespace, secret.Name, secret) {
   204  		return nil // Secret found, do nothing
   205  	}
   206  
   207  	adminPassword, err := generateArgoAdminPassword()
   208  	if err != nil {
   209  		return err
   210  	}
   211  
   212  	secret.Data = map[string][]byte{
   213  		common.ArgoCDKeyAdminPassword: adminPassword,
   214  	}
   215  
   216  	if err := controllerutil.SetControllerReference(cr, secret, r.Scheme); err != nil {
   217  		return err
   218  	}
   219  	return r.Client.Create(context.TODO(), secret)
   220  }
   221  
   222  // reconcileClusterTLSSecret ensures the TLS Secret is created for the ArgoCD cluster.
   223  func (r *ReconcileArgoCD) reconcileClusterTLSSecret(cr *argoproj.ArgoCD) error {
   224  	secret := argoutil.NewTLSSecret(cr, "tls")
   225  	if argoutil.IsObjectFound(r.Client, cr.Namespace, secret.Name, secret) {
   226  		return nil // Secret found, do nothing
   227  	}
   228  
   229  	caSecret := argoutil.NewSecretWithSuffix(cr, "ca")
   230  	caSecret, err := argoutil.FetchSecret(r.Client, cr.ObjectMeta, caSecret.Name)
   231  	if err != nil {
   232  		return err
   233  	}
   234  
   235  	caCert, err := argoutil.ParsePEMEncodedCert(caSecret.Data[corev1.TLSCertKey])
   236  	if err != nil {
   237  		return err
   238  	}
   239  
   240  	caKey, err := argoutil.ParsePEMEncodedPrivateKey(caSecret.Data[corev1.TLSPrivateKeyKey])
   241  	if err != nil {
   242  		return err
   243  	}
   244  
   245  	secret, err = newCertificateSecret("tls", caCert, caKey, cr)
   246  	if err != nil {
   247  		return err
   248  	}
   249  
   250  	if err := controllerutil.SetControllerReference(cr, secret, r.Scheme); err != nil {
   251  		return err
   252  	}
   253  
   254  	return r.Client.Create(context.TODO(), secret)
   255  }
   256  
   257  // reconcileClusterCASecret ensures the CA Secret is created for the ArgoCD cluster.
   258  func (r *ReconcileArgoCD) reconcileClusterCASecret(cr *argoproj.ArgoCD) error {
   259  	secret := argoutil.NewSecretWithSuffix(cr, "ca")
   260  	if argoutil.IsObjectFound(r.Client, cr.Namespace, secret.Name, secret) {
   261  		return nil // Secret found, do nothing
   262  	}
   263  
   264  	secret, err := newCASecret(cr)
   265  	if err != nil {
   266  		return err
   267  	}
   268  
   269  	if err := controllerutil.SetControllerReference(cr, secret, r.Scheme); err != nil {
   270  		return err
   271  	}
   272  	return r.Client.Create(context.TODO(), secret)
   273  }
   274  
   275  // reconcileClusterSecrets will reconcile all Secret resources for the ArgoCD cluster.
   276  func (r *ReconcileArgoCD) reconcileClusterSecrets(cr *argoproj.ArgoCD) error {
   277  	if err := r.reconcileClusterMainSecret(cr); err != nil {
   278  		return err
   279  	}
   280  
   281  	if err := r.reconcileClusterCASecret(cr); err != nil {
   282  		return err
   283  	}
   284  
   285  	if err := r.reconcileClusterTLSSecret(cr); err != nil {
   286  		return err
   287  	}
   288  
   289  	if err := r.reconcileClusterPermissionsSecret(cr); err != nil {
   290  		return err
   291  	}
   292  
   293  	if err := r.reconcileGrafanaSecret(cr); err != nil {
   294  		return err
   295  	}
   296  
   297  	return nil
   298  }
   299  
   300  // reconcileExistingArgoSecret will ensure that the Argo CD Secret is up to date.
   301  func (r *ReconcileArgoCD) reconcileExistingArgoSecret(cr *argoproj.ArgoCD, secret *corev1.Secret, clusterSecret *corev1.Secret, tlsSecret *corev1.Secret) error {
   302  	changed := false
   303  
   304  	if secret.Data == nil {
   305  		secret.Data = make(map[string][]byte)
   306  	}
   307  
   308  	if secret.Data[common.ArgoCDKeyServerSecretKey] == nil {
   309  		sessionKey, err := generateArgoServerSessionKey()
   310  		if err != nil {
   311  			return err
   312  		}
   313  		secret.Data[common.ArgoCDKeyServerSecretKey] = sessionKey
   314  	}
   315  
   316  	// reset the value to default only when secret.data field is nil
   317  	if hasArgoAdminPasswordChanged(secret, clusterSecret) {
   318  		pwBytes, ok := clusterSecret.Data[common.ArgoCDKeyAdminPassword]
   319  		if ok && secret.Data[common.ArgoCDKeyAdminPassword] == nil {
   320  			hashedPassword, err := argopass.HashPassword(strings.TrimRight(string(pwBytes), "\n"))
   321  			if err != nil {
   322  				return err
   323  			}
   324  
   325  			secret.Data[common.ArgoCDKeyAdminPassword] = []byte(hashedPassword)
   326  			secret.Data[common.ArgoCDKeyAdminPasswordMTime] = nowBytes()
   327  			changed = true
   328  		}
   329  	}
   330  
   331  	if hasArgoTLSChanged(secret, tlsSecret) {
   332  		secret.Data[common.ArgoCDKeyTLSCert] = tlsSecret.Data[common.ArgoCDKeyTLSCert]
   333  		secret.Data[common.ArgoCDKeyTLSPrivateKey] = tlsSecret.Data[common.ArgoCDKeyTLSPrivateKey]
   334  		changed = true
   335  	}
   336  
   337  	if cr.Spec.SSO != nil && cr.Spec.SSO.Provider.ToLower() == argoproj.SSOProviderTypeDex {
   338  		dexOIDCClientSecret, err := r.getDexOAuthClientSecret(cr)
   339  		if err != nil {
   340  			return err
   341  		}
   342  		actual := string(secret.Data[common.ArgoCDDexSecretKey])
   343  		if dexOIDCClientSecret != nil {
   344  			expected := *dexOIDCClientSecret
   345  			if actual != expected {
   346  				secret.Data[common.ArgoCDDexSecretKey] = []byte(*dexOIDCClientSecret)
   347  				changed = true
   348  			}
   349  		}
   350  	}
   351  
   352  	if changed {
   353  		log.Info("updating argo secret")
   354  		if err := r.Client.Update(context.TODO(), secret); err != nil {
   355  			return err
   356  		}
   357  	}
   358  
   359  	return nil
   360  }
   361  
   362  // reconcileGrafanaSecret will ensure that the Grafana Secret is present.
   363  func (r *ReconcileArgoCD) reconcileGrafanaSecret(cr *argoproj.ArgoCD) error {
   364  	if !cr.Spec.Grafana.Enabled {
   365  		return nil // Grafana not enabled, do nothing.
   366  	}
   367  
   368  	log.Info(grafanaDeprecatedWarning)
   369  
   370  	return nil
   371  }
   372  
   373  // reconcileClusterPermissionsSecret ensures ArgoCD instance is namespace-scoped
   374  func (r *ReconcileArgoCD) reconcileClusterPermissionsSecret(cr *argoproj.ArgoCD) error {
   375  	var clusterConfigInstance bool
   376  	secret := argoutil.NewSecretWithSuffix(cr, "default-cluster-config")
   377  	secret.Labels[common.ArgoCDSecretTypeLabel] = "cluster"
   378  	dataBytes, _ := json.Marshal(map[string]interface{}{
   379  		"tlsClientConfig": map[string]interface{}{
   380  			"insecure": false,
   381  		},
   382  	})
   383  
   384  	namespaceList := corev1.NamespaceList{}
   385  	listOption := client.MatchingLabels{
   386  		common.ArgoCDManagedByLabel: cr.Namespace,
   387  	}
   388  	if err := r.Client.List(context.TODO(), &namespaceList, listOption); err != nil {
   389  		return err
   390  	}
   391  
   392  	var namespaces []string
   393  	for _, namespace := range namespaceList.Items {
   394  		namespaces = append(namespaces, namespace.Name)
   395  	}
   396  
   397  	if !containsString(namespaces, cr.Namespace) {
   398  		namespaces = append(namespaces, cr.Namespace)
   399  	}
   400  	sort.Strings(namespaces)
   401  
   402  	secret.Data = map[string][]byte{
   403  		"config":     dataBytes,
   404  		"name":       []byte("in-cluster"),
   405  		"server":     []byte(common.ArgoCDDefaultServer),
   406  		"namespaces": []byte(strings.Join(namespaces, ",")),
   407  	}
   408  
   409  	if allowedNamespace(cr.Namespace, os.Getenv("ARGOCD_CLUSTER_CONFIG_NAMESPACES")) {
   410  		clusterConfigInstance = true
   411  	}
   412  
   413  	clusterSecrets, err := r.getClusterSecrets(cr)
   414  	if err != nil {
   415  		return err
   416  	}
   417  
   418  	for _, s := range clusterSecrets.Items {
   419  		// check if cluster secret with default server address exists
   420  		if string(s.Data["server"]) == common.ArgoCDDefaultServer {
   421  			// if the cluster belongs to cluster config namespace,
   422  			// remove all namespaces from cluster secret,
   423  			// else update the list of namespaces if value differs.
   424  			if clusterConfigInstance {
   425  				delete(s.Data, "namespaces")
   426  			} else {
   427  				ns := strings.Split(string(s.Data["namespaces"]), ",")
   428  				for _, n := range namespaces {
   429  					if !containsString(ns, strings.TrimSpace(n)) {
   430  						ns = append(ns, strings.TrimSpace(n))
   431  					}
   432  				}
   433  				sort.Strings(ns)
   434  				s.Data["namespaces"] = []byte(strings.Join(ns, ","))
   435  			}
   436  			return r.Client.Update(context.TODO(), &s)
   437  		}
   438  	}
   439  
   440  	if clusterConfigInstance {
   441  		// do nothing
   442  		return nil
   443  	}
   444  
   445  	if err := controllerutil.SetControllerReference(cr, secret, r.Scheme); err != nil {
   446  		return err
   447  	}
   448  	return r.Client.Create(context.TODO(), secret)
   449  }
   450  
   451  // reconcileRepoServerTLSSecret checks whether the argocd-repo-server-tls secret
   452  // has changed since our last reconciliation loop. It does so by comparing the
   453  // checksum of tls.crt and tls.key in the status of the ArgoCD CR against the
   454  // values calculated from the live state in the cluster.
   455  func (r *ReconcileArgoCD) reconcileRepoServerTLSSecret(cr *argoproj.ArgoCD) error {
   456  	var tlsSecretObj corev1.Secret
   457  	var sha256sum string
   458  
   459  	log.Info("reconciling repo-server TLS secret")
   460  
   461  	tlsSecretName := types.NamespacedName{Namespace: cr.Namespace, Name: common.ArgoCDRepoServerTLSSecretName}
   462  	err := r.Client.Get(context.TODO(), tlsSecretName, &tlsSecretObj)
   463  	if err != nil {
   464  		if !apierrors.IsNotFound(err) {
   465  			return err
   466  		}
   467  	} else if tlsSecretObj.Type != corev1.SecretTypeTLS {
   468  		// We only process secrets of type kubernetes.io/tls
   469  		return nil
   470  	} else {
   471  		// We do the checksum over a concatenated byte stream of cert + key
   472  		crt, crtOk := tlsSecretObj.Data[corev1.TLSCertKey]
   473  		key, keyOk := tlsSecretObj.Data[corev1.TLSPrivateKeyKey]
   474  		if crtOk && keyOk {
   475  			var sumBytes []byte
   476  			sumBytes = append(sumBytes, crt...)
   477  			sumBytes = append(sumBytes, key...)
   478  			sha256sum = fmt.Sprintf("%x", sha256.Sum256(sumBytes))
   479  		}
   480  	}
   481  
   482  	// The content of the TLS secret has changed since we last looked if the
   483  	// calculated checksum doesn't match the one stored in the status.
   484  	if cr.Status.RepoTLSChecksum != sha256sum {
   485  		// We store the value early to prevent a possible restart loop, for the
   486  		// cost of a possibly missed restart when we cannot update the status
   487  		// field of the resource.
   488  		cr.Status.RepoTLSChecksum = sha256sum
   489  		err = r.Client.Status().Update(context.TODO(), cr)
   490  		if err != nil {
   491  			return err
   492  		}
   493  
   494  		// Trigger rollout of API server
   495  		apiDepl := newDeploymentWithSuffix("server", "server", cr)
   496  		err = r.triggerRollout(apiDepl, "repo.tls.cert.changed")
   497  		if err != nil {
   498  			return err
   499  		}
   500  
   501  		// Trigger rollout of repository server
   502  		repoDepl := newDeploymentWithSuffix("repo-server", "repo-server", cr)
   503  		err = r.triggerRollout(repoDepl, "repo.tls.cert.changed")
   504  		if err != nil {
   505  			return err
   506  		}
   507  
   508  		// Trigger rollout of application controller
   509  		controllerSts := newStatefulSetWithSuffix("application-controller", "application-controller", cr)
   510  		err = r.triggerRollout(controllerSts, "repo.tls.cert.changed")
   511  		if err != nil {
   512  			return err
   513  		}
   514  	}
   515  
   516  	return nil
   517  }
   518  
   519  // reconcileRedisTLSSecret checks whether the argocd-operator-redis-tls secret
   520  // has changed since our last reconciliation loop. It does so by comparing the
   521  // checksum of tls.crt and tls.key in the status of the ArgoCD CR against the
   522  // values calculated from the live state in the cluster.
   523  func (r *ReconcileArgoCD) reconcileRedisTLSSecret(cr *argoproj.ArgoCD, useTLSForRedis bool) error {
   524  	var tlsSecretObj corev1.Secret
   525  	var sha256sum string
   526  
   527  	log.Info("reconciling redis-server TLS secret")
   528  
   529  	tlsSecretName := types.NamespacedName{Namespace: cr.Namespace, Name: common.ArgoCDRedisServerTLSSecretName}
   530  	err := r.Client.Get(context.TODO(), tlsSecretName, &tlsSecretObj)
   531  	if err != nil {
   532  		if !apierrors.IsNotFound(err) {
   533  			return err
   534  		}
   535  	} else if tlsSecretObj.Type != corev1.SecretTypeTLS {
   536  		// We only process secrets of type kubernetes.io/tls
   537  		return nil
   538  	} else {
   539  		// We do the checksum over a concatenated byte stream of cert + key
   540  		crt, crtOk := tlsSecretObj.Data[corev1.TLSCertKey]
   541  		key, keyOk := tlsSecretObj.Data[corev1.TLSPrivateKeyKey]
   542  		if crtOk && keyOk {
   543  			var sumBytes []byte
   544  			sumBytes = append(sumBytes, crt...)
   545  			sumBytes = append(sumBytes, key...)
   546  			sha256sum = fmt.Sprintf("%x", sha256.Sum256(sumBytes))
   547  		}
   548  	}
   549  
   550  	// The content of the TLS secret has changed since we last looked if the
   551  	// calculated checksum doesn't match the one stored in the status.
   552  	if cr.Status.RedisTLSChecksum != sha256sum {
   553  		// We store the value early to prevent a possible restart loop, for the
   554  		// cost of a possibly missed restart when we cannot update the status
   555  		// field of the resource.
   556  		cr.Status.RedisTLSChecksum = sha256sum
   557  		err = r.Client.Status().Update(context.TODO(), cr)
   558  		if err != nil {
   559  			return err
   560  		}
   561  
   562  		// Trigger rollout of redis
   563  		if cr.Spec.HA.Enabled {
   564  			err = r.recreateRedisHAConfigMap(cr, useTLSForRedis)
   565  			if err != nil {
   566  				return err
   567  			}
   568  			err = r.recreateRedisHAHealthConfigMap(cr, useTLSForRedis)
   569  			if err != nil {
   570  				return err
   571  			}
   572  			haProxyDepl := newDeploymentWithSuffix("redis-ha-haproxy", "redis", cr)
   573  			err = r.triggerRollout(haProxyDepl, "redis.tls.cert.changed")
   574  			if err != nil {
   575  				return err
   576  			}
   577  			// If we use triggerRollout on the redis stateful set, kubernetes will attempt to restart the  pods
   578  			// one at a time, and the first one to restart (which will be using tls) will hang as it tries to
   579  			// communicate with the existing pods (which are not using tls) to establish which is the master.
   580  			// So instead we delete the stateful set, which will delete all the pods.
   581  			redisSts := newStatefulSetWithSuffix("redis-ha-server", "redis", cr)
   582  			if argoutil.IsObjectFound(r.Client, redisSts.Namespace, redisSts.Name, redisSts) {
   583  				err = r.Client.Delete(context.TODO(), redisSts)
   584  				if err != nil {
   585  					return err
   586  				}
   587  			}
   588  		} else {
   589  			redisDepl := newDeploymentWithSuffix("redis", "redis", cr)
   590  			err = r.triggerRollout(redisDepl, "redis.tls.cert.changed")
   591  			if err != nil {
   592  				return err
   593  			}
   594  		}
   595  
   596  		// Trigger rollout of API server
   597  		apiDepl := newDeploymentWithSuffix("server", "server", cr)
   598  		err = r.triggerRollout(apiDepl, "redis.tls.cert.changed")
   599  		if err != nil {
   600  			return err
   601  		}
   602  
   603  		// Trigger rollout of repository server
   604  		repoDepl := newDeploymentWithSuffix("repo-server", "repo-server", cr)
   605  		err = r.triggerRollout(repoDepl, "redis.tls.cert.changed")
   606  		if err != nil {
   607  			return err
   608  		}
   609  
   610  		// Trigger rollout of application controller
   611  		controllerSts := newStatefulSetWithSuffix("application-controller", "application-controller", cr)
   612  		err = r.triggerRollout(controllerSts, "redis.tls.cert.changed")
   613  		if err != nil {
   614  			return err
   615  		}
   616  	}
   617  
   618  	return nil
   619  }
   620  
   621  // reconcileSecrets will reconcile all ArgoCD Secret resources.
   622  func (r *ReconcileArgoCD) reconcileSecrets(cr *argoproj.ArgoCD) error {
   623  	if err := r.reconcileClusterSecrets(cr); err != nil {
   624  		return err
   625  	}
   626  
   627  	if err := r.reconcileArgoSecret(cr); err != nil {
   628  		return err
   629  	}
   630  
   631  	return nil
   632  }
   633  
   634  func (r *ReconcileArgoCD) getClusterSecrets(cr *argoproj.ArgoCD) (*corev1.SecretList, error) {
   635  
   636  	clusterSecrets := &corev1.SecretList{}
   637  	opts := &client.ListOptions{
   638  		LabelSelector: labels.SelectorFromSet(map[string]string{
   639  			common.ArgoCDSecretTypeLabel: "cluster",
   640  		}),
   641  		Namespace: cr.Namespace,
   642  	}
   643  
   644  	if err := r.Client.List(context.TODO(), clusterSecrets, opts); err != nil {
   645  		return nil, err
   646  	}
   647  
   648  	return clusterSecrets, nil
   649  }