mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-24 20:24:09 +00:00
Protect remainingItemCount behind a feature flag. Also updating the API doc
This commit is contained in:
parent
9577cbfb9f
commit
1ad5cb5bb1
@ -82,11 +82,18 @@ type ListMeta struct {
|
|||||||
// message.
|
// message.
|
||||||
Continue string `json:"continue,omitempty" protobuf:"bytes,3,opt,name=continue"`
|
Continue string `json:"continue,omitempty" protobuf:"bytes,3,opt,name=continue"`
|
||||||
|
|
||||||
// RemainingItemCount is the number of subsequent items in the list which are not included in this
|
// remainingItemCount is the number of subsequent items in the list which are not included in this
|
||||||
// list response. If the list request contained label or field selectors, then the number of
|
// list response. If the list request contained label or field selectors, then the number of
|
||||||
// remaining items is unknown and this field will be unset. If the list is complete (either
|
// remaining items is unknown and the field will be left unset and omitted during serialization.
|
||||||
// because it is unpaginated or because this is the last page), then there are no more remaining
|
// If the list is complete (either because it is not chunking or because this is the last chunk),
|
||||||
// items and this field will also be unset. Servers older than v1.15 do not set this field.
|
// then there are no more remaining items and this field will be left unset and omitted during
|
||||||
|
// serialization.
|
||||||
|
// Servers older than v1.15 do not set this field.
|
||||||
|
// The intended use of the remainingItemCount is *estimating* the size of a collection. Clients
|
||||||
|
// should not rely on the remainingItemCount to be set or to be exact.
|
||||||
|
//
|
||||||
|
// This field is alpha and can be changed or removed without notice.
|
||||||
|
//
|
||||||
// +optional
|
// +optional
|
||||||
RemainingItemCount *int64 `json:"remainingItemCount,omitempty" protobuf:"bytes,4,opt,name=remainingItemCount"`
|
RemainingItemCount *int64 `json:"remainingItemCount,omitempty" protobuf:"bytes,4,opt,name=remainingItemCount"`
|
||||||
}
|
}
|
||||||
|
@ -86,6 +86,13 @@ const (
|
|||||||
// committing.
|
// committing.
|
||||||
DryRun featuregate.Feature = "DryRun"
|
DryRun featuregate.Feature = "DryRun"
|
||||||
|
|
||||||
|
// owner: @caesarxuchao
|
||||||
|
// alpha: v1.15
|
||||||
|
//
|
||||||
|
// Allow apiservers to show a count of remaining items in the response
|
||||||
|
// to a chunking list request.
|
||||||
|
RemainingItemCount featuregate.Feature = "RemainingItemCount"
|
||||||
|
|
||||||
// owner: @apelisse, @lavalamp
|
// owner: @apelisse, @lavalamp
|
||||||
// alpha: v1.14
|
// alpha: v1.14
|
||||||
//
|
//
|
||||||
@ -140,6 +147,7 @@ var defaultKubernetesFeatureGates = map[featuregate.Feature]featuregate.FeatureS
|
|||||||
APIResponseCompression: {Default: false, PreRelease: featuregate.Alpha},
|
APIResponseCompression: {Default: false, PreRelease: featuregate.Alpha},
|
||||||
APIListChunking: {Default: true, PreRelease: featuregate.Beta},
|
APIListChunking: {Default: true, PreRelease: featuregate.Beta},
|
||||||
DryRun: {Default: true, PreRelease: featuregate.Beta},
|
DryRun: {Default: true, PreRelease: featuregate.Beta},
|
||||||
|
RemainingItemCount: {Default: false, PreRelease: featuregate.Alpha},
|
||||||
ServerSideApply: {Default: false, PreRelease: featuregate.Alpha},
|
ServerSideApply: {Default: false, PreRelease: featuregate.Alpha},
|
||||||
StorageVersionHash: {Default: false, PreRelease: featuregate.Alpha},
|
StorageVersionHash: {Default: false, PreRelease: featuregate.Alpha},
|
||||||
WinOverlay: {Default: false, PreRelease: featuregate.Alpha},
|
WinOverlay: {Default: false, PreRelease: featuregate.Alpha},
|
||||||
|
@ -36,10 +36,12 @@ import (
|
|||||||
"k8s.io/apimachinery/pkg/conversion"
|
"k8s.io/apimachinery/pkg/conversion"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
"k8s.io/apimachinery/pkg/watch"
|
"k8s.io/apimachinery/pkg/watch"
|
||||||
|
"k8s.io/apiserver/pkg/features"
|
||||||
"k8s.io/apiserver/pkg/storage"
|
"k8s.io/apiserver/pkg/storage"
|
||||||
"k8s.io/apiserver/pkg/storage/etcd"
|
"k8s.io/apiserver/pkg/storage/etcd"
|
||||||
"k8s.io/apiserver/pkg/storage/etcd/metrics"
|
"k8s.io/apiserver/pkg/storage/etcd/metrics"
|
||||||
"k8s.io/apiserver/pkg/storage/value"
|
"k8s.io/apiserver/pkg/storage/value"
|
||||||
|
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||||
utiltrace "k8s.io/utils/trace"
|
utiltrace "k8s.io/utils/trace"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -622,9 +624,11 @@ func (s *store) List(ctx context.Context, key, resourceVersion string, pred stor
|
|||||||
// 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 pred.Empty() {
|
if utilfeature.DefaultFeatureGate.Enabled(features.RemainingItemCount) {
|
||||||
c := int64(getResp.Count - pred.Limit)
|
if pred.Empty() {
|
||||||
remainingItemCount = &c
|
c := int64(getResp.Count - pred.Limit)
|
||||||
|
remainingItemCount = &c
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return s.versioner.UpdateList(listObj, uint64(returnedRV), next, remainingItemCount)
|
return s.versioner.UpdateList(listObj, uint64(returnedRV), next, remainingItemCount)
|
||||||
}
|
}
|
||||||
|
@ -43,10 +43,13 @@ import (
|
|||||||
"k8s.io/apimachinery/pkg/watch"
|
"k8s.io/apimachinery/pkg/watch"
|
||||||
"k8s.io/apiserver/pkg/apis/example"
|
"k8s.io/apiserver/pkg/apis/example"
|
||||||
examplev1 "k8s.io/apiserver/pkg/apis/example/v1"
|
examplev1 "k8s.io/apiserver/pkg/apis/example/v1"
|
||||||
|
"k8s.io/apiserver/pkg/features"
|
||||||
"k8s.io/apiserver/pkg/storage"
|
"k8s.io/apiserver/pkg/storage"
|
||||||
"k8s.io/apiserver/pkg/storage/etcd"
|
"k8s.io/apiserver/pkg/storage/etcd"
|
||||||
storagetests "k8s.io/apiserver/pkg/storage/tests"
|
storagetests "k8s.io/apiserver/pkg/storage/tests"
|
||||||
"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"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -751,6 +754,7 @@ func TestTransformationFailure(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestList(t *testing.T) {
|
func TestList(t *testing.T) {
|
||||||
|
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.RemainingItemCount, true)()
|
||||||
codec := apitesting.TestCodec(codecs, examplev1.SchemeGroupVersion)
|
codec := apitesting.TestCodec(codecs, examplev1.SchemeGroupVersion)
|
||||||
cluster := integration.NewClusterV3(t, &integration.ClusterConfig{Size: 1})
|
cluster := integration.NewClusterV3(t, &integration.ClusterConfig{Size: 1})
|
||||||
defer cluster.Terminate(t)
|
defer cluster.Terminate(t)
|
||||||
|
@ -30,12 +30,18 @@ import (
|
|||||||
"k8s.io/apimachinery/pkg/api/errors"
|
"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"
|
||||||
e2elog "k8s.io/kubernetes/test/e2e/framework/log"
|
e2elog "k8s.io/kubernetes/test/e2e/framework/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
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() {
|
||||||
@ -89,11 +95,13 @@ var _ = SIGDescribe("Servers with support for API chunking", func() {
|
|||||||
lastRV = list.ResourceVersion
|
lastRV = list.ResourceVersion
|
||||||
}
|
}
|
||||||
gomega.Expect(list.ResourceVersion).To(gomega.Equal(lastRV))
|
gomega.Expect(list.ResourceVersion).To(gomega.Equal(lastRV))
|
||||||
if list.GetContinue() == "" {
|
if shouldCheckRemainingItem() {
|
||||||
gomega.Expect(list.GetRemainingItemCount()).To(gomega.BeNil())
|
if list.GetContinue() == "" {
|
||||||
} else {
|
gomega.Expect(list.GetRemainingItemCount()).To(gomega.BeNil())
|
||||||
gomega.Expect(list.GetRemainingItemCount()).ToNot(gomega.BeNil())
|
} else {
|
||||||
gomega.Expect(int(*list.GetRemainingItemCount()) + len(list.Items) + found).To(gomega.BeNumerically("==", numberOfTotalResources))
|
gomega.Expect(list.GetRemainingItemCount()).ToNot(gomega.BeNil())
|
||||||
|
gomega.Expect(int(*list.GetRemainingItemCount()) + len(list.Items) + found).To(gomega.BeNumerically("==", numberOfTotalResources))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
for _, item := range list.Items {
|
for _, item := range list.Items {
|
||||||
gomega.Expect(item.Name).To(gomega.Equal(fmt.Sprintf("template-%04d", found)))
|
gomega.Expect(item.Name).To(gomega.Equal(fmt.Sprintf("template-%04d", found)))
|
||||||
@ -127,11 +135,13 @@ 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 list.GetContinue() == "" {
|
if shouldCheckRemainingItem() {
|
||||||
gomega.Expect(list.GetRemainingItemCount()).To(gomega.BeNil())
|
if list.GetContinue() == "" {
|
||||||
} else {
|
gomega.Expect(list.GetRemainingItemCount()).To(gomega.BeNil())
|
||||||
gomega.Expect(list.GetRemainingItemCount()).ToNot(gomega.BeNil())
|
} else {
|
||||||
gomega.Expect(int(*list.GetRemainingItemCount()) + len(list.Items)).To(gomega.BeNumerically("==", numberOfTotalResources))
|
gomega.Expect(list.GetRemainingItemCount()).ToNot(gomega.BeNil())
|
||||||
|
gomega.Expect(int(*list.GetRemainingItemCount()) + len(list.Items)).To(gomega.BeNumerically("==", numberOfTotalResources))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
e2elog.Logf("Retrieved %d/%d results with rv %s and continue %s", len(list.Items), opts.Limit, list.ResourceVersion, firstToken)
|
e2elog.Logf("Retrieved %d/%d results with rv %s and continue %s", len(list.Items), opts.Limit, list.ResourceVersion, firstToken)
|
||||||
|
|
||||||
@ -167,11 +177,14 @@ var _ = SIGDescribe("Servers with support for API chunking", func() {
|
|||||||
gomega.Expect(list.ResourceVersion).ToNot(gomega.Equal(firstRV))
|
gomega.Expect(list.ResourceVersion).ToNot(gomega.Equal(firstRV))
|
||||||
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 list.GetContinue() == "" {
|
|
||||||
gomega.Expect(list.GetRemainingItemCount()).To(gomega.BeNil())
|
if shouldCheckRemainingItem() {
|
||||||
} else {
|
if list.GetContinue() == "" {
|
||||||
gomega.Expect(list.GetRemainingItemCount()).ToNot(gomega.BeNil())
|
gomega.Expect(list.GetRemainingItemCount()).To(gomega.BeNil())
|
||||||
gomega.Expect(int(*list.GetRemainingItemCount()) + len(list.Items) + found).To(gomega.BeNumerically("==", numberOfTotalResources))
|
} else {
|
||||||
|
gomega.Expect(list.GetRemainingItemCount()).ToNot(gomega.BeNil())
|
||||||
|
gomega.Expect(int(*list.GetRemainingItemCount()) + len(list.Items) + found).To(gomega.BeNumerically("==", numberOfTotalResources))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
for _, item := range list.Items {
|
for _, item := range list.Items {
|
||||||
gomega.Expect(item.Name).To(gomega.Equal(fmt.Sprintf("template-%04d", found)))
|
gomega.Expect(item.Name).To(gomega.Equal(fmt.Sprintf("template-%04d", found)))
|
||||||
@ -184,11 +197,13 @@ var _ = SIGDescribe("Servers with support for API chunking", func() {
|
|||||||
for {
|
for {
|
||||||
list, err := client.List(opts)
|
list, err := client.List(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 list.GetContinue() == "" {
|
if shouldCheckRemainingItem() {
|
||||||
gomega.Expect(list.GetRemainingItemCount()).To(gomega.BeNil())
|
if list.GetContinue() == "" {
|
||||||
} else {
|
gomega.Expect(list.GetRemainingItemCount()).To(gomega.BeNil())
|
||||||
gomega.Expect(list.GetRemainingItemCount()).ToNot(gomega.BeNil())
|
} else {
|
||||||
gomega.Expect(int(*list.GetRemainingItemCount()) + len(list.Items) + found).To(gomega.BeNumerically("==", numberOfTotalResources))
|
gomega.Expect(list.GetRemainingItemCount()).ToNot(gomega.BeNil())
|
||||||
|
gomega.Expect(int(*list.GetRemainingItemCount()) + len(list.Items) + found).To(gomega.BeNumerically("==", numberOfTotalResources))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
e2elog.Logf("Retrieved %d/%d results with rv %s and continue %s", len(list.Items), opts.Limit, list.ResourceVersion, list.Continue)
|
e2elog.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))
|
||||||
|
Loading…
Reference in New Issue
Block a user