Change:
if len(name) == 0 { _, name, _ = scope.Namer.ObjectName(obj) } <del> admissionAttributes := admission.NewAttributesRecord(obj, nil, scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Create, options, dryrun.IsDryRun(options.DryRun), userInfo) if mutatingAdmission, ok := admit.(admission.MutationInterface); ok && mutatingAdmission.Handles(admission.Create) { err = mutatingAdmission.Admit(ctx, admissionAttributes, scope) if err != nil { scope.err(err, w, req) return } } if scope.FieldManager != nil { liveObj, err := scope.Creater.New(scope.Kind) if err != nil { scope.err(fmt.Errorf("failed to create new object (Create for %v): %v", scope.Kind, err), w, req) return </del> <ins> trace.Step("About to store object in database") result, err := finishRequest(timeout, func() (runtime.Object, error) { if scope.FieldManager != nil { liveObj, err := scope.Creater.New(scope.Kind) if err != nil { return nil, fmt.Errorf("failed to create new object (Create for %v): %v", scope.Kind, err) } obj, err = scope.FieldManager.Update(liveObj, obj, managerOrUserAgent(options.FieldManager, req.UserAgent())) if err != nil { return nil, fmt.Errorf("failed to update object (Create for %v) managed fields: %v", scope.Kind, err) } </ins> } <del> obj, err = scope.FieldManager.Update(liveObj, obj, managerOrUserAgent(options.FieldManager, req.UserAgent())) if err != nil { scope.err(fmt.Errorf("failed to update object (Create for %v) managed fields: %v", scope.Kind, err), w, req) return </del> <ins> admissionAttributes := admission.NewAttributesRecord(obj, nil, scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Create, options, dryrun.IsDryRun(options.DryRun), userInfo) if mutatingAdmission, ok := admit.(admission.MutationInterface); ok && mutatingAdmission.Handles(admission.Create) { if err := mutatingAdmission.Admit(ctx, admissionAttributes, scope); err != nil { return nil, err } </ins> } <del> } trace.Step("About to store object in database") result, err := finishRequest(timeout, func() (runtime.Object, error) { </del> return r.Create( ctx, name,

Discussion:
What happened: Currently, the mutating admission happens before updating managedFields in create requests, but in the update and patch handlers, mutating admission happens after updating managedFields. This causes the ownership of fields modified by mutating webhooks to be inconsistent based on the request verb. /sig api-machinery /cc /priority backlog
/wg apply
/assign

What happened: I noticed that in both the update and patch handler, the apiserver calls admission webhooks from within the asynchronous function, but in the create handler, we only call validating webhooks this way, and call the mutating admission chain from outside this asynchronous/timeout function () I think this could create inconsistent behavior in the case of a create request with a long running mutating admission step, since the timeout in finishRequest wouldn't include this part of the request /sig api-machinery /cc /priority backlog
/cc
I think the thinking is that Create doesn't need retries due to conflicts, so these hooks wouldn't need to be called multiple times. (but then why would the validating ones?) Honestly there is no good reason to have a finishRequest at all; it's a holdover from a very early long running operation concept that was removed.
until every single call in our request handling stack honors context deadline, I think we need it. we're getting closer, but I'm not positive we are quite there. at first glance, for this particular case, moving inside the function passed to finishRequest might be all that is needed