Skip to content

Commit 0f5aba9

Browse files
authored
Squashed commit of the following: (#2248)
commit 3b534a4 Merge: f264518 a24b949 Author: Ludovic Cleroux <[email protected]> Date: Tue Apr 25 12:58:42 2023 +0200 Merge branch 'kubernetes-sigs:main' into add-fake-client-interception commit f264518 Author: Ludovic Cleroux <[email protected]> Date: Tue Apr 25 12:57:18 2023 +0200 PR comments commit e358be3 Author: Ludovic Cleroux <[email protected]> Date: Sun Apr 16 10:38:37 2023 +0200 Allow the interceptor to be discovered from the fake ClientBuilder commit bf25854 Author: Ludovic Cleroux <[email protected]> Date: Sun Apr 16 10:21:37 2023 +0200 Remove dependency on fake client commit 6063435 Author: Ludovic Cleroux <[email protected]> Date: Sun Apr 16 10:02:42 2023 +0200 Cleanup commit b66b57a Author: Ludovic Cleroux <[email protected]> Date: Sun Apr 16 10:02:05 2023 +0200 Cleanup commit 483deea Author: Ludovic Cleroux <[email protected]> Date: Sun Apr 16 10:00:39 2023 +0200 Cleanup commit 9c3df6a Author: Ludovic Cleroux <[email protected]> Date: Sun Apr 16 09:59:19 2023 +0200 Slight refactor commit 4fb67c0 Author: Ludovic Cleroux <[email protected]> Date: Sun Apr 16 09:56:24 2023 +0200 Slight refactor & lint commit 5221233 Author: Ludovic Cleroux <[email protected]> Date: Sun Apr 16 09:30:52 2023 +0200 Move interceptor to new package commit d74b869 Merge: 9642a63 d989e66 Author: Ludovic Cleroux <[email protected]> Date: Sun Apr 16 09:21:31 2023 +0200 Merge branch 'kubernetes-sigs:main' into add-fake-client-interception commit 9642a63 Author: Ludovic Cleroux <[email protected]> Date: Wed Mar 29 09:34:28 2023 +0200 Fixed SubResource/Status calling logic commit 00597ac Author: Ludovic Cleroux <[email protected]> Date: Tue Mar 28 09:44:36 2023 +0200 Remove unnecessary Status() intercept commit cd8451c Author: Ludovic Cleroux <[email protected]> Date: Mon Mar 27 15:52:40 2023 +0200 Fix goimports commit c80013e Author: Ludovic Cleroux <[email protected]> Date: Mon Mar 27 15:42:45 2023 +0200 Fix missing comments commit fe27862 Author: Ludovic Cleroux <[email protected]> Date: Mon Mar 27 15:30:13 2023 +0200 Added methods to intercept calls to a fake k8s client
1 parent a24b949 commit 0f5aba9

File tree

4 files changed

+633
-2
lines changed

4 files changed

+633
-2
lines changed

pkg/client/fake/client.go

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ import (
2828
"strings"
2929
"sync"
3030

31+
"sigs.k8s.io/controller-runtime/pkg/client/interceptor"
32+
3133
apierrors "k8s.io/apimachinery/pkg/api/errors"
3234
"k8s.io/apimachinery/pkg/api/meta"
3335
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -107,6 +109,7 @@ type ClientBuilder struct {
107109
initRuntimeObjects []runtime.Object
108110
withStatusSubresource []client.Object
109111
objectTracker testing.ObjectTracker
112+
interceptorFuncs *interceptor.Funcs
110113

111114
// indexes maps each GroupVersionKind (GVK) to the indexes registered for that GVK.
112115
// The inner map maps from index name to IndexerFunc.
@@ -192,12 +195,18 @@ func (f *ClientBuilder) WithIndex(obj runtime.Object, field string, extractValue
192195
}
193196

194197
// WithStatusSubresource configures the passed object with a status subresource, which means
195-
// calls to Update and Patch will not alters its status.
198+
// calls to Update and Patch will not alter its status.
196199
func (f *ClientBuilder) WithStatusSubresource(o ...client.Object) *ClientBuilder {
197200
f.withStatusSubresource = append(f.withStatusSubresource, o...)
198201
return f
199202
}
200203

204+
// WithInterceptorFuncs configures the client methods to be intercepted using the provided interceptor.Funcs.
205+
func (f *ClientBuilder) WithInterceptorFuncs(interceptorFuncs interceptor.Funcs) *ClientBuilder {
206+
f.interceptorFuncs = &interceptorFuncs
207+
return f
208+
}
209+
201210
// Build builds and returns a new fake client.
202211
func (f *ClientBuilder) Build() client.WithWatch {
203212
if f.scheme == nil {
@@ -240,13 +249,19 @@ func (f *ClientBuilder) Build() client.WithWatch {
240249
}
241250
}
242251

243-
return &fakeClient{
252+
var result client.WithWatch = &fakeClient{
244253
tracker: tracker,
245254
scheme: f.scheme,
246255
restMapper: f.restMapper,
247256
indexes: f.indexes,
248257
withStatusSubresource: withStatusSubResource,
249258
}
259+
260+
if f.interceptorFuncs != nil {
261+
result = interceptor.NewClient(result, *f.interceptorFuncs)
262+
}
263+
264+
return result
250265
}
251266

252267
const trackerAddResourceVersion = "999"

pkg/client/fake/client_test.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ import (
2323
"strconv"
2424
"time"
2525

26+
"sigs.k8s.io/controller-runtime/pkg/client/interceptor"
27+
2628
"github.com/google/go-cmp/cmp"
2729
. "github.com/onsi/ginkgo/v2"
2830
. "github.com/onsi/gomega"
@@ -1449,4 +1451,17 @@ var _ = Describe("Fake client builder", func() {
14491451
func(client.Object) []string { return []string{"foo"} })
14501452
}).To(Panic())
14511453
})
1454+
1455+
It("should wrap the fake client with an interceptor when WithInterceptorFuncs is called", func() {
1456+
var called bool
1457+
cli := NewClientBuilder().WithInterceptorFuncs(interceptor.Funcs{
1458+
Get: func(ctx context.Context, client client.WithWatch, key client.ObjectKey, obj client.Object, opts ...client.GetOption) error {
1459+
called = true
1460+
return nil
1461+
},
1462+
}).Build()
1463+
err := cli.Get(context.Background(), client.ObjectKey{}, &corev1.Pod{})
1464+
Expect(err).NotTo(HaveOccurred())
1465+
Expect(called).To(BeTrue())
1466+
})
14521467
})

