{"action":"created","comment":{"url":"https://api.github.com/repos/GoogleCloudPlatform/kubernetes/pulls/comments/29880297","id":29880297,"diff_hunk":"@@ -0,0 +1,278 @@\n+/*\n+Copyright 2015 The Kubernetes Authors All rights reserved.\n+\n+Licensed under the Apache License, Version 2.0 (the \"License\");\n+you may not use this file except in compliance with the License.\n+You may obtain a copy of the License at\n+\n+    http://www.apache.org/licenses/LICENSE-2.0\n+\n+Unless required by applicable law or agreed to in writing, software\n+distributed under the License is distributed on an \"AS IS\" BASIS,\n+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n+See the License for the specific language governing permissions and\n+limitations under the License.\n+*/\n+\n+package allocator\n+\n+import (\n+\t\"errors\"\n+\t\"fmt\"\n+\t\"math/big\"\n+\t\"math/rand\"\n+\t\"net\"\n+\t\"sync\"\n+)\n+\n+// Interface manages the allocation of IP addresses out of a range. Interface\n+// should be threadsafe.\n+type Interface interface {\n+\tAllocate(net.IP) error\n+\tAllocateNext() (net.IP, error)\n+\tRelease(net.IP) error\n+}\n+\n+// Snapshottable is an Interface that can be snapshotted and restored. Snapshottable\n+// should be threadsafe.\n+type Snapshottable interface {\n+\tInterface\n+\tSnapshot() (*net.IPNet, []byte)\n+\tRestore(*net.IPNet, []byte) error\n+}\n+\n+var (\n+\tErrFull               = errors.New(\"range is full\")\n+\tErrNotInRange         = errors.New(\"provided IP is not in the valid range\")\n+\tErrAllocated          = errors.New(\"provided IP is already allocated\")\n+\tErrMismatchedNetwork  = errors.New(\"the provided network does not match the current range\")\n+\tErrAllocationDisabled = errors.New(\"IP addresses cannot be allocated at this time\")\n+)\n+\n+// Range is a contiguous block of IPs that can be allocated atomically.\n+type Range struct {\n+\tnet *net.IPNet\n+\t// base is a cached version of the start IP in the CIDR range as a *big.Int\n+\tbase *big.Int\n+\t// strategy is the strategy for choosing the next available IP out of the range\n+\tstrategy allocateStrategy\n+\t// max is the maximum size of the range\n+\tmax int\n+\n+\t// lock guards the following members\n+\tlock sync.Mutex\n+\t// count is the number of currently allocated elements in the range\n+\tcount int\n+\t// allocated is a bit array of the allocated ips in the range\n+\tallocated *big.Int\n+}\n+\n+// allocateStrategy is a search strategy in the allocation map for a valid IP.\n+type allocateStrategy func(allocated *big.Int, max, count int) (int, error)\n+\n+// NewCIDRRange creates a Range over a net.IPNet.\n+func NewCIDRRange(cidr *net.IPNet) *Range {\n+\tones, bits := cidr.Mask.Size()\n+\tmax := int(1) << uint(bits-ones)\n+\tr := Range{\n+\t\tnet:      cidr,\n+\t\tstrategy: randomScanStrategy,\n+\t\tbase:     bigForIP(cidr.IP),\n+\t\tmax:      max,\n+\n+\t\tallocated: big.NewInt(0),\n+\t\tcount:     0,\n+\t}\n+\treturn &r\n+}\n+\n+// Free returns the count of IP addresses left in the range.\n+func (r *Range) Free() int {","path":"pkg/registry/service/allocator/allocator.go","position":90,"original_position":90,"commit_id":"2d42ac6b4e848d42ae7c2f32772840809e9bf6df","original_commit_id":"c5e66e8bdbafab7db16e85a03ff8bbfbbdf0c6e1","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":"Could be, but I wanted an allocator to be able to check remaining eventually to handle heuristics and warnings.  It's not on the allocator.Interface, but it's something the etcd service controller might want to print (and should probably include max as well)\n\n----- Original Message -----\n> > +\tones, bits := cidr.Mask.Size()\n> > +\tmax := int(1) << uint(bits-ones)\n> > +\tr := Range{\n> > +\t\tnet:      cidr,\n> > +\t\tstrategy: randomScanStrategy,\n> > +\t\tbase:     bigForIP(cidr.IP),\n> > +\t\tmax:      max,\n> > +\n> > +\t\tallocated: big.NewInt(0),\n> > +\t\tcount:     0,\n> > +\t}\n> > +\treturn &r\n> > +}\n> > +\n> > +// Free returns the count of IP addresses left in the range.\n> > +func (r *Range) Free() int {\n> \n> afaict this could be an unexported symbol?\n> \n> ---\n> Reply to this email directly or view it on GitHub:\n> https://github.com/GoogleCloudPlatform/kubernetes/pull/7704/files#r29878931\n","created_at":"2015-05-07T18:19:58Z","updated_at":"2015-05-07T18:19:58Z","html_url":"https://github.com/GoogleCloudPlatform/kubernetes/pull/7704#discussion_r29880297","pull_request_url":"https://api.github.com/repos/GoogleCloudPlatform/kubernetes/pulls/7704","_links":{"self":{"href":"https://api.github.com/repos/GoogleCloudPlatform/kubernetes/pulls/comments/29880297"},"html":{"href":"https://github.com/GoogleCloudPlatform/kubernetes/pull/7704#discussion_r29880297"},"pull_request":{"href":"https://api.github.com/repos/GoogleCloudPlatform/kubernetes/pulls/7704"}}},"pull_request":{"url":"https://api.github.com/repos/GoogleCloudPlatform/kubernetes/pulls/7704","id":34645540,"html_url":"https://github.com/GoogleCloudPlatform/kubernetes/pull/7704","diff_url":"https://github.com/GoogleCloudPlatform/kubernetes/pull/7704.diff","patch_url":"https://github.com/GoogleCloudPlatform/kubernetes/pull/7704.patch","issue_url":"https://api.github.com/repos/GoogleCloudPlatform/kubernetes/issues/7704","number":7704,"state":"open","locked":false,"title":"Use etcd to allocate PortalIPs instead of in-mem","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":"Addresses #473\r\n\r\nBasic design:\r\n\r\n1. The service registry asks an allocator for an IP (same as today)\r\n2. That allocator is backed by an in-memory state (represented as a bit array of allocated IPs in a range), which is in turn backed by etcd\r\n3. On an allocation request, the local state is reserved, then we try an atomic CAS on etcd to set the allocation\r\n4. If the CAS fails, we reload the state from etcd and try the allocation again\r\n5. If there is no state in Etcd all service calls fail with an error \"Allocation is disabled\"\r\n6. A controller loop is responsible for booting the state in etcd on creation, and then periodically recalculating the allocation map based on what is actually calculated from services.\r\n\r\nEach allocator in the master has a lock and we use optimistic concurrency to guard changes to the global state (each master can only make progress by performing the CAS) so each master will contend during high service creation load.\r\n\r\nThe repair controller logs any errors - it is the responsibility of the administrator to see those issues and correct them via delete/recreate of a service.\r\n\r\nA subsequent optimization will be to move non-requested portalIP allocation to a controller loop (distinct from the Repair controller).\r\n\r\nThe new allocator should be IPv6 safe.","created_at":"2015-05-04T15:13:44Z","updated_at":"2015-05-07T18:19:58Z","closed_at":null,"merged_at":null,"merge_commit_sha":"eeff07e27c874b796a8be585804b6c468b990d66","assignee":{"login":"a-robinson","id":7085343,"avatar_url":"https://avatars.githubusercontent.com/u/7085343?v=3","gravatar_id":"","url":"https://api.github.com/users/a-robinson","html_url":"https://github.com/a-robinson","followers_url":"https://api.github.com/users/a-robinson/followers","following_url":"https://api.github.com/users/a-robinson/following{/other_user}","gists_url":"https://api.github.com/users/a-robinson/gists{/gist_id}","starred_url":"https://api.github.com/users/a-robinson/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/a-robinson/subscriptions","organizations_url":"https://api.github.com/users/a-robinson/orgs","repos_url":"https://api.github.com/users/a-robinson/repos","events_url":"https://api.github.com/users/a-robinson/events{/privacy}","received_events_url":"https://api.github.com/users/a-robinson/received_events","type":"User","site_admin":false},"milestone":null,"commits_url":"https://api.github.com/repos/GoogleCloudPlatform/kubernetes/pulls/7704/commits","review_comments_url":"https://api.github.com/repos/GoogleCloudPlatform/kubernetes/pulls/7704/comments","review_comment_url":"https://api.github.com/repos/GoogleCloudPlatform/kubernetes/pulls/comments{/number}","comments_url":"https://api.github.com/repos/GoogleCloudPlatform/kubernetes/issues/7704/comments","statuses_url":"https://api.github.com/repos/GoogleCloudPlatform/kubernetes/statuses/2d42ac6b4e848d42ae7c2f32772840809e9bf6df","head":{"label":"smarterclayton:parallel_allocator","ref":"parallel_allocator","sha":"2d42ac6b4e848d42ae7c2f32772840809e9bf6df","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},"repo":{"id":21225407,"name":"kubernetes","full_name":"smarterclayton/kubernetes","owner":{"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},"private":false,"html_url":"https://github.com/smarterclayton/kubernetes","description":"Container Cluster Manager","fork":true,"url":"https://api.github.com/repos/smarterclayton/kubernetes","forks_url":"https://api.github.com/repos/smarterclayton/kubernetes/forks","keys_url":"https://api.github.com/repos/smarterclayton/kubernetes/keys{/key_id}","collaborators_url":"https://api.github.com/repos/smarterclayton/kubernetes/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/smarterclayton/kubernetes/teams","hooks_url":"https://api.github.com/repos/smarterclayton/kubernetes/hooks","issue_events_url":"https://api.github.com/repos/smarterclayton/kubernetes/issues/events{/number}","events_url":"https://api.github.com/repos/smarterclayton/kubernetes/events","assignees_url":"https://api.github.com/repos/smarterclayton/kubernetes/assignees{/user}","branches_url":"https://api.github.com/repos/smarterclayton/kubernetes/branches{/branch}","tags_url":"https://api.github.com/repos/smarterclayton/kubernetes/tags","blobs_url":"https://api.github.com/repos/smarterclayton/kubernetes/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/smarterclayton/kubernetes/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/smarterclayton/kubernetes/git/refs{/sha}","trees_url":"https://api.github.com/repos/smarterclayton/kubernetes/git/trees{/sha}","statuses_url":"https://api.github.com/repos/smarterclayton/kubernetes/statuses/{sha}","languages_url":"https://api.github.com/repos/smarterclayton/kubernetes/languages","stargazers_url":"https://api.github.com/repos/smarterclayton/kubernetes/stargazers","contributors_url":"https://api.github.com/repos/smarterclayton/kubernetes/contributors","subscribers_url":"https://api.github.com/repos/smarterclayton/kubernetes/subscribers","subscription_url":"https://api.github.com/repos/smarterclayton/kubernetes/subscription","commits_url":"https://api.github.com/repos/smarterclayton/kubernetes/commits{/sha}","git_commits_url":"https://api.github.com/repos/smarterclayton/kubernetes/git/commits{/sha}","comments_url":"https://api.github.com/repos/smarterclayton/kubernetes/comments{/number}","issue_comment_url":"https://api.github.com/repos/smarterclayton/kubernetes/issues/comments{/number}","contents_url":"https://api.github.com/repos/smarterclayton/kubernetes/contents/{+path}","compare_url":"https://api.github.com/repos/smarterclayton/kubernetes/compare/{base}...{head}","merges_url":"https://api.github.com/repos/smarterclayton/kubernetes/merges","archive_url":"https://api.github.com/repos/smarterclayton/kubernetes/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/smarterclayton/kubernetes/downloads","issues_url":"https://api.github.com/repos/smarterclayton/kubernetes/issues{/number}","pulls_url":"https://api.github.com/repos/smarterclayton/kubernetes/pulls{/number}","milestones_url":"https://api.github.com/repos/smarterclayton/kubernetes/milestones{/number}","notifications_url":"https://api.github.com/repos/smarterclayton/kubernetes/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/smarterclayton/kubernetes/labels{/name}","releases_url":"https://api.github.com/repos/smarterclayton/kubernetes/releases{/id}","created_at":"2014-06-26T02:28:13Z","updated_at":"2015-03-26T16:39:31Z","pushed_at":"2015-05-07T13:45:12Z","git_url":"git://github.com/smarterclayton/kubernetes.git","ssh_url":"git@github.com:smarterclayton/kubernetes.git","clone_url":"https://github.com/smarterclayton/kubernetes.git","svn_url":"https://github.com/smarterclayton/kubernetes","homepage":null,"size":53874,"stargazers_count":0,"watchers_count":0,"language":"Go","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":1,"mirror_url":null,"open_issues_count":0,"forks":1,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"GoogleCloudPlatform:master","ref":"master","sha":"1d59a0351f29ac86a5a99e09a30430e49aded2d8","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 from Google","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-05-07T18:08:05Z","pushed_at":"2015-05-07T18:11:56Z","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":254902,"stargazers_count":7535,"watchers_count":7535,"language":"Go","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":true,"forks_count":1526,"mirror_url":null,"open_issues_count":1008,"forks":1526,"open_issues":1008,"watchers":7535,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/GoogleCloudPlatform/kubernetes/pulls/7704"},"html":{"href":"https://github.com/GoogleCloudPlatform/kubernetes/pull/7704"},"issue":{"href":"https://api.github.com/repos/GoogleCloudPlatform/kubernetes/issues/7704"},"comments":{"href":"https://api.github.com/repos/GoogleCloudPlatform/kubernetes/issues/7704/comments"},"review_comments":{"href":"https://api.github.com/repos/GoogleCloudPlatform/kubernetes/pulls/7704/comments"},"review_comment":{"href":"https://api.github.com/repos/GoogleCloudPlatform/kubernetes/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/GoogleCloudPlatform/kubernetes/pulls/7704/commits"},"statuses":{"href":"https://api.github.com/repos/GoogleCloudPlatform/kubernetes/statuses/2d42ac6b4e848d42ae7c2f32772840809e9bf6df"}}}}