Skip to content

Commit 076c7b0

Browse files
authored
Merge pull request kubernetes#130079 from yongruilin/compatibility-remove-reset
[compatibility version] Avoid resetting config when adding flags
2 parents f9b27ed + dab8758 commit 076c7b0

File tree

4 files changed

+98
-14
lines changed

4 files changed

+98
-14
lines changed

cmd/kube-scheduler/app/testing/testserver.go

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,15 +26,17 @@ import (
2626
"github.com/spf13/pflag"
2727

2828
"k8s.io/apimachinery/pkg/util/wait"
29+
utilcompatibility "k8s.io/apiserver/pkg/util/compatibility"
30+
utilfeature "k8s.io/apiserver/pkg/util/feature"
2931
"k8s.io/client-go/kubernetes"
3032
restclient "k8s.io/client-go/rest"
33+
"k8s.io/component-base/compatibility"
3134
"k8s.io/component-base/configz"
3235
logsapi "k8s.io/component-base/logs/api/v1"
36+
"k8s.io/klog/v2"
3337
"k8s.io/kubernetes/cmd/kube-scheduler/app"
3438
kubeschedulerconfig "k8s.io/kubernetes/cmd/kube-scheduler/app/config"
3539
"k8s.io/kubernetes/cmd/kube-scheduler/app/options"
36-
37-
"k8s.io/klog/v2"
3840
)
3941

