{"action":"created","comment":{"url":"https://api.github.com/repos/istio/proxy/pulls/comments/118125363","pull_request_review_id":39887452,"id":118125363,"diff_hunk":"@@ -0,0 +1,302 @@\n+/* Copyright 2017 Google Inc. All Rights Reserved.\n+ *\n+ * Licensed under the Apache License, Version 2.0 (the \"License\");\n+ * you may not use this file except in compliance with the License.\n+ * You may obtain a copy of the License at\n+ *\n+ *    http://www.apache.org/licenses/LICENSE-2.0\n+ *\n+ * Unless required by applicable law or agreed to in writing, software\n+ * distributed under the License is distributed on an \"AS IS\" BASIS,\n+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n+ * See the License for the specific language governing permissions and\n+ * limitations under the License.\n+ */\n+#include \"contrib/endpoints/src/api_manager/config_manager.h\"\n+#include \"contrib/endpoints/src/api_manager/utils/marshalling.h\"\n+#include \"utils/marshalling.h\"\n+\n+#include \"google/api/servicemanagement/v1/servicemanager.pb.h\"\n+#include \"utils/md5.h\"\n+\n+#include <math.h>\n+\n+#include <chrono>\n+#include <ctime>\n+#include <future>\n+#include <iostream>\n+#include <limits>\n+#include <mutex>\n+#include <regex>\n+#include <thread>\n+\n+using ::google::api::Service;\n+using ::google::api::servicemanagement::v1::ListServiceRolloutsResponse;\n+using ::google::api::servicemanagement::v1::Rollout_RolloutStatus;\n+\n+namespace google {\n+namespace api_manager {\n+\n+namespace {\n+\n+const char kDelimiter[] = \"\\0\";\n+const int kDelimiterLength = 1;\n+\n+// Initial metadata fetch timeout (1s)\n+const int kInceptionFetchTimeout = 1000;\n+// Maximum number of retries to fetch metadata\n+const int kInceptionFetchRetries = 5;\n+// Default rollouts refresh interval in ms\n+const int kConfigUpdateCheckInterval = 60000;\n+\n+const char kRolloutStrategyManaged[] = \"managed\";\n+// Default service management API url\n+const char kServiceManagementService[] =\n+    \"https://servicemanagement.googleapis.com\";\n+const char kServiceManagementServiceManager[] =\n+    \"/google.api.servicemanagement.v1.ServiceManager\";\n+// config_id validation\n+const char kValidConfigDateFormat[] = \"%Y-%m-%d\";\n+const char kConfigRevisionDelimeter = 'r';\n+// static configs for error handling\n+static std::vector<std::pair<std::string, int>> empty_configs;\n+\n+// Check config_id is valid\n+bool isValidConfigId(const std::string& config_id) {\n+  if (config_id.length() < 12) {\n+    return false;\n+  }\n+\n+  if (config_id[10] != kConfigRevisionDelimeter) {\n+    return false;\n+  }\n+\n+  struct tm tm;\n+  std::string date_part = config_id.substr(0, 10);\n+  if (!strptime(date_part.c_str(), kValidConfigDateFormat, &tm)) {\n+    return false;\n+  }\n+\n+  std::string revision_part = config_id.substr(11);\n+  if (std::stoi(revision_part) == 0 && revision_part.compare(\"0\") != 0) {\n+    return false;\n+  }\n+\n+  return true;\n+}\n+};\n+\n+ConfigManager::ConfigManager(\n+    std::shared_ptr<context::GlobalContext> global_context,\n+    ApiManagerCallbackFunction config_roollout_callback)\n+    : global_context_(global_context),\n+      config_roollout_callback_(config_roollout_callback),\n+      service_management_url_(kServiceManagementService),\n+      refresh_interval_ms_(kConfigUpdateCheckInterval) {\n+  if (global_context_->server_config()->has_service_management_config()) {\n+    // update ServiceManagement service API url\n+    if (!global_context_->server_config()\n+             ->service_management_config()\n+             .url()\n+             .empty()) {\n+      service_management_url_ =\n+          global_context_->server_config()->service_management_config().url();\n+    }\n+    // update refresh interval in ms\n+    if (global_context_->server_config()\n+            ->service_management_config()\n+            .refresh_interval_ms() > 0) {\n+      refresh_interval_ms_ = global_context_->server_config()\n+                                 ->service_management_config()\n+                                 .refresh_interval_ms();\n+    }\n+  }\n+}\n+\n+void ConfigManager::Init() {\n+  if (global_context_->service_name().empty() ||\n+      global_context_->config_id().empty()) {\n+    std::function<void(utils::Status)> callback = [this](utils::Status status) {\n+      on_fetch_metadata(status);\n+    };\n+\n+    GlobalFetchGceMetadata(global_context_, callback);\n+  } else {\n+    on_fetch_metadata(utils::Status::OK);\n+  }\n+}\n+\n+void ConfigManager::on_fetch_metadata(utils::Status status) {\n+  if (status.ok()) {\n+    // Update service_name\n+    if (global_context_->service_name().empty()) {\n+      global_context_->set_service_name(\n+          global_context_->gce_metadata()->endpoints_service_name());\n+    }\n+\n+    // Update config_id\n+    if (global_context_->config_id().empty()) {\n+      global_context_->config_id(\n+          global_context_->gce_metadata()->endpoints_service_config_id());\n+    }\n+\n+    // Call ApiManager call with Code::ABORTED, ESP will stop moving forward\n+    if (global_context_->service_name().empty()) {\n+      std::string msg = \"API service name not specified in configuration files\";\n+      global_context_->env()->LogError(msg);\n+      config_roollout_callback_(utils::Status(Code::ABORTED, msg),\n+                                empty_configs);\n+      return;\n+    }\n+\n+    // TODO(jaebong) config_id should not be empty for the first version\n+    // This part will be removed after the rollouts feature added\n+    if (global_context_->config_id().empty()) {\n+      std::string msg = \"API config_id not specified in configuration files\";\n+      global_context_->env()->LogError(msg);\n+      config_roollout_callback_(utils::Status(Code::ABORTED, msg),\n+                                empty_configs);\n+      return;\n+    }\n+\n+    // Check config_id is invalid\n+    if (!isValidConfigId(global_context_->config_id())) {\n+      std::string msg = \"Invalid config_id: \" + global_context_->config_id();\n+      global_context_->env()->LogError(msg);\n+      config_roollout_callback_(utils::Status(Code::ABORTED, msg),\n+                                empty_configs);\n+      return;\n+    }\n+\n+    std::function<void(utils::Status)> callback = [this](utils::Status status) {\n+      on_fetch_auth_token(status);\n+    };\n+    // Fetch service account token\n+    GlobalFetchServiceAccountToken(global_context_, callback);\n+  } else if (status.code() == Code::INTERNAL) {\n+    // Failed to fetch meta data\n+    global_context_->env()->LogError(\"Metadata fetch failed.\");\n+    config_roollout_callback_(utils::Status(Code::ABORTED, status.message()),\n+                              empty_configs);\n+  } else {\n+    // We should not get here\n+    global_context_->env()->LogError(\"Unexpected status: \" + status.ToString());\n+    config_roollout_callback_(utils::Status(Code::ABORTED, status.message()),\n+                              empty_configs);\n+  }\n+}\n+\n+void ConfigManager::on_fetch_auth_token(utils::Status status) {\n+  if (status.ok()) {\n+    if (global_context_->service_account_token()) {\n+      // register auth token for servicemanagement services\n+      global_context_->service_account_token()->SetAudience(\n+          auth::ServiceAccountToken::JWT_TOKEN_FOR_SERVICEMANAGEMENT_SERVICES,\n+          service_management_url_ + kServiceManagementServiceManager);\n+    }\n+\n+    // Fetch configs from the Inception\n+    std::shared_ptr<ConfigsFetchInfo> fetchInfo(new ConfigsFetchInfo);\n+    // For now, config manager has only one config_id (100% rollout)\n+    fetchInfo->rollouts = {{global_context_->config_id(), 0}};\n+    fetch_configs(fetchInfo, 0);\n+\n+  } else if (status.code() == Code::INTERNAL) {\n+    // Failed to fetch auth token.\n+    global_context_->env()->LogError(\"Auth token fetch failed.\");\n+    config_roollout_callback_(utils::Status(Code::ABORTED, status.message()),\n+                              empty_configs);\n+  } else {\n+    // We should not get here\n+    global_context_->env()->LogError(\"Unexpected status: \" + status.ToString());\n+    config_roollout_callback_(utils::Status(Code::ABORTED, status.message()),\n+                              empty_configs);\n+  }\n+}\n+\n+void ConfigManager::fetch_configs(std::shared_ptr<ConfigsFetchInfo> fetchInfo,","path":"contrib/endpoints/src/api_manager/config_manager.cc","position":217,"original_position":217,"commit_id":"6deb8acd5f72f6640d758d49bfeb21188dec48ed","original_commit_id":"6deb8acd5f72f6640d758d49bfeb21188dec48ed","user":{"login":"qiwzhang","id":11790727,"avatar_url":"https://avatars3.githubusercontent.com/u/11790727?v=3","gravatar_id":"","url":"https://api.github.com/users/qiwzhang","html_url":"https://github.com/qiwzhang","followers_url":"https://api.github.com/users/qiwzhang/followers","following_url":"https://api.github.com/users/qiwzhang/following{/other_user}","gists_url":"https://api.github.com/users/qiwzhang/gists{/gist_id}","starred_url":"https://api.github.com/users/qiwzhang/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/qiwzhang/subscriptions","organizations_url":"https://api.github.com/users/qiwzhang/orgs","repos_url":"https://api.github.com/users/qiwzhang/repos","events_url":"https://api.github.com/users/qiwzhang/events{/privacy}","received_events_url":"https://api.github.com/users/qiwzhang/received_events","type":"User","site_admin":false},"body":"can we move all fetch_configs logic into ConfigsFetchInfo class,   it will take a on_done callback,  ConfigManager will continue to execture on the on_done().\r\n","created_at":"2017-05-23T22:53:37Z","updated_at":"2017-05-23T22:53:44Z","html_url":"https://github.com/istio/proxy/pull/338#discussion_r118125363","pull_request_url":"https://api.github.com/repos/istio/proxy/pulls/338","_links":{"self":{"href":"https://api.github.com/repos/istio/proxy/pulls/comments/118125363"},"html":{"href":"https://github.com/istio/proxy/pull/338#discussion_r118125363"},"pull_request":{"href":"https://api.github.com/repos/istio/proxy/pulls/338"}}},"pull_request":{"url":"https://api.github.com/repos/istio/proxy/pulls/338","id":121849410,"html_url":"https://github.com/istio/proxy/pull/338","diff_url":"https://github.com/istio/proxy/pull/338.diff","patch_url":"https://github.com/istio/proxy/pull/338.patch","issue_url":"https://api.github.com/repos/istio/proxy/issues/338","number":338,"state":"open","locked":false,"title":"Created the ConfigManager interface","user":{"login":"mangchiandjjoe","id":1411263,"avatar_url":"https://avatars3.githubusercontent.com/u/1411263?v=3","gravatar_id":"","url":"https://api.github.com/users/mangchiandjjoe","html_url":"https://github.com/mangchiandjjoe","followers_url":"https://api.github.com/users/mangchiandjjoe/followers","following_url":"https://api.github.com/users/mangchiandjjoe/following{/other_user}","gists_url":"https://api.github.com/users/mangchiandjjoe/gists{/gist_id}","starred_url":"https://api.github.com/users/mangchiandjjoe/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mangchiandjjoe/subscriptions","organizations_url":"https://api.github.com/users/mangchiandjjoe/orgs","repos_url":"https://api.github.com/users/mangchiandjjoe/repos","events_url":"https://api.github.com/users/mangchiandjjoe/events{/privacy}","received_events_url":"https://api.github.com/users/mangchiandjjoe/received_events","type":"User","site_admin":false},"body":"","created_at":"2017-05-22T19:34:46Z","updated_at":"2017-05-23T22:53:44Z","closed_at":null,"merged_at":null,"merge_commit_sha":"17c5bfdf642481bc9d48e027ad5ea6801d221777","assignee":null,"assignees":[],"requested_reviewers":[],"milestone":null,"commits_url":"https://api.github.com/repos/istio/proxy/pulls/338/commits","review_comments_url":"https://api.github.com/repos/istio/proxy/pulls/338/comments","review_comment_url":"https://api.github.com/repos/istio/proxy/pulls/comments{/number}","comments_url":"https://api.github.com/repos/istio/proxy/issues/338/comments","statuses_url":"https://api.github.com/repos/istio/proxy/statuses/6deb8acd5f72f6640d758d49bfeb21188dec48ed","head":{"label":"mangchiandjjoe:master","ref":"master","sha":"6deb8acd5f72f6640d758d49bfeb21188dec48ed","user":{"login":"mangchiandjjoe","id":1411263,"avatar_url":"https://avatars3.githubusercontent.com/u/1411263?v=3","gravatar_id":"","url":"https://api.github.com/users/mangchiandjjoe","html_url":"https://github.com/mangchiandjjoe","followers_url":"https://api.github.com/users/mangchiandjjoe/followers","following_url":"https://api.github.com/users/mangchiandjjoe/following{/other_user}","gists_url":"https://api.github.com/users/mangchiandjjoe/gists{/gist_id}","starred_url":"https://api.github.com/users/mangchiandjjoe/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mangchiandjjoe/subscriptions","organizations_url":"https://api.github.com/users/mangchiandjjoe/orgs","repos_url":"https://api.github.com/users/mangchiandjjoe/repos","events_url":"https://api.github.com/users/mangchiandjjoe/events{/privacy}","received_events_url":"https://api.github.com/users/mangchiandjjoe/received_events","type":"User","site_admin":false},"repo":{"id":92091991,"name":"proxy","full_name":"mangchiandjjoe/proxy","owner":{"login":"mangchiandjjoe","id":1411263,"avatar_url":"https://avatars3.githubusercontent.com/u/1411263?v=3","gravatar_id":"","url":"https://api.github.com/users/mangchiandjjoe","html_url":"https://github.com/mangchiandjjoe","followers_url":"https://api.github.com/users/mangchiandjjoe/followers","following_url":"https://api.github.com/users/mangchiandjjoe/following{/other_user}","gists_url":"https://api.github.com/users/mangchiandjjoe/gists{/gist_id}","starred_url":"https://api.github.com/users/mangchiandjjoe/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mangchiandjjoe/subscriptions","organizations_url":"https://api.github.com/users/mangchiandjjoe/orgs","repos_url":"https://api.github.com/users/mangchiandjjoe/repos","events_url":"https://api.github.com/users/mangchiandjjoe/events{/privacy}","received_events_url":"https://api.github.com/users/mangchiandjjoe/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/mangchiandjjoe/proxy","description":"The Istio proxy components.","fork":true,"url":"https://api.github.com/repos/mangchiandjjoe/proxy","forks_url":"https://api.github.com/repos/mangchiandjjoe/proxy/forks","keys_url":"https://api.github.com/repos/mangchiandjjoe/proxy/keys{/key_id}","collaborators_url":"https://api.github.com/repos/mangchiandjjoe/proxy/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/mangchiandjjoe/proxy/teams","hooks_url":"https://api.github.com/repos/mangchiandjjoe/proxy/hooks","issue_events_url":"https://api.github.com/repos/mangchiandjjoe/proxy/issues/events{/number}","events_url":"https://api.github.com/repos/mangchiandjjoe/proxy/events","assignees_url":"https://api.github.com/repos/mangchiandjjoe/proxy/assignees{/user}","branches_url":"https://api.github.com/repos/mangchiandjjoe/proxy/branches{/branch}","tags_url":"https://api.github.com/repos/mangchiandjjoe/proxy/tags","blobs_url":"https://api.github.com/repos/mangchiandjjoe/proxy/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/mangchiandjjoe/proxy/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/mangchiandjjoe/proxy/git/refs{/sha}","trees_url":"https://api.github.com/repos/mangchiandjjoe/proxy/git/trees{/sha}","statuses_url":"https://api.github.com/repos/mangchiandjjoe/proxy/statuses/{sha}","languages_url":"https://api.github.com/repos/mangchiandjjoe/proxy/languages","stargazers_url":"https://api.github.com/repos/mangchiandjjoe/proxy/stargazers","contributors_url":"https://api.github.com/repos/mangchiandjjoe/proxy/contributors","subscribers_url":"https://api.github.com/repos/mangchiandjjoe/proxy/subscribers","subscription_url":"https://api.github.com/repos/mangchiandjjoe/proxy/subscription","commits_url":"https://api.github.com/repos/mangchiandjjoe/proxy/commits{/sha}","git_commits_url":"https://api.github.com/repos/mangchiandjjoe/proxy/git/commits{/sha}","comments_url":"https://api.github.com/repos/mangchiandjjoe/proxy/comments{/number}","issue_comment_url":"https://api.github.com/repos/mangchiandjjoe/proxy/issues/comments{/number}","contents_url":"https://api.github.com/repos/mangchiandjjoe/proxy/contents/{+path}","compare_url":"https://api.github.com/repos/mangchiandjjoe/proxy/compare/{base}...{head}","merges_url":"https://api.github.com/repos/mangchiandjjoe/proxy/merges","archive_url":"https://api.github.com/repos/mangchiandjjoe/proxy/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/mangchiandjjoe/proxy/downloads","issues_url":"https://api.github.com/repos/mangchiandjjoe/proxy/issues{/number}","pulls_url":"https://api.github.com/repos/mangchiandjjoe/proxy/pulls{/number}","milestones_url":"https://api.github.com/repos/mangchiandjjoe/proxy/milestones{/number}","notifications_url":"https://api.github.com/repos/mangchiandjjoe/proxy/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/mangchiandjjoe/proxy/labels{/name}","releases_url":"https://api.github.com/repos/mangchiandjjoe/proxy/releases{/id}","deployments_url":"https://api.github.com/repos/mangchiandjjoe/proxy/deployments","created_at":"2017-05-22T19:32:13Z","updated_at":"2017-05-22T19:32:15Z","pushed_at":"2017-05-23T22:10:14Z","git_url":"git://github.com/mangchiandjjoe/proxy.git","ssh_url":"git@github.com:mangchiandjjoe/proxy.git","clone_url":"https://github.com/mangchiandjjoe/proxy.git","svn_url":"https://github.com/mangchiandjjoe/proxy","homepage":"","size":934,"stargazers_count":0,"watchers_count":0,"language":"C++","has_issues":false,"has_projects":true,"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":"istio:master","ref":"master","sha":"ab192387d458134b7bec080be7156d459ee8382f","user":{"login":"istio","id":23534644,"avatar_url":"https://avatars2.githubusercontent.com/u/23534644?v=3","gravatar_id":"","url":"https://api.github.com/users/istio","html_url":"https://github.com/istio","followers_url":"https://api.github.com/users/istio/followers","following_url":"https://api.github.com/users/istio/following{/other_user}","gists_url":"https://api.github.com/users/istio/gists{/gist_id}","starred_url":"https://api.github.com/users/istio/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/istio/subscriptions","organizations_url":"https://api.github.com/users/istio/orgs","repos_url":"https://api.github.com/users/istio/repos","events_url":"https://api.github.com/users/istio/events{/privacy}","received_events_url":"https://api.github.com/users/istio/received_events","type":"Organization","site_admin":false},"repo":{"id":75322405,"name":"proxy","full_name":"istio/proxy","owner":{"login":"istio","id":23534644,"avatar_url":"https://avatars2.githubusercontent.com/u/23534644?v=3","gravatar_id":"","url":"https://api.github.com/users/istio","html_url":"https://github.com/istio","followers_url":"https://api.github.com/users/istio/followers","following_url":"https://api.github.com/users/istio/following{/other_user}","gists_url":"https://api.github.com/users/istio/gists{/gist_id}","starred_url":"https://api.github.com/users/istio/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/istio/subscriptions","organizations_url":"https://api.github.com/users/istio/orgs","repos_url":"https://api.github.com/users/istio/repos","events_url":"https://api.github.com/users/istio/events{/privacy}","received_events_url":"https://api.github.com/users/istio/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/istio/proxy","description":"The Istio proxy components.","fork":false,"url":"https://api.github.com/repos/istio/proxy","forks_url":"https://api.github.com/repos/istio/proxy/forks","keys_url":"https://api.github.com/repos/istio/proxy/keys{/key_id}","collaborators_url":"https://api.github.com/repos/istio/proxy/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/istio/proxy/teams","hooks_url":"https://api.github.com/repos/istio/proxy/hooks","issue_events_url":"https://api.github.com/repos/istio/proxy/issues/events{/number}","events_url":"https://api.github.com/repos/istio/proxy/events","assignees_url":"https://api.github.com/repos/istio/proxy/assignees{/user}","branches_url":"https://api.github.com/repos/istio/proxy/branches{/branch}","tags_url":"https://api.github.com/repos/istio/proxy/tags","blobs_url":"https://api.github.com/repos/istio/proxy/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/istio/proxy/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/istio/proxy/git/refs{/sha}","trees_url":"https://api.github.com/repos/istio/proxy/git/trees{/sha}","statuses_url":"https://api.github.com/repos/istio/proxy/statuses/{sha}","languages_url":"https://api.github.com/repos/istio/proxy/languages","stargazers_url":"https://api.github.com/repos/istio/proxy/stargazers","contributors_url":"https://api.github.com/repos/istio/proxy/contributors","subscribers_url":"https://api.github.com/repos/istio/proxy/subscribers","subscription_url":"https://api.github.com/repos/istio/proxy/subscription","commits_url":"https://api.github.com/repos/istio/proxy/commits{/sha}","git_commits_url":"https://api.github.com/repos/istio/proxy/git/commits{/sha}","comments_url":"https://api.github.com/repos/istio/proxy/comments{/number}","issue_comment_url":"https://api.github.com/repos/istio/proxy/issues/comments{/number}","contents_url":"https://api.github.com/repos/istio/proxy/contents/{+path}","compare_url":"https://api.github.com/repos/istio/proxy/compare/{base}...{head}","merges_url":"https://api.github.com/repos/istio/proxy/merges","archive_url":"https://api.github.com/repos/istio/proxy/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/istio/proxy/downloads","issues_url":"https://api.github.com/repos/istio/proxy/issues{/number}","pulls_url":"https://api.github.com/repos/istio/proxy/pulls{/number}","milestones_url":"https://api.github.com/repos/istio/proxy/milestones{/number}","notifications_url":"https://api.github.com/repos/istio/proxy/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/istio/proxy/labels{/name}","releases_url":"https://api.github.com/repos/istio/proxy/releases{/id}","deployments_url":"https://api.github.com/repos/istio/proxy/deployments","created_at":"2016-12-01T18:51:04Z","updated_at":"2017-05-20T08:50:51Z","pushed_at":"2017-05-23T22:10:16Z","git_url":"git://github.com/istio/proxy.git","ssh_url":"git@github.com:istio/proxy.git","clone_url":"https://github.com/istio/proxy.git","svn_url":"https://github.com/istio/proxy","homepage":"","size":927,"stargazers_count":25,"watchers_count":25,"language":"C++","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":30,"mirror_url":null,"open_issues_count":14,"forks":30,"open_issues":14,"watchers":25,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/istio/proxy/pulls/338"},"html":{"href":"https://github.com/istio/proxy/pull/338"},"issue":{"href":"https://api.github.com/repos/istio/proxy/issues/338"},"comments":{"href":"https://api.github.com/repos/istio/proxy/issues/338/comments"},"review_comments":{"href":"https://api.github.com/repos/istio/proxy/pulls/338/comments"},"review_comment":{"href":"https://api.github.com/repos/istio/proxy/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/istio/proxy/pulls/338/commits"},"statuses":{"href":"https://api.github.com/repos/istio/proxy/statuses/6deb8acd5f72f6640d758d49bfeb21188dec48ed"}}}}
{"actor":{"display_login":"qiwzhang"}}