diff --git a/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/equality.go b/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/equality.go index 366d88fcc50b..a1f27f1d106e 100644 --- a/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/equality.go +++ b/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/equality.go @@ -22,6 +22,7 @@ import ( "os" "reflect" "strconv" + "sync" "time" "k8s.io/apimachinery/pkg/api/equality" @@ -33,42 +34,41 @@ import ( "k8s.io/klog/v2" ) -func determineAvoidNoopTimestampUpdatesEnabled() bool { - if avoidNoopTimestampUpdatesString, exists := os.LookupEnv("KUBE_APISERVER_AVOID_NOOP_SSA_TIMESTAMP_UPDATES"); exists { - if ret, err := strconv.ParseBool(avoidNoopTimestampUpdatesString); err == nil { - return ret - } else { - klog.Errorf("failed to parse envar KUBE_APISERVER_AVOID_NOOP_SSA_TIMESTAMP_UPDATES: %v", err) - } - } - - // enabled by default - return true -} - var ( - avoidNoopTimestampUpdatesEnabled = determineAvoidNoopTimestampUpdatesEnabled() + avoidTimestampEqualities conversion.Equalities + initAvoidTimestampEqualities sync.Once ) -var avoidTimestampEqualities = func() conversion.Equalities { - var eqs = equality.Semantic.Copy() +func getAvoidTimestampEqualities() conversion.Equalities { + initAvoidTimestampEqualities.Do(func() { + if avoidNoopTimestampUpdatesString, exists := os.LookupEnv("KUBE_APISERVER_AVOID_NOOP_SSA_TIMESTAMP_UPDATES"); exists { + if ret, err := strconv.ParseBool(avoidNoopTimestampUpdatesString); err == nil && !ret { + // leave avoidTimestampEqualities empty. + return + } else { + klog.Errorf("failed to parse envar KUBE_APISERVER_AVOID_NOOP_SSA_TIMESTAMP_UPDATES: %v", err) + } + } - err := eqs.AddFunc( - func(a, b metav1.ManagedFieldsEntry) bool { - // Two objects' managed fields are equivalent if, ignoring timestamp, - // the objects are deeply equal. - a.Time = nil - b.Time = nil - return reflect.DeepEqual(a, b) - }, - ) + var eqs = equality.Semantic.Copy() + err := eqs.AddFunc( + func(a, b metav1.ManagedFieldsEntry) bool { + // Two objects' managed fields are equivalent if, ignoring timestamp, + // the objects are deeply equal. + a.Time = nil + b.Time = nil + return reflect.DeepEqual(a, b) + }, + ) - if err != nil { - panic(err) - } + if err != nil { + panic(fmt.Errorf("failed to instantiate semantic equalities: %w", err)) + } - return eqs -}() + avoidTimestampEqualities = eqs + }) + return avoidTimestampEqualities +} // IgnoreManagedFieldsTimestampsTransformer reverts timestamp updates // if the non-managed parts of the object are equivalent @@ -77,7 +77,8 @@ func IgnoreManagedFieldsTimestampsTransformer( newObj runtime.Object, oldObj runtime.Object, ) (res runtime.Object, err error) { - if !avoidNoopTimestampUpdatesEnabled { + equalities := getAvoidTimestampEqualities() + if len(equalities.Equalities) == 0 { return newObj, nil } @@ -154,11 +155,11 @@ func IgnoreManagedFieldsTimestampsTransformer( // This condition ensures the managed fields are always compared first. If // this check fails, the if statement will short circuit. If the check // succeeds the slow path is taken which compares entire objects. - if !avoidTimestampEqualities.DeepEqualWithNilDifferentFromEmpty(oldManagedFields, newManagedFields) { + if !equalities.DeepEqualWithNilDifferentFromEmpty(oldManagedFields, newManagedFields) { return newObj, nil } - if avoidTimestampEqualities.DeepEqualWithNilDifferentFromEmpty(newObj, oldObj) { + if equalities.DeepEqualWithNilDifferentFromEmpty(newObj, oldObj) { // Remove any changed timestamps, so that timestamp is not the only // change seen by etcd. //