-
Notifications
You must be signed in to change notification settings - Fork 40k
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
add PATCH verb to apiserver #4706
Conversation
7483ba1
to
ed95937
Compare
Assigning to @bgrant0607, feel free to rebalance. Maybe @smarterclayton is also interested. |
ctx := ctxFn(req) | ||
ctx = api.WithNamespace(ctx, namespace) | ||
|
||
original, err := r.Get(ctx, name) |
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.
This results in two successive reads that are not controlled by the resource version, which I think is wrong. The client has to be able to specify which version to check against. So there's at least a check missing here for the input version against the get version.
I'm mentally debating whether passing a structured type down into Update would be more appropriate (delta, with attached api version). Even a generic patch resource being passed down would work. I mildly dislike reading here then reading again on update - I'd prefer to do it exactly once, in the storage. That's something we could fix later, but it still seems messy.
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.
Makes sense. We could validate that the client passes a resource version in their patch. The intent was to add PATCH without needing to add an extra Patch method to the registry's RESTs. What's the reason that the registry does a get from etcd after 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.
----- Original Message -----
+func PatchResource(r RESTPatcher, ctxFn ContextFunc, namer ScopeNamer,
codec runtime.Codec, resource string, admit admission.Interface)
restful.RouteFunction {
- return func(req *restful.Request, res *restful.Response) {
w := res.ResponseWriter
document, move timeout out of this function and declare it in// TODO: we either want to remove timeout or document it (if we
api_installer)timeout := parseTimeout(req.Request.URL.Query().Get("timeout"))
namespace, name, err := namer.Name(req)
if err != nil {
notFound(w, req.Request)
return
}
ctx := ctxFn(req)
ctx = api.WithNamespace(ctx, namespace)
original, err := r.Get(ctx, name)
Makes sense. We could validate that the client passes a resource version in
their patch. The intent was to add PATCH without needing to add an extra
Patch method to the registry's RESTs. What's the reason that the registry
does a get from etcd after update?
Get before update - it's so that we know that what is stored so we can do a CAS. In the pod changes I made the REST logic happen between the initial get and the CAS, so that you had the existing state to compare to. There's a problem there which is merging - when we merge what the server has with the client's input, we have to make the decision at that time about what to preserve (that's why pod/status was going to exist, so it could have a different decision). Right now we throw away the status section and combine it with the spec (so that users who naively GET -> PUT overwrite don't overwrite status). If we didn't do that, all clients would have to do merges correctly (which they aren't) and some users should be prevented from changing status like pod.status.host, which they aren't today. Admission control probably would be better to run given both the old and the new value as well, and being able to calculate exact deltas for authorization is a nice to have.
Let's slate a time to sit down and go through all the edge cases here - I like the approach, it just highlights how we're not doing everything right for Update today.
Reply to this email directly or view it on GitHub:
https://github.com/GoogleCloudPlatform/kubernetes/pull/4706/files#r25143674
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.
Agree with @smarterclayton. There's probably some refactoring of Update in which we pass in a function that takes the old object with status removed etc and merges the new object, such that both PATCH and PUT could produce such a function.
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.
Yeah - same thought here. We needed that on client update as well (the way merge is handled is ugly right now).
On Feb 23, 2015, at 4:30 PM, Daniel Smith notifications@github.com wrote:
In pkg/apiserver/resthandler.go:
+func PatchResource(r RESTPatcher, ctxFn ContextFunc, namer ScopeNamer, codec runtime.Codec, resource string, admit admission.Interface) restful.RouteFunction {
- return func(req *restful.Request, res *restful.Response) {
w := res.ResponseWriter
// TODO: we either want to remove timeout or document it (if we document, move timeout out of this function and declare it in api_installer)
timeout := parseTimeout(req.Request.URL.Query().Get("timeout"))
namespace, name, err := namer.Name(req)
if err != nil {
notFound(w, req.Request)
return
}
ctx := ctxFn(req)
ctx = api.WithNamespace(ctx, namespace)
Agree with @smarterclayton. There's probably some refactoring of Update in which we pass in a function that takes the old object with status removed etc and merges the new object, such that both PATCH and PUT could produce such a function.original, err := r.Get(ctx, name)
—
Reply to this email directly or view it on GitHub.
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.
@lavalamp The only issue with that is only a few Update implementations do reads before update. RESTs that use AtomicUpdate do read and most that use SetObj do not read. It might be nice to standardize on an Update mechanism that could skip the initial read when unnecessary.
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.
My next set of changes to update will make implementing patch under AtomicUpdate feasible. For now, that doesn't need to block this change. We should leave a todo here to that effect.
On Mar 1, 2015, at 2:08 PM, Mike Danese notifications@github.com wrote:
In pkg/apiserver/resthandler.go:
+func PatchResource(r RESTPatcher, ctxFn ContextFunc, namer ScopeNamer, codec runtime.Codec, resource string, admit admission.Interface) restful.RouteFunction {
- return func(req *restful.Request, res *restful.Response) {
w := res.ResponseWriter
// TODO: we either want to remove timeout or document it (if we document, move timeout out of this function and declare it in api_installer)
timeout := parseTimeout(req.Request.URL.Query().Get("timeout"))
namespace, name, err := namer.Name(req)
if err != nil {
notFound(w, req.Request)
return
}
ctx := ctxFn(req)
ctx = api.WithNamespace(ctx, namespace)
@lavalamp The only issue with that is only a few Update implementations do reads before update. RESTs that use AtomicUpdate do read and most that use SetObj do not read. It might be nice to standardize on an Update mechanism that could skip the initial read when unnecessary.original, err := r.Get(ctx, name)
—
Reply to this email directly or view it on GitHub.
I'd like to sketch out how patch fits in with the other changes going on in update (ie whether we should reorganize update so we can avoid double reads) and how we distinguish intent on PATCH to allow status. |
We can hold this pending more discussion on the use case / api of PATCH. |
@ghodss and @nikhiljindal may also be interested. |
} | ||
|
||
// PATCH requires same permission as UPDATE | ||
err = admit.Admit(admission.NewAttributesRecord(obj, namespace, resource, "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.
Might as well check this before doing the read/merge?
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.
Done.
Work on status shouldn't hold up patch, IMO. I'll add some thoughts about status to #2726. |
What's the rush to get patch in? Agree it's nice, but I'd put the priority on getting our existing verbs working before we add new ones.
|
No huge rush, but configuration (reconcile) will depend heavily on patch, and I see it as a separate issue from status. The refactoring you are doing, which admittedly is partly in support of status, is a bigger reason to go slowly with this. |
I should have a prelim status pull up tomorrow - we can circle the wagons on what we want to do with update, then if necessary we can do the simple patch now and implement the "correct" patch. I want to close on finalizers and acl so we know whether we need to implement per property policy rules in our engine, with that we probably have the last of the near term update reqts closed (except splitting into two objects which belongs below the storage interface anyway).
|
9be6299
to
4ce03bc
Compare
54ada3f
to
ae2017c
Compare
One more question. Is the "patch body contains a resourceVersion" condition something we want to validate on input? If there is no resourceVersion should we just update against the current resource? |
I'm uncomfortable encouraging clients to not patch with resource version. However, arbitrary patch isn't unreasonable to support. I would not do anything specific around resource version right now, and we can add it in later.
|
Current iteration will honor resource version when it is passed in the patch body, otherwise it will use the resourceVersion returned in the first Get so the get/patch/write cycle will be controlled by resourceVersion as well. |
330cbcf
to
329c132
Compare
LGTM, waiting for rerun of Travis, sorry for the long delay. |
4b861f8
to
587bfc4
Compare
@ghodss How would this PR need to change in order to support custom list merging? |
I'm going to merge this as-is (Shippable passed, Travis is backed up), but we've decided to change merge semantics: #4889. I'm counting on lack of documentation and kubectl integration to prevent rapid proliferation of usage. :-) |
add PATCH verb to apiserver
That's helpful actually - I will open a new PR that enhances this with list merging. |
This add's a PATCH verb to all resources who's RESTStorage implements RESTGetter and RESTUpdater. Patch implements the merge patch defined in rfc7386. I'm opening this to discuss what how the pkg/client.Client Patch api should look, or if it should be included in the Client api. The difficulty is that when a patch is converted into an api type, information is lost to zero values of fields, thus a patch must be applied as a json string or a
map[string]interface{}
in order to maintain all information. I will be refactoringkubectl update --patch
as well.Fixes #4578