/Users/deen/code/yugabyte-db/src/yb/yql/cql/cqlserver/cqlserver-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 <memory> |
15 | | #include <string> |
16 | | #include <vector> |
17 | | |
18 | | #include "yb/gutil/strings/join.h" |
19 | | #include "yb/gutil/strings/substitute.h" |
20 | | |
21 | | #include "yb/integration-tests/yb_table_test_base.h" |
22 | | |
23 | | #include "yb/tserver/heartbeater.h" |
24 | | #include "yb/tserver/mini_tablet_server.h" |
25 | | #include "yb/tserver/tablet_server.h" |
26 | | |
27 | | #include "yb/util/bytes_formatter.h" |
28 | | #include "yb/util/cast.h" |
29 | | #include "yb/util/net/net_util.h" |
30 | | #include "yb/util/net/socket.h" |
31 | | #include "yb/util/result.h" |
32 | | #include "yb/util/status_log.h" |
33 | | #include "yb/util/test_util.h" |
34 | | |
35 | | #include "yb/yql/cql/cqlserver/cql_server.h" |
36 | | |
37 | | DECLARE_bool(cql_server_always_send_events); |
38 | | DECLARE_bool(use_cassandra_authentication); |
39 | | |
40 | | namespace yb { |
41 | | namespace cqlserver { |
42 | | |
43 | | using namespace yb::ql; // NOLINT |
44 | | using std::string; |
45 | | using std::unique_ptr; |
46 | | using std::vector; |
47 | | using strings::Substitute; |
48 | | using yb::integration_tests::YBTableTestBase; |
49 | | |
50 | | class TestCQLService : public YBTableTestBase { |
51 | | public: |
52 | | void SetUp() override; |
53 | | void TearDown() override; |
54 | | |
55 | | void TestSchemaChangeEvent(); |
56 | | |
57 | | protected: |
58 | | void SendRequestAndExpectTimeout(const string& cmd); |
59 | | |
60 | | void SendRequestAndExpectResponse(const string& cmd, const string& expected_resp); |
61 | | |
62 | 0 | int server_port() { return cql_server_port_; } |
63 | | private: |
64 | | Status SendRequestAndGetResponse( |
65 | | const string& cmd, size_t expected_resp_length, int timeout_in_millis = 60000); |
66 | | |
67 | | Socket client_sock_; |
68 | | unique_ptr<boost::asio::io_service> io_; |
69 | | unique_ptr<CQLServer> server_; |
70 | | int cql_server_port_ = 0; |
71 | | unique_ptr<FileLock> cql_port_lock_; |
72 | | unique_ptr<FileLock> cql_webserver_lock_; |
73 | | static constexpr size_t kBufLen = 1024; |
74 | | size_t resp_bytes_read_ = 0; |
75 | | uint8_t resp_[kBufLen]; |
76 | | }; |
77 | | |
78 | 0 | void TestCQLService::SetUp() { |
79 | 0 | YBTableTestBase::SetUp(); |
80 | |
|
81 | 0 | CQLServerOptions opts; |
82 | 0 | cql_server_port_ = GetFreePort(&cql_port_lock_); |
83 | 0 | opts.rpc_opts.rpc_bind_addresses = strings::Substitute("0.0.0.0:$0", cql_server_port_); |
84 | | // No need to save the webserver port, as we don't plan on using it. Just use a unique free port. |
85 | 0 | opts.webserver_opts.port = GetFreePort(&cql_webserver_lock_); |
86 | 0 | string fs_root = GetTestPath("CQLServerTest-fsroot"); |
87 | 0 | opts.fs_opts.wal_paths = {fs_root}; |
88 | 0 | opts.fs_opts.data_paths = {fs_root}; |
89 | |
|
90 | 0 | auto master_rpc_addrs = master_rpc_addresses_as_strings(); |
91 | 0 | opts.master_addresses_flag = JoinStrings(master_rpc_addrs, ","); |
92 | 0 | auto master_addresses = std::make_shared<server::MasterAddresses>(); |
93 | 0 | for (const auto& hp_str : master_rpc_addrs) { |
94 | 0 | HostPort hp; |
95 | 0 | CHECK_OK(hp.ParseString(hp_str, cql_server_port_)); |
96 | 0 | master_addresses->push_back({std::move(hp)}); |
97 | 0 | } |
98 | 0 | opts.SetMasterAddresses(master_addresses); |
99 | |
|
100 | 0 | io_.reset(new boost::asio::io_service()); |
101 | 0 | server_.reset(new CQLServer(opts, io_.get(), mini_cluster()->mini_tablet_server(0)->server())); |
102 | 0 | LOG(INFO) << "Starting CQL server..."; |
103 | 0 | CHECK_OK(server_->Start()); |
104 | 0 | LOG(INFO) << "CQL server successfully started."; |
105 | |
|
106 | 0 | Endpoint remote(IpAddress(), server_port()); |
107 | 0 | CHECK_OK(client_sock_.Init(0)); |
108 | 0 | CHECK_OK(client_sock_.SetNoDelay(false)); |
109 | 0 | LOG(INFO) << "Connecting to CQL server " << remote; |
110 | 0 | CHECK_OK(client_sock_.Connect(remote)); |
111 | 0 | } |
112 | | |
113 | 0 | void TestCQLService::TearDown() { |
114 | 0 | EXPECT_OK(client_sock_.Close()); |
115 | 0 | DeleteTable(); |
116 | 0 | WARN_NOT_OK(mini_cluster()->mini_tablet_server(0)->server()->heartbeater()->Stop(), |
117 | 0 | "Failed to stop heartbeater"); |
118 | 0 | server_->Shutdown(); |
119 | 0 | YBTableTestBase::TearDown(); |
120 | 0 | } |
121 | | |
122 | | Status TestCQLService::SendRequestAndGetResponse( |
123 | 0 | const string& cmd, size_t expected_resp_length, int timeout_in_millis) { |
124 | 0 | LOG(INFO) << "Send CQL: {" << FormatBytesAsStr(cmd) << "}"; |
125 | | // Send the request. |
126 | 0 | auto bytes_written = EXPECT_RESULT(client_sock_.Write(to_uchar_ptr(cmd.c_str()), cmd.length())); |
127 | |
|
128 | 0 | EXPECT_EQ(cmd.length(), bytes_written); |
129 | | |
130 | | // Receive the response. |
131 | 0 | MonoTime deadline = MonoTime::Now(); |
132 | 0 | deadline.AddDelta(MonoDelta::FromMilliseconds(timeout_in_millis)); |
133 | 0 | resp_bytes_read_ = VERIFY_RESULT(client_sock_.BlockingRecv( |
134 | 0 | resp_, expected_resp_length, deadline)); |
135 | 0 | LOG(INFO) << "Received CQL: {" << |
136 | 0 | FormatBytesAsStr(reinterpret_cast<char*>(resp_), resp_bytes_read_) << "}"; |
137 | |
|
138 | 0 | if (expected_resp_length != resp_bytes_read_) { |
139 | 0 | return STATUS(IOError, |
140 | 0 | Substitute("Received $1 bytes instead of $2", |
141 | 0 | resp_bytes_read_, |
142 | 0 | expected_resp_length)); |
143 | 0 | } |
144 | | |
145 | | // Try to read 1 more byte - the read must fail (no more data in the socket). |
146 | 0 | deadline = MonoTime::Now(); |
147 | 0 | deadline.AddDelta(MonoDelta::FromMilliseconds(200)); |
148 | 0 | auto bytes_read = client_sock_.BlockingRecv(&resp_[expected_resp_length], 1, deadline); |
149 | 0 | EXPECT_FALSE(bytes_read.ok()); |
150 | 0 | EXPECT_TRUE(bytes_read.status().IsTimedOut()); |
151 | |
|
152 | 0 | return Status::OK(); |
153 | 0 | } |
154 | | |
155 | 0 | void TestCQLService::SendRequestAndExpectTimeout(const string& cmd) { |
156 | | // Don't expect to receive even 1 byte. |
157 | 0 | ASSERT_TRUE(SendRequestAndGetResponse(cmd, 1).IsTimedOut()); |
158 | 0 | } |
159 | | |
160 | 0 | void TestCQLService::SendRequestAndExpectResponse(const string& cmd, const string& expected_resp) { |
161 | 0 | CHECK_OK(SendRequestAndGetResponse(cmd, expected_resp.length())); |
162 | 0 | const string resp(reinterpret_cast<char*>(resp_), resp_bytes_read_); |
163 | |
|
164 | 0 | if (expected_resp != resp) { |
165 | 0 | LOG(ERROR) << "Expected: {" << FormatBytesAsStr(expected_resp) << |
166 | 0 | "} Got: {" << FormatBytesAsStr(resp) << "}"; |
167 | 0 | } |
168 | | |
169 | | // Verify that the response is as expected. |
170 | 0 | CHECK_EQ(expected_resp, resp); |
171 | 0 | } |
172 | | |
173 | | // The following test cases test the CQL protocol marshalling/unmarshalling with hand-coded |
174 | | // request messages and expected responses. They are good as basic and error-handling tests. |
175 | | // These are expected to be few. |
176 | | // |
177 | | // TODO(Robert) - add more tests using Cassandra C++ driver when the CQL server can respond |
178 | | // to queries. |
179 | 0 | TEST_F(TestCQLService, StartupRequest) { |
180 | 0 | LOG(INFO) << "Test CQL STARTUP request"; |
181 | | // Send STARTUP request using version V3 |
182 | 0 | SendRequestAndExpectResponse( |
183 | 0 | BINARY_STRING("\x03\x00\x00\x00\x01" "\x00\x00\x00\x16" |
184 | 0 | "\x00\x01" "\x00\x0b" "CQL_VERSION" |
185 | 0 | "\x00\x05" "3.0.0"), |
186 | 0 | BINARY_STRING("\x83\x00\x00\x00\x02" "\x00\x00\x00\x00")); |
187 | | |
188 | | // Send STARTUP request using version V4 |
189 | 0 | SendRequestAndExpectResponse( |
190 | 0 | BINARY_STRING("\x04\x00\x00\x00\x01" "\x00\x00\x00\x16" |
191 | 0 | "\x00\x01" "\x00\x0b" "CQL_VERSION" |
192 | 0 | "\x00\x05" "3.0.0"), |
193 | 0 | BINARY_STRING("\x84\x00\x00\x00\x02" "\x00\x00\x00\x00")); |
194 | | |
195 | | // Send STARTUP request using version V5 |
196 | 0 | SendRequestAndExpectResponse( |
197 | 0 | BINARY_STRING("\x05\x00\x00\x00\x01" "\x00\x00\x00\x16" |
198 | 0 | "\x00\x01" "\x00\x0b" "CQL_VERSION" |
199 | 0 | "\x00\x05" "3.0.0"), |
200 | 0 | BINARY_STRING("\x84\x00\x00\x00\x00" "\x00\x00\x00\x58" |
201 | 0 | "\x00\x00\x00\x0a" "\x00\x52" |
202 | 0 | "Invalid or unsupported protocol version 5. " |
203 | 0 | "Supported versions are between 3 and 4.")); |
204 | | |
205 | | // Send STARTUP request with compression |
206 | 0 | SendRequestAndExpectResponse( |
207 | 0 | BINARY_STRING("\x04\x01\x00\x00\x01" "\x00\x00\x00\x16" |
208 | 0 | "\x00\x01" "\x00\x0b" "CQL_VERSION" |
209 | 0 | "\x00\x05" "3.0.0"), |
210 | 0 | BINARY_STRING("\x84\x00\x00\x00\x00" "\x00\x00\x00\x2e" |
211 | 0 | "\x00\x00\x00\x0a" "\x00\x28" |
212 | 0 | "STARTUP request should not be compressed")); |
213 | 0 | } |
214 | | |
215 | 0 | TEST_F(TestCQLService, OptionsRequest) { |
216 | 0 | LOG(INFO) << "Test CQL OPTIONS request"; |
217 | | // Send OPTIONS request using version V4 |
218 | 0 | SendRequestAndExpectResponse( |
219 | 0 | BINARY_STRING("\x04\x00\x00\x00\x05" "\x00\x00\x00\x00"), |
220 | 0 | BINARY_STRING("\x84\x00\x00\x00\x06" "\x00\x00\x00\x3b" |
221 | 0 | "\x00\x02" "\x00\x0b" "COMPRESSION" |
222 | 0 | "\x00\x02" "\x00\x03" "lz4" "\x00\x06" "snappy" |
223 | 0 | "\x00\x0b" "CQL_VERSION" |
224 | 0 | "\x00\x02" "\x00\x05" "3.0.0" "\x00\x05" "3.4.2")); |
225 | 0 | } |
226 | | |
227 | 0 | TEST_F(TestCQLService, InvalidRequest) { |
228 | 0 | LOG(INFO) << "Test invalid CQL request"; |
229 | | // Send response (0x84) as request |
230 | 0 | SendRequestAndExpectResponse( |
231 | 0 | BINARY_STRING("\x84\x00\x00\x00\x00" "\x00\x00\x00\x22" |
232 | 0 | "\x00\x00\x00\x0a" "\x00\x1c" "Unsupported protocol version"), |
233 | 0 | BINARY_STRING("\x84\x00\x00\x00\x00" "\x00\x00\x00\x13" |
234 | 0 | "\x00\x00\x00\x0a" "\x00\x0d" "Not a request")); |
235 | | |
236 | | // Send ERROR as request |
237 | 0 | SendRequestAndExpectResponse( |
238 | 0 | BINARY_STRING("\x04\x00\x00\x00\x00" "\x00\x00\x00\x22" |
239 | 0 | "\x00\x00\x00\x0a" "\x00\x1c" "Unsupported protocol version"), |
240 | 0 | BINARY_STRING("\x84\x00\x00\x00\x00" "\x00\x00\x00\x1a" |
241 | 0 | "\x00\x00\x00\x0a" "\x00\x14" "Not a request opcode")); |
242 | | |
243 | | // Send an unknown opcode |
244 | 0 | SendRequestAndExpectResponse( |
245 | 0 | BINARY_STRING("\x04\x00\x00\x00\xff" "\x00\x00\x00\x22" |
246 | 0 | "\x00\x00\x00\x0a" "\x00\x1c" "Unsupported protocol version"), |
247 | 0 | BINARY_STRING("\x84\x00\x00\x00\x00" "\x00\x00\x00\x14" |
248 | 0 | "\x00\x00\x00\x0a" "\x00\x0e" "Unknown opcode")); |
249 | | |
250 | | // Send truncated request |
251 | 0 | SendRequestAndExpectResponse( |
252 | 0 | BINARY_STRING("\x04\x00\x00\x00\x01" "\x00\x00\x00\x0f" |
253 | 0 | "\x00\x01" "\x00\x0b" "CQL_VERSION"), |
254 | 0 | BINARY_STRING("\x84\x00\x00\x00\x00" "\x00\x00\x00\x1b" |
255 | 0 | "\x00\x00\x00\x0a" "\x00\x15" "Truncated CQL message")); |
256 | | |
257 | | // Send truncated string in request |
258 | 0 | SendRequestAndExpectResponse( |
259 | 0 | BINARY_STRING("\x04\x00\x00\x00\x01" "\x00\x00\x00\x16" |
260 | 0 | "\x00\x01" "\x00\x0b" "CQL_VERSION" |
261 | 0 | "\x00\x15" "3.0.0"), |
262 | 0 | BINARY_STRING("\x84\x00\x00\x00\x00" "\x00\x00\x00\x1b" |
263 | 0 | "\x00\x00\x00\x0a" "\x00\x15" "Truncated CQL message")); |
264 | | |
265 | | // Send request with extra trailing bytes |
266 | 0 | SendRequestAndExpectResponse( |
267 | 0 | BINARY_STRING("\x04\x00\x00\x00\x01" "\x00\x00\x00\x18" |
268 | 0 | "\x00\x01" "\x00\x0b" "CQL_VERSION" |
269 | 0 | "\x00\x05" "3.0.0" |
270 | 0 | "\x00\0x00"), |
271 | 0 | BINARY_STRING("\x84\x00\x00\x00\x00" "\x00\x00\x00\x1d" |
272 | 0 | "\x00\x00\x00\x0a" "\x00\x17" "Request length too long")); |
273 | 0 | } |
274 | | |
275 | 0 | TEST_F(TestCQLService, TestCQLServerEventConst) { |
276 | 0 | std::unique_ptr<SchemaChangeEventResponse> response( |
277 | 0 | new SchemaChangeEventResponse("", "", "", "", {})); |
278 | 0 | constexpr size_t kSize = sizeof(CQLServerEvent); |
279 | 0 | std::unique_ptr<CQLServerEvent> event(new CQLServerEvent(std::move(response))); |
280 | 0 | auto event_list = std::make_shared<CQLServerEventList>(); |
281 | 0 | event_list->AddEvent(std::move(event)); |
282 | 0 | yb::rpc::OutboundDataPtr data(event_list); |
283 | 0 | void* ptr = event_list.get(); |
284 | 0 | char buffer[kSize]; |
285 | 0 | memcpy(buffer, ptr, kSize); |
286 | 0 | data->Transferred(Status::OK(), nullptr); |
287 | 0 | ASSERT_EQ(0, memcmp(buffer, ptr, kSize)); |
288 | 0 | data->Transferred(STATUS(NetworkError, "Dummy"), nullptr); |
289 | 0 | ASSERT_EQ(0, memcmp(buffer, ptr, kSize)); |
290 | 0 | } |
291 | | |
292 | 0 | void TestCQLService::TestSchemaChangeEvent() { |
293 | 0 | LOG(INFO) << "Test CQL SCHEMA_CHANGE event with gflag cql_server_always_send_events = " << |
294 | 0 | FLAGS_cql_server_always_send_events; |
295 | | |
296 | | // Send STARTUP request using version V4. |
297 | 0 | SendRequestAndExpectResponse( |
298 | 0 | BINARY_STRING("\x04\x00\x00\x00\x01" "\x00\x00\x00\x16" |
299 | 0 | "\x00\x01" "\x00\x0b" "CQL_VERSION" |
300 | 0 | "\x00\x05" "3.0.0"), |
301 | 0 | BINARY_STRING("\x84\x00\x00\x00\x02" "\x00\x00\x00\x00")); |
302 | | |
303 | | // Send CREATE KEYSPACE IF NOT EXISTS "kong" |
304 | | // WITH REPLICATION = {'class': 'SimpleStrategy', 'replication_factor': 1} |
305 | | // Expecting only one CQL message as the result: CQL RESULT (opcode=8). |
306 | 0 | string expected_response = |
307 | 0 | BINARY_STRING("\x84\x00\x00\x00\x08" "\x00\x00\x00\x1d" "\x00\x00\x00\x05" |
308 | 0 | "\x00\x07" "CREATED" |
309 | 0 | "\x00\x08" "KEYSPACE" |
310 | 0 | "\x00\x04" "kong"); |
311 | 0 | if (FLAGS_cql_server_always_send_events) { |
312 | | // Expecting 2 CQL messages as the result: CQL RESULT (opcode=8) + CQL EVENT (opcode=0x0c). |
313 | 0 | expected_response += |
314 | 0 | BINARY_STRING("\x84\x00\xff\xff\x0c" "\x00\x00\x00\x28" |
315 | 0 | "\x00\x0d" "SCHEMA_CHANGE" |
316 | 0 | "\x00\x07" "CREATED" |
317 | 0 | "\x00\x08" "KEYSPACE" |
318 | 0 | "\x00\x04" "kong"); |
319 | 0 | } |
320 | |
|
321 | 0 | SendRequestAndExpectResponse( |
322 | 0 | BINARY_STRING("\x04\x00\x00\x00\x07" "\x00\x00\x00\x99" |
323 | 0 | "\x00\x00" "\x00\x8c" " CREATE KEYSPACE IF NOT EXISTS \"kong\"" |
324 | 0 | "\x0a" " WITH REPLICATION = {'class': " |
325 | 0 | "'SimpleStrategy', 'replication_factor': 1}" |
326 | 0 | "\x0a" " " |
327 | 0 | "\x0a" " " |
328 | 0 | "\x00\x01" "\x14\x00\x00\x03\xe8\x00\x08"), |
329 | 0 | expected_response); |
330 | | |
331 | | // Send USE "kong" |
332 | 0 | SendRequestAndExpectResponse( |
333 | 0 | BINARY_STRING("\x04\x00\x00\x00\x07" "\x00\x00\x00\x11" |
334 | 0 | "\x00\x00" "\x00\x0a" "USE \"kong\"" |
335 | 0 | "\x00\x01\x00"), |
336 | 0 | BINARY_STRING("\x84\x00\x00\x00\x08" "\x00\x00\x00\x0a" "\x00\x00\x00\x03" |
337 | 0 | "\x00\x04" "kong")); |
338 | | |
339 | | // Send CREATE TABLE IF NOT EXISTS schema_meta( |
340 | | // key text, subsystem text, last_executed text, executed set<text>, |
341 | | // pending set<text>, PRIMARY KEY (key, subsystem)) |
342 | | // Expecting only one CQL message as the result: CQL RESULT (opcode=8). |
343 | 0 | expected_response = |
344 | 0 | BINARY_STRING("\x84\x00\x00\x00\x08" "\x00\x00\x00\x27" "\x00\x00\x00\x05" |
345 | 0 | "\x00\x07" "CREATED" |
346 | 0 | "\x00\x05" "TABLE" |
347 | 0 | "\x00\x04" "kong" |
348 | 0 | "\x00\x0b" "schema_meta"); |
349 | 0 | if (FLAGS_cql_server_always_send_events) { |
350 | | // Expecting 2 CQL messages as the result: CQL RESULT (opcode=8) + CQL EVENT (opcode=0x0c). |
351 | 0 | expected_response += |
352 | 0 | BINARY_STRING("\x84\x00\xff\xff\x0c" "\x00\x00\x00\x32" |
353 | 0 | "\x00\x0d" "SCHEMA_CHANGE" |
354 | 0 | "\x00\x07" "CREATED" |
355 | 0 | "\x00\x05" "TABLE" |
356 | 0 | "\x00\x04" "kong" |
357 | 0 | "\x00\x0b" "schema_meta"); |
358 | 0 | } |
359 | |
|
360 | 0 | SendRequestAndExpectResponse( |
361 | 0 | BINARY_STRING("\x04\x00\x00\x00\x07" "\x00\x00\x01\x0d" |
362 | 0 | "\x00\x00" "\x01\x00" " CREATE TABLE IF NOT EXISTS schema_meta(" |
363 | 0 | "\x0a" " key text," |
364 | 0 | "\x0a" " subsystem text," |
365 | 0 | "\x0a" " last_executed text," |
366 | 0 | "\x0a" " executed set<text>," |
367 | 0 | "\x0a" " pending set<text>," |
368 | 0 | "\x0a" |
369 | 0 | "\x0a" " PRIMARY KEY (key, subsystem)" |
370 | 0 | "\x0a" " )" |
371 | 0 | "\x0a" " " |
372 | 0 | "\x00\x01" "\x14\x00\x00\x03\xe8\x00\x08"), |
373 | 0 | expected_response); |
374 | | |
375 | | // Send REGISTER request to subscribe for the events: TOPOLOGY_CHANGE, STATUS_CHANGE, |
376 | | // SCHEMA_CHANGE |
377 | 0 | SendRequestAndExpectResponse( |
378 | 0 | BINARY_STRING("\x04\x00\x00\x00\x0b" "\x00\x00\x00\x31" |
379 | 0 | "\x00\x03" "\x00\x0f" "TOPOLOGY_CHANGE" |
380 | 0 | "\x00\x0d" "STATUS_CHANGE" |
381 | 0 | "\x00\x0d" "SCHEMA_CHANGE"), |
382 | 0 | BINARY_STRING("\x84\x00\x00\x00\x02" "\x00\x00\x00\x00")); |
383 | | |
384 | | // Send CREATE TABLE IF NOT EXISTS schema_meta2( |
385 | | // key text, subsystem text, last_executed text, executed set<text>, |
386 | | // pending set<text>, PRIMARY KEY (key, subsystem)) |
387 | 0 | SendRequestAndExpectResponse( |
388 | 0 | BINARY_STRING("\x04\x00\x00\x00\x07" "\x00\x00\x01\x0d" |
389 | 0 | "\x00\x00" "\x01\x00" " CREATE TABLE IF NOT EXISTS schema_meta2(" |
390 | 0 | "\x0a" " key text," |
391 | 0 | "\x0a" " subsystem text," |
392 | 0 | "\x0a" " last_executed text," |
393 | 0 | "\x0a" " executed set<text>," |
394 | 0 | "\x0a" " pending set<text>," |
395 | 0 | "\x0a" |
396 | 0 | "\x0a" " PRIMARY KEY (key, subsystem)" |
397 | 0 | "\x0a" " )" |
398 | 0 | "\x0a" " " |
399 | 0 | "\x00\x01" "\x14\x00\x00\x03\xe8\x00\x08"), |
400 | | // Expecting 2 CQL messages as the result: CQL RESULT (opcode=8) + CQL EVENT (opcode=0x0c). |
401 | 0 | BINARY_STRING("\x84\x00\x00\x00\x08" "\x00\x00\x00\x28" "\x00\x00\x00\x05" |
402 | 0 | "\x00\x07" "CREATED" |
403 | 0 | "\x00\x05" "TABLE" |
404 | 0 | "\x00\x04" "kong" |
405 | 0 | "\x00\x0c" "schema_meta2" |
406 | 0 | "\x84\x00\xff\xff\x0c" "\x00\x00\x00\x33" |
407 | 0 | "\x00\x0d" "SCHEMA_CHANGE" |
408 | 0 | "\x00\x07" "CREATED" |
409 | 0 | "\x00\x05" "TABLE" |
410 | 0 | "\x00\x04" "kong" |
411 | 0 | "\x00\x0c" "schema_meta2")); |
412 | 0 | } |
413 | | |
414 | 0 | TEST_F(TestCQLService, TestSchemaChangeEvent) { |
415 | 0 | TestSchemaChangeEvent(); |
416 | 0 | } |
417 | | |
418 | | class TestCQLServiceWithGFlag : public TestCQLService { |
419 | | public: |
420 | 0 | void SetUp() override { |
421 | 0 | FLAGS_cql_server_always_send_events = true; |
422 | 0 | TestCQLService::SetUp(); |
423 | 0 | } |
424 | | }; |
425 | | |
426 | 0 | TEST_F(TestCQLServiceWithGFlag, TestSchemaChangeEventWithGFlag) { |
427 | 0 | TestSchemaChangeEvent(); |
428 | 0 | } |
429 | | |
430 | 0 | TEST_F(TestCQLService, TestReadSystemTable) { |
431 | | // Send STARTUP request using version V4. |
432 | 0 | SendRequestAndExpectResponse( |
433 | 0 | BINARY_STRING("\x04\x00\x00\x00\x01" "\x00\x00\x00\x16" |
434 | 0 | "\x00\x01" "\x00\x0b" "CQL_VERSION" |
435 | 0 | "\x00\x05" "3.0.0"), |
436 | 0 | BINARY_STRING("\x84\x00\x00\x00\x02" // 0x02 = READY |
437 | 0 | "\x00\x00\x00\x00")); // zero body size |
438 | |
|
439 | 0 | SendRequestAndExpectResponse( |
440 | 0 | BINARY_STRING("\x04\x00\x00\x00\x07" // 0x07 = QUERY |
441 | 0 | "\x00\x00\x00\x23" // body size |
442 | 0 | "\x00\x00\x00\x1c" "SELECT key FROM system.local" |
443 | 0 | "\x00\x01" // consistency: 0x0001 = ONE |
444 | 0 | "\x00"), // bit flags |
445 | 0 | BINARY_STRING("\x84\x00\x00\x00\x08" // 0x08 = RESULT |
446 | 0 | "\x00\x00\x00\x2f" // body size |
447 | 0 | "\x00\x00\x00\x02" // 0x00000002 = ROWS |
448 | 0 | "\x00\x00\x00\x01" // flags: 0x01 = Global_tables_spec |
449 | 0 | "\x00\x00\x00\x01" // column count |
450 | 0 | "\x00\x06" "system" |
451 | 0 | "\x00\x05" "local" |
452 | 0 | "\x00\x03" "key" |
453 | 0 | "\x00\x0d" // type id: 0x000D = Varchar |
454 | 0 | "\x00\x00\x00\x01" // row count |
455 | 0 | "\x00\x00\x00\x05" "local")); |
456 | 0 | } |
457 | | |
458 | | class TestCQLServiceWithCassAuth : public TestCQLService { |
459 | | public: |
460 | 0 | void SetUp() override { |
461 | 0 | FLAGS_use_cassandra_authentication = true; |
462 | 0 | TestCQLService::SetUp(); |
463 | 0 | } |
464 | | }; |
465 | | |
466 | 0 | TEST_F(TestCQLServiceWithCassAuth, TestReadSystemTableNotAuthenticated) { |
467 | | // Send STARTUP request using version V4. |
468 | 0 | SendRequestAndExpectResponse( |
469 | 0 | BINARY_STRING("\x04\x00\x00\x00\x01" "\x00\x00\x00\x16" |
470 | 0 | "\x00\x01" "\x00\x0b" "CQL_VERSION" |
471 | 0 | "\x00\x05" "3.0.0"), |
472 | 0 | BINARY_STRING("\x84\x00\x00\x00\x03" // 0x03 = AUTHENTICATE |
473 | 0 | "\x00\x00\x00\x31" // body size |
474 | 0 | "\x00\x2F" "org.apache.cassandra.auth.PasswordAuthenticator")); |
475 | | |
476 | | // Try to skip authorization and query the data. |
477 | 0 | SendRequestAndExpectResponse( |
478 | 0 | BINARY_STRING("\x04\x00\x00\x00\x07" // 0x07 = QUERY |
479 | 0 | "\x00\x00\x00\x23" // body size |
480 | 0 | "\x00\x00\x00\x1c" "SELECT key FROM system.local" |
481 | 0 | "\x00\x01" // consistency: 0x0001 = ONE |
482 | 0 | "\x00"), // bit flags |
483 | 0 | BINARY_STRING("\x84\x00\x00\x00\x00" // 0x00 = ERROR |
484 | 0 | "\x00\x00\x00\x3b" // body size |
485 | 0 | "\x00\x00\x00\x00" // error code |
486 | 0 | "\x00\x35" "Could not execute statement by not authenticated user")); |
487 | 0 | } |
488 | | |
489 | 0 | TEST_F(TestCQLServiceWithCassAuth, TestReadSystemTableAuthenticated) { |
490 | | // Send STARTUP request using version V4. |
491 | 0 | SendRequestAndExpectResponse( |
492 | 0 | BINARY_STRING("\x04\x00\x00\x00\x01" "\x00\x00\x00\x16" |
493 | 0 | "\x00\x01" "\x00\x0b" "CQL_VERSION" |
494 | 0 | "\x00\x05" "3.0.0"), |
495 | 0 | BINARY_STRING("\x84\x00\x00\x00\x03" // 0x03 = AUTHENTICATE |
496 | 0 | "\x00\x00\x00\x31" // body size |
497 | 0 | "\x00\x2F" "org.apache.cassandra.auth.PasswordAuthenticator")); |
498 | | |
499 | | // Invalid authorization: send wrong user name. |
500 | 0 | SendRequestAndExpectResponse( |
501 | 0 | BINARY_STRING("\x04\x00\x00\x00\x0f" // 0x0F = AUTH_RESPONSE |
502 | 0 | "\x00\x00\x00\x17" // body size |
503 | 0 | "\x00\x00\x00\x13" "\x00" "acssandra" "\x00" "password"), |
504 | 0 | BINARY_STRING("\x84\x00\x00\x00\x00" // 0x00 = ERROR |
505 | 0 | "\x00\x00\x00\x41" // body size |
506 | 0 | "\x00\x00\x01\x00" // error code |
507 | 0 | "\x00\x3b" "Provided username 'acssandra' and/or password are incorrect")); |
508 | | // Invalid authorization: send wrong password. |
509 | 0 | SendRequestAndExpectResponse( |
510 | 0 | BINARY_STRING("\x04\x00\x00\x00\x0f" // 0x0F = AUTH_RESPONSE |
511 | 0 | "\x00\x00\x00\x17" // body size |
512 | 0 | "\x00\x00\x00\x13" "\x00" "cassandra" "\x00" "password"), |
513 | 0 | BINARY_STRING("\x84\x00\x00\x00\x00" // 0x00 = ERROR |
514 | 0 | "\x00\x00\x00\x41" // body size |
515 | 0 | "\x00\x00\x01\x00" // error code |
516 | 0 | "\x00\x3b" "Provided username 'cassandra' and/or password are incorrect")); |
517 | | // Invalid authorization: send null token. |
518 | 0 | SendRequestAndExpectResponse( |
519 | 0 | BINARY_STRING("\x04\x00\x00\x00\x0f" // 0x0F = AUTH_RESPONSE |
520 | 0 | "\x00\x00\x00\x04" // body size |
521 | 0 | "\x00\x00\x00\x00"), |
522 | 0 | BINARY_STRING("\x84\x00\x00\x00\x00" // 0x00 = ERROR |
523 | 0 | "\x00\x00\x00\x1a" // body size |
524 | 0 | "\x00\x00\x00\x0a" // error code |
525 | 0 | "\x00\x14" "Invalid empty token!")); |
526 | | // Invalid authorization: send token 'deadbeaf'. |
527 | 0 | SendRequestAndExpectResponse( |
528 | 0 | BINARY_STRING("\x04\x00\x00\x00\x0f" // 0x0F = AUTH_RESPONSE |
529 | 0 | "\x00\x00\x00\x08" // body size |
530 | 0 | "\x00\x00\x00\x04" "\xde\xad\xbe\xaf"), |
531 | 0 | BINARY_STRING("\x84\x00\x00\x00\x00" // 0x00 = ERROR |
532 | 0 | "\x00\x00\x00\x30" // body size |
533 | 0 | "\x00\x00\x00\x0a" // error code |
534 | 0 | "\x00\x2a" "Invalid format. Message must begin with \\0")); |
535 | | // Invalid authorization: send token '\x00deadbeaf'. |
536 | 0 | SendRequestAndExpectResponse( |
537 | 0 | BINARY_STRING("\x04\x00\x00\x00\x0f" // 0x0F = AUTH_RESPONSE |
538 | 0 | "\x00\x00\x00\x09" // body size |
539 | 0 | "\x00\x00\x00\x05" "\x00\xde\xad\xbe\xaf"), |
540 | 0 | BINARY_STRING("\x84\x00\x00\x00\x00" // 0x00 = ERROR |
541 | 0 | "\x00\x00\x00\x3c" // body size |
542 | 0 | "\x00\x00\x00\x0a" // error code |
543 | 0 | "\x00\x36" "Invalid format. Message must contain \\0 after username")); |
544 | | |
545 | | // Correct authorization. |
546 | 0 | SendRequestAndExpectResponse( |
547 | 0 | BINARY_STRING("\x04\x00\x00\x00\x0f" // 0x0F = AUTH_RESPONSE |
548 | 0 | "\x00\x00\x00\x18" // body size |
549 | 0 | "\x00\x00\x00\x14" "\x00" "cassandra" "\x00" "cassandra"), |
550 | 0 | BINARY_STRING("\x84\x00\x00\x00\x10" // 0x10 = AUTH_SUCCESS |
551 | 0 | "\x00\x00\x00\x04" // body size |
552 | 0 | "\x00\x00\x00\x00")); // empty token |
553 | | |
554 | | // Query the data. |
555 | 0 | SendRequestAndExpectResponse( |
556 | 0 | BINARY_STRING("\x04\x00\x00\x00\x07" // 0x07 = QUERY |
557 | 0 | "\x00\x00\x00\x23" // body size |
558 | 0 | "\x00\x00\x00\x1c" "SELECT key FROM system.local" |
559 | 0 | "\x00\x01" // consistency: 0x0001 = ONE |
560 | 0 | "\x00"), // bit flags |
561 | 0 | BINARY_STRING("\x84\x00\x00\x00\x08" // 0x08 = RESULT |
562 | 0 | "\x00\x00\x00\x2f" // body size |
563 | 0 | "\x00\x00\x00\x02" // 0x00000002 = ROWS |
564 | 0 | "\x00\x00\x00\x01" // flags: 0x01 = Global_tables_spec |
565 | 0 | "\x00\x00\x00\x01" // column count |
566 | 0 | "\x00\x06" "system" |
567 | 0 | "\x00\x05" "local" |
568 | 0 | "\x00\x03" "key" |
569 | 0 | "\x00\x0d" // type id: 0x000D = Varchar |
570 | 0 | "\x00\x00\x00\x01" // row count |
571 | 0 | "\x00\x00\x00\x05" "local")); |
572 | 0 | } |
573 | | |
574 | | } // namespace cqlserver |
575 | | } // namespace yb |