Skip to content

Commit e9f1f03

Browse files
author
Moritz Clasmeier
committed
Improve metadata comparison: We should not only filter out the resourceVersion, but also a few other fields managed by the cluster
1 parent a5ae759 commit e9f1f03

1 file changed

Lines changed: 30 additions & 9 deletions

File tree

pkg/reconciler/internal/updater/updater.go

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -197,8 +197,8 @@ func (u *Updater) Apply(ctx context.Context, baseObj *unstructured.Unstructured)
197197

198198
func isSafeForUpdate(logger logr.Logger, inMemory *unstructured.Unstructured, onCluster *unstructured.Unstructured) bool {
199199
// Compare metadata (excluding resourceVersion).
200-
inMemoryMetadata := metadataWithoutResourceVersion(inMemory)
201-
onClusterMetadata := metadataWithoutResourceVersion(onCluster)
200+
inMemoryMetadata := reducedMetadata(inMemory)
201+
onClusterMetadata := reducedMetadata(onCluster)
202202
if !reflect.DeepEqual(inMemoryMetadata, onClusterMetadata) {
203203
// Diff in metadata. Nothing we can do about it -> Fail.
204204
logger.V(1).Info("Not refreshing object due to metadata mismatch",
@@ -221,21 +221,42 @@ func isSafeForUpdate(logger logr.Logger, inMemory *unstructured.Unstructured, on
221221
return true
222222
}
223223

224-
func metadataWithoutResourceVersion(u *unstructured.Unstructured) map[string]interface{} {
224+
// recudedMetadata returns the metadata of the given unstructured object,
225+
// excluding a couple of fields which should not be taken into account
226+
// for equality checks. The excluded fields are:
227+
//
228+
// - metadata.resourceVersion
229+
// - metadata.managedFields
230+
// - metadata.annotation."kubectl.kubernetes.io/last-applied-configuration"
231+
func reducedMetadata(u *unstructured.Unstructured) map[string]interface{} {
225232
metadata, ok := u.Object["metadata"].(map[string]interface{})
226233
if !ok {
227234
return nil
228235
}
229-
modifiedMetadata := make(map[string]interface{}, len(metadata))
230-
for k, v := range metadata {
231-
if k == "resourceVersion" {
232-
continue
233-
}
234-
modifiedMetadata[k] = v
236+
modifiedMetadata := excludeFromMap(metadata, "resourceVersion", "managedFields")
237+
if annotations, found := modifiedMetadata["annotations"].(map[string]interface{}); found {
238+
modifiedMetadata["annotations"] = excludeFromMap(annotations, "kubectl.kubernetes.io/last-applied-configuration")
235239
}
240+
236241
return modifiedMetadata
237242
}
238243

244+
// excludeFromMap returns a new map that contains all key-value pairs from inputMap
245+
// except those whose keys are in the keys slice.
246+
func excludeFromMap(inputMap map[string]interface{}, keys ...string) map[string]interface{} {
247+
resultMap := make(map[string]interface{}, len(inputMap))
248+
excludeKeys := make(map[string]struct{})
249+
for _, k := range keys {
250+
excludeKeys[k] = struct{}{}
251+
}
252+
for k, v := range inputMap {
253+
if _, found := excludeKeys[k]; !found {
254+
resultMap[k] = v
255+
}
256+
}
257+
return resultMap
258+
}
259+
239260
func (u *Updater) tryRefresh(ctx context.Context, obj *unstructured.Unstructured) (bool, error) {
240261
// Re-fetch object with client.
241262
current := &unstructured.Unstructured{}

0 commit comments

Comments
 (0)