From 8f01fd254509d772bdf301bc3508dc54d2885bd3 Mon Sep 17 00:00:00 2001 From: Andrew Kim Date: Mon, 4 Mar 2019 12:35:31 -0500 Subject: [PATCH 1/2] move generic feature gate code from k8s.io/apiserver to k8s.io/component-base Kubernetes-commit: b2831a686c31b5db72f3106b3688f00c40ba1b00 --- pkg/util/feature/feature_gate.go | 318 +-------------- pkg/util/feature/feature_gate_test.go | 386 ------------------ .../feature/testing/feature_gate_testing.go | 44 -- 3 files changed, 4 insertions(+), 744 deletions(-) delete mode 100644 pkg/util/feature/feature_gate_test.go delete mode 100644 pkg/util/feature/testing/feature_gate_testing.go diff --git a/pkg/util/feature/feature_gate.go b/pkg/util/feature/feature_gate.go index 90f8300bd..a001cfa70 100644 --- a/pkg/util/feature/feature_gate.go +++ b/pkg/util/feature/feature_gate.go @@ -17,327 +17,17 @@ limitations under the License. package feature import ( - "fmt" - "sort" - "strconv" - "strings" - "sync" - "sync/atomic" - - "github.com/spf13/pflag" - "k8s.io/klog" -) - -type Feature string - -const ( - flagName = "feature-gates" - - // allAlphaGate is a global toggle for alpha features. Per-feature key - // values override the default set by allAlphaGate. Examples: - // AllAlpha=false,NewFeature=true will result in newFeature=true - // AllAlpha=true,NewFeature=false will result in newFeature=false - allAlphaGate Feature = "AllAlpha" + "k8s.io/component-base/featuregate" ) var ( - // The generic features. - defaultFeatures = map[Feature]FeatureSpec{ - allAlphaGate: {Default: false, PreRelease: Alpha}, - } - - // Special handling for a few gates. - specialFeatures = map[Feature]func(known map[Feature]FeatureSpec, enabled map[Feature]bool, val bool){ - allAlphaGate: setUnsetAlphaGates, - } - // DefaultMutableFeatureGate is a mutable version of DefaultFeatureGate. // Only top-level commands/options setup and the k8s.io/apiserver/pkg/util/feature/testing package should make use of this. // Tests that need to modify feature gates for the duration of their test should use: - // defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features., )() - DefaultMutableFeatureGate MutableFeatureGate = NewFeatureGate() + // defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features., )() + DefaultMutableFeatureGate featuregate.MutableFeatureGate = featuregate.NewFeatureGate() // DefaultFeatureGate is a shared global FeatureGate. // Top-level commands/options setup that needs to modify this feature gate should use DefaultMutableFeatureGate. - DefaultFeatureGate FeatureGate = DefaultMutableFeatureGate + DefaultFeatureGate featuregate.FeatureGate = DefaultMutableFeatureGate ) - -type FeatureSpec struct { - // Default is the default enablement state for the feature - Default bool - // LockToDefault indicates that the feature is locked to its default and cannot be changed - LockToDefault bool - // PreRelease indicates the maturity level of the feature - PreRelease prerelease -} - -type prerelease string - -const ( - // Values for PreRelease. - Alpha = prerelease("ALPHA") - Beta = prerelease("BETA") - GA = prerelease("") - - // Deprecated - Deprecated = prerelease("DEPRECATED") -) - -// FeatureGate indicates whether a given feature is enabled or not -type FeatureGate interface { - // Enabled returns true if the key is enabled. - Enabled(key Feature) bool - // KnownFeatures returns a slice of strings describing the FeatureGate's known features. - KnownFeatures() []string - // DeepCopy returns a deep copy of the FeatureGate object, such that gates can be - // set on the copy without mutating the original. This is useful for validating - // config against potential feature gate changes before committing those changes. - DeepCopy() MutableFeatureGate -} - -// MutableFeatureGate parses and stores flag gates for known features from -// a string like feature1=true,feature2=false,... -type MutableFeatureGate interface { - FeatureGate - - // AddFlag adds a flag for setting global feature gates to the specified FlagSet. - AddFlag(fs *pflag.FlagSet) - // Set parses and stores flag gates for known features - // from a string like feature1=true,feature2=false,... - Set(value string) error - // SetFromMap stores flag gates for known features from a map[string]bool or returns an error - SetFromMap(m map[string]bool) error - // Add adds features to the featureGate. - Add(features map[Feature]FeatureSpec) error -} - -// featureGate implements FeatureGate as well as pflag.Value for flag parsing. -type featureGate struct { - special map[Feature]func(map[Feature]FeatureSpec, map[Feature]bool, bool) - - // lock guards writes to known, enabled, and reads/writes of closed - lock sync.Mutex - // known holds a map[Feature]FeatureSpec - known *atomic.Value - // enabled holds a map[Feature]bool - enabled *atomic.Value - // closed is set to true when AddFlag is called, and prevents subsequent calls to Add - closed bool -} - -func setUnsetAlphaGates(known map[Feature]FeatureSpec, enabled map[Feature]bool, val bool) { - for k, v := range known { - if v.PreRelease == Alpha { - if _, found := enabled[k]; !found { - enabled[k] = val - } - } - } -} - -// Set, String, and Type implement pflag.Value -var _ pflag.Value = &featureGate{} - -func NewFeatureGate() *featureGate { - known := map[Feature]FeatureSpec{} - for k, v := range defaultFeatures { - known[k] = v - } - - knownValue := &atomic.Value{} - knownValue.Store(known) - - enabled := map[Feature]bool{} - enabledValue := &atomic.Value{} - enabledValue.Store(enabled) - - f := &featureGate{ - known: knownValue, - special: specialFeatures, - enabled: enabledValue, - } - return f -} - -// Set parses a string of the form "key1=value1,key2=value2,..." into a -// map[string]bool of known keys or returns an error. -func (f *featureGate) Set(value string) error { - m := make(map[string]bool) - for _, s := range strings.Split(value, ",") { - if len(s) == 0 { - continue - } - arr := strings.SplitN(s, "=", 2) - k := strings.TrimSpace(arr[0]) - if len(arr) != 2 { - return fmt.Errorf("missing bool value for %s", k) - } - v := strings.TrimSpace(arr[1]) - boolValue, err := strconv.ParseBool(v) - if err != nil { - return fmt.Errorf("invalid value of %s=%s, err: %v", k, v, err) - } - m[k] = boolValue - } - return f.SetFromMap(m) -} - -// SetFromMap stores flag gates for known features from a map[string]bool or returns an error -func (f *featureGate) SetFromMap(m map[string]bool) error { - f.lock.Lock() - defer f.lock.Unlock() - - // Copy existing state - known := map[Feature]FeatureSpec{} - for k, v := range f.known.Load().(map[Feature]FeatureSpec) { - known[k] = v - } - enabled := map[Feature]bool{} - for k, v := range f.enabled.Load().(map[Feature]bool) { - enabled[k] = v - } - - for k, v := range m { - k := Feature(k) - featureSpec, ok := known[k] - if !ok { - return fmt.Errorf("unrecognized feature gate: %s", k) - } - if featureSpec.LockToDefault && featureSpec.Default != v { - return fmt.Errorf("cannot set feature gate %v to %v, feature is locked to %v", k, v, featureSpec.Default) - } - enabled[k] = v - // Handle "special" features like "all alpha gates" - if fn, found := f.special[k]; found { - fn(known, enabled, v) - } - - if featureSpec.PreRelease == Deprecated { - klog.Warningf("Setting deprecated feature gate %s=%t. It will be removed in a future release.", k, v) - } else if featureSpec.PreRelease == GA { - klog.Warningf("Setting GA feature gate %s=%t. It will be removed in a future release.", k, v) - } - } - - // Persist changes - f.known.Store(known) - f.enabled.Store(enabled) - - klog.V(1).Infof("feature gates: %v", f.enabled) - return nil -} - -// String returns a string containing all enabled feature gates, formatted as "key1=value1,key2=value2,...". -func (f *featureGate) String() string { - pairs := []string{} - for k, v := range f.enabled.Load().(map[Feature]bool) { - pairs = append(pairs, fmt.Sprintf("%s=%t", k, v)) - } - sort.Strings(pairs) - return strings.Join(pairs, ",") -} - -func (f *featureGate) Type() string { - return "mapStringBool" -} - -// Add adds features to the featureGate. -func (f *featureGate) Add(features map[Feature]FeatureSpec) error { - f.lock.Lock() - defer f.lock.Unlock() - - if f.closed { - return fmt.Errorf("cannot add a feature gate after adding it to the flag set") - } - - // Copy existing state - known := map[Feature]FeatureSpec{} - for k, v := range f.known.Load().(map[Feature]FeatureSpec) { - known[k] = v - } - - for name, spec := range features { - if existingSpec, found := known[name]; found { - if existingSpec == spec { - continue - } - return fmt.Errorf("feature gate %q with different spec already exists: %v", name, existingSpec) - } - - known[name] = spec - } - - // Persist updated state - f.known.Store(known) - - return nil -} - -// Enabled returns true if the key is enabled. -func (f *featureGate) Enabled(key Feature) bool { - if v, ok := f.enabled.Load().(map[Feature]bool)[key]; ok { - return v - } - return f.known.Load().(map[Feature]FeatureSpec)[key].Default -} - -// AddFlag adds a flag for setting global feature gates to the specified FlagSet. -func (f *featureGate) AddFlag(fs *pflag.FlagSet) { - f.lock.Lock() - // TODO(mtaufen): Shouldn't we just close it on the first Set/SetFromMap instead? - // Not all components expose a feature gates flag using this AddFlag method, and - // in the future, all components will completely stop exposing a feature gates flag, - // in favor of componentconfig. - f.closed = true - f.lock.Unlock() - - known := f.KnownFeatures() - fs.Var(f, flagName, ""+ - "A set of key=value pairs that describe feature gates for alpha/experimental features. "+ - "Options are:\n"+strings.Join(known, "\n")) -} - -// KnownFeatures returns a slice of strings describing the FeatureGate's known features. -// Deprecated and GA features are hidden from the list. -func (f *featureGate) KnownFeatures() []string { - var known []string - for k, v := range f.known.Load().(map[Feature]FeatureSpec) { - if v.PreRelease == GA || v.PreRelease == Deprecated { - continue - } - known = append(known, fmt.Sprintf("%s=true|false (%s - default=%t)", k, v.PreRelease, v.Default)) - } - sort.Strings(known) - return known -} - -// DeepCopy returns a deep copy of the FeatureGate object, such that gates can be -// set on the copy without mutating the original. This is useful for validating -// config against potential feature gate changes before committing those changes. -func (f *featureGate) DeepCopy() MutableFeatureGate { - // Copy existing state. - known := map[Feature]FeatureSpec{} - for k, v := range f.known.Load().(map[Feature]FeatureSpec) { - known[k] = v - } - enabled := map[Feature]bool{} - for k, v := range f.enabled.Load().(map[Feature]bool) { - enabled[k] = v - } - - // Store copied state in new atomics. - knownValue := &atomic.Value{} - knownValue.Store(known) - enabledValue := &atomic.Value{} - enabledValue.Store(enabled) - - // Construct a new featureGate around the copied state. - // Note that specialFeatures is treated as immutable by convention, - // and we maintain the value of f.closed across the copy. - return &featureGate{ - special: specialFeatures, - known: knownValue, - enabled: enabledValue, - closed: f.closed, - } -} diff --git a/pkg/util/feature/feature_gate_test.go b/pkg/util/feature/feature_gate_test.go deleted file mode 100644 index aa42b3d3e..000000000 --- a/pkg/util/feature/feature_gate_test.go +++ /dev/null @@ -1,386 +0,0 @@ -/* -Copyright 2016 The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package feature - -import ( - "fmt" - "strings" - "testing" - - "github.com/spf13/pflag" - "github.com/stretchr/testify/assert" -) - -func TestFeatureGateFlag(t *testing.T) { - // gates for testing - const testAlphaGate Feature = "TestAlpha" - const testBetaGate Feature = "TestBeta" - - tests := []struct { - arg string - expect map[Feature]bool - parseError string - }{ - { - arg: "", - expect: map[Feature]bool{ - allAlphaGate: false, - testAlphaGate: false, - testBetaGate: false, - }, - }, - { - arg: "fooBarBaz=true", - expect: map[Feature]bool{ - allAlphaGate: false, - testAlphaGate: false, - testBetaGate: false, - }, - parseError: "unrecognized feature gate: fooBarBaz", - }, - { - arg: "AllAlpha=false", - expect: map[Feature]bool{ - allAlphaGate: false, - testAlphaGate: false, - testBetaGate: false, - }, - }, - { - arg: "AllAlpha=true", - expect: map[Feature]bool{ - allAlphaGate: true, - testAlphaGate: true, - testBetaGate: false, - }, - }, - { - arg: "AllAlpha=banana", - expect: map[Feature]bool{ - allAlphaGate: false, - testAlphaGate: false, - testBetaGate: false, - }, - parseError: "invalid value of AllAlpha", - }, - { - arg: "AllAlpha=false,TestAlpha=true", - expect: map[Feature]bool{ - allAlphaGate: false, - testAlphaGate: true, - testBetaGate: false, - }, - }, - { - arg: "TestAlpha=true,AllAlpha=false", - expect: map[Feature]bool{ - allAlphaGate: false, - testAlphaGate: true, - testBetaGate: false, - }, - }, - { - arg: "AllAlpha=true,TestAlpha=false", - expect: map[Feature]bool{ - allAlphaGate: true, - testAlphaGate: false, - testBetaGate: false, - }, - }, - { - arg: "TestAlpha=false,AllAlpha=true", - expect: map[Feature]bool{ - allAlphaGate: true, - testAlphaGate: false, - testBetaGate: false, - }, - }, - { - arg: "TestBeta=true,AllAlpha=false", - expect: map[Feature]bool{ - allAlphaGate: false, - testAlphaGate: false, - testBetaGate: true, - }, - }, - } - for i, test := range tests { - fs := pflag.NewFlagSet("testfeaturegateflag", pflag.ContinueOnError) - f := NewFeatureGate() - f.Add(map[Feature]FeatureSpec{ - testAlphaGate: {Default: false, PreRelease: Alpha}, - testBetaGate: {Default: false, PreRelease: Beta}, - }) - f.AddFlag(fs) - - err := fs.Parse([]string{fmt.Sprintf("--%s=%s", flagName, test.arg)}) - if test.parseError != "" { - if !strings.Contains(err.Error(), test.parseError) { - t.Errorf("%d: Parse() Expected %v, Got %v", i, test.parseError, err) - } - } else if err != nil { - t.Errorf("%d: Parse() Expected nil, Got %v", i, err) - } - for k, v := range test.expect { - if actual := f.enabled.Load().(map[Feature]bool)[k]; actual != v { - t.Errorf("%d: expected %s=%v, Got %v", i, k, v, actual) - } - } - } -} - -func TestFeatureGateOverride(t *testing.T) { - const testAlphaGate Feature = "TestAlpha" - const testBetaGate Feature = "TestBeta" - - // Don't parse the flag, assert defaults are used. - var f *featureGate = NewFeatureGate() - f.Add(map[Feature]FeatureSpec{ - testAlphaGate: {Default: false, PreRelease: Alpha}, - testBetaGate: {Default: false, PreRelease: Beta}, - }) - - f.Set("TestAlpha=true,TestBeta=true") - if f.Enabled(testAlphaGate) != true { - t.Errorf("Expected true") - } - if f.Enabled(testBetaGate) != true { - t.Errorf("Expected true") - } - - f.Set("TestAlpha=false") - if f.Enabled(testAlphaGate) != false { - t.Errorf("Expected false") - } - if f.Enabled(testBetaGate) != true { - t.Errorf("Expected true") - } -} - -func TestFeatureGateFlagDefaults(t *testing.T) { - // gates for testing - const testAlphaGate Feature = "TestAlpha" - const testBetaGate Feature = "TestBeta" - - // Don't parse the flag, assert defaults are used. - var f *featureGate = NewFeatureGate() - f.Add(map[Feature]FeatureSpec{ - testAlphaGate: {Default: false, PreRelease: Alpha}, - testBetaGate: {Default: true, PreRelease: Beta}, - }) - - if f.Enabled(testAlphaGate) != false { - t.Errorf("Expected false") - } - if f.Enabled(testBetaGate) != true { - t.Errorf("Expected true") - } -} - -func TestFeatureGateKnownFeatures(t *testing.T) { - // gates for testing - const ( - testAlphaGate Feature = "TestAlpha" - testBetaGate Feature = "TestBeta" - testGAGate Feature = "TestGA" - testDeprecatedGate Feature = "TestDeprecated" - ) - - // Don't parse the flag, assert defaults are used. - var f *featureGate = NewFeatureGate() - f.Add(map[Feature]FeatureSpec{ - testAlphaGate: {Default: false, PreRelease: Alpha}, - testBetaGate: {Default: true, PreRelease: Beta}, - testGAGate: {Default: true, PreRelease: GA}, - testDeprecatedGate: {Default: false, PreRelease: Deprecated}, - }) - - known := strings.Join(f.KnownFeatures(), " ") - - assert.Contains(t, known, testAlphaGate) - assert.Contains(t, known, testBetaGate) - assert.NotContains(t, known, testGAGate) - assert.NotContains(t, known, testDeprecatedGate) -} - -func TestFeatureGateSetFromMap(t *testing.T) { - // gates for testing - const testAlphaGate Feature = "TestAlpha" - const testBetaGate Feature = "TestBeta" - const testLockedTrueGate Feature = "TestLockedTrue" - const testLockedFalseGate Feature = "TestLockedFalse" - - tests := []struct { - name string - setmap map[string]bool - expect map[Feature]bool - setmapError string - }{ - { - name: "set TestAlpha and TestBeta true", - setmap: map[string]bool{ - "TestAlpha": true, - "TestBeta": true, - }, - expect: map[Feature]bool{ - testAlphaGate: true, - testBetaGate: true, - }, - }, - { - name: "set TestBeta true", - setmap: map[string]bool{ - "TestBeta": true, - }, - expect: map[Feature]bool{ - testAlphaGate: false, - testBetaGate: true, - }, - }, - { - name: "set TestAlpha false", - setmap: map[string]bool{ - "TestAlpha": false, - }, - expect: map[Feature]bool{ - testAlphaGate: false, - testBetaGate: false, - }, - }, - { - name: "set TestInvaild true", - setmap: map[string]bool{ - "TestInvaild": true, - }, - expect: map[Feature]bool{ - testAlphaGate: false, - testBetaGate: false, - }, - setmapError: "unrecognized feature gate:", - }, - { - name: "set locked gates", - setmap: map[string]bool{ - "TestLockedTrue": true, - "TestLockedFalse": false, - }, - expect: map[Feature]bool{ - testAlphaGate: false, - testBetaGate: false, - }, - }, - { - name: "set locked gates", - setmap: map[string]bool{ - "TestLockedTrue": false, - }, - expect: map[Feature]bool{ - testAlphaGate: false, - testBetaGate: false, - }, - setmapError: "cannot set feature gate TestLockedTrue to false, feature is locked to true", - }, - { - name: "set locked gates", - setmap: map[string]bool{ - "TestLockedFalse": true, - }, - expect: map[Feature]bool{ - testAlphaGate: false, - testBetaGate: false, - }, - setmapError: "cannot set feature gate TestLockedFalse to true, feature is locked to false", - }, - } - for i, test := range tests { - t.Run(fmt.Sprintf("SetFromMap %s", test.name), func(t *testing.T) { - f := NewFeatureGate() - f.Add(map[Feature]FeatureSpec{ - testAlphaGate: {Default: false, PreRelease: Alpha}, - testBetaGate: {Default: false, PreRelease: Beta}, - testLockedTrueGate: {Default: true, PreRelease: GA, LockToDefault: true}, - testLockedFalseGate: {Default: false, PreRelease: GA, LockToDefault: true}, - }) - err := f.SetFromMap(test.setmap) - if test.setmapError != "" { - if err == nil { - t.Errorf("expected error, got none") - } else if !strings.Contains(err.Error(), test.setmapError) { - t.Errorf("%d: SetFromMap(%#v) Expected err:%v, Got err:%v", i, test.setmap, test.setmapError, err) - } - } else if err != nil { - t.Errorf("%d: SetFromMap(%#v) Expected success, Got err:%v", i, test.setmap, err) - } - for k, v := range test.expect { - if actual := f.Enabled(k); actual != v { - t.Errorf("%d: SetFromMap(%#v) Expected %s=%v, Got %s=%v", i, test.setmap, k, v, k, actual) - } - } - }) - } -} - -func TestFeatureGateString(t *testing.T) { - // gates for testing - const testAlphaGate Feature = "TestAlpha" - const testBetaGate Feature = "TestBeta" - const testGAGate Feature = "TestGA" - - featuremap := map[Feature]FeatureSpec{ - testGAGate: {Default: true, PreRelease: GA}, - testAlphaGate: {Default: false, PreRelease: Alpha}, - testBetaGate: {Default: true, PreRelease: Beta}, - } - - tests := []struct { - setmap map[string]bool - expect string - }{ - { - setmap: map[string]bool{ - "TestAlpha": false, - }, - expect: "TestAlpha=false", - }, - { - setmap: map[string]bool{ - "TestAlpha": false, - "TestBeta": true, - }, - expect: "TestAlpha=false,TestBeta=true", - }, - { - setmap: map[string]bool{ - "TestGA": true, - "TestAlpha": false, - "TestBeta": true, - }, - expect: "TestAlpha=false,TestBeta=true,TestGA=true", - }, - } - for i, test := range tests { - t.Run(fmt.Sprintf("SetFromMap %s", test.expect), func(t *testing.T) { - f := NewFeatureGate() - f.Add(featuremap) - f.SetFromMap(test.setmap) - result := f.String() - if result != test.expect { - t.Errorf("%d: SetFromMap(%#v) Expected %s, Got %s", i, test.setmap, test.expect, result) - } - }) - } -} diff --git a/pkg/util/feature/testing/feature_gate_testing.go b/pkg/util/feature/testing/feature_gate_testing.go deleted file mode 100644 index b4afc9ae7..000000000 --- a/pkg/util/feature/testing/feature_gate_testing.go +++ /dev/null @@ -1,44 +0,0 @@ -/* -Copyright 2017 The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package testing - -import ( - "fmt" - "testing" - - "k8s.io/apiserver/pkg/util/feature" -) - -// SetFeatureGateDuringTest sets the specified gate to the specified value, and returns a function that restores the original value. -// Failures to set or restore cause the test to fail. -// -// Example use: -// -// defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features., true)() -func SetFeatureGateDuringTest(tb testing.TB, gate feature.FeatureGate, f feature.Feature, value bool) func() { - originalValue := gate.Enabled(f) - - if err := gate.(feature.MutableFeatureGate).Set(fmt.Sprintf("%s=%v", f, value)); err != nil { - tb.Errorf("error setting %s=%v: %v", f, value, err) - } - - return func() { - if err := gate.(feature.MutableFeatureGate).Set(fmt.Sprintf("%s=%v", f, originalValue)); err != nil { - tb.Errorf("error restoring %s=%v: %v", f, originalValue, err) - } - } -} From ef71315b08f4df22ddaf7e829faf0d0f3921deb0 Mon Sep 17 00:00:00 2001 From: Andrew Kim Date: Mon, 4 Mar 2019 12:46:52 -0500 Subject: [PATCH 2/2] update import of generic featuregate code from k8s.io/apiserver/pkg/util/feature -> k8s.io/component-base/featuregate Kubernetes-commit: c91913924543e1d29f3f3d51354701df9df75def --- pkg/endpoints/apiserver_test.go | 4 +- pkg/features/kube_features.go | 55 +++++++++++----------- pkg/server/options/audit_test.go | 4 +- pkg/storage/cacher/cacher_whitebox_test.go | 6 +-- pkg/storage/tests/cacher_test.go | 6 +-- pkg/util/feature/feature_gate.go | 2 +- 6 files changed, 39 insertions(+), 38 deletions(-) diff --git a/pkg/endpoints/apiserver_test.go b/pkg/endpoints/apiserver_test.go index 61583fab3..553948c08 100644 --- a/pkg/endpoints/apiserver_test.go +++ b/pkg/endpoints/apiserver_test.go @@ -73,7 +73,7 @@ import ( "k8s.io/apiserver/pkg/registry/rest" "k8s.io/apiserver/pkg/server/filters" utilfeature "k8s.io/apiserver/pkg/util/feature" - utilfeaturetesting "k8s.io/apiserver/pkg/util/feature/testing" + featuregatetesting "k8s.io/component-base/featuregate/testing" ) type alwaysMutatingDeny struct{} @@ -4093,7 +4093,7 @@ func (storage *SimpleRESTStorageWithDeleteCollection) DeleteCollection(ctx conte } func TestDryRunDisabled(t *testing.T) { - defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.DryRun, false)() + defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.DryRun, false)() tests := []struct { path string diff --git a/pkg/features/kube_features.go b/pkg/features/kube_features.go index 517c940b5..f74fb108d 100644 --- a/pkg/features/kube_features.go +++ b/pkg/features/kube_features.go @@ -20,6 +20,7 @@ import ( "k8s.io/apimachinery/pkg/util/runtime" utilfeature "k8s.io/apiserver/pkg/util/feature" + "k8s.io/component-base/featuregate" ) const ( @@ -35,7 +36,7 @@ const ( // // StreamingProxyRedirects controls whether the apiserver should intercept (and follow) // redirects from the backend (Kubelet) for streaming requests (exec/attach/port-forward). - StreamingProxyRedirects utilfeature.Feature = "StreamingProxyRedirects" + StreamingProxyRedirects featuregate.Feature = "StreamingProxyRedirects" // owner: @tallclair // alpha: v1.10 @@ -43,7 +44,7 @@ const ( // // ValidateProxyRedirects controls whether the apiserver should validate that redirects are only // followed to the same host. Only used if StreamingProxyRedirects is enabled. - ValidateProxyRedirects utilfeature.Feature = "ValidateProxyRedirects" + ValidateProxyRedirects featuregate.Feature = "ValidateProxyRedirects" // owner: @tallclair // alpha: v1.7 @@ -53,20 +54,20 @@ const ( // AdvancedAuditing enables a much more general API auditing pipeline, which includes support for // pluggable output backends and an audit policy specifying how different requests should be // audited. - AdvancedAuditing utilfeature.Feature = "AdvancedAuditing" + AdvancedAuditing featuregate.Feature = "AdvancedAuditing" // owner: @pbarker // alpha: v1.13 // // DynamicAuditing enables configuration of audit policy and webhook backends through an // AuditSink API object. - DynamicAuditing utilfeature.Feature = "DynamicAuditing" + DynamicAuditing featuregate.Feature = "DynamicAuditing" // owner: @ilackams // alpha: v1.7 // // Enables compression of REST responses (GET and LIST only) - APIResponseCompression utilfeature.Feature = "APIResponseCompression" + APIResponseCompression featuregate.Feature = "APIResponseCompression" // owner: @smarterclayton // alpha: v1.8 @@ -74,7 +75,7 @@ const ( // // Allow API clients to retrieve resource lists in chunks rather than // all at once. - APIListChunking utilfeature.Feature = "APIListChunking" + APIListChunking featuregate.Feature = "APIListChunking" // owner: @apelisse // alpha: v1.12 @@ -83,45 +84,45 @@ const ( // Allow requests to be processed but not stored, so that // validation, merging, mutation can be tested without // committing. - DryRun utilfeature.Feature = "DryRun" + DryRun featuregate.Feature = "DryRun" // owner: @apelisse, @lavalamp // alpha: v1.14 // // Server-side apply. Merging happens on the server. - ServerSideApply utilfeature.Feature = "ServerSideApply" + ServerSideApply featuregate.Feature = "ServerSideApply" // owner: @caesarxuchao // alpha: v1.14 // // Allow apiservers to expose the storage version hash in the discovery // document. - StorageVersionHash utilfeature.Feature = "StorageVersionHash" + StorageVersionHash featuregate.Feature = "StorageVersionHash" // owner: @ksubrmnn // alpha: v1.14 // // Allows kube-proxy to run in Overlay mode for Windows - WinOverlay utilfeature.Feature = "WinOverlay" + WinOverlay featuregate.Feature = "WinOverlay" // owner: @ksubrmnn // alpha: v1.14 // // Allows kube-proxy to create DSR loadbalancers for Windows - WinDSR utilfeature.Feature = "WinDSR" + WinDSR featuregate.Feature = "WinDSR" // owner: @wojtek-t // alpha: v1.15 // // Enables support for watch bookmark events. - WatchBookmark utilfeature.Feature = "WatchBookmark" + WatchBookmark featuregate.Feature = "WatchBookmark" // owner: @MikeSpreitzer @yue9944882 // alpha: v1.15 // // // Enables managing request concurrency with prioritization and fairness at each server - RequestManagement utilfeature.Feature = "RequestManagement" + RequestManagement featuregate.Feature = "RequestManagement" ) func init() { @@ -131,18 +132,18 @@ func init() { // defaultKubernetesFeatureGates consists of all known Kubernetes-specific feature keys. // To add a new feature, define a key for it above and add it here. The features will be // available throughout Kubernetes binaries. -var defaultKubernetesFeatureGates = map[utilfeature.Feature]utilfeature.FeatureSpec{ - StreamingProxyRedirects: {Default: true, PreRelease: utilfeature.Beta}, - ValidateProxyRedirects: {Default: true, PreRelease: utilfeature.Beta}, - AdvancedAuditing: {Default: true, PreRelease: utilfeature.GA}, - DynamicAuditing: {Default: false, PreRelease: utilfeature.Alpha}, - APIResponseCompression: {Default: false, PreRelease: utilfeature.Alpha}, - APIListChunking: {Default: true, PreRelease: utilfeature.Beta}, - DryRun: {Default: true, PreRelease: utilfeature.Beta}, - ServerSideApply: {Default: false, PreRelease: utilfeature.Alpha}, - StorageVersionHash: {Default: false, PreRelease: utilfeature.Alpha}, - WinOverlay: {Default: false, PreRelease: utilfeature.Alpha}, - WinDSR: {Default: false, PreRelease: utilfeature.Alpha}, - WatchBookmark: {Default: false, PreRelease: utilfeature.Alpha}, - RequestManagement: {Default: false, PreRelease: utilfeature.Alpha}, +var defaultKubernetesFeatureGates = map[featuregate.Feature]featuregate.FeatureSpec{ + StreamingProxyRedirects: {Default: true, PreRelease: featuregate.Beta}, + ValidateProxyRedirects: {Default: true, PreRelease: featuregate.Beta}, + AdvancedAuditing: {Default: true, PreRelease: featuregate.GA}, + DynamicAuditing: {Default: false, PreRelease: featuregate.Alpha}, + APIResponseCompression: {Default: false, PreRelease: featuregate.Alpha}, + APIListChunking: {Default: true, PreRelease: featuregate.Beta}, + DryRun: {Default: true, PreRelease: featuregate.Beta}, + ServerSideApply: {Default: false, PreRelease: featuregate.Alpha}, + StorageVersionHash: {Default: false, PreRelease: featuregate.Alpha}, + WinOverlay: {Default: false, PreRelease: featuregate.Alpha}, + WinDSR: {Default: false, PreRelease: featuregate.Alpha}, + WatchBookmark: {Default: false, PreRelease: featuregate.Alpha}, + RequestManagement: {Default: false, PreRelease: featuregate.Alpha}, } diff --git a/pkg/server/options/audit_test.go b/pkg/server/options/audit_test.go index 7137c521b..55ddb0de5 100644 --- a/pkg/server/options/audit_test.go +++ b/pkg/server/options/audit_test.go @@ -28,11 +28,11 @@ import ( "k8s.io/apiserver/pkg/features" "k8s.io/apiserver/pkg/server" utilfeature "k8s.io/apiserver/pkg/util/feature" - utilfeaturetesting "k8s.io/apiserver/pkg/util/feature/testing" "k8s.io/client-go/informers" "k8s.io/client-go/kubernetes/fake" restclient "k8s.io/client-go/rest" "k8s.io/client-go/tools/clientcmd/api/v1" + featuregatetesting "k8s.io/component-base/featuregate/testing" "github.com/spf13/pflag" "github.com/stretchr/testify/assert" @@ -46,7 +46,7 @@ func TestAuditValidOptions(t *testing.T) { policy := makeTmpPolicy(t) defer os.Remove(policy) - defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.DynamicAuditing, true)() + defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.DynamicAuditing, true)() clientConfig := &restclient.Config{} informerFactory := informers.NewSharedInformerFactory(fake.NewSimpleClientset(), 0) diff --git a/pkg/storage/cacher/cacher_whitebox_test.go b/pkg/storage/cacher/cacher_whitebox_test.go index a737b9ccb..70d0a93ec 100644 --- a/pkg/storage/cacher/cacher_whitebox_test.go +++ b/pkg/storage/cacher/cacher_whitebox_test.go @@ -42,7 +42,7 @@ import ( "k8s.io/apiserver/pkg/features" "k8s.io/apiserver/pkg/storage" utilfeature "k8s.io/apiserver/pkg/util/feature" - utilfeaturetesting "k8s.io/apiserver/pkg/util/feature/testing" + featuregatetesting "k8s.io/component-base/featuregate/testing" ) var ( @@ -539,7 +539,7 @@ func TestTimeBucketWatchersBasic(t *testing.T) { } func testCacherSendBookmarkEvents(t *testing.T, watchCacheEnabled, allowWatchBookmarks, expectedBookmarks bool) { - defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.WatchBookmark, watchCacheEnabled)() + defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.WatchBookmark, watchCacheEnabled)() backingStorage := &dummyStorage{} cacher, _ := newTestCacher(backingStorage, 1000) defer cacher.Stop() @@ -638,7 +638,7 @@ func TestCacherSendBookmarkEvents(t *testing.T) { } func TestDispatchingBookmarkEventsWithConcurrentStop(t *testing.T) { - defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.WatchBookmark, true)() + defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.WatchBookmark, true)() backingStorage := &dummyStorage{} cacher, _ := newTestCacher(backingStorage, 1000) defer cacher.Stop() diff --git a/pkg/storage/tests/cacher_test.go b/pkg/storage/tests/cacher_test.go index 62d3af9de..0f151688b 100644 --- a/pkg/storage/tests/cacher_test.go +++ b/pkg/storage/tests/cacher_test.go @@ -49,7 +49,7 @@ import ( "k8s.io/apiserver/pkg/storage/etcd3" "k8s.io/apiserver/pkg/storage/value" utilfeature "k8s.io/apiserver/pkg/util/feature" - utilfeaturetesting "k8s.io/apiserver/pkg/util/feature/testing" + featuregatetesting "k8s.io/component-base/featuregate/testing" ) var ( @@ -786,7 +786,7 @@ func TestCacherListerWatcherPagination(t *testing.T) { } func TestWatchDispatchBookmarkEvents(t *testing.T) { - defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.WatchBookmark, true)() + defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.WatchBookmark, true)() server, etcdStorage := newEtcdTestStorage(t, etcdtest.PathPrefix()) defer server.Terminate(t) @@ -848,7 +848,7 @@ func TestWatchDispatchBookmarkEvents(t *testing.T) { } func TestWatchBookmarksWithCorrectResourceVersion(t *testing.T) { - defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.WatchBookmark, true)() + defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.WatchBookmark, true)() server, etcdStorage := newEtcdTestStorage(t, etcdtest.PathPrefix()) defer server.Terminate(t) diff --git a/pkg/util/feature/feature_gate.go b/pkg/util/feature/feature_gate.go index a001cfa70..5911b7568 100644 --- a/pkg/util/feature/feature_gate.go +++ b/pkg/util/feature/feature_gate.go @@ -22,7 +22,7 @@ import ( var ( // DefaultMutableFeatureGate is a mutable version of DefaultFeatureGate. - // Only top-level commands/options setup and the k8s.io/apiserver/pkg/util/feature/testing package should make use of this. + // Only top-level commands/options setup and the k8s.io/component-base/featuregate/testing package should make use of this. // Tests that need to modify feature gates for the duration of their test should use: // defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features., )() DefaultMutableFeatureGate featuregate.MutableFeatureGate = featuregate.NewFeatureGate()