@@ -17,57 +17,72 @@ 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
+ pth := pth
62
+ if v , found := cacheObjMeta [pth ]; found {
63
+ delete (cacheObjMeta , pth )
64
+ defer func () { //nolint:gocritic
65
+ if err == nil {
66
+ err = unstructured .SetNestedField (cacheObject .Object , v , "metadata" , pth )
67
+ }
68
+ }()
69
+ }
70
+ if v , found := localObjMeta [pth ]; found {
71
+ delete (localObjMeta , pth )
72
+ defer func () { //nolint:gocritic
73
+ if err == nil {
74
+ err = unstructured .SetNestedField (localObject .Object , v , "metadata" , pth )
75
+ }
76
+ }()
77
+ }
63
78
}
64
79
if cacheObjAnnotationsRaw , found := cacheObjMeta ["annotations" ]; found {
65
80
cacheObjAnnotations , ok := cacheObjAnnotationsRaw .(map [string ]interface {})
66
81
if ! ok {
67
- return false , fmt .Errorf ("metadata.annotations field of unstructuredCacheObject is of the type %T, expected map[string]interface{}" , cacheObjAnnotationsRaw )
82
+ return false , "" , fmt .Errorf ("metadata.annotations field of unstructuredCacheObject is of the type %T, expected map[string]interface{}" , cacheObjAnnotationsRaw )
68
83
}
69
84
if shard , hasShard := cacheObjAnnotations [genericrequest .ShardAnnotationKey ]; hasShard {
70
- unstructured . RemoveNestedField (cacheObjAnnotations , genericrequest .ShardAnnotationKey )
85
+ delete (cacheObjAnnotations , genericrequest .ShardAnnotationKey )
71
86
defer func () {
72
87
if err == nil {
73
88
err = unstructured .SetNestedField (cacheObject .Object , shard , "metadata" , "annotations" , genericrequest .ShardAnnotationKey )
@@ -77,35 +92,29 @@ func ensureMeta(cacheObject *unstructured.Unstructured, localObject *unstructure
77
92
// TODO: in the future the original RV will be stored in an annotation
78
93
}
79
94
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
95
changed = ! reflect .DeepEqual (cacheObjMeta , localObjMeta )
92
96
if ! changed {
93
- return false , nil
97
+ return false , "" , nil
98
+ }
99
+
100
+ reason = ".metadata changed"
101
+ if klog .FromContext (context .Background ()).V (5 ).Enabled () {
102
+ reason += " " + cmp .Diff (localObjMeta , cacheObjMeta )
94
103
}
95
104
96
105
newCacheObjMeta := map [string ]interface {}{}
97
106
for k , v := range localObjMeta {
98
107
newCacheObjMeta [k ] = v
99
108
}
100
- return true , unstructured .SetNestedMap (cacheObject .Object , newCacheObjMeta , "metadata" )
109
+ return true , reason , unstructured .SetNestedMap (cacheObject .Object , newCacheObjMeta , "metadata" )
101
110
}
102
111
103
112
// ensureRemaining changes unstructuredCacheObject to match unstructuredLocalObject except for the metadata field
104
113
// returns true when the unstructuredCacheObject was updated.
105
- func ensureRemaining (cacheObject * unstructured.Unstructured , localObject * unstructured.Unstructured ) (bool , error ) {
114
+ func ensureRemaining (cacheObject * unstructured.Unstructured , localObject * unstructured.Unstructured ) (bool , string , error ) {
106
115
cacheObjMeta , found , err := unstructured .NestedFieldNoCopy (cacheObject .Object , "metadata" )
107
116
if err != nil {
108
- return false , err
117
+ return false , "" , err
109
118
}
110
119
if found {
111
120
unstructured .RemoveNestedField (cacheObject .Object , "metadata" )
@@ -116,7 +125,7 @@ func ensureRemaining(cacheObject *unstructured.Unstructured, localObject *unstru
116
125
117
126
localObjMeta , found , err := unstructured .NestedFieldNoCopy (localObject .Object , "metadata" )
118
127
if err != nil {
119
- return false , err
128
+ return false , "" , err
120
129
}
121
130
if found {
122
131
unstructured .RemoveNestedField (localObject .Object , "metadata" )
@@ -127,15 +136,20 @@ func ensureRemaining(cacheObject *unstructured.Unstructured, localObject *unstru
127
136
128
137
changed := ! reflect .DeepEqual (cacheObject .Object , localObject .Object )
129
138
if ! changed {
130
- return false , nil
139
+ return false , "" , nil
140
+ }
141
+
142
+ reason := "object changed"
143
+ if klog .FromContext (context .Background ()).V (5 ).Enabled () {
144
+ reason += " " + cmp .Diff (localObject .Object , cacheObject .Object )
131
145
}
132
146
133
147
newCacheObj := map [string ]interface {}{}
134
148
for k , v := range localObject .Object {
135
149
newCacheObj [k ] = v
136
150
}
137
151
cacheObject .Object = newCacheObj
138
- return true , nil
152
+ return true , reason , nil
139
153
}
140
154
141
155
func toUnstructured (obj interface {}) (* unstructured.Unstructured , error ) {
0 commit comments