/************************************************************************ * EOS - the CERN Disk Storage System * * Copyright (C) 2020 CERN/Switzerland * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see .* ************************************************************************/ //------------------------------------------------------------------------------ //! @author Georgios Bitzes - CERN //! @brief Tests related to the configuration //------------------------------------------------------------------------------ #include "unit_tests/with_qdb/TestUtils.hh" #include "mgm/config/QuarkConfigHandler.hh" #include "common/Assert.hh" #include using namespace eos; class VariousTests : public eos::UnitTestsWithQDBFixture {}; class ConfigurationTests : public eos::UnitTestsWithQDBFixture {}; TEST_F(VariousTests, Ping) { std::unique_ptr qcl = makeQClient(); qclient::redisReplyPtr reply = qcl->exec("PING").get(); ASSERT_EQ(qclient::describeRedisReply(reply), "PONG"); } TEST_F(ConfigurationTests, BasicFetch) { std::unique_ptr qcl = makeQClient(); qclient::redisReplyPtr reply = qcl->exec("HSET", "eos-config:default", "a", "b").get(); ASSERT_EQ(qclient::describeRedisReply(reply), "(integer) 1"); eos::mgm::QuarkConfigHandler ch(getContactDetails()); std::map cfmap; ASSERT_TRUE(ch.fetchConfiguration("default", cfmap)); ASSERT_EQ(cfmap.size(), 1u); ASSERT_EQ(cfmap["a"], "b"); bool exists = false; ASSERT_TRUE(ch.checkExistence("default", exists)); ASSERT_TRUE(exists); ASSERT_TRUE(ch.checkExistence("default-2", exists)); ASSERT_FALSE(exists); qclient::redisReplyPtr reply2 = qcl->exec("SADD", "eos-config:default-3", "a", "b").get(); ASSERT_EQ(qclient::describeRedisReply(reply2), "(integer) 2"); common::Status st = ch.checkExistence("default-3", exists); ASSERT_FALSE(st); ASSERT_EQ(st.toString(), "(22): Received unexpected response in HLEN existence check: Unexpected reply type; was expecting INTEGER, received (error) ERR Invalid argument: WRONGTYPE Operation against a key holding the wrong kind of value"); } TEST_F(ConfigurationTests, Listing) { std::unique_ptr qcl = makeQClient(); qclient::redisReplyPtr reply = qcl->exec("HSET", "eos-config:default", "a", "b").get(); ASSERT_EQ(qclient::describeRedisReply(reply), "(integer) 1"); qclient::redisReplyPtr reply2 = qcl->exec("HSET", "eos-config:default-2", "a", "b").get(); ASSERT_EQ(qclient::describeRedisReply(reply2), "(integer) 1"); qclient::redisReplyPtr reply3 = qcl->exec("HSET", "eos-config-backup:default-1", "a", "b").get(); ASSERT_EQ(qclient::describeRedisReply(reply3), "(integer) 1"); eos::mgm::QuarkConfigHandler ch(getContactDetails()); std::vector configs, backups; ASSERT_TRUE(ch.listConfigurations(configs, backups)); ASSERT_EQ(configs.size(), 2u); ASSERT_EQ(configs[0], "default"); ASSERT_EQ(configs[1], "default-2"); ASSERT_EQ(backups.size(), 1u); ASSERT_EQ(backups[0], "default-1"); } TEST_F(ConfigurationTests, TrimBackups) { std::unique_ptr qcl = makeQClient(); qclient::redisReplyPtr rep; rep = qcl->exec("HSET", "eos-config-backup:default-1a", "a", "b").get(); ASSERT_EQ(qclient::describeRedisReply(rep), "(integer) 1"); rep = qcl->exec("HSET", "eos-config-backup:default-2b", "a", "b").get(); ASSERT_EQ(qclient::describeRedisReply(rep), "(integer) 1"); rep = qcl->exec("HSET", "eos-config-backup:default-3c", "a", "b").get(); ASSERT_EQ(qclient::describeRedisReply(rep), "(integer) 1"); rep = qcl->exec("HSET", "eos-config-backup:default-4d", "a", "b").get(); ASSERT_EQ(qclient::describeRedisReply(rep), "(integer) 1"); rep = qcl->exec("HSET", "eos-config-backup:aaaaaa-1", "a", "b").get(); ASSERT_EQ(qclient::describeRedisReply(rep), "(integer) 1"); rep = qcl->exec("HSET", "eos-config-backup:zzzzz-1", "a", "b").get(); ASSERT_EQ(qclient::describeRedisReply(rep), "(integer) 1"); eos::mgm::QuarkConfigHandler ch(getContactDetails()); size_t deleted; common::Status st = ch.trimBackups("default", 2, deleted); ASSERT_TRUE(st); ASSERT_EQ(deleted, 2); std::vector configs, backups; ASSERT_TRUE(ch.listConfigurations(configs, backups)); std::vector expectedConfigs = {}; std::vector expectedBackups = {"aaaaaa-1", "default-3c", "default-4d", "zzzzz-1"}; ASSERT_EQ(configs, expectedConfigs); ASSERT_EQ(backups, expectedBackups); } std::string padZeroes(const std::string& str, size_t len) { if (str.size() < len) { std::ostringstream ss; for (size_t i = 0; i < len - str.size(); i++) { ss << "0"; } ss << str; return ss.str(); } return str; } TEST_F(ConfigurationTests, TrimBackupsHit200Limit) { std::unique_ptr qcl = makeQClient(); qclient::redisReplyPtr rep; ASSERT_EQ(padZeroes(SSTR(1), 3), "001"); ASSERT_EQ(padZeroes(SSTR(11), 3), "011"); ASSERT_EQ(padZeroes(SSTR(111), 3), "111"); for (size_t i = 0; i < 300; i++) { std::string key = padZeroes(SSTR(i), 3); rep = qcl->exec("HSET", SSTR("eos-config-backup:default-" << key), "a", "b").get(); ASSERT_EQ(qclient::describeRedisReply(rep), "(integer) 1"); } eos::mgm::QuarkConfigHandler ch(getContactDetails()); size_t deleted; common::Status st = ch.trimBackups("default", 10, deleted); ASSERT_TRUE(st); ASSERT_EQ(deleted, 200); std::vector configs, backups; ASSERT_TRUE(ch.listConfigurations(configs, backups)); ASSERT_EQ(backups.size(), 100u); for (size_t i = 0; i < backups.size(); i++) { ASSERT_EQ(backups[i], SSTR("default-" << padZeroes(SSTR(i + 200), 3))); } } TEST_F(ConfigurationTests, WriteRead) { eos::mgm::QuarkConfigHandler ch(getContactDetails()); ASSERT_TRUE(ch.checkConnection(std::chrono::seconds(1))); std::map configuration, configuration2; configuration["a"] = "b"; configuration["c"] = "d"; ASSERT_TRUE(ch.writeConfiguration("default", configuration, false).get()); ASSERT_TRUE(ch.fetchConfiguration("default", configuration2)); ASSERT_EQ(configuration, configuration2); ASSERT_FALSE(ch.writeConfiguration("default", configuration, false).get()); configuration["d"] = "e"; ASSERT_TRUE(ch.writeConfiguration("default", configuration, true).get()); ASSERT_FALSE(configuration == configuration2); ASSERT_TRUE(ch.fetchConfiguration("default", configuration2)); ASSERT_EQ(configuration, configuration2); } TEST_F(ConfigurationTests, HashKeys) { ASSERT_EQ(eos::mgm::QuarkConfigHandler::FormHashKey("default"), "eos-config:default"); ASSERT_EQ(eos::mgm::QuarkConfigHandler::FormBackupHashKey("default", 1588936606), "eos-config-backup:default-20200508111646"); } TEST_F(ConfigurationTests, TailLog) { std::unique_ptr qcl = makeQClient(); qclient::redisReplyPtr reply = qcl->exec("deque-push-back", "eos-config-changelog", "aaa", "bbb", "ccc", "ddd", "eee").get(); ASSERT_EQ(qclient::describeRedisReply(reply), "(integer) 5"); std::vector changelog; eos::mgm::QuarkConfigHandler ch(getContactDetails()); ASSERT_TRUE(ch.tailChangelog(100, changelog)); ASSERT_EQ(changelog.size(), 5u); ASSERT_EQ(changelog[0], "aaa"); ASSERT_EQ(changelog[1], "bbb"); ASSERT_EQ(changelog[2], "ccc"); ASSERT_EQ(changelog[3], "ddd"); ASSERT_EQ(changelog[4], "eee"); ASSERT_TRUE(ch.tailChangelog(2, changelog)); ASSERT_EQ(changelog.size(), 2u); ASSERT_EQ(changelog[0], "ddd"); ASSERT_EQ(changelog[1], "eee"); } TEST_F(ConfigurationTests, AppendChangelog) { eos::mgm::ConfigChangelogEntry entry; eos::mgm::ConfigModification* modif = entry.add_modifications(); modif->set_key("aa"); modif->set_previous_value("b"); modif->set_new_value("c"); eos::mgm::QuarkConfigHandler ch(getContactDetails()); common::Status st = ch.appendChangelog(entry).get(); ASSERT_TRUE(st) << st.toString(); }