Skip to content

✨ Add Limit and Continue functional list options for client #341

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Sep 17, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
139 changes: 139 additions & 0 deletions pkg/client/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1904,6 +1904,89 @@ var _ = Describe("Client", func() {
close(done)
}, serverSideTimeoutSeconds)

It("should filter results using limit and continue options", func() {

makeDeployment := func(suffix string) *appsv1.Deployment {
return &appsv1.Deployment{
ObjectMeta: metav1.ObjectMeta{
Name: fmt.Sprintf("deployment-%s", suffix),
},
Spec: appsv1.DeploymentSpec{
Selector: &metav1.LabelSelector{
MatchLabels: map[string]string{"foo": "bar"},
},
Template: corev1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{"foo": "bar"}},
Spec: corev1.PodSpec{Containers: []corev1.Container{{Name: "nginx", Image: "nginx"}}},
},
},
}
}

By("creating 4 deployments")
dep1 := makeDeployment("1")
dep1, err := clientset.AppsV1().Deployments(ns).Create(dep1)
Expect(err).NotTo(HaveOccurred())
defer deleteDeployment(dep1, ns)

dep2 := makeDeployment("2")
dep2, err = clientset.AppsV1().Deployments(ns).Create(dep2)
Expect(err).NotTo(HaveOccurred())
defer deleteDeployment(dep2, ns)

dep3 := makeDeployment("3")
dep3, err = clientset.AppsV1().Deployments(ns).Create(dep3)
Expect(err).NotTo(HaveOccurred())
defer deleteDeployment(dep3, ns)

dep4 := makeDeployment("4")
dep4, err = clientset.AppsV1().Deployments(ns).Create(dep4)
Expect(err).NotTo(HaveOccurred())
defer deleteDeployment(dep4, ns)

cl, err := client.New(cfg, client.Options{})
Expect(err).NotTo(HaveOccurred())

By("listing 1 deployment when limit=1 is used")
deps := &appsv1.DeploymentList{}
err = cl.List(context.Background(), deps,
client.Limit(1),
)
Expect(err).NotTo(HaveOccurred())

Expect(deps.Items).To(HaveLen(1))
Expect(deps.Continue).NotTo(BeEmpty())
Expect(deps.Items[0].Name).To(Equal(dep1.Name))

continueToken := deps.Continue

By("listing the next deployment when previous continuation token is used and limit=1")
deps = &appsv1.DeploymentList{}
err = cl.List(context.Background(), deps,
client.Limit(1),
client.Continue(continueToken),
)
Expect(err).NotTo(HaveOccurred())

Expect(deps.Items).To(HaveLen(1))
Expect(deps.Continue).NotTo(BeEmpty())
Expect(deps.Items[0].Name).To(Equal(dep2.Name))

continueToken = deps.Continue

By("listing the 2 remaining deployments when previous continuation token is used without a limit")
deps = &appsv1.DeploymentList{}
err = cl.List(context.Background(), deps,
client.Continue(continueToken),
)
Expect(err).NotTo(HaveOccurred())

Expect(deps.Items).To(HaveLen(2))
Expect(deps.Continue).To(BeEmpty())
Expect(deps.Items[0].Name).To(Equal(dep3.Name))
Expect(deps.Items[1].Name).To(Equal(dep4.Name))
}, serverSideTimeoutSeconds)

PIt("should fail if the object doesn't have meta", func() {

})
Expand Down Expand Up @@ -2309,11 +2392,15 @@ var _ = Describe("Client", func() {
client.MatchingField("field1", "bar"),
client.InNamespace("test-namespace"),
client.MatchingLabels{"foo": "bar"},
client.Limit(1),
client.Continue("foo"),
})
mlo := lo.AsListOptions()
Expect(mlo).NotTo(BeNil())
Expect(mlo.LabelSelector).To(Equal("foo=bar"))
Expect(mlo.FieldSelector).To(Equal("field1=bar"))
Expect(mlo.Limit).To(Equal(int64(1)))
Expect(mlo.Continue).To(Equal("foo"))
})

