From 49e53d550eac2f1cefba998706500fce4570ba8c Mon Sep 17 00:00:00 2001 From: Boaz Sade Date: Thu, 19 Jan 2023 19:59:07 +0200 Subject: [PATCH] feat(server): admin port and interface (#709) * feat(server): admin port and interface Signed-off-by: Boaz Sade --- README.md | 3 +++ src/facade/dragonfly_connection.cc | 26 +++++++++++++++++++++++--- src/server/dfly_main.cc | 21 +++++++++++++++++++++ 3 files changed, 47 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 55a52d358778..e329bf793777 100644 --- a/README.md +++ b/README.md @@ -143,6 +143,9 @@ In addition, it has Dragonfly specific arguments options: * `hz` - key expiry evaluation frequency. Default is 100. Lower frequency uses less cpu when idle at the expense of slower eviction rate. * `save_schedule` - glob spec for the UTC time to save a snapshot which matches HH:MM (24h time). default: "" + * `primary_port_http_enabled` - If true allows accessing http console on main TCP port, default: true + * `admin_port` - If set, would enable admin access to console on the assigned port. This supports both HTTP and RESP protocols. default disabled + * `admin_bind` - If set, the admin consol TCP connection would be bind the given address. This supports both HTTP and RESP protocols. default any ### Example Start Script, with popular options: diff --git a/src/facade/dragonfly_connection.cc b/src/facade/dragonfly_connection.cc index d5dae41f1f07..e730af0b799e 100644 --- a/src/facade/dragonfly_connection.cc +++ b/src/facade/dragonfly_connection.cc @@ -25,7 +25,17 @@ ABSL_FLAG(bool, tcp_nodelay, false, "Configures dragonfly connections with socket option TCP_NODELAY"); -ABSL_FLAG(bool, http_admin_console, true, "If true allows accessing http console on main TCP port"); +ABSL_FLAG(bool, primary_port_http_enabled, true, + "If true allows accessing http console on main TCP port"); + +ABSL_FLAG( + std::uint16_t, admin_port, 0, + "If set, would enable admin access to console on the assigned port. This supports both HTTP " + "and RESP protocols"); +ABSL_FLAG(std::string, admin_bind, "", + "If set, the admin consol TCP connection would be bind the given address. " + "This supports both HTTP and RESP " + "protocols"); using namespace util; using namespace std; @@ -326,8 +336,8 @@ void Connection::HandleRequests() { #endif io::Result http_res{false}; - if (absl::GetFlag(FLAGS_http_admin_console)) - http_res = CheckForHttpProto(peer); + + http_res = CheckForHttpProto(peer); if (http_res) { if (*http_res) { @@ -404,6 +414,16 @@ uint32 Connection::GetClientId() const { } io::Result Connection::CheckForHttpProto(FiberSocketBase* peer) { + bool enabled = absl::GetFlag(FLAGS_primary_port_http_enabled); + if (!enabled) { + uint16_t admin_port = absl::GetFlag(FLAGS_admin_port); + // check if this connection is from the admin port, if so, override primary_port_http_enabled + LinuxSocketBase* lsb = static_cast(socket_.get()); + enabled = lsb->LocalEndpoint().port() == admin_port; + } + if (!enabled) { + return false; + } size_t last_len = 0; do { auto buf = io_buf_.AppendBuffer(); diff --git a/src/server/dfly_main.cc b/src/server/dfly_main.cc index 57f9155c9260..01cd3afdca22 100644 --- a/src/server/dfly_main.cc +++ b/src/server/dfly_main.cc @@ -72,6 +72,8 @@ std::string AbslUnparseFlag(const MaxMemoryFlag& flag) { ABSL_DECLARE_FLAG(uint32_t, port); ABSL_DECLARE_FLAG(uint32_t, dbnum); ABSL_DECLARE_FLAG(uint32_t, memcache_port); +ABSL_DECLARE_FLAG(uint16_t, admin_port); +ABSL_DECLARE_FLAG(std::string, admin_bind); ABSL_FLAG(bool, use_large_pages, false, "If true - uses large memory pages for allocations"); ABSL_FLAG(string, bind, "", @@ -319,6 +321,25 @@ bool RunEngine(ProactorPool* pool, AcceptServer* acceptor) { } } + std::uint16_t admin_port = GetFlag(FLAGS_admin_port); + if (admin_port != 0) { + const std::string& admin_bind = GetFlag(FLAGS_admin_bind); + // Note passing the result of c_str() for empty string in optimized mode don't work, we must + // explicitly set this to null in this case + const char* interface_addr = admin_bind.empty() ? nullptr : admin_bind.c_str(); + const std::string printable_addr = + absl::StrCat("admin socket ", interface_addr ? interface_addr : "any", ":", admin_port); + Listener* admin_listener = new Listener{Protocol::REDIS, &service}; + error_code ec = acceptor->AddListener(interface_addr, admin_port, admin_listener); + + if (ec) { + LOG(ERROR) << "Failed to open " << printable_addr << ", error: " << ec.message(); + delete admin_listener; + } else { + LOG(INFO) << "Listening on " << printable_addr; + } + } + error_code ec = acceptor->AddListener(bind_addr, port, main_listener); if (ec) {