@@ -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,28 @@ func newClient(config *rest.Config, options Options) (*client, error) {
143161 scheme : options .Scheme ,
144162 mapper : options .Mapper ,
145163 }
164+ if options .Cache == nil {
165+ return c , nil
166+ }
167+
168+ // We want a cache if we're here.
169+ if options .Cache .Reader == nil {
170+ return nil , fmt .Errorf ("must provide a Options.Cache.Reader when using a cache" )
171+ }
146172
173+ // Set the cache.
174+ c .cache = options .Cache .Reader
175+
176+ // Load uncached GVKs.
177+ c .cacheUnstructured = options .Cache .Unstructured
178+ uncachedGVKs := map [schema.GroupVersionKind ]struct {}{}
179+ for _ , obj := range options .Cache .DisableFor {
180+ gvk , err := c .GroupVersionKindFor (obj )
181+ if err != nil {
182+ return nil , err
183+ }
184+ uncachedGVKs [gvk ] = struct {}{}
185+ }
147186 return c , nil
148187}
149188
@@ -157,6 +196,35 @@ type client struct {
157196 metadataClient metadataClient
158197 scheme * runtime.Scheme
159198 mapper meta.RESTMapper
199+
200+ cache Reader
201+ uncachedGVKs map [schema.GroupVersionKind ]struct {}
202+ cacheUnstructured bool
203+ }
204+
205+ func (c * client ) shouldBypassCache (obj runtime.Object ) (bool , error ) {
206+ if c .cache == nil {
207+ return true , nil
208+ }
209+
210+ gvk , err := c .GroupVersionKindFor (obj )
211+ if err != nil {
212+ return false , err
213+ }
214+ // TODO: this is producing unsafe guesses that don't actually work,
215+ // but it matches ~99% of the cases out there.
216+ if meta .IsListType (obj ) {
217+ gvk .Kind = strings .TrimSuffix (gvk .Kind , "List" )
218+ }
219+ if _ , isUncached := c .uncachedGVKs [gvk ]; isUncached {
220+ return true , nil
221+ }
222+ if ! c .cacheUnstructured {
223+ _ , isUnstructured := obj .(* unstructured.Unstructured )
224+ _ , isUnstructuredList := obj .(* unstructured.UnstructuredList )
225+ return isUnstructured || isUnstructuredList , nil
226+ }
227+ return false , nil
160228}
161229
162230// resetGroupVersionKind is a helper function to restore and preserve GroupVersionKind on an object.
@@ -169,12 +237,12 @@ func (c *client) resetGroupVersionKind(obj runtime.Object, gvk schema.GroupVersi
169237}
170238
171239// GroupVersionKindFor returns the GroupVersionKind for the given object.
172- func (c * client ) GroupVersionKindFor (obj Object ) (schema.GroupVersionKind , error ) {
240+ func (c * client ) GroupVersionKindFor (obj runtime. Object ) (schema.GroupVersionKind , error ) {
173241 return apiutil .GVKForObject (obj , c .scheme )
174242}
175243
176244// IsObjectNamespaced returns true if the GroupVersionKind of the object is namespaced.
177- func (c * client ) IsObjectNamespaced (obj Object ) (bool , error ) {
245+ func (c * client ) IsObjectNamespaced (obj runtime. Object ) (bool , error ) {
178246 return apiutil .IsObjectNamespaced (obj , c .scheme , c .mapper )
179247}
180248
@@ -252,6 +320,12 @@ func (c *client) Patch(ctx context.Context, obj Object, patch Patch, opts ...Pat
252320
253321// Get implements client.Client.
254322func (c * client ) Get (ctx context.Context , key ObjectKey , obj Object , opts ... GetOption ) error {
323+ if isUncached , err := c .shouldBypassCache (obj ); err != nil {
324+ return err
325+ } else if ! isUncached {
326+ return c .cache .Get (ctx , key , obj , opts ... )
327+ }
328+
255329 switch obj .(type ) {
256330 case * unstructured.Unstructured :
257331 return c .unstructuredClient .Get (ctx , key , obj , opts ... )
@@ -266,6 +340,12 @@ func (c *client) Get(ctx context.Context, key ObjectKey, obj Object, opts ...Get
266340
267341// List implements client.Client.
268342func (c * client ) List (ctx context.Context , obj ObjectList , opts ... ListOption ) error {
343+ if isUncached , err := c .shouldBypassCache (obj ); err != nil {
344+ return err
345+ } else if ! isUncached {
346+ return c .cache .List (ctx , obj , opts ... )
347+ }
348+
269349 switch x := obj .(type ) {
270350 case * unstructured.UnstructuredList :
271351 return c .unstructuredClient .List (ctx , obj , opts ... )
0 commit comments