Summary
When a DependencyRule references the same target resource type through multiple
field paths, the deletion webhook's denial message lists each blocking dependent
many times instead of once.
Reproduction
DependencyRule with 4 dependency entries, all targeting resource endpoints:
.spec.defaults.srcRef.name
.spec.defaults.dstRef.name
.spec.artifact.srcRef.name
.spec.artifact.dstRef.name
A single Order/order1 sets the two defaults.* paths to ep-with-secret, then
delete the endpoint:
admission webhook denied the request: cannot delete endpoints/ep-with-secret:
still referenced by Order/order1, Order/order1, Order/order1, Order/order1,
Order/order1, Order/order1, Order/order1, Order/order1
The count is (fields the Order actually populates) × (total fields the rule targets at endpoints) = 2 × 4 = 8.
Root cause
Two compounding bugs:
-
Registry (rule_registry.go) — rebuildTargetIndex appends the rule key
to byTarget[GVR] once per index field rather than once per rule. With N
fields targeting the same GVR, FindByTargetGVR then iterates N key-copies ×
N matching fields = N² entries (each field path repeated N times) instead
of N.
-
Validator (deletion_validator.go) — blockers is concatenated across
all entries with no deduplication, so the same dependent object is listed once
per matching entry. This is independent: even with a correct registry, an Order
referencing the target through two fields would still appear twice.
Expected behavior
The denial message lists each blocking dependent object exactly once, regardless
of how many fields reference the target. Deletion protection itself already works
correctly — only the message is wrong/noisy.
Fix outline
- Dedupe keys per GVR in
rebuildTargetIndex so FindByTargetGVR returns one
entry per matching field.
- Deduplicate
blockers by dependent identity (kind/namespace/name) before
building the message.
Summary
When a
DependencyRulereferences the same target resource type through multiplefield paths, the deletion webhook's denial message lists each blocking dependent
many times instead of once.
Reproduction
DependencyRulewith 4 dependency entries, all targeting resourceendpoints:.spec.defaults.srcRef.name.spec.defaults.dstRef.name.spec.artifact.srcRef.name.spec.artifact.dstRef.nameA single
Order/order1sets the twodefaults.*paths toep-with-secret, thendelete the endpoint:
The count is
(fields the Order actually populates) × (total fields the rule targets at endpoints)=2 × 4 = 8.Root cause
Two compounding bugs:
Registry (
rule_registry.go) —rebuildTargetIndexappends the rule keyto
byTarget[GVR]once per index field rather than once per rule. With Nfields targeting the same GVR,
FindByTargetGVRthen iterates N key-copies ×N matching fields = N² entries (each field path repeated N times) instead
of N.
Validator (
deletion_validator.go) —blockersis concatenated acrossall entries with no deduplication, so the same dependent object is listed once
per matching entry. This is independent: even with a correct registry, an Order
referencing the target through two fields would still appear twice.
Expected behavior
The denial message lists each blocking dependent object exactly once, regardless
of how many fields reference the target. Deletion protection itself already works
correctly — only the message is wrong/noisy.
Fix outline
rebuildTargetIndexsoFindByTargetGVRreturns oneentry per matching field.
blockersby dependent identity (kind/namespace/name) beforebuilding the message.