@@ -17,57 +17,71 @@ limitations under the License.
1717package replication
1818
1919import (
20+ "context"
2021 "fmt"
2122 "reflect"
2223
24+ "github.com/google/go-cmp/cmp"
25+
2326 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
2427 "k8s.io/apimachinery/pkg/runtime"
2528 genericrequest "k8s.io/apiserver/pkg/endpoints/request"
29+ "k8s.io/klog/v2"
2630)
2731
2832// ensureMeta changes unstructuredCacheObject's metadata to match unstructuredLocalObject's metadata except the ResourceVersion and the shard annotation fields.
29- func ensureMeta (cacheObject * unstructured.Unstructured , localObject * unstructured.Unstructured ) (changed bool , err error ) {
33+ func ensureMeta (cacheObject * unstructured.Unstructured , localObject * unstructured.Unstructured ) (changed bool , reason string , err error ) {
3034 cacheObjMetaRaw , hasCacheObjMetaRaw , err := unstructured .NestedFieldNoCopy (cacheObject .Object , "metadata" )
3135 if err != nil {
32- return false , err
36+ return false , "" , err
3337 }
3438 cacheObjMeta , ok := cacheObjMetaRaw .(map [string ]interface {})
3539 if ! ok {
36- return false , fmt .Errorf ("metadata field of unstructuredCacheObject is of the type %T, expected map[string]interface{}" , cacheObjMetaRaw )
40+ return false , "" , fmt .Errorf ("metadata field of unstructuredCacheObject is of the type %T, expected map[string]interface{}" , cacheObjMetaRaw )
3741 }
3842 localObjMetaRaw , hasLocalObjMetaRaw , err := unstructured .NestedFieldNoCopy (localObject .Object , "metadata" )
3943 if err != nil {
40- return false , err
44+ return false , "" , err
4145 }
4246 localObjMeta , ok := localObjMetaRaw .(map [string ]interface {})
4347 if ! ok {
44- return false , fmt .Errorf ("metadata field of unstructuredLocalObjectMeta is of the type %T, expected map[string]interface{}" , localObjMetaRaw )
48+ return false , "" , fmt .Errorf ("metadata field of unstructuredLocalObjectMeta is of the type %T, expected map[string]interface{}" , localObjMetaRaw )
4549 }
4650 if ! hasLocalObjMetaRaw && ! hasCacheObjMetaRaw {
47- return false , nil // no-op
51+ return false , "" , nil // no-op
4852 }
4953 if ! hasLocalObjMetaRaw {
5054 unstructured .RemoveNestedField (cacheObject .Object , "metadata" )
51- return true , nil
55+ return true , "local .metadata disappeared" , nil
5256 }
5357
54- // before we can compare the cache object we need to
55- // store, remove and then bring back fields that are unique only to the cache object
56- if cacheObjRV , found := cacheObjMeta ["resourceVersion" ]; found {
57- unstructured .RemoveNestedField (cacheObjMeta , "resourceVersion" )
58- defer func () {
59- if err == nil {
60- err = unstructured .SetNestedField (cacheObject .Object , cacheObjRV , "metadata" , "resourceVersion" )
61- }
62- }()
58+ // before we can compare the local and cache objects we need to remove certain
59+ // field we know will be different, and bring them back after the comparison.
60+ for _ , pth := range []string {"resourceVersion" , "generation" , "managedFields" } {
61+ if v , found := cacheObjMeta [pth ]; found {
62+ delete (cacheObjMeta , pth )
63+ defer func () {
64+ if err == nil {
65+ err = unstructured .SetNestedField (cacheObject .Object , v , "metadata" , pth )
66+ }
67+ }()
68+ }
69+ if v , found := localObjMeta [pth ]; found {
70+ delete (localObjMeta , pth )
71+ defer func () {
72+ if err == nil {
73+ err = unstructured .SetNestedField (localObject .Object , v , "metadata" , pth )
74+ }
75+ }()
76+ }
6377 }
6478 if cacheObjAnnotationsRaw , found := cacheObjMeta ["annotations" ]; found {
6579 cacheObjAnnotations , ok := cacheObjAnnotationsRaw .(map [string ]interface {})
6680 if ! ok {
67- return false , fmt .Errorf ("metadata.annotations field of unstructuredCacheObject is of the type %T, expected map[string]interface{}" , cacheObjAnnotationsRaw )
81+ return false , "" , fmt .Errorf ("metadata.annotations field of unstructuredCacheObject is of the type %T, expected map[string]interface{}" , cacheObjAnnotationsRaw )
6882 }
6983 if shard , hasShard := cacheObjAnnotations [genericrequest .ShardAnnotationKey ]; hasShard {
70- unstructured . RemoveNestedField (cacheObjAnnotations , genericrequest .ShardAnnotationKey )
84+ delete (cacheObjAnnotations , genericrequest .ShardAnnotationKey )
7185 defer func () {
7286 if err == nil {
7387 err = unstructured .SetNestedField (cacheObject .Object , shard , "metadata" , "annotations" , genericrequest .ShardAnnotationKey )
@@ -77,35 +91,29 @@ func ensureMeta(cacheObject *unstructured.Unstructured, localObject *unstructure
7791 // TODO: in the future the original RV will be stored in an annotation
7892 }
7993
80- // before we can compare with the local object we need to
81- // store, remove and then bring back the ResourceVersion on the local object
82- if localObjRV , found := localObjMeta ["resourceVersion" ]; found {
83- unstructured .RemoveNestedField (localObjMeta , "resourceVersion" )
84- defer func () {
85- if err == nil {
86- localObjMeta ["resourceVersion" ] = localObjRV
87- }
88- }()
89- }
90-
9194 changed = ! reflect .DeepEqual (cacheObjMeta , localObjMeta )
9295 if ! changed {
93- return false , nil
96+ return false , "" , nil
97+ }
98+
99+ reason = ".metadata changed"
100+ if klog .FromContext (context .Background ()).V (5 ).Enabled () {
101+ reason += " " + cmp .Diff (localObjMeta , cacheObjMeta )
94102 }
95103
96104 newCacheObjMeta := map [string ]interface {}{}
97105 for k , v := range localObjMeta {
98106 newCacheObjMeta [k ] = v
99107 }
100- return true , unstructured .SetNestedMap (cacheObject .Object , newCacheObjMeta , "metadata" )
108+ return true , reason , unstructured .SetNestedMap (cacheObject .Object , newCacheObjMeta , "metadata" )
101109}
102110
103111// ensureRemaining changes unstructuredCacheObject to match unstructuredLocalObject except for the metadata field
104112// returns true when the unstructuredCacheObject was updated.
105- func ensureRemaining (cacheObject * unstructured.Unstructured , localObject * unstructured.Unstructured ) (bool , error ) {
113+ func ensureRemaining (cacheObject * unstructured.Unstructured , localObject * unstructured.Unstructured ) (bool , string , error ) {
106114 cacheObjMeta , found , err := unstructured .NestedFieldNoCopy (cacheObject .Object , "metadata" )
107115 if err != nil {
108- return false , err
116+ return false , "" , err
109117 }
110118 if found {
111119 unstructured .RemoveNestedField (cacheObject .Object , "metadata" )
@@ -116,7 +124,7 @@ func ensureRemaining(cacheObject *unstructured.Unstructured, localObject *unstru
116124
117125 localObjMeta , found , err := unstructured .NestedFieldNoCopy (localObject .Object , "metadata" )
118126 if err != nil {
119- return false , err
127+ return false , "" , err
120128 }
121129 if found {
122130 unstructured .RemoveNestedField (localObject .Object , "metadata" )
@@ -127,15 +135,20 @@ func ensureRemaining(cacheObject *unstructured.Unstructured, localObject *unstru
127135
128136 changed := ! reflect .DeepEqual (cacheObject .Object , localObject .Object )
129137 if ! changed {
130- return false , nil
138+ return false , "" , nil
139+ }
140+
141+ reason := "object changed"
142+ if klog .FromContext (context .Background ()).V (5 ).Enabled () {
143+ reason += " " + cmp .Diff (localObject .Object , cacheObject .Object )
131144 }
132145
133146 newCacheObj := map [string ]interface {}{}
134147 for k , v := range localObject .Object {
135148 newCacheObj [k ] = v
136149 }
137150 cacheObject .Object = newCacheObj
138- return true , nil
151+ return true , reason , nil
139152}
140153
141154func toUnstructured (obj interface {}) (* unstructured.Unstructured , error ) {
0 commit comments