pkg/client/interceptor/intercept.go

Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
package interceptor
2+
3+
import (
4+
"context"
5+
6+
"k8s.io/apimachinery/pkg/api/meta"
7+
"k8s.io/apimachinery/pkg/runtime"
8+
"k8s.io/apimachinery/pkg/runtime/schema"
9+
"k8s.io/apimachinery/pkg/watch"
10+
"sigs.k8s.io/controller-runtime/pkg/client"
11+
)
12+
13+
type (
14+
15+
// Funcs contains functions that are called instead of the underlying client's methods.
16+
Funcs struct {
17+
Get func(ctx context.Context, client client.WithWatch, key client.ObjectKey, obj client.Object, opts ...client.GetOption) error
18+
List func(ctx context.Context, client client.WithWatch, list client.ObjectList, opts ...client.ListOption) error
19+
Create func(ctx context.Context, client client.WithWatch, obj client.Object, opts ...client.CreateOption) error
20+
Delete func(ctx context.Context, client client.WithWatch, obj client.Object, opts ...client.DeleteOption) error
21+
DeleteAllOf func(ctx context.Context, client client.WithWatch, obj client.Object, opts ...client.DeleteAllOfOption) error
22+
Update func(ctx context.Context, client client.WithWatch, obj client.Object, opts ...client.UpdateOption) error
23+
Patch func(ctx context.Context, client client.WithWatch, obj client.Object, patch client.Patch, opts ...client.PatchOption) error
24+
Watch func(ctx context.Context, client client.WithWatch, obj client.ObjectList, opts ...client.ListOption) (watch.Interface, error)
25+
SubResource func(client client.WithWatch, subResource string) client.SubResourceClient
26+
}
27+
28+
// SubResourceFuncs is a set of functions that can be used to intercept calls to a SubResourceClient.
29+
SubResourceFuncs struct {
30+
Get func(ctx context.Context, client client.SubResourceClient, obj client.Object, subResource client.Object, opts ...client.SubResourceGetOption) error
31+
Create func(ctx context.Context, client client.SubResourceClient, obj client.Object, subResource client.Object, opts ...client.SubResourceCreateOption) error
32+
Update func(ctx context.Context, client client.SubResourceClient, obj client.Object, opts ...client.SubResourceUpdateOption) error
33+
Patch func(ctx context.Context, client client.SubResourceClient, obj client.Object, patch client.Patch, opts ...client.SubResourcePatchOption) error
34+
}
35+
)
36+
37+
// NewClient returns a new interceptor client that calls the functions in funcs instead of the underlying client's methods, if they are not nil.
38+
func NewClient(interceptedClient client.WithWatch, funcs Funcs) client.WithWatch {
39+
return interceptor{client: interceptedClient, funcs: funcs}
40+
}
41+
42+
// NewSubResourceClient returns a SubResourceClient that intercepts calls to the provided client with the provided functions.
43+
func NewSubResourceClient(interceptedClient client.SubResourceClient, funcs SubResourceFuncs) client.SubResourceClient {
44+
return subResourceInterceptor{client: interceptedClient, funcs: funcs}
45+
}
46+
47+
type interceptor struct {
48+
client client.WithWatch
49+
funcs Funcs
50+
}
51+
52+
var _ client.WithWatch = &interceptor{}
53+
54+
func (c interceptor) GroupVersionKindFor(obj runtime.Object) (schema.GroupVersionKind, error) {
55+
return c.client.GroupVersionKindFor(obj)
56+
}
57+
58+
func (c interceptor) IsObjectNamespaced(obj runtime.Object) (bool, error) {
59+
return c.client.IsObjectNamespaced(obj)
60+
}
61+
62+
func (c interceptor) Get(ctx context.Context, key client.ObjectKey, obj client.Object, opts ...client.GetOption) error {
63+
if c.funcs.Get != nil {
64+
return c.funcs.Get(ctx, c.client, key, obj, opts...)
65+
}
66+
return c.client.Get(ctx, key, obj, opts...)
67+
}
68+
69+
func (c interceptor) List(ctx context.Context, list client.ObjectList, opts ...client.ListOption) error {
70+
if c.funcs.List != nil {
71+
return c.funcs.List(ctx, c.client, list, opts...)
72+
}
73+
return c.client.List(ctx, list, opts...)
74+
}
75+
76+
func (c interceptor) Create(ctx context.Context, obj client.Object, opts ...client.CreateOption) error {
77+
if c.funcs.Create != nil {
78+
return c.funcs.Create(ctx, c.client, obj, opts...)
79+
}
80+
return c.client.Create(ctx, obj, opts...)
81+
}
82+
83+
func (c interceptor) Delete(ctx context.Context, obj client.Object, opts ...client.DeleteOption) error {
84+
if c.funcs.Delete != nil {
85+
return c.funcs.Delete(ctx, c.client, obj, opts...)
86+
}
87+
return c.client.Delete(ctx, obj, opts...)
88+
}
89+
90+
func (c interceptor) Update(ctx context.Context, obj client.Object, opts ...client.UpdateOption) error {
91+
if c.funcs.Update != nil {
92+
return c.funcs.Update(ctx, c.client, obj, opts...)
93+
}
94+
return c.client.Update(ctx, obj, opts...)
95+
}
96+
97+
func (c interceptor) Patch(ctx context.Context, obj client.Object, patch client.Patch, opts ...client.PatchOption) error {
98+
if c.funcs.Patch != nil {
99+
return c.funcs.Patch(ctx, c.client, obj, patch, opts...)
100+
}
101+
return c.client.Patch(ctx, obj, patch, opts...)
102+
}
103+
104+
func (c interceptor) DeleteAllOf(ctx context.Context, obj client.Object, opts ...client.DeleteAllOfOption) error {
105+
if c.funcs.DeleteAllOf != nil {
106+
return c.funcs.DeleteAllOf(ctx, c.client, obj, opts...)
107+
}
108+
return c.client.DeleteAllOf(ctx, obj, opts...)
109+
}
110+
111+
func (c interceptor) Status() client.SubResourceWriter {
112+
return c.SubResource("status")
113+
}
114+
115+
func (c interceptor) SubResource(subResource string) client.SubResourceClient {
116+
if c.funcs.SubResource != nil {
117+
return c.funcs.SubResource(c.client, subResource)
118+
}
119+
return c.client.SubResource(subResource)
120+
}
121+
122+
func (c interceptor) Scheme() *runtime.Scheme {
123+
return c.client.Scheme()
124+
}
125+
126+
func (c interceptor) RESTMapper() meta.RESTMapper {
127+
return c.client.RESTMapper()
128+
}
129+
130+
func (c interceptor) Watch(ctx context.Context, obj client.ObjectList, opts ...client.ListOption) (watch.Interface, error) {
131+
if c.funcs.Watch != nil {
132+
return c.funcs.Watch(ctx, c.client, obj, opts...)
133+
}
134+
return c.client.Watch(ctx, obj, opts...)
135+
}
136+
137+
type subResourceInterceptor struct {
138+
client client.SubResourceClient
139+
funcs SubResourceFuncs
140+
}
141+
142+
var _ client.SubResourceClient = &subResourceInterceptor{}
143+
144+
func (s subResourceInterceptor) Get(ctx context.Context, obj client.Object, subResource client.Object, opts ...client.SubResourceGetOption) error {
145+
if s.funcs.Get != nil {
146+
return s.funcs.Get(ctx, s.client, obj, subResource, opts...)
147+
}
148+
return s.client.Get(ctx, obj, subResource, opts...)
149+
}
150+
151+
func (s subResourceInterceptor) Create(ctx context.Context, obj client.Object, subResource client.Object, opts ...client.SubResourceCreateOption) error {
152+
if s.funcs.Create != nil {
153+
return s.funcs.Create(ctx, s.client, obj, subResource, opts...)
154+
}
155+
return s.client.Create(ctx, obj, subResource, opts...)
156+
}
157+
158+
func (s subResourceInterceptor) Update(ctx context.Context, obj client.Object, opts ...client.SubResourceUpdateOption) error {
159+
if s.funcs.Update != nil {
160+
return s.funcs.Update(ctx, s.client, obj, opts...)
161+
}
162+
return s.client.Update(ctx, obj, opts...)
163+
}
164+
165+
func (s subResourceInterceptor) Patch(ctx context.Context, obj client.Object, patch client.Patch, opts ...client.SubResourcePatchOption) error {
166+
if s.funcs.Patch != nil {
167+
return s.funcs.Patch(ctx, s.client, obj, patch, opts...)
168+
}
169+
return s.client.Patch(ctx, obj, patch, opts...)
170+
}

0 commit comments

Comments
 (0)