Skip to content

fix: the informer can not register use GetInformer instead#5931

Merged
Skarlso merged 10 commits intoexternal-secrets:mainfrom
Skarlso:fix-informer-custom-resource-watch
Feb 13, 2026
Merged

fix: the informer can not register use GetInformer instead#5931
Skarlso merged 10 commits intoexternal-secrets:mainfrom
Skarlso:fix-informer-custom-resource-watch

Conversation

@Skarlso
Copy link
Copy Markdown
Contributor

@Skarlso Skarlso commented Feb 6, 2026

Problem Statement

The Informer for Kind did not work for other CRDs, let's just use the proper informer registration for it.

Related Issue

Fixes #5587

Proposed Changes

How do you like to solve the issue and why?

Format

Please ensure that your PR follows the following format for the title:

feat(scope): add new feature
fix(scope): fix bug
docs(scope): update documentation
chore(scope): update build tool or dependencies
ref(scope): refactor code
clean(scope): provider cleanup
test(scope): add tests
perf(scope): improve performance
desig(scope): improve design

Where scope is optionally one of:

  • charts
  • release
  • testing
  • security
  • templating

Checklist

  • I have read the contribution guidelines
  • All commits are signed with git commit --signoff
  • My changes have reasonable test coverage
  • All tests pass with make test
  • I ensured my PR is ready for review with make reviewable

Summary

Switch informer registration to use cache.GetInformer with an unstructured.Unstructured (obj.SetGroupVersionKind(gvk)) so informers work for arbitrary CRDs. Add pre-emptive generic-target validation and content-hash checks to short-circuit reconciliation when a managed target is up-to-date. Gate informer lifecycle and lazy registration by target CreationPolicy and AllowGenericTargets. Unify manifest/templating flow to a single return path, annotate manifests with a data/spec content hash, and change createSimpleManifest to return *unstructured.Unstructured (removed error return). Add unit tests for the informer manager and for generic-target hashing/validation.

Key changes

  • pkg/controllers/externalsecret/informer_manager.go

    • Use unstructured.Unstructured with obj.SetGroupVersionKind and cache.GetInformer(ctx, obj) / RemoveInformer(ctx, obj) instead of GetInformerForKind.
    • DefaultInformerManager: EnsureInformer, ReleaseInformer, IsManaged, GetInformer, SetQueue and enqueue handler; deduplicate by GVK.
  • pkg/controllers/externalsecret/externalsecret_controller.go

    • Add isGenericTargetValid(...) and genericTargetContentHash(...); skip reconcile work when valid and refresh not needed.
    • Lazily register informers and respect CreationPolicy (None/Merge/Orphan/Owner) and feature gating.
  • pkg/controllers/externalsecret/externalsecret_controller_manifest.go

    • applyTemplateToManifest now accepts existingObj, centralizes result/error handling, initializes data/spec for manifests, computes and writes AnnotationDataHash.
    • createSimpleManifest now returns *unstructured.Unstructured and places non-ConfigMap/Secret data under spec.
  • Tests

    • pkg/controllers/externalsecret/informer_manager_test.go: validates Unstructured informer creation, GVK deduplication, queue/cache error propagation, and ReleaseInformer behavior.
    • pkg/controllers/externalsecret/externalsecret_controller_manifest_test.go: tests for generic-target content hashing and validation; updated createSimpleManifest usage.

Addresses issue: #5587 — fixes informer registration for CRDs and improves generic-target templating/manifest handling.

Notes: contributor guidance and PR checklist present but some items (contribution guidelines read, commits signed, tests added/passing, readiness) are unchecked.

@github-actions github-actions bot added kind/bug Categorizes issue or PR as related to a bug. size/m labels Feb 6, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Feb 6, 2026

Walkthrough

Adds generic-target validation and content-hash annotation, short-circuits reconcile when no refresh needed, switches informer lookup to use Unstructured (preserving GVK), conditionally registers informers, centralizes manifest templating return flow, updates createSimpleManifest signature, and adds informer/manifest unit tests.

Changes

