@@ -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.
254321func (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.
268341func (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 ... )
0 commit comments