mirror of
https://github.com/kubernetes/kubernetes.git
synced 2026-06-11 01:41:54 -04:00
Merge pull request #138907 from Jefftree/remove-locked-apimachinery-feature-gates
Remove locked GA feature gates (sig-api-machinery)
This commit is contained in:
commit
692d9f21dd
27 changed files with 214 additions and 1129 deletions
|
|
@ -32,11 +32,9 @@ import (
|
|||
utilerrors "k8s.io/apimachinery/pkg/util/errors"
|
||||
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
"k8s.io/client-go/discovery"
|
||||
v1clientset "k8s.io/client-go/kubernetes/typed/core/v1"
|
||||
"k8s.io/client-go/metadata"
|
||||
"k8s.io/kubernetes/pkg/features"
|
||||
)
|
||||
|
||||
// NamespacedResourcesDeleterInterface is the interface to delete a namespace with all resources in it.
|
||||
|
|
@ -530,7 +528,7 @@ func (d *namespacedResourcesDeleter) deleteAllContent(ctx context.Context, ns *v
|
|||
}
|
||||
podsGVR := schema.GroupVersionResource{Group: "", Version: "v1", Resource: "pods"}
|
||||
|
||||
if _, hasPods := groupVersionResources[podsGVR]; hasPods && utilfeature.DefaultFeatureGate.Enabled(features.OrderedNamespaceDeletion) {
|
||||
if _, hasPods := groupVersionResources[podsGVR]; hasPods {
|
||||
// Ensure all pods in the namespace are deleted first
|
||||
gvrDeletionMetadata, err := d.deleteAllContentForGroupVersionResource(ctx, podsGVR, namespace, namespaceDeletedAt)
|
||||
if err != nil {
|
||||
|
|
@ -564,7 +562,7 @@ func (d *namespacedResourcesDeleter) deleteAllContent(ctx context.Context, ns *v
|
|||
|
||||
// Proceed with deleting other resources in the namespace
|
||||
for gvr := range groupVersionResources {
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.OrderedNamespaceDeletion) && gvr.Group == podsGVR.Group &&
|
||||
if gvr.Group == podsGVR.Group &&
|
||||
gvr.Version == podsGVR.Version && gvr.Resource == podsGVR.Resource {
|
||||
continue
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,8 +23,6 @@ import (
|
|||
"net/http"
|
||||
"time"
|
||||
|
||||
noopoteltrace "go.opentelemetry.io/otel/trace/noop"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
utilnet "k8s.io/apimachinery/pkg/util/net"
|
||||
|
|
@ -168,10 +166,8 @@ func BuildGenericConfig(
|
|||
if lastErr = s.EgressSelector.ApplyTo(genericConfig); lastErr != nil {
|
||||
return
|
||||
}
|
||||
if utilfeature.DefaultFeatureGate.Enabled(genericfeatures.APIServerTracing) {
|
||||
if lastErr = s.Traces.ApplyTo(genericConfig.EgressSelector, genericConfig); lastErr != nil {
|
||||
return
|
||||
}
|
||||
if lastErr = s.Traces.ApplyTo(genericConfig.EgressSelector, genericConfig); lastErr != nil {
|
||||
return
|
||||
}
|
||||
// wrap the definitions to revert any changes from disabled features
|
||||
getOpenAPIDefinitions = openapi.GetOpenAPIDefinitionsWithoutDisabledFeatures(getOpenAPIDefinitions)
|
||||
|
|
@ -189,11 +185,7 @@ func BuildGenericConfig(
|
|||
if genericConfig.EgressSelector != nil {
|
||||
s.Etcd.StorageConfig.Transport.EgressLookup = genericConfig.EgressSelector.Lookup
|
||||
}
|
||||
if utilfeature.DefaultFeatureGate.Enabled(genericfeatures.APIServerTracing) {
|
||||
s.Etcd.StorageConfig.Transport.TracerProvider = genericConfig.TracerProvider
|
||||
} else {
|
||||
s.Etcd.StorageConfig.Transport.TracerProvider = noopoteltrace.NewTracerProvider()
|
||||
}
|
||||
s.Etcd.StorageConfig.Transport.TracerProvider = genericConfig.TracerProvider
|
||||
|
||||
storageFactoryConfig := kubeapiserver.NewStorageFactoryConfigEffectiveVersion(genericConfig.EffectiveVersion)
|
||||
storageFactoryConfig.APIResourceConfig = genericConfig.MergedResourceConfig
|
||||
|
|
|
|||
|
|
@ -692,12 +692,6 @@ const (
|
|||
// Enables opportunistic batching in the scheduler.
|
||||
OpportunisticBatching featuregate.Feature = "OpportunisticBatching"
|
||||
|
||||
// owner: @cici37
|
||||
// kep: https://kep.k8s.io/5080
|
||||
//
|
||||
// Enables ordered namespace deletion.
|
||||
OrderedNamespaceDeletion featuregate.Feature = "OrderedNamespaceDeletion"
|
||||
|
||||
// owner: @tallclair
|
||||
//
|
||||
// Enables relisting individual pods on-demand.
|
||||
|
|
@ -1043,14 +1037,6 @@ const (
|
|||
// Enables support for the StorageVersionMigrator controller.
|
||||
StorageVersionMigrator featuregate.Feature = "StorageVersionMigrator"
|
||||
|
||||
// owner: @serathius
|
||||
// Allow API server JSON encoder to encode collections item by item, instead of all at once.
|
||||
StreamingCollectionEncodingToJSON featuregate.Feature = "StreamingCollectionEncodingToJSON"
|
||||
|
||||
// owner: serathius
|
||||
// Allow API server Protobuf encoder to encode collections item by item, instead of all at once.
|
||||
StreamingCollectionEncodingToProtobuf featuregate.Feature = "StreamingCollectionEncodingToProtobuf"
|
||||
|
||||
// owner: @danwinship
|
||||
// kep: https://kep.k8s.io/4858
|
||||
//
|
||||
|
|
@ -1675,12 +1661,6 @@ var defaultVersionedKubernetesFeatureGates = map[featuregate.Feature]featuregate
|
|||
{Version: version.MustParse("1.35"), Default: true, PreRelease: featuregate.Beta},
|
||||
},
|
||||
|
||||
OrderedNamespaceDeletion: {
|
||||
{Version: version.MustParse("1.30"), Default: false, PreRelease: featuregate.Beta},
|
||||
{Version: version.MustParse("1.33"), Default: true, PreRelease: featuregate.Beta},
|
||||
{Version: version.MustParse("1.34"), Default: true, PreRelease: featuregate.GA, LockToDefault: true}, // remove in 1.37
|
||||
},
|
||||
|
||||
PLEGOnDemandRelist: {
|
||||
{Version: version.MustParse("1.36"), Default: true, PreRelease: featuregate.Beta},
|
||||
},
|
||||
|
|
@ -1950,16 +1930,6 @@ var defaultVersionedKubernetesFeatureGates = map[featuregate.Feature]featuregate
|
|||
{Version: version.MustParse("1.35"), Default: false, PreRelease: featuregate.Beta},
|
||||
},
|
||||
|
||||
StreamingCollectionEncodingToJSON: {
|
||||
{Version: version.MustParse("1.33"), Default: true, PreRelease: featuregate.Beta},
|
||||
{Version: version.MustParse("1.34"), Default: true, PreRelease: featuregate.GA, LockToDefault: true},
|
||||
},
|
||||
|
||||
StreamingCollectionEncodingToProtobuf: {
|
||||
{Version: version.MustParse("1.33"), Default: true, PreRelease: featuregate.Beta},
|
||||
{Version: version.MustParse("1.34"), Default: true, PreRelease: featuregate.GA, LockToDefault: true},
|
||||
},
|
||||
|
||||
StrictIPCIDRValidation: {
|
||||
{Version: version.MustParse("1.33"), Default: false, PreRelease: featuregate.Alpha},
|
||||
{Version: version.MustParse("1.36"), Default: true, PreRelease: featuregate.Beta},
|
||||
|
|
@ -2087,12 +2057,6 @@ var defaultVersionedKubernetesFeatureGates = map[featuregate.Feature]featuregate
|
|||
{Version: version.MustParse("1.26"), Default: true, PreRelease: featuregate.Beta},
|
||||
},
|
||||
|
||||
genericfeatures.APIServerTracing: {
|
||||
{Version: version.MustParse("1.22"), Default: false, PreRelease: featuregate.Alpha},
|
||||
{Version: version.MustParse("1.27"), Default: true, PreRelease: featuregate.Beta},
|
||||
{Version: version.MustParse("1.34"), Default: true, PreRelease: featuregate.GA, LockToDefault: true}, // remove in 1.37
|
||||
},
|
||||
|
||||
genericfeatures.APIServingWithRoutine: {
|
||||
{Version: version.MustParse("1.30"), Default: false, PreRelease: featuregate.Alpha},
|
||||
},
|
||||
|
|
@ -2111,11 +2075,6 @@ var defaultVersionedKubernetesFeatureGates = map[featuregate.Feature]featuregate
|
|||
{Version: version.MustParse("1.32"), Default: false, PreRelease: featuregate.Alpha},
|
||||
},
|
||||
|
||||
genericfeatures.BtreeWatchCache: {
|
||||
{Version: version.MustParse("1.32"), Default: true, PreRelease: featuregate.Beta},
|
||||
{Version: version.MustParse("1.33"), Default: true, PreRelease: featuregate.GA, LockToDefault: true},
|
||||
},
|
||||
|
||||
genericfeatures.CBORServingAndStorage: {
|
||||
{Version: version.MustParse("1.32"), Default: false, PreRelease: featuregate.Alpha},
|
||||
},
|
||||
|
|
@ -2124,12 +2083,6 @@ var defaultVersionedKubernetesFeatureGates = map[featuregate.Feature]featuregate
|
|||
{Version: version.MustParse("1.31"), Default: false, PreRelease: featuregate.Beta},
|
||||
},
|
||||
|
||||
genericfeatures.ConsistentListFromCache: {
|
||||
{Version: version.MustParse("1.28"), Default: false, PreRelease: featuregate.Alpha},
|
||||
{Version: version.MustParse("1.31"), Default: true, PreRelease: featuregate.Beta},
|
||||
{Version: version.MustParse("1.34"), Default: true, PreRelease: featuregate.GA, LockToDefault: true},
|
||||
},
|
||||
|
||||
genericfeatures.ConsistentListFromCacheSkipTimeoutFallback: {
|
||||
{Version: version.MustParse("1.37"), Default: false, PreRelease: featuregate.Alpha},
|
||||
},
|
||||
|
|
@ -2193,17 +2146,6 @@ var defaultVersionedKubernetesFeatureGates = map[featuregate.Feature]featuregate
|
|||
{Version: version.MustParse("1.33"), Default: true, PreRelease: featuregate.Beta},
|
||||
},
|
||||
|
||||
genericfeatures.ResilientWatchCacheInitialization: {
|
||||
{Version: version.MustParse("1.31"), Default: true, PreRelease: featuregate.Beta},
|
||||
{Version: version.MustParse("1.34"), Default: true, PreRelease: featuregate.GA, LockToDefault: true},
|
||||
},
|
||||
|
||||
genericfeatures.RetryGenerateName: {
|
||||
{Version: version.MustParse("1.30"), Default: false, PreRelease: featuregate.Alpha},
|
||||
{Version: version.MustParse("1.31"), Default: true, PreRelease: featuregate.Beta},
|
||||
{Version: version.MustParse("1.32"), Default: true, LockToDefault: true, PreRelease: featuregate.GA},
|
||||
},
|
||||
|
||||
genericfeatures.SeparateCacheWatchRPC: {
|
||||
{Version: version.MustParse("1.28"), Default: true, PreRelease: featuregate.Beta},
|
||||
{Version: version.MustParse("1.33"), Default: false, PreRelease: featuregate.Deprecated},
|
||||
|
|
@ -2480,8 +2422,6 @@ var defaultKubernetesFeatureGateDependencies = map[featuregate.Feature][]feature
|
|||
|
||||
OpportunisticBatching: {},
|
||||
|
||||
OrderedNamespaceDeletion: {},
|
||||
|
||||
PLEGOnDemandRelist: {},
|
||||
|
||||
PersistentVolumeClaimUnusedSinceTime: {},
|
||||
|
|
@ -2592,10 +2532,6 @@ var defaultKubernetesFeatureGateDependencies = map[featuregate.Feature][]feature
|
|||
|
||||
StorageVersionMigrator: {},
|
||||
|
||||
StreamingCollectionEncodingToJSON: {},
|
||||
|
||||
StreamingCollectionEncodingToProtobuf: {},
|
||||
|
||||
StrictIPCIDRValidation: {},
|
||||
|
||||
SupplementalGroupsPolicy: {},
|
||||
|
|
@ -2646,8 +2582,6 @@ var defaultKubernetesFeatureGateDependencies = map[featuregate.Feature][]feature
|
|||
|
||||
genericfeatures.APIServerIdentity: {},
|
||||
|
||||
genericfeatures.APIServerTracing: {},
|
||||
|
||||
genericfeatures.APIServingWithRoutine: {},
|
||||
|
||||
genericfeatures.AggregatedDiscoveryRemoveBetaType: {},
|
||||
|
|
@ -2656,15 +2590,11 @@ var defaultKubernetesFeatureGateDependencies = map[featuregate.Feature][]feature
|
|||
|
||||
genericfeatures.AllowUnsafeMalformedObjectDeletion: {},
|
||||
|
||||
genericfeatures.BtreeWatchCache: {},
|
||||
|
||||
genericfeatures.CBORServingAndStorage: {},
|
||||
|
||||
genericfeatures.ConcurrentWatchObjectDecode: {},
|
||||
|
||||
genericfeatures.ConsistentListFromCache: {},
|
||||
|
||||
genericfeatures.ConsistentListFromCacheSkipTimeoutFallback: {genericfeatures.ConsistentListFromCache},
|
||||
genericfeatures.ConsistentListFromCacheSkipTimeoutFallback: {},
|
||||
|
||||
genericfeatures.ConstrainedImpersonation: {},
|
||||
|
||||
|
|
@ -2687,10 +2617,6 @@ var defaultKubernetesFeatureGateDependencies = map[featuregate.Feature][]feature
|
|||
|
||||
genericfeatures.RemoteRequestHeaderUID: {},
|
||||
|
||||
genericfeatures.ResilientWatchCacheInitialization: {},
|
||||
|
||||
genericfeatures.RetryGenerateName: {},
|
||||
|
||||
genericfeatures.SeparateCacheWatchRPC: {},
|
||||
|
||||
genericfeatures.ShardedListAndWatch: {},
|
||||
|
|
|
|||
|
|
@ -40,7 +40,6 @@ import (
|
|||
utilnet "k8s.io/apimachinery/pkg/util/net"
|
||||
utilvalidation "k8s.io/apimachinery/pkg/util/validation"
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
apiserverfeatures "k8s.io/apiserver/pkg/features"
|
||||
"k8s.io/apiserver/pkg/registry/generic"
|
||||
"k8s.io/apiserver/pkg/registry/rest"
|
||||
"k8s.io/apiserver/pkg/storage"
|
||||
|
|
@ -452,15 +451,11 @@ func GetAttrs(obj runtime.Object) (labels.Set, fields.Set, error) {
|
|||
|
||||
// MatchPod returns a generic matcher for a given label and field selector.
|
||||
func MatchPod(label labels.Selector, field fields.Selector) storage.SelectionPredicate {
|
||||
var indexFields = []string{"spec.nodeName"}
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.StorageNamespaceIndex) && !utilfeature.DefaultFeatureGate.Enabled(apiserverfeatures.BtreeWatchCache) {
|
||||
indexFields = append(indexFields, "metadata.namespace")
|
||||
}
|
||||
return storage.SelectionPredicate{
|
||||
Label: label,
|
||||
Field: field,
|
||||
GetAttrs: GetAttrs,
|
||||
IndexFields: indexFields,
|
||||
IndexFields: []string{"spec.nodeName"},
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -478,24 +473,11 @@ func NodeNameIndexFunc(obj interface{}) ([]string, error) {
|
|||
return []string{pod.Spec.NodeName}, nil
|
||||
}
|
||||
|
||||
// NamespaceIndexFunc return value name of given object.
|
||||
func NamespaceIndexFunc(obj interface{}) ([]string, error) {
|
||||
pod, ok := obj.(*api.Pod)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("not a pod")
|
||||
}
|
||||
return []string{pod.Namespace}, nil
|
||||
}
|
||||
|
||||
// Indexers returns the indexers for pod storage.
|
||||
func Indexers() *cache.Indexers {
|
||||
var indexers = cache.Indexers{
|
||||
return &cache.Indexers{
|
||||
storage.FieldIndex("spec.nodeName"): NodeNameIndexFunc,
|
||||
}
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.StorageNamespaceIndex) && !utilfeature.DefaultFeatureGate.Enabled(apiserverfeatures.BtreeWatchCache) {
|
||||
indexers[storage.FieldIndex("metadata.namespace")] = NamespaceIndexFunc
|
||||
}
|
||||
return &indexers
|
||||
}
|
||||
|
||||
// ToSelectableFields returns a field set that represents the object
|
||||
|
|
|
|||
|
|
@ -73,16 +73,13 @@ func (c *GenericConfig) NewRESTStorage(apiResourceConfigSource serverstorage.API
|
|||
ParameterCodec: legacyscheme.ParameterCodec,
|
||||
NegotiatedSerializer: legacyscheme.Codecs,
|
||||
}
|
||||
opts := []serializer.CodecFactoryOptionsMutator{}
|
||||
opts := []serializer.CodecFactoryOptionsMutator{
|
||||
serializer.WithStreamingCollectionEncodingToJSON(),
|
||||
serializer.WithStreamingCollectionEncodingToProtobuf(),
|
||||
}
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.CBORServingAndStorage) {
|
||||
opts = append(opts, serializer.WithSerializer(cbor.NewSerializerInfo))
|
||||
}
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.StreamingCollectionEncodingToJSON) {
|
||||
opts = append(opts, serializer.WithStreamingCollectionEncodingToJSON())
|
||||
}
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.StreamingCollectionEncodingToProtobuf) {
|
||||
opts = append(opts, serializer.WithStreamingCollectionEncodingToProtobuf())
|
||||
}
|
||||
if len(opts) != 0 {
|
||||
apiGroupInfo.NegotiatedSerializer = serializer.NewCodecFactory(legacyscheme.Scheme, opts...)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -888,7 +888,6 @@ func (r *crdHandler) getOrCreateServingInfoFor(uid types.UID, name string) (*crd
|
|||
clusterScoped := crd.Spec.Scope == apiextensionsv1.ClusterScoped
|
||||
|
||||
// CRDs explicitly do not support protobuf, but some objects returned by the API server do
|
||||
streamingCollections := utilfeature.DefaultFeatureGate.Enabled(features.StreamingCollectionEncodingToJSON)
|
||||
negotiatedSerializer := unstructuredNegotiatedSerializer{
|
||||
typer: typer,
|
||||
creator: creator,
|
||||
|
|
@ -902,11 +901,11 @@ func (r *crdHandler) getOrCreateServingInfoFor(uid types.UID, name string) (*crd
|
|||
MediaTypeType: "application",
|
||||
MediaTypeSubType: "json",
|
||||
EncodesAsText: true,
|
||||
Serializer: json.NewSerializerWithOptions(json.DefaultMetaFactory, creator, typer, json.SerializerOptions{StreamingCollectionsEncoding: streamingCollections}),
|
||||
Serializer: json.NewSerializerWithOptions(json.DefaultMetaFactory, creator, typer, json.SerializerOptions{StreamingCollectionsEncoding: true}),
|
||||
PrettySerializer: json.NewSerializerWithOptions(json.DefaultMetaFactory, creator, typer, json.SerializerOptions{Pretty: true}),
|
||||
StrictSerializer: json.NewSerializerWithOptions(json.DefaultMetaFactory, creator, typer, json.SerializerOptions{
|
||||
Strict: true,
|
||||
StreamingCollectionsEncoding: streamingCollections,
|
||||
StreamingCollectionsEncoding: true,
|
||||
}),
|
||||
StreamSerializer: &runtime.StreamSerializerInfo{
|
||||
EncodesAsText: true,
|
||||
|
|
@ -930,7 +929,7 @@ func (r *crdHandler) getOrCreateServingInfoFor(uid types.UID, name string) (*crd
|
|||
MediaTypeType: "application",
|
||||
MediaTypeSubType: "vnd.kubernetes.protobuf",
|
||||
Serializer: protobuf.NewSerializerWithOptions(creator, typer, protobuf.SerializerOptions{
|
||||
StreamingCollectionsEncoding: utilfeature.DefaultFeatureGate.Enabled(features.StreamingCollectionEncodingToProtobuf),
|
||||
StreamingCollectionsEncoding: true,
|
||||
}),
|
||||
StreamSerializer: &runtime.StreamSerializerInfo{
|
||||
Serializer: protobuf.NewRawSerializer(creator, typer),
|
||||
|
|
@ -1007,16 +1006,13 @@ func (r *crdHandler) getOrCreateServingInfoFor(uid types.UID, name string) (*crd
|
|||
scaleScope := *requestScopes[v.Name]
|
||||
scaleConverter := scale.NewScaleConverter()
|
||||
scaleScope.Subresource = "scale"
|
||||
var opts []serializer.CodecFactoryOptionsMutator
|
||||
opts := []serializer.CodecFactoryOptionsMutator{
|
||||
serializer.WithStreamingCollectionEncodingToJSON(),
|
||||
serializer.WithStreamingCollectionEncodingToProtobuf(),
|
||||
}
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.CBORServingAndStorage) {
|
||||
opts = append(opts, serializer.WithSerializer(cbor.NewSerializerInfo))
|
||||
}
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.StreamingCollectionEncodingToJSON) {
|
||||
opts = append(opts, serializer.WithStreamingCollectionEncodingToJSON())
|
||||
}
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.StreamingCollectionEncodingToProtobuf) {
|
||||
opts = append(opts, serializer.WithStreamingCollectionEncodingToProtobuf())
|
||||
}
|
||||
scaleScope.Serializer = serializer.NewCodecFactory(scaleConverter.Scheme(), opts...)
|
||||
scaleScope.Kind = autoscalingv1.SchemeGroupVersion.WithKind("Scale")
|
||||
scaleScope.Namer = handlers.ContextBasedNaming{
|
||||
|
|
|
|||
|
|
@ -44,11 +44,6 @@ const (
|
|||
// Assigns each kube-apiserver an ID in a cluster.
|
||||
APIServerIdentity featuregate.Feature = "APIServerIdentity"
|
||||
|
||||
// owner: @dashpole
|
||||
//
|
||||
// Add support for distributed tracing in the API Server
|
||||
APIServerTracing featuregate.Feature = "APIServerTracing"
|
||||
|
||||
// owner: @linxiulei
|
||||
//
|
||||
// Enables serving watch requests in separate goroutines.
|
||||
|
|
@ -75,11 +70,6 @@ const (
|
|||
// resources using the Kubernetes API only.
|
||||
AllowUnsafeMalformedObjectDeletion featuregate.Feature = "AllowUnsafeMalformedObjectDeletion"
|
||||
|
||||
// owner: @serathius
|
||||
//
|
||||
// Replaces watch cache hashmap implementation with a btree based one, bringing performance improvements.
|
||||
BtreeWatchCache featuregate.Feature = "BtreeWatchCache"
|
||||
|
||||
// owner: @benluddy
|
||||
// kep: https://kep.k8s.io/4222
|
||||
//
|
||||
|
|
@ -91,12 +81,6 @@ const (
|
|||
// Enables concurrent watch object decoding to avoid starving watch cache when conversion webhook is installed.
|
||||
ConcurrentWatchObjectDecode featuregate.Feature = "ConcurrentWatchObjectDecode"
|
||||
|
||||
// owner: @serathius
|
||||
// kep: http://kep.k8s.io/2340
|
||||
//
|
||||
// Allow the API server to serve consistent lists from cache
|
||||
ConsistentListFromCache featuregate.Feature = "ConsistentListFromCache"
|
||||
|
||||
// owner: @yedou37
|
||||
//
|
||||
// Skip the timeout fallback to storage when a consistent LIST from cache cannot be served,
|
||||
|
|
@ -205,17 +189,6 @@ const (
|
|||
// headers when forwarding requests to the servers serving the aggregated API.
|
||||
RemoteRequestHeaderUID featuregate.Feature = "RemoteRequestHeaderUID"
|
||||
|
||||
// owner: @wojtek-t
|
||||
//
|
||||
// Enables resilient watchcache initialization to avoid controlplane
|
||||
// overload.
|
||||
ResilientWatchCacheInitialization featuregate.Feature = "ResilientWatchCacheInitialization"
|
||||
|
||||
// owner: @jpbetz
|
||||
// Resource create requests using generateName are retried automatically by the apiserver
|
||||
// if the generated name conflicts with an existing resource name, up to a maximum number of 7 retries.
|
||||
RetryGenerateName featuregate.Feature = "RetryGenerateName"
|
||||
|
||||
// owner: @cici37
|
||||
//
|
||||
// Allow watch cache to create a watch on a dedicated RPC.
|
||||
|
|
@ -246,14 +219,6 @@ const (
|
|||
// document.
|
||||
StorageVersionHash featuregate.Feature = "StorageVersionHash"
|
||||
|
||||
// owner: @serathius
|
||||
// Allow API server JSON encoder to encode collections item by item, instead of all at once.
|
||||
StreamingCollectionEncodingToJSON featuregate.Feature = "StreamingCollectionEncodingToJSON"
|
||||
|
||||
// owner: @serathius
|
||||
// Allow API server Protobuf encoder to encode collections item by item, instead of all at once.
|
||||
StreamingCollectionEncodingToProtobuf featuregate.Feature = "StreamingCollectionEncodingToProtobuf"
|
||||
|
||||
// owner: @aramase, @enj, @nabokihms
|
||||
// kep: https://kep.k8s.io/3331
|
||||
//
|
||||
|
|
@ -331,12 +296,6 @@ var defaultVersionedKubernetesFeatureGates = map[featuregate.Feature]featuregate
|
|||
{Version: version.MustParse("1.26"), Default: true, PreRelease: featuregate.Beta},
|
||||
},
|
||||
|
||||
APIServerTracing: {
|
||||
{Version: version.MustParse("1.22"), Default: false, PreRelease: featuregate.Alpha},
|
||||
{Version: version.MustParse("1.27"), Default: true, PreRelease: featuregate.Beta},
|
||||
{Version: version.MustParse("1.34"), Default: true, PreRelease: featuregate.GA, LockToDefault: true}, // remove in 1.37
|
||||
},
|
||||
|
||||
APIServingWithRoutine: {
|
||||
{Version: version.MustParse("1.30"), Default: false, PreRelease: featuregate.Alpha},
|
||||
},
|
||||
|
|
@ -355,11 +314,6 @@ var defaultVersionedKubernetesFeatureGates = map[featuregate.Feature]featuregate
|
|||
{Version: version.MustParse("1.32"), Default: false, PreRelease: featuregate.Alpha},
|
||||
},
|
||||
|
||||
BtreeWatchCache: {
|
||||
{Version: version.MustParse("1.32"), Default: true, PreRelease: featuregate.Beta},
|
||||
{Version: version.MustParse("1.33"), Default: true, PreRelease: featuregate.GA, LockToDefault: true},
|
||||
},
|
||||
|
||||
CBORServingAndStorage: {
|
||||
{Version: version.MustParse("1.32"), Default: false, PreRelease: featuregate.Alpha},
|
||||
},
|
||||
|
|
@ -368,12 +322,6 @@ var defaultVersionedKubernetesFeatureGates = map[featuregate.Feature]featuregate
|
|||
{Version: version.MustParse("1.31"), Default: false, PreRelease: featuregate.Beta},
|
||||
},
|
||||
|
||||
ConsistentListFromCache: {
|
||||
{Version: version.MustParse("1.28"), Default: false, PreRelease: featuregate.Alpha},
|
||||
{Version: version.MustParse("1.31"), Default: true, PreRelease: featuregate.Beta},
|
||||
{Version: version.MustParse("1.34"), Default: true, PreRelease: featuregate.GA, LockToDefault: true},
|
||||
},
|
||||
|
||||
ConsistentListFromCacheSkipTimeoutFallback: {
|
||||
{Version: version.MustParse("1.37"), Default: false, PreRelease: featuregate.Alpha},
|
||||
},
|
||||
|
|
@ -437,17 +385,6 @@ var defaultVersionedKubernetesFeatureGates = map[featuregate.Feature]featuregate
|
|||
{Version: version.MustParse("1.33"), Default: true, PreRelease: featuregate.Beta},
|
||||
},
|
||||
|
||||
ResilientWatchCacheInitialization: {
|
||||
{Version: version.MustParse("1.31"), Default: true, PreRelease: featuregate.Beta},
|
||||
{Version: version.MustParse("1.34"), Default: true, PreRelease: featuregate.GA, LockToDefault: true},
|
||||
},
|
||||
|
||||
RetryGenerateName: {
|
||||
{Version: version.MustParse("1.30"), Default: false, PreRelease: featuregate.Alpha},
|
||||
{Version: version.MustParse("1.31"), Default: true, PreRelease: featuregate.Beta},
|
||||
{Version: version.MustParse("1.32"), Default: true, LockToDefault: true, PreRelease: featuregate.GA},
|
||||
},
|
||||
|
||||
SeparateCacheWatchRPC: {
|
||||
{Version: version.MustParse("1.28"), Default: true, PreRelease: featuregate.Beta},
|
||||
{Version: version.MustParse("1.33"), Default: false, PreRelease: featuregate.Deprecated},
|
||||
|
|
@ -471,16 +408,6 @@ var defaultVersionedKubernetesFeatureGates = map[featuregate.Feature]featuregate
|
|||
{Version: version.MustParse("1.15"), Default: true, PreRelease: featuregate.Beta},
|
||||
},
|
||||
|
||||
StreamingCollectionEncodingToJSON: {
|
||||
{Version: version.MustParse("1.33"), Default: true, PreRelease: featuregate.Beta},
|
||||
{Version: version.MustParse("1.34"), Default: true, PreRelease: featuregate.GA, LockToDefault: true},
|
||||
},
|
||||
|
||||
StreamingCollectionEncodingToProtobuf: {
|
||||
{Version: version.MustParse("1.33"), Default: true, PreRelease: featuregate.Beta},
|
||||
{Version: version.MustParse("1.34"), Default: true, PreRelease: featuregate.GA, LockToDefault: true},
|
||||
},
|
||||
|
||||
StructuredAuthenticationConfigurationEgressSelector: {
|
||||
{Version: version.MustParse("1.34"), Default: true, PreRelease: featuregate.Beta},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -452,7 +452,7 @@ const maxNameGenerationCreateAttempts = 8
|
|||
// hooks). Tests which call this might want to call DeepCopy if they expect to
|
||||
// be able to examine the input and output objects for differences.
|
||||
func (e *Store) Create(ctx context.Context, obj runtime.Object, createValidation rest.ValidateObjectFunc, options *metav1.CreateOptions) (runtime.Object, error) {
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.RetryGenerateName) && needsNameGeneration(obj) {
|
||||
if needsNameGeneration(obj) {
|
||||
return e.createWithGenerateNameRetry(ctx, obj, createValidation, options)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -45,12 +45,10 @@ import (
|
|||
"k8s.io/apimachinery/pkg/selection"
|
||||
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
"k8s.io/apimachinery/pkg/util/version"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
"k8s.io/apiserver/pkg/apis/example"
|
||||
examplev1 "k8s.io/apiserver/pkg/apis/example/v1"
|
||||
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
|
||||
"k8s.io/apiserver/pkg/features"
|
||||
"k8s.io/apiserver/pkg/registry/generic"
|
||||
"k8s.io/apiserver/pkg/registry/rest"
|
||||
"k8s.io/apiserver/pkg/storage"
|
||||
|
|
@ -59,9 +57,7 @@ import (
|
|||
"k8s.io/apiserver/pkg/storage/names"
|
||||
"k8s.io/apiserver/pkg/storage/storagebackend/factory"
|
||||
storagetesting "k8s.io/apiserver/pkg/storage/testing"
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
featuregatetesting "k8s.io/component-base/featuregate/testing"
|
||||
)
|
||||
|
||||
var scheme = runtime.NewScheme()
|
||||
|
|
@ -451,38 +447,6 @@ func TestStoreCreateWithRetryNameGenerate(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestStoreCreateWithRetryNameGenerateFeatureDisabled(t *testing.T) {
|
||||
// Preserve testing of disabled RetryGenerateName feature gate since it can still be disabled when emulation version is set.
|
||||
featuregatetesting.SetFeatureGateEmulationVersionDuringTest(t, utilfeature.DefaultFeatureGate, version.MustParse("1.31"))
|
||||
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.RetryGenerateName, false)
|
||||
namedObj := func(id int) *example.Pod {
|
||||
return &example.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: fmt.Sprintf("prefix-%d", id), Namespace: "test"},
|
||||
Spec: example.PodSpec{NodeName: "machine"},
|
||||
}
|
||||
}
|
||||
|
||||
generateNameObj := &example.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{GenerateName: "prefix-", Namespace: "test"},
|
||||
Spec: example.PodSpec{NodeName: "machine"},
|
||||
}
|
||||
|
||||
testContext := genericapirequest.WithNamespace(genericapirequest.NewContext(), "test")
|
||||
destroyFunc, registry := NewTestGenericStoreRegistry(t)
|
||||
defer destroyFunc()
|
||||
|
||||
registry.CreateStrategy = &testRESTStrategy{scheme, &sequentialNameGenerator{}, true, false, true}
|
||||
|
||||
_, err := registry.Create(testContext, namedObj(0), rest.ValidateAllObjectFunc, &metav1.CreateOptions{})
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
_, err = registry.Create(testContext, generateNameObj, rest.ValidateAllObjectFunc, &metav1.CreateOptions{})
|
||||
if err == nil || !errors.IsAlreadyExists(err) {
|
||||
t.Error("Expected already exists error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewCreateOptionsFromUpdateOptions(t *testing.T) {
|
||||
f := randfill.New().NilChance(0.0).NumElements(1, 1)
|
||||
|
||||
|
|
@ -2464,13 +2428,11 @@ func newTestGenericStoreRegistry(t *testing.T, scheme *runtime.Scheme, hasCacheE
|
|||
if err != nil {
|
||||
t.Fatalf("Couldn't create cacher: %v", err)
|
||||
}
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.ResilientWatchCacheInitialization) {
|
||||
// The tests assume that Get/GetList/Watch calls shouldn't fail.
|
||||
// However, 429 error can now be returned if watchcache is under initialization.
|
||||
// To avoid rewriting all tests, we wait for watchcache to initialize.
|
||||
if err := cacher.Wait(context.Background()); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// The tests assume that Get/GetList/Watch calls shouldn't fail.
|
||||
// However, 429 error can now be returned if watchcache is under initialization.
|
||||
// To avoid rewriting all tests, we wait for watchcache to initialize.
|
||||
if err := cacher.Wait(context.Background()); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
d := destroyFunc
|
||||
delegator := cacherstorage.NewCacheDelegator(cacher, s)
|
||||
|
|
@ -2990,76 +2952,3 @@ func TestValidateIndexers(t *testing.T) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
type predictableNameGenerator struct {
|
||||
index int
|
||||
}
|
||||
|
||||
func (p *predictableNameGenerator) GenerateName(base string) string {
|
||||
p.index++
|
||||
return fmt.Sprintf("%s%d", base, p.index)
|
||||
}
|
||||
|
||||
func TestStoreCreateGenerateNameConflict(t *testing.T) {
|
||||
// Preserve testing of disabled RetryGenerateName feature gate since it can still be disabled when emulation version is set.
|
||||
featuregatetesting.SetFeatureGateEmulationVersionDuringTest(t, utilfeature.DefaultFeatureGate, version.MustParse("1.31"))
|
||||
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.RetryGenerateName, false)
|
||||
|
||||
// podA will be stored with name foo12345
|
||||
podA := &example.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "foo1", Namespace: "test"},
|
||||
Spec: example.PodSpec{NodeName: "machine"},
|
||||
}
|
||||
// podB will generate the same name as podA "foo1" in the first try
|
||||
podB := &example.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{GenerateName: "foo", Namespace: "test"},
|
||||
Spec: example.PodSpec{NodeName: "machine2"},
|
||||
}
|
||||
|
||||
testContext := genericapirequest.WithNamespace(genericapirequest.NewContext(), "test")
|
||||
destroyFunc, registry := NewTestGenericStoreRegistry(t)
|
||||
defer destroyFunc()
|
||||
// re-define delete strategy to have graceful delete capability
|
||||
defaultCreateStrategy := &testRESTStrategy{scheme, &predictableNameGenerator{}, true, false, true}
|
||||
registry.CreateStrategy = defaultCreateStrategy
|
||||
|
||||
// create the object (DeepCopy because the registry mutates the objects)
|
||||
objA, err := registry.Create(testContext, podA.DeepCopy(), rest.ValidateAllObjectFunc, &metav1.CreateOptions{})
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
|
||||
// get the object
|
||||
checkobjA, err := registry.Get(testContext, podA.Name, &metav1.GetOptions{})
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
|
||||
// verify objects are equal
|
||||
if e, a := objA, checkobjA; !reflect.DeepEqual(e, a) {
|
||||
t.Errorf("Expected %#v, got %#v", e, a)
|
||||
}
|
||||
|
||||
// now try to create the second pod (DeepCopy because the registry mutate the objects)
|
||||
_, err = registry.Create(testContext, podB.DeepCopy(), rest.ValidateAllObjectFunc, &metav1.CreateOptions{})
|
||||
if !errors.IsAlreadyExists(err) {
|
||||
t.Errorf("Unexpected error: %+v", err)
|
||||
}
|
||||
|
||||
// check the 'alraedy exists' msg correspond to the generated name
|
||||
msg := &err.(*errors.StatusError).ErrStatus.Message
|
||||
if !strings.Contains(*msg, "already exists, the server was not able to generate a unique name for the object") {
|
||||
t.Errorf("Unexpected error without the 'was not able to generate a unique name' in message: %v", err)
|
||||
}
|
||||
|
||||
// now try to create the second pod again
|
||||
objB, err := registry.Create(testContext, podB.DeepCopy(), rest.ValidateAllObjectFunc, &metav1.CreateOptions{})
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
|
||||
if objB.(*example.Pod).Name != "foo2" && objB.(*example.Pod).GenerateName != "foo" {
|
||||
t.Errorf("Unexpected object: %+v", objB)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1069,9 +1069,7 @@ func DefaultBuildHandlerChain(apiHandler http.Handler, c *Config) http.Handler {
|
|||
|
||||
// WithTracing comes after authentication so we can allow authenticated
|
||||
// clients to influence sampling.
|
||||
if c.FeatureGate.Enabled(genericfeatures.APIServerTracing) {
|
||||
handler = genericapifilters.WithTracing(handler, c.TracerProvider)
|
||||
}
|
||||
handler = genericapifilters.WithTracing(handler, c.TracerProvider)
|
||||
failedHandler = filterlatency.TrackCompleted(failedHandler)
|
||||
handler = filterlatency.TrackCompleted(handler)
|
||||
handler = genericapifilters.WithAuthentication(handler, c.Authentication.Authenticator, failedHandler, c.Authentication.APIAudiences, c.Authentication.RequestHeaderConfig)
|
||||
|
|
|
|||
|
|
@ -1017,16 +1017,13 @@ func (s *GenericAPIServer) newAPIGroupVersion(apiGroupInfo *APIGroupInfo, groupV
|
|||
// NewDefaultAPIGroupInfo returns an APIGroupInfo stubbed with "normal" values
|
||||
// exposed for easier composition from other packages
|
||||
func NewDefaultAPIGroupInfo(group string, scheme *runtime.Scheme, parameterCodec runtime.ParameterCodec, codecs serializer.CodecFactory) APIGroupInfo {
|
||||
opts := []serializer.CodecFactoryOptionsMutator{}
|
||||
opts := []serializer.CodecFactoryOptionsMutator{
|
||||
serializer.WithStreamingCollectionEncodingToJSON(),
|
||||
serializer.WithStreamingCollectionEncodingToProtobuf(),
|
||||
}
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.CBORServingAndStorage) {
|
||||
opts = append(opts, serializer.WithSerializer(cbor.NewSerializerInfo))
|
||||
}
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.StreamingCollectionEncodingToJSON) {
|
||||
opts = append(opts, serializer.WithStreamingCollectionEncodingToJSON())
|
||||
}
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.StreamingCollectionEncodingToProtobuf) {
|
||||
opts = append(opts, serializer.WithStreamingCollectionEncodingToProtobuf())
|
||||
}
|
||||
if len(opts) != 0 {
|
||||
codecs = serializer.NewCodecFactory(scheme, opts...)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,9 +21,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/spf13/pflag"
|
||||
"k8s.io/apiserver/pkg/features"
|
||||
"k8s.io/apiserver/pkg/server"
|
||||
"k8s.io/apiserver/pkg/util/feature"
|
||||
clientgoinformers "k8s.io/client-go/informers"
|
||||
clientgoclientset "k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/rest"
|
||||
|
|
@ -72,9 +70,7 @@ func (o *CoreAPIOptions) ApplyTo(config *server.RecommendedConfig) error {
|
|||
return err
|
||||
}
|
||||
}
|
||||
if feature.DefaultFeatureGate.Enabled(features.APIServerTracing) {
|
||||
kubeconfig.Wrap(tracing.WrapperFor(config.TracerProvider))
|
||||
}
|
||||
kubeconfig.Wrap(tracing.WrapperFor(config.TracerProvider))
|
||||
clientgoExternalClient, err := clientgoclientset.NewForConfig(kubeconfig)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create Kubernetes clientset: %v", err)
|
||||
|
|
|
|||
|
|
@ -34,7 +34,6 @@ import (
|
|||
"k8s.io/apimachinery/pkg/runtime/serializer"
|
||||
"k8s.io/apiserver/pkg/apis/apiserver"
|
||||
"k8s.io/apiserver/pkg/apis/apiserver/install"
|
||||
"k8s.io/apiserver/pkg/features"
|
||||
"k8s.io/apiserver/pkg/server"
|
||||
"k8s.io/apiserver/pkg/server/egressselector"
|
||||
"k8s.io/apiserver/pkg/util/feature"
|
||||
|
|
@ -87,9 +86,6 @@ func (o *TracingOptions) ApplyTo(es *egressselector.EgressSelector, c *server.Co
|
|||
if o == nil || o.ConfigFile == "" {
|
||||
return nil
|
||||
}
|
||||
if !feature.DefaultFeatureGate.Enabled(features.APIServerTracing) {
|
||||
return fmt.Errorf("APIServerTracing feature is not enabled, but tracing config file was provided")
|
||||
}
|
||||
|
||||
traceConfig, err := ReadTracingConfiguration(o.ConfigFile)
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -521,19 +521,9 @@ func (c *Cacher) Watch(ctx context.Context, key string, opts storage.ListOptions
|
|||
return nil, err
|
||||
}
|
||||
|
||||
var readyGeneration int
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.ResilientWatchCacheInitialization) {
|
||||
var err error
|
||||
var downtime time.Duration
|
||||
readyGeneration, downtime, err = c.ready.checkAndReadGeneration()
|
||||
if err != nil {
|
||||
return nil, errors.NewTooManyRequests(err.Error(), calculateRetryAfterForUnreadyCache(downtime))
|
||||
}
|
||||
} else {
|
||||
readyGeneration, err = c.ready.waitAndReadGeneration(ctx)
|
||||
if err != nil {
|
||||
return nil, errors.NewServiceUnavailable(err.Error())
|
||||
}
|
||||
readyGeneration, downtime, err := c.ready.checkAndReadGeneration()
|
||||
if err != nil {
|
||||
return nil, errors.NewTooManyRequests(err.Error(), calculateRetryAfterForUnreadyCache(downtime))
|
||||
}
|
||||
|
||||
// determine the namespace and name scope of the watch, first from the request, secondarily from the field selector
|
||||
|
|
@ -760,16 +750,10 @@ func (c *Cacher) GetList(ctx context.Context, key string, opts storage.ListOptio
|
|||
attribute.Stringer("type", c.groupResource))
|
||||
defer span.End(500 * time.Millisecond)
|
||||
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.ResilientWatchCacheInitialization) {
|
||||
if downtime, err := c.ready.check(); err != nil {
|
||||
// If Cacher is not initialized, reject List requests
|
||||
// as described in https://kep.k8s.io/4568
|
||||
return errors.NewTooManyRequests(err.Error(), calculateRetryAfterForUnreadyCache(downtime))
|
||||
}
|
||||
} else {
|
||||
if err := c.ready.wait(ctx); err != nil {
|
||||
return errors.NewServiceUnavailable(err.Error())
|
||||
}
|
||||
if downtime, err := c.ready.check(); err != nil {
|
||||
// If Cacher is not initialized, reject List requests
|
||||
// as described in https://kep.k8s.io/4568
|
||||
return errors.NewTooManyRequests(err.Error(), calculateRetryAfterForUnreadyCache(downtime))
|
||||
}
|
||||
span.AddEvent("Ready")
|
||||
|
||||
|
|
|
|||
|
|
@ -32,7 +32,6 @@ import (
|
|||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/util/rand"
|
||||
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||
"k8s.io/apimachinery/pkg/util/version"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
"k8s.io/apiserver/pkg/apis/example"
|
||||
examplev1 "k8s.io/apiserver/pkg/apis/example/v1"
|
||||
|
|
@ -181,36 +180,30 @@ func TestListPaging(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestLists(t *testing.T) {
|
||||
for _, consistentRead := range []bool{true, false} {
|
||||
for _, listFromCacheSnapshot := range []bool{true, false} {
|
||||
t.Run(fmt.Sprintf("ConsistentListFromCache=%v,ListFromCacheSnapshot=%v", consistentRead, listFromCacheSnapshot), func(t *testing.T) {
|
||||
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.ListFromCacheSnapshot, listFromCacheSnapshot)
|
||||
if !consistentRead {
|
||||
featuregatetesting.SetFeatureGateEmulationVersionDuringTest(t, utilfeature.DefaultFeatureGate, version.MustParse("1.33"))
|
||||
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.ConsistentListFromCache, false)
|
||||
}
|
||||
t.Run("List", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
ctx, cacher, server, terminate := testSetupWithEtcdServer(t)
|
||||
t.Cleanup(terminate)
|
||||
storagetesting.RunTestList(ctx, t, cacher, compactStore(cacher, server.V3Client.Client), true, server.V3Client.Kubernetes.(*storagetesting.KubernetesRecorder))
|
||||
})
|
||||
|
||||
t.Run("ConsistentList", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
ctx, cacher, server, terminate := testSetupWithEtcdServer(t)
|
||||
t.Cleanup(terminate)
|
||||
storagetesting.RunTestConsistentList(ctx, t, cacher, increaseRVFunc(server.V3Client.Client), true, consistentRead, listFromCacheSnapshot)
|
||||
})
|
||||
|
||||
t.Run("GetListNonRecursive", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
ctx, cacher, server, terminate := testSetupWithEtcdServer(t)
|
||||
t.Cleanup(terminate)
|
||||
storagetesting.RunTestGetListNonRecursive(ctx, t, increaseRVFunc(server.V3Client.Client), cacher)
|
||||
})
|
||||
for _, listFromCacheSnapshot := range []bool{true, false} {
|
||||
t.Run(fmt.Sprintf("ListFromCacheSnapshot=%v", listFromCacheSnapshot), func(t *testing.T) {
|
||||
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.ListFromCacheSnapshot, listFromCacheSnapshot)
|
||||
t.Run("List", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
ctx, cacher, server, terminate := testSetupWithEtcdServer(t)
|
||||
t.Cleanup(terminate)
|
||||
storagetesting.RunTestList(ctx, t, cacher, compactStore(cacher, server.V3Client.Client), true, server.V3Client.Kubernetes.(*storagetesting.KubernetesRecorder))
|
||||
})
|
||||
}
|
||||
|
||||
t.Run("ConsistentList", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
ctx, cacher, server, terminate := testSetupWithEtcdServer(t)
|
||||
t.Cleanup(terminate)
|
||||
storagetesting.RunTestConsistentList(ctx, t, cacher, increaseRVFunc(server.V3Client.Client), true, true, listFromCacheSnapshot)
|
||||
})
|
||||
|
||||
t.Run("GetListNonRecursive", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
ctx, cacher, server, terminate := testSetupWithEtcdServer(t)
|
||||
t.Cleanup(terminate)
|
||||
storagetesting.RunTestGetListNonRecursive(ctx, t, increaseRVFunc(server.V3Client.Client), cacher)
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -592,13 +585,11 @@ func testSetupWithEtcdServer(t testing.TB, opts ...setupOption) (context.Context
|
|||
t.Fatalf("Failed to inject list errors: %v", err)
|
||||
}
|
||||
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.ResilientWatchCacheInitialization) {
|
||||
// The tests assume that Get/GetList/Watch calls shouldn't fail.
|
||||
// However, 429 error can now be returned if watchcache is under initialization.
|
||||
// To avoid rewriting all tests, we wait for watchcache to initialize.
|
||||
if err := cacher.Wait(ctx); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// The tests assume that Get/GetList/Watch calls shouldn't fail.
|
||||
// However, 429 error can now be returned if watchcache is under initialization.
|
||||
// To avoid rewriting all tests, we wait for watchcache to initialize.
|
||||
if err := cacher.Wait(ctx); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
delegator := NewCacheDelegator(cacher, wrappedStorage)
|
||||
terminate := func() {
|
||||
|
|
@ -613,11 +604,6 @@ func testSetupWithEtcdServer(t testing.TB, opts ...setupOption) (context.Context
|
|||
func testSetupWithEtcdAndCreateWrapper(t *testing.T, opts ...setupOption) (storage.Interface, tearDownFunc) {
|
||||
_, cacher, _, tearDown := testSetupWithEtcdServer(t, opts...)
|
||||
|
||||
if !utilfeature.DefaultFeatureGate.Enabled(features.ResilientWatchCacheInitialization) {
|
||||
if err := cacher.cacher.ready.wait(context.TODO()); err != nil {
|
||||
t.Fatalf("unexpected error waiting for the cache to be ready")
|
||||
}
|
||||
}
|
||||
return &createWrapper{CacheDelegator: cacher}, tearDown
|
||||
}
|
||||
|
||||
|
|
@ -647,35 +633,17 @@ func (c *createWrapper) Create(ctx context.Context, key string, obj, out runtime
|
|||
|
||||
func BenchmarkStoreCreateList(b *testing.B) {
|
||||
klog.SetLogger(logr.Discard())
|
||||
storeOptions := []struct {
|
||||
name string
|
||||
btreeEnabled bool
|
||||
}{
|
||||
{
|
||||
name: "Btree",
|
||||
btreeEnabled: true,
|
||||
},
|
||||
{
|
||||
name: "Map",
|
||||
btreeEnabled: false,
|
||||
},
|
||||
}
|
||||
for _, store := range storeOptions {
|
||||
b.Run(fmt.Sprintf("Store=%s", store.name), func(b *testing.B) {
|
||||
featuregatetesting.SetFeatureGateDuringTest(b, utilfeature.DefaultFeatureGate, features.BtreeWatchCache, store.btreeEnabled)
|
||||
for _, rvm := range []metav1.ResourceVersionMatch{metav1.ResourceVersionMatchNotOlderThan, metav1.ResourceVersionMatchExact} {
|
||||
b.Run(fmt.Sprintf("RV=%s", rvm), func(b *testing.B) {
|
||||
for _, useIndex := range []bool{true, false} {
|
||||
b.Run(fmt.Sprintf("Indexed=%v", useIndex), func(b *testing.B) {
|
||||
opts := []setupOption{}
|
||||
if useIndex {
|
||||
opts = append(opts, withNodeNameAndNamespaceIndex)
|
||||
}
|
||||
ctx, cacher, _, terminate := testSetupWithEtcdServer(b, opts...)
|
||||
b.Cleanup(terminate)
|
||||
storagetesting.RunBenchmarkStoreListCreate(ctx, b, cacher, rvm)
|
||||
})
|
||||
for _, rvm := range []metav1.ResourceVersionMatch{metav1.ResourceVersionMatchNotOlderThan, metav1.ResourceVersionMatchExact} {
|
||||
b.Run(fmt.Sprintf("RV=%s", rvm), func(b *testing.B) {
|
||||
for _, useIndex := range []bool{true, false} {
|
||||
b.Run(fmt.Sprintf("Indexed=%v", useIndex), func(b *testing.B) {
|
||||
opts := []setupOption{}
|
||||
if useIndex {
|
||||
opts = append(opts, withNodeNameAndNamespaceIndex)
|
||||
}
|
||||
ctx, cacher, _, terminate := testSetupWithEtcdServer(b, opts...)
|
||||
b.Cleanup(terminate)
|
||||
storagetesting.RunBenchmarkStoreListCreate(ctx, b, cacher, rvm)
|
||||
})
|
||||
}
|
||||
})
|
||||
|
|
@ -709,36 +677,18 @@ func BenchmarkStoreList(b *testing.B) {
|
|||
for _, dims := range dimensions {
|
||||
b.Run(fmt.Sprintf("Namespaces=%d/Pods=%d/Nodes=%d", dims.namespaceCount, dims.namespaceCount*dims.podPerNamespaceCount, dims.nodeCount), func(b *testing.B) {
|
||||
data := storagetesting.PrepareBenchchmarkData(dims.namespaceCount, dims.podPerNamespaceCount, dims.nodeCount)
|
||||
storeOptions := []struct {
|
||||
name string
|
||||
btreeEnabled bool
|
||||
}{
|
||||
{
|
||||
name: "Btree",
|
||||
btreeEnabled: true,
|
||||
},
|
||||
{
|
||||
name: "Map",
|
||||
btreeEnabled: false,
|
||||
},
|
||||
ctx, cacher, _, terminate := testSetupWithEtcdServer(b, withNodeNameAndNamespaceIndex)
|
||||
b.Cleanup(terminate)
|
||||
var out example.Pod
|
||||
for _, pod := range data.Pods {
|
||||
err := cacher.Create(ctx, computePodKey(pod), pod, &out, 0)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
for _, store := range storeOptions {
|
||||
b.Run(fmt.Sprintf("Store=%s", store.name), func(b *testing.B) {
|
||||
featuregatetesting.SetFeatureGateDuringTest(b, utilfeature.DefaultFeatureGate, features.BtreeWatchCache, store.btreeEnabled)
|
||||
ctx, cacher, _, terminate := testSetupWithEtcdServer(b, withNodeNameAndNamespaceIndex)
|
||||
b.Cleanup(terminate)
|
||||
var out example.Pod
|
||||
for _, pod := range data.Pods {
|
||||
err := cacher.Create(ctx, computePodKey(pod), pod, &out, 0)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
for _, useIndex := range []bool{true, false} {
|
||||
b.Run(fmt.Sprintf("Indexed=%v", useIndex), func(b *testing.B) {
|
||||
storagetesting.RunBenchmarkStoreList(ctx, b, cacher, data, useIndex)
|
||||
})
|
||||
}
|
||||
for _, useIndex := range []bool{true, false} {
|
||||
b.Run(fmt.Sprintf("Indexed=%v", useIndex), func(b *testing.B) {
|
||||
storagetesting.RunBenchmarkStoreList(ctx, b, cacher, data, useIndex)
|
||||
})
|
||||
}
|
||||
})
|
||||
|
|
|
|||
|
|
@ -43,7 +43,6 @@ import (
|
|||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/sharding"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/util/version"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
"k8s.io/apimachinery/pkg/watch"
|
||||
"k8s.io/apiserver/pkg/apis/example"
|
||||
|
|
@ -106,13 +105,11 @@ func newTestCacher(s storage.Interface) (*Cacher, storage.Versioner, error) {
|
|||
return nil, versioner, err
|
||||
}
|
||||
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.ResilientWatchCacheInitialization) {
|
||||
// The tests assume that Get/GetList/Watch calls shouldn't fail.
|
||||
// However, 429 error can now be returned if watchcache is under initialization.
|
||||
// To avoid rewriting all tests, we wait for watcache to initialize.
|
||||
if err := cacher.Wait(context.Background()); err != nil {
|
||||
return nil, storage.APIObjectVersioner{}, err
|
||||
}
|
||||
// The tests assume that Get/GetList/Watch calls shouldn't fail.
|
||||
// However, 429 error can now be returned if watchcache is under initialization.
|
||||
// To avoid rewriting all tests, we wait for watcache to initialize.
|
||||
if err := cacher.Wait(context.Background()); err != nil {
|
||||
return nil, storage.APIObjectVersioner{}, err
|
||||
}
|
||||
return cacher, versioner, nil
|
||||
}
|
||||
|
|
@ -296,67 +293,46 @@ func TestShouldDelegateList(t *testing.T) {
|
|||
snapshotAvailableOverrides[opts{Recursive: true, ResourceVersion: oldRV, ResourceVersionMatch: metav1.ResourceVersionMatchExact}] = false
|
||||
snapshotAvailableOverrides[opts{Recursive: true, ResourceVersion: oldRV, ResourceVersionMatch: metav1.ResourceVersionMatchExact, Limit: 100}] = false
|
||||
|
||||
t.Run("ConsistentListFromCache=false", func(t *testing.T) {
|
||||
featuregatetesting.SetFeatureGateEmulationVersionDuringTest(t, utilfeature.DefaultFeatureGate, version.MustParse("1.33"))
|
||||
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.ConsistentListFromCache, false)
|
||||
t.Run("ListFromCacheSnapshot=false", func(t *testing.T) {
|
||||
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.ListFromCacheSnapshot, false)
|
||||
runTestCases(t, false, testCases)
|
||||
})
|
||||
// TODO(p0lyn0mial): the following tests assume that etcdfeature.DefaultFeatureSupportChecker.Supports(storage.RequestWatchProgress)
|
||||
// evaluates to true. Otherwise the cache will be bypassed and the test will fail.
|
||||
//
|
||||
// If you were to run only TestGetListCacheBypass you would see that the test fail.
|
||||
// However in CI all test are run and there must be a test(s) that properly
|
||||
// initialize the storage layer so that the mentioned method evaluates to true
|
||||
forceRequestWatchProgressSupport(t)
|
||||
|
||||
t.Run("ListFromCacheSnapshot=true", func(t *testing.T) {
|
||||
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.ListFromCacheSnapshot, true)
|
||||
t.Run("SnapshotAvailable=false", func(t *testing.T) {
|
||||
runTestCases(t, false, testCases, listFromSnapshotEnabledOverrides)
|
||||
})
|
||||
t.Run("SnapshotAvailable=true", func(t *testing.T) {
|
||||
runTestCases(t, true, testCases, listFromSnapshotEnabledOverrides, snapshotAvailableOverrides)
|
||||
})
|
||||
})
|
||||
consistentListFromCacheOverrides := map[opts]bool{}
|
||||
for _, recursive := range []bool{true, false} {
|
||||
consistentListFromCacheOverrides[opts{Recursive: recursive}] = false
|
||||
consistentListFromCacheOverrides[opts{Limit: 100, Recursive: recursive}] = false
|
||||
}
|
||||
|
||||
t.Run("ListFromCacheSnapshot=false", func(t *testing.T) {
|
||||
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.ListFromCacheSnapshot, false)
|
||||
runTestCases(t, false, testCases, consistentListFromCacheOverrides)
|
||||
})
|
||||
t.Run("ConsistentListFromCache=true", func(t *testing.T) {
|
||||
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.ConsistentListFromCache, true)
|
||||
// TODO(p0lyn0mial): the following tests assume that etcdfeature.DefaultFeatureSupportChecker.Supports(storage.RequestWatchProgress)
|
||||
// evaluates to true. Otherwise the cache will be bypassed and the test will fail.
|
||||
//
|
||||
// If you were to run only TestGetListCacheBypass you would see that the test fail.
|
||||
// However in CI all test are run and there must be a test(s) that properly
|
||||
// initialize the storage layer so that the mentioned method evaluates to true
|
||||
forceRequestWatchProgressSupport(t)
|
||||
t.Run("ListFromCacheSnapshot=true", func(t *testing.T) {
|
||||
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.ListFromCacheSnapshot, true)
|
||||
|
||||
consistentListFromCacheOverrides := map[opts]bool{}
|
||||
for _, recursive := range []bool{true, false} {
|
||||
consistentListFromCacheOverrides[opts{Recursive: recursive}] = false
|
||||
consistentListFromCacheOverrides[opts{Limit: 100, Recursive: recursive}] = false
|
||||
}
|
||||
|
||||
t.Run("ListFromCacheSnapshot=false", func(t *testing.T) {
|
||||
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.ListFromCacheSnapshot, false)
|
||||
runTestCases(t, false, testCases, consistentListFromCacheOverrides)
|
||||
consistentReadWithSnapshotOverrides := map[opts]bool{}
|
||||
// Continues with negative RV are same as consistent read.
|
||||
consistentReadWithSnapshotOverrides[opts{Recursive: true, Limit: 0, Continue: continueOnNegativeRV}] = false
|
||||
consistentReadWithSnapshotOverrides[opts{Recursive: true, Limit: 100, Continue: continueOnNegativeRV}] = false
|
||||
consistentReadWithSnapshotOverrides[opts{Recursive: true, ResourceVersion: "0", Limit: 0, Continue: continueOnNegativeRV}] = false
|
||||
consistentReadWithSnapshotOverrides[opts{Recursive: true, ResourceVersion: "0", Limit: 100, Continue: continueOnNegativeRV}] = false
|
||||
// Exact on RV not yet observed by cache
|
||||
consistentReadWithSnapshotOverrides[opts{Recursive: true, ResourceVersion: etcdRV, Limit: 100}] = false
|
||||
consistentReadWithSnapshotOverrides[opts{Recursive: true, ResourceVersion: etcdRV, ResourceVersionMatch: metav1.ResourceVersionMatchExact}] = false
|
||||
consistentReadWithSnapshotOverrides[opts{Recursive: true, ResourceVersion: etcdRV, ResourceVersionMatch: metav1.ResourceVersionMatchExact, Limit: 100}] = false
|
||||
consistentReadWithSnapshotOverrides[opts{Recursive: true, Continue: continueOnEtcdRV}] = false
|
||||
consistentReadWithSnapshotOverrides[opts{Recursive: true, ResourceVersion: "0", Continue: continueOnEtcdRV}] = false
|
||||
consistentReadWithSnapshotOverrides[opts{Recursive: true, Continue: continueOnEtcdRV, Limit: 100}] = false
|
||||
consistentReadWithSnapshotOverrides[opts{Recursive: true, ResourceVersion: "0", Continue: continueOnEtcdRV, Limit: 100}] = false
|
||||
t.Run("SnapshotAvailable=false", func(t *testing.T) {
|
||||
runTestCases(t, false, testCases, listFromSnapshotEnabledOverrides, consistentListFromCacheOverrides, consistentReadWithSnapshotOverrides)
|
||||
})
|
||||
t.Run("ListFromCacheSnapshot=true", func(t *testing.T) {
|
||||
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.ListFromCacheSnapshot, true)
|
||||
|
||||
consistentReadWithSnapshotOverrides := map[opts]bool{}
|
||||
// Continues with negative RV are same as consistent read.
|
||||
consistentReadWithSnapshotOverrides[opts{Recursive: true, Limit: 0, Continue: continueOnNegativeRV}] = false
|
||||
consistentReadWithSnapshotOverrides[opts{Recursive: true, Limit: 100, Continue: continueOnNegativeRV}] = false
|
||||
consistentReadWithSnapshotOverrides[opts{Recursive: true, ResourceVersion: "0", Limit: 0, Continue: continueOnNegativeRV}] = false
|
||||
consistentReadWithSnapshotOverrides[opts{Recursive: true, ResourceVersion: "0", Limit: 100, Continue: continueOnNegativeRV}] = false
|
||||
// Exact on RV not yet observed by cache
|
||||
consistentReadWithSnapshotOverrides[opts{Recursive: true, ResourceVersion: etcdRV, Limit: 100}] = false
|
||||
consistentReadWithSnapshotOverrides[opts{Recursive: true, ResourceVersion: etcdRV, ResourceVersionMatch: metav1.ResourceVersionMatchExact}] = false
|
||||
consistentReadWithSnapshotOverrides[opts{Recursive: true, ResourceVersion: etcdRV, ResourceVersionMatch: metav1.ResourceVersionMatchExact, Limit: 100}] = false
|
||||
consistentReadWithSnapshotOverrides[opts{Recursive: true, Continue: continueOnEtcdRV}] = false
|
||||
consistentReadWithSnapshotOverrides[opts{Recursive: true, ResourceVersion: "0", Continue: continueOnEtcdRV}] = false
|
||||
consistentReadWithSnapshotOverrides[opts{Recursive: true, Continue: continueOnEtcdRV, Limit: 100}] = false
|
||||
consistentReadWithSnapshotOverrides[opts{Recursive: true, ResourceVersion: "0", Continue: continueOnEtcdRV, Limit: 100}] = false
|
||||
t.Run("SnapshotAvailable=false", func(t *testing.T) {
|
||||
runTestCases(t, false, testCases, listFromSnapshotEnabledOverrides, consistentListFromCacheOverrides, consistentReadWithSnapshotOverrides)
|
||||
})
|
||||
t.Run("SnapshotAvailable=true", func(t *testing.T) {
|
||||
runTestCases(t, true, testCases, listFromSnapshotEnabledOverrides, consistentListFromCacheOverrides, consistentReadWithSnapshotOverrides, snapshotAvailableOverrides)
|
||||
})
|
||||
t.Run("SnapshotAvailable=true", func(t *testing.T) {
|
||||
runTestCases(t, true, testCases, listFromSnapshotEnabledOverrides, consistentListFromCacheOverrides, consistentReadWithSnapshotOverrides, snapshotAvailableOverrides)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
|
@ -371,12 +347,11 @@ func mustAtoi(s string) int {
|
|||
|
||||
func TestConsistentReadFallback(t *testing.T) {
|
||||
tcs := []struct {
|
||||
name string
|
||||
consistentReadsEnabled bool
|
||||
skipStorageFallback bool
|
||||
watchCacheRV string
|
||||
storageRV string
|
||||
fallbackError bool
|
||||
name string
|
||||
skipStorageFallback bool
|
||||
watchCacheRV string
|
||||
storageRV string
|
||||
fallbackError bool
|
||||
|
||||
expectError bool
|
||||
expectTooManyRequests bool
|
||||
|
|
@ -387,7 +362,6 @@ func TestConsistentReadFallback(t *testing.T) {
|
|||
}{
|
||||
{
|
||||
name: "Success",
|
||||
consistentReadsEnabled: true,
|
||||
watchCacheRV: "42",
|
||||
storageRV: "42",
|
||||
expectRV: "42",
|
||||
|
|
@ -400,7 +374,6 @@ apiserver_watch_cache_consistent_read_total{fallback="false", group="", resource
|
|||
},
|
||||
{
|
||||
name: "Fallback",
|
||||
consistentReadsEnabled: true,
|
||||
watchCacheRV: "2",
|
||||
storageRV: "42",
|
||||
expectRV: "42",
|
||||
|
|
@ -414,7 +387,6 @@ apiserver_watch_cache_consistent_read_total{fallback="true", group="", resource=
|
|||
},
|
||||
{
|
||||
name: "Fallback Failure",
|
||||
consistentReadsEnabled: true,
|
||||
watchCacheRV: "2",
|
||||
storageRV: "42",
|
||||
fallbackError: true,
|
||||
|
|
@ -429,7 +401,6 @@ apiserver_watch_cache_consistent_read_total{fallback="true", group="", resource=
|
|||
},
|
||||
{
|
||||
name: "Skip Storage Fallback",
|
||||
consistentReadsEnabled: true,
|
||||
skipStorageFallback: true,
|
||||
watchCacheRV: "2",
|
||||
storageRV: "42",
|
||||
|
|
@ -443,23 +414,10 @@ apiserver_watch_cache_consistent_read_total{fallback="true", group="", resource=
|
|||
apiserver_watch_cache_consistent_read_total{fallback="skipped", group="", resource="pods", success="false"} 1
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "Disabled",
|
||||
watchCacheRV: "2",
|
||||
storageRV: "42",
|
||||
expectRV: "42",
|
||||
expectRequestsToStorage: 1,
|
||||
expectMetric: ``,
|
||||
},
|
||||
}
|
||||
for _, tc := range tcs {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
if tc.consistentReadsEnabled {
|
||||
forceRequestWatchProgressSupport(t)
|
||||
} else {
|
||||
featuregatetesting.SetFeatureGateEmulationVersionDuringTest(t, utilfeature.DefaultFeatureGate, version.MustParse("1.33"))
|
||||
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.ConsistentListFromCache, false)
|
||||
}
|
||||
forceRequestWatchProgressSupport(t)
|
||||
if tc.skipStorageFallback {
|
||||
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.ConsistentListFromCacheSkipTimeoutFallback, true)
|
||||
}
|
||||
|
|
@ -646,53 +604,10 @@ func TestMatchExactResourceVersionFallback(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestGetListNonRecursiveCacheBypass(t *testing.T) {
|
||||
featuregatetesting.SetFeatureGateEmulationVersionDuringTest(t, utilfeature.DefaultFeatureGate, version.MustParse("1.33"))
|
||||
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.ConsistentListFromCache, false)
|
||||
backingStorage := &cachertesting.MockStorage{}
|
||||
cacher, _, err := newTestCacher(backingStorage)
|
||||
if err != nil {
|
||||
t.Fatalf("Couldn't create cacher: %v", err)
|
||||
}
|
||||
defer cacher.Stop()
|
||||
delegator := NewCacheDelegator(cacher, backingStorage)
|
||||
defer delegator.Stop()
|
||||
|
||||
pred := storage.SelectionPredicate{
|
||||
Limit: 500,
|
||||
}
|
||||
result := &example.PodList{}
|
||||
|
||||
if !utilfeature.DefaultFeatureGate.Enabled(features.ResilientWatchCacheInitialization) {
|
||||
if err := cacher.ready.wait(context.Background()); err != nil {
|
||||
t.Fatalf("unexpected error waiting for the cache to be ready")
|
||||
}
|
||||
}
|
||||
|
||||
// Inject error to underlying layer and check if cacher is not bypassed.
|
||||
backingStorage.InjectGetListError(errDummy)
|
||||
err = delegator.GetList(context.TODO(), "/pods/ns", storage.ListOptions{
|
||||
ResourceVersion: "0",
|
||||
Predicate: pred,
|
||||
}, result)
|
||||
if err != nil {
|
||||
t.Errorf("GetList with Limit and RV=0 should be served from cache: %v", err)
|
||||
}
|
||||
|
||||
err = delegator.GetList(context.TODO(), "/pods/ns", storage.ListOptions{
|
||||
ResourceVersion: "",
|
||||
Predicate: pred,
|
||||
}, result)
|
||||
if !errors.Is(err, errDummy) {
|
||||
t.Errorf("GetList with Limit without RV=0 should bypass cacher: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetListNonRecursiveCacheWithConsistentListFromCache(t *testing.T) {
|
||||
// Set feature gates once at the beginning since we only care about ConsistentListFromCache=true and ListFromCacheSnapshot=false
|
||||
// Set feature gates once at the beginning since we only care about ListFromCacheSnapshot=false
|
||||
featuregatetesting.SetFeatureGatesDuringTest(t, utilfeature.DefaultFeatureGate, featuregatetesting.FeatureOverrides{
|
||||
features.ConsistentListFromCache: true,
|
||||
features.ListFromCacheSnapshot: false,
|
||||
features.ListFromCacheSnapshot: false,
|
||||
})
|
||||
forceRequestWatchProgressSupport(t)
|
||||
|
||||
|
|
@ -821,12 +736,6 @@ func TestGetCacheBypass(t *testing.T) {
|
|||
|
||||
result := &example.Pod{}
|
||||
|
||||
if !utilfeature.DefaultFeatureGate.Enabled(features.ResilientWatchCacheInitialization) {
|
||||
if err := cacher.ready.wait(context.Background()); err != nil {
|
||||
t.Fatalf("unexpected error waiting for the cache to be ready")
|
||||
}
|
||||
}
|
||||
|
||||
// Inject error to underlying layer and check if cacher is not bypassed.
|
||||
backingStorage.InjectGetListError(errDummy)
|
||||
err = delegator.Get(context.TODO(), "/pods/ns/pod-0", storage.GetOptions{
|
||||
|
|
@ -856,12 +765,6 @@ func TestWatchCacheBypass(t *testing.T) {
|
|||
delegator := NewCacheDelegator(cacher, backingStorage)
|
||||
defer delegator.Stop()
|
||||
|
||||
if !utilfeature.DefaultFeatureGate.Enabled(features.ResilientWatchCacheInitialization) {
|
||||
if err := cacher.ready.wait(context.Background()); err != nil {
|
||||
t.Fatalf("unexpected error waiting for the cache to be ready")
|
||||
}
|
||||
}
|
||||
|
||||
_, err = delegator.Watch(context.TODO(), "/pods/ns", storage.ListOptions{
|
||||
ResourceVersion: "0",
|
||||
Predicate: storage.Everything,
|
||||
|
|
@ -879,46 +782,6 @@ func TestWatchCacheBypass(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestTooManyRequestsNotReturned(t *testing.T) {
|
||||
// Ensure that with ResilientWatchCacheInitialization feature disabled, we don't return 429
|
||||
// errors when watchcache is not initialized.
|
||||
featuregatetesting.SetFeatureGateEmulationVersionDuringTest(t, utilfeature.DefaultFeatureGate, version.MustParse("1.33"))
|
||||
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.ResilientWatchCacheInitialization, false)
|
||||
|
||||
dummyErr := fmt.Errorf("dummy")
|
||||
backingStorage := &cachertesting.MockStorage{GetListErr: dummyErr}
|
||||
cacher, _, err := newTestCacherWithoutSyncing(backingStorage, clock.RealClock{})
|
||||
if err != nil {
|
||||
t.Fatalf("Couldn't create cacher: %v", err)
|
||||
}
|
||||
defer cacher.Stop()
|
||||
delegator := NewCacheDelegator(cacher, backingStorage)
|
||||
defer delegator.Stop()
|
||||
|
||||
opts := storage.ListOptions{
|
||||
ResourceVersion: "0",
|
||||
Predicate: storage.Everything,
|
||||
}
|
||||
|
||||
// Cancel the request so that it doesn't hang forever.
|
||||
listCtx, listCancel := context.WithTimeout(context.Background(), 250*time.Millisecond)
|
||||
defer listCancel()
|
||||
|
||||
result := &example.PodList{}
|
||||
err = delegator.GetList(listCtx, "/pods/ns", opts, result)
|
||||
if err != nil && apierrors.IsTooManyRequests(err) {
|
||||
t.Errorf("Unexpected 429 error without ResilientWatchCacheInitialization feature for List")
|
||||
}
|
||||
|
||||
watchCtx, watchCancel := context.WithTimeout(context.Background(), 250*time.Millisecond)
|
||||
defer watchCancel()
|
||||
|
||||
_, err = delegator.Watch(watchCtx, "/pods/ns", opts)
|
||||
if err != nil && apierrors.IsTooManyRequests(err) {
|
||||
t.Errorf("Unexpected 429 error without ResilientWatchCacheInitialization feature for Watch")
|
||||
}
|
||||
}
|
||||
|
||||
func TestEmptyWatchEventCache(t *testing.T) {
|
||||
server, etcdStorage := newEtcdTestStorage(t, etcd3testing.PathPrefix())
|
||||
defer server.Terminate(t)
|
||||
|
|
@ -1033,14 +896,8 @@ func TestWatchNotHangingOnStartupFailure(t *testing.T) {
|
|||
// Ensure that it terminates when its context is cancelled
|
||||
// (e.g. the request is terminated for whatever reason).
|
||||
_, err = cacher.Watch(ctx, "/pods/ns", storage.ListOptions{ResourceVersion: "0"})
|
||||
if !utilfeature.DefaultFeatureGate.Enabled(features.ResilientWatchCacheInitialization) {
|
||||
if err == nil || err.Error() != apierrors.NewServiceUnavailable(context.Canceled.Error()).Error() {
|
||||
t.Errorf("Unexpected error: %#v", err)
|
||||
}
|
||||
} else {
|
||||
if err == nil || !strings.Contains(err.Error(), "storage is (re)initializing") {
|
||||
t.Errorf("Unexpected error: %#v", err)
|
||||
}
|
||||
if err == nil || !strings.Contains(err.Error(), "storage is (re)initializing") {
|
||||
t.Errorf("Unexpected error: %#v", err)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1052,12 +909,6 @@ func TestWatcherNotGoingBackInTime(t *testing.T) {
|
|||
}
|
||||
defer cacher.Stop()
|
||||
|
||||
if !utilfeature.DefaultFeatureGate.Enabled(features.ResilientWatchCacheInitialization) {
|
||||
if err := cacher.ready.wait(context.Background()); err != nil {
|
||||
t.Fatalf("unexpected error waiting for the cache to be ready")
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure there is some budget for slowing down processing.
|
||||
cacher.dispatchTimeoutBudget.returnUnused(100 * time.Millisecond)
|
||||
|
||||
|
|
@ -1141,12 +992,6 @@ func TestCacherDontAcceptRequestsStopped(t *testing.T) {
|
|||
delegator := NewCacheDelegator(cacher, backingStorage)
|
||||
defer delegator.Stop()
|
||||
|
||||
if !utilfeature.DefaultFeatureGate.Enabled(features.ResilientWatchCacheInitialization) {
|
||||
if err := cacher.ready.wait(context.Background()); err != nil {
|
||||
t.Fatalf("unexpected error waiting for the cache to be ready")
|
||||
}
|
||||
}
|
||||
|
||||
w, err := delegator.Watch(context.Background(), "/pods/ns", storage.ListOptions{ResourceVersion: "0", Predicate: storage.Everything})
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create watch: %v", err)
|
||||
|
|
@ -1177,14 +1022,8 @@ func TestCacherDontAcceptRequestsStopped(t *testing.T) {
|
|||
IgnoreNotFound: true,
|
||||
ResourceVersion: "1",
|
||||
}, result)
|
||||
if !utilfeature.DefaultFeatureGate.Enabled(features.ResilientWatchCacheInitialization) {
|
||||
if err == nil {
|
||||
t.Fatalf("Success to create Get: %v", err)
|
||||
}
|
||||
} else {
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to get object: %v:", err)
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to get object: %v:", err)
|
||||
}
|
||||
|
||||
listResult := &example.PodList{}
|
||||
|
|
@ -1195,14 +1034,8 @@ func TestCacherDontAcceptRequestsStopped(t *testing.T) {
|
|||
Limit: 500,
|
||||
},
|
||||
}, listResult)
|
||||
if !utilfeature.DefaultFeatureGate.Enabled(features.ResilientWatchCacheInitialization) {
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create GetList: %v", err)
|
||||
}
|
||||
} else {
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to list objects: %v", err)
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to list objects: %v", err)
|
||||
}
|
||||
|
||||
select {
|
||||
|
|
@ -1329,12 +1162,6 @@ func TestCacherNoLeakWithMultipleWatchers(t *testing.T) {
|
|||
}
|
||||
defer cacher.Stop()
|
||||
|
||||
if !utilfeature.DefaultFeatureGate.Enabled(features.ResilientWatchCacheInitialization) {
|
||||
if err := cacher.ready.wait(context.Background()); err != nil {
|
||||
t.Fatalf("unexpected error waiting for the cache to be ready")
|
||||
}
|
||||
}
|
||||
|
||||
pred := storage.Everything
|
||||
pred.AllowWatchBookmarks = true
|
||||
|
||||
|
|
@ -1410,11 +1237,6 @@ func testCacherSendBookmarkEvents(t *testing.T, allowWatchBookmarks, expectedBoo
|
|||
}
|
||||
defer cacher.Stop()
|
||||
|
||||
if !utilfeature.DefaultFeatureGate.Enabled(features.ResilientWatchCacheInitialization) {
|
||||
if err := cacher.ready.wait(context.Background()); err != nil {
|
||||
t.Fatalf("unexpected error waiting for the cache to be ready")
|
||||
}
|
||||
}
|
||||
pred := storage.Everything
|
||||
pred.AllowWatchBookmarks = allowWatchBookmarks
|
||||
|
||||
|
|
@ -1510,12 +1332,6 @@ func TestInitialEventsEndBookmark(t *testing.T) {
|
|||
}
|
||||
defer cacher.Stop()
|
||||
|
||||
if !utilfeature.DefaultFeatureGate.Enabled(features.ResilientWatchCacheInitialization) {
|
||||
if err := cacher.ready.wait(context.Background()); err != nil {
|
||||
t.Fatalf("unexpected error waiting for the cache to be ready")
|
||||
}
|
||||
}
|
||||
|
||||
makePod := func(index uint64) *example.Pod {
|
||||
return &example.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
|
|
@ -1611,11 +1427,6 @@ func TestCacherSendsMultipleWatchBookmarks(t *testing.T) {
|
|||
// resolution how frequency we recompute.
|
||||
cacher.bookmarkWatchers.bookmarkFrequency = time.Second
|
||||
|
||||
if !utilfeature.DefaultFeatureGate.Enabled(features.ResilientWatchCacheInitialization) {
|
||||
if err := cacher.ready.wait(context.Background()); err != nil {
|
||||
t.Fatalf("unexpected error waiting for the cache to be ready")
|
||||
}
|
||||
}
|
||||
pred := storage.Everything
|
||||
pred.AllowWatchBookmarks = true
|
||||
|
||||
|
|
@ -1682,12 +1493,6 @@ func TestDispatchingBookmarkEventsWithConcurrentStop(t *testing.T) {
|
|||
}
|
||||
defer cacher.Stop()
|
||||
|
||||
if !utilfeature.DefaultFeatureGate.Enabled(features.ResilientWatchCacheInitialization) {
|
||||
if err := cacher.ready.wait(context.Background()); err != nil {
|
||||
t.Fatalf("unexpected error waiting for the cache to be ready")
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure there is some budget for slowing down processing.
|
||||
cacher.dispatchTimeoutBudget.returnUnused(100 * time.Millisecond)
|
||||
|
||||
|
|
@ -1761,12 +1566,6 @@ func TestBookmarksOnResourceVersionUpdates(t *testing.T) {
|
|||
// Ensure that bookmarks are sent more frequently than every 1m.
|
||||
cacher.bookmarkWatchers = newTimeBucketWatchers(clock.RealClock{}, 2*time.Second)
|
||||
|
||||
if !utilfeature.DefaultFeatureGate.Enabled(features.ResilientWatchCacheInitialization) {
|
||||
if err := cacher.ready.wait(context.Background()); err != nil {
|
||||
t.Fatalf("unexpected error waiting for the cache to be ready")
|
||||
}
|
||||
}
|
||||
|
||||
makePod := func(i int) *examplev1.Pod {
|
||||
return &examplev1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
|
|
@ -1840,12 +1639,6 @@ func TestStartingResourceVersion(t *testing.T) {
|
|||
}
|
||||
defer cacher.Stop()
|
||||
|
||||
if !utilfeature.DefaultFeatureGate.Enabled(features.ResilientWatchCacheInitialization) {
|
||||
if err := cacher.ready.wait(context.Background()); err != nil {
|
||||
t.Fatalf("unexpected error waiting for the cache to be ready")
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure there is some budget for slowing down processing.
|
||||
// We use the fakeTimeBudget to prevent this test from flaking under
|
||||
// the following conditions:
|
||||
|
|
@ -1921,12 +1714,6 @@ func TestDispatchEventWillNotBeBlockedByTimedOutWatcher(t *testing.T) {
|
|||
}
|
||||
defer cacher.Stop()
|
||||
|
||||
if !utilfeature.DefaultFeatureGate.Enabled(features.ResilientWatchCacheInitialization) {
|
||||
if err := cacher.ready.wait(context.Background()); err != nil {
|
||||
t.Fatalf("unexpected error waiting for the cache to be ready")
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure there is some budget for slowing down processing.
|
||||
// We use the fakeTimeBudget to prevent this test from flaking under
|
||||
// the following conditions:
|
||||
|
|
@ -2064,12 +1851,6 @@ func TestCachingDeleteEvents(t *testing.T) {
|
|||
}
|
||||
defer cacher.Stop()
|
||||
|
||||
if !utilfeature.DefaultFeatureGate.Enabled(features.ResilientWatchCacheInitialization) {
|
||||
if err := cacher.ready.wait(context.Background()); err != nil {
|
||||
t.Fatalf("unexpected error waiting for the cache to be ready")
|
||||
}
|
||||
}
|
||||
|
||||
fooPredicate := storage.SelectionPredicate{
|
||||
Label: labels.SelectorFromSet(map[string]string{"foo": "true"}),
|
||||
Field: fields.Everything(),
|
||||
|
|
@ -2147,12 +1928,6 @@ func testCachingObjects(t *testing.T, watchersCount int) {
|
|||
}
|
||||
defer cacher.Stop()
|
||||
|
||||
if !utilfeature.DefaultFeatureGate.Enabled(features.ResilientWatchCacheInitialization) {
|
||||
if err := cacher.ready.wait(context.Background()); err != nil {
|
||||
t.Fatalf("unexpected error waiting for the cache to be ready")
|
||||
}
|
||||
}
|
||||
|
||||
dispatchedEvents := []*watchCacheEvent{}
|
||||
cacher.watchCache.eventHandler = func(event *watchCacheEvent) {
|
||||
dispatchedEvents = append(dispatchedEvents, event)
|
||||
|
|
@ -2244,12 +2019,6 @@ func TestCacheIntervalInvalidationStopsWatch(t *testing.T) {
|
|||
}
|
||||
defer cacher.Stop()
|
||||
|
||||
if !utilfeature.DefaultFeatureGate.Enabled(features.ResilientWatchCacheInitialization) {
|
||||
if err := cacher.ready.wait(context.Background()); err != nil {
|
||||
t.Fatalf("unexpected error waiting for the cache to be ready")
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure there is enough budget for slow processing since
|
||||
// the entire watch cache is going to be served through the
|
||||
// interval and events won't be popped from the cacheWatcher's
|
||||
|
|
@ -2324,8 +2093,7 @@ func TestCacheIntervalInvalidationStopsWatch(t *testing.T) {
|
|||
|
||||
func TestWaitUntilWatchCacheFreshAndForceAllEvents(t *testing.T) {
|
||||
featuregatetesting.SetFeatureGatesDuringTest(t, utilfeature.DefaultFeatureGate, featuregatetesting.FeatureOverrides{
|
||||
features.WatchList: true,
|
||||
features.ConsistentListFromCache: true,
|
||||
features.WatchList: true,
|
||||
})
|
||||
forceRequestWatchProgressSupport(t)
|
||||
|
||||
|
|
@ -2455,12 +2223,6 @@ func TestWaitUntilWatchCacheFreshAndForceAllEvents(t *testing.T) {
|
|||
}
|
||||
defer cacher.Stop()
|
||||
|
||||
if !utilfeature.DefaultFeatureGate.Enabled(features.ResilientWatchCacheInitialization) {
|
||||
if err := cacher.ready.wait(context.Background()); err != nil {
|
||||
t.Fatalf("unexpected error waiting for the cache to be ready")
|
||||
}
|
||||
}
|
||||
|
||||
w, err := cacher.Watch(context.Background(), "/pods/ns", scenario.opts)
|
||||
require.NoError(t, err, "failed to create watch: %v")
|
||||
defer w.Stop()
|
||||
|
|
@ -2619,12 +2381,6 @@ func TestWatchListIsSynchronisedWhenNoEventsFromStoreReceived(t *testing.T) {
|
|||
require.NoError(t, err, "failed to create cacher")
|
||||
defer cacher.Stop()
|
||||
|
||||
if !utilfeature.DefaultFeatureGate.Enabled(features.ResilientWatchCacheInitialization) {
|
||||
if err := cacher.ready.wait(context.Background()); err != nil {
|
||||
t.Fatalf("unexpected error waiting for the cache to be ready")
|
||||
}
|
||||
}
|
||||
|
||||
pred := storage.Everything
|
||||
pred.AllowWatchBookmarks = true
|
||||
opts := storage.ListOptions{
|
||||
|
|
@ -2651,12 +2407,6 @@ func TestForgetWatcher(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
defer cacher.Stop()
|
||||
|
||||
if !utilfeature.DefaultFeatureGate.Enabled(features.ResilientWatchCacheInitialization) {
|
||||
if err := cacher.ready.wait(context.Background()); err != nil {
|
||||
t.Fatalf("unexpected error waiting for the cache to be ready")
|
||||
}
|
||||
}
|
||||
|
||||
assertCacherInternalState := func(expectedWatchersCounter, expectedValueWatchersCounter int) {
|
||||
cacher.Lock()
|
||||
defer cacher.Unlock()
|
||||
|
|
@ -3045,12 +2795,6 @@ func TestGetBookmarkAfterResourceVersionLockedFunc(t *testing.T) {
|
|||
|
||||
defer cacher.Stop()
|
||||
|
||||
if !utilfeature.DefaultFeatureGate.Enabled(features.ResilientWatchCacheInitialization) {
|
||||
if err := cacher.ready.wait(context.Background()); err != nil {
|
||||
t.Fatalf("unexpected error waiting for the cache to be ready")
|
||||
}
|
||||
}
|
||||
|
||||
cacher.watchCache.UpdateResourceVersion(fmt.Sprintf("%d", scenario.watchCacheResourceVersion))
|
||||
parsedResourceVersion := 0
|
||||
if len(scenario.opts.ResourceVersion) > 0 {
|
||||
|
|
@ -3291,9 +3035,6 @@ func TestListIndexer(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestRetryAfterForUnreadyCache(t *testing.T) {
|
||||
if !utilfeature.DefaultFeatureGate.Enabled(features.ResilientWatchCacheInitialization) {
|
||||
t.Skipf("the test requires %v to be enabled", features.ResilientWatchCacheInitialization)
|
||||
}
|
||||
backingStorage := &cachertesting.MockStorage{}
|
||||
clock := testingclock.NewFakeClock(time.Now())
|
||||
cacher, _, err := newTestCacherWithoutSyncing(backingStorage, clock)
|
||||
|
|
|
|||
|
|
@ -146,33 +146,18 @@ func (c *CacheDelegator) Get(ctx context.Context, key string, opts storage.GetOp
|
|||
return c.storage.Get(ctx, key, opts, objPtr)
|
||||
}
|
||||
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.ResilientWatchCacheInitialization) {
|
||||
if !c.cacher.Ready() {
|
||||
// If Cache is not initialized, delegator Get requests to storage
|
||||
// as described in https://kep.k8s.io/4568
|
||||
span.AddEvent("About to Get from underlying storage - cache not initialized")
|
||||
return c.storage.Get(ctx, key, opts, objPtr)
|
||||
}
|
||||
if !c.cacher.Ready() {
|
||||
// If Cache is not initialized, delegator Get requests to storage
|
||||
// as described in https://kep.k8s.io/4568
|
||||
span.AddEvent("About to Get from underlying storage - cache not initialized")
|
||||
return c.storage.Get(ctx, key, opts, objPtr)
|
||||
}
|
||||
// If resourceVersion is specified, serve it from cache.
|
||||
// It's guaranteed that the returned value is at least that
|
||||
// fresh as the given resourceVersion.
|
||||
getRV, err := c.cacher.versioner.ParseResourceVersion(opts.ResourceVersion)
|
||||
if err != nil {
|
||||
if _, err := c.cacher.versioner.ParseResourceVersion(opts.ResourceVersion); err != nil {
|
||||
return err
|
||||
}
|
||||
// Do not create a trace - it's not for free and there are tons
|
||||
// of Get requests. We can add it if it will be really needed.
|
||||
if !utilfeature.DefaultFeatureGate.Enabled(features.ResilientWatchCacheInitialization) {
|
||||
if getRV == 0 && !c.cacher.Ready() {
|
||||
// If Cacher is not yet initialized and we don't require any specific
|
||||
// minimal resource version, simply forward the request to storage.
|
||||
return c.storage.Get(ctx, key, opts, objPtr)
|
||||
}
|
||||
if err := c.cacher.ready.wait(ctx); err != nil {
|
||||
return errors.NewServiceUnavailable(err.Error())
|
||||
}
|
||||
}
|
||||
span.AddEvent("About to fetch object from cache")
|
||||
return c.cacher.Get(ctx, key, opts, objPtr)
|
||||
}
|
||||
|
|
@ -190,23 +175,14 @@ func (c *CacheDelegator) GetList(ctx context.Context, key string, opts storage.L
|
|||
return c.storage.GetList(ctx, key, opts, listObj)
|
||||
}
|
||||
|
||||
listRV, err := c.cacher.versioner.ParseResourceVersion(opts.ResourceVersion)
|
||||
if err != nil {
|
||||
if _, err := c.cacher.versioner.ParseResourceVersion(opts.ResourceVersion); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.ResilientWatchCacheInitialization) {
|
||||
if !c.cacher.Ready() && shouldDelegateListOnNotReadyCache(opts) {
|
||||
// If Cacher is not initialized, delegator List requests to storage
|
||||
// as described in https://kep.k8s.io/4568
|
||||
return c.storage.GetList(ctx, key, opts, listObj)
|
||||
}
|
||||
} else {
|
||||
if listRV == 0 && !c.cacher.Ready() {
|
||||
// If Cacher is not yet initialized and we don't require any specific
|
||||
// minimal resource version, simply forward the request to storage.
|
||||
return c.storage.GetList(ctx, key, opts, listObj)
|
||||
}
|
||||
if !c.cacher.Ready() && shouldDelegateListOnNotReadyCache(opts) {
|
||||
// If Cacher is not initialized, delegator List requests to storage
|
||||
// as described in https://kep.k8s.io/4568
|
||||
return c.storage.GetList(ctx, key, opts, listObj)
|
||||
}
|
||||
err = c.cacher.GetList(ctx, key, opts, listObj)
|
||||
success := "true"
|
||||
|
|
|
|||
|
|
@ -18,10 +18,8 @@ package delegator
|
|||
|
||||
import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apiserver/pkg/features"
|
||||
"k8s.io/apiserver/pkg/storage"
|
||||
etcdfeature "k8s.io/apiserver/pkg/storage/feature"
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
)
|
||||
|
||||
func ShouldDelegateListMeta(opts *metav1.ListOptions, cache Helper) (Result, error) {
|
||||
|
|
@ -107,7 +105,5 @@ func (c CacheWithoutSnapshots) ShouldDelegateConsistentRead() (Result, error) {
|
|||
// ConsistentReadSupported returns whether cache can be used to serve reads with RV not yet observed by cache, including both consistent reads.
|
||||
// Function is located here to avoid import cycles between staging/src/k8s.io/apiserver/pkg/storage/cacher/delegator.go and staging/src/k8s.io/apiserver/pkg/util/flow_control/request/list_work_estimator.go.
|
||||
func ConsistentReadSupported() bool {
|
||||
consistentListFromCacheEnabled := utilfeature.DefaultFeatureGate.Enabled(features.ConsistentListFromCache)
|
||||
requestWatchProgressSupported := etcdfeature.DefaultFeatureSupportChecker.Supports(storage.RequestWatchProgress)
|
||||
return consistentListFromCacheEnabled && requestWatchProgressSupported
|
||||
return etcdfeature.DefaultFeatureSupportChecker.Supports(storage.RequestWatchProgress)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,8 +22,6 @@ import (
|
|||
"k8s.io/apimachinery/pkg/fields"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apiserver/pkg/features"
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
)
|
||||
|
||||
|
|
@ -79,10 +77,7 @@ type OrderedLister interface {
|
|||
}
|
||||
|
||||
func NewIndexer(indexers *cache.Indexers) Indexer {
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.BtreeWatchCache) {
|
||||
return newThreadedBtreeStoreIndexer(ElementIndexers(indexers), btreeDegree)
|
||||
}
|
||||
return cache.NewIndexer(ElementKey, ElementIndexers(indexers))
|
||||
return newThreadedBtreeStoreIndexer(ElementIndexers(indexers), btreeDegree)
|
||||
}
|
||||
|
||||
// Computing a key of an object is generally non-trivial (it performs
|
||||
|
|
|
|||
|
|
@ -35,7 +35,6 @@ import (
|
|||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/util/version"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
"k8s.io/apimachinery/pkg/watch"
|
||||
"k8s.io/apiserver/pkg/features"
|
||||
|
|
@ -554,7 +553,6 @@ func TestWaitUntilFreshAndGetList(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestWaitUntilFreshAndListFromCache(t *testing.T) {
|
||||
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.ConsistentListFromCache, true)
|
||||
forceRequestWatchProgressSupport(t)
|
||||
ctx := context.Background()
|
||||
store := newTestWatchCache(3, DefaultEventFreshDuration, &cache.Indexers{})
|
||||
|
|
@ -609,54 +607,33 @@ func TestWaitUntilFreshAndGet(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestWaitUntilFreshAndListTimeout(t *testing.T) {
|
||||
tcs := []struct {
|
||||
name string
|
||||
ConsistentListFromCache bool
|
||||
}{
|
||||
{
|
||||
name: "FromStorage",
|
||||
ConsistentListFromCache: false,
|
||||
},
|
||||
{
|
||||
name: "FromCache",
|
||||
ConsistentListFromCache: true,
|
||||
},
|
||||
ctx := context.Background()
|
||||
store := newTestWatchCache(3, DefaultEventFreshDuration, &cache.Indexers{})
|
||||
defer store.Stop()
|
||||
fc := store.clock.(*testingclock.FakeClock)
|
||||
|
||||
// In background, step clock after the below call starts the timer.
|
||||
go func() {
|
||||
for !fc.HasWaiters() {
|
||||
time.Sleep(time.Millisecond)
|
||||
}
|
||||
store.Add(makeTestPod("foo", 2))
|
||||
store.bookmarkRevision <- 3
|
||||
fc.Step(blockTimeout)
|
||||
|
||||
// Add an object to make sure the test would
|
||||
// eventually fail instead of just waiting
|
||||
// forever.
|
||||
time.Sleep(30 * time.Second)
|
||||
store.Add(makeTestPod("bar", 4))
|
||||
}()
|
||||
|
||||
_, _, err := store.WaitUntilFreshAndGetList(ctx, "", storage.ListOptions{ResourceVersion: "4", Predicate: storage.Everything})
|
||||
if !errors.IsTimeout(err) {
|
||||
t.Errorf("expected timeout error but got: %v", err)
|
||||
}
|
||||
for _, tc := range tcs {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
if !tc.ConsistentListFromCache {
|
||||
featuregatetesting.SetFeatureGateEmulationVersionDuringTest(t, utilfeature.DefaultFeatureGate, version.MustParse("1.33"))
|
||||
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.ConsistentListFromCache, tc.ConsistentListFromCache)
|
||||
}
|
||||
ctx := context.Background()
|
||||
store := newTestWatchCache(3, DefaultEventFreshDuration, &cache.Indexers{})
|
||||
defer store.Stop()
|
||||
fc := store.clock.(*testingclock.FakeClock)
|
||||
|
||||
// In background, step clock after the below call starts the timer.
|
||||
go func() {
|
||||
for !fc.HasWaiters() {
|
||||
time.Sleep(time.Millisecond)
|
||||
}
|
||||
store.Add(makeTestPod("foo", 2))
|
||||
store.bookmarkRevision <- 3
|
||||
fc.Step(blockTimeout)
|
||||
|
||||
// Add an object to make sure the test would
|
||||
// eventually fail instead of just waiting
|
||||
// forever.
|
||||
time.Sleep(30 * time.Second)
|
||||
store.Add(makeTestPod("bar", 4))
|
||||
}()
|
||||
|
||||
_, _, err := store.WaitUntilFreshAndGetList(ctx, "", storage.ListOptions{ResourceVersion: "4", Predicate: storage.Everything})
|
||||
if !errors.IsTimeout(err) {
|
||||
t.Errorf("expected timeout error but got: %v", err)
|
||||
}
|
||||
if !storage.IsTooLargeResourceVersion(err) {
|
||||
t.Errorf("expected 'Too large resource version' cause in error but got: %v", err)
|
||||
}
|
||||
})
|
||||
if !storage.IsTooLargeResourceVersion(err) {
|
||||
t.Errorf("expected 'Too large resource version' cause in error but got: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -200,9 +200,7 @@ func New(c *kubernetes.Client, compactor Compactor, codec runtime.Codec, newFunc
|
|||
w.getCurrentStorageRV = func(ctx context.Context) (uint64, error) {
|
||||
return s.GetCurrentResourceVersion(ctx)
|
||||
}
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.ConsistentListFromCache) || utilfeature.DefaultFeatureGate.Enabled(features.WatchList) {
|
||||
etcdfeature.DefaultFeatureSupportChecker.CheckClient(c.Ctx(), c, storage.RequestWatchProgress)
|
||||
}
|
||||
etcdfeature.DefaultFeatureSupportChecker.CheckClient(c.Ctx(), c, storage.RequestWatchProgress)
|
||||
return s, nil
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -319,17 +319,15 @@ var newETCD3Client = func(c storagebackend.TransportConfig) (*kubernetes.Client,
|
|||
grpc.WithChainUnaryInterceptor(grpcpromClientMetrics.UnaryClientInterceptor()),
|
||||
grpc.WithChainStreamInterceptor(grpcpromClientMetrics.StreamClientInterceptor()),
|
||||
}
|
||||
if utilfeature.DefaultFeatureGate.Enabled(genericfeatures.APIServerTracing) {
|
||||
tracingOpts := []otelgrpc.Option{
|
||||
otelgrpc.WithMessageEvents(otelgrpc.ReceivedEvents, otelgrpc.SentEvents),
|
||||
otelgrpc.WithPropagators(tracing.Propagators()),
|
||||
otelgrpc.WithTracerProvider(c.TracerProvider),
|
||||
}
|
||||
// Even with Noop TracerProvider, the otelgrpc still handles context propagation.
|
||||
// See https://github.com/open-telemetry/opentelemetry-go/tree/main/example/passthrough
|
||||
dialOptions = append(dialOptions,
|
||||
grpc.WithStatsHandler(otelgrpc.NewClientHandler(tracingOpts...)))
|
||||
tracingOpts := []otelgrpc.Option{
|
||||
otelgrpc.WithMessageEvents(otelgrpc.ReceivedEvents, otelgrpc.SentEvents),
|
||||
otelgrpc.WithPropagators(tracing.Propagators()),
|
||||
otelgrpc.WithTracerProvider(c.TracerProvider),
|
||||
}
|
||||
// Even with Noop TracerProvider, the otelgrpc still handles context propagation.
|
||||
// See https://github.com/open-telemetry/opentelemetry-go/tree/main/example/passthrough
|
||||
dialOptions = append(dialOptions,
|
||||
grpc.WithStatsHandler(otelgrpc.NewClientHandler(tracingOpts...)))
|
||||
if egressDialer != nil {
|
||||
dialer := func(ctx context.Context, addr string) (net.Conn, error) {
|
||||
if strings.Contains(addr, "//") {
|
||||
|
|
|
|||
|
|
@ -709,14 +709,7 @@ func RunTestList(ctx context.Context, t *testing.T, store storage.Interface, com
|
|||
pred: storage.Everything,
|
||||
expectedOut: []example.Pod{*updatedPod},
|
||||
expectCacherRequestsToEtcd: func() []RecordedList {
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.ConsistentListFromCache) {
|
||||
return nil
|
||||
}
|
||||
return []RecordedList{
|
||||
{
|
||||
Key: "/registry/pods/first/",
|
||||
},
|
||||
}
|
||||
return nil
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
@ -878,14 +871,7 @@ func RunTestList(ctx context.Context, t *testing.T, store storage.Interface, com
|
|||
pred: storage.Everything,
|
||||
expectedOut: []example.Pod{},
|
||||
expectCacherRequestsToEtcd: func() []RecordedList {
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.ConsistentListFromCache) {
|
||||
return nil
|
||||
}
|
||||
return []RecordedList{
|
||||
{
|
||||
Key: "/registry/pods/non-existing/",
|
||||
},
|
||||
}
|
||||
return nil
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
@ -897,14 +883,7 @@ func RunTestList(ctx context.Context, t *testing.T, store storage.Interface, com
|
|||
},
|
||||
expectedOut: []example.Pod{},
|
||||
expectCacherRequestsToEtcd: func() []RecordedList {
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.ConsistentListFromCache) {
|
||||
return nil
|
||||
}
|
||||
return []RecordedList{
|
||||
{
|
||||
Key: "/registry/pods/first/",
|
||||
},
|
||||
}
|
||||
return nil
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
@ -932,15 +911,7 @@ func RunTestList(ctx context.Context, t *testing.T, store storage.Interface, com
|
|||
expectContinueExact: encodeContinueOrDie(createdPods[1].Name+"\x00", int64(mustAtoi(currentRV))),
|
||||
expectedRemainingItemCount: ptr.To[int64](1),
|
||||
expectCacherRequestsToEtcd: func() []RecordedList {
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.ConsistentListFromCache) {
|
||||
return nil
|
||||
}
|
||||
return []RecordedList{
|
||||
{
|
||||
Key: "/registry/pods/second/",
|
||||
ListOptions: kubernetes.ListOptions{Revision: 0, Limit: 1},
|
||||
},
|
||||
}
|
||||
return nil
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
@ -1151,14 +1122,7 @@ func RunTestList(ctx context.Context, t *testing.T, store storage.Interface, com
|
|||
pred: storage.Everything,
|
||||
expectedOut: []example.Pod{*createdPods[1], *createdPods[2]},
|
||||
expectCacherRequestsToEtcd: func() []RecordedList {
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.ConsistentListFromCache) {
|
||||
return nil
|
||||
}
|
||||
return []RecordedList{
|
||||
{
|
||||
Key: "/registry/pods/second/",
|
||||
},
|
||||
}
|
||||
return nil
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
@ -1180,23 +1144,7 @@ func RunTestList(ctx context.Context, t *testing.T, store storage.Interface, com
|
|||
expectedOut: []example.Pod{*createdPods[3]},
|
||||
expectContinue: true,
|
||||
expectCacherRequestsToEtcd: func() []RecordedList {
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.ConsistentListFromCache) {
|
||||
return nil
|
||||
}
|
||||
return []RecordedList{
|
||||
{
|
||||
Key: "/registry/pods/",
|
||||
ListOptions: kubernetes.ListOptions{Limit: 1},
|
||||
},
|
||||
{
|
||||
Key: "/registry/pods/",
|
||||
ListOptions: kubernetes.ListOptions{Revision: int64(continueRV) + 1, Limit: 2, Continue: "/registry/pods/first/bar\x00"},
|
||||
},
|
||||
{
|
||||
Key: "/registry/pods/",
|
||||
ListOptions: kubernetes.ListOptions{Revision: int64(continueRV) + 1, Limit: 4, Continue: "/registry/pods/second/foo\x00"},
|
||||
},
|
||||
}
|
||||
return nil
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
@ -1224,19 +1172,7 @@ func RunTestList(ctx context.Context, t *testing.T, store storage.Interface, com
|
|||
expectedOut: []example.Pod{*createdPods[3]},
|
||||
expectContinue: false,
|
||||
expectCacherRequestsToEtcd: func() []RecordedList {
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.ConsistentListFromCache) {
|
||||
return nil
|
||||
}
|
||||
return []RecordedList{
|
||||
{
|
||||
Key: "/registry/pods/",
|
||||
ListOptions: kubernetes.ListOptions{Revision: 0, Limit: 2},
|
||||
},
|
||||
{
|
||||
Key: "/registry/pods/",
|
||||
ListOptions: kubernetes.ListOptions{Revision: int64(continueRV) + 1, Limit: 4, Continue: "/registry/pods/second/bar\x00"},
|
||||
},
|
||||
}
|
||||
return nil
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
@ -1276,15 +1212,7 @@ func RunTestList(ctx context.Context, t *testing.T, store storage.Interface, com
|
|||
expectContinue: true,
|
||||
expectedOut: []example.Pod{*updatedPod, *createdPods[1]},
|
||||
expectCacherRequestsToEtcd: func() []RecordedList {
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.ConsistentListFromCache) {
|
||||
return nil
|
||||
}
|
||||
return []RecordedList{
|
||||
{
|
||||
Key: "/registry/pods/",
|
||||
ListOptions: kubernetes.ListOptions{Revision: 0, Limit: 2},
|
||||
},
|
||||
}
|
||||
return nil
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
@ -1310,19 +1238,7 @@ func RunTestList(ctx context.Context, t *testing.T, store storage.Interface, com
|
|||
},
|
||||
expectedOut: []example.Pod{*createdPods[2], *createdPods[4]},
|
||||
expectCacherRequestsToEtcd: func() []RecordedList {
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.ConsistentListFromCache) {
|
||||
return nil
|
||||
}
|
||||
return []RecordedList{
|
||||
{
|
||||
Key: "/registry/pods/",
|
||||
ListOptions: kubernetes.ListOptions{Revision: 0, Limit: 2},
|
||||
},
|
||||
{
|
||||
Key: "/registry/pods/",
|
||||
ListOptions: kubernetes.ListOptions{Revision: int64(continueRV) + 1, Limit: 4, Continue: "/registry/pods/second/bar\x00"},
|
||||
},
|
||||
}
|
||||
return nil
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
@ -1417,15 +1333,7 @@ func RunTestList(ctx context.Context, t *testing.T, store storage.Interface, com
|
|||
},
|
||||
expectedOut: []example.Pod{*createdPods[2], *createdPods[4]},
|
||||
expectCacherRequestsToEtcd: func() []RecordedList {
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.ConsistentListFromCache) {
|
||||
return nil
|
||||
}
|
||||
return []RecordedList{
|
||||
{
|
||||
Key: "/registry/pods/",
|
||||
ListOptions: kubernetes.ListOptions{Revision: 0, Limit: 5},
|
||||
},
|
||||
}
|
||||
return nil
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
@ -1450,15 +1358,7 @@ func RunTestList(ctx context.Context, t *testing.T, store storage.Interface, com
|
|||
},
|
||||
expectedOut: []example.Pod{*createdPods[3]},
|
||||
expectCacherRequestsToEtcd: func() []RecordedList {
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.ConsistentListFromCache) {
|
||||
return nil
|
||||
}
|
||||
return []RecordedList{
|
||||
{
|
||||
Key: "/registry/pods/",
|
||||
ListOptions: kubernetes.ListOptions{Revision: 0, Limit: 5},
|
||||
},
|
||||
}
|
||||
return nil
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
@ -1479,14 +1379,7 @@ func RunTestList(ctx context.Context, t *testing.T, store storage.Interface, com
|
|||
pred: storage.Everything,
|
||||
expectedOut: []example.Pod{*updatedPod, *createdPods[1], *createdPods[2], *createdPods[3], *createdPods[4]},
|
||||
expectCacherRequestsToEtcd: func() []RecordedList {
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.ConsistentListFromCache) {
|
||||
return nil
|
||||
}
|
||||
return []RecordedList{
|
||||
{
|
||||
Key: "/registry/pods/",
|
||||
},
|
||||
}
|
||||
return nil
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
@ -1529,14 +1422,7 @@ func RunTestList(ctx context.Context, t *testing.T, store storage.Interface, com
|
|||
expectRV: currentRV,
|
||||
expectedOut: []example.Pod{},
|
||||
expectCacherRequestsToEtcd: func() []RecordedList {
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.ConsistentListFromCache) {
|
||||
return nil
|
||||
}
|
||||
return []RecordedList{
|
||||
{
|
||||
Key: "/registry/pods/empty/",
|
||||
},
|
||||
}
|
||||
return nil
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
@ -1725,7 +1611,7 @@ func RunTestList(ctx context.Context, t *testing.T, store storage.Interface, com
|
|||
rvMatch: metav1.ResourceVersionMatchExact,
|
||||
expectRV: fmt.Sprint(continueRV + 1),
|
||||
expectCacherRequestsToEtcd: func() []RecordedList {
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.ConsistentListFromCache) && utilfeature.DefaultFeatureGate.Enabled(features.ListFromCacheSnapshot) {
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.ListFromCacheSnapshot) {
|
||||
return nil
|
||||
}
|
||||
return []RecordedList{
|
||||
|
|
@ -1955,7 +1841,7 @@ func RunTestList(ctx context.Context, t *testing.T, store storage.Interface, com
|
|||
expectContinueExact: encodeContinueOrDie(updatedPod.Namespace+"/"+updatedPod.Name+"\x00", int64(continueRV+1)),
|
||||
expectedRemainingItemCount: ptr.To[int64](4),
|
||||
expectCacherRequestsToEtcd: func() []RecordedList {
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.ConsistentListFromCache) && utilfeature.DefaultFeatureGate.Enabled(features.ListFromCacheSnapshot) {
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.ListFromCacheSnapshot) {
|
||||
return nil
|
||||
}
|
||||
return []RecordedList{
|
||||
|
|
@ -2148,7 +2034,7 @@ func RunTestList(ctx context.Context, t *testing.T, store storage.Interface, com
|
|||
expectContinueExact: encodeContinueOrDie(createdPods[1].Namespace+"/"+createdPods[1].Name+"\x00", int64(continueRV+1)),
|
||||
expectedRemainingItemCount: ptr.To[int64](3),
|
||||
expectCacherRequestsToEtcd: func() []RecordedList {
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.ConsistentListFromCache) && utilfeature.DefaultFeatureGate.Enabled(features.ListFromCacheSnapshot) {
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.ListFromCacheSnapshot) {
|
||||
return nil
|
||||
}
|
||||
return []RecordedList{
|
||||
|
|
@ -2170,7 +2056,7 @@ func RunTestList(ctx context.Context, t *testing.T, store storage.Interface, com
|
|||
expectedOut: []example.Pod{*createdPods[1], *createdPods[2], *createdPods[3], *createdPods[4]},
|
||||
expectRV: currentRV,
|
||||
expectCacherRequestsToEtcd: func() []RecordedList {
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.ConsistentListFromCache) && utilfeature.DefaultFeatureGate.Enabled(features.ListFromCacheSnapshot) {
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.ListFromCacheSnapshot) {
|
||||
return nil
|
||||
}
|
||||
return []RecordedList{
|
||||
|
|
@ -2196,7 +2082,7 @@ func RunTestList(ctx context.Context, t *testing.T, store storage.Interface, com
|
|||
expectRV: currentRV,
|
||||
expectedRemainingItemCount: ptr.To[int64](2),
|
||||
expectCacherRequestsToEtcd: func() []RecordedList {
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.ConsistentListFromCache) && utilfeature.DefaultFeatureGate.Enabled(features.ListFromCacheSnapshot) {
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.ListFromCacheSnapshot) {
|
||||
return nil
|
||||
}
|
||||
return []RecordedList{
|
||||
|
|
@ -2218,7 +2104,7 @@ func RunTestList(ctx context.Context, t *testing.T, store storage.Interface, com
|
|||
expectedOut: []example.Pod{*createdPods[3], *createdPods[4]},
|
||||
expectRV: currentRV,
|
||||
expectCacherRequestsToEtcd: func() []RecordedList {
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.ConsistentListFromCache) && utilfeature.DefaultFeatureGate.Enabled(features.ListFromCacheSnapshot) {
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.ListFromCacheSnapshot) {
|
||||
return nil
|
||||
}
|
||||
return []RecordedList{
|
||||
|
|
|
|||
|
|
@ -29,9 +29,7 @@ import (
|
|||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
utilnet "k8s.io/apimachinery/pkg/util/net"
|
||||
"k8s.io/apiserver/pkg/features"
|
||||
egressselector "k8s.io/apiserver/pkg/server/egressselector"
|
||||
"k8s.io/apiserver/pkg/util/feature"
|
||||
"k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
|
||||
|
|
@ -59,9 +57,7 @@ func NewDefaultAuthenticationInfoResolverWrapper(
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if feature.DefaultFeatureGate.Enabled(features.APIServerTracing) {
|
||||
ret.Wrap(tracing.WrapperFor(tp))
|
||||
}
|
||||
ret.Wrap(tracing.WrapperFor(tp))
|
||||
|
||||
if egressSelector != nil {
|
||||
networkContext := egressselector.ControlPlane.AsNetworkContext()
|
||||
|
|
@ -84,9 +80,7 @@ func NewDefaultAuthenticationInfoResolverWrapper(
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if feature.DefaultFeatureGate.Enabled(features.APIServerTracing) {
|
||||
ret.Wrap(tracing.WrapperFor(tp))
|
||||
}
|
||||
ret.Wrap(tracing.WrapperFor(tp))
|
||||
|
||||
if egressSelector != nil {
|
||||
networkContext := egressselector.Cluster.AsNetworkContext()
|
||||
|
|
|
|||
|
|
@ -167,7 +167,7 @@ func (r *proxyHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|||
|
||||
proxyRoundTripper = transport.NewAuthProxyRoundTripper(user.GetName(), userUID, user.GetGroups(), user.GetExtra(), proxyRoundTripper)
|
||||
|
||||
if utilfeature.DefaultFeatureGate.Enabled(genericfeatures.APIServerTracing) && !upgrade {
|
||||
if !upgrade {
|
||||
tracingWrapper := tracing.WrapperFor(r.tracerProvider)
|
||||
proxyRoundTripper = tracingWrapper(proxyRoundTripper)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@
|
|||
|---------|---------|--------|-------|------|----|------------|--------------|-------|
|
||||
| APIResponseCompression | :ballot_box_with_check: 1.16+ | | 1.8–1.15 | 1.16– | | | | [code](https://cs.k8s.io/?q=%5CbAPIResponseCompression%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/kubernetes) [KEPs](https://cs.k8s.io/?q=%5CbAPIResponseCompression%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/enhancements) |
|
||||
| APIServerIdentity | :ballot_box_with_check: 1.26+ | | 1.20–1.25 | 1.26– | | | | [code](https://cs.k8s.io/?q=%5CbAPIServerIdentity%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/kubernetes) [KEPs](https://cs.k8s.io/?q=%5CbAPIServerIdentity%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/enhancements) |
|
||||
| APIServerTracing | :ballot_box_with_check: 1.27+ | :closed_lock_with_key: 1.34+ | 1.22–1.26 | 1.27–1.33 | 1.34– | | | [code](https://cs.k8s.io/?q=%5CbAPIServerTracing%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/kubernetes) [KEPs](https://cs.k8s.io/?q=%5CbAPIServerTracing%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/enhancements) |
|
||||
| APIServingWithRoutine | | | 1.30– | | | | | [code](https://cs.k8s.io/?q=%5CbAPIServingWithRoutine%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/kubernetes) [KEPs](https://cs.k8s.io/?q=%5CbAPIServingWithRoutine%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/enhancements) |
|
||||
| AggregatedDiscoveryRemoveBetaType | :ballot_box_with_check: 1.33+ | :closed_lock_with_key: 1.35+ | | | 1.0–1.32 | 1.33– | | [code](https://cs.k8s.io/?q=%5CbAggregatedDiscoveryRemoveBetaType%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/kubernetes) [KEPs](https://cs.k8s.io/?q=%5CbAggregatedDiscoveryRemoveBetaType%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/enhancements) |
|
||||
| AllowDNSOnlyNodeCSR | :ballot_box_with_check: 1.0+ | | | | 1.0–1.30 | 1.31– | | [code](https://cs.k8s.io/?q=%5CbAllowDNSOnlyNodeCSR%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/kubernetes) [KEPs](https://cs.k8s.io/?q=%5CbAllowDNSOnlyNodeCSR%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/enhancements) |
|
||||
|
|
@ -18,7 +17,6 @@
|
|||
| AllowUnsafeMalformedObjectDeletion | | | 1.32– | | | | | [code](https://cs.k8s.io/?q=%5CbAllowUnsafeMalformedObjectDeletion%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/kubernetes) [KEPs](https://cs.k8s.io/?q=%5CbAllowUnsafeMalformedObjectDeletion%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/enhancements) |
|
||||
| AtomicFIFO | :ballot_box_with_check: 1.36+ | | | 1.36– | | | | [code](https://cs.k8s.io/?q=%5CbAtomicFIFO%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/kubernetes) [KEPs](https://cs.k8s.io/?q=%5CbAtomicFIFO%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/enhancements) |
|
||||
| AuthorizePodWebsocketUpgradeCreatePermission | :ballot_box_with_check: 1.35+ | | | 1.35– | | | | [code](https://cs.k8s.io/?q=%5CbAuthorizePodWebsocketUpgradeCreatePermission%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/kubernetes) [KEPs](https://cs.k8s.io/?q=%5CbAuthorizePodWebsocketUpgradeCreatePermission%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/enhancements) |
|
||||
| BtreeWatchCache | :ballot_box_with_check: 1.32+ | :closed_lock_with_key: 1.33+ | | 1.32 | 1.33– | | | [code](https://cs.k8s.io/?q=%5CbBtreeWatchCache%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/kubernetes) [KEPs](https://cs.k8s.io/?q=%5CbBtreeWatchCache%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/enhancements) |
|
||||
| CBORServingAndStorage | | | 1.32– | | | | | [code](https://cs.k8s.io/?q=%5CbCBORServingAndStorage%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/kubernetes) [KEPs](https://cs.k8s.io/?q=%5CbCBORServingAndStorage%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/enhancements) |
|
||||
| CPUManagerPolicyAlphaOptions | | | 1.23– | | | | | [code](https://cs.k8s.io/?q=%5CbCPUManagerPolicyAlphaOptions%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/kubernetes) [KEPs](https://cs.k8s.io/?q=%5CbCPUManagerPolicyAlphaOptions%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/enhancements) |
|
||||
| CPUManagerPolicyBetaOptions | :ballot_box_with_check: 1.23+ | | | 1.23– | | | | [code](https://cs.k8s.io/?q=%5CbCPUManagerPolicyBetaOptions%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/kubernetes) [KEPs](https://cs.k8s.io/?q=%5CbCPUManagerPolicyBetaOptions%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/enhancements) |
|
||||
|
|
@ -41,8 +39,7 @@
|
|||
| ComponentFlagz | :ballot_box_with_check: 1.36+ | | 1.32–1.35 | 1.36– | | | | [code](https://cs.k8s.io/?q=%5CbComponentFlagz%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/kubernetes) [KEPs](https://cs.k8s.io/?q=%5CbComponentFlagz%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/enhancements) |
|
||||
| ComponentStatusz | :ballot_box_with_check: 1.36+ | | 1.32–1.35 | 1.36– | | | | [code](https://cs.k8s.io/?q=%5CbComponentStatusz%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/kubernetes) [KEPs](https://cs.k8s.io/?q=%5CbComponentStatusz%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/enhancements) |
|
||||
| ConcurrentWatchObjectDecode | | | | 1.31– | | | | [code](https://cs.k8s.io/?q=%5CbConcurrentWatchObjectDecode%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/kubernetes) [KEPs](https://cs.k8s.io/?q=%5CbConcurrentWatchObjectDecode%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/enhancements) |
|
||||
| ConsistentListFromCache | :ballot_box_with_check: 1.31+ | :closed_lock_with_key: 1.34+ | 1.28–1.30 | 1.31–1.33 | 1.34– | | | [code](https://cs.k8s.io/?q=%5CbConsistentListFromCache%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/kubernetes) [KEPs](https://cs.k8s.io/?q=%5CbConsistentListFromCache%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/enhancements) |
|
||||
| ConsistentListFromCacheSkipTimeoutFallback | | | 1.37– | | | | ConsistentListFromCache | [code](https://cs.k8s.io/?q=%5CbConsistentListFromCacheSkipTimeoutFallback%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/kubernetes) [KEPs](https://cs.k8s.io/?q=%5CbConsistentListFromCacheSkipTimeoutFallback%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/enhancements) |
|
||||
| ConsistentListFromCacheSkipTimeoutFallback | | | 1.37– | | | | | [code](https://cs.k8s.io/?q=%5CbConsistentListFromCacheSkipTimeoutFallback%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/kubernetes) [KEPs](https://cs.k8s.io/?q=%5CbConsistentListFromCacheSkipTimeoutFallback%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/enhancements) |
|
||||
| ConstrainedImpersonation | :ballot_box_with_check: 1.36+ | | 1.35 | 1.36– | | | | [code](https://cs.k8s.io/?q=%5CbConstrainedImpersonation%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/kubernetes) [KEPs](https://cs.k8s.io/?q=%5CbConstrainedImpersonation%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/enhancements) |
|
||||
| ContainerCheckpoint | :ballot_box_with_check: 1.30+ | | 1.25–1.29 | 1.30– | | | | [code](https://cs.k8s.io/?q=%5CbContainerCheckpoint%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/kubernetes) [KEPs](https://cs.k8s.io/?q=%5CbContainerCheckpoint%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/enhancements) |
|
||||
| ContainerRestartRules | :ballot_box_with_check: 1.35+ | | 1.34 | 1.35– | | | | [code](https://cs.k8s.io/?q=%5CbContainerRestartRules%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/kubernetes) [KEPs](https://cs.k8s.io/?q=%5CbContainerRestartRules%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/enhancements) |
|
||||
|
|
@ -138,7 +135,6 @@
|
|||
| NominatedNodeNameForExpectation | :ballot_box_with_check: 1.35+ | | 1.34 | 1.35– | | | | [code](https://cs.k8s.io/?q=%5CbNominatedNodeNameForExpectation%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/kubernetes) [KEPs](https://cs.k8s.io/?q=%5CbNominatedNodeNameForExpectation%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/enhancements) |
|
||||
| OpenAPIEnums | :ballot_box_with_check: 1.24+ | | 1.23 | 1.24– | | | | [code](https://cs.k8s.io/?q=%5CbOpenAPIEnums%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/kubernetes) [KEPs](https://cs.k8s.io/?q=%5CbOpenAPIEnums%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/enhancements) |
|
||||
| OpportunisticBatching | :ballot_box_with_check: 1.35+ | | | 1.35– | | | | [code](https://cs.k8s.io/?q=%5CbOpportunisticBatching%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/kubernetes) [KEPs](https://cs.k8s.io/?q=%5CbOpportunisticBatching%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/enhancements) |
|
||||
| OrderedNamespaceDeletion | :ballot_box_with_check: 1.33+ | :closed_lock_with_key: 1.34+ | | 1.30–1.33 | 1.34– | | | [code](https://cs.k8s.io/?q=%5CbOrderedNamespaceDeletion%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/kubernetes) [KEPs](https://cs.k8s.io/?q=%5CbOrderedNamespaceDeletion%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/enhancements) |
|
||||
| PLEGOnDemandRelist | :ballot_box_with_check: 1.36+ | | | 1.36– | | | | [code](https://cs.k8s.io/?q=%5CbPLEGOnDemandRelist%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/kubernetes) [KEPs](https://cs.k8s.io/?q=%5CbPLEGOnDemandRelist%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/enhancements) |
|
||||
| PersistentVolumeClaimUnusedSinceTime | | | 1.36– | | | | | [code](https://cs.k8s.io/?q=%5CbPersistentVolumeClaimUnusedSinceTime%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/kubernetes) [KEPs](https://cs.k8s.io/?q=%5CbPersistentVolumeClaimUnusedSinceTime%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/enhancements) |
|
||||
| PodAndContainerStatsFromCRI | | | 1.23– | | | | | [code](https://cs.k8s.io/?q=%5CbPodAndContainerStatsFromCRI%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/kubernetes) [KEPs](https://cs.k8s.io/?q=%5CbPodAndContainerStatsFromCRI%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/enhancements) |
|
||||
|
|
@ -168,11 +164,9 @@
|
|||
| ReloadKubeletClientCAFile | :ballot_box_with_check: 1.36+ | | | 1.36– | | | | [code](https://cs.k8s.io/?q=%5CbReloadKubeletClientCAFile%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/kubernetes) [KEPs](https://cs.k8s.io/?q=%5CbReloadKubeletClientCAFile%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/enhancements) |
|
||||
| ReloadKubeletServerCertificateFile | :ballot_box_with_check: 1.31+ | | | 1.31– | | | | [code](https://cs.k8s.io/?q=%5CbReloadKubeletServerCertificateFile%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/kubernetes) [KEPs](https://cs.k8s.io/?q=%5CbReloadKubeletServerCertificateFile%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/enhancements) |
|
||||
| RemoteRequestHeaderUID | :ballot_box_with_check: 1.33+ | | 1.32 | 1.33– | | | | [code](https://cs.k8s.io/?q=%5CbRemoteRequestHeaderUID%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/kubernetes) [KEPs](https://cs.k8s.io/?q=%5CbRemoteRequestHeaderUID%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/enhancements) |
|
||||
| ResilientWatchCacheInitialization | :ballot_box_with_check: 1.31+ | :closed_lock_with_key: 1.34+ | | 1.31–1.33 | 1.34– | | | [code](https://cs.k8s.io/?q=%5CbResilientWatchCacheInitialization%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/kubernetes) [KEPs](https://cs.k8s.io/?q=%5CbResilientWatchCacheInitialization%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/enhancements) |
|
||||
| ResourceHealthStatus | :ballot_box_with_check: 1.36+ | | 1.31–1.35 | 1.36– | | | DynamicResourceAllocation | [code](https://cs.k8s.io/?q=%5CbResourceHealthStatus%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/kubernetes) [KEPs](https://cs.k8s.io/?q=%5CbResourceHealthStatus%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/enhancements) |
|
||||
| ResourceHealthStatusMessage | :ballot_box_with_check: 1.36+ | | | 1.36– | | | ResourceHealthStatus | [code](https://cs.k8s.io/?q=%5CbResourceHealthStatusMessage%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/kubernetes) [KEPs](https://cs.k8s.io/?q=%5CbResourceHealthStatusMessage%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/enhancements) |
|
||||
| RestartAllContainersOnContainerExits | :ballot_box_with_check: 1.36+ | | 1.35 | 1.36– | | | ContainerRestartRules<br>NodeDeclaredFeatures | [code](https://cs.k8s.io/?q=%5CbRestartAllContainersOnContainerExits%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/kubernetes) [KEPs](https://cs.k8s.io/?q=%5CbRestartAllContainersOnContainerExits%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/enhancements) |
|
||||
| RetryGenerateName | :ballot_box_with_check: 1.31+ | :closed_lock_with_key: 1.32+ | 1.30 | 1.31 | 1.32– | | | [code](https://cs.k8s.io/?q=%5CbRetryGenerateName%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/kubernetes) [KEPs](https://cs.k8s.io/?q=%5CbRetryGenerateName%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/enhancements) |
|
||||
| RotateKubeletServerCertificate | :ballot_box_with_check: 1.12+ | | 1.7–1.11 | 1.12– | | | | [code](https://cs.k8s.io/?q=%5CbRotateKubeletServerCertificate%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/kubernetes) [KEPs](https://cs.k8s.io/?q=%5CbRotateKubeletServerCertificate%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/enhancements) |
|
||||
| RuntimeClassInImageCriApi | | | 1.29– | | | | | [code](https://cs.k8s.io/?q=%5CbRuntimeClassInImageCriApi%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/kubernetes) [KEPs](https://cs.k8s.io/?q=%5CbRuntimeClassInImageCriApi%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/enhancements) |
|
||||
| SELinuxChangePolicy | :ballot_box_with_check: 1.33+ | :closed_lock_with_key: 1.36+ | 1.32 | 1.33–1.35 | 1.36– | | | [code](https://cs.k8s.io/?q=%5CbSELinuxChangePolicy%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/kubernetes) [KEPs](https://cs.k8s.io/?q=%5CbSELinuxChangePolicy%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/enhancements) |
|
||||
|
|
@ -201,8 +195,6 @@
|
|||
| StorageVersionAPI | | | 1.20– | | | | APIServerIdentity | [code](https://cs.k8s.io/?q=%5CbStorageVersionAPI%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/kubernetes) [KEPs](https://cs.k8s.io/?q=%5CbStorageVersionAPI%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/enhancements) |
|
||||
| StorageVersionHash | :ballot_box_with_check: 1.15+ | | 1.14 | 1.15– | | | | [code](https://cs.k8s.io/?q=%5CbStorageVersionHash%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/kubernetes) [KEPs](https://cs.k8s.io/?q=%5CbStorageVersionHash%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/enhancements) |
|
||||
| StorageVersionMigrator | | | 1.30–1.34 | 1.35– | | | | [code](https://cs.k8s.io/?q=%5CbStorageVersionMigrator%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/kubernetes) [KEPs](https://cs.k8s.io/?q=%5CbStorageVersionMigrator%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/enhancements) |
|
||||
| StreamingCollectionEncodingToJSON | :ballot_box_with_check: 1.33+ | :closed_lock_with_key: 1.34+ | | 1.33 | 1.34– | | | [code](https://cs.k8s.io/?q=%5CbStreamingCollectionEncodingToJSON%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/kubernetes) [KEPs](https://cs.k8s.io/?q=%5CbStreamingCollectionEncodingToJSON%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/enhancements) |
|
||||
| StreamingCollectionEncodingToProtobuf | :ballot_box_with_check: 1.33+ | :closed_lock_with_key: 1.34+ | | 1.33 | 1.34– | | | [code](https://cs.k8s.io/?q=%5CbStreamingCollectionEncodingToProtobuf%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/kubernetes) [KEPs](https://cs.k8s.io/?q=%5CbStreamingCollectionEncodingToProtobuf%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/enhancements) |
|
||||
| StrictIPCIDRValidation | :ballot_box_with_check: 1.36+ | | 1.33–1.35 | 1.36– | | | | [code](https://cs.k8s.io/?q=%5CbStrictIPCIDRValidation%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/kubernetes) [KEPs](https://cs.k8s.io/?q=%5CbStrictIPCIDRValidation%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/enhancements) |
|
||||
| StructuredAuthenticationConfigurationEgressSelector | :ballot_box_with_check: 1.34+ | | | 1.34– | | | | [code](https://cs.k8s.io/?q=%5CbStructuredAuthenticationConfigurationEgressSelector%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/kubernetes) [KEPs](https://cs.k8s.io/?q=%5CbStructuredAuthenticationConfigurationEgressSelector%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/enhancements) |
|
||||
| StructuredAuthenticationConfigurationJWKSMetrics | :ballot_box_with_check: 1.35+ | | | 1.35– | | | | [code](https://cs.k8s.io/?q=%5CbStructuredAuthenticationConfigurationJWKSMetrics%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/kubernetes) [KEPs](https://cs.k8s.io/?q=%5CbStructuredAuthenticationConfigurationJWKSMetrics%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/enhancements) |
|
||||
|
|
|
|||
|
|
@ -87,20 +87,6 @@
|
|||
lockToDefault: false
|
||||
preRelease: Beta
|
||||
version: "1.26"
|
||||
- name: APIServerTracing
|
||||
versionedSpecs:
|
||||
- default: false
|
||||
lockToDefault: false
|
||||
preRelease: Alpha
|
||||
version: "1.22"
|
||||
- default: true
|
||||
lockToDefault: false
|
||||
preRelease: Beta
|
||||
version: "1.27"
|
||||
- default: true
|
||||
lockToDefault: true
|
||||
preRelease: GA
|
||||
version: "1.34"
|
||||
- name: APIServingWithRoutine
|
||||
versionedSpecs:
|
||||
- default: false
|
||||
|
|
@ -113,16 +99,6 @@
|
|||
lockToDefault: false
|
||||
preRelease: Beta
|
||||
version: "1.35"
|
||||
- name: BtreeWatchCache
|
||||
versionedSpecs:
|
||||
- default: true
|
||||
lockToDefault: false
|
||||
preRelease: Beta
|
||||
version: "1.32"
|
||||
- default: true
|
||||
lockToDefault: true
|
||||
preRelease: GA
|
||||
version: "1.33"
|
||||
- name: CBORServingAndStorage
|
||||
versionedSpecs:
|
||||
- default: false
|
||||
|
|
@ -207,20 +183,6 @@
|
|||
lockToDefault: false
|
||||
preRelease: Beta
|
||||
version: "1.31"
|
||||
- name: ConsistentListFromCache
|
||||
versionedSpecs:
|
||||
- default: false
|
||||
lockToDefault: false
|
||||
preRelease: Alpha
|
||||
version: "1.28"
|
||||
- default: true
|
||||
lockToDefault: false
|
||||
preRelease: Beta
|
||||
version: "1.31"
|
||||
- default: true
|
||||
lockToDefault: true
|
||||
preRelease: GA
|
||||
version: "1.34"
|
||||
- name: ConsistentListFromCacheSkipTimeoutFallback
|
||||
versionedSpecs:
|
||||
- default: false
|
||||
|
|
@ -1281,20 +1243,6 @@
|
|||
lockToDefault: false
|
||||
preRelease: Beta
|
||||
version: "1.35"
|
||||
- name: OrderedNamespaceDeletion
|
||||
versionedSpecs:
|
||||
- default: false
|
||||
lockToDefault: false
|
||||
preRelease: Beta
|
||||
version: "1.30"
|
||||
- default: true
|
||||
lockToDefault: false
|
||||
preRelease: Beta
|
||||
version: "1.33"
|
||||
- default: true
|
||||
lockToDefault: true
|
||||
preRelease: GA
|
||||
version: "1.34"
|
||||
- name: PersistentVolumeClaimUnusedSinceTime
|
||||
versionedSpecs:
|
||||
- default: false
|
||||
|
|
@ -1585,16 +1533,6 @@
|
|||
lockToDefault: false
|
||||
preRelease: Beta
|
||||
version: "1.33"
|
||||
- name: ResilientWatchCacheInitialization
|
||||
versionedSpecs:
|
||||
- default: true
|
||||
lockToDefault: false
|
||||
preRelease: Beta
|
||||
version: "1.31"
|
||||
- default: true
|
||||
lockToDefault: true
|
||||
preRelease: GA
|
||||
version: "1.34"
|
||||
- name: ResourceHealthStatus
|
||||
versionedSpecs:
|
||||
- default: false
|
||||
|
|
@ -1621,20 +1559,6 @@
|
|||
lockToDefault: false
|
||||
preRelease: Beta
|
||||
version: "1.36"
|
||||
- name: RetryGenerateName
|
||||
versionedSpecs:
|
||||
- default: false
|
||||
lockToDefault: false
|
||||
preRelease: Alpha
|
||||
version: "1.30"
|
||||
- default: true
|
||||
lockToDefault: false
|
||||
preRelease: Beta
|
||||
version: "1.31"
|
||||
- default: true
|
||||
lockToDefault: true
|
||||
preRelease: GA
|
||||
version: "1.32"
|
||||
- name: RotateKubeletServerCertificate
|
||||
versionedSpecs:
|
||||
- default: false
|
||||
|
|
@ -1899,26 +1823,6 @@
|
|||
lockToDefault: false
|
||||
preRelease: Beta
|
||||
version: "1.35"
|
||||
- name: StreamingCollectionEncodingToJSON
|
||||
versionedSpecs:
|
||||
- default: true
|
||||
lockToDefault: false
|
||||
preRelease: Beta
|
||||
version: "1.33"
|
||||
- default: true
|
||||
lockToDefault: true
|
||||
preRelease: GA
|
||||
version: "1.34"
|
||||
- name: StreamingCollectionEncodingToProtobuf
|
||||
versionedSpecs:
|
||||
- default: true
|
||||
lockToDefault: false
|
||||
preRelease: Beta
|
||||
version: "1.33"
|
||||
- default: true
|
||||
lockToDefault: true
|
||||
preRelease: GA
|
||||
version: "1.34"
|
||||
- name: StrictIPCIDRValidation
|
||||
versionedSpecs:
|
||||
- default: false
|
||||
|
|
|
|||
Loading…
Reference in a new issue