{"action":"created","comment":{"url":"https://api.github.com/repos/kubernetes/kubernetes/pulls/comments/189913177","pull_request_review_id":122183533,"id":189913177,"diff_hunk":"@@ -281,65 +276,164 @@ func (mounter *NsenterMounter) MakeFile(pathname string) error {\n \treturn nil\n }\n \n-func (mounter *NsenterMounter) ExistsPath(pathname string) bool {\n-\targs := []string{pathname}\n-\t_, err := mounter.ne.Exec(\"ls\", args).CombinedOutput()\n-\tif err == nil {\n-\t\treturn true\n+func (mounter *NsenterMounter) ExistsPath(pathname string) (bool, error) {\n+\t// Resolve the symlinks but allow the target not to exist. EvalSymlinks\n+\t// would return an generic error when the target does not exist.\n+\thostPath, err := mounter.ne.EvalSymlinks(pathname, false /* mustExist */)\n+\tif err != nil {\n+\t\treturn false, err\n \t}\n-\treturn false\n+\tkubeletpath := mounter.ne.KubeletPath(hostPath)\n+\treturn utilfile.FileExists(kubeletpath)\n }\n \n func (mounter *NsenterMounter) CleanSubPaths(podDir string, volumeName string) error {\n \treturn doCleanSubPaths(mounter, podDir, volumeName)\n }\n \n-// getPidOnHost returns kubelet's pid in the host pid namespace\n-func (mounter *NsenterMounter) getPidOnHost(procStatusPath string) (int, error) {\n-\t// Get the PID from /rootfs/proc/self/status\n-\tstatusBytes, err := utilio.ConsistentRead(procStatusPath, maxListTries)\n-\tif err != nil {\n-\t\treturn 0, fmt.Errorf(\"error reading %s: %s\", procStatusPath, err)\n-\t}\n-\tmatches := pidRegExp.FindSubmatch(statusBytes)\n-\tif len(matches) < 2 {\n-\t\treturn 0, fmt.Errorf(\"cannot parse %s: no Pid:\", procStatusPath)\n-\t}\n-\treturn strconv.Atoi(string(matches[1]))\n-}\n-\n func (mounter *NsenterMounter) PrepareSafeSubpath(subPath Subpath) (newHostPath string, cleanupAction func(), err error) {\n-\thostPid, err := mounter.getPidOnHost(hostProcSelfStatusPath)\n-\tif err != nil {\n-\t\treturn \"\", nil, err\n-\t}\n-\tglog.V(4).Infof(\"Kubelet's PID on the host is %d\", hostPid)\n-\n \t// Bind-mount the subpath to avoid using symlinks in subpaths.\n-\tnewHostPath, err = doBindSubPath(mounter, subPath, hostPid)\n+\tnewHostPath, err = doNsEnterBindSubPath(mounter, subPath)\n \n \t// There is no action when the container starts. Bind-mount will be cleaned\n \t// when container stops by CleanSubPaths.\n \tcleanupAction = nil\n \treturn newHostPath, cleanupAction, err\n }\n \n-func (mounter *NsenterMounter) SafeMakeDir(pathname string, base string, perm os.FileMode) error {\n-\treturn doSafeMakeDir(pathname, base, perm)\n+func (mounter *NsenterMounter) SafeMakeDir(subdir string, base string, perm os.FileMode) error {\n+\tfullSubdirPath := filepath.Join(base, subdir)\n+\tevaluatedSubdirPath, err := mounter.ne.EvalSymlinks(fullSubdirPath, false /* mustExist */)\n+\tif err != nil {\n+\t\treturn fmt.Errorf(\"error resolving symlinks in %s: %s\", fullSubdirPath, err)\n+\t}\n+\tevaluatedSubdirPath = filepath.Clean(evaluatedSubdirPath)\n+\n+\tevaluatedBase, err := mounter.ne.EvalSymlinks(base, true /* mustExist */)\n+\tif err != nil {\n+\t\treturn fmt.Errorf(\"error resolving symlinks in %s: %s\", base, err)\n+\t}\n+\tevaluatedBase = filepath.Clean(evaluatedBase)\n+\n+\trootDir := filepath.Clean(mounter.rootDir)\n+\tif pathWithinBase(evaluatedBase, rootDir) {\n+\t\t// Base is in /var/lib/kubelet. This directory is shared between the\n+\t\t// container with kubelet and the host. We don't need to add '/rootfs'.\n+\t\t// This is useful when /rootfs is mounted as read-only - we can still\n+\t\t// create subpaths for paths in /var/lib/kubelet.\n+\t\treturn doSafeMakeDir(evaluatedSubdirPath, evaluatedBase, perm)\n+\t}\n+\n+\t// Base is somewhere on the host's filesystem. Add /rootfs and try to make\n+\t// the directory there.\n+\t// This requires /rootfs to be writable.\n+\tkubeletSubdirPath := mounter.ne.KubeletPath(evaluatedSubdirPath)\n+\tkubeletBase := mounter.ne.KubeletPath(evaluatedBase)\n+\treturn doSafeMakeDir(kubeletSubdirPath, kubeletBase, perm)\n }\n \n func (mounter *NsenterMounter) GetMountRefs(pathname string) ([]string, error) {\n-\thostpath, err := mounter.ne.EvalSymlinks(pathname)\n+\thostpath, err := mounter.ne.EvalSymlinks(pathname, true /* mustExist */)\n \tif err != nil {\n \t\treturn nil, err\n \t}\n \treturn getMountRefsByDev(mounter, hostpath)\n }\n \n+func doNsEnterBindSubPath(mounter *NsenterMounter, subpath Subpath) (hostPath string, err error) {\n+\t// Linux, kubelet runs in a container:\n+\t// - safely open the subpath\n+\t// - bind-mount the subpath to target (this can be unsafe)\n+\t// - check that we mounted the right thing by comparing device ID and inode\n+\t//   of the subpath (via safely opened fd) and the target (that's under our\n+\t//   control)\n+\n+\t// Evaluate all symlinks here once for all subsequent functions.\n+\tevaluatedHostVolumePath, err := mounter.ne.EvalSymlinks(subpath.VolumePath, true /*mustExist*/)\n+\tif err != nil {\n+\t\treturn \"\", fmt.Errorf(\"error resolving symlinks in %q: %v\", subpath.VolumePath, err)\n+\t}\n+\tevaluatedHostSubpath, err := mounter.ne.EvalSymlinks(subpath.Path, true /*mustExist*/)\n+\tif err != nil {\n+\t\treturn \"\", fmt.Errorf(\"error resolving symlinks in %q: %v\", subpath.Path, err)\n+\t}\n+\tglog.V(5).Infof(\"doBindSubPath %q (%q) for volumepath %q\", subpath.Path, evaluatedHostSubpath, subpath.VolumePath)\n+\tsubpath.VolumePath = mounter.ne.KubeletPath(evaluatedHostVolumePath)\n+\tsubpath.Path = mounter.ne.KubeletPath(evaluatedHostSubpath)\n+\n+\t// Check the subpath is correct and open it\n+\tfd, err := safeOpenSubPath(mounter, subpath)\n+\tif err != nil {\n+\t\treturn \"\", err\n+\t}\n+\tdefer syscall.Close(fd)\n+\n+\talreadyMounted, bindPathTarget, err := prepareSubpathTarget(mounter, subpath)\n+\tif err != nil {\n+\t\treturn \"\", err\n+\t}\n+\tif alreadyMounted {\n+\t\treturn bindPathTarget, nil\n+\t}\n+\n+\tsuccess := false\n+\tdefer func() {\n+\t\t// Cleanup subpath on error\n+\t\tif !success {\n+\t\t\tglog.V(4).Infof(\"doNsEnterBindSubPath() failed for %q, cleaning up subpath\", bindPathTarget)\n+\t\t\tif cleanErr := cleanSubPath(mounter, subpath); cleanErr != nil {\n+\t\t\t\tglog.Errorf(\"Failed to clean subpath %q: %v\", bindPathTarget, cleanErr)\n+\t\t\t}\n+\t\t}\n+\t}()\n+\n+\t// Leap of faith: optimistically expect that nobody has modified previously\n+\t// expanded evalSubPath with evil symlinks and bind-mount it.\n+\t// Mount is done on the host! don't use kubelet path!\n+\tglog.V(5).Infof(\"bind mounting %q at %q\", evaluatedHostSubpath, bindPathTarget)\n+\tif err = mounter.Mount(evaluatedHostSubpath, bindPathTarget, \"\" /*fstype*/, []string{\"bind\"}); err != nil {\n+\t\treturn \"\", fmt.Errorf(\"error mounting %s: %s\", evaluatedHostSubpath, err)\n+\t}\n+\n+\t// Check that the bind-mount target is the same inode and device as the\n+\t// source that we keept open, i.e. we mounted the right thing.\n+\tvar srcStat, dstStat unix.Stat_t","path":"pkg/util/mount/nsenter_mount.go","position":null,"original_position":207,"commit_id":"de49b967e4f58d0895e1820e81b83eba117dc6ea","original_commit_id":"26195e2fbac2006e18384d30db2338a4a7593b6f","user":{"login":"jsafrane","id":1745006,"avatar_url":"https://avatars2.githubusercontent.com/u/1745006?v=4","gravatar_id":"","url":"https://api.github.com/users/jsafrane","html_url":"https://github.com/jsafrane","followers_url":"https://api.github.com/users/jsafrane/followers","following_url":"https://api.github.com/users/jsafrane/following{/other_user}","gists_url":"https://api.github.com/users/jsafrane/gists{/gist_id}","starred_url":"https://api.github.com/users/jsafrane/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jsafrane/subscriptions","organizations_url":"https://api.github.com/users/jsafrane/orgs","repos_url":"https://api.github.com/users/jsafrane/repos","events_url":"https://api.github.com/users/jsafrane/events{/privacy}","received_events_url":"https://api.github.com/users/jsafrane/received_events","type":"User","site_admin":false},"body":"Ok, I added some unit tests that tries to find two directories on different filesystems. I don't like /dev/shm usage there, but it should not matter much in Kubernetes tests.","created_at":"2018-05-22T14:04:15Z","updated_at":"2018-05-22T14:04:15Z","html_url":"https://github.com/kubernetes/kubernetes/pull/63143#discussion_r189913177","pull_request_url":"https://api.github.com/repos/kubernetes/kubernetes/pulls/63143","author_association":"MEMBER","_links":{"self":{"href":"https://api.github.com/repos/kubernetes/kubernetes/pulls/comments/189913177"},"html":{"href":"https://github.com/kubernetes/kubernetes/pull/63143#discussion_r189913177"},"pull_request":{"href":"https://api.github.com/repos/kubernetes/kubernetes/pulls/63143"}},"in_reply_to_id":186499819},"pull_request":{"url":"https://api.github.com/repos/kubernetes/kubernetes/pulls/63143","id":184041129,"html_url":"https://github.com/kubernetes/kubernetes/pull/63143","diff_url":"https://github.com/kubernetes/kubernetes/pull/63143.diff","patch_url":"https://github.com/kubernetes/kubernetes/pull/63143.patch","issue_url":"https://api.github.com/repos/kubernetes/kubernetes/issues/63143","number":63143,"state":"open","locked":false,"title":"Containerized subpath","user":{"login":"jsafrane","id":1745006,"avatar_url":"https://avatars2.githubusercontent.com/u/1745006?v=4","gravatar_id":"","url":"https://api.github.com/users/jsafrane","html_url":"https://github.com/jsafrane","followers_url":"https://api.github.com/users/jsafrane/followers","following_url":"https://api.github.com/users/jsafrane/following{/other_user}","gists_url":"https://api.github.com/users/jsafrane/gists{/gist_id}","starred_url":"https://api.github.com/users/jsafrane/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jsafrane/subscriptions","organizations_url":"https://api.github.com/users/jsafrane/orgs","repos_url":"https://api.github.com/users/jsafrane/repos","events_url":"https://api.github.com/users/jsafrane/events{/privacy}","received_events_url":"https://api.github.com/users/jsafrane/received_events","type":"User","site_admin":false},"body":"**What this PR does / why we need it**:\r\nContainerized kubelet needs a different implementation of `PrepareSafeSubpath` than kubelet running directly on the host.\r\n\r\nOn the host we safely open the subpath and then bind-mount `/proc/<pidof kubelet>/fd/<descriptor of opened subpath>`.\r\n\r\nWith kubelet running in a container, `/proc/xxx/fd/yy` on the host contains path that works only inside the container, i.e. `/rootfs/path/to/subpath` and thus any bind-mount on the host fails.\r\n\r\nSolution:\r\n- safely open the subpath and gets its device ID and inode number\r\n- blindly bind-mount the subpath to `/var/lib/kubelet/pods/<uid>/volume-subpaths/<name of container>/<id of mount>`. This is potentially unsafe, because user can change the subpath source to a link to a bad place (say `/run/docker.sock`) just before the bind-mount.\r\n- get device ID and inode number of the destination. Typical users can't modify this file, as it lies on /var/lib/kubelet on the host.\r\n- compare these device IDs and inode numbers.\r\n\r\n**Which issue(s) this PR fixes**\r\nFixes #61456\r\n\r\n**Special notes for your reviewer**:\r\n\r\nThe PR contains some refactoring of `doBindSubPath` to extract the common code. New `doNsEnterBindSubPath` is added for the nsenter related parts.\r\n\r\nTODO:\r\n\r\n- e2e testing\r\n\r\n**Release note**:\r\n<!--  Write your release note:\r\n1. Enter your extended release note in the below block. If the PR requires additional action from users switching to the new release, include the string \"action required\".\r\n2. If no release note is required, just write \"NONE\".\r\n-->\r\n```release-note\r\nNONE\r\n```\r\n","created_at":"2018-04-25T13:45:03Z","updated_at":"2018-05-22T14:04:15Z","closed_at":null,"merged_at":null,"merge_commit_sha":"ebe986e23e68da43d14a9f45b44788dcb0552b06","assignee":{"login":"msau42","id":24448061,"avatar_url":"https://avatars3.githubusercontent.com/u/24448061?v=4","gravatar_id":"","url":"https://api.github.com/users/msau42","html_url":"https://github.com/msau42","followers_url":"https://api.github.com/users/msau42/followers","following_url":"https://api.github.com/users/msau42/following{/other_user}","gists_url":"https://api.github.com/users/msau42/gists{/gist_id}","starred_url":"https://api.github.com/users/msau42/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/msau42/subscriptions","organizations_url":"https://api.github.com/users/msau42/orgs","repos_url":"https://api.github.com/users/msau42/repos","events_url":"https://api.github.com/users/msau42/events{/privacy}","received_events_url":"https://api.github.com/users/msau42/received_events","type":"User","site_admin":false},"assignees":[{"login":"msau42","id":24448061,"avatar_url":"https://avatars3.githubusercontent.com/u/24448061?v=4","gravatar_id":"","url":"https://api.github.com/users/msau42","html_url":"https://github.com/msau42","followers_url":"https://api.github.com/users/msau42/followers","following_url":"https://api.github.com/users/msau42/following{/other_user}","gists_url":"https://api.github.com/users/msau42/gists{/gist_id}","starred_url":"https://api.github.com/users/msau42/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/msau42/subscriptions","organizations_url":"https://api.github.com/users/msau42/orgs","repos_url":"https://api.github.com/users/msau42/repos","events_url":"https://api.github.com/users/msau42/events{/privacy}","received_events_url":"https://api.github.com/users/msau42/received_events","type":"User","site_admin":false}],"requested_reviewers":[{"login":"smarterclayton","id":1163175,"avatar_url":"https://avatars0.githubusercontent.com/u/1163175?v=4","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}],"requested_teams":[],"labels":[{"id":477397086,"url":"https://api.github.com/repos/kubernetes/kubernetes/labels/cncf-cla:%20yes","name":"cncf-cla: yes","color":"bfe5bf","default":false},{"id":349530249,"url":"https://api.github.com/repos/kubernetes/kubernetes/labels/release-note-none","name":"release-note-none","color":"c2e0c6","default":false},{"id":169428334,"url":"https://api.github.com/repos/kubernetes/kubernetes/labels/sig/storage","name":"sig/storage","color":"d2b48c","default":false},{"id":253451093,"url":"https://api.github.com/repos/kubernetes/kubernetes/labels/size/XXL","name":"size/XXL","color":"ee0000","default":false}],"milestone":null,"commits_url":"https://api.github.com/repos/kubernetes/kubernetes/pulls/63143/commits","review_comments_url":"https://api.github.com/repos/kubernetes/kubernetes/pulls/63143/comments","review_comment_url":"https://api.github.com/repos/kubernetes/kubernetes/pulls/comments{/number}","comments_url":"https://api.github.com/repos/kubernetes/kubernetes/issues/63143/comments","statuses_url":"https://api.github.com/repos/kubernetes/kubernetes/statuses/de49b967e4f58d0895e1820e81b83eba117dc6ea","head":{"label":"jsafrane:containerized-subpath","ref":"containerized-subpath","sha":"de49b967e4f58d0895e1820e81b83eba117dc6ea","user":{"login":"jsafrane","id":1745006,"avatar_url":"https://avatars2.githubusercontent.com/u/1745006?v=4","gravatar_id":"","url":"https://api.github.com/users/jsafrane","html_url":"https://github.com/jsafrane","followers_url":"https://api.github.com/users/jsafrane/followers","following_url":"https://api.github.com/users/jsafrane/following{/other_user}","gists_url":"https://api.github.com/users/jsafrane/gists{/gist_id}","starred_url":"https://api.github.com/users/jsafrane/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jsafrane/subscriptions","organizations_url":"https://api.github.com/users/jsafrane/orgs","repos_url":"https://api.github.com/users/jsafrane/repos","events_url":"https://api.github.com/users/jsafrane/events{/privacy}","received_events_url":"https://api.github.com/users/jsafrane/received_events","type":"User","site_admin":false},"repo":{"id":33241093,"name":"kubernetes","full_name":"jsafrane/kubernetes","owner":{"login":"jsafrane","id":1745006,"avatar_url":"https://avatars2.githubusercontent.com/u/1745006?v=4","gravatar_id":"","url":"https://api.github.com/users/jsafrane","html_url":"https://github.com/jsafrane","followers_url":"https://api.github.com/users/jsafrane/followers","following_url":"https://api.github.com/users/jsafrane/following{/other_user}","gists_url":"https://api.github.com/users/jsafrane/gists{/gist_id}","starred_url":"https://api.github.com/users/jsafrane/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jsafrane/subscriptions","organizations_url":"https://api.github.com/users/jsafrane/orgs","repos_url":"https://api.github.com/users/jsafrane/repos","events_url":"https://api.github.com/users/jsafrane/events{/privacy}","received_events_url":"https://api.github.com/users/jsafrane/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/jsafrane/kubernetes","description":"Container Cluster Manager from Google","fork":true,"url":"https://api.github.com/repos/jsafrane/kubernetes","forks_url":"https://api.github.com/repos/jsafrane/kubernetes/forks","keys_url":"https://api.github.com/repos/jsafrane/kubernetes/keys{/key_id}","collaborators_url":"https://api.github.com/repos/jsafrane/kubernetes/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/jsafrane/kubernetes/teams","hooks_url":"https://api.github.com/repos/jsafrane/kubernetes/hooks","issue_events_url":"https://api.github.com/repos/jsafrane/kubernetes/issues/events{/number}","events_url":"https://api.github.com/repos/jsafrane/kubernetes/events","assignees_url":"https://api.github.com/repos/jsafrane/kubernetes/assignees{/user}","branches_url":"https://api.github.com/repos/jsafrane/kubernetes/branches{/branch}","tags_url":"https://api.github.com/repos/jsafrane/kubernetes/tags","blobs_url":"https://api.github.com/repos/jsafrane/kubernetes/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/jsafrane/kubernetes/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/jsafrane/kubernetes/git/refs{/sha}","trees_url":"https://api.github.com/repos/jsafrane/kubernetes/git/trees{/sha}","statuses_url":"https://api.github.com/repos/jsafrane/kubernetes/statuses/{sha}","languages_url":"https://api.github.com/repos/jsafrane/kubernetes/languages","stargazers_url":"https://api.github.com/repos/jsafrane/kubernetes/stargazers","contributors_url":"https://api.github.com/repos/jsafrane/kubernetes/contributors","subscribers_url":"https://api.github.com/repos/jsafrane/kubernetes/subscribers","subscription_url":"https://api.github.com/repos/jsafrane/kubernetes/subscription","commits_url":"https://api.github.com/repos/jsafrane/kubernetes/commits{/sha}","git_commits_url":"https://api.github.com/repos/jsafrane/kubernetes/git/commits{/sha}","comments_url":"https://api.github.com/repos/jsafrane/kubernetes/comments{/number}","issue_comment_url":"https://api.github.com/repos/jsafrane/kubernetes/issues/comments{/number}","contents_url":"https://api.github.com/repos/jsafrane/kubernetes/contents/{+path}","compare_url":"https://api.github.com/repos/jsafrane/kubernetes/compare/{base}...{head}","merges_url":"https://api.github.com/repos/jsafrane/kubernetes/merges","archive_url":"https://api.github.com/repos/jsafrane/kubernetes/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/jsafrane/kubernetes/downloads","issues_url":"https://api.github.com/repos/jsafrane/kubernetes/issues{/number}","pulls_url":"https://api.github.com/repos/jsafrane/kubernetes/pulls{/number}","milestones_url":"https://api.github.com/repos/jsafrane/kubernetes/milestones{/number}","notifications_url":"https://api.github.com/repos/jsafrane/kubernetes/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/jsafrane/kubernetes/labels{/name}","releases_url":"https://api.github.com/repos/jsafrane/kubernetes/releases{/id}","deployments_url":"https://api.github.com/repos/jsafrane/kubernetes/deployments","created_at":"2015-04-01T10:16:41Z","updated_at":"2018-05-22T10:56:35Z","pushed_at":"2018-05-22T13:55:34Z","git_url":"git://github.com/jsafrane/kubernetes.git","ssh_url":"git@github.com:jsafrane/kubernetes.git","clone_url":"https://github.com/jsafrane/kubernetes.git","svn_url":"https://github.com/jsafrane/kubernetes","homepage":"http://kubernetes.io","size":551715,"stargazers_count":0,"watchers_count":0,"language":"Go","has_issues":false,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"archived":false,"open_issues_count":1,"license":{"key":"apache-2.0","name":"Apache License 2.0","spdx_id":"Apache-2.0","url":"https://api.github.com/licenses/apache-2.0"},"forks":0,"open_issues":1,"watchers":0,"default_branch":"master"}},"base":{"label":"kubernetes:master","ref":"master","sha":"e85b81bbee098a7ec75cc894a8785867ec586798","user":{"login":"kubernetes","id":13629408,"avatar_url":"https://avatars2.githubusercontent.com/u/13629408?v=4","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://avatars2.githubusercontent.com/u/13629408?v=4","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":"2018-05-22T12:45:27Z","pushed_at":"2018-05-22T14:02:45Z","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":779524,"stargazers_count":36613,"watchers_count":36613,"language":"Go","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":12885,"mirror_url":null,"archived":false,"open_issues_count":3255,"license":{"key":"apache-2.0","name":"Apache License 2.0","spdx_id":"Apache-2.0","url":"https://api.github.com/licenses/apache-2.0"},"forks":12885,"open_issues":3255,"watchers":36613,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/kubernetes/kubernetes/pulls/63143"},"html":{"href":"https://github.com/kubernetes/kubernetes/pull/63143"},"issue":{"href":"https://api.github.com/repos/kubernetes/kubernetes/issues/63143"},"comments":{"href":"https://api.github.com/repos/kubernetes/kubernetes/issues/63143/comments"},"review_comments":{"href":"https://api.github.com/repos/kubernetes/kubernetes/pulls/63143/comments"},"review_comment":{"href":"https://api.github.com/repos/kubernetes/kubernetes/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/kubernetes/kubernetes/pulls/63143/commits"},"statuses":{"href":"https://api.github.com/repos/kubernetes/kubernetes/statuses/de49b967e4f58d0895e1820e81b83eba117dc6ea"}},"author_association":"MEMBER"}}
{"actor":{"display_login":"jsafrane"}}