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>

Kubernetes-commit: 8fc3a33454ba38783bb63de41ecf5343e2ced67c
This commit is contained in:
Siyuan Zhang 2024-12-20 07:03:03 +00:00 committed by Kubernetes Publisher
parent 9bb5fd51d1
commit 9bb4aa730a
15 changed files with 252 additions and 68 deletions

View File

@ -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": {

View File

@ -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()
}

View File

@ -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.

View File

@ -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 {

View File

@ -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

View File

@ -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())

View File

@ -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,

View File

@ -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,
},
},
}

View File

@ -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{

View File

@ -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.

View File

@ -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,
},
}

View File

@ -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{},
"",

View File

@ -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))
}

View File

@ -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)
}

View File

@ -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)
}
})
}
}