diff --git a/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/create.go b/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/create.go index 73e1fb99dd7..1f3ce809448 100644 --- a/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/create.go +++ b/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/create.go @@ -163,8 +163,10 @@ func createHandler(r rest.NamedCreater, scope *RequestScope, admit admission.Int userInfo, _ := request.UserFrom(ctx) - // if this object supports namespace info if objectMeta, err := meta.Accessor(obj); err == nil { + // Wipe fields which cannot take user-provided values + rest.WipeObjectMetaSystemFields(objectMeta) + // ensure namespace on the object is correct, or error if a conflicting namespace was set in the object if err := rest.EnsureObjectNamespaceMatchesRequestNamespace(rest.ExpectedNamespaceForResource(namespace, scope.Resource), objectMeta); err != nil { scope.err(err, w, req) diff --git a/staging/src/k8s.io/apiserver/pkg/registry/generic/registry/store.go b/staging/src/k8s.io/apiserver/pkg/registry/generic/registry/store.go index cf3863408ad..67015bbe35c 100644 --- a/staging/src/k8s.io/apiserver/pkg/registry/generic/registry/store.go +++ b/staging/src/k8s.io/apiserver/pkg/registry/generic/registry/store.go @@ -382,6 +382,13 @@ func finishNothing(context.Context, bool) {} func (e *Store) Create(ctx context.Context, obj runtime.Object, createValidation rest.ValidateObjectFunc, options *metav1.CreateOptions) (runtime.Object, error) { var finishCreate FinishFunc = finishNothing + // Init metadata as early as possible. + if objectMeta, err := meta.Accessor(obj); err != nil { + return nil, err + } else { + rest.FillObjectMetaSystemFields(objectMeta) + } + if e.BeginCreate != nil { fn, err := e.BeginCreate(ctx, obj, options) if err != nil { @@ -558,6 +565,13 @@ func (e *Store) Update(ctx context.Context, name string, objInfo rest.UpdatedObj doUnconditionalUpdate := newResourceVersion == 0 && e.UpdateStrategy.AllowUnconditionalUpdate() if existingResourceVersion == 0 { + // Init metadata as early as possible. + if objectMeta, err := meta.Accessor(obj); err != nil { + return nil, nil, err + } else { + rest.FillObjectMetaSystemFields(objectMeta) + } + var finishCreate FinishFunc = finishNothing if e.BeginCreate != nil { diff --git a/staging/src/k8s.io/apiserver/pkg/registry/rest/create.go b/staging/src/k8s.io/apiserver/pkg/registry/rest/create.go index 23cff3a1469..40bf8e20e96 100644 --- a/staging/src/k8s.io/apiserver/pkg/registry/rest/create.go +++ b/staging/src/k8s.io/apiserver/pkg/registry/rest/create.go @@ -100,6 +100,11 @@ func BeforeCreate(strategy RESTCreateStrategy, ctx context.Context, obj runtime. return kerr } + // ensure that system-critical metadata has been populated + if !metav1.HasObjectMetaSystemFieldValues(objectMeta) { + return errors.NewInternalError(fmt.Errorf("system metadata was not initialized")) + } + // ensure namespace on the object is correct, or error if a conflicting namespace was set in the object requestNamespace, ok := genericapirequest.NamespaceFrom(ctx) if !ok { @@ -109,10 +114,7 @@ func BeforeCreate(strategy RESTCreateStrategy, ctx context.Context, obj runtime. return err } - objectMeta.SetDeletionTimestamp(nil) - objectMeta.SetDeletionGracePeriodSeconds(nil) strategy.PrepareForCreate(ctx, obj) - FillObjectMetaSystemFields(objectMeta) if len(objectMeta.GetGenerateName()) > 0 && len(objectMeta.GetName()) == 0 { objectMeta.SetName(strategy.GenerateName(objectMeta.GetGenerateName())) diff --git a/staging/src/k8s.io/apiserver/pkg/registry/rest/meta.go b/staging/src/k8s.io/apiserver/pkg/registry/rest/meta.go index 6741b3da342..12c1e5f98c1 100644 --- a/staging/src/k8s.io/apiserver/pkg/registry/rest/meta.go +++ b/staging/src/k8s.io/apiserver/pkg/registry/rest/meta.go @@ -23,11 +23,19 @@ import ( "k8s.io/apimachinery/pkg/util/uuid" ) +// WipeObjectMetaSystemFields erases fields that are managed by the system on ObjectMeta. +func WipeObjectMetaSystemFields(meta metav1.Object) { + meta.SetCreationTimestamp(metav1.Time{}) + meta.SetUID("") + meta.SetDeletionTimestamp(nil) + meta.SetDeletionGracePeriodSeconds(nil) + meta.SetSelfLink("") +} + // FillObjectMetaSystemFields populates fields that are managed by the system on ObjectMeta. func FillObjectMetaSystemFields(meta metav1.Object) { meta.SetCreationTimestamp(metav1.Now()) meta.SetUID(uuid.NewUUID()) - meta.SetSelfLink("") } // EnsureObjectNamespaceMatchesRequestNamespace returns an error if obj.Namespace and requestNamespace diff --git a/staging/src/k8s.io/apiserver/pkg/registry/rest/meta_test.go b/staging/src/k8s.io/apiserver/pkg/registry/rest/meta_test.go index feafa56495e..ad1e43fde42 100644 --- a/staging/src/k8s.io/apiserver/pkg/registry/rest/meta_test.go +++ b/staging/src/k8s.io/apiserver/pkg/registry/rest/meta_test.go @@ -23,14 +23,33 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) +// TestWipeObjectMetaSystemFields validates that system populated fields are set on an object +func TestWipeObjectMetaSystemFields(t *testing.T) { + resource := metav1.ObjectMeta{} + WipeObjectMetaSystemFields(&resource) + if !resource.CreationTimestamp.Time.IsZero() { + t.Errorf("resource.CreationTimestamp is set") + } + if len(resource.UID) != 0 { + t.Errorf("resource.UID is set") + } + if resource.DeletionTimestamp != nil { + t.Errorf("resource.DeletionTimestamp is set") + } + if resource.DeletionGracePeriodSeconds != nil { + t.Errorf("resource.DeletionGracePeriodSeconds is set") + } + if len(resource.SelfLink) != 0 { + t.Errorf("resource.SelfLink is set") + } +} + // TestFillObjectMetaSystemFields validates that system populated fields are set on an object func TestFillObjectMetaSystemFields(t *testing.T) { resource := metav1.ObjectMeta{} FillObjectMetaSystemFields(&resource) if resource.CreationTimestamp.Time.IsZero() { t.Errorf("resource.CreationTimestamp is zero") - } else if len(resource.UID) == 0 { - t.Errorf("resource.UID missing") } if len(resource.UID) == 0 { t.Errorf("resource.UID missing")