Skip to content

Commit bddf1b8

Browse files
committed
chore: refactor VersionMonitor into a separate file
Signed-off-by: Roman Gershman <[email protected]>
1 parent 6905389 commit bddf1b8

File tree

5 files changed

+230
-190
lines changed

5 files changed

+230
-190
lines changed

src/server/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
add_executable(dragonfly dfly_main.cc)
1+
add_executable(dragonfly dfly_main.cc version_monitor.cc)
22
cxx_link(dragonfly base dragonfly_lib)
33

44
if (CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64" AND CMAKE_BUILD_TYPE STREQUAL "Release")

src/server/dfly_main.cc

Lines changed: 8 additions & 188 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,10 @@
2323
#endif
2424

2525
#include <mimalloc.h>
26-
#include <openssl/err.h>
2726
#include <signal.h>
2827

2928
#include <iostream>
3029
#include <memory>
31-
#include <regex>
3230

3331
#include "base/init.h"
3432
#include "base/proc_util.h" // for GetKernelVersion
@@ -40,10 +38,10 @@
4038
#include "server/generic_family.h"
4139
#include "server/main_service.h"
4240
#include "server/version.h"
41+
#include "server/version_monitor.h"
4342
#include "strings/human_readable.h"
4443
#include "util/accept_server.h"
4544
#include "util/fibers/pool.h"
46-
#include "util/http/http_client.h"
4745
#include "util/varz.h"
4846

4947
#ifdef __APPLE__
@@ -88,189 +86,6 @@ namespace {
8886

8987
using util::http::TlsClient;
9088

91-
std::optional<std::string> GetVersionString(const std::string& version_str) {
92-
// The server sends a message such as {"latest": "0.12.0"}
93-
const auto reg_match_expr = R"(\{\"latest"\:[ \t]*\"([0-9]+\.[0-9]+\.[0-9]+)\"\})";
94-
VLOG(1) << "checking version '" << version_str << "'";
95-
auto const regex = std::regex(reg_match_expr);
96-
std::smatch match;
97-
if (std::regex_match(version_str, match, regex) && match.size() > 1) {
98-
// the second entry is the match to the group that holds the version string
99-
return match[1].str();
100-
} else {
101-
LOG_FIRST_N(WARNING, 1) << "Remote version - invalid version number: '" << version_str << "'";
102-
return std::nullopt;
103-
}
104-
}
105-
106-
std::optional<std::string> GetRemoteVersion(ProactorBase* proactor, SSL_CTX* ssl_context,
107-
const std::string host, std::string_view service,
108-
const std::string& resource,
109-
const std::string& ver_header) {
110-
namespace bh = boost::beast::http;
111-
using ResponseType = bh::response<bh::string_body>;
112-
113-
bh::request<bh::string_body> req{bh::verb::get, resource, 11 /*http 1.1*/};
114-
req.set(bh::field::host, host);
115-
req.set(bh::field::user_agent, ver_header);
116-
ResponseType res;
117-
TlsClient http_client{proactor};
118-
http_client.set_connect_timeout_ms(2000);
119-
120-
auto ec = http_client.Connect(host, service, ssl_context);
121-
122-
if (ec) {
123-
LOG_FIRST_N(WARNING, 1) << "Remote version - connection error [" << host << ":" << service
124-
<< "] : " << ec.message();
125-
return nullopt;
126-
}
127-
128-
ec = http_client.Send(req, &res);
129-
if (!ec) {
130-
VLOG(1) << "successfully got response from HTTP GET for host " << host << ":" << service << "/"
131-
<< resource << " response code is " << res.result();
132-
133-
if (res.result() == bh::status::ok) {
134-
return GetVersionString(res.body());
135-
}
136-
} else {
137-
static bool is_logged{false};
138-
if (!is_logged) {
139-
is_logged = true;
140-
141-
#if (OPENSSL_VERSION_NUMBER >= 0x30000000L)
142-
const char* func_err = "ssl_internal_error";
143-
#else
144-
const char* func_err = ERR_func_error_string(ec.value());
145-
#endif
146-
147-
// Unfortunately AsioStreamAdapter looses the original error category
148-
// because std::error_code can not be converted into boost::system::error_code.
149-
// It's fixed in later versions of Boost, but for now we assume it's from TLS.
150-
LOG(WARNING) << "Remote version - HTTP GET error [" << host << ":" << service << resource
151-
<< "], error: " << ec.value();
152-
LOG(WARNING) << "ssl error: " << func_err << "/" << ERR_reason_error_string(ec.value());
153-
}
154-
}
155-
156-
return nullopt;
157-
}
158-
159-
struct VersionMonitor {
160-
Fiber version_fiber_;
161-
Done monitor_ver_done_;
162-
163-
void Run(ProactorPool* proactor_pool);
164-
165-
void Shutdown() {
166-
monitor_ver_done_.Notify();
167-
if (version_fiber_.IsJoinable()) {
168-
version_fiber_.Join();
169-
}
170-
}
171-
172-
private:
173-
struct SslDeleter {
174-
void operator()(SSL_CTX* ssl) {
175-
if (ssl) {
176-
TlsClient::FreeContext(ssl);
177-
}
178-
}
179-
};
180-
181-
using SslPtr = std::unique_ptr<SSL_CTX, SslDeleter>;
182-
void RunTask(SslPtr);
183-
184-
bool IsVersionOutdated(std::string_view remote, std::string_view current) const;
185-
};
186-
187-
bool VersionMonitor::IsVersionOutdated(const std::string_view remote,
188-
const std::string_view current) const {
189-
const absl::InlinedVector<absl::string_view, 3> remote_xyz = absl::StrSplit(remote, ".");
190-
const absl::InlinedVector<absl::string_view, 3> current_xyz = absl::StrSplit(current, ".");
191-
if (remote_xyz.size() != current_xyz.size()) {
192-
LOG(WARNING) << "Can't compare Dragonfly version " << current << " to latest version "
193-
<< remote;
194-
return false;
195-
}
196-
const auto print_to_log = [](const std::string_view version, const absl::string_view part) {
197-
LOG(WARNING) << "Can't parse " << version << " part of version " << part << " as a number";
198-
};
199-
for (size_t i = 0; i < remote_xyz.size(); ++i) {
200-
size_t remote_x = 0;
201-
if (!absl::SimpleAtoi(remote_xyz[i], &remote_x)) {
202-
print_to_log(remote, remote_xyz[i]);
203-
return false;
204-
}
205-
size_t current_x = 0;
206-
if (!absl::SimpleAtoi(current_xyz[i], &current_x)) {
207-
print_to_log(current, current_xyz[i]);
208-
return false;
209-
}
210-
if (remote_x > current_x) {
211-
return true;
212-
}
213-
214-
if (remote_x < current_x) {
215-
return false;
216-
}
217-
}
218-
219-
return false;
220-
}
221-
222-
void VersionMonitor::Run(ProactorPool* proactor_pool) {
223-
// Avoid running dev environments.
224-
if (getenv("DFLY_DEV_ENV")) {
225-
LOG(WARNING) << "Running in dev environment (DFLY_DEV_ENV is set) - version monitoring is "
226-
"disabled";
227-
return;
228-
}
229-
// not a production release tag.
230-
if (!GetFlag(FLAGS_version_check) || kGitTag[0] != 'v' || strchr(kGitTag, '-')) {
231-
return;
232-
}
233-
234-
SslPtr ssl_ctx(TlsClient::CreateSslContext());
235-
if (!ssl_ctx) {
236-
VLOG(1) << "Remote version - failed to create SSL context - cannot run version monitoring";
237-
return;
238-
}
239-
240-
version_fiber_ = proactor_pool->GetNextProactor()->LaunchFiber(
241-
[ssl_ctx = std::move(ssl_ctx), this]() mutable { RunTask(std::move(ssl_ctx)); });
242-
}
243-
244-
void VersionMonitor::RunTask(SslPtr ssl_ctx) {
245-
const auto loop_sleep_time = std::chrono::hours(24); // every 24 hours
246-
247-
const std::string host_name = "version.dragonflydb.io";
248-
const std::string_view port = "443";
249-
const std::string resource = "/v1";
250-
string_view current_version(kGitTag);
251-
252-
current_version.remove_prefix(1);
253-
const std::string version_header = absl::StrCat("DragonflyDB/", current_version);
254-
255-
ProactorBase* my_pb = ProactorBase::me();
256-
while (true) {
257-
const std::optional<std::string> remote_version =
258-
GetRemoteVersion(my_pb, ssl_ctx.get(), host_name, port, resource, version_header);
259-
if (remote_version) {
260-
const std::string_view rv = remote_version.value();
261-
if (IsVersionOutdated(rv, current_version)) {
262-
LOG_FIRST_N(INFO, 1) << "Your current version '" << current_version
263-
<< "' is not the latest version. A newer version '" << rv
264-
<< "' is now available. Please consider an update.";
265-
}
266-
}
267-
if (monitor_ver_done_.WaitFor(loop_sleep_time)) {
268-
VLOG(1) << "finish running version monitor task";
269-
return;
270-
}
271-
}
272-
}
273-
27489
enum class TermColor { kDefault, kRed, kGreen, kYellow };
27590
// Returns the ANSI color code for the given color. TermColor::kDefault is
27691
// an invalid input.
@@ -459,7 +274,11 @@ bool RunEngine(ProactorPool* pool, AcceptServer* acceptor) {
459274
service.Init(acceptor, listeners, opts);
460275

461276
VersionMonitor version_monitor;
462-
version_monitor.Run(pool);
277+
278+
// check if it's a production release tag.
279+
if (GetFlag(FLAGS_version_check) && kGitTag[0] == 'v' && strchr(kGitTag, '-') == nullptr) {
280+
version_monitor.Run(pool);
281+
}
463282

464283
// Start the acceptor loop and wait for the server to shutdown.
465284
acceptor->Run();
@@ -733,9 +552,10 @@ void PrintBasicUsageInfo() {
733552

734553
void ParseFlagsFromEnv() {
735554
if (getenv("DFLY_PASSWORD")) {
736-
LOG(WARNING)
555+
LOG(FATAL)
737556
<< "DFLY_PASSWORD environment variable is being deprecated in favour of DFLY_requirepass";
738557
}
558+
739559
// Allowed environment variable names that can have
740560
// DFLY_ prefix, but don't necessarily have an ABSL flag created
741561
absl::flat_hash_set<std::string_view> ignored_environment_flag_names = {"DEV_ENV", "PASSWORD"};

0 commit comments

Comments
 (0)