mirror of
				https://github.com/optim-enterprises-bv/kubernetes.git
				synced 2025-10-31 18:28:13 +00:00 
			
		
		
		
	Refactor compatibility version code
Replace DefaultComponentGlobalsRegistry with new instance of componentGlobalsRegistry in test api server. Signed-off-by: Siyuan Zhang <sizhang@google.com> move kube effective version validation out of component base. Signed-off-by: Siyuan Zhang <sizhang@google.com> move DefaultComponentGlobalsRegistry out of component base. Signed-off-by: Siyuan Zhang <sizhang@google.com> move ComponentGlobalsRegistry out of featuregate pkg. Signed-off-by: Siyuan Zhang <sizhang@google.com> remove usage of DefaultComponentGlobalsRegistry in test files. Signed-off-by: Siyuan Zhang <sizhang@google.com> change non-test DefaultKubeEffectiveVersion to use DefaultBuildEffectiveVersion. Signed-off-by: Siyuan Zhang <sizhang@google.com> Restore useDefaultBuildBinaryVersion in effective version. Signed-off-by: Siyuan Zhang <sizhang@google.com> rename DefaultKubeEffectiveVersion to DefaultKubeEffectiveVersionForTest. Signed-off-by: Siyuan Zhang <sizhang@google.com> pass options.ComponentGlobalsRegistry into config for controller manager and scheduler. Signed-off-by: Siyuan Zhang <sizhang@google.com> Pass apiserver effective version to DefaultResourceEncodingConfig. Signed-off-by: Siyuan Zhang <sizhang@google.com> change statusz registry to take effective version from the components. Signed-off-by: Siyuan Zhang <sizhang@google.com> Address review comments Signed-off-by: Siyuan Zhang <sizhang@google.com> update vendor Signed-off-by: Siyuan Zhang <sizhang@google.com>
This commit is contained in:
		| @@ -35,10 +35,10 @@ import ( | ||||
| 	auditbuffered "k8s.io/apiserver/plugin/pkg/audit/buffered" | ||||
| 	audittruncate "k8s.io/apiserver/plugin/pkg/audit/truncate" | ||||
| 	cliflag "k8s.io/component-base/cli/flag" | ||||
| 	basecompatibility "k8s.io/component-base/compatibility" | ||||
| 	"k8s.io/component-base/featuregate" | ||||
| 	"k8s.io/component-base/logs" | ||||
| 	"k8s.io/component-base/metrics" | ||||
| 	utilversion "k8s.io/component-base/version" | ||||
| 	kapi "k8s.io/kubernetes/pkg/apis/core" | ||||
| 	controlplaneapiserver "k8s.io/kubernetes/pkg/controlplane/apiserver/options" | ||||
| 	"k8s.io/kubernetes/pkg/controlplane/reconcilers" | ||||
| @@ -49,14 +49,12 @@ import ( | ||||
| ) | ||||
|  | ||||
| func TestAddFlags(t *testing.T) { | ||||
| 	componentGlobalsRegistry := featuregate.DefaultComponentGlobalsRegistry | ||||
| 	t.Cleanup(func() { | ||||
| 		componentGlobalsRegistry.Reset() | ||||
| 	}) | ||||
| 	componentGlobalsRegistry := basecompatibility.NewComponentGlobalsRegistry() | ||||
| 	fs := pflag.NewFlagSet("addflagstest", pflag.PanicOnError) | ||||
|  | ||||
| 	utilruntime.Must(componentGlobalsRegistry.Register("test", utilversion.NewEffectiveVersion("1.32"), featuregate.NewFeatureGate())) | ||||
| 	utilruntime.Must(componentGlobalsRegistry.Register("test", basecompatibility.NewEffectiveVersionFromString("1.32", "1.31", "1.31"), featuregate.NewFeatureGate())) | ||||
| 	s := NewServerRunOptions() | ||||
| 	s.GenericServerRunOptions.ComponentGlobalsRegistry = componentGlobalsRegistry | ||||
| 	for _, f := range s.Flags().FlagSets { | ||||
| 		fs.AddFlagSet(f) | ||||
| 	} | ||||
| @@ -150,7 +148,7 @@ func TestAddFlags(t *testing.T) { | ||||
| 				JSONPatchMaxCopyBytes:        int64(3 * 1024 * 1024), | ||||
| 				MaxRequestBodyBytes:          int64(3 * 1024 * 1024), | ||||
| 				ComponentGlobalsRegistry:     componentGlobalsRegistry, | ||||
| 				ComponentName:                featuregate.DefaultKubeComponent, | ||||
| 				ComponentName:                basecompatibility.DefaultKubeComponent, | ||||
| 			}, | ||||
| 			Admission: &kubeoptions.AdmissionOptions{ | ||||
| 				GenericAdmission: &apiserveroptions.AdmissionOptions{ | ||||
|   | ||||
| @@ -24,7 +24,6 @@ import ( | ||||
|  | ||||
| 	genericoptions "k8s.io/apiserver/pkg/server/options" | ||||
| 	utilfeature "k8s.io/apiserver/pkg/util/feature" | ||||
| 	utilversion "k8s.io/component-base/version" | ||||
| 	netutils "k8s.io/utils/net" | ||||
|  | ||||
| 	controlplaneapiserver "k8s.io/kubernetes/pkg/controlplane/apiserver/options" | ||||
| @@ -142,10 +141,5 @@ func (s CompletedOptions) Validate() []error { | ||||
| 		errs = append(errs, fmt.Errorf("--apiserver-count should be a positive number, but value '%d' provided", s.MasterCount)) | ||||
| 	} | ||||
|  | ||||
| 	effectiveVersion := s.GenericServerRunOptions.ComponentGlobalsRegistry.EffectiveVersionFor(s.GenericServerRunOptions.ComponentName) | ||||
| 	if err := utilversion.ValidateKubeEffectiveVersion(effectiveVersion); err != nil { | ||||
| 		errs = append(errs, err) | ||||
| 	} | ||||
|  | ||||
| 	return errs | ||||
| } | ||||
|   | ||||
| @@ -41,6 +41,7 @@ import ( | ||||
| 	"k8s.io/client-go/rest" | ||||
| 	cliflag "k8s.io/component-base/cli/flag" | ||||
| 	"k8s.io/component-base/cli/globalflag" | ||||
| 	basecompatibility "k8s.io/component-base/compatibility" | ||||
| 	"k8s.io/component-base/featuregate" | ||||
| 	"k8s.io/component-base/logs" | ||||
| 	logsapi "k8s.io/component-base/logs/api/v1" | ||||
| @@ -64,10 +65,9 @@ func init() { | ||||
|  | ||||
| // NewAPIServerCommand creates a *cobra.Command object with default parameters | ||||
| func NewAPIServerCommand() *cobra.Command { | ||||
| 	_, featureGate := featuregate.DefaultComponentGlobalsRegistry.ComponentGlobalsOrRegister( | ||||
| 		featuregate.DefaultKubeComponent, utilversion.DefaultBuildEffectiveVersion(), utilfeature.DefaultMutableFeatureGate) | ||||
| 	s := options.NewServerRunOptions() | ||||
| 	ctx := genericapiserver.SetupSignalContext() | ||||
| 	featureGate := s.GenericServerRunOptions.ComponentGlobalsRegistry.FeatureGateFor(basecompatibility.DefaultKubeComponent) | ||||
|  | ||||
| 	cmd := &cobra.Command{ | ||||
| 		Use: "kube-apiserver", | ||||
| @@ -79,7 +79,7 @@ cluster's shared state through which all other components interact.`, | ||||
| 		// stop printing usage when the command errors | ||||
| 		SilenceUsage: true, | ||||
| 		PersistentPreRunE: func(*cobra.Command, []string) error { | ||||
| 			if err := featuregate.DefaultComponentGlobalsRegistry.Set(); err != nil { | ||||
| 			if err := s.GenericServerRunOptions.ComponentGlobalsRegistry.Set(); err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 			// silence client-go warnings. | ||||
| @@ -108,7 +108,7 @@ cluster's shared state through which all other components interact.`, | ||||
| 				return utilerrors.NewAggregate(errs) | ||||
| 			} | ||||
| 			// add feature enablement metrics | ||||
| 			featureGate.AddMetrics() | ||||
| 			featureGate.(featuregate.MutableFeatureGate).AddMetrics() | ||||
| 			return Run(ctx, completedOptions) | ||||
| 		}, | ||||
| 		Args: func(cmd *cobra.Command, args []string) error { | ||||
|   | ||||
| @@ -43,22 +43,20 @@ import ( | ||||
| 	"k8s.io/apimachinery/pkg/api/errors" | ||||
| 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||||
| 	utilerrors "k8s.io/apimachinery/pkg/util/errors" | ||||
| 	utilruntime "k8s.io/apimachinery/pkg/util/runtime" | ||||
| 	"k8s.io/apimachinery/pkg/util/version" | ||||
| 	"k8s.io/apimachinery/pkg/util/wait" | ||||
| 	serveroptions "k8s.io/apiserver/pkg/server/options" | ||||
| 	"k8s.io/apiserver/pkg/storage/storagebackend" | ||||
| 	"k8s.io/apiserver/pkg/storageversion" | ||||
| 	"k8s.io/apiserver/pkg/util/compatibility" | ||||
| 	utilfeature "k8s.io/apiserver/pkg/util/feature" | ||||
| 	"k8s.io/client-go/kubernetes" | ||||
| 	restclient "k8s.io/client-go/rest" | ||||
| 	clientgotransport "k8s.io/client-go/transport" | ||||
| 	"k8s.io/client-go/util/cert" | ||||
| 	"k8s.io/client-go/util/keyutil" | ||||
| 	"k8s.io/component-base/featuregate" | ||||
| 	basecompatibility "k8s.io/component-base/compatibility" | ||||
| 	featuregatetesting "k8s.io/component-base/featuregate/testing" | ||||
| 	logsapi "k8s.io/component-base/logs/api/v1" | ||||
| 	utilversion "k8s.io/component-base/version" | ||||
| 	"k8s.io/klog/v2" | ||||
| 	"k8s.io/kube-aggregator/pkg/apiserver" | ||||
| 	"k8s.io/kubernetes/pkg/features" | ||||
| @@ -104,11 +102,8 @@ type TestServerInstanceOptions struct { | ||||
| 	// an apiserver version skew scenario where all apiservers use the same proxyCA to verify client connections. | ||||
| 	ProxyCA *ProxyCA | ||||
| 	// Set the BinaryVersion of server effective version. | ||||
| 	// If empty, effective version will default to version.DefaultKubeBinaryVersion. | ||||
| 	// If empty, effective version will default to DefaultKubeEffectiveVersion. | ||||
| 	BinaryVersion string | ||||
| 	// Set the EmulationVersion of server effective version. | ||||
| 	// If empty, emulation version will default to the effective version. | ||||
| 	EmulationVersion string | ||||
| 	// Set non-default request timeout in the server. | ||||
| 	RequestTimeout time.Duration | ||||
| } | ||||
| @@ -194,21 +189,20 @@ func StartTestServer(t ktesting.TB, instanceOptions *TestServerInstanceOptions, | ||||
|  | ||||
| 	fs := pflag.NewFlagSet("test", pflag.PanicOnError) | ||||
|  | ||||
| 	featureGate := utilfeature.DefaultMutableFeatureGate | ||||
| 	featureGate.AddMetrics() | ||||
| 	effectiveVersion := utilversion.DefaultKubeEffectiveVersion() | ||||
| 	featureGate := utilfeature.DefaultMutableFeatureGate.DeepCopy() | ||||
| 	effectiveVersion := compatibility.DefaultKubeEffectiveVersionForTest() | ||||
| 	if instanceOptions.BinaryVersion != "" { | ||||
| 		effectiveVersion = utilversion.NewEffectiveVersion(instanceOptions.BinaryVersion) | ||||
| 		effectiveVersion = basecompatibility.NewEffectiveVersionFromString(instanceOptions.BinaryVersion, "", "") | ||||
| 	} | ||||
| 	if instanceOptions.EmulationVersion != "" { | ||||
| 		effectiveVersion.SetEmulationVersion(version.MustParse(instanceOptions.EmulationVersion)) | ||||
| 	effectiveVersion.SetEmulationVersion(featureGate.EmulationVersion()) | ||||
| 	componentGlobalsRegistry := basecompatibility.NewComponentGlobalsRegistry() | ||||
| 	if err := componentGlobalsRegistry.Register(basecompatibility.DefaultKubeComponent, effectiveVersion, featureGate); err != nil { | ||||
| 		return result, err | ||||
| 	} | ||||
| 	// need to call SetFeatureGateEmulationVersionDuringTest to reset the feature gate emulation version at the end of the test. | ||||
| 	featuregatetesting.SetFeatureGateEmulationVersionDuringTest(t, featureGate, effectiveVersion.EmulationVersion()) | ||||
| 	featuregate.DefaultComponentGlobalsRegistry.Reset() | ||||
| 	utilruntime.Must(featuregate.DefaultComponentGlobalsRegistry.Register(featuregate.DefaultKubeComponent, effectiveVersion, featureGate)) | ||||
|  | ||||
| 	s := options.NewServerRunOptions() | ||||
| 	// set up new instance of ComponentGlobalsRegistry instead of using the DefaultComponentGlobalsRegistry to avoid contention in parallel tests. | ||||
| 	s.Options.GenericServerRunOptions.ComponentGlobalsRegistry = componentGlobalsRegistry | ||||
| 	if instanceOptions.RequestTimeout > 0 { | ||||
| 		s.GenericServerRunOptions.RequestTimeout = instanceOptions.RequestTimeout | ||||
| 	} | ||||
| @@ -330,15 +324,6 @@ func StartTestServer(t ktesting.TB, instanceOptions *TestServerInstanceOptions, | ||||
| 			return result, err | ||||
| 		} | ||||
| 		s.Authentication.ClientCert.ClientCA = clientCACertFile | ||||
| 		if utilfeature.DefaultFeatureGate.Enabled(features.UnknownVersionInteroperabilityProxy) { | ||||
| 			// TODO: set up a general clean up for testserver | ||||
| 			if clientgotransport.DialerStopCh == wait.NeverStop { | ||||
| 				ctx, cancel := context.WithTimeout(context.Background(), time.Hour) | ||||
| 				t.Cleanup(cancel) | ||||
| 				clientgotransport.DialerStopCh = ctx.Done() | ||||
| 			} | ||||
| 			s.PeerCAFile = filepath.Join(s.SecureServing.ServerCert.CertDirectory, s.SecureServing.ServerCert.PairName+".crt") | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	s.SecureServing.ExternalAddress = s.SecureServing.Listener.Addr().(*net.TCPAddr).IP // use listener addr although it is a loopback device | ||||
| @@ -374,8 +359,32 @@ func StartTestServer(t ktesting.TB, instanceOptions *TestServerInstanceOptions, | ||||
| 		s.Authentication.RequestHeader.ExtraHeaderPrefixes = extraHeaders | ||||
| 	} | ||||
|  | ||||
| 	if err := featuregate.DefaultComponentGlobalsRegistry.Set(); err != nil { | ||||
| 		return result, err | ||||
| 	if err := componentGlobalsRegistry.Set(); err != nil { | ||||
| 		return result, fmt.Errorf("%w\nIf you are using SetFeatureGate*DuringTest, try using --emulated-version and --feature-gates flags instead", err) | ||||
| 	} | ||||
| 	// If the local ComponentGlobalsRegistry is changed by the flags, | ||||
| 	// we need to copy the new feature values back to the DefaultFeatureGate because most feature checks still use the DefaultFeatureGate. | ||||
| 	// We cannot directly use DefaultFeatureGate in ComponentGlobalsRegistry because the changes done by ComponentGlobalsRegistry.Set() will not be undone at the end of the test. | ||||
| 	if !featureGate.EmulationVersion().EqualTo(utilfeature.DefaultMutableFeatureGate.EmulationVersion()) { | ||||
| 		featuregatetesting.SetFeatureGateEmulationVersionDuringTest(t, utilfeature.DefaultMutableFeatureGate, effectiveVersion.EmulationVersion()) | ||||
| 	} | ||||
| 	for f := range utilfeature.DefaultMutableFeatureGate.GetAll() { | ||||
| 		if featureGate.Enabled(f) != utilfeature.DefaultFeatureGate.Enabled(f) { | ||||
| 			featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, f, featureGate.Enabled(f)) | ||||
| 		} | ||||
| 	} | ||||
| 	utilfeature.DefaultMutableFeatureGate.AddMetrics() | ||||
|  | ||||
| 	if instanceOptions.EnableCertAuth { | ||||
| 		if featureGate.Enabled(features.UnknownVersionInteroperabilityProxy) { | ||||
| 			// TODO: set up a general clean up for testserver | ||||
| 			if clientgotransport.DialerStopCh == wait.NeverStop { | ||||
| 				ctx, cancel := context.WithTimeout(context.Background(), time.Hour) | ||||
| 				t.Cleanup(cancel) | ||||
| 				clientgotransport.DialerStopCh = ctx.Done() | ||||
| 			} | ||||
| 			s.PeerCAFile = filepath.Join(s.SecureServing.ServerCert.CertDirectory, s.SecureServing.ServerCert.PairName+".crt") | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	saSigningKeyFile, err := os.CreateTemp("/tmp", "insecure_test_key") | ||||
|   | ||||
| @@ -21,6 +21,7 @@ import ( | ||||
| 	clientset "k8s.io/client-go/kubernetes" | ||||
| 	restclient "k8s.io/client-go/rest" | ||||
| 	"k8s.io/client-go/tools/record" | ||||
| 	basecompatibility "k8s.io/component-base/compatibility" | ||||
| 	kubectrlmgrconfig "k8s.io/kubernetes/pkg/controller/apis/config" | ||||
| ) | ||||
|  | ||||
| @@ -43,6 +44,9 @@ type Config struct { | ||||
|  | ||||
| 	EventBroadcaster record.EventBroadcaster | ||||
| 	EventRecorder    record.EventRecorder | ||||
|  | ||||
| 	// ComponentGlobalsRegistry is the registry where the effective versions and feature gates for all components are stored. | ||||
| 	ComponentGlobalsRegistry basecompatibility.ComponentGlobalsRegistry | ||||
| } | ||||
|  | ||||
| type completedConfig struct { | ||||
|   | ||||
| @@ -54,6 +54,7 @@ import ( | ||||
| 	"k8s.io/client-go/util/keyutil" | ||||
| 	cliflag "k8s.io/component-base/cli/flag" | ||||
| 	"k8s.io/component-base/cli/globalflag" | ||||
| 	basecompatibility "k8s.io/component-base/compatibility" | ||||
| 	"k8s.io/component-base/configz" | ||||
| 	"k8s.io/component-base/featuregate" | ||||
| 	"k8s.io/component-base/logs" | ||||
| @@ -99,9 +100,6 @@ const ( | ||||
|  | ||||
| // NewControllerManagerCommand creates a *cobra.Command object with default parameters | ||||
| func NewControllerManagerCommand() *cobra.Command { | ||||
| 	_, _ = featuregate.DefaultComponentGlobalsRegistry.ComponentGlobalsOrRegister( | ||||
| 		featuregate.DefaultKubeComponent, utilversion.DefaultBuildEffectiveVersion(), utilfeature.DefaultMutableFeatureGate) | ||||
|  | ||||
| 	s, err := options.NewKubeControllerManagerOptions() | ||||
| 	if err != nil { | ||||
| 		klog.Background().Error(err, "Unable to initialize command options") | ||||
| @@ -142,7 +140,7 @@ controller, and serviceaccounts controller.`, | ||||
| 			} | ||||
|  | ||||
| 			// add feature enablement metrics | ||||
| 			fg := s.ComponentGlobalsRegistry.FeatureGateFor(featuregate.DefaultKubeComponent) | ||||
| 			fg := s.ComponentGlobalsRegistry.FeatureGateFor(basecompatibility.DefaultKubeComponent) | ||||
| 			fg.(featuregate.MutableFeatureGate).AddMetrics() | ||||
| 			return Run(context.Background(), c.Complete()) | ||||
| 		}, | ||||
| @@ -218,7 +216,7 @@ func Run(ctx context.Context, c *config.CompletedConfig) error { | ||||
| 		slis.SLIMetricsWithReset{}.Install(unsecuredMux) | ||||
|  | ||||
| 		if utilfeature.DefaultFeatureGate.Enabled(zpagesfeatures.ComponentStatusz) { | ||||
| 			statusz.Install(unsecuredMux, kubeControllerManager, statusz.NewRegistry()) | ||||
| 			statusz.Install(unsecuredMux, kubeControllerManager, statusz.NewRegistry(c.ComponentGlobalsRegistry.EffectiveVersionFor(basecompatibility.DefaultKubeComponent))) | ||||
| 		} | ||||
|  | ||||
| 		handler := genericcontrollermanager.BuildHandlerChain(unsecuredMux, &c.Authorization, &c.Authentication) | ||||
| @@ -289,11 +287,11 @@ func Run(ctx context.Context, c *config.CompletedConfig) error { | ||||
| 	} | ||||
|  | ||||
| 	if utilfeature.DefaultFeatureGate.Enabled(kubefeatures.CoordinatedLeaderElection) { | ||||
| 		binaryVersion, err := semver.ParseTolerant(featuregate.DefaultComponentGlobalsRegistry.EffectiveVersionFor(featuregate.DefaultKubeComponent).BinaryVersion().String()) | ||||
| 		binaryVersion, err := semver.ParseTolerant(c.ComponentGlobalsRegistry.EffectiveVersionFor(basecompatibility.DefaultKubeComponent).BinaryVersion().String()) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		emulationVersion, err := semver.ParseTolerant(featuregate.DefaultComponentGlobalsRegistry.EffectiveVersionFor(featuregate.DefaultKubeComponent).EmulationVersion().String()) | ||||
| 		emulationVersion, err := semver.ParseTolerant(c.ComponentGlobalsRegistry.EffectiveVersionFor(basecompatibility.DefaultKubeComponent).EmulationVersion().String()) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
|   | ||||
| @@ -25,6 +25,7 @@ import ( | ||||
| 	utilerrors "k8s.io/apimachinery/pkg/util/errors" | ||||
| 	utilruntime "k8s.io/apimachinery/pkg/util/runtime" | ||||
| 	apiserveroptions "k8s.io/apiserver/pkg/server/options" | ||||
| 	"k8s.io/apiserver/pkg/util/compatibility" | ||||
| 	utilfeature "k8s.io/apiserver/pkg/util/feature" | ||||
| 	clientgofeaturegate "k8s.io/client-go/features" | ||||
| 	clientset "k8s.io/client-go/kubernetes" | ||||
| @@ -36,11 +37,11 @@ import ( | ||||
| 	cpnames "k8s.io/cloud-provider/names" | ||||
| 	cpoptions "k8s.io/cloud-provider/options" | ||||
| 	cliflag "k8s.io/component-base/cli/flag" | ||||
| 	basecompatibility "k8s.io/component-base/compatibility" | ||||
| 	"k8s.io/component-base/featuregate" | ||||
| 	"k8s.io/component-base/logs" | ||||
| 	logsapi "k8s.io/component-base/logs/api/v1" | ||||
| 	"k8s.io/component-base/metrics" | ||||
| 	utilversion "k8s.io/component-base/version" | ||||
| 	cmoptions "k8s.io/controller-manager/options" | ||||
| 	"k8s.io/klog/v2" | ||||
| 	kubectrlmgrconfigv1alpha1 "k8s.io/kube-controller-manager/config/v1alpha1" | ||||
| @@ -106,7 +107,7 @@ type KubeControllerManagerOptions struct { | ||||
| 	ShowHiddenMetricsForVersion string | ||||
|  | ||||
| 	// ComponentGlobalsRegistry is the registry where the effective versions and feature gates for all components are stored. | ||||
| 	ComponentGlobalsRegistry featuregate.ComponentGlobalsRegistry | ||||
| 	ComponentGlobalsRegistry basecompatibility.ComponentGlobalsRegistry | ||||
| } | ||||
|  | ||||
| // NewKubeControllerManagerOptions creates a new KubeControllerManagerOptions with a default config. | ||||
| @@ -116,10 +117,12 @@ func NewKubeControllerManagerOptions() (*KubeControllerManagerOptions, error) { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	if featuregate.DefaultComponentGlobalsRegistry.EffectiveVersionFor(featuregate.DefaultKubeComponent) == nil { | ||||
| 	componentGlobalsRegistry := compatibility.DefaultComponentGlobalsRegistry | ||||
|  | ||||
| 	if componentGlobalsRegistry.EffectiveVersionFor(basecompatibility.DefaultKubeComponent) == nil { | ||||
| 		featureGate := utilfeature.DefaultMutableFeatureGate | ||||
| 		effectiveVersion := utilversion.DefaultKubeEffectiveVersion() | ||||
| 		utilruntime.Must(featuregate.DefaultComponentGlobalsRegistry.Register(featuregate.DefaultKubeComponent, effectiveVersion, featureGate)) | ||||
| 		effectiveVersion := compatibility.DefaultBuildEffectiveVersion() | ||||
| 		utilruntime.Must(componentGlobalsRegistry.Register(basecompatibility.DefaultKubeComponent, effectiveVersion, featureGate)) | ||||
| 	} | ||||
|  | ||||
| 	s := KubeControllerManagerOptions{ | ||||
| @@ -211,7 +214,7 @@ func NewKubeControllerManagerOptions() (*KubeControllerManagerOptions, error) { | ||||
| 		Authorization:            apiserveroptions.NewDelegatingAuthorizationOptions(), | ||||
| 		Metrics:                  metrics.NewOptions(), | ||||
| 		Logs:                     logs.NewOptions(), | ||||
| 		ComponentGlobalsRegistry: featuregate.DefaultComponentGlobalsRegistry, | ||||
| 		ComponentGlobalsRegistry: componentGlobalsRegistry, | ||||
| 	} | ||||
|  | ||||
| 	s.Authentication.RemoteKubeConfigFileOptional = true | ||||
| @@ -230,7 +233,6 @@ func NewKubeControllerManagerOptions() (*KubeControllerManagerOptions, error) { | ||||
| 	s.GarbageCollectorController.GCIgnoredResources = gcIgnoredResources | ||||
| 	s.Generic.LeaderElection.ResourceName = "kube-controller-manager" | ||||
| 	s.Generic.LeaderElection.ResourceNamespace = "kube-system" | ||||
|  | ||||
| 	return &s, nil | ||||
| } | ||||
|  | ||||
| @@ -452,7 +454,6 @@ func (s *KubeControllerManagerOptions) Validate(allControllers []string, disable | ||||
| 	errs = append(errs, s.Authentication.Validate()...) | ||||
| 	errs = append(errs, s.Authorization.Validate()...) | ||||
| 	errs = append(errs, s.Metrics.Validate()...) | ||||
| 	errs = append(errs, utilversion.ValidateKubeEffectiveVersion(s.ComponentGlobalsRegistry.EffectiveVersionFor(featuregate.DefaultKubeComponent))) | ||||
|  | ||||
| 	// in-tree cloud providers are disabled since v1.31 (KEP-2395) | ||||
| 	if len(s.KubeCloudShared.CloudProvider.Name) > 0 && !cloudprovider.IsExternal(s.KubeCloudShared.CloudProvider.Name) { | ||||
| @@ -498,10 +499,11 @@ func (s KubeControllerManagerOptions) Config(allControllers []string, disabledBy | ||||
| 	eventRecorder := eventBroadcaster.NewRecorder(clientgokubescheme.Scheme, v1.EventSource{Component: KubeControllerManagerUserAgent}) | ||||
|  | ||||
| 	c := &kubecontrollerconfig.Config{ | ||||
| 		Client:           client, | ||||
| 		Kubeconfig:       kubeconfig, | ||||
| 		EventBroadcaster: eventBroadcaster, | ||||
| 		EventRecorder:    eventRecorder, | ||||
| 		Client:                   client, | ||||
| 		Kubeconfig:               kubeconfig, | ||||
| 		EventBroadcaster:         eventBroadcaster, | ||||
| 		EventRecorder:            eventRecorder, | ||||
| 		ComponentGlobalsRegistry: s.ComponentGlobalsRegistry, | ||||
| 	} | ||||
| 	if err := s.ApplyTo(c, allControllers, disabledByDefaultControllers, controllerAliases); err != nil { | ||||
| 		return nil, err | ||||
|   | ||||
| @@ -34,7 +34,7 @@ import ( | ||||
|  | ||||
| 	"k8s.io/apiserver/pkg/apis/apiserver" | ||||
| 	apiserveroptions "k8s.io/apiserver/pkg/server/options" | ||||
| 	utilversion "k8s.io/component-base/version" | ||||
| 	basecompatibility "k8s.io/component-base/compatibility" | ||||
|  | ||||
| 	componentbaseconfig "k8s.io/component-base/config" | ||||
| 	"k8s.io/component-base/featuregate" | ||||
| @@ -447,10 +447,11 @@ func TestAddFlags(t *testing.T) { | ||||
| 			AlwaysAllowPaths:             []string{"/healthz", "/readyz", "/livez"}, // note: this does not match /healthz/ or /healthz/* | ||||
| 			AlwaysAllowGroups:            []string{"system:masters"}, | ||||
| 		}, | ||||
| 		Master:                   "192.168.4.20", | ||||
| 		Metrics:                  &metrics.Options{}, | ||||
| 		Logs:                     logs.NewOptions(), | ||||
| 		ComponentGlobalsRegistry: featuregate.DefaultComponentGlobalsRegistry, | ||||
| 		Master:  "192.168.4.20", | ||||
| 		Metrics: &metrics.Options{}, | ||||
| 		Logs:    logs.NewOptions(), | ||||
| 		// ignores comparing ComponentGlobalsRegistry in this test. | ||||
| 		ComponentGlobalsRegistry: s.ComponentGlobalsRegistry, | ||||
| 	} | ||||
|  | ||||
| 	// Sort GCIgnoredResources because it's built from a map, which means the | ||||
| @@ -736,27 +737,6 @@ func TestApplyTo(t *testing.T) { | ||||
| } | ||||
|  | ||||
| func TestEmulatedVersion(t *testing.T) { | ||||
| 	var cleanupAndSetupFunc = func() featuregate.FeatureGate { | ||||
| 		componentGlobalsRegistry := featuregate.DefaultComponentGlobalsRegistry | ||||
| 		componentGlobalsRegistry.Reset() // make sure this test have a clean state | ||||
| 		t.Cleanup(func() { | ||||
| 			componentGlobalsRegistry.Reset() // make sure this test doesn't leak a dirty state | ||||
| 		}) | ||||
|  | ||||
| 		verKube := utilversion.NewEffectiveVersion("1.32") | ||||
| 		fg := featuregate.NewVersionedFeatureGate(version.MustParse("1.32")) | ||||
| 		utilruntime.Must(fg.AddVersioned(map[featuregate.Feature]featuregate.VersionedSpecs{ | ||||
| 			"kubeA": { | ||||
| 				{Version: version.MustParse("1.30"), Default: false, PreRelease: featuregate.Beta}, | ||||
| 				{Version: version.MustParse("1.32"), Default: true, LockToDefault: true, PreRelease: featuregate.GA}, | ||||
| 			}, | ||||
| 			"kubeB": { | ||||
| 				{Version: version.MustParse("1.31"), Default: false, PreRelease: featuregate.Alpha}, | ||||
| 			}, | ||||
| 		})) | ||||
| 		utilruntime.Must(componentGlobalsRegistry.Register(featuregate.DefaultKubeComponent, verKube, fg)) | ||||
| 		return fg | ||||
| 	} | ||||
|  | ||||
| 	testcases := []struct { | ||||
| 		name              string | ||||
| @@ -808,9 +788,8 @@ func TestEmulatedVersion(t *testing.T) { | ||||
|  | ||||
| 	for _, tc := range testcases { | ||||
| 		t.Run(tc.name, func(t *testing.T) { | ||||
| 			fg := cleanupAndSetupFunc() | ||||
|  | ||||
| 			fs, s := setupControllerManagerFlagSet(t) | ||||
| 			fg := s.ComponentGlobalsRegistry.FeatureGateFor(basecompatibility.DefaultKubeComponent) | ||||
| 			err := fs.Parse(tc.flags) | ||||
| 			checkTestError(t, err, false, "") | ||||
| 			err = s.Validate([]string{""}, []string{""}, nil) | ||||
| @@ -1558,6 +1537,22 @@ func setupControllerManagerFlagSet(t *testing.T) (*pflag.FlagSet, *KubeControlle | ||||
| 		t.Fatal(fmt.Errorf("NewKubeControllerManagerOptions failed with %w", err)) | ||||
| 	} | ||||
|  | ||||
| 	componentGlobalsRegistry := basecompatibility.NewComponentGlobalsRegistry() | ||||
|  | ||||
| 	verKube := basecompatibility.NewEffectiveVersionFromString("1.32", "1.31", "1.31") | ||||
| 	fg := featuregate.NewVersionedFeatureGate(version.MustParse("1.32")) | ||||
| 	utilruntime.Must(fg.AddVersioned(map[featuregate.Feature]featuregate.VersionedSpecs{ | ||||
| 		"kubeA": { | ||||
| 			{Version: version.MustParse("1.30"), Default: false, PreRelease: featuregate.Beta}, | ||||
| 			{Version: version.MustParse("1.32"), Default: true, LockToDefault: true, PreRelease: featuregate.GA}, | ||||
| 		}, | ||||
| 		"kubeB": { | ||||
| 			{Version: version.MustParse("1.31"), Default: false, PreRelease: featuregate.Alpha}, | ||||
| 		}, | ||||
| 	})) | ||||
| 	utilruntime.Must(componentGlobalsRegistry.Register(basecompatibility.DefaultKubeComponent, verKube, fg)) | ||||
| 	s.ComponentGlobalsRegistry = componentGlobalsRegistry | ||||
|  | ||||
| 	for _, f := range s.Flags([]string{""}, []string{""}, nil).FlagSets { | ||||
| 		fs.AddFlagSet(f) | ||||
| 	} | ||||
|   | ||||
| @@ -42,6 +42,7 @@ import ( | ||||
| 	"k8s.io/apiserver/pkg/server/healthz" | ||||
| 	"k8s.io/apiserver/pkg/server/mux" | ||||
| 	"k8s.io/apiserver/pkg/server/routes" | ||||
| 	"k8s.io/apiserver/pkg/util/compatibility" | ||||
| 	utilfeature "k8s.io/apiserver/pkg/util/feature" | ||||
| 	"k8s.io/client-go/informers" | ||||
| 	clientset "k8s.io/client-go/kubernetes" | ||||
| @@ -474,7 +475,7 @@ func serveMetrics(ctx context.Context, bindAddress string, proxyMode kubeproxyco | ||||
| 	} | ||||
|  | ||||
| 	if utilfeature.DefaultFeatureGate.Enabled(zpagesfeatures.ComponentStatusz) { | ||||
| 		statusz.Install(proxyMux, kubeProxy, statusz.NewRegistry()) | ||||
| 		statusz.Install(proxyMux, kubeProxy, statusz.NewRegistry(compatibility.DefaultBuildEffectiveVersion())) | ||||
| 	} | ||||
|  | ||||
| 	fn := func() { | ||||
|   | ||||
| @@ -26,6 +26,7 @@ import ( | ||||
| 	restclient "k8s.io/client-go/rest" | ||||
| 	"k8s.io/client-go/tools/events" | ||||
| 	"k8s.io/client-go/tools/leaderelection" | ||||
| 	basecompatibility "k8s.io/component-base/compatibility" | ||||
| 	"k8s.io/component-base/zpages/flagz" | ||||
| 	kubeschedulerconfig "k8s.io/kubernetes/pkg/scheduler/apis/config" | ||||
| ) | ||||
| @@ -61,6 +62,9 @@ type Config struct { | ||||
| 	// value, the pod will be moved from unschedulablePods to backoffQ or activeQ. | ||||
| 	// If this value is empty, the default value (5min) will be used. | ||||
| 	PodMaxInUnschedulablePodsDuration time.Duration | ||||
|  | ||||
| 	// ComponentGlobalsRegistry is the registry where the effective versions and feature gates for all components are stored. | ||||
| 	ComponentGlobalsRegistry basecompatibility.ComponentGlobalsRegistry | ||||
| } | ||||
|  | ||||
| type completedConfig struct { | ||||
|   | ||||
| @@ -28,6 +28,7 @@ import ( | ||||
| 	utilruntime "k8s.io/apimachinery/pkg/util/runtime" | ||||
| 	"k8s.io/apimachinery/pkg/util/uuid" | ||||
| 	apiserveroptions "k8s.io/apiserver/pkg/server/options" | ||||
| 	"k8s.io/apiserver/pkg/util/compatibility" | ||||
| 	utilfeature "k8s.io/apiserver/pkg/util/feature" | ||||
| 	"k8s.io/client-go/dynamic" | ||||
| 	"k8s.io/client-go/dynamic/dynamicinformer" | ||||
| @@ -39,13 +40,12 @@ import ( | ||||
| 	"k8s.io/client-go/tools/leaderelection/resourcelock" | ||||
| 	"k8s.io/client-go/tools/record" | ||||
| 	cliflag "k8s.io/component-base/cli/flag" | ||||
| 	basecompatibility "k8s.io/component-base/compatibility" | ||||
| 	componentbaseconfig "k8s.io/component-base/config" | ||||
| 	"k8s.io/component-base/config/options" | ||||
| 	"k8s.io/component-base/featuregate" | ||||
| 	"k8s.io/component-base/logs" | ||||
| 	logsapi "k8s.io/component-base/logs/api/v1" | ||||
| 	"k8s.io/component-base/metrics" | ||||
| 	utilversion "k8s.io/component-base/version" | ||||
| 	zpagesfeatures "k8s.io/component-base/zpages/features" | ||||
| 	"k8s.io/component-base/zpages/flagz" | ||||
| 	"k8s.io/klog/v2" | ||||
| @@ -78,7 +78,7 @@ type Options struct { | ||||
| 	Master string | ||||
|  | ||||
| 	// ComponentGlobalsRegistry is the registry where the effective versions and feature gates for all components are stored. | ||||
| 	ComponentGlobalsRegistry featuregate.ComponentGlobalsRegistry | ||||
| 	ComponentGlobalsRegistry basecompatibility.ComponentGlobalsRegistry | ||||
|  | ||||
| 	// Flags hold the parsed CLI flags. | ||||
| 	Flags *cliflag.NamedFlagSets | ||||
| @@ -86,12 +86,17 @@ type Options struct { | ||||
|  | ||||
| // NewOptions returns default scheduler app options. | ||||
| func NewOptions() *Options { | ||||
| 	componentGlobalsRegistry := compatibility.DefaultComponentGlobalsRegistry | ||||
| 	// make sure DefaultKubeComponent is registered in the DefaultComponentGlobalsRegistry. | ||||
| 	if featuregate.DefaultComponentGlobalsRegistry.EffectiveVersionFor(featuregate.DefaultKubeComponent) == nil { | ||||
| 	if componentGlobalsRegistry.EffectiveVersionFor(basecompatibility.DefaultKubeComponent) == nil { | ||||
| 		featureGate := utilfeature.DefaultMutableFeatureGate | ||||
| 		effectiveVersion := utilversion.DefaultKubeEffectiveVersion() | ||||
| 		utilruntime.Must(featuregate.DefaultComponentGlobalsRegistry.Register(featuregate.DefaultKubeComponent, effectiveVersion, featureGate)) | ||||
| 		effectiveVersion := compatibility.DefaultBuildEffectiveVersion() | ||||
| 		utilruntime.Must(componentGlobalsRegistry.Register(basecompatibility.DefaultKubeComponent, effectiveVersion, featureGate)) | ||||
| 	} | ||||
| 	return NewOptionsWithComponentGlobalsRegistry(componentGlobalsRegistry) | ||||
| } | ||||
|  | ||||
| func NewOptionsWithComponentGlobalsRegistry(componentGlobalsRegistry basecompatibility.ComponentGlobalsRegistry) *Options { | ||||
| 	o := &Options{ | ||||
| 		SecureServing:  apiserveroptions.NewSecureServingOptions().WithLoopback(), | ||||
| 		Authentication: apiserveroptions.NewDelegatingAuthenticationOptions(), | ||||
| @@ -110,7 +115,7 @@ func NewOptions() *Options { | ||||
| 		}, | ||||
| 		Metrics:                  metrics.NewOptions(), | ||||
| 		Logs:                     logs.NewOptions(), | ||||
| 		ComponentGlobalsRegistry: featuregate.DefaultComponentGlobalsRegistry, | ||||
| 		ComponentGlobalsRegistry: componentGlobalsRegistry, | ||||
| 	} | ||||
|  | ||||
| 	o.Authentication.TolerateInClusterLookupFailure = true | ||||
| @@ -287,11 +292,6 @@ func (o *Options) Validate() []error { | ||||
| 	errs = append(errs, o.Authorization.Validate()...) | ||||
| 	errs = append(errs, o.Metrics.Validate()...) | ||||
|  | ||||
| 	effectiveVersion := o.ComponentGlobalsRegistry.EffectiveVersionFor(featuregate.DefaultKubeComponent) | ||||
| 	if err := utilversion.ValidateKubeEffectiveVersion(effectiveVersion); err != nil { | ||||
| 		errs = append(errs, err) | ||||
| 	} | ||||
|  | ||||
| 	return errs | ||||
| } | ||||
|  | ||||
| @@ -337,6 +337,7 @@ func (o *Options) Config(ctx context.Context) (*schedulerappconfig.Config, error | ||||
| 	dynClient := dynamic.NewForConfigOrDie(c.KubeConfig) | ||||
| 	c.DynInformerFactory = dynamicinformer.NewFilteredDynamicSharedInformerFactory(dynClient, 0, corev1.NamespaceAll, nil) | ||||
| 	c.LeaderElection = leaderElectionConfig | ||||
| 	c.ComponentGlobalsRegistry = o.ComponentGlobalsRegistry | ||||
|  | ||||
| 	return c, nil | ||||
| } | ||||
|   | ||||
| @@ -32,8 +32,8 @@ import ( | ||||
| 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||||
| 	"k8s.io/apimachinery/pkg/runtime" | ||||
| 	apiserveroptions "k8s.io/apiserver/pkg/server/options" | ||||
| 	basecompatibility "k8s.io/component-base/compatibility" | ||||
| 	componentbaseconfig "k8s.io/component-base/config" | ||||
| 	"k8s.io/component-base/featuregate" | ||||
| 	"k8s.io/component-base/logs" | ||||
| 	"k8s.io/klog/v2/ktesting" | ||||
| 	v1 "k8s.io/kube-scheduler/config/v1" | ||||
| @@ -282,6 +282,8 @@ profiles: | ||||
| 	defaultPodMaxBackoffSeconds := int64(10) | ||||
| 	defaultPercentageOfNodesToScore := ptr.To[int32](0) | ||||
|  | ||||
| 	componentGlobalsRegistry := basecompatibility.NewComponentGlobalsRegistry() | ||||
|  | ||||
| 	testcases := []struct { | ||||
| 		name             string | ||||
| 		options          *Options | ||||
| @@ -323,7 +325,7 @@ profiles: | ||||
| 					AlwaysAllowGroups:            []string{"system:masters"}, | ||||
| 				}, | ||||
| 				Logs:                     logs.NewOptions(), | ||||
| 				ComponentGlobalsRegistry: featuregate.DefaultComponentGlobalsRegistry, | ||||
| 				ComponentGlobalsRegistry: componentGlobalsRegistry, | ||||
| 			}, | ||||
| 			expectedUsername: "config", | ||||
| 			expectedConfig: kubeschedulerconfig.KubeSchedulerConfiguration{ | ||||
| @@ -375,7 +377,7 @@ profiles: | ||||
| 					return cfg | ||||
| 				}(), | ||||
| 				Logs:                     logs.NewOptions(), | ||||
| 				ComponentGlobalsRegistry: featuregate.DefaultComponentGlobalsRegistry, | ||||
| 				ComponentGlobalsRegistry: componentGlobalsRegistry, | ||||
| 			}, | ||||
| 			expectedError: "no kind \"KubeSchedulerConfiguration\" is registered for version \"componentconfig/v1alpha1\"", | ||||
| 		}, | ||||
| @@ -384,7 +386,7 @@ profiles: | ||||
| 			options: &Options{ | ||||
| 				ConfigFile:               unknownVersionConfig, | ||||
| 				Logs:                     logs.NewOptions(), | ||||
| 				ComponentGlobalsRegistry: featuregate.DefaultComponentGlobalsRegistry, | ||||
| 				ComponentGlobalsRegistry: componentGlobalsRegistry, | ||||
| 			}, | ||||
| 			expectedError: "no kind \"KubeSchedulerConfiguration\" is registered for version \"kubescheduler.config.k8s.io/unknown\"", | ||||
| 		}, | ||||
| @@ -393,7 +395,7 @@ profiles: | ||||
| 			options: &Options{ | ||||
| 				ConfigFile:               noVersionConfig, | ||||
| 				Logs:                     logs.NewOptions(), | ||||
| 				ComponentGlobalsRegistry: featuregate.DefaultComponentGlobalsRegistry, | ||||
| 				ComponentGlobalsRegistry: componentGlobalsRegistry, | ||||
| 			}, | ||||
| 			expectedError: "Object 'apiVersion' is missing", | ||||
| 		}, | ||||
| @@ -430,7 +432,7 @@ profiles: | ||||
| 					AlwaysAllowGroups:            []string{"system:masters"}, | ||||
| 				}, | ||||
| 				Logs:                     logs.NewOptions(), | ||||
| 				ComponentGlobalsRegistry: featuregate.DefaultComponentGlobalsRegistry, | ||||
| 				ComponentGlobalsRegistry: componentGlobalsRegistry, | ||||
| 			}, | ||||
| 			expectedUsername: "flag", | ||||
| 			expectedConfig: kubeschedulerconfig.KubeSchedulerConfiguration{ | ||||
| @@ -503,7 +505,7 @@ profiles: | ||||
| 					AlwaysAllowGroups:            []string{"system:masters"}, | ||||
| 				}, | ||||
| 				Logs:                     logs.NewOptions(), | ||||
| 				ComponentGlobalsRegistry: featuregate.DefaultComponentGlobalsRegistry, | ||||
| 				ComponentGlobalsRegistry: componentGlobalsRegistry, | ||||
| 			}, | ||||
| 			expectedConfig: kubeschedulerconfig.KubeSchedulerConfiguration{ | ||||
| 				TypeMeta: metav1.TypeMeta{ | ||||
| @@ -548,7 +550,7 @@ profiles: | ||||
| 			options: &Options{ | ||||
| 				ConfigFile:               pluginConfigFile, | ||||
| 				Logs:                     logs.NewOptions(), | ||||
| 				ComponentGlobalsRegistry: featuregate.DefaultComponentGlobalsRegistry, | ||||
| 				ComponentGlobalsRegistry: componentGlobalsRegistry, | ||||
| 			}, | ||||
| 			expectedUsername: "config", | ||||
| 			expectedConfig: kubeschedulerconfig.KubeSchedulerConfiguration{ | ||||
| @@ -669,7 +671,7 @@ profiles: | ||||
| 			options: &Options{ | ||||
| 				ConfigFile:               multiProfilesConfig, | ||||
| 				Logs:                     logs.NewOptions(), | ||||
| 				ComponentGlobalsRegistry: featuregate.DefaultComponentGlobalsRegistry, | ||||
| 				ComponentGlobalsRegistry: componentGlobalsRegistry, | ||||
| 			}, | ||||
| 			expectedUsername: "config", | ||||
| 			expectedConfig: kubeschedulerconfig.KubeSchedulerConfiguration{ | ||||
| @@ -784,7 +786,7 @@ profiles: | ||||
| 			name: "no config", | ||||
| 			options: &Options{ | ||||
| 				Logs:                     logs.NewOptions(), | ||||
| 				ComponentGlobalsRegistry: featuregate.DefaultComponentGlobalsRegistry, | ||||
| 				ComponentGlobalsRegistry: componentGlobalsRegistry, | ||||
| 			}, | ||||
| 			expectedError: "no configuration has been provided", | ||||
| 		}, | ||||
| @@ -793,7 +795,7 @@ profiles: | ||||
| 			options: &Options{ | ||||
| 				ConfigFile:               unknownFieldConfig, | ||||
| 				Logs:                     logs.NewOptions(), | ||||
| 				ComponentGlobalsRegistry: featuregate.DefaultComponentGlobalsRegistry, | ||||
| 				ComponentGlobalsRegistry: componentGlobalsRegistry, | ||||
| 			}, | ||||
| 			expectedError: `unknown field "foo"`, | ||||
| 			checkErrFn:    runtime.IsStrictDecodingError, | ||||
| @@ -803,7 +805,7 @@ profiles: | ||||
| 			options: &Options{ | ||||
| 				ConfigFile:               duplicateFieldConfig, | ||||
| 				Logs:                     logs.NewOptions(), | ||||
| 				ComponentGlobalsRegistry: featuregate.DefaultComponentGlobalsRegistry, | ||||
| 				ComponentGlobalsRegistry: componentGlobalsRegistry, | ||||
| 			}, | ||||
| 			expectedError: `key "leaderElect" already set`, | ||||
| 			checkErrFn:    runtime.IsStrictDecodingError, | ||||
| @@ -813,7 +815,7 @@ profiles: | ||||
| 			options: &Options{ | ||||
| 				ConfigFile:               highThroughputProfileConfig, | ||||
| 				Logs:                     logs.NewOptions(), | ||||
| 				ComponentGlobalsRegistry: featuregate.DefaultComponentGlobalsRegistry, | ||||
| 				ComponentGlobalsRegistry: componentGlobalsRegistry, | ||||
| 			}, | ||||
| 			expectedUsername: "config", | ||||
| 			expectedConfig: kubeschedulerconfig.KubeSchedulerConfiguration{ | ||||
|   | ||||
| @@ -46,6 +46,7 @@ import ( | ||||
| 	"k8s.io/client-go/tools/leaderelection" | ||||
| 	cliflag "k8s.io/component-base/cli/flag" | ||||
| 	"k8s.io/component-base/cli/globalflag" | ||||
| 	basecompatibility "k8s.io/component-base/compatibility" | ||||
| 	"k8s.io/component-base/configz" | ||||
| 	"k8s.io/component-base/featuregate" | ||||
| 	"k8s.io/component-base/logs" | ||||
| @@ -84,10 +85,6 @@ type Option func(runtime.Registry) error | ||||
|  | ||||
| // NewSchedulerCommand creates a *cobra.Command object with default parameters and registryOptions | ||||
| func NewSchedulerCommand(registryOptions ...Option) *cobra.Command { | ||||
| 	// explicitly register (if not already registered) the kube effective version and feature gate in DefaultComponentGlobalsRegistry, | ||||
| 	// which will be used in NewOptions. | ||||
| 	_, _ = featuregate.DefaultComponentGlobalsRegistry.ComponentGlobalsOrRegister( | ||||
| 		featuregate.DefaultKubeComponent, utilversion.DefaultBuildEffectiveVersion(), utilfeature.DefaultMutableFeatureGate) | ||||
| 	opts := options.NewOptions() | ||||
|  | ||||
| 	cmd := &cobra.Command{ | ||||
| @@ -138,7 +135,7 @@ for more information about scheduling and the kube-scheduler component.`, | ||||
| // runCommand runs the scheduler. | ||||
| func runCommand(cmd *cobra.Command, opts *options.Options, registryOptions ...Option) error { | ||||
| 	verflag.PrintAndExitIfRequested() | ||||
| 	fg := opts.ComponentGlobalsRegistry.FeatureGateFor(featuregate.DefaultKubeComponent) | ||||
| 	fg := opts.ComponentGlobalsRegistry.FeatureGateFor(basecompatibility.DefaultKubeComponent) | ||||
| 	// Activate logging as soon as possible, after that | ||||
| 	// show flags with the final logging configuration. | ||||
| 	if err := logsapi.ValidateAndApply(opts.Logs, fg); err != nil { | ||||
| @@ -216,11 +213,11 @@ func Run(ctx context.Context, cc *schedulerserverconfig.CompletedConfig, sched * | ||||
| 	readyzChecks = append(readyzChecks, handlerSyncCheck) | ||||
|  | ||||
| 	if cc.LeaderElection != nil && utilfeature.DefaultFeatureGate.Enabled(kubefeatures.CoordinatedLeaderElection) { | ||||
| 		binaryVersion, err := semver.ParseTolerant(featuregate.DefaultComponentGlobalsRegistry.EffectiveVersionFor(featuregate.DefaultKubeComponent).BinaryVersion().String()) | ||||
| 		binaryVersion, err := semver.ParseTolerant(cc.ComponentGlobalsRegistry.EffectiveVersionFor(basecompatibility.DefaultKubeComponent).BinaryVersion().String()) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		emulationVersion, err := semver.ParseTolerant(featuregate.DefaultComponentGlobalsRegistry.EffectiveVersionFor(featuregate.DefaultKubeComponent).EmulationVersion().String()) | ||||
| 		emulationVersion, err := semver.ParseTolerant(cc.ComponentGlobalsRegistry.EffectiveVersionFor(basecompatibility.DefaultKubeComponent).EmulationVersion().String()) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
|   | ||||
| @@ -36,10 +36,10 @@ import ( | ||||
| 	utilruntime "k8s.io/apimachinery/pkg/util/runtime" | ||||
| 	"k8s.io/apimachinery/pkg/util/version" | ||||
| 	"k8s.io/apiserver/pkg/util/feature" | ||||
| 	basecompatibility "k8s.io/component-base/compatibility" | ||||
| 	componentbaseconfig "k8s.io/component-base/config" | ||||
| 	"k8s.io/component-base/featuregate" | ||||
| 	featuregatetesting "k8s.io/component-base/featuregate/testing" | ||||
| 	utilversion "k8s.io/component-base/version" | ||||
| 	configv1 "k8s.io/kube-scheduler/config/v1" | ||||
| 	"k8s.io/kubernetes/cmd/kube-scheduler/app/options" | ||||
| 	"k8s.io/kubernetes/pkg/features" | ||||
| @@ -438,12 +438,8 @@ leaderElection: | ||||
| 			for k, v := range tc.restoreFeatures { | ||||
| 				featuregatetesting.SetFeatureGateDuringTest(t, feature.DefaultFeatureGate, k, v) | ||||
| 			} | ||||
| 			componentGlobalsRegistry := featuregate.DefaultComponentGlobalsRegistry | ||||
| 			t.Cleanup(func() { | ||||
| 				componentGlobalsRegistry.Reset() | ||||
| 			}) | ||||
| 			componentGlobalsRegistry.Reset() | ||||
| 			verKube := utilversion.NewEffectiveVersion("1.32") | ||||
| 			componentGlobalsRegistry := basecompatibility.NewComponentGlobalsRegistry() | ||||
| 			verKube := basecompatibility.NewEffectiveVersionFromString("1.32", "1.31", "1.31") | ||||
| 			fg := feature.DefaultFeatureGate.DeepCopy() | ||||
| 			utilruntime.Must(fg.AddVersioned(map[featuregate.Feature]featuregate.VersionedSpecs{ | ||||
| 				"kubeA": { | ||||
| @@ -454,10 +450,10 @@ leaderElection: | ||||
| 					{Version: version.MustParse("1.31"), Default: false, PreRelease: featuregate.Alpha}, | ||||
| 				}, | ||||
| 			})) | ||||
| 			utilruntime.Must(componentGlobalsRegistry.Register(featuregate.DefaultKubeComponent, verKube, fg)) | ||||
| 			utilruntime.Must(componentGlobalsRegistry.Register(basecompatibility.DefaultKubeComponent, verKube, fg)) | ||||
|  | ||||
| 			fs := pflag.NewFlagSet("test", pflag.PanicOnError) | ||||
| 			opts := options.NewOptions() | ||||
| 			opts := options.NewOptionsWithComponentGlobalsRegistry(componentGlobalsRegistry) | ||||
|  | ||||
| 			// use listeners instead of static ports so parallel test runs don't conflict | ||||
| 			opts.SecureServing.Listener = makeListener(t) | ||||
|   | ||||
| @@ -189,8 +189,7 @@ func BuildGenericConfig( | ||||
| 		s.Etcd.StorageConfig.Transport.TracerProvider = noopoteltrace.NewTracerProvider() | ||||
| 	} | ||||
|  | ||||
| 	storageFactoryConfig := kubeapiserver.NewStorageFactoryConfig() | ||||
| 	storageFactoryConfig.CurrentVersion = genericConfig.EffectiveVersion | ||||
| 	storageFactoryConfig := kubeapiserver.NewStorageFactoryConfigEffectiveVersion(genericConfig.EffectiveVersion) | ||||
| 	storageFactoryConfig.APIResourceConfig = genericConfig.MergedResourceConfig | ||||
| 	storageFactoryConfig.DefaultResourceEncoding.SetEffectiveVersion(genericConfig.EffectiveVersion) | ||||
| 	storageFactory, lastErr = storageFactoryConfig.Complete(s.Etcd).New() | ||||
|   | ||||
| @@ -40,10 +40,10 @@ import ( | ||||
| 	auditbuffered "k8s.io/apiserver/plugin/pkg/audit/buffered" | ||||
| 	audittruncate "k8s.io/apiserver/plugin/pkg/audit/truncate" | ||||
| 	cliflag "k8s.io/component-base/cli/flag" | ||||
| 	basecompatibility "k8s.io/component-base/compatibility" | ||||
| 	"k8s.io/component-base/featuregate" | ||||
| 	"k8s.io/component-base/logs" | ||||
| 	"k8s.io/component-base/metrics" | ||||
| 	utilversion "k8s.io/component-base/version" | ||||
| 	kubeoptions "k8s.io/kubernetes/pkg/kubeapiserver/options" | ||||
| 	"k8s.io/kubernetes/pkg/serviceaccount" | ||||
| 	v1alpha1testing "k8s.io/kubernetes/pkg/serviceaccount/externaljwt/plugin/testing/v1alpha1" | ||||
| @@ -51,13 +51,11 @@ import ( | ||||
| ) | ||||
|  | ||||
| func TestAddFlags(t *testing.T) { | ||||
| 	componentGlobalsRegistry := featuregate.DefaultComponentGlobalsRegistry | ||||
| 	t.Cleanup(func() { | ||||
| 		componentGlobalsRegistry.Reset() | ||||
| 	}) | ||||
| 	componentGlobalsRegistry := basecompatibility.NewComponentGlobalsRegistry() | ||||
| 	fs := pflag.NewFlagSet("addflagstest", pflag.PanicOnError) | ||||
| 	utilruntime.Must(componentGlobalsRegistry.Register("test", utilversion.NewEffectiveVersion("1.32"), featuregate.NewFeatureGate())) | ||||
| 	utilruntime.Must(componentGlobalsRegistry.Register("test", basecompatibility.NewEffectiveVersionFromString("1.32", "1.31", "1.31"), featuregate.NewFeatureGate())) | ||||
| 	s := NewOptions() | ||||
| 	s.GenericServerRunOptions.ComponentGlobalsRegistry = componentGlobalsRegistry | ||||
| 	var fss cliflag.NamedFlagSets | ||||
| 	s.AddFlags(&fss) | ||||
| 	for _, f := range fss.FlagSets { | ||||
| @@ -141,7 +139,7 @@ func TestAddFlags(t *testing.T) { | ||||
| 			JSONPatchMaxCopyBytes:        int64(3 * 1024 * 1024), | ||||
| 			MaxRequestBodyBytes:          int64(3 * 1024 * 1024), | ||||
| 			ComponentGlobalsRegistry:     componentGlobalsRegistry, | ||||
| 			ComponentName:                featuregate.DefaultKubeComponent, | ||||
| 			ComponentName:                basecompatibility.DefaultKubeComponent, | ||||
| 		}, | ||||
| 		Admission: &kubeoptions.AdmissionOptions{ | ||||
| 			GenericAdmission: &apiserveroptions.AdmissionOptions{ | ||||
|   | ||||
| @@ -25,6 +25,7 @@ import ( | ||||
| 	kubeapiserveradmission "k8s.io/apiserver/pkg/admission" | ||||
| 	genericoptions "k8s.io/apiserver/pkg/server/options" | ||||
| 	utilfeature "k8s.io/apiserver/pkg/util/feature" | ||||
| 	basecompatibility "k8s.io/component-base/compatibility" | ||||
| 	"k8s.io/component-base/featuregate" | ||||
| 	basemetrics "k8s.io/component-base/metrics" | ||||
| 	"k8s.io/kubernetes/pkg/features" | ||||
| @@ -202,7 +203,7 @@ func TestValidateOptions(t *testing.T) { | ||||
| 			name:         "validate master count equal 0", | ||||
| 			expectErrors: true, | ||||
| 			options: &Options{ | ||||
| 				GenericServerRunOptions: &genericoptions.ServerRunOptions{ComponentGlobalsRegistry: featuregate.DefaultComponentGlobalsRegistry}, | ||||
| 				GenericServerRunOptions: &genericoptions.ServerRunOptions{ComponentGlobalsRegistry: basecompatibility.NewComponentGlobalsRegistry()}, | ||||
| 				Etcd:                    &genericoptions.EtcdOptions{}, | ||||
| 				SecureServing:           &genericoptions.SecureServingOptionsWithLoopback{}, | ||||
| 				Audit:                   &genericoptions.AuditOptions{}, | ||||
| @@ -229,7 +230,7 @@ func TestValidateOptions(t *testing.T) { | ||||
| 			name:         "validate token request enable not attempted", | ||||
| 			expectErrors: true, | ||||
| 			options: &Options{ | ||||
| 				GenericServerRunOptions: &genericoptions.ServerRunOptions{ComponentGlobalsRegistry: featuregate.DefaultComponentGlobalsRegistry}, | ||||
| 				GenericServerRunOptions: &genericoptions.ServerRunOptions{ComponentGlobalsRegistry: basecompatibility.NewComponentGlobalsRegistry()}, | ||||
| 				Etcd:                    &genericoptions.EtcdOptions{}, | ||||
| 				SecureServing:           &genericoptions.SecureServingOptionsWithLoopback{}, | ||||
| 				Audit:                   &genericoptions.AuditOptions{}, | ||||
|   | ||||
| @@ -161,7 +161,7 @@ func (c completedConfig) New(name string, delegationTarget genericapiserver.Dele | ||||
| 	} | ||||
|  | ||||
| 	if utilfeature.DefaultFeatureGate.Enabled(zpagesfeatures.ComponentStatusz) { | ||||
| 		statusz.Install(s.GenericAPIServer.Handler.NonGoRestfulMux, name, statusz.NewRegistry()) | ||||
| 		statusz.Install(s.GenericAPIServer.Handler.NonGoRestfulMux, name, statusz.NewRegistry(c.Generic.EffectiveVersion)) | ||||
| 	} | ||||
|  | ||||
| 	if utilfeature.DefaultFeatureGate.Enabled(apiserverfeatures.CoordinatedLeaderElection) { | ||||
|   | ||||
| @@ -55,6 +55,7 @@ import ( | ||||
| 	"k8s.io/apiserver/pkg/server/resourceconfig" | ||||
| 	serverstorage "k8s.io/apiserver/pkg/server/storage" | ||||
| 	etcd3testing "k8s.io/apiserver/pkg/storage/etcd3/testing" | ||||
| 	"k8s.io/apiserver/pkg/util/compatibility" | ||||
| 	"k8s.io/apiserver/pkg/util/openapi" | ||||
| 	"k8s.io/client-go/discovery" | ||||
| 	"k8s.io/client-go/informers" | ||||
| @@ -103,7 +104,7 @@ func setUp(t *testing.T) (*etcd3testing.EtcdTestServer, Config, *assert.Assertio | ||||
| 		}, | ||||
| 	} | ||||
|  | ||||
| 	config.ControlPlane.Generic.EffectiveVersion = utilversion.DefaultKubeEffectiveVersion() | ||||
| 	config.ControlPlane.Generic.EffectiveVersion = compatibility.DefaultKubeEffectiveVersionForTest() | ||||
| 	storageFactoryConfig := kubeapiserver.NewStorageFactoryConfig() | ||||
| 	storageFactoryConfig.DefaultResourceEncoding.SetEffectiveVersion(config.ControlPlane.Generic.EffectiveVersion) | ||||
| 	storageConfig.StorageObjectCountTracker = config.ControlPlane.Generic.StorageObjectCountTracker | ||||
| @@ -241,7 +242,7 @@ func TestVersion(t *testing.T) { | ||||
| 		t.Errorf("unexpected error: %v", err) | ||||
| 	} | ||||
| 	expectedInfo := utilversion.Get() | ||||
| 	kubeVersion := utilversion.DefaultKubeEffectiveVersion().BinaryVersion() | ||||
| 	kubeVersion := compatibility.DefaultKubeEffectiveVersionForTest().BinaryVersion() | ||||
| 	expectedInfo.Major = fmt.Sprintf("%d", kubeVersion.Major()) | ||||
| 	expectedInfo.Minor = fmt.Sprintf("%d", kubeVersion.Minor()) | ||||
|  | ||||
|   | ||||
| @@ -25,7 +25,8 @@ import ( | ||||
| 	"k8s.io/apiserver/pkg/server/resourceconfig" | ||||
| 	serverstorage "k8s.io/apiserver/pkg/server/storage" | ||||
| 	"k8s.io/apiserver/pkg/storage/storagebackend" | ||||
| 	version "k8s.io/component-base/version" | ||||
| 	"k8s.io/apiserver/pkg/util/compatibility" | ||||
| 	basecompatibility "k8s.io/component-base/compatibility" | ||||
| 	"k8s.io/kubernetes/pkg/api/legacyscheme" | ||||
| 	"k8s.io/kubernetes/pkg/apis/admissionregistration" | ||||
| 	"k8s.io/kubernetes/pkg/apis/apps" | ||||
| @@ -61,6 +62,11 @@ func DefaultWatchCacheSizes() map[schema.GroupResource]int { | ||||
|  | ||||
| // NewStorageFactoryConfig returns a new StorageFactoryConfig set up with necessary resource overrides. | ||||
| func NewStorageFactoryConfig() *StorageFactoryConfig { | ||||
| 	return NewStorageFactoryConfigEffectiveVersion(compatibility.DefaultComponentGlobalsRegistry.EffectiveVersionFor(basecompatibility.DefaultKubeComponent)) | ||||
| } | ||||
|  | ||||
| // NewStorageFactoryConfigEffectiveVersion returns a new StorageFactoryConfig set up with necessary resource overrides for a given EffectiveVersion. | ||||
| func NewStorageFactoryConfigEffectiveVersion(effectiveVersion basecompatibility.EffectiveVersion) *StorageFactoryConfig { | ||||
| 	resources := []schema.GroupVersionResource{ | ||||
| 		// If a resource has to be stored in a version that is not the | ||||
| 		// latest, then it can be listed here. Usually this is the case | ||||
| @@ -87,7 +93,7 @@ func NewStorageFactoryConfig() *StorageFactoryConfig { | ||||
|  | ||||
| 	return &StorageFactoryConfig{ | ||||
| 		Serializer:                legacyscheme.Codecs, | ||||
| 		DefaultResourceEncoding:   serverstorage.NewDefaultResourceEncodingConfig(legacyscheme.Scheme), | ||||
| 		DefaultResourceEncoding:   serverstorage.NewDefaultResourceEncodingConfigForEffectiveVersion(legacyscheme.Scheme, effectiveVersion), | ||||
| 		ResourceEncodingOverrides: resources, | ||||
| 	} | ||||
| } | ||||
| @@ -101,7 +107,6 @@ type StorageFactoryConfig struct { | ||||
| 	Serializer                runtime.StorageSerializer | ||||
| 	ResourceEncodingOverrides []schema.GroupVersionResource | ||||
| 	EtcdServersOverrides      []string | ||||
| 	CurrentVersion            version.EffectiveVersion | ||||
| } | ||||
|  | ||||
| // Complete completes the StorageFactoryConfig with provided etcdOptions returning completedStorageFactoryConfig. | ||||
|   | ||||
| @@ -59,6 +59,7 @@ import ( | ||||
| 	"k8s.io/apiserver/pkg/server/healthz" | ||||
| 	"k8s.io/apiserver/pkg/server/httplog" | ||||
| 	"k8s.io/apiserver/pkg/server/routes" | ||||
| 	"k8s.io/apiserver/pkg/util/compatibility" | ||||
| 	utilfeature "k8s.io/apiserver/pkg/util/feature" | ||||
| 	"k8s.io/apiserver/pkg/util/flushwriter" | ||||
| 	"k8s.io/component-base/configz" | ||||
| @@ -565,7 +566,7 @@ func (s *Server) InstallDebuggingHandlers() { | ||||
|  | ||||
| 	if utilfeature.DefaultFeatureGate.Enabled(zpagesfeatures.ComponentStatusz) { | ||||
| 		s.addMetricsBucketMatcher("statusz") | ||||
| 		statusz.Install(s.restfulCont, ComponentKubelet, statusz.NewRegistry()) | ||||
| 		statusz.Install(s.restfulCont, ComponentKubelet, statusz.NewRegistry(compatibility.DefaultBuildEffectiveVersion())) | ||||
| 	} | ||||
|  | ||||
| 	// The /runningpods endpoint is used for testing only. | ||||
|   | ||||
| @@ -24,7 +24,6 @@ import ( | ||||
|  | ||||
| 	"k8s.io/apiextensions-apiserver/pkg/cmd/server/options" | ||||
| 	genericapiserver "k8s.io/apiserver/pkg/server" | ||||
| 	"k8s.io/component-base/featuregate" | ||||
| ) | ||||
|  | ||||
| func NewServerCommand(ctx context.Context, out, errOut io.Writer) *cobra.Command { | ||||
| @@ -34,7 +33,7 @@ func NewServerCommand(ctx context.Context, out, errOut io.Writer) *cobra.Command | ||||
| 		Short: "Launch an API extensions API server", | ||||
| 		Long:  "Launch an API extensions API server", | ||||
| 		PersistentPreRunE: func(*cobra.Command, []string) error { | ||||
| 			return featuregate.DefaultComponentGlobalsRegistry.Set() | ||||
| 			return o.ServerRunOptions.ComponentGlobalsRegistry.Set() | ||||
| 		}, | ||||
| 		RunE: func(c *cobra.Command, args []string) error { | ||||
| 			if err := o.Complete(); err != nil { | ||||
|   | ||||
| @@ -31,18 +31,18 @@ import ( | ||||
| 	extensionsapiserver "k8s.io/apiextensions-apiserver/pkg/apiserver" | ||||
| 	"k8s.io/apiextensions-apiserver/pkg/cmd/server/options" | ||||
| 	generatedopenapi "k8s.io/apiextensions-apiserver/pkg/generated/openapi" | ||||
| 	utilruntime "k8s.io/apimachinery/pkg/util/runtime" | ||||
| 	"k8s.io/apimachinery/pkg/util/wait" | ||||
| 	openapinamer "k8s.io/apiserver/pkg/endpoints/openapi" | ||||
| 	genericapiserver "k8s.io/apiserver/pkg/server" | ||||
| 	"k8s.io/apiserver/pkg/storage/storagebackend" | ||||
| 	"k8s.io/apiserver/pkg/util/compatibility" | ||||
| 	utilfeature "k8s.io/apiserver/pkg/util/feature" | ||||
| 	"k8s.io/apiserver/pkg/util/openapi" | ||||
| 	"k8s.io/client-go/kubernetes" | ||||
| 	restclient "k8s.io/client-go/rest" | ||||
| 	"k8s.io/component-base/featuregate" | ||||
| 	basecompatibility "k8s.io/component-base/compatibility" | ||||
| 	featuregatetesting "k8s.io/component-base/featuregate/testing" | ||||
| 	logsapi "k8s.io/component-base/logs/api/v1" | ||||
| 	utilversion "k8s.io/component-base/version" | ||||
| 	"k8s.io/klog/v2" | ||||
| ) | ||||
|  | ||||
| @@ -124,15 +124,17 @@ func StartTestServer(t Logger, _ *TestServerInstanceOptions, customFlags []strin | ||||
|  | ||||
| 	fs := pflag.NewFlagSet("test", pflag.PanicOnError) | ||||
|  | ||||
| 	featureGate := utilfeature.DefaultMutableFeatureGate | ||||
|  | ||||
| 	// Configure the effective version. | ||||
| 	effectiveVersion := utilversion.DefaultKubeEffectiveVersion() | ||||
| 	effectiveVersion.SetEmulationVersion(featureGate.EmulationVersion()) | ||||
|  | ||||
| 	featuregate.DefaultComponentGlobalsRegistry.Reset() | ||||
| 	utilruntime.Must(featuregate.DefaultComponentGlobalsRegistry.Register(featuregate.DefaultKubeComponent, effectiveVersion, featureGate)) | ||||
| 	s := options.NewCustomResourceDefinitionsServerOptions(os.Stdout, os.Stderr) | ||||
|  | ||||
| 	// set up new instance of ComponentGlobalsRegistry instead of using the DefaultComponentGlobalsRegistry to avoid contention in parallel tests. | ||||
| 	featureGate := utilfeature.DefaultMutableFeatureGate.DeepCopy() | ||||
| 	effectiveVersion := compatibility.DefaultKubeEffectiveVersionForTest() | ||||
| 	effectiveVersion.SetEmulationVersion(featureGate.EmulationVersion()) | ||||
| 	componentGlobalsRegistry := basecompatibility.NewComponentGlobalsRegistry() | ||||
| 	if err := componentGlobalsRegistry.Register(basecompatibility.DefaultKubeComponent, effectiveVersion, featureGate); err != nil { | ||||
| 		return result, err | ||||
| 	} | ||||
| 	s.ServerRunOptions.ComponentGlobalsRegistry = componentGlobalsRegistry | ||||
| 	s.AddFlags(fs) | ||||
|  | ||||
| 	s.RecommendedOptions.SecureServing.Listener, s.RecommendedOptions.SecureServing.BindPort, err = createLocalhostListenerOnFreePort() | ||||
| @@ -155,8 +157,18 @@ func StartTestServer(t Logger, _ *TestServerInstanceOptions, customFlags []strin | ||||
|  | ||||
| 	fs.Parse(customFlags) | ||||
|  | ||||
| 	if err := featuregate.DefaultComponentGlobalsRegistry.Set(); err != nil { | ||||
| 		return result, err | ||||
| 	if err := componentGlobalsRegistry.Set(); err != nil { | ||||
| 		return result, fmt.Errorf("%w\nIf you are using SetFeatureGate*DuringTest, try using --emulated-version and --feature-gates flags instead", err) | ||||
| 	} | ||||
| 	// If the local ComponentGlobalsRegistry is changed by the flags, | ||||
| 	// we need to copy the new feature values back to the DefaultFeatureGate because most feature checks still use the DefaultFeatureGate. | ||||
| 	if !featureGate.EmulationVersion().EqualTo(utilfeature.DefaultMutableFeatureGate.EmulationVersion()) { | ||||
| 		featuregatetesting.SetFeatureGateEmulationVersionDuringTest(t.(featuregatetesting.TB), utilfeature.DefaultMutableFeatureGate, effectiveVersion.EmulationVersion()) | ||||
| 	} | ||||
| 	for f := range utilfeature.DefaultMutableFeatureGate.GetAll() { | ||||
| 		if featureGate.Enabled(f) != utilfeature.DefaultFeatureGate.Enabled(f) { | ||||
| 			featuregatetesting.SetFeatureGateDuringTest(t.(featuregatetesting.TB), utilfeature.DefaultFeatureGate, f, featureGate.Enabled(f)) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if err := s.Complete(); err != nil { | ||||
|   | ||||
| @@ -41,7 +41,6 @@ import ( | ||||
| 	"k8s.io/apimachinery/pkg/runtime" | ||||
| 	"k8s.io/apimachinery/pkg/runtime/schema" | ||||
| 	"k8s.io/apimachinery/pkg/util/sets" | ||||
| 	"k8s.io/apimachinery/pkg/util/version" | ||||
| 	"k8s.io/apimachinery/pkg/util/wait" | ||||
| 	"k8s.io/apimachinery/pkg/watch" | ||||
| 	utilfeature "k8s.io/apiserver/pkg/util/feature" | ||||
| @@ -593,9 +592,7 @@ func TestFieldSelectorOpenAPI(t *testing.T) { | ||||
|  | ||||
| func TestFieldSelectorDropFields(t *testing.T) { | ||||
| 	_, ctx := ktesting.NewTestContext(t) | ||||
| 	featuregatetesting.SetFeatureGateEmulationVersionDuringTest(t, utilfeature.DefaultFeatureGate, version.MustParse("1.31")) | ||||
| 	featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, apiextensionsfeatures.CustomResourceFieldSelectors, false) | ||||
| 	tearDown, apiExtensionClient, _, err := fixtures.StartDefaultServerWithClients(t) | ||||
| 	tearDown, apiExtensionClient, _, err := fixtures.StartDefaultServerWithClients(t, "--emulated-version=1.31", "--feature-gates=CustomResourceFieldSelectors=false") | ||||
| 	if err != nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
| @@ -675,9 +672,8 @@ func TestFieldSelectorDropFields(t *testing.T) { | ||||
| } | ||||
|  | ||||
| func TestFieldSelectorDisablement(t *testing.T) { | ||||
| 	featuregatetesting.SetFeatureGateEmulationVersionDuringTest(t, utilfeature.DefaultFeatureGate, version.MustParse("1.31")) | ||||
| 	_, ctx := ktesting.NewTestContext(t) | ||||
| 	tearDown, config, _, err := fixtures.StartDefaultServer(t) | ||||
| 	tearDown, config, _, err := fixtures.StartDefaultServer(t, "--emulated-version=1.31") | ||||
| 	if err != nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
|   | ||||
| @@ -34,7 +34,6 @@ import ( | ||||
| 	"github.com/stretchr/testify/assert" | ||||
| 	asn1util "k8s.io/apimachinery/pkg/apis/asn1" | ||||
| 	"k8s.io/apimachinery/pkg/util/sets" | ||||
| 	"k8s.io/apimachinery/pkg/util/version" | ||||
| 	"k8s.io/apiserver/pkg/authentication/authenticator" | ||||
| 	"k8s.io/apiserver/pkg/authentication/user" | ||||
| 	"k8s.io/apiserver/pkg/features" | ||||
| @@ -799,9 +798,6 @@ func TestX509(t *testing.T) { | ||||
| 			ExpectErr: false, | ||||
| 			setupFunc: func(t *testing.T) { | ||||
| 				t.Helper() | ||||
| 				// This statement can be removed once utilversion.DefaultKubeEffectiveVersion() is >= 1.33 | ||||
| 				featuregatetesting.SetFeatureGateEmulationVersionDuringTest(t, feature.DefaultFeatureGate, version.MustParse("1.33")) | ||||
|  | ||||
| 			}, | ||||
| 		}, | ||||
| 		"common name and empty UID with feature gate disabled": { | ||||
| @@ -822,8 +818,6 @@ func TestX509(t *testing.T) { | ||||
| 			ExpectErr: false, | ||||
| 			setupFunc: func(t *testing.T) { | ||||
| 				t.Helper() | ||||
| 				// This statement can be removed once utilversion.DefaultKubeEffectiveVersion() is >= 1.33 | ||||
| 				featuregatetesting.SetFeatureGateEmulationVersionDuringTest(t, feature.DefaultFeatureGate, version.MustParse("1.33")) | ||||
| 				featuregatetesting.SetFeatureGateDuringTest(t, feature.DefaultFeatureGate, features.AllowParsingUserUIDFromCertAuth, false) | ||||
| 			}, | ||||
| 		}, | ||||
| @@ -836,9 +830,6 @@ func TestX509(t *testing.T) { | ||||
| 			ExpectErrMsg: regexp.MustCompile("UID cannot be an empty string"), | ||||
| 			setupFunc: func(t *testing.T) { | ||||
| 				t.Helper() | ||||
| 				// This statement can be removed once utilversion.DefaultKubeEffectiveVersion() is >= 1.33 | ||||
| 				featuregatetesting.SetFeatureGateEmulationVersionDuringTest(t, feature.DefaultFeatureGate, version.MustParse("1.33")) | ||||
|  | ||||
| 			}, | ||||
| 		}, | ||||
| 		"ca with non-string UID": { | ||||
| @@ -850,9 +841,6 @@ func TestX509(t *testing.T) { | ||||
| 			ExpectErrMsg: regexp.MustCompile("unable to parse UID into a string"), | ||||
| 			setupFunc: func(t *testing.T) { | ||||
| 				t.Helper() | ||||
| 				// This statement can be removed once utilversion.DefaultKubeEffectiveVersion() is >= 1.33 | ||||
| 				featuregatetesting.SetFeatureGateEmulationVersionDuringTest(t, feature.DefaultFeatureGate, version.MustParse("1.33")) | ||||
|  | ||||
| 			}, | ||||
| 		}, | ||||
| 		"ca with multiple UIDs": { | ||||
| @@ -866,9 +854,6 @@ func TestX509(t *testing.T) { | ||||
| 			ExpectErrMsg: regexp.MustCompile("expected 1 UID, but found multiple"), | ||||
| 			setupFunc: func(t *testing.T) { | ||||
| 				t.Helper() | ||||
| 				// This statement can be removed once utilversion.DefaultKubeEffectiveVersion() is >= 1.33 | ||||
| 				featuregatetesting.SetFeatureGateEmulationVersionDuringTest(t, feature.DefaultFeatureGate, version.MustParse("1.33")) | ||||
|  | ||||
| 			}, | ||||
| 		}, | ||||
| 		"ca with multiple organizations": { | ||||
|   | ||||
| @@ -32,9 +32,9 @@ import ( | ||||
| 	celconfig "k8s.io/apiserver/pkg/apis/cel" | ||||
| 	"k8s.io/apiserver/pkg/cel/library" | ||||
| 	genericfeatures "k8s.io/apiserver/pkg/features" | ||||
| 	"k8s.io/apiserver/pkg/util/compatibility" | ||||
| 	utilfeature "k8s.io/apiserver/pkg/util/feature" | ||||
| 	"k8s.io/component-base/featuregate" | ||||
| 	utilversion "k8s.io/component-base/version" | ||||
| 	basecompatibility "k8s.io/component-base/compatibility" | ||||
| ) | ||||
|  | ||||
| // DefaultCompatibilityVersion returns a default compatibility version for use with EnvSet | ||||
| @@ -50,9 +50,9 @@ import ( | ||||
| // A default version number equal to the current Kubernetes major.minor version | ||||
| // indicates fast forward CEL features that can be used when rollback is no longer needed. | ||||
| func DefaultCompatibilityVersion() *version.Version { | ||||
| 	effectiveVer := featuregate.DefaultComponentGlobalsRegistry.EffectiveVersionFor(featuregate.DefaultKubeComponent) | ||||
| 	effectiveVer := compatibility.DefaultComponentGlobalsRegistry.EffectiveVersionFor(basecompatibility.DefaultKubeComponent) | ||||
| 	if effectiveVer == nil { | ||||
| 		effectiveVer = utilversion.DefaultKubeEffectiveVersion() | ||||
| 		effectiveVer = compatibility.DefaultBuildEffectiveVersion() | ||||
| 	} | ||||
| 	return effectiveVer.MinCompatibilityVersion() | ||||
| } | ||||
|   | ||||
| @@ -73,12 +73,12 @@ import ( | ||||
| 	flowcontrolrequest "k8s.io/apiserver/pkg/util/flowcontrol/request" | ||||
| 	"k8s.io/client-go/informers" | ||||
| 	restclient "k8s.io/client-go/rest" | ||||
| 	basecompatibility "k8s.io/component-base/compatibility" | ||||
| 	"k8s.io/component-base/featuregate" | ||||
| 	"k8s.io/component-base/logs" | ||||
| 	"k8s.io/component-base/metrics/features" | ||||
| 	"k8s.io/component-base/metrics/prometheus/slis" | ||||
| 	"k8s.io/component-base/tracing" | ||||
| 	utilversion "k8s.io/component-base/version" | ||||
| 	"k8s.io/component-base/zpages/flagz" | ||||
| 	"k8s.io/klog/v2" | ||||
| 	openapicommon "k8s.io/kube-openapi/pkg/common" | ||||
| @@ -153,7 +153,7 @@ type Config struct { | ||||
|  | ||||
| 	// EffectiveVersion determines which apis and features are available | ||||
| 	// based on when the api/feature lifecyle. | ||||
| 	EffectiveVersion utilversion.EffectiveVersion | ||||
| 	EffectiveVersion basecompatibility.EffectiveVersion | ||||
| 	// FeatureGate is a way to plumb feature gate through if you have them. | ||||
| 	FeatureGate featuregate.FeatureGate | ||||
| 	// AuditBackend is where audit events are sent to. | ||||
|   | ||||
| @@ -47,9 +47,9 @@ import ( | ||||
| 	"k8s.io/client-go/informers" | ||||
| 	"k8s.io/client-go/kubernetes/fake" | ||||
| 	"k8s.io/client-go/rest" | ||||
| 	basecompatibility "k8s.io/component-base/compatibility" | ||||
| 	featuregatetesting "k8s.io/component-base/featuregate/testing" | ||||
| 	"k8s.io/component-base/tracing" | ||||
| 	utilversion "k8s.io/component-base/version" | ||||
| 	"k8s.io/klog/v2/ktesting" | ||||
| 	netutils "k8s.io/utils/net" | ||||
| ) | ||||
| @@ -124,7 +124,7 @@ func TestNewWithDelegate(t *testing.T) { | ||||
| 	delegateConfig.PublicAddress = netutils.ParseIPSloppy("192.168.10.4") | ||||
| 	delegateConfig.LegacyAPIGroupPrefixes = sets.NewString("/api") | ||||
| 	delegateConfig.LoopbackClientConfig = &rest.Config{} | ||||
| 	delegateConfig.EffectiveVersion = utilversion.NewEffectiveVersion("") | ||||
| 	delegateConfig.EffectiveVersion = basecompatibility.NewEffectiveVersionFromString("", "", "") | ||||
| 	clientset := fake.NewSimpleClientset() | ||||
| 	if clientset == nil { | ||||
| 		t.Fatal("unable to create fake client set") | ||||
| @@ -157,7 +157,7 @@ func TestNewWithDelegate(t *testing.T) { | ||||
| 	wrappingConfig.PublicAddress = netutils.ParseIPSloppy("192.168.10.4") | ||||
| 	wrappingConfig.LegacyAPIGroupPrefixes = sets.NewString("/api") | ||||
| 	wrappingConfig.LoopbackClientConfig = &rest.Config{} | ||||
| 	wrappingConfig.EffectiveVersion = utilversion.NewEffectiveVersion("") | ||||
| 	wrappingConfig.EffectiveVersion = basecompatibility.NewEffectiveVersionFromString("", "", "") | ||||
|  | ||||
| 	wrappingConfig.HealthzChecks = append(wrappingConfig.HealthzChecks, healthz.NamedCheck("wrapping-health", func(r *http.Request) error { | ||||
| 		return fmt.Errorf("wrapping failed healthcheck") | ||||
| @@ -434,7 +434,7 @@ func TestNewFeatureGatedSerializer(t *testing.T) { | ||||
| 		} | ||||
| 	}))) | ||||
| 	config.ExternalAddress = "192.168.10.4:443" | ||||
| 	config.EffectiveVersion = utilversion.NewEffectiveVersion("") | ||||
| 	config.EffectiveVersion = basecompatibility.NewEffectiveVersionFromString("", "", "") | ||||
| 	config.LoopbackClientConfig = &rest.Config{} | ||||
|  | ||||
| 	if _, err := config.Complete(nil).New("test", NewEmptyDelegate()); err != nil { | ||||
|   | ||||
| @@ -54,8 +54,8 @@ import ( | ||||
| 	"k8s.io/apiserver/pkg/storageversion" | ||||
| 	utilfeature "k8s.io/apiserver/pkg/util/feature" | ||||
| 	restclient "k8s.io/client-go/rest" | ||||
| 	basecompatibility "k8s.io/component-base/compatibility" | ||||
| 	"k8s.io/component-base/featuregate" | ||||
| 	utilversion "k8s.io/component-base/version" | ||||
| 	"k8s.io/klog/v2" | ||||
| 	openapibuilder3 "k8s.io/kube-openapi/pkg/builder3" | ||||
| 	openapicommon "k8s.io/kube-openapi/pkg/common" | ||||
| @@ -244,7 +244,7 @@ type GenericAPIServer struct { | ||||
|  | ||||
| 	// EffectiveVersion determines which apis and features are available | ||||
| 	// based on when the api/feature lifecyle. | ||||
| 	EffectiveVersion utilversion.EffectiveVersion | ||||
| 	EffectiveVersion basecompatibility.EffectiveVersion | ||||
| 	// FeatureGate is a way to plumb feature gate through if you have them. | ||||
| 	FeatureGate featuregate.FeatureGate | ||||
|  | ||||
|   | ||||
| @@ -38,6 +38,7 @@ import ( | ||||
| 	"k8s.io/apimachinery/pkg/runtime/serializer" | ||||
| 	utilruntime "k8s.io/apimachinery/pkg/util/runtime" | ||||
| 	"k8s.io/apimachinery/pkg/util/sets" | ||||
| 	utilversion "k8s.io/apimachinery/pkg/util/version" | ||||
| 	"k8s.io/apimachinery/pkg/util/wait" | ||||
| 	"k8s.io/apimachinery/pkg/version" | ||||
| 	"k8s.io/apiserver/pkg/apis/example" | ||||
| @@ -52,7 +53,7 @@ import ( | ||||
| 	"k8s.io/client-go/informers" | ||||
| 	"k8s.io/client-go/kubernetes/fake" | ||||
| 	restclient "k8s.io/client-go/rest" | ||||
| 	utilversion "k8s.io/component-base/version" | ||||
| 	basecompatibility "k8s.io/component-base/compatibility" | ||||
| 	"k8s.io/klog/v2/ktesting" | ||||
| 	kubeopenapi "k8s.io/kube-openapi/pkg/common" | ||||
| 	"k8s.io/kube-openapi/pkg/validation/spec" | ||||
| @@ -138,7 +139,7 @@ func setUp(t *testing.T) (Config, *assert.Assertions) { | ||||
| 	if clientset == nil { | ||||
| 		t.Fatal("unable to create fake client set") | ||||
| 	} | ||||
| 	config.EffectiveVersion = utilversion.NewEffectiveVersion("") | ||||
| 	config.EffectiveVersion = basecompatibility.NewEffectiveVersionFromString("", "", "") | ||||
| 	config.OpenAPIConfig = DefaultOpenAPIConfig(testGetOpenAPIDefinitions, openapinamer.NewDefinitionNamer(runtime.NewScheme())) | ||||
| 	config.OpenAPIConfig.Info.Version = "unversioned" | ||||
| 	config.OpenAPIV3Config = DefaultOpenAPIV3Config(testGetOpenAPIDefinitions, openapinamer.NewDefinitionNamer(runtime.NewScheme())) | ||||
| @@ -460,8 +461,8 @@ func TestNotRestRoutesHaveAuth(t *testing.T) { | ||||
| 	config.EnableProfiling = true | ||||
|  | ||||
| 	kubeVersion := fakeVersion() | ||||
| 	effectiveVersion := utilversion.NewEffectiveVersion(kubeVersion.String()) | ||||
| 	effectiveVersion.Set(effectiveVersion.BinaryVersion().WithInfo(kubeVersion), effectiveVersion.EmulationVersion(), effectiveVersion.MinCompatibilityVersion()) | ||||
| 	binaryVersion := utilversion.MustParse(kubeVersion.String()) | ||||
| 	effectiveVersion := basecompatibility.NewEffectiveVersion(binaryVersion, false, binaryVersion, binaryVersion.SubtractMinor(1)) | ||||
| 	config.EffectiveVersion = effectiveVersion | ||||
|  | ||||
| 	s, err := config.Complete(nil).New("test", NewEmptyDelegate()) | ||||
|   | ||||
| @@ -27,9 +27,9 @@ import ( | ||||
| 	"k8s.io/apimachinery/pkg/util/errors" | ||||
| 	utilruntime "k8s.io/apimachinery/pkg/util/runtime" | ||||
| 	"k8s.io/apiserver/pkg/server" | ||||
| 	"k8s.io/apiserver/pkg/util/compatibility" | ||||
| 	utilfeature "k8s.io/apiserver/pkg/util/feature" | ||||
| 	"k8s.io/component-base/featuregate" | ||||
| 	utilversion "k8s.io/component-base/version" | ||||
| 	basecompatibility "k8s.io/component-base/compatibility" | ||||
|  | ||||
| 	"github.com/spf13/pflag" | ||||
| ) | ||||
| @@ -95,22 +95,22 @@ type ServerRunOptions struct { | ||||
| 	ShutdownWatchTerminationGracePeriod time.Duration | ||||
|  | ||||
| 	// ComponentGlobalsRegistry is the registry where the effective versions and feature gates for all components are stored. | ||||
| 	ComponentGlobalsRegistry featuregate.ComponentGlobalsRegistry | ||||
| 	ComponentGlobalsRegistry basecompatibility.ComponentGlobalsRegistry | ||||
| 	// ComponentName is name under which the server's global variabled are registered in the ComponentGlobalsRegistry. | ||||
| 	ComponentName string | ||||
| } | ||||
|  | ||||
| func NewServerRunOptions() *ServerRunOptions { | ||||
| 	if featuregate.DefaultComponentGlobalsRegistry.EffectiveVersionFor(featuregate.DefaultKubeComponent) == nil { | ||||
| 	if compatibility.DefaultComponentGlobalsRegistry.EffectiveVersionFor(basecompatibility.DefaultKubeComponent) == nil { | ||||
| 		featureGate := utilfeature.DefaultMutableFeatureGate | ||||
| 		effectiveVersion := utilversion.DefaultKubeEffectiveVersion() | ||||
| 		utilruntime.Must(featuregate.DefaultComponentGlobalsRegistry.Register(featuregate.DefaultKubeComponent, effectiveVersion, featureGate)) | ||||
| 		effectiveVersion := compatibility.DefaultBuildEffectiveVersion() | ||||
| 		utilruntime.Must(compatibility.DefaultComponentGlobalsRegistry.Register(basecompatibility.DefaultKubeComponent, effectiveVersion, featureGate)) | ||||
| 	} | ||||
|  | ||||
| 	return NewServerRunOptionsForComponent(featuregate.DefaultKubeComponent, featuregate.DefaultComponentGlobalsRegistry) | ||||
| 	return NewServerRunOptionsForComponent(basecompatibility.DefaultKubeComponent, compatibility.DefaultComponentGlobalsRegistry) | ||||
| } | ||||
|  | ||||
| func NewServerRunOptionsForComponent(componentName string, componentGlobalsRegistry featuregate.ComponentGlobalsRegistry) *ServerRunOptions { | ||||
| func NewServerRunOptionsForComponent(componentName string, componentGlobalsRegistry basecompatibility.ComponentGlobalsRegistry) *ServerRunOptions { | ||||
| 	defaults := server.NewConfig(serializer.CodecFactory{}) | ||||
| 	return &ServerRunOptions{ | ||||
| 		MaxRequestsInFlight:                 defaults.MaxRequestsInFlight, | ||||
|   | ||||
| @@ -26,15 +26,15 @@ import ( | ||||
| 	utilruntime "k8s.io/apimachinery/pkg/util/runtime" | ||||
| 	"k8s.io/apimachinery/pkg/util/version" | ||||
| 	utilfeature "k8s.io/apiserver/pkg/util/feature" | ||||
| 	"k8s.io/component-base/featuregate" | ||||
| 	utilversion "k8s.io/component-base/version" | ||||
| 	basecompatibility "k8s.io/component-base/compatibility" | ||||
| 	netutils "k8s.io/utils/net" | ||||
| ) | ||||
|  | ||||
| func TestServerRunOptionsValidate(t *testing.T) { | ||||
| 	testRegistry := featuregate.NewComponentGlobalsRegistry() | ||||
| 	defaultComponentGlobalsRegistry := basecompatibility.NewComponentGlobalsRegistry() | ||||
| 	testRegistry := basecompatibility.NewComponentGlobalsRegistry() | ||||
| 	featureGate := utilfeature.DefaultFeatureGate.DeepCopy() | ||||
| 	effectiveVersion := utilversion.NewEffectiveVersion("1.35") | ||||
| 	effectiveVersion := basecompatibility.NewEffectiveVersionFromString("1.35", "1.32", "1.32") | ||||
| 	effectiveVersion.SetEmulationVersion(version.MajorMinor(1, 31)) | ||||
| 	testComponent := "test" | ||||
| 	utilruntime.Must(testRegistry.Register(testComponent, effectiveVersion, featureGate)) | ||||
| @@ -55,7 +55,7 @@ func TestServerRunOptionsValidate(t *testing.T) { | ||||
| 				MinRequestTimeout:           1800, | ||||
| 				JSONPatchMaxCopyBytes:       10 * 1024 * 1024, | ||||
| 				MaxRequestBodyBytes:         10 * 1024 * 1024, | ||||
| 				ComponentGlobalsRegistry:    featuregate.DefaultComponentGlobalsRegistry, | ||||
| 				ComponentGlobalsRegistry:    defaultComponentGlobalsRegistry, | ||||
| 			}, | ||||
| 			expectErr: "--max-requests-inflight can not be negative value", | ||||
| 		}, | ||||
| @@ -70,7 +70,7 @@ func TestServerRunOptionsValidate(t *testing.T) { | ||||
| 				MinRequestTimeout:           1800, | ||||
| 				JSONPatchMaxCopyBytes:       10 * 1024 * 1024, | ||||
| 				MaxRequestBodyBytes:         10 * 1024 * 1024, | ||||
| 				ComponentGlobalsRegistry:    featuregate.DefaultComponentGlobalsRegistry, | ||||
| 				ComponentGlobalsRegistry:    defaultComponentGlobalsRegistry, | ||||
| 			}, | ||||
| 			expectErr: "--max-mutating-requests-inflight can not be negative value", | ||||
| 		}, | ||||
| @@ -85,7 +85,7 @@ func TestServerRunOptionsValidate(t *testing.T) { | ||||
| 				MinRequestTimeout:           1800, | ||||
| 				JSONPatchMaxCopyBytes:       10 * 1024 * 1024, | ||||
| 				MaxRequestBodyBytes:         10 * 1024 * 1024, | ||||
| 				ComponentGlobalsRegistry:    featuregate.DefaultComponentGlobalsRegistry, | ||||
| 				ComponentGlobalsRegistry:    defaultComponentGlobalsRegistry, | ||||
| 			}, | ||||
| 			expectErr: "--request-timeout can not be negative value", | ||||
| 		}, | ||||
| @@ -100,7 +100,7 @@ func TestServerRunOptionsValidate(t *testing.T) { | ||||
| 				MinRequestTimeout:           -1800, | ||||
| 				JSONPatchMaxCopyBytes:       10 * 1024 * 1024, | ||||
| 				MaxRequestBodyBytes:         10 * 1024 * 1024, | ||||
| 				ComponentGlobalsRegistry:    featuregate.DefaultComponentGlobalsRegistry, | ||||
| 				ComponentGlobalsRegistry:    defaultComponentGlobalsRegistry, | ||||
| 			}, | ||||
| 			expectErr: "--min-request-timeout can not be negative value", | ||||
| 		}, | ||||
| @@ -115,7 +115,7 @@ func TestServerRunOptionsValidate(t *testing.T) { | ||||
| 				MinRequestTimeout:           1800, | ||||
| 				JSONPatchMaxCopyBytes:       -10 * 1024 * 1024, | ||||
| 				MaxRequestBodyBytes:         10 * 1024 * 1024, | ||||
| 				ComponentGlobalsRegistry:    featuregate.DefaultComponentGlobalsRegistry, | ||||
| 				ComponentGlobalsRegistry:    defaultComponentGlobalsRegistry, | ||||
| 			}, | ||||
| 			expectErr: "ServerRunOptions.JSONPatchMaxCopyBytes can not be negative value", | ||||
| 		}, | ||||
| @@ -130,7 +130,7 @@ func TestServerRunOptionsValidate(t *testing.T) { | ||||
| 				MinRequestTimeout:           1800, | ||||
| 				JSONPatchMaxCopyBytes:       10 * 1024 * 1024, | ||||
| 				MaxRequestBodyBytes:         -10 * 1024 * 1024, | ||||
| 				ComponentGlobalsRegistry:    featuregate.DefaultComponentGlobalsRegistry, | ||||
| 				ComponentGlobalsRegistry:    defaultComponentGlobalsRegistry, | ||||
| 			}, | ||||
| 			expectErr: "ServerRunOptions.MaxRequestBodyBytes can not be negative value", | ||||
| 		}, | ||||
| @@ -146,7 +146,7 @@ func TestServerRunOptionsValidate(t *testing.T) { | ||||
| 				JSONPatchMaxCopyBytes:       10 * 1024 * 1024, | ||||
| 				MaxRequestBodyBytes:         10 * 1024 * 1024, | ||||
| 				LivezGracePeriod:            -time.Second, | ||||
| 				ComponentGlobalsRegistry:    featuregate.DefaultComponentGlobalsRegistry, | ||||
| 				ComponentGlobalsRegistry:    defaultComponentGlobalsRegistry, | ||||
| 			}, | ||||
| 			expectErr: "--livez-grace-period can not be a negative value", | ||||
| 		}, | ||||
| @@ -162,7 +162,7 @@ func TestServerRunOptionsValidate(t *testing.T) { | ||||
| 				JSONPatchMaxCopyBytes:       10 * 1024 * 1024, | ||||
| 				MaxRequestBodyBytes:         10 * 1024 * 1024, | ||||
| 				ShutdownDelayDuration:       -time.Second, | ||||
| 				ComponentGlobalsRegistry:    featuregate.DefaultComponentGlobalsRegistry, | ||||
| 				ComponentGlobalsRegistry:    defaultComponentGlobalsRegistry, | ||||
| 			}, | ||||
| 			expectErr: "--shutdown-delay-duration can not be negative value", | ||||
| 		}, | ||||
| @@ -178,7 +178,7 @@ func TestServerRunOptionsValidate(t *testing.T) { | ||||
| 				MinRequestTimeout:           1800, | ||||
| 				JSONPatchMaxCopyBytes:       10 * 1024 * 1024, | ||||
| 				MaxRequestBodyBytes:         10 * 1024 * 1024, | ||||
| 				ComponentGlobalsRegistry:    featuregate.DefaultComponentGlobalsRegistry, | ||||
| 				ComponentGlobalsRegistry:    defaultComponentGlobalsRegistry, | ||||
| 			}, | ||||
| 			expectErr: "--strict-transport-security-directives invalid, allowed values: max-age=expireTime, includeSubDomains, preload. see https://tools.ietf.org/html/rfc6797#section-6.1 for more information", | ||||
| 		}, | ||||
| @@ -211,7 +211,7 @@ func TestServerRunOptionsValidate(t *testing.T) { | ||||
| 				MinRequestTimeout:           1800, | ||||
| 				JSONPatchMaxCopyBytes:       10 * 1024 * 1024, | ||||
| 				MaxRequestBodyBytes:         10 * 1024 * 1024, | ||||
| 				ComponentGlobalsRegistry:    featuregate.DefaultComponentGlobalsRegistry, | ||||
| 				ComponentGlobalsRegistry:    defaultComponentGlobalsRegistry, | ||||
| 			}, | ||||
| 		}, | ||||
| 	} | ||||
|   | ||||
| @@ -46,7 +46,7 @@ import ( | ||||
| 	"k8s.io/client-go/discovery" | ||||
| 	restclient "k8s.io/client-go/rest" | ||||
| 	cliflag "k8s.io/component-base/cli/flag" | ||||
| 	utilversion "k8s.io/component-base/version" | ||||
| 	basecompatibility "k8s.io/component-base/compatibility" | ||||
| 	"k8s.io/klog/v2/ktesting" | ||||
| 	netutils "k8s.io/utils/net" | ||||
| ) | ||||
| @@ -278,7 +278,7 @@ func TestServerRunWithSNI(t *testing.T) { | ||||
| 			// launch server | ||||
| 			config := setUp(t) | ||||
| 			v := fakeVersion() | ||||
| 			config.EffectiveVersion = utilversion.NewEffectiveVersion(v.String()) | ||||
| 			config.EffectiveVersion = basecompatibility.NewEffectiveVersionFromString(v.String(), "", "") | ||||
|  | ||||
| 			config.EnableIndex = true | ||||
| 			secureOptions := (&SecureServingOptions{ | ||||
|   | ||||
| @@ -22,7 +22,8 @@ import ( | ||||
| 	"k8s.io/apimachinery/pkg/runtime" | ||||
| 	"k8s.io/apimachinery/pkg/runtime/schema" | ||||
| 	apimachineryversion "k8s.io/apimachinery/pkg/util/version" | ||||
| 	version "k8s.io/component-base/version" | ||||
| 	"k8s.io/apiserver/pkg/util/compatibility" | ||||
| 	basecompatibility "k8s.io/component-base/compatibility" | ||||
| ) | ||||
|  | ||||
| type ResourceEncodingConfig interface { | ||||
| @@ -43,7 +44,7 @@ type DefaultResourceEncodingConfig struct { | ||||
| 	// resources records the overriding encoding configs for individual resources. | ||||
| 	resources        map[schema.GroupResource]*OverridingResourceEncoding | ||||
| 	scheme           *runtime.Scheme | ||||
| 	effectiveVersion version.EffectiveVersion | ||||
| 	effectiveVersion basecompatibility.EffectiveVersion | ||||
| } | ||||
|  | ||||
| type OverridingResourceEncoding struct { | ||||
| @@ -54,7 +55,11 @@ type OverridingResourceEncoding struct { | ||||
| var _ ResourceEncodingConfig = &DefaultResourceEncodingConfig{} | ||||
|  | ||||
| func NewDefaultResourceEncodingConfig(scheme *runtime.Scheme) *DefaultResourceEncodingConfig { | ||||
| 	return &DefaultResourceEncodingConfig{resources: map[schema.GroupResource]*OverridingResourceEncoding{}, scheme: scheme, effectiveVersion: version.DefaultKubeEffectiveVersion()} | ||||
| 	return NewDefaultResourceEncodingConfigForEffectiveVersion(scheme, compatibility.DefaultComponentGlobalsRegistry.EffectiveVersionFor(basecompatibility.DefaultKubeComponent)) | ||||
| } | ||||
|  | ||||
| func NewDefaultResourceEncodingConfigForEffectiveVersion(scheme *runtime.Scheme, effectiveVersion basecompatibility.EffectiveVersion) *DefaultResourceEncodingConfig { | ||||
| 	return &DefaultResourceEncodingConfig{resources: map[schema.GroupResource]*OverridingResourceEncoding{}, scheme: scheme, effectiveVersion: effectiveVersion} | ||||
| } | ||||
|  | ||||
| func (o *DefaultResourceEncodingConfig) SetResourceEncoding(resourceBeingStored schema.GroupResource, externalEncodingVersion, internalVersion schema.GroupVersion) { | ||||
| @@ -64,7 +69,7 @@ func (o *DefaultResourceEncodingConfig) SetResourceEncoding(resourceBeingStored | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (o *DefaultResourceEncodingConfig) SetEffectiveVersion(effectiveVersion version.EffectiveVersion) { | ||||
| func (o *DefaultResourceEncodingConfig) SetEffectiveVersion(effectiveVersion basecompatibility.EffectiveVersion) { | ||||
| 	o.effectiveVersion = effectiveVersion | ||||
| } | ||||
|  | ||||
| @@ -121,7 +126,7 @@ type replacementInterface interface { | ||||
| 	APILifecycleReplacement() schema.GroupVersionKind | ||||
| } | ||||
|  | ||||
| func emulatedStorageVersion(binaryVersionOfResource schema.GroupVersion, example runtime.Object, effectiveVersion version.EffectiveVersion, scheme *runtime.Scheme) (schema.GroupVersion, error) { | ||||
| func emulatedStorageVersion(binaryVersionOfResource schema.GroupVersion, example runtime.Object, effectiveVersion basecompatibility.EffectiveVersion, scheme *runtime.Scheme) (schema.GroupVersion, error) { | ||||
| 	if example == nil || effectiveVersion == nil { | ||||
| 		return binaryVersionOfResource, nil | ||||
| 	} | ||||
| @@ -172,7 +177,7 @@ func emulatedStorageVersion(binaryVersionOfResource schema.GroupVersion, example | ||||
| 		} | ||||
|  | ||||
| 		// If it was introduced after current compatibility version, don't use it | ||||
| 		// skip the introduced check for test when currentVersion is 0.0 to test all apis | ||||
| 		// skip the introduced check for test when current compatibility version is 0.0 to test all apis | ||||
| 		if introduced, hasIntroduced := exampleOfGVK.(introducedInterface); hasIntroduced && (compatibilityVersion.Major() > 0 || compatibilityVersion.Minor() > 0) { | ||||
|  | ||||
| 			// Skip versions that have a replacement. | ||||
|   | ||||
| @@ -25,7 +25,7 @@ import ( | ||||
| 	"k8s.io/apimachinery/pkg/test" | ||||
| 	utilruntime "k8s.io/apimachinery/pkg/util/runtime" | ||||
| 	utilversion "k8s.io/apimachinery/pkg/util/version" | ||||
| 	"k8s.io/component-base/version" | ||||
| 	basecompatibility "k8s.io/component-base/compatibility" | ||||
| ) | ||||
|  | ||||
| func TestEmulatedStorageVersion(t *testing.T) { | ||||
| @@ -33,21 +33,21 @@ func TestEmulatedStorageVersion(t *testing.T) { | ||||
| 		name             string | ||||
| 		scheme           *runtime.Scheme | ||||
| 		binaryVersion    schema.GroupVersion | ||||
| 		effectiveVersion version.EffectiveVersion | ||||
| 		effectiveVersion basecompatibility.EffectiveVersion | ||||
| 		want             schema.GroupVersion | ||||
| 	}{ | ||||
| 		{ | ||||
| 			name:             "pick compatible", | ||||
| 			scheme:           AlphaBetaScheme(utilversion.MustParse("1.31"), utilversion.MustParse("1.32")), | ||||
| 			binaryVersion:    v1beta1, | ||||
| 			effectiveVersion: version.NewEffectiveVersion("1.32"), | ||||
| 			effectiveVersion: basecompatibility.NewEffectiveVersionFromString("1.32", "", ""), | ||||
| 			want:             v1alpha1, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name:             "alpha has been replaced, pick binary version", | ||||
| 			scheme:           AlphaReplacedBetaScheme(utilversion.MustParse("1.31"), utilversion.MustParse("1.32")), | ||||
| 			binaryVersion:    v1beta1, | ||||
| 			effectiveVersion: version.NewEffectiveVersion("1.32"), | ||||
| 			effectiveVersion: basecompatibility.NewEffectiveVersionFromString("1.32", "", ""), | ||||
| 			want:             v1beta1, | ||||
| 		}, | ||||
| 	} | ||||
|   | ||||
| @@ -33,7 +33,7 @@ import ( | ||||
| 	"k8s.io/apiserver/pkg/apis/example2" | ||||
| 	example2install "k8s.io/apiserver/pkg/apis/example2/install" | ||||
| 	"k8s.io/apiserver/pkg/storage/storagebackend" | ||||
| 	version "k8s.io/component-base/version" | ||||
| 	basecompatibility "k8s.io/component-base/compatibility" | ||||
| ) | ||||
|  | ||||
| var ( | ||||
| @@ -569,8 +569,7 @@ func TestStorageFactoryCompatibilityVersion(t *testing.T) { | ||||
|  | ||||
| 		gvk := gvks[0] | ||||
| 		t.Run(gvk.GroupKind().String()+"@"+tc.effectiveVersion, func(t *testing.T) { | ||||
| 			config := NewDefaultResourceEncodingConfig(sch) | ||||
| 			config.SetEffectiveVersion(version.NewEffectiveVersion(tc.effectiveVersion)) | ||||
| 			config := NewDefaultResourceEncodingConfigForEffectiveVersion(sch, basecompatibility.NewEffectiveVersionFromString(tc.effectiveVersion, "", "")) | ||||
| 			f := NewDefaultStorageFactory( | ||||
| 				storagebackend.Config{}, | ||||
| 				"", | ||||
|   | ||||
| @@ -0,0 +1,53 @@ | ||||
| /* | ||||
| Copyright 2025 The Kubernetes Authors. | ||||
|  | ||||
| Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| you may not use this file except in compliance with the License. | ||||
| You may obtain a copy of the License at | ||||
|  | ||||
|     http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  | ||||
| Unless required by applicable law or agreed to in writing, software | ||||
| distributed under the License is distributed on an "AS IS" BASIS, | ||||
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| See the License for the specific language governing permissions and | ||||
| limitations under the License. | ||||
| */ | ||||
|  | ||||
| package compatibility | ||||
|  | ||||
| import ( | ||||
| 	utilruntime "k8s.io/apimachinery/pkg/util/runtime" | ||||
| 	utilfeature "k8s.io/apiserver/pkg/util/feature" | ||||
| 	basecompatibility "k8s.io/component-base/compatibility" | ||||
| ) | ||||
|  | ||||
| // DefaultComponentGlobalsRegistry is the global var to store the effective versions and feature gates for all components for easy access. | ||||
| // Example usage: | ||||
| // // register the component effective version and feature gate first | ||||
| // wardleEffectiveVersion := basecompatibility.NewEffectiveVersion("1.2") | ||||
| // wardleFeatureGate := featuregate.NewFeatureGate() | ||||
| // utilruntime.Must(compatibility.DefaultComponentGlobalsRegistry.Register(apiserver.WardleComponentName, wardleEffectiveVersion, wardleFeatureGate, false)) | ||||
| // | ||||
| //	cmd := &cobra.Command{ | ||||
| //	 ... | ||||
| //		// call DefaultComponentGlobalsRegistry.Set() in PersistentPreRunE to ensure the feature gates are set based on emulation version right after parsing the flags. | ||||
| //		PersistentPreRunE: func(*cobra.Command, []string) error { | ||||
| //			if err := compatibility.DefaultComponentGlobalsRegistry.Set(); err != nil { | ||||
| //				return err | ||||
| //			} | ||||
| //	 ... | ||||
| //		}, | ||||
| //		RunE: func(c *cobra.Command, args []string) error { | ||||
| //			// call compatibility.DefaultComponentGlobalsRegistry.Validate() somewhere | ||||
| //		}, | ||||
| //	} | ||||
| // | ||||
| // flags := cmd.Flags() | ||||
| // // add flags | ||||
| // compatibility.DefaultComponentGlobalsRegistry.AddFlags(flags) | ||||
| var DefaultComponentGlobalsRegistry basecompatibility.ComponentGlobalsRegistry = basecompatibility.NewComponentGlobalsRegistry() | ||||
|  | ||||
| func init() { | ||||
| 	utilruntime.Must(DefaultComponentGlobalsRegistry.Register(basecompatibility.DefaultKubeComponent, DefaultBuildEffectiveVersion(), utilfeature.DefaultMutableFeatureGate)) | ||||
| } | ||||
| @@ -0,0 +1,65 @@ | ||||
| /* | ||||
| Copyright 2025 The Kubernetes Authors. | ||||
|  | ||||
| Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| you may not use this file except in compliance with the License. | ||||
| You may obtain a copy of the License at | ||||
|  | ||||
|     http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  | ||||
| Unless required by applicable law or agreed to in writing, software | ||||
| distributed under the License is distributed on an "AS IS" BASIS, | ||||
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| See the License for the specific language governing permissions and | ||||
| limitations under the License. | ||||
| */ | ||||
|  | ||||
| package compatibility | ||||
|  | ||||
| import ( | ||||
| 	"k8s.io/apimachinery/pkg/util/version" | ||||
| 	basecompatibility "k8s.io/component-base/compatibility" | ||||
| 	baseversion "k8s.io/component-base/version" | ||||
| ) | ||||
|  | ||||
| // minimumKubeEmulationVersion is the first release emulation version is introduced, | ||||
| // so the emulation version cannot go lower than that. | ||||
| var minimumKubeEmulationVersion *version.Version = version.MajorMinor(1, 31) | ||||
|  | ||||
| // DefaultBuildEffectiveVersion returns the MutableEffectiveVersion based on the | ||||
| // current build information. | ||||
| func DefaultBuildEffectiveVersion() basecompatibility.MutableEffectiveVersion { | ||||
| 	binaryVersion := defaultBuildBinaryVersion() | ||||
| 	useDefaultBuildBinaryVersion := true | ||||
| 	// fall back to the hard coded kube version only when the git tag is not available for local unit tests. | ||||
| 	if binaryVersion.Major() == 0 && binaryVersion.Minor() == 0 { | ||||
| 		useDefaultBuildBinaryVersion = false | ||||
| 		binaryVersion = version.MustParse(baseversion.DefaultKubeBinaryVersion) | ||||
| 	} | ||||
| 	versionFloor := kubeEffectiveVersionFloors(binaryVersion) | ||||
| 	return basecompatibility.NewEffectiveVersion(binaryVersion, useDefaultBuildBinaryVersion, versionFloor, versionFloor) | ||||
| } | ||||
|  | ||||
| func kubeEffectiveVersionFloors(binaryVersion *version.Version) *version.Version { | ||||
| 	// both emulationVersion and minCompatibilityVersion can be set to binaryVersion - 3 | ||||
| 	versionFloor := binaryVersion.WithPatch(0).SubtractMinor(3) | ||||
| 	if versionFloor.LessThan(minimumKubeEmulationVersion) { | ||||
| 		versionFloor = minimumKubeEmulationVersion | ||||
| 	} | ||||
| 	return versionFloor | ||||
| } | ||||
|  | ||||
| // DefaultKubeEffectiveVersionForTest returns the MutableEffectiveVersion based on the | ||||
| // latest K8s release hardcoded in DefaultKubeBinaryVersion. | ||||
| // DefaultKubeBinaryVersion is hard coded because defaultBuildBinaryVersion would return 0.0 when test is run without a git tag. | ||||
| // We do not enforce the N-3..N emulation version range in tests so that the tests would not automatically fail when there is a version bump. | ||||
| // Only used in tests. | ||||
| func DefaultKubeEffectiveVersionForTest() basecompatibility.MutableEffectiveVersion { | ||||
| 	binaryVersion := version.MustParse(baseversion.DefaultKubeBinaryVersion).WithInfo(baseversion.Get()) | ||||
| 	return basecompatibility.NewEffectiveVersion(binaryVersion, false, version.MustParse("0.0"), version.MustParse("0.0")) | ||||
| } | ||||
|  | ||||
| func defaultBuildBinaryVersion() *version.Version { | ||||
| 	verInfo := baseversion.Get() | ||||
| 	return version.MustParse(verInfo.String()).WithInfo(verInfo) | ||||
| } | ||||
| @@ -0,0 +1,76 @@ | ||||
| /* | ||||
| Copyright 2025 The Kubernetes Authors. | ||||
|  | ||||
| Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| you may not use this file except in compliance with the License. | ||||
| You may obtain a copy of the License at | ||||
|  | ||||
|     http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  | ||||
| Unless required by applicable law or agreed to in writing, software | ||||
| distributed under the License is distributed on an "AS IS" BASIS, | ||||
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| See the License for the specific language governing permissions and | ||||
| limitations under the License. | ||||
| */ | ||||
|  | ||||
| package compatibility | ||||
|  | ||||
| import ( | ||||
| 	"testing" | ||||
|  | ||||
| 	"k8s.io/apimachinery/pkg/util/version" | ||||
| 	basecompatibility "k8s.io/component-base/compatibility" | ||||
| ) | ||||
|  | ||||
| func TestValidateKubeEffectiveVersion(t *testing.T) { | ||||
| 	tests := []struct { | ||||
| 		name                    string | ||||
| 		emulationVersion        string | ||||
| 		minCompatibilityVersion string | ||||
| 		expectErr               bool | ||||
| 	}{ | ||||
| 		{ | ||||
| 			name:                    "valid versions", | ||||
| 			emulationVersion:        "v1.32.0", | ||||
| 			minCompatibilityVersion: "v1.31.0", | ||||
| 			expectErr:               false, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name:                    "emulationVersion too low", | ||||
| 			emulationVersion:        "v1.30.0", | ||||
| 			minCompatibilityVersion: "v1.31.0", | ||||
| 			expectErr:               true, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name:                    "minCompatibilityVersion too low", | ||||
| 			emulationVersion:        "v1.31.0", | ||||
| 			minCompatibilityVersion: "v1.30.0", | ||||
| 			expectErr:               true, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name:                    "both versions too low", | ||||
| 			emulationVersion:        "v1.30.0", | ||||
| 			minCompatibilityVersion: "v1.29.0", | ||||
| 			expectErr:               true, | ||||
| 		}, | ||||
| 	} | ||||
|  | ||||
| 	for _, test := range tests { | ||||
| 		t.Run(test.name, func(t *testing.T) { | ||||
| 			binaryVersion := version.MustParseGeneric("1.33") | ||||
| 			versionFloor := kubeEffectiveVersionFloors(binaryVersion) | ||||
| 			effective := basecompatibility.NewEffectiveVersion(binaryVersion, false, versionFloor, versionFloor) | ||||
| 			effective.SetEmulationVersion(version.MustParseGeneric(test.emulationVersion)) | ||||
| 			effective.SetMinCompatibilityVersion(version.MustParseGeneric(test.minCompatibilityVersion)) | ||||
|  | ||||
| 			err := effective.Validate() | ||||
| 			if test.expectErr && err == nil { | ||||
| 				t.Error("expected error, but got nil") | ||||
| 			} | ||||
| 			if !test.expectErr && err != nil { | ||||
| 				t.Errorf("unexpected error: %v", err) | ||||
| 			} | ||||
| 		}) | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										12
									
								
								staging/src/k8s.io/component-base/compatibility/OWNERS
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								staging/src/k8s.io/component-base/compatibility/OWNERS
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,12 @@ | ||||
| # See the OWNERS docs at https://go.k8s.io/owners | ||||
|  | ||||
| # Currently assigned this directory to sig-api-machinery since this is | ||||
| # an interface to the version definition in "k8s.io/apiserver/pkg/util/compatibility". | ||||
|  | ||||
| approvers: | ||||
|   - sig-api-machinery-api-approvers | ||||
| reviewers: | ||||
|   - sig-api-machinery-api-reviewers | ||||
|   - siyuanfoundation | ||||
| labels: | ||||
|   - sig/api-machinery | ||||
| @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and | ||||
| limitations under the License. | ||||
| */ | ||||
| 
 | ||||
| package featuregate | ||||
| package compatibility | ||||
| 
 | ||||
| import ( | ||||
| 	"fmt" | ||||
| @@ -26,38 +26,12 @@ import ( | ||||
| 	utilruntime "k8s.io/apimachinery/pkg/util/runtime" | ||||
| 	"k8s.io/apimachinery/pkg/util/version" | ||||
| 	cliflag "k8s.io/component-base/cli/flag" | ||||
| 	baseversion "k8s.io/component-base/version" | ||||
| 	"k8s.io/component-base/featuregate" | ||||
| 	"k8s.io/klog/v2" | ||||
| ) | ||||
| 
 | ||||
| // DefaultComponentGlobalsRegistry is the global var to store the effective versions and feature gates for all components for easy access. | ||||
| // Example usage: | ||||
| // // register the component effective version and feature gate first | ||||
| // _, _ = utilversion.DefaultComponentGlobalsRegistry.ComponentGlobalsOrRegister(utilversion.DefaultKubeComponent, utilversion.DefaultKubeEffectiveVersion(), utilfeature.DefaultMutableFeatureGate) | ||||
| // wardleEffectiveVersion := utilversion.NewEffectiveVersion("1.2") | ||||
| // wardleFeatureGate := featuregate.NewFeatureGate() | ||||
| // utilruntime.Must(utilversion.DefaultComponentGlobalsRegistry.Register(apiserver.WardleComponentName, wardleEffectiveVersion, wardleFeatureGate, false)) | ||||
| // | ||||
| //	cmd := &cobra.Command{ | ||||
| //	 ... | ||||
| //		// call DefaultComponentGlobalsRegistry.Set() in PersistentPreRunE | ||||
| //		PersistentPreRunE: func(*cobra.Command, []string) error { | ||||
| //			if err := utilversion.DefaultComponentGlobalsRegistry.Set(); err != nil { | ||||
| //				return err | ||||
| //			} | ||||
| //	 ... | ||||
| //		}, | ||||
| //		RunE: func(c *cobra.Command, args []string) error { | ||||
| //			// call utilversion.DefaultComponentGlobalsRegistry.Validate() somewhere | ||||
| //		}, | ||||
| //	} | ||||
| // | ||||
| // flags := cmd.Flags() | ||||
| // // add flags | ||||
| // utilversion.DefaultComponentGlobalsRegistry.AddFlags(flags) | ||||
| var DefaultComponentGlobalsRegistry ComponentGlobalsRegistry = NewComponentGlobalsRegistry() | ||||
| 
 | ||||
| const ( | ||||
| 	// DefaultKubeComponent is the component name for k8s control plane components. | ||||
| 	DefaultKubeComponent = "kube" | ||||
| 
 | ||||
| 	klogLevel = 2 | ||||
| @@ -65,10 +39,10 @@ const ( | ||||
| 
 | ||||
| type VersionMapping func(from *version.Version) *version.Version | ||||
| 
 | ||||
| // ComponentGlobals stores the global variables for a component for easy access. | ||||
| // ComponentGlobals stores the global variables for a component for easy access, including feature gate and effective version. | ||||
| type ComponentGlobals struct { | ||||
| 	effectiveVersion baseversion.MutableEffectiveVersion | ||||
| 	featureGate      MutableVersionedFeatureGate | ||||
| 	effectiveVersion MutableEffectiveVersion | ||||
| 	featureGate      featuregate.MutableVersionedFeatureGate | ||||
| 
 | ||||
| 	// emulationVersionMapping contains the mapping from the emulation version of this component | ||||
| 	// to the emulation version of another component. | ||||
| @@ -84,22 +58,24 @@ type ComponentGlobals struct { | ||||
| 	dependentMinCompatibilityVersion bool | ||||
| } | ||||
| 
 | ||||
| // ComponentGlobalsRegistry stores the global variables for different components for easy access, including feature gate and effective version of each component. | ||||
| type ComponentGlobalsRegistry interface { | ||||
| 	// EffectiveVersionFor returns the EffectiveVersion registered under the component. | ||||
| 	// Returns nil if the component is not registered. | ||||
| 	EffectiveVersionFor(component string) baseversion.EffectiveVersion | ||||
| 	EffectiveVersionFor(component string) EffectiveVersion | ||||
| 	// FeatureGateFor returns the FeatureGate registered under the component. | ||||
| 	// Returns nil if the component is not registered. | ||||
| 	FeatureGateFor(component string) FeatureGate | ||||
| 	FeatureGateFor(component string) featuregate.FeatureGate | ||||
| 	// Register registers the EffectiveVersion and FeatureGate for a component. | ||||
| 	// returns error if the component is already registered. | ||||
| 	Register(component string, effectiveVersion baseversion.MutableEffectiveVersion, featureGate MutableVersionedFeatureGate) error | ||||
| 	Register(component string, effectiveVersion MutableEffectiveVersion, featureGate featuregate.MutableVersionedFeatureGate) error | ||||
| 	// ComponentGlobalsOrRegister would return the registered global variables for the component if it already exists in the registry. | ||||
| 	// Otherwise, the provided variables would be registered under the component, and the same variables would be returned. | ||||
| 	ComponentGlobalsOrRegister(component string, effectiveVersion baseversion.MutableEffectiveVersion, featureGate MutableVersionedFeatureGate) (baseversion.MutableEffectiveVersion, MutableVersionedFeatureGate) | ||||
| 	ComponentGlobalsOrRegister(component string, effectiveVersion MutableEffectiveVersion, featureGate featuregate.MutableVersionedFeatureGate) (MutableEffectiveVersion, featuregate.MutableVersionedFeatureGate) | ||||
| 	// AddFlags adds flags of "--emulated-version" and "--feature-gates" | ||||
| 	AddFlags(fs *pflag.FlagSet) | ||||
| 	// Set sets the flags for all global variables for all components registered. | ||||
| 	// A component's feature gate and effective version would not be updated until Set() is called. | ||||
| 	Set() error | ||||
| 	// SetFallback calls Set() if it has never been called. | ||||
| 	SetFallback() error | ||||
| @@ -118,9 +94,13 @@ type ComponentGlobalsRegistry interface { | ||||
| type componentGlobalsRegistry struct { | ||||
| 	componentGlobals map[string]*ComponentGlobals | ||||
| 	mutex            sync.RWMutex | ||||
| 	// list of component name to emulation version set from the flag. | ||||
| 	// emulationVersionConfig stores the list of component name to emulation version set from the flag. | ||||
| 	// When the `--emulated-version` flag is parsed, it would not take effect until Set() is called, | ||||
| 	// because the emulation version needs to be set before the feature gate is set. | ||||
| 	emulationVersionConfig []string | ||||
| 	// map of component name to the list of feature gates set from the flag. | ||||
| 	// featureGatesConfig stores the map of component name to the list of feature gates set from the flag. | ||||
| 	// When the `--feature-gates` flag is parsed, it would not take effect until Set() is called, | ||||
| 	// because the emulation version needs to be set before the feature gate is set. | ||||
| 	featureGatesConfig map[string][]string | ||||
| 	// set stores if the Set() function for the registry is already called. | ||||
| 	set bool | ||||
| @@ -143,7 +123,7 @@ func (r *componentGlobalsRegistry) Reset() { | ||||
| 	r.set = false | ||||
| } | ||||
| 
 | ||||
| func (r *componentGlobalsRegistry) EffectiveVersionFor(component string) baseversion.EffectiveVersion { | ||||
| func (r *componentGlobalsRegistry) EffectiveVersionFor(component string) EffectiveVersion { | ||||
| 	r.mutex.RLock() | ||||
| 	defer r.mutex.RUnlock() | ||||
| 	globals, ok := r.componentGlobals[component] | ||||
| @@ -153,7 +133,7 @@ func (r *componentGlobalsRegistry) EffectiveVersionFor(component string) basever | ||||
| 	return globals.effectiveVersion | ||||
| } | ||||
| 
 | ||||
| func (r *componentGlobalsRegistry) FeatureGateFor(component string) FeatureGate { | ||||
| func (r *componentGlobalsRegistry) FeatureGateFor(component string) featuregate.FeatureGate { | ||||
| 	r.mutex.RLock() | ||||
| 	defer r.mutex.RUnlock() | ||||
| 	globals, ok := r.componentGlobals[component] | ||||
| @@ -163,7 +143,7 @@ func (r *componentGlobalsRegistry) FeatureGateFor(component string) FeatureGate | ||||
| 	return globals.featureGate | ||||
| } | ||||
| 
 | ||||
| func (r *componentGlobalsRegistry) unsafeRegister(component string, effectiveVersion baseversion.MutableEffectiveVersion, featureGate MutableVersionedFeatureGate) error { | ||||
| func (r *componentGlobalsRegistry) unsafeRegister(component string, effectiveVersion MutableEffectiveVersion, featureGate featuregate.MutableVersionedFeatureGate) error { | ||||
| 	if _, ok := r.componentGlobals[component]; ok { | ||||
| 		return fmt.Errorf("component globals of %s already registered", component) | ||||
| 	} | ||||
| @@ -182,7 +162,7 @@ func (r *componentGlobalsRegistry) unsafeRegister(component string, effectiveVer | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func (r *componentGlobalsRegistry) Register(component string, effectiveVersion baseversion.MutableEffectiveVersion, featureGate MutableVersionedFeatureGate) error { | ||||
| func (r *componentGlobalsRegistry) Register(component string, effectiveVersion MutableEffectiveVersion, featureGate featuregate.MutableVersionedFeatureGate) error { | ||||
| 	if effectiveVersion == nil { | ||||
| 		return fmt.Errorf("cannot register nil effectiveVersion") | ||||
| 	} | ||||
| @@ -191,7 +171,7 @@ func (r *componentGlobalsRegistry) Register(component string, effectiveVersion b | ||||
| 	return r.unsafeRegister(component, effectiveVersion, featureGate) | ||||
| } | ||||
| 
 | ||||
| func (r *componentGlobalsRegistry) ComponentGlobalsOrRegister(component string, effectiveVersion baseversion.MutableEffectiveVersion, featureGate MutableVersionedFeatureGate) (baseversion.MutableEffectiveVersion, MutableVersionedFeatureGate) { | ||||
| func (r *componentGlobalsRegistry) ComponentGlobalsOrRegister(component string, effectiveVersion MutableEffectiveVersion, featureGate featuregate.MutableVersionedFeatureGate) (MutableEffectiveVersion, featuregate.MutableVersionedFeatureGate) { | ||||
| 	r.mutex.Lock() | ||||
| 	defer r.mutex.Unlock() | ||||
| 	globals, ok := r.componentGlobals[component] | ||||
| @@ -219,22 +199,16 @@ func (r *componentGlobalsRegistry) unsafeKnownFeatures() []string { | ||||
| func (r *componentGlobalsRegistry) unsafeVersionFlagOptions(isEmulation bool) []string { | ||||
| 	var vs []string | ||||
| 	for component, globals := range r.componentGlobals { | ||||
| 		binaryVer := globals.effectiveVersion.BinaryVersion() | ||||
| 		if isEmulation { | ||||
| 			if globals.dependentEmulationVersion { | ||||
| 				continue | ||||
| 			} | ||||
| 			// emulated version could be between binaryMajor.{binaryMinor} and binaryMajor.{binaryMinor} | ||||
| 			// TODO: change to binaryMajor.{binaryMinor-1} and binaryMajor.{binaryMinor} in 1.32 | ||||
| 			vs = append(vs, fmt.Sprintf("%s=%s..%s (default=%s)", component, | ||||
| 				binaryVer.SubtractMinor(0).String(), binaryVer.String(), globals.effectiveVersion.EmulationVersion().String())) | ||||
| 			vs = append(vs, fmt.Sprintf("%s=%s", component, globals.effectiveVersion.AllowedEmulationVersionRange())) | ||||
| 		} else { | ||||
| 			if globals.dependentMinCompatibilityVersion { | ||||
| 				continue | ||||
| 			} | ||||
| 			// min compatibility version could be between binaryMajor.{binaryMinor-1} and binaryMajor.{binaryMinor} | ||||
| 			vs = append(vs, fmt.Sprintf("%s=%s..%s (default=%s)", component, | ||||
| 				binaryVer.SubtractMinor(1).String(), binaryVer.String(), globals.effectiveVersion.MinCompatibilityVersion().String())) | ||||
| 			vs = append(vs, fmt.Sprintf("%s=%s", component, globals.effectiveVersion.AllowedMinCompatibilityVersionRange())) | ||||
| 		} | ||||
| 	} | ||||
| 	sort.Strings(vs) | ||||
| @@ -1,5 +1,5 @@ | ||||
| /* | ||||
| Copyright 2024 The Kubernetes Authors. | ||||
| Copyright 2025 The Kubernetes Authors. | ||||
| 
 | ||||
| Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| you may not use this file except in compliance with the License. | ||||
| @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and | ||||
| limitations under the License. | ||||
| */ | ||||
| 
 | ||||
| package featuregate | ||||
| package compatibility | ||||
| 
 | ||||
| import ( | ||||
| 	"fmt" | ||||
| @@ -25,7 +25,7 @@ import ( | ||||
| 
 | ||||
| 	utilruntime "k8s.io/apimachinery/pkg/util/runtime" | ||||
| 	"k8s.io/apimachinery/pkg/util/version" | ||||
| 	baseversion "k8s.io/component-base/version" | ||||
| 	"k8s.io/component-base/featuregate" | ||||
| ) | ||||
| 
 | ||||
| const ( | ||||
| @@ -34,8 +34,8 @@ const ( | ||||
| 
 | ||||
| func TestEffectiveVersionRegistry(t *testing.T) { | ||||
| 	r := NewComponentGlobalsRegistry() | ||||
| 	ver1 := baseversion.NewEffectiveVersion("1.31") | ||||
| 	ver2 := baseversion.NewEffectiveVersion("1.28") | ||||
| 	ver1 := NewEffectiveVersionFromString("1.31", "", "") | ||||
| 	ver2 := NewEffectiveVersionFromString("1.28", "", "") | ||||
| 
 | ||||
| 	if r.EffectiveVersionFor(testComponent) != nil { | ||||
| 		t.Fatalf("expected nil EffectiveVersion initially") | ||||
| @@ -57,40 +57,40 @@ func TestEffectiveVersionRegistry(t *testing.T) { | ||||
| 
 | ||||
| func testRegistry(t *testing.T) *componentGlobalsRegistry { | ||||
| 	r := NewComponentGlobalsRegistry() | ||||
| 	verKube := baseversion.NewEffectiveVersion("1.31") | ||||
| 	fgKube := NewVersionedFeatureGate(version.MustParse("0.0")) | ||||
| 	err := fgKube.AddVersioned(map[Feature]VersionedSpecs{ | ||||
| 	verKube := NewEffectiveVersionFromString("1.31", "1.31", "1.30") | ||||
| 	fgKube := featuregate.NewVersionedFeatureGate(version.MustParse("0.0")) | ||||
| 	err := fgKube.AddVersioned(map[featuregate.Feature]featuregate.VersionedSpecs{ | ||||
| 		"kubeA": { | ||||
| 			{Version: version.MustParse("1.27"), Default: false, PreRelease: Alpha}, | ||||
| 			{Version: version.MustParse("1.28"), Default: false, PreRelease: Beta}, | ||||
| 			{Version: version.MustParse("1.31"), Default: true, LockToDefault: true, PreRelease: GA}, | ||||
| 			{Version: version.MustParse("1.27"), Default: false, PreRelease: featuregate.Alpha}, | ||||
| 			{Version: version.MustParse("1.28"), Default: false, PreRelease: featuregate.Beta}, | ||||
| 			{Version: version.MustParse("1.31"), Default: true, LockToDefault: true, PreRelease: featuregate.GA}, | ||||
| 		}, | ||||
| 		"kubeB": { | ||||
| 			{Version: version.MustParse("1.30"), Default: false, PreRelease: Alpha}, | ||||
| 			{Version: version.MustParse("1.30"), Default: false, PreRelease: featuregate.Alpha}, | ||||
| 		}, | ||||
| 		"commonC": { | ||||
| 			{Version: version.MustParse("1.27"), Default: false, PreRelease: Alpha}, | ||||
| 			{Version: version.MustParse("1.29"), Default: true, PreRelease: Beta}, | ||||
| 			{Version: version.MustParse("1.27"), Default: false, PreRelease: featuregate.Alpha}, | ||||
| 			{Version: version.MustParse("1.29"), Default: true, PreRelease: featuregate.Beta}, | ||||
| 		}, | ||||
| 	}) | ||||
| 	if err != nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
| 
 | ||||
| 	verTest := baseversion.NewEffectiveVersion("2.8") | ||||
| 	fgTest := NewVersionedFeatureGate(version.MustParse("0.0")) | ||||
| 	err = fgTest.AddVersioned(map[Feature]VersionedSpecs{ | ||||
| 	verTest := NewEffectiveVersionFromString("2.8", "2.8", "2.7") | ||||
| 	fgTest := featuregate.NewVersionedFeatureGate(version.MustParse("0.0")) | ||||
| 	err = fgTest.AddVersioned(map[featuregate.Feature]featuregate.VersionedSpecs{ | ||||
| 		"testA": { | ||||
| 			{Version: version.MustParse("2.7"), Default: false, PreRelease: Alpha}, | ||||
| 			{Version: version.MustParse("2.8"), Default: false, PreRelease: Beta}, | ||||
| 			{Version: version.MustParse("2.10"), Default: true, PreRelease: GA}, | ||||
| 			{Version: version.MustParse("2.7"), Default: false, PreRelease: featuregate.Alpha}, | ||||
| 			{Version: version.MustParse("2.8"), Default: false, PreRelease: featuregate.Beta}, | ||||
| 			{Version: version.MustParse("2.10"), Default: true, PreRelease: featuregate.GA}, | ||||
| 		}, | ||||
| 		"testB": { | ||||
| 			{Version: version.MustParse("2.9"), Default: false, PreRelease: Alpha}, | ||||
| 			{Version: version.MustParse("2.9"), Default: false, PreRelease: featuregate.Alpha}, | ||||
| 		}, | ||||
| 		"commonC": { | ||||
| 			{Version: version.MustParse("2.7"), Default: false, PreRelease: Alpha}, | ||||
| 			{Version: version.MustParse("2.9"), Default: true, PreRelease: Beta}, | ||||
| 			{Version: version.MustParse("2.7"), Default: false, PreRelease: featuregate.Alpha}, | ||||
| 			{Version: version.MustParse("2.9"), Default: true, PreRelease: featuregate.Beta}, | ||||
| 		}, | ||||
| 	}) | ||||
| 	if err != nil { | ||||
| @@ -154,8 +154,8 @@ func TestFlags(t *testing.T) { | ||||
| 		parseError                   string | ||||
| 		expectedKubeEmulationVersion string | ||||
| 		expectedTestEmulationVersion string | ||||
| 		expectedKubeFeatureValues    map[Feature]bool | ||||
| 		expectedTestFeatureValues    map[Feature]bool | ||||
| 		expectedKubeFeatureValues    map[featuregate.Feature]bool | ||||
| 		expectedTestFeatureValues    map[featuregate.Feature]bool | ||||
| 	}{ | ||||
| 		{ | ||||
| 			name:                         "setting kube emulation version", | ||||
| @@ -214,8 +214,8 @@ func TestFlags(t *testing.T) { | ||||
| 			}, | ||||
| 			expectedKubeEmulationVersion: "1.31", | ||||
| 			expectedTestEmulationVersion: "2.7", | ||||
| 			expectedKubeFeatureValues:    map[Feature]bool{"kubeA": true, "kubeB": false, "commonC": true}, | ||||
| 			expectedTestFeatureValues:    map[Feature]bool{"testA": true, "testB": false, "commonC": false}, | ||||
| 			expectedKubeFeatureValues:    map[featuregate.Feature]bool{"kubeA": true, "kubeB": false, "commonC": true}, | ||||
| 			expectedTestFeatureValues:    map[featuregate.Feature]bool{"testA": true, "testB": false, "commonC": false}, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name: "setting future test feature flag", | ||||
| @@ -235,8 +235,8 @@ func TestFlags(t *testing.T) { | ||||
| 			}, | ||||
| 			expectedKubeEmulationVersion: "1.30", | ||||
| 			expectedTestEmulationVersion: "2.7", | ||||
| 			expectedKubeFeatureValues:    map[Feature]bool{"kubeA": false, "kubeB": true, "commonC": false}, | ||||
| 			expectedTestFeatureValues:    map[Feature]bool{"testA": false, "testB": false, "commonC": true}, | ||||
| 			expectedKubeFeatureValues:    map[featuregate.Feature]bool{"kubeA": false, "kubeB": true, "commonC": false}, | ||||
| 			expectedTestFeatureValues:    map[featuregate.Feature]bool{"testA": false, "testB": false, "commonC": true}, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name: "setting kube feature flag with different prefix", | ||||
| @@ -313,9 +313,9 @@ func TestFlags(t *testing.T) { | ||||
| 
 | ||||
| func TestVersionMapping(t *testing.T) { | ||||
| 	r := NewComponentGlobalsRegistry() | ||||
| 	ver1 := baseversion.NewEffectiveVersion("0.58") | ||||
| 	ver2 := baseversion.NewEffectiveVersion("1.28") | ||||
| 	ver3 := baseversion.NewEffectiveVersion("2.10") | ||||
| 	ver1 := NewEffectiveVersionFromString("0.58", "", "") | ||||
| 	ver2 := NewEffectiveVersionFromString("1.28", "", "") | ||||
| 	ver3 := NewEffectiveVersionFromString("2.10", "", "") | ||||
| 
 | ||||
| 	utilruntime.Must(r.Register("test1", ver1, nil)) | ||||
| 	utilruntime.Must(r.Register("test2", ver2, nil)) | ||||
| @@ -355,9 +355,9 @@ func TestVersionMapping(t *testing.T) { | ||||
| 
 | ||||
| func TestVersionMappingWithMultipleDependency(t *testing.T) { | ||||
| 	r := NewComponentGlobalsRegistry() | ||||
| 	ver1 := baseversion.NewEffectiveVersion("0.58") | ||||
| 	ver2 := baseversion.NewEffectiveVersion("1.28") | ||||
| 	ver3 := baseversion.NewEffectiveVersion("2.10") | ||||
| 	ver1 := NewEffectiveVersionFromString("0.58", "", "") | ||||
| 	ver2 := NewEffectiveVersionFromString("1.28", "", "") | ||||
| 	ver3 := NewEffectiveVersionFromString("2.10", "", "") | ||||
| 
 | ||||
| 	utilruntime.Must(r.Register("test1", ver1, nil)) | ||||
| 	utilruntime.Must(r.Register("test2", ver2, nil)) | ||||
| @@ -382,9 +382,9 @@ func TestVersionMappingWithMultipleDependency(t *testing.T) { | ||||
| 
 | ||||
| func TestVersionMappingWithCyclicDependency(t *testing.T) { | ||||
| 	r := NewComponentGlobalsRegistry() | ||||
| 	ver1 := baseversion.NewEffectiveVersion("0.58") | ||||
| 	ver2 := baseversion.NewEffectiveVersion("1.28") | ||||
| 	ver3 := baseversion.NewEffectiveVersion("2.10") | ||||
| 	ver1 := NewEffectiveVersionFromString("0.58", "", "") | ||||
| 	ver2 := NewEffectiveVersionFromString("1.28", "", "") | ||||
| 	ver3 := NewEffectiveVersionFromString("2.10", "", "") | ||||
| 
 | ||||
| 	utilruntime.Must(r.Register("test1", ver1, nil)) | ||||
| 	utilruntime.Must(r.Register("test2", ver2, nil)) | ||||
							
								
								
									
										213
									
								
								staging/src/k8s.io/component-base/compatibility/version.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										213
									
								
								staging/src/k8s.io/component-base/compatibility/version.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,213 @@ | ||||
| /* | ||||
| Copyright 2025 The Kubernetes Authors. | ||||
|  | ||||
| Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| you may not use this file except in compliance with the License. | ||||
| You may obtain a copy of the License at | ||||
|  | ||||
|     http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  | ||||
| Unless required by applicable law or agreed to in writing, software | ||||
| distributed under the License is distributed on an "AS IS" BASIS, | ||||
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| See the License for the specific language governing permissions and | ||||
| limitations under the License. | ||||
| */ | ||||
|  | ||||
| package compatibility | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"sync/atomic" | ||||
|  | ||||
| 	"k8s.io/apimachinery/pkg/util/version" | ||||
| 	baseversion "k8s.io/component-base/version" | ||||
| ) | ||||
|  | ||||
| // EffectiveVersion stores all the version information of a component. | ||||
| type EffectiveVersion interface { | ||||
| 	// BinaryVersion is the binary version of a component. Tied to a particular binary release. | ||||
| 	BinaryVersion() *version.Version | ||||
| 	// EmulationVersion is the version a component emulate its capabilities (APIs, features, ...) of. | ||||
| 	// If EmulationVersion is set to be different from BinaryVersion, the component will emulate the behavior of this version instead of the underlying binary version. | ||||
| 	EmulationVersion() *version.Version | ||||
| 	// MinCompatibilityVersion is the minimum version a component is compatible with (in terms of storage versions, validation rules, ...). | ||||
| 	MinCompatibilityVersion() *version.Version | ||||
| 	EqualTo(other EffectiveVersion) bool | ||||
| 	String() string | ||||
| 	Validate() []error | ||||
| 	// AllowedEmulationVersionRange returns the string of the allowed range of emulation version. | ||||
| 	// Used only for docs/help. | ||||
| 	AllowedEmulationVersionRange() string | ||||
| 	// AllowedMinCompatibilityVersionRange returns the string of the allowed range of min compatibility version. | ||||
| 	// Used only for docs/help. | ||||
| 	AllowedMinCompatibilityVersionRange() string | ||||
| } | ||||
|  | ||||
| type MutableEffectiveVersion interface { | ||||
| 	EffectiveVersion | ||||
| 	SetEmulationVersion(emulationVersion *version.Version) | ||||
| 	SetMinCompatibilityVersion(minCompatibilityVersion *version.Version) | ||||
| } | ||||
|  | ||||
| type effectiveVersion struct { | ||||
| 	// When true, BinaryVersion() returns the current binary version | ||||
| 	useDefaultBuildBinaryVersion atomic.Bool | ||||
| 	// Holds the last binary version stored in Set() | ||||
| 	binaryVersion atomic.Pointer[version.Version] | ||||
| 	// If the emulationVersion is set by the users, it could only contain major and minor versions. | ||||
| 	// In tests, emulationVersion could be the same as the binary version, or set directly, | ||||
| 	// which can have "alpha" as pre-release to continue serving expired apis while we clean up the test. | ||||
| 	emulationVersion atomic.Pointer[version.Version] | ||||
| 	// minCompatibilityVersion could only contain major and minor versions. | ||||
| 	minCompatibilityVersion atomic.Pointer[version.Version] | ||||
| 	// emulationVersionFloor is the minimum emulationVersion allowed. No limit if nil. | ||||
| 	emulationVersionFloor *version.Version | ||||
| 	// minCompatibilityVersionFloor is the minimum minCompatibilityVersionFloor allowed. No limit if nil. | ||||
| 	minCompatibilityVersionFloor *version.Version | ||||
| } | ||||
|  | ||||
| func (m *effectiveVersion) BinaryVersion() *version.Version { | ||||
| 	if m.useDefaultBuildBinaryVersion.Load() { | ||||
| 		return defaultBuildBinaryVersion() | ||||
| 	} | ||||
| 	return m.binaryVersion.Load() | ||||
| } | ||||
|  | ||||
| func (m *effectiveVersion) EmulationVersion() *version.Version { | ||||
| 	ver := m.emulationVersion.Load() | ||||
| 	if ver != nil { | ||||
| 		// Emulation version can have "alpha" as pre-release to continue serving expired apis while we clean up the test. | ||||
| 		// The pre-release should not be accessible to the users. | ||||
| 		return ver.WithPreRelease(m.BinaryVersion().PreRelease()) | ||||
| 	} | ||||
| 	return ver | ||||
| } | ||||
|  | ||||
| func (m *effectiveVersion) MinCompatibilityVersion() *version.Version { | ||||
| 	return m.minCompatibilityVersion.Load() | ||||
| } | ||||
|  | ||||
| func (m *effectiveVersion) EqualTo(other EffectiveVersion) bool { | ||||
| 	return m.BinaryVersion().EqualTo(other.BinaryVersion()) && m.EmulationVersion().EqualTo(other.EmulationVersion()) && m.MinCompatibilityVersion().EqualTo(other.MinCompatibilityVersion()) | ||||
| } | ||||
|  | ||||
| func (m *effectiveVersion) String() string { | ||||
| 	if m == nil { | ||||
| 		return "<nil>" | ||||
| 	} | ||||
| 	return fmt.Sprintf("{BinaryVersion: %s, EmulationVersion: %s, MinCompatibilityVersion: %s}", | ||||
| 		m.BinaryVersion().String(), m.EmulationVersion().String(), m.MinCompatibilityVersion().String()) | ||||
| } | ||||
|  | ||||
| func majorMinor(ver *version.Version) *version.Version { | ||||
| 	if ver == nil { | ||||
| 		return ver | ||||
| 	} | ||||
| 	return version.MajorMinor(ver.Major(), ver.Minor()) | ||||
| } | ||||
|  | ||||
| func (m *effectiveVersion) SetEmulationVersion(emulationVersion *version.Version) { | ||||
| 	m.emulationVersion.Store(majorMinor(emulationVersion)) | ||||
| 	// set the default minCompatibilityVersion to be emulationVersion - 1 if possible | ||||
| 	minCompatibilityVersion := majorMinor(emulationVersion.SubtractMinor(1)) | ||||
| 	if minCompatibilityVersion.LessThan(m.minCompatibilityVersionFloor) { | ||||
| 		minCompatibilityVersion = m.minCompatibilityVersionFloor | ||||
| 	} | ||||
| 	m.minCompatibilityVersion.Store(minCompatibilityVersion) | ||||
| } | ||||
|  | ||||
| // SetMinCompatibilityVersion should be called after SetEmulationVersion | ||||
| func (m *effectiveVersion) SetMinCompatibilityVersion(minCompatibilityVersion *version.Version) { | ||||
| 	m.minCompatibilityVersion.Store(majorMinor(minCompatibilityVersion)) | ||||
| } | ||||
|  | ||||
| func (m *effectiveVersion) AllowedEmulationVersionRange() string { | ||||
| 	binaryVersion := m.BinaryVersion() | ||||
| 	if binaryVersion == nil { | ||||
| 		return "" | ||||
| 	} | ||||
|  | ||||
| 	// Consider patch version to be 0. | ||||
| 	binaryVersion = version.MajorMinor(binaryVersion.Major(), binaryVersion.Minor()) | ||||
|  | ||||
| 	floor := m.emulationVersionFloor | ||||
| 	if floor == nil { | ||||
| 		floor = version.MajorMinor(0, 0) | ||||
| 	} | ||||
|  | ||||
| 	return fmt.Sprintf("%s..%s (default=%s)", floor.String(), binaryVersion.String(), m.EmulationVersion().String()) | ||||
| } | ||||
|  | ||||
| func (m *effectiveVersion) AllowedMinCompatibilityVersionRange() string { | ||||
| 	binaryVersion := m.BinaryVersion() | ||||
| 	if binaryVersion == nil { | ||||
| 		return "" | ||||
| 	} | ||||
|  | ||||
| 	// Consider patch version to be 0. | ||||
| 	binaryVersion = version.MajorMinor(binaryVersion.Major(), binaryVersion.Minor()) | ||||
|  | ||||
| 	floor := m.minCompatibilityVersionFloor | ||||
| 	if floor == nil { | ||||
| 		floor = version.MajorMinor(0, 0) | ||||
| 	} | ||||
|  | ||||
| 	return fmt.Sprintf("%s..%s (default=%s)", floor.String(), binaryVersion.String(), m.MinCompatibilityVersion().String()) | ||||
| } | ||||
|  | ||||
| func (m *effectiveVersion) Validate() []error { | ||||
| 	var errs []error | ||||
| 	// Validate only checks the major and minor versions. | ||||
| 	binaryVersion := m.BinaryVersion().WithPatch(0) | ||||
| 	emulationVersion := m.emulationVersion.Load() | ||||
| 	minCompatibilityVersion := m.minCompatibilityVersion.Load() | ||||
| 	// emulationVersion can only be between emulationVersionFloor and binaryVersion | ||||
| 	if emulationVersion.GreaterThan(binaryVersion) || emulationVersion.LessThan(m.emulationVersionFloor) { | ||||
| 		errs = append(errs, fmt.Errorf("emulation version %s is not between [%s, %s]", emulationVersion.String(), m.emulationVersionFloor.String(), binaryVersion.String())) | ||||
| 	} | ||||
| 	// minCompatibilityVersion can only be between minCompatibilityVersionFloor and emulationVersion | ||||
| 	if minCompatibilityVersion.GreaterThan(emulationVersion) || minCompatibilityVersion.LessThan(m.minCompatibilityVersionFloor) { | ||||
| 		errs = append(errs, fmt.Errorf("minCompatibilityVersion version %s is not between [%s, %s]", minCompatibilityVersion.String(), m.minCompatibilityVersionFloor.String(), emulationVersion.String())) | ||||
| 	} | ||||
| 	return errs | ||||
| } | ||||
|  | ||||
| // NewEffectiveVersion creates a MutableEffectiveVersion from the binaryVersion. | ||||
| // If useDefaultBuildBinaryVersion is true, the call of BinaryVersion() will always return the current binary version. | ||||
| // NewEffectiveVersion(binaryVersion, true) should only be used if the binary version is dynamic. | ||||
| // Otherwise, use NewEffectiveVersion(binaryVersion, false) or NewEffectiveVersionFromString. | ||||
| func NewEffectiveVersion(binaryVersion *version.Version, useDefaultBuildBinaryVersion bool, emulationVersionFloor, minCompatibilityVersionFloor *version.Version) MutableEffectiveVersion { | ||||
| 	effective := &effectiveVersion{ | ||||
| 		emulationVersionFloor:        emulationVersionFloor, | ||||
| 		minCompatibilityVersionFloor: minCompatibilityVersionFloor, | ||||
| 	} | ||||
| 	compatVersion := binaryVersion.SubtractMinor(1) | ||||
| 	effective.binaryVersion.Store(binaryVersion) | ||||
| 	effective.useDefaultBuildBinaryVersion.Store(useDefaultBuildBinaryVersion) | ||||
| 	effective.SetEmulationVersion(binaryVersion) | ||||
| 	effective.SetMinCompatibilityVersion(compatVersion) | ||||
| 	return effective | ||||
| } | ||||
|  | ||||
| // NewEffectiveVersionFromString creates a MutableEffectiveVersion from the binaryVersion string. | ||||
| func NewEffectiveVersionFromString(binaryVer, emulationVerFloor, minCompatibilityVerFloor string) MutableEffectiveVersion { | ||||
| 	if binaryVer == "" { | ||||
| 		return &effectiveVersion{} | ||||
| 	} | ||||
| 	binaryVersion := version.MustParse(binaryVer) | ||||
| 	emulationVersionFloor := version.MajorMinor(0, 0) | ||||
| 	if emulationVerFloor != "" { | ||||
| 		emulationVersionFloor = version.MustParse(emulationVerFloor) | ||||
| 	} | ||||
| 	minCompatibilityVersionFloor := version.MajorMinor(0, 0) | ||||
| 	if minCompatibilityVerFloor != "" { | ||||
| 		minCompatibilityVersionFloor = version.MustParse(minCompatibilityVerFloor) | ||||
| 	} | ||||
| 	return NewEffectiveVersion(binaryVersion, false, emulationVersionFloor, minCompatibilityVersionFloor) | ||||
| } | ||||
|  | ||||
| func defaultBuildBinaryVersion() *version.Version { | ||||
| 	verInfo := baseversion.Get() | ||||
| 	return version.MustParse(verInfo.String()).WithInfo(verInfo) | ||||
| } | ||||
							
								
								
									
										148
									
								
								staging/src/k8s.io/component-base/compatibility/version_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										148
									
								
								staging/src/k8s.io/component-base/compatibility/version_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,148 @@ | ||||
| /* | ||||
| Copyright 2024 The Kubernetes Authors. | ||||
|  | ||||
| Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| you may not use this file except in compliance with the License. | ||||
| You may obtain a copy of the License at | ||||
|  | ||||
|     http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  | ||||
| Unless required by applicable law or agreed to in writing, software | ||||
| distributed under the License is distributed on an "AS IS" BASIS, | ||||
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| See the License for the specific language governing permissions and | ||||
| limitations under the License. | ||||
| */ | ||||
|  | ||||
| package compatibility | ||||
|  | ||||
| import ( | ||||
| 	"testing" | ||||
|  | ||||
| 	"k8s.io/apimachinery/pkg/util/version" | ||||
| ) | ||||
|  | ||||
| func TestValidate(t *testing.T) { | ||||
| 	tests := []struct { | ||||
| 		name                         string | ||||
| 		binaryVersion                string | ||||
| 		emulationVersion             string | ||||
| 		minCompatibilityVersion      string | ||||
| 		emulationVersionFloor        string | ||||
| 		minCompatibilityVersionFloor string | ||||
| 		expectErrors                 bool | ||||
| 	}{ | ||||
| 		{ | ||||
| 			name:                    "patch version diff ok", | ||||
| 			binaryVersion:           "v1.32.1", | ||||
| 			emulationVersion:        "v1.32.2", | ||||
| 			minCompatibilityVersion: "v1.32.5", | ||||
| 		}, | ||||
| 		{ | ||||
| 			name:                    "emulation version greater than binary not ok", | ||||
| 			binaryVersion:           "v1.32.2", | ||||
| 			emulationVersion:        "v1.33.0", | ||||
| 			minCompatibilityVersion: "v1.31.0", | ||||
| 			expectErrors:            true, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name:                    "min compatibility version greater than emulation version not ok", | ||||
| 			binaryVersion:           "v1.32.2", | ||||
| 			emulationVersion:        "v1.31.0", | ||||
| 			minCompatibilityVersion: "v1.32.0", | ||||
| 			expectErrors:            true, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name:                         "between floor and binary ok", | ||||
| 			binaryVersion:                "v1.32.1", | ||||
| 			emulationVersion:             "v1.31.0", | ||||
| 			minCompatibilityVersion:      "v1.30.0", | ||||
| 			emulationVersionFloor:        "v1.31.0", | ||||
| 			minCompatibilityVersionFloor: "v1.30.0", | ||||
| 		}, | ||||
| 		{ | ||||
| 			name:                         "emulation version less than floor not ok", | ||||
| 			binaryVersion:                "v1.32.1", | ||||
| 			emulationVersion:             "v1.30.0", | ||||
| 			minCompatibilityVersion:      "v1.30.0", | ||||
| 			emulationVersionFloor:        "v1.31.0", | ||||
| 			minCompatibilityVersionFloor: "v1.30.0", | ||||
| 			expectErrors:                 true, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name:                         "min compatibility version less than floor not ok", | ||||
| 			binaryVersion:                "v1.32.1", | ||||
| 			emulationVersion:             "v1.31.0", | ||||
| 			minCompatibilityVersion:      "v1.29.0", | ||||
| 			emulationVersionFloor:        "v1.31.0", | ||||
| 			minCompatibilityVersionFloor: "v1.30.0", | ||||
| 			expectErrors:                 true, | ||||
| 		}, | ||||
| 	} | ||||
|  | ||||
| 	for _, test := range tests { | ||||
| 		t.Run(test.name, func(t *testing.T) { | ||||
| 			effective := NewEffectiveVersionFromString(test.binaryVersion, test.emulationVersionFloor, test.minCompatibilityVersionFloor) | ||||
| 			emulationVersion := version.MustParseGeneric(test.emulationVersion) | ||||
| 			minCompatibilityVersion := version.MustParseGeneric(test.minCompatibilityVersion) | ||||
| 			effective.SetEmulationVersion(emulationVersion) | ||||
| 			effective.SetMinCompatibilityVersion(minCompatibilityVersion) | ||||
|  | ||||
| 			errs := effective.Validate() | ||||
| 			if len(errs) > 0 && !test.expectErrors { | ||||
| 				t.Errorf("expected no errors, errors found %+v", errs) | ||||
| 			} | ||||
|  | ||||
| 			if len(errs) == 0 && test.expectErrors { | ||||
| 				t.Errorf("expected errors, no errors found") | ||||
| 			} | ||||
| 		}) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestSetEmulationVersion(t *testing.T) { | ||||
| 	tests := []struct { | ||||
| 		name                          string | ||||
| 		binaryVersion                 string | ||||
| 		emulationVersion              string | ||||
| 		expectMinCompatibilityVersion string | ||||
| 		emulationVersionFloor         string | ||||
| 		minCompatibilityVersionFloor  string | ||||
| 	}{ | ||||
| 		{ | ||||
| 			name:                          "minCompatibilityVersion default to 1 minor less than emulationVersion", | ||||
| 			binaryVersion:                 "v1.34", | ||||
| 			emulationVersion:              "v1.32", | ||||
| 			expectMinCompatibilityVersion: "v1.31", | ||||
| 			emulationVersionFloor:         "v1.31", | ||||
| 			minCompatibilityVersionFloor:  "v1.31", | ||||
| 		}, | ||||
| 		{ | ||||
| 			name:                          "minCompatibilityVersion default to emulationVersion when hitting the floor", | ||||
| 			binaryVersion:                 "v1.34", | ||||
| 			emulationVersion:              "v1.31", | ||||
| 			expectMinCompatibilityVersion: "v1.31", | ||||
| 			emulationVersionFloor:         "v1.31", | ||||
| 			minCompatibilityVersionFloor:  "v1.31", | ||||
| 		}, | ||||
| 	} | ||||
|  | ||||
| 	for _, test := range tests { | ||||
| 		t.Run(test.name, func(t *testing.T) { | ||||
| 			effective := NewEffectiveVersionFromString(test.binaryVersion, test.emulationVersionFloor, test.minCompatibilityVersionFloor) | ||||
|  | ||||
| 			emulationVersion := version.MustParseGeneric(test.emulationVersion) | ||||
| 			effective.SetEmulationVersion(emulationVersion) | ||||
| 			errs := effective.Validate() | ||||
| 			if len(errs) > 0 { | ||||
| 				t.Fatalf("expected no Validate errors, errors found %+v", errs) | ||||
| 				return | ||||
| 			} | ||||
|  | ||||
| 			expectMinCompatibilityVersion := version.MustParseGeneric(test.expectMinCompatibilityVersion) | ||||
| 			if !effective.MinCompatibilityVersion().EqualTo(expectMinCompatibilityVersion) { | ||||
| 				t.Errorf("expected minCompatibilityVersion %s, got %s", expectMinCompatibilityVersion.String(), effective.MinCompatibilityVersion().String()) | ||||
| 			} | ||||
| 		}) | ||||
| 	} | ||||
| } | ||||
| @@ -19,43 +19,10 @@ package version | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"runtime" | ||||
| 	"sync/atomic" | ||||
|  | ||||
| 	"k8s.io/apimachinery/pkg/util/version" | ||||
| 	apimachineryversion "k8s.io/apimachinery/pkg/version" | ||||
| ) | ||||
|  | ||||
| var minimumKubeEmulationVersion *version.Version = version.MajorMinor(1, 31) | ||||
|  | ||||
| type EffectiveVersion interface { | ||||
| 	BinaryVersion() *version.Version | ||||
| 	EmulationVersion() *version.Version | ||||
| 	MinCompatibilityVersion() *version.Version | ||||
| 	EqualTo(other EffectiveVersion) bool | ||||
| 	String() string | ||||
| 	Validate() []error | ||||
| } | ||||
|  | ||||
| type MutableEffectiveVersion interface { | ||||
| 	EffectiveVersion | ||||
| 	Set(binaryVersion, emulationVersion, minCompatibilityVersion *version.Version) | ||||
| 	SetEmulationVersion(emulationVersion *version.Version) | ||||
| 	SetMinCompatibilityVersion(minCompatibilityVersion *version.Version) | ||||
| } | ||||
|  | ||||
| type effectiveVersion struct { | ||||
| 	// When true, BinaryVersion() returns the current binary version | ||||
| 	useDefaultBuildBinaryVersion atomic.Bool | ||||
| 	// Holds the last binary version stored in Set() | ||||
| 	binaryVersion atomic.Pointer[version.Version] | ||||
| 	// If the emulationVersion is set by the users, it could only contain major and minor versions. | ||||
| 	// In tests, emulationVersion could be the same as the binary version, or set directly, | ||||
| 	// which can have "alpha" as pre-release to continue serving expired apis while we clean up the test. | ||||
| 	emulationVersion atomic.Pointer[version.Version] | ||||
| 	// minCompatibilityVersion could only contain major and minor versions. | ||||
| 	minCompatibilityVersion atomic.Pointer[version.Version] | ||||
| } | ||||
|  | ||||
| // Get returns the overall codebase version. It's for detecting | ||||
| // what code a binary was built from. | ||||
| func Get() apimachineryversion.Info { | ||||
| @@ -73,134 +40,3 @@ func Get() apimachineryversion.Info { | ||||
| 		Platform:     fmt.Sprintf("%s/%s", runtime.GOOS, runtime.GOARCH), | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (m *effectiveVersion) BinaryVersion() *version.Version { | ||||
| 	if m.useDefaultBuildBinaryVersion.Load() { | ||||
| 		return defaultBuildBinaryVersion() | ||||
| 	} | ||||
| 	return m.binaryVersion.Load() | ||||
| } | ||||
|  | ||||
| func (m *effectiveVersion) EmulationVersion() *version.Version { | ||||
| 	ver := m.emulationVersion.Load() | ||||
| 	if ver != nil { | ||||
| 		// Emulation version can have "alpha" as pre-release to continue serving expired apis while we clean up the test. | ||||
| 		// The pre-release should not be accessible to the users. | ||||
| 		return ver.WithPreRelease(m.BinaryVersion().PreRelease()) | ||||
| 	} | ||||
| 	return ver | ||||
| } | ||||
|  | ||||
| func (m *effectiveVersion) MinCompatibilityVersion() *version.Version { | ||||
| 	return m.minCompatibilityVersion.Load() | ||||
| } | ||||
|  | ||||
| func (m *effectiveVersion) EqualTo(other EffectiveVersion) bool { | ||||
| 	return m.BinaryVersion().EqualTo(other.BinaryVersion()) && m.EmulationVersion().EqualTo(other.EmulationVersion()) && m.MinCompatibilityVersion().EqualTo(other.MinCompatibilityVersion()) | ||||
| } | ||||
|  | ||||
| func (m *effectiveVersion) String() string { | ||||
| 	if m == nil { | ||||
| 		return "<nil>" | ||||
| 	} | ||||
| 	return fmt.Sprintf("{BinaryVersion: %s, EmulationVersion: %s, MinCompatibilityVersion: %s}", | ||||
| 		m.BinaryVersion().String(), m.EmulationVersion().String(), m.MinCompatibilityVersion().String()) | ||||
| } | ||||
|  | ||||
| func majorMinor(ver *version.Version) *version.Version { | ||||
| 	if ver == nil { | ||||
| 		return ver | ||||
| 	} | ||||
| 	return version.MajorMinor(ver.Major(), ver.Minor()) | ||||
| } | ||||
|  | ||||
| func (m *effectiveVersion) Set(binaryVersion, emulationVersion, minCompatibilityVersion *version.Version) { | ||||
| 	m.binaryVersion.Store(binaryVersion) | ||||
| 	m.useDefaultBuildBinaryVersion.Store(false) | ||||
| 	m.emulationVersion.Store(majorMinor(emulationVersion)) | ||||
| 	m.minCompatibilityVersion.Store(majorMinor(minCompatibilityVersion)) | ||||
| } | ||||
|  | ||||
| func (m *effectiveVersion) SetEmulationVersion(emulationVersion *version.Version) { | ||||
| 	m.emulationVersion.Store(majorMinor(emulationVersion)) | ||||
| 	// set the default minCompatibilityVersion to be emulationVersion - 1 | ||||
| 	m.minCompatibilityVersion.Store(majorMinor(emulationVersion.SubtractMinor(1))) | ||||
| } | ||||
|  | ||||
| // SetMinCompatibilityVersion should be called after SetEmulationVersion | ||||
| func (m *effectiveVersion) SetMinCompatibilityVersion(minCompatibilityVersion *version.Version) { | ||||
| 	m.minCompatibilityVersion.Store(majorMinor(minCompatibilityVersion)) | ||||
| } | ||||
|  | ||||
| func (m *effectiveVersion) Validate() []error { | ||||
| 	var errs []error | ||||
| 	// Validate only checks the major and minor versions. | ||||
| 	binaryVersion := m.BinaryVersion().WithPatch(0) | ||||
| 	emulationVersion := m.emulationVersion.Load() | ||||
| 	minCompatibilityVersion := m.minCompatibilityVersion.Load() | ||||
|  | ||||
| 	// emulationVersion can only be 1.{binaryMinor-3}...1.{binaryMinor} | ||||
| 	maxEmuVer := binaryVersion | ||||
| 	minEmuVer := binaryVersion.SubtractMinor(3) | ||||
| 	if emulationVersion.GreaterThan(maxEmuVer) || emulationVersion.LessThan(minEmuVer) { | ||||
| 		errs = append(errs, fmt.Errorf("emulation version %s is not between [%s, %s]", emulationVersion.String(), minEmuVer.String(), maxEmuVer.String())) | ||||
| 	} | ||||
| 	// minCompatibilityVersion can only be 1.{binaryMinor-3} to 1.{binaryMinor} | ||||
| 	maxCompVer := emulationVersion | ||||
| 	minCompVer := binaryVersion.SubtractMinor(4) | ||||
| 	if minCompatibilityVersion.GreaterThan(maxCompVer) || minCompatibilityVersion.LessThan(minCompVer) { | ||||
| 		errs = append(errs, fmt.Errorf("minCompatibilityVersion version %s is not between [%s, %s]", minCompatibilityVersion.String(), minCompVer.String(), maxCompVer.String())) | ||||
| 	} | ||||
| 	return errs | ||||
| } | ||||
|  | ||||
| func newEffectiveVersion(binaryVersion *version.Version, useDefaultBuildBinaryVersion bool) MutableEffectiveVersion { | ||||
| 	effective := &effectiveVersion{} | ||||
| 	compatVersion := binaryVersion.SubtractMinor(1) | ||||
| 	effective.Set(binaryVersion, binaryVersion, compatVersion) | ||||
| 	effective.useDefaultBuildBinaryVersion.Store(useDefaultBuildBinaryVersion) | ||||
| 	return effective | ||||
| } | ||||
|  | ||||
| func NewEffectiveVersion(binaryVer string) MutableEffectiveVersion { | ||||
| 	if binaryVer == "" { | ||||
| 		return &effectiveVersion{} | ||||
| 	} | ||||
| 	binaryVersion := version.MustParse(binaryVer) | ||||
| 	return newEffectiveVersion(binaryVersion, false) | ||||
| } | ||||
|  | ||||
| func defaultBuildBinaryVersion() *version.Version { | ||||
| 	verInfo := Get() | ||||
| 	return version.MustParse(verInfo.String()).WithInfo(verInfo) | ||||
| } | ||||
|  | ||||
| // DefaultBuildEffectiveVersion returns the MutableEffectiveVersion based on the | ||||
| // current build information. | ||||
| func DefaultBuildEffectiveVersion() MutableEffectiveVersion { | ||||
| 	binaryVersion := defaultBuildBinaryVersion() | ||||
| 	if binaryVersion.Major() == 0 && binaryVersion.Minor() == 0 { | ||||
| 		return DefaultKubeEffectiveVersion() | ||||
| 	} | ||||
| 	return newEffectiveVersion(binaryVersion, true) | ||||
| } | ||||
|  | ||||
| // DefaultKubeEffectiveVersion returns the MutableEffectiveVersion based on the | ||||
| // latest K8s release. | ||||
| func DefaultKubeEffectiveVersion() MutableEffectiveVersion { | ||||
| 	binaryVersion := version.MustParse(DefaultKubeBinaryVersion).WithInfo(Get()) | ||||
| 	return newEffectiveVersion(binaryVersion, false) | ||||
| } | ||||
|  | ||||
| // ValidateKubeEffectiveVersion validates the EmulationVersion is at least 1.31 and MinCompatibilityVersion | ||||
| // is at least 1.30 for kube components. | ||||
| func ValidateKubeEffectiveVersion(effectiveVersion EffectiveVersion) error { | ||||
| 	if !effectiveVersion.EmulationVersion().AtLeast(minimumKubeEmulationVersion) { | ||||
| 		return fmt.Errorf("emulation version needs to be greater or equal to 1.31, got %s", effectiveVersion.EmulationVersion().String()) | ||||
| 	} | ||||
| 	if !effectiveVersion.MinCompatibilityVersion().AtLeast(minimumKubeEmulationVersion.SubtractMinor(1)) { | ||||
| 		return fmt.Errorf("minCompatibilityVersion version needs to be greater or equal to 1.30, got %s", effectiveVersion.MinCompatibilityVersion().String()) | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|   | ||||
| @@ -1,185 +0,0 @@ | ||||
| /* | ||||
| Copyright 2024 The Kubernetes Authors. | ||||
|  | ||||
| Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| you may not use this file except in compliance with the License. | ||||
| You may obtain a copy of the License at | ||||
|  | ||||
|     http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  | ||||
| Unless required by applicable law or agreed to in writing, software | ||||
| distributed under the License is distributed on an "AS IS" BASIS, | ||||
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| See the License for the specific language governing permissions and | ||||
| limitations under the License. | ||||
| */ | ||||
|  | ||||
| package version | ||||
|  | ||||
| import ( | ||||
| 	"testing" | ||||
|  | ||||
| 	"k8s.io/apimachinery/pkg/util/version" | ||||
| ) | ||||
|  | ||||
| func TestValidate(t *testing.T) { | ||||
| 	tests := []struct { | ||||
| 		name                    string | ||||
| 		binaryVersion           string | ||||
| 		emulationVersion        string | ||||
| 		minCompatibilityVersion string | ||||
| 		expectErrors            bool | ||||
| 	}{ | ||||
| 		{ | ||||
| 			name:                    "patch version diff ok", | ||||
| 			binaryVersion:           "v1.32.2", | ||||
| 			emulationVersion:        "v1.32.1", | ||||
| 			minCompatibilityVersion: "v1.31.5", | ||||
| 		}, | ||||
| 		{ | ||||
| 			name:                    "emulation version one minor lower than binary ok", | ||||
| 			binaryVersion:           "v1.32.2", | ||||
| 			emulationVersion:        "v1.31.0", | ||||
| 			minCompatibilityVersion: "v1.31.0", | ||||
| 		}, | ||||
| 		{ | ||||
| 			name:                    "emulation version two minor lower than binary ok", | ||||
| 			binaryVersion:           "v1.33.2", | ||||
| 			emulationVersion:        "v1.31.0", | ||||
| 			minCompatibilityVersion: "v1.31.0", | ||||
| 			expectErrors:            false, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name:                    "emulation version three minor lower than binary ok", | ||||
| 			binaryVersion:           "v1.35.0", | ||||
| 			emulationVersion:        "v1.32.0", | ||||
| 			minCompatibilityVersion: "v1.32.0", | ||||
| 		}, | ||||
| 		{ | ||||
| 			name:                    "emulation version four minor lower than binary not ok", | ||||
| 			binaryVersion:           "v1.36.0", | ||||
| 			emulationVersion:        "v1.32.0", | ||||
| 			minCompatibilityVersion: "v1.32.0", | ||||
| 			expectErrors:            true, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name:                    "emulation version one minor higher than binary not ok", | ||||
| 			binaryVersion:           "v1.32.2", | ||||
| 			emulationVersion:        "v1.33.0", | ||||
| 			minCompatibilityVersion: "v1.31.0", | ||||
| 			expectErrors:            true, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name:                    "emulation version two minor higher than binary not ok", | ||||
| 			binaryVersion:           "v1.32.2", | ||||
| 			emulationVersion:        "v1.34.0", | ||||
| 			minCompatibilityVersion: "v1.31.0", | ||||
| 			expectErrors:            true, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name:                    "compatibility version same as binary ok", | ||||
| 			binaryVersion:           "v1.32.2", | ||||
| 			emulationVersion:        "v1.32.0", | ||||
| 			minCompatibilityVersion: "v1.32.0", | ||||
| 			expectErrors:            false, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name:                    "compatibility version two minor lower than binary ok", | ||||
| 			binaryVersion:           "v1.32.2", | ||||
| 			emulationVersion:        "v1.32.0", | ||||
| 			minCompatibilityVersion: "v1.30.0", | ||||
| 			expectErrors:            false, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name:                    "compatibility version three minor lower than binary ok", | ||||
| 			binaryVersion:           "v1.34.2", | ||||
| 			emulationVersion:        "v1.33.0", | ||||
| 			minCompatibilityVersion: "v1.31.0", | ||||
| 			expectErrors:            false, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name:                    "compatibility version one minor higher than binary not ok", | ||||
| 			binaryVersion:           "v1.32.2", | ||||
| 			emulationVersion:        "v1.32.0", | ||||
| 			minCompatibilityVersion: "v1.33.0", | ||||
| 			expectErrors:            true, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name:                    "emulation version lower than compatibility version not ok", | ||||
| 			binaryVersion:           "v1.34.2", | ||||
| 			emulationVersion:        "v1.32.0", | ||||
| 			minCompatibilityVersion: "v1.33.0", | ||||
| 			expectErrors:            true, | ||||
| 		}, | ||||
| 	} | ||||
|  | ||||
| 	for _, test := range tests { | ||||
| 		t.Run(test.name, func(t *testing.T) { | ||||
| 			binaryVersion := version.MustParseGeneric(test.binaryVersion) | ||||
| 			effective := &effectiveVersion{} | ||||
| 			emulationVersion := version.MustParseGeneric(test.emulationVersion) | ||||
| 			minCompatibilityVersion := version.MustParseGeneric(test.minCompatibilityVersion) | ||||
| 			effective.Set(binaryVersion, emulationVersion, minCompatibilityVersion) | ||||
|  | ||||
| 			errs := effective.Validate() | ||||
| 			if len(errs) > 0 && !test.expectErrors { | ||||
| 				t.Errorf("expected no errors, errors found %+v", errs) | ||||
| 			} | ||||
|  | ||||
| 			if len(errs) == 0 && test.expectErrors { | ||||
| 				t.Errorf("expected errors, no errors found") | ||||
| 			} | ||||
| 		}) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestValidateKubeEffectiveVersion(t *testing.T) { | ||||
| 	tests := []struct { | ||||
| 		name                    string | ||||
| 		emulationVersion        string | ||||
| 		minCompatibilityVersion string | ||||
| 		expectErr               bool | ||||
| 	}{ | ||||
| 		{ | ||||
| 			name:                    "valid versions", | ||||
| 			emulationVersion:        "v1.31.0", | ||||
| 			minCompatibilityVersion: "v1.31.0", | ||||
| 			expectErr:               false, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name:                    "emulationVersion too low", | ||||
| 			emulationVersion:        "v1.30.0", | ||||
| 			minCompatibilityVersion: "v1.31.0", | ||||
| 			expectErr:               true, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name:                    "minCompatibilityVersion too low", | ||||
| 			emulationVersion:        "v1.31.0", | ||||
| 			minCompatibilityVersion: "v1.29.0", | ||||
| 			expectErr:               true, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name:                    "both versions too low", | ||||
| 			emulationVersion:        "v1.30.0", | ||||
| 			minCompatibilityVersion: "v1.30.0", | ||||
| 			expectErr:               true, | ||||
| 		}, | ||||
| 	} | ||||
|  | ||||
| 	for _, test := range tests { | ||||
| 		t.Run(test.name, func(t *testing.T) { | ||||
|  | ||||
| 			effective := NewEffectiveVersion("1.32") | ||||
| 			effective.SetEmulationVersion(version.MustParseGeneric(test.emulationVersion)) | ||||
| 			effective.SetMinCompatibilityVersion(version.MustParseGeneric(test.minCompatibilityVersion)) | ||||
|  | ||||
| 			err := ValidateKubeEffectiveVersion(effective) | ||||
| 			if test.expectErr && err == nil { | ||||
| 				t.Error("expected error, but got nil") | ||||
| 			} | ||||
| 			if !test.expectErr && err != nil { | ||||
| 				t.Errorf("unexpected error: %v", err) | ||||
| 			} | ||||
| 		}) | ||||
| 	} | ||||
| } | ||||
| @@ -20,9 +20,9 @@ import ( | ||||
| 	"time" | ||||
|  | ||||
| 	"k8s.io/apimachinery/pkg/util/version" | ||||
| 	"k8s.io/component-base/featuregate" | ||||
| 	"k8s.io/klog/v2" | ||||
|  | ||||
| 	"k8s.io/component-base/compatibility" | ||||
| 	compbasemetrics "k8s.io/component-base/metrics" | ||||
| 	utilversion "k8s.io/component-base/version" | ||||
| ) | ||||
| @@ -34,9 +34,12 @@ type statuszRegistry interface { | ||||
| 	emulationVersion() *version.Version | ||||
| } | ||||
|  | ||||
| type registry struct{} | ||||
| type registry struct { | ||||
| 	// componentGlobalsRegistry compatibility.ComponentGlobalsRegistry | ||||
| 	effectiveVersion compatibility.EffectiveVersion | ||||
| } | ||||
|  | ||||
| func (registry) processStartTime() time.Time { | ||||
| func (*registry) processStartTime() time.Time { | ||||
| 	start, err := compbasemetrics.GetProcessStart() | ||||
| 	if err != nil { | ||||
| 		klog.Errorf("Could not get process start time, %v", err) | ||||
| @@ -45,23 +48,20 @@ func (registry) processStartTime() time.Time { | ||||
| 	return time.Unix(int64(start), 0) | ||||
| } | ||||
|  | ||||
| func (registry) goVersion() string { | ||||
| func (*registry) goVersion() string { | ||||
| 	return utilversion.Get().GoVersion | ||||
| } | ||||
|  | ||||
| func (registry) binaryVersion() *version.Version { | ||||
| 	effectiveVer := featuregate.DefaultComponentGlobalsRegistry.EffectiveVersionFor(featuregate.DefaultKubeComponent) | ||||
| 	if effectiveVer != nil { | ||||
| 		return effectiveVer.BinaryVersion() | ||||
| func (r *registry) binaryVersion() *version.Version { | ||||
| 	if r.effectiveVersion != nil { | ||||
| 		return r.effectiveVersion.BinaryVersion() | ||||
| 	} | ||||
|  | ||||
| 	return utilversion.DefaultKubeEffectiveVersion().BinaryVersion() | ||||
| 	return version.MustParse(utilversion.Get().String()) | ||||
| } | ||||
|  | ||||
| func (registry) emulationVersion() *version.Version { | ||||
| 	effectiveVer := featuregate.DefaultComponentGlobalsRegistry.EffectiveVersionFor(featuregate.DefaultKubeComponent) | ||||
| 	if effectiveVer != nil { | ||||
| 		return effectiveVer.EmulationVersion() | ||||
| func (r *registry) emulationVersion() *version.Version { | ||||
| 	if r.effectiveVersion != nil { | ||||
| 		return r.effectiveVersion.EmulationVersion() | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
|   | ||||
| @@ -20,14 +20,12 @@ import ( | ||||
| 	"testing" | ||||
|  | ||||
| 	"github.com/stretchr/testify/assert" | ||||
| 	utilruntime "k8s.io/apimachinery/pkg/util/runtime" | ||||
| 	"k8s.io/apimachinery/pkg/util/version" | ||||
| 	"k8s.io/component-base/featuregate" | ||||
| 	"k8s.io/component-base/compatibility" | ||||
| 	utilversion "k8s.io/component-base/version" | ||||
| ) | ||||
|  | ||||
| func TestBinaryVersion(t *testing.T) { | ||||
| 	componentGlobalsRegistry := featuregate.DefaultComponentGlobalsRegistry | ||||
| 	tests := []struct { | ||||
| 		name                    string | ||||
| 		setFakeEffectiveVersion bool | ||||
| @@ -42,20 +40,18 @@ func TestBinaryVersion(t *testing.T) { | ||||
| 		}, | ||||
| 		{ | ||||
| 			name:              "binaryVersion without effective version", | ||||
| 			wantBinaryVersion: utilversion.DefaultKubeEffectiveVersion().BinaryVersion(), | ||||
| 			wantBinaryVersion: version.MustParse(utilversion.Get().String()), | ||||
| 		}, | ||||
| 	} | ||||
|  | ||||
| 	for _, tt := range tests { | ||||
| 		componentGlobalsRegistry.Reset() | ||||
| 		t.Run(tt.name, func(t *testing.T) { | ||||
| 			registry := ®istry{} | ||||
| 			if tt.setFakeEffectiveVersion { | ||||
| 				verKube := utilversion.NewEffectiveVersion(tt.fakeVersion) | ||||
| 				fg := featuregate.NewVersionedFeatureGate(version.MustParse(tt.fakeVersion)) | ||||
| 				utilruntime.Must(componentGlobalsRegistry.Register(featuregate.DefaultKubeComponent, verKube, fg)) | ||||
| 				verKube := compatibility.NewEffectiveVersionFromString(tt.fakeVersion, "", "") | ||||
| 				registry.effectiveVersion = verKube | ||||
| 			} | ||||
|  | ||||
| 			registry := ®istry{} | ||||
| 			got := registry.binaryVersion() | ||||
| 			assert.Equal(t, tt.wantBinaryVersion, got) | ||||
| 		}) | ||||
| @@ -63,7 +59,6 @@ func TestBinaryVersion(t *testing.T) { | ||||
| } | ||||
|  | ||||
| func TestEmulationVersion(t *testing.T) { | ||||
| 	componentGlobalsRegistry := featuregate.DefaultComponentGlobalsRegistry | ||||
| 	tests := []struct { | ||||
| 		name                    string | ||||
| 		setFakeEffectiveVersion bool | ||||
| @@ -83,16 +78,14 @@ func TestEmulationVersion(t *testing.T) { | ||||
| 	} | ||||
|  | ||||
| 	for _, tt := range tests { | ||||
| 		componentGlobalsRegistry.Reset() | ||||
| 		t.Run(tt.name, func(t *testing.T) { | ||||
| 			registry := ®istry{} | ||||
| 			if tt.setFakeEffectiveVersion { | ||||
| 				verKube := utilversion.NewEffectiveVersion("0.0.0") | ||||
| 				verKube := compatibility.NewEffectiveVersionFromString("0.0.0", "", "") | ||||
| 				verKube.SetEmulationVersion(version.MustParse(tt.fakeEmulVer)) | ||||
| 				fg := featuregate.NewVersionedFeatureGate(version.MustParse(tt.fakeEmulVer)) | ||||
| 				utilruntime.Must(componentGlobalsRegistry.Register(featuregate.DefaultKubeComponent, verKube, fg)) | ||||
| 				registry.effectiveVersion = verKube | ||||
| 			} | ||||
|  | ||||
| 			registry := ®istry{} | ||||
| 			got := registry.emulationVersion() | ||||
| 			if tt.wantEmul != nil && got != nil { | ||||
| 				assert.Equal(t, tt.wantEmul.Major(), got.Major()) | ||||
|   | ||||
| @@ -24,6 +24,7 @@ import ( | ||||
| 	"net/http" | ||||
| 	"time" | ||||
|  | ||||
| 	"k8s.io/component-base/compatibility" | ||||
| 	"k8s.io/component-base/zpages/httputil" | ||||
| 	"k8s.io/klog/v2" | ||||
| ) | ||||
| @@ -62,8 +63,8 @@ type mux interface { | ||||
| 	Handle(path string, handler http.Handler) | ||||
| } | ||||
|  | ||||
| func NewRegistry() statuszRegistry { | ||||
| 	return registry{} | ||||
| func NewRegistry(effectiveVersion compatibility.EffectiveVersion) statuszRegistry { | ||||
| 	return ®istry{effectiveVersion: effectiveVersion} | ||||
| } | ||||
|  | ||||
| func Install(m mux, componentName string, reg statuszRegistry) { | ||||
|   | ||||
| @@ -45,7 +45,6 @@ require ( | ||||
| 	github.com/google/gofuzz v1.2.0 // indirect | ||||
| 	github.com/google/uuid v1.6.0 // indirect | ||||
| 	github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect | ||||
| 	github.com/inconshreveable/mousetrap v1.1.0 // indirect | ||||
| 	github.com/josharian/intern v1.0.0 // indirect | ||||
| 	github.com/json-iterator/go v1.1.12 // indirect | ||||
| 	github.com/mailru/easyjson v0.7.7 // indirect | ||||
| @@ -57,7 +56,6 @@ require ( | ||||
| 	github.com/prometheus/client_model v0.6.1 // indirect | ||||
| 	github.com/prometheus/common v0.55.0 // indirect | ||||
| 	github.com/prometheus/procfs v0.15.1 // indirect | ||||
| 	github.com/spf13/cobra v1.8.1 // indirect | ||||
| 	github.com/spf13/pflag v1.0.5 // indirect | ||||
| 	github.com/x448/float16 v0.8.4 // indirect | ||||
| 	go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 // indirect | ||||
|   | ||||
							
								
								
									
										4
									
								
								staging/src/k8s.io/cri-client/go.sum
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										4
									
								
								staging/src/k8s.io/cri-client/go.sum
									
									
									
										generated
									
									
									
								
							| @@ -18,7 +18,6 @@ github.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91 | ||||
| github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= | ||||
| github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= | ||||
| github.com/cncf/xds/go v0.0.0-20240423153145-555b57ec207b/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8= | ||||
| github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= | ||||
| github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= | ||||
| github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | ||||
| github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= | ||||
| @@ -70,7 +69,6 @@ github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/ad | ||||
| github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= | ||||
| github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 h1:bkypFPDjIYGfCYD5mRBvpqxfYX1YCS1PXdKYWi8FsN0= | ||||
| github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k= | ||||
| github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= | ||||
| github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= | ||||
| github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= | ||||
| github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= | ||||
| @@ -117,9 +115,7 @@ github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoG | ||||
| github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= | ||||
| github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= | ||||
| github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= | ||||
| github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= | ||||
| github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= | ||||
| github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= | ||||
| github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= | ||||
| github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= | ||||
| github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= | ||||
|   | ||||
| @@ -31,7 +31,6 @@ import ( | ||||
| 	genericapiserver "k8s.io/apiserver/pkg/server" | ||||
| 	"k8s.io/apiserver/pkg/server/filters" | ||||
| 	genericoptions "k8s.io/apiserver/pkg/server/options" | ||||
| 	"k8s.io/component-base/featuregate" | ||||
| 	"k8s.io/kube-aggregator/pkg/apis/apiregistration/v1beta1" | ||||
| 	"k8s.io/kube-aggregator/pkg/apiserver" | ||||
| 	aggregatorscheme "k8s.io/kube-aggregator/pkg/apiserver/scheme" | ||||
| @@ -63,7 +62,7 @@ func NewCommandStartAggregator(ctx context.Context, defaults *AggregatorOptions) | ||||
| 		Short: "Launch a API aggregator and proxy server", | ||||
| 		Long:  "Launch a API aggregator and proxy server", | ||||
| 		PersistentPreRunE: func(*cobra.Command, []string) error { | ||||
| 			return featuregate.DefaultComponentGlobalsRegistry.Set() | ||||
| 			return o.ServerRunOptions.ComponentGlobalsRegistry.Set() | ||||
| 		}, | ||||
| 		RunE: func(c *cobra.Command, args []string) error { | ||||
| 			if err := o.Complete(); err != nil { | ||||
|   | ||||
| @@ -33,9 +33,11 @@ import ( | ||||
| 	"k8s.io/apiserver/pkg/endpoints/openapi" | ||||
| 	genericapiserver "k8s.io/apiserver/pkg/server" | ||||
| 	genericoptions "k8s.io/apiserver/pkg/server/options" | ||||
| 	"k8s.io/apiserver/pkg/util/compatibility" | ||||
| 	utilfeature "k8s.io/apiserver/pkg/util/feature" | ||||
| 	basecompatibility "k8s.io/component-base/compatibility" | ||||
| 	"k8s.io/component-base/featuregate" | ||||
| 	utilversion "k8s.io/component-base/version" | ||||
| 	baseversion "k8s.io/component-base/version" | ||||
| 	"k8s.io/sample-apiserver/pkg/admission/plugin/banflunder" | ||||
| 	"k8s.io/sample-apiserver/pkg/admission/wardleinitializer" | ||||
| 	"k8s.io/sample-apiserver/pkg/apis/wardle/v1alpha1" | ||||
| @@ -51,6 +53,8 @@ const defaultEtcdPathPrefix = "/registry/wardle.example.com" | ||||
| // WardleServerOptions contains state for master/api server | ||||
| type WardleServerOptions struct { | ||||
| 	RecommendedOptions *genericoptions.RecommendedOptions | ||||
| 	// ComponentGlobalsRegistry is the registry where the effective versions and feature gates for all components are stored. | ||||
| 	ComponentGlobalsRegistry basecompatibility.ComponentGlobalsRegistry | ||||
|  | ||||
| 	SharedInformerFactory informers.SharedInformerFactory | ||||
| 	StdOut                io.Writer | ||||
| @@ -63,7 +67,7 @@ func WardleVersionToKubeVersion(ver *version.Version) *version.Version { | ||||
| 	if ver.Major() != 1 { | ||||
| 		return nil | ||||
| 	} | ||||
| 	kubeVer := utilversion.DefaultKubeEffectiveVersion().BinaryVersion() | ||||
| 	kubeVer := version.MustParse(baseversion.DefaultKubeBinaryVersion) | ||||
| 	// "1.2" maps to kubeVer | ||||
| 	offset := int(ver.Minor()) - 2 | ||||
| 	mappedVer := kubeVer.OffsetMinor(offset) | ||||
| @@ -80,6 +84,7 @@ func NewWardleServerOptions(out, errOut io.Writer) *WardleServerOptions { | ||||
| 			defaultEtcdPathPrefix, | ||||
| 			apiserver.Codecs.LegacyCodec(v1alpha1.SchemeGroupVersion), | ||||
| 		), | ||||
| 		ComponentGlobalsRegistry: compatibility.DefaultComponentGlobalsRegistry, | ||||
|  | ||||
| 		StdOut: out, | ||||
| 		StdErr: errOut, | ||||
| @@ -99,7 +104,7 @@ func NewCommandStartWardleServer(ctx context.Context, defaults *WardleServerOpti | ||||
| 			if skipDefaultComponentGlobalsRegistrySet { | ||||
| 				return nil | ||||
| 			} | ||||
| 			return featuregate.DefaultComponentGlobalsRegistry.Set() | ||||
| 			return defaults.ComponentGlobalsRegistry.Set() | ||||
| 		}, | ||||
| 		RunE: func(c *cobra.Command, args []string) error { | ||||
| 			if err := o.Complete(); err != nil { | ||||
| @@ -135,8 +140,8 @@ func NewCommandStartWardleServer(ctx context.Context, defaults *WardleServerOpti | ||||
| 	// Register the "Wardle" component with the global component registry, | ||||
| 	// associating it with its effective version and feature gate configuration. | ||||
| 	// Will skip if the component has been registered, like in the integration test. | ||||
| 	_, wardleFeatureGate := featuregate.DefaultComponentGlobalsRegistry.ComponentGlobalsOrRegister( | ||||
| 		apiserver.WardleComponentName, utilversion.NewEffectiveVersion(defaultWardleVersion), | ||||
| 	_, wardleFeatureGate := defaults.ComponentGlobalsRegistry.ComponentGlobalsOrRegister( | ||||
| 		apiserver.WardleComponentName, basecompatibility.NewEffectiveVersionFromString(defaultWardleVersion, "", ""), | ||||
| 		featuregate.NewVersionedFeatureGate(version.MustParse(defaultWardleVersion))) | ||||
|  | ||||
| 	// Add versioned feature specifications for the "BanFlunder" feature. | ||||
| @@ -150,14 +155,14 @@ func NewCommandStartWardleServer(ctx context.Context, defaults *WardleServerOpti | ||||
| 	})) | ||||
|  | ||||
| 	// Register the default kube component if not already present in the global registry. | ||||
| 	_, _ = featuregate.DefaultComponentGlobalsRegistry.ComponentGlobalsOrRegister(featuregate.DefaultKubeComponent, | ||||
| 		utilversion.NewEffectiveVersion(utilversion.DefaultKubeBinaryVersion), utilfeature.DefaultMutableFeatureGate) | ||||
| 	_, _ = defaults.ComponentGlobalsRegistry.ComponentGlobalsOrRegister(basecompatibility.DefaultKubeComponent, | ||||
| 		basecompatibility.NewEffectiveVersionFromString(baseversion.DefaultKubeBinaryVersion, "", ""), utilfeature.DefaultMutableFeatureGate) | ||||
|  | ||||
| 	// Set the emulation version mapping from the "Wardle" component to the kube component. | ||||
| 	// This ensures that the emulation version of the latter is determined by the emulation version of the former. | ||||
| 	utilruntime.Must(featuregate.DefaultComponentGlobalsRegistry.SetEmulationVersionMapping(apiserver.WardleComponentName, featuregate.DefaultKubeComponent, WardleVersionToKubeVersion)) | ||||
| 	utilruntime.Must(defaults.ComponentGlobalsRegistry.SetEmulationVersionMapping(apiserver.WardleComponentName, basecompatibility.DefaultKubeComponent, WardleVersionToKubeVersion)) | ||||
|  | ||||
| 	featuregate.DefaultComponentGlobalsRegistry.AddFlags(flags) | ||||
| 	defaults.ComponentGlobalsRegistry.AddFlags(flags) | ||||
|  | ||||
| 	return cmd | ||||
| } | ||||
| @@ -166,13 +171,13 @@ func NewCommandStartWardleServer(ctx context.Context, defaults *WardleServerOpti | ||||
| func (o WardleServerOptions) Validate(args []string) error { | ||||
| 	errors := []error{} | ||||
| 	errors = append(errors, o.RecommendedOptions.Validate()...) | ||||
| 	errors = append(errors, featuregate.DefaultComponentGlobalsRegistry.Validate()...) | ||||
| 	errors = append(errors, o.ComponentGlobalsRegistry.Validate()...) | ||||
| 	return utilerrors.NewAggregate(errors) | ||||
| } | ||||
|  | ||||
| // Complete fills in fields required to have valid data | ||||
| func (o *WardleServerOptions) Complete() error { | ||||
| 	if featuregate.DefaultComponentGlobalsRegistry.FeatureGateFor(apiserver.WardleComponentName).Enabled("BanFlunder") { | ||||
| 	if o.ComponentGlobalsRegistry.FeatureGateFor(apiserver.WardleComponentName).Enabled("BanFlunder") { | ||||
| 		// register admission plugins | ||||
| 		banflunder.Register(o.RecommendedOptions.Admission.Plugins) | ||||
|  | ||||
| @@ -209,8 +214,8 @@ func (o *WardleServerOptions) Config() (*apiserver.Config, error) { | ||||
| 	serverConfig.OpenAPIV3Config.Info.Title = "Wardle" | ||||
| 	serverConfig.OpenAPIV3Config.Info.Version = "0.1" | ||||
|  | ||||
| 	serverConfig.FeatureGate = featuregate.DefaultComponentGlobalsRegistry.FeatureGateFor(featuregate.DefaultKubeComponent) | ||||
| 	serverConfig.EffectiveVersion = featuregate.DefaultComponentGlobalsRegistry.EffectiveVersionFor(apiserver.WardleComponentName) | ||||
| 	serverConfig.FeatureGate = o.ComponentGlobalsRegistry.FeatureGateFor(basecompatibility.DefaultKubeComponent) | ||||
| 	serverConfig.EffectiveVersion = o.ComponentGlobalsRegistry.EffectiveVersionFor(apiserver.WardleComponentName) | ||||
|  | ||||
| 	if err := o.RecommendedOptions.ApplyTo(serverConfig); err != nil { | ||||
| 		return nil, err | ||||
|   | ||||
| @@ -20,13 +20,13 @@ import ( | ||||
| 	"testing" | ||||
|  | ||||
| 	"k8s.io/apimachinery/pkg/util/version" | ||||
| 	utilversion "k8s.io/component-base/version" | ||||
| 	"k8s.io/apiserver/pkg/util/compatibility" | ||||
|  | ||||
| 	"github.com/stretchr/testify/assert" | ||||
| ) | ||||
|  | ||||
| func TestWardleEmulationVersionToKubeEmulationVersion(t *testing.T) { | ||||
| 	defaultKubeEffectiveVersion := utilversion.DefaultKubeEffectiveVersion() | ||||
| 	defaultKubeEffectiveVersion := compatibility.DefaultKubeEffectiveVersionForTest() | ||||
|  | ||||
| 	testCases := []struct { | ||||
| 		desc                     string | ||||
|   | ||||
| @@ -22,7 +22,6 @@ import ( | ||||
| 	"crypto/x509" | ||||
| 	"encoding/json" | ||||
| 	"io" | ||||
| 	"k8s.io/apimachinery/pkg/util/version" | ||||
| 	"net/http" | ||||
| 	"net/http/httptest" | ||||
| 	"strconv" | ||||
| @@ -634,9 +633,9 @@ func TestMatchConditionsWithoutStrictCostEnforcement(t *testing.T) { | ||||
| 	for _, testcase := range testcases { | ||||
| 		t.Run(testcase.name, func(t *testing.T) { | ||||
| 			upCh := recorder.Reset() | ||||
| 			featuregatetesting.SetFeatureGateEmulationVersionDuringTest(t, utilfeature.DefaultFeatureGate, version.MustParse("1.31")) | ||||
| 			featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, genericfeatures.StrictCostEnforcementForWebhooks, false) | ||||
| 			server, err := apiservertesting.StartTestServer(t, &apiservertesting.TestServerInstanceOptions{EmulationVersion: "1.31"}, []string{ | ||||
| 			server, err := apiservertesting.StartTestServer(t, nil, []string{ | ||||
| 				"--emulated-version", "1.31", | ||||
| 				"--feature-gates", "StrictCostEnforcementForWebhooks=false", | ||||
| 				"--disable-admission-plugins=ServiceAccount", | ||||
| 			}, framework.SharedEtcd()) | ||||
| 			if err != nil { | ||||
|   | ||||
| @@ -56,10 +56,12 @@ import ( | ||||
| 	"k8s.io/apimachinery/pkg/types" | ||||
| 	utiljson "k8s.io/apimachinery/pkg/util/json" | ||||
| 	"k8s.io/apimachinery/pkg/util/uuid" | ||||
| 	"k8s.io/apimachinery/pkg/util/version" | ||||
| 	"k8s.io/apimachinery/pkg/util/wait" | ||||
| 	"k8s.io/apimachinery/pkg/watch" | ||||
| 	"k8s.io/apiserver/pkg/endpoints/handlers" | ||||
| 	"k8s.io/apiserver/pkg/storage/storagebackend" | ||||
| 	"k8s.io/apiserver/pkg/util/compatibility" | ||||
| 	utilfeature "k8s.io/apiserver/pkg/util/feature" | ||||
| 	"k8s.io/client-go/discovery/cached/memory" | ||||
| 	"k8s.io/client-go/dynamic" | ||||
| @@ -70,7 +72,6 @@ import ( | ||||
| 	"k8s.io/client-go/restmapper" | ||||
| 	"k8s.io/client-go/tools/pager" | ||||
| 	featuregatetesting "k8s.io/component-base/featuregate/testing" | ||||
| 	utilversion "k8s.io/component-base/version" | ||||
| 	"k8s.io/klog/v2" | ||||
| 	"k8s.io/kubernetes/cmd/kube-apiserver/app/options" | ||||
| 	kubeapiservertesting "k8s.io/kubernetes/cmd/kube-apiserver/app/testing" | ||||
| @@ -3198,8 +3199,7 @@ func TestEmulatedStorageVersion(t *testing.T) { | ||||
| 	for emulatedVersion, cases := range groupedCases { | ||||
| 		t.Run(emulatedVersion, func(t *testing.T) { | ||||
| 			server := kubeapiservertesting.StartTestServerOrDie( | ||||
| 				t, &kubeapiservertesting.TestServerInstanceOptions{BinaryVersion: emulatedVersion}, | ||||
| 				[]string{"--emulated-version=kube=" + emulatedVersion, `--storage-media-type=application/json`}, framework.SharedEtcd()) | ||||
| 				t, nil, []string{"--emulated-version=kube=" + emulatedVersion, `--storage-media-type=application/json`}, framework.SharedEtcd()) | ||||
| 			defer server.TearDownFn() | ||||
|  | ||||
| 			client := clientset.NewForConfigOrDie(server.ClientConfig) | ||||
| @@ -3302,7 +3302,7 @@ func TestAllowedEmulationVersions(t *testing.T) { | ||||
| 	}{ | ||||
| 		{ | ||||
| 			name:             "default", | ||||
| 			emulationVersion: utilversion.DefaultKubeEffectiveVersion().EmulationVersion().String(), | ||||
| 			emulationVersion: compatibility.DefaultKubeEffectiveVersionForTest().EmulationVersion().String(), | ||||
| 		}, | ||||
| 	} | ||||
|  | ||||
| @@ -3337,6 +3337,7 @@ func TestAllowedEmulationVersions(t *testing.T) { | ||||
| } | ||||
|  | ||||
| func TestEnableEmulationVersion(t *testing.T) { | ||||
| 	featuregatetesting.SetFeatureGateEmulationVersionDuringTest(t, utilfeature.DefaultFeatureGate, version.MustParse("1.32")) | ||||
| 	server := kubeapiservertesting.StartTestServerOrDie(t, | ||||
| 		&kubeapiservertesting.TestServerInstanceOptions{BinaryVersion: "1.32"}, | ||||
| 		[]string{"--emulated-version=kube=1.31"}, framework.SharedEtcd()) | ||||
| @@ -3398,6 +3399,7 @@ func TestEnableEmulationVersion(t *testing.T) { | ||||
| } | ||||
|  | ||||
| func TestDisableEmulationVersion(t *testing.T) { | ||||
| 	featuregatetesting.SetFeatureGateEmulationVersionDuringTest(t, utilfeature.DefaultFeatureGate, version.MustParse("1.32")) | ||||
| 	server := kubeapiservertesting.StartTestServerOrDie(t, | ||||
| 		&kubeapiservertesting.TestServerInstanceOptions{BinaryVersion: "1.32"}, | ||||
| 		[]string{}, framework.SharedEtcd()) | ||||
|   | ||||
| @@ -20,7 +20,6 @@ import ( | ||||
| 	"context" | ||||
| 	"encoding/json" | ||||
| 	"fmt" | ||||
| 	"k8s.io/apimachinery/pkg/util/version" | ||||
| 	"net/http" | ||||
| 	"net/http/httptest" | ||||
| 	"os" | ||||
| @@ -2235,9 +2234,9 @@ func Test_CostLimitForValidation(t *testing.T) { | ||||
| func Test_CostLimitForValidationWithFeatureDisabled(t *testing.T) { | ||||
| 	resetPolicyRefreshInterval := generic.SetPolicyRefreshIntervalForTests(policyRefreshInterval) | ||||
| 	defer resetPolicyRefreshInterval() | ||||
| 	featuregatetesting.SetFeatureGateEmulationVersionDuringTest(t, utilfeature.DefaultFeatureGate, version.MustParse("1.31")) | ||||
| 	featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, genericfeatures.StrictCostEnforcementForVAP, false) | ||||
| 	server, err := apiservertesting.StartTestServer(t, &apiservertesting.TestServerInstanceOptions{EmulationVersion: "1.31"}, []string{ | ||||
| 	server, err := apiservertesting.StartTestServer(t, nil, []string{ | ||||
| 		"--emulated-version", "1.31", | ||||
| 		"--feature-gates", "StrictCostEnforcementForVAP=false", | ||||
| 		"--enable-admission-plugins", "ValidatingAdmissionPolicy", | ||||
| 	}, framework.SharedEtcd()) | ||||
| 	if err != nil { | ||||
|   | ||||
| @@ -47,6 +47,7 @@ import ( | ||||
| 	"k8s.io/apimachinery/pkg/util/wait" | ||||
| 	"k8s.io/apimachinery/pkg/watch" | ||||
| 	"k8s.io/apiserver/pkg/features" | ||||
| 	"k8s.io/apiserver/pkg/util/compatibility" | ||||
| 	utilfeature "k8s.io/apiserver/pkg/util/feature" | ||||
| 	appsv1ac "k8s.io/client-go/applyconfigurations/apps/v1" | ||||
| 	autoscalingv1ac "k8s.io/client-go/applyconfigurations/autoscaling/v1" | ||||
| @@ -83,7 +84,7 @@ func TestClient(t *testing.T) { | ||||
| 		t.Fatalf("unexpected error: %v", err) | ||||
| 	} | ||||
| 	expectedInfo := utilversion.Get() | ||||
| 	kubeVersion := utilversion.DefaultKubeEffectiveVersion().BinaryVersion() | ||||
| 	kubeVersion := compatibility.DefaultKubeEffectiveVersionForTest().BinaryVersion() | ||||
| 	expectedInfo.Major = fmt.Sprintf("%d", kubeVersion.Major()) | ||||
| 	expectedInfo.Minor = fmt.Sprintf("%d", kubeVersion.Minor()) | ||||
|  | ||||
|   | ||||
| @@ -25,7 +25,6 @@ import ( | ||||
| 	"path/filepath" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
| 	"sync" | ||||
| 	"testing" | ||||
| 	"time" | ||||
|  | ||||
| @@ -117,7 +116,7 @@ func newTransformTest(tb testing.TB, transformerConfigYAML string, reload bool, | ||||
| 		return nil, fmt.Errorf("failed to read config file: %w", err) | ||||
| 	} | ||||
|  | ||||
| 	if e.kubeAPIServer, err = startTestServerLocked( | ||||
| 	if e.kubeAPIServer, err = kubeapiservertesting.StartTestServer( | ||||
| 		tb, nil, | ||||
| 		e.getEncryptionOptions(reload), e.storageConfig); err != nil { | ||||
| 		e.cleanUp() | ||||
| @@ -148,15 +147,6 @@ func newTransformTest(tb testing.TB, transformerConfigYAML string, reload bool, | ||||
| 	return &e, nil | ||||
| } | ||||
|  | ||||
| var startTestServerLock sync.Mutex | ||||
|  | ||||
| // startTestServerLocked prevents parallel calls to kubeapiservertesting.StartTestServer because it messes with global state. | ||||
| func startTestServerLocked(t ktesting.TB, instanceOptions *kubeapiservertesting.TestServerInstanceOptions, customFlags []string, storageConfig *storagebackend.Config) (result kubeapiservertesting.TestServer, err error) { | ||||
| 	startTestServerLock.Lock() | ||||
| 	defer startTestServerLock.Unlock() | ||||
| 	return kubeapiservertesting.StartTestServer(t, instanceOptions, customFlags, storageConfig) | ||||
| } | ||||
|  | ||||
| func (e *transformTest) cleanUp() { | ||||
| 	if e.configDir != "" { | ||||
| 		os.RemoveAll(e.configDir) | ||||
|   | ||||
| @@ -25,6 +25,7 @@ import ( | ||||
| 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||||
| 	"k8s.io/apimachinery/pkg/runtime/schema" | ||||
| 	"k8s.io/apimachinery/pkg/util/version" | ||||
| 	"k8s.io/apiserver/pkg/util/compatibility" | ||||
| 	utilversion "k8s.io/component-base/version" | ||||
|  | ||||
| 	"k8s.io/kubernetes/test/utils/image" | ||||
| @@ -34,9 +35,9 @@ import ( | ||||
| // Tests aiming for full coverage of versions should test fixtures of all supported versions. | ||||
| func GetSupportedEmulatedVersions() []string { | ||||
| 	return []string{ | ||||
| 		utilversion.DefaultKubeEffectiveVersion().BinaryVersion().SubtractMinor(2).String(), | ||||
| 		utilversion.DefaultKubeEffectiveVersion().BinaryVersion().SubtractMinor(1).String(), | ||||
| 		utilversion.DefaultKubeEffectiveVersion().BinaryVersion().String(), | ||||
| 		compatibility.DefaultKubeEffectiveVersionForTest().BinaryVersion().SubtractMinor(2).String(), | ||||
| 		compatibility.DefaultKubeEffectiveVersionForTest().BinaryVersion().SubtractMinor(1).String(), | ||||
| 		compatibility.DefaultKubeEffectiveVersionForTest().BinaryVersion().String(), | ||||
| 	} | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -104,7 +104,6 @@ func testEtcdStoragePathWithVersion(t *testing.T, v string) { | ||||
| 		// only understand v1beta1. | ||||
| 		featuregatetesting.SetFeatureGateDuringTest(t, feature.DefaultFeatureGate, features.MultiCIDRServiceAllocator, false) | ||||
| 	} | ||||
| 	registerEffectiveEmulationVersion(t) | ||||
|  | ||||
| 	apiServer := StartRealAPIServerOrDie(t, func(opts *options.ServerRunOptions) { | ||||
| 		// Disable alphas when emulating previous versions. | ||||
|   | ||||
| @@ -36,9 +36,9 @@ import ( | ||||
| 	"k8s.io/apimachinery/pkg/runtime/schema" | ||||
| 	utilerrors "k8s.io/apimachinery/pkg/util/errors" | ||||
| 	"k8s.io/apimachinery/pkg/util/json" | ||||
| 	utilruntime "k8s.io/apimachinery/pkg/util/runtime" | ||||
| 	"k8s.io/apimachinery/pkg/util/wait" | ||||
| 	genericapiserveroptions "k8s.io/apiserver/pkg/server/options" | ||||
| 	"k8s.io/apiserver/pkg/util/compatibility" | ||||
| 	"k8s.io/apiserver/pkg/util/feature" | ||||
| 	cacheddiscovery "k8s.io/client-go/discovery/cached/memory" | ||||
| 	"k8s.io/client-go/dynamic" | ||||
| @@ -46,9 +46,8 @@ import ( | ||||
| 	restclient "k8s.io/client-go/rest" | ||||
| 	"k8s.io/client-go/restmapper" | ||||
| 	utiltesting "k8s.io/client-go/util/testing" | ||||
| 	"k8s.io/component-base/featuregate" | ||||
| 	basecompatibility "k8s.io/component-base/compatibility" | ||||
| 	featuregatetesting "k8s.io/component-base/featuregate/testing" | ||||
| 	utilversion "k8s.io/component-base/version" | ||||
| 	"k8s.io/kubernetes/cmd/kube-apiserver/app" | ||||
| 	"k8s.io/kubernetes/cmd/kube-apiserver/app/options" | ||||
| 	"k8s.io/kubernetes/test/integration" | ||||
| @@ -67,17 +66,6 @@ AwEHoUQDQgAEH6cuzP8XuD5wal6wf9M6xDljTOPLX2i8uIp/C/ASqiIGUeeKQtX0 | ||||
| /IR3qCXyThP/dbCiHrF3v1cuhBOHY8CLVg== | ||||
| -----END EC PRIVATE KEY-----` | ||||
|  | ||||
| func registerEffectiveEmulationVersion(t *testing.T) { | ||||
| 	featureGate := feature.DefaultMutableFeatureGate | ||||
| 	featureGate.AddMetrics() | ||||
|  | ||||
| 	effectiveVersion := utilversion.DefaultKubeEffectiveVersion() | ||||
| 	effectiveVersion.SetEmulationVersion(featureGate.EmulationVersion()) | ||||
| 	featuregatetesting.SetFeatureGateEmulationVersionDuringTest(t, featureGate, effectiveVersion.EmulationVersion()) | ||||
| 	featuregate.DefaultComponentGlobalsRegistry.Reset() | ||||
| 	utilruntime.Must(featuregate.DefaultComponentGlobalsRegistry.Register(featuregate.DefaultKubeComponent, effectiveVersion, featureGate)) | ||||
| } | ||||
|  | ||||
| // StartRealAPIServerOrDie starts an API server that is appropriate for use in tests that require one of every resource | ||||
| func StartRealAPIServerOrDie(t *testing.T, configFuncs ...func(*options.ServerRunOptions)) *APIServer { | ||||
| 	tCtx := ktesting.Init(t) | ||||
| @@ -108,6 +96,17 @@ func StartRealAPIServerOrDie(t *testing.T, configFuncs ...func(*options.ServerRu | ||||
| 	} | ||||
|  | ||||
| 	opts := options.NewServerRunOptions() | ||||
| 	// If EmulationVersion of DefaultFeatureGate is set during test, we need to propagate it to the apiserver ComponentGlobalsRegistry. | ||||
| 	featureGate := feature.DefaultMutableFeatureGate.DeepCopy() | ||||
| 	effectiveVersion := compatibility.DefaultKubeEffectiveVersionForTest() | ||||
| 	effectiveVersion.SetEmulationVersion(featureGate.EmulationVersion()) | ||||
| 	// set up new instance of ComponentGlobalsRegistry instead of using the DefaultComponentGlobalsRegistry to avoid contention in parallel tests. | ||||
| 	componentGlobalsRegistry := basecompatibility.NewComponentGlobalsRegistry() | ||||
| 	if err := componentGlobalsRegistry.Register(basecompatibility.DefaultKubeComponent, effectiveVersion, featureGate); err != nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
| 	opts.GenericServerRunOptions.ComponentGlobalsRegistry = componentGlobalsRegistry | ||||
|  | ||||
| 	opts.Options.SecureServing.Listener = listener | ||||
| 	opts.Options.SecureServing.ServerCert.CertDirectory = certDir | ||||
| 	opts.Options.ServiceAccountSigningKeyFile = saSigningKeyFile.Name() | ||||
| @@ -123,6 +122,19 @@ func StartRealAPIServerOrDie(t *testing.T, configFuncs ...func(*options.ServerRu | ||||
| 	for _, f := range configFuncs { | ||||
| 		f(opts) | ||||
| 	} | ||||
|  | ||||
| 	// If the local ComponentGlobalsRegistry is changed by configFuncs, | ||||
| 	// we need to copy the new feature values back to the DefaultFeatureGate because most feature checks still use the DefaultFeatureGate. | ||||
| 	if !featureGate.EmulationVersion().EqualTo(feature.DefaultMutableFeatureGate.EmulationVersion()) { | ||||
| 		featuregatetesting.SetFeatureGateEmulationVersionDuringTest(t, feature.DefaultMutableFeatureGate, effectiveVersion.EmulationVersion()) | ||||
| 	} | ||||
| 	for f := range feature.DefaultMutableFeatureGate.GetAll() { | ||||
| 		if featureGate.Enabled(f) != feature.DefaultFeatureGate.Enabled(f) { | ||||
| 			featuregatetesting.SetFeatureGateDuringTest(t, feature.DefaultFeatureGate, f, featureGate.Enabled(f)) | ||||
| 		} | ||||
| 	} | ||||
| 	feature.DefaultMutableFeatureGate.AddMetrics() | ||||
|  | ||||
| 	completedOptions, err := opts.Complete(tCtx) | ||||
| 	if err != nil { | ||||
| 		t.Fatal(err) | ||||
|   | ||||
| @@ -55,9 +55,9 @@ import ( | ||||
| 	clientcmdapi "k8s.io/client-go/tools/clientcmd/api" | ||||
| 	"k8s.io/client-go/transport" | ||||
| 	"k8s.io/client-go/util/cert" | ||||
| 	basecompatibility "k8s.io/component-base/compatibility" | ||||
| 	"k8s.io/component-base/featuregate" | ||||
| 	featuregatetesting "k8s.io/component-base/featuregate/testing" | ||||
| 	utilversion "k8s.io/component-base/version" | ||||
| 	apiregistrationv1 "k8s.io/kube-aggregator/pkg/apis/apiregistration/v1" | ||||
| 	aggregatorclient "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset" | ||||
| 	"k8s.io/kubernetes/cmd/kube-apiserver/app" | ||||
| @@ -282,11 +282,8 @@ func testFrontProxyConfig(t *testing.T, withUID bool) { | ||||
| 		extraKASFlags = []string{"--requestheader-uid-headers=x-remote-uid"} | ||||
| 	} | ||||
|  | ||||
| 	// each wardle binary is bundled with a specific kube binary. | ||||
| 	kubeBinaryVersion := sampleserver.WardleVersionToKubeVersion(version.MustParse(wardleBinaryVersion)).String() | ||||
|  | ||||
| 	// start up the KAS and prepare the options for the wardle API server | ||||
| 	testKAS, wardleOptions, wardlePort := prepareAggregatedWardleAPIServer(ctx, t, testNamespace, kubeBinaryVersion, wardleBinaryVersion, extraKASFlags, withUID) | ||||
| 	testKAS, wardleOptions, wardlePort := prepareAggregatedWardleAPIServer(ctx, t, testNamespace, wardleBinaryVersion, extraKASFlags, withUID) | ||||
| 	kubeConfig := getKubeConfig(testKAS) | ||||
|  | ||||
| 	// create the SA that we will use to query the aggregated API | ||||
| @@ -402,10 +399,7 @@ func testAggregatedAPIServer(t *testing.T, setWardleFeatureGate, banFlunder bool | ||||
| 	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute) | ||||
| 	t.Cleanup(cancel) | ||||
|  | ||||
| 	// each wardle binary is bundled with a specific kube binary. | ||||
| 	kubeBinaryVersion := sampleserver.WardleVersionToKubeVersion(version.MustParse(wardleBinaryVersion)).String() | ||||
|  | ||||
| 	testKAS, wardleOptions, wardlePort := prepareAggregatedWardleAPIServer(ctx, t, testNamespace, kubeBinaryVersion, wardleBinaryVersion, nil, false) | ||||
| 	testKAS, wardleOptions, wardlePort := prepareAggregatedWardleAPIServer(ctx, t, testNamespace, wardleBinaryVersion, nil, false) | ||||
| 	kubeClientConfig := getKubeConfig(testKAS) | ||||
|  | ||||
| 	wardleCertDir, _ := os.MkdirTemp("", "test-integration-wardle-server") | ||||
| @@ -685,7 +679,7 @@ func TestAggregatedAPIServerRejectRedirectResponse(t *testing.T) { | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func prepareAggregatedWardleAPIServer(ctx context.Context, t *testing.T, namespace, kubebinaryVersion, wardleBinaryVersion string, kubeAPIServerFlags []string, withUID bool) (*kastesting.TestServer, *sampleserver.WardleServerOptions, int) { | ||||
| func prepareAggregatedWardleAPIServer(ctx context.Context, t *testing.T, namespace, wardleBinaryVersion string, kubeAPIServerFlags []string, withUID bool) (*kastesting.TestServer, *sampleserver.WardleServerOptions, int) { | ||||
| 	// makes the kube-apiserver very responsive.  it's normally a minute | ||||
| 	dynamiccertificates.FileRefreshDuration = 1 * time.Second | ||||
|  | ||||
| @@ -697,22 +691,17 @@ func prepareAggregatedWardleAPIServer(ctx context.Context, t *testing.T, namespa | ||||
| 	// endpoints cannot have loopback IPs so we need to override the resolver itself | ||||
| 	t.Cleanup(app.SetServiceResolverForTests(staticURLServiceResolver(fmt.Sprintf("https://127.0.0.1:%d", wardlePort)))) | ||||
|  | ||||
| 	// TODO figure out how to actually make BinaryVersion/EmulationVersion work with Wardle and KAS at the same time when Alpha FG are being set | ||||
| 	if withUID { | ||||
| 		kubebinaryVersion = "" | ||||
| 	} | ||||
|  | ||||
| 	testServer := kastesting.StartTestServerOrDie(t, | ||||
| 		&kastesting.TestServerInstanceOptions{ | ||||
| 			EnableCertAuth: true, | ||||
| 			BinaryVersion:  kubebinaryVersion, | ||||
| 		}, | ||||
| 		kubeAPIServerFlags, | ||||
| 		framework.SharedEtcd()) | ||||
| 	t.Cleanup(func() { testServer.TearDownFn() }) | ||||
|  | ||||
| 	_, _ = featuregate.DefaultComponentGlobalsRegistry.ComponentGlobalsOrRegister( | ||||
| 		apiserver.WardleComponentName, utilversion.NewEffectiveVersion(wardleBinaryVersion), | ||||
| 	componentGlobalsRegistry := testServer.ServerOpts.Options.GenericServerRunOptions.ComponentGlobalsRegistry | ||||
| 	_, _ = componentGlobalsRegistry.ComponentGlobalsOrRegister( | ||||
| 		apiserver.WardleComponentName, basecompatibility.NewEffectiveVersionFromString(wardleBinaryVersion, "", ""), | ||||
| 		featuregate.NewVersionedFeatureGate(version.MustParse(wardleBinaryVersion))) | ||||
|  | ||||
| 	kubeClient := client.NewForConfigOrDie(getKubeConfig(testServer)) | ||||
| @@ -740,6 +729,7 @@ func prepareAggregatedWardleAPIServer(ctx context.Context, t *testing.T, namespa | ||||
| 	} | ||||
|  | ||||
| 	wardleOptions := sampleserver.NewWardleServerOptions(os.Stdout, os.Stderr) | ||||
| 	wardleOptions.ComponentGlobalsRegistry = componentGlobalsRegistry | ||||
| 	// ensure this is a SAN on the generated cert for service FQDN | ||||
| 	wardleOptions.AlternateDNS = []string{ | ||||
| 		fmt.Sprintf("api.%s.svc", namespace), | ||||
|   | ||||
| @@ -35,9 +35,13 @@ import ( | ||||
| 	"k8s.io/apimachinery/pkg/util/wait" | ||||
| 	genericapiserver "k8s.io/apiserver/pkg/server" | ||||
| 	genericapiserveroptions "k8s.io/apiserver/pkg/server/options" | ||||
| 	"k8s.io/apiserver/pkg/util/compatibility" | ||||
| 	utilfeature "k8s.io/apiserver/pkg/util/feature" | ||||
| 	client "k8s.io/client-go/kubernetes" | ||||
| 	"k8s.io/client-go/rest" | ||||
| 	"k8s.io/client-go/util/cert" | ||||
| 	basecompatibility "k8s.io/component-base/compatibility" | ||||
| 	featuregatetesting "k8s.io/component-base/featuregate/testing" | ||||
| 	aggregatorscheme "k8s.io/kube-aggregator/pkg/apiserver/scheme" | ||||
| 	netutils "k8s.io/utils/net" | ||||
|  | ||||
| @@ -136,6 +140,17 @@ func StartTestServer(ctx context.Context, t testing.TB, setup TestServerSetup) ( | ||||
| 	} | ||||
|  | ||||
| 	opts := options.NewServerRunOptions() | ||||
| 	// If EmulationVersion of DefaultFeatureGate is set during test, we need to propagate it to the apiserver ComponentGlobalsRegistry. | ||||
| 	featureGate := utilfeature.DefaultMutableFeatureGate.DeepCopy() | ||||
| 	effectiveVersion := compatibility.DefaultKubeEffectiveVersionForTest() | ||||
| 	effectiveVersion.SetEmulationVersion(featureGate.EmulationVersion()) | ||||
| 	// set up new instance of ComponentGlobalsRegistry instead of using the DefaultComponentGlobalsRegistry to avoid contention in parallel tests. | ||||
| 	componentGlobalsRegistry := basecompatibility.NewComponentGlobalsRegistry() | ||||
| 	if err := componentGlobalsRegistry.Register(basecompatibility.DefaultKubeComponent, effectiveVersion, featureGate); err != nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
| 	opts.GenericServerRunOptions.ComponentGlobalsRegistry = componentGlobalsRegistry | ||||
|  | ||||
| 	opts.SecureServing.Listener = listener | ||||
| 	opts.SecureServing.BindAddress = netutils.ParseIPSloppy("127.0.0.1") | ||||
| 	opts.SecureServing.ServerCert.CertDirectory = certDir | ||||
| @@ -158,6 +173,18 @@ func StartTestServer(ctx context.Context, t testing.TB, setup TestServerSetup) ( | ||||
| 		setup.ModifyServerRunOptions(opts) | ||||
| 	} | ||||
|  | ||||
| 	// If the local ComponentGlobalsRegistry is changed by ModifyServerRunOptions, | ||||
| 	// we need to copy the new feature values back to the DefaultFeatureGate because most feature checks still use the DefaultFeatureGate. | ||||
| 	if !featureGate.EmulationVersion().EqualTo(utilfeature.DefaultMutableFeatureGate.EmulationVersion()) { | ||||
| 		featuregatetesting.SetFeatureGateEmulationVersionDuringTest(t, utilfeature.DefaultMutableFeatureGate, effectiveVersion.EmulationVersion()) | ||||
| 	} | ||||
| 	for f := range utilfeature.DefaultMutableFeatureGate.GetAll() { | ||||
| 		if featureGate.Enabled(f) != utilfeature.DefaultFeatureGate.Enabled(f) { | ||||
| 			featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, f, featureGate.Enabled(f)) | ||||
| 		} | ||||
| 	} | ||||
| 	utilfeature.DefaultMutableFeatureGate.AddMetrics() | ||||
|  | ||||
| 	completedOptions, err := opts.Complete(ctx) | ||||
| 	if err != nil { | ||||
| 		t.Fatal(err) | ||||
|   | ||||
| @@ -1230,9 +1230,8 @@ func TestMutablePodSchedulingDirectives(t *testing.T) { | ||||
| // Test disabling of RelaxedDNSSearchValidation after a Pod has been created | ||||
| func TestRelaxedDNSSearchValidation(t *testing.T) { | ||||
| 	// Disable ServiceAccount admission plugin as we don't have serviceaccount controller running. | ||||
| 	server := kubeapiservertesting.StartTestServerOrDie(t, | ||||
| 		&kubeapiservertesting.TestServerInstanceOptions{BinaryVersion: "1.32"}, | ||||
| 		framework.DefaultTestServerFlags(), framework.SharedEtcd()) | ||||
| 	server := kubeapiservertesting.StartTestServerOrDie(t, nil, | ||||
| 		append(framework.DefaultTestServerFlags(), "--emulated-version=1.32"), framework.SharedEtcd()) | ||||
| 	defer server.TearDownFn() | ||||
|  | ||||
| 	client := clientset.NewForConfigOrDie(server.ClientConfig) | ||||
|   | ||||
| @@ -19,7 +19,7 @@ package service | ||||
| import ( | ||||
| 	"context" | ||||
| 	"encoding/json" | ||||
| 	"k8s.io/apimachinery/pkg/util/version" | ||||
| 	"fmt" | ||||
| 	"testing" | ||||
| 	"time" | ||||
|  | ||||
| @@ -34,10 +34,8 @@ import ( | ||||
| 	clientset "k8s.io/client-go/kubernetes" | ||||
| 	servicecontroller "k8s.io/cloud-provider/controllers/service" | ||||
| 	fakecloud "k8s.io/cloud-provider/fake" | ||||
| 	featuregatetesting "k8s.io/component-base/featuregate/testing" | ||||
| 	controllersmetrics "k8s.io/component-base/metrics/prometheus/controllers" | ||||
| 	kubeapiservertesting "k8s.io/kubernetes/cmd/kube-apiserver/app/testing" | ||||
| 	"k8s.io/kubernetes/pkg/features" | ||||
| 	"k8s.io/kubernetes/test/integration/framework" | ||||
| 	"k8s.io/utils/net" | ||||
| 	utilpointer "k8s.io/utils/pointer" | ||||
| @@ -708,13 +706,12 @@ func Test_ServiceLoadBalancerIPMode(t *testing.T) { | ||||
|  | ||||
| 	for _, tc := range testCases { | ||||
| 		t.Run("", func(t *testing.T) { | ||||
| 			var testServerOptions *kubeapiservertesting.TestServerInstanceOptions | ||||
| 			serverFlags := framework.DefaultTestServerFlags() | ||||
| 			if !tc.ipModeEnabled { | ||||
| 				testServerOptions = &kubeapiservertesting.TestServerInstanceOptions{EmulationVersion: "1.31"} | ||||
| 				featuregatetesting.SetFeatureGateEmulationVersionDuringTest(t, utilfeature.DefaultFeatureGate, version.MustParse("1.31")) | ||||
| 				serverFlags = append(serverFlags, "--emulated-version=1.31") | ||||
| 			} | ||||
| 			featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.LoadBalancerIPMode, tc.ipModeEnabled) | ||||
| 			server := kubeapiservertesting.StartTestServerOrDie(t, testServerOptions, framework.DefaultTestServerFlags(), framework.SharedEtcd()) | ||||
| 			serverFlags = append(serverFlags, fmt.Sprintf("--feature-gates=LoadBalancerIPMode=%v", tc.ipModeEnabled)) | ||||
| 			server := kubeapiservertesting.StartTestServerOrDie(t, nil, serverFlags, framework.SharedEtcd()) | ||||
| 			defer server.TearDownFn() | ||||
|  | ||||
| 			client, err := clientset.NewForConfig(server.ClientConfig) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Siyuan Zhang
					Siyuan Zhang