@@ -17,57 +17,71 @@ limitations under the License.
17
17
package replication
18
18
19
19
import (
20
+ "context"
20
21
"fmt"
21
22
"reflect"
22
23
24
+ "github.com/google/go-cmp/cmp"
25
+
23
26
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
24
27
"k8s.io/apimachinery/pkg/runtime"
25
28
genericrequest "k8s.io/apiserver/pkg/endpoints/request"
29
+ "k8s.io/klog/v2"
26
30
)
27
31
28
32
// 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 ) {
30
34
cacheObjMetaRaw , hasCacheObjMetaRaw , err := unstructured .NestedFieldNoCopy (cacheObject .Object , "metadata" )
31
35
if err != nil {
32
- return false , err
36
+ return false , "" , err
33
37
}
34
38
cacheObjMeta , ok := cacheObjMetaRaw .(map [string ]interface {})
35
39
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 )
37
41
}
38
42
localObjMetaRaw , hasLocalObjMetaRaw , err := unstructured .NestedFieldNoCopy (localObject .Object , "metadata" )
39
43
if err != nil {
40
- return false , err
44
+ return false , "" , err
41
45
}
42
46
localObjMeta , ok := localObjMetaRaw .(map [string ]interface {})
43
47
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 )
45
49
}
46
50
if ! hasLocalObjMetaRaw && ! hasCacheObjMetaRaw {
47
- return false , nil // no-op
51
+ return false , "" , nil // no-op
48
52
}
49
53
if ! hasLocalObjMetaRaw {
50
54
unstructured .RemoveNestedField (cacheObject .Object , "metadata" )
51
- return true , nil
55
+ return true , "local .metadata disappeared" , nil
52
56
}
53
57
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
+ }
63
77
}
64
78
if cacheObjAnnotationsRaw , found := cacheObjMeta ["annotations" ]; found {
65
79
cacheObjAnnotations , ok := cacheObjAnnotationsRaw .(map [string ]interface {})
66
80
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 )
68
82
}
69
83
if shard , hasShard := cacheObjAnnotations [genericrequest .ShardAnnotationKey ]; hasShard {
70
- unstructured . RemoveNestedField (cacheObjAnnotations , genericrequest .ShardAnnotationKey )
84
+ delete (cacheObjAnnotations , genericrequest .ShardAnnotationKey )
71
85
defer func () {
72
86
if err == nil {
73
87
err = unstructured .SetNestedField (cacheObject .Object , shard , "metadata" , "annotations" , genericrequest .ShardAnnotationKey )
@@ -77,35 +91,29 @@ func ensureMeta(cacheObject *unstructured.Unstructured, localObject *unstructure
77
91
// TODO: in the future the original RV will be stored in an annotation
78
92
}
79
93
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
-
91
94
changed = ! reflect .DeepEqual (cacheObjMeta , localObjMeta )
92
95
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 )
94
102
}
95
103
96
104
newCacheObjMeta := map [string ]interface {}{}
97
105
for k , v := range localObjMeta {
98
106
newCacheObjMeta [k ] = v
99
107
}
100
- return true , unstructured .SetNestedMap (cacheObject .Object , newCacheObjMeta , "metadata" )
108
+ return true , reason , unstructured .SetNestedMap (cacheObject .Object , newCacheObjMeta , "metadata" )
101
109
}
102
110
103
111
// ensureRemaining changes unstructuredCacheObject to match unstructuredLocalObject except for the metadata field
104
112
// 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 ) {
106
114
cacheObjMeta , found , err := unstructured .NestedFieldNoCopy (cacheObject .Object , "metadata" )
107
115
if err != nil {
108
- return false , err
116
+ return false , "" , err
109
117
}
110
118
if found {
111
119
unstructured .RemoveNestedField (cacheObject .Object , "metadata" )
@@ -116,7 +124,7 @@ func ensureRemaining(cacheObject *unstructured.Unstructured, localObject *unstru
116
124
117
125
localObjMeta , found , err := unstructured .NestedFieldNoCopy (localObject .Object , "metadata" )
118
126
if err != nil {
119
- return false , err
127
+ return false , "" , err
120
128
}
121
129
if found {
122
130
unstructured .RemoveNestedField (localObject .Object , "metadata" )
@@ -127,15 +135,20 @@ func ensureRemaining(cacheObject *unstructured.Unstructured, localObject *unstru
127
135
128
136
changed := ! reflect .DeepEqual (cacheObject .Object , localObject .Object )
129
137
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 )
131
144
}
132
145
133
146
newCacheObj := map [string ]interface {}{}
134
147
for k , v := range localObject .Object {
135
148
newCacheObj [k ] = v
136
149
}
137
150
cacheObject .Object = newCacheObj
138
- return true , nil
151
+ return true , reason , nil
139
152
}
140
153
141
154
func toUnstructured (obj interface {}) (* unstructured.Unstructured , error ) {
0 commit comments