Repository navigation
GITOPS-11058 use argocd-redis secret by default #1320
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
9ef2889
38d6cfb
c0f03f3
a23bfcc
ff944d7
517451e
f188d51
6022d43
4c5a516
6c28bd4
9efdd3a
7ca48c7
c9466d6
11b8288
4d783f0
03d96a7
aff38b0
ee50874
256c9d9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -35,6 +35,7 @@ import ( | |
| "github.com/redhat-developer/gitops-operator/argocd-operator/common" | ||
| "github.com/redhat-developer/gitops-operator/argocd-operator/controllers/argoutil" | ||
|
|
||
| appsv1 "k8s.io/api/apps/v1" | ||
| corev1 "k8s.io/api/core/v1" | ||
| apierrors "k8s.io/apimachinery/pkg/api/errors" | ||
| metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
|
|
@@ -331,7 +332,7 @@ func (r *ReconcileArgoCD) reconcileClusterSecrets(cr *argoproj.ArgoCD) error { | |
| return err | ||
| } | ||
|
|
||
| if err := r.reconcileRedisInitialPasswordSecret(cr); err != nil { | ||
| if err := r.reconcileRedisSecret(cr); err != nil { | ||
| return err | ||
| } | ||
|
|
||
|
|
@@ -1141,49 +1142,211 @@ func (r *ReconcileArgoCD) getClusterSecrets(cr *argoproj.ArgoCD) (*corev1.Secret | |
| return clusterSecrets, nil | ||
| } | ||
|
|
||
| // reconcileRedisInitialPasswordSecret will ensure that the redis Secret is present for the cluster. | ||
| func (r *ReconcileArgoCD) reconcileRedisInitialPasswordSecret(cr *argoproj.ArgoCD) error { | ||
| secret := argoutil.NewSecretWithSuffix(cr, "redis-initial-password") | ||
| const REDIS_INITIAL_PASSWORD = "redis-initial-password" | ||
| const ARGOCD_REDIS = "argocd-redis" | ||
|
|
||
| func (r *ReconcileArgoCD) reconcileRedisSecret(cr *argoproj.ArgoCD) error { | ||
| // Fix for GITOPS-11058 to be compatible with upstream Argo CD naming conventions and Argo CD CLI in core mode. | ||
| // Old secret name [instance]-redis-initial-password | ||
| // New secret name argocd-redis - upstream | ||
|
|
||
| new := argoutil.NewSecretWithName(cr, ARGOCD_REDIS) | ||
| newExists, err := r.checkSecretExistence(cr, new) | ||
| if err != nil { | ||
| return err | ||
| } | ||
|
|
||
| if newExists { | ||
| if secretIsComplete(new) { | ||
| // Healthy - keep it | ||
| return r.deleteLegacyRedisSecret(cr) | ||
| } | ||
| // secret not healthy, update | ||
| new = argoutil.NewSecretWithName(cr, ARGOCD_REDIS) | ||
| redisInitialPassword, err := generateRedisAdminPassword() | ||
| if err != nil { | ||
| return err | ||
| } | ||
|
|
||
| new.Data = argoutil.GetRedisSecretData(redisInitialPassword) | ||
|
|
||
| err = controllerutil.SetControllerReference(cr, new, r.Scheme) | ||
| if err != nil { | ||
| return err | ||
| } | ||
|
|
||
| if err := r.deleteLegacyRedisSecret(cr); err != nil { | ||
| return err | ||
| } | ||
| argoutil.LogResourceUpdate(log, new) | ||
| return r.Update(context.TODO(), new) | ||
| } | ||
|
|
||
| // argocd-redis secret does not exist | ||
| old := argoutil.NewSecretWithSuffix(cr, REDIS_INITIAL_PASSWORD) | ||
| oldExists, err := r.checkSecretExistence(cr, old) | ||
| if err != nil { | ||
| return err | ||
| } | ||
|
|
||
| // We should not rely on completeness check for old secret | ||
| // Old secrets might not contain users.acl but still contain ArgoCDKeyAdminPassword or auth | ||
| var pwd []byte | ||
| if oldExists { | ||
| pwd = salvageRedisPassword(old) | ||
| } | ||
| if pwd == nil { | ||
| pwd, err = generateRedisAdminPassword() | ||
| if err != nil { | ||
| return err | ||
| } | ||
| } | ||
| new.Data = argoutil.GetRedisSecretData(pwd) | ||
|
|
||
| err = controllerutil.SetControllerReference(cr, new, r.Scheme) | ||
| if err != nil { | ||
| return err | ||
| } | ||
|
|
||
| if err := r.deleteLegacyRedisSecret(cr); err != nil { | ||
| return err | ||
| } | ||
| argoutil.LogResourceCreation(log, new) | ||
| return r.Create(context.TODO(), new) | ||
| } | ||
|
|
||
| func (r *ReconcileArgoCD) checkSecretExistence(cr *argoproj.ArgoCD, s *corev1.Secret) (bool, error) { | ||
| existed := true | ||
| // Recreate if the secret or some of its keys are missing | ||
| err := argoutil.FetchObject(r.Client, cr.Namespace, secret.Name, secret) | ||
| err := argoutil.FetchObject(r.Client, cr.Namespace, s.Name, s) | ||
| if err != nil { | ||
| if !apierrors.IsNotFound(err) { | ||
| return err | ||
| return false, err | ||
| } | ||
| existed = false | ||
| } | ||
| if secret.Data != nil { | ||
| _, hasPwd := secret.Data[common.ArgoCDKeyAdminPassword] | ||
| _, hasAuth := secret.Data["auth"] | ||
| _, hasUsername := secret.Data["auth_username"] | ||
| _, hasAcl := secret.Data["users.acl"] | ||
| if hasPwd && hasAuth && hasUsername && hasAcl { | ||
| return nil // Healthy - keep it | ||
| return existed, nil | ||
| } | ||
|
|
||
| // Checks secret usage in: Pod, Deployment or StatefulSet in the namespace | ||
| func (r *ReconcileArgoCD) secretInUse(namespace string, secretName string) (bool, error) { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Rather than doing the recocile here, can we do it in the respective reconcile logic. We shouldn't update pod spec directly. Should do it only via deployment or statefulsets
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think I didn't get what you mean by "We shouldn't update pod spec directly" as we don't update them. I would still keep the check for pod usage because we shouldn't delete secret while pod is still mounting the legacy secret. So checking pods usage until all of them are replaced is safer to avoid crash. What I would consider is moving the secret usage check to |
||
| pods := &corev1.PodList{} | ||
| if err := r.List(context.TODO(), pods, client.InNamespace(namespace)); err != nil { | ||
| return false, err | ||
| } | ||
| for i := range pods.Items { | ||
| if secretReferencedByPodSpec(&pods.Items[i].Spec, secretName) { | ||
| return true, nil | ||
| } | ||
| } | ||
|
|
||
| deployments := &appsv1.DeploymentList{} | ||
| if err := r.List(context.TODO(), deployments, client.InNamespace(namespace)); err != nil { | ||
| return false, err | ||
| } | ||
| for i := range deployments.Items { | ||
| if secretReferencedByPodSpec(&deployments.Items[i].Spec.Template.Spec, secretName) { | ||
| return true, nil | ||
| } | ||
| } | ||
|
|
||
| if existed { | ||
| // Drop unsettable fields created by FetchObject | ||
| secret = argoutil.NewSecretWithSuffix(cr, "redis-initial-password") | ||
| statefulSets := &appsv1.StatefulSetList{} | ||
| if err := r.List(context.TODO(), statefulSets, client.InNamespace(namespace)); err != nil { | ||
| return false, err | ||
| } | ||
| for i := range statefulSets.Items { | ||
| if secretReferencedByPodSpec(&statefulSets.Items[i].Spec.Template.Spec, secretName) { | ||
| return true, nil | ||
| } | ||
| } | ||
|
|
||
| return false, nil | ||
| } | ||
|
|
||
| func secretReferencedByPodSpec(spec *corev1.PodSpec, secretName string) bool { | ||
| for _, volume := range spec.Volumes { | ||
| if volume.Secret != nil && volume.Secret.SecretName == secretName { | ||
| return true | ||
| } | ||
| if volume.Projected == nil { | ||
| continue | ||
| } | ||
| for _, source := range volume.Projected.Sources { | ||
| if source.Secret != nil && source.Secret.Name == secretName { | ||
| return true | ||
| } | ||
| } | ||
| } | ||
|
|
||
| redisInitialPassword, err := generateRedisAdminPassword() | ||
| containers := append(append([]corev1.Container{}, spec.InitContainers...), spec.Containers...) | ||
| for _, container := range containers { | ||
| for _, env := range container.Env { | ||
| if env.ValueFrom != nil && env.ValueFrom.SecretKeyRef != nil && env.ValueFrom.SecretKeyRef.Name == secretName { | ||
| return true | ||
| } | ||
| } | ||
| for _, envFrom := range container.EnvFrom { | ||
| if envFrom.SecretRef != nil && envFrom.SecretRef.Name == secretName { | ||
| return true | ||
| } | ||
| } | ||
| } | ||
|
|
||
| return false | ||
| } | ||
|
|
||
| func (r *ReconcileArgoCD) deleteLegacyRedisSecret(cr *argoproj.ArgoCD) error { | ||
| // Delete [instance]-redis-initial-password | ||
| legacy := argoutil.NewSecretWithSuffix(cr, REDIS_INITIAL_PASSWORD) | ||
|
|
||
| exists, err := r.checkSecretExistence(cr, legacy) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| if !exists { | ||
| return nil | ||
| } | ||
|
|
||
| secret.Data = argoutil.GetRedisSecretData(redisInitialPassword) | ||
|
|
||
| if err := controllerutil.SetControllerReference(cr, secret, r.Scheme); err != nil { | ||
| inUse, err := r.secretInUse(cr.Namespace, legacy.Name) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| if inUse { | ||
| return nil | ||
| } | ||
|
|
||
| if existed { | ||
| argoutil.LogResourceUpdate(log, secret) | ||
| return r.Update(context.TODO(), secret) | ||
| argoutil.LogResourceDeletion(log, legacy) | ||
| err = r.Delete(context.TODO(), legacy) | ||
| if err != nil { | ||
| if !apierrors.IsNotFound(err) { | ||
| return err | ||
| } | ||
| } | ||
| argoutil.LogResourceCreation(log, secret) | ||
| return r.Create(context.TODO(), secret) | ||
| return nil | ||
| } | ||
|
|
||
| func secretIsComplete(s *corev1.Secret) bool { | ||
| healthy := false | ||
| if s.Data != nil { | ||
| _, hasPwd := s.Data[common.ArgoCDKeyAdminPassword] | ||
| _, hasAuth := s.Data["auth"] | ||
| _, hasUsername := s.Data["auth_username"] | ||
| _, hasAcl := s.Data["users.acl"] | ||
|
|
||
| if hasPwd && hasAuth && hasUsername && hasAcl { | ||
| healthy = true | ||
| } | ||
| } | ||
| return healthy | ||
| } | ||
|
|
||
| func salvageRedisPassword(s *corev1.Secret) []byte { | ||
| pwd := s.Data[common.ArgoCDKeyAdminPassword] | ||
| if len(pwd) == 0 { | ||
| pwd = s.Data["auth"] | ||
| } | ||
|
|
||
| if len(pwd) == 0 { | ||
| return nil | ||
| } | ||
| return pwd | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In this code path, new password value will be injected. Not sure the
LogResourceUpdatecommunicate that cleanly enough. Also, unsure when/how can this actually happen.IMO
prepareSecretForUpdateCreateshould try to salvage the password if the key exists.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is how it was handled before when we had old secret.
I did reuse the logic but this time for
argocd-redissecret