mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-12 05:21:58 +00:00
Merge pull request #119503 from wojtek-t/pagination_ga
Graduate APIListChunking to GA
This commit is contained in:
commit
2e6eafca3f
@ -265,7 +265,7 @@ fi
|
|||||||
RUN_CCM_CONTROLLERS="${RUN_CCM_CONTROLLERS:-*,-gkenetworkparamset}"
|
RUN_CCM_CONTROLLERS="${RUN_CCM_CONTROLLERS:-*,-gkenetworkparamset}"
|
||||||
|
|
||||||
# List of the set of feature gates recognized by the GCP CCM
|
# List of the set of feature gates recognized by the GCP CCM
|
||||||
export CCM_FEATURE_GATES="APIListChunking,APIPriorityAndFairness,APIResponseCompression,APIServerIdentity,APIServerTracing,AllAlpha,AllBeta,CustomResourceValidationExpressions,KMSv2,OpenAPIEnums,OpenAPIV3,RemainingItemCount,ServerSideFieldValidation,StorageVersionAPI,StorageVersionHash"
|
export CCM_FEATURE_GATES="APIPriorityAndFairness,APIResponseCompression,APIServerIdentity,APIServerTracing,AllAlpha,AllBeta,CustomResourceValidationExpressions,KMSv2,OpenAPIEnums,OpenAPIV3,ServerSideFieldValidation,StorageVersionAPI,StorageVersionHash"
|
||||||
|
|
||||||
# Optional: set feature gates
|
# Optional: set feature gates
|
||||||
# shellcheck disable=SC2034 # Variables sourced in other scripts.
|
# shellcheck disable=SC2034 # Variables sourced in other scripts.
|
||||||
|
@ -316,7 +316,7 @@ if [[ -n "${NODE_ACCELERATORS}" ]]; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# List of the set of feature gates recognized by the GCP CCM
|
# List of the set of feature gates recognized by the GCP CCM
|
||||||
export CCM_FEATURE_GATES="APIListChunking,APIPriorityAndFairness,APIResponseCompression,APIServerIdentity,APIServerTracing,AllAlpha,AllBeta,CustomResourceValidationExpressions,KMSv2,OpenAPIEnums,OpenAPIV3,RemainingItemCount,ServerSideFieldValidation,StorageVersionAPI,StorageVersionHash"
|
export CCM_FEATURE_GATES="APIPriorityAndFairness,APIResponseCompression,APIServerIdentity,APIServerTracing,AllAlpha,AllBeta,CustomResourceValidationExpressions,KMSv2,OpenAPIEnums,OpenAPIV3,ServerSideFieldValidation,StorageVersionAPI,StorageVersionHash"
|
||||||
|
|
||||||
# Optional: Install cluster DNS.
|
# Optional: Install cluster DNS.
|
||||||
# Set CLUSTER_DNS_CORE_DNS to 'false' to install kube-dns instead of CoreDNS.
|
# Set CLUSTER_DNS_CORE_DNS to 'false' to install kube-dns instead of CoreDNS.
|
||||||
|
@ -92,7 +92,7 @@ func createAggregatorConfig(
|
|||||||
// we assume that the etcd options have been completed already. avoid messing with anything outside
|
// we assume that the etcd options have been completed already. avoid messing with anything outside
|
||||||
// of changes to StorageConfig as that may lead to unexpected behavior when the options are applied.
|
// of changes to StorageConfig as that may lead to unexpected behavior when the options are applied.
|
||||||
etcdOptions := *commandOptions.Etcd
|
etcdOptions := *commandOptions.Etcd
|
||||||
etcdOptions.StorageConfig.Paging = utilfeature.DefaultFeatureGate.Enabled(genericfeatures.APIListChunking)
|
etcdOptions.StorageConfig.Paging = true
|
||||||
etcdOptions.StorageConfig.Codec = aggregatorscheme.Codecs.LegacyCodec(v1.SchemeGroupVersion, v1beta1.SchemeGroupVersion)
|
etcdOptions.StorageConfig.Codec = aggregatorscheme.Codecs.LegacyCodec(v1.SchemeGroupVersion, v1beta1.SchemeGroupVersion)
|
||||||
etcdOptions.StorageConfig.EncodeVersioner = runtime.NewMultiGroupVersioner(v1.SchemeGroupVersion, schema.GroupKind{Group: v1beta1.GroupName})
|
etcdOptions.StorageConfig.EncodeVersioner = runtime.NewMultiGroupVersioner(v1.SchemeGroupVersion, schema.GroupKind{Group: v1beta1.GroupName})
|
||||||
etcdOptions.SkipHealthEndpoints = true // avoid double wiring of health checks
|
etcdOptions.SkipHealthEndpoints = true // avoid double wiring of health checks
|
||||||
|
@ -24,9 +24,7 @@ import (
|
|||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
"k8s.io/apiserver/pkg/admission"
|
"k8s.io/apiserver/pkg/admission"
|
||||||
"k8s.io/apiserver/pkg/features"
|
|
||||||
"k8s.io/apiserver/pkg/server"
|
"k8s.io/apiserver/pkg/server"
|
||||||
"k8s.io/apiserver/pkg/util/feature"
|
|
||||||
"k8s.io/apiserver/pkg/util/webhook"
|
"k8s.io/apiserver/pkg/util/webhook"
|
||||||
"k8s.io/client-go/informers"
|
"k8s.io/client-go/informers"
|
||||||
|
|
||||||
@ -52,7 +50,7 @@ func CreateAPIExtensionsConfig(
|
|||||||
// we assume that the etcd options have been completed already. avoid messing with anything outside
|
// we assume that the etcd options have been completed already. avoid messing with anything outside
|
||||||
// of changes to StorageConfig as that may lead to unexpected behavior when the options are applied.
|
// of changes to StorageConfig as that may lead to unexpected behavior when the options are applied.
|
||||||
etcdOptions := *commandOptions.Etcd
|
etcdOptions := *commandOptions.Etcd
|
||||||
etcdOptions.StorageConfig.Paging = feature.DefaultFeatureGate.Enabled(features.APIListChunking)
|
etcdOptions.StorageConfig.Paging = true
|
||||||
// this is where the true decodable levels come from.
|
// this is where the true decodable levels come from.
|
||||||
etcdOptions.StorageConfig.Codec = apiextensionsapiserver.Codecs.LegacyCodec(v1beta1.SchemeGroupVersion, v1.SchemeGroupVersion)
|
etcdOptions.StorageConfig.Codec = apiextensionsapiserver.Codecs.LegacyCodec(v1beta1.SchemeGroupVersion, v1.SchemeGroupVersion)
|
||||||
// prefer the more compact serialization (v1beta1) for storage until https://issue.k8s.io/82292 is resolved for objects whose v1 serialization is too big but whose v1beta1 serialization can be stored
|
// prefer the more compact serialization (v1beta1) for storage until https://issue.k8s.io/82292 is resolved for objects whose v1 serialization is too big but whose v1beta1 serialization can be stored
|
||||||
|
@ -1209,7 +1209,7 @@ var defaultKubernetesFeatureGates = map[featuregate.Feature]featuregate.FeatureS
|
|||||||
|
|
||||||
genericfeatures.AggregatedDiscoveryEndpoint: {Default: true, PreRelease: featuregate.Beta},
|
genericfeatures.AggregatedDiscoveryEndpoint: {Default: true, PreRelease: featuregate.Beta},
|
||||||
|
|
||||||
genericfeatures.APIListChunking: {Default: true, PreRelease: featuregate.Beta},
|
genericfeatures.APIListChunking: {Default: true, PreRelease: featuregate.GA, LockToDefault: true}, // remove in 1.32
|
||||||
|
|
||||||
genericfeatures.APIPriorityAndFairness: {Default: true, PreRelease: featuregate.Beta},
|
genericfeatures.APIPriorityAndFairness: {Default: true, PreRelease: featuregate.Beta},
|
||||||
|
|
||||||
|
@ -54,6 +54,7 @@ const (
|
|||||||
// owner: @smarterclayton
|
// owner: @smarterclayton
|
||||||
// alpha: v1.8
|
// alpha: v1.8
|
||||||
// beta: v1.9
|
// beta: v1.9
|
||||||
|
// stable: 1.29
|
||||||
//
|
//
|
||||||
// Allow API clients to retrieve resource lists in chunks rather than
|
// Allow API clients to retrieve resource lists in chunks rather than
|
||||||
// all at once.
|
// all at once.
|
||||||
@ -153,6 +154,7 @@ const (
|
|||||||
// owner: @caesarxuchao
|
// owner: @caesarxuchao
|
||||||
// alpha: v1.15
|
// alpha: v1.15
|
||||||
// beta: v1.16
|
// beta: v1.16
|
||||||
|
// stable: 1.29
|
||||||
//
|
//
|
||||||
// Allow apiservers to show a count of remaining items in the response
|
// Allow apiservers to show a count of remaining items in the response
|
||||||
// to a chunking list request.
|
// to a chunking list request.
|
||||||
@ -238,7 +240,7 @@ var defaultKubernetesFeatureGates = map[featuregate.Feature]featuregate.FeatureS
|
|||||||
|
|
||||||
AdmissionWebhookMatchConditions: {Default: true, PreRelease: featuregate.Beta},
|
AdmissionWebhookMatchConditions: {Default: true, PreRelease: featuregate.Beta},
|
||||||
|
|
||||||
APIListChunking: {Default: true, PreRelease: featuregate.Beta},
|
APIListChunking: {Default: true, PreRelease: featuregate.GA, LockToDefault: true}, // remove in 1.32
|
||||||
|
|
||||||
APIPriorityAndFairness: {Default: true, PreRelease: featuregate.Beta},
|
APIPriorityAndFairness: {Default: true, PreRelease: featuregate.Beta},
|
||||||
|
|
||||||
@ -264,7 +266,7 @@ var defaultKubernetesFeatureGates = map[featuregate.Feature]featuregate.FeatureS
|
|||||||
|
|
||||||
OpenAPIV3: {Default: true, PreRelease: featuregate.GA, LockToDefault: true}, // remove in 1.29
|
OpenAPIV3: {Default: true, PreRelease: featuregate.GA, LockToDefault: true}, // remove in 1.29
|
||||||
|
|
||||||
RemainingItemCount: {Default: true, PreRelease: featuregate.Beta},
|
RemainingItemCount: {Default: true, PreRelease: featuregate.GA, LockToDefault: true}, // remove in 1.32
|
||||||
|
|
||||||
RemoveSelfLink: {Default: true, PreRelease: featuregate.GA, LockToDefault: true},
|
RemoveSelfLink: {Default: true, PreRelease: featuregate.GA, LockToDefault: true},
|
||||||
|
|
||||||
|
@ -25,9 +25,7 @@ import (
|
|||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
"k8s.io/apimachinery/pkg/util/sets"
|
"k8s.io/apimachinery/pkg/util/sets"
|
||||||
"k8s.io/apiserver/pkg/features"
|
|
||||||
"k8s.io/apiserver/pkg/storage/storagebackend"
|
"k8s.io/apiserver/pkg/storage/storagebackend"
|
||||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
|
||||||
"k8s.io/klog/v2"
|
"k8s.io/klog/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -156,7 +154,7 @@ func NewDefaultStorageFactory(
|
|||||||
resourceConfig APIResourceConfigSource,
|
resourceConfig APIResourceConfigSource,
|
||||||
specialDefaultResourcePrefixes map[schema.GroupResource]string,
|
specialDefaultResourcePrefixes map[schema.GroupResource]string,
|
||||||
) *DefaultStorageFactory {
|
) *DefaultStorageFactory {
|
||||||
config.Paging = utilfeature.DefaultFeatureGate.Enabled(features.APIListChunking)
|
config.Paging = true
|
||||||
if len(defaultMediaType) == 0 {
|
if len(defaultMediaType) == 0 {
|
||||||
defaultMediaType = runtime.ContentTypeJSON
|
defaultMediaType = runtime.ContentTypeJSON
|
||||||
}
|
}
|
||||||
@ -185,14 +183,6 @@ func (s *DefaultStorageFactory) SetEtcdPrefix(groupResource schema.GroupResource
|
|||||||
s.Overrides[groupResource] = overrides
|
s.Overrides[groupResource] = overrides
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetDisableAPIListChunking allows a specific resource to disable paging at the storage layer, to prevent
|
|
||||||
// exposure of key names in continuations. This may be overridden by feature gates.
|
|
||||||
func (s *DefaultStorageFactory) SetDisableAPIListChunking(groupResource schema.GroupResource) {
|
|
||||||
overrides := s.Overrides[groupResource]
|
|
||||||
overrides.disablePaging = true
|
|
||||||
s.Overrides[groupResource] = overrides
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetResourceEtcdPrefix sets the prefix for a resource, but not the base-dir. You'll end up in `etcdPrefix/resourceEtcdPrefix`.
|
// SetResourceEtcdPrefix sets the prefix for a resource, but not the base-dir. You'll end up in `etcdPrefix/resourceEtcdPrefix`.
|
||||||
func (s *DefaultStorageFactory) SetResourceEtcdPrefix(groupResource schema.GroupResource, prefix string) {
|
func (s *DefaultStorageFactory) SetResourceEtcdPrefix(groupResource schema.GroupResource, prefix string) {
|
||||||
overrides := s.Overrides[groupResource]
|
overrides := s.Overrides[groupResource]
|
||||||
|
@ -724,15 +724,14 @@ func shouldDelegateList(opts storage.ListOptions) bool {
|
|||||||
resourceVersion := opts.ResourceVersion
|
resourceVersion := opts.ResourceVersion
|
||||||
pred := opts.Predicate
|
pred := opts.Predicate
|
||||||
match := opts.ResourceVersionMatch
|
match := opts.ResourceVersionMatch
|
||||||
pagingEnabled := utilfeature.DefaultFeatureGate.Enabled(features.APIListChunking)
|
|
||||||
consistentListFromCacheEnabled := utilfeature.DefaultFeatureGate.Enabled(features.ConsistentListFromCache)
|
consistentListFromCacheEnabled := utilfeature.DefaultFeatureGate.Enabled(features.ConsistentListFromCache)
|
||||||
|
|
||||||
// Serve consistent reads from storage if ConsistentListFromCache is disabled
|
// Serve consistent reads from storage if ConsistentListFromCache is disabled
|
||||||
consistentReadFromStorage := resourceVersion == "" && !consistentListFromCacheEnabled
|
consistentReadFromStorage := resourceVersion == "" && !consistentListFromCacheEnabled
|
||||||
// Watch cache doesn't support continuations, so serve them from etcd.
|
// Watch cache doesn't support continuations, so serve them from etcd.
|
||||||
hasContinuation := pagingEnabled && len(pred.Continue) > 0
|
hasContinuation := len(pred.Continue) > 0
|
||||||
// Serve paginated requests about revision "0" from watch cache to avoid overwhelming etcd.
|
// Serve paginated requests about revision "0" from watch cache to avoid overwhelming etcd.
|
||||||
hasLimit := pagingEnabled && pred.Limit > 0 && resourceVersion != "0"
|
hasLimit := pred.Limit > 0 && resourceVersion != "0"
|
||||||
// Watch cache only supports ResourceVersionMatchNotOlderThan (default).
|
// Watch cache only supports ResourceVersionMatchNotOlderThan (default).
|
||||||
unsupportedMatch := match != "" && match != metav1.ResourceVersionMatchNotOlderThan
|
unsupportedMatch := match != "" && match != metav1.ResourceVersionMatchNotOlderThan
|
||||||
|
|
||||||
|
@ -39,11 +39,9 @@ import (
|
|||||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||||
"k8s.io/apimachinery/pkg/watch"
|
"k8s.io/apimachinery/pkg/watch"
|
||||||
"k8s.io/apiserver/pkg/audit"
|
"k8s.io/apiserver/pkg/audit"
|
||||||
"k8s.io/apiserver/pkg/features"
|
|
||||||
"k8s.io/apiserver/pkg/storage"
|
"k8s.io/apiserver/pkg/storage"
|
||||||
"k8s.io/apiserver/pkg/storage/etcd3/metrics"
|
"k8s.io/apiserver/pkg/storage/etcd3/metrics"
|
||||||
"k8s.io/apiserver/pkg/storage/value"
|
"k8s.io/apiserver/pkg/storage/value"
|
||||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
|
||||||
"k8s.io/component-base/tracing"
|
"k8s.io/component-base/tracing"
|
||||||
"k8s.io/klog/v2"
|
"k8s.io/klog/v2"
|
||||||
)
|
)
|
||||||
@ -840,11 +838,9 @@ func (s *store) GetList(ctx context.Context, key string, opts storage.ListOption
|
|||||||
// getResp.Count counts in objects that do not match the pred.
|
// getResp.Count counts in objects that do not match the pred.
|
||||||
// Instead of returning inaccurate count for non-empty selectors, we return nil.
|
// Instead of returning inaccurate count for non-empty selectors, we return nil.
|
||||||
// Only set remainingItemCount if the predicate is empty.
|
// Only set remainingItemCount if the predicate is empty.
|
||||||
if utilfeature.DefaultFeatureGate.Enabled(features.RemainingItemCount) {
|
if pred.Empty() {
|
||||||
if pred.Empty() {
|
c := int64(getResp.Count - pred.Limit)
|
||||||
c := int64(getResp.Count - pred.Limit)
|
remainingItemCount = &c
|
||||||
remainingItemCount = &c
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return s.versioner.UpdateList(listObj, uint64(returnedRV), next, remainingItemCount)
|
return s.versioner.UpdateList(listObj, uint64(returnedRV), next, remainingItemCount)
|
||||||
}
|
}
|
||||||
|
@ -36,11 +36,8 @@ import (
|
|||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
"k8s.io/apimachinery/pkg/watch"
|
"k8s.io/apimachinery/pkg/watch"
|
||||||
"k8s.io/apiserver/pkg/apis/example"
|
"k8s.io/apiserver/pkg/apis/example"
|
||||||
"k8s.io/apiserver/pkg/features"
|
|
||||||
"k8s.io/apiserver/pkg/storage"
|
"k8s.io/apiserver/pkg/storage"
|
||||||
"k8s.io/apiserver/pkg/storage/value"
|
"k8s.io/apiserver/pkg/storage/value"
|
||||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
|
||||||
featuregatetesting "k8s.io/component-base/featuregate/testing"
|
|
||||||
utilpointer "k8s.io/utils/pointer"
|
utilpointer "k8s.io/utils/pointer"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -479,8 +476,6 @@ func RunTestPreconditionalDeleteWithSuggestion(ctx context.Context, t *testing.T
|
|||||||
}
|
}
|
||||||
|
|
||||||
func RunTestList(ctx context.Context, t *testing.T, store storage.Interface, compaction Compaction, ignoreWatchCacheTests bool) {
|
func RunTestList(ctx context.Context, t *testing.T, store storage.Interface, compaction Compaction, ignoreWatchCacheTests bool) {
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.RemainingItemCount, true)()
|
|
||||||
|
|
||||||
initialRV, preset, err := seedMultiLevelData(ctx, store)
|
initialRV, preset, err := seedMultiLevelData(ctx, store)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@ -1113,8 +1108,6 @@ func RunTestList(ctx context.Context, t *testing.T, store storage.Interface, com
|
|||||||
}
|
}
|
||||||
|
|
||||||
func RunTestListWithoutPaging(ctx context.Context, t *testing.T, store storage.Interface) {
|
func RunTestListWithoutPaging(ctx context.Context, t *testing.T, store storage.Interface) {
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.RemainingItemCount, true)()
|
|
||||||
|
|
||||||
_, preset, err := seedMultiLevelData(ctx, store)
|
_, preset, err := seedMultiLevelData(ctx, store)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
@ -117,8 +117,7 @@ func (e *listWorkEstimator) estimate(r *http.Request, flowSchemaName, priorityLe
|
|||||||
}
|
}
|
||||||
|
|
||||||
limit := numStored
|
limit := numStored
|
||||||
if utilfeature.DefaultFeatureGate.Enabled(features.APIListChunking) && listOptions.Limit > 0 &&
|
if listOptions.Limit > 0 && listOptions.Limit < numStored {
|
||||||
listOptions.Limit < numStored {
|
|
||||||
limit = listOptions.Limit
|
limit = listOptions.Limit
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -165,15 +164,14 @@ func key(requestInfo *apirequest.RequestInfo) string {
|
|||||||
func shouldListFromStorage(query url.Values, opts *metav1.ListOptions) bool {
|
func shouldListFromStorage(query url.Values, opts *metav1.ListOptions) bool {
|
||||||
resourceVersion := opts.ResourceVersion
|
resourceVersion := opts.ResourceVersion
|
||||||
match := opts.ResourceVersionMatch
|
match := opts.ResourceVersionMatch
|
||||||
pagingEnabled := utilfeature.DefaultFeatureGate.Enabled(features.APIListChunking)
|
|
||||||
consistentListFromCacheEnabled := utilfeature.DefaultFeatureGate.Enabled(features.ConsistentListFromCache)
|
consistentListFromCacheEnabled := utilfeature.DefaultFeatureGate.Enabled(features.ConsistentListFromCache)
|
||||||
|
|
||||||
// Serve consistent reads from storage if ConsistentListFromCache is disabled
|
// Serve consistent reads from storage if ConsistentListFromCache is disabled
|
||||||
consistentReadFromStorage := resourceVersion == "" && !consistentListFromCacheEnabled
|
consistentReadFromStorage := resourceVersion == "" && !consistentListFromCacheEnabled
|
||||||
// Watch cache doesn't support continuations, so serve them from etcd.
|
// Watch cache doesn't support continuations, so serve them from etcd.
|
||||||
hasContinuation := pagingEnabled && len(opts.Continue) > 0
|
hasContinuation := len(opts.Continue) > 0
|
||||||
// Serve paginated requests about revision "0" from watch cache to avoid overwhelming etcd.
|
// Serve paginated requests about revision "0" from watch cache to avoid overwhelming etcd.
|
||||||
hasLimit := pagingEnabled && opts.Limit > 0 && resourceVersion != "0"
|
hasLimit := opts.Limit > 0 && resourceVersion != "0"
|
||||||
// Watch cache only supports ResourceVersionMatchNotOlderThan (default).
|
// Watch cache only supports ResourceVersionMatchNotOlderThan (default).
|
||||||
unsupportedMatch := match != "" && match != metav1.ResourceVersionMatchNotOlderThan
|
unsupportedMatch := match != "" && match != metav1.ResourceVersionMatchNotOlderThan
|
||||||
|
|
||||||
|
@ -123,7 +123,7 @@ func (o *WardleServerOptions) Config() (*apiserver.Config, error) {
|
|||||||
return nil, fmt.Errorf("error creating self-signed certificates: %v", err)
|
return nil, fmt.Errorf("error creating self-signed certificates: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
o.RecommendedOptions.Etcd.StorageConfig.Paging = utilfeature.DefaultFeatureGate.Enabled(features.APIListChunking)
|
o.RecommendedOptions.Etcd.StorageConfig.Paging = true
|
||||||
|
|
||||||
o.RecommendedOptions.ExtraAdmissionInitializers = func(c *genericapiserver.RecommendedConfig) ([]admission.PluginInitializer, error) {
|
o.RecommendedOptions.ExtraAdmissionInitializers = func(c *genericapiserver.RecommendedConfig) ([]admission.PluginInitializer, error) {
|
||||||
client, err := clientset.NewForConfig(c.LoopbackClientConfig)
|
client, err := clientset.NewForConfig(c.LoopbackClientConfig)
|
||||||
|
@ -19,7 +19,6 @@ package apimachinery
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/rand"
|
|
||||||
"reflect"
|
"reflect"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -30,18 +29,12 @@ import (
|
|||||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/util/wait"
|
"k8s.io/apimachinery/pkg/util/wait"
|
||||||
"k8s.io/apiserver/pkg/features"
|
|
||||||
"k8s.io/apiserver/pkg/storage/storagebackend"
|
"k8s.io/apiserver/pkg/storage/storagebackend"
|
||||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
|
||||||
"k8s.io/client-go/util/workqueue"
|
"k8s.io/client-go/util/workqueue"
|
||||||
"k8s.io/kubernetes/test/e2e/framework"
|
"k8s.io/kubernetes/test/e2e/framework"
|
||||||
admissionapi "k8s.io/pod-security-admission/api"
|
admissionapi "k8s.io/pod-security-admission/api"
|
||||||
)
|
)
|
||||||
|
|
||||||
func shouldCheckRemainingItem() bool {
|
|
||||||
return utilfeature.DefaultFeatureGate.Enabled(features.RemainingItemCount)
|
|
||||||
}
|
|
||||||
|
|
||||||
const numberOfTotalResources = 400
|
const numberOfTotalResources = 400
|
||||||
|
|
||||||
var _ = SIGDescribe("Servers with support for API chunking", func() {
|
var _ = SIGDescribe("Servers with support for API chunking", func() {
|
||||||
@ -76,7 +69,7 @@ var _ = SIGDescribe("Servers with support for API chunking", func() {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
ginkgo.It("should return chunks of results for list calls", func(ctx context.Context) {
|
framework.ConformanceIt("should return chunks of results for list calls", func(ctx context.Context) {
|
||||||
ns := f.Namespace.Name
|
ns := f.Namespace.Name
|
||||||
c := f.ClientSet
|
c := f.ClientSet
|
||||||
client := c.CoreV1().PodTemplates(ns)
|
client := c.CoreV1().PodTemplates(ns)
|
||||||
@ -86,7 +79,9 @@ var _ = SIGDescribe("Servers with support for API chunking", func() {
|
|||||||
found := 0
|
found := 0
|
||||||
var lastRV string
|
var lastRV string
|
||||||
for {
|
for {
|
||||||
opts.Limit = int64(rand.Int31n(numberOfTotalResources/10) + 1)
|
// With numberOfTotalResources=400, we want to ensure that both
|
||||||
|
// number of items per page and number of pages are non-trivial.
|
||||||
|
opts.Limit = 17
|
||||||
list, err := client.List(ctx, opts)
|
list, err := client.List(ctx, opts)
|
||||||
framework.ExpectNoError(err, "failed to list pod templates in namespace: %s, given limit: %d", ns, opts.Limit)
|
framework.ExpectNoError(err, "failed to list pod templates in namespace: %s, given limit: %d", ns, opts.Limit)
|
||||||
framework.Logf("Retrieved %d/%d results with rv %s and continue %s", len(list.Items), opts.Limit, list.ResourceVersion, list.Continue)
|
framework.Logf("Retrieved %d/%d results with rv %s and continue %s", len(list.Items), opts.Limit, list.ResourceVersion, list.Continue)
|
||||||
@ -96,13 +91,11 @@ var _ = SIGDescribe("Servers with support for API chunking", func() {
|
|||||||
lastRV = list.ResourceVersion
|
lastRV = list.ResourceVersion
|
||||||
}
|
}
|
||||||
framework.ExpectEqual(list.ResourceVersion, lastRV)
|
framework.ExpectEqual(list.ResourceVersion, lastRV)
|
||||||
if shouldCheckRemainingItem() {
|
if list.GetContinue() == "" {
|
||||||
if list.GetContinue() == "" {
|
gomega.Expect(list.GetRemainingItemCount()).To(gomega.BeNil())
|
||||||
gomega.Expect(list.GetRemainingItemCount()).To(gomega.BeNil())
|
} else {
|
||||||
} else {
|
gomega.Expect(list.GetRemainingItemCount()).ToNot(gomega.BeNil())
|
||||||
gomega.Expect(list.GetRemainingItemCount()).ToNot(gomega.BeNil())
|
gomega.Expect(int(*list.GetRemainingItemCount()) + len(list.Items) + found).To(gomega.BeNumerically("==", numberOfTotalResources))
|
||||||
gomega.Expect(int(*list.GetRemainingItemCount()) + len(list.Items) + found).To(gomega.BeNumerically("==", numberOfTotalResources))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
for _, item := range list.Items {
|
for _, item := range list.Items {
|
||||||
framework.ExpectEqual(item.Name, fmt.Sprintf("template-%04d", found))
|
framework.ExpectEqual(item.Name, fmt.Sprintf("template-%04d", found))
|
||||||
@ -123,7 +116,7 @@ var _ = SIGDescribe("Servers with support for API chunking", func() {
|
|||||||
gomega.Expect(list.Items).To(gomega.HaveLen(numberOfTotalResources))
|
gomega.Expect(list.Items).To(gomega.HaveLen(numberOfTotalResources))
|
||||||
})
|
})
|
||||||
|
|
||||||
ginkgo.It("should support continue listing from the last key if the original version has been compacted away, though the list is inconsistent [Slow]", func(ctx context.Context) {
|
framework.ConformanceIt("should support continue listing from the last key if the original version has been compacted away, though the list is inconsistent [Slow]", func(ctx context.Context) {
|
||||||
ns := f.Namespace.Name
|
ns := f.Namespace.Name
|
||||||
c := f.ClientSet
|
c := f.ClientSet
|
||||||
client := c.CoreV1().PodTemplates(ns)
|
client := c.CoreV1().PodTemplates(ns)
|
||||||
@ -136,13 +129,11 @@ var _ = SIGDescribe("Servers with support for API chunking", func() {
|
|||||||
framework.ExpectNoError(err, "failed to list pod templates in namespace: %s, given limit: %d", ns, opts.Limit)
|
framework.ExpectNoError(err, "failed to list pod templates in namespace: %s, given limit: %d", ns, opts.Limit)
|
||||||
firstToken := list.Continue
|
firstToken := list.Continue
|
||||||
firstRV := list.ResourceVersion
|
firstRV := list.ResourceVersion
|
||||||
if shouldCheckRemainingItem() {
|
if list.GetContinue() == "" {
|
||||||
if list.GetContinue() == "" {
|
gomega.Expect(list.GetRemainingItemCount()).To(gomega.BeNil())
|
||||||
gomega.Expect(list.GetRemainingItemCount()).To(gomega.BeNil())
|
} else {
|
||||||
} else {
|
gomega.Expect(list.GetRemainingItemCount()).ToNot(gomega.BeNil())
|
||||||
gomega.Expect(list.GetRemainingItemCount()).ToNot(gomega.BeNil())
|
gomega.Expect(int(*list.GetRemainingItemCount()) + len(list.Items)).To(gomega.BeNumerically("==", numberOfTotalResources))
|
||||||
gomega.Expect(int(*list.GetRemainingItemCount()) + len(list.Items)).To(gomega.BeNumerically("==", numberOfTotalResources))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
framework.Logf("Retrieved %d/%d results with rv %s and continue %s", len(list.Items), opts.Limit, list.ResourceVersion, firstToken)
|
framework.Logf("Retrieved %d/%d results with rv %s and continue %s", len(list.Items), opts.Limit, list.ResourceVersion, firstToken)
|
||||||
|
|
||||||
@ -179,13 +170,11 @@ var _ = SIGDescribe("Servers with support for API chunking", func() {
|
|||||||
gomega.Expect(len(list.Items)).To(gomega.BeNumerically("==", opts.Limit))
|
gomega.Expect(len(list.Items)).To(gomega.BeNumerically("==", opts.Limit))
|
||||||
found := int(oneTenth)
|
found := int(oneTenth)
|
||||||
|
|
||||||
if shouldCheckRemainingItem() {
|
if list.GetContinue() == "" {
|
||||||
if list.GetContinue() == "" {
|
gomega.Expect(list.GetRemainingItemCount()).To(gomega.BeNil())
|
||||||
gomega.Expect(list.GetRemainingItemCount()).To(gomega.BeNil())
|
} else {
|
||||||
} else {
|
gomega.Expect(list.GetRemainingItemCount()).ToNot(gomega.BeNil())
|
||||||
gomega.Expect(list.GetRemainingItemCount()).ToNot(gomega.BeNil())
|
gomega.Expect(int(*list.GetRemainingItemCount()) + len(list.Items) + found).To(gomega.BeNumerically("==", numberOfTotalResources))
|
||||||
gomega.Expect(int(*list.GetRemainingItemCount()) + len(list.Items) + found).To(gomega.BeNumerically("==", numberOfTotalResources))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
for _, item := range list.Items {
|
for _, item := range list.Items {
|
||||||
framework.ExpectEqual(item.Name, fmt.Sprintf("template-%04d", found))
|
framework.ExpectEqual(item.Name, fmt.Sprintf("template-%04d", found))
|
||||||
@ -198,13 +187,11 @@ var _ = SIGDescribe("Servers with support for API chunking", func() {
|
|||||||
for {
|
for {
|
||||||
list, err := client.List(ctx, opts)
|
list, err := client.List(ctx, opts)
|
||||||
framework.ExpectNoError(err, "failed to list pod templates in namespace: %s, given limit: %d", ns, opts.Limit)
|
framework.ExpectNoError(err, "failed to list pod templates in namespace: %s, given limit: %d", ns, opts.Limit)
|
||||||
if shouldCheckRemainingItem() {
|
if list.GetContinue() == "" {
|
||||||
if list.GetContinue() == "" {
|
gomega.Expect(list.GetRemainingItemCount()).To(gomega.BeNil())
|
||||||
gomega.Expect(list.GetRemainingItemCount()).To(gomega.BeNil())
|
} else {
|
||||||
} else {
|
gomega.Expect(list.GetRemainingItemCount()).ToNot(gomega.BeNil())
|
||||||
gomega.Expect(list.GetRemainingItemCount()).ToNot(gomega.BeNil())
|
gomega.Expect(int(*list.GetRemainingItemCount()) + len(list.Items) + found).To(gomega.BeNumerically("==", numberOfTotalResources))
|
||||||
gomega.Expect(int(*list.GetRemainingItemCount()) + len(list.Items) + found).To(gomega.BeNumerically("==", numberOfTotalResources))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
framework.Logf("Retrieved %d/%d results with rv %s and continue %s", len(list.Items), opts.Limit, list.ResourceVersion, list.Continue)
|
framework.Logf("Retrieved %d/%d results with rv %s and continue %s", len(list.Items), opts.Limit, list.ResourceVersion, list.Continue)
|
||||||
gomega.Expect(len(list.Items)).To(gomega.BeNumerically("<=", opts.Limit))
|
gomega.Expect(len(list.Items)).To(gomega.BeNumerically("<=", opts.Limit))
|
||||||
|
@ -53,16 +53,13 @@ import (
|
|||||||
"k8s.io/apimachinery/pkg/util/wait"
|
"k8s.io/apimachinery/pkg/util/wait"
|
||||||
"k8s.io/apimachinery/pkg/watch"
|
"k8s.io/apimachinery/pkg/watch"
|
||||||
"k8s.io/apiserver/pkg/endpoints/handlers"
|
"k8s.io/apiserver/pkg/endpoints/handlers"
|
||||||
"k8s.io/apiserver/pkg/features"
|
|
||||||
"k8s.io/apiserver/pkg/storage/storagebackend"
|
"k8s.io/apiserver/pkg/storage/storagebackend"
|
||||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
|
||||||
"k8s.io/client-go/dynamic"
|
"k8s.io/client-go/dynamic"
|
||||||
clientset "k8s.io/client-go/kubernetes"
|
clientset "k8s.io/client-go/kubernetes"
|
||||||
appsv1 "k8s.io/client-go/kubernetes/typed/apps/v1"
|
appsv1 "k8s.io/client-go/kubernetes/typed/apps/v1"
|
||||||
"k8s.io/client-go/metadata"
|
"k8s.io/client-go/metadata"
|
||||||
restclient "k8s.io/client-go/rest"
|
restclient "k8s.io/client-go/rest"
|
||||||
"k8s.io/client-go/tools/pager"
|
"k8s.io/client-go/tools/pager"
|
||||||
featuregatetesting "k8s.io/component-base/featuregate/testing"
|
|
||||||
"k8s.io/klog/v2"
|
"k8s.io/klog/v2"
|
||||||
"k8s.io/kubernetes/cmd/kube-apiserver/app/options"
|
"k8s.io/kubernetes/cmd/kube-apiserver/app/options"
|
||||||
kubeapiservertesting "k8s.io/kubernetes/cmd/kube-apiserver/app/testing"
|
kubeapiservertesting "k8s.io/kubernetes/cmd/kube-apiserver/app/testing"
|
||||||
@ -381,8 +378,6 @@ func TestListOptions(t *testing.T) {
|
|||||||
ctx, cancel := context.WithCancel(ctx)
|
ctx, cancel := context.WithCancel(ctx)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.APIListChunking, true)()
|
|
||||||
|
|
||||||
var storageTransport *storagebackend.TransportConfig
|
var storageTransport *storagebackend.TransportConfig
|
||||||
clientSet, _, tearDownFn := framework.StartTestServer(ctx, t, framework.TestServerSetup{
|
clientSet, _, tearDownFn := framework.StartTestServer(ctx, t, framework.TestServerSetup{
|
||||||
ModifyServerRunOptions: func(opts *options.ServerRunOptions) {
|
ModifyServerRunOptions: func(opts *options.ServerRunOptions) {
|
||||||
@ -576,9 +571,8 @@ func testListOptionsCase(t *testing.T, rsClient appsv1.ReplicaSetInterface, watc
|
|||||||
|
|
||||||
// Cacher.GetList defines this for logic to decide if the watch cache is skipped. We need to know it to know if
|
// Cacher.GetList defines this for logic to decide if the watch cache is skipped. We need to know it to know if
|
||||||
// the limit is respected when testing here.
|
// the limit is respected when testing here.
|
||||||
pagingEnabled := utilfeature.DefaultFeatureGate.Enabled(features.APIListChunking)
|
hasContinuation := len(opts.Continue) > 0
|
||||||
hasContinuation := pagingEnabled && len(opts.Continue) > 0
|
hasLimit := opts.Limit > 0 && opts.ResourceVersion != "0"
|
||||||
hasLimit := pagingEnabled && opts.Limit > 0 && opts.ResourceVersion != "0"
|
|
||||||
skipWatchCache := opts.ResourceVersion == "" || hasContinuation || hasLimit || isExact
|
skipWatchCache := opts.ResourceVersion == "" || hasContinuation || hasLimit || isExact
|
||||||
usingWatchCache := watchCacheEnabled && !skipWatchCache
|
usingWatchCache := watchCacheEnabled && !skipWatchCache
|
||||||
|
|
||||||
@ -627,8 +621,6 @@ func TestListResourceVersion0(t *testing.T) {
|
|||||||
ctx, cancel := context.WithCancel(ctx)
|
ctx, cancel := context.WithCancel(ctx)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.APIListChunking, true)()
|
|
||||||
|
|
||||||
clientSet, _, tearDownFn := framework.StartTestServer(ctx, t, framework.TestServerSetup{
|
clientSet, _, tearDownFn := framework.StartTestServer(ctx, t, framework.TestServerSetup{
|
||||||
ModifyServerRunOptions: func(opts *options.ServerRunOptions) {
|
ModifyServerRunOptions: func(opts *options.ServerRunOptions) {
|
||||||
opts.Etcd.EnableWatchCache = tc.watchCacheEnabled
|
opts.Etcd.EnableWatchCache = tc.watchCacheEnabled
|
||||||
@ -685,7 +677,6 @@ func TestListResourceVersion0(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestAPIListChunking(t *testing.T) {
|
func TestAPIListChunking(t *testing.T) {
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.APIListChunking, true)()
|
|
||||||
ctx, clientSet, _, tearDownFn := setup(t)
|
ctx, clientSet, _, tearDownFn := setup(t)
|
||||||
defer tearDownFn()
|
defer tearDownFn()
|
||||||
|
|
||||||
@ -753,7 +744,6 @@ func TestAPIListChunking(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestAPIListChunkingWithLabelSelector(t *testing.T) {
|
func TestAPIListChunkingWithLabelSelector(t *testing.T) {
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.APIListChunking, true)()
|
|
||||||
ctx, clientSet, _, tearDownFn := setup(t)
|
ctx, clientSet, _, tearDownFn := setup(t)
|
||||||
defer tearDownFn()
|
defer tearDownFn()
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user