Skip to content

Commit 7557702

Browse files
committed
Remove DelegatedClient, move Options in client.New
Signed-off-by: Vince Prignano <[email protected]>
1 parent 595f569 commit 7557702

File tree

11 files changed

+156
-264
lines changed

11 files changed

+156
-264
lines changed

pkg/client/client.go

Lines changed: 94 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,21 @@ import (
3636
"sigs.k8s.io/controller-runtime/pkg/log"
3737
)
3838

39+
// Options are creation options for a Client.
40+
type Options struct {
41+
// Scheme, if provided, will be used to map go structs to GroupVersionKinds
42+
Scheme *runtime.Scheme
43+
44+
// Mapper, if provided, will be used to map GroupVersionKinds to Resources
45+
Mapper meta.RESTMapper
46+
47+
Cache *CacheOptions
48+
49+
// WarningHandler is used to configure the warning handler responsible for
50+
// surfacing and handling warnings messages sent by the API server.
51+
WarningHandler WarningHandlerOptions
52+
}
53+
3954
// WarningHandlerOptions are options for configuring a
4055
// warning handler for the client which is responsible
4156
// for surfacing API Server warnings.
@@ -50,19 +65,21 @@ type WarningHandlerOptions struct {
5065
AllowDuplicateLogs bool
5166
}
5267

53-
// Options are creation options for a Client.
54-
type Options struct {
55-
// Scheme, if provided, will be used to map go structs to GroupVersionKinds
56-
Scheme *runtime.Scheme
57-
58-
// Mapper, if provided, will be used to map GroupVersionKinds to Resources
59-
Mapper meta.RESTMapper
60-
61-
// Opts is used to configure the warning handler responsible for
62-
// surfacing and handling warnings messages sent by the API server.
63-
Opts WarningHandlerOptions
68+
// CacheOptions are options for creating a cache-backed client.
69+
type CacheOptions struct {
70+
// Reader is a cache-backed reader that will be used to read objects from the cache.
71+
// +required
72+
Reader Reader
73+
// DisableFor is a list of objects that should not be read from the cache.
74+
DisableFor []Object
75+
// Unstructured is a flag that indicates whether the cache-backed client should
76+
// read unstructured objects or lists from the cache.
77+
Unstructured bool
6478
}
6579

80+
// NewClientFunc allows a user to define how to create a client.
81+
type NewClientFunc func(config *rest.Config, options Options) (Client, error)
82+
6683
// New returns a new Client using the provided config and Options.
6784
// The returned client reads *and* writes directly from the server
6885
// (it doesn't use object caches). It understands how to work with
@@ -82,7 +99,7 @@ func newClient(config *rest.Config, options Options) (*client, error) {
8299
return nil, fmt.Errorf("must provide non-nil rest.Config to client.New")
83100
}
84101

85-
if !options.Opts.SuppressWarnings {
102+
if !options.WarningHandler.SuppressWarnings {
86103
// surface warnings
87104
logger := log.Log.WithName("KubeAPIWarningLogger")
88105
// Set a WarningHandler, the default WarningHandler
@@ -93,7 +110,7 @@ func newClient(config *rest.Config, options Options) (*client, error) {
93110
config.WarningHandler = log.NewKubeAPIWarningLogger(
94111
logger,
95112
log.KubeAPIWarningLoggerOptions{
96-
Deduplicate: !options.Opts.AllowDuplicateLogs,
113+
Deduplicate: !options.WarningHandler.AllowDuplicateLogs,
97114
},
98115
)
99116
}
@@ -143,7 +160,28 @@ func newClient(config *rest.Config, options Options) (*client, error) {
143160
scheme: options.Scheme,
144161
mapper: options.Mapper,
145162
}
163+
if options.Cache == nil {
164+
return c, nil
165+
}
166+
167+
// We want a cache if we're here.
168+
if options.Cache.Reader == nil {
169+
return nil, fmt.Errorf("must provide a Options.Cache.Reader when using a cache")
170+
}
146171

172+
// Set the cache.
173+
c.cache = options.Cache.Reader
174+
175+
// Load uncached GVKs.
176+
c.cacheUnstructured = options.Cache.Unstructured
177+
uncachedGVKs := map[schema.GroupVersionKind]struct{}{}
178+
for _, obj := range options.Cache.DisableFor {
179+
gvk, err := c.GroupVersionKindFor(obj)
180+
if err != nil {
181+
return nil, err
182+
}
183+
uncachedGVKs[gvk] = struct{}{}
184+
}
147185
return c, nil
148186
}
149187

@@ -157,6 +195,35 @@ type client struct {
157195
metadataClient metadataClient
158196
scheme *runtime.Scheme
159197
mapper meta.RESTMapper
198+
199+
cache Reader
200+
uncachedGVKs map[schema.GroupVersionKind]struct{}
201+
cacheUnstructured bool
202+
}
203+
204+
func (c *client) shouldBypassCache(obj runtime.Object) (bool, error) {
205+
if c.cache == nil {
206+
return true, nil
207+
}
208+
209+
gvk, err := c.GroupVersionKindFor(obj)
210+
if err != nil {
211+
return false, err
212+
}
213+
// TODO: this is producing unsafe guesses that don't actually work,
214+
// but it matches ~99% of the cases out there.
215+
if meta.IsListType(obj) {
216+
gvk.Kind = strings.TrimSuffix(gvk.Kind, "List")
217+
}
218+
if _, isUncached := c.uncachedGVKs[gvk]; isUncached {
219+
return true, nil
220+
}
221+
if !c.cacheUnstructured {
222+
_, isUnstructured := obj.(*unstructured.Unstructured)
223+
_, isUnstructuredList := obj.(*unstructured.UnstructuredList)
224+
return isUnstructured || isUnstructuredList, nil
225+
}
226+
return false, nil
160227
}
161228

162229
// resetGroupVersionKind is a helper function to restore and preserve GroupVersionKind on an object.
@@ -169,12 +236,12 @@ func (c *client) resetGroupVersionKind(obj runtime.Object, gvk schema.GroupVersi
169236
}
170237

171238
// GroupVersionKindFor returns the GroupVersionKind for the given object.
172-
func (c *client) GroupVersionKindFor(obj Object) (schema.GroupVersionKind, error) {
239+
func (c *client) GroupVersionKindFor(obj runtime.Object) (schema.GroupVersionKind, error) {
173240
return apiutil.GVKForObject(obj, c.scheme)
174241
}
175242

176243
// IsObjectNamespaced returns true if the GroupVersionKind of the object is namespaced.
177-
func (c *client) IsObjectNamespaced(obj Object) (bool, error) {
244+
func (c *client) IsObjectNamespaced(obj runtime.Object) (bool, error) {
178245
return apiutil.IsObjectNamespaced(obj, c.scheme, c.mapper)
179246
}
180247

@@ -252,6 +319,12 @@ func (c *client) Patch(ctx context.Context, obj Object, patch Patch, opts ...Pat
252319

253320
// Get implements client.Client.
254321
func (c *client) Get(ctx context.Context, key ObjectKey, obj Object, opts ...GetOption) error {
322+
if isUncached, err := c.shouldBypassCache(obj); err != nil {
323+
return err
324+
} else if !isUncached {
325+
return c.cache.Get(ctx, key, obj, opts...)
326+
}
327+
255328
switch obj.(type) {
256329
case *unstructured.Unstructured:
257330
return c.unstructuredClient.Get(ctx, key, obj, opts...)
@@ -266,6 +339,12 @@ func (c *client) Get(ctx context.Context, key ObjectKey, obj Object, opts ...Get
266339

267340
// List implements client.Client.
268341
func (c *client) List(ctx context.Context, obj ObjectList, opts ...ListOption) error {
342+
if isUncached, err := c.shouldBypassCache(obj); err != nil {
343+
return err
344+
} else if !isUncached {
345+
return c.cache.List(ctx, obj, opts...)
346+
}
347+
269348
switch x := obj.(type) {
270349
case *unstructured.UnstructuredList:
271350
return c.unstructuredClient.List(ctx, obj, opts...)

pkg/client/client_test.go

Lines changed: 33 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -3541,20 +3541,19 @@ U5wwSivyi7vmegHKmblOzNVKA5qPO8zWzqBC
35413541
})
35423542
})
35433543

3544-
var _ = Describe("DelegatingClient", func() {
3544+
var _ = Describe("ClientWithCache", func() {
35453545
Describe("Get", func() {
35463546
It("should call cache reader when structured object", func() {
35473547
cachedReader := &fakeReader{}
3548-
cl, err := client.New(cfg, client.Options{})
3549-
Expect(err).NotTo(HaveOccurred())
3550-
dReader, err := client.NewDelegatingClient(client.NewDelegatingClientInput{
3551-
CacheReader: cachedReader,
3552-
Client: cl,
3548+
cl, err := client.New(cfg, client.Options{
3549+
Cache: &client.CacheOptions{
3550+
Reader: cachedReader,
3551+
},
35533552
})
35543553
Expect(err).NotTo(HaveOccurred())
35553554
var actual appsv1.Deployment
35563555
key := client.ObjectKey{Namespace: "ns", Name: "name"}
3557-
Expect(dReader.Get(context.TODO(), key, &actual)).To(Succeed())
3556+
Expect(cl.Get(context.TODO(), key, &actual)).To(Succeed())
35583557
Expect(1).To(Equal(cachedReader.Called))
35593558
})
35603559

@@ -3590,11 +3589,10 @@ var _ = Describe("DelegatingClient", func() {
35903589
})
35913590
It("should call client reader when not cached", func() {
35923591
cachedReader := &fakeReader{}
3593-
cl, err := client.New(cfg, client.Options{})
3594-
Expect(err).NotTo(HaveOccurred())
3595-
dReader, err := client.NewDelegatingClient(client.NewDelegatingClientInput{
3596-
CacheReader: cachedReader,
3597-
Client: cl,
3592+
cl, err := client.New(cfg, client.Options{
3593+
Cache: &client.CacheOptions{
3594+
Reader: cachedReader,
3595+
},
35983596
})
35993597
Expect(err).NotTo(HaveOccurred())
36003598

@@ -3606,17 +3604,16 @@ var _ = Describe("DelegatingClient", func() {
36063604
})
36073605
actual.SetName(dep.Name)
36083606
key := client.ObjectKey{Namespace: dep.Namespace, Name: dep.Name}
3609-
Expect(dReader.Get(context.TODO(), key, actual)).To(Succeed())
3607+
Expect(cl.Get(context.TODO(), key, actual)).To(Succeed())
36103608
Expect(0).To(Equal(cachedReader.Called))
36113609
})
36123610
It("should call cache reader when cached", func() {
36133611
cachedReader := &fakeReader{}
3614-
cl, err := client.New(cfg, client.Options{})
3615-
Expect(err).NotTo(HaveOccurred())
3616-
dReader, err := client.NewDelegatingClient(client.NewDelegatingClientInput{
3617-
CacheReader: cachedReader,
3618-
Client: cl,
3619-
CacheUnstructured: true,
3612+
cl, err := client.New(cfg, client.Options{
3613+
Cache: &client.CacheOptions{
3614+
Reader: cachedReader,
3615+
Unstructured: true,
3616+
},
36203617
})
36213618
Expect(err).NotTo(HaveOccurred())
36223619

@@ -3628,34 +3625,32 @@ var _ = Describe("DelegatingClient", func() {
36283625
})
36293626
actual.SetName(dep.Name)
36303627
key := client.ObjectKey{Namespace: dep.Namespace, Name: dep.Name}
3631-
Expect(dReader.Get(context.TODO(), key, actual)).To(Succeed())
3628+
Expect(cl.Get(context.TODO(), key, actual)).To(Succeed())
36323629
Expect(1).To(Equal(cachedReader.Called))
36333630
})
36343631
})
36353632
})
36363633
Describe("List", func() {
36373634
It("should call cache reader when structured object", func() {
36383635
cachedReader := &fakeReader{}
3639-
cl, err := client.New(cfg, client.Options{})
3640-
Expect(err).NotTo(HaveOccurred())
3641-
dReader, err := client.NewDelegatingClient(client.NewDelegatingClientInput{
3642-
CacheReader: cachedReader,
3643-
Client: cl,
3636+
cl, err := client.New(cfg, client.Options{
3637+
Cache: &client.CacheOptions{
3638+
Reader: cachedReader,
3639+
},
36443640
})
36453641
Expect(err).NotTo(HaveOccurred())
36463642
var actual appsv1.DeploymentList
3647-
Expect(dReader.List(context.Background(), &actual)).To(Succeed())
3643+
Expect(cl.List(context.Background(), &actual)).To(Succeed())
36483644
Expect(1).To(Equal(cachedReader.Called))
36493645
})
36503646

36513647
When("listing unstructured objects", func() {
36523648
It("should call client reader when not cached", func() {
36533649
cachedReader := &fakeReader{}
3654-
cl, err := client.New(cfg, client.Options{})
3655-
Expect(err).NotTo(HaveOccurred())
3656-
dReader, err := client.NewDelegatingClient(client.NewDelegatingClientInput{
3657-
CacheReader: cachedReader,
3658-
Client: cl,
3650+
cl, err := client.New(cfg, client.Options{
3651+
Cache: &client.CacheOptions{
3652+
Reader: cachedReader,
3653+
},
36593654
})
36603655
Expect(err).NotTo(HaveOccurred())
36613656

@@ -3665,17 +3660,16 @@ var _ = Describe("DelegatingClient", func() {
36653660
Kind: "DeploymentList",
36663661
Version: "v1",
36673662
})
3668-
Expect(dReader.List(context.Background(), actual)).To(Succeed())
3663+
Expect(cl.List(context.Background(), actual)).To(Succeed())
36693664
Expect(0).To(Equal(cachedReader.Called))
36703665
})
36713666
It("should call cache reader when cached", func() {
36723667
cachedReader := &fakeReader{}
3673-
cl, err := client.New(cfg, client.Options{})
3674-
Expect(err).NotTo(HaveOccurred())
3675-
dReader, err := client.NewDelegatingClient(client.NewDelegatingClientInput{
3676-
CacheReader: cachedReader,
3677-
Client: cl,
3678-
CacheUnstructured: true,
3668+
cl, err := client.New(cfg, client.Options{
3669+
Cache: &client.CacheOptions{
3670+
Reader: cachedReader,
3671+
Unstructured: true,
3672+
},
36793673
})
36803674
Expect(err).NotTo(HaveOccurred())
36813675

@@ -3685,7 +3679,7 @@ var _ = Describe("DelegatingClient", func() {
36853679
Kind: "DeploymentList",
36863680
Version: "v1",
36873681
})
3688-
Expect(dReader.List(context.Background(), actual)).To(Succeed())
3682+
Expect(cl.List(context.Background(), actual)).To(Succeed())
36893683
Expect(1).To(Equal(cachedReader.Called))
36903684
})
36913685
})

pkg/client/dryrun.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,12 +48,12 @@ func (c *dryRunClient) RESTMapper() meta.RESTMapper {
4848
}
4949

5050
// GroupVersionKindFor returns the GroupVersionKind for the given object.
51-
func (c *dryRunClient) GroupVersionKindFor(obj Object) (schema.GroupVersionKind, error) {
51+
func (c *dryRunClient) GroupVersionKindFor(obj runtime.Object) (schema.GroupVersionKind, error) {
5252
return c.client.GroupVersionKindFor(obj)
5353
}
5454

5555
// IsObjectNamespaced returns true if the GroupVersionKind of the object is namespaced.
56-
func (c *dryRunClient) IsObjectNamespaced(obj Object) (bool, error) {
56+
func (c *dryRunClient) IsObjectNamespaced(obj runtime.Object) (bool, error) {
5757
return c.client.IsObjectNamespaced(obj)
5858
}
5959

pkg/client/fake/client.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -564,12 +564,12 @@ func (c *fakeClient) RESTMapper() meta.RESTMapper {
564564
}
565565

566566
// GroupVersionKindFor returns the GroupVersionKind for the given object.
567-
func (c *fakeClient) GroupVersionKindFor(obj client.Object) (schema.GroupVersionKind, error) {
567+
func (c *fakeClient) GroupVersionKindFor(obj runtime.Object) (schema.GroupVersionKind, error) {
568568
return apiutil.GVKForObject(obj, c.scheme)
569569
}
570570

571571
// IsObjectNamespaced returns true if the GroupVersionKind of the object is namespaced.
572-
func (c *fakeClient) IsObjectNamespaced(obj client.Object) (bool, error) {
572+
func (c *fakeClient) IsObjectNamespaced(obj runtime.Object) (bool, error) {
573573
return apiutil.IsObjectNamespaced(obj, c.scheme, c.restMapper)
574574
}
575575

pkg/client/interfaces.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -171,9 +171,9 @@ type Client interface {
171171
// RESTMapper returns the rest this client is using.
172172
RESTMapper() meta.RESTMapper
173173
// GroupVersionKindFor returns the GroupVersionKind for the given object.
174-
GroupVersionKindFor(obj Object) (schema.GroupVersionKind, error)
174+
GroupVersionKindFor(obj runtime.Object) (schema.GroupVersionKind, error)
175175
// IsObjectNamespaced returns true if the GroupVersionKind of the object is namespaced.
176-
IsObjectNamespaced(obj Object) (bool, error)
176+
IsObjectNamespaced(obj runtime.Object) (bool, error)
177177
}
178178

179179
// WithWatch supports Watch on top of the CRUD operations supported by

pkg/client/namespaced_client.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,12 +53,12 @@ func (n *namespacedClient) RESTMapper() meta.RESTMapper {
5353
}
5454

5555
// GroupVersionKindFor returns the GroupVersionKind for the given object.
56-
func (n *namespacedClient) GroupVersionKindFor(obj Object) (schema.GroupVersionKind, error) {
56+
func (n *namespacedClient) GroupVersionKindFor(obj runtime.Object) (schema.GroupVersionKind, error) {
5757
return n.client.GroupVersionKindFor(obj)
5858
}
5959

6060
// IsObjectNamespaced returns true if the GroupVersionKind of the object is namespaced.
61-
func (n *namespacedClient) IsObjectNamespaced(obj Object) (bool, error) {
61+
func (n *namespacedClient) IsObjectNamespaced(obj runtime.Object) (bool, error) {
6262
return n.client.IsObjectNamespaced(obj)
6363
}
6464

0 commit comments

Comments
 (0)