1
16
17 package featuregate
18
19 import (
20 "context"
21 "fmt"
22 "sort"
23 "strconv"
24 "strings"
25 "sync"
26 "sync/atomic"
27
28 "github.com/spf13/pflag"
29
30 "k8s.io/apimachinery/pkg/util/naming"
31 featuremetrics "k8s.io/component-base/metrics/prometheus/feature"
32 "k8s.io/klog/v2"
33 )
34
35 type Feature string
36
37 const (
38 flagName = "feature-gates"
39
40
41
42
43
44 allAlphaGate Feature = "AllAlpha"
45
46
47
48
49
50 allBetaGate Feature = "AllBeta"
51 )
52
53 var (
54
55 defaultFeatures = map[Feature]FeatureSpec{
56 allAlphaGate: {Default: false, PreRelease: Alpha},
57 allBetaGate: {Default: false, PreRelease: Beta},
58 }
59
60
61 specialFeatures = map[Feature]func(known map[Feature]FeatureSpec, enabled map[Feature]bool, val bool){
62 allAlphaGate: setUnsetAlphaGates,
63 allBetaGate: setUnsetBetaGates,
64 }
65 )
66
67 type FeatureSpec struct {
68
69 Default bool
70
71 LockToDefault bool
72
73 PreRelease prerelease
74 }
75
76 type prerelease string
77
78 const (
79
80 Alpha = prerelease("ALPHA")
81 Beta = prerelease("BETA")
82 GA = prerelease("")
83
84
85 Deprecated = prerelease("DEPRECATED")
86 )
87
88
89 type FeatureGate interface {
90
91 Enabled(key Feature) bool
92
93 KnownFeatures() []string
94
95
96
97 DeepCopy() MutableFeatureGate
98 }
99
100
101
102 type MutableFeatureGate interface {
103 FeatureGate
104
105
106 AddFlag(fs *pflag.FlagSet)
107
108
109 Set(value string) error
110
111 SetFromMap(m map[string]bool) error
112
113 Add(features map[Feature]FeatureSpec) error
114
115 GetAll() map[Feature]FeatureSpec
116
117 AddMetrics()
118
119
120
121
122
123
124
125
126
127
128 OverrideDefault(name Feature, override bool) error
129 }
130
131
132 type featureGate struct {
133 featureGateName string
134
135 special map[Feature]func(map[Feature]FeatureSpec, map[Feature]bool, bool)
136
137
138 lock sync.Mutex
139
140 known atomic.Value
141
142 enabled atomic.Value
143
144 closed bool
145 }
146
147 func setUnsetAlphaGates(known map[Feature]FeatureSpec, enabled map[Feature]bool, val bool) {
148 for k, v := range known {
149 if v.PreRelease == Alpha {
150 if _, found := enabled[k]; !found {
151 enabled[k] = val
152 }
153 }
154 }
155 }
156
157 func setUnsetBetaGates(known map[Feature]FeatureSpec, enabled map[Feature]bool, val bool) {
158 for k, v := range known {
159 if v.PreRelease == Beta {
160 if _, found := enabled[k]; !found {
161 enabled[k] = val
162 }
163 }
164 }
165 }
166
167
168 var _ pflag.Value = &featureGate{}
169
170
171
172 var internalPackages = []string{"k8s.io/component-base/featuregate/feature_gate.go"}
173
174 func NewFeatureGate() *featureGate {
175 known := map[Feature]FeatureSpec{}
176 for k, v := range defaultFeatures {
177 known[k] = v
178 }
179
180 f := &featureGate{
181 featureGateName: naming.GetNameFromCallsite(internalPackages...),
182 special: specialFeatures,
183 }
184 f.known.Store(known)
185 f.enabled.Store(map[Feature]bool{})
186
187 return f
188 }
189
190
191
192 func (f *featureGate) Set(value string) error {
193 m := make(map[string]bool)
194 for _, s := range strings.Split(value, ",") {
195 if len(s) == 0 {
196 continue
197 }
198 arr := strings.SplitN(s, "=", 2)
199 k := strings.TrimSpace(arr[0])
200 if len(arr) != 2 {
201 return fmt.Errorf("missing bool value for %s", k)
202 }
203 v := strings.TrimSpace(arr[1])
204 boolValue, err := strconv.ParseBool(v)
205 if err != nil {
206 return fmt.Errorf("invalid value of %s=%s, err: %v", k, v, err)
207 }
208 m[k] = boolValue
209 }
210 return f.SetFromMap(m)
211 }
212
213
214 func (f *featureGate) SetFromMap(m map[string]bool) error {
215 f.lock.Lock()
216 defer f.lock.Unlock()
217
218
219 known := map[Feature]FeatureSpec{}
220 for k, v := range f.known.Load().(map[Feature]FeatureSpec) {
221 known[k] = v
222 }
223 enabled := map[Feature]bool{}
224 for k, v := range f.enabled.Load().(map[Feature]bool) {
225 enabled[k] = v
226 }
227
228 for k, v := range m {
229 k := Feature(k)
230 featureSpec, ok := known[k]
231 if !ok {
232 return fmt.Errorf("unrecognized feature gate: %s", k)
233 }
234 if featureSpec.LockToDefault && featureSpec.Default != v {
235 return fmt.Errorf("cannot set feature gate %v to %v, feature is locked to %v", k, v, featureSpec.Default)
236 }
237 enabled[k] = v
238
239 if fn, found := f.special[k]; found {
240 fn(known, enabled, v)
241 }
242
243 if featureSpec.PreRelease == Deprecated {
244 klog.Warningf("Setting deprecated feature gate %s=%t. It will be removed in a future release.", k, v)
245 } else if featureSpec.PreRelease == GA {
246 klog.Warningf("Setting GA feature gate %s=%t. It will be removed in a future release.", k, v)
247 }
248 }
249
250
251 f.known.Store(known)
252 f.enabled.Store(enabled)
253
254 klog.V(1).Infof("feature gates: %v", f.enabled)
255 return nil
256 }
257
258
259 func (f *featureGate) String() string {
260 pairs := []string{}
261 for k, v := range f.enabled.Load().(map[Feature]bool) {
262 pairs = append(pairs, fmt.Sprintf("%s=%t", k, v))
263 }
264 sort.Strings(pairs)
265 return strings.Join(pairs, ",")
266 }
267
268 func (f *featureGate) Type() string {
269 return "mapStringBool"
270 }
271
272
273 func (f *featureGate) Add(features map[Feature]FeatureSpec) error {
274 f.lock.Lock()
275 defer f.lock.Unlock()
276
277 if f.closed {
278 return fmt.Errorf("cannot add a feature gate after adding it to the flag set")
279 }
280
281
282 known := map[Feature]FeatureSpec{}
283 for k, v := range f.known.Load().(map[Feature]FeatureSpec) {
284 known[k] = v
285 }
286
287 for name, spec := range features {
288 if existingSpec, found := known[name]; found {
289 if existingSpec == spec {
290 continue
291 }
292 return fmt.Errorf("feature gate %q with different spec already exists: %v", name, existingSpec)
293 }
294
295 known[name] = spec
296 }
297
298
299 f.known.Store(known)
300
301 return nil
302 }
303
304 func (f *featureGate) OverrideDefault(name Feature, override bool) error {
305 f.lock.Lock()
306 defer f.lock.Unlock()
307
308 if f.closed {
309 return fmt.Errorf("cannot override default for feature %q: gates already added to a flag set", name)
310 }
311
312 known := map[Feature]FeatureSpec{}
313 for name, spec := range f.known.Load().(map[Feature]FeatureSpec) {
314 known[name] = spec
315 }
316
317 spec, ok := known[name]
318 switch {
319 case !ok:
320 return fmt.Errorf("cannot override default: feature %q is not registered", name)
321 case spec.LockToDefault:
322 return fmt.Errorf("cannot override default: feature %q default is locked to %t", name, spec.Default)
323 case spec.PreRelease == Deprecated:
324 klog.Warningf("Overriding default of deprecated feature gate %s=%t. It will be removed in a future release.", name, override)
325 case spec.PreRelease == GA:
326 klog.Warningf("Overriding default of GA feature gate %s=%t. It will be removed in a future release.", name, override)
327 }
328
329 spec.Default = override
330 known[name] = spec
331 f.known.Store(known)
332
333 return nil
334 }
335
336
337 func (f *featureGate) GetAll() map[Feature]FeatureSpec {
338 retval := map[Feature]FeatureSpec{}
339 for k, v := range f.known.Load().(map[Feature]FeatureSpec) {
340 retval[k] = v
341 }
342 return retval
343 }
344
345
346 func (f *featureGate) Enabled(key Feature) bool {
347 if v, ok := f.enabled.Load().(map[Feature]bool)[key]; ok {
348 return v
349 }
350 if v, ok := f.known.Load().(map[Feature]FeatureSpec)[key]; ok {
351 return v.Default
352 }
353
354 panic(fmt.Errorf("feature %q is not registered in FeatureGate %q", key, f.featureGateName))
355 }
356
357
358 func (f *featureGate) AddFlag(fs *pflag.FlagSet) {
359 f.lock.Lock()
360
361
362
363
364 f.closed = true
365 f.lock.Unlock()
366
367 known := f.KnownFeatures()
368 fs.Var(f, flagName, ""+
369 "A set of key=value pairs that describe feature gates for alpha/experimental features. "+
370 "Options are:\n"+strings.Join(known, "\n"))
371 }
372
373 func (f *featureGate) AddMetrics() {
374 for feature, featureSpec := range f.GetAll() {
375 featuremetrics.RecordFeatureInfo(context.Background(), string(feature), string(featureSpec.PreRelease), f.Enabled(feature))
376 }
377 }
378
379
380
381 func (f *featureGate) KnownFeatures() []string {
382 var known []string
383 for k, v := range f.known.Load().(map[Feature]FeatureSpec) {
384 if v.PreRelease == GA || v.PreRelease == Deprecated {
385 continue
386 }
387 known = append(known, fmt.Sprintf("%s=true|false (%s - default=%t)", k, v.PreRelease, v.Default))
388 }
389 sort.Strings(known)
390 return known
391 }
392
393
394
395
396 func (f *featureGate) DeepCopy() MutableFeatureGate {
397
398 known := map[Feature]FeatureSpec{}
399 for k, v := range f.known.Load().(map[Feature]FeatureSpec) {
400 known[k] = v
401 }
402 enabled := map[Feature]bool{}
403 for k, v := range f.enabled.Load().(map[Feature]bool) {
404 enabled[k] = v
405 }
406
407
408
409
410 fg := &featureGate{
411 special: specialFeatures,
412 closed: f.closed,
413 }
414
415 fg.known.Store(known)
416 fg.enabled.Store(enabled)
417
418 return fg
419 }
420
View as plain text