...

Source file src/k8s.io/client-go/features/envvar_test.go

Documentation: k8s.io/client-go/features

     1  /*
     2  Copyright 2024 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 features
    18  
    19  import (
    20  	"fmt"
    21  	"testing"
    22  
    23  	"github.com/stretchr/testify/require"
    24  )
    25  
    26  func TestEnvVarFeatureGates(t *testing.T) {
    27  	defaultTestFeatures := map[Feature]FeatureSpec{
    28  		"TestAlpha": {
    29  			Default:       false,
    30  			LockToDefault: false,
    31  			PreRelease:    "Alpha",
    32  		},
    33  		"TestBeta": {
    34  			Default:       true,
    35  			LockToDefault: false,
    36  			PreRelease:    "Beta",
    37  		},
    38  	}
    39  	expectedDefaultFeaturesState := map[Feature]bool{"TestAlpha": false, "TestBeta": true}
    40  
    41  	copyExpectedStateMap := func(toCopy map[Feature]bool) map[Feature]bool {
    42  		m := map[Feature]bool{}
    43  		for k, v := range toCopy {
    44  			m[k] = v
    45  		}
    46  		return m
    47  	}
    48  
    49  	scenarios := []struct {
    50  		name                                string
    51  		features                            map[Feature]FeatureSpec
    52  		envVariables                        map[string]string
    53  		expectedFeaturesState               map[Feature]bool
    54  		expectedInternalEnabledFeatureState map[Feature]bool
    55  	}{
    56  		{
    57  			name: "can add empty features",
    58  		},
    59  		{
    60  			name:                  "no env var, features get Defaults assigned",
    61  			features:              defaultTestFeatures,
    62  			expectedFeaturesState: expectedDefaultFeaturesState,
    63  		},
    64  		{
    65  			name:                  "incorrect env var, feature gets Default assigned",
    66  			features:              defaultTestFeatures,
    67  			envVariables:          map[string]string{"TestAlpha": "true"},
    68  			expectedFeaturesState: expectedDefaultFeaturesState,
    69  		},
    70  		{
    71  			name:         "correct env var changes the feature gets state",
    72  			features:     defaultTestFeatures,
    73  			envVariables: map[string]string{"KUBE_FEATURE_TestAlpha": "true"},
    74  			expectedFeaturesState: func() map[Feature]bool {
    75  				expectedDefaultFeaturesStateCopy := copyExpectedStateMap(expectedDefaultFeaturesState)
    76  				expectedDefaultFeaturesStateCopy["TestAlpha"] = true
    77  				return expectedDefaultFeaturesStateCopy
    78  			}(),
    79  			expectedInternalEnabledFeatureState: map[Feature]bool{"TestAlpha": true},
    80  		},
    81  		{
    82  			name:                  "incorrect env var value gets ignored",
    83  			features:              defaultTestFeatures,
    84  			envVariables:          map[string]string{"KUBE_FEATURE_TestAlpha": "TrueFalse"},
    85  			expectedFeaturesState: expectedDefaultFeaturesState,
    86  		},
    87  		{
    88  			name:                  "empty env var value gets ignored",
    89  			features:              defaultTestFeatures,
    90  			envVariables:          map[string]string{"KUBE_FEATURE_TestAlpha": ""},
    91  			expectedFeaturesState: expectedDefaultFeaturesState,
    92  		},
    93  		{
    94  			name: "a feature LockToDefault wins",
    95  			features: map[Feature]FeatureSpec{
    96  				"TestAlpha": {
    97  					Default:       true,
    98  					LockToDefault: true,
    99  					PreRelease:    "Alpha",
   100  				},
   101  			},
   102  			envVariables:          map[string]string{"KUBE_FEATURE_TestAlpha": "False"},
   103  			expectedFeaturesState: map[Feature]bool{"TestAlpha": true},
   104  		},
   105  		{
   106  			name: "setting a feature to LockToDefault changes the internal state",
   107  			features: map[Feature]FeatureSpec{
   108  				"TestAlpha": {
   109  					Default:       true,
   110  					LockToDefault: true,
   111  					PreRelease:    "Alpha",
   112  				},
   113  			},
   114  			envVariables:                        map[string]string{"KUBE_FEATURE_TestAlpha": "True"},
   115  			expectedFeaturesState:               map[Feature]bool{"TestAlpha": true},
   116  			expectedInternalEnabledFeatureState: map[Feature]bool{"TestAlpha": true},
   117  		},
   118  	}
   119  	for _, scenario := range scenarios {
   120  		t.Run(scenario.name, func(t *testing.T) {
   121  			for k, v := range scenario.envVariables {
   122  				t.Setenv(k, v)
   123  			}
   124  			target := newEnvVarFeatureGates(scenario.features)
   125  
   126  			for expectedFeature, expectedValue := range scenario.expectedFeaturesState {
   127  				actualValue := target.Enabled(expectedFeature)
   128  				require.Equal(t, actualValue, expectedValue, "expected feature=%v, to be=%v, not=%v", expectedFeature, expectedValue, actualValue)
   129  			}
   130  
   131  			enabledInternalMap := target.enabled.Load().(map[Feature]bool)
   132  			require.Len(t, enabledInternalMap, len(scenario.expectedInternalEnabledFeatureState))
   133  
   134  			for expectedFeature, expectedInternalPresence := range scenario.expectedInternalEnabledFeatureState {
   135  				featureInternalValue, featureSet := enabledInternalMap[expectedFeature]
   136  				require.Equal(t, expectedInternalPresence, featureSet, "feature %v present = %v, expected = %v", expectedFeature, featureSet, expectedInternalPresence)
   137  
   138  				expectedFeatureInternalValue := scenario.expectedFeaturesState[expectedFeature]
   139  				require.Equal(t, expectedFeatureInternalValue, featureInternalValue)
   140  			}
   141  		})
   142  	}
   143  }
   144  
   145  func TestEnvVarFeatureGatesEnabledPanic(t *testing.T) {
   146  	target := newEnvVarFeatureGates(nil)
   147  	require.PanicsWithError(t, fmt.Errorf("feature %q is not registered in FeatureGates %q", "UnknownFeature", target.callSiteName).Error(), func() { target.Enabled("UnknownFeature") })
   148  }
   149  
   150  func TestHasAlreadyReadEnvVar(t *testing.T) {
   151  	target := newEnvVarFeatureGates(nil)
   152  	require.False(t, target.hasAlreadyReadEnvVar())
   153  
   154  	_ = target.getEnabledMapFromEnvVar()
   155  	require.True(t, target.hasAlreadyReadEnvVar())
   156  }
   157  

View as plain text