Repository: kubernetes/kubernetes (language: go)

Issue:
Migrate existing features to versioned feature gate
### What would you like to be added?

part of https://github.com/kubernetes/enhancements/issues/4330, all existing features should migrate to the new `map[Feature]VersionedSpecs` format.

Currently, features are added to the feature gate with the format like
```
var defaultKubernetesFeatureGates = map[featuregate.Feature]featuregate.FeatureSpec{
	FeatureA: {Default: true, PreRelease: featuregate.Beta},
}
```
In the new versioned format, they should keep track of all the lifecycle changes of the feature, like: 
```
var defaultVersionedKubernetesFeatureGates = map[featuregate.Feature]featuregate.VersionedSpecs{
	FeatureA: {
		{Version: version.MustParse("1.30"), Default: false, PreRelease: featuregate.Alpha},
                {Version: version.MustParse("1.32"), Default: true, PreRelease: featuregate.Beta},
	},
}
```

### Why is this needed?

to complete https://github.com/kubernetes/enhancements/issues/4330


Discussion / hints:
/sig api-machinery
/assign @siyuanfoundation 
/triage accepted
/sig architecture
/cc @haitch @Jefftree

Fix patch:
diff --git a/staging/src/k8s.io/component-base/logs/api/v1/kube_features.go b/staging/src/k8s.io/component-base/logs/api/v1/kube_features.go
index 7dd4a49543324..fe23f579314cc 100644
--- a/staging/src/k8s.io/component-base/logs/api/v1/kube_features.go
+++ b/staging/src/k8s.io/component-base/logs/api/v1/kube_features.go
@@ -17,6 +17,7 @@ limitations under the License.
 package v1
 
 import (
+	"k8s.io/apimachinery/pkg/util/version"
 	"k8s.io/component-base/featuregate"
 )
 
