Wire test-only feature gate for CBOR serving.

To mitigate the risk of introducing a new protocol, integration tests for CBOR will be written using
a test-only feature gate instance that is not wired to runtime options. On alpha graduation, the
test-only feature gate instance will be replaced by a normal feature gate in the existing apiserver
feature gate instance.

Kubernetes-commit: 0cad1a89b6721308746cc1a12f12de31a259a0d3
This commit is contained in:
Ben Luddy 2024-10-23 16:36:25 -04:00 committed by Kubernetes Publisher
parent 781f771b86
commit f27bb5491e
4 changed files with 58 additions and 1 deletions

View File

@ -87,6 +87,15 @@ const (
// Allows authorization to use field and label selectors.
AuthorizeWithSelectors featuregate.Feature = "AuthorizeWithSelectors"
// owner: @benluddy
// kep: https://kep.k8s.io/4222
//
// Enables CBOR as a supported encoding for requests and responses, and as the
// preferred storage encoding for custom resources.
//
// This feature is currently PRE-ALPHA and MUST NOT be enabled outside of integration tests.
TestOnlyCBORServingAndStorage featuregate.Feature = "TestOnlyCBORServingAndStorage"
// owner: @serathius
// Enables concurrent watch object decoding to avoid starving watch cache when conversion webhook is installed.
ConcurrentWatchObjectDecode featuregate.Feature = "ConcurrentWatchObjectDecode"
@ -238,6 +247,7 @@ const (
func init() {
runtime.Must(utilfeature.DefaultMutableFeatureGate.Add(defaultKubernetesFeatureGates))
runtime.Must(utilfeature.DefaultMutableFeatureGate.AddVersioned(defaultVersionedKubernetesFeatureGates))
runtime.Must(utilfeature.TestOnlyMutableFeatureGate.AddVersioned(testOnlyVersionedKubernetesFeatureGates))
}
// defaultVersionedKubernetesFeatureGates consists of all known Kubernetes-specific feature keys with VersionedSpecs.
@ -410,3 +420,12 @@ var defaultVersionedKubernetesFeatureGates = map[featuregate.Feature]featuregate
// defaultKubernetesFeatureGates consists of legacy unversioned Kubernetes-specific feature keys.
// Please do not add to this struct and use defaultVersionedKubernetesFeatureGates instead.
var defaultKubernetesFeatureGates = map[featuregate.Feature]featuregate.FeatureSpec{}
// testOnlyVersionedKubernetesFeatureGates consists of features that require programmatic enablement
// for integration testing, but have not yet graduated to alpha in a release and must not be enabled
// by a runtime option.
var testOnlyVersionedKubernetesFeatureGates = map[featuregate.Feature]featuregate.VersionedSpecs{
TestOnlyCBORServingAndStorage: {
{Version: version.MustParse("1.32"), Default: false, PreRelease: featuregate.Alpha},
},
}

View File

@ -742,7 +742,7 @@ func (c *RecommendedConfig) Complete() CompletedConfig {
return c.Config.Complete(c.SharedInformerFactory)
}
var allowedMediaTypes = []string{
var defaultAllowedMediaTypes = []string{
runtime.ContentTypeJSON,
runtime.ContentTypeYAML,
runtime.ContentTypeProtobuf,
@ -755,6 +755,10 @@ func (c completedConfig) New(name string, delegationTarget DelegationTarget) (*G
if c.Serializer == nil {
return nil, fmt.Errorf("Genericapiserver.New() called with config.Serializer == nil")
}
allowedMediaTypes := defaultAllowedMediaTypes
if utilfeature.TestOnlyFeatureGate.Enabled(genericfeatures.TestOnlyCBORServingAndStorage) {
allowedMediaTypes = append(allowedMediaTypes, runtime.ContentTypeCBOR)
}
for _, info := range c.Serializer.SupportedMediaTypes() {
var ok bool
for _, mt := range allowedMediaTypes {

View File

@ -29,6 +29,7 @@ import (
"time"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/serializer"
"k8s.io/apimachinery/pkg/util/json"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/apimachinery/pkg/util/wait"
@ -40,12 +41,14 @@ import (
"k8s.io/apiserver/pkg/authentication/user"
"k8s.io/apiserver/pkg/authorization/authorizer"
"k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/apiserver/pkg/features"
"k8s.io/apiserver/pkg/server/healthz"
utilfeature "k8s.io/apiserver/pkg/util/feature"
utilversion "k8s.io/apiserver/pkg/util/version"
"k8s.io/client-go/informers"
"k8s.io/client-go/kubernetes/fake"
"k8s.io/client-go/rest"
featuregatetesting "k8s.io/component-base/featuregate/testing"
"k8s.io/component-base/tracing"
"k8s.io/klog/v2/ktesting"
netutils "k8s.io/utils/net"
@ -419,3 +422,22 @@ func TestNewErrorForbiddenSerializer(t *testing.T) {
t.Errorf("unexpected error: %v", err)
}
}
func TestNewFeatureGatedSerializer(t *testing.T) {
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.TestOnlyFeatureGate, features.TestOnlyCBORServingAndStorage, true)
config := NewConfig(serializer.NewCodecFactory(scheme, serializer.WithSerializer(func(creater runtime.ObjectCreater, typer runtime.ObjectTyper) runtime.SerializerInfo {
return runtime.SerializerInfo{
MediaType: "application/cbor",
MediaTypeType: "application",
MediaTypeSubType: "cbor",
}
})))
config.ExternalAddress = "192.168.10.4:443"
config.EffectiveVersion = utilversion.NewEffectiveVersion("")
config.LoopbackClientConfig = &rest.Config{}
if _, err := config.Complete(nil).New("test", NewEmptyDelegate()); err != nil {
t.Errorf("unexpected error: %v", err)
}
}

View File

@ -31,3 +31,15 @@ var (
// Top-level commands/options setup that needs to modify this feature gate should use DefaultMutableFeatureGate.
DefaultFeatureGate featuregate.FeatureGate = DefaultMutableFeatureGate
)
var (
// TestOnlyMutableFeatureGate is a mutable version of TestOnlyFeatureGate. Only top-level
// commands/options setup and the k8s.io/component-base/featuregate/testing package should
// make use of this.
TestOnlyMutableFeatureGate featuregate.MutableVersionedFeatureGate = featuregate.NewFeatureGate()
// TestOnlyFeatureGate is a shared global FeatureGate for features that have not yet
// graduated to alpha and require programmatic feature enablement for pre-alpha integration
// testing without exposing the feature as a runtime option.
TestOnlyFeatureGate featuregate.FeatureGate = TestOnlyMutableFeatureGate
)