Skip to content

feat(api): make spec.source.version optional + rename Destination print column#97

Merged
be0x74a merged 15 commits into
mainfrom
feat/api-source-version-optional
May 10, 2026
Merged

feat(api): make spec.source.version optional + rename Destination print column#97
be0x74a merged 15 commits into
mainfrom
feat/api-source-version-optional

Conversation

@be0x74a
Copy link
Copy Markdown
Member

@be0x74a be0x74a commented May 10, 2026

Summary

Two adjustments to the v1 SourceRef / print-column shape, bundled because they share regen + doc surfaces.

1. spec.source.version is now optional for the core group

Drop the SourceRef CEL XValidation rule (size(self.group) != 0 || size(self.version) != 0, introduced in #76) that required version when group was empty. Bare kind: ConfigMap (no group, no version) is now the lead form for core-group sources. resolveGVR already handled empty Version uniformly via the RESTMapper, so no controller code change — pure admission relaxation.

  • API: spec.source.version is now optional for any group. Empty resolves to the RESTMapper's preferred served version (for core, currently v1). Existing manifests with explicit version: v1 continue to validate unchanged.
  • Tests: new envtest spec asserts a bare-Kind Projection reaches Ready=True and surfaces the expected SourceResolved message (resolved /ConfigMap to preferred version v1). Regression check verified — re-adding the CEL rule fails the test with the original admission error, then reverting passes again with no working-tree drift.
  • Docs: rewrote samples, examples, and 9 user-facing docs to lead with bare-Kind for core sources. Explicit rescission breadcrumb in docs/api-stability.md so future grep paths (XValidation, size(self.group), Rescinded, version is required when group is empty) land cleanly. Three independent post-implementation review passes on the full doc tree caught and fixed contradictions in README.md, CLAUDE.md, docs/comparison.md, and the literal CEL admission error strings quoted in docs/troubleshooting.md.

2. Destination print column → Destination-Name

Renamed on both Projection and ClusterProjection. Both CRDs map the column to .status.destinationName (the name only, not a fuller location). Destination was asymmetric with Source-Name and ambiguous on ClusterProjection, where it sits next to Targets (the namespace count). The rename:

  • symmetric with Source-Name
  • matches the JSONPath path
  • removes the where-vs-what ambiguity on the cluster CRD

Doc tables in README, docs/index.md, docs/getting-started.md (Projection + ClusterProjection), and docs/crd-reference.md (both Print-columns tables + the explanatory sentence) updated. Scripts parsing kubectl get projection table output by column name need to be updated; pre-v1.0 with no known users so no migration shim.

Pre-v1.0, no users — clean relaxation, no migration shim, no deprecation cycle.

Test plan

  • make test passes (envtest k8s 1.34.1, controller coverage 82.6%, no regressions)
  • make manifests generate docs-ref sync-chart-crds update-crd-golden runs clean — all six CRD copies (config/crd/bases × 2, api/v1/testdata × 2, charts/projection/crds × 2) are byte-identical
  • Synthetic regression: re-adding the CEL rule causes the new admission test to fail at admission with the rule's exact message; reverting restores green
  • Manual smoke: applied examples/configmap-cross-namespace.yaml to a Kind v1.32.0 cluster — Projection reaches Ready=True in ~11s; SourceResolved.message reads exactly resolved /ConfigMap to preferred version v1 (matches the envtest assertion); destination ConfigMap mirrored with the projection.sh/owned-by-projection annotation
  • Manual smoke: applied a Projection with explicit group: "" + version: v1 (the pre-relaxation form) — admits and reconciles to Ready=True; SourceResolved.message is empty as expected for pinned sources (per resolvedVersionMessage in conditions.go:45-46)
  • Demo regen: hack/record-demo.sh ran clean against a fresh kind-projection-demo cluster with the new operator; docs/assets/demo.cast and docs/assets/demo.gif updated

be0x74a added 12 commits May 8, 2026 20:05
Drop the CEL rule that required version when group was empty. Core
sources can now be referenced by kind alone (e.g. kind: ConfigMap),
matching the unpinned form already valid for non-core groups. Empty
version resolves to the cluster's preferred served version via the
RESTMapper, identical to the existing non-core path; the controller
already handles this case in resolveGVR, so no controller code
changes.

Existing manifests with explicit version: v1 continue to work
unchanged — this is a pure relaxation, not a breaking change.
…ed admission

Two stale comments induced by the previous commit (3fed56e):
- SourceRef.Version godoc said "Omit for non-core groups" — implied
  omission was invalid for core, contradicting the new struct-level
  paragraph that says both group and version are optional.
- resolveGVR's leading comment claimed admission rejects empty Version
  with empty Group, which is no longer true.

Reflows into the rendered CRD descriptions (and goldens) verbatim.
Closes a regression vector the bare-Kind test previously missed: a
defaulter that injected version: v1 would silently mask the unpinned
code path while still letting Ready=True flip. Asserting the resolved-
version message ("resolved /ConfigMap to preferred version v1") forces
the test to traverse the empty-Version branch in resolveGVR — symmetric
with the existing unpinned-apps-Deployment spec.
Final-review pass found three Important + two Minor surviving claims
that contradicted the relaxed admission shape:

- docs/concepts.md: reconcile-lifecycle prose said "(non-core groups
  only)" — drop the parenthetical.
