Skip to content

Resources namespaced via kustomization namespace: are duplicated as namespace-less phantoms #820

Description

@banschikovde

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions