Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .coverage-baseline
Original file line number Diff line number Diff line change
@@ -1 +1 @@
75.6
75.8
74 changes: 74 additions & 0 deletions docs/UPGRADING.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,80 @@ guidance that the changelog's breaking-change entries link to.
We are pre-1.0, so breaking changes bump the **minor** version (release-please is configured with
`bump-minor-pre-major`) rather than the major. Read the relevant entry before upgrading across it.

## Unreleased — a `patches:` block no longer refuses the folder (next minor; more folders accepted)

A kustomization declaring `patches:` used to refuse the whole `GitTarget`. Not the edit — the
**target**. A folder whose patch touched a replica count also lost `images:`/`replicas:`
edit-through, which the patch had nothing to do with.

A patch is now **tolerated as read-only build context**:

- the folder is **accepted**, and what it renders is mirrored;
- the patch file is **retained, never managed** — it is a build input, not a manifest. (It is a KRM
document, so without this the operator would index it as one: match a live object to it, mirror a
whole Deployment over the sparse patch, or sweep it away as an orphan.)
- `images:` / `replicas:` edit-through works in a patched folder exactly as it does anywhere else;
- an edit to a field **the patch owns** is refused *per object* — `WriteBoundaryRefused`, naming the
file and the object — because authoring a patch is still not supported.

**Tolerating a patch is not authoring one.** Nothing is ever written into a patch file.

Exactly one shape is tolerated. The rest are refused **by name**, so the message says what to fix:

| Shape | Verdict |
|---|---|
| `patches: [{path: patch.yaml}]` — a sparse KRM document inside the tree | **tolerated** |
| `patches: [{patch: "..."}]` — inline (including an inline JSON6902 op list) | refused: `patches-inline` |
| `patches: [{path: json-patch.yaml}]` where the file is an `op`/`path`/`value` list | refused: `patches-json6902` |
| a `path:` naming no file in the tree, or escaping it | refused: `patches-outside-tree` |
| `patchesStrategicMerge:`, `patchesJson6902:` (deprecated spellings) | refused under their own names |

## Unreleased — the build's output stops leaking into the build's input (next minor; bug fix + behavior change)

**If your kustomization declares `labels:`, `commonLabels:`, `commonAnnotations:` or `namespace:`,
the operator has been writing those injected values into your source manifests.** Measured, on a
folder we accept today, with nothing changed in the cluster and nothing changed in the render:

```yaml
# kustomization.yaml (yours) # deployment.yaml, after one reconcile (ours)
labels: metadata:
- pairs: labels:
env: prod env: prod # <- the OVERLAY's, absorbed into the BASE
commonAnnotations: annotations:
owner: platform owner: platform
```

The writer mirrors a live object into the file that produced it — but under kustomize that file is
not what the cluster runs, and mirroring the live object straight back writes the build's own
output into the build's input. Every reconcile of an unchanged folder produced a commit, and the
file was left wrong: delete the kustomization later and the injected values are now yours forever.
In a base shared by two overlays, the value baked in is **one environment's**.

The fix needs no model of any transformer, and it is now the rule the writer follows:

> **Where the live object and the render agree, the source keeps its bytes. Where they disagree,
> the user changed something, and that is what we write.**

**Nothing needs migration** — this is a fix, and it makes the operator stop rewriting files it
should have left alone. If a past reconcile has already baked injected metadata into a manifest,
the operator will not remove it for you; remove it by hand and it will not come back.

**Two behavior changes go with it.**

*The re-render now runs for any document a kustomization produces*, not only for one an
`images:`/`replicas:` entry governs. A change to a field the build supplies (relabelling a live
object whose label a `labels:` block sets, say) cannot be expressed in the repository: the source
file cannot hold it, because the build would stamp its own value straight back. That write now
**refuses the flush** — `GitPathAccepted=False` / `WriteBoundaryRefused`, naming the file and the
object — where before it was committed and silently never converged.

*A live change the projection cannot place is refused* (`unplaceable-edit`). It fires when the
build and the live object have **both** rewritten one list whose elements carry no unique `name:`
to pair them by — the source's `args:` rewritten by a patch, for example. There is no honest way
to say which of the source's bytes you meant to keep, and pairing the lists by position is
measurably wrong (kustomize *prepends* a container a patch adds), so the operator refuses rather
than guesses.

## Unreleased — kustomize decides what it renders, and what it touched (next minor; bug fixes + behavior change)

The write path no longer contains a re-implementation of kustomize's image and replica
Expand Down
3 changes: 2 additions & 1 deletion docs/design/support-boundary/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,10 @@
| Topic | Docs |
|---|---|
| **The boundary** | [support-contract.md](support-contract.md) — the single statement · [kustomize-support-boundary.md](kustomize-support-boundary.md) — field taxonomy + layout allowlist · [gittarget-granularity-and-cross-environment-edits.md](gittarget-granularity-and-cross-environment-edits.md) — **the write boundary; the one home of fan-in = 1** |
| **Renderers & provenance** | [render-attribution.md](render-attribution.md) — attribution and verification · [render-root-scoping.md](render-root-scoping.md) — render roots and the oracle · [render-fidelity.md](render-fidelity.md) — **our render is not the orchestrator's; refuse where they diverge** · [kpt-and-krm-functions.md](kpt-and-krm-functions.md) — how Kpt packages, setters, and KRM functions may fit safely |
| **Orchestrators & expansion** | [orchestrator-knowledge-boundary.md](orchestrator-knowledge-boundary.md) — renderability vs ownership; claims about paths · [expansion-boundary-and-corpus-organisation.md](expansion-boundary-and-corpus-organisation.md) — provenance; ApplicationSet vs ResourceSet; Helm · [`../../facts/expansion-provenance-markers.md`](../../facts/expansion-provenance-markers.md) — **the measured markers** · [argocd-bi-directional.md](argocd-bi-directional.md) — why `selfHeal` is incompatible |
| **Documents & secrets** | [resource-capability-model.md](resource-capability-model.md) — what may I do to this document · [write-only-encrypted-secrets.md](write-only-encrypted-secrets.md) — SOPS · [sealed-secrets-and-external-secrets.md](sealed-secrets-and-external-secrets.md) |
| **Edits with no home** | [unreflectable-edits-and-write-gating.md](unreflectable-edits-and-write-gating.md) |
| **Edits with no home** | [unreflectable-edits-and-write-gating.md](unreflectable-edits-and-write-gating.md) — tier-1/2/3 accounting · [admission-consent.md](admission-consent.md) — say yes to a blast-radius refusal · [orchestrator-reconcile-trigger.md](orchestrator-reconcile-trigger.md) — revert a refusal / order around origin drift |
| **Discovery (read-only)** | [repo-discovery-and-onboarding-scan.md](repo-discovery-and-onboarding-scan.md) |
| **Shipped** | [finished/images-and-replicas-edit-through.md](finished/images-and-replicas-edit-through.md) · [finished/higher-level-krm-documents.md](finished/higher-level-krm-documents.md) |
| **Evidence** | [`test/fixtures/gitops-layouts/`](../../../test/fixtures/gitops-layouts/) — the corpus of real-world repo shapes, and the generated behavioural baseline beside it |
Expand Down
Loading