1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package provider
17
18 import (
19 "context"
20 "reflect"
21 "strconv"
22
23 lru "github.com/hashicorp/golang-lru"
24 "k8s.io/apimachinery/pkg/api/meta"
25 "k8s.io/apimachinery/pkg/runtime"
26 "k8s.io/apimachinery/pkg/types"
27 "sigs.k8s.io/controller-runtime/pkg/client"
28 "sigs.k8s.io/controller-runtime/pkg/client/apiutil"
29 )
30
31 var _ client.Client = &UpdatedClient{}
32
33 type UpdatedClient struct {
34 client client.Client
35 scheme *runtime.Scheme
36
37 cache *lru.Cache
38 }
39
40 func (c *UpdatedClient) objectKey(key client.ObjectKey, obj client.Object) (string, error) {
41 gvk, err := apiutil.GVKForObject(obj, c.scheme)
42 if err != nil {
43 return "", err
44 }
45
46 return gvk.String() + "/" + key.String(), nil
47 }
48
49 func (c *UpdatedClient) Get(ctx context.Context, key client.ObjectKey, obj client.Object) error {
50 err := c.client.Get(ctx, key, obj)
51 if err != nil {
52 return err
53 }
54
55 objectKey, err := c.objectKey(key, obj)
56 if err != nil {
57 return err
58 }
59
60 cachedObject, ok := c.cache.Get(objectKey)
61 if ok {
62 cachedMeta, err := meta.Accessor(cachedObject)
63 if err != nil {
64 return nil
65 }
66
67 objMeta, err := meta.Accessor(obj)
68 if err != nil {
69 return nil
70 }
71
72 cachedResourceVersion, err := strconv.Atoi(cachedMeta.GetResourceVersion())
73 if err != nil {
74 return nil
75 }
76 newResourceVersion, err := strconv.Atoi(objMeta.GetResourceVersion())
77 if err != nil {
78 return nil
79 }
80 if cachedResourceVersion >= newResourceVersion {
81 cachedObject := cachedObject.(runtime.Object).DeepCopyObject()
82
83 reflect.ValueOf(obj).Elem().Set(reflect.ValueOf(cachedObject).Elem())
84 }
85 }
86
87 return nil
88 }
89
90 func (c *UpdatedClient) List(ctx context.Context, list client.ObjectList, opts ...client.ListOption) error {
91 return c.client.List(ctx, list, opts...)
92 }
93
94 func (c *UpdatedClient) Create(ctx context.Context, obj client.Object, opts ...client.CreateOption) error {
95 return c.client.Create(ctx, obj, opts...)
96 }
97
98 func (c *UpdatedClient) Delete(ctx context.Context, obj client.Object, opts ...client.DeleteOption) error {
99 return c.client.Delete(ctx, obj, opts...)
100 }
101
102 func (c *UpdatedClient) Update(ctx context.Context, obj client.Object, opts ...client.UpdateOption) error {
103 err := c.client.Update(ctx, obj, opts...)
104 if err != nil {
105 return err
106 }
107
108 err = c.writeCache(obj)
109 if err != nil {
110 return err
111 }
112
113 return nil
114 }
115
116 func (c *UpdatedClient) writeCache(obj client.Object) error {
117 objMeta, err := meta.Accessor(obj)
118 if err != nil {
119 return nil
120 }
121
122 objectKey, err := c.objectKey(types.NamespacedName{
123 Namespace: objMeta.GetNamespace(),
124 Name: objMeta.GetName(),
125 }, obj)
126 if err != nil {
127 return err
128 }
129
130 c.cache.Add(objectKey, obj.DeepCopyObject())
131
132 return nil
133 }
134
135 func (c *UpdatedClient) Patch(ctx context.Context, obj client.Object, patch client.Patch, opts ...client.PatchOption) error {
136 return c.client.Patch(ctx, obj, patch, opts...)
137 }
138
139 func (c *UpdatedClient) DeleteAllOf(ctx context.Context, obj client.Object, opts ...client.DeleteAllOfOption) error {
140 return c.client.DeleteAllOf(ctx, obj, opts...)
141 }
142
143 func (c *UpdatedClient) Scheme() *runtime.Scheme {
144 return c.client.Scheme()
145 }
146
147 func (c *UpdatedClient) RESTMapper() meta.RESTMapper {
148 return c.client.RESTMapper()
149 }
150
151 func (c *UpdatedClient) Status() client.StatusWriter {
152 return &UpdatedStatusWriter{
153 statusWriter: c.client.Status(),
154 client: c,
155 }
156 }
157
158 type UpdatedStatusWriter struct {
159 statusWriter client.StatusWriter
160 client *UpdatedClient
161 }
162
163 func (c *UpdatedStatusWriter) Update(ctx context.Context, obj client.Object, opts ...client.UpdateOption) error {
164 err := c.statusWriter.Update(ctx, obj, opts...)
165 if err != nil {
166 return err
167 }
168
169 err = c.client.writeCache(obj)
170 if err != nil {
171 return err
172 }
173
174 return nil
175 }
176
177 func (c *UpdatedStatusWriter) Patch(ctx context.Context, obj client.Object, patch client.Patch, opts ...client.PatchOption) error {
178 return c.statusWriter.Patch(ctx, obj, patch, opts...)
179 }
180