/Users/deen/code/yugabyte-db/ent/src/yb/integration-tests/encryption-test.cc
Line | Count | Source (jump to first uncovered line) |
1 | | // Copyright (c) YugaByte, Inc. |
2 | | // |
3 | | // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except |
4 | | // in compliance with the License. You may obtain a copy of the License at |
5 | | // |
6 | | // http://www.apache.org/licenses/LICENSE-2.0 |
7 | | // |
8 | | // Unless required by applicable law or agreed to in writing, software distributed under the License |
9 | | // is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
10 | | // or implied. See the License for the specific language governing permissions and limitations |
11 | | // under the License. |
12 | | // |
13 | | |
14 | | #include <gtest/gtest.h> |
15 | | |
16 | | #include "yb/client/table.h" |
17 | | #include "yb/client/table_handle.h" |
18 | | #include "yb/client/yb_table_name.h" |
19 | | |
20 | | #include "yb/integration-tests/cluster_itest_util.h" |
21 | | #include "yb/integration-tests/yb_table_test_base.h" |
22 | | #include "yb/integration-tests/yb_mini_cluster_test_base.h" |
23 | | #include "yb/integration-tests/cluster_verifier.h" |
24 | | |
25 | | #include "yb/master/encryption_manager.h" |
26 | | |
27 | | #include "yb/tools/yb-admin_client.h" |
28 | | |
29 | | #include "yb/util/test_util.h" |
30 | | #include "yb/util/random_util.h" |
31 | | #include "yb/util/status_log.h" |
32 | | #include "yb/util/stol_utils.h" |
33 | | #include "yb/util/string_util.h" |
34 | | |
35 | | DECLARE_int64(db_write_buffer_size); |
36 | | DECLARE_int32(memstore_size_mb); |
37 | | DECLARE_int32(load_balancer_max_concurrent_tablet_remote_bootstraps); |
38 | | DECLARE_int32(heartbeat_interval_ms); |
39 | | DECLARE_int64(encryption_counter_min); |
40 | | DECLARE_int64(encryption_counter_max); |
41 | | |
42 | | using namespace std::chrono_literals; |
43 | | |
44 | | namespace yb { |
45 | | namespace integration_tests { |
46 | | |
47 | | constexpr uint32_t kNumKeys = 1024; |
48 | | constexpr uint32_t kNumFlushes = 8; |
49 | | constexpr uint32_t kNumCompactions = 2; |
50 | | constexpr uint32_t kKeySize = 100; |
51 | | constexpr uint32_t kCounterOverflowDefault = 0xFFFFFFE0; |
52 | | |
53 | | |
54 | | class EncryptionTest : public YBTableTestBase, public testing::WithParamInterface<bool> { |
55 | | public: |
56 | | |
57 | 0 | bool use_external_mini_cluster() override { return true; } |
58 | | |
59 | 0 | bool use_yb_admin_client() override { return true; } |
60 | | |
61 | 0 | bool enable_ysql() override { return false; } |
62 | | |
63 | 0 | size_t num_tablet_servers() override { |
64 | 0 | return 3; |
65 | 0 | } |
66 | | |
67 | 0 | size_t num_masters() override { |
68 | 0 | return 3; |
69 | 0 | } |
70 | | |
71 | 0 | int num_tablets() override { |
72 | 0 | return 3; |
73 | 0 | } |
74 | | |
75 | 0 | void SetUp() override { |
76 | 0 | FLAGS_load_balancer_max_concurrent_tablet_remote_bootstraps = 1; |
77 | |
|
78 | 0 | YBTableTestBase::SetUp(); |
79 | 0 | } |
80 | | |
81 | | void BeforeCreateTable() override { |
82 | | ASSERT_NO_FATALS(AddUniverseKeys()); |
83 | | ASSERT_NO_FATALS(RotateKey()); |
84 | | // Wait for the key to be propagated to tserver through heartbeat. |
85 | | SleepFor(MonoDelta::FromMilliseconds(2 * FLAGS_heartbeat_interval_ms)); |
86 | | } |
87 | | |
88 | 0 | void WriteWorkload(uint32_t start, uint32_t end) { |
89 | 0 | auto total_num_keys = end - start; |
90 | 0 | for (uint32_t i = start; i < end; i++) { |
91 | 0 | string s(kKeySize, 'a' + (i % 26)); |
92 | 0 | PutKeyValue(Format("k_$0", i), s); |
93 | 0 | auto num_keys_written = i - start + 1; |
94 | 0 | if (num_keys_written % (total_num_keys / kNumFlushes) == 0) { |
95 | 0 | ASSERT_OK(client_->FlushTables({table_->id()}, false, 30, false)); |
96 | 0 | } |
97 | 0 | if (num_keys_written % (total_num_keys / kNumCompactions) == 0) { |
98 | 0 | ASSERT_OK(client_->FlushTables({table_->id()}, false, 30, true)); |
99 | 0 | } |
100 | 0 | } |
101 | 0 | } |
102 | | |
103 | 0 | void VerifyWrittenRecords() { |
104 | 0 | auto result_kvs = GetScanResults(client::TableRange(table_)); |
105 | 0 | for (uint32_t i = 0; i < result_kvs.size(); i++) { |
106 | 0 | auto split = StringSplit(result_kvs[i].first, '_'); |
107 | 0 | int32_t key = CHECK_RESULT(CheckedStoInt<int32_t>(split.back())); |
108 | 0 | ASSERT_EQ(Format("k_$0", key), result_kvs[i].first); |
109 | 0 | ASSERT_TRUE(string(kKeySize, 'a' + (key % 26)) == result_kvs[i].second); |
110 | 0 | } |
111 | 0 | } |
112 | | |
113 | 0 | void AddUniverseKeys() { |
114 | 0 | current_key_id_ = RandomHumanReadableString(16); |
115 | 0 | auto bytes = RandomBytes(32); |
116 | 0 | ASSERT_OK(yb_admin_client_->AddUniverseKeyToAllMasters( |
117 | 0 | current_key_id_, std::string(bytes.begin(), bytes.end()))); |
118 | 0 | } |
119 | | |
120 | 0 | CHECKED_STATUS WaitForAllMastersHaveLatestKeyInMemory() { |
121 | 0 | return LoggedWaitFor([&]() -> Result<bool> { |
122 | 0 | return yb_admin_client_->AllMastersHaveUniverseKeyInMemory(current_key_id_).ok(); |
123 | 0 | }, 30s, "Wait for all masters to have key in memory"); |
124 | 0 | } |
125 | | |
126 | 0 | void RotateKey() { |
127 | 0 | ASSERT_OK(WaitForAllMastersHaveLatestKeyInMemory()); |
128 | 0 | ASSERT_OK(yb_admin_client_->RotateUniverseKeyInMemory(current_key_id_)); |
129 | 0 | ASSERT_OK(yb_admin_client_->IsEncryptionEnabled()); |
130 | 0 | } |
131 | | |
132 | 0 | CHECKED_STATUS WaitForLoadBalanced() { |
133 | 0 | SleepFor(MonoDelta::FromSeconds(5)); |
134 | 0 | return LoggedWaitFor([&]() -> Result<bool> { |
135 | 0 | return client_->IsLoadBalanced(3); |
136 | 0 | }, MonoDelta::FromSeconds(30), "Wait for load balanced"); |
137 | 0 | } |
138 | | |
139 | 0 | void DisableEncryption() { |
140 | 0 | ASSERT_OK(yb_admin_client_->DisableEncryptionInMemory()); |
141 | 0 | } |
142 | | private: |
143 | | std::string current_key_id_ = ""; |
144 | | }; |
145 | | |
146 | | INSTANTIATE_TEST_CASE_P(TestWithCounterOverflow, EncryptionTest, ::testing::Bool()); |
147 | | |
148 | 0 | TEST_P(EncryptionTest, BasicWriteRead) { |
149 | 0 | if (GetParam()) { |
150 | | // If testing with counter overflow, make sure we set counter to a value that will overflow |
151 | | // for sst files. |
152 | 0 | FLAGS_encryption_counter_min = kCounterOverflowDefault; |
153 | 0 | FLAGS_encryption_counter_max = kCounterOverflowDefault; |
154 | 0 | } |
155 | |
|
156 | 0 | WriteWorkload(0, kNumKeys); |
157 | 0 | ASSERT_NO_FATALS(VerifyWrittenRecords()); |
158 | 0 | ClusterVerifier cv(external_mini_cluster()); |
159 | 0 | ASSERT_NO_FATALS(cv.CheckCluster()); |
160 | 0 | } |
161 | | |
162 | 0 | TEST_F(EncryptionTest, MasterLeaderRestart) { |
163 | 0 | WriteWorkload(0, kNumKeys); |
164 | | // Restart the master leader. |
165 | 0 | auto* master_leader = external_mini_cluster()->GetLeaderMaster(); |
166 | 0 | master_leader->Shutdown(); |
167 | 0 | ASSERT_OK(master_leader->Restart()); |
168 | | // Recreate the admin client after restarting master leader. |
169 | 0 | CreateAdminClient(); |
170 | 0 | ASSERT_OK(WaitForAllMastersHaveLatestKeyInMemory()); |
171 | | // Restart the tablet servers and make sure they can contact the new master leader for the key. |
172 | 0 | for (size_t i = 0; i < external_mini_cluster()->num_tablet_servers(); i++) { |
173 | 0 | external_mini_cluster()->tablet_server(i)->Shutdown(); |
174 | 0 | CHECK_OK(external_mini_cluster()->tablet_server(i)->Restart()); |
175 | 0 | SleepFor(MonoDelta::FromSeconds(5));\ |
176 | | /*ASSERT_OK(external_mini_cluster()->WaitForTabletsRunning( |
177 | | external_mini_cluster()->tablet_server(i), MonoDelta::FromSeconds(30)));*/ |
178 | 0 | } |
179 | |
|
180 | 0 | ASSERT_OK(WaitForLoadBalanced()); |
181 | 0 | WriteWorkload(kNumKeys, 2 * kNumKeys); |
182 | 0 | ASSERT_NO_FATALS(VerifyWrittenRecords()); |
183 | 0 | ClusterVerifier cv(external_mini_cluster()); |
184 | 0 | ASSERT_NO_FATALS(cv.CheckCluster()); |
185 | 0 | } |
186 | | |
187 | 0 | TEST_F(EncryptionTest, AllMastersRestart) { |
188 | 0 | WriteWorkload(0, kNumKeys); |
189 | 0 | for (size_t i = 0; i < external_mini_cluster()->num_masters(); i++) { |
190 | 0 | external_mini_cluster()->master(i)->Shutdown(); |
191 | 0 | CHECK_OK(external_mini_cluster()->master(i)->Restart()); |
192 | 0 | } |
193 | |
|
194 | 0 | ASSERT_OK(WaitForLoadBalanced()); |
195 | 0 | WriteWorkload(kNumKeys, 2 * kNumKeys); |
196 | 0 | ASSERT_NO_FATALS(VerifyWrittenRecords()); |
197 | 0 | ClusterVerifier cv(external_mini_cluster()); |
198 | 0 | ASSERT_NO_FATALS(cv.CheckCluster()); |
199 | 0 | } |
200 | | |
201 | 0 | TEST_F(EncryptionTest, RollingMasterRestart) { |
202 | 0 | WriteWorkload(0, kNumKeys); |
203 | 0 | for (size_t i = 0; i < external_mini_cluster()->num_masters(); i++) { |
204 | 0 | external_mini_cluster()->master(i)->Shutdown(); |
205 | 0 | CHECK_OK(external_mini_cluster()->master(i)->Restart()); |
206 | 0 | ASSERT_OK(WaitForAllMastersHaveLatestKeyInMemory()); |
207 | 0 | } |
208 | | // Recreate the admin client after rolling the masters. |
209 | 0 | CreateAdminClient(); |
210 | | // Test that each master bootstraps from each other. |
211 | 0 | ASSERT_NO_FATALS(AddUniverseKeys()); |
212 | 0 | ASSERT_NO_FATALS(RotateKey()); |
213 | |
|
214 | 0 | ASSERT_OK(WaitForLoadBalanced()); |
215 | 0 | WriteWorkload(kNumKeys, 2 * kNumKeys); |
216 | 0 | ASSERT_NO_FATALS(VerifyWrittenRecords()); |
217 | 0 | ClusterVerifier cv(external_mini_cluster()); |
218 | 0 | ASSERT_NO_FATALS(cv.CheckCluster()); |
219 | 0 | } |
220 | | |
221 | 0 | TEST_F(EncryptionTest, AddServer) { |
222 | | // Write 1000 values, add a server, and write 1000 more. |
223 | 0 | WriteWorkload(0, kNumKeys); |
224 | 0 | ASSERT_OK(external_mini_cluster()->AddTabletServer()); |
225 | 0 | ASSERT_OK(WaitForLoadBalanced()); |
226 | 0 | WriteWorkload(kNumKeys, 2 * kNumKeys); |
227 | 0 | ASSERT_NO_FATALS(VerifyWrittenRecords()); |
228 | 0 | ClusterVerifier cv(external_mini_cluster()); |
229 | 0 | ASSERT_NO_FATALS(cv.CheckCluster()); |
230 | 0 | } |
231 | | |
232 | | TEST_F(EncryptionTest, RotateKey) { |
233 | | // Write 1000 values, rotate a new key, and write 1000 more. |
234 | | WriteWorkload(0, kNumKeys); |
235 | | ASSERT_NO_FATALS(VerifyWrittenRecords()); |
236 | | ASSERT_NO_FATALS(AddUniverseKeys()); |
237 | | ASSERT_NO_FATALS(RotateKey()); |
238 | | WriteWorkload(kNumKeys, 2 * kNumKeys); |
239 | | ASSERT_NO_FATALS(VerifyWrittenRecords()); |
240 | | ClusterVerifier cv(external_mini_cluster()); |
241 | | ASSERT_NO_FATALS(cv.CheckCluster()); |
242 | | } |
243 | | |
244 | | TEST_F(EncryptionTest, DisableEncryption) { |
245 | | // Write 1000 values, disable encryption, and write 1000 more. |
246 | | WriteWorkload(0, kNumKeys); |
247 | | ASSERT_NO_FATALS(VerifyWrittenRecords()); |
248 | | ASSERT_NO_FATALS(DisableEncryption()); |
249 | | WriteWorkload(kNumKeys, 2 * kNumKeys); |
250 | | ASSERT_NO_FATALS(VerifyWrittenRecords()); |
251 | | ClusterVerifier cv(external_mini_cluster()); |
252 | | ASSERT_NO_FATALS(cv.CheckCluster()); |
253 | | } |
254 | | |
255 | 0 | TEST_F(EncryptionTest, EmptyTable) { |
256 | | // No values added, make sure add server works with empty tables. |
257 | 0 | ASSERT_OK(external_mini_cluster()->AddTabletServer()); |
258 | 0 | ASSERT_OK(WaitForLoadBalanced()); |
259 | 0 | ClusterVerifier cv(external_mini_cluster()); |
260 | 0 | ASSERT_NO_FATALS(cv.CheckCluster()); |
261 | 0 | } |
262 | | |
263 | | TEST_F(EncryptionTest, EnableEncryption) { |
264 | | // Disable encryption, add 1000 values, enable, and write 1000 more. |
265 | | ASSERT_NO_FATALS(DisableEncryption()); |
266 | | WriteWorkload(0, kNumKeys); |
267 | | ASSERT_NO_FATALS(VerifyWrittenRecords()); |
268 | | ASSERT_NO_FATALS(AddUniverseKeys()); |
269 | | ASSERT_NO_FATALS(RotateKey()); |
270 | | WriteWorkload(kNumKeys, 2 * kNumKeys); |
271 | | ASSERT_NO_FATALS(VerifyWrittenRecords()); |
272 | | ClusterVerifier cv(external_mini_cluster()); |
273 | | ASSERT_NO_FATALS(cv.CheckCluster()); |
274 | | } |
275 | | |
276 | 0 | TEST_F(EncryptionTest, ServerRestart) { |
277 | 0 | WriteWorkload(0, kNumKeys); |
278 | 0 | auto* tablet_server = external_mini_cluster()->tablet_server(0); |
279 | 0 | tablet_server->Shutdown(); |
280 | 0 | ASSERT_OK(tablet_server->Restart()); |
281 | 0 | ASSERT_OK(external_mini_cluster()->WaitForTabletsRunning( |
282 | 0 | tablet_server, MonoDelta::FromSeconds(30))); |
283 | 0 | ClusterVerifier cv(external_mini_cluster()); |
284 | 0 | ASSERT_NO_FATALS(cv.CheckCluster()); |
285 | 0 | } |
286 | | |
287 | | } // namespace integration_tests |
288 | | } // namespace yb |