Initialize UID earlier in the Create process

Before:
  Create()
      BeginCreate()
      BeforeCreate()
          init UID <---------------------
          strategy code

After:
  Create()
      init UID <-------------------------
      BeginCreate()
      BeforeCreate()
          strategy code

This also wipes UID early (suggested by David) and asserts it is set in
BeforeCreate().

Kubernetes-commit: 5615de51f9e768dd01d7fe49a48e8db756bd8ac8
This commit is contained in:
Tim Hockin 2022-06-17 15:32:25 -07:00 committed by Kubernetes Publisher
parent 010d9e3ada
commit 970b3ee9bb
5 changed files with 52 additions and 7 deletions

View File

@ -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)

View File

@ -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 {

View File

@ -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()))

View File

@ -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

View File

@ -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")