Cohort / File(s) Summary
ExternalSecret controller
pkg/controllers/externalsecret/externalsecret_controller.go
Adds isGenericTargetValid(...) and genericTargetContentHash(...); conditionally fetches existing generic targets per CreationPolicy, skips work when target is valid and not due for refresh, and adjusts informer registration to be conditional.
Manifest templating & tests
pkg/controllers/externalsecret/externalsecret_controller_manifest.go, pkg/controllers/externalsecret/externalsecret_controller_manifest_test.go
Unifies applyTemplateToManifest flow to accept an existingObj, initializes manifest data/spec, computes and sets AnnotationDataHash via genericTargetContentHash, changes createSimpleManifest to return only *unstructured.Unstructured, and expands tests for content-hash and validity scenarios.
Informer manager implementation
pkg/controllers/externalsecret/informer_manager.go
Replaces GetInformerForKind(ctx, gvk) usage with constructing an unstructured.Unstructured (setting GVK) and calling GetInformer(ctx, obj)/RemoveInformer(ctx, obj), preserving GVK on created informers.
Informer manager tests
pkg/controllers/externalsecret/informer_manager_test.go
Adds comprehensive unit tests with fake cache/informer covering Unstructured-based informer creation, GVK deduplication, queue/error handling, ReleaseInformer behavior, and internal state transitions.

Possibly related PRs

🚥 Pre-merge checks | ✅ 1 | ❌ 1
❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Linked Issues check ❓ Inconclusive The PR addresses the core issue #5587 by fixing informer registration for CRDs through GetInformer usage and adding content-hash validation, but the code changes do not comprehensively resolve all nested templating field-path issues described in the linked issue. Verify that template targeting of nested CRD fields (e.g., spec.slack) now works without unknown-field errors and that field paths are correctly populated with template variables.
✅ Passed checks (1 passed)
Check name Status Explanation
Out of Scope Changes check ✅ Passed All changes are scoped to externalsecret controller files and directly support informer registration fixes and generic target validation without introducing unrelated functionality.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


No actionable comments were generated in the recent review. 🎉


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Signed-off-by: Gergely Brautigam <182850+Skarlso@users.noreply.github.com>
@Skarlso Skarlso force-pushed the fix-informer-custom-resource-watch branch from 813222d to a2f27b1 Compare February 6, 2026 12:07
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
pkg/controllers/externalsecret/informer_manager.go (1)

250-257: ⚠️ Potential issue | 🟠 Major

Match RemoveInformer object type to GetInformer: use Unstructured consistently.

The informer is created via GetInformer(ctx, &unstructured.Unstructured{}) at line 119, but removed via RemoveInformer(ctx, &metav1.PartialObjectMetadata{}) at line 254. Controller-runtime's cache implementation routes removals based on the object type, so RemoveInformer will look for the informer in the PartialObjectMetadata map rather than the Unstructured map where it was registered. This causes the removal to fail silently, leaking the informer resource.

Proposed fix
 	if len(entry.externalSecrets) == 0 {
-		partial := &metav1.PartialObjectMetadata{}
-		partial.SetGroupVersionKind(gvk)
+		obj := &unstructured.Unstructured{}
+		obj.SetGroupVersionKind(gvk)
 
-		if err := m.cache.RemoveInformer(ctx, partial); err != nil {
+		if err := m.cache.RemoveInformer(ctx, obj); err != nil {
 			m.log.Error(err, "failed to remove informer, will clean up tracking anyway",
 				"gvk", key)
 		}
🤖 Fix all issues with AI agents
In `@pkg/controllers/externalsecret/externalsecret_controller.go`:
- Around line 278-281: The generic-target path currently returns early when
shouldRefresh(externalSecret) is false and thus skips both validity checks and
informer registration, creating drift gaps; modify the generic flow in
reconcileGenericTarget (or the caller that uses shouldRefresh) to either 1) add
an equivalent validity check like isSecretValid(existingSecret, externalSecret)
before skipping—lookup the existingSecret for the generic target and only return
when both !shouldRefresh(...) && isSecretValid(... )—or 2) ensure the informer
registration logic currently inside reconcileGenericTarget is executed
regardless of shouldRefresh so informers are registered on first reconcile
(move/registerInformer call out of the refresh-only branch); update
getRequeueResult usage accordingly so requeue behavior remains identical.
🧹 Nitpick comments (2)
pkg/controllers/externalsecret/informer_manager_test.go (2)

51-65: fakeCache doesn't mock RemoveInformer, leaving the full-release path untested.

When the last ExternalSecret is released, ReleaseInformer calls m.cache.RemoveInformer(ctx, partial). Since fakeCache embeds a nil runtimecache.Cache, this would panic if exercised. Consider adding a RemoveInformer mock and a test that releases all ESs for a GVK to verify the informer is fully cleaned up:

Suggested additions
 type fakeCache struct {
 	runtimecache.Cache
 	getInformerCalled bool
 	getInformerObj    client.Object
 	getInformerErr    error
+	removeInformerCalled bool
+	removeInformerObj    client.Object
+	removeInformerErr    error
 }
