Resources namespaced via kustomization namespace: are duplicated as namespace-less phantoms
flate version: v0.4.10 (latest at time of writing)
Related: #819 (different root cause, same flate test false-positive symptom in
the triggering repo).
Summary
When a resource receives its namespace from a kustomization namespace: field
(rather than an explicit metadata.namespace) and that kustomization is pulled
into the Flux KS via a relative-path resources: reference, flate loads the
resource twice: once correctly namespaced (from the kustomize-built path)
and once as a namespace-less phantom (from a direct file load that bypasses the
kustomize namespace transform). The phantom then fails source/dependency
lookups because it searches the wrong (empty/default) namespace.
Minimal reproducer
Directory tree:
cluster/
ks.yaml # Flux Kustomization: path ./cluster/per-cluster
shared/demo/
kustomization.yaml # namespace: demo
hr.yaml # HelmRelease demo (no metadata.namespace)
helmrepo.yaml # HelmRepository demo (no metadata.namespace)
per-cluster/
kustomization.yaml # resources: [../shared/demo] <- relative ref
cluster/shared/demo/kustomization.yaml:
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: demo
resources:
- hr.yaml
- helmrepo.yaml
cluster/shared/demo/hr.yaml:
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: demo
spec:
interval: 5m
chart:
spec:
chart: nginx
version: "15.5.0"
sourceRef:
kind: HelmRepository
name: demo
cluster/shared/demo/helmrepo.yaml:
apiVersion: source.toolkit.fluxcd.io/v1
kind: HelmRepository
metadata:
name: demo
spec:
interval: 1h
url: https://charts.bitnami.com/bitnami
cluster/per-cluster/kustomization.yaml:
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../shared/demo
cluster/ks.yaml:
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: app
namespace: flux-system
spec:
interval: 5m
path: ./cluster/per-cluster
prune: true
sourceRef:
kind: GitRepository
name: flux-system
Expected vs actual
kustomize build applies namespace: demo to both objects:
$ kustomize build cluster/per-cluster | grep -E 'kind:|name:|namespace:'
kind: HelmRelease
name: demo
namespace: demo
kind: HelmRepository
name: demo
namespace: demo
flate emits the HelmRelease twice — once namespaced (✓) and once namespace-less
(✗, fails chart-source lookup):
$ flate get hr --path cluster
NAMESPACE NAME CHART VERSION SOURCE
demo -demo/nginx 15.5.0 demo # phantom (no ns)
demo demo demo-demo/nginx 15.5.0 demo # correct
flate error: reconcile completed with 1 failure(s):
HelmRelease/demo: chart source HelmRepository/demo not ready: dependency not found
flate test likewise reports both HelmRelease demo/demo (PASS) and
HelmRelease demo (FAIL) and returns a non-zero exit code.
Analysis
flate reconciles the KS path through kustomize and obtains the correctly
namespaced objects. Additionally, a direct file walk loads the raw
hr.yaml/helmrepo.yaml (which carry no metadata.namespace) and registers
namespace-less copies in the store. The two loads are not deduplicated against
each other because their namespace differs. The phantom HelmRelease then
resolves sourceRef.name: demo against the default namespace, misses the
HelmRepository (which lives in demo), and fails.
Adding an explicit metadata.namespace: demo to hr.yaml/helmrepo.yaml makes
the two loads agree and the phantom disappears — confirming the divergence is
specifically the kustomize namespace: transform not being applied to the
direct-file-load copy.
Real-world impact
Any repo that centralizes namespace via kustomization namespace: (a common
pattern) rather than stamping metadata.namespace on every object produces one
phantom FAIL per HelmRelease (and analogous resources) in flate test, forcing
a non-zero exit even though every real resource reconciles cleanly. This makes
flate test unusable as a CI gate for such repos without either sprinkling
redundant metadata.namespace on every object or filtering the false positives.
Resources namespaced via kustomization
namespace:are duplicated as namespace-less phantomsflate version:
v0.4.10(latest at time of writing)Related: #819 (different root cause, same
flate testfalse-positive symptom inthe triggering repo).
Summary
When a resource receives its namespace from a kustomization
namespace:field(rather than an explicit
metadata.namespace) and that kustomization is pulledinto the Flux KS via a relative-path
resources:reference, flate loads theresource twice: once correctly namespaced (from the kustomize-built path)
and once as a namespace-less phantom (from a direct file load that bypasses the
kustomize namespace transform). The phantom then fails source/dependency
lookups because it searches the wrong (empty/default) namespace.
Minimal reproducer
Directory tree:
cluster/shared/demo/kustomization.yaml:cluster/shared/demo/hr.yaml:cluster/shared/demo/helmrepo.yaml:cluster/per-cluster/kustomization.yaml:cluster/ks.yaml:Expected vs actual
kustomize buildappliesnamespace: demoto both objects:flate emits the HelmRelease twice — once namespaced (✓) and once namespace-less
(✗, fails chart-source lookup):
flate testlikewise reports bothHelmRelease demo/demo(PASS) andHelmRelease demo(FAIL) and returns a non-zero exit code.Analysis
flate reconciles the KS path through kustomize and obtains the correctly
namespaced objects. Additionally, a direct file walk loads the raw
hr.yaml/helmrepo.yaml(which carry nometadata.namespace) and registersnamespace-less copies in the store. The two loads are not deduplicated against
each other because their
namespacediffers. The phantom HelmRelease thenresolves
sourceRef.name: demoagainst the default namespace, misses theHelmRepository (which lives in
demo), and fails.Adding an explicit
metadata.namespace: demotohr.yaml/helmrepo.yamlmakesthe two loads agree and the phantom disappears — confirming the divergence is
specifically the kustomize
namespace:transform not being applied to thedirect-file-load copy.
Real-world impact
Any repo that centralizes namespace via kustomization
namespace:(a commonpattern) rather than stamping
metadata.namespaceon every object produces onephantom FAIL per HelmRelease (and analogous resources) in
flate test, forcinga non-zero exit even though every real resource reconciles cleanly. This makes
flate testunusable as a CI gate for such repos without either sprinklingredundant
metadata.namespaceon every object or filtering the false positives.