...
1
16
17 package features
18
19 import (
20 "fmt"
21 "os"
22 "strconv"
23 "sync"
24 "sync/atomic"
25
26 "k8s.io/apimachinery/pkg/util/naming"
27 utilruntime "k8s.io/apimachinery/pkg/util/runtime"
28 "k8s.io/klog/v2"
29 )
30
31
32
33 var internalPackages = []string{"k8s.io/client-go/features/envvar.go"}
34
35 var _ Gates = &envVarFeatureGates{}
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50 func newEnvVarFeatureGates(features map[Feature]FeatureSpec) *envVarFeatureGates {
51 known := map[Feature]FeatureSpec{}
52 for name, spec := range features {
53 known[name] = spec
54 }
55
56 fg := &envVarFeatureGates{
57 callSiteName: naming.GetNameFromCallsite(internalPackages...),
58 known: known,
59 }
60 fg.enabled.Store(map[Feature]bool{})
61
62 return fg
63 }
64
65
66 type envVarFeatureGates struct {
67
68
69 callSiteName string
70
71
72 readEnvVarsOnce sync.Once
73
74
75 known map[Feature]FeatureSpec
76
77
78
79 enabled atomic.Value
80
81
82
83 readEnvVars atomic.Bool
84 }
85
86
87 func (f *envVarFeatureGates) Enabled(key Feature) bool {
88 if v, ok := f.getEnabledMapFromEnvVar()[key]; ok {
89 return v
90 }
91 if v, ok := f.known[key]; ok {
92 return v.Default
93 }
94 panic(fmt.Errorf("feature %q is not registered in FeatureGates %q", key, f.callSiteName))
95 }
96
97
98
99
100 func (f *envVarFeatureGates) getEnabledMapFromEnvVar() map[Feature]bool {
101 f.readEnvVarsOnce.Do(func() {
102 featureGatesState := map[Feature]bool{}
103 for feature, featureSpec := range f.known {
104 featureState, featureStateSet := os.LookupEnv(fmt.Sprintf("KUBE_FEATURE_%s", feature))
105 if !featureStateSet {
106 continue
107 }
108 boolVal, boolErr := strconv.ParseBool(featureState)
109 switch {
110 case boolErr != nil:
111 utilruntime.HandleError(fmt.Errorf("cannot set feature gate %q to %q, due to %v", feature, featureState, boolErr))
112 case featureSpec.LockToDefault:
113 if boolVal != featureSpec.Default {
114 utilruntime.HandleError(fmt.Errorf("cannot set feature gate %q to %q, feature is locked to %v", feature, featureState, featureSpec.Default))
115 break
116 }
117 featureGatesState[feature] = featureSpec.Default
118 default:
119 featureGatesState[feature] = boolVal
120 }
121 }
122 f.enabled.Store(featureGatesState)
123 f.readEnvVars.Store(true)
124
125 for feature, featureSpec := range f.known {
126 if featureState, ok := featureGatesState[feature]; ok {
127 klog.V(1).InfoS("Feature gate updated state", "feature", feature, "enabled", featureState)
128 continue
129 }
130 klog.V(1).InfoS("Feature gate default state", "feature", feature, "enabled", featureSpec.Default)
131 }
132 })
133 return f.enabled.Load().(map[Feature]bool)
134 }
135
136 func (f *envVarFeatureGates) hasAlreadyReadEnvVar() bool {
137 return f.readEnvVars.Load()
138 }
139
View as plain text