diff --git a/pkg/printers/tablegenerator.go b/pkg/printers/tablegenerator.go index 7f4da18f1ef..a9d62c45a61 100644 --- a/pkg/printers/tablegenerator.go +++ b/pkg/printers/tablegenerator.go @@ -112,7 +112,7 @@ func (h *HumanReadablePrinter) GenerateTable(obj runtime.Object, options PrintOp table.ResourceVersion = m.GetResourceVersion() table.SelfLink = m.GetSelfLink() table.Continue = m.GetContinue() - table.SetRemainingItemCount(m.GetRemainingItemCount()) + table.RemainingItemCount = m.GetRemainingItemCount() } else { if m, err := meta.CommonAccessor(obj); err == nil { table.ResourceVersion = m.GetResourceVersion() diff --git a/staging/src/k8s.io/apiextensions-apiserver/pkg/registry/customresource/tableconvertor/tableconvertor.go b/staging/src/k8s.io/apiextensions-apiserver/pkg/registry/customresource/tableconvertor/tableconvertor.go index 587ec8284e6..7e0d64e7abf 100644 --- a/staging/src/k8s.io/apiextensions-apiserver/pkg/registry/customresource/tableconvertor/tableconvertor.go +++ b/staging/src/k8s.io/apiextensions-apiserver/pkg/registry/customresource/tableconvertor/tableconvertor.go @@ -87,7 +87,7 @@ func (c *convertor) ConvertToTable(ctx context.Context, obj runtime.Object, tabl table.ResourceVersion = m.GetResourceVersion() table.SelfLink = m.GetSelfLink() table.Continue = m.GetContinue() - table.SetRemainingItemCount(m.GetRemainingItemCount()) + table.RemainingItemCount = m.GetRemainingItemCount() } else { if m, err := meta.CommonAccessor(obj); err == nil { table.ResourceVersion = m.GetResourceVersion() diff --git a/staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/meta.go b/staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/meta.go index 8de14872b85..37141bd5dac 100644 --- a/staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/meta.go +++ b/staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/meta.go @@ -94,8 +94,8 @@ type ListInterface interface { SetSelfLink(selfLink string) GetContinue() string SetContinue(c string) - GetRemainingItemCount() int64 - SetRemainingItemCount(c int64) + GetRemainingItemCount() *int64 + SetRemainingItemCount(c *int64) } // Type exposes the type and APIVersion of versioned or internal API objects. @@ -115,20 +115,8 @@ func (meta *ListMeta) GetSelfLink() string { return meta.SelfLink func (meta *ListMeta) SetSelfLink(selfLink string) { meta.SelfLink = selfLink } func (meta *ListMeta) GetContinue() string { return meta.Continue } func (meta *ListMeta) SetContinue(c string) { meta.Continue = c } - -func (meta *ListMeta) GetRemainingItemCount() int64 { - if meta.RemainingItemCount != nil { - return *meta.RemainingItemCount - } - return 0 -} - -func (meta *ListMeta) SetRemainingItemCount(c int64) { - if meta.RemainingItemCount == nil { - meta.RemainingItemCount = new(int64) - } - *meta.RemainingItemCount = c -} +func (meta *ListMeta) GetRemainingItemCount() *int64 { return meta.RemainingItemCount } +func (meta *ListMeta) SetRemainingItemCount(c *int64) { meta.RemainingItemCount = c } func (obj *TypeMeta) GetObjectKind() schema.ObjectKind { return obj } diff --git a/staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured/helpers.go b/staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured/helpers.go index a90849176b3..3b07e86db8f 100644 --- a/staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured/helpers.go +++ b/staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured/helpers.go @@ -283,6 +283,14 @@ func getNestedInt64(obj map[string]interface{}, fields ...string) int64 { return val } +func getNestedInt64Pointer(obj map[string]interface{}, fields ...string) *int64 { + val, found, err := NestedInt64(obj, fields...) + if !found || err != nil { + return nil + } + return &val +} + func jsonPath(fields []string) string { return "." + strings.Join(fields, ".") } diff --git a/staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured/unstructured.go b/staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured/unstructured.go index 63d5b9fcfe5..8b35a21cbcb 100644 --- a/staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured/unstructured.go +++ b/staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured/unstructured.go @@ -320,12 +320,16 @@ func (u *Unstructured) SetContinue(c string) { u.setNestedField(c, "metadata", "continue") } -func (u *Unstructured) GetRemainingItemCount() int64 { - return getNestedInt64(u.Object, "metadata", "remainingItemCount") +func (u *Unstructured) GetRemainingItemCount() *int64 { + return getNestedInt64Pointer(u.Object, "metadata", "remainingItemCount") } -func (u *Unstructured) SetRemainingItemCount(c int64) { - u.setNestedField(c, "metadata", "remainingItemCount") +func (u *Unstructured) SetRemainingItemCount(c *int64) { + if c == nil { + RemoveNestedField(u.Object, "metadata", "remainingItemCount") + } else { + u.setNestedField(*c, "metadata", "remainingItemCount") + } } func (u *Unstructured) GetCreationTimestamp() metav1.Time { diff --git a/staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured/unstructured_list.go b/staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured/unstructured_list.go index f0c204899ad..44d897c377d 100644 --- a/staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured/unstructured_list.go +++ b/staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured/unstructured_list.go @@ -166,12 +166,16 @@ func (u *UnstructuredList) SetContinue(c string) { u.setNestedField(c, "metadata", "continue") } -func (u *UnstructuredList) GetRemainingItemCount() int64 { - return getNestedInt64(u.Object, "metadata", "remainingItemCount") +func (u *UnstructuredList) GetRemainingItemCount() *int64 { + return getNestedInt64Pointer(u.Object, "metadata", "remainingItemCount") } -func (u *UnstructuredList) SetRemainingItemCount(c int64) { - u.setNestedField(c, "metadata", "remainingItemCount") +func (u *UnstructuredList) SetRemainingItemCount(c *int64) { + if c == nil { + RemoveNestedField(u.Object, "metadata", "remainingItemCount") + } else { + u.setNestedField(*c, "metadata", "remainingItemCount") + } } func (u *UnstructuredList) SetGroupVersionKind(gvk schema.GroupVersionKind) { diff --git a/staging/src/k8s.io/apimachinery/pkg/test/api_meta_meta_test.go b/staging/src/k8s.io/apimachinery/pkg/test/api_meta_meta_test.go index 74496f264b6..49fcf8702c2 100644 --- a/staging/src/k8s.io/apimachinery/pkg/test/api_meta_meta_test.go +++ b/staging/src/k8s.io/apimachinery/pkg/test/api_meta_meta_test.go @@ -209,26 +209,14 @@ type InternalTypeMeta struct { OwnerReferences []metav1.OwnerReference `json:"ownerReferences,omitempty"` } -func (m *InternalTypeMeta) GetResourceVersion() string { return m.ResourceVersion } -func (m *InternalTypeMeta) SetResourceVersion(rv string) { m.ResourceVersion = rv } -func (m *InternalTypeMeta) GetSelfLink() string { return m.SelfLink } -func (m *InternalTypeMeta) SetSelfLink(link string) { m.SelfLink = link } -func (m *InternalTypeMeta) GetContinue() string { return m.Continue } -func (m *InternalTypeMeta) SetContinue(c string) { m.Continue = c } - -func (m *InternalTypeMeta) GetRemainingItemCount() int64 { - if m.RemainingItemCount != nil { - return *m.RemainingItemCount - } - return 0 -} - -func (m *InternalTypeMeta) SetRemainingItemCount(c int64) { - if m.RemainingItemCount == nil { - m.RemainingItemCount = new(int64) - } - *m.RemainingItemCount = c -} +func (m *InternalTypeMeta) GetResourceVersion() string { return m.ResourceVersion } +func (m *InternalTypeMeta) SetResourceVersion(rv string) { m.ResourceVersion = rv } +func (m *InternalTypeMeta) GetSelfLink() string { return m.SelfLink } +func (m *InternalTypeMeta) SetSelfLink(link string) { m.SelfLink = link } +func (m *InternalTypeMeta) GetContinue() string { return m.Continue } +func (m *InternalTypeMeta) SetContinue(c string) { m.Continue = c } +func (m *InternalTypeMeta) GetRemainingItemCount() *int64 { return m.RemainingItemCount } +func (m *InternalTypeMeta) SetRemainingItemCount(c *int64) { m.RemainingItemCount = c } type MyAPIObject struct { TypeMeta InternalTypeMeta `json:",inline"` diff --git a/staging/src/k8s.io/apiserver/pkg/registry/rest/table.go b/staging/src/k8s.io/apiserver/pkg/registry/rest/table.go index f6a8fdd1e22..684f4acdcba 100644 --- a/staging/src/k8s.io/apiserver/pkg/registry/rest/table.go +++ b/staging/src/k8s.io/apiserver/pkg/registry/rest/table.go @@ -67,7 +67,7 @@ func (c defaultTableConvertor) ConvertToTable(ctx context.Context, object runtim table.ResourceVersion = m.GetResourceVersion() table.SelfLink = m.GetSelfLink() table.Continue = m.GetContinue() - table.SetRemainingItemCount(m.GetRemainingItemCount()) + table.RemainingItemCount = m.GetRemainingItemCount() } else { if m, err := meta.CommonAccessor(object); err == nil { table.ResourceVersion = m.GetResourceVersion() diff --git a/staging/src/k8s.io/apiserver/pkg/storage/cacher/cacher.go b/staging/src/k8s.io/apiserver/pkg/storage/cacher/cacher.go index b10582c0dea..97e1e42f11b 100644 --- a/staging/src/k8s.io/apiserver/pkg/storage/cacher/cacher.go +++ b/staging/src/k8s.io/apiserver/pkg/storage/cacher/cacher.go @@ -605,7 +605,7 @@ func (c *Cacher) GetToList(ctx context.Context, key string, resourceVersion stri } } if c.versioner != nil { - if err := c.versioner.UpdateList(listObj, readResourceVersion, "", 0); err != nil { + if err := c.versioner.UpdateList(listObj, readResourceVersion, "", nil); err != nil { return err } } @@ -680,7 +680,7 @@ func (c *Cacher) List(ctx context.Context, key string, resourceVersion string, p } trace.Step(fmt.Sprintf("Filtered %d items", listVal.Len())) if c.versioner != nil { - if err := c.versioner.UpdateList(listObj, readResourceVersion, "", 0); err != nil { + if err := c.versioner.UpdateList(listObj, readResourceVersion, "", nil); err != nil { return err } } diff --git a/staging/src/k8s.io/apiserver/pkg/storage/cacher/cacher_whitebox_test.go b/staging/src/k8s.io/apiserver/pkg/storage/cacher/cacher_whitebox_test.go index 0be7d887992..4e51a6979b5 100644 --- a/staging/src/k8s.io/apiserver/pkg/storage/cacher/cacher_whitebox_test.go +++ b/staging/src/k8s.io/apiserver/pkg/storage/cacher/cacher_whitebox_test.go @@ -217,7 +217,7 @@ type testVersioner struct{} func (testVersioner) UpdateObject(obj runtime.Object, resourceVersion uint64) error { return meta.NewAccessor().SetResourceVersion(obj, strconv.FormatUint(resourceVersion, 10)) } -func (testVersioner) UpdateList(obj runtime.Object, resourceVersion uint64, continueValue string, count int64) error { +func (testVersioner) UpdateList(obj runtime.Object, resourceVersion uint64, continueValue string, count *int64) error { listAccessor, err := meta.ListAccessor(obj) if err != nil || listAccessor == nil { return err diff --git a/staging/src/k8s.io/apiserver/pkg/storage/etcd/api_object_versioner.go b/staging/src/k8s.io/apiserver/pkg/storage/etcd/api_object_versioner.go index dc2b35da091..cfae4693972 100644 --- a/staging/src/k8s.io/apiserver/pkg/storage/etcd/api_object_versioner.go +++ b/staging/src/k8s.io/apiserver/pkg/storage/etcd/api_object_versioner.go @@ -44,7 +44,7 @@ func (a APIObjectVersioner) UpdateObject(obj runtime.Object, resourceVersion uin } // UpdateList implements Versioner -func (a APIObjectVersioner) UpdateList(obj runtime.Object, resourceVersion uint64, nextKey string, count int64) error { +func (a APIObjectVersioner) UpdateList(obj runtime.Object, resourceVersion uint64, nextKey string, count *int64) error { listAccessor, err := meta.ListAccessor(obj) if err != nil || listAccessor == nil { return err diff --git a/staging/src/k8s.io/apiserver/pkg/storage/etcd3/BUILD b/staging/src/k8s.io/apiserver/pkg/storage/etcd3/BUILD index 7927c782e95..f03ccf7060b 100644 --- a/staging/src/k8s.io/apiserver/pkg/storage/etcd3/BUILD +++ b/staging/src/k8s.io/apiserver/pkg/storage/etcd3/BUILD @@ -41,6 +41,7 @@ go_test( "//vendor/github.com/coreos/pkg/capnslog:go_default_library", "//vendor/github.com/stretchr/testify/assert:go_default_library", "//vendor/github.com/stretchr/testify/require:go_default_library", + "//vendor/k8s.io/utils/pointer:go_default_library", ], ) diff --git a/staging/src/k8s.io/apiserver/pkg/storage/etcd3/store.go b/staging/src/k8s.io/apiserver/pkg/storage/etcd3/store.go index 349c0a5e4dc..c9ca0a976be 100644 --- a/staging/src/k8s.io/apiserver/pkg/storage/etcd3/store.go +++ b/staging/src/k8s.io/apiserver/pkg/storage/etcd3/store.go @@ -391,7 +391,7 @@ func (s *store) GetToList(ctx context.Context, key string, resourceVersion strin } } // update version with cluster level revision - return s.versioner.UpdateList(listObj, uint64(getResp.Header.Revision), "", 0) + return s.versioner.UpdateList(listObj, uint64(getResp.Header.Revision), "", nil) } func (s *store) Count(key string) (int64, error) { @@ -618,17 +618,19 @@ func (s *store) List(ctx context.Context, key, resourceVersion string, pred stor if err != nil { return err } - remainingItemCount := getResp.Count - pred.Limit + var remainingItemCount *int64 // getResp.Count counts in objects that do not match the pred. - // Instead of returning inaccurate count, return 0. - if !pred.Empty() { - remainingItemCount = 0 + // Instead of returning inaccurate count for non-empty selectors, we return nil. + // Only set remainingItemCount if the predicate is empty. + if pred.Empty() { + c := int64(getResp.Count - pred.Limit) + remainingItemCount = &c } return s.versioner.UpdateList(listObj, uint64(returnedRV), next, remainingItemCount) } // no continuation - return s.versioner.UpdateList(listObj, uint64(returnedRV), "", 0) + return s.versioner.UpdateList(listObj, uint64(returnedRV), "", nil) } // growSlice takes a slice value and grows its capacity up diff --git a/staging/src/k8s.io/apiserver/pkg/storage/etcd3/store_test.go b/staging/src/k8s.io/apiserver/pkg/storage/etcd3/store_test.go index 0dec9f27d64..72ed4227b2b 100644 --- a/staging/src/k8s.io/apiserver/pkg/storage/etcd3/store_test.go +++ b/staging/src/k8s.io/apiserver/pkg/storage/etcd3/store_test.go @@ -47,6 +47,7 @@ import ( "k8s.io/apiserver/pkg/storage/etcd" storagetests "k8s.io/apiserver/pkg/storage/tests" "k8s.io/apiserver/pkg/storage/value" + utilpointer "k8s.io/utils/pointer" ) var scheme = runtime.NewScheme() @@ -831,7 +832,7 @@ func TestList(t *testing.T) { pred storage.SelectionPredicate expectedOut []*example.Pod expectContinue bool - expectedRemainingItemCount int64 + expectedRemainingItemCount *int64 expectError bool }{ { @@ -884,7 +885,7 @@ func TestList(t *testing.T) { }, expectedOut: []*example.Pod{preset[1].storedObj}, expectContinue: true, - expectedRemainingItemCount: 1, + expectedRemainingItemCount: utilpointer.Int64Ptr(1), }, { name: "test List with limit when paging disabled", @@ -1062,8 +1063,8 @@ func TestList(t *testing.T) { t.Errorf("(%s): length of list want=%d, got=%d", tt.name, len(tt.expectedOut), len(out.Items)) continue } - if e, a := tt.expectedRemainingItemCount, out.ListMeta.GetRemainingItemCount(); e != a { - t.Errorf("(%s): remainingItemCount want=%d, got=%d", tt.name, e, a) + if e, a := tt.expectedRemainingItemCount, out.ListMeta.GetRemainingItemCount(); (e == nil) != (a == nil) || (e != nil && a != nil && *e != *a) { + t.Errorf("(%s): remainingItemCount want=%#v, got=%#v", tt.name, e, a) } for j, wantPod := range tt.expectedOut { getPod := &out.Items[j] diff --git a/staging/src/k8s.io/apiserver/pkg/storage/interfaces.go b/staging/src/k8s.io/apiserver/pkg/storage/interfaces.go index 1e78be8da5f..e649c87f5aa 100644 --- a/staging/src/k8s.io/apiserver/pkg/storage/interfaces.go +++ b/staging/src/k8s.io/apiserver/pkg/storage/interfaces.go @@ -44,8 +44,8 @@ type Versioner interface { // database. continueValue is optional and indicates that more results are available if the client // passes that value to the server in a subsequent call. remainingItemCount indicates the number // of remaining objects if the list is partial. The remainingItemCount field is omitted during - // serialization if it is set to 0. - UpdateList(obj runtime.Object, resourceVersion uint64, continueValue string, remainingItemCount int64) error + // serialization if it is set to nil. + UpdateList(obj runtime.Object, resourceVersion uint64, continueValue string, remainingItemCount *int64) error // PrepareObjectForStorage should set SelfLink and ResourceVersion to the empty value. Should // return an error if the specified object cannot be updated. PrepareObjectForStorage(obj runtime.Object) error diff --git a/staging/src/k8s.io/kube-aggregator/pkg/registry/apiservice/etcd/etcd.go b/staging/src/k8s.io/kube-aggregator/pkg/registry/apiservice/etcd/etcd.go index 804976a2820..1179f69d6fb 100644 --- a/staging/src/k8s.io/kube-aggregator/pkg/registry/apiservice/etcd/etcd.go +++ b/staging/src/k8s.io/kube-aggregator/pkg/registry/apiservice/etcd/etcd.go @@ -73,7 +73,7 @@ func (c *REST) ConvertToTable(ctx context.Context, obj runtime.Object, tableOpti table.ResourceVersion = m.GetResourceVersion() table.SelfLink = m.GetSelfLink() table.Continue = m.GetContinue() - table.SetRemainingItemCount(m.GetRemainingItemCount()) + table.RemainingItemCount = m.GetRemainingItemCount() } else { if m, err := meta.CommonAccessor(obj); err == nil { table.ResourceVersion = m.GetResourceVersion() diff --git a/test/e2e/apimachinery/chunking.go b/test/e2e/apimachinery/chunking.go index 1f773c9a6f3..513a52b3bbb 100644 --- a/test/e2e/apimachinery/chunking.go +++ b/test/e2e/apimachinery/chunking.go @@ -89,7 +89,12 @@ var _ = SIGDescribe("Servers with support for API chunking", func() { lastRV = list.ResourceVersion } gomega.Expect(list.ResourceVersion).To(gomega.Equal(lastRV)) - gomega.Expect(int(list.GetRemainingItemCount()) + len(list.Items) + found).To(gomega.BeNumerically("==", numberOfTotalResources)) + if list.GetContinue() == "" { + gomega.Expect(list.GetRemainingItemCount()).To(gomega.BeNil()) + } 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 { gomega.Expect(item.Name).To(gomega.Equal(fmt.Sprintf("template-%04d", found))) found++ @@ -122,7 +127,12 @@ var _ = SIGDescribe("Servers with support for API chunking", func() { gomega.Expect(err).ToNot(gomega.HaveOccurred(), "failed to list pod templates in namespace: %s, given limit: %d", ns, opts.Limit) firstToken := list.Continue firstRV := list.ResourceVersion - gomega.Expect(int(list.GetRemainingItemCount()) + len(list.Items)).To(gomega.BeNumerically("==", numberOfTotalResources)) + if list.GetContinue() == "" { + gomega.Expect(list.GetRemainingItemCount()).To(gomega.BeNil()) + } else { + 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) ginkgo.By("retrieving the second page until the token expires") @@ -157,7 +167,12 @@ var _ = SIGDescribe("Servers with support for API chunking", func() { gomega.Expect(list.ResourceVersion).ToNot(gomega.Equal(firstRV)) gomega.Expect(len(list.Items)).To(gomega.BeNumerically("==", opts.Limit)) found := int(oneTenth) - gomega.Expect(int(list.GetRemainingItemCount()) + len(list.Items) + found).To(gomega.BeNumerically("==", numberOfTotalResources)) + if list.GetContinue() == "" { + gomega.Expect(list.GetRemainingItemCount()).To(gomega.BeNil()) + } 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 { gomega.Expect(item.Name).To(gomega.Equal(fmt.Sprintf("template-%04d", found))) found++ @@ -169,7 +184,12 @@ var _ = SIGDescribe("Servers with support for API chunking", func() { for { list, err := client.List(opts) gomega.Expect(err).ToNot(gomega.HaveOccurred(), "failed to list pod templates in namespace: %s, given limit: %d", ns, opts.Limit) - gomega.Expect(int(list.GetRemainingItemCount()) + len(list.Items) + found).To(gomega.BeNumerically("==", numberOfTotalResources)) + if list.GetContinue() == "" { + gomega.Expect(list.GetRemainingItemCount()).To(gomega.BeNil()) + } else { + 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) gomega.Expect(len(list.Items)).To(gomega.BeNumerically("<=", opts.Limit)) gomega.Expect(list.ResourceVersion).To(gomega.Equal(lastRV))