- docs/index.md: features-at-a-glance bullet said "on non-core groups"
  — replace with "for any group".
- docs/comparison.md: K8s ≥ 1.32 prerequisites cited the rescinded
  SourceRef CEL rule as a reason for the version floor — drop it.
  ClusterProjection's destination mutex rules are now the only reason.
- docs/getting-started.md: tightened "group is required and version is
  optional" to match crd-reference's "Required: no" on group itself.
- CLAUDE.md: stale CEL claim listed the dropped rule as current state
  of SourceRef — replace with the new optional-everywhere wording.
Three independent review passes found 15 residual issues that the
previous final-review missed. Hard contradictions on the lead docs
that the previous pass didn't open:

- README.md "Validated at admission" still claimed CEL enforces
  version-required-when-group-empty (the rescinded rule).
- README.md "Any Kind" still said preferred-version lookup applies
  "for non-core groups" only.
- CLAUDE.md SourceRef summary said "all required" + repeated the
  rescinded CEL claim — directly contradicting line 46 of the same
  file (which the prior fixup had updated correctly).

Drifted citations and stale literals:

- docs/api-stability.md cited a nonexistent
  api/v1/clusterprojection_types_golden_test.go file (the single
  golden test covers both CRDs in one table).
- docs/comparison.md still listed CEL on SourceRef among admission
  validations.
- docs/troubleshooting.md quoted apiserver admission errors for the
  destination CEL rules with the wrong literal text — users grepping
  the actual error wouldn't find the doc entry. Replaced with the
  exact CEL `message=` strings from clusterprojection_types.go.
- examples/configmap-cross-namespace.yaml and
  examples/with-overlay-annotations.yaml comments referenced the v0.2
  `projection.sh/owned-by` annotation key (current key is
  `projection.sh/owned-by-projection` /
  `projection.sh/owned-by-cluster-projection`).

Lead-with-bare-Kind consistency on the entry-point demos:

- README 60-second demo, docs/index.md Projection demo, docs/index.md
  ClusterProjection demo, docs/concepts.md three Destination examples
  all still spelled out group: "" + version: v1 for core ConfigMap
  sources, despite getting-started.md, samples/, and examples/ being
  flipped to bare-Kind by earlier commits in this branch.
@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 10, 2026

Bench smoke — mixed-typical

End-to-end source-update latency from a 2-vCPU GHA runner. Treat absolute numbers as a sanity check, not a perf claim — runner noise is high. The point of this check is to catch shape-break regressions on api/v1 / controller / bench changes before merge. (Self-heal and ns-flip distributions are recorded in bench.json but omitted here for signal-to-noise.)

Profile

100 namespaced Projections + 50 CP-selector destinations + 10 CP-list destinations, layered in one bootstrap.

Results — source-update latency

Path Samples p50 p95 p99
NP single-target 100 15.8ms 17.8ms 31.8ms
CP-selector earliest 30 49.8ms 96.4ms 101.8ms
CP-selector slowest 30 119.7ms 169.6ms 178.5ms
CP-list earliest 30 35.9ms 58.9ms 129.4ms
CP-list slowest 30 35.9ms 58.9ms 129.4ms

Total wall: 87s • Commit: a1b0909Workflow run

be0x74a added 3 commits May 10, 2026 21:29
Both Projection and ClusterProjection mapped the Destination column to
.status.destinationName (just the name, not a fuller location). The
unqualified label was asymmetric with Source-Name and ambiguous on
ClusterProjection (sitting next to Targets, which is the namespace count).

Rename to Destination-Name on both CRDs:
- symmetric with Source-Name
- matches the JSONPath path
- removes the where-vs-what ambiguity on the cluster CRD

Updated doc references in README, docs/index, docs/getting-started, and
docs/crd-reference (both Print columns tables + the explanatory sentence).
@be0x74a be0x74a changed the title feat(api): make spec.source.version optional for core group feat(api): make spec.source.version optional + rename Destination print column May 10, 2026
@be0x74a be0x74a merged commit a2c4af3 into main May 10, 2026
16 checks passed
@be0x74a be0x74a deleted the feat/api-source-version-optional branch May 10, 2026 19:41
be0x74a added a commit that referenced this pull request May 10, 2026
* release: v0.3.1 prep (CHANGELOG, chart bump)

Promote Unreleased entries (#90 SourceNotFound distinction, #94 release-time
bench workflow, #97 source.version optional + Destination column rename) to
the [0.3.1] - 2026-05-10 heading. Bump chart version and appVersion to 0.3.1.
Refresh artifacthub.io/crdsExamples to use bare-Kind for the core ConfigMap
sources, matching the new lead form shipped in #97.

* docs: bump install/cosign examples to v0.3.1; fix v0.3.0 misattribution

Pre-tag documentation sweep found two classes of issue:

- Install examples in README, getting-started, security (cosign), and the
  chart README still referenced v0.3.0. Bumped to v0.3.1 to match the
  release being cut.
- docs/troubleshooting.md attributed the source.version relaxation to
  v0.3.0 in two places — incorrect, since v0.3.0 still carried the CEL
  rule (rescinded only in v0.3.1, per CHANGELOG and api-stability.md).
  Fixed by retiming one mention to pre-v0.3.1/v0.3.1 and rephrasing the
  other to a present-tense, version-neutral statement.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant