diff --git a/conformance/tests/l0-static/prod-baremetal-not-dev-surface/check.sh b/conformance/tests/l0-static/prod-baremetal-not-dev-surface/check.sh index 1fd041a..b980cf8 100755 --- a/conformance/tests/l0-static/prod-baremetal-not-dev-surface/check.sh +++ b/conformance/tests/l0-static/prod-baremetal-not-dev-surface/check.sh @@ -437,12 +437,172 @@ tracked = [f for f in subprocess.run(["git", "-C", str(ROOT), "ls-files", "-z"], capture_output=True, text=True, check=True ).stdout.split("\0") if f] RENDERED_DIRS = ("platform/", "platform-config", "apps/", "catalog/", "clusters/base/") -for f in tracked: - if f.startswith(RENDERED_DIRS) and re.search(rf"(^|[/_.-]){re.escape(prod_token)}([/_.-]|$)", - f, re.I): - check(False, - f"{f} is a prod-specific file inside the shared configuration — every environment " - "renders these paths, so prod-only content here is development against prod") + +# THE ONE STRUCTURAL EXCEPTION: AN OVERLAY-SELECTED ENVIRONMENT HALF. +# ------------------------------------------------------------------ +# A filename carrying an environment token is a HEURISTIC for the harm R-ENV-03 names +# (content only prod runs, in a path every environment renders). It is not the harm +# itself. There is exactly one shape where a `-prod` name is the OPPOSITE of a prod-only +# fork: a credential that is TWO Secret objects, BOTH applied to BOTH clusters as base +# resources, where a cluster-vars knob decides only which one the shared consumer +# REFERENCES (clusters/base/cluster-vars.yaml, BACKUP_CREDENTIALS_SUFFIX: dev backs up to +# MinIO, prod to Cloudflare R2, and one shared Secret meant either environment's rotation +# broke the other). Nothing is forked: every environment renders the identical manifests +# and the identical object set, which is precisely the R-ENV-02 contract. +# +# So the exception is not a path allowlist — it is the PROPERTY that makes that shape +# safe, asserted from the tree, and a file is excused ONLY when all of it holds: +# +# (A) it declares nothing but Secrets whose names end in the environment suffix — the +# only kind of object this repo has a selection mechanism for; +# (1) it is listed in its own directory's kustomization.yaml, so EVERY environment +# applies it, and every environment's value of the selector resolves to an object +# that some resource of that same kustomization declares (same namespace) — no +# environment is left selecting a Secret that does not exist; +# (2) nothing references the suffixed name literally: every reference is a substituted +# expression `${VAR}`, and the suffixed FILE is named only by the +# kustomization that applies it — so no shared manifest hardcodes prod's half; +# (3) that VAR is defined for the base AND for every overlay (StrictPostBuildSubstitutions +# fails on an UNDEFINED variable, so an overlay that forgot it would break that +# cluster's reconciliation), and prod's value is the one that selects THIS file. +# +# References are read from PARSED YAML VALUES, never from raw text: the mechanism is +# documented by name in comments beside the resources (cluster-vars says so explicitly), +# and a name in a comment references nothing. Anything that fails any condition still +# fails, naming the condition it broke. +ENV_SUFFIX = f"-{prod_token}" +# Every environment that renders the shared configuration. +ENVS = ["base"] + sorted(p.parent.name + for p in (ROOT / "clusters").glob("*/kustomization.yaml") + if p.parent.name != "base") + + +def _docs(rel: str): + p = ROOT / rel + if not p.is_file(): + return None + try: + return [d for d in yaml.safe_load_all(p.read_text(errors="replace")) + if isinstance(d, dict)] + except Exception: + return None + + +def _strings(node): + if isinstance(node, str): + yield node + elif isinstance(node, dict): + for k, v in node.items(): + yield from _strings(k) + yield from _strings(v) + elif isinstance(node, (list, tuple)): + for v in node: + yield from _strings(v) + + +RENDERED_YAML = [f for f in tracked + if f.startswith(RENDERED_DIRS) and f.endswith((".yaml", ".yml"))] +VALUE_STRINGS = {f: list(_strings(_docs(f) or [])) for f in RENDERED_YAML} + + +def overlay_selected(f: str) -> list[str]: + """[] if f is an overlay-SELECTED environment half; else the conditions it breaks.""" + docs = _docs(f) + objs = [((d.get("kind")), (d.get("metadata") or {}).get("name"), + (d.get("metadata") or {}).get("namespace")) for d in (docs or [])] + if not objs or any(k != "Secret" or not isinstance(n, str) or not n.endswith(ENV_SUFFIX) + for k, n, _ in objs): + return [f"(A) it does not consist solely of Secret objects whose names end in " + f"{ENV_SUFFIX!r} — an overlay can select which SECRET a shared manifest " + f"references, and nothing else, so any other prod-suffixed object in a " + f"shared path is prod-only content"] + + reasons: list[str] = [] + d = str(Path(f).parent) + kfile = f"{d}/kustomization.yaml" + kdocs = _docs(kfile) or [] + res = [str(r) for r in ((kdocs[0].get("resources") if kdocs else None) or [])] + if Path(f).name not in res: + reasons.append(f"(1) {kfile} does not list it in `resources`, so it is not applied " + f"in every environment — one environment would carry an object the " + f"other does not") + + # Every Secret the applying kustomization declares: name -> namespace. + declared: dict[str, object] = {} + for r in res: + for dd in (_docs(f"{d}/{r}") or []): + if dd.get("kind") == "Secret": + md = dd.get("metadata") or {} + declared[str(md.get("name"))] = md.get("namespace") + + for _, name, ns in objs: + base_name = name[:-len(ENV_SUFFIX)] + pat = re.escape(base_name) + r"\$\{([A-Za-z_][A-Za-z0-9_]*)\}" + sel = sorted({m for ss in VALUE_STRINGS.values() for s in ss + for m in re.findall(pat, s)}) + # (2) literal references to the suffixed OBJECT — anywhere but this file, except + # the applying kustomization naming this FILE (which is how it gets applied). + lit = sorted({f2 for f2, ss in VALUE_STRINGS.items() for s in ss + if name in s and f2 != f + and not (f2 == kfile and s == Path(f).name)}) + if lit: + reasons.append(f"(2) {', '.join(lit)} names the Secret {name!r} literally — a " + f"shared manifest that hardcodes prod's half makes every " + f"environment use prod's credential; reference " + f"{base_name}${{...}} instead") + # (2) literal references to the suffixed FILE from anywhere but its kustomization. + stray = sorted({f2 for f2, ss in VALUE_STRINGS.items() for s in ss + if Path(f).name in s and f2 not in (f, kfile)}) + if stray: + reasons.append(f"(2) {', '.join(stray)} names the file {Path(f).name!r} " + f"literally — only {kfile} may apply it") + if not sel: + reasons.append(f"(2) no manifest references {base_name}${{...}}: nothing selects " + f"between the halves, so this file is prod-only content that " + f"only prod could ever consume") + continue + if len(sel) > 1: + reasons.append(f"(2) {base_name} is selected by more than one variable {sel} — " + f"they can drift apart and leave one consumer on the other " + f"environment's credential") + var = sel[0] + vals = env_values(var) + missing = [e for e in ENVS if e not in vals] + if missing: + reasons.append(f"(3) ${{{var}}} is not defined for {missing} (defined for " + f"{sorted(vals)}) — StrictPostBuildSubstitutions fails on an " + f"undefined variable, so that environment would stop " + f"reconciling, or silently inherit the other's credential") + for e, v in sorted(vals.items()): + want = base_name + v + if want not in declared: + reasons.append(f"(1) environment {e!r} selects Secret {want!r} via " + f"${{{var}}}={v!r}, which no resource of {kfile} declares — " + f"that environment renders a dangling reference") + elif declared[want] != ns: + reasons.append(f"(1) environment {e!r} selects Secret {want!r}, declared in " + f"namespace {declared[want]!r}, but this half is in {ns!r} — " + f"the halves are not interchangeable") + if prod_overlay and prod_overlay in vals and base_name + vals[prod_overlay] != name: + reasons.append(f"(3) the prod overlay's ${{{var}}}={vals[prod_overlay]!r} selects " + f"{base_name + vals[prod_overlay]!r}, not this file's {name!r} — " + f"prod's half is applied but no environment selects it") + return reasons + + +flagged = [f for f in tracked + if f.startswith(RENDERED_DIRS) + and re.search(rf"(^|[/_.-]){re.escape(prod_token)}([/_.-]|$)", f, re.I)] +for f in flagged: + why = overlay_selected(f) + check(not why, + f"{f} is a prod-specific file inside the shared configuration — every environment " + f"renders these paths, so prod-only content here is development against prod. It is " + f"not an overlay-selected environment half either: " + " AND ".join(why)) + if not why: + note(f"{f} carries the {prod_token!r} token but is an OVERLAY-SELECTED half: both " + f"halves are applied in every environment and only the cluster-vars selector " + f"differs (R-ENV-02), so it is not prod-only content") check(len(tracked) > 50, f"git ls-files returned only {len(tracked)} paths — not a real checkout") # --------------------------- (6) prod work exists only with a declaration of readiness