+
+func (f *fakeCache) RemoveInformer(ctx context.Context, obj client.Object) error {
+	f.removeInformerCalled = true
+	f.removeInformerObj = obj
+	return f.removeInformerErr
+}

Then add a test that ensures releasing all ESs triggers RemoveInformer and clears the entry from the informers map.


165-191: Partial release test is correct but consider also testing full cleanup.

This test verifies partial release (one of two ESs removed). As noted above, a test for releasing the last ES would exercise the RemoveInformer and delete(m.informers, key) path, and verify IsManaged returns false afterward.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
pkg/controllers/externalsecret/informer_manager.go (1)

250-257: ⚠️ Potential issue | 🔴 Critical

Use Unstructured instead of PartialObjectMetadata when removing informers — the current mismatch causes silent cleanup failure and resource leaks.

The informer is registered in EnsureInformer with &unstructured.Unstructured{} (line 117–119), but ReleaseInformer attempts removal with &metav1.PartialObjectMetadata{} (line 251). controller-runtime's cache maintains separate internal maps for each object "flavor" (structured, unstructured, metadata). Since RemoveInformer only removes from the map corresponding to the object type passed, removal with PartialObjectMetadata silently succeeds without actually stopping the unstructured informer—the tracking entry is deleted, but the underlying informer keeps running and consuming resources.

Fix
 	if len(entry.externalSecrets) == 0 {
-		partial := &metav1.PartialObjectMetadata{}
-		partial.SetGroupVersionKind(gvk)
-
-		if err := m.cache.RemoveInformer(ctx, partial); err != nil {
+		obj := &unstructured.Unstructured{}
+		obj.SetGroupVersionKind(gvk)
+
+		if err := m.cache.RemoveInformer(ctx, obj); err != nil {
 			m.log.Error(err, "failed to remove informer, will clean up tracking anyway",
 				"gvk", key)
 		}

Signed-off-by: Gergely Brautigam <182850+Skarlso@users.noreply.github.com>

On-behalf-of: Gergely Brautigam <gergely.brautigam@sap.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
pkg/controllers/externalsecret/externalsecret_controller.go (1)

658-673: ⚠️ Potential issue | 🔴 Critical

Nil pointer dereference in isGenericTargetValid when target resource is deleted externally.

When getGenericResource returns (nil, notFoundErr) for a missing target (line 663), the isGenericTargetValid function is called with existing=nil at line 670. The function immediately dereferences existingTarget at GetUID() without a null check, causing a panic.

This is reachable: after a controller restart where the ExternalSecret has a recent sync (shouldRefresh → false) but the target resource has been deleted externally.

🔧 Fix — add nil guard before calling `isGenericTargetValid`
-	if !shouldRefresh(externalSecret) && isGenericTargetValid(existing, externalSecret) {
+	if !shouldRefresh(externalSecret) && existing != nil && isGenericTargetValid(existing, externalSecret) {
 		log.V(1).Info("skipping refresh of generic target")
 		return r.getRequeueResult(externalSecret), nil
 	}

Alternatively, add the nil guard inside isGenericTargetValid before calling GetUID().

🤖 Fix all issues with AI agents
In `@pkg/controllers/externalsecret/externalsecret_controller.go`:
- Around line 1181-1205: The function isGenericTargetValid should defensively
handle a nil existingTarget: add a nil guard at the start of
isGenericTargetValid(existingTarget *unstructured.Unstructured, es
*esv1.ExternalSecret) that returns false if existingTarget == nil (unless
CreationPolicy==CreatePolicyOrphan), to avoid NPEs when callers pass nil;
additionally re-evaluate the TODO by computing a data-hash for unstructured
targets (e.g., hash existingTarget.Object or a specific sub-path like ".data" or
".spec") and compare against esv1.AnnotationDataHash using the same hashing
helper (esutils.ObjectHash or equivalent) to restore drift detection for
isGenericTargetValid.

Signed-off-by: Gergely Brautigam <182850+Skarlso@users.noreply.github.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@pkg/controllers/externalsecret/externalsecret_controller.go`:
- Around line 1181-1200: The code crashes when getGenericResource returns nil
for a not-found resource and isGenericTargetValid is called with
existingTarget==nil; add a nil guard to avoid dereferencing: update
isGenericTargetValid(existingTarget *unstructured.Unstructured, es
*esv1.ExternalSecret) to immediately return false if existingTarget == nil (so
callers like the callsite after getGenericResource no longer panic), keeping the
existing checks on GetUID, labels (esv1.LabelManaged), and annotation
(esv1.AnnotationDataHash vs genericTargetContentHash) unchanged.
- Around line 1202-1214: The default branch of genericTargetContentHash hashes
the whole obj.Object (including metadata.annotations), causing a mismatch after
applyTemplateToManifest writes AnnotationDataHash; change
genericTargetContentHash so that in the fallback it hashes a copy of the object
with metadata (or at minimum metadata.annotations) removed before calling
esutils.ObjectHash. Update genericTargetContentHash (referenced by
isGenericTargetValid and applyTemplateToManifest) to create a shallow/deep copy
of content, delete copy["metadata"] or copy["metadata"].(annotations) and then
compute esutils.ObjectHash on that sanitized map so the pre-annotation and
stored-annotation hashes match.
🧹 Nitpick comments (1)
pkg/controllers/externalsecret/externalsecret_controller.go (1)

658-673: Provider data is fetched before the validity/refresh check, unlike the Secret path.

In the Secret reconciliation path (line 354), shouldRefresh and isSecretValid are evaluated before calling GetProviderSecretData (line 399), avoiding unnecessary provider calls when the target is already valid. Here, GetProviderSecretData runs at line 620, well before the validity check at line 670. This means every reconciliation triggers a provider call even when the generic target is valid and no refresh is needed.

Consider moving the existing-resource fetch and validity check above GetProviderSecretData, or at least before the provider call, to avoid unnecessary external calls on every requeue.

Signed-off-by: Gergely Brautigam <182850+Skarlso@users.noreply.github.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
pkg/controllers/externalsecret/externalsecret_controller_manifest_test.go (1)

284-337: ⚠️ Potential issue | 🟡 Minor

Add test coverage for isGenericTargetValid and genericTargetContentHash error paths.

The functions isGenericTargetValid (line 1188) and genericTargetContentHash (line 1215) in externalsecret_controller.go lack test coverage. Specifically:

  • genericTargetContentHash returns an error when an object has neither spec nor data fields, but this error path is not tested.
  • isGenericTargetValid does not validate null pointers and calls genericTargetContentHash, but neither function is tested in this file.

Add tests for:

  • genericTargetContentHash with objects containing spec, data, and neither (expecting an error).
  • isGenericTargetValid with nil/empty targets, valid targets, and mismatched hashes.
🧹 Nitpick comments (3)
pkg/controllers/externalsecret/externalsecret_controller.go (3)

670-679: Validity check placement means GetProviderSecretData is called even when skipping refresh.

GetProviderSecretData (line 620) is called unconditionally before the validity/refresh check at line 676. In the Secret path (line 354), shouldRefresh is checked before fetching provider data. This means generic targets always call the provider even when skipping, adding latency and unnecessary API calls.

Consider moving the validity/refresh check before GetProviderSecretData, or at least before the provider call. The existing resource fetch (lines 658-668) would need to happen before the check, which it already does.

♻️ Suggested restructure sketch
 func (r *Reconciler) reconcileGenericTarget(...) (ctrl.Result, error) {
-	// retrieve the provider secret data
-	dataMap, err := r.GetProviderSecretData(ctx, externalSecret)
-	if err != nil { ... }
-
-	// if no data was found, handle it according to deletion policy
-	if len(dataMap) == 0 { ... }
-
 	// Check if we need to fetch existing resource first
 	existing := &unstructured.Unstructured{}
 	if externalSecret.Spec.Target.CreationPolicy == esv1.CreatePolicyMerge || ... {
 		existing, getErr = r.getGenericResource(ctx, log, externalSecret)
 		...
 	}
 
 	valid, err := isGenericTargetValid(existing, externalSecret)
 	...
 	if !shouldRefresh(externalSecret) && valid {
 		log.V(1).Info("skipping refresh of generic target")
 		return r.getRequeueResult(externalSecret), nil
 	}
 
+	// retrieve the provider secret data
+	dataMap, err := r.GetProviderSecretData(ctx, externalSecret)
+	if err != nil { ... }
+
+	// if no data was found, handle it according to deletion policy
+	if len(dataMap) == 0 { ... }
+
 	// render the template for the manifest
 	...

734-743: Informer registered even for CreatePolicyNone.

For CreatePolicyNone (line 697), no resource is created or updated, yet the informer is still registered at line 737. This is unnecessary work — there's nothing to watch for drift detection. Consider gating the informer registration to only run after an actual create/update.


658-668: existing initialization is effectively overwritten for Merge/Orphan/Owner, leaving stale empty object for other policies.

Line 658 initializes existing as an empty &unstructured.Unstructured{}. For CreatePolicyNone, this empty object flows into isGenericTargetValid (line 670) where GetUID() == "" returns (false, nil). This works correctly by accident — the empty unstructured has no UID. But the intent would be clearer if existing were initialized to nil and the nil guard were added to isGenericTargetValid (as noted in the other comment).

Signed-off-by: Gergely Brautigam <182850+Skarlso@users.noreply.github.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
pkg/controllers/externalsecret/externalsecret_controller_manifest.go (1)

261-265: ⚠️ Potential issue | 🟡 Minor

Unchecked type assertion can panic.

If obj.Object["spec"] exists but is not map[string]any (e.g., set to a different type by a prior code path or an unusual existing object), line 264 will panic.

Proposed fix — use a checked assertion
-	if obj.Object["spec"] == nil {
-		obj.Object["spec"] = make(map[string]any)
-	}
-	spec := obj.Object["spec"].(map[string]any)
-	spec["data"] = data
+	if obj.Object["spec"] == nil {
+		obj.Object["spec"] = make(map[string]any)
+	}
+	spec, ok := obj.Object["spec"].(map[string]any)
+	if !ok {
+		obj.Object["spec"] = map[string]any{"data": data}
+		return obj
+	}
+	spec["data"] = data
🤖 Fix all issues with AI agents
In `@pkg/controllers/externalsecret/externalsecret_controller_manifest.go`:
- Around line 232-235: The error message uses es.Spec.Target.Name which may be
empty; update the error return in the genericTargetContentHash error branch to
use getTargetName(es) instead (i.e., replace es.Spec.Target.Name with
getTargetName(es)) so the logged target identifier is consistent and non-empty;
locate the code around the call to genericTargetContentHash(result) in
externalsecret_controller_manifest.go and change the fmt.Errorf call
accordingly.

Comment on lines +232 to 235
hash, err := genericTargetContentHash(result)
if err != nil {
return nil, fmt.Errorf("failed to hash target %q content: %w", es.Spec.Target.Name, err)
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Error message may print an empty target name.

es.Spec.Target.Name can be empty when the name falls back to es.Name (via getTargetName). Consider using getTargetName(es) here for a consistent, non-empty identifier in the error message.

Proposed fix
-	hash, err := genericTargetContentHash(result)
-	if err != nil {
-		return nil, fmt.Errorf("failed to hash target %q content: %w", es.Spec.Target.Name, err)
+	hash, err := genericTargetContentHash(result)
+	if err != nil {
+		return nil, fmt.Errorf("failed to hash target %q content: %w", getTargetName(es), err)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
hash, err := genericTargetContentHash(result)
if err != nil {
return nil, fmt.Errorf("failed to hash target %q content: %w", es.Spec.Target.Name, err)
}
hash, err := genericTargetContentHash(result)
if err != nil {
return nil, fmt.Errorf("failed to hash target %q content: %w", getTargetName(es), err)
}
🤖 Prompt for AI Agents
In `@pkg/controllers/externalsecret/externalsecret_controller_manifest.go` around
lines 232 - 235, The error message uses es.Spec.Target.Name which may be empty;
update the error return in the genericTargetContentHash error branch to use
getTargetName(es) instead (i.e., replace es.Spec.Target.Name with
getTargetName(es)) so the logged target identifier is consistent and non-empty;
locate the code around the call to genericTargetContentHash(result) in
externalsecret_controller_manifest.go and change the fmt.Errorf call
accordingly.

@github-actions github-actions bot added the size/l label Feb 7, 2026
Signed-off-by: Gergely Brautigam <182850+Skarlso@users.noreply.github.com>
@Skarlso Skarlso force-pushed the fix-informer-custom-resource-watch branch from c0a1cee to a7ec76c Compare February 7, 2026 18:37
…ched

Signed-off-by: Gergely Brautigam <182850+Skarlso@users.noreply.github.com>
@Skarlso Skarlso moved this to In Review in External Secrets Feb 8, 2026
@sonarqubecloud
Copy link
Copy Markdown

@Skarlso Skarlso merged commit 413d1f9 into external-secrets:main Feb 13, 2026
29 checks passed
@github-project-automation github-project-automation bot moved this from In Review to Done in External Secrets Feb 13, 2026
@Skarlso Skarlso deleted the fix-informer-custom-resource-watch branch February 13, 2026 17:52
nutmos pushed a commit to nutmos/external-secrets that referenced this pull request Feb 18, 2026
…secrets#5931)

Signed-off-by: Nattapong Ekudomsuk <nuttapong_mos@hotmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

kind/bug Categorizes issue or PR as related to a bug. size/l size/m

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

alpha feature "Targeting Custom Resources" - "Advanced Path Targeting" templating works not as expected

2 participants