Skip to content

Deletion webhook lists a dependent N× when a rule references the same target via multiple fields #106

Description

@BergCyrill

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:

  1. 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.

  2. 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

  1. Dedupe keys per GVR in rebuildTargetIndex so FindByTargetGVR returns one
    entry per matching field.
  2. Deduplicate blockers by dependent identity (kind/namespace/name) before
    building the message.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Fields

No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions