feat(api): make spec.source.version optional + rename Destination print column#97
Merged
Conversation
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.
Bench smoke —
|
| 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: a1b0909 • Workflow run
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).
4 tasks
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Two adjustments to the v1 SourceRef / print-column shape, bundled because they share regen + doc surfaces.
1.
spec.source.versionis now optional for the core groupDrop the SourceRef CEL
XValidationrule (size(self.group) != 0 || size(self.version) != 0, introduced in #76) that requiredversionwhengroupwas empty. Barekind: ConfigMap(no group, no version) is now the lead form for core-group sources.resolveGVRalready handled emptyVersionuniformly via the RESTMapper, so no controller code change — pure admission relaxation.spec.source.versionis now optional for any group. Empty resolves to the RESTMapper's preferred served version (for core, currentlyv1). Existing manifests with explicitversion: v1continue to validate unchanged.Ready=Trueand surfaces the expectedSourceResolvedmessage (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/api-stability.mdso 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 inREADME.md,CLAUDE.md,docs/comparison.md, and the literal CEL admission error strings quoted indocs/troubleshooting.md.2.
Destinationprint column →Destination-NameRenamed on both
ProjectionandClusterProjection. Both CRDs map the column to.status.destinationName(the name only, not a fuller location).Destinationwas asymmetric withSource-Nameand ambiguous onClusterProjection, where it sits next toTargets(the namespace count). The rename:Source-NameDoc tables in README,
docs/index.md,docs/getting-started.md(Projection + ClusterProjection), anddocs/crd-reference.md(both Print-columns tables + the explanatory sentence) updated. Scripts parsingkubectl get projectiontable 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 testpasses (envtest k8s 1.34.1, controller coverage 82.6%, no regressions)make manifests generate docs-ref sync-chart-crds update-crd-goldenruns clean — all six CRD copies (config/crd/bases × 2, api/v1/testdata × 2, charts/projection/crds × 2) are byte-identicalexamples/configmap-cross-namespace.yamlto a Kind v1.32.0 cluster — Projection reachesReady=Truein ~11s;SourceResolved.messagereads exactlyresolved /ConfigMap to preferred version v1(matches the envtest assertion); destination ConfigMap mirrored with theprojection.sh/owned-by-projectionannotationgroup: ""+version: v1(the pre-relaxation form) — admits and reconciles toReady=True;SourceResolved.messageis empty as expected for pinned sources (perresolvedVersionMessageinconditions.go:45-46)hack/record-demo.shran clean against a freshkind-projection-democluster with the new operator;docs/assets/demo.castanddocs/assets/demo.gifupdated