Skip to content

Commit 04b1b70

Browse files
sebglingvagabund
authored andcommitted
Enable labels list options in fake client
This commit allows passing label matches to the fake client List() function. This is done in a way similar to the CachedReader. Both now share a common call to `objectutil.FilterWithLabels`. Signed-off-by: sebgl <[email protected]>
1 parent 52ef898 commit 04b1b70

File tree

4 files changed

+98
-27
lines changed

4 files changed

+98
-27
lines changed

pkg/cache/internal/cache_reader.go

Lines changed: 9 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import (
3030
"k8s.io/apimachinery/pkg/selection"
3131
"k8s.io/client-go/tools/cache"
3232
"sigs.k8s.io/controller-runtime/pkg/client"
33+
"sigs.k8s.io/controller-runtime/pkg/internal/objectutil"
3334
)
3435

3536
// CacheReader is a CacheReader
@@ -115,33 +116,19 @@ func (c *CacheReader) List(_ context.Context, opts *client.ListOptions, out runt
115116
labelSel = opts.LabelSelector
116117
}
117118

118-
outItems, err := c.getListItems(objs, labelSel)
119-
if err != nil {
120-
return err
121-
}
122-
return apimeta.SetList(out, outItems)
123-
}
124-
125-
func (c *CacheReader) getListItems(objs []interface{}, labelSel labels.Selector) ([]runtime.Object, error) {
126-
outItems := make([]runtime.Object, 0, len(objs))
119+
runtimeObjs := make([]runtime.Object, 0, len(objs))
127120
for _, item := range objs {
128121
obj, isObj := item.(runtime.Object)
129122
if !isObj {
130-
return nil, fmt.Errorf("cache contained %T, which is not an Object", obj)
123+
return fmt.Errorf("cache contained %T, which is not an Object", obj)
131124
}
132-
meta, err := apimeta.Accessor(obj)
133-
if err != nil {
134-
return nil, err
135-
}
136-
if labelSel != nil {
137-
lbls := labels.Set(meta.GetLabels())
138-
if !labelSel.Matches(lbls) {
139-
continue
140-
}
141-
}
142-
outItems = append(outItems, obj.DeepCopyObject())
125+
runtimeObjs = append(runtimeObjs, obj)
126+
}
127+
filteredItems, err := objectutil.FilterWithLabels(runtimeObjs, labelSel)
128+
if err != nil {
129+
return err
143130
}
144-
return outItems, nil
131+
return apimeta.SetList(out, filteredItems)
145132
}
146133

147134
// objectKeyToStorageKey converts an object key to store key.

pkg/client/fake/client.go

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import (
3131

3232
"sigs.k8s.io/controller-runtime/pkg/client"
3333
"sigs.k8s.io/controller-runtime/pkg/client/apiutil"
34+
"sigs.k8s.io/controller-runtime/pkg/internal/objectutil"
3435
logf "sigs.k8s.io/controller-runtime/pkg/runtime/log"
3536
)
3637

@@ -110,7 +111,25 @@ func (c *fakeClient) List(ctx context.Context, opts *client.ListOptions, list ru
110111
}
111112
decoder := scheme.Codecs.UniversalDecoder()
112113
_, _, err = decoder.Decode(j, nil, list)
113-
return err
114+
if err != nil {
115+
return err
116+
}
117+
118+
if opts.LabelSelector != nil {
119+
objs, err := meta.ExtractList(list)
120+
if err != nil {
121+
return err
122+
}
123+
filteredObjs, err := objectutil.FilterWithLabels(objs, opts.LabelSelector)
124+
if err != nil {
125+
return err
126+
}
127+
err = meta.SetList(list, filteredObjs)
128+
if err != nil {
129+
return err
130+
}
131+
}
132+
return nil
114133
}
115134

