Skip to content

Commit 8d86e9b

Browse files
authored
feat(server): adding version monitoring task (dragonflydb#586)
feat(server): adding version monoitoring task Signed-off-by: Boaz Sade <[email protected]> Signed-off-by: Boaz Sade <[email protected]>
1 parent bbc809a commit 8d86e9b

File tree

3 files changed

+123
-3
lines changed

3 files changed

+123
-3
lines changed

src/server/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ add_library(dragonfly_lib channel_slice.cc command_registry.cc
2121
zset_family.cc version.cc bitops_family.cc container_utils.cc
2222
serializer_commons.cc journal/serializer.cc journal/executor.cc)
2323

24-
cxx_link(dragonfly_lib dfly_transaction dfly_facade redis_lib strings_lib html_lib
24+
cxx_link(dragonfly_lib dfly_transaction dfly_facade redis_lib strings_lib html_lib http_client_lib
2525
absl::random_random TRDP::jsoncons zstd TRDP::lz4)
2626

2727
add_library(dfly_test_lib test_utils.cc)

src/server/dfly_main.cc

Lines changed: 121 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
#include <mimalloc.h>
1717
#include <signal.h>
1818

19+
#include <regex>
20+
1921
#include "base/init.h"
2022
#include "base/proc_util.h" // for GetKernelVersion
2123
#include "facade/dragonfly_listener.h"
@@ -28,6 +30,7 @@
2830
#include "strings/human_readable.h"
2931
#include "util/accept_server.h"
3032
#include "util/epoll/epoll_pool.h"
33+
#include "util/http/http_client.h"
3134
#include "util/uring/uring_pool.h"
3235
#include "util/varz.h"
3336

@@ -85,6 +88,9 @@ ABSL_FLAG(MaxMemoryFlag, maxmemory, MaxMemoryFlag(0),
8588
"0 - means the program will automatically determine its maximum memory usage. "
8689
"default: 0");
8790

91+
ABSL_FLAG(bool, version_check, true,
92+
"If true, Will monitor for new releases on Dragonfly servers once a day.");
93+
8894
using namespace util;
8995
using namespace facade;
9096
using namespace io;
@@ -96,6 +102,117 @@ namespace dfly {
96102

97103
namespace {
98104

105+
using util::http::TlsClient;
106+
107+
const std::string_view kInvalidVersion{"unknown version"};
108+
109+
std::string GetVersionString(const std::string& from) {
110+
// The server sends a message such as {"latest": "0.12.0"}
111+
const auto reg_match_expr = R"(\{\"latest"\:[ \t]*\"([0-9]+\.[0-9]+\.[0-9]+)\"\})";
112+
VLOG(1) << "checking version '" << from << "'";
113+
auto const regex = std::regex(reg_match_expr);
114+
std::smatch match;
115+
if (std::regex_match(from, match, regex) && match.size() > 1) {
116+
// the second entry is the match to the group that holds the version string
117+
return match[1].str();
118+
} else {
119+
return std::string{kInvalidVersion};
120+
}
121+
}
122+
123+
std::string GetRemoteVersion(ProactorBase* proactor, SSL_CTX* ssl_context, const std::string host,
124+
std::string_view service, const std::string& resource,
125+
const std::string& ver_header) {
126+
namespace bh = boost::beast::http;
127+
using ResponseType = bh::response<bh::string_body>;
128+
129+
bh::request<bh::string_body> req{bh::verb::get, resource, 11 /*http 1.1*/};
130+
req.set(bh::field::host, host);
131+
req.set(bh::field::user_agent, ver_header);
132+
ResponseType res;
133+
TlsClient http_client{proactor};
134+
http_client.set_connect_timeout_ms(2000);
135+
136+
auto ec = http_client.Connect(host, service, ssl_context);
137+
if (!ec) {
138+
ec = http_client.Send(req, &res);
139+
if (!ec) {
140+
VLOG(1) << "successfully got response from HTTP GET for host " << host << ":" << service
141+
<< "/" << resource << " response code is " << res.result();
142+
143+
if (res.result() == bh::status::ok) {
144+
return GetVersionString(res.body());
145+
}
146+
} else {
147+
VLOG(1) << "failed to process send HTTP GET to " << host << "@" << service << " at resource '"
148+
<< resource << "', error: " << ec.message();
149+
}
150+
} else {
151+
VLOG(1) << "failed to connect: " << ec.message();
152+
}
153+
return std::string{kInvalidVersion};
154+
}
155+
156+
struct VersionMonitor {
157+
fibers_ext::Fiber version_fiber_;
158+
fibers_ext::Done monitor_ver_done_;
159+
160+
void Run(ProactorPool* proactor_pool) {
161+
if (GetFlag(FLAGS_version_check)) {
162+
version_fiber_ = proactor_pool->GetNextProactor()->LaunchFiber([this] { RunTask(); });
163+
}
164+
}
165+
166+
void Shutdown() {
167+
monitor_ver_done_.Notify();
168+
if (version_fiber_.IsJoinable()) {
169+
version_fiber_.Join();
170+
}
171+
}
172+
173+
private:
174+
void RunTask();
175+
};
176+
177+
void VersionMonitor::RunTask() {
178+
const auto loop_sleep_time = std::chrono::hours(24); // every 24 hours
179+
180+
const std::string host_name = "version.dragonflydb.io";
181+
const std::string_view port = "443";
182+
const std::string resource = "/v1";
183+
const std::string dev_version_name = "dev";
184+
const std::string version_header = std::string("DragonflyDB/") + kGitTag;
185+
186+
// Don't run in dev environment - i.e. where we don't build with
187+
// real version number
188+
if (kGitTag == dev_version_name) {
189+
return;
190+
}
191+
192+
SSL_CTX* context = TlsClient::CreateSslContext();
193+
if (!context) {
194+
VLOG(1) << "failed to create SSL context - cannot run version monitoring";
195+
return;
196+
}
197+
198+
ProactorBase* my_pb = ProactorBase::me();
199+
while (true) {
200+
const std::string remote_version =
201+
GetRemoteVersion(my_pb, context, host_name, port, resource, version_header);
202+
203+
if (remote_version != kGitTag) {
204+
LOG_FIRST_N(INFO, 1) << "Your current version '" << kGitTag
205+
<< "' is not the latest version. A newer version '" << remote_version
206+
<< "' is now available. Please consider an update.";
207+
}
208+
if (monitor_ver_done_.WaitFor(loop_sleep_time)) {
209+
TlsClient::FreeContext(context);
210+
VLOG(1) << "finish running version monitor task";
211+
return;
212+
}
213+
}
214+
}
215+
99216
enum class TermColor { kDefault, kRed, kGreen, kYellow };
100217
// Returns the ANSI color code for the given color. TermColor::kDefault is
101218
// an invalid input.
@@ -195,9 +312,12 @@ bool RunEngine(ProactorPool* pool, AcceptServer* acceptor) {
195312
acceptor->AddListener(mc_port, new Listener{Protocol::MEMCACHE, &service});
196313
}
197314

315+
VersionMonitor version_monitor;
316+
198317
acceptor->Run();
318+
version_monitor.Run(pool);
199319
acceptor->Wait();
200-
320+
version_monitor.Shutdown();
201321
service.Shutdown();
202322

203323
if (unlink_uds) {

0 commit comments

Comments
 (0)