...

Source file src/k8s.io/component-base/featuregate/feature_gate.go

Documentation: k8s.io/component-base/featuregate

     1  /*
     2  Copyright 2016 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 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  	// allAlphaGate is a global toggle for alpha features. Per-feature key
    41  	// values override the default set by allAlphaGate. Examples:
    42  	//   AllAlpha=false,NewFeature=true  will result in newFeature=true
    43  	//   AllAlpha=true,NewFeature=false  will result in newFeature=false
    44  	allAlphaGate Feature = "AllAlpha"
    45  
    46  	// allBetaGate is a global toggle for beta features. Per-feature key
    47  	// values override the default set by allBetaGate. Examples:
    48  	//   AllBeta=false,NewFeature=true  will result in NewFeature=true
    49  	//   AllBeta=true,NewFeature=false  will result in NewFeature=false
    50  	allBetaGate Feature = "AllBeta"
    51  )
    52  
    53  var (
    54  	// The generic features.
    55  	defaultFeatures = map[Feature]FeatureSpec{
    56  		allAlphaGate: {Default: false, PreRelease: Alpha},
    57  		allBetaGate:  {Default: false, PreRelease: Beta},
    58  	}
    59  
    60  	// Special handling for a few gates.
    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  	// Default is the default enablement state for the feature
    69  	Default bool
    70  	// LockToDefault indicates that the feature is locked to its default and cannot be changed
    71  	LockToDefault bool
    72  	// PreRelease indicates the maturity level of the feature
    73  	PreRelease prerelease
    74  }
    75  
    76  type prerelease string
    77  
    78  const (
    79  	// Values for PreRelease.
    80  	Alpha = prerelease("ALPHA")
    81  	Beta  = prerelease("BETA")
    82  	GA    = prerelease("")
    83  
    84  	// Deprecated
    85  	Deprecated = prerelease("DEPRECATED")
    86  )
    87  
    88  // FeatureGate indicates whether a given feature is enabled or not
    89  type FeatureGate interface {
    90  	// Enabled returns true if the key is enabled.
    91  	Enabled(key Feature) bool
    92  	// KnownFeatures returns a slice of strings describing the FeatureGate's known features.
    93  	KnownFeatures() []string
    94  	// DeepCopy returns a deep copy of the FeatureGate object, such that gates can be
    95  	// set on the copy without mutating the original. This is useful for validating
    96  	// config against potential feature gate changes before committing those changes.
    97  	DeepCopy() MutableFeatureGate
    98  }
    99  
   100  // MutableFeatureGate parses and stores flag gates for known features from
   101  // a string like feature1=true,feature2=false,...
   102  type MutableFeatureGate interface {
   103  	FeatureGate
   104  
   105  	// AddFlag adds a flag for setting global feature gates to the specified FlagSet.
   106  	AddFlag(fs *pflag.FlagSet)
   107  	// Set parses and stores flag gates for known features
   108  	// from a string like feature1=true,feature2=false,...
   109  	Set(value string) error
   110  	// SetFromMap stores flag gates for known features from a map[string]bool or returns an error
   111  	SetFromMap(m map[string]bool) error
   112  	// Add adds features to the featureGate.
   113  	Add(features map[Feature]FeatureSpec) error
   114  	// GetAll returns a copy of the map of known feature names to feature specs.
   115  	GetAll() map[Feature]FeatureSpec
   116  	// AddMetrics adds feature enablement metrics
   117  	AddMetrics()
   118  	// OverrideDefault sets a local override for the registered default value of a named
   119  	// feature. If the feature has not been previously registered (e.g. by a call to Add), has a
   120  	// locked default, or if the gate has already registered itself with a FlagSet, a non-nil
   121  	// error is returned.
   122  	//
   123  	// When two or more components consume a common feature, one component can override its
   124  	// default at runtime in order to adopt new defaults before or after the other
   125  	// components. For example, a new feature can be evaluated with a limited blast radius by
   126  	// overriding its default to true for a limited number of components without simultaneously
   127  	// changing its default for all consuming components.
   128  	OverrideDefault(name Feature, override bool) error
   129  }
   130  
   131  // featureGate implements FeatureGate as well as pflag.Value for flag parsing.
   132  type featureGate struct {
   133  	featureGateName string
   134  
   135  	special map[Feature]func(map[Feature]FeatureSpec, map[Feature]bool, bool)
   136  
   137  	// lock guards writes to known, enabled, and reads/writes of closed
   138  	lock sync.Mutex
   139  	// known holds a map[Feature]FeatureSpec
   140  	known atomic.Value
   141  	// enabled holds a map[Feature]bool
   142  	enabled atomic.Value
   143  	// closed is set to true when AddFlag is called, and prevents subsequent calls to Add
   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  // Set, String, and Type implement pflag.Value
   168  var _ pflag.Value = &featureGate{}
   169  
   170  // internalPackages are packages that ignored when creating a name for featureGates. These packages are in the common
   171  // call chains, so they'd be unhelpful as names.
   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  // Set parses a string of the form "key1=value1,key2=value2,..." into a
   191  // map[string]bool of known keys or returns an error.
   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  // SetFromMap stores flag gates for known features from a map[string]bool or returns an error
   214  func (f *featureGate) SetFromMap(m map[string]bool) error {
   215  	f.lock.Lock()
   216  	defer f.lock.Unlock()
   217  
   218  	// Copy existing state
   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  		// Handle "special" features like "all alpha gates"
   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  	// Persist changes
   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  // String returns a string containing all enabled feature gates, formatted as "key1=value1,key2=value2,...".
   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  // Add adds features to the featureGate.
   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  	// Copy existing state
   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  	// Persist updated state
   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  // GetAll returns a copy of the map of known feature names to feature specs.
   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  // Enabled returns true if the key is enabled.  If the key is not known, this call will panic.
   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  // AddFlag adds a flag for setting global feature gates to the specified FlagSet.
   358  func (f *featureGate) AddFlag(fs *pflag.FlagSet) {
   359  	f.lock.Lock()
   360  	// TODO(mtaufen): Shouldn't we just close it on the first Set/SetFromMap instead?
   361  	// Not all components expose a feature gates flag using this AddFlag method, and
   362  	// in the future, all components will completely stop exposing a feature gates flag,
   363  	// in favor of componentconfig.
   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  // KnownFeatures returns a slice of strings describing the FeatureGate's known features.
   380  // Deprecated and GA features are hidden from the list.
   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  // DeepCopy returns a deep copy of the FeatureGate object, such that gates can be
   394  // set on the copy without mutating the original. This is useful for validating
   395  // config against potential feature gate changes before committing those changes.
   396  func (f *featureGate) DeepCopy() MutableFeatureGate {
   397  	// Copy existing state.
   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  	// Construct a new featureGate around the copied state.
   408  	// Note that specialFeatures is treated as immutable by convention,
   409  	// and we maintain the value of f.closed across the copy.
   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