116135
func (c *fakeClient) Create(ctx context.Context, obj runtime.Object) error {

pkg/client/fake/client_test.go

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import (
3030

3131
var _ = Describe("Fake client", func() {
3232
var dep *appsv1.Deployment
33+
var dep2 *appsv1.Deployment
3334
var cm *corev1.ConfigMap
3435
var cl client.Client
3536

@@ -40,6 +41,15 @@ var _ = Describe("Fake client", func() {
4041
Namespace: "ns1",
4142
},
4243
}
44+
dep2 = &appsv1.Deployment{
45+
ObjectMeta: metav1.ObjectMeta{
46+
Name: "test-deployment-2",
47+
Namespace: "ns1",
48+
Labels: map[string]string{
49+
"test-label": "label-value",
50+
},
51+
},
52+
}
4353
cm = &corev1.ConfigMap{
4454
ObjectMeta: metav1.ObjectMeta{
4555
Name: "test-cm",
@@ -71,8 +81,20 @@ var _ = Describe("Fake client", func() {
7181
Namespace: "ns1",
7282
}, list)
7383
Expect(err).To(BeNil())
84+
Expect(list.Items).To(HaveLen(2))
85+
Expect(list.Items).To(ConsistOf(*dep, *dep2))
86+
})
87+
88+
It("should support filtering by labels", func() {
89+
By("Listing deployments with a particular label")
90+
list := &appsv1.DeploymentList{}
91+
err := cl.List(nil,
92+
client.InNamespace("ns1").MatchingLabels(map[string]string{
93+
"test-label": "label-value",
94+
}), list)
95+
Expect(err).To(BeNil())
7496
Expect(list.Items).To(HaveLen(1))
75-
Expect(list.Items).To(ConsistOf(*dep))
97+
Expect(list.Items).To(ConsistOf(*dep2))
7698
})
7799

78100
It("should be able to Create", func() {
@@ -133,13 +155,14 @@ var _ = Describe("Fake client", func() {
133155
Namespace: "ns1",
134156
}, list)
135157
Expect(err).To(BeNil())
136-
Expect(list.Items).To(HaveLen(0))
158+
Expect(list.Items).To(HaveLen(1))
159+
Expect(list.Items).To(ConsistOf(*dep2))
137160
})
138161
}
139162

140163
Context("with default scheme.Scheme", func() {
141164
BeforeEach(func(done Done) {
142-
cl = NewFakeClient(dep, cm)
165+
cl = NewFakeClient(dep, dep2, cm)
143166
close(done)
144167
})
145168
AssertClientBehavior()
@@ -150,7 +173,7 @@ var _ = Describe("Fake client", func() {
150173
scheme := runtime.NewScheme()
151174
corev1.AddToScheme(scheme)
152175
appsv1.AddToScheme(scheme)
153-
cl = NewFakeClientWithScheme(scheme, dep, cm)
176+
cl = NewFakeClientWithScheme(scheme, dep, dep2, cm)
154177
close(done)
155178
})
156179
AssertClientBehavior()

pkg/internal/objectutil/filter.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/*
2+
Copyright 2018 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package objectutil
18+
19+
import (
20+
apimeta "k8s.io/apimachinery/pkg/api/meta"
21+
"k8s.io/apimachinery/pkg/labels"
22+
"k8s.io/apimachinery/pkg/runtime"
23+
)
24+
25+
// FilterWithLabels returns a copy of the items in objs matching labelSel
26+
func FilterWithLabels(objs []runtime.Object, labelSel labels.Selector) ([]runtime.Object, error) {
27+
outItems := make([]runtime.Object, 0, len(objs))
28+
for _, obj := range objs {
29+
meta, err := apimeta.Accessor(obj)
30+
if err != nil {
31+
return nil, err
32+
}
33+
if labelSel != nil {
34+
lbls := labels.Set(meta.GetLabels())
35+
if !labelSel.Matches(lbls) {
36+
continue
37+
}
38+
}
39+
outItems = append(outItems, obj.DeepCopyObject())
40+
}
41+
return outItems, nil
42+
}

0 commit comments

Comments
 (0)