features.go 6.6 KB

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