Skip to content

Commit 8b77b1c

Browse files
committed
Update feature gate documentation for compatibility version
1 parent ffe9bfb commit 8b77b1c

File tree

1 file changed

+98
-4
lines changed

1 file changed

+98
-4
lines changed

contributors/devel/sig-architecture/feature-gates.md

Lines changed: 98 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,33 @@ While some exceptions may happen, approvers should use the following guidance:
105105

106106
[API changes]: https://github.com/kubernetes/community/blob/master/sig-architecture/api-review-process.md#what-parts-of-a-pr-are-api-changes
107107

108+
### Compatibility versions
109+
110+
The Kubernetes "compatibility version" feature promises that control-plane
111+
components can be configured to behave as if they were any of the three previous
112+
releases, which includes (non-alpha) feature gates and gate-controlled APIs and logic.
113+
114+
As a feature progresses through the lifecycle, we must preserve enough
115+
information to allow such compatible configuration, including both the old and
116+
new states, along with the version that the transition occurred.
117+
118+
```
119+
RetryGenerateName: {
120+
{Version: version.MustParse("1.30"), Default: false, PreRelease: featuregate.Alpha},
121+
{Version: version.MustParse("1.31"), Default: true, PreRelease: featuregate.Beta},
122+
{Version: version.MustParse("1.32"), Default: true, LockToDefault: true, PreRelease: featuregate.GA},
123+
},
124+
```
125+
126+
In this example, a component at v1.33 can be configured to behave as v1.32+ or
127+
to be compatible with v1.31 (where the gate was disableable) or v1.30 (where the
128+
gate was off by default). That component at v1.34 can be configured to behave as
129+
v1.32+ or to be compatible with v1.31 (where the gate was disableable), but
130+
v1.30 falls outside the "three releases" window.
131+
132+
At v1.35, the component can only be configured to be compatible as far back as
133+
1.32 (where the gate was locked on), and so the lifecycle is complete.
134+
108135
### Alpha features
109136

110137
* `PreRelease` is set to `featuregate.Alpha`
@@ -152,17 +179,66 @@ other action outside of Kubernetes to use it. This gives some grace period for
152179
users to take action, but such feature gates will eventually set
153180
`LockToDefault` to `true` and then be retired, like normal.
154181

155-
[After at least two releases post-GA and deprecation](https://kubernetes.io/docs/reference/using-api/deprecation-policy/#deprecation),
156-
feature gates should be removed. Typically, we add a comment in
182+
To achieve our [compatibility version](#compatibility-versions) promise, after
183+
three releases where a feature has been locked to the default value (whether
184+
that feature is GA or deprecated), feature gates and references should be
185+
removed. We use three releases because it corresponds to roughly one year in the
186+
Kubernetes development cycle which is our [support
187+
period](https://kubernetes.io/releases/patch-releases/#support-period). For
188+
example, if a gate was `LockToDefault: true` in kubernetes version `X`, it may
189+
be removed in version `X+3` (which must be compatible with `X+2`, `X+1`, and
190+
`X`, all of which were also locked).
191+
192+
Typically, we add a comment in
157193
[the code](https://github.com/kubernetes/kubernetes/blob/master/pkg/features/kube_features.go)
158194
such as: `// remove in 1.23` to signal when we plan to remove the feature gate.
159195
We provide this grace period to give users time to stop referencing "finished"
160196
gates. If a feature gate is removed and a user has forgotten to drop the
161197
reference to it (e.g. in the CLI flags of `kube-apiserver`), then they will see
162198
a hard failure.
163199

164-
When we set `LockToDefault` to `true`, we also remove all references to the feature
165-
gate from the codebase.
200+
#### Disablement Tests
201+
202+
Typically for full coverage, unit and integration tests exist for both when a
203+
feature is enabled and disabled. When a feature is promoted to GA, it is usually
204+
locked to true by default and cannot be unset in testing. For integration with
205+
compatibility version, feature disablement tests should be maintained until the
206+
GA feature is fully removed three releases after promotion. To test scenarios
207+
where the feature gate may still be disabled, emulation version should be set in
208+
disablement tests. For these disablement tests, simply set emulation version to
209+
the version before the GA promotion to support disablement. The emulation
210+
version would be set the line before the feature gate is set.
211+
212+
For example, if the "CustomResourceFieldSelectors" becomes GA in version 1.32,
213+
the emulation version is set to v1.31.
214+
215+
```go
216+
featuregatetesting.SetFeatureGateEmulationVersionDuringTest(t, utilfeature.DefaultFeatureGate, version.MustParse("1.31"))
217+
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, apiextensionsfeatures.CustomResourceFieldSelectors, false)
218+
```
219+
220+
When the feature gate is removed three releases later in v1.35, the disablement
221+
feature gate test is then removed. For tests using a matrix, emulation version
222+
should only be set on tests that disable the feature.
223+
224+
225+
```go
226+
testcases := []struct{
227+
...
228+
featureEnabled bool
229+
}
230+
231+
for _, tc := range testcases {
232+
if !tc.featureEnabled {
233+
featuregatetesting.SetFeatureGateEmulationVersionDuringTest(t, utilfeature.DefaultFeatureGate, version.MustParse("1.31"))
234+
}
235+
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.MyFeature, tc.featureEnabled)
236+
}
237+
```
238+
239+
Disablement tests are only required to be preserved for components and libraries
240+
that support compatibility version. Tests for node and kubelet are unaffected by
241+
compatibility version.
166242

167243
### Deprecation
168244

@@ -181,6 +257,24 @@ to unbreak themselves (and then file a bug). If this happens, we must
181257
reconsider the deprecation and may choose to abandon it entirely by changing
182258
the gate back to `true` for a release or two and eventually removing it.
183259

260+
Once the [deprecation period](https://kubernetes.io/docs/reference/using-api/deprecation-policy/#deprecation)
261+
has passed, the gate should be locked to the default value (`LockToDefault:
262+
true`). As with GA features, all references to the feature gate must be kept for
263+
a minimum of three releases after gate has been locked to the default value. the
264+
gate, all references to it, and all gated logic may be removed after those three
265+
releases. See [compatibility version](#compatibility-versions) for more details.
266+
267+
For example:
268+
269+
```
270+
DeprecatedFeature: {
271+
{Version: version.MustParse("1.29"), Default: false, PreRelease: featuregate.Alpha}, // feature graduated to alpha.
272+
{Version: version.MustParse("1.30"), Default: true, PreRelease: featuregate.Beta}, // feature graduated to beta.
273+
{Version: version.MustParse("1.32"), Default: false, PreRelease: featuregate.Deprecated}, // feature is deprecated and turned off. LockToDefault is unset to give users time to transition.
274+
{Version: version.MustParse("1.34"), Default: false, LockToDefault: true, PreRelease: featuregate.Deprecated}, // feature is deprecated, off, and locked. remove in v1.37 once versions up to v1.34 cannot be emulated anymore.
275+
},
276+
```
277+
184278
NOTE: We do not remove GA fields from the API.
185279

186280
### Other scenarios

0 commit comments

Comments
 (0)