@@ -105,6 +105,33 @@ While some exceptions may happen, approvers should use the following guidance:
105
105
106
106
[ API changes ] : https://github.com/kubernetes/community/blob/master/sig-architecture/api-review-process.md#what-parts-of-a-pr-are-api-changes
107
107
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
+
108
135
### Alpha features
109
136
110
137
* ` PreRelease ` is set to ` featuregate.Alpha `
@@ -152,17 +179,66 @@ other action outside of Kubernetes to use it. This gives some grace period for
152
179
users to take action, but such feature gates will eventually set
153
180
` LockToDefault ` to ` true ` and then be retired, like normal.
154
181
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
157
193
[ the code] ( https://github.com/kubernetes/kubernetes/blob/master/pkg/features/kube_features.go )
158
194
such as: ` // remove in 1.23 ` to signal when we plan to remove the feature gate.
159
195
We provide this grace period to give users time to stop referencing "finished"
160
196
gates. If a feature gate is removed and a user has forgotten to drop the
161
197
reference to it (e.g. in the CLI flags of ` kube-apiserver ` ), then they will see
162
198
a hard failure.
163
199
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.
166
242
167
243
### Deprecation
168
244
@@ -181,6 +257,24 @@ to unbreak themselves (and then file a bug). If this happens, we must
181
257
reconsider the deprecation and may choose to abandon it entirely by changing
182
258
the gate back to ` true ` for a release or two and eventually removing it.
183
259
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
+
184
278
NOTE: We do not remove GA fields from the API.
185
279
186
280
### Other scenarios
0 commit comments