1 #include <boost/tokenizer.hpp>
3 #include <gtest/gtest.h>
5 #include "logging/logging.h"
6 #include "storage/sql_utils.h"
7 #include "storage/sqlstorage.h"
8 #include "uptane/directorrepository.h"
9 #include "uptane/imagerepository.h"
10 #include "utilities/utils.h"
12 boost::filesystem::path test_data_dir;
14 typedef boost::tokenizer<boost::char_separator<char> > sql_tokenizer;
16 static std::map<std::string, std::string> parseSchema() {
17 std::map<std::string, std::string>
result;
18 std::vector<std::string> tokens;
19 enum { STATE_INIT, STATE_CREATE, STATE_INSERT, STATE_TABLE, STATE_NAME, STATE_TRIGGER, STATE_TRIGGER_END };
20 boost::char_separator<char> sep(
" \"\t\r\n",
"(),;");
21 std::string schema(libaktualizr_current_schema);
22 sql_tokenizer tok(schema, sep);
23 int parsing_state = STATE_INIT;
27 for (sql_tokenizer::iterator it = tok.begin(); it != tok.end(); ++it) {
28 std::string token = *it;
32 value = value +
" " + token;
34 switch (parsing_state) {
36 if (token ==
"CREATE") {
37 parsing_state = STATE_CREATE;
38 }
else if (token ==
"INSERT") {
39 parsing_state = STATE_INSERT;
45 if (token ==
"TABLE") {
46 parsing_state = STATE_TABLE;
47 }
else if (token ==
"TRIGGER") {
48 parsing_state = STATE_TRIGGER;
58 parsing_state = STATE_INIT;
64 parsing_state = STATE_TRIGGER_END;
67 case STATE_TRIGGER_END:
72 parsing_state = STATE_INIT;
76 if (token ==
"(" || token ==
")" || token ==
"," || token ==
";") {
80 parsing_state = STATE_NAME;
87 parsing_state = STATE_INIT;
97 static bool tableSchemasEqual(
const std::string& left,
const std::string& right) {
98 boost::char_separator<char> sep(
" \"\t\r\n",
"(),;");
99 sql_tokenizer tokl(left, sep);
100 sql_tokenizer tokr(right, sep);
102 sql_tokenizer::iterator it_l;
103 sql_tokenizer::iterator it_r;
104 for (it_l = tokl.begin(), it_r = tokr.begin(); it_l != tokl.end() && it_r != tokr.end(); ++it_l, ++it_r) {
105 if (*it_l != *it_r)
return false;
107 return (it_l == tokl.end()) && (it_r == tokr.end());
110 static bool dbSchemaCheck(
SQLStorage& storage) {
111 std::map<std::string, std::string> tables = parseSchema();
112 if (tables.empty()) {
113 LOG_ERROR <<
"Could not parse schema";
117 for (
auto it = tables.begin(); it != tables.end(); ++it) {
118 std::string schema_from_db = storage.getTableSchemaFromDb(it->first);
119 if (!tableSchemasEqual(schema_from_db, it->second)) {
120 LOG_ERROR <<
"Schemas don't match for " << it->first;
121 LOG_ERROR <<
"Expected " << it->second;
122 LOG_ERROR <<
"Found " << schema_from_db;
130 std::unique_ptr<TemporaryDirectory> dir;
131 boost::filesystem::path db_path;
134 static TempSQLDb makeDbWithVersion(DbVersion version) {
136 tdb.dir = std_::make_unique<TemporaryDirectory>();
137 tdb.db_path = tdb.dir->Path() /
"test.db";
142 for (uint32_t k = 0; k <= static_cast<uint32_t>(version); k++) {
143 if (db.exec(libaktualizr_schema_migrations.at(k),
nullptr,
nullptr) != SQLITE_OK) {
144 throw std::runtime_error(
"Migration run failed");
152 TEST(sqlstorage, migrate) {
155 config.path = temp_dir.Path();
158 boost::filesystem::remove_all(config.sqldb_path.get(config.path));
159 EXPECT_FALSE(dbSchemaCheck(storage));
160 EXPECT_TRUE(storage.dbMigrate());
161 EXPECT_TRUE(dbSchemaCheck(storage));
165 TEST(sqlstorage, migrate_back) {
168 config.path = temp_dir.Path();
171 auto ver = storage.getVersion();
175 std::string migration_script =
178 CREATE TABLE test_table(test_text TEXT NOT NULL, test_int INT NOT NULL);\
179 INSERT INTO test_table VALUES(\"test_text\", 123);\
180 DELETE FROM version;\
181 INSERT INTO version VALUES( " +
182 std::to_string(
static_cast<int>(ver) + 1) +
184 COMMIT TRANSACTION;";
185 db.exec(migration_script, NULL, NULL);
187 std::string back_migration_script =
189 DROP TABLE test_table; \
190 DELETE FROM version;\
191 INSERT INTO version VALUES(" +
192 std::to_string(
static_cast<int>(ver)) +
");";
194 auto statement = db.prepareStatement(
"insert into rollback_migrations VALUES (?,?);",
static_cast<int>(ver) + 1,
195 back_migration_script);
199 EXPECT_EQ(
static_cast<int>(storage.getVersion()),
static_cast<int>(ver) + 1);
200 EXPECT_TRUE(storage.dbMigrate());
201 EXPECT_LT(
static_cast<int>(storage.getVersion()),
static_cast<int>(ver) + 1);
202 EXPECT_TRUE(dbSchemaCheck(storage));
205 TEST(sqlstorage, rollback_to_15) {
208 config.path = temp_dir.Path();
212 auto ver = storage.getVersion();
213 ASSERT_TRUE(storage.dbMigrateBackward(
static_cast<int>(ver), 15));
214 ASSERT_EQ(
static_cast<int>(storage.getVersion()), 15);
218 TEST(sqlstorage, MigrationVersionCheck) {
221 config.path = temp_dir.Path();
224 EXPECT_EQ(
static_cast<int32_t
>(storage.getVersion()), libaktualizr_schema_migrations.size() - 1);
228 TEST(sqlstorage, WrongDatabaseCheck) {
231 config.path = temp_dir.Path();
233 SQLite3Guard db(config.sqldb_path.get(config.path).c_str());
234 if (db.exec(
"CREATE TABLE some_table(somefield INTEGER);", NULL, NULL) != SQLITE_OK) {
235 FAIL() <<
"Unable to create an SQL database for testing.";
242 TEST(sqlstorage, DbMigration7to8) {
245 auto tdb = makeDbWithVersion(DbVersion(7));
249 if (db.exec(
"INSERT INTO primary_keys VALUES ('priv', 'pub');",
nullptr,
nullptr) != SQLITE_OK) {
253 if (db.exec(
"INSERT INTO device_info VALUES ('device', 1);",
nullptr,
nullptr) != SQLITE_OK) {
258 if (db.exec(libaktualizr_schema_migrations.at(8),
nullptr,
nullptr) != SQLITE_OK) {
263 auto statement = db.prepareStatement(
"SELECT private, public FROM primary_keys;");
264 if (statement.step() != SQLITE_ROW) {
268 EXPECT_EQ(statement.get_result_col_str(0).value(),
"priv");
269 EXPECT_EQ(statement.get_result_col_str(1).value(),
"pub");
271 statement = db.prepareStatement(
"SELECT device_id, is_registered FROM device_info;");
272 if (statement.step() != SQLITE_ROW) {
276 EXPECT_EQ(statement.get_result_col_str(0).value(),
"device");
277 EXPECT_EQ(statement.get_result_col_int(1), 1);
280 TEST(sqlstorage, DbMigration12to13) {
283 auto tdb = makeDbWithVersion(DbVersion(12));
287 if (db.exec(
"INSERT INTO ecu_serials VALUES ('primary_ecu', 'primary_hw', 1);",
nullptr,
nullptr) != SQLITE_OK) {
291 if (db.exec(
"INSERT INTO ecu_serials VALUES ('secondary_ecu', 'secondary_hw', 0);",
nullptr,
nullptr) != SQLITE_OK) {
295 if (db.exec(
"INSERT INTO installed_versions VALUES ('sha256', 'v1', 1, 2);",
nullptr,
nullptr) != SQLITE_OK) {
300 if (db.exec(libaktualizr_schema_migrations.at(13),
nullptr,
nullptr) != SQLITE_OK) {
301 std::cout << db.errmsg() <<
"\n";
302 FAIL() <<
"Migration 12 to 13 failed";
306 auto statement = db.prepareStatement(
307 "SELECT ecu_serial, sha256, name, hashes, length, is_current, is_pending FROM installed_versions;");
308 if (statement.step() != SQLITE_ROW) {
309 FAIL() <<
"installed_versions is empty";
312 EXPECT_EQ(statement.get_result_col_str(0).value(),
"primary_ecu");
313 EXPECT_EQ(statement.get_result_col_str(1).value(),
"sha256");
314 EXPECT_EQ(statement.get_result_col_str(2).value(),
"v1");
315 EXPECT_EQ(statement.get_result_col_str(3).value(),
"");
316 EXPECT_EQ(statement.get_result_col_int(4), 2);
317 EXPECT_EQ(statement.get_result_col_int(5), 1);
318 EXPECT_EQ(statement.get_result_col_int(6), 0);
320 if (statement.step() != SQLITE_DONE) {
321 FAIL() <<
"Too many rows";
325 TEST(sqlstorage, DbMigration18to19) {
328 auto tdb = makeDbWithVersion(DbVersion(18));
332 if (db.exec(
"INSERT INTO ecu_serials VALUES ('secondary_ecu', 'secondary_hw', 0);",
nullptr,
nullptr) != SQLITE_OK) {
336 if (db.exec(
"INSERT INTO ecu_serials VALUES ('primary_ecu', 'primary_hw', 1);",
nullptr,
nullptr) != SQLITE_OK) {
341 if (db.exec(libaktualizr_schema_migrations.at(19),
nullptr,
nullptr) != SQLITE_OK) {
342 std::cout << db.errmsg() <<
"\n";
343 FAIL() <<
"Migration 18 to 19 failed";
347 auto statement = db.prepareStatement(
"SELECT serial, hardware_id, is_primary FROM ecu_serials ORDER BY id;");
348 if (statement.step() != SQLITE_ROW) {
349 FAIL() <<
"ecu_serials is empty";
352 EXPECT_EQ(statement.get_result_col_str(0).value(),
"primary_ecu");
353 EXPECT_EQ(statement.get_result_col_str(1).value(),
"primary_hw");
354 EXPECT_EQ(statement.get_result_col_int(2), 1);
356 if (statement.step() != SQLITE_ROW) {
357 FAIL() <<
"ecu_serials contains only one element";
360 EXPECT_EQ(statement.get_result_col_str(0).value(),
"secondary_ecu");
361 EXPECT_EQ(statement.get_result_col_str(1).value(),
"secondary_hw");
362 EXPECT_EQ(statement.get_result_col_int(2), 0);
364 if (statement.step() != SQLITE_DONE) {
365 FAIL() <<
"Too many rows";
369 TEST(sqlstorage, DbMigration19to20) {
372 auto tdb = makeDbWithVersion(DbVersion(19));
375 if (db.exec(
"INSERT INTO ecu_serials(serial,hardware_id,is_primary) VALUES ('primary_ecu', 'primary_hw', 1);",
376 nullptr,
nullptr) != SQLITE_OK) {
380 if (db.exec(
"INSERT INTO installed_versions VALUES ('primary_ecu', 'shav1', 'v1', 'sha256:shav1', 2, 'cor1', 0, 0);",
381 nullptr,
nullptr) != SQLITE_OK) {
384 if (db.exec(
"INSERT INTO installed_versions VALUES ('primary_ecu', 'shav2', 'v2', 'sha256:shav2', 3, 'cor2', 1, 0);",
385 nullptr,
nullptr) != SQLITE_OK) {
390 if (db.exec(libaktualizr_schema_migrations.at(20),
nullptr,
nullptr) != SQLITE_OK) {
391 std::cout << db.errmsg() <<
"\n";
392 FAIL() <<
"Migration 19 to 20 failed";
396 auto statement = db.prepareStatement(
397 "SELECT ecu_serial, sha256, name, hashes, length, correlation_id, is_current, is_pending, "
398 "was_installed FROM installed_versions ORDER BY id;");
399 if (statement.step() != SQLITE_ROW) {
400 FAIL() <<
"installed_versions is empty";
403 EXPECT_EQ(statement.get_result_col_str(0).value(),
"primary_ecu");
404 EXPECT_EQ(statement.get_result_col_str(1).value(),
"shav1");
405 EXPECT_EQ(statement.get_result_col_str(2).value(),
"v1");
406 EXPECT_EQ(statement.get_result_col_str(3).value(),
"sha256:shav1");
407 EXPECT_EQ(statement.get_result_col_int(4), 2);
408 EXPECT_EQ(statement.get_result_col_str(5).value(),
"cor1");
409 EXPECT_EQ(statement.get_result_col_int(6), 0);
410 EXPECT_EQ(statement.get_result_col_int(7), 0);
411 EXPECT_EQ(statement.get_result_col_int(8), 1);
413 if (statement.step() != SQLITE_ROW) {
414 FAIL() <<
"installed_versions contains only one element";
417 EXPECT_EQ(statement.get_result_col_str(0).value(),
"primary_ecu");
418 EXPECT_EQ(statement.get_result_col_str(1).value(),
"shav2");
419 EXPECT_EQ(statement.get_result_col_str(2).value(),
"v2");
420 EXPECT_EQ(statement.get_result_col_str(3).value(),
"sha256:shav2");
421 EXPECT_EQ(statement.get_result_col_int(4), 3);
422 EXPECT_EQ(statement.get_result_col_str(5).value(),
"cor2");
423 EXPECT_EQ(statement.get_result_col_int(6), 1);
424 EXPECT_EQ(statement.get_result_col_int(7), 0);
425 EXPECT_EQ(statement.get_result_col_int(8), 1);
427 if (statement.step() != SQLITE_DONE) {
428 FAIL() <<
"Too many rows";
435 TEST(sqlstorage, migrate_root_works) {
438 config.path = temp_dir.Path();
440 boost::filesystem::remove_all(config.sqldb_path.get(config.path));
441 boost::filesystem::copy(test_data_dir /
"version5.sql", config.sqldb_path.get(config.path));
444 EXPECT_TRUE(storage.dbMigrate());
445 EXPECT_TRUE(dbSchemaCheck(storage));
448 std::string raw_director_root;
449 storage.loadRoot(&raw_director_root, Uptane::RepositoryType::Director(),
Uptane::Version());
451 EXPECT_NO_THROW(director.initRoot(
Uptane::RepositoryType(Uptane::RepositoryType::DIRECTOR), raw_director_root));
453 std::string raw_director_targets;
454 storage.loadNonRoot(&raw_director_targets, Uptane::RepositoryType::Director(), Uptane::Role::Targets());
456 EXPECT_NO_THROW(director.verifyTargets(raw_director_targets));
459 std::string raw_image_root;
460 storage.loadRoot(&raw_image_root, Uptane::RepositoryType::Image(),
Uptane::Version());
462 EXPECT_NO_THROW(imagerepository.initRoot(
Uptane::RepositoryType(Uptane::RepositoryType::IMAGE), raw_image_root));
465 EXPECT_NE(raw_director_root, raw_image_root);
466 Json::Value director_json = Utils::parseJSON(raw_director_root);
467 Json::Value sign = director_json[
"signed"];
468 EXPECT_EQ(sign[
"_type"],
"Root");
469 EXPECT_TRUE(sign[
"keys"].isMember(
"1ba3b2932863c0c6e5ff857ecdeb476b69b8b9f9ba4e36723eb10faf7768818b"));
473 TEST(sqlstorage, migrate_from_fs) {
476 config.path = temp_dir.Path() /
"config";
480 Utils::copyDir(test_data_dir /
"fs_snapshot", config.path);
481 ASSERT_GE(chmod(config.path.c_str(), S_IRWXU), 0);
484 auto storage = INvStorage::newStorage(config);
486 EXPECT_TRUE(storage->loadPrimaryKeys(
nullptr,
nullptr));
487 EXPECT_TRUE(storage->loadTlsCreds(
nullptr,
nullptr,
nullptr));
488 EXPECT_TRUE(storage->loadLatestRoot(
nullptr, Uptane::RepositoryType::Director()));
489 EXPECT_TRUE(storage->loadDeviceId(
nullptr));
492 EXPECT_TRUE(storage->loadEcuSerials(&serials));
493 EXPECT_EQ(serials.size(), 3);
495 std::vector<MisconfiguredEcu> misconfigured;
496 EXPECT_TRUE(storage->loadMisconfiguredEcus(&misconfigured));
497 EXPECT_EQ(misconfigured.size(), 1);
499 EXPECT_TRUE(storage->loadEcuRegistered());
501 boost::optional<Uptane::Target> installed;
502 storage->loadPrimaryInstalledVersions(&installed,
nullptr);
503 EXPECT_TRUE(!!installed);
508 boost::filesystem::recursive_directory_iterator repo_dir_it(config.path), repo_dir_end;
509 for (; repo_dir_it != repo_dir_end; ++repo_dir_it) {
510 const auto p = repo_dir_it->path();
511 if (p == config.sqldb_path.get(config.path)) {
514 FAIL() << p <<
" should not exist anymore";
519 int main(
int argc,
char** argv) {
520 ::testing::InitGoogleTest(&argc, argv);
522 logger_set_threshold(boost::log::trivial::trace);
524 std::cout <<
"Please pass the directory containing sql migration scripts as the first argument\n";
528 test_data_dir = argv[1];
530 if (!boost::filesystem::is_directory(test_data_dir)) {
531 std::cout << test_data_dir <<
" is not a directory\n";
535 return RUN_ALL_TESTS();