Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions redis/src/storages/redis/impl/secdist_redis.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ struct RedisSettings {
std::vector<std::string> shards;
std::vector<HostPort> sentinels;
storages::redis::Password password{std::string()};
storages::redis::Password sentinel_password{std::string()};
storages::redis::ConnectionSecurity secure_connection{storages::redis::ConnectionSecurity::kNone};
std::size_t database_index{0};
};
Expand Down
4 changes: 2 additions & 2 deletions redis/src/storages/redis/impl/sentinel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,7 @@ std::shared_ptr<Sentinel> Sentinel::CreateSentinel(
const testsuite::RedisControl& testsuite_redis_control
) {
const auto& password = settings.password;
const auto& sentinel_password = settings.sentinel_password;

const std::vector<std::string>& shards = settings.shards;
LOG_DEBUG() << "shards.size() = " << shards.size();
Expand All @@ -193,13 +194,12 @@ std::shared_ptr<Sentinel> Sentinel::CreateSentinel(
LOG_DEBUG() << "sentinels.size() = " << settings.sentinels.size();
for (const auto& sentinel : settings.sentinels) {
LOG_DEBUG() << "sentinel: host = " << sentinel.host << " port = " << sentinel.port;
// SENTINEL MASTERS/SLAVES works without auth, sentinel has no AUTH command.
// CLUSTER SLOTS works after auth only. Masters and slaves used instead of
// sentinels in cluster mode.
conns.emplace_back(
sentinel.host,
sentinel.port,
(key_shard_factory.IsClusterStrategy() ? password : Password("")),
(key_shard_factory.IsClusterStrategy() ? password : sentinel_password),
false,
settings.secure_connection
);
Expand Down
65 changes: 65 additions & 0 deletions redis/src/storages/redis/impl/server_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include <storages/redis/impl/secdist_redis.hpp>
#include <storages/redis/impl/sentinel.hpp>
#include <storages/redis/impl/thread_pools.hpp>
#include <userver/dynamic_config/test_helpers.hpp>

USERVER_NAMESPACE_BEGIN

Expand Down Expand Up @@ -101,6 +102,70 @@ TEST(Redis, AuthTimeout) {
PeriodicCheck([&] { return !IsConnected(*redis); });
}

TEST(Redis, SentinelAuth) {
const size_t master_count = 1;
const size_t slave_count = 2;
const size_t sentinel_count = 3;
const int magic_value = 46;
const size_t redis_thread_count = 1;
const std::string redis_name = "redis_name";

auto init_server_array = [&](size_t size, const std::string& description, std::optional<int> magic_value_add
) -> std::vector<std::unique_ptr<MockRedisServer>> {
std::vector<std::unique_ptr<MockRedisServer>> servers;
for (size_t i = 0; i < size; i++) {
servers.emplace_back(std::make_unique<MockRedisServer>(description + '-' + std::to_string(i)));
auto& server = *servers.back();
server.RegisterPingHandler();
if (magic_value_add) {
server.RegisterHandlerWithConstReply("GET", *magic_value_add + i);
}
}
return servers;
};

auto masters = init_server_array(master_count, "masters", magic_value);
auto slaves = init_server_array(slave_count, "slaves", magic_value);
auto sentinels = init_server_array(sentinel_count, "sentinels", magic_value);
auto thread_pool = std::make_shared<storages::redis::impl::ThreadPools>(1, redis_thread_count);
std::shared_ptr<storages::redis::impl::Sentinel> sentinel_client;

std::vector<MockRedisServer::SlaveInfo> slave_infos;
for (const auto& slave : slaves) {
slave_infos.emplace_back(redis_name, kLocalhost, slave->GetPort());
}

for (auto& sentinel : sentinels) {
sentinel->RegisterSentinelMastersHandler({{redis_name, kLocalhost, masters.at(0)->GetPort()}});
sentinel->RegisterSentinelSlavesHandler(redis_name, slave_infos);
}

std::vector<MockRedisServer::HandlerPtr> auth_handlers;
for (auto& sentinel : sentinels) {
auth_handlers.push_back(sentinel->RegisterStatusReplyHandler("AUTH", "OK"));
}

secdist::RedisSettings settings;
settings.shards = {redis_name};
settings.sentinel_password = storages::redis::Password("pass");
for (const auto& sentinel : sentinels) {
settings.sentinels.emplace_back(kLocalhost, sentinel->GetPort());
}
sentinel_client = storages::redis::impl::Sentinel::CreateSentinel(
thread_pool, settings, "test_shard_group_name", dynamic_config::GetDefaultSource(), "test_client_name", {""}
);

sentinel_client->WaitConnectedDebug(slaves.empty());

for (auto& handler : auth_handlers) {
EXPECT_TRUE(handler->WaitForFirstReply(kSmallPeriod));
}

for (const auto& sentinel : sentinels) {
EXPECT_TRUE(sentinel->WaitForFirstPingReply(kSmallPeriod));
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a subscribe test too

}

TEST(Redis, Select) {
MockRedisServer server;
auto ping_handler = server.RegisterPingHandler();
Expand Down
8 changes: 6 additions & 2 deletions redis/src/storages/redis/impl/subscribe_sentinel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ std::shared_ptr<SubscribeSentinel> SubscribeSentinel::Create(
const testsuite::RedisControl& testsuite_redis_control
) {
const auto& password = settings.password;
const auto& sentinel_password = settings.password;

const std::vector<std::string>& shards = settings.shards;
LOG_DEBUG() << "shards.size() = " << shards.size();
Expand All @@ -153,11 +154,14 @@ std::shared_ptr<SubscribeSentinel> SubscribeSentinel::Create(
LOG_DEBUG() << "sentinels.size() = " << settings.sentinels.size();
for (const auto& sentinel : settings.sentinels) {
LOG_DEBUG() << "sentinel: host = " << sentinel.host << " port = " << sentinel.port;
// SENTINEL MASTERS/SLAVES works without auth, sentinel has no AUTH command.
// CLUSTER SLOTS works after auth only. Masters and slaves used instead of
// sentinels in cluster mode.
conns.emplace_back(
sentinel.host, sentinel.port, (is_cluster_mode ? password : Password("")), false, settings.secure_connection
sentinel.host,
sentinel.port,
(is_cluster_mode ? password : sentinel_password),
false,
settings.secure_connection
);
}
LOG_DEBUG() << "redis command_control: " << command_control.ToString();
Expand Down
2 changes: 2 additions & 0 deletions redis/src/storages/redis/redis_secdist.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ RedisMapSettings::RedisMapSettings(const formats::json::Value& doc) {

USERVER_NAMESPACE::secdist::RedisSettings settings;
settings.password = storages::redis::Password(GetString(client_settings, "password"));
settings.sentinel_password =
storages::redis::Password(client_settings["sentinel_password"].As<std::string>(""));
settings.secure_connection = GetValue<bool>(client_settings, "secure_connection", false)
? USERVER_NAMESPACE::storages::redis::ConnectionSecurity::kTLS
: USERVER_NAMESPACE::storages::redis::ConnectionSecurity::kNone;
Expand Down
Loading