client-go/features: add Set method to envvar impl
This commit is contained in:
		@@ -47,6 +47,10 @@ var _ Gates = &envVarFeatureGates{}
 | 
				
			|||||||
//
 | 
					//
 | 
				
			||||||
// Please note that environmental variables can only be set to the boolean value.
 | 
					// Please note that environmental variables can only be set to the boolean value.
 | 
				
			||||||
// Incorrect values will be ignored and logged.
 | 
					// Incorrect values will be ignored and logged.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Features can also be set directly via the Set method.
 | 
				
			||||||
 | 
					// In that case, these features take precedence over
 | 
				
			||||||
 | 
					// features set via environmental variables.
 | 
				
			||||||
func newEnvVarFeatureGates(features map[Feature]FeatureSpec) *envVarFeatureGates {
 | 
					func newEnvVarFeatureGates(features map[Feature]FeatureSpec) *envVarFeatureGates {
 | 
				
			||||||
	known := map[Feature]FeatureSpec{}
 | 
						known := map[Feature]FeatureSpec{}
 | 
				
			||||||
	for name, spec := range features {
 | 
						for name, spec := range features {
 | 
				
			||||||
@@ -57,7 +61,8 @@ func newEnvVarFeatureGates(features map[Feature]FeatureSpec) *envVarFeatureGates
 | 
				
			|||||||
		callSiteName: naming.GetNameFromCallsite(internalPackages...),
 | 
							callSiteName: naming.GetNameFromCallsite(internalPackages...),
 | 
				
			||||||
		known:        known,
 | 
							known:        known,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	fg.enabled.Store(map[Feature]bool{})
 | 
						fg.enabledViaEnvVar.Store(map[Feature]bool{})
 | 
				
			||||||
 | 
						fg.enabledViaSetMethod = map[Feature]bool{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return fg
 | 
						return fg
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -74,9 +79,17 @@ type envVarFeatureGates struct {
 | 
				
			|||||||
	// known holds known feature gates
 | 
						// known holds known feature gates
 | 
				
			||||||
	known map[Feature]FeatureSpec
 | 
						known map[Feature]FeatureSpec
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// enabled holds a map[Feature]bool
 | 
						// enabledViaEnvVar holds a map[Feature]bool
 | 
				
			||||||
	// with values explicitly set via env var
 | 
						// with values explicitly set via env var
 | 
				
			||||||
	enabled atomic.Value
 | 
						enabledViaEnvVar atomic.Value
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// lockEnabledViaSetMethod protects enabledViaSetMethod
 | 
				
			||||||
 | 
						lockEnabledViaSetMethod sync.RWMutex
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// enabledViaSetMethod holds values explicitly set
 | 
				
			||||||
 | 
						// via Set method, features stored in this map take
 | 
				
			||||||
 | 
						// precedence over features stored in enabledViaEnvVar
 | 
				
			||||||
 | 
						enabledViaSetMethod map[Feature]bool
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// readEnvVars holds the boolean value which
 | 
						// readEnvVars holds the boolean value which
 | 
				
			||||||
	// indicates whether readEnvVarsOnce has been called.
 | 
						// indicates whether readEnvVarsOnce has been called.
 | 
				
			||||||
@@ -85,6 +98,15 @@ type envVarFeatureGates struct {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// Enabled returns true if the key is enabled. If the key is not known, this call will panic.
 | 
					// Enabled returns true if the key is enabled. If the key is not known, this call will panic.
 | 
				
			||||||
func (f *envVarFeatureGates) Enabled(key Feature) bool {
 | 
					func (f *envVarFeatureGates) Enabled(key Feature) bool {
 | 
				
			||||||
 | 
						if v, ok := f.wasFeatureEnabledViaSetMethod(key); ok {
 | 
				
			||||||
 | 
							// ensue that the state of all known features
 | 
				
			||||||
 | 
							// is loaded from environment variables
 | 
				
			||||||
 | 
							// on the first call to Enabled method.
 | 
				
			||||||
 | 
							if !f.hasAlreadyReadEnvVar() {
 | 
				
			||||||
 | 
								_ = f.getEnabledMapFromEnvVar()
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return v
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	if v, ok := f.getEnabledMapFromEnvVar()[key]; ok {
 | 
						if v, ok := f.getEnabledMapFromEnvVar()[key]; ok {
 | 
				
			||||||
		return v
 | 
							return v
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -94,6 +116,26 @@ func (f *envVarFeatureGates) Enabled(key Feature) bool {
 | 
				
			|||||||
	panic(fmt.Errorf("feature %q is not registered in FeatureGates %q", key, f.callSiteName))
 | 
						panic(fmt.Errorf("feature %q is not registered in FeatureGates %q", key, f.callSiteName))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Set sets the given feature to the given value.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Features set via this method take precedence over
 | 
				
			||||||
 | 
					// the features set via environment variables.
 | 
				
			||||||
 | 
					func (f *envVarFeatureGates) Set(featureName Feature, featureValue bool) error {
 | 
				
			||||||
 | 
						feature, ok := f.known[featureName]
 | 
				
			||||||
 | 
						if !ok {
 | 
				
			||||||
 | 
							return fmt.Errorf("feature %q is not registered in FeatureGates %q", featureName, f.callSiteName)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if feature.LockToDefault && feature.Default != featureValue {
 | 
				
			||||||
 | 
							return fmt.Errorf("cannot set feature gate %q to %v, feature is locked to %v", featureName, featureValue, feature.Default)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						f.lockEnabledViaSetMethod.Lock()
 | 
				
			||||||
 | 
						defer f.lockEnabledViaSetMethod.Unlock()
 | 
				
			||||||
 | 
						f.enabledViaSetMethod[featureName] = featureValue
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// getEnabledMapFromEnvVar will fill the enabled map on the first call.
 | 
					// getEnabledMapFromEnvVar will fill the enabled map on the first call.
 | 
				
			||||||
// This is the only time a known feature can be set to a value
 | 
					// This is the only time a known feature can be set to a value
 | 
				
			||||||
// read from the corresponding environmental variable.
 | 
					// read from the corresponding environmental variable.
 | 
				
			||||||
@@ -119,7 +161,7 @@ func (f *envVarFeatureGates) getEnabledMapFromEnvVar() map[Feature]bool {
 | 
				
			|||||||
				featureGatesState[feature] = boolVal
 | 
									featureGatesState[feature] = boolVal
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		f.enabled.Store(featureGatesState)
 | 
							f.enabledViaEnvVar.Store(featureGatesState)
 | 
				
			||||||
		f.readEnvVars.Store(true)
 | 
							f.readEnvVars.Store(true)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		for feature, featureSpec := range f.known {
 | 
							for feature, featureSpec := range f.known {
 | 
				
			||||||
@@ -130,7 +172,15 @@ func (f *envVarFeatureGates) getEnabledMapFromEnvVar() map[Feature]bool {
 | 
				
			|||||||
			klog.V(1).InfoS("Feature gate default state", "feature", feature, "enabled", featureSpec.Default)
 | 
								klog.V(1).InfoS("Feature gate default state", "feature", feature, "enabled", featureSpec.Default)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
	return f.enabled.Load().(map[Feature]bool)
 | 
						return f.enabledViaEnvVar.Load().(map[Feature]bool)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (f *envVarFeatureGates) wasFeatureEnabledViaSetMethod(key Feature) (bool, bool) {
 | 
				
			||||||
 | 
						f.lockEnabledViaSetMethod.RLock()
 | 
				
			||||||
 | 
						defer f.lockEnabledViaSetMethod.RUnlock()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						value, found := f.enabledViaSetMethod[key]
 | 
				
			||||||
 | 
						return value, found
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (f *envVarFeatureGates) hasAlreadyReadEnvVar() bool {
 | 
					func (f *envVarFeatureGates) hasAlreadyReadEnvVar() bool {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -23,8 +23,7 @@ import (
 | 
				
			|||||||
	"github.com/stretchr/testify/require"
 | 
						"github.com/stretchr/testify/require"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestEnvVarFeatureGates(t *testing.T) {
 | 
					var defaultTestFeatures = map[Feature]FeatureSpec{
 | 
				
			||||||
	defaultTestFeatures := map[Feature]FeatureSpec{
 | 
					 | 
				
			||||||
	"TestAlpha": {
 | 
						"TestAlpha": {
 | 
				
			||||||
		Default:       false,
 | 
							Default:       false,
 | 
				
			||||||
		LockToDefault: false,
 | 
							LockToDefault: false,
 | 
				
			||||||
@@ -35,9 +34,10 @@ func TestEnvVarFeatureGates(t *testing.T) {
 | 
				
			|||||||
		LockToDefault: false,
 | 
							LockToDefault: false,
 | 
				
			||||||
		PreRelease:    "Beta",
 | 
							PreRelease:    "Beta",
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
	}
 | 
					}
 | 
				
			||||||
	expectedDefaultFeaturesState := map[Feature]bool{"TestAlpha": false, "TestBeta": true}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestEnvVarFeatureGates(t *testing.T) {
 | 
				
			||||||
 | 
						expectedDefaultFeaturesState := map[Feature]bool{"TestAlpha": false, "TestBeta": true}
 | 
				
			||||||
	copyExpectedStateMap := func(toCopy map[Feature]bool) map[Feature]bool {
 | 
						copyExpectedStateMap := func(toCopy map[Feature]bool) map[Feature]bool {
 | 
				
			||||||
		m := map[Feature]bool{}
 | 
							m := map[Feature]bool{}
 | 
				
			||||||
		for k, v := range toCopy {
 | 
							for k, v := range toCopy {
 | 
				
			||||||
@@ -50,8 +50,11 @@ func TestEnvVarFeatureGates(t *testing.T) {
 | 
				
			|||||||
		name              string
 | 
							name              string
 | 
				
			||||||
		features          map[Feature]FeatureSpec
 | 
							features          map[Feature]FeatureSpec
 | 
				
			||||||
		envVariables      map[string]string
 | 
							envVariables      map[string]string
 | 
				
			||||||
 | 
							setMethodFeatures map[Feature]bool
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		expectedFeaturesState                           map[Feature]bool
 | 
							expectedFeaturesState                           map[Feature]bool
 | 
				
			||||||
		expectedInternalEnabledFeatureState map[Feature]bool
 | 
							expectedInternalEnabledViaEnvVarFeatureState    map[Feature]bool
 | 
				
			||||||
 | 
							expectedInternalEnabledViaSetMethodFeatureState map[Feature]bool
 | 
				
			||||||
	}{
 | 
						}{
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			name: "can add empty features",
 | 
								name: "can add empty features",
 | 
				
			||||||
@@ -76,7 +79,7 @@ func TestEnvVarFeatureGates(t *testing.T) {
 | 
				
			|||||||
				expectedDefaultFeaturesStateCopy["TestAlpha"] = true
 | 
									expectedDefaultFeaturesStateCopy["TestAlpha"] = true
 | 
				
			||||||
				return expectedDefaultFeaturesStateCopy
 | 
									return expectedDefaultFeaturesStateCopy
 | 
				
			||||||
			}(),
 | 
								}(),
 | 
				
			||||||
			expectedInternalEnabledFeatureState: map[Feature]bool{"TestAlpha": true},
 | 
								expectedInternalEnabledViaEnvVarFeatureState: map[Feature]bool{"TestAlpha": true},
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			name:                  "incorrect env var value gets ignored",
 | 
								name:                  "incorrect env var value gets ignored",
 | 
				
			||||||
@@ -113,7 +116,23 @@ func TestEnvVarFeatureGates(t *testing.T) {
 | 
				
			|||||||
			},
 | 
								},
 | 
				
			||||||
			envVariables:          map[string]string{"KUBE_FEATURE_TestAlpha": "True"},
 | 
								envVariables:          map[string]string{"KUBE_FEATURE_TestAlpha": "True"},
 | 
				
			||||||
			expectedFeaturesState: map[Feature]bool{"TestAlpha": true},
 | 
								expectedFeaturesState: map[Feature]bool{"TestAlpha": true},
 | 
				
			||||||
			expectedInternalEnabledFeatureState: map[Feature]bool{"TestAlpha": true},
 | 
								expectedInternalEnabledViaEnvVarFeatureState: map[Feature]bool{"TestAlpha": true},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name:                  "setting a feature via the Set method works",
 | 
				
			||||||
 | 
								features:              defaultTestFeatures,
 | 
				
			||||||
 | 
								setMethodFeatures:     map[Feature]bool{"TestAlpha": true},
 | 
				
			||||||
 | 
								expectedFeaturesState: map[Feature]bool{"TestAlpha": true},
 | 
				
			||||||
 | 
								expectedInternalEnabledViaSetMethodFeatureState: map[Feature]bool{"TestAlpha": true},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name:                  "setting a feature via the Set method wins",
 | 
				
			||||||
 | 
								features:              defaultTestFeatures,
 | 
				
			||||||
 | 
								setMethodFeatures:     map[Feature]bool{"TestAlpha": false},
 | 
				
			||||||
 | 
								envVariables:          map[string]string{"KUBE_FEATURE_TestAlpha": "True"},
 | 
				
			||||||
 | 
								expectedFeaturesState: map[Feature]bool{"TestAlpha": false},
 | 
				
			||||||
 | 
								expectedInternalEnabledViaEnvVarFeatureState:    map[Feature]bool{"TestAlpha": true},
 | 
				
			||||||
 | 
								expectedInternalEnabledViaSetMethodFeatureState: map[Feature]bool{"TestAlpha": false},
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	for _, scenario := range scenarios {
 | 
						for _, scenario := range scenarios {
 | 
				
			||||||
@@ -123,20 +142,33 @@ func TestEnvVarFeatureGates(t *testing.T) {
 | 
				
			|||||||
			}
 | 
								}
 | 
				
			||||||
			target := newEnvVarFeatureGates(scenario.features)
 | 
								target := newEnvVarFeatureGates(scenario.features)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								for k, v := range scenario.setMethodFeatures {
 | 
				
			||||||
 | 
									err := target.Set(k, v)
 | 
				
			||||||
 | 
									require.NoError(t, err)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
			for expectedFeature, expectedValue := range scenario.expectedFeaturesState {
 | 
								for expectedFeature, expectedValue := range scenario.expectedFeaturesState {
 | 
				
			||||||
				actualValue := target.Enabled(expectedFeature)
 | 
									actualValue := target.Enabled(expectedFeature)
 | 
				
			||||||
				require.Equal(t, actualValue, expectedValue, "expected feature=%v, to be=%v, not=%v", expectedFeature, expectedValue, actualValue)
 | 
									require.Equal(t, actualValue, expectedValue, "expected feature=%v, to be=%v, not=%v", expectedFeature, expectedValue, actualValue)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			enabledInternalMap := target.enabled.Load().(map[Feature]bool)
 | 
								enabledViaEnvVarInternalMap := target.enabledViaEnvVar.Load().(map[Feature]bool)
 | 
				
			||||||
			require.Len(t, enabledInternalMap, len(scenario.expectedInternalEnabledFeatureState))
 | 
								require.Len(t, enabledViaEnvVarInternalMap, len(scenario.expectedInternalEnabledViaEnvVarFeatureState))
 | 
				
			||||||
 | 
								for expectedFeatureName, expectedFeatureValue := range scenario.expectedInternalEnabledViaEnvVarFeatureState {
 | 
				
			||||||
 | 
									actualFeatureValue, wasExpectedFeatureFound := enabledViaEnvVarInternalMap[expectedFeatureName]
 | 
				
			||||||
 | 
									if !wasExpectedFeatureFound {
 | 
				
			||||||
 | 
										t.Errorf("feature %v has not been found in enabledViaEnvVarInternalMap", expectedFeatureName)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									require.Equal(t, expectedFeatureValue, actualFeatureValue, "feature %v has incorrect value = %v, expected = %v", expectedFeatureName, actualFeatureValue, expectedFeatureValue)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			for expectedFeature, expectedInternalPresence := range scenario.expectedInternalEnabledFeatureState {
 | 
								enabledViaSetMethodInternalMap := target.enabledViaSetMethod
 | 
				
			||||||
				featureInternalValue, featureSet := enabledInternalMap[expectedFeature]
 | 
								require.Len(t, enabledViaSetMethodInternalMap, len(scenario.expectedInternalEnabledViaSetMethodFeatureState))
 | 
				
			||||||
				require.Equal(t, expectedInternalPresence, featureSet, "feature %v present = %v, expected = %v", expectedFeature, featureSet, expectedInternalPresence)
 | 
								for expectedFeatureName, expectedFeatureValue := range scenario.expectedInternalEnabledViaSetMethodFeatureState {
 | 
				
			||||||
 | 
									actualFeatureValue, wasExpectedFeatureFound := enabledViaSetMethodInternalMap[expectedFeatureName]
 | 
				
			||||||
				expectedFeatureInternalValue := scenario.expectedFeaturesState[expectedFeature]
 | 
									if !wasExpectedFeatureFound {
 | 
				
			||||||
				require.Equal(t, expectedFeatureInternalValue, featureInternalValue)
 | 
										t.Errorf("feature %v has not been found in enabledViaSetMethod", expectedFeatureName)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									require.Equal(t, expectedFeatureValue, actualFeatureValue, "feature %v has incorrect value = %v, expected = %v", expectedFeatureName, actualFeatureValue, expectedFeatureValue)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		})
 | 
							})
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -154,3 +186,48 @@ func TestHasAlreadyReadEnvVar(t *testing.T) {
 | 
				
			|||||||
	_ = target.getEnabledMapFromEnvVar()
 | 
						_ = target.getEnabledMapFromEnvVar()
 | 
				
			||||||
	require.True(t, target.hasAlreadyReadEnvVar())
 | 
						require.True(t, target.hasAlreadyReadEnvVar())
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestEnvVarFeatureGatesSetNegative(t *testing.T) {
 | 
				
			||||||
 | 
						scenarios := []struct {
 | 
				
			||||||
 | 
							name         string
 | 
				
			||||||
 | 
							features     map[Feature]FeatureSpec
 | 
				
			||||||
 | 
							featureName  Feature
 | 
				
			||||||
 | 
							featureValue bool
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							expectedErr func(string) error
 | 
				
			||||||
 | 
						}{
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name:     "empty feature name returns an error",
 | 
				
			||||||
 | 
								features: defaultTestFeatures,
 | 
				
			||||||
 | 
								expectedErr: func(callSiteName string) error {
 | 
				
			||||||
 | 
									return fmt.Errorf("feature %q is not registered in FeatureGates %q", "", callSiteName)
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name:        "setting unknown feature returns an error",
 | 
				
			||||||
 | 
								features:    defaultTestFeatures,
 | 
				
			||||||
 | 
								featureName: "Unknown",
 | 
				
			||||||
 | 
								expectedErr: func(callSiteName string) error {
 | 
				
			||||||
 | 
									return fmt.Errorf("feature %q is not registered in FeatureGates %q", "Unknown", callSiteName)
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name:         "setting locked feature returns an error",
 | 
				
			||||||
 | 
								features:     map[Feature]FeatureSpec{"LockedFeature": {LockToDefault: true, Default: true}},
 | 
				
			||||||
 | 
								featureName:  "LockedFeature",
 | 
				
			||||||
 | 
								featureValue: false,
 | 
				
			||||||
 | 
								expectedErr: func(_ string) error {
 | 
				
			||||||
 | 
									return fmt.Errorf("cannot set feature gate %q to %v, feature is locked to %v", "LockedFeature", false, true)
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, scenario := range scenarios {
 | 
				
			||||||
 | 
							t.Run(scenario.name, func(t *testing.T) {
 | 
				
			||||||
 | 
								target := newEnvVarFeatureGates(scenario.features)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								err := target.Set(scenario.featureName, scenario.featureValue)
 | 
				
			||||||
 | 
								require.Equal(t, scenario.expectedErr(target.callSiteName), err)
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -30,6 +30,15 @@ func TestAddFeaturesToExistingFeatureGates(t *testing.T) {
 | 
				
			|||||||
	require.Equal(t, defaultKubernetesFeatureGates, fakeFeatureGates.specs)
 | 
						require.Equal(t, defaultKubernetesFeatureGates, fakeFeatureGates.specs)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestReplaceFeatureGatesWithWarningIndicator(t *testing.T) {
 | 
				
			||||||
 | 
						defaultFeatureGates := FeatureGates()
 | 
				
			||||||
 | 
						require.Panics(t, func() { defaultFeatureGates.Enabled("Foo") }, "reading an unregistered feature gate Foo should panic")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if !replaceFeatureGatesWithWarningIndicator(defaultFeatureGates) {
 | 
				
			||||||
 | 
							t.Error("replacing the default feature gates after reading a value hasn't produced a warning")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type fakeRegistry struct {
 | 
					type fakeRegistry struct {
 | 
				
			||||||
	specs map[Feature]FeatureSpec
 | 
						specs map[Feature]FeatureSpec
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user