1 /* 2 Copyright 2024 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package features 18 19 import ( 20 "errors" 21 22 utilruntime "k8s.io/apimachinery/pkg/util/runtime" 23 "sync/atomic" 24 ) 25 26 // NOTE: types Feature, FeatureSpec, prerelease (and its values) 27 // were duplicated from the component-base repository 28 // 29 // for more information please refer to https://docs.google.com/document/d/1g9BGCRw-7ucUxO6OtCWbb3lfzUGA_uU9178wLdXAIfs 30 31 const ( 32 // Values for PreRelease. 33 Alpha = prerelease("ALPHA") 34 Beta = prerelease("BETA") 35 GA = prerelease("") 36 37 // Deprecated 38 Deprecated = prerelease("DEPRECATED") 39 ) 40 41 type prerelease string 42 43 type Feature string 44 45 type FeatureSpec struct { 46 // Default is the default enablement state for the feature 47 Default bool 48 // LockToDefault indicates that the feature is locked to its default and cannot be changed 49 LockToDefault bool 50 // PreRelease indicates the maturity level of the feature 51 PreRelease prerelease 52 } 53 54 // Gates indicates whether a given feature is enabled or not. 55 type Gates interface { 56 // Enabled returns true if the key is enabled. 57 Enabled(key Feature) bool 58 } 59 60 // Registry represents an external feature gates registry. 61 type Registry interface { 62 // Add adds existing feature gates to the provided registry. 63 // 64 // As of today, this method is used by AddFeaturesToExistingFeatureGates and 65 // ReplaceFeatureGates to take control of the features exposed by this library. 66 Add(map[Feature]FeatureSpec) error 67 } 68 69 // FeatureGates returns the feature gates exposed by this library. 70 // 71 // By default, only the default features gates will be returned. 72 // The default implementation allows controlling the features 73 // via environmental variables. 74 // For example, if you have a feature named "MyFeature" 75 // setting an environmental variable "KUBE_FEATURE_MyFeature" 76 // will allow you to configure the state of that feature. 77 // 78 // Please note that the actual set of the feature gates 79 // might be overwritten by calling ReplaceFeatureGates method. 80 func FeatureGates() Gates { 81 return featureGates.Load().(*featureGatesWrapper).Gates 82 } 83 84 // AddFeaturesToExistingFeatureGates adds the default feature gates to the provided registry. 85 // Usually this function is combined with ReplaceFeatureGates to take control of the 86 // features exposed by this library. 87 func AddFeaturesToExistingFeatureGates(registry Registry) error { 88 return registry.Add(defaultKubernetesFeatureGates) 89 } 90 91 // ReplaceFeatureGates overwrites the default implementation of the feature gates 92 // used by this library. 93 // 94 // Useful for binaries that would like to have full control of the features 95 // exposed by this library, such as allowing consumers of a binary 96 // to interact with the features via a command line flag. 97 // 98 // For example: 99 // 100 // // first, register client-go's features to your registry. 101 // clientgofeaturegate.AddFeaturesToExistingFeatureGates(utilfeature.DefaultMutableFeatureGate) 102 // // then replace client-go's feature gates implementation with your implementation 103 // clientgofeaturegate.ReplaceFeatureGates(utilfeature.DefaultMutableFeatureGate) 104 func ReplaceFeatureGates(newFeatureGates Gates) { 105 if replaceFeatureGatesWithWarningIndicator(newFeatureGates) { 106 utilruntime.HandleError(errors.New("the default feature gates implementation has already been used and now it's being overwritten. This might lead to unexpected behaviour. Check your initialization order")) 107 } 108 } 109 110 func replaceFeatureGatesWithWarningIndicator(newFeatureGates Gates) bool { 111 shouldProduceWarning := false 112 113 if defaultFeatureGates, ok := FeatureGates().(*envVarFeatureGates); ok { 114 if defaultFeatureGates.hasAlreadyReadEnvVar() { 115 shouldProduceWarning = true 116 } 117 } 118 wrappedFeatureGates := &featureGatesWrapper{newFeatureGates} 119 featureGates.Store(wrappedFeatureGates) 120 121 return shouldProduceWarning 122 } 123 124 func init() { 125 envVarGates := newEnvVarFeatureGates(defaultKubernetesFeatureGates) 126 127 wrappedFeatureGates := &featureGatesWrapper{envVarGates} 128 featureGates.Store(wrappedFeatureGates) 129 } 130 131 // featureGatesWrapper a thin wrapper to satisfy featureGates variable (atomic.Value). 132 // That is, all calls to Store for a given Value must use values of the same concrete type. 133 type featureGatesWrapper struct { 134 Gates 135 } 136 137 var ( 138 // featureGates is a shared global FeatureGates. 139 // 140 // Top-level commands/options setup that needs to modify this feature gates 141 // should use AddFeaturesToExistingFeatureGates followed by ReplaceFeatureGates. 142 featureGates = &atomic.Value{} 143 ) 144