4042
func init() {
@@ -98,6 +100,16 @@ func StartTestServer(ctx context.Context, customFlags []string) (result TestServ
98100
fs := pflag.NewFlagSet("test", pflag.PanicOnError)
99101

100102
opts := options.NewOptions()
103+
// set up new instance of ComponentGlobalsRegistry instead of using the DefaultComponentGlobalsRegistry to avoid contention in parallel tests.
104+
featureGate := utilfeature.DefaultMutableFeatureGate.DeepCopy()
105+
effectiveVersion := utilcompatibility.DefaultKubeEffectiveVersionForTest()
106+
effectiveVersion.SetEmulationVersion(featureGate.EmulationVersion())
107+
componentGlobalsRegistry := compatibility.NewComponentGlobalsRegistry()
108+
if err := componentGlobalsRegistry.Register(compatibility.DefaultKubeComponent, effectiveVersion, featureGate); err != nil {
109+
return result, err
110+
}
111+
opts.ComponentGlobalsRegistry = componentGlobalsRegistry
112+
101113
nfs := opts.Flags
102114
for _, f := range nfs.FlagSets {
103115
fs.AddFlagSet(f)
@@ -106,6 +118,20 @@ func StartTestServer(ctx context.Context, customFlags []string) (result TestServ
106118
if err := opts.ComponentGlobalsRegistry.Set(); err != nil {
107119
return result, err
108120
}
121+
// If the local ComponentGlobalsRegistry is changed by the flags,
122+
// we need to copy the new feature values back to the DefaultFeatureGate because most feature checks still use the DefaultFeatureGate.
123+
if !featureGate.EmulationVersion().EqualTo(utilfeature.DefaultMutableFeatureGate.EmulationVersion()) {
124+
if err := utilfeature.DefaultMutableFeatureGate.SetEmulationVersion(effectiveVersion.EmulationVersion()); err != nil {
125+
return result, err
126+
}
127+
}
128+
for f := range utilfeature.DefaultMutableFeatureGate.GetAll() {
129+
if featureGate.Enabled(f) != utilfeature.DefaultFeatureGate.Enabled(f) {
130+
if err := utilfeature.DefaultMutableFeatureGate.Set(fmt.Sprintf("%s=%v", f, featureGate.Enabled(f))); err != nil {
131+
return result, err
132+
}
133+
}
134+
}
109135

110136
if opts.SecureServing.BindPort != 0 {
111137
opts.SecureServing.Listener, opts.SecureServing.BindPort, err = createListenerOnFreePort()

staging/src/k8s.io/component-base/compatibility/registry.go

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,9 @@ type componentGlobalsRegistry struct {
102102
// When the `--feature-gates` flag is parsed, it would not take effect until Set() is called,
103103
// because the emulation version needs to be set before the feature gate is set.
104104
featureGatesConfig map[string][]string
105+
// featureGatesConfigFlags stores a pointer to the flag value, allowing other commands
106+
// to append to the feature gates configuration rather than overwriting it
107+
featureGatesConfigFlags *cliflag.ColonSeparatedMultimapStringString
105108
// set stores if the Set() function for the registry is already called.
106109
set bool
107110
}
@@ -120,6 +123,7 @@ func (r *componentGlobalsRegistry) Reset() {
120123
r.componentGlobals = make(map[string]*ComponentGlobals)
121124
r.emulationVersionConfig = nil
122125
r.featureGatesConfig = nil
126+
r.featureGatesConfigFlags = nil
123127
r.set = false
124128
}
125129

@@ -226,19 +230,17 @@ func (r *componentGlobalsRegistry) AddFlags(fs *pflag.FlagSet) {
226230
globals.featureGate.Close()
227231
}
228232
}
229-
if r.emulationVersionConfig != nil || r.featureGatesConfig != nil {
230-
klog.Warning("calling componentGlobalsRegistry.AddFlags more than once, the registry will be set by the latest flags")
231-
}
232-
r.emulationVersionConfig = []string{}
233-
r.featureGatesConfig = make(map[string][]string)
234233

235234
fs.StringSliceVar(&r.emulationVersionConfig, "emulated-version", r.emulationVersionConfig, ""+
236235
"The versions different components emulate their capabilities (APIs, features, ...) of.\n"+
237236
"If set, the component will emulate the behavior of this version instead of the underlying binary version.\n"+
238237
"Version format could only be major.minor, for example: '--emulated-version=wardle=1.2,kube=1.31'. Options are:\n"+strings.Join(r.unsafeVersionFlagOptions(true), "\n")+
239238
"If the component is not specified, defaults to \"kube\"")
240239

241-
fs.Var(cliflag.NewColonSeparatedMultimapStringStringAllowDefaultEmptyKey(&r.featureGatesConfig), "feature-gates", "Comma-separated list of component:key=value pairs that describe feature gates for alpha/experimental features of different components.\n"+
240+
if r.featureGatesConfigFlags == nil {
241+
r.featureGatesConfigFlags = cliflag.NewColonSeparatedMultimapStringStringAllowDefaultEmptyKey(&r.featureGatesConfig)
242+
}
243+
fs.Var(r.featureGatesConfigFlags, "feature-gates", "Comma-separated list of component:key=value pairs that describe feature gates for alpha/experimental features of different components.\n"+
242244
"If the component is not specified, defaults to \"kube\". This flag can be repeatedly invoked. For example: --feature-gates 'wardle:featureA=true,wardle:featureB=false' --feature-gates 'kube:featureC=true'"+
243245
"Options are:\n"+strings.Join(r.unsafeKnownFeatures(), "\n"))
244246
}
@@ -415,7 +417,7 @@ func (r *componentGlobalsRegistry) SetEmulationVersionMapping(fromComponent, toC
415417
return fmt.Errorf("EmulationVersion from %s to %s already exists", fromComponent, toComponent)
416418
}
417419
versionMapping[toComponent] = f
418-
klog.V(klogLevel).Infof("setting the default EmulationVersion of %s based on mapping from the default EmulationVersion of %s", fromComponent, toComponent)
420+
klog.V(klogLevel).Infof("setting the default EmulationVersion of %s based on mapping from the default EmulationVersion of %s", toComponent, fromComponent)
419421
defaultFromVersion := r.componentGlobals[fromComponent].effectiveVersion.EmulationVersion()
420422
emulationVersions, err := r.getFullEmulationVersionConfig(map[string]*version.Version{fromComponent: defaultFromVersion})
421423
if err != nil {

staging/src/k8s.io/component-base/compatibility/registry_test.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,7 @@ func TestVersionedFeatureGateFlags(t *testing.T) {
150150
func TestFlags(t *testing.T) {
151151
tests := []struct {
152152
name string
153+
setupRegistry func(r *componentGlobalsRegistry) error
153154
flags []string
154155
parseError string
155156
expectedKubeEmulationVersion string
@@ -272,14 +273,46 @@ func TestFlags(t *testing.T) {
272273
},
273274
parseError: "component not registered: test3",
274275
},
276+
{
277+
name: "feature gates config should accumulate across multiple flag sets",
278+
setupRegistry: func(r *componentGlobalsRegistry) error {
279+
fs := pflag.NewFlagSet("setupTestflag", pflag.ContinueOnError)
280+
r.AddFlags(fs)
281+
return fs.Parse([]string{"--feature-gates=test:commonC=true"})
282+
},
283+
flags: []string{
284+
"--feature-gates=test:testA=true",
285+
},
286+
expectedTestFeatureValues: map[featuregate.Feature]bool{"testA": true, "commonC": true},
287+
},
288+
{
289+
name: "feature gates config should be overridden when set multiple times one the same feature",
290+
setupRegistry: func(r *componentGlobalsRegistry) error {
291+
fs := pflag.NewFlagSet("setupTestflag", pflag.ContinueOnError)
292+
r.AddFlags(fs)
293+
return fs.Parse([]string{"--feature-gates=test:testA=false"})
294+
},
295+
flags: []string{
296+
"--feature-gates=test:testA=true",
297+
},
298+
expectedTestFeatureValues: map[featuregate.Feature]bool{"testA": true},
299+
},
275300
}
276301
for i, test := range tests {
277302
t.Run(test.name, func(t *testing.T) {
278303
fs := pflag.NewFlagSet("testflag", pflag.ContinueOnError)
279304
r := testRegistry(t)
305+
if test.setupRegistry != nil {
306+
if err := test.setupRegistry(r); err != nil {
307+
t.Fatalf("failed to setup registry: %v", err)
308+
}
309+
}
280310
r.AddFlags(fs)
281311
err := fs.Parse(test.flags)
282312
if err == nil {
313+
// AddFlags again to check whether there is no resetting on the config.
314+
fs = pflag.NewFlagSet("testflag2", pflag.ContinueOnError)
315+
r.AddFlags(fs)
283316
err = r.Set()
284317
}
285318
if test.parseError != "" {

test/integration/examples/apiserver_test.go

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ import (
4848
"k8s.io/apiserver/pkg/features"
4949
"k8s.io/apiserver/pkg/server/dynamiccertificates"
5050
genericapiserveroptions "k8s.io/apiserver/pkg/server/options"
51+
utilcompatibility "k8s.io/apiserver/pkg/util/compatibility"
5152
utilfeature "k8s.io/apiserver/pkg/util/feature"
5253
client "k8s.io/client-go/kubernetes"
5354
"k8s.io/client-go/rest"
@@ -263,6 +264,7 @@ func TestFrontProxyConfig(t *testing.T) {
263264
testFrontProxyConfig(t, false)
264265
})
265266
t.Run("WithUID", func(t *testing.T) {
267+
featuregatetesting.SetFeatureGateEmulationVersionDuringTest(t, utilfeature.DefaultFeatureGate, version.MajorMinor(1, 33))
266268
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.RemoteRequestHeaderUID, true)
267269
testFrontProxyConfig(t, true)
268270
})
@@ -277,7 +279,13 @@ func testFrontProxyConfig(t *testing.T, withUID bool) {
277279
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute)
278280
t.Cleanup(cancel)
279281

280-
var extraKASFlags []string
282+
// Set the emulation version for the kube-apiserver testserver by mapping
283+
// the wardle version to the kube version.
284+
wardleEmulationVersion := version.MustParse(wardleBinaryVersion)
285+
kubeEmulationVersion := sampleserver.WardleVersionToKubeVersion(wardleEmulationVersion)
286+
extraKASFlags := []string{
287+
fmt.Sprintf("--emulated-version=kube=%s", kubeEmulationVersion.String()),
288+
}
281289
if withUID {
282290
extraKASFlags = []string{"--requestheader-uid-headers=x-remote-uid"}
283291
}
@@ -393,19 +401,27 @@ func (f roundTripperFunc) RoundTrip(req *http.Request) (*http.Response, error) {
393401
return f(req)
394402
}
395403

396-
func testAggregatedAPIServer(t *testing.T, setWardleFeatureGate, banFlunder bool, wardleBinaryVersion, wardleEmulationVersion string) {
404+
func testAggregatedAPIServer(t *testing.T, setWardleFeatureGate, banFlunder bool, wardleBinaryVersionRaw, wardleEmulationVersionRaw string) {
397405
const testNamespace = "kube-wardle"
398406

399407
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute)
400408
t.Cleanup(cancel)
401409

402-
testKAS, wardleOptions, wardlePort := prepareAggregatedWardleAPIServer(ctx, t, testNamespace, wardleBinaryVersion, nil, false)
410+
// set the emulation version for the kube-apiserver testserver by mapping
411+
// the wardle version to the kube version.
412+
wardleEmulationVersion := version.MustParse(wardleEmulationVersionRaw)
413+
kubeEmulationVersion := sampleserver.WardleVersionToKubeVersion(wardleEmulationVersion)
414+
extraKASFlags := []string{
415+
fmt.Sprintf("--emulated-version=kube=%s", kubeEmulationVersion.String()),
416+
}
417+
418+
testKAS, wardleOptions, wardlePort := prepareAggregatedWardleAPIServer(ctx, t, testNamespace, wardleBinaryVersionRaw, extraKASFlags, false)
403419
kubeClientConfig := getKubeConfig(testKAS)
404420

405421
wardleCertDir, _ := os.MkdirTemp("", "test-integration-wardle-server")
406422
defer os.RemoveAll(wardleCertDir)
407423

408-
directWardleClientConfig := runPreparedWardleServer(ctx, t, wardleOptions, wardleCertDir, wardlePort, setWardleFeatureGate, banFlunder, wardleEmulationVersion, kubeClientConfig, false)
424+
directWardleClientConfig := runPreparedWardleServer(ctx, t, wardleOptions, wardleCertDir, wardlePort, setWardleFeatureGate, banFlunder, wardleEmulationVersionRaw, kubeClientConfig, false)
409425

410426
// now we're finally ready to test. These are what's run by default now
411427
wardleDirectClient := client.NewForConfigOrDie(directWardleClientConfig)
@@ -699,7 +715,14 @@ func prepareAggregatedWardleAPIServer(ctx context.Context, t *testing.T, namespa
699715
framework.SharedEtcd())
700716
t.Cleanup(func() { testServer.TearDownFn() })
701717

702-
componentGlobalsRegistry := testServer.ServerOpts.Options.GenericServerRunOptions.ComponentGlobalsRegistry
718+
// Create a new registry since the testServer's ComponentGlobalsRegistry is already Set(),
719+
// and wardle server would try to Set() again in the test.
720+
componentGlobalsRegistry := basecompatibility.NewComponentGlobalsRegistry()
721+
_, _ = componentGlobalsRegistry.ComponentGlobalsOrRegister(
722+
basecompatibility.DefaultKubeComponent,
723+
utilcompatibility.DefaultKubeEffectiveVersionForTest(),
724+
utilfeature.DefaultFeatureGate.DeepCopy(),
725+
)
703726
_, _ = componentGlobalsRegistry.ComponentGlobalsOrRegister(
704727
apiserver.WardleComponentName, basecompatibility.NewEffectiveVersionFromString(wardleBinaryVersion, "", ""),
705728
featuregate.NewVersionedFeatureGate(version.MustParse(wardleBinaryVersion)))

0 commit comments

Comments
 (0)