{"action":"created","comment":{"url":"https://api.github.com/repos/envoyproxy/envoy/pulls/comments/209769684","pull_request_review_id":145848299,"id":209769684,"node_id":"MDI0OlB1bGxSZXF1ZXN0UmV2aWV3Q29tbWVudDIwOTc2OTY4NA==","diff_hunk":"@@ -226,32 +149,187 @@ TEST_F(MainCommonTest, LegacyMain) {\n   EXPECT_EQ(EXIT_SUCCESS, return_code);\n }\n \n-TEST_F(MainCommonTest, AdminRequestGetStatsAndQuit) {\n+class AdminRequestTest : public MainCommonTest {\n+protected:\n+  AdminRequestTest()\n+      : envoy_return_(false), envoy_started_(false), envoy_finished_(false),\n+        pause_before_run_(false), pause_after_run_(false) {\n+    addArg(\"--disable-hot-restart\");\n+  }\n+\n+  // Runs a an admin request specified in path, blocking until completion, and\n+  // returning the response body.\n+  std::string adminRequest(absl::string_view path, absl::string_view method) {\n+    absl::Notification done;\n+    std::string out;\n+    main_common_->adminRequest(\n+        path, method,\n+        [&done, &out](const Http::HeaderMap& /*response_headers*/, absl::string_view body) {\n+          out = std::string(body);\n+          done.Notify();\n+        });\n+    done.WaitForNotification();\n+    return out;\n+  }\n+\n+  // Initiates Envoy running in its own thread.\n+  void startEnvoy() {\n+    envoy_thread_ = std::make_unique<Thread::Thread>([this]() {\n+      // Note: main_common_ is accesesed in the testing thread, but\n+      // is race-free, as MainCommon::run() does not return until\n+      // triggered with an adminRequest POST to /quitquitquit, which\n+      // is done in the testing thread.\n+      main_common_ = std::make_unique<MainCommon>(argc(), argv());\n+      envoy_started_ = true;\n+      started_.Notify();\n+      pauseResumeInterlock(pause_before_run_);\n+      bool status = main_common_->run();\n+      pauseResumeInterlock(pause_after_run_);\n+      main_common_.reset();\n+      envoy_finished_ = true;\n+      envoy_return_ = status;\n+      finished_.Notify();\n+    });\n+  }\n+\n+  // Conditionally pauses at a critical point in the Envoy thread waiting, waiting\n+  // the for the test thread to try something. The test thread can then call resume_.Notify()\n+  // to allow the Envoy thread to resume.\n+  void pauseResumeInterlock(bool enable) {\n+    if (enable) {\n+      pause_point_.Notify();\n+      resume_.WaitForNotification();\n+    }\n+  }\n+\n+  // Having triggered Envoy to quit (via signal or /quitquitquit), this blocks until Envoy exits.\n+  bool waitForEnvoyToExit() {\n+    finished_.WaitForNotification();\n+    envoy_thread_->join();\n+    return envoy_return_;\n+  }\n+\n+  std::unique_ptr<Thread::Thread> envoy_thread_;\n+  std::unique_ptr<MainCommon> main_common_;\n+  absl::Notification started_;\n+  absl::Notification finished_;\n+  absl::Notification resume_;\n+  absl::Notification pause_point_;\n+  bool envoy_return_;\n+  bool envoy_started_;\n+  bool envoy_finished_;\n+  bool pause_before_run_;\n+  bool pause_after_run_;\n+};\n+\n+TEST_F(AdminRequestTest, AdminRequestGetStatsAndQuit) {\n   if (!Envoy::TestEnvironment::shouldRunTestForIpVersion(Network::Address::IpVersion::v4)) {\n     return;\n   }\n-  addArg(\"--disable-hot-restart\");\n   startEnvoy();\n-  waitForEnvoyToStart();\n+  started_.WaitForNotification();\n   EXPECT_THAT(adminRequest(\"/stats\", \"GET\"), HasSubstr(\"filesystem.reopen_failed\"));\n   adminRequest(\"/quitquitquit\", \"POST\");\n   EXPECT_TRUE(waitForEnvoyToExit());\n }\n \n // This test is identical to the above one, except that instead of using an admin /quitquitquit,\n // we send ourselves a SIGTERM, which should have the same effect.\n-TEST_F(MainCommonTest, AdminRequestGetStatsAndKill) {\n+TEST_F(AdminRequestTest, AdminRequestGetStatsAndKill) {\n   if (!Envoy::TestEnvironment::shouldRunTestForIpVersion(Network::Address::IpVersion::v4)) {\n     return;\n   }\n-  addArg(\"--disable-hot-restart\");\n   startEnvoy();\n-  waitForEnvoyToStart();\n+  started_.WaitForNotification();\n   EXPECT_THAT(adminRequest(\"/stats\", \"GET\"), HasSubstr(\"filesystem.reopen_failed\"));\n   kill(getpid(), SIGTERM);\n   EXPECT_TRUE(waitForEnvoyToExit());\n }\n \n+TEST_F(AdminRequestTest, AdminRequestBeforeRun) {\n+  if (!Envoy::TestEnvironment::shouldRunTestForIpVersion(Network::Address::IpVersion::v4)) {\n+    return;\n+  }\n+  // Induce the situation where the Envoy thread is active, and main_common_ is constructed,\n+  // but run() hasn't been issued yet. AdminRequests will not finish immediately, but will\n+  // do so at some point after run() is allowed to start.\n+  pause_before_run_ = true;\n+  startEnvoy();\n+  pause_point_.WaitForNotification();\n+\n+  bool admin_handler_was_called = false;\n+  std::string out;\n+  main_common_->adminRequest(\n+      \"/stats\", \"GET\",\n+      [&admin_handler_was_called, &out](const Http::HeaderMap& /*response_headers*/,\n+                                        absl::string_view body) {\n+        admin_handler_was_called = true;\n+        out = std::string(body);\n+      });\n+\n+  // The admin handler can't be called until after we let run() go.\n+  EXPECT_FALSE(admin_handler_was_called);\n+\n+  // Now unblock the envoy thread so it can wake up and process outstanding posts.\n+  resume_.Notify();\n+\n+  // We don't get a notification when run(), so it's not safe to check whether the\n+  // admin handler is called until after we quit.\n+  adminRequest(\"/quitquitquit\", \"POST\");\n+  EXPECT_TRUE(waitForEnvoyToExit());\n+  EXPECT_TRUE(admin_handler_was_called);\n+  EXPECT_THAT(out, HasSubstr(\"filesystem.reopen_failed\"));\n+}\n+\n+// Class to track whether an object has been destroyed, which it does by bumping an atomic.\n+class DestroyCounter {\n+public:\n+  explicit DestroyCounter(std::atomic<uint64_t>& destroy_count) : destroy_count_(destroy_count) {}","path":"test/exe/main_common_test.cc","position":null,"original_position":259,"commit_id":"4dfafe89fc35d7a9055a65b2b7b79e9a83fe898f","original_commit_id":"4d7644387e242de1964e7948dc828e5a41fc7c12","user":{"login":"jmarantz","id":1942589,"node_id":"MDQ6VXNlcjE5NDI1ODk=","avatar_url":"https://avatars3.githubusercontent.com/u/1942589?v=4","gravatar_id":"","url":"https://api.github.com/users/jmarantz","html_url":"https://github.com/jmarantz","followers_url":"https://api.github.com/users/jmarantz/followers","following_url":"https://api.github.com/users/jmarantz/following{/other_user}","gists_url":"https://api.github.com/users/jmarantz/gists{/gist_id}","starred_url":"https://api.github.com/users/jmarantz/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jmarantz/subscriptions","organizations_url":"https://api.github.com/users/jmarantz/orgs","repos_url":"https://api.github.com/users/jmarantz/repos","events_url":"https://api.github.com/users/jmarantz/events{/privacy}","received_events_url":"https://api.github.com/users/jmarantz/received_events","type":"User","site_admin":false},"body":"Added comment.","created_at":"2018-08-13T21:45:08Z","updated_at":"2018-08-13T21:52:38Z","html_url":"https://github.com/envoyproxy/envoy/pull/4114#discussion_r209769684","pull_request_url":"https://api.github.com/repos/envoyproxy/envoy/pulls/4114","author_association":"CONTRIBUTOR","_links":{"self":{"href":"https://api.github.com/repos/envoyproxy/envoy/pulls/comments/209769684"},"html":{"href":"https://github.com/envoyproxy/envoy/pull/4114#discussion_r209769684"},"pull_request":{"href":"https://api.github.com/repos/envoyproxy/envoy/pulls/4114"}},"in_reply_to_id":209760635},"pull_request":{"url":"https://api.github.com/repos/envoyproxy/envoy/pulls/4114","id":207783999,"node_id":"MDExOlB1bGxSZXF1ZXN0MjA3NzgzOTk5","html_url":"https://github.com/envoyproxy/envoy/pull/4114","diff_url":"https://github.com/envoyproxy/envoy/pull/4114.diff","patch_url":"https://github.com/envoyproxy/envoy/pull/4114.patch","issue_url":"https://api.github.com/repos/envoyproxy/envoy/issues/4114","number":4114,"state":"open","locked":false,"title":"Add tests for corner-cases around sending requests before run() starts or after run() ends.","user":{"login":"jmarantz","id":1942589,"node_id":"MDQ6VXNlcjE5NDI1ODk=","avatar_url":"https://avatars3.githubusercontent.com/u/1942589?v=4","gravatar_id":"","url":"https://api.github.com/users/jmarantz","html_url":"https://github.com/jmarantz","followers_url":"https://api.github.com/users/jmarantz/followers","following_url":"https://api.github.com/users/jmarantz/following{/other_user}","gists_url":"https://api.github.com/users/jmarantz/gists{/gist_id}","starred_url":"https://api.github.com/users/jmarantz/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jmarantz/subscriptions","organizations_url":"https://api.github.com/users/jmarantz/orgs","repos_url":"https://api.github.com/users/jmarantz/repos","events_url":"https://api.github.com/users/jmarantz/events{/privacy}","received_events_url":"https://api.github.com/users/jmarantz/received_events","type":"User","site_admin":false},"body":"Signed-off-by: Joshua Marantz <jmarantz@google.com>\r\n\r\n*Description*: There was concern around wanting to understand exactly what happens if an adminRequest gets issued prior run() starting, or right after run() ends.\r\n\r\n*Risk Level*: none (just new tests)\r\n*Testing*: //test/exe:main_common_test\r\n*Docs Changes*: n/a\r\n*Release Notes*: n/a\r\n\r\n","created_at":"2018-08-11T17:14:00Z","updated_at":"2018-08-13T21:52:38Z","closed_at":null,"merged_at":null,"merge_commit_sha":"92b318d0afae9118e0d76b7c8692b0e1e977812f","assignee":{"login":"dnoe","id":1183641,"node_id":"MDQ6VXNlcjExODM2NDE=","avatar_url":"https://avatars2.githubusercontent.com/u/1183641?v=4","gravatar_id":"","url":"https://api.github.com/users/dnoe","html_url":"https://github.com/dnoe","followers_url":"https://api.github.com/users/dnoe/followers","following_url":"https://api.github.com/users/dnoe/following{/other_user}","gists_url":"https://api.github.com/users/dnoe/gists{/gist_id}","starred_url":"https://api.github.com/users/dnoe/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dnoe/subscriptions","organizations_url":"https://api.github.com/users/dnoe/orgs","repos_url":"https://api.github.com/users/dnoe/repos","events_url":"https://api.github.com/users/dnoe/events{/privacy}","received_events_url":"https://api.github.com/users/dnoe/received_events","type":"User","site_admin":false},"assignees":[{"login":"dnoe","id":1183641,"node_id":"MDQ6VXNlcjExODM2NDE=","avatar_url":"https://avatars2.githubusercontent.com/u/1183641?v=4","gravatar_id":"","url":"https://api.github.com/users/dnoe","html_url":"https://github.com/dnoe","followers_url":"https://api.github.com/users/dnoe/followers","following_url":"https://api.github.com/users/dnoe/following{/other_user}","gists_url":"https://api.github.com/users/dnoe/gists{/gist_id}","starred_url":"https://api.github.com/users/dnoe/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dnoe/subscriptions","organizations_url":"https://api.github.com/users/dnoe/orgs","repos_url":"https://api.github.com/users/dnoe/repos","events_url":"https://api.github.com/users/dnoe/events{/privacy}","received_events_url":"https://api.github.com/users/dnoe/received_events","type":"User","site_admin":false}],"requested_reviewers":[],"requested_teams":[],"labels":[],"milestone":null,"commits_url":"https://api.github.com/repos/envoyproxy/envoy/pulls/4114/commits","review_comments_url":"https://api.github.com/repos/envoyproxy/envoy/pulls/4114/comments","review_comment_url":"https://api.github.com/repos/envoyproxy/envoy/pulls/comments{/number}","comments_url":"https://api.github.com/repos/envoyproxy/envoy/issues/4114/comments","statuses_url":"https://api.github.com/repos/envoyproxy/envoy/statuses/4dfafe89fc35d7a9055a65b2b7b79e9a83fe898f","head":{"label":"jmarantz:post-across-main-common","ref":"post-across-main-common","sha":"4dfafe89fc35d7a9055a65b2b7b79e9a83fe898f","user":{"login":"jmarantz","id":1942589,"node_id":"MDQ6VXNlcjE5NDI1ODk=","avatar_url":"https://avatars3.githubusercontent.com/u/1942589?v=4","gravatar_id":"","url":"https://api.github.com/users/jmarantz","html_url":"https://github.com/jmarantz","followers_url":"https://api.github.com/users/jmarantz/followers","following_url":"https://api.github.com/users/jmarantz/following{/other_user}","gists_url":"https://api.github.com/users/jmarantz/gists{/gist_id}","starred_url":"https://api.github.com/users/jmarantz/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jmarantz/subscriptions","organizations_url":"https://api.github.com/users/jmarantz/orgs","repos_url":"https://api.github.com/users/jmarantz/repos","events_url":"https://api.github.com/users/jmarantz/events{/privacy}","received_events_url":"https://api.github.com/users/jmarantz/received_events","type":"User","site_admin":false},"repo":{"id":109787330,"node_id":"MDEwOlJlcG9zaXRvcnkxMDk3ODczMzA=","name":"envoy","full_name":"jmarantz/envoy","owner":{"login":"jmarantz","id":1942589,"node_id":"MDQ6VXNlcjE5NDI1ODk=","avatar_url":"https://avatars3.githubusercontent.com/u/1942589?v=4","gravatar_id":"","url":"https://api.github.com/users/jmarantz","html_url":"https://github.com/jmarantz","followers_url":"https://api.github.com/users/jmarantz/followers","following_url":"https://api.github.com/users/jmarantz/following{/other_user}","gists_url":"https://api.github.com/users/jmarantz/gists{/gist_id}","starred_url":"https://api.github.com/users/jmarantz/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jmarantz/subscriptions","organizations_url":"https://api.github.com/users/jmarantz/orgs","repos_url":"https://api.github.com/users/jmarantz/repos","events_url":"https://api.github.com/users/jmarantz/events{/privacy}","received_events_url":"https://api.github.com/users/jmarantz/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/jmarantz/envoy","description":"C++ front/service proxy","fork":true,"url":"https://api.github.com/repos/jmarantz/envoy","forks_url":"https://api.github.com/repos/jmarantz/envoy/forks","keys_url":"https://api.github.com/repos/jmarantz/envoy/keys{/key_id}","collaborators_url":"https://api.github.com/repos/jmarantz/envoy/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/jmarantz/envoy/teams","hooks_url":"https://api.github.com/repos/jmarantz/envoy/hooks","issue_events_url":"https://api.github.com/repos/jmarantz/envoy/issues/events{/number}","events_url":"https://api.github.com/repos/jmarantz/envoy/events","assignees_url":"https://api.github.com/repos/jmarantz/envoy/assignees{/user}","branches_url":"https://api.github.com/repos/jmarantz/envoy/branches{/branch}","tags_url":"https://api.github.com/repos/jmarantz/envoy/tags","blobs_url":"https://api.github.com/repos/jmarantz/envoy/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/jmarantz/envoy/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/jmarantz/envoy/git/refs{/sha}","trees_url":"https://api.github.com/repos/jmarantz/envoy/git/trees{/sha}","statuses_url":"https://api.github.com/repos/jmarantz/envoy/statuses/{sha}","languages_url":"https://api.github.com/repos/jmarantz/envoy/languages","stargazers_url":"https://api.github.com/repos/jmarantz/envoy/stargazers","contributors_url":"https://api.github.com/repos/jmarantz/envoy/contributors","subscribers_url":"https://api.github.com/repos/jmarantz/envoy/subscribers","subscription_url":"https://api.github.com/repos/jmarantz/envoy/subscription","commits_url":"https://api.github.com/repos/jmarantz/envoy/commits{/sha}","git_commits_url":"https://api.github.com/repos/jmarantz/envoy/git/commits{/sha}","comments_url":"https://api.github.com/repos/jmarantz/envoy/comments{/number}","issue_comment_url":"https://api.github.com/repos/jmarantz/envoy/issues/comments{/number}","contents_url":"https://api.github.com/repos/jmarantz/envoy/contents/{+path}","compare_url":"https://api.github.com/repos/jmarantz/envoy/compare/{base}...{head}","merges_url":"https://api.github.com/repos/jmarantz/envoy/merges","archive_url":"https://api.github.com/repos/jmarantz/envoy/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/jmarantz/envoy/downloads","issues_url":"https://api.github.com/repos/jmarantz/envoy/issues{/number}","pulls_url":"https://api.github.com/repos/jmarantz/envoy/pulls{/number}","milestones_url":"https://api.github.com/repos/jmarantz/envoy/milestones{/number}","notifications_url":"https://api.github.com/repos/jmarantz/envoy/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/jmarantz/envoy/labels{/name}","releases_url":"https://api.github.com/repos/jmarantz/envoy/releases{/id}","deployments_url":"https://api.github.com/repos/jmarantz/envoy/deployments","created_at":"2017-11-07T04:36:02Z","updated_at":"2018-08-13T14:46:05Z","pushed_at":"2018-08-13T21:51:45Z","git_url":"git://github.com/jmarantz/envoy.git","ssh_url":"git@github.com:jmarantz/envoy.git","clone_url":"https://github.com/jmarantz/envoy.git","svn_url":"https://github.com/jmarantz/envoy","homepage":"https://envoyproxy.github.io/","size":60581,"stargazers_count":0,"watchers_count":0,"language":"C++","has_issues":false,"has_projects":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":0,"mirror_url":null,"archived":false,"open_issues_count":0,"license":{"key":"apache-2.0","name":"Apache License 2.0","spdx_id":"Apache-2.0","url":"https://api.github.com/licenses/apache-2.0","node_id":"MDc6TGljZW5zZTI="},"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"envoyproxy:master","ref":"master","sha":"1381673ad2d74bab4667942abdd8ef75c812e75e","user":{"login":"envoyproxy","id":30125649,"node_id":"MDEyOk9yZ2FuaXphdGlvbjMwMTI1NjQ5","avatar_url":"https://avatars1.githubusercontent.com/u/30125649?v=4","gravatar_id":"","url":"https://api.github.com/users/envoyproxy","html_url":"https://github.com/envoyproxy","followers_url":"https://api.github.com/users/envoyproxy/followers","following_url":"https://api.github.com/users/envoyproxy/following{/other_user}","gists_url":"https://api.github.com/users/envoyproxy/gists{/gist_id}","starred_url":"https://api.github.com/users/envoyproxy/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/envoyproxy/subscriptions","organizations_url":"https://api.github.com/users/envoyproxy/orgs","repos_url":"https://api.github.com/users/envoyproxy/repos","events_url":"https://api.github.com/users/envoyproxy/events{/privacy}","received_events_url":"https://api.github.com/users/envoyproxy/received_events","type":"Organization","site_admin":false},"repo":{"id":65214191,"node_id":"MDEwOlJlcG9zaXRvcnk2NTIxNDE5MQ==","name":"envoy","full_name":"envoyproxy/envoy","owner":{"login":"envoyproxy","id":30125649,"node_id":"MDEyOk9yZ2FuaXphdGlvbjMwMTI1NjQ5","avatar_url":"https://avatars1.githubusercontent.com/u/30125649?v=4","gravatar_id":"","url":"https://api.github.com/users/envoyproxy","html_url":"https://github.com/envoyproxy","followers_url":"https://api.github.com/users/envoyproxy/followers","following_url":"https://api.github.com/users/envoyproxy/following{/other_user}","gists_url":"https://api.github.com/users/envoyproxy/gists{/gist_id}","starred_url":"https://api.github.com/users/envoyproxy/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/envoyproxy/subscriptions","organizations_url":"https://api.github.com/users/envoyproxy/orgs","repos_url":"https://api.github.com/users/envoyproxy/repos","events_url":"https://api.github.com/users/envoyproxy/events{/privacy}","received_events_url":"https://api.github.com/users/envoyproxy/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/envoyproxy/envoy","description":"C++ front/service proxy","fork":false,"url":"https://api.github.com/repos/envoyproxy/envoy","forks_url":"https://api.github.com/repos/envoyproxy/envoy/forks","keys_url":"https://api.github.com/repos/envoyproxy/envoy/keys{/key_id}","collaborators_url":"https://api.github.com/repos/envoyproxy/envoy/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/envoyproxy/envoy/teams","hooks_url":"https://api.github.com/repos/envoyproxy/envoy/hooks","issue_events_url":"https://api.github.com/repos/envoyproxy/envoy/issues/events{/number}","events_url":"https://api.github.com/repos/envoyproxy/envoy/events","assignees_url":"https://api.github.com/repos/envoyproxy/envoy/assignees{/user}","branches_url":"https://api.github.com/repos/envoyproxy/envoy/branches{/branch}","tags_url":"https://api.github.com/repos/envoyproxy/envoy/tags","blobs_url":"https://api.github.com/repos/envoyproxy/envoy/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/envoyproxy/envoy/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/envoyproxy/envoy/git/refs{/sha}","trees_url":"https://api.github.com/repos/envoyproxy/envoy/git/trees{/sha}","statuses_url":"https://api.github.com/repos/envoyproxy/envoy/statuses/{sha}","languages_url":"https://api.github.com/repos/envoyproxy/envoy/languages","stargazers_url":"https://api.github.com/repos/envoyproxy/envoy/stargazers","contributors_url":"https://api.github.com/repos/envoyproxy/envoy/contributors","subscribers_url":"https://api.github.com/repos/envoyproxy/envoy/subscribers","subscription_url":"https://api.github.com/repos/envoyproxy/envoy/subscription","commits_url":"https://api.github.com/repos/envoyproxy/envoy/commits{/sha}","git_commits_url":"https://api.github.com/repos/envoyproxy/envoy/git/commits{/sha}","comments_url":"https://api.github.com/repos/envoyproxy/envoy/comments{/number}","issue_comment_url":"https://api.github.com/repos/envoyproxy/envoy/issues/comments{/number}","contents_url":"https://api.github.com/repos/envoyproxy/envoy/contents/{+path}","compare_url":"https://api.github.com/repos/envoyproxy/envoy/compare/{base}...{head}","merges_url":"https://api.github.com/repos/envoyproxy/envoy/merges","archive_url":"https://api.github.com/repos/envoyproxy/envoy/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/envoyproxy/envoy/downloads","issues_url":"https://api.github.com/repos/envoyproxy/envoy/issues{/number}","pulls_url":"https://api.github.com/repos/envoyproxy/envoy/pulls{/number}","milestones_url":"https://api.github.com/repos/envoyproxy/envoy/milestones{/number}","notifications_url":"https://api.github.com/repos/envoyproxy/envoy/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/envoyproxy/envoy/labels{/name}","releases_url":"https://api.github.com/repos/envoyproxy/envoy/releases{/id}","deployments_url":"https://api.github.com/repos/envoyproxy/envoy/deployments","created_at":"2016-08-08T15:07:24Z","updated_at":"2018-08-13T21:30:46Z","pushed_at":"2018-08-13T21:51:47Z","git_url":"git://github.com/envoyproxy/envoy.git","ssh_url":"git@github.com:envoyproxy/envoy.git","clone_url":"https://github.com/envoyproxy/envoy.git","svn_url":"https://github.com/envoyproxy/envoy","homepage":"https://www.envoyproxy.io","size":18887,"stargazers_count":6180,"watchers_count":6180,"language":"C++","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":814,"mirror_url":null,"archived":false,"open_issues_count":325,"license":{"key":"apache-2.0","name":"Apache License 2.0","spdx_id":"Apache-2.0","url":"https://api.github.com/licenses/apache-2.0","node_id":"MDc6TGljZW5zZTI="},"forks":814,"open_issues":325,"watchers":6180,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/envoyproxy/envoy/pulls/4114"},"html":{"href":"https://github.com/envoyproxy/envoy/pull/4114"},"issue":{"href":"https://api.github.com/repos/envoyproxy/envoy/issues/4114"},"comments":{"href":"https://api.github.com/repos/envoyproxy/envoy/issues/4114/comments"},"review_comments":{"href":"https://api.github.com/repos/envoyproxy/envoy/pulls/4114/comments"},"review_comment":{"href":"https://api.github.com/repos/envoyproxy/envoy/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/envoyproxy/envoy/pulls/4114/commits"},"statuses":{"href":"https://api.github.com/repos/envoyproxy/envoy/statuses/4dfafe89fc35d7a9055a65b2b7b79e9a83fe898f"}},"author_association":"CONTRIBUTOR"}}
{"actor":{"display_login":"jmarantz"}}