feature_gate.go 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. /*
  2. Copyright 2016 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 config
  14. import (
  15. "fmt"
  16. "sort"
  17. "strconv"
  18. "strings"
  19. "github.com/golang/glog"
  20. "github.com/spf13/pflag"
  21. )
  22. const (
  23. flagName = "feature-gates"
  24. // All known feature keys
  25. // To add a new feature, define a key for it below and add
  26. // a featureSpec entry to knownFeatures.
  27. // allAlphaGate is a global toggle for alpha features. Per-feature key
  28. // values override the default set by allAlphaGate. Examples:
  29. // AllAlpha=false,NewFeature=true will result in newFeature=true
  30. // AllAlpha=true,NewFeature=false will result in newFeature=false
  31. allAlphaGate = "AllAlpha"
  32. externalTrafficLocalOnly = "AllowExtTrafficLocalEndpoints"
  33. appArmor = "AppArmor"
  34. dynamicKubeletConfig = "DynamicKubeletConfig"
  35. dynamicVolumeProvisioning = "DynamicVolumeProvisioning"
  36. )
  37. var (
  38. // Default values for recorded features. Every new feature gate should be
  39. // represented here.
  40. knownFeatures = map[string]featureSpec{
  41. allAlphaGate: {false, alpha},
  42. externalTrafficLocalOnly: {false, alpha},
  43. appArmor: {true, beta},
  44. dynamicKubeletConfig: {false, alpha},
  45. dynamicVolumeProvisioning: {true, alpha},
  46. }
  47. // Special handling for a few gates.
  48. specialFeatures = map[string]func(f *featureGate, val bool){
  49. allAlphaGate: setUnsetAlphaGates,
  50. }
  51. // DefaultFeatureGate is a shared global FeatureGate.
  52. DefaultFeatureGate = &featureGate{
  53. known: knownFeatures,
  54. special: specialFeatures,
  55. }
  56. )
  57. type featureSpec struct {
  58. enabled bool
  59. prerelease prerelease
  60. }
  61. type prerelease string
  62. const (
  63. // Values for prerelease.
  64. alpha = prerelease("ALPHA")
  65. beta = prerelease("BETA")
  66. ga = prerelease("")
  67. )
  68. // FeatureGate parses and stores flag gates for known features from
  69. // a string like feature1=true,feature2=false,...
  70. type FeatureGate interface {
  71. AddFlag(fs *pflag.FlagSet)
  72. // Every feature gate should add method here following this template:
  73. //
  74. // // owner: @username
  75. // // alpha: v1.4
  76. // MyFeature() bool
  77. // owner: @timstclair
  78. // beta: v1.4
  79. AppArmor() bool
  80. // owner: @girishkalele
  81. // alpha: v1.4
  82. ExternalTrafficLocalOnly() bool
  83. // owner: @saad-ali
  84. // alpha: v1.3
  85. DynamicVolumeProvisioning() bool
  86. // owner: mtaufen
  87. // alpha: v1.4
  88. DynamicKubeletConfig() bool
  89. }
  90. // featureGate implements FeatureGate as well as pflag.Value for flag parsing.
  91. type featureGate struct {
  92. known map[string]featureSpec
  93. special map[string]func(*featureGate, bool)
  94. enabled map[string]bool
  95. }
  96. func setUnsetAlphaGates(f *featureGate, val bool) {
  97. for k, v := range f.known {
  98. if v.prerelease == alpha {
  99. if _, found := f.enabled[k]; !found {
  100. f.enabled[k] = val
  101. }
  102. }
  103. }
  104. }
  105. // Set, String, and Type implement pflag.Value
  106. // Set Parses a string of the form // "key1=value1,key2=value2,..." into a
  107. // map[string]bool of known keys or returns an error.
  108. func (f *featureGate) Set(value string) error {
  109. f.enabled = make(map[string]bool)
  110. for _, s := range strings.Split(value, ",") {
  111. if len(s) == 0 {
  112. continue
  113. }
  114. arr := strings.SplitN(s, "=", 2)
  115. k := strings.TrimSpace(arr[0])
  116. _, ok := f.known[k]
  117. if !ok {
  118. return fmt.Errorf("unrecognized key: %s", k)
  119. }
  120. if len(arr) != 2 {
  121. return fmt.Errorf("missing bool value for %s", k)
  122. }
  123. v := strings.TrimSpace(arr[1])
  124. boolValue, err := strconv.ParseBool(v)
  125. if err != nil {
  126. return fmt.Errorf("invalid value of %s: %s, err: %v", k, v, err)
  127. }
  128. f.enabled[k] = boolValue
  129. // Handle "special" features like "all alpha gates"
  130. if fn, found := f.special[k]; found {
  131. fn(f, boolValue)
  132. }
  133. }
  134. glog.Infof("feature gates: %v", f.enabled)
  135. return nil
  136. }
  137. func (f *featureGate) String() string {
  138. pairs := []string{}
  139. for k, v := range f.enabled {
  140. pairs = append(pairs, fmt.Sprintf("%s=%t", k, v))
  141. }
  142. sort.Strings(pairs)
  143. return strings.Join(pairs, ",")
  144. }
  145. func (f *featureGate) Type() string {
  146. return "mapStringBool"
  147. }
  148. // ExternalTrafficLocalOnly returns value for AllowExtTrafficLocalEndpoints
  149. func (f *featureGate) ExternalTrafficLocalOnly() bool {
  150. return f.lookup(externalTrafficLocalOnly)
  151. }
  152. // AppArmor returns the value for the AppArmor feature gate.
  153. func (f *featureGate) AppArmor() bool {
  154. return f.lookup(appArmor)
  155. }
  156. // DynamicKubeletConfig returns value for dynamicKubeletConfig
  157. func (f *featureGate) DynamicKubeletConfig() bool {
  158. return f.lookup(dynamicKubeletConfig)
  159. }
  160. // DynamicVolumeProvisioning returns value for dynamicVolumeProvisioning
  161. func (f *featureGate) DynamicVolumeProvisioning() bool {
  162. return f.lookup(dynamicVolumeProvisioning)
  163. }
  164. func (f *featureGate) lookup(key string) bool {
  165. defaultValue := f.known[key].enabled
  166. if f.enabled != nil {
  167. if v, ok := f.enabled[key]; ok {
  168. return v
  169. }
  170. }
  171. return defaultValue
  172. }
  173. // AddFlag adds a flag for setting global feature gates to the specified FlagSet.
  174. func (f *featureGate) AddFlag(fs *pflag.FlagSet) {
  175. var known []string
  176. for k, v := range f.known {
  177. pre := ""
  178. if v.prerelease != ga {
  179. pre = fmt.Sprintf("%s - ", v.prerelease)
  180. }
  181. known = append(known, fmt.Sprintf("%s=true|false (%sdefault=%t)", k, pre, v.enabled))
  182. }
  183. fs.Var(f, flagName, ""+
  184. "A set of key=value pairs that describe feature gates for alpha/experimental features. "+
  185. "Options are:\n"+strings.Join(known, "\n"))
  186. }