docs(api): move the CiliumFQDN/CalicoFQDN removal floor to v2.0.0 - #1867
Merged
Conversation
karlkfi
added a commit
that referenced
this pull request
Sep 8, 2026
…estion Review of #1867 refuted Q1068's one-line fix. Listing the request's own version in a webhook rule stops the apiserver converting, so a v2beta1 object reaches a v2alpha1-typed handler and DecodeInto rejects it; under failurePolicy: Fail that turns a silent loss of validation into every write being rejected. The row now says so, sizes M, and names the envtest that settles the apiserver half. Q1076 records the option Q452 did not consider: a CEL transition rule on the v2 schema alone, admitting an alias only where oldSelf carried one. It has to be settled before the tag, and its feature-gate premise is unmeasured.
karlkfi
added a commit
that referenced
this pull request
Sep 8, 2026
The third carriage bullet claimed the annotation carrier could not be inert because the reconciler would have to read it. Both halves are false: the GMC reconciles the spoke, so the conversion restores the value into the typed field and no controller names the annotation, and the RunnerSet annotations carry the same hand-settable hazard the bullet ascribed uniquely to the hypothetical FQDN one. The decision is unchanged and the real objection is stated instead: extending a known unvalidated-annotation hazard to an egress-enforcement backend, which is a weighting rather than a mechanical bar. Also records the ratcheting question as considered (Q1076), pre-empts the hub-versus-storage escape, states the carry-the-string cost as the read-modify-write break it actually is, and replaces a forecast of project intent in the operator notice with the committed fact.
karlkfi
added a commit
that referenced
this pull request
Sep 8, 2026
Q1076 claimed both transition rules exist in both v2 versions. Measured: actionsgateway_types.go carries githubURL's in v2alpha1 and v2beta1; runnerset_types.go carries acquisitionProtocol's in v2alpha1 only, because v2beta1 dropped the field. That is the same fact the annotation carrier exists for, so the row now says which is which. Drops the clause naming an earlier draft of the alias decision. The repo squash-merges, so the commit it points at does not reach main and a reader of the merged doc cannot find it. The substance it introduced -- that the objection is a weighting rather than a mechanical bar -- stays.
karlkfi
marked this pull request as ready for review
September 8, 2026 05:18
karlkfi
marked this pull request as draft
September 8, 2026 05:44
This was referenced Sep 8, 2026
karlkfi
force-pushed
the
claude/q452-29ed0a
branch
from
September 8, 2026 08:37
d332ec8 to
d13ce5d
Compare
karlkfi
marked this pull request as ready for review
September 8, 2026 08:37
karlkfi
force-pushed
the
claude/q452-29ed0a
branch
from
September 8, 2026 18:38
6007aff to
fec9766
Compare
…itical path Q1082 is decided: v2beta1 is not served past v2.0.0, because v2.0.0 is a major release and a major release may make breaking changes. Q452 follows from it and closes with the opposite answer to the one it was heading for: v2 omits the deprecated FQDN aliases. Q1085 carries what the decision puts on the critical path and Path 1 would not have needed: the formal v2beta1 deprecation notice in the release notes, an admission change from warn to reject on new alias writes, and the pre-upgrade check for unmigrated objects. The check is the load-bearing piece, because one object naming an alias fails the conversion request it is batched into and breaks LIST at v2 cluster-wide. Q1068 is corrected rather than closed. v2beta1 now goes at the same tag as v2alpha1, so the un-matched rules leave v2 as the only served version, and the alias warning that surfaced the defect dies with the aliases instead of riding into the gap. The other four kinds' validators are what is at risk. Q1083 keeps its own scope: the notice period is release-count only either way.
Announces a removal; does not perform one. Nothing on the published API surface changes here: api-surface-since.sh v1.7.0 reports no added fields, no changed defaults, and "Added or changed enum constraints: (none)". The enum is still CIDR;FQDN;CiliumFQDN;CalicoFQDN, admission still warns rather than rejects, and every object valid before this commit is valid after it. The break lands at v2.0.0, when the versions defining the aliases are removed. Follows docs(api) at 30399a0, which named the v3.0.0 floor for the same two values. This moves that floor rather than introducing a new kind of claim. Q1082 decided that v2.0.0 stops serving v2beta1: it is a major release, and dropping a served version is a breaking change a major release may make. Q452 follows. The aliases are enum members of v2alpha1 and v2beta1, v2.0.0 removes both, and the GA v2 version does not define them, so they go at v2.0.0 and GA is born clean. The floor moves with the premise, not with the rule. Q428's reasoning was sound at every step: an API element is removed by incrementing the version, so the aliases live exactly as long as v2beta1 does. The input changed. Every site stating the derived v3.0.0 floor now states v2.0.0: the enum godoc in both v2 versions, the generated CRD descriptions and chart templates, the admission warning, and the operator, design and plan docs. api-review.md keeps the worked example and gains the lesson: state a floor with what it is derived from, so a reader can tell which half a change invalidates. The operator instruction changes with it. Migrating stops being optional, because v2 cannot represent an object naming an alias and the conversion contract cannot report one object as absent: convertedObjects must match the input list in size and order, Result is a single Status for the request, and on failure convertedObjects is empty. One unmigrated EgressProxy therefore breaks kubectl get egressproxies at v2 for the cluster, not for itself. v2beta1 joins the v2.0.0 removal table for the cost reason the other three share rather than a mechanism they share: one breaking migration instead of two. Rule 4b is untouched by semver, so the release before v2.0.0 still serves v2beta1 and v2 side by side. The two records that stated the old floor as settled are annotated rather than rewritten: what 1.3 announced is what 1.3 announced.
The v2 graduation now spans two releases: Phase 2 serves v2 beside v2beta1 in 1.9, and the storage advance plus the removals stay at v2.0.0. Q413 owns Phase 2, so it blocks the 1.9 tag rather than the 2.0 one. Q1085's deadline is 1.9 for the same reason: that is the first release in which v2 is served, so it is the first in which a conversion that cannot represent an alias can be asked for. 1.8 is the better target for the reject and the check, and the row says so rather than treating the deadline as the plan. Q413 is not split. Phase 2 and Phase 4 land in different releases now, but the split is only useful once the soak closes and someone picks the work up.
… Phase 2 v2-ga.md had Phase 2 add v2 and mark it storage, with Phase 3 migrating and dropping the superseded versions, both inside the v2.0.0 cut. That advances the storage version in the same release that introduces v2, which Rule 4b does not allow, and the ladder ran 1.8 to 2.0 with no release in between. The gap predates the v2beta1 retirement decision and is sharpened by it: before, an operator upgrading to 2.0 still had v2beta1 served, and now they do not. Rule 4b is a convention rather than something the apiserver enforces, so the ladder records what it is worth here instead of citing it. Once stored objects are rewritten as v2, a cluster cannot return to a release whose CRDs lack v2. With no overlap release that destination is v1.8.0, which would make the v2.0.0 upgrade the one with no way back, on the largest upgrade the project asks anyone to make. The overlap is also the only place the v2beta1-to-v2 conversion edge runs before it is mandatory; Phase 1's soak validates v2beta1's shape and says nothing about a conversion that does not exist yet. So 1.9 serves both and moves no storage version, and the storage marker moves from Phase 2 to Phase 3.
An audit of what blocks each release found three tracking gaps and one missing row. Q1086 is the missing row. v2-ga.md Phase 3 opens by marking v2 storage and rewriting stored objects, and Phase 4 cuts on top of that, but the mechanism does not exist: StorageVersionMigration and storedVersions appear nowhere outside vendor/, measured across the tree. It was recorded in one sentence of a plan doc and tracked nowhere. Sized L rather than the M the phase table implies, because the mechanism choice is unmade and the evidence half is unbuilt. Q273 and Q264 gain 2.0-gate. They are Phase 3's content, so a query for what blocks v2.0.0 returned one row of four. Being deferred was not the reason they lacked the label: Q413 is deferred and carries 1.9-gate. Q1087 is the gate that would have caught this. Nothing binds a gate-labelled row to its release plan in either direction; check-release-ladder binds only the punted table to status, and roadmap-check rule 7 fires only for feature and security rows. The row names the hard part rather than assuming it: the label-to-plan mapping is not a filename template, since 2.0's plan is v2-ga.md, and it must be read off the ladder table.
…x row The 1.9 rung existed as one ladder row pointing at v2-ga.md Phase 2, so the overlap release had nowhere stating its scope, its Definition of Done or its gating rows, which every rung from 1.0 to 1.8 has. release-1.9.md carries the rollback argument for why the rung exists, the scope ledger, and a section on what the release must NOT do: marking v2 the storage version is the one way to ship 1.9 and still not satisfy Rule 4b, and Phase 2's step 1 still carries that marker. Its Definition of Done makes the Rule 4b evidence a deliverable of this tag rather than something v2.0.0's pre-flight is expected to re-derive, since a later release cannot establish that an earlier one served both versions. The v2-ga.md index row was stale: it still recorded Q452 as v2 defining both aliases, the answer that flipped. It now records the omission, names the 1.9 split, and lists all four Phase 3 rows rather than two. release-1.8.md gains Q1085's notice in its scope ledger. The docs half shipped with Q452, which is what makes 1.8 the one-release-ahead announcement; what it still owes is the release-notes line.
karlkfi
force-pushed
the
claude/q452-29ed0a
branch
from
September 8, 2026 19:00
fec9766 to
a3492a5
Compare
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.
What
v2beta1is no longer served pastv2.0.0, and the deprecatedCiliumFQDN/CalicoFQDNaliases go with it.This announces that removal; it does not perform one. Nothing on the
published API surface changes here —
api-surface-since.sh v1.7.0reports noadded fields, no changed defaults, and "Added or changed enum constraints:
(none)". The enum is still
CIDR;FQDN;CiliumFQDN;CalicoFQDN, admission stillwarns rather than rejects, and every object valid before this PR is valid after
it. The break lands at
v2.0.0, two releases out. Typeddocs(api)to match30399a0b0,
which named the
v3.0.0floor for the same two values.This PR was drafted pending the Q1082 decision, which is now made:
v2.0.0is amajor release, and dropping a served version is a breaking change a major
release may make. Q452 follows from it and lands with the opposite answer to
the one this branch previously argued — GA
v2omits the aliases.Three pieces:
The decision, recorded.
v2beta1-retirement.mdgets the outcome and keeps the pre-decision analysis as taken, so the cost that
was accepted stays legible.
v2-ga.md§ Phase 2 recordsQ452's answer and Phase 3 gains
v2beta1as a fourth removal.The published floor moves from
v3.0.0tov2.0.0. Q428's reasoning wassound at every step — an API element is removed by incrementing the version, so
the aliases live exactly as long as
v2beta1does — and only its input changed.Every site stating the derived floor now states
v2.0.0: the enum godoc in bothv2 versions, the regenerated CRD descriptions and chart templates, the admission
warning and its test, and the operator, design and plan docs.
api-review.mdkeeps the worked example andgains the lesson — state a floor with what it is derived from, so the next
reader can tell which half a change invalidates.
The operator instruction changes. Migrating stops being optional.
v2cannotrepresent an object naming an alias, and the conversion contract cannot report
one object as absent:
convertedObjectsmust match the input list in size andorder,
Resultis a singleStatusfor the whole request, and on failureconvertedObjectsis empty.ConversionRequest.Objectsis a list, so oneunmigrated
EgressProxybreakskubectl get egressproxiesatv2for thecluster rather than for itself.
Backlog
Q452 and Q1082 close. Q1085 is new and carries what the decision puts on the
critical path: the release-notes deprecation notice, an admission change from
warn to reject on new alias writes, and the pre-upgrade check. Q1068 is
corrected rather than closed —
v2beta1now goes at the same tag asv2alpha1,so the un-matched rules leave
v2as the only served version, and the aliaswarning that surfaced the defect dies with the aliases instead of riding into
the gap.
What this does not do
Rule 4b is untouched by semver: the release before
v2.0.0still has to servev2beta1andv2side by side. That overlap, the storage migration and Q1085'snotice all remain ahead, and none of them is in this PR.
The ladder gains a 1.9 rung
Raised while reviewing the above: the v2-ga plan could not deliver
v2.0.0aswritten. Phase 2 was "add
v2to each kind, mark it storage" and Phase 3"storage migration, then drop", both inside the
v2.0.0cut, and the ladderran 1.8 → 2.0 with nothing between. That advances the storage version in the
same release that introduces
v2, which Rule 4b does not allow.The gap predates this PR's decision and is sharpened by it: before, an operator
upgrading to
v2.0.0still hadv2beta1served, and now they do not.Rule 4b is a convention rather than something the apiserver enforces, so
release-ladder.mdrecords what it is worthhere rather than citing it. Once stored objects are rewritten as
v2, a clustercannot return to a release whose CRDs lack
v2; with no overlap release thatdestination is
v1.8.0, making thev2.0.0upgrade the one with no way back —on the largest upgrade this project asks anyone to make. The overlap is also the
only place the
v2beta1↔v2conversion edge runs before it is mandatory,which Phase 1's soak does not cover because that conversion does not exist yet.
So:
v2beta1deprecation notice this PR shipsv2served besidev2beta1, storage unchanged. Phase 2 minus the storage marker, plus Q1085's reject and pre-upgrade checkv2, objects migrate, then four removals1.9-gateis declared and Q413 carries it, since Phase 2 is 1.9's wholecontent. Q1085 moves from
2.0-gateto1.9-gate: 1.9 is the first releaseserving
v2, so it is the first in which a conversion that cannot represent analias can be requested. 1.8 remains the better target for the reject and the
check, and the row says so rather than treating the deadline as the plan.
Q413 is deliberately not split. Its phases now land in different releases,
but the split is only useful once the soak closes and someone picks the work up.
Tracking audit: four gaps closed
An audit of what actually blocks each release (205 rows) found only three
carried a gate label, and the picture was incomplete in four places:
v2-ga.mdPhase 3 opens bymarking
v2storage and rewriting stored objects, and the mechanism does notexist —
StorageVersionMigrationandstoredVersionsappear nowhere outsidevendor/. It was one sentence in a plan doc, tracked nowhere. Now Q1086,sized
Lrather than theMthe phase table implies, because the mechanismchoice is unmade.
v2.0.0without2.0-gate. They are Phase 3'scontent, so a query for what blocks 2.0 returned one row of four. Being
deferredwas not the reason: Q413 is deferred and carries1.9-gate.release-1.9.md, including a what this releasemust NOT do section: marking
v2storage is the one way to ship 1.9 andstill not satisfy Rule 4b, and Phase 2's step 1 still carries that marker.
v2-ga.mdindex row was stale — still recording Q452 asv2definingboth aliases, the answer this PR flips.
Q1087 is the gate that would have caught two of these: nothing binds a
gate-labelled row to its release plan in either direction.
check-release-ladderbinds only the punted table to
status:, androadmap-checkrule 7 fires onlyfor
feature/securityrows, leaving everydebt,ci,testsandmilestoneblocker unbound. The row names the hard part rather than assumingit: the label-to-plan mapping is not a filename template, since 2.0's plan is
v2-ga.md, so it has to be read off the ladder table.Testing
make check,make docs-gatesandmake docs-buildgreen.make manifests,make chart-crdsandmake api-referenceregenerated; the warning test nowasserts
v2.0.0and assertsv3.0.0is absent, since naming the next majorwould understate the removal by a whole release.