{"action":"created","comment":{"url":"https://api.github.com/repos/kubernetes/kubernetes/pulls/comments/97556224","pull_request_review_id":18168479,"id":97556224,"diff_hunk":"@@ -224,22 +228,187 @@ func getNamedCertificateMap(certs []namedTlsCert) (map[string]*tls.Certificate,\n \treturn byName, nil\n }\n \n+// tcpConnectionChainListener is responsible to invoking any connection handling\n+// code which is invoked after AcceptTCP() has been called but before the\n+// connection is usable by the server for communicating with the client. We\n+// assume if an error is returned from a chain handler that it will return a nil\n+// conn and have already closed that conn.\n+type tcpConnectionChainHandler interface {\n+\tinit(*net.TCPConn) error\n+\twrap(net.Conn) (net.Conn, error)\n+}\n+\n+type tcpConnectionChainListener struct {\n+\t*net.TCPListener\n+\tchain []tcpConnectionChainHandler\n+}\n+\n+func (l tcpConnectionChainListener) Accept() (net.Conn, error) {\n+\ttc, err := l.AcceptTCP()\n+\tif err != nil {\n+\t\treturn nil, err\n+\t}\n+\tfor _, handler := range l.chain {\n+\t\terr = handler.init(tc)\n+\t\tif err != nil {\n+\t\t\treturn nil, err\n+\t\t}\n+\t}\n+\tvar conn net.Conn = tc\n+\tfor _, handler := range l.chain {\n+\t\tconn, err = handler.wrap(conn)\n+\t\tif err != nil {\n+\t\t\treturn nil, err\n+\t\t}\n+\t}\n+\treturn tc, nil\n+}\n+\n // tcpKeepAliveListener sets TCP keep-alive timeouts on accepted\n // connections. It's used by ListenAndServe and ListenAndServeTLS so\n // dead TCP connections (e.g. closing laptop mid-download) eventually\n // go away.\n //\n // Copied from Go 1.7.2 net/http/server.go\n-type tcpKeepAliveListener struct {\n-\t*net.TCPListener\n+type tcpKeepAliveHandler struct {\n+}\n+\n+func (ln tcpKeepAliveHandler) init(conn *net.TCPConn) error {\n+\tconn.SetKeepAlive(true)\n+\tconn.SetKeepAlivePeriod(defaultKeepAlivePeriod)\n+\treturn nil\n+}\n+\n+func (ln tcpKeepAliveHandler) wrap(conn net.Conn) (net.Conn, error) {\n+\treturn conn, nil\n }\n \n-func (ln tcpKeepAliveListener) Accept() (net.Conn, error) {\n-\ttc, err := ln.AcceptTCP()\n+// ipBasedLimitListener accepts at most n simultaneous connections from a\n+// given IP address. We also limit the total simultaneous connections.\n+// None of this is applied to localhost/loopback connections.\n+//\n+// Based on Go 1.7.4 net/netutil/listen.go\n+// TODO: (cheftako) : IPv6, CIDR Range rather than individual IP Addresses?\n+// Considered using atomic for per IP limit value,\n+// but problem initing map value safely\n+type ipBasedLimit struct {\n+\t// Map in which we map the serialized ip to the current count\n+\tlimits map[string]int\n+\n+\t// Current total count of the connections in existance (no localhost)\n+\ttotal int\n+\n+\t// Lock used to ensure the limits and total are updated atomicaly\n+\tmutex sync.Mutex\n+\n+\t// The maximum concurent connections an IP is allowed to have open\n+\tipMax int\n+\n+\t// The total maximum concurrent connections allowed open (Except localhost)\n+\tmax int\n+}\n+\n+func (i *ipBasedLimit) incrementIp(ipser string) error {\n+\tif i.max <= 0 {\n+\t\treturn nil\n+\t}\n+\tip := net.ParseIP(ipser)\n+\tif ip == nil {\n+\t\tglog.Infof(\"Received request from client with invalid ip %s\", ipser)\n+\t\treturn limitedError{ip: ipser}\n+\t}\n+\tif ip.IsLoopback() {\n+\t\treturn nil\n+\t}\n+\ti.mutex.Lock()\n+\tdefer i.mutex.Unlock()\n+\tcurrent := i.limits[ipser]\n+\tif current >= i.ipMax || i.total >= i.max {\n+\t\treturn limitedError{ip: ipser}\n+\t}\n+\ti.total++\n+\ti.limits[ipser] = current + 1\n+\treturn nil\n+}\n+\n+func (i *ipBasedLimit) decrementIp(ipser string) {\n+\tif i.max <= 0 {\n+\t\treturn\n+\t}\n+\tip := net.ParseIP(ipser)\n+\tif ip == nil || ip.IsLoopback() {\n+\t\treturn\n+\t}\n+\ti.mutex.Lock()\n+\tdefer i.mutex.Unlock()\n+\ti.total--\n+\tcurrent := i.limits[ipser]\n+\tif current > 1 {\n+\t\ti.limits[ipser] = current - 1\n+\t} else {\n+\t\tglog.Infof(\"Cleaned up all references for IP %s\", ipser)\n+\t\tdelete(i.limits, ipser)\n+\t}\n+}\n+\n+// The actual structure/handler which attaches the limiting code to a connection\n+type ipBasedLimitHandler struct {\n+\t// The limit structure for this connection type.\n+\tipLimit *ipBasedLimit\n+}\n+\n+func (h ipBasedLimitHandler) init(conn *net.TCPConn) error {\n+\treturn nil\n+}\n+\n+func (h ipBasedLimitHandler) wrap(conn net.Conn) (net.Conn, error) {\n+\tip, _, err := net.SplitHostPort(conn.RemoteAddr().String())\n \tif err != nil {\n+\t\tconn.Close() // Not decrementing as we have not yet incremented\n+\t\tglog.Infof(\"Something went wrong getting the remote address %v\", err)\n \t\treturn nil, err\n \t}\n-\ttc.SetKeepAlive(true)\n-\ttc.SetKeepAlivePeriod(defaultKeepAlivePeriod)\n-\treturn tc, nil\n+\tlocal := conn.LocalAddr().String()\n+\tglog.Infof(\"Entered ip based limit Accept() for IP %s on %s\", ip, local)\n+\terr = h.ipLimit.incrementIp(ip)\n+\tif err != nil {\n+\t\tconn.Close() // Not decrementing as we did not successfully increment\n+\t\tglog.Infof(\"Denied accept to IP %s\", ip)","path":"pkg/genericapiserver/server/serve.go","position":194,"original_position":194,"commit_id":"81606b67144bda83f15c38ff20d04fd88825c9db","original_commit_id":"81606b67144bda83f15c38ff20d04fd88825c9db","user":{"login":"liggitt","id":980082,"avatar_url":"https://avatars.githubusercontent.com/u/980082?v=3","gravatar_id":"","url":"https://api.github.com/users/liggitt","html_url":"https://github.com/liggitt","followers_url":"https://api.github.com/users/liggitt/followers","following_url":"https://api.github.com/users/liggitt/following{/other_user}","gists_url":"https://api.github.com/users/liggitt/gists{/gist_id}","starred_url":"https://api.github.com/users/liggitt/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/liggitt/subscriptions","organizations_url":"https://api.github.com/users/liggitt/orgs","repos_url":"https://api.github.com/users/liggitt/repos","events_url":"https://api.github.com/users/liggitt/events{/privacy}","received_events_url":"https://api.github.com/users/liggitt/received_events","type":"User","site_admin":false},"body":"so this does a straight connection close, with no http or API response? that seems wrong. have we verified what control plane components do when they get this response? there is existing logic (though I'm not sure how well tested) that tries to behave well in the presence of 429 errors, but I'm not sure what clients would do with this","created_at":"2017-01-24T14:25:24Z","updated_at":"2017-01-24T14:25:24Z","html_url":"https://github.com/kubernetes/kubernetes/pull/39901#discussion_r97556224","pull_request_url":"https://api.github.com/repos/kubernetes/kubernetes/pulls/39901","_links":{"self":{"href":"https://api.github.com/repos/kubernetes/kubernetes/pulls/comments/97556224"},"html":{"href":"https://github.com/kubernetes/kubernetes/pull/39901#discussion_r97556224"},"pull_request":{"href":"https://api.github.com/repos/kubernetes/kubernetes/pulls/39901"}}},"pull_request":{"url":"https://api.github.com/repos/kubernetes/kubernetes/pulls/39901","id":101539580,"html_url":"https://github.com/kubernetes/kubernetes/pull/39901","diff_url":"https://github.com/kubernetes/kubernetes/pull/39901.diff","patch_url":"https://github.com/kubernetes/kubernetes/pull/39901.patch","issue_url":"https://api.github.com/repos/kubernetes/kubernetes/issues/39901","number":39901,"state":"open","locked":false,"title":"Working on an IP based connection limit to harden APIServer.","user":{"login":"cheftako","id":947971,"avatar_url":"https://avatars.githubusercontent.com/u/947971?v=3","gravatar_id":"","url":"https://api.github.com/users/cheftako","html_url":"https://github.com/cheftako","followers_url":"https://api.github.com/users/cheftako/followers","following_url":"https://api.github.com/users/cheftako/following{/other_user}","gists_url":"https://api.github.com/users/cheftako/gists{/gist_id}","starred_url":"https://api.github.com/users/cheftako/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/cheftako/subscriptions","organizations_url":"https://api.github.com/users/cheftako/orgs","repos_url":"https://api.github.com/users/cheftako/repos","events_url":"https://api.github.com/users/cheftako/events{/privacy}","received_events_url":"https://api.github.com/users/cheftako/received_events","type":"User","site_admin":false},"body":"Implemented a chain handler for handling the connection options.\r\n(keep-alive, ip-limiting, ...)\r\nAdded white list functionality for loopback.\r\nAdded a total count of connections limit as well.\r\nPut the IpLimit into ServingInfo and set up via config options.\r\nAdded tests to cover the basic behaviors of IpBaseLimit functionality.\r\nAdded parrallel access tests for IPBasedLimit\r\nPutting in the generated files.\r\n\r\n**Release note**:\r\nFormerly the APIServer only limited requests after the SSL handshake. \r\nThis fix is not applied to localhost or non SSL connections. \r\nThe limit is applied on both a total and per IP basis.\r\nThe IP limit is the smaller of either half the write or read limit.\r\nThe total limit is the sum of the write and read limits.\r\n","created_at":"2017-01-14T00:17:33Z","updated_at":"2017-01-24T14:25:24Z","closed_at":null,"merged_at":null,"merge_commit_sha":"1023170b3b242f60d8f8b9fae1d0b01db249e352","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/39901/commits","review_comments_url":"https://api.github.com/repos/kubernetes/kubernetes/pulls/39901/comments","review_comment_url":"https://api.github.com/repos/kubernetes/kubernetes/pulls/comments{/number}","comments_url":"https://api.github.com/repos/kubernetes/kubernetes/issues/39901/comments","statuses_url":"https://api.github.com/repos/kubernetes/kubernetes/statuses/81606b67144bda83f15c38ff20d04fd88825c9db","head":{"label":"cheftako:ipLimit","ref":"ipLimit","sha":"81606b67144bda83f15c38ff20d04fd88825c9db","user":{"login":"cheftako","id":947971,"avatar_url":"https://avatars.githubusercontent.com/u/947971?v=3","gravatar_id":"","url":"https://api.github.com/users/cheftako","html_url":"https://github.com/cheftako","followers_url":"https://api.github.com/users/cheftako/followers","following_url":"https://api.github.com/users/cheftako/following{/other_user}","gists_url":"https://api.github.com/users/cheftako/gists{/gist_id}","starred_url":"https://api.github.com/users/cheftako/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/cheftako/subscriptions","organizations_url":"https://api.github.com/users/cheftako/orgs","repos_url":"https://api.github.com/users/cheftako/repos","events_url":"https://api.github.com/users/cheftako/events{/privacy}","received_events_url":"https://api.github.com/users/cheftako/received_events","type":"User","site_admin":false},"repo":{"id":78583769,"name":"kubernetes","full_name":"cheftako/kubernetes","owner":{"login":"cheftako","id":947971,"avatar_url":"https://avatars.githubusercontent.com/u/947971?v=3","gravatar_id":"","url":"https://api.github.com/users/cheftako","html_url":"https://github.com/cheftako","followers_url":"https://api.github.com/users/cheftako/followers","following_url":"https://api.github.com/users/cheftako/following{/other_user}","gists_url":"https://api.github.com/users/cheftako/gists{/gist_id}","starred_url":"https://api.github.com/users/cheftako/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/cheftako/subscriptions","organizations_url":"https://api.github.com/users/cheftako/orgs","repos_url":"https://api.github.com/users/cheftako/repos","events_url":"https://api.github.com/users/cheftako/events{/privacy}","received_events_url":"https://api.github.com/users/cheftako/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/cheftako/kubernetes","description":"Production-Grade Container Scheduling and Management","fork":true,"url":"https://api.github.com/repos/cheftako/kubernetes","forks_url":"https://api.github.com/repos/cheftako/kubernetes/forks","keys_url":"https://api.github.com/repos/cheftako/kubernetes/keys{/key_id}","collaborators_url":"https://api.github.com/repos/cheftako/kubernetes/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/cheftako/kubernetes/teams","hooks_url":"https://api.github.com/repos/cheftako/kubernetes/hooks","issue_events_url":"https://api.github.com/repos/cheftako/kubernetes/issues/events{/number}","events_url":"https://api.github.com/repos/cheftako/kubernetes/events","assignees_url":"https://api.github.com/repos/cheftako/kubernetes/assignees{/user}","branches_url":"https://api.github.com/repos/cheftako/kubernetes/branches{/branch}","tags_url":"https://api.github.com/repos/cheftako/kubernetes/tags","blobs_url":"https://api.github.com/repos/cheftako/kubernetes/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/cheftako/kubernetes/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/cheftako/kubernetes/git/refs{/sha}","trees_url":"https://api.github.com/repos/cheftako/kubernetes/git/trees{/sha}","statuses_url":"https://api.github.com/repos/cheftako/kubernetes/statuses/{sha}","languages_url":"https://api.github.com/repos/cheftako/kubernetes/languages","stargazers_url":"https://api.github.com/repos/cheftako/kubernetes/stargazers","contributors_url":"https://api.github.com/repos/cheftako/kubernetes/contributors","subscribers_url":"https://api.github.com/repos/cheftako/kubernetes/subscribers","subscription_url":"https://api.github.com/repos/cheftako/kubernetes/subscription","commits_url":"https://api.github.com/repos/cheftako/kubernetes/commits{/sha}","git_commits_url":"https://api.github.com/repos/cheftako/kubernetes/git/commits{/sha}","comments_url":"https://api.github.com/repos/cheftako/kubernetes/comments{/number}","issue_comment_url":"https://api.github.com/repos/cheftako/kubernetes/issues/comments{/number}","contents_url":"https://api.github.com/repos/cheftako/kubernetes/contents/{+path}","compare_url":"https://api.github.com/repos/cheftako/kubernetes/compare/{base}...{head}","merges_url":"https://api.github.com/repos/cheftako/kubernetes/merges","archive_url":"https://api.github.com/repos/cheftako/kubernetes/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/cheftako/kubernetes/downloads","issues_url":"https://api.github.com/repos/cheftako/kubernetes/issues{/number}","pulls_url":"https://api.github.com/repos/cheftako/kubernetes/pulls{/number}","milestones_url":"https://api.github.com/repos/cheftako/kubernetes/milestones{/number}","notifications_url":"https://api.github.com/repos/cheftako/kubernetes/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/cheftako/kubernetes/labels{/name}","releases_url":"https://api.github.com/repos/cheftako/kubernetes/releases{/id}","deployments_url":"https://api.github.com/repos/cheftako/kubernetes/deployments","created_at":"2017-01-10T23:21:46Z","updated_at":"2017-01-10T23:22:28Z","pushed_at":"2017-01-19T20:53:21Z","git_url":"git://github.com/cheftako/kubernetes.git","ssh_url":"git@github.com:cheftako/kubernetes.git","clone_url":"https://github.com/cheftako/kubernetes.git","svn_url":"https://github.com/cheftako/kubernetes","homepage":"http://kubernetes.io","size":389884,"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":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"kubernetes:master","ref":"master","sha":"cc2250cb982091bf72822e168296f34dc038c271","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":"2017-01-24T12:56:12Z","pushed_at":"2017-01-24T14:25:23Z","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":513027,"stargazers_count":20105,"watchers_count":20105,"language":"Go","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":true,"forks_count":6826,"mirror_url":null,"open_issues_count":5532,"forks":6826,"open_issues":5532,"watchers":20105,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/kubernetes/kubernetes/pulls/39901"},"html":{"href":"https://github.com/kubernetes/kubernetes/pull/39901"},"issue":{"href":"https://api.github.com/repos/kubernetes/kubernetes/issues/39901"},"comments":{"href":"https://api.github.com/repos/kubernetes/kubernetes/issues/39901/comments"},"review_comments":{"href":"https://api.github.com/repos/kubernetes/kubernetes/pulls/39901/comments"},"review_comment":{"href":"https://api.github.com/repos/kubernetes/kubernetes/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/kubernetes/kubernetes/pulls/39901/commits"},"statuses":{"href":"https://api.github.com/repos/kubernetes/kubernetes/statuses/81606b67144bda83f15c38ff20d04fd88825c9db"}}}}
{"actor":{"display_login":"liggitt"}}