From 463f95f85639fc1db8eb59c4bd11d7949296ff06 Mon Sep 17 00:00:00 2001 From: zhihao jian Date: Fri, 25 Jul 2025 14:38:20 +0800 Subject: [PATCH 1/2] added context-based warning suppression to SetEmulationVersion to eliminate test noise Signed-off-by: zhihao jian optimize SetEmulationVersionWithContext logic --- .../featuregate/feature_gate.go | 54 ++++++- .../featuregate/feature_gate_test.go | 139 +++++++++++++++++- .../featuregate/testing/feature_gate.go | 10 +- 3 files changed, 195 insertions(+), 8 deletions(-) diff --git a/staging/src/k8s.io/component-base/featuregate/feature_gate.go b/staging/src/k8s.io/component-base/featuregate/feature_gate.go index 30e430d56e9dd..a8fc2e9644dd3 100644 --- a/staging/src/k8s.io/component-base/featuregate/feature_gate.go +++ b/staging/src/k8s.io/component-base/featuregate/feature_gate.go @@ -165,6 +165,13 @@ type MutableVersionedFeatureGate interface { // Otherwise, the emulationVersion will be the same as the binary version. // If set, the feature defaults and availability will be as if the binary is at the emulated version. SetEmulationVersion(emulationVersion *version.Version) error + // SetEmulationVersionWithContext is like SetEmulationVersion but accepts a context for controlling behavior. + // When features have been queried and would change value due to the emulation version change, + // warnings will be logged. If a test logger is provided in the context via WithTestLoggerContext, + // warnings will be redirected to the test output instead of the global klog. + // If features would change and no test logger is provided, an error will be returned to prevent + // unexpected behavior in production environments. + SetEmulationVersionWithContext(ctx context.Context, emulationVersion *version.Version) error // GetAll returns a copy of the map of known feature names to versioned feature specs. GetAllVersioned() map[Feature]VersionedSpecs // AddVersioned adds versioned feature specs to the featureGate. @@ -543,6 +550,10 @@ func (f *featureGate) GetAllVersioned() map[Feature]VersionedSpecs { } func (f *featureGate) SetEmulationVersion(emulationVersion *version.Version) error { + return f.SetEmulationVersionWithContext(context.Background(), emulationVersion) +} + +func (f *featureGate) SetEmulationVersionWithContext(ctx context.Context, emulationVersion *version.Version) error { if emulationVersion.EqualTo(f.EmulationVersion()) { return nil } @@ -561,14 +572,29 @@ func (f *featureGate) SetEmulationVersion(emulationVersion *version.Version) err queriedFeatures := f.queriedFeatures.Load().(sets.Set[Feature]) known := f.known.Load().(map[Feature]VersionedSpecs) + changingFeatures := []string{} + contextLogger := getContextLogger(ctx) + for feature := range queriedFeatures { newVal := featureEnabled(feature, enabled, known, emulationVersion) oldVal := featureEnabled(feature, f.enabled.Load().(map[Feature]bool), known, f.EmulationVersion()) if newVal != oldVal { - klog.Warningf("SetEmulationVersion will change already queried feature:%s from %v to %v", feature, oldVal, newVal) + changingFeatures = append(changingFeatures, fmt.Sprintf("%s (from %v to %v)", feature, oldVal, newVal)) + warningMsg := fmt.Sprintf("SetEmulationVersion will change already queried feature:%s from %v to %v", feature, oldVal, newVal) + // Try to log to context logger first, fallback to global klog + if contextLogger != nil { + contextLogger.Logf("WARNING: %s", warningMsg) + } else { + klog.Warningf("%s", warningMsg) + } } } + // If features would change and no context logger is provided, return an error + if len(changingFeatures) > 0 && contextLogger == nil { + return fmt.Errorf("SetEmulationVersion would change already queried features: %s", strings.Join(changingFeatures, ", ")) + } + if len(errs) == 0 { // Persist changes f.enabled.Store(enabled) @@ -669,6 +695,32 @@ func (f *featureGate) AddMetrics() { } } +// testLoggerKey is used to store test logger in context +type testLoggerKey struct{} + +// WithTestLoggerContext returns a context with test logger for redirecting warnings to test output +func WithTestLoggerContext(ctx context.Context, logger interface { + Logf(format string, args ...interface{}) +}) context.Context { + return context.WithValue(ctx, testLoggerKey{}, logger) +} + +// getContextLogger retrieves logger from context if available +func getContextLogger(ctx context.Context) interface { + Logf(format string, args ...interface{}) +} { + if ctx == nil { + return nil + } + logger, ok := ctx.Value(testLoggerKey{}).(interface { + Logf(format string, args ...interface{}) + }) + if ok { + return logger + } + return nil +} + // KnownFeatures returns a slice of strings describing the FeatureGate's known features. // preAlpha, Deprecated and GA features are hidden from the list. func (f *featureGate) KnownFeatures() []string { diff --git a/staging/src/k8s.io/component-base/featuregate/feature_gate_test.go b/staging/src/k8s.io/component-base/featuregate/feature_gate_test.go index ba626435069f4..2e4db405f7910 100644 --- a/staging/src/k8s.io/component-base/featuregate/feature_gate_test.go +++ b/staging/src/k8s.io/component-base/featuregate/feature_gate_test.go @@ -17,6 +17,7 @@ limitations under the License. package featuregate import ( + "context" "fmt" "reflect" "sort" @@ -1330,14 +1331,14 @@ func TestVersionedFeatureGateOverrideDefault(t *testing.T) { if !f.Enabled("TestFeature2") { t.Error("expected TestFeature2 to have effective default of true") } - require.NoError(t, f.SetEmulationVersion(version.MustParse("1.29"))) + require.NoError(t, f.SetEmulationVersionWithContext(WithTestLoggerContext(context.Background(), t), version.MustParse("1.29"))) if !f.Enabled("TestFeature1") { t.Error("expected TestFeature1 to have effective default of true") } if f.Enabled("TestFeature2") { t.Error("expected TestFeature2 to have effective default of false") } - require.NoError(t, f.SetEmulationVersion(version.MustParse("1.26"))) + require.NoError(t, f.SetEmulationVersionWithContext(WithTestLoggerContext(context.Background(), t), version.MustParse("1.26"))) if f.Enabled("TestFeature1") { t.Error("expected TestFeature1 to have effective default of false") } @@ -1383,11 +1384,11 @@ func TestVersionedFeatureGateOverrideDefault(t *testing.T) { assert.True(t, f.Enabled("TestFeature")) assert.False(t, fcopy.Enabled("TestFeature")) - require.NoError(t, f.SetEmulationVersion(version.MustParse("1.29"))) + require.NoError(t, f.SetEmulationVersionWithContext(WithTestLoggerContext(context.Background(), t), version.MustParse("1.29"))) assert.False(t, f.Enabled("TestFeature")) assert.False(t, fcopy.Enabled("TestFeature")) - require.NoError(t, fcopy.SetEmulationVersion(version.MustParse("1.29"))) + require.NoError(t, fcopy.SetEmulationVersionWithContext(WithTestLoggerContext(context.Background(), t), version.MustParse("1.29"))) assert.False(t, f.Enabled("TestFeature")) assert.True(t, fcopy.Enabled("TestFeature")) }) @@ -1737,7 +1738,7 @@ func TestResetFeatureValueToDefault(t *testing.T) { assert.True(t, f.Enabled("TestAlpha")) assert.True(t, f.Enabled("TestBeta")) - require.NoError(t, f.SetEmulationVersion(version.MustParse("1.28"))) + require.NoError(t, f.SetEmulationVersionWithContext(WithTestLoggerContext(context.Background(), t), version.MustParse("1.28"))) assert.False(t, f.Enabled("AllAlpha")) assert.False(t, f.Enabled("AllBeta")) assert.False(t, f.Enabled("TestAlpha")) @@ -1905,3 +1906,131 @@ func TestAddVersioned(t *testing.T) { }) } } + +// TestSetEmulationVersionWithContext tests various scenarios for SetEmulationVersionWithContext +func TestSetEmulationVersionWithContext(t *testing.T) { + tests := []struct { + name string + features map[Feature]VersionedSpecs + queriedFeatures []string + targetVersion *version.Version + context context.Context + expectError bool + errorContains []string + expectNoError bool + }{ + { + name: "warning redirection with test logger", + features: map[Feature]VersionedSpecs{ + "TestFeature": { + {Version: version.MustParse("1.28"), Default: false, PreRelease: Alpha}, + {Version: version.MustParse("1.29"), Default: true, PreRelease: Beta}, + }, + }, + queriedFeatures: []string{"TestFeature"}, + targetVersion: version.MustParse("1.28"), + context: WithTestLoggerContext(context.Background(), t), + expectNoError: true, + }, + { + name: "error on feature change without test logger", + features: map[Feature]VersionedSpecs{ + "TestFeature": { + {Version: version.MustParse("1.28"), Default: false, PreRelease: Alpha}, + {Version: version.MustParse("1.29"), Default: true, PreRelease: Beta}, + }, + }, + queriedFeatures: []string{"TestFeature"}, + targetVersion: version.MustParse("1.28"), + context: context.Background(), + expectError: true, + errorContains: []string{"SetEmulationVersion would change already queried features"}, + }, + { + name: "multiple features changing", + features: map[Feature]VersionedSpecs{ + "TestFeature1": { + {Version: version.MustParse("1.28"), Default: false, PreRelease: Alpha}, + {Version: version.MustParse("1.29"), Default: true, PreRelease: Beta}, + }, + "TestFeature2": { + {Version: version.MustParse("1.28"), Default: true, PreRelease: Alpha}, + {Version: version.MustParse("1.29"), Default: false, PreRelease: Beta}, + }, + }, + queriedFeatures: []string{"TestFeature1", "TestFeature2"}, + targetVersion: version.MustParse("1.28"), + context: context.Background(), + expectError: true, + errorContains: []string{"TestFeature1", "TestFeature2"}, + }, + { + name: "no change with regular context", + features: map[Feature]VersionedSpecs{ + "TestFeature": { + {Version: version.MustParse("1.28"), Default: true, PreRelease: Alpha}, + {Version: version.MustParse("1.29"), Default: true, PreRelease: Beta}, + }, + }, + queriedFeatures: []string{"TestFeature"}, + targetVersion: version.MustParse("1.28"), + context: context.Background(), + expectNoError: true, + }, + { + name: "no change with test logger context", + features: map[Feature]VersionedSpecs{ + "TestFeature": { + {Version: version.MustParse("1.28"), Default: true, PreRelease: Alpha}, + {Version: version.MustParse("1.29"), Default: true, PreRelease: Beta}, + }, + }, + queriedFeatures: []string{"TestFeature"}, + targetVersion: version.MustParse("1.28"), + context: WithTestLoggerContext(context.Background(), t), + expectNoError: true, + }, + { + name: "nil context with feature change", + features: map[Feature]VersionedSpecs{ + "TestFeature": { + {Version: version.MustParse("1.28"), Default: false, PreRelease: Alpha}, + {Version: version.MustParse("1.29"), Default: true, PreRelease: Beta}, + }, + }, + queriedFeatures: []string{"TestFeature"}, + targetVersion: version.MustParse("1.28"), + context: nil, + expectError: true, + errorContains: []string{"SetEmulationVersion would change already queried features"}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // Create feature gate + f := NewVersionedFeatureGate(version.MustParse("1.29")) + + // Add features + err := f.AddVersioned(tt.features) + require.NoError(t, err) + + // Query features to mark them as queried + for _, featureName := range tt.queriedFeatures { + _ = f.Enabled(Feature(featureName)) + } + + // Test SetEmulationVersionWithContext + err = f.SetEmulationVersionWithContext(tt.context, tt.targetVersion) + + if tt.expectError { + require.Error(t, err) + for _, expectedText := range tt.errorContains { + require.Contains(t, err.Error(), expectedText) + } + } else if tt.expectNoError { + require.NoError(t, err) + } + }) + } +} diff --git a/staging/src/k8s.io/component-base/featuregate/testing/feature_gate.go b/staging/src/k8s.io/component-base/featuregate/testing/feature_gate.go index 1d7fc46768ee8..3ca9ea6cfef76 100644 --- a/staging/src/k8s.io/component-base/featuregate/testing/feature_gate.go +++ b/staging/src/k8s.io/component-base/featuregate/testing/feature_gate.go @@ -17,6 +17,7 @@ limitations under the License. package testing import ( + "context" "fmt" "strings" "sync" @@ -124,13 +125,17 @@ func SetFeatureGateEmulationVersionDuringTest(tb TB, gate featuregate.FeatureGat tb.Helper() detectParallelOverrideCleanup := detectParallelOverrideEmulationVersion(tb, ver) originalEmuVer := gate.(featuregate.MutableVersionedFeatureGate).EmulationVersion() - if err := gate.(featuregate.MutableVersionedFeatureGate).SetEmulationVersion(ver); err != nil { + // Use context with test logger to redirect warnings to test output + ctx := featuregate.WithTestLoggerContext(context.Background(), tb) + if err := gate.(featuregate.MutableVersionedFeatureGate).SetEmulationVersionWithContext(ctx, ver); err != nil { tb.Fatalf("failed to set emulation version to %s during test: %v", ver.String(), err) } tb.Cleanup(func() { tb.Helper() detectParallelOverrideCleanup() - if err := gate.(featuregate.MutableVersionedFeatureGate).SetEmulationVersion(originalEmuVer); err != nil { + // Use context with test logger to redirect warnings to test output during cleanup + ctx := featuregate.WithTestLoggerContext(context.Background(), tb) + if err := gate.(featuregate.MutableVersionedFeatureGate).SetEmulationVersionWithContext(ctx, originalEmuVer); err != nil { tb.Fatalf("failed to restore emulation version to %s during test", originalEmuVer.String()) } }) @@ -196,5 +201,6 @@ type TB interface { Fatal(args ...any) Fatalf(format string, args ...any) Helper() + Logf(format string, args ...interface{}) Name() string } From b90220fa882f8c9962bfa3e6b9a4b81fcd708ff1 Mon Sep 17 00:00:00 2001 From: zhihao jian Date: Sun, 10 Aug 2025 00:27:33 +0800 Subject: [PATCH 2/2] use setEmulationVersionForTest Signed-off-by: zhihao jian fix fix lint --- .../featuregate/feature_gate.go | 23 +-- .../featuregate/feature_gate_test.go | 139 +----------------- .../featuregate/testing/feature_gate.go | 27 +++- 3 files changed, 34 insertions(+), 155 deletions(-) diff --git a/staging/src/k8s.io/component-base/featuregate/feature_gate.go b/staging/src/k8s.io/component-base/featuregate/feature_gate.go index a8fc2e9644dd3..447bd37ecdfac 100644 --- a/staging/src/k8s.io/component-base/featuregate/feature_gate.go +++ b/staging/src/k8s.io/component-base/featuregate/feature_gate.go @@ -165,13 +165,7 @@ type MutableVersionedFeatureGate interface { // Otherwise, the emulationVersion will be the same as the binary version. // If set, the feature defaults and availability will be as if the binary is at the emulated version. SetEmulationVersion(emulationVersion *version.Version) error - // SetEmulationVersionWithContext is like SetEmulationVersion but accepts a context for controlling behavior. - // When features have been queried and would change value due to the emulation version change, - // warnings will be logged. If a test logger is provided in the context via WithTestLoggerContext, - // warnings will be redirected to the test output instead of the global klog. - // If features would change and no test logger is provided, an error will be returned to prevent - // unexpected behavior in production environments. - SetEmulationVersionWithContext(ctx context.Context, emulationVersion *version.Version) error + // GetAll returns a copy of the map of known feature names to versioned feature specs. GetAllVersioned() map[Feature]VersionedSpecs // AddVersioned adds versioned feature specs to the featureGate. @@ -550,10 +544,16 @@ func (f *featureGate) GetAllVersioned() map[Feature]VersionedSpecs { } func (f *featureGate) SetEmulationVersion(emulationVersion *version.Version) error { - return f.SetEmulationVersionWithContext(context.Background(), emulationVersion) + return f.setEmulationVersionWithContext(context.Background(), emulationVersion) } +// SetEmulationVersionWithContext allows passing a context for test logger support. +// This is primarily intended for testing scenarios. func (f *featureGate) SetEmulationVersionWithContext(ctx context.Context, emulationVersion *version.Version) error { + return f.setEmulationVersionWithContext(ctx, emulationVersion) +} + +func (f *featureGate) setEmulationVersionWithContext(ctx context.Context, emulationVersion *version.Version) error { if emulationVersion.EqualTo(f.EmulationVersion()) { return nil } @@ -572,14 +572,12 @@ func (f *featureGate) SetEmulationVersionWithContext(ctx context.Context, emulat queriedFeatures := f.queriedFeatures.Load().(sets.Set[Feature]) known := f.known.Load().(map[Feature]VersionedSpecs) - changingFeatures := []string{} contextLogger := getContextLogger(ctx) for feature := range queriedFeatures { newVal := featureEnabled(feature, enabled, known, emulationVersion) oldVal := featureEnabled(feature, f.enabled.Load().(map[Feature]bool), known, f.EmulationVersion()) if newVal != oldVal { - changingFeatures = append(changingFeatures, fmt.Sprintf("%s (from %v to %v)", feature, oldVal, newVal)) warningMsg := fmt.Sprintf("SetEmulationVersion will change already queried feature:%s from %v to %v", feature, oldVal, newVal) // Try to log to context logger first, fallback to global klog if contextLogger != nil { @@ -590,11 +588,6 @@ func (f *featureGate) SetEmulationVersionWithContext(ctx context.Context, emulat } } - // If features would change and no context logger is provided, return an error - if len(changingFeatures) > 0 && contextLogger == nil { - return fmt.Errorf("SetEmulationVersion would change already queried features: %s", strings.Join(changingFeatures, ", ")) - } - if len(errs) == 0 { // Persist changes f.enabled.Store(enabled) diff --git a/staging/src/k8s.io/component-base/featuregate/feature_gate_test.go b/staging/src/k8s.io/component-base/featuregate/feature_gate_test.go index 2e4db405f7910..ba626435069f4 100644 --- a/staging/src/k8s.io/component-base/featuregate/feature_gate_test.go +++ b/staging/src/k8s.io/component-base/featuregate/feature_gate_test.go @@ -17,7 +17,6 @@ limitations under the License. package featuregate import ( - "context" "fmt" "reflect" "sort" @@ -1331,14 +1330,14 @@ func TestVersionedFeatureGateOverrideDefault(t *testing.T) { if !f.Enabled("TestFeature2") { t.Error("expected TestFeature2 to have effective default of true") } - require.NoError(t, f.SetEmulationVersionWithContext(WithTestLoggerContext(context.Background(), t), version.MustParse("1.29"))) + require.NoError(t, f.SetEmulationVersion(version.MustParse("1.29"))) if !f.Enabled("TestFeature1") { t.Error("expected TestFeature1 to have effective default of true") } if f.Enabled("TestFeature2") { t.Error("expected TestFeature2 to have effective default of false") } - require.NoError(t, f.SetEmulationVersionWithContext(WithTestLoggerContext(context.Background(), t), version.MustParse("1.26"))) + require.NoError(t, f.SetEmulationVersion(version.MustParse("1.26"))) if f.Enabled("TestFeature1") { t.Error("expected TestFeature1 to have effective default of false") } @@ -1384,11 +1383,11 @@ func TestVersionedFeatureGateOverrideDefault(t *testing.T) { assert.True(t, f.Enabled("TestFeature")) assert.False(t, fcopy.Enabled("TestFeature")) - require.NoError(t, f.SetEmulationVersionWithContext(WithTestLoggerContext(context.Background(), t), version.MustParse("1.29"))) + require.NoError(t, f.SetEmulationVersion(version.MustParse("1.29"))) assert.False(t, f.Enabled("TestFeature")) assert.False(t, fcopy.Enabled("TestFeature")) - require.NoError(t, fcopy.SetEmulationVersionWithContext(WithTestLoggerContext(context.Background(), t), version.MustParse("1.29"))) + require.NoError(t, fcopy.SetEmulationVersion(version.MustParse("1.29"))) assert.False(t, f.Enabled("TestFeature")) assert.True(t, fcopy.Enabled("TestFeature")) }) @@ -1738,7 +1737,7 @@ func TestResetFeatureValueToDefault(t *testing.T) { assert.True(t, f.Enabled("TestAlpha")) assert.True(t, f.Enabled("TestBeta")) - require.NoError(t, f.SetEmulationVersionWithContext(WithTestLoggerContext(context.Background(), t), version.MustParse("1.28"))) + require.NoError(t, f.SetEmulationVersion(version.MustParse("1.28"))) assert.False(t, f.Enabled("AllAlpha")) assert.False(t, f.Enabled("AllBeta")) assert.False(t, f.Enabled("TestAlpha")) @@ -1906,131 +1905,3 @@ func TestAddVersioned(t *testing.T) { }) } } - -// TestSetEmulationVersionWithContext tests various scenarios for SetEmulationVersionWithContext -func TestSetEmulationVersionWithContext(t *testing.T) { - tests := []struct { - name string - features map[Feature]VersionedSpecs - queriedFeatures []string - targetVersion *version.Version - context context.Context - expectError bool - errorContains []string - expectNoError bool - }{ - { - name: "warning redirection with test logger", - features: map[Feature]VersionedSpecs{ - "TestFeature": { - {Version: version.MustParse("1.28"), Default: false, PreRelease: Alpha}, - {Version: version.MustParse("1.29"), Default: true, PreRelease: Beta}, - }, - }, - queriedFeatures: []string{"TestFeature"}, - targetVersion: version.MustParse("1.28"), - context: WithTestLoggerContext(context.Background(), t), - expectNoError: true, - }, - { - name: "error on feature change without test logger", - features: map[Feature]VersionedSpecs{ - "TestFeature": { - {Version: version.MustParse("1.28"), Default: false, PreRelease: Alpha}, - {Version: version.MustParse("1.29"), Default: true, PreRelease: Beta}, - }, - }, - queriedFeatures: []string{"TestFeature"}, - targetVersion: version.MustParse("1.28"), - context: context.Background(), - expectError: true, - errorContains: []string{"SetEmulationVersion would change already queried features"}, - }, - { - name: "multiple features changing", - features: map[Feature]VersionedSpecs{ - "TestFeature1": { - {Version: version.MustParse("1.28"), Default: false, PreRelease: Alpha}, - {Version: version.MustParse("1.29"), Default: true, PreRelease: Beta}, - }, - "TestFeature2": { - {Version: version.MustParse("1.28"), Default: true, PreRelease: Alpha}, - {Version: version.MustParse("1.29"), Default: false, PreRelease: Beta}, - }, - }, - queriedFeatures: []string{"TestFeature1", "TestFeature2"}, - targetVersion: version.MustParse("1.28"), - context: context.Background(), - expectError: true, - errorContains: []string{"TestFeature1", "TestFeature2"}, - }, - { - name: "no change with regular context", - features: map[Feature]VersionedSpecs{ - "TestFeature": { - {Version: version.MustParse("1.28"), Default: true, PreRelease: Alpha}, - {Version: version.MustParse("1.29"), Default: true, PreRelease: Beta}, - }, - }, - queriedFeatures: []string{"TestFeature"}, - targetVersion: version.MustParse("1.28"), - context: context.Background(), - expectNoError: true, - }, - { - name: "no change with test logger context", - features: map[Feature]VersionedSpecs{ - "TestFeature": { - {Version: version.MustParse("1.28"), Default: true, PreRelease: Alpha}, - {Version: version.MustParse("1.29"), Default: true, PreRelease: Beta}, - }, - }, - queriedFeatures: []string{"TestFeature"}, - targetVersion: version.MustParse("1.28"), - context: WithTestLoggerContext(context.Background(), t), - expectNoError: true, - }, - { - name: "nil context with feature change", - features: map[Feature]VersionedSpecs{ - "TestFeature": { - {Version: version.MustParse("1.28"), Default: false, PreRelease: Alpha}, - {Version: version.MustParse("1.29"), Default: true, PreRelease: Beta}, - }, - }, - queriedFeatures: []string{"TestFeature"}, - targetVersion: version.MustParse("1.28"), - context: nil, - expectError: true, - errorContains: []string{"SetEmulationVersion would change already queried features"}, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - // Create feature gate - f := NewVersionedFeatureGate(version.MustParse("1.29")) - - // Add features - err := f.AddVersioned(tt.features) - require.NoError(t, err) - - // Query features to mark them as queried - for _, featureName := range tt.queriedFeatures { - _ = f.Enabled(Feature(featureName)) - } - - // Test SetEmulationVersionWithContext - err = f.SetEmulationVersionWithContext(tt.context, tt.targetVersion) - - if tt.expectError { - require.Error(t, err) - for _, expectedText := range tt.errorContains { - require.Contains(t, err.Error(), expectedText) - } - } else if tt.expectNoError { - require.NoError(t, err) - } - }) - } -} diff --git a/staging/src/k8s.io/component-base/featuregate/testing/feature_gate.go b/staging/src/k8s.io/component-base/featuregate/testing/feature_gate.go index 3ca9ea6cfef76..67e8fa4a309d7 100644 --- a/staging/src/k8s.io/component-base/featuregate/testing/feature_gate.go +++ b/staging/src/k8s.io/component-base/featuregate/testing/feature_gate.go @@ -125,22 +125,37 @@ func SetFeatureGateEmulationVersionDuringTest(tb TB, gate featuregate.FeatureGat tb.Helper() detectParallelOverrideCleanup := detectParallelOverrideEmulationVersion(tb, ver) originalEmuVer := gate.(featuregate.MutableVersionedFeatureGate).EmulationVersion() - // Use context with test logger to redirect warnings to test output - ctx := featuregate.WithTestLoggerContext(context.Background(), tb) - if err := gate.(featuregate.MutableVersionedFeatureGate).SetEmulationVersionWithContext(ctx, ver); err != nil { + + if err := setEmulationVersionForTest(gate.(featuregate.MutableVersionedFeatureGate), ver, tb); err != nil { tb.Fatalf("failed to set emulation version to %s during test: %v", ver.String(), err) } tb.Cleanup(func() { tb.Helper() detectParallelOverrideCleanup() - // Use context with test logger to redirect warnings to test output during cleanup - ctx := featuregate.WithTestLoggerContext(context.Background(), tb) - if err := gate.(featuregate.MutableVersionedFeatureGate).SetEmulationVersionWithContext(ctx, originalEmuVer); err != nil { + if err := setEmulationVersionForTest(gate.(featuregate.MutableVersionedFeatureGate), originalEmuVer, tb); err != nil { tb.Fatalf("failed to restore emulation version to %s during test", originalEmuVer.String()) } }) } +// setEmulationVersionForTest sets emulation version with test-friendly warning handling. +// This uses context to redirect warnings to test output. +func setEmulationVersionForTest(gate featuregate.MutableVersionedFeatureGate, ver *version.Version, tb TB) error { + // Check if this gate supports context-aware emulation version setting + type contextAwareGate interface { + SetEmulationVersionWithContext(ctx context.Context, emulationVersion *version.Version) error + } + + if contextGate, ok := gate.(contextAwareGate); ok { + // Use context with test logger to redirect warnings to test output + ctx := featuregate.WithTestLoggerContext(context.Background(), tb) + return contextGate.SetEmulationVersionWithContext(ctx, ver) + } + + // Fallback to regular method for implementations that don't support context + return gate.SetEmulationVersion(ver) +} + func detectParallelOverride(tb TB, f featuregate.Feature) func() { tb.Helper() overrideLock.Lock()