Skip to content

Commit 8f10214

Browse files
Add Limit and Continue functional list options
Adds Limit and Continue list options for pagination. Note that this change does not support setting Limit and Continue for `DeleteAllOfOptions` via functional options since it is not supported by the server.
1 parent d7467fc commit 8f10214

File tree

2 files changed

+169
-1
lines changed

2 files changed

+169
-1
lines changed

pkg/client/client_test.go

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1904,6 +1904,89 @@ var _ = Describe("Client", func() {
19041904
close(done)
19051905
}, serverSideTimeoutSeconds)
19061906

1907+
It("should filter results using limit and continue options", func() {
1908+
1909+
makeDeployment := func(suffix string) *appsv1.Deployment {
1910+
return &appsv1.Deployment{
1911+
ObjectMeta: metav1.ObjectMeta{
1912+
Name: fmt.Sprintf("deployment-%s", suffix),
1913+
},
1914+
Spec: appsv1.DeploymentSpec{
1915+
Selector: &metav1.LabelSelector{
1916+
MatchLabels: map[string]string{"foo": "bar"},
1917+
},
1918+
Template: corev1.PodTemplateSpec{
1919+
ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{"foo": "bar"}},
1920+
Spec: corev1.PodSpec{Containers: []corev1.Container{{Name: "nginx", Image: "nginx"}}},
1921+
},
1922+
},
1923+
}
1924+
}
1925+
1926+
By("creating 4 deployments")
1927+
dep1 := makeDeployment("1")
1928+
dep1, err := clientset.AppsV1().Deployments(ns).Create(dep1)
1929+
Expect(err).NotTo(HaveOccurred())
1930+
defer deleteDeployment(dep1, ns)
1931+
1932+
dep2 := makeDeployment("2")
1933+
dep2, err = clientset.AppsV1().Deployments(ns).Create(dep2)
1934+
Expect(err).NotTo(HaveOccurred())
1935+
defer deleteDeployment(dep2, ns)
1936+
1937+
dep3 := makeDeployment("3")
1938+
dep3, err = clientset.AppsV1().Deployments(ns).Create(dep3)
1939+
Expect(err).NotTo(HaveOccurred())
1940+
defer deleteDeployment(dep3, ns)
1941+
1942+
dep4 := makeDeployment("4")
1943+
dep4, err = clientset.AppsV1().Deployments(ns).Create(dep4)
1944+
Expect(err).NotTo(HaveOccurred())
1945+
defer deleteDeployment(dep4, ns)
1946+
1947+
cl, err := client.New(cfg, client.Options{})
1948+
Expect(err).NotTo(HaveOccurred())
1949+
1950+
By("listing 1 deployment when limit=1 is used")
1951+
deps := &appsv1.DeploymentList{}
1952+
err = cl.List(context.Background(), deps,
1953+
client.Limit(1),
1954+
)
1955+
Expect(err).NotTo(HaveOccurred())
1956+
1957+
Expect(deps.Items).To(HaveLen(1))
1958+
Expect(deps.Continue).NotTo(BeEmpty())
1959+
Expect(deps.Items[0].Name).To(Equal(dep1.Name))
1960+
1961+
continueToken := deps.Continue
1962+
1963+
By("listing the next deployment when previous continuation token is used and limit=1")
1964+
deps = &appsv1.DeploymentList{}
1965+
err = cl.List(context.Background(), deps,
1966+
client.Limit(1),
1967+
client.Continue(continueToken),
1968+
)
1969+
Expect(err).NotTo(HaveOccurred())
1970+
1971+
Expect(deps.Items).To(HaveLen(1))
1972+
Expect(deps.Continue).NotTo(BeEmpty())
1973+
Expect(deps.Items[0].Name).To(Equal(dep2.Name))
1974+
1975+
continueToken = deps.Continue
1976+
1977+
By("listing the 2 remaining deployments when previous continuation token is used without a limit")
1978+
deps = &appsv1.DeploymentList{}
1979+
err = cl.List(context.Background(), deps,
1980+
client.Continue(continueToken),
1981+
)
1982+
Expect(err).NotTo(HaveOccurred())
1983+
1984+
Expect(deps.Items).To(HaveLen(2))
1985+
Expect(deps.Continue).To(BeEmpty())
1986+
Expect(deps.Items[0].Name).To(Equal(dep3.Name))
1987+
Expect(deps.Items[1].Name).To(Equal(dep4.Name))
1988+
}, serverSideTimeoutSeconds)
1989+
19071990
PIt("should fail if the object doesn't have meta", func() {
19081991

19091992
})
@@ -2309,11 +2392,15 @@ var _ = Describe("Client", func() {
23092392
client.MatchingField("field1", "bar"),
23102393
client.InNamespace("test-namespace"),
23112394
client.MatchingLabels{"foo": "bar"},
2395+
client.Limit(1),
2396+
client.Continue("foo"),
23122397
})
23132398
mlo := lo.AsListOptions()
23142399
Expect(mlo).NotTo(BeNil())
23152400
Expect(mlo.LabelSelector).To(Equal("foo=bar"))
23162401
Expect(mlo.FieldSelector).To(Equal("field1=bar"))
2402+
Expect(mlo.Limit).To(Equal(int64(1)))
2403+
Expect(mlo.Continue).To(Equal("foo"))
23172404
})
23182405

