-
Notifications
You must be signed in to change notification settings - Fork 38.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fixes operation for "create on update" #65572
fixes operation for "create on update" #65572
Conversation
/kind bug |
/cc @jennybuckley |
3c40cf1
to
043aec7
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for fixing this!
Just a couple comments but otherwise it looks good to me. Also I think it should have a release note since it will fix (change) the behavior of update.
var transformers []rest.TransformFunc | ||
if mutatingAdmission, ok := admit.(admission.MutationInterface); ok && mutatingAdmission.Handles(admission.Update) { | ||
transformers = append(transformers, func(ctx context.Context, newObj, oldObj runtime.Object) (runtime.Object, error) { | ||
if accessor, err := meta.Accessor(oldObj); err == nil && accessor.GetResourceVersion() == "" { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we also need to take the && mutatingAdmission.Handles(admission.Update)
part out of line 101 and move it after this check. because the operation is currently hard coded as admission.Update there, and It should also be dependent on whether the object exists.
Also, have you looked into how the patcher checks if it is looking at a persisted object? It uses UID since that is guaranteed not to be specified until the object exists. Resource Version might work the same way, but maybe for consistency we should do it the same as in the patcher. https://github.com/kubernetes/kubernetes/blob/master/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/patch.go#L319-L323
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jennybuckley Nice catch. Just sync the change to PATCH. PTAL again. Thx :)
043aec7
to
83dcd7d
Compare
83dcd7d
to
4de1837
Compare
if mutatingAdmission.Handles(admission.Update) { | ||
return newObj, mutatingAdmission.Admit(admission.NewAttributesRecord(newObj, oldObj, scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Update, userInfo)) | ||
} | ||
return newObj, nil |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For reviewers, this line makes the transformer function dummy.
/test pull-kubernetes-e2e-gce |
/cc @adohe |
Overall LGTM. I think @lavalamp could help approve this. |
/assign @lavalamp |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for fixing this!
Just a couple comments but otherwise it looks good to me. Also I think it should have a release note since it will fix (change) the behavior of update.
admissionCheck := func(updatedObject runtime.Object, currentObject runtime.Object) error { | ||
if mutatingAdmission, ok := admit.(admission.MutationInterface); ok && admit.Handles(admission.Update) { | ||
return mutatingAdmission.Admit(admission.NewAttributesRecord(updatedObject, currentObject, scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Update, userInfo)) | ||
if mutatingAdmission, ok := admit.(admission.MutationInterface); ok { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We shouldn't change the patcher, because it doesn't currently allow create on update anyway.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jennybuckley Thx for reviewing. I thought that patcher would finally call store.Update
here. So the object would also be created when its AllowCreateOnUpdate
is true. Am I missing sth?😃
updateObject, _, updateErr := p.restPatcher.Update(ctx, p.name, p.updatedObjectInfo, p.createValidation, p.updateValidation) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thats true, but that the patcher also checks if the object already exists in it's applyPatch function and returns an error if the object doesn't exist yet.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jennybuckley Make sense 👍
transformers = append(transformers, func(ctx context.Context, newObj, oldObj runtime.Object) (runtime.Object, error) { | ||
return newObj, mutatingAdmission.Admit(admission.NewAttributesRecord(newObj, oldObj, scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Update, userInfo)) | ||
if mutatingAdmission.Handles(admission.Create) { | ||
if accessor, err := meta.Accessor(oldObj); err == nil && accessor.GetResourceVersion() == "" { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this check should be the outer one, and only one of the checks mutatingAdmission.Handles(admission.Create)
/mutatingAdmission.Handles(admission.Update)
should be done, depending on the result of this accessor.GetResourceVersion() == ""
check.
The way you have it now doesn't work in the case where mutatingAdmission.Handles(admission.Create)
is false, accessor.GetResourceVersion() == ""
is true, and mutatingAdmission.Handles(admission.Update)
is true
In that case it would call Update admission, but it should be calling nothing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice catch :) Thx
4de1837
to
7bccd3f
Compare
@@ -122,8 +129,8 @@ func PatchResource(r rest.Patcher, scope RequestScope, admit admission.Interface | |||
kind: scope.Kind, | |||
resource: scope.Resource, | |||
|
|||
createValidation: rest.AdmissionToValidateObjectFunc(admit, staticAdmissionAttributes), | |||
updateValidation: rest.AdmissionToValidateObjectUpdateFunc(admit, staticAdmissionAttributes), | |||
createValidation: rest.AdmissionToValidateObjectFunc(admit, admission.NewAttributesRecord(nil, nil, scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Create, userInfo)), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I try to create a pod on update, which is not allowed. Will this still have effect? For example, will this increase quota usage?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@CaoShuFeng Thanks for reviewing. I do find some codes need to be trimed in resourcequota controller as u can see in my following commit. And AFAICT resourcequotas would still be fine after these changes WDYT?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry that I have no suggestions about how to fix this issue. But I'd be happy to point it out when I see something doesn't work as expected.
Maybe we can ask for good suggestions from more experienced developers. :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@CaoShuFeng Thx all the same. I'm all ears😃
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, I don't see any invocation of NewObjectCountEvaluator
with allowCreateOnUpdate=true
, not in kube and not in OpenShift. Maybe @derekwaynecarr can shed some light on that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In other words: did we miss creations on update in the quota mechanism before this PR?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@sttts I don't see either even search through the whole project. It should have been pruned before 😅
It looks pretty good. As a last thing I would like to see a "predetermined breaking point" in the patch handler, i.e.:
|
e1aa7aa
to
1b10960
Compare
@@ -108,6 +108,14 @@ func PatchResource(r rest.Patcher, scope RequestScope, admit admission.Interface | |||
userInfo, _ := request.UserFrom(ctx) | |||
staticAdmissionAttributes := admission.NewAttributesRecord(nil, nil, scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Update, userInfo) | |||
admissionCheck := func(updatedObject runtime.Object, currentObject runtime.Object) error { | |||
isNotZeroObject, err := hasUID(currentObject) | |||
if err != nil { | |||
return fmt.Errorf("unexpected error when extracting UID from oldObj: %s", err.Error()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we usually do %v
and err
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if isNotZeroObject, err := hasUID(currentObject); err != nil { ... } else if !isNotZeroObject { ... }
@@ -108,6 +108,14 @@ func PatchResource(r rest.Patcher, scope RequestScope, admit admission.Interface | |||
userInfo, _ := request.UserFrom(ctx) | |||
staticAdmissionAttributes := admission.NewAttributesRecord(nil, nil, scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Update, userInfo) | |||
admissionCheck := func(updatedObject runtime.Object, currentObject runtime.Object) error { | |||
isNotZeroObject, err := hasUID(currentObject) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
isZeroObject := !hasUID(...)
makes the logic simpler
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@sttts I'm afraid we cannot do this because unary operator !
can't be applied to a function returning 2 values.🧐
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
add another helper. Or even simpler: isPreExistingObject instead of isNotZeroObject. Just to get rid of the double negation.
1b10960
to
7c8a9e8
Compare
@sttts https://github.com/kubernetes/kubernetes/pull/64892/files#diff-15bdfc59ec78ff09b509d4a347560a1bR361 to the serverside apply feature branch which adds that functionality, also makes sure that the admission controllers are called correctly. By trying to change it here too we aren't actually changing any behavior, it would just make it harder to merge later. |
} else if !isNotZeroObject { | ||
// if we allow this some day, we have this TODO: call the mutating admission chain with the CREATE verb instead of UPDATE | ||
panic("a PATCH cannot create objects") | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
all this is kind of ugly, agreeing with @jennybuckley. Let's condense the change for the patch here into the simple TODO, no panic. Lgtm then.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@sttts Done
7c8a9e8
to
4dc5807
Compare
isNotZeroObject, err := hasUID(currentObject) | ||
if err != nil { | ||
return fmt.Errorf("unexpected error when extracting UID from oldObj: %v", err.Error()) | ||
} else if !isNotZeroObject { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
misunderstanding I think. Even remove the upper logic. Just a comment: // if we allow create-on-patch, TODO: ...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@sttts Oops.. Indeed
4dc5807
to
c6c66ea
Compare
@@ -108,6 +108,7 @@ func PatchResource(r rest.Patcher, scope RequestScope, admit admission.Interface | |||
userInfo, _ := request.UserFrom(ctx) | |||
staticAdmissionAttributes := admission.NewAttributesRecord(nil, nil, scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Update, userInfo) | |||
admissionCheck := func(updatedObject runtime.Object, currentObject runtime.Object) error { | |||
// if we allow this some day, we have this TODO: call the mutating admission chain with the CREATE verb instead of UPDATE |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what is "this"? 🙂 needs some context, like "if we allow create-on-patch, we have this TODO: ..."
remove create-on-update logic for quota controller review: add more error check remove unused args revert changes in patch.go use hasUID to judge if it's a create-on-update
c6c66ea
to
ccb1ec7
Compare
/test pull-kubernetes-e2e-kops-aws |
@sttts PTAL |
lgtm from my side. /assign @deads2k for final review and approval. |
It all looks good to me too, but I'll hold off on giving lgtm since @deads2k is planning to review this. |
transformers = append(transformers, func(ctx context.Context, newObj, oldObj runtime.Object) (runtime.Object, error) { | ||
return newObj, mutatingAdmission.Admit(admission.NewAttributesRecord(newObj, oldObj, scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Update, userInfo)) | ||
isNotZeroObject, err := hasUID(oldObj) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The old admission code (at least for quota) used to compare against nil. Am I to understand the old code was wrong?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The old admission code (at least for quota) used to compare against nil. Am I to understand the old code was wrong?
Ah, it probably was for create on update, but not for create. That kind of makes sense. I'm glad this is getting fixed.
/lgtm |
/test all Tests are more than 96 hours old. Re-running tests. |
[APPROVALNOTIFIER] This PR is APPROVED This pull-request has been approved by: deads2k, yue9944882 The full list of commands accepted by this bot can be found here. The pull request process is described here
Needs approval from an approver in each of these files:
Approvers can indicate their approval by writing |
/test all [submit-queue is verifying that this PR is safe to merge] |
Automatic merge from submit-queue. If you want to cherry-pick this change to another branch, please follow the instructions here. |
What this PR does / why we need it:
Set operation to
admission.Create
for create-on-update requests.Which issue(s) this PR fixes (optional, in
fixes #<issue number>(, fixes #<issue_number>, ...)
format, will close the issue(s) when PR gets merged):Fixes #65553
Special notes for your reviewer:
Release note: