You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@kudu.apache.org by da...@apache.org on 2017/09/21 20:15:44 UTC

[2/3] kudu git commit: KUDU-501 Redirect to leader master web UI

KUDU-501 Redirect to leader master web UI

The /table and /tables page of the master web UI only work
on the leader master. Previously, we just showed a gross error when the
user tried to access these pages on a non-leader. This adds a nice
redirect instead (or an error about why a redirect wasn't possible).

Change-Id: If31543b7899976ec02704111e9e789035c44dfe1
Reviewed-on: http://gerrit.cloudera.org:8080/8068
Tested-by: Kudu Jenkins
Reviewed-by: Will Berkeley <wd...@gmail.com>


Project: http://git-wip-us.apache.org/repos/asf/kudu/repo
Commit: http://git-wip-us.apache.org/repos/asf/kudu/commit/4daa3ce8
Tree: http://git-wip-us.apache.org/repos/asf/kudu/tree/4daa3ce8
Diff: http://git-wip-us.apache.org/repos/asf/kudu/diff/4daa3ce8

Branch: refs/heads/master
Commit: 4daa3ce8742280495c7b6f9a9b2a828e3572ea2c
Parents: e66fb10
Author: Will Berkeley <wd...@apache.org>
Authored: Wed Sep 13 23:59:36 2017 -0700
Committer: Will Berkeley <wd...@gmail.com>
Committed: Thu Sep 21 20:05:23 2017 +0000

----------------------------------------------------------------------
 src/kudu/integration-tests/CMakeLists.txt       |  2 +-
 .../integration-tests/webserver-stress-itest.cc |  6 +-
 src/kudu/master/master-path-handlers.cc         | 87 ++++++++++++++++++--
 src/kudu/master/master-path-handlers.h          |  8 ++
 www/table.mustache                              |  7 ++
 www/tables.mustache                             |  6 ++
 6 files changed, 107 insertions(+), 9 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/kudu/blob/4daa3ce8/src/kudu/integration-tests/CMakeLists.txt
----------------------------------------------------------------------
diff --git a/src/kudu/integration-tests/CMakeLists.txt b/src/kudu/integration-tests/CMakeLists.txt
index 43582d1..8c36fdc 100644
--- a/src/kudu/integration-tests/CMakeLists.txt
+++ b/src/kudu/integration-tests/CMakeLists.txt
@@ -104,7 +104,7 @@ ADD_KUDU_TEST(token_signer-itest RESOURCE_LOCK "master-rpc-ports")
 ADD_KUDU_TEST(ts_recovery-itest)
 ADD_KUDU_TEST(ts_tablet_manager-itest)
 ADD_KUDU_TEST(update_scan_delta_compact-test RUN_SERIAL true)
-ADD_KUDU_TEST(webserver-stress-itest)
+ADD_KUDU_TEST(webserver-stress-itest RESOURCE_LOCK "master-rpc-ports")
 ADD_KUDU_TEST(write_throttling-itest)
 
 if (NOT APPLE)

http://git-wip-us.apache.org/repos/asf/kudu/blob/4daa3ce8/src/kudu/integration-tests/webserver-stress-itest.cc
----------------------------------------------------------------------
diff --git a/src/kudu/integration-tests/webserver-stress-itest.cc b/src/kudu/integration-tests/webserver-stress-itest.cc
index 8c6cd92..b9503de 100644
--- a/src/kudu/integration-tests/webserver-stress-itest.cc
+++ b/src/kudu/integration-tests/webserver-stress-itest.cc
@@ -44,7 +44,11 @@ TEST_F(KuduTest, TestWebUIDoesNotCrashCluster) {
 #endif
   const int kNumTablets = 50;
 
-  ExternalMiniCluster cluster;
+  ExternalMiniClusterOptions opts;
+  opts.master_rpc_ports = { 11010, 11011, 11012 };
+  opts.num_masters = opts.master_rpc_ports.size();
+
+  ExternalMiniCluster cluster(opts);
   ASSERT_OK(cluster.Start());
 
   // Start pounding the master and tserver's web UIs.

http://git-wip-us.apache.org/repos/asf/kudu/blob/4daa3ce8/src/kudu/master/master-path-handlers.cc
----------------------------------------------------------------------
diff --git a/src/kudu/master/master-path-handlers.cc b/src/kudu/master/master-path-handlers.cc
index ebd42f2..bf3d12d 100644
--- a/src/kudu/master/master-path-handlers.cc
+++ b/src/kudu/master/master-path-handlers.cc
@@ -43,6 +43,7 @@
 #include "kudu/gutil/ref_counted.h"
 #include "kudu/gutil/stringprintf.h"
 #include "kudu/gutil/strings/join.h"
+#include "kudu/gutil/strings/numbers.h"
 #include "kudu/gutil/strings/substitute.h"
 #include "kudu/master/catalog_manager.h"
 #include "kudu/master/master.h"
@@ -140,11 +141,33 @@ void MasterPathHandlers::HandleTabletServers(const Webserver::WebRequest& req,
   generate_table(dead_tserver_rows, "Dead Tablet Servers", output);
 }
 
-void MasterPathHandlers::HandleCatalogManager(const Webserver::WebRequest& req,
-                                              EasyJson* output) {
+namespace {
+
+// Extracts the value of the 'redirects' parameter from 'req'; returns 0 if the
+// parameter doesn't exist or couldn't be parsed.
+int ExtractRedirectsFromRequest(const Webserver::WebRequest& req) {
+  string redirects_str;
+  int redirects = 0;
+  if (FindCopy(req.parsed_args, "redirects", &redirects_str)) {
+    if (!safe_strto32(redirects_str, &redirects)) {
+      return 0;
+    }
+  }
+  return redirects;
+}
+
+} // anonymous namespace
+
+void MasterPathHandlers::HandleCatalogManager(const Webserver::WebRequest& req, EasyJson* output) {
   CatalogManager::ScopedLeaderSharedLock l(master_->catalog_manager());
-  if (!l.first_failed_status().ok()) {
-    (*output)["error"] = Substitute("Master is not ready: $0",  l.first_failed_status().ToString());
+  if (!l.catalog_status().ok()) {
+    (*output)["error"] = Substitute("Master is not ready: $0",  l.catalog_status().ToString());
+    return;
+  }
+  if (!l.leader_status().ok()) {
+    // Track redirects to prevent a redirect loop.
+    int redirects = ExtractRedirectsFromRequest(req);
+    SetupLeaderMasterRedirect("tables?", redirects, output);
     return;
   }
 
@@ -206,15 +229,21 @@ void MasterPathHandlers::HandleTablePage(const Webserver::WebRequest& req,
   }
 
   CatalogManager::ScopedLeaderSharedLock l(master_->catalog_manager());
-  if (!l.first_failed_status().ok()) {
-    (*output)["error"] = Substitute("Master is not ready: ", l.first_failed_status().ToString());
+  if (!l.catalog_status().ok()) {
+    (*output)["error"] = Substitute("Master is not ready: $0", l.catalog_status().ToString());
+    return;
+  }
+  if (!l.leader_status().ok()) {
+    // Track redirects to prevent a redirect loop.
+    int redirects = ExtractRedirectsFromRequest(req);
+    SetupLeaderMasterRedirect(Substitute("table?id=$0", table_id), redirects, output);
     return;
   }
 
   scoped_refptr<TableInfo> table;
   Status s = master_->catalog_manager()->GetTableInfo(table_id, &table);
   if (!s.ok()) {
-    (*output)["error"] = Substitute("Master is not ready: ", s.ToString());
+    (*output)["error"] = Substitute("Master is not ready: $0", s.ToString());
     return;
   }
 
@@ -635,5 +664,49 @@ string MasterPathHandlers::MasterAddrsToCsv() const {
   return addr.ToString();
 }
 
+Status MasterPathHandlers::GetLeaderMasterHttpAddr(string* leader_http_addr) const {
+  vector<ServerEntryPB> masters;
+  RETURN_NOT_OK_PREPEND(master_->ListMasters(&masters), "unable to list masters");
+  for (const auto& master : masters) {
+    if (master.has_error()) {
+      continue;
+    }
+    if (master.role() != RaftPeerPB::LEADER) {
+      continue;
+    }
+    const ServerRegistrationPB& reg = master.registration();
+    if (reg.http_addresses().empty()) {
+      return Status::NotFound("leader master has no http address");
+    }
+    *leader_http_addr = Substitute("$0://$1:$2",
+                                   reg.https_enabled() ? "https" : "http",
+                                   reg.http_addresses(0).host(),
+                                   reg.http_addresses(0).port());
+    return Status::OK();
+  }
+  return Status::NotFound("no leader master known to this master");
+}
+
+void MasterPathHandlers::SetupLeaderMasterRedirect(const string& path,
+                                                   int redirects,
+                                                   EasyJson* output) const {
+  // Allow 3 redirects.
+  const int max_redirects = 3;
+  (*output)["error"] = "Master is not the leader.";
+  if (redirects >= max_redirects) {
+    (*output)["redirect_error"] = "Too many redirects attempting to find the leader master.";
+    return;
+  }
+  string leader_http_addr;
+  Status s = GetLeaderMasterHttpAddr(&leader_http_addr);
+  if (!s.ok()) {
+    (*output)["redirect_error"] = Substitute("Unable to redirect to leader master: $0",
+                                             s.ToString());
+    return;
+  }
+  (*output)["leader_redirect"] = Substitute("$0/$1&redirects=$2",
+                                            leader_http_addr, path, redirects + 1);
+}
+
 } // namespace master
 } // namespace kudu

http://git-wip-us.apache.org/repos/asf/kudu/blob/4daa3ce8/src/kudu/master/master-path-handlers.h
----------------------------------------------------------------------
diff --git a/src/kudu/master/master-path-handlers.h b/src/kudu/master/master-path-handlers.h
index 9bd05c7..6195a74 100644
--- a/src/kudu/master/master-path-handlers.h
+++ b/src/kudu/master/master-path-handlers.h
@@ -73,6 +73,14 @@ class MasterPathHandlers {
   // Return a CSV of master addresses suitable for display.
   std::string MasterAddrsToCsv() const;
 
+  // If a leader master is known and has an http address, place it in leader_http_addr.
+  Status GetLeaderMasterHttpAddr(std::string* leader_http_addr) const;
+
+  // Adds the necessary properties to 'output' to set up a redirect to the leader master, or
+  // provide an error message if no redirect is possible.
+  // The redirect will link to <master web UI url>/path&redirects=(redirects + 1).
+  void SetupLeaderMasterRedirect(const std::string& path, int redirects, EasyJson* output) const;
+
   Master* master_;
   DISALLOW_COPY_AND_ASSIGN(MasterPathHandlers);
 };

http://git-wip-us.apache.org/repos/asf/kudu/blob/4daa3ce8/www/table.mustache
----------------------------------------------------------------------
diff --git a/www/table.mustache b/www/table.mustache
index 624edb3..575d3fc 100644
--- a/www/table.mustache
+++ b/www/table.mustache
@@ -19,6 +19,12 @@ under the License.
 {{#error}}
   <div class="text-error">{{.}}</div>
 {{/error}}
+{{#redirect_error}}
+  <div class="text-error">{{.}}</div>
+{{/redirect_error}}
+{{#leader_redirect}}
+  <div>You can find this page on the <a href="{{{.}}}">leader master's web UI</a>.</div>
+{{/leader_redirect}}
 {{^error}}
   <h2>Table: {{name}} ({{id}})</h2>
   <table class="table table-striped">
@@ -27,6 +33,7 @@ under the License.
     <tr><td>State:</td><td>{{state}} {{#state_msg}}({{.}}){{/state_msg}}</td></tr>
     </tbody>
   </table>
+
   <h3>Schema</h3>
   <table class='table table-striped'>
     <thead><tr>

http://git-wip-us.apache.org/repos/asf/kudu/blob/4daa3ce8/www/tables.mustache
----------------------------------------------------------------------
diff --git a/www/tables.mustache b/www/tables.mustache
index 670b4b8..e50fd8c 100644
--- a/www/tables.mustache
+++ b/www/tables.mustache
@@ -20,6 +20,12 @@ under the License.
 {{#error}}
   <div class="text-error">{{.}}</div>
 {{/error}}
+{{#redirect_error}}
+  <div class="text-error">{{.}}</div>
+{{/redirect_error}}
+{{#leader_redirect}}
+  <div>You can find this page on the <a href="{{{.}}}">leader master's web UI</a>.</div>
+{{/leader_redirect}}
 {{^error}}
 There are {{num_tables}} tables
 <table class="table table-striped">