23192406
It("should be populated by MatchingLabels", func() {
@@ -2343,6 +2430,58 @@ var _ = Describe("Client", func() {
23432430
do = &client.ListOptions{}
23442431
Expect(do.AsListOptions()).To(Equal(&metav1.ListOptions{}))
23452432
})
2433+
2434+
It("should be populated by Limit", func() {
2435+
lo := &client.ListOptions{}
2436+
client.Limit(1).ApplyToList(lo)
2437+
Expect(lo).NotTo(BeNil())
2438+
Expect(lo.Limit).To(Equal(int64(1)))
2439+
})
2440+
2441+
It("should ignore Limit when converted to metav1.ListOptions and watch is true", func() {
2442+
lo := &client.ListOptions{
2443+
Raw: &metav1.ListOptions{Watch: true},
2444+
}
2445+
lo.ApplyOptions([]client.ListOption{
2446+
client.Limit(1),
2447+
})
2448+
mlo := lo.AsListOptions()
2449+
Expect(mlo).NotTo(BeNil())
2450+
Expect(mlo.Limit).To(BeZero())
2451+
})
2452+
2453+
It("should be populated by Continue", func() {
2454+
lo := &client.ListOptions{}
2455+
client.Continue("foo").ApplyToList(lo)
2456+
Expect(lo).NotTo(BeNil())
2457+
Expect(lo.Continue).To(Equal("foo"))
2458+
})
2459+
2460+
It("should ignore Continue token when converted to metav1.ListOptions and watch is true", func() {
2461+
lo := &client.ListOptions{
2462+
Raw: &metav1.ListOptions{Watch: true},
2463+
}
2464+
lo.ApplyOptions([]client.ListOption{
2465+
client.Continue("foo"),
2466+
})
2467+
mlo := lo.AsListOptions()
2468+
Expect(mlo).NotTo(BeNil())
2469+
Expect(mlo.Continue).To(BeEmpty())
2470+
})
2471+
2472+
It("should ignore both Limit and Continue token when converted to metav1.ListOptions and watch is true", func() {
2473+
lo := &client.ListOptions{
2474+
Raw: &metav1.ListOptions{Watch: true},
2475+
}
2476+
lo.ApplyOptions([]client.ListOption{
2477+
client.Limit(1),
2478+
client.Continue("foo"),
2479+
})
2480+
mlo := lo.AsListOptions()
2481+
Expect(mlo).NotTo(BeNil())
2482+
Expect(mlo.Limit).To(BeZero())
2483+
Expect(mlo.Continue).To(BeEmpty())
2484+
})
23462485
})
23472486

23482487
Describe("UpdateOptions", func() {

pkg/client/options.go

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -267,9 +267,20 @@ type ListOptions struct {
267267
// non-namespaced objects, or to list across all namespaces.
268268
Namespace string
269269

270+
// Limit specifies the maximum number of results to return from the server. The server may
271+
// not support this field on all resource types, but if it does and more results remain it
272+
// will set the continue field on the returned list object. This field is not supported if watch
273+
// is true in the Raw ListOptions.
274+
Limit int64
275+
// Continue is a token returned by the server that lets a client retrieve chunks of results
276+
// from the server by specifying limit. The server may reject requests for continuation tokens
277+
// it does not recognize and will return a 410 error if the token can no longer be used because
278+
// it has expired. This field is not supported if watch is true in the Raw ListOptions.
279+
Continue string
280+
270281
// Raw represents raw ListOptions, as passed to the API server. Note
271282
// that these may not be respected by all implementations of interface,
272-
// and the LabelSelector and FieldSelector fields are ignored.
283+
// and the LabelSelector, FieldSelector, Limit and Continue fields are ignored.
273284
Raw *metav1.ListOptions
274285
}
275286

@@ -288,6 +299,10 @@ func (o *ListOptions) AsListOptions() *metav1.ListOptions {
288299
if o.FieldSelector != nil {
289300
o.Raw.FieldSelector = o.FieldSelector.String()
290301
}
302+
if !o.Raw.Watch {
303+
o.Raw.Limit = o.Limit
304+
o.Raw.Continue = o.Continue
305+
}
291306
return o.Raw
292307
}
293308

@@ -346,6 +361,20 @@ func (n InNamespace) ApplyToDeleteAllOf(opts *DeleteAllOfOptions) {
346361
n.ApplyToList(&opts.ListOptions)
347362
}
348363

364+
// Limit specifies the maximum number of results to return from the server.
365+
type Limit int64
366+
367+
func (l Limit) ApplyToList(opts *ListOptions) {
368+
opts.Limit = int64(l)
369+
}
370+
371+
// Continue sets a continuation token to retrieve chunks of results when using limit.
372+
type Continue string
373+
374+
func (c Continue) ApplyToList(opts *ListOptions) {
375+
opts.Continue = string(c)
376+
}
377+
349378
// }}}
350379

351380
// {{{ Update Options

0 commit comments

Comments
 (0)