{"action":"created","comment":{"url":"https://api.github.com/repos/GoogleCloudPlatform/kubernetes/pulls/comments/25170164","id":25170164,"diff_hunk":"@@ -171,6 +172,85 @@ func CreateResource(r RESTCreater, ctxFn ContextFunc, namer ScopeNamer, codec ru\n \t}\n }\n \n+// PatchResource returns a function that will handle a resource patch\n+func PatchResource(r RESTPatcher, ctxFn ContextFunc, namer ScopeNamer, codec runtime.Codec, resource string, admit admission.Interface) restful.RouteFunction {\n+\treturn func(req *restful.Request, res *restful.Response) {\n+\t\tw := res.ResponseWriter\n+\n+\t\t// 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)\n+\t\ttimeout := parseTimeout(req.Request.URL.Query().Get(\"timeout\"))\n+\n+\t\tnamespace, name, err := namer.Name(req)\n+\t\tif err != nil {\n+\t\t\tnotFound(w, req.Request)\n+\t\t\treturn\n+\t\t}\n+\t\tctx := ctxFn(req)\n+\t\tctx = api.WithNamespace(ctx, namespace)\n+\n+\t\toriginal, err := r.Get(ctx, name)","path":"pkg/apiserver/resthandler.go","position":28,"original_position":28,"commit_id":"f16c82d94e27b3287a3f291831b9557f8f747290","original_commit_id":"f16c82d94e27b3287a3f291831b9557f8f747290","user":{"login":"smarterclayton","id":1163175,"avatar_url":"https://avatars.githubusercontent.com/u/1163175?v=3","gravatar_id":"","url":"https://api.github.com/users/smarterclayton","html_url":"https://github.com/smarterclayton","followers_url":"https://api.github.com/users/smarterclayton/followers","following_url":"https://api.github.com/users/smarterclayton/following{/other_user}","gists_url":"https://api.github.com/users/smarterclayton/gists{/gist_id}","starred_url":"https://api.github.com/users/smarterclayton/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/smarterclayton/subscriptions","organizations_url":"https://api.github.com/users/smarterclayton/orgs","repos_url":"https://api.github.com/users/smarterclayton/repos","events_url":"https://api.github.com/users/smarterclayton/events{/privacy}","received_events_url":"https://api.github.com/users/smarterclayton/received_events","type":"User","site_admin":false},"body":"\n\n----- Original Message -----\n> > +func PatchResource(r RESTPatcher, ctxFn ContextFunc, namer ScopeNamer,\n> > codec runtime.Codec, resource string, admit admission.Interface)\n> > restful.RouteFunction {\n> > +\treturn func(req *restful.Request, res *restful.Response) {\n> > +\t\tw := res.ResponseWriter\n> > +\n> > +\t\t// TODO: we either want to remove timeout or document it (if we\n> > document, move timeout out of this function and declare it in\n> > api_installer)\n> > +\t\ttimeout := parseTimeout(req.Request.URL.Query().Get(\"timeout\"))\n> > +\n> > +\t\tnamespace, name, err := namer.Name(req)\n> > +\t\tif err != nil {\n> > +\t\t\tnotFound(w, req.Request)\n> > +\t\t\treturn\n> > +\t\t}\n> > +\t\tctx := ctxFn(req)\n> > +\t\tctx = api.WithNamespace(ctx, namespace)\n> > +\n> > +\t\toriginal, err := r.Get(ctx, name)\n> \n> Makes sense. We could validate that the client passes a resource version in\n> their patch. The intent was to add PATCH without needing to add an extra\n> Patch method to the registry's RESTs. What's the reason that the registry\n> does a get from etcd after update?\n\nGet 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.\n\nLet'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.\n\n> \n> ---\n> Reply to this email directly or view it on GitHub:\n> https://github.com/GoogleCloudPlatform/kubernetes/pull/4706/files#r25143674\n","created_at":"2015-02-23T15:07:12Z","updated_at":"2015-02-23T15:07:12Z","html_url":"https://github.com/GoogleCloudPlatform/kubernetes/pull/4706#discussion_r25170164","pull_request_url":"https://api.github.com/repos/GoogleCloudPlatform/kubernetes/pulls/4706","_links":{"self":{"href":"https://api.github.com/repos/GoogleCloudPlatform/kubernetes/pulls/comments/25170164"},"html":{"href":"https://github.com/GoogleCloudPlatform/kubernetes/pull/4706#discussion_r25170164"},"pull_request":{"href":"https://api.github.com/repos/GoogleCloudPlatform/kubernetes/pulls/4706"}}},"pull_request":{"url":"https://api.github.com/repos/GoogleCloudPlatform/kubernetes/pulls/4706","id":29766332,"html_url":"https://github.com/GoogleCloudPlatform/kubernetes/pull/4706","diff_url":"https://github.com/GoogleCloudPlatform/kubernetes/pull/4706.diff","patch_url":"https://github.com/GoogleCloudPlatform/kubernetes/pull/4706.patch","issue_url":"https://api.github.com/repos/GoogleCloudPlatform/kubernetes/issues/4706","number":4706,"state":"open","locked":false,"title":"add PATCH verb to apiserver","user":{"login":"mikedanese","id":1787169,"avatar_url":"https://avatars.githubusercontent.com/u/1787169?v=3","gravatar_id":"","url":"https://api.github.com/users/mikedanese","html_url":"https://github.com/mikedanese","followers_url":"https://api.github.com/users/mikedanese/followers","following_url":"https://api.github.com/users/mikedanese/following{/other_user}","gists_url":"https://api.github.com/users/mikedanese/gists{/gist_id}","starred_url":"https://api.github.com/users/mikedanese/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mikedanese/subscriptions","organizations_url":"https://api.github.com/users/mikedanese/orgs","repos_url":"https://api.github.com/users/mikedanese/repos","events_url":"https://api.github.com/users/mikedanese/events{/privacy}","received_events_url":"https://api.github.com/users/mikedanese/received_events","type":"User","site_admin":false},"body":"This add's a PATCH verb to all resources who's RESTStorage implements RESTGetter and RESTUpdater. Patch implements the merge patch defined in [rfc7386](https://tools.ietf.org/html/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 refactoring `kubectl update --patch` as well.\r\n```\r\n curl localhost:8080/api/v1beta1/pods/web \\\r\n    --request PATCH  \\\r\n    -H \"Content-Type:application/merge-patch+json\" \\\r\n    -d '{\"labels\":{\"new\":\"label\"}}'\r\n```\r\n\r\nFixes #4578","created_at":"2015-02-22T06:02:07Z","updated_at":"2015-02-23T15:07:12Z","closed_at":null,"merged_at":null,"merge_commit_sha":"11aa5879b3f297f055fb16f84a3c3b43fb5b78aa","assignee":{"login":"bgrant0607","id":7725777,"avatar_url":"https://avatars.githubusercontent.com/u/7725777?v=3","gravatar_id":"","url":"https://api.github.com/users/bgrant0607","html_url":"https://github.com/bgrant0607","followers_url":"https://api.github.com/users/bgrant0607/followers","following_url":"https://api.github.com/users/bgrant0607/following{/other_user}","gists_url":"https://api.github.com/users/bgrant0607/gists{/gist_id}","starred_url":"https://api.github.com/users/bgrant0607/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/bgrant0607/subscriptions","organizations_url":"https://api.github.com/users/bgrant0607/orgs","repos_url":"https://api.github.com/users/bgrant0607/repos","events_url":"https://api.github.com/users/bgrant0607/events{/privacy}","received_events_url":"https://api.github.com/users/bgrant0607/received_events","type":"User","site_admin":false},"milestone":null,"commits_url":"https://api.github.com/repos/GoogleCloudPlatform/kubernetes/pulls/4706/commits","review_comments_url":"https://api.github.com/repos/GoogleCloudPlatform/kubernetes/pulls/4706/comments","review_comment_url":"https://api.github.com/repos/GoogleCloudPlatform/kubernetes/pulls/comments{/number}","comments_url":"https://api.github.com/repos/GoogleCloudPlatform/kubernetes/issues/4706/comments","statuses_url":"https://api.github.com/repos/GoogleCloudPlatform/kubernetes/statuses/f16c82d94e27b3287a3f291831b9557f8f747290","head":{"label":"mikedanese:PATCH","ref":"PATCH","sha":"f16c82d94e27b3287a3f291831b9557f8f747290","user":{"login":"mikedanese","id":1787169,"avatar_url":"https://avatars.githubusercontent.com/u/1787169?v=3","gravatar_id":"","url":"https://api.github.com/users/mikedanese","html_url":"https://github.com/mikedanese","followers_url":"https://api.github.com/users/mikedanese/followers","following_url":"https://api.github.com/users/mikedanese/following{/other_user}","gists_url":"https://api.github.com/users/mikedanese/gists{/gist_id}","starred_url":"https://api.github.com/users/mikedanese/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mikedanese/subscriptions","organizations_url":"https://api.github.com/users/mikedanese/orgs","repos_url":"https://api.github.com/users/mikedanese/repos","events_url":"https://api.github.com/users/mikedanese/events{/privacy}","received_events_url":"https://api.github.com/users/mikedanese/received_events","type":"User","site_admin":false},"repo":{"id":26203986,"name":"kubernetes","full_name":"mikedanese/kubernetes","owner":{"login":"mikedanese","id":1787169,"avatar_url":"https://avatars.githubusercontent.com/u/1787169?v=3","gravatar_id":"","url":"https://api.github.com/users/mikedanese","html_url":"https://github.com/mikedanese","followers_url":"https://api.github.com/users/mikedanese/followers","following_url":"https://api.github.com/users/mikedanese/following{/other_user}","gists_url":"https://api.github.com/users/mikedanese/gists{/gist_id}","starred_url":"https://api.github.com/users/mikedanese/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mikedanese/subscriptions","organizations_url":"https://api.github.com/users/mikedanese/orgs","repos_url":"https://api.github.com/users/mikedanese/repos","events_url":"https://api.github.com/users/mikedanese/events{/privacy}","received_events_url":"https://api.github.com/users/mikedanese/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/mikedanese/kubernetes","description":"Container Cluster Manager","fork":true,"url":"https://api.github.com/repos/mikedanese/kubernetes","forks_url":"https://api.github.com/repos/mikedanese/kubernetes/forks","keys_url":"https://api.github.com/repos/mikedanese/kubernetes/keys{/key_id}","collaborators_url":"https://api.github.com/repos/mikedanese/kubernetes/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/mikedanese/kubernetes/teams","hooks_url":"https://api.github.com/repos/mikedanese/kubernetes/hooks","issue_events_url":"https://api.github.com/repos/mikedanese/kubernetes/issues/events{/number}","events_url":"https://api.github.com/repos/mikedanese/kubernetes/events","assignees_url":"https://api.github.com/repos/mikedanese/kubernetes/assignees{/user}","branches_url":"https://api.github.com/repos/mikedanese/kubernetes/branches{/branch}","tags_url":"https://api.github.com/repos/mikedanese/kubernetes/tags","blobs_url":"https://api.github.com/repos/mikedanese/kubernetes/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/mikedanese/kubernetes/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/mikedanese/kubernetes/git/refs{/sha}","trees_url":"https://api.github.com/repos/mikedanese/kubernetes/git/trees{/sha}","statuses_url":"https://api.github.com/repos/mikedanese/kubernetes/statuses/{sha}","languages_url":"https://api.github.com/repos/mikedanese/kubernetes/languages","stargazers_url":"https://api.github.com/repos/mikedanese/kubernetes/stargazers","contributors_url":"https://api.github.com/repos/mikedanese/kubernetes/contributors","subscribers_url":"https://api.github.com/repos/mikedanese/kubernetes/subscribers","subscription_url":"https://api.github.com/repos/mikedanese/kubernetes/subscription","commits_url":"https://api.github.com/repos/mikedanese/kubernetes/commits{/sha}","git_commits_url":"https://api.github.com/repos/mikedanese/kubernetes/git/commits{/sha}","comments_url":"https://api.github.com/repos/mikedanese/kubernetes/comments{/number}","issue_comment_url":"https://api.github.com/repos/mikedanese/kubernetes/issues/comments{/number}","contents_url":"https://api.github.com/repos/mikedanese/kubernetes/contents/{+path}","compare_url":"https://api.github.com/repos/mikedanese/kubernetes/compare/{base}...{head}","merges_url":"https://api.github.com/repos/mikedanese/kubernetes/merges","archive_url":"https://api.github.com/repos/mikedanese/kubernetes/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/mikedanese/kubernetes/downloads","issues_url":"https://api.github.com/repos/mikedanese/kubernetes/issues{/number}","pulls_url":"https://api.github.com/repos/mikedanese/kubernetes/pulls{/number}","milestones_url":"https://api.github.com/repos/mikedanese/kubernetes/milestones{/number}","notifications_url":"https://api.github.com/repos/mikedanese/kubernetes/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/mikedanese/kubernetes/labels{/name}","releases_url":"https://api.github.com/repos/mikedanese/kubernetes/releases{/id}","created_at":"2014-11-05T05:02:25Z","updated_at":"2015-02-22T06:51:45Z","pushed_at":"2015-02-22T22:34:09Z","git_url":"git://github.com/mikedanese/kubernetes.git","ssh_url":"git@github.com:mikedanese/kubernetes.git","clone_url":"https://github.com/mikedanese/kubernetes.git","svn_url":"https://github.com/mikedanese/kubernetes","homepage":null,"size":31326,"stargazers_count":0,"watchers_count":0,"language":"Go","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":true,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"GoogleCloudPlatform:master","ref":"master","sha":"86a0193f51c148c3f71e46242678da65e1af5b6e","user":{"login":"GoogleCloudPlatform","id":2810941,"avatar_url":"https://avatars.githubusercontent.com/u/2810941?v=3","gravatar_id":"","url":"https://api.github.com/users/GoogleCloudPlatform","html_url":"https://github.com/GoogleCloudPlatform","followers_url":"https://api.github.com/users/GoogleCloudPlatform/followers","following_url":"https://api.github.com/users/GoogleCloudPlatform/following{/other_user}","gists_url":"https://api.github.com/users/GoogleCloudPlatform/gists{/gist_id}","starred_url":"https://api.github.com/users/GoogleCloudPlatform/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/GoogleCloudPlatform/subscriptions","organizations_url":"https://api.github.com/users/GoogleCloudPlatform/orgs","repos_url":"https://api.github.com/users/GoogleCloudPlatform/repos","events_url":"https://api.github.com/users/GoogleCloudPlatform/events{/privacy}","received_events_url":"https://api.github.com/users/GoogleCloudPlatform/received_events","type":"Organization","site_admin":false},"repo":{"id":20580498,"name":"kubernetes","full_name":"GoogleCloudPlatform/kubernetes","owner":{"login":"GoogleCloudPlatform","id":2810941,"avatar_url":"https://avatars.githubusercontent.com/u/2810941?v=3","gravatar_id":"","url":"https://api.github.com/users/GoogleCloudPlatform","html_url":"https://github.com/GoogleCloudPlatform","followers_url":"https://api.github.com/users/GoogleCloudPlatform/followers","following_url":"https://api.github.com/users/GoogleCloudPlatform/following{/other_user}","gists_url":"https://api.github.com/users/GoogleCloudPlatform/gists{/gist_id}","starred_url":"https://api.github.com/users/GoogleCloudPlatform/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/GoogleCloudPlatform/subscriptions","organizations_url":"https://api.github.com/users/GoogleCloudPlatform/orgs","repos_url":"https://api.github.com/users/GoogleCloudPlatform/repos","events_url":"https://api.github.com/users/GoogleCloudPlatform/events{/privacy}","received_events_url":"https://api.github.com/users/GoogleCloudPlatform/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/GoogleCloudPlatform/kubernetes","description":"Container Cluster Manager","fork":false,"url":"https://api.github.com/repos/GoogleCloudPlatform/kubernetes","forks_url":"https://api.github.com/repos/GoogleCloudPlatform/kubernetes/forks","keys_url":"https://api.github.com/repos/GoogleCloudPlatform/kubernetes/keys{/key_id}","collaborators_url":"https://api.github.com/repos/GoogleCloudPlatform/kubernetes/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/GoogleCloudPlatform/kubernetes/teams","hooks_url":"https://api.github.com/repos/GoogleCloudPlatform/kubernetes/hooks","issue_events_url":"https://api.github.com/repos/GoogleCloudPlatform/kubernetes/issues/events{/number}","events_url":"https://api.github.com/repos/GoogleCloudPlatform/kubernetes/events","assignees_url":"https://api.github.com/repos/GoogleCloudPlatform/kubernetes/assignees{/user}","branches_url":"https://api.github.com/repos/GoogleCloudPlatform/kubernetes/branches{/branch}","tags_url":"https://api.github.com/repos/GoogleCloudPlatform/kubernetes/tags","blobs_url":"https://api.github.com/repos/GoogleCloudPlatform/kubernetes/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/GoogleCloudPlatform/kubernetes/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/GoogleCloudPlatform/kubernetes/git/refs{/sha}","trees_url":"https://api.github.com/repos/GoogleCloudPlatform/kubernetes/git/trees{/sha}","statuses_url":"https://api.github.com/repos/GoogleCloudPlatform/kubernetes/statuses/{sha}","languages_url":"https://api.github.com/repos/GoogleCloudPlatform/kubernetes/languages","stargazers_url":"https://api.github.com/repos/GoogleCloudPlatform/kubernetes/stargazers","contributors_url":"https://api.github.com/repos/GoogleCloudPlatform/kubernetes/contributors","subscribers_url":"https://api.github.com/repos/GoogleCloudPlatform/kubernetes/subscribers","subscription_url":"https://api.github.com/repos/GoogleCloudPlatform/kubernetes/subscription","commits_url":"https://api.github.com/repos/GoogleCloudPlatform/kubernetes/commits{/sha}","git_commits_url":"https://api.github.com/repos/GoogleCloudPlatform/kubernetes/git/commits{/sha}","comments_url":"https://api.github.com/repos/GoogleCloudPlatform/kubernetes/comments{/number}","issue_comment_url":"https://api.github.com/repos/GoogleCloudPlatform/kubernetes/issues/comments{/number}","contents_url":"https://api.github.com/repos/GoogleCloudPlatform/kubernetes/contents/{+path}","compare_url":"https://api.github.com/repos/GoogleCloudPlatform/kubernetes/compare/{base}...{head}","merges_url":"https://api.github.com/repos/GoogleCloudPlatform/kubernetes/merges","archive_url":"https://api.github.com/repos/GoogleCloudPlatform/kubernetes/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/GoogleCloudPlatform/kubernetes/downloads","issues_url":"https://api.github.com/repos/GoogleCloudPlatform/kubernetes/issues{/number}","pulls_url":"https://api.github.com/repos/GoogleCloudPlatform/kubernetes/pulls{/number}","milestones_url":"https://api.github.com/repos/GoogleCloudPlatform/kubernetes/milestones{/number}","notifications_url":"https://api.github.com/repos/GoogleCloudPlatform/kubernetes/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/GoogleCloudPlatform/kubernetes/labels{/name}","releases_url":"https://api.github.com/repos/GoogleCloudPlatform/kubernetes/releases{/id}","created_at":"2014-06-06T22:56:04Z","updated_at":"2015-02-23T13:41:04Z","pushed_at":"2015-02-23T03:23:01Z","git_url":"git://github.com/GoogleCloudPlatform/kubernetes.git","ssh_url":"git@github.com:GoogleCloudPlatform/kubernetes.git","clone_url":"https://github.com/GoogleCloudPlatform/kubernetes.git","svn_url":"https://github.com/GoogleCloudPlatform/kubernetes","homepage":"http://kubernetes.io","size":83511,"stargazers_count":6296,"watchers_count":6296,"language":"Go","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":true,"forks_count":1105,"mirror_url":null,"open_issues_count":749,"forks":1105,"open_issues":749,"watchers":6296,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/GoogleCloudPlatform/kubernetes/pulls/4706"},"html":{"href":"https://github.com/GoogleCloudPlatform/kubernetes/pull/4706"},"issue":{"href":"https://api.github.com/repos/GoogleCloudPlatform/kubernetes/issues/4706"},"comments":{"href":"https://api.github.com/repos/GoogleCloudPlatform/kubernetes/issues/4706/comments"},"review_comments":{"href":"https://api.github.com/repos/GoogleCloudPlatform/kubernetes/pulls/4706/comments"},"review_comment":{"href":"https://api.github.com/repos/GoogleCloudPlatform/kubernetes/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/GoogleCloudPlatform/kubernetes/pulls/4706/commits"},"statuses":{"href":"https://api.github.com/repos/GoogleCloudPlatform/kubernetes/statuses/f16c82d94e27b3287a3f291831b9557f8f747290"}}}}