diff --git a/staging/src/k8s.io/apiextensions-apiserver/pkg/registry/customresource/strategy.go b/staging/src/k8s.io/apiextensions-apiserver/pkg/registry/customresource/strategy.go index 3f298d5c3a4f4..851b9d6ca98c1 100644 --- a/staging/src/k8s.io/apiextensions-apiserver/pkg/registry/customresource/strategy.go +++ b/staging/src/k8s.io/apiextensions-apiserver/pkg/registry/customresource/strategy.go @@ -33,6 +33,7 @@ import ( "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/util/validation/field" + genericapirequest "k8s.io/apiserver/pkg/endpoints/request" "k8s.io/apiserver/pkg/features" apiserverstorage "k8s.io/apiserver/pkg/storage" "k8s.io/apiserver/pkg/storage/names" @@ -114,6 +115,15 @@ func (a customResourceStrategy) PrepareForCreate(ctx context.Context, obj runtim } accessor, _ := meta.Accessor(obj) + if _, found := accessor.GetAnnotations()[genericapirequest.AnnotationKey]; found { + // in general the shard annotation is not attached to objects, instead, it is assigned by the storage layer on the fly + // to avoid an additional UPDATE request (mismatch on the generation field) replicated objects have the shard annotation set + // thus we need to remove the shard annotation and simply return early so that the generation is not reset to 1 + annotations := accessor.GetAnnotations() + delete(annotations, genericapirequest.AnnotationKey) + accessor.SetAnnotations(annotations) + return + } accessor.SetGeneration(1) } @@ -144,6 +154,11 @@ func (a customResourceStrategy) PrepareForUpdate(ctx context.Context, obj, old r if !apiequality.Semantic.DeepEqual(newCopyContent, oldCopyContent) { oldAccessor, _ := meta.Accessor(oldCustomResourceObject) newAccessor, _ := meta.Accessor(newCustomResourceObject) + if _, found := oldAccessor.GetAnnotations()[genericapirequest.AnnotationKey]; found { + // the presence of the annotation indicates the object is from the cache server. + // since the objects from the cache should not be modified in any way, just return early. + return + } newAccessor.SetGeneration(oldAccessor.GetGeneration() + 1) } } diff --git a/staging/src/k8s.io/apiserver/pkg/registry/rest/update.go b/staging/src/k8s.io/apiserver/pkg/registry/rest/update.go index e5ae7af0ebba6..de34ef8d69b64 100644 --- a/staging/src/k8s.io/apiserver/pkg/registry/rest/update.go +++ b/staging/src/k8s.io/apiserver/pkg/registry/rest/update.go @@ -126,7 +126,12 @@ func BeforeUpdate(strategy RESTUpdateStrategy, ctx context.Context, obj, old run if err != nil { return err } - objectMeta.SetGeneration(oldMeta.GetGeneration()) + if len(oldMeta.GetAnnotations()[genericapirequest.AnnotationKey]) == 0 || objectMeta.GetGeneration() == 0 { + // the absence of the annotation indicates the object is NOT from the cache server, + // if the new object doesn't have its generation set, just rewrite it from the old object + // otherwise we are dealing with an object from the cache server that wants its generation to be updated + objectMeta.SetGeneration(oldMeta.GetGeneration()) + } // Ensure managedFields state is removed unless ServerSideApply is enabled if !utilfeature.DefaultFeatureGate.Enabled(features.ServerSideApply) {