It("should be populated by MatchingLabels", func() {
Expand Down Expand Up @@ -2343,6 +2430,58 @@ var _ = Describe("Client", func() {
do = &client.ListOptions{}
Expect(do.AsListOptions()).To(Equal(&metav1.ListOptions{}))
})

It("should be populated by Limit", func() {
lo := &client.ListOptions{}
client.Limit(1).ApplyToList(lo)
Expect(lo).NotTo(BeNil())
Expect(lo.Limit).To(Equal(int64(1)))
})

It("should ignore Limit when converted to metav1.ListOptions and watch is true", func() {
lo := &client.ListOptions{
Raw: &metav1.ListOptions{Watch: true},
}
lo.ApplyOptions([]client.ListOption{
client.Limit(1),
})
mlo := lo.AsListOptions()
Expect(mlo).NotTo(BeNil())
Expect(mlo.Limit).To(BeZero())
})

It("should be populated by Continue", func() {
lo := &client.ListOptions{}
client.Continue("foo").ApplyToList(lo)
Expect(lo).NotTo(BeNil())
Expect(lo.Continue).To(Equal("foo"))
})

It("should ignore Continue token when converted to metav1.ListOptions and watch is true", func() {
lo := &client.ListOptions{
Raw: &metav1.ListOptions{Watch: true},
}
lo.ApplyOptions([]client.ListOption{
client.Continue("foo"),
})
mlo := lo.AsListOptions()
Expect(mlo).NotTo(BeNil())
Expect(mlo.Continue).To(BeEmpty())
})

It("should ignore both Limit and Continue token when converted to metav1.ListOptions and watch is true", func() {
lo := &client.ListOptions{
Raw: &metav1.ListOptions{Watch: true},
}
lo.ApplyOptions([]client.ListOption{
client.Limit(1),
client.Continue("foo"),
})
mlo := lo.AsListOptions()
Expect(mlo).NotTo(BeNil())
Expect(mlo.Limit).To(BeZero())
Expect(mlo.Continue).To(BeEmpty())
})
})

Describe("UpdateOptions", func() {
Expand Down
35 changes: 34 additions & 1 deletion pkg/client/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -267,9 +267,20 @@ type ListOptions struct {
// non-namespaced objects, or to list across all namespaces.
Namespace string

// Limit specifies the maximum number of results to return from the server. The server may
// not support this field on all resource types, but if it does and more results remain it
// will set the continue field on the returned list object. This field is not supported if watch
// is true in the Raw ListOptions.
Limit int64
// Continue is a token returned by the server that lets a client retrieve chunks of results
// from the server by specifying limit. The server may reject requests for continuation tokens
// it does not recognize and will return a 410 error if the token can no longer be used because
// it has expired. This field is not supported if watch is true in the Raw ListOptions.
Continue string

// Raw represents raw ListOptions, as passed to the API server. Note
// that these may not be respected by all implementations of interface,
// and the LabelSelector and FieldSelector fields are ignored.
// and the LabelSelector, FieldSelector, Limit and Continue fields are ignored.
Raw *metav1.ListOptions
}

Expand All @@ -288,6 +299,10 @@ func (o *ListOptions) AsListOptions() *metav1.ListOptions {
if o.FieldSelector != nil {
o.Raw.FieldSelector = o.FieldSelector.String()
}
if !o.Raw.Watch {
o.Raw.Limit = o.Limit
o.Raw.Continue = o.Continue
}
return o.Raw
}

Expand Down Expand Up @@ -346,6 +361,24 @@ func (n InNamespace) ApplyToDeleteAllOf(opts *DeleteAllOfOptions) {
n.ApplyToList(&opts.ListOptions)
}

// Limit specifies the maximum number of results to return from the server.
// Limit does not implement DeleteAllOfOption interface because the server
// does not support setting it for deletecollection operations.
type Limit int64

func (l Limit) ApplyToList(opts *ListOptions) {
opts.Limit = int64(l)
}

// Continue sets a continuation token to retrieve chunks of results when using limit.
// Continue does not implement DeleteAllOfOption interface because the server
// does not support setting it for deletecollection operations.
type Continue string

func (c Continue) ApplyToList(opts *ListOptions) {
opts.Continue = string(c)
}

// }}}

// {{{ Update Options
Expand Down