|
|
@@ -54,14 +54,12 @@ import (
|
|
|
// Metrics.
|
|
|
"github.com/external-secrets/external-secrets/pkg/controllers/externalsecret/esmetrics"
|
|
|
ctrlmetrics "github.com/external-secrets/external-secrets/pkg/controllers/metrics"
|
|
|
- "github.com/external-secrets/external-secrets/pkg/controllers/util"
|
|
|
+ ctrlutil "github.com/external-secrets/external-secrets/pkg/controllers/util"
|
|
|
"github.com/external-secrets/external-secrets/runtime/esutils"
|
|
|
"github.com/external-secrets/external-secrets/runtime/esutils/resolvers"
|
|
|
|
|
|
// Loading registered generators.
|
|
|
_ "github.com/external-secrets/external-secrets/pkg/register"
|
|
|
- // Loading registered providers.
|
|
|
- _ "github.com/external-secrets/external-secrets/pkg/register"
|
|
|
)
|
|
|
|
|
|
const (
|
|
|
@@ -130,7 +128,10 @@ var (
|
|
|
ErrSecretRemoveCtrlRef = fmt.Errorf("could not remove controller reference on secret")
|
|
|
)
|
|
|
|
|
|
-const indexESTargetSecretNameField = ".metadata.targetSecretName"
|
|
|
+const (
|
|
|
+ indexESTargetSecretNameField = ".metadata.targetSecretName"
|
|
|
+ indexESTargetResourceField = ".spec.target.resource"
|
|
|
+)
|
|
|
|
|
|
// Reconciler reconciles a ExternalSecret object.
|
|
|
type Reconciler struct {
|
|
|
@@ -144,7 +145,11 @@ type Reconciler struct {
|
|
|
ClusterSecretStoreEnabled bool
|
|
|
EnableFloodGate bool
|
|
|
EnableGeneratorState bool
|
|
|
+ AllowGenericTargets bool
|
|
|
recorder record.EventRecorder
|
|
|
+
|
|
|
+ // informerManager manages dynamic informers for generic targets
|
|
|
+ informerManager InformerManager
|
|
|
}
|
|
|
|
|
|
// Reconcile implements the main reconciliation loop
|
|
|
@@ -195,6 +200,18 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (result ct
|
|
|
return ctrl.Result{}, err
|
|
|
}
|
|
|
|
|
|
+ // Release informer for generic targets
|
|
|
+ if isGenericTarget(externalSecret) && r.informerManager != nil {
|
|
|
+ gvk := getTargetGVK(externalSecret)
|
|
|
+ esName := types.NamespacedName{Name: externalSecret.Name, Namespace: externalSecret.Namespace}
|
|
|
+ if err := r.informerManager.ReleaseInformer(ctx, gvk, esName); err != nil {
|
|
|
+ log.Error(err, "failed to release informer for generic target",
|
|
|
+ "group", gvk.Group,
|
|
|
+ "version", gvk.Version,
|
|
|
+ "kind", gvk.Kind)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
// Remove finalizer if it exists
|
|
|
if updated := controllerutil.RemoveFinalizer(externalSecret, ExternalSecretFinalizer); updated {
|
|
|
if err := r.Update(ctx, externalSecret); err != nil {
|
|
|
@@ -232,6 +249,30 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (result ct
|
|
|
return ctrl.Result{}, nil
|
|
|
}
|
|
|
|
|
|
+ // if this is a generic target, use a different reconciliation path
|
|
|
+ if isGenericTarget(externalSecret) {
|
|
|
+ // update the status of the ExternalSecret when this function returns, if needed
|
|
|
+ currentStatus := *externalSecret.Status.DeepCopy()
|
|
|
+ defer func() {
|
|
|
+ if equality.Semantic.DeepEqual(currentStatus, externalSecret.Status) {
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ updateErr := r.Status().Update(ctx, externalSecret)
|
|
|
+ if updateErr != nil && !apierrors.IsConflict(updateErr) {
|
|
|
+ log.Error(updateErr, logErrorUpdateESStatus)
|
|
|
+ }
|
|
|
+ }()
|
|
|
+
|
|
|
+ // validate generic target configuration early
|
|
|
+ if err := r.validateGenericTarget(log, externalSecret); err != nil {
|
|
|
+ r.markAsFailed("invalid generic target", err, externalSecret, syncCallsError.With(resourceLabels), esv1.ConditionReasonSecretSyncedError)
|
|
|
+ return ctrl.Result{}, nil // don't requeue as this is a configuration error that is not recoverable
|
|
|
+ }
|
|
|
+
|
|
|
+ return r.reconcileGenericTarget(ctx, externalSecret, log, start, resourceLabels, syncCallsError)
|
|
|
+ }
|
|
|
+
|
|
|
// the target secret name defaults to the ExternalSecret name, if not explicitly set
|
|
|
secretName := externalSecret.Spec.Target.Name
|
|
|
if secretName == "" {
|
|
|
@@ -352,7 +393,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (result ct
|
|
|
// retrieve the provider secret data.
|
|
|
dataMap, err := r.GetProviderSecretData(ctx, externalSecret)
|
|
|
if err != nil {
|
|
|
- r.markAsFailed(msgErrorGetSecretData, err, externalSecret, syncCallsError.With(resourceLabels))
|
|
|
+ r.markAsFailed(msgErrorGetSecretData, err, externalSecret, syncCallsError.With(resourceLabels), esv1.ConditionReasonSecretSyncedError)
|
|
|
return ctrl.Result{}, err
|
|
|
}
|
|
|
|
|
|
@@ -367,7 +408,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (result ct
|
|
|
creationPolicy := externalSecret.Spec.Target.CreationPolicy
|
|
|
if creationPolicy != esv1.CreatePolicyOwner {
|
|
|
err = fmt.Errorf(errDeleteCreatePolicy, secretName, creationPolicy)
|
|
|
- r.markAsFailed(msgErrorDeleteSecret, err, externalSecret, syncCallsError.With(resourceLabels))
|
|
|
+ r.markAsFailed(msgErrorDeleteSecret, err, externalSecret, syncCallsError.With(resourceLabels), esv1.ConditionReasonSecretSyncedError)
|
|
|
return ctrl.Result{}, nil
|
|
|
}
|
|
|
|
|
|
@@ -375,7 +416,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (result ct
|
|
|
if existingSecret.UID != "" {
|
|
|
err = r.Delete(ctx, existingSecret)
|
|
|
if err != nil && !apierrors.IsNotFound(err) {
|
|
|
- r.markAsFailed(msgErrorDeleteSecret, err, externalSecret, syncCallsError.With(resourceLabels))
|
|
|
+ r.markAsFailed(msgErrorDeleteSecret, err, externalSecret, syncCallsError.With(resourceLabels), esv1.ConditionReasonSecretSyncedError)
|
|
|
return ctrl.Result{}, err
|
|
|
}
|
|
|
r.recorder.Event(externalSecret, v1.EventTypeNormal, esv1.ReasonDeleted, eventDeleted)
|
|
|
@@ -513,7 +554,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (result ct
|
|
|
// for example, if the target secret name was changed
|
|
|
err = r.deleteOrphanedSecrets(ctx, externalSecret, secretName)
|
|
|
if err != nil {
|
|
|
- r.markAsFailed(msgErrorDeleteOrphaned, err, externalSecret, syncCallsError.With(resourceLabels))
|
|
|
+ r.markAsFailed(msgErrorDeleteOrphaned, err, externalSecret, syncCallsError.With(resourceLabels), esv1.ConditionReasonSecretSyncedError)
|
|
|
return ctrl.Result{}, err
|
|
|
}
|
|
|
|
|
|
@@ -535,25 +576,25 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (result ct
|
|
|
// detect errors indicating that we failed to set ourselves as the owner of the secret
|
|
|
// NOTE: this error cant be fixed by retrying so we don't return an error (which would requeue immediately)
|
|
|
if errors.Is(err, ErrSecretSetCtrlRef) {
|
|
|
- r.markAsFailed(msgErrorBecomeOwner, err, externalSecret, syncCallsError.With(resourceLabels))
|
|
|
+ r.markAsFailed(msgErrorBecomeOwner, err, externalSecret, syncCallsError.With(resourceLabels), esv1.ConditionReasonSecretSyncedError)
|
|
|
return ctrl.Result{}, nil
|
|
|
}
|
|
|
|
|
|
// detect errors indicating that the secret has another ExternalSecret as owner
|
|
|
// NOTE: this error cant be fixed by retrying so we don't return an error (which would requeue immediately)
|
|
|
if errors.Is(err, ErrSecretIsOwned) {
|
|
|
- r.markAsFailed(msgErrorIsOwned, err, externalSecret, syncCallsError.With(resourceLabels))
|
|
|
+ r.markAsFailed(msgErrorIsOwned, err, externalSecret, syncCallsError.With(resourceLabels), esv1.ConditionReasonSecretSyncedError)
|
|
|
return ctrl.Result{}, nil
|
|
|
}
|
|
|
|
|
|
// detect errors indicating that the secret is immutable
|
|
|
// NOTE: this error cant be fixed by retrying so we don't return an error (which would requeue immediately)
|
|
|
if errors.Is(err, ErrSecretImmutable) {
|
|
|
- r.markAsFailed(msgErrorUpdateImmutable, err, externalSecret, syncCallsError.With(resourceLabels))
|
|
|
+ r.markAsFailed(msgErrorUpdateImmutable, err, externalSecret, syncCallsError.With(resourceLabels), esv1.ConditionReasonSecretSyncedError)
|
|
|
return ctrl.Result{}, nil
|
|
|
}
|
|
|
|
|
|
- r.markAsFailed(msgErrorUpdateSecret, err, externalSecret, syncCallsError.With(resourceLabels))
|
|
|
+ r.markAsFailed(msgErrorUpdateSecret, err, externalSecret, syncCallsError.With(resourceLabels), esv1.ConditionReasonSecretSyncedError)
|
|
|
return ctrl.Result{}, err
|
|
|
}
|
|
|
|
|
|
@@ -561,6 +602,119 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (result ct
|
|
|
return r.getRequeueResult(externalSecret), nil
|
|
|
}
|
|
|
|
|
|
+// reconcileGenericTarget handles reconciliation for generic targets (ConfigMaps, Custom Resources).
|
|
|
+func (r *Reconciler) reconcileGenericTarget(ctx context.Context, externalSecret *esv1.ExternalSecret, log logr.Logger, start time.Time, resourceLabels map[string]string, syncCallsError *prometheus.CounterVec) (ctrl.Result, error) {
|
|
|
+ // retrieve the provider secret data
|
|
|
+ dataMap, err := r.GetProviderSecretData(ctx, externalSecret)
|
|
|
+ if err != nil {
|
|
|
+ r.markAsFailed(msgErrorGetSecretData, err, externalSecret, syncCallsError.With(resourceLabels), esv1.ConditionReasonResourceSyncedError)
|
|
|
+ return ctrl.Result{}, err
|
|
|
+ }
|
|
|
+
|
|
|
+ // if no data was found, handle it according to deletion policy
|
|
|
+ if len(dataMap) == 0 {
|
|
|
+ switch externalSecret.Spec.Target.DeletionPolicy {
|
|
|
+ case esv1.DeletionPolicyDelete:
|
|
|
+ // safeguard that we only can delete resources we own
|
|
|
+ creationPolicy := externalSecret.Spec.Target.CreationPolicy
|
|
|
+ if creationPolicy != esv1.CreatePolicyOwner {
|
|
|
+ err = fmt.Errorf("unable to delete resource: creationPolicy=%s is not Owner", creationPolicy)
|
|
|
+ r.markAsFailed("could not delete resource", err, externalSecret, syncCallsError.With(resourceLabels), esv1.ConditionReasonResourceSyncedError)
|
|
|
+ return ctrl.Result{}, nil
|
|
|
+ }
|
|
|
+
|
|
|
+ // delete the resource if it exists
|
|
|
+ err = r.deleteGenericResource(ctx, log, externalSecret)
|
|
|
+ if err != nil {
|
|
|
+ r.markAsFailed("could not delete resource", err, externalSecret, syncCallsError.With(resourceLabels), esv1.ConditionReasonResourceSyncedError)
|
|
|
+ return ctrl.Result{}, err
|
|
|
+ }
|
|
|
+
|
|
|
+ r.markAsDone(externalSecret, start, log, esv1.ConditionReasonResourceDeleted, msgDeleted)
|
|
|
+ return r.getRequeueResult(externalSecret), nil
|
|
|
+
|
|
|
+ case esv1.DeletionPolicyRetain:
|
|
|
+ r.markAsDone(externalSecret, start, log, esv1.ConditionReasonResourceSynced, msgSyncedRetain)
|
|
|
+ return r.getRequeueResult(externalSecret), nil
|
|
|
+
|
|
|
+ case esv1.DeletionPolicyMerge:
|
|
|
+ // continue to process with empty data
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // render the template for the manifest
|
|
|
+ obj, err := r.applyTemplateToManifest(ctx, externalSecret, dataMap)
|
|
|
+ if err != nil {
|
|
|
+ r.markAsFailed("could not apply template to manifest", err, externalSecret, syncCallsError.With(resourceLabels), esv1.ConditionReasonResourceSyncedError)
|
|
|
+ return ctrl.Result{}, err
|
|
|
+ }
|
|
|
+
|
|
|
+ // handle creation policies
|
|
|
+ switch externalSecret.Spec.Target.CreationPolicy {
|
|
|
+ case esv1.CreatePolicyNone:
|
|
|
+ log.V(1).Info("resource creation skipped due to CreationPolicy=None")
|
|
|
+ err = nil
|
|
|
+
|
|
|
+ case esv1.CreatePolicyMerge:
|
|
|
+ // for Merge policy, only update if resource exists
|
|
|
+ existing, getErr := r.getGenericResource(ctx, log, externalSecret)
|
|
|
+ if getErr != nil && !apierrors.IsNotFound(getErr) {
|
|
|
+ r.markAsFailed("could not get target resource", getErr, externalSecret, syncCallsError.With(resourceLabels), esv1.ConditionReasonResourceSyncedError)
|
|
|
+ return ctrl.Result{}, getErr
|
|
|
+ }
|
|
|
+
|
|
|
+ if existing == nil || existing.GetUID() == "" {
|
|
|
+ r.markAsDone(externalSecret, start, log, esv1.ConditionReasonResourceMissing, "resource will not be created due to CreationPolicy=Merge")
|
|
|
+ return r.getRequeueResult(externalSecret), nil
|
|
|
+ }
|
|
|
+
|
|
|
+ obj.SetResourceVersion(existing.GetResourceVersion())
|
|
|
+ obj.SetUID(existing.GetUID())
|
|
|
+
|
|
|
+ // update the existing resource
|
|
|
+ err = r.updateGenericResource(ctx, log, externalSecret, obj)
|
|
|
+ case esv1.CreatePolicyOrphan, esv1.CreatePolicyOwner:
|
|
|
+ existing, getErr := r.getGenericResource(ctx, log, externalSecret)
|
|
|
+ if getErr != nil && !apierrors.IsNotFound(getErr) {
|
|
|
+ r.markAsFailed("could not get target resource", getErr, externalSecret, syncCallsError.With(resourceLabels), esv1.ConditionReasonResourceSyncedError)
|
|
|
+ return ctrl.Result{}, getErr
|
|
|
+ }
|
|
|
+
|
|
|
+ if existing != nil {
|
|
|
+ obj.SetResourceVersion(existing.GetResourceVersion())
|
|
|
+ obj.SetUID(existing.GetUID())
|
|
|
+ err = r.updateGenericResource(ctx, log, externalSecret, obj)
|
|
|
+ } else {
|
|
|
+ err = r.createGenericResource(ctx, log, externalSecret, obj)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if err != nil {
|
|
|
+ // if we got an update conflict, requeue immediately
|
|
|
+ if apierrors.IsConflict(err) {
|
|
|
+ log.V(1).Info("conflict while updating resource, will requeue")
|
|
|
+ return ctrl.Result{RequeueAfter: 1 * time.Second}, nil
|
|
|
+ }
|
|
|
+
|
|
|
+ r.markAsFailed(msgErrorUpdateSecret, err, externalSecret, syncCallsError.With(resourceLabels), esv1.ConditionReasonResourceSyncedError)
|
|
|
+ return ctrl.Result{}, err
|
|
|
+ }
|
|
|
+
|
|
|
+ // Ensure an informer exists for this GVK to enable drift detection (only if not already managed)
|
|
|
+ gvk := getTargetGVK(externalSecret)
|
|
|
+ esName := types.NamespacedName{Name: externalSecret.Name, Namespace: externalSecret.Namespace}
|
|
|
+ if _, err := r.informerManager.EnsureInformer(ctx, gvk, esName); err != nil {
|
|
|
+ // Log the error but don't fail reconciliation - the resource was successfully created/updated
|
|
|
+ log.Error(err, "failed to register informer for generic target, drift detection may not work",
|
|
|
+ "group", gvk.Group,
|
|
|
+ "version", gvk.Version,
|
|
|
+ "kind", gvk.Kind)
|
|
|
+ }
|
|
|
+
|
|
|
+ r.markAsDone(externalSecret, start, log, esv1.ConditionReasonResourceSynced, msgSynced)
|
|
|
+ return r.getRequeueResult(externalSecret), nil
|
|
|
+}
|
|
|
+
|
|
|
// getRequeueResult create a result with requeueAfter based on the ExternalSecret refresh interval.
|
|
|
func (r *Reconciler) getRequeueResult(externalSecret *esv1.ExternalSecret) ctrl.Result {
|
|
|
// default to the global requeue interval
|
|
|
@@ -620,20 +774,26 @@ func (r *Reconciler) markAsDone(externalSecret *esv1.ExternalSecret, start time.
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-func (r *Reconciler) markAsFailed(msg string, err error, externalSecret *esv1.ExternalSecret, counter prometheus.Counter) {
|
|
|
+func (r *Reconciler) markAsFailed(msg string, err error, externalSecret *esv1.ExternalSecret, counter prometheus.Counter, reason string) {
|
|
|
r.recorder.Event(externalSecret, v1.EventTypeWarning, esv1.ReasonUpdateFailed, err.Error())
|
|
|
- conditionSynced := NewExternalSecretCondition(esv1.ExternalSecretReady, v1.ConditionFalse, esv1.ConditionReasonSecretSyncedError, msg)
|
|
|
+ conditionSynced := NewExternalSecretCondition(esv1.ExternalSecretReady, v1.ConditionFalse, reason, msg)
|
|
|
SetExternalSecretCondition(externalSecret, *conditionSynced)
|
|
|
counter.Inc()
|
|
|
}
|
|
|
|
|
|
func (r *Reconciler) cleanupManagedSecrets(ctx context.Context, log logr.Logger, externalSecret *esv1.ExternalSecret) error {
|
|
|
- // Only delete secrets if DeletionPolicy is Delete
|
|
|
+ // Only delete resources if DeletionPolicy is Delete
|
|
|
if externalSecret.Spec.Target.DeletionPolicy != esv1.DeletionPolicyDelete {
|
|
|
- log.V(1).Info("skipping secret deletion due to DeletionPolicy", "policy", externalSecret.Spec.Target.DeletionPolicy)
|
|
|
+ log.V(1).Info("skipping resource deletion due to DeletionPolicy", "policy", externalSecret.Spec.Target.DeletionPolicy)
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
+ // if this is a generic target, use deleteGenericResource
|
|
|
+ if isGenericTarget(externalSecret) {
|
|
|
+ return r.deleteGenericResource(ctx, log, externalSecret)
|
|
|
+ }
|
|
|
+
|
|
|
+ // handle Secret deletion
|
|
|
secretName := externalSecret.Spec.Target.Name
|
|
|
if secretName == "" {
|
|
|
secretName = externalSecret.Name
|
|
|
@@ -697,8 +857,10 @@ func (r *Reconciler) createSecret(ctx context.Context, mutationFunc func(secret
|
|
|
// define and mutate the new secret
|
|
|
newSecret := &v1.Secret{
|
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
|
- Name: secretName,
|
|
|
- Namespace: es.Namespace,
|
|
|
+ Name: secretName,
|
|
|
+ Namespace: es.Namespace,
|
|
|
+ Labels: map[string]string{},
|
|
|
+ Annotations: map[string]string{},
|
|
|
},
|
|
|
Data: make(map[string][]byte),
|
|
|
}
|
|
|
@@ -992,13 +1154,21 @@ func isSecretValid(existingSecret *v1.Secret, es *esv1.ExternalSecret) bool {
|
|
|
}
|
|
|
|
|
|
// SetupWithManager returns a new controller builder that will be started by the provided Manager.
|
|
|
-func (r *Reconciler) SetupWithManager(mgr ctrl.Manager, opts controller.Options) error {
|
|
|
+func (r *Reconciler) SetupWithManager(ctx context.Context, mgr ctrl.Manager, opts controller.Options) error {
|
|
|
r.recorder = mgr.GetEventRecorderFor("external-secrets")
|
|
|
+ // Initialize informer manager only if generic targets are allowed
|
|
|
+ if r.AllowGenericTargets && r.informerManager == nil {
|
|
|
+ r.informerManager = NewInformerManager(ctx, mgr.GetCache(), r.Client, r.Log.WithName("informer-manager"))
|
|
|
+ }
|
|
|
|
|
|
// index ExternalSecrets based on the target secret name,
|
|
|
// this lets us quickly find all ExternalSecrets which target a specific Secret
|
|
|
- if err := mgr.GetFieldIndexer().IndexField(context.Background(), &esv1.ExternalSecret{}, indexESTargetSecretNameField, func(obj client.Object) []string {
|
|
|
+ if err := mgr.GetFieldIndexer().IndexField(ctx, &esv1.ExternalSecret{}, indexESTargetSecretNameField, func(obj client.Object) []string {
|
|
|
es := obj.(*esv1.ExternalSecret)
|
|
|
+ // Don't index generic targets here (they use indexESTargetResourceField)
|
|
|
+ if isGenericTarget(es) {
|
|
|
+ return nil
|
|
|
+ }
|
|
|
// if the target name is set, use that as the index
|
|
|
if es.Spec.Target.Name != "" {
|
|
|
return []string{es.Spec.Target.Name}
|
|
|
@@ -1009,13 +1179,30 @@ func (r *Reconciler) SetupWithManager(mgr ctrl.Manager, opts controller.Options)
|
|
|
return err
|
|
|
}
|
|
|
|
|
|
+ // index ExternalSecrets based on the target resource (GVK + name)
|
|
|
+ // this lets us quickly find all ExternalSecrets which target a specific generic resource
|
|
|
+ if err := mgr.GetFieldIndexer().IndexField(ctx, &esv1.ExternalSecret{}, indexESTargetResourceField, func(obj client.Object) []string {
|
|
|
+ es := obj.(*esv1.ExternalSecret)
|
|
|
+ if !r.AllowGenericTargets || !isGenericTarget(es) {
|
|
|
+ return nil
|
|
|
+ }
|
|
|
+
|
|
|
+ gvk := getTargetGVK(es)
|
|
|
+ targetName := getTargetName(es)
|
|
|
+ // Index format: "group/version/kind/name"
|
|
|
+ return []string{fmt.Sprintf("%s/%s/%s/%s", gvk.Group, gvk.Version, gvk.Kind, targetName)}
|
|
|
+ }); err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+
|
|
|
// predicate function to ignore secret events unless they have the "managed" label
|
|
|
secretHasESLabel := predicate.NewPredicateFuncs(func(object client.Object) bool {
|
|
|
value, hasLabel := object.GetLabels()[esv1.LabelManaged]
|
|
|
return hasLabel && value == esv1.LabelManagedValue
|
|
|
})
|
|
|
|
|
|
- return ctrl.NewControllerManagedBy(mgr).
|
|
|
+ // Build the controller
|
|
|
+ builder := ctrl.NewControllerManagedBy(mgr).
|
|
|
WithOptions(opts).
|
|
|
For(&esv1.ExternalSecret{}).
|
|
|
// we cant use Owns(), as we don't set ownerReferences when the creationPolicy is not Owner.
|
|
|
@@ -1024,8 +1211,15 @@ func (r *Reconciler) SetupWithManager(mgr ctrl.Manager, opts controller.Options)
|
|
|
&v1.Secret{},
|
|
|
handler.EnqueueRequestsFromMapFunc(r.findObjectsForSecret),
|
|
|
builder.WithPredicates(predicate.ResourceVersionChangedPredicate{}, secretHasESLabel),
|
|
|
- ).
|
|
|
- Complete(r)
|
|
|
+ )
|
|
|
+
|
|
|
+ // Watch generic targets dynamically via the informer manager
|
|
|
+ // Only add this watch source if the feature is enabled
|
|
|
+ if r.AllowGenericTargets {
|
|
|
+ builder = builder.WatchesRawSource(r.informerManager.Source())
|
|
|
+ }
|
|
|
+
|
|
|
+ return builder.Complete(r)
|
|
|
}
|
|
|
|
|
|
func (r *Reconciler) findObjectsForSecret(ctx context.Context, secret client.Object) []reconcile.Request {
|