@@ -31,17 +32,16 @@ const (
 	// used by a call chain.
 	ContextualLogging featuregate.Feature = "ContextualLogging"
 
-	// contextualLoggingDefault is now true because the feature reached beta
-	// and performance comparisons showed no relevant degradation when
-	// enabling it.
-	contextualLoggingDefault = true
-
 	// Allow fine-tuning of experimental, alpha-quality logging options.
 	//
 	// Per https://groups.google.com/g/kubernetes-sig-architecture/c/Nxsc7pfe5rw/m/vF2djJh0BAAJ
 	// we want to avoid a proliferation of feature gates. This feature gate:
 	// - will guard *a group* of logging options whose quality level is alpha.
 	// - will never graduate to beta or stable.
+	//
+	// IMPORTANT: Unlike typical feature gates, LoggingAlphaOptions is NOT affected by
+	// emulation version changes. Its behavior remains constant regardless of the
+	// emulation version being used.
 	LoggingAlphaOptions featuregate.Feature = "LoggingAlphaOptions"
 
 	// Allow fine-tuning of experimental, beta-quality logging options.
@@ -51,22 +51,32 @@ const (
 	// - will guard *a group* of logging options whose quality level is beta.
 	// - is thus *introduced* as beta
 	// - will never graduate to stable.
+	//
+	// IMPORTANT: Unlike typical feature gates, LoggingBetaOptions is NOT affected by
+	// emulation version changes. Its behavior remains constant regardless of the
+	// emulation version being used.
 	LoggingBetaOptions featuregate.Feature = "LoggingBetaOptions"
 
 	// Stable logging options. Always enabled.
 	LoggingStableOptions featuregate.Feature = "LoggingStableOptions"
 )
 
-func featureGates() map[featuregate.Feature]featuregate.FeatureSpec {
-	return map[featuregate.Feature]featuregate.FeatureSpec{
-		ContextualLogging: {Default: contextualLoggingDefault, PreRelease: featuregate.Beta},
-
-		LoggingAlphaOptions: {Default: false, PreRelease: featuregate.Alpha},
-		LoggingBetaOptions:  {Default: true, PreRelease: featuregate.Beta},
+func featureGates() map[featuregate.Feature]featuregate.VersionedSpecs {
+	return map[featuregate.Feature]featuregate.VersionedSpecs{
+		ContextualLogging: {
+			{Version: version.MustParse("1.24"), Default: false, PreRelease: featuregate.Alpha},
+			{Version: version.MustParse("1.30"), Default: true, PreRelease: featuregate.Beta},
+		},
+		LoggingAlphaOptions: {
+			{Version: version.MustParse("1.24"), Default: false, PreRelease: featuregate.Alpha},
+		},
+		LoggingBetaOptions: {
+			{Version: version.MustParse("1.24"), Default: true, PreRelease: featuregate.Beta},
+		},
 	}
 }
 
 // AddFeatureGates adds all feature gates used by this package.
-func AddFeatureGates(mutableFeatureGate featuregate.MutableFeatureGate) error {
-	return mutableFeatureGate.Add(featureGates()) //nolint:forbidigo // The logging feature gates are by design unversioned (perpetual alpha/beta).
+func AddFeatureGates(mutableFeatureGate featuregate.MutableVersionedFeatureGate) error {
+	return mutableFeatureGate.AddVersioned(featureGates())
 }
diff --git a/staging/src/k8s.io/component-base/logs/api/v1/options.go b/staging/src/k8s.io/component-base/logs/api/v1/options.go
index c328e6e1e2245..95c0a2cba986b 100644
--- a/staging/src/k8s.io/component-base/logs/api/v1/options.go
+++ b/staging/src/k8s.io/component-base/logs/api/v1/options.go
@@ -153,7 +153,7 @@ func Validate(c *LoggingConfiguration, featureGate featuregate.FeatureGate, fldP
 		errs = append(errs, field.Invalid(fldPath.Child("format"), c.Format, "Unsupported log format"))
 	} else if format != nil {
 		if format.feature != LoggingStableOptions {
-			enabled := featureGates()[format.feature].Default
+			enabled := featureGates()[format.feature][len(featureGates()[format.feature])-1].Default
 			if featureGate != nil {
 				enabled = featureGate.Enabled(format.feature)
 			}
@@ -228,7 +228,7 @@ func apply(c *LoggingConfiguration, options *LoggingOptions, featureGate feature
 	p := &parameters{
 		C:                        c,
 		Options:                  options,
-		ContextualLoggingEnabled: contextualLoggingDefault,
+		ContextualLoggingEnabled: true,
 	}
 	if featureGate != nil {
 		p.ContextualLoggingEnabled = featureGate.Enabled(ContextualLogging)


Test patch:
diff --git a/test/featuregates_linter/test_data/unversioned_feature_list.yaml b/test/featuregates_linter/test_data/unversioned_feature_list.yaml
index 0044ff28514b3..fe51488c7066f 100644
--- a/test/featuregates_linter/test_data/unversioned_feature_list.yaml
+++ b/test/featuregates_linter/test_data/unversioned_feature_list.yaml
@@ -1,18 +1,1 @@
-- name: ContextualLogging
-  versionedSpecs:
-  - default: true
-    lockToDefault: false
-    preRelease: Beta
-    version: ""
-- name: LoggingAlphaOptions
-  versionedSpecs:
-  - default: false
-    lockToDefault: false
-    preRelease: Alpha
-    version: ""
-- name: LoggingBetaOptions
-  versionedSpecs:
-  - default: true
-    lockToDefault: false
-    preRelease: Beta
-    version: ""
+[]
diff --git a/test/featuregates_linter/test_data/versioned_feature_list.yaml b/test/featuregates_linter/test_data/versioned_feature_list.yaml
index deb494360aab3..5c05d471b8a90 100644
--- a/test/featuregates_linter/test_data/versioned_feature_list.yaml
+++ b/test/featuregates_linter/test_data/versioned_feature_list.yaml
@@ -258,6 +258,16 @@
     lockToDefault: false
     preRelease: Beta
     version: "1.30"
+- name: ContextualLogging
+  versionedSpecs:
+  - default: false
+    lockToDefault: false
+    preRelease: Alpha
+    version: "1.24"
+  - default: true
+    lockToDefault: false
+    preRelease: Beta
+    version: "1.30"
 - name: CoordinatedLeaderElection
   versionedSpecs:
   - default: false
@@ -732,6 +742,18 @@
     lockToDefault: true
     preRelease: GA
     version: "1.31"
+- name: LoggingAlphaOptions
+  versionedSpecs:
+  - default: false
+    lockToDefault: false
+    preRelease: Alpha
+    version: "1.24"
+- name: LoggingBetaOptions
+  versionedSpecs:
+  - default: true
+    lockToDefault: false
+    preRelease: Beta
+    version: "1.24"
 - name: MatchLabelKeysInPodAffinity
   versionedSpecs:
   - default: false