Skip to content

Commit

Permalink
Squashed commit of the following: (#2248)
Browse files Browse the repository at this point in the history
commit 3b534a4
Merge: f264518 a24b949
Author: Ludovic Cleroux <ludydoo@gmail.com>
Date:   Tue Apr 25 12:58:42 2023 +0200

    Merge branch 'kubernetes-sigs:main' into add-fake-client-interception

commit f264518
Author: Ludovic Cleroux <ludydoo@gmail.com>
Date:   Tue Apr 25 12:57:18 2023 +0200

    PR comments

commit e358be3
Author: Ludovic Cleroux <ludydoo@gmail.com>
Date:   Sun Apr 16 10:38:37 2023 +0200

    Allow the interceptor to be discovered from the fake ClientBuilder

commit bf25854
Author: Ludovic Cleroux <ludydoo@gmail.com>
Date:   Sun Apr 16 10:21:37 2023 +0200

    Remove dependency on fake client

commit 6063435
Author: Ludovic Cleroux <ludydoo@gmail.com>
Date:   Sun Apr 16 10:02:42 2023 +0200

    Cleanup

commit b66b57a
Author: Ludovic Cleroux <ludydoo@gmail.com>
Date:   Sun Apr 16 10:02:05 2023 +0200

    Cleanup

commit 483deea
Author: Ludovic Cleroux <ludydoo@gmail.com>
Date:   Sun Apr 16 10:00:39 2023 +0200

    Cleanup

commit 9c3df6a
Author: Ludovic Cleroux <ludydoo@gmail.com>
Date:   Sun Apr 16 09:59:19 2023 +0200

    Slight refactor

commit 4fb67c0
Author: Ludovic Cleroux <ludydoo@gmail.com>
Date:   Sun Apr 16 09:56:24 2023 +0200

    Slight refactor & lint

commit 5221233
Author: Ludovic Cleroux <ludydoo@gmail.com>
Date:   Sun Apr 16 09:30:52 2023 +0200

    Move interceptor to new package

commit d74b869
Merge: 9642a63 d989e66
Author: Ludovic Cleroux <ludydoo@gmail.com>
Date:   Sun Apr 16 09:21:31 2023 +0200

    Merge branch 'kubernetes-sigs:main' into add-fake-client-interception

commit 9642a63
Author: Ludovic Cleroux <ludydoo@gmail.com>
Date:   Wed Mar 29 09:34:28 2023 +0200

    Fixed SubResource/Status calling logic

commit 00597ac
Author: Ludovic Cleroux <ludydoo@gmail.com>
Date:   Tue Mar 28 09:44:36 2023 +0200

    Remove unnecessary Status() intercept

commit cd8451c
Author: Ludovic Cleroux <ludydoo@gmail.com>
Date:   Mon Mar 27 15:52:40 2023 +0200

    Fix goimports

commit c80013e
Author: Ludovic Cleroux <ludydoo@gmail.com>
Date:   Mon Mar 27 15:42:45 2023 +0200

    Fix missing comments

commit fe27862
Author: Ludovic Cleroux <ludydoo@gmail.com>
Date:   Mon Mar 27 15:30:13 2023 +0200

    Added methods to intercept calls to a fake k8s client
  • Loading branch information
ludydoo committed Apr 25, 2023
1 parent a24b949 commit 0f5aba9
Show file tree
Hide file tree
Showing 4 changed files with 633 additions and 2 deletions.
19 changes: 17 additions & 2 deletions pkg/client/fake/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ import (
"strings"
"sync"

"sigs.k8s.io/controller-runtime/pkg/client/interceptor"

apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
Expand Down Expand Up @@ -107,6 +109,7 @@ type ClientBuilder struct {
initRuntimeObjects []runtime.Object
withStatusSubresource []client.Object
objectTracker testing.ObjectTracker
interceptorFuncs *interceptor.Funcs

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

// WithStatusSubresource configures the passed object with a status subresource, which means
// calls to Update and Patch will not alters its status.
// calls to Update and Patch will not alter its status.
func (f *ClientBuilder) WithStatusSubresource(o ...client.Object) *ClientBuilder {
f.withStatusSubresource = append(f.withStatusSubresource, o...)
return f
}

// WithInterceptorFuncs configures the client methods to be intercepted using the provided interceptor.Funcs.
func (f *ClientBuilder) WithInterceptorFuncs(interceptorFuncs interceptor.Funcs) *ClientBuilder {
f.interceptorFuncs = &interceptorFuncs
return f
}

// Build builds and returns a new fake client.
func (f *ClientBuilder) Build() client.WithWatch {
if f.scheme == nil {
Expand Down Expand Up @@ -240,13 +249,19 @@ func (f *ClientBuilder) Build() client.WithWatch {
}
}

return &fakeClient{
var result client.WithWatch = &fakeClient{
tracker: tracker,
scheme: f.scheme,
restMapper: f.restMapper,
indexes: f.indexes,
withStatusSubresource: withStatusSubResource,
}

if f.interceptorFuncs != nil {
result = interceptor.NewClient(result, *f.interceptorFuncs)
}

return result
}

const trackerAddResourceVersion = "999"
Expand Down
15 changes: 15 additions & 0 deletions pkg/client/fake/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ import (
"strconv"
"time"

"sigs.k8s.io/controller-runtime/pkg/client/interceptor"

"github.com/google/go-cmp/cmp"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
Expand Down Expand Up @@ -1449,4 +1451,17 @@ var _ = Describe("Fake client builder", func() {
func(client.Object) []string { return []string{"foo"} })
}).To(Panic())
})

It("should wrap the fake client with an interceptor when WithInterceptorFuncs is called", func() {
var called bool
cli := NewClientBuilder().WithInterceptorFuncs(interceptor.Funcs{
Get: func(ctx context.Context, client client.WithWatch, key client.ObjectKey, obj client.Object, opts ...client.GetOption) error {
called = true
return nil
},
}).Build()
err := cli.Get(context.Background(), client.ObjectKey{}, &corev1.Pod{})
Expect(err).NotTo(HaveOccurred())
Expect(called).To(BeTrue())
})
})
170 changes: 170 additions & 0 deletions pkg/client/interceptor/intercept.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
package interceptor

import (
"context"

"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/watch"
"sigs.k8s.io/controller-runtime/pkg/client"
)

type (

// Funcs contains functions that are called instead of the underlying client's methods.
Funcs struct {
Get func(ctx context.Context, client client.WithWatch, key client.ObjectKey, obj client.Object, opts ...client.GetOption) error
List func(ctx context.Context, client client.WithWatch, list client.ObjectList, opts ...client.ListOption) error
Create func(ctx context.Context, client client.WithWatch, obj client.Object, opts ...client.CreateOption) error
Delete func(ctx context.Context, client client.WithWatch, obj client.Object, opts ...client.DeleteOption) error
DeleteAllOf func(ctx context.Context, client client.WithWatch, obj client.Object, opts ...client.DeleteAllOfOption) error
Update func(ctx context.Context, client client.WithWatch, obj client.Object, opts ...client.UpdateOption) error
Patch func(ctx context.Context, client client.WithWatch, obj client.Object, patch client.Patch, opts ...client.PatchOption) error
Watch func(ctx context.Context, client client.WithWatch, obj client.ObjectList, opts ...client.ListOption) (watch.Interface, error)
SubResource func(client client.WithWatch, subResource string) client.SubResourceClient
}

// SubResourceFuncs is a set of functions that can be used to intercept calls to a SubResourceClient.
SubResourceFuncs struct {
Get func(ctx context.Context, client client.SubResourceClient, obj client.Object, subResource client.Object, opts ...client.SubResourceGetOption) error
Create func(ctx context.Context, client client.SubResourceClient, obj client.Object, subResource client.Object, opts ...client.SubResourceCreateOption) error
Update func(ctx context.Context, client client.SubResourceClient, obj client.Object, opts ...client.SubResourceUpdateOption) error
Patch func(ctx context.Context, client client.SubResourceClient, obj client.Object, patch client.Patch, opts ...client.SubResourcePatchOption) error
}
)

// NewClient returns a new interceptor client that calls the functions in funcs instead of the underlying client's methods, if they are not nil.
func NewClient(interceptedClient client.WithWatch, funcs Funcs) client.WithWatch {
return interceptor{client: interceptedClient, funcs: funcs}
}

// NewSubResourceClient returns a SubResourceClient that intercepts calls to the provided client with the provided functions.
func NewSubResourceClient(interceptedClient client.SubResourceClient, funcs SubResourceFuncs) client.SubResourceClient {
return subResourceInterceptor{client: interceptedClient, funcs: funcs}
}

type interceptor struct {
client client.WithWatch
funcs Funcs
}

var _ client.WithWatch = &interceptor{}

func (c interceptor) GroupVersionKindFor(obj runtime.Object) (schema.GroupVersionKind, error) {
return c.client.GroupVersionKindFor(obj)
}

func (c interceptor) IsObjectNamespaced(obj runtime.Object) (bool, error) {
return c.client.IsObjectNamespaced(obj)
}

func (c interceptor) Get(ctx context.Context, key client.ObjectKey, obj client.Object, opts ...client.GetOption) error {
if c.funcs.Get != nil {
return c.funcs.Get(ctx, c.client, key, obj, opts...)
}
return c.client.Get(ctx, key, obj, opts...)
}

func (c interceptor) List(ctx context.Context, list client.ObjectList, opts ...client.ListOption) error {
if c.funcs.List != nil {
return c.funcs.List(ctx, c.client, list, opts...)
}
return c.client.List(ctx, list, opts...)
}

func (c interceptor) Create(ctx context.Context, obj client.Object, opts ...client.CreateOption) error {
if c.funcs.Create != nil {
return c.funcs.Create(ctx, c.client, obj, opts...)
}
return c.client.Create(ctx, obj, opts...)
}

func (c interceptor) Delete(ctx context.Context, obj client.Object, opts ...client.DeleteOption) error {
if c.funcs.Delete != nil {
return c.funcs.Delete(ctx, c.client, obj, opts...)
}
return c.client.Delete(ctx, obj, opts...)
}

func (c interceptor) Update(ctx context.Context, obj client.Object, opts ...client.UpdateOption) error {
if c.funcs.Update != nil {
return c.funcs.Update(ctx, c.client, obj, opts...)
}
return c.client.Update(ctx, obj, opts...)
}

func (c interceptor) Patch(ctx context.Context, obj client.Object, patch client.Patch, opts ...client.PatchOption) error {
if c.funcs.Patch != nil {
return c.funcs.Patch(ctx, c.client, obj, patch, opts...)
}
return c.client.Patch(ctx, obj, patch, opts...)
}

func (c interceptor) DeleteAllOf(ctx context.Context, obj client.Object, opts ...client.DeleteAllOfOption) error {
if c.funcs.DeleteAllOf != nil {
return c.funcs.DeleteAllOf(ctx, c.client, obj, opts...)
}
return c.client.DeleteAllOf(ctx, obj, opts...)
}

func (c interceptor) Status() client.SubResourceWriter {
return c.SubResource("status")
}

func (c interceptor) SubResource(subResource string) client.SubResourceClient {
if c.funcs.SubResource != nil {
return c.funcs.SubResource(c.client, subResource)
}
return c.client.SubResource(subResource)
}

func (c interceptor) Scheme() *runtime.Scheme {
return c.client.Scheme()
}

func (c interceptor) RESTMapper() meta.RESTMapper {
return c.client.RESTMapper()
}

func (c interceptor) Watch(ctx context.Context, obj client.ObjectList, opts ...client.ListOption) (watch.Interface, error) {
if c.funcs.Watch != nil {
return c.funcs.Watch(ctx, c.client, obj, opts...)
}
return c.client.Watch(ctx, obj, opts...)
}

type subResourceInterceptor struct {
client client.SubResourceClient
funcs SubResourceFuncs
}

var _ client.SubResourceClient = &subResourceInterceptor{}

func (s subResourceInterceptor) Get(ctx context.Context, obj client.Object, subResource client.Object, opts ...client.SubResourceGetOption) error {
if s.funcs.Get != nil {
return s.funcs.Get(ctx, s.client, obj, subResource, opts...)
}
return s.client.Get(ctx, obj, subResource, opts...)
}

func (s subResourceInterceptor) Create(ctx context.Context, obj client.Object, subResource client.Object, opts ...client.SubResourceCreateOption) error {
if s.funcs.Create != nil {
return s.funcs.Create(ctx, s.client, obj, subResource, opts...)
}
return s.client.Create(ctx, obj, subResource, opts...)
}

func (s subResourceInterceptor) Update(ctx context.Context, obj client.Object, opts ...client.SubResourceUpdateOption) error {
if s.funcs.Update != nil {
return s.funcs.Update(ctx, s.client, obj, opts...)
}
return s.client.Update(ctx, obj, opts...)
}

func (s subResourceInterceptor) Patch(ctx context.Context, obj client.Object, patch client.Patch, opts ...client.SubResourcePatchOption) error {
if s.funcs.Patch != nil {
return s.funcs.Patch(ctx, s.client, obj, patch, opts...)
}
return s.client.Patch(ctx, obj, patch, opts...)
}
Loading

0 comments on commit 0f5aba9

Please sign in to comment.