Skip to content

ReleaseBinding same-namespace filter ignores an explicit targetRef.namespace #714

Description

@cbrgm

Summary

Found by CodeRabbit on #711. The same-namespace ReleaseBinding collection in the Target reconciler only accepts an empty targetRef.namespace, while the render-registry mapping treats both empty and the target's own namespace as local. So a ReleaseBinding that sets spec.targetRef.namespace explicitly to the target's own namespace falls through the cracks: the same-ns path skips it (namespace is not "") and the cross-ns path needs a ReferenceGrant which nobody creates for the own namespace. Result -> the Target can report no ReleaseBindings even though a perfectly valid one exists.

This is pre-existing behavior. It already was == "" before the ObjectReference refactor, #711 only renamed the field and keeps behavior 1:1 on purpose, so this is the follow-up to normalize it.

Where

  • pkg/controller/target_controller.go:268 -> same-ns filter, empty only:
    if rb.Spec.TargetRef.Name == target.Name && rb.Spec.TargetRef.Namespace == "" {
  • pkg/controller/target_controller.go:1165 -> the consistent one (registry mapping):
    (t.Spec.RenderRegistryRef.Namespace == "" || t.Spec.RenderRegistryRef.Namespace == reg.Namespace)
  • pkg/controller/target_controller.go:1434 -> the cross-ns collect uses rb.Spec.TargetRef.Namespace != target.Namespace, needs a look too so we dont double count a binding that sets its own namespace explicitly.

What needs to change

  • Accept explicit same-namespace in the same-ns filter, i.e. Namespace == "" || Namespace == target.Namespace, like the registry mapping already does
  • Make sure the same-ns and cross-ns paths dont double-count a binding with targetRef.namespace == target.Namespace (dedup)
  • Imho best would be to normalize the "empty or own namespace = local" rule across all ref consumers so they all behave the same

Notes

How to reproduce

Truly minimal, no render stack needed here (no ComponentVersion, no working Secret, no actual rendering). The bug already shows up at binding-collection, so 3 objects in one namespace are enough.

  1. Local cluster with SolAr -> make dev-cluster and deploy solar the usual dev way.
  2. Apply these into one namespace demo. The Registry only needs solarSecretRef set so the Target gets past registry-resolution, the Secret itself does not need to exist, and the Release does not need to exist either:
apiVersion: solar.opendefense.cloud/v1alpha1
kind: Registry
metadata:
  name: reg
  namespace: demo
spec:
  hostname: example.local:5000
  solarSecretRef:
    name: dummy          # only needs to be non-nil, the Secret itself can be absent
---
apiVersion: solar.opendefense.cloud/v1alpha1
kind: Target
metadata:
  name: cluster-1
  namespace: demo
spec:
  renderRegistryRef:
    name: reg
---
apiVersion: solar.opendefense.cloud/v1alpha1
kind: ReleaseBinding
metadata:
  name: repro-explicit-ns
  namespace: demo
spec:
  targetRef:
    name: cluster-1
    namespace: demo      # <- explicit, same ns as the binding itself
  releaseRef:
    name: any-release    # does not need to exist
  1. Look at the Target condition:
kubectl get target cluster-1 -n demo \
  -o jsonpath='{range .status.conditions[?(@.type=="ReleasesRendered")]}{.reason}{"\n"}{end}'

Actual: NoReleaseBindings, the binding is dropped even though it sits right there.
Expected: the Target sees the binding.

Now remove the namespace: demo line under targetRef (or leave it empty) and re-apply -> the reason flips away from NoReleaseBindings, the Target now moves on to resolving the Release. That flip is the whole bug, and you never needed a real render pipeline to see it.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingneeds-triageIssue that has not been reviewed

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions