Skip to content

Commit 58c1830

Browse files
author
Chao Xu
committed
Add RemainingItemCount to ListMeta
1 parent 4b7c607 commit 58c1830

File tree

12 files changed

+95
-44
lines changed

12 files changed

+95
-44
lines changed

staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/meta.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,8 @@ type ListInterface interface {
9494
SetSelfLink(selfLink string)
9595
GetContinue() string
9696
SetContinue(c string)
97+
GetRemainingItemCount() int64
98+
SetRemainingItemCount(c int64)
9799
}
98100

99101
// Type exposes the type and APIVersion of versioned or internal API objects.
@@ -111,6 +113,8 @@ func (meta *ListMeta) GetSelfLink() string { return meta.SelfLink
111113
func (meta *ListMeta) SetSelfLink(selfLink string) { meta.SelfLink = selfLink }
112114
func (meta *ListMeta) GetContinue() string { return meta.Continue }
113115
func (meta *ListMeta) SetContinue(c string) { meta.Continue = c }
116+
func (meta *ListMeta) GetRemainingItemCount() int64 { return meta.RemainingItemCount }
117+
func (meta *ListMeta) SetRemainingItemCount(c int64) { meta.RemainingItemCount = c }
114118

115119
func (obj *TypeMeta) GetObjectKind() schema.ObjectKind { return obj }
116120

staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/types.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,14 @@ type ListMeta struct {
8181
// identical to the value in the first response, unless you have received this token from an error
8282
// message.
8383
Continue string `json:"continue,omitempty" protobuf:"bytes,3,opt,name=continue"`
84+
85+
// RemainingItemCount is the number of subsequent items in the list which are not included in this
86+
// list response. If the list request contained label or field selectors, then the number of
87+
// remaining items is unknown and this field will be unset. If the list is complete (either
88+
// because it is unpaginated or because this is the last page), then there are no more remaining
89+
// items and this field will also be unset. Servers older than v1.15 do not set this field.
90+
// +optional
91+
RemainingItemCount int64 `json:"remainingItemCount,omitempty" protobuf:"bytes,4,opt,name=remainingItemCount"`
8492
}
8593

8694
// These are internal finalizer values for Kubernetes-like APIs, must be qualified name unless defined here

staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured/helpers.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,14 @@ func getNestedString(obj map[string]interface{}, fields ...string) string {
275275
return val
276276
}
277277

278+
func getNestedInt64(obj map[string]interface{}, fields ...string) int64 {
279+
val, found, err := NestedInt64(obj, fields...)
280+
if !found || err != nil {
281+
return 0
282+
}
283+
return val
284+
}
285+
278286
func jsonPath(fields []string) string {
279287
return "." + strings.Join(fields, ".")
280288
}

staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured/unstructured_list.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,14 @@ func (u *UnstructuredList) SetContinue(c string) {
166166
u.setNestedField(c, "metadata", "continue")
167167
}
168168

169+
func (u *UnstructuredList) GetRemainingItemCount() int64 {
170+
return getNestedInt64(u.Object, "metadata", "remainingItemCount")
171+
}
172+
173+
func (u *UnstructuredList) SetRemainingItemCount(c int64) {
174+
u.setNestedField(c, "metadata", "remainingItemCount")
175+
}
176+
169177
func (u *UnstructuredList) SetGroupVersionKind(gvk schema.GroupVersionKind) {
170178
u.SetAPIVersion(gvk.GroupVersion().String())
171179
u.SetKind(gvk.Kind)

staging/src/k8s.io/apimachinery/pkg/test/api_meta_meta_test.go

Lines changed: 23 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -193,28 +193,31 @@ func TestGenericTypeMeta(t *testing.T) {
193193
}
194194

195195
type InternalTypeMeta struct {
196-
Kind string `json:"kind,omitempty"`
197-
Namespace string `json:"namespace,omitempty"`
198-
Name string `json:"name,omitempty"`
199-
GenerateName string `json:"generateName,omitempty"`
200-
UID string `json:"uid,omitempty"`
201-
CreationTimestamp metav1.Time `json:"creationTimestamp,omitempty"`
202-
SelfLink string `json:"selfLink,omitempty"`
203-
ResourceVersion string `json:"resourceVersion,omitempty"`
204-
Continue string `json:"next,omitempty"`
205-
APIVersion string `json:"apiVersion,omitempty"`
206-
Labels map[string]string `json:"labels,omitempty"`
207-
Annotations map[string]string `json:"annotations,omitempty"`
208-
Finalizers []string `json:"finalizers,omitempty"`
209-
OwnerReferences []metav1.OwnerReference `json:"ownerReferences,omitempty"`
196+
Kind string `json:"kind,omitempty"`
197+
Namespace string `json:"namespace,omitempty"`
198+
Name string `json:"name,omitempty"`
199+
GenerateName string `json:"generateName,omitempty"`
200+
UID string `json:"uid,omitempty"`
201+
CreationTimestamp metav1.Time `json:"creationTimestamp,omitempty"`
202+
SelfLink string `json:"selfLink,omitempty"`
203+
ResourceVersion string `json:"resourceVersion,omitempty"`
204+
Continue string `json:"next,omitempty"`
205+
RemainingItemCount int64 `json:"remainingItemCount,omitempty"`
206+
APIVersion string `json:"apiVersion,omitempty"`
207+
Labels map[string]string `json:"labels,omitempty"`
208+
Annotations map[string]string `json:"annotations,omitempty"`
209+
Finalizers []string `json:"finalizers,omitempty"`
210+
OwnerReferences []metav1.OwnerReference `json:"ownerReferences,omitempty"`
210211
}
211212

212-
func (m *InternalTypeMeta) GetResourceVersion() string { return m.ResourceVersion }
213-
func (m *InternalTypeMeta) SetResourceVersion(rv string) { m.ResourceVersion = rv }
214-
func (m *InternalTypeMeta) GetSelfLink() string { return m.SelfLink }
215-
func (m *InternalTypeMeta) SetSelfLink(link string) { m.SelfLink = link }
216-
func (m *InternalTypeMeta) GetContinue() string { return m.Continue }
217-
func (m *InternalTypeMeta) SetContinue(c string) { m.Continue = c }
213+
func (m *InternalTypeMeta) GetResourceVersion() string { return m.ResourceVersion }
214+
func (m *InternalTypeMeta) SetResourceVersion(rv string) { m.ResourceVersion = rv }
215+
func (m *InternalTypeMeta) GetSelfLink() string { return m.SelfLink }
216+
func (m *InternalTypeMeta) SetSelfLink(link string) { m.SelfLink = link }
217+
func (m *InternalTypeMeta) GetContinue() string { return m.Continue }
218+
func (m *InternalTypeMeta) SetContinue(c string) { m.Continue = c }
219+
func (m *InternalTypeMeta) GetRemainingItemCount() int64 { return m.RemainingItemCount }
220+
func (m *InternalTypeMeta) SetRemainingItemCount(c int64) { m.RemainingItemCount = c }
218221

219222
type MyAPIObject struct {
220223
TypeMeta InternalTypeMeta `json:",inline"`

staging/src/k8s.io/apiserver/pkg/storage/cacher/cacher.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -604,7 +604,7 @@ func (c *Cacher) GetToList(ctx context.Context, key string, resourceVersion stri
604604
}
605605
}
606606
if c.versioner != nil {
607-
if err := c.versioner.UpdateList(listObj, readResourceVersion, ""); err != nil {
607+
if err := c.versioner.UpdateList(listObj, readResourceVersion, "", 0); err != nil {
608608
return err
609609
}
610610
}
@@ -679,7 +679,7 @@ func (c *Cacher) List(ctx context.Context, key string, resourceVersion string, p
679679
}
680680
trace.Step(fmt.Sprintf("Filtered %d items", listVal.Len()))
681681
if c.versioner != nil {
682-
if err := c.versioner.UpdateList(listObj, readResourceVersion, ""); err != nil {
682+
if err := c.versioner.UpdateList(listObj, readResourceVersion, "", 0); err != nil {
683683
return err
684684
}
685685
}

staging/src/k8s.io/apiserver/pkg/storage/cacher/cacher_whitebox_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -217,13 +217,14 @@ type testVersioner struct{}
217217
func (testVersioner) UpdateObject(obj runtime.Object, resourceVersion uint64) error {
218218
return meta.NewAccessor().SetResourceVersion(obj, strconv.FormatUint(resourceVersion, 10))
219219
}
220-
func (testVersioner) UpdateList(obj runtime.Object, resourceVersion uint64, continueValue string) error {
220+
func (testVersioner) UpdateList(obj runtime.Object, resourceVersion uint64, continueValue string, count int64) error {
221221
listAccessor, err := meta.ListAccessor(obj)
222222
if err != nil || listAccessor == nil {
223223
return err
224224
}
225225
listAccessor.SetResourceVersion(strconv.FormatUint(resourceVersion, 10))
226226
listAccessor.SetContinue(continueValue)
227+
listAccessor.SetRemainingItemCount(count)
227228
return nil
228229
}
229230
func (testVersioner) PrepareObjectForStorage(obj runtime.Object) error {

staging/src/k8s.io/apiserver/pkg/storage/etcd/api_object_versioner.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ func (a APIObjectVersioner) UpdateObject(obj runtime.Object, resourceVersion uin
4444
}
4545

4646
// UpdateList implements Versioner
47-
func (a APIObjectVersioner) UpdateList(obj runtime.Object, resourceVersion uint64, nextKey string) error {
47+
func (a APIObjectVersioner) UpdateList(obj runtime.Object, resourceVersion uint64, nextKey string, count int64) error {
4848
listAccessor, err := meta.ListAccessor(obj)
4949
if err != nil || listAccessor == nil {
5050
return err
@@ -55,6 +55,7 @@ func (a APIObjectVersioner) UpdateList(obj runtime.Object, resourceVersion uint6
5555
}
5656
listAccessor.SetResourceVersion(versionString)
5757
listAccessor.SetContinue(nextKey)
58+
listAccessor.SetRemainingItemCount(count)
5859
return nil
5960
}
6061

staging/src/k8s.io/apiserver/pkg/storage/etcd3/store.go

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -413,7 +413,7 @@ func (s *store) GetToList(ctx context.Context, key string, resourceVersion strin
413413
}
414414
}
415415
// update version with cluster level revision
416-
return s.versioner.UpdateList(listObj, uint64(getResp.Header.Revision), "")
416+
return s.versioner.UpdateList(listObj, uint64(getResp.Header.Revision), "", 0)
417417
}
418418

419419
func (s *store) Count(key string) (int64, error) {
@@ -576,9 +576,10 @@ func (s *store) List(ctx context.Context, key, resourceVersion string, pred stor
576576
// loop until we have filled the requested limit from etcd or there are no more results
577577
var lastKey []byte
578578
var hasMore bool
579+
var getResp *clientv3.GetResponse
579580
for {
580581
startTime := time.Now()
581-
getResp, err := s.client.KV.Get(ctx, key, options...)
582+
getResp, err = s.client.KV.Get(ctx, key, options...)
582583
metrics.RecordEtcdRequestLatency("list", getTypeName(listPtr), startTime)
583584
if err != nil {
584585
return interpretListError(err, len(pred.Continue) > 0, continueKey, keyPrefix)
@@ -639,11 +640,17 @@ func (s *store) List(ctx context.Context, key, resourceVersion string, pred stor
639640
if err != nil {
640641
return err
641642
}
642-
return s.versioner.UpdateList(listObj, uint64(returnedRV), next)
643+
remainingItemCount := getResp.Count - pred.Limit
644+
// getResp.Count counts in objects that do not match the pred.
645+
// Instead of returning inaccurate count, return 0.
646+
if !pred.Empty() {
647+
remainingItemCount = 0
648+
}
649+
return s.versioner.UpdateList(listObj, uint64(returnedRV), next, remainingItemCount)
643650
}
644651

645652
// no continuation
646-
return s.versioner.UpdateList(listObj, uint64(returnedRV), "")
653+
return s.versioner.UpdateList(listObj, uint64(returnedRV), "", 0)
647654
}
648655

649656
// growSlice takes a slice value and grows its capacity up

staging/src/k8s.io/apiserver/pkg/storage/etcd3/store_test.go

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -825,14 +825,15 @@ func TestList(t *testing.T) {
825825
}
826826

827827
tests := []struct {
828-
name string
829-
disablePaging bool
830-
rv string
831-
prefix string
832-
pred storage.SelectionPredicate
833-
expectedOut []*example.Pod
834-
expectContinue bool
835-
expectError bool
828+
name string
829+
disablePaging bool
830+
rv string
831+
prefix string
832+
pred storage.SelectionPredicate
833+
expectedOut []*example.Pod
834+
expectContinue bool
835+
expectedRemainingItemCount int64
836+
expectError bool
836837
}{
837838
{
838839
name: "rejects invalid resource version",
@@ -882,8 +883,9 @@ func TestList(t *testing.T) {
882883
Field: fields.Everything(),
883884
Limit: 1,
884885
},
885-
expectedOut: []*example.Pod{preset[1].storedObj},
886-
expectContinue: true,
886+
expectedOut: []*example.Pod{preset[1].storedObj},
887+
expectContinue: true,
888+
expectedRemainingItemCount: 1,
887889
},
888890
{
889891
name: "test List with limit when paging disabled",
@@ -1061,6 +1063,9 @@ func TestList(t *testing.T) {
10611063
t.Errorf("(%s): length of list want=%d, got=%d", tt.name, len(tt.expectedOut), len(out.Items))
10621064
continue
10631065
}
1066+
if e, a := tt.expectedRemainingItemCount, out.ListMeta.RemainingItemCount; e != a {
1067+
t.Errorf("(%s): remainingItemCount want=%d, got=%d", tt.name, e, a)
1068+
}
10641069
for j, wantPod := range tt.expectedOut {
10651070
getPod := &out.Items[j]
10661071
if !reflect.DeepEqual(wantPod, getPod) {

0 commit comments

Comments
 (0)