diff --git a/controllers/hcpvaultsecretsapp_controller.go b/controllers/hcpvaultsecretsapp_controller.go index be42427c..00387034 100644 --- a/controllers/hcpvaultsecretsapp_controller.go +++ b/controllers/hcpvaultsecretsapp_controller.go @@ -24,6 +24,7 @@ import ( apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" "k8s.io/client-go/tools/record" "k8s.io/utils/ptr" ctrl "sigs.k8s.io/controller-runtime" @@ -90,8 +91,9 @@ type HCPVaultSecretsAppReconciler struct { BackOffRegistry *BackOffRegistry } -// initializedOrphanedShadowSecretCleanup is used to ensure that only one orphaned shadow secret cleanup goroutine is started -var initializedOrphanedShadowSecretCleanup = false +// orphanedShadowSecretCleanupInitialized is used to ensure that the goroutine that does cleanup of shadow secrets +// that belong to deleted HCPVaultSecretsApp instances is only started once +var orphanedShadowSecretCleanupInitialized = false // +kubebuilder:rbac:groups=secrets.hashicorp.com,resources=hcpvaultsecretsapps,verbs=get;list;watch;create;update;patch;delete // +kubebuilder:rbac:groups=secrets.hashicorp.com,resources=hcpvaultsecretsapps/status,verbs=get;update;patch @@ -126,41 +128,11 @@ func (r *HCPVaultSecretsAppReconciler) Reconcile(ctx context.Context, req ctrl.R return ctrl.Result{}, r.handleDeletion(ctx, o) } - go func() { - // this goroutine clean ups orphaned shadow secrets once per hour - ticker := time.NewTicker(1 * time.Hour) - defer ticker.Stop() - - for { - select { - case <-ticker.C: - // retrieve all shadow secrets in the vso namespace - secrets := &corev1.SecretList{} - if err := r.Client.List(ctx, secrets, client.InNamespace(common.OperatorNamespace)); err != nil { - logger.Error(err, "Failed to list secrets") - continue - } - - for _, secret := range secrets.Items { - appName := secret.Labels[hvsaLabelPrefix+"/hvs-app-name"] - app := &secretsv1beta1.HCPVaultSecretsApp{} - - // get the HCPVaultSecretsApp associated with the shadow secret - if err := r.Client.Get(ctx, client.ObjectKey{Namespace: secret.Namespace, Name: appName}, app); err != nil { - logger.Error(err, "Failed to get HCPVaultSecretsApp") - continue - } - - // check if it was deleted and remove the shadow secret if it was - if app.GetDeletionTimestamp() != nil { - if err := r.Client.Delete(ctx, &secret); err != nil { - logger.Error(err, "Failed to delete orphaned shadow secret") - } - } - } - } - } - }() + if !orphanedShadowSecretCleanupInitialized { + go r.startShadowSecretCleanupRoutine(ctx) + // prevent the shadow secret cleanup goroutine from being started more than once + orphanedShadowSecretCleanupInitialized = true + } var requeueAfter time.Duration if o.Spec.RefreshAfter != "" { @@ -327,6 +299,57 @@ func (r *HCPVaultSecretsAppReconciler) Reconcile(ctx context.Context, req ctrl.R }, nil } +func (r *HCPVaultSecretsAppReconciler) startShadowSecretCleanupRoutine(ctx context.Context) { + logger := log.FromContext(ctx) + // cleanup of orphaned shadow secrets runs once every hour + ticker := time.NewTicker(1 * time.Hour) + + for { + select { + case <-ticker.C: + err := r.cleanupOrphanedShadowSecrets(ctx) + if err != nil { + logger.Error(err, "Failed to cleanup orphaned shadow secrets") + } + } + } +} + +func (r *HCPVaultSecretsAppReconciler) cleanupOrphanedShadowSecrets(ctx context.Context) error { + logger := log.FromContext(ctx) + + // get all secrets in the operator namespace + secrets := corev1.SecretList{} + if err := r.List(ctx, &secrets, client.InNamespace(common.OperatorNamespace)); err != nil { + logger.Error(err, "Failed to list secrets") + } + + for _, secret := range secrets.Items { + namespace := secret.Labels[hvsaLabelPrefix+"/namespace"] + name := secret.Labels[hvsaLabelPrefix+"/name"] + + o := &secretsv1beta1.HCPVaultSecretsApp{} + + // get the HCPVaultSecretsApp instance that that the shadow secret belongs to (if applicable) + err := r.Get(ctx, types.NamespacedName{Namespace: namespace, Name: name}, o) + if err != nil { + logger.Error(err, "Error getting resource from k8s", "secret", secret.Name) + continue + } + + // delete the HCPVaultSecretsApp if it no longer exists + if o.GetDeletionTimestamp() != nil { + if err := r.handleDeletion(ctx, o); err != nil { + return err + } + + logger.Info("Deleted resources associated with orphaned HCPVaultSecretsApp", "app", o.Name) + } + } + + return nil +} + func (r *HCPVaultSecretsAppReconciler) updateStatus(ctx context.Context, o *secretsv1beta1.HCPVaultSecretsApp) error { o.Status.LastGeneration = o.GetGeneration() if err := r.Status().Update(ctx, o); err != nil {