Prevent resource version to custom resource on no-op writes

For ObjectMeta pruning, we round trip through marshalling and
unmarshalling. If the ObjectMeta contained any strings with "" (or other
fields with empty values) _and_ the respective fields are omitempty,
those fields will be lost in the round trip process.

This makes ObjectMeta after the no-op write different from the one
before the write.

Resource version is incremented every time data is written to etcd.
Writes to etcd short-circuit if the bytes being written are identical
to the bytes already present. So this ends up incrementing the
resourceVersion even on no-op writes.

The zero values are set in BeforeCreate and BeforeUpdate. This commit
updates BeforeUpdate such that zero values are only set when the
object does not have a zero value for the respective field.

Kubernetes-commit: d691748aa64376e4b90ae2c01aca14d1be2b442c
This commit is contained in:
Nikhita Raghunath 2018-08-20 20:13:55 +05:30 committed by Kubernetes Publisher
parent 1fcab2fe23
commit 4c15aa6ed8
2 changed files with 10 additions and 4 deletions

View File

@ -81,7 +81,7 @@ func BeforeCreate(strategy RESTCreateStrategy, ctx context.Context, obj runtime.
if !ValidNamespace(ctx, objectMeta) { if !ValidNamespace(ctx, objectMeta) {
return errors.NewBadRequest("the namespace of the provided object does not match the namespace sent on the request") return errors.NewBadRequest("the namespace of the provided object does not match the namespace sent on the request")
} }
} else { } else if len(objectMeta.GetNamespace()) > 0 {
objectMeta.SetNamespace(metav1.NamespaceNone) objectMeta.SetNamespace(metav1.NamespaceNone)
} }
objectMeta.SetDeletionTimestamp(nil) objectMeta.SetDeletionTimestamp(nil)
@ -98,7 +98,9 @@ func BeforeCreate(strategy RESTCreateStrategy, ctx context.Context, obj runtime.
} }
// ClusterName is ignored and should not be saved // ClusterName is ignored and should not be saved
objectMeta.SetClusterName("") if len(objectMeta.GetClusterName()) > 0 {
objectMeta.SetClusterName("")
}
if errs := strategy.Validate(ctx, obj); len(errs) > 0 { if errs := strategy.Validate(ctx, obj); len(errs) > 0 {
return errors.NewInvalid(kind.GroupKind(), objectMeta.GetName(), errs) return errors.NewInvalid(kind.GroupKind(), objectMeta.GetName(), errs)

View File

@ -83,6 +83,7 @@ func validateCommonFields(obj, old runtime.Object, strategy RESTUpdateStrategy)
// BeforeUpdate ensures that common operations for all resources are performed on update. It only returns // BeforeUpdate ensures that common operations for all resources are performed on update. It only returns
// errors that can be converted to api.Status. It will invoke update validation with the provided existing // errors that can be converted to api.Status. It will invoke update validation with the provided existing
// and updated objects. // and updated objects.
// It sets zero values only if the object does not have a zero value for the respective field.
func BeforeUpdate(strategy RESTUpdateStrategy, ctx context.Context, obj, old runtime.Object) error { func BeforeUpdate(strategy RESTUpdateStrategy, ctx context.Context, obj, old runtime.Object) error {
objectMeta, kind, kerr := objectMetaAndKind(strategy, obj) objectMeta, kind, kerr := objectMetaAndKind(strategy, obj)
if kerr != nil { if kerr != nil {
@ -92,9 +93,10 @@ func BeforeUpdate(strategy RESTUpdateStrategy, ctx context.Context, obj, old run
if !ValidNamespace(ctx, objectMeta) { if !ValidNamespace(ctx, objectMeta) {
return errors.NewBadRequest("the namespace of the provided object does not match the namespace sent on the request") return errors.NewBadRequest("the namespace of the provided object does not match the namespace sent on the request")
} }
} else { } else if len(objectMeta.GetNamespace()) > 0 {
objectMeta.SetNamespace(metav1.NamespaceNone) objectMeta.SetNamespace(metav1.NamespaceNone)
} }
// Ensure requests cannot update generation // Ensure requests cannot update generation
oldMeta, err := meta.Accessor(old) oldMeta, err := meta.Accessor(old)
if err != nil { if err != nil {
@ -111,7 +113,9 @@ func BeforeUpdate(strategy RESTUpdateStrategy, ctx context.Context, obj, old run
strategy.PrepareForUpdate(ctx, obj, old) strategy.PrepareForUpdate(ctx, obj, old)
// ClusterName is ignored and should not be saved // ClusterName is ignored and should not be saved
objectMeta.SetClusterName("") if len(objectMeta.GetClusterName()) > 0 {
objectMeta.SetClusterName("")
}
// Use the existing UID if none is provided // Use the existing UID if none is provided
if len(objectMeta.GetUID()) == 0 { if len(objectMeta.GetUID()) == 0 {
objectMeta.SetUID(oldMeta.GetUID()) objectMeta.SetUID(oldMeta.GetUID())