{"action":"created","comment":{"url":"https://api.github.com/repos/envoyproxy/envoy/pulls/comments/197781426","pull_request_review_id":131584917,"id":197781426,"node_id":"MDI0OlB1bGxSZXF1ZXN0UmV2aWV3Q29tbWVudDE5Nzc4MTQyNg==","diff_hunk":"@@ -0,0 +1,433 @@\n+#include <chrono>\n+#include <memory>\n+#include <sstream>\n+\n+#include \"common/stats/stats_impl.h\"\n+\n+#include \"extensions/stat_sinks/hystrix/hystrix.h\"\n+\n+#include \"test/mocks/server/mocks.h\"\n+#include \"test/mocks/stats/mocks.h\"\n+#include \"test/mocks/upstream/mocks.h\"\n+#include \"test/test_common/utility.h\"\n+\n+#include \"absl/strings/str_split.h\"\n+#include \"gmock/gmock.h\"\n+#include \"gtest/gtest.h\"\n+\n+using testing::InSequence;\n+using testing::Invoke;\n+using testing::NiceMock;\n+using testing::Return;\n+using testing::_;\n+\n+namespace Envoy {\n+namespace Extensions {\n+namespace StatSinks {\n+namespace Hystrix {\n+\n+class ClusterTestInfo {\n+\n+public:\n+  ClusterTestInfo(const std::string cluster_name) : cluster_name_(cluster_name) {\n+    ON_CALL(cluster_, info()).WillByDefault(Return(cluster_info_ptr_));\n+    ON_CALL(*cluster_info_, name()).WillByDefault(testing::ReturnRefOfCopy(cluster_name_));\n+    ON_CALL(*cluster_info_, statsScope()).WillByDefault(ReturnRef(cluster_stats_scope_));\n+\n+    // Set gauge value.\n+    membership_total_gauge_.name_ = \"membership_total\";\n+    ON_CALL(cluster_stats_scope_, gauge(\"membership_total\"))\n+        .WillByDefault(ReturnRef(membership_total_gauge_));\n+    ON_CALL(membership_total_gauge_, value()).WillByDefault(Return(5));\n+\n+    // Attach counters.\n+    setCounterForTest(success_counter_, \"upstream_rq_2xx\");\n+    setCounterForTest(error_5xx_counter_, \"upstream_rq_5xx\");\n+    setCounterForTest(retry_5xx_counter_, \"retry.upstream_rq_5xx\");\n+    setCounterForTest(error_4xx_counter_, \"upstream_rq_4xx\");\n+    setCounterForTest(retry_4xx_counter_, \"retry.upstream_rq_4xx\");\n+    setCountersToZero();\n+  }\n+\n+  // Attach the counter to cluster_stat_scope and set default value.\n+  void setCounterForTest(NiceMock<Stats::MockCounter>& counter, std::string counter_name) {\n+    counter.name_ = counter_name;\n+    ON_CALL(cluster_stats_scope_, counter(counter_name)).WillByDefault(ReturnRef(counter));\n+  }\n+\n+  void setCountersToZero() {\n+    ON_CALL(error_5xx_counter_, value()).WillByDefault(Return(0));\n+    ON_CALL(retry_5xx_counter_, value()).WillByDefault(Return(0));\n+    ON_CALL(error_4xx_counter_, value()).WillByDefault(Return(0));\n+    ON_CALL(retry_4xx_counter_, value()).WillByDefault(Return(0));\n+    ON_CALL(success_counter_, value()).WillByDefault(Return(0));\n+  }\n+\n+  // Set counter return values to simulate traffic\n+  void setCounterReturnValues(const uint64_t i, const uint64_t success_step,\n+                              const uint64_t error_4xx_step, const uint64_t error_4xx_retry_step,\n+                              const uint64_t error_5xx_step, const uint64_t error_5xx_retry_step,\n+                              const uint64_t timeout_step, const uint64_t timeout_retry_step,\n+                              const uint64_t rejected_step) {\n+    ON_CALL(error_5xx_counter_, value()).WillByDefault(Return((i + 1) * error_5xx_step));\n+    ON_CALL(retry_5xx_counter_, value()).WillByDefault(Return((i + 1) * error_5xx_retry_step));\n+    ON_CALL(error_4xx_counter_, value()).WillByDefault(Return((i + 1) * error_4xx_step));\n+    ON_CALL(retry_4xx_counter_, value()).WillByDefault(Return((i + 1) * error_4xx_retry_step));\n+    ON_CALL(success_counter_, value()).WillByDefault(Return((i + 1) * success_step));\n+    cluster_info_->stats().upstream_rq_timeout_.add(timeout_step);\n+    cluster_info_->stats().upstream_rq_per_try_timeout_.add(timeout_retry_step);\n+    cluster_info_->stats().upstream_rq_pending_overflow_.add(rejected_step);\n+  }\n+\n+  NiceMock<Upstream::MockCluster> cluster_;\n+  Upstream::MockClusterInfo* cluster_info_ = new NiceMock<Upstream::MockClusterInfo>();\n+  Upstream::ClusterInfoConstSharedPtr cluster_info_ptr_{cluster_info_};\n+\n+  NiceMock<Stats::MockStore> stats_store_;\n+  NiceMock<Stats::MockStore> cluster_stats_scope_;\n+  const std::string cluster_name_;\n+\n+  NiceMock<Stats::MockGauge> membership_total_gauge_;\n+  NiceMock<Stats::MockCounter> success_counter_;\n+  NiceMock<Stats::MockCounter> error_5xx_counter_;\n+  NiceMock<Stats::MockCounter> retry_5xx_counter_;\n+  NiceMock<Stats::MockCounter> error_4xx_counter_;\n+  NiceMock<Stats::MockCounter> retry_4xx_counter_;\n+};\n+\n+class HystrixSinkTest : public testing::Test {\n+public:\n+  HystrixSinkTest() { sink_.reset(new HystrixSink(server_, window_size_)); }\n+\n+  // Return the value corresponding to key in an event stream.\n+  absl::string_view getStreamField(absl::string_view data_message, absl::string_view key) {\n+    absl::string_view::size_type key_pos = data_message.find(key);\n+    EXPECT_NE(absl::string_view::npos, key_pos);\n+    absl::string_view trim_data_before_Key = data_message.substr(key_pos);\n+    key_pos = trim_data_before_Key.find(\" \");\n+    EXPECT_NE(absl::string_view::npos, key_pos);\n+    absl::string_view trim_data_after_value = trim_data_before_Key.substr(key_pos + 1);\n+    key_pos = trim_data_after_value.find(\",\");\n+    EXPECT_NE(absl::string_view::npos, key_pos);\n+    absl::string_view actual = trim_data_after_value.substr(0, key_pos);\n+    return actual;\n+  }\n+\n+  // Return a string without quotes.\n+  absl::string_view getStringStreamField(absl::string_view data_message, absl::string_view key) {\n+    absl::string_view value = getStreamField(data_message, key);\n+    return value.substr(1, value.length() - 2);\n+  }\n+\n+  Buffer::OwnedImpl createClusterAndCallbacks() {\n+    // Set cluster.\n+    cluster_map_.emplace(cluster1_name_, cluster1_.cluster_);\n+    ON_CALL(server_, clusterManager()).WillByDefault(ReturnRef(cluster_manager_));\n+    ON_CALL(cluster_manager_, clusters()).WillByDefault(Return(cluster_map_));\n+\n+    Buffer::OwnedImpl buffer;\n+    auto encode_callback = [&buffer](Buffer::Instance& data, bool) {\n+      // Set callbacks to send data to buffer. This will append to the end of the buffer, so\n+      // multiple calls will all be dumped one after another into this buffer.\n+      buffer.add(data);\n+    };\n+    ON_CALL(callbacks_, encodeData(_, _)).WillByDefault(Invoke(encode_callback));\n+    return buffer;\n+  }\n+\n+  void addClusterToMap(const std::string& cluster_name, NiceMock<Upstream::MockCluster>& cluster) {\n+    cluster_map_.emplace(cluster_name, cluster);\n+    // Redefining since cluster_map_ is returned by value.\n+    ON_CALL(cluster_manager_, clusters()).WillByDefault(Return(cluster_map_));\n+  }\n+\n+  void removeClusterFromMap(const std::string& cluster_name) {\n+    cluster_map_.erase(cluster_name);\n+    // Redefining since cluster_map_ is returned by value.\n+    ON_CALL(cluster_manager_, clusters()).WillByDefault(Return(cluster_map_));\n+  }\n+\n+  void addSecondClusterHelper(Buffer::OwnedImpl& buffer) {\n+    buffer.drain(buffer.length());\n+    cluster2_.setCountersToZero();\n+    addClusterToMap(cluster2_name_, cluster2_.cluster_);\n+  }\n+\n+  std::unordered_map<std::string, std::string>\n+  addSecondClusterAndSendDataHelper(Buffer::OwnedImpl& buffer, const uint64_t success_step,\n+                                    const uint64_t error_step, const uint64_t timeout_step,\n+                                    const uint64_t success_step2, const uint64_t error_step2,\n+                                    const uint64_t timeout_step2) {\n+\n+    // Add new cluster.\n+    addSecondClusterHelper(buffer);\n+\n+    // Generate data to both clusters.\n+    for (uint64_t i = 0; i < (window_size_ + 1); i++) {\n+      buffer.drain(buffer.length());\n+      cluster1_.setCounterReturnValues(i, success_step, error_step, 0, 0, 0, timeout_step, 0, 0);\n+      cluster2_.setCounterReturnValues(i, success_step2, error_step2, 0, 0, 0, timeout_step2, 0, 0);\n+      sink_->flush(source_);\n+    }\n+\n+    return buildClusterMap(TestUtility::bufferToString(buffer));\n+  }\n+\n+  void removeSecondClusterHelper(Buffer::OwnedImpl& buffer) {\n+    buffer.drain(buffer.length());\n+    removeClusterFromMap(cluster2_name_);\n+    sink_->flush(source_);\n+  }\n+\n+  void validateResults(const std::string& data_message, uint64_t success_step, uint64_t error_step,\n+                       uint64_t timeout_step, uint64_t timeout_retry_step, uint64_t rejected_step,\n+                       uint64_t window_size) {\n+    EXPECT_EQ(getStreamField(data_message, \"rollingCountSemaphoreRejected\"),\n+              std::to_string(window_size * rejected_step));\n+    EXPECT_EQ(getStreamField(data_message, \"rollingCountSuccess\"),\n+              std::to_string(window_size * success_step));\n+    EXPECT_EQ(getStreamField(data_message, \"rollingCountTimeout\"),\n+              std::to_string(window_size * (timeout_step + timeout_retry_step)));\n+    EXPECT_EQ(getStreamField(data_message, \"errorCount\"),\n+              std::to_string(window_size *\n+                             (error_step - timeout_step))); // Note that on regular operation,\n+    // 5xx and timeout are raised together, so timeouts are reduced from 5xx count\n+    uint64_t total = error_step + success_step + rejected_step + timeout_retry_step;\n+    EXPECT_EQ(getStreamField(data_message, \"requestCount\"), std::to_string(window_size * total));\n+    if (total != 0) {\n+      EXPECT_EQ(\n+          getStreamField(data_message, \"errorPercentage\"),\n+          std::to_string(static_cast<uint64_t>(\n+              100 * (static_cast<double>(total - success_step) / static_cast<double>(total)))));\n+    } else {\n+      EXPECT_EQ(getStreamField(data_message, \"errorPercentage\"), \"0\");\n+    }\n+  }\n+\n+  std::unordered_map<std::string, std::string> buildClusterMap(absl::string_view data_message) {\n+    std::unordered_map<std::string, std::string> cluster_message_map;\n+    std::vector<std::string> messages =\n+        absl::StrSplit(data_message, \"data:\", absl::SkipWhitespace());\n+    for (auto message : messages) {\n+      if (absl::StrContains(getStreamField(message, \"type\"), \"HystrixCommand\")) {\n+        std::string cluster_name(getStringStreamField(message, \"name\"));\n+        cluster_message_map[cluster_name] = message;\n+      }\n+    }\n+    return cluster_message_map;\n+  }\n+\n+  TestRandomGenerator rand_;\n+  uint64_t window_size_ = rand_.random() % 10 + 5; // Arbitrary reasonable number.","path":"test/extensions/stats_sinks/hystrix/hystrix_test.cc","position":221,"original_position":221,"commit_id":"43901b99d01c297044837b139c890f6d630713d9","original_commit_id":"43901b99d01c297044837b139c890f6d630713d9","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":"nice!  I didn't realize this but, but I just poked around, and this both echoes the seed, and provides a way for you to re-run with the same seed.\r\n","created_at":"2018-06-25T12:44:26Z","updated_at":"2018-06-25T12:46:08Z","html_url":"https://github.com/envoyproxy/envoy/pull/3425#discussion_r197781426","pull_request_url":"https://api.github.com/repos/envoyproxy/envoy/pulls/3425","author_association":"CONTRIBUTOR","_links":{"self":{"href":"https://api.github.com/repos/envoyproxy/envoy/pulls/comments/197781426"},"html":{"href":"https://github.com/envoyproxy/envoy/pull/3425#discussion_r197781426"},"pull_request":{"href":"https://api.github.com/repos/envoyproxy/envoy/pulls/3425"}}},"pull_request":{"url":"https://api.github.com/repos/envoyproxy/envoy/pulls/3425","id":188837699,"node_id":"MDExOlB1bGxSZXF1ZXN0MTg4ODM3Njk5","html_url":"https://github.com/envoyproxy/envoy/pull/3425","diff_url":"https://github.com/envoyproxy/envoy/pull/3425.diff","patch_url":"https://github.com/envoyproxy/envoy/pull/3425.patch","issue_url":"https://api.github.com/repos/envoyproxy/envoy/issues/3425","number":3425,"state":"open","locked":false,"title":"support for hystrix dashboard through an admin endpoint, using sink","user":{"login":"trabetti","id":31645500,"node_id":"MDQ6VXNlcjMxNjQ1NTAw","avatar_url":"https://avatars0.githubusercontent.com/u/31645500?v=4","gravatar_id":"","url":"https://api.github.com/users/trabetti","html_url":"https://github.com/trabetti","followers_url":"https://api.github.com/users/trabetti/followers","following_url":"https://api.github.com/users/trabetti/following{/other_user}","gists_url":"https://api.github.com/users/trabetti/gists{/gist_id}","starred_url":"https://api.github.com/users/trabetti/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/trabetti/subscriptions","organizations_url":"https://api.github.com/users/trabetti/orgs","repos_url":"https://api.github.com/users/trabetti/repos","events_url":"https://api.github.com/users/trabetti/events{/privacy}","received_events_url":"https://api.github.com/users/trabetti/received_events","type":"User","site_admin":false},"body":"Signed-off-by: talis talis@il.ibm.com\r\n\r\nadmin filter: add support for hystrix dashboard output\r\n\r\nDescription:\r\nThis PR adds an admin endoint for hystrix dashboard output.\r\nWhen there is a request to admin_port/hystrix_event_stream, envoy starts sending SSE stream, using the same socket of the initial request.\r\nThe admin URL can be accessed directly from hystrix dashboard.\r\n\r\nRisk Level: Medium\r\n\r\nTesting:\r\nAdded unit testing hystrix_test.cc - WIP\r\nThe admin endpoint was not tested, since it returns an infinite event stream, so current mechanisms for testing admin endpoint do not work. If there are any suggestions on how to test it, we will add.\r\n\r\nDocs Changes:\r\n\r\nRelease Notes:\r\nSupport was added to hystrix event stream output. From hystrix dashboard, set the Eureka URL to envoy_ip:8001, and add stream from http://envoy_ip:8001/hystrix_event_stream\r\nNote that not all hystrix dashboard statistics are supported. More detail can be found here: <link to the documentation generated from docs/root/operations/admin.rst >\r\n\r\nFixes #1244\r\nsplit from PR #2736","created_at":"2018-05-17T19:27:13Z","updated_at":"2018-06-25T12:46:08Z","closed_at":null,"merged_at":null,"merge_commit_sha":"ba938dbf4a2fdeffa4af1ce1fd14154ca84a980a","assignee":{"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},"assignees":[{"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},{"login":"mrice32","id":11791551,"node_id":"MDQ6VXNlcjExNzkxNTUx","avatar_url":"https://avatars3.githubusercontent.com/u/11791551?v=4","gravatar_id":"","url":"https://api.github.com/users/mrice32","html_url":"https://github.com/mrice32","followers_url":"https://api.github.com/users/mrice32/followers","following_url":"https://api.github.com/users/mrice32/following{/other_user}","gists_url":"https://api.github.com/users/mrice32/gists{/gist_id}","starred_url":"https://api.github.com/users/mrice32/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mrice32/subscriptions","organizations_url":"https://api.github.com/users/mrice32/orgs","repos_url":"https://api.github.com/users/mrice32/repos","events_url":"https://api.github.com/users/mrice32/events{/privacy}","received_events_url":"https://api.github.com/users/mrice32/received_events","type":"User","site_admin":false}],"requested_reviewers":[],"requested_teams":[],"labels":[],"milestone":null,"commits_url":"https://api.github.com/repos/envoyproxy/envoy/pulls/3425/commits","review_comments_url":"https://api.github.com/repos/envoyproxy/envoy/pulls/3425/comments","review_comment_url":"https://api.github.com/repos/envoyproxy/envoy/pulls/comments{/number}","comments_url":"https://api.github.com/repos/envoyproxy/envoy/issues/3425/comments","statuses_url":"https://api.github.com/repos/envoyproxy/envoy/statuses/43901b99d01c297044837b139c890f6d630713d9","head":{"label":"trabetti:hystrix_dashboard_support","ref":"hystrix_dashboard_support","sha":"43901b99d01c297044837b139c890f6d630713d9","user":{"login":"trabetti","id":31645500,"node_id":"MDQ6VXNlcjMxNjQ1NTAw","avatar_url":"https://avatars0.githubusercontent.com/u/31645500?v=4","gravatar_id":"","url":"https://api.github.com/users/trabetti","html_url":"https://github.com/trabetti","followers_url":"https://api.github.com/users/trabetti/followers","following_url":"https://api.github.com/users/trabetti/following{/other_user}","gists_url":"https://api.github.com/users/trabetti/gists{/gist_id}","starred_url":"https://api.github.com/users/trabetti/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/trabetti/subscriptions","organizations_url":"https://api.github.com/users/trabetti/orgs","repos_url":"https://api.github.com/users/trabetti/repos","events_url":"https://api.github.com/users/trabetti/events{/privacy}","received_events_url":"https://api.github.com/users/trabetti/received_events","type":"User","site_admin":false},"repo":{"id":108457854,"node_id":"MDEwOlJlcG9zaXRvcnkxMDg0NTc4NTQ=","name":"envoy","full_name":"trabetti/envoy","owner":{"login":"trabetti","id":31645500,"node_id":"MDQ6VXNlcjMxNjQ1NTAw","avatar_url":"https://avatars0.githubusercontent.com/u/31645500?v=4","gravatar_id":"","url":"https://api.github.com/users/trabetti","html_url":"https://github.com/trabetti","followers_url":"https://api.github.com/users/trabetti/followers","following_url":"https://api.github.com/users/trabetti/following{/other_user}","gists_url":"https://api.github.com/users/trabetti/gists{/gist_id}","starred_url":"https://api.github.com/users/trabetti/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/trabetti/subscriptions","organizations_url":"https://api.github.com/users/trabetti/orgs","repos_url":"https://api.github.com/users/trabetti/repos","events_url":"https://api.github.com/users/trabetti/events{/privacy}","received_events_url":"https://api.github.com/users/trabetti/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/trabetti/envoy","description":"C++ front/service proxy","fork":true,"url":"https://api.github.com/repos/trabetti/envoy","forks_url":"https://api.github.com/repos/trabetti/envoy/forks","keys_url":"https://api.github.com/repos/trabetti/envoy/keys{/key_id}","collaborators_url":"https://api.github.com/repos/trabetti/envoy/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/trabetti/envoy/teams","hooks_url":"https://api.github.com/repos/trabetti/envoy/hooks","issue_events_url":"https://api.github.com/repos/trabetti/envoy/issues/events{/number}","events_url":"https://api.github.com/repos/trabetti/envoy/events","assignees_url":"https://api.github.com/repos/trabetti/envoy/assignees{/user}","branches_url":"https://api.github.com/repos/trabetti/envoy/branches{/branch}","tags_url":"https://api.github.com/repos/trabetti/envoy/tags","blobs_url":"https://api.github.com/repos/trabetti/envoy/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/trabetti/envoy/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/trabetti/envoy/git/refs{/sha}","trees_url":"https://api.github.com/repos/trabetti/envoy/git/trees{/sha}","statuses_url":"https://api.github.com/repos/trabetti/envoy/statuses/{sha}","languages_url":"https://api.github.com/repos/trabetti/envoy/languages","stargazers_url":"https://api.github.com/repos/trabetti/envoy/stargazers","contributors_url":"https://api.github.com/repos/trabetti/envoy/contributors","subscribers_url":"https://api.github.com/repos/trabetti/envoy/subscribers","subscription_url":"https://api.github.com/repos/trabetti/envoy/subscription","commits_url":"https://api.github.com/repos/trabetti/envoy/commits{/sha}","git_commits_url":"https://api.github.com/repos/trabetti/envoy/git/commits{/sha}","comments_url":"https://api.github.com/repos/trabetti/envoy/comments{/number}","issue_comment_url":"https://api.github.com/repos/trabetti/envoy/issues/comments{/number}","contents_url":"https://api.github.com/repos/trabetti/envoy/contents/{+path}","compare_url":"https://api.github.com/repos/trabetti/envoy/compare/{base}...{head}","merges_url":"https://api.github.com/repos/trabetti/envoy/merges","archive_url":"https://api.github.com/repos/trabetti/envoy/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/trabetti/envoy/downloads","issues_url":"https://api.github.com/repos/trabetti/envoy/issues{/number}","pulls_url":"https://api.github.com/repos/trabetti/envoy/pulls{/number}","milestones_url":"https://api.github.com/repos/trabetti/envoy/milestones{/number}","notifications_url":"https://api.github.com/repos/trabetti/envoy/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/trabetti/envoy/labels{/name}","releases_url":"https://api.github.com/repos/trabetti/envoy/releases{/id}","deployments_url":"https://api.github.com/repos/trabetti/envoy/deployments","created_at":"2017-10-26T19:43:33Z","updated_at":"2017-10-26T19:43:38Z","pushed_at":"2018-06-25T12:20:39Z","git_url":"git://github.com/trabetti/envoy.git","ssh_url":"git@github.com:trabetti/envoy.git","clone_url":"https://github.com/trabetti/envoy.git","svn_url":"https://github.com/trabetti/envoy","homepage":"https://envoyproxy.github.io/","size":16663,"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":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","node_id":"MDc6TGljZW5zZTI="},"forks":0,"open_issues":1,"watchers":0,"default_branch":"master"}},"base":{"label":"envoyproxy:master","ref":"master","sha":"067f8f6523f63d6f0ccd3d44e6fd2db97804af20","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-06-25T05:06:04Z","pushed_at":"2018-06-25T12:20:42Z","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":16830,"stargazers_count":5348,"watchers_count":5348,"language":"C++","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":684,"mirror_url":null,"archived":false,"open_issues_count":329,"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":684,"open_issues":329,"watchers":5348,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/envoyproxy/envoy/pulls/3425"},"html":{"href":"https://github.com/envoyproxy/envoy/pull/3425"},"issue":{"href":"https://api.github.com/repos/envoyproxy/envoy/issues/3425"},"comments":{"href":"https://api.github.com/repos/envoyproxy/envoy/issues/3425/comments"},"review_comments":{"href":"https://api.github.com/repos/envoyproxy/envoy/pulls/3425/comments"},"review_comment":{"href":"https://api.github.com/repos/envoyproxy/envoy/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/envoyproxy/envoy/pulls/3425/commits"},"statuses":{"href":"https://api.github.com/repos/envoyproxy/envoy/statuses/43901b99d01c297044837b139c890f6d630713d9"}},"author_association":"CONTRIBUTOR"}}
{"actor":{"display_login":"jmarantz"}}