{"action":"created","comment":{"url":"https://api.github.com/repos/kubernetes/kubernetes/pulls/comments/75609430","id":75609430,"diff_hunk":"@@ -0,0 +1,216 @@\n+<!-- BEGIN MUNGE: UNVERSIONED_WARNING -->\n+\n+<!-- BEGIN STRIP_FOR_RELEASE -->\n+\n+<img src=\"http://kubernetes.io/kubernetes/img/warning.png\" alt=\"WARNING\"\n+     width=\"25\" height=\"25\">\n+<img src=\"http://kubernetes.io/kubernetes/img/warning.png\" alt=\"WARNING\"\n+     width=\"25\" height=\"25\">\n+<img src=\"http://kubernetes.io/kubernetes/img/warning.png\" alt=\"WARNING\"\n+     width=\"25\" height=\"25\">\n+<img src=\"http://kubernetes.io/kubernetes/img/warning.png\" alt=\"WARNING\"\n+     width=\"25\" height=\"25\">\n+<img src=\"http://kubernetes.io/kubernetes/img/warning.png\" alt=\"WARNING\"\n+     width=\"25\" height=\"25\">\n+\n+<h2>PLEASE NOTE: This document applies to the HEAD of the source tree</h2>\n+\n+If you are using a released version of Kubernetes, you should\n+refer to the docs that go with that version.\n+\n+Documentation for other releases can be found at\n+[releases.k8s.io](http://releases.k8s.io).\n+</strong>\n+--\n+\n+<!-- END STRIP_FOR_RELEASE -->\n+\n+<!-- END MUNGE: UNVERSIONED_WARNING -->\n+\n+# New Storage Interface\n+\n+## Motivation\n+\n+The current storage interface was evolved from the legacy etcd2 implementation. It has a few issues that prevents us from further optimizing the storage path efficiency while keeping the code maintainable and readable.\n+\n+We have several goals for the new storage design. Once we reach these goals, the storage layer should be a completely decoupled component from other core logic parts of Kubernetes. That way we can make it easier to maintain without affecting any external code.\n+\n+This list of goals we are trying to achieve with this proposal:\n+\n+- maintainability: The storage code should be straightforward and easy to understand. The storage layer should have very clear responsibility boundary. It should not require huge amount of effort for people to understand other parts of Kubernetes in order to start coding and fixing issues in storage layer.\n+\n+- testability: The storage code should be >90% (now it is ~70%) test covered. Storage developers should not rely on external packages to test the functionality of the storage layer. Users should feel confident in the correctness of the implementation as long as the unit/integration tests pass.\n+\n+- clear API: The storage API should be minimalistic and with clear guarantees.  Users of storage pkg should see the guarantees immediately from the methods naming and related docs. For example, we should have guarantees between Read and Write. Should the user expect to read the value at least as new as the previous local write? Or should the user expect to read the value at least as new as the current global write?\n+\n+- CPU and memory efficient: The storage layer should be designed to support object indexing and watch indexing. Storage layer should be designed to hide all storage related optimization details. The storage should be the single place to optimize server storage. If an index is built, users should expect to select objects in reasonable amount of time (for example < O(logN) for list). To optimize caching size of pod objects, we should do all the work in storage system without touching the pod object defined at API layer.\n+\n+- easy to troubleshoot: Users should be able to identify easily if the storage layer is the current bottleneck of their cluster and why. We should expose enough metrics and write understandable docs to tell users how to do the troubleshooting.\n+\n+- easy to support multiple generic storage backends: The storage layer should have a clear separation between operations on Kubernetes objects and generic key-value pairs. To implement a new key-value or database backend, developers should not have to write duplicated code to handle Kubernetes logic. Ideally, they do not even need to know anything about Kubernetes at all.\n+\n+- backward compatible: Any changes should not introduce backward compatibility issues across at least last two minor leases from the end-user perspective. Using the improved storage system with existing etcd3 backend should just work.\n+\n+## Direction\n+\n+We could evolve the current storage layer towards the goal we listed above. It is doable, but it might involve significant interface and implement changes. Also it requires continuous changes of the consumers of the current storage API. During this process, we also need to ensure everything works perfectly. There is no alpha, or beta since we will be changing the single critical path. This is risky, time consuming, and hard to revert.\n+\n+The solution we proposed here is to start a storage layer from scratch. As Daniel Smith [mentioned](https://github.com/kubernetes/kubernetes/pull/28508#issuecomment-232508836)\n+\n+> Just to (probably unnecessarily) reiterate my position, I think we're going to need side-by-side implementations of both old and new storage styles, so we shouldn't end up locked into whatever we do now.\n+\n+We also believe we should start a side-by-side storage implementation to reach the goal we listed. That way, we won’t be affected by any existing issues, and it gives us the opportunities to rethink the design from the ground up, to achieve the goals more clearly. The only risk is that the effort will be completely wasted, if we cannot reach the goals.\n+\n+## Proposed Storage API\n+\n+```go\n+\n+// Storage stores and persists objects.\n+// Storage provides read-write access to the objects based on string key.\n+type Storage interface {\n+\t// Wait waits until the storage has seen any version >= given version.\n+\t// If version == 0, it provides a guarantee that once returning reads should see all writes from this interface.\n+\tWait(ctx context.Context, version int64)\n+\n+\t// Put puts an object with a key, and returns current object on given key.\n+\t// If conditions is not nil, it will do atomic Put. If compare failed, it will return existing object on given key.\n+\t// See \"Conditions\" docs for more details on compare logic and returned error.\n+\tPut(ctx context.Context, key StorageKey, obj runtime.Object, conditions *Conditions) (*KeyResponse, error)\n+\n+\t// Delete deletes a key and its object, and returns deleted object on given key.\n+\t// conditions has the same guarantee as Put().\n+\tDelete(ctx context.Context, key StorageKey, conditions *Conditions) (*KeyResponse, error)\n+\n+\t// Get gets the current object of given key.\n+\t// If version = 0, it will serve data from local cache.\n+\t// If version > 0, it will get the current state of the key at the time given version is committed.\n+\t// If no object exists on the key, it will return not found error.\n+\tGet(ctx context.Context, key StorageKey, version int64) (*KeyResponse, error)\n+\n+\t// List lists all objects that has give prefix in keys and satisfies selectors.\n+\t// version has the same guarantee as Get().\n+\tList(ctx context.Context, prefix StorageKey, version int64, ss ...Selector) (*ListResponse, error)\n+\n+\t// WatchPrefix watches a prefix after given version. If version is 0, we will watch from current state.\n+\t// It returns notifications of any keys that has given prefix.\n+\t// Given selectors, it returns events that contained object of interest, either of current and previous.\n+\t// If there is any problem establishing the watch channel, it will return error.\n+\t// After channel is established, any error will be returned from WatchChan immediately before it's closed.\n+\tWatchPrefix(ctx context.Context, prefix StorageKey, version int64, ss ...Selector) (WatchChan, error)\n+\n+\t// AddIndex adds a new index.\n+\t// An index indicates a field(s) of a type of an object that would be queried or watched frequently and\n+\t// thus require better efficiency and performance.\n+\t// If an index exists for a field, complexity of a query on it should be no more than O(logN)\n+\t// where N is the total number of objects for List or watchers for watch event delivery.\n+\t// See FieldValueGetFunc for more details on field value retrieving.\n+\tAddIndex(indexName, field string, g FieldValueGetFunc) error\n+\t// DeleteIndex deletes an index.\n+\tDeleteIndex(indexName string)\n+}\n+\n+// KeyResponse contains the object as a response for a key.\n+type KeyResponse struct {\n+\tObject runtime.Object\n+\t// Version is the storage version of the object in this response.\n+\tVersion int64\n+}\n+\n+// ListResponse contains objects as a response for a prefix.\n+type ListResponse struct {\n+\tKeys []*KeyResponse\n+\t// HeadVersion is the latest version known by this storage.\n+\tHeadVersion int64\n+}\n+\n+// Conditions is used in do atomic conditional operations.\n+// If compare failed, corresponding method should return storage version conflict error.\n+type Conditions struct {\n+\t// PrevVersion is used to compare with existing object’s version.\n+\t// Compare would only succeed if versions were matched.\n+\t// Note that PrevVersion=0 means no previous object existed.\n+\tPrevVersion int64\n+}\n+\n+// FieldValueGetFunc returns the value in the object corresponding to given field.\n+// field is uniquely indicating a field of a type of object.\n+// It returns (value, true) if field exists in the object. Otherwise it returns (\"\", false).\n+type FieldValueGetFunc func(field string, obj runtime.Object) (string, bool)\n+\n+// Selector is used to express one selection predicate.\n+// Op is the operator relating corresponding field's value to given values.\n+// See FieldValueGetFunc for details on Field and FVGetFunc.\n+type Selector struct {\n+\tOp        Operator\n+\tField     string\n+\tValues    []string\n+\tFVGetFunc FieldValueGetFunc\n+}\n+\n+type WatchChan <-chan WatchResponse\n+\n+type WatchResponse struct {\n+\tType       EventType\n+\tObject     runtime.Object\n+\tPrevObject runtime.Object\n+\tErr        error\n+}\n+\n+// StorageKey consists of multiple paths and after being joined by \"/\" become a unique path.\n+type StorageKey []string\n+```\n+\n+We made the following changes based on the current Storage.Interface:\n+\n+#### 1. More index friendly\n+","path":"docs/design/apiserver_storage.md","position":166,"original_position":166,"commit_id":"7033c6d4560c7ecc0d8184b62c3cd6a1d4db2e5c","original_commit_id":"7033c6d4560c7ecc0d8184b62c3cd6a1d4db2e5c","user":{"login":"HardySimpson","id":1173940,"avatar_url":"https://avatars.githubusercontent.com/u/1173940?v=3","gravatar_id":"","url":"https://api.github.com/users/HardySimpson","html_url":"https://github.com/HardySimpson","followers_url":"https://api.github.com/users/HardySimpson/followers","following_url":"https://api.github.com/users/HardySimpson/following{/other_user}","gists_url":"https://api.github.com/users/HardySimpson/gists{/gist_id}","starred_url":"https://api.github.com/users/HardySimpson/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/HardySimpson/subscriptions","organizations_url":"https://api.github.com/users/HardySimpson/orgs","repos_url":"https://api.github.com/users/HardySimpson/repos","events_url":"https://api.github.com/users/HardySimpson/events{/privacy}","received_events_url":"https://api.github.com/users/HardySimpson/received_events","type":"User","site_admin":false},"body":"The index part is which I am mainly interested.  \r\nSo, now we can pass a filter function of the Object value to the etcd in list and watch?\r\nwhat's the interface looks like? as I know now etcd only support key index rather than value index","created_at":"2016-08-22T01:40:25Z","updated_at":"2016-08-22T01:40:25Z","html_url":"https://github.com/kubernetes/kubernetes/pull/29888#discussion_r75609430","pull_request_url":"https://api.github.com/repos/kubernetes/kubernetes/pulls/29888","_links":{"self":{"href":"https://api.github.com/repos/kubernetes/kubernetes/pulls/comments/75609430"},"html":{"href":"https://github.com/kubernetes/kubernetes/pull/29888#discussion_r75609430"},"pull_request":{"href":"https://api.github.com/repos/kubernetes/kubernetes/pulls/29888"}}},"pull_request":{"url":"https://api.github.com/repos/kubernetes/kubernetes/pulls/29888","id":79654293,"html_url":"https://github.com/kubernetes/kubernetes/pull/29888","diff_url":"https://github.com/kubernetes/kubernetes/pull/29888.diff","patch_url":"https://github.com/kubernetes/kubernetes/pull/29888.patch","issue_url":"https://api.github.com/repos/kubernetes/kubernetes/issues/29888","number":29888,"state":"open","locked":false,"title":"new design of apiserver storage","user":{"login":"hongchaodeng","id":920884,"avatar_url":"https://avatars.githubusercontent.com/u/920884?v=3","gravatar_id":"","url":"https://api.github.com/users/hongchaodeng","html_url":"https://github.com/hongchaodeng","followers_url":"https://api.github.com/users/hongchaodeng/followers","following_url":"https://api.github.com/users/hongchaodeng/following{/other_user}","gists_url":"https://api.github.com/users/hongchaodeng/gists{/gist_id}","starred_url":"https://api.github.com/users/hongchaodeng/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/hongchaodeng/subscriptions","organizations_url":"https://api.github.com/users/hongchaodeng/orgs","repos_url":"https://api.github.com/users/hongchaodeng/repos","events_url":"https://api.github.com/users/hongchaodeng/events{/privacy}","received_events_url":"https://api.github.com/users/hongchaodeng/received_events","type":"User","site_admin":false},"body":"new design of apiserver storage\n\n<!-- Reviewable:start -->\n---\nThis change is [<img src=\"https://reviewable.kubernetes.io/review_button.svg\" height=\"34\" align=\"absmiddle\" alt=\"Reviewable\"/>](https://reviewable.kubernetes.io/reviews/kubernetes/kubernetes/29888)\n<!-- Reviewable:end -->\n","created_at":"2016-08-01T23:32:24Z","updated_at":"2016-08-22T01:40:26Z","closed_at":null,"merged_at":null,"merge_commit_sha":"1c655691337011caa6874e932d1a214b359734f7","assignee":{"login":"lavalamp","id":647318,"avatar_url":"https://avatars.githubusercontent.com/u/647318?v=3","gravatar_id":"","url":"https://api.github.com/users/lavalamp","html_url":"https://github.com/lavalamp","followers_url":"https://api.github.com/users/lavalamp/followers","following_url":"https://api.github.com/users/lavalamp/following{/other_user}","gists_url":"https://api.github.com/users/lavalamp/gists{/gist_id}","starred_url":"https://api.github.com/users/lavalamp/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/lavalamp/subscriptions","organizations_url":"https://api.github.com/users/lavalamp/orgs","repos_url":"https://api.github.com/users/lavalamp/repos","events_url":"https://api.github.com/users/lavalamp/events{/privacy}","received_events_url":"https://api.github.com/users/lavalamp/received_events","type":"User","site_admin":false},"assignees":[{"login":"lavalamp","id":647318,"avatar_url":"https://avatars.githubusercontent.com/u/647318?v=3","gravatar_id":"","url":"https://api.github.com/users/lavalamp","html_url":"https://github.com/lavalamp","followers_url":"https://api.github.com/users/lavalamp/followers","following_url":"https://api.github.com/users/lavalamp/following{/other_user}","gists_url":"https://api.github.com/users/lavalamp/gists{/gist_id}","starred_url":"https://api.github.com/users/lavalamp/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/lavalamp/subscriptions","organizations_url":"https://api.github.com/users/lavalamp/orgs","repos_url":"https://api.github.com/users/lavalamp/repos","events_url":"https://api.github.com/users/lavalamp/events{/privacy}","received_events_url":"https://api.github.com/users/lavalamp/received_events","type":"User","site_admin":false}],"milestone":null,"commits_url":"https://api.github.com/repos/kubernetes/kubernetes/pulls/29888/commits","review_comments_url":"https://api.github.com/repos/kubernetes/kubernetes/pulls/29888/comments","review_comment_url":"https://api.github.com/repos/kubernetes/kubernetes/pulls/comments{/number}","comments_url":"https://api.github.com/repos/kubernetes/kubernetes/issues/29888/comments","statuses_url":"https://api.github.com/repos/kubernetes/kubernetes/statuses/7033c6d4560c7ecc0d8184b62c3cd6a1d4db2e5c","head":{"label":"hongchaodeng:nsdesign","ref":"nsdesign","sha":"7033c6d4560c7ecc0d8184b62c3cd6a1d4db2e5c","user":{"login":"hongchaodeng","id":920884,"avatar_url":"https://avatars.githubusercontent.com/u/920884?v=3","gravatar_id":"","url":"https://api.github.com/users/hongchaodeng","html_url":"https://github.com/hongchaodeng","followers_url":"https://api.github.com/users/hongchaodeng/followers","following_url":"https://api.github.com/users/hongchaodeng/following{/other_user}","gists_url":"https://api.github.com/users/hongchaodeng/gists{/gist_id}","starred_url":"https://api.github.com/users/hongchaodeng/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/hongchaodeng/subscriptions","organizations_url":"https://api.github.com/users/hongchaodeng/orgs","repos_url":"https://api.github.com/users/hongchaodeng/repos","events_url":"https://api.github.com/users/hongchaodeng/events{/privacy}","received_events_url":"https://api.github.com/users/hongchaodeng/received_events","type":"User","site_admin":false},"repo":{"id":45423343,"name":"kubernetes","full_name":"hongchaodeng/kubernetes","owner":{"login":"hongchaodeng","id":920884,"avatar_url":"https://avatars.githubusercontent.com/u/920884?v=3","gravatar_id":"","url":"https://api.github.com/users/hongchaodeng","html_url":"https://github.com/hongchaodeng","followers_url":"https://api.github.com/users/hongchaodeng/followers","following_url":"https://api.github.com/users/hongchaodeng/following{/other_user}","gists_url":"https://api.github.com/users/hongchaodeng/gists{/gist_id}","starred_url":"https://api.github.com/users/hongchaodeng/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/hongchaodeng/subscriptions","organizations_url":"https://api.github.com/users/hongchaodeng/orgs","repos_url":"https://api.github.com/users/hongchaodeng/repos","events_url":"https://api.github.com/users/hongchaodeng/events{/privacy}","received_events_url":"https://api.github.com/users/hongchaodeng/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/hongchaodeng/kubernetes","description":"Container Cluster Manager from Google","fork":true,"url":"https://api.github.com/repos/hongchaodeng/kubernetes","forks_url":"https://api.github.com/repos/hongchaodeng/kubernetes/forks","keys_url":"https://api.github.com/repos/hongchaodeng/kubernetes/keys{/key_id}","collaborators_url":"https://api.github.com/repos/hongchaodeng/kubernetes/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/hongchaodeng/kubernetes/teams","hooks_url":"https://api.github.com/repos/hongchaodeng/kubernetes/hooks","issue_events_url":"https://api.github.com/repos/hongchaodeng/kubernetes/issues/events{/number}","events_url":"https://api.github.com/repos/hongchaodeng/kubernetes/events","assignees_url":"https://api.github.com/repos/hongchaodeng/kubernetes/assignees{/user}","branches_url":"https://api.github.com/repos/hongchaodeng/kubernetes/branches{/branch}","tags_url":"https://api.github.com/repos/hongchaodeng/kubernetes/tags","blobs_url":"https://api.github.com/repos/hongchaodeng/kubernetes/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/hongchaodeng/kubernetes/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/hongchaodeng/kubernetes/git/refs{/sha}","trees_url":"https://api.github.com/repos/hongchaodeng/kubernetes/git/trees{/sha}","statuses_url":"https://api.github.com/repos/hongchaodeng/kubernetes/statuses/{sha}","languages_url":"https://api.github.com/repos/hongchaodeng/kubernetes/languages","stargazers_url":"https://api.github.com/repos/hongchaodeng/kubernetes/stargazers","contributors_url":"https://api.github.com/repos/hongchaodeng/kubernetes/contributors","subscribers_url":"https://api.github.com/repos/hongchaodeng/kubernetes/subscribers","subscription_url":"https://api.github.com/repos/hongchaodeng/kubernetes/subscription","commits_url":"https://api.github.com/repos/hongchaodeng/kubernetes/commits{/sha}","git_commits_url":"https://api.github.com/repos/hongchaodeng/kubernetes/git/commits{/sha}","comments_url":"https://api.github.com/repos/hongchaodeng/kubernetes/comments{/number}","issue_comment_url":"https://api.github.com/repos/hongchaodeng/kubernetes/issues/comments{/number}","contents_url":"https://api.github.com/repos/hongchaodeng/kubernetes/contents/{+path}","compare_url":"https://api.github.com/repos/hongchaodeng/kubernetes/compare/{base}...{head}","merges_url":"https://api.github.com/repos/hongchaodeng/kubernetes/merges","archive_url":"https://api.github.com/repos/hongchaodeng/kubernetes/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/hongchaodeng/kubernetes/downloads","issues_url":"https://api.github.com/repos/hongchaodeng/kubernetes/issues{/number}","pulls_url":"https://api.github.com/repos/hongchaodeng/kubernetes/pulls{/number}","milestones_url":"https://api.github.com/repos/hongchaodeng/kubernetes/milestones{/number}","notifications_url":"https://api.github.com/repos/hongchaodeng/kubernetes/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/hongchaodeng/kubernetes/labels{/name}","releases_url":"https://api.github.com/repos/hongchaodeng/kubernetes/releases{/id}","deployments_url":"https://api.github.com/repos/hongchaodeng/kubernetes/deployments","created_at":"2015-11-02T21:30:01Z","updated_at":"2016-04-17T11:13:25Z","pushed_at":"2016-08-21T23:00:06Z","git_url":"git://github.com/hongchaodeng/kubernetes.git","ssh_url":"git@github.com:hongchaodeng/kubernetes.git","clone_url":"https://github.com/hongchaodeng/kubernetes.git","svn_url":"https://github.com/hongchaodeng/kubernetes","homepage":"http://kubernetes.io","size":305263,"stargazers_count":0,"watchers_count":0,"language":"Go","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":1,"forks":0,"open_issues":1,"watchers":0,"default_branch":"master"}},"base":{"label":"kubernetes:master","ref":"master","sha":"f614cb4347d42ea120bdb6e16529005f8f786aa9","user":{"login":"kubernetes","id":13629408,"avatar_url":"https://avatars.githubusercontent.com/u/13629408?v=3","gravatar_id":"","url":"https://api.github.com/users/kubernetes","html_url":"https://github.com/kubernetes","followers_url":"https://api.github.com/users/kubernetes/followers","following_url":"https://api.github.com/users/kubernetes/following{/other_user}","gists_url":"https://api.github.com/users/kubernetes/gists{/gist_id}","starred_url":"https://api.github.com/users/kubernetes/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/kubernetes/subscriptions","organizations_url":"https://api.github.com/users/kubernetes/orgs","repos_url":"https://api.github.com/users/kubernetes/repos","events_url":"https://api.github.com/users/kubernetes/events{/privacy}","received_events_url":"https://api.github.com/users/kubernetes/received_events","type":"Organization","site_admin":false},"repo":{"id":20580498,"name":"kubernetes","full_name":"kubernetes/kubernetes","owner":{"login":"kubernetes","id":13629408,"avatar_url":"https://avatars.githubusercontent.com/u/13629408?v=3","gravatar_id":"","url":"https://api.github.com/users/kubernetes","html_url":"https://github.com/kubernetes","followers_url":"https://api.github.com/users/kubernetes/followers","following_url":"https://api.github.com/users/kubernetes/following{/other_user}","gists_url":"https://api.github.com/users/kubernetes/gists{/gist_id}","starred_url":"https://api.github.com/users/kubernetes/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/kubernetes/subscriptions","organizations_url":"https://api.github.com/users/kubernetes/orgs","repos_url":"https://api.github.com/users/kubernetes/repos","events_url":"https://api.github.com/users/kubernetes/events{/privacy}","received_events_url":"https://api.github.com/users/kubernetes/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/kubernetes/kubernetes","description":"Production-Grade Container Scheduling and Management","fork":false,"url":"https://api.github.com/repos/kubernetes/kubernetes","forks_url":"https://api.github.com/repos/kubernetes/kubernetes/forks","keys_url":"https://api.github.com/repos/kubernetes/kubernetes/keys{/key_id}","collaborators_url":"https://api.github.com/repos/kubernetes/kubernetes/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/kubernetes/kubernetes/teams","hooks_url":"https://api.github.com/repos/kubernetes/kubernetes/hooks","issue_events_url":"https://api.github.com/repos/kubernetes/kubernetes/issues/events{/number}","events_url":"https://api.github.com/repos/kubernetes/kubernetes/events","assignees_url":"https://api.github.com/repos/kubernetes/kubernetes/assignees{/user}","branches_url":"https://api.github.com/repos/kubernetes/kubernetes/branches{/branch}","tags_url":"https://api.github.com/repos/kubernetes/kubernetes/tags","blobs_url":"https://api.github.com/repos/kubernetes/kubernetes/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/kubernetes/kubernetes/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/kubernetes/kubernetes/git/refs{/sha}","trees_url":"https://api.github.com/repos/kubernetes/kubernetes/git/trees{/sha}","statuses_url":"https://api.github.com/repos/kubernetes/kubernetes/statuses/{sha}","languages_url":"https://api.github.com/repos/kubernetes/kubernetes/languages","stargazers_url":"https://api.github.com/repos/kubernetes/kubernetes/stargazers","contributors_url":"https://api.github.com/repos/kubernetes/kubernetes/contributors","subscribers_url":"https://api.github.com/repos/kubernetes/kubernetes/subscribers","subscription_url":"https://api.github.com/repos/kubernetes/kubernetes/subscription","commits_url":"https://api.github.com/repos/kubernetes/kubernetes/commits{/sha}","git_commits_url":"https://api.github.com/repos/kubernetes/kubernetes/git/commits{/sha}","comments_url":"https://api.github.com/repos/kubernetes/kubernetes/comments{/number}","issue_comment_url":"https://api.github.com/repos/kubernetes/kubernetes/issues/comments{/number}","contents_url":"https://api.github.com/repos/kubernetes/kubernetes/contents/{+path}","compare_url":"https://api.github.com/repos/kubernetes/kubernetes/compare/{base}...{head}","merges_url":"https://api.github.com/repos/kubernetes/kubernetes/merges","archive_url":"https://api.github.com/repos/kubernetes/kubernetes/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/kubernetes/kubernetes/downloads","issues_url":"https://api.github.com/repos/kubernetes/kubernetes/issues{/number}","pulls_url":"https://api.github.com/repos/kubernetes/kubernetes/pulls{/number}","milestones_url":"https://api.github.com/repos/kubernetes/kubernetes/milestones{/number}","notifications_url":"https://api.github.com/repos/kubernetes/kubernetes/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/kubernetes/kubernetes/labels{/name}","releases_url":"https://api.github.com/repos/kubernetes/kubernetes/releases{/id}","deployments_url":"https://api.github.com/repos/kubernetes/kubernetes/deployments","created_at":"2014-06-06T22:56:04Z","updated_at":"2016-08-22T01:11:30Z","pushed_at":"2016-08-22T01:21:24Z","git_url":"git://github.com/kubernetes/kubernetes.git","ssh_url":"git@github.com:kubernetes/kubernetes.git","clone_url":"https://github.com/kubernetes/kubernetes.git","svn_url":"https://github.com/kubernetes/kubernetes","homepage":"http://kubernetes.io","size":533642,"stargazers_count":16241,"watchers_count":16241,"language":"Go","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":true,"forks_count":5213,"mirror_url":null,"open_issues_count":4203,"forks":5213,"open_issues":4203,"watchers":16241,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/kubernetes/kubernetes/pulls/29888"},"html":{"href":"https://github.com/kubernetes/kubernetes/pull/29888"},"issue":{"href":"https://api.github.com/repos/kubernetes/kubernetes/issues/29888"},"comments":{"href":"https://api.github.com/repos/kubernetes/kubernetes/issues/29888/comments"},"review_comments":{"href":"https://api.github.com/repos/kubernetes/kubernetes/pulls/29888/comments"},"review_comment":{"href":"https://api.github.com/repos/kubernetes/kubernetes/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/kubernetes/kubernetes/pulls/29888/commits"},"statuses":{"href":"https://api.github.com/repos/kubernetes/kubernetes/statuses/7033c6d4560c7ecc0d8184b62c3cd6a1d4db2e5c"}}}}
{"actor":{"display_login":"HardySimpson"}}