From 5d33ccd6d2e10222ab962c0646f8d9ab90e29648 Mon Sep 17 00:00:00 2001 From: Xinzhao Xu Date: Tue, 29 Aug 2023 14:59:26 +0800 Subject: [PATCH] kubernetes/client: add a new function to generate cache client only --- kubernetes/client/generic/client.go | 105 ++++++++++++++----- kubernetes/client/typed/typed.go | 2 +- kubernetes/client/typed/typed_test.go | 20 ++-- kubernetes/client/typed/unstructured.go | 2 +- kubernetes/client/typed/unstructured_test.go | 18 ++-- 5 files changed, 102 insertions(+), 45 deletions(-) diff --git a/kubernetes/client/generic/client.go b/kubernetes/client/generic/client.go index 41166d8..7fe4547 100644 --- a/kubernetes/client/generic/client.go +++ b/kubernetes/client/generic/client.go @@ -3,8 +3,10 @@ package generic import ( "context" "errors" + "net/http" "time" + "k8s.io/apimachinery/pkg/api/meta" "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/kubernetes/scheme" "k8s.io/client-go/rest" @@ -19,6 +21,8 @@ type Options struct { scheme *runtime.Scheme cacheReader bool ctx context.Context + httpClient *http.Client + mapper meta.RESTMapper } // WithSyncPeriod sets the SyncPeriod time option. @@ -53,6 +57,61 @@ func WithContext(ctx context.Context) func(opts *Options) { } } +// WithHTTPClient sets the HTTPClient for the client. +func WithHTTPClient(httpClient *http.Client) func(opts *Options) { + return func(opts *Options) { + opts.httpClient = httpClient + } +} + +// WithMapper sets the Mapper for the client. +func WithMapper(mapper meta.RESTMapper) func(opts *Options) { + return func(opts *Options) { + opts.mapper = mapper + } +} + +// NewCache returns a controller-runtime cache client implementation. +func NewCache(config *rest.Config, options ...func(*Options)) (cache.Cache, error) { + opts := &Options{ + scheme: scheme.Scheme, + ctx: context.Background(), + } + for _, f := range options { + f(opts) + } + + if opts.httpClient == nil { + httpClient, err := rest.HTTPClientFor(config) + if err != nil { + return nil, err + } + opts.httpClient = httpClient + } + if opts.mapper == nil { + mapper, err := apiutil.NewDynamicRESTMapper(config, opts.httpClient) + if err != nil { + return nil, err + } + opts.mapper = mapper + } + + cacheClient, err := cache.New(config, cache.Options{ + HTTPClient: opts.httpClient, + Scheme: opts.scheme, + Mapper: opts.mapper, + SyncPeriod: opts.syncPeriod, + }) + if err != nil { + return nil, err + } + go cacheClient.Start(opts.ctx) // nolint + if !cacheClient.WaitForCacheSync(opts.ctx) { + return nil, errors.New("WaitForCacheSync failed") + } + return cacheClient, nil +} + // NewClient returns a controller-runtime generic Client implementation. func NewClient(config *rest.Config, options ...func(*Options)) (client.Client, error) { opts := &Options{ @@ -64,44 +123,42 @@ func NewClient(config *rest.Config, options ...func(*Options)) (client.Client, e f(opts) } - httpClient, err := rest.HTTPClientFor(config) - if err != nil { - return nil, err + if opts.httpClient == nil { + httpClient, err := rest.HTTPClientFor(config) + if err != nil { + return nil, err + } + opts.httpClient = httpClient } - mapper, err := apiutil.NewDynamicRESTMapper(config, httpClient) - if err != nil { - return nil, err + if opts.mapper == nil { + mapper, err := apiutil.NewDynamicRESTMapper(config, opts.httpClient) + if err != nil { + return nil, err + } + opts.mapper = mapper } clientOptions := client.Options{ - HTTPClient: httpClient, + HTTPClient: opts.httpClient, Scheme: opts.scheme, - Mapper: mapper, + Mapper: opts.mapper, } if opts.cacheReader { - cacheClient, err := cache.New(config, cache.Options{ - HTTPClient: httpClient, - Scheme: opts.scheme, - Mapper: mapper, - SyncPeriod: opts.syncPeriod, - }) + cacheClient, err := NewCache(config, + WithHTTPClient(opts.httpClient), + WithScheme(opts.scheme), + WithMapper(opts.mapper), + WithSyncPeriod(opts.syncPeriod), + WithContext(opts.ctx), + ) if err != nil { return nil, err } - go cacheClient.Start(opts.ctx) // nolint - if !cacheClient.WaitForCacheSync(opts.ctx) { - return nil, errors.New("WaitForCacheSync failed") - } clientOptions.Cache = &client.CacheOptions{ Reader: cacheClient, Unstructured: true, } } - - genericClient, err := client.New(config, clientOptions) - if err != nil { - return nil, err - } - return genericClient, nil + return client.New(config, clientOptions) } diff --git a/kubernetes/client/typed/typed.go b/kubernetes/client/typed/typed.go index 090bd9e..8217f25 100644 --- a/kubernetes/client/typed/typed.go +++ b/kubernetes/client/typed/typed.go @@ -39,7 +39,7 @@ func NewTypedClient(gvk schema.GroupVersionKind, opts ...func(*options)) (Client } o.config = inClusterConfig } - cache, err := genericclient.NewClient(o.config, genericclient.WithScheme(o.scheme)) + cache, err := genericclient.NewCache(o.config, genericclient.WithScheme(o.scheme)) if err != nil { return nil, err } diff --git a/kubernetes/client/typed/typed_test.go b/kubernetes/client/typed/typed_test.go index f53bdd3..17621ce 100644 --- a/kubernetes/client/typed/typed_test.go +++ b/kubernetes/client/typed/typed_test.go @@ -6,9 +6,9 @@ import ( corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/types" - "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client/fake" ) @@ -24,7 +24,7 @@ var ( func TestTypedGet(t *testing.T) { tests := []struct { name string - objs []client.Object + objs []runtime.Object gvk schema.GroupVersionKind key types.NamespacedName isErr bool @@ -32,7 +32,7 @@ func TestTypedGet(t *testing.T) { }{ { name: "normal test", - objs: []client.Object{pod1}, + objs: []runtime.Object{pod1}, gvk: corev1.SchemeGroupVersion.WithKind("Pod"), key: types.NamespacedName{ Name: pod1Name, @@ -41,7 +41,7 @@ func TestTypedGet(t *testing.T) { }, { name: "not exists test", - objs: []client.Object{pod1}, + objs: []runtime.Object{pod1}, gvk: corev1.SchemeGroupVersion.WithKind("Pod"), key: types.NamespacedName{ Name: "aaa", @@ -50,7 +50,7 @@ func TestTypedGet(t *testing.T) { }, { name: "wrong type test", - objs: []client.Object{pod1}, + objs: []runtime.Object{pod1}, gvk: corev1.SchemeGroupVersion.WithKind("Pod1"), key: types.NamespacedName{ Name: pod1Name, @@ -60,7 +60,7 @@ func TestTypedGet(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - c := fake.NewClientBuilder().WithObjects(tt.objs...).Build() + c := fake.NewFakeClient(tt.objs...) typedClient, _ := NewTypedClient(tt.gvk, WithClientReader(c)) got, err := typedClient.Get(context.TODO(), tt.key) @@ -81,27 +81,27 @@ func TestTypedGet(t *testing.T) { func TestTypedList(t *testing.T) { tests := []struct { name string - objs []client.Object + objs []runtime.Object gvk schema.GroupVersionKind isErr bool wanted int }{ { name: "normal test", - objs: []client.Object{pod1}, + objs: []runtime.Object{pod1}, gvk: corev1.SchemeGroupVersion.WithKind("Pod"), wanted: 1, }, { name: "wrong type test", - objs: []client.Object{pod1}, + objs: []runtime.Object{pod1}, gvk: corev1.SchemeGroupVersion.WithKind("Pod1"), isErr: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - c := fake.NewClientBuilder().WithObjects(tt.objs...).Build() + c := fake.NewFakeClient(tt.objs...) typedClient, _ := NewTypedClient(tt.gvk, WithClientReader(c)) got, err := typedClient.List(context.TODO(), metav1.NamespaceAll) diff --git a/kubernetes/client/typed/unstructured.go b/kubernetes/client/typed/unstructured.go index e699426..beb09cd 100644 --- a/kubernetes/client/typed/unstructured.go +++ b/kubernetes/client/typed/unstructured.go @@ -35,7 +35,7 @@ func NewUnstructuredTypedClient(gvk schema.GroupVersionKind, opts ...func(*optio } o.config = inClusterConfig } - cache, err := genericclient.NewClient(o.config) + cache, err := genericclient.NewCache(o.config) if err != nil { return nil, err } diff --git a/kubernetes/client/typed/unstructured_test.go b/kubernetes/client/typed/unstructured_test.go index cdfcb5d..150ea9c 100644 --- a/kubernetes/client/typed/unstructured_test.go +++ b/kubernetes/client/typed/unstructured_test.go @@ -7,9 +7,9 @@ import ( corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/types" - "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client/fake" unstructuredutils "github.com/iawia002/lia/kubernetes/unstructured" @@ -18,7 +18,7 @@ import ( func TestUnstructuredGet(t *testing.T) { tests := []struct { name string - objs []client.Object + objs []runtime.Object gvk schema.GroupVersionKind key types.NamespacedName isErr bool @@ -26,7 +26,7 @@ func TestUnstructuredGet(t *testing.T) { }{ { name: "normal test", - objs: []client.Object{pod1}, + objs: []runtime.Object{pod1}, gvk: corev1.SchemeGroupVersion.WithKind("Pod"), key: types.NamespacedName{ Name: pod1Name, @@ -35,7 +35,7 @@ func TestUnstructuredGet(t *testing.T) { }, { name: "not exists test", - objs: []client.Object{pod1}, + objs: []runtime.Object{pod1}, gvk: corev1.SchemeGroupVersion.WithKind("Pod"), key: types.NamespacedName{ Name: "aaa", @@ -44,7 +44,7 @@ func TestUnstructuredGet(t *testing.T) { }, { name: "wrong type test", - objs: []client.Object{pod1}, + objs: []runtime.Object{pod1}, gvk: corev1.SchemeGroupVersion.WithKind("Pod1"), key: types.NamespacedName{ Name: pod1Name, @@ -54,7 +54,7 @@ func TestUnstructuredGet(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - c := fake.NewClientBuilder().WithObjects(tt.objs...).Build() + c := fake.NewFakeClient(tt.objs...) typedClient, _ := NewUnstructuredTypedClient(tt.gvk, WithClientReader(c)) got, err := typedClient.Get(context.TODO(), tt.key) @@ -76,21 +76,21 @@ func TestUnstructuredGet(t *testing.T) { func TestUnstructuredList(t *testing.T) { tests := []struct { name string - objs []client.Object + objs []runtime.Object gvk schema.GroupVersionKind isErr bool wanted int }{ { name: "normal test", - objs: []client.Object{pod1}, + objs: []runtime.Object{pod1}, gvk: corev1.SchemeGroupVersion.WithKind("Pod"), wanted: 1, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - c := fake.NewClientBuilder().WithObjects(tt.objs...).Build() + c := fake.NewFakeClient(tt.objs...) typedClient, _ := NewUnstructuredTypedClient(tt.gvk, WithClientReader(c)) got, err := typedClient.List(context.TODO(), metav1.NamespaceAll)