Skip to content

Commit 2aaefc3

Browse files
Add Limit and Continue functional list options for client
1 parent 89c373a commit 2aaefc3

File tree

2 files changed

+173
-1
lines changed

2 files changed

+173
-1
lines changed

pkg/client/client_test.go

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1430,6 +1430,89 @@ var _ = Describe("Client", func() {
14301430
close(done)
14311431
}, serverSideTimeoutSeconds)
14321432

1433+
It("should filter results using limit and continue options", func() {
1434+
1435+
makeDeployment := func(suffix string) *appsv1.Deployment {
1436+
return &appsv1.Deployment{
1437+
ObjectMeta: metav1.ObjectMeta{
1438+
Name: fmt.Sprintf("deployment-%s", suffix),
1439+
},
1440+
Spec: appsv1.DeploymentSpec{
1441+
Selector: &metav1.LabelSelector{
1442+
MatchLabels: map[string]string{"foo": "bar"},
1443+
},
1444+
Template: corev1.PodTemplateSpec{
1445+
ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{"foo": "bar"}},
1446+
Spec: corev1.PodSpec{Containers: []corev1.Container{{Name: "nginx", Image: "nginx"}}},
1447+
},
1448+
},
1449+
}
1450+
}
1451+
1452+
By("creating 4 deployments")
1453+
dep1 := makeDeployment("1")
1454+
dep1, err := clientset.AppsV1().Deployments(ns).Create(dep1)
1455+
Expect(err).NotTo(HaveOccurred())
1456+
defer deleteDeployment(dep1, ns)
1457+
1458+
dep2 := makeDeployment("2")
1459+
dep2, err = clientset.AppsV1().Deployments(ns).Create(dep2)
1460+
Expect(err).NotTo(HaveOccurred())
1461+
defer deleteDeployment(dep2, ns)
1462+
1463+
dep3 := makeDeployment("3")
1464+
dep3, err = clientset.AppsV1().Deployments(ns).Create(dep3)
1465+
Expect(err).NotTo(HaveOccurred())
1466+
defer deleteDeployment(dep3, ns)
1467+
1468+
dep4 := makeDeployment("4")
1469+
dep4, err = clientset.AppsV1().Deployments(ns).Create(dep4)
1470+
Expect(err).NotTo(HaveOccurred())
1471+
defer deleteDeployment(dep4, ns)
1472+
1473+
cl, err := client.New(cfg, client.Options{})
1474+
Expect(err).NotTo(HaveOccurred())
1475+
1476+
By("listing 1 deployment when limit=1 is used")
1477+
deps := &appsv1.DeploymentList{}
1478+
err = cl.List(context.Background(), deps,
1479+
client.Limit(1),
1480+
)
1481+
Expect(err).NotTo(HaveOccurred())
1482+
1483+
Expect(deps.Items).To(HaveLen(1))
1484+
Expect(deps.Continue).NotTo(BeEmpty())
1485+
Expect(deps.Items[0].Name).To(Equal(dep1.Name))
1486+
1487+
continueToken := deps.Continue
1488+
1489+
By("listing the next deployment when previous continuation token is used and limit=1")
1490+
deps = &appsv1.DeploymentList{}
1491+
err = cl.List(context.Background(), deps,
1492+
client.Limit(1),
1493+
client.Continue(continueToken),
1494+
)
1495+
Expect(err).NotTo(HaveOccurred())
1496+
1497+
Expect(deps.Items).To(HaveLen(1))
1498+
Expect(deps.Continue).NotTo(BeEmpty())
1499+
Expect(deps.Items[0].Name).To(Equal(dep2.Name))
1500+
1501+
continueToken = deps.Continue
1502+
1503+
By("listing the 2 remaining deployments when previous continuation token is used without a limit")
1504+
deps = &appsv1.DeploymentList{}
1505+
err = cl.List(context.Background(), deps,
1506+
client.Continue(continueToken),
1507+
)
1508+
Expect(err).NotTo(HaveOccurred())
1509+
1510+
Expect(deps.Items).To(HaveLen(2))
1511+
Expect(deps.Continue).To(BeEmpty())
1512+
Expect(deps.Items[0].Name).To(Equal(dep3.Name))
1513+
Expect(deps.Items[1].Name).To(Equal(dep4.Name))
1514+
}, serverSideTimeoutSeconds)
1515+
14331516
PIt("should fail if the object doesn't have meta", func() {
14341517

14351518
})
@@ -1838,6 +1921,50 @@ var _ = Describe("Client", func() {
18381921
Expect(lo.Namespace).To(Equal("test"))
18391922
})
18401923

1924+
It("should be able to set Limit", func() {
1925+
lo := &client.ListOptions{}
1926+
lo = lo.SetLimit(1)
1927+
Expect(lo.Limit).To(Equal(int64(1)))
1928+
})
1929+
1930+
It("should be created from Limit", func() {
1931+
lo := &client.ListOptions{}
1932+
client.Limit(1)(lo)
1933+
Expect(lo).NotTo(BeNil())
1934+
Expect(lo.Limit).To(Equal(int64(1)))
1935+
})
1936+
1937+
It("should ignore Limit when converted to metav1.ListOptions and watch is true", func() {
1938+
lo := &client.ListOptions{
1939+
Raw: &metav1.ListOptions{Watch: true},
1940+
}
1941+
mlo := lo.SetLimit(1).AsListOptions()
1942+
Expect(mlo).NotTo(BeNil())
1943+
Expect(mlo.Limit).To(BeZero())
1944+
})
1945+
1946+
It("should be able to set Continue token", func() {
1947+
lo := &client.ListOptions{}
1948+
lo = lo.SetContinue("foo")
1949+
Expect(lo.Continue).To(Equal("foo"))
1950+
})
1951+
1952+
It("should be created from Continue", func() {
1953+
lo := &client.ListOptions{}
1954+
client.Continue("foo")(lo)
1955+
Expect(lo).NotTo(BeNil())
1956+
Expect(lo.Continue).To(Equal("foo"))
1957+
})
1958+
1959+
It("should ignore Continue token when converted to metav1.ListOptions and watch is true", func() {
1960+
lo := &client.ListOptions{
1961+
Raw: &metav1.ListOptions{Watch: true},
1962+
}
1963+
mlo := lo.SetContinue("foo").AsListOptions()
1964+
Expect(mlo).NotTo(BeNil())
1965+
Expect(mlo.Limit).To(BeZero())
1966+
})
1967+
18411968
It("should allow pre-built ListOptions", func() {
18421969
lo := &client.ListOptions{}
18431970
newLo := &client.ListOptions{}

pkg/client/interfaces.go

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -205,9 +205,20 @@ type ListOptions struct {
205205
// non-namespaced objects, or to list across all namespaces.
206206
Namespace string
207207

208+
// Limit specifies the maximum number of results to return from the server. The server may
209+
// not support this field on all resource types, but if it does and more results remain it
210+
// will set the continue field on the returned list object. This field is not supported if watch
211+
// is true in the Raw ListOptions.
212+
Limit int64
213+
// Continue is a token returned by the server that lets a client retrieve chunks of results
214+
// from the server by specifying limit. The server may reject requests for continuation tokens
215+
// it does not recognize and will return a 410 error if the token can no longer be used because
216+
// it has expired. This field is not supported if watch is true in the Raw ListOptions.
217+
Continue string
218+
208219
// Raw represents raw ListOptions, as passed to the API server. Note
209220
// that these may not be respected by all implementations of interface,
210-
// and the LabelSelector and FieldSelector fields are ignored.
221+
// and the LabelSelector, FieldSelector, Limit and Continue fields are ignored.
211222
Raw *metav1.ListOptions
212223
}
213224

@@ -248,6 +259,10 @@ func (o *ListOptions) AsListOptions() *metav1.ListOptions {
248259
if o.FieldSelector != nil {
249260
o.Raw.FieldSelector = o.FieldSelector.String()
250261
}
262+
if !o.Raw.Watch {
263+
o.Raw.Limit = o.Limit
264+
o.Raw.Continue = o.Continue
265+
}
251266
return o.Raw
252267
}
253268

@@ -290,6 +305,20 @@ func (o *ListOptions) InNamespace(ns string) *ListOptions {
290305
return o
291306
}
292307

308+
// SetLimit is a convenience function that sets the limit, and
309+
// then returns the options. It mutates the list options.
310+
func (o *ListOptions) SetLimit(n int64) *ListOptions {
311+
o.Limit = n
312+
return o
313+
}
314+
315+
// SetContinue is a convenience function that sets the continuation
316+
// token, and then returns the options. It mutates the list options.
317+
func (o *ListOptions) SetContinue(token string) *ListOptions {
318+
o.Continue = token
319+
return o
320+
}
321+
293322
// MatchingLabels is a functional option that sets the LabelSelector field of
294323
// a ListOptions struct.
295324
func MatchingLabels(lbls map[string]string) ListOptionFunc {
@@ -316,6 +345,22 @@ func InNamespace(ns string) ListOptionFunc {
316345
}
317346
}
318347

348+
// Limit is a functional option that sets the Limit field of
349+
// a ListOptions struct.
350+
func Limit(n int64) ListOptionFunc {
351+
return func(opts *ListOptions) {
352+
opts.Limit = n
353+
}
354+
}
355+
356+
// Continue is a functional option that sets the Continue field of
357+
// a ListOptions struct.
358+
func Continue(token string) ListOptionFunc {
359+
return func(opts *ListOptions) {
360+
opts.Continue = token
361+
}
362+
}
363+
319364
// UseListOptions is a functional option that replaces the fields of a
320365
// ListOptions struct with those of a different ListOptions struct.
321366
//

0 commit comments

Comments
 (0)