Skip to content

Commit 07da2e1

Browse files
committed
Merge branch 'mysql-8.0' into mysql-trunk
Change-Id: I372535645d5a08d6d653b160756454c26c499e4d
2 parents 3a12f23 + bbf229d commit 07da2e1

File tree

3 files changed

+144
-33
lines changed

3 files changed

+144
-33
lines changed

router/src/harness/src/keyring/keyring_manager.cc

Lines changed: 57 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
#include "dim.h"
3333
#include "keyring/keyring_file.h"
3434
#include "keyring/master_key_file.h"
35+
#include "mysql/harness/filesystem.h"
3536
#include "random_generator.h"
3637

3738
/*
@@ -96,40 +97,53 @@ static std::pair<std::string, std::string> get_master_key(
9697
if (e.code() != std::errc::no_such_file_or_directory || !create_if_needed) {
9798
throw;
9899
}
99-
}
100100

101-
std::string master_key;
102-
// get the key for the keyring from the master key file, decrypting it with
103-
// the scramble
104-
if (!master_scramble.empty()) {
105-
try {
106-
// look up for the master_key for this given keyring file
107-
master_key = mkf.get(keyring_file_path, master_scramble);
108-
} catch (const std::out_of_range &) {
109-
// missing key will be handled further down
110-
}
101+
return {};
111102
}
112103

104+
// look up for the master_key for this given keyring file
105+
std::string master_key =
106+
mkf.get(mysql_harness::Path(keyring_file_path).real_path().str(),
107+
master_scramble);
108+
109+
// if there is no master key for the "read-path' based keyring path, try to
110+
// lookup via the path itself.
113111
if (master_key.empty()) {
114-
if (!create_if_needed)
115-
throw std::runtime_error("Master key for keyring at '" +
116-
keyring_file_path + "' could not be read");
117-
// if the master key doesn't exist anywhere yet, generate one and store it
118-
mysql_harness::RandomGeneratorInterface &rg =
119-
mysql_harness::DIM::instance().get_RandomGenerator();
120-
master_key = rg.generate_strong_password(kKeyLength);
121-
// scramble to encrypt the master key with, which should be stored in the
122-
// keyring
112+
master_key = mkf.get(keyring_file_path, master_scramble);
113+
}
114+
115+
return {master_key, master_scramble};
116+
}
117+
118+
static std::pair<std::string, std::string> create_initial_keyring_pair(
119+
MasterKeyFile &mkf, const std::string &keyring_file_path,
120+
std::string master_scramble) {
121+
// if the master key doesn't exist anywhere yet, generate one and store it
122+
mysql_harness::RandomGeneratorInterface &rg =
123+
mysql_harness::DIM::instance().get_RandomGenerator();
124+
std::string master_key = rg.generate_strong_password(kKeyLength);
125+
126+
// scramble to encrypt the master key with, which should be stored in the
127+
// keyring
128+
if (master_scramble.empty()) {
123129
master_scramble = rg.generate_strong_password(kKeyLength);
124-
mkf.add(keyring_file_path, master_key, master_scramble);
130+
131+
KeyringFile kf;
132+
kf.set_header(master_scramble);
133+
kf.save(keyring_file_path, master_key);
125134
}
126-
return std::make_pair(master_key, master_scramble);
135+
136+
// use the real-path to store/lookup the keyring
137+
mkf.add(mysql_harness::Path(keyring_file_path).real_path().str(), master_key,
138+
master_scramble);
139+
140+
mkf.save();
141+
142+
return {master_key, master_scramble};
127143
}
128144

129145
bool init_keyring(const std::string &keyring_file_path,
130146
const std::string &master_key_path, bool create_if_needed) {
131-
std::string master_key;
132-
std::string master_scramble;
133147
MasterKeyFile mkf(master_key_path);
134148

135149
try {
@@ -142,30 +156,40 @@ bool init_keyring(const std::string &keyring_file_path,
142156
}
143157

144158
// throws std::runtime_error (anything else?)
145-
std::tie(master_key, master_scramble) =
159+
auto [master_key, master_scramble] =
146160
get_master_key(mkf, keyring_file_path, create_if_needed);
147161

148-
bool existed =
149-
init_keyring_with_key(keyring_file_path, master_key, create_if_needed);
150-
if (create_if_needed && !existed) {
151-
g_keyring->set_header(master_scramble);
152-
flush_keyring();
162+
// if there is a master-scramble, the
163+
const bool keyring_existed{!master_scramble.empty()};
164+
165+
if (master_key.empty()) {
166+
if (!create_if_needed) {
167+
throw std::runtime_error("Master key for keyring at '" +
168+
keyring_file_path + "' could not be read");
169+
}
170+
153171
try {
154-
mkf.save();
172+
std::tie(master_key, master_scramble) =
173+
create_initial_keyring_pair(mkf, keyring_file_path, master_scramble);
155174
} catch (const std::system_error &e) {
156175
throw std::system_error(
157176
e.code(), "Unable to save master key to " + master_key_path);
158177
}
159178
}
160-
return existed;
179+
180+
// load the keyring.
181+
init_keyring_with_key(keyring_file_path, master_key, false);
182+
183+
return keyring_existed;
161184
}
162185

163186
bool init_keyring_with_key(const std::string &keyring_file_path,
164187
const std::string &master_key,
165188
bool create_if_needed) {
166189
if (g_keyring) throw std::logic_error("Keyring already initialized");
167190
bool existed = false;
168-
std::unique_ptr<KeyringFile> key_store(new KeyringFile());
191+
192+
auto key_store = std::make_unique<KeyringFile>();
169193
try {
170194
key_store->load(keyring_file_path, master_key);
171195
existed = true;

router/src/harness/tests/test_keyring_manager.cc

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -392,6 +392,38 @@ TEST(KeyringManager, regression) {
392392
mysql_harness::reset_keyring();
393393
}
394394

395+
#ifndef _WIN32
396+
TEST(KeyringManager, symlink_dir) {
397+
SCOPED_TRACE("// prepare symlinked directory");
398+
TempDirectory tmpdir;
399+
400+
auto subdir = mysql_harness::Path(tmpdir.name()).join("subdir").str();
401+
auto symlinkdir = mysql_harness::Path(tmpdir.name()).join("symlink").str();
402+
ASSERT_EQ(mysql_harness::mkdir(subdir, 0700), 0);
403+
ASSERT_EQ(symlink(subdir.c_str(), symlinkdir.c_str()), 0);
404+
405+
auto keyring = symlinkdir + "/keyring";
406+
auto masterring = symlinkdir + "/keyfile";
407+
408+
SCOPED_TRACE("// create the encrypted keyring.");
409+
EXPECT_FALSE(mysql_harness::init_keyring(keyring, masterring, true));
410+
mysql_harness::reset_keyring();
411+
412+
SCOPED_TRACE("// try to open it again, via the symlink dir.");
413+
EXPECT_TRUE(mysql_harness::init_keyring(keyring, masterring, false));
414+
mysql_harness::reset_keyring();
415+
416+
SCOPED_TRACE("// try to open it again, via the real dir.");
417+
EXPECT_TRUE(
418+
mysql_harness::init_keyring(subdir + "/keyring", masterring, false));
419+
mysql_harness::reset_keyring();
420+
421+
SCOPED_TRACE("// try to open it again, via the real dir.");
422+
EXPECT_TRUE(mysql_harness::init_keyring(subdir + "/keyring",
423+
subdir + "/keyfile", false));
424+
}
425+
#endif
426+
395427
int main(int argc, char **argv) {
396428
::testing::InitGoogleTest(&argc, argv);
397429
return RUN_ALL_TESTS();

router/tests/component/test_bootstrap.cc

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929

3030
#include <fstream>
3131
#include <string>
32+
#include <system_error>
3233

3334
#include <gmock/gmock-matchers.h>
3435
#include <gtest/gtest.h>
@@ -61,6 +62,7 @@
6162
#include "script_generator.h"
6263
#include "socket_operations.h"
6364
#include "tcp_port_pool.h"
65+
#include "test/temp_directory.h"
6466

6567
/**
6668
* @file
@@ -74,6 +76,59 @@ using mysqlrouter::ClusterType;
7476
// for the test with no param
7577
class RouterBootstrapTest : public RouterComponentBootstrapTest {};
7678

79+
#ifndef _WIN32
80+
// needs symlink()
81+
TEST_F(RouterBootstrapTest, bootstrap_and_run_from_symlinked_dir) {
82+
RecordProperty("Description",
83+
"Bootstrap into a symlinked directory and check that the "
84+
"router can run from that directory.");
85+
const auto server_port = port_pool_.get_next_available();
86+
const auto server_x_port = port_pool_.get_next_available();
87+
const auto http_port = port_pool_.get_next_available();
88+
89+
std::vector<Config> config{
90+
{"127.0.0.1", server_port, http_port,
91+
get_data_dir().join("bootstrap_gr.js").str()},
92+
};
93+
94+
SCOPED_TRACE("// prepare symlinked directory");
95+
TempDirectory tmpdir;
96+
97+
auto subdir = mysql_harness::Path(tmpdir.name()).join("subdir").str();
98+
auto symlinkdir = mysql_harness::Path(tmpdir.name()).join("symlink").str();
99+
ASSERT_EQ(mysql_harness::mkdir(subdir, 0700), 0);
100+
ASSERT_EQ(symlink(subdir.c_str(), symlinkdir.c_str()), 0);
101+
102+
// point the bootstrap at the symlink dir.
103+
bootstrap_dir.reset(symlinkdir);
104+
105+
SCOPED_TRACE("// bootstrap into the symlink dir");
106+
ASSERT_NO_FATAL_FAILURE(bootstrap_failover(
107+
config, ClusterType::GR_V2, {}, EXIT_SUCCESS, {}, 30s, {2, 0, 3},
108+
{
109+
"--conf-set-option=DEFAULT.plugin_folder=" +
110+
mysql_harness::get_plugin_dir(get_origin().str()),
111+
"--conf-set-option=DEFAULT.logging_folder=" + get_logging_dir().str(),
112+
"--conf-set-option=DEFAULT.keyring_path=" + symlinkdir +
113+
"/data/keyring",
114+
}));
115+
116+
SCOPED_TRACE("// launch mock-server for router");
117+
const std::string runtime_json_stmts =
118+
get_data_dir().join("metadata_dynamic_nodes_v2_gr.js").str();
119+
120+
// launch mock server that is our metadata server
121+
launch_mysql_server_mock(runtime_json_stmts, server_port, EXIT_SUCCESS, false,
122+
http_port);
123+
set_mock_metadata(http_port, "cluster-specific-id",
124+
{GRNode{server_port, "uuid-1"}}, 0,
125+
{ClusterNode{server_port, "uuid-1", server_x_port}});
126+
127+
SCOPED_TRACE("// launch router with bootstrapped config");
128+
launch_router({"-c", bootstrap_dir.name() + "/mysqlrouter.conf"});
129+
}
130+
#endif
131+
77132
struct BootstrapTestParam {
78133
ClusterType cluster_type;
79134
std::string description;

0 commit comments

Comments
 (0)