@@ -36,6 +36,22 @@ 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, if provided, is used to read objects from the cache.
48+ Cache * CacheOptions
49+
50+ // WarningHandler is used to configure the warning handler responsible for
51+ // surfacing and handling warnings messages sent by the API server.
52+ WarningHandler WarningHandlerOptions
53+ }
54+
3955// WarningHandlerOptions are options for configuring a
4056// warning handler for the client which is responsible
4157// for surfacing API Server warnings.
@@ -50,19 +66,21 @@ type WarningHandlerOptions struct {
5066 AllowDuplicateLogs bool
5167}
5268
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
69+ // CacheOptions are options for creating a cache-backed client.
70+ type CacheOptions struct {
71+ // Reader is a cache-backed reader that will be used to read objects from the cache.
72+ // +required
73+ Reader Reader
74+ // DisableFor is a list of objects that should not be read from the cache.
75+ DisableFor []Object
76+ // Unstructured is a flag that indicates whether the cache-backed client should
77+ // read unstructured objects or lists from the cache.
78+ Unstructured bool
6479}
6580
81+ // NewClientFunc allows a user to define how to create a client.
82+ type NewClientFunc func (config * rest.Config , options Options ) (Client , error )
83+
6684// New returns a new Client using the provided config and Options.
6785// The returned client reads *and* writes directly from the server
6886// (it doesn't use object caches). It understands how to work with
@@ -82,7 +100,7 @@ func newClient(config *rest.Config, options Options) (*client, error) {
82100 return nil , fmt .Errorf ("must provide non-nil rest.Config to client.New" )
83101 }
84102
85- if ! options .Opts .SuppressWarnings {
103+ if ! options .WarningHandler .SuppressWarnings {
86104 // surface warnings
87105 logger := log .Log .WithName ("KubeAPIWarningLogger" )
88106 // Set a WarningHandler, the default WarningHandler
@@ -93,7 +111,7 @@ func newClient(config *rest.Config, options Options) (*client, error) {
93111 config .WarningHandler = log .NewKubeAPIWarningLogger (
94112 logger ,
95113 log.KubeAPIWarningLoggerOptions {
96- Deduplicate : ! options .Opts .AllowDuplicateLogs ,
114+ Deduplicate : ! options .WarningHandler .AllowDuplicateLogs ,
97115 },
98116 )
99117 }
@@ -143,7 +161,24 @@ func newClient(config *rest.Config, options Options) (*client, error) {
143161 scheme : options .Scheme ,
144162 mapper : options .Mapper ,
145163 }
164+ if options .Cache == nil || options .Cache .Reader == nil {
165+ return c , nil
166+ }
167+
168+ // We want a cache if we're here.
169+ // Set the cache.
170+ c .cache = options .Cache .Reader
146171
172+ // Load uncached GVKs.
173+ c .cacheUnstructured = options .Cache .Unstructured
174+ uncachedGVKs := map [schema.GroupVersionKind ]struct {}{}
175+ for _ , obj := range options .Cache .DisableFor {
176+ gvk , err := c .GroupVersionKindFor (obj )
177+ if err != nil {
178+ return nil , err
179+ }
180+ uncachedGVKs [gvk ] = struct {}{}
181+ }
147182 return c , nil
148183}
149184
@@ -157,6 +192,35 @@ type client struct {
157192 metadataClient metadataClient
158193 scheme * runtime.Scheme
159194 mapper meta.RESTMapper
195+
196+ cache Reader
197+ uncachedGVKs map [schema.GroupVersionKind ]struct {}
198+ cacheUnstructured bool
199+ }
200+
201+ func (c * client ) shouldBypassCache (obj runtime.Object ) (bool , error ) {
202+ if c .cache == nil {
203+ return true , nil
204+ }
205+
206+ gvk , err := c .GroupVersionKindFor (obj )
207+ if err != nil {
208+ return false , err
209+ }
210+ // TODO: this is producing unsafe guesses that don't actually work,
211+ // but it matches ~99% of the cases out there.
212+ if meta .IsListType (obj ) {
213+ gvk .Kind = strings .TrimSuffix (gvk .Kind , "List" )
214+ }
215+ if _ , isUncached := c .uncachedGVKs [gvk ]; isUncached {
216+ return true , nil
217+ }
218+ if ! c .cacheUnstructured {
219+ _ , isUnstructured := obj .(* unstructured.Unstructured )
220+ _ , isUnstructuredList := obj .(* unstructured.UnstructuredList )
221+ return isUnstructured || isUnstructuredList , nil
222+ }
223+ return false , nil
160224}
161225
162226// resetGroupVersionKind is a helper function to restore and preserve GroupVersionKind on an object.
@@ -169,12 +233,12 @@ func (c *client) resetGroupVersionKind(obj runtime.Object, gvk schema.GroupVersi
169233}
170234
171235// GroupVersionKindFor returns the GroupVersionKind for the given object.
172- func (c * client ) GroupVersionKindFor (obj Object ) (schema.GroupVersionKind , error ) {
236+ func (c * client ) GroupVersionKindFor (obj runtime. Object ) (schema.GroupVersionKind , error ) {
173237 return apiutil .GVKForObject (obj , c .scheme )
174238}
175239
176240// IsObjectNamespaced returns true if the GroupVersionKind of the object is namespaced.
177- func (c * client ) IsObjectNamespaced (obj Object ) (bool , error ) {
241+ func (c * client ) IsObjectNamespaced (obj runtime. Object ) (bool , error ) {
178242 return apiutil .IsObjectNamespaced (obj , c .scheme , c .mapper )
179243}
180244
@@ -252,6 +316,12 @@ func (c *client) Patch(ctx context.Context, obj Object, patch Patch, opts ...Pat
252316
253317// Get implements client.Client.
254318func (c * client ) Get (ctx context.Context , key ObjectKey , obj Object , opts ... GetOption ) error {
319+ if isUncached , err := c .shouldBypassCache (obj ); err != nil {
320+ return err
321+ } else if ! isUncached {
322+ return c .cache .Get (ctx , key , obj , opts ... )
323+ }
324+
255325 switch obj .(type ) {
256326 case * unstructured.Unstructured :
257327 return c .unstructuredClient .Get (ctx , key , obj , opts ... )
@@ -266,6 +336,12 @@ func (c *client) Get(ctx context.Context, key ObjectKey, obj Object, opts ...Get
266336
267337// List implements client.Client.
268338func (c * client ) List (ctx context.Context , obj ObjectList , opts ... ListOption ) error {
339+ if isUncached , err := c .shouldBypassCache (obj ); err != nil {
340+ return err
341+ } else if ! isUncached {
342+ return c .cache .List (ctx , obj , opts ... )
343+ }
344+
269345 switch x := obj .(type ) {
270346 case * unstructured.UnstructuredList :
271347 return c .unstructuredClient .List (ctx , obj , opts ... )
0 commit comments