@@ -85,7 +85,7 @@ func (c *Controller) reconcile(ctx context.Context, gvrKey string) error {
8585 r := & replicationReconciler {
8686 shardName : c .shardName ,
8787 localLabelSelector : c .localLabelSelector ,
88- getLocalCopy : func (cluster logicalcluster.Name , namespace , name string ) (* unstructured.Unstructured , error ) {
88+ getLocalPartialObjectMetadata : func (cluster logicalcluster.Name , namespace , name string ) (* unstructured.Unstructured , error ) {
8989 gvr := gvrFromKey
9090 key := kcpcache .ToClusterAwareKey (cluster .String (), namespace , name )
9191 obj , exists , err := c .replicated .Local .GetIndexer ().GetByKey (key )
@@ -111,7 +111,7 @@ func (c *Controller) reconcile(ctx context.Context, gvrKey string) error {
111111 u .SetAPIVersion (gvr .GroupVersion ().String ())
112112 return u , nil
113113 },
114- getGlobalCopy : func (cluster logicalcluster.Name , namespace , name string ) (* unstructured. Unstructured , error ) {
114+ getCachedObject : func (ctx context. Context , cluster logicalcluster.Name , namespace , name string ) (* cachev1alpha1. CachedObject , error ) {
115115 gvr := gvrFromKey
116116 if gvr .Group == "" {
117117 gvr .Group = "core"
@@ -129,28 +129,53 @@ func (c *Controller) reconcile(ctx context.Context, gvrKey string) error {
129129 return nil , fmt .Errorf ("found multiple objects for %v|%v/%v" , cluster , namespace , name )
130130 }
131131
132- obj := objs [0 ]
132+ return objs [0 ].(* cachev1alpha1.CachedObject ), nil
133+ },
134+ getGlobalCopyFromCachedObject : func (cachedObj * cachev1alpha1.CachedObject ) (* unstructured.Unstructured , error ) {
135+ gvr := gvrFromKey
133136
134- u , err := toUnstructured (obj )
137+ u , err := toUnstructured (& cachedObj . Spec . Raw )
135138 if err != nil {
136139 return nil , err
137140 }
138- if _ , ok := obj .(* unstructured.Unstructured ); ok {
139- u = u .DeepCopy ()
140- }
141-
141+ u = u .DeepCopy ()
142142 u .SetKind (c .replicated .Kind )
143143 u .SetAPIVersion (gvr .GroupVersion ().String ())
144-
145144 return u , nil
146145 },
147- createObject : func (ctx context.Context , cluster logicalcluster.Name , obj * unstructured.Unstructured ) (* cachev1alpha1.CachedObject , error ) {
146+ getLocalCopy : func (ctx context.Context , cluster logicalcluster.Name , namespace , name string ) (* unstructured.Unstructured , error ) {
147+ gvr := gvrFromKey
148+
149+ obj , err := c .dynamicClusterClient .Cluster (cluster .Path ()).
150+ Resource (gvrFromKey ).
151+ Namespace (namespace ).
152+ Get (ctx , name , metav1.GetOptions {})
153+ if err != nil {
154+ return nil , err
155+ }
156+
157+ obj .SetKind (c .replicated .Kind )
158+ obj .SetAPIVersion (gvr .GroupVersion ().String ())
159+
160+ // Append system annotations to the object.
161+ annotations := obj .GetAnnotations ()
162+ if annotations == nil {
163+ annotations = map [string ]string {}
164+ }
165+ annotations [genericrequest .ShardAnnotationKey ] = c .shardName
166+ annotations [AnnotationKeyOriginalResourceUID ] = string (obj .GetUID ())
167+ annotations [AnnotationKeyOriginalResourceVersion ] = obj .GetResourceVersion ()
168+ obj .SetAnnotations (annotations )
169+
170+ return obj , nil
171+ },
172+ createObject : func (ctx context.Context , cluster logicalcluster.Name , local * unstructured.Unstructured ) (* cachev1alpha1.CachedObject , error ) {
148173 gvr := gvrFromKey
149174 if gvr .Group == "" {
150175 gvr .Group = "core"
151176 }
152177
153- objBytes , err := json .Marshal (obj )
178+ objBytes , err := json .Marshal (local )
154179 if err != nil {
155180 return nil , err
156181 }
@@ -160,9 +185,9 @@ func (c *Controller) reconcile(ctx context.Context, gvrKey string) error {
160185 APIVersion : cachev1alpha1 .SchemeGroupVersion .String (),
161186 },
162187 ObjectMeta : metav1.ObjectMeta {
163- Name : GenCachedObjectName (gvr , obj .GetNamespace (), obj .GetName ()),
164- Labels : obj .GetLabels (),
165- Annotations : obj .GetAnnotations (),
188+ Name : GenCachedObjectName (gvr , local .GetNamespace (), local .GetName ()),
189+ Labels : local .GetLabels (),
190+ Annotations : local .GetAnnotations (),
166191 CreationTimestamp : metav1 .NewTime (time .Now ()),
167192 },
168193 Spec : cachev1alpha1.CachedObjectSpec {
@@ -177,19 +202,19 @@ func (c *Controller) reconcile(ctx context.Context, gvrKey string) error {
177202 cacheObj .Labels [LabelKeyObjectGroup ] = gvr .Group
178203 cacheObj .Labels [LabelKeyObjectVersion ] = gvr .Version
179204 cacheObj .Labels [LabelKeyObjectResource ] = gvr .Resource
180- cacheObj .Labels [LabelKeyObjectOriginalName ] = obj .GetName ()
181- cacheObj .Labels [LabelKeyObjectOriginalNamespace ] = obj .GetNamespace ()
205+ cacheObj .Labels [LabelKeyObjectOriginalName ] = local .GetName ()
206+ cacheObj .Labels [LabelKeyObjectOriginalNamespace ] = local .GetNamespace ()
182207
183208 u , err := c .kcpCacheClient .Cluster (cluster .Path ()).CacheV1alpha1 ().CachedObjects ().Create (ctx , cacheObj , metav1.CreateOptions {})
184209 return u , err
185210 },
186- updateObject : func (ctx context.Context , cluster logicalcluster.Name , obj * unstructured.Unstructured ) (* cachev1alpha1.CachedObject , error ) {
211+ updateCachedObjectWithLocalUnstructured : func (ctx context.Context , cluster logicalcluster.Name , origCachedObj * cachev1alpha1. CachedObject , local * unstructured.Unstructured ) (* cachev1alpha1.CachedObject , error ) {
187212 gvr := gvrFromKey
188213 if gvr .Group == "" {
189214 gvr .Group = "core"
190215 }
191216
192- objBytes , err := json .Marshal (obj )
217+ objBytes , err := json .Marshal (local )
193218 if err != nil {
194219 return nil , err
195220 }
@@ -200,10 +225,10 @@ func (c *Controller) reconcile(ctx context.Context, gvrKey string) error {
200225 APIVersion : cachev1alpha1 .SchemeGroupVersion .String (),
201226 },
202227 ObjectMeta : metav1.ObjectMeta {
203- Name : GenCachedObjectName (gvr , obj .GetNamespace (), obj .GetName ()),
204- Labels : obj .GetLabels (),
205- Annotations : obj .GetAnnotations (),
206- ResourceVersion : obj .GetResourceVersion (),
228+ Name : GenCachedObjectName (gvr , local .GetNamespace (), local .GetName ()),
229+ Labels : origCachedObj .GetLabels (),
230+ Annotations : origCachedObj .GetAnnotations (),
231+ ResourceVersion : origCachedObj .GetResourceVersion (),
207232 },
208233 Spec : cachev1alpha1.CachedObjectSpec {
209234 Raw : runtime.RawExtension {Raw : objBytes },
@@ -217,8 +242,8 @@ func (c *Controller) reconcile(ctx context.Context, gvrKey string) error {
217242 cacheObj .Labels [LabelKeyObjectGroup ] = gvr .Group
218243 cacheObj .Labels [LabelKeyObjectVersion ] = gvr .Version
219244 cacheObj .Labels [LabelKeyObjectResource ] = gvr .Resource
220- cacheObj .Labels [LabelKeyObjectOriginalName ] = obj .GetName ()
221- cacheObj .Labels [LabelKeyObjectOriginalNamespace ] = obj .GetNamespace ()
245+ cacheObj .Labels [LabelKeyObjectOriginalName ] = local .GetName ()
246+ cacheObj .Labels [LabelKeyObjectOriginalNamespace ] = local .GetNamespace ()
222247
223248 return c .kcpCacheClient .Cluster (cluster .Path ()).CacheV1alpha1 ().CachedObjects ().Update (ctx , cacheObj , metav1.UpdateOptions {})
224249 },
@@ -246,12 +271,14 @@ type replicationReconciler struct {
246271 deleted bool
247272 localLabelSelector labels.Selector
248273
249- getLocalCopy func (cluster logicalcluster.Name , namespace , name string ) (* unstructured.Unstructured , error )
250- getGlobalCopy func (cluster logicalcluster.Name , namespace , name string ) (* unstructured.Unstructured , error )
274+ getLocalPartialObjectMetadata func (cluster logicalcluster.Name , namespace , name string ) (* unstructured.Unstructured , error )
275+ getCachedObject func (ctx context.Context , cluster logicalcluster.Name , namespace , name string ) (* cachev1alpha1.CachedObject , error )
276+ getGlobalCopyFromCachedObject func (cachedObj * cachev1alpha1.CachedObject ) (* unstructured.Unstructured , error )
277+ getLocalCopy func (ctx context.Context , cluster logicalcluster.Name , namespace , name string ) (* unstructured.Unstructured , error )
251278
252- createObject func (ctx context.Context , cluster logicalcluster.Name , obj * unstructured.Unstructured ) (* cachev1alpha1.CachedObject , error )
253- updateObject func (ctx context.Context , cluster logicalcluster.Name , obj * unstructured.Unstructured ) (* cachev1alpha1.CachedObject , error )
254- deleteObject func (ctx context.Context , cluster logicalcluster.Name , ns , name string ) error
279+ createObject func (ctx context.Context , cluster logicalcluster.Name , local * unstructured.Unstructured ) (* cachev1alpha1.CachedObject , error )
280+ updateCachedObjectWithLocalUnstructured func (ctx context.Context , cluster logicalcluster.Name , cachedObj * cachev1alpha1. CachedObject , local * unstructured.Unstructured ) (* cachev1alpha1.CachedObject , error )
281+ deleteObject func (ctx context.Context , cluster logicalcluster.Name , ns , name string ) error
255282}
256283
257284// reconcile makes sure that the object under the given key from the local shard is replicated to the cache server.
@@ -271,29 +298,28 @@ func (r *replicationReconciler) reconcile(ctx context.Context, key string) error
271298 return nil
272299 }
273300
274- localCopy , err := r .getLocalCopy (clusterName , ns , name )
301+ localPartialObjMeta , err := r .getLocalPartialObjectMetadata (clusterName , ns , name )
275302 if err != nil && ! apierrors .IsNotFound (err ) {
276303 utilruntime .HandleError (err )
277304 return nil
278305 }
279306 localExists := ! apierrors .IsNotFound (err )
280307
281308 // we only replicate objects that match the label selector.
282- if localExists && r .localLabelSelector != nil && ! r .localLabelSelector .Matches (labels .Set (localCopy .GetLabels ())) {
309+ if localExists && r .localLabelSelector != nil && ! r .localLabelSelector .Matches (labels .Set (localPartialObjMeta .GetLabels ())) {
283310 logger .V (2 ).WithValues ("cluster" , clusterName , "namespace" , ns , "name" , name ).Info ("Object does not match label selector, skipping" )
284311 return nil
285312 }
286313
287- globalCopy , err := r .getGlobalCopy ( clusterName , ns , name )
314+ cachedObj , err := r .getCachedObject ( ctx , clusterName , ns , name )
288315 if err != nil && ! apierrors .IsNotFound (err ) {
289- utilruntime .HandleError (err )
290- return nil
316+ return err
291317 }
292- globalExists := ! apierrors .IsNotFound (err )
318+ cachedObjExists := ! apierrors .IsNotFound (err )
293319
294320 // local is gone or being deleted. Delete in cache.
295- if ! localExists || ! localCopy .GetDeletionTimestamp ().IsZero () {
296- if ! globalExists {
321+ if ! localExists || ! localPartialObjMeta .GetDeletionTimestamp ().IsZero () {
322+ if ! cachedObjExists {
297323 return nil
298324 }
299325
@@ -306,24 +332,30 @@ func (r *replicationReconciler) reconcile(ctx context.Context, key string) error
306332 return nil
307333 }
308334
309- // local exists, global doesn't. Create in cache.
310- if ! globalExists {
311- originalRV := localCopy .GetResourceVersion ()
312- originalUID := localCopy .GetUID ()
313-
314- localCopy .SetResourceVersion ("" )
315- annotations := localCopy .GetAnnotations ()
316- if annotations == nil {
317- annotations = map [string ]string {}
335+ var globalCopy * unstructured.Unstructured
336+ if cachedObjExists {
337+ globalCopy , err = r .getGlobalCopyFromCachedObject (cachedObj )
338+ if err != nil {
339+ return err
340+ }
341+ // Exit early if there were no changes on the resource.
342+ if localPartialObjMeta .GetResourceVersion () != "" && globalCopy .GetResourceVersion () == localPartialObjMeta .GetResourceVersion () {
343+ logger .V (4 ).Info ("Object is up to date" )
344+ return nil
318345 }
319- annotations [genericrequest .ShardAnnotationKey ] = r .shardName
320- annotations [AnnotationKeyOriginalResourceVersion ] = originalRV
321- annotations [AnnotationKeyOriginalResourceUID ] = string (originalUID )
346+ }
322347
323- localCopy .SetAnnotations (annotations )
348+ // The local DDSIF informer yields only PartialObjectMetadata, and we need the full object for replication.
349+ localCopy , err := r .getLocalCopy (ctx , clusterName , ns , name )
350+ if err != nil {
351+ // Return any error we get. If it's NotFound, we want to requeue in that case too: the local DDSIF
352+ // informer probably hasn't caught up yet, and we may need to clean up the replicated CachedObject.
353+ return err
354+ }
324355
356+ if ! cachedObjExists {
325357 logger .V (2 ).Info ("Creating object in global cache" )
326- _ , err : = r .createObject (ctx , clusterName , localCopy )
358+ _ , err = r .createObject (ctx , clusterName , localCopy )
327359 return err
328360 }
329361
@@ -342,6 +374,6 @@ func (r *replicationReconciler) reconcile(ctx context.Context, key string) error
342374 }
343375
344376 logger .V (2 ).WithValues ("kind" , globalCopy .GetKind (), "namespace" , globalCopy .GetNamespace (), "name" , globalCopy .GetName ()).Info ("Updating object in global cache" )
345- _ , err = r .updateObject (ctx , clusterName , globalCopy ) // no need for patch because there is only this actor
377+ _ , err = r .updateCachedObjectWithLocalUnstructured (ctx , clusterName , cachedObj , localCopy ) // no need for patch because there is only this actor
346378 return err
347379}
0 commit comments