client_list_unittest.cc 49 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251
  1. // Copyright (C) 2012-2013 Internet Systems Consortium, Inc. ("ISC")
  2. //
  3. // Permission to use, copy, modify, and/or distribute this software for any
  4. // purpose with or without fee is hereby granted, provided that the above
  5. // copyright notice and this permission notice appear in all copies.
  6. //
  7. // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
  8. // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
  9. // AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
  10. // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  11. // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
  12. // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  13. // PERFORMANCE OF THIS SOFTWARE.
  14. #include <config.h>
  15. #include <datasrc/client_list.h>
  16. #include <datasrc/client.h>
  17. #include <datasrc/cache_config.h>
  18. #include <datasrc/zone_iterator.h>
  19. #include <datasrc/exceptions.h>
  20. #include <datasrc/memory/memory_client.h>
  21. #include <datasrc/memory/zone_table_segment.h>
  22. #include <datasrc/memory/zone_finder.h>
  23. #include <datasrc/memory/zone_writer.h>
  24. #include <datasrc/tests/mock_client.h>
  25. #include <dns/rrclass.h>
  26. #include <dns/rrttl.h>
  27. #include <dns/rdataclass.h>
  28. #include <gtest/gtest.h>
  29. #include <boost/shared_ptr.hpp>
  30. #include <set>
  31. #include <fstream>
  32. using namespace isc::datasrc;
  33. using isc::datasrc::unittest::MockDataSourceClient;
  34. using isc::datasrc::memory::InMemoryClient;
  35. using isc::datasrc::memory::ZoneTableSegment;
  36. using isc::datasrc::memory::InMemoryZoneFinder;
  37. using namespace isc::data;
  38. using namespace isc::dns;
  39. // don't import the entire boost namespace. It will unexpectedly hide uintXX_t
  40. // for some systems.
  41. using boost::shared_ptr;
  42. using namespace std;
  43. namespace {
  44. // The test version is the same as the normal version. We, however, add
  45. // some methods to dig directly in the internals, for the tests.
  46. class TestedList : public ConfigurableClientList {
  47. public:
  48. TestedList(const RRClass& rrclass) :
  49. ConfigurableClientList(rrclass)
  50. {}
  51. DataSources& getDataSources() { return (data_sources_); }
  52. // Overwrite the list's method to get a data source with given type
  53. // and configuration. We mock the data source and don't create the
  54. // container. This is just to avoid some complexity in the tests.
  55. virtual DataSourcePair getDataSourceClient(const string& type,
  56. const ConstElementPtr&
  57. configuration)
  58. {
  59. if (type == "error") {
  60. isc_throw(DataSourceError, "The error data source type");
  61. }
  62. if (type == "MasterFiles") {
  63. return (DataSourcePair(0, DataSourceClientContainerPtr()));
  64. }
  65. shared_ptr<MockDataSourceClient>
  66. ds(new MockDataSourceClient(type, configuration));
  67. // Make sure it is deleted when the test list is deleted.
  68. to_delete_.push_back(ds);
  69. return (DataSourcePair(ds.get(), DataSourceClientContainerPtr()));
  70. }
  71. private:
  72. // Hold list of data sources created internally, so they are preserved
  73. // until the end of the test and then deleted.
  74. vector<shared_ptr<MockDataSourceClient> > to_delete_;
  75. };
  76. const char* ds_zones[][3] = {
  77. {
  78. "example.org.",
  79. "example.com.",
  80. NULL
  81. },
  82. {
  83. "sub.example.org.",
  84. NULL, NULL
  85. },
  86. {
  87. NULL, NULL, NULL
  88. },
  89. {
  90. "sub.example.org.",
  91. NULL, NULL
  92. }
  93. };
  94. const size_t ds_count = (sizeof(ds_zones) / sizeof(*ds_zones));
  95. class ListTest : public ::testing::Test {
  96. public:
  97. ListTest() :
  98. rrclass_(RRClass::IN()),
  99. // The empty list corresponds to a list with no elements inside
  100. list_(new TestedList(rrclass_)),
  101. config_elem_(Element::fromJSON("["
  102. "{"
  103. " \"type\": \"test_type\","
  104. " \"params\": {}"
  105. "}]")),
  106. config_elem_zones_(Element::fromJSON("["
  107. "{"
  108. " \"type\": \"test_type\","
  109. " \"params\": [\"example.org\", \"example.com\", "
  110. " \"noiter.org\", \"null.org\"]"
  111. "}]")),
  112. ztable_segment_(ZoneTableSegment::create(rrclass_, "local"))
  113. {
  114. for (size_t i(0); i < ds_count; ++ i) {
  115. shared_ptr<MockDataSourceClient>
  116. ds(new MockDataSourceClient(ds_zones[i]));
  117. ds_.push_back(ds);
  118. ds_info_.push_back(ConfigurableClientList::DataSourceInfo(
  119. ds.get(), DataSourceClientContainerPtr(),
  120. boost::shared_ptr<internal::CacheConfig>(),
  121. rrclass_, ""));
  122. }
  123. }
  124. // Install a "fake" cached zone using a temporary underlying data source
  125. // client. If 'enabled' is set to false, emulate a disabled cache, in
  126. // which case there will be no data in memory.
  127. void prepareCache(size_t index, const Name& zone, bool enabled = true) {
  128. ConfigurableClientList::DataSourceInfo& dsrc_info =
  129. list_->getDataSources()[index];
  130. MockDataSourceClient* mock_client =
  131. static_cast<MockDataSourceClient*>(dsrc_info.data_src_client_);
  132. // Disable some default features of the mock to distinguish the
  133. // temporary case from normal case.
  134. mock_client->disableA();
  135. mock_client->disableBadIterator();
  136. // Build new cache config to load the specified zone, and replace
  137. // the data source info with the new config.
  138. ConstElementPtr cache_conf_elem =
  139. Element::fromJSON("{\"type\": \"mock\","
  140. " \"cache-enable\": " +
  141. string(enabled ? "true," : "false,") +
  142. " \"cache-zones\": "
  143. " [\"" + zone.toText() + "\"]}");
  144. boost::shared_ptr<internal::CacheConfig> cache_conf(
  145. new internal::CacheConfig("mock", mock_client, *cache_conf_elem,
  146. true));
  147. dsrc_info = ConfigurableClientList::DataSourceInfo(
  148. dsrc_info.data_src_client_,
  149. dsrc_info.container_,
  150. cache_conf, rrclass_, dsrc_info.name_);
  151. // Load the data into the zone table.
  152. if (enabled) {
  153. boost::scoped_ptr<memory::ZoneWriter> writer(
  154. new memory::ZoneWriter(
  155. *dsrc_info.ztable_segment_,
  156. cache_conf->getLoadAction(rrclass_, zone),
  157. zone, rrclass_, false));
  158. writer->load();
  159. writer->install();
  160. writer->cleanup(); // not absolutely necessary, but just in case
  161. }
  162. // On completion of load revert to the previous state of underlying
  163. // data source.
  164. mock_client->enableA();
  165. mock_client->enableBadIterator();
  166. }
  167. // Check the positive result is as we expect it.
  168. void positiveResult(const ClientList::FindResult& result,
  169. const shared_ptr<MockDataSourceClient>& dsrc,
  170. const Name& name, bool exact,
  171. const char* test, bool from_cache = false)
  172. {
  173. SCOPED_TRACE(test);
  174. ASSERT_NE(ZoneFinderPtr(), result.finder_);
  175. EXPECT_EQ(name, result.finder_->getOrigin());
  176. EXPECT_EQ(exact, result.exact_match_);
  177. // If it is a positive result, there's something to keep
  178. // alive, even when we don't know what it is.
  179. // Any better idea how to test it actually keeps the thing
  180. // alive?
  181. EXPECT_NE(shared_ptr<ClientList::FindResult::LifeKeeper>(),
  182. result.life_keeper_);
  183. if (from_cache) {
  184. EXPECT_NE(shared_ptr<InMemoryZoneFinder>(),
  185. boost::dynamic_pointer_cast<InMemoryZoneFinder>(
  186. result.finder_)) << "Finder is not from cache";
  187. EXPECT_TRUE(NULL !=
  188. dynamic_cast<InMemoryClient*>(result.dsrc_client_));
  189. } else {
  190. EXPECT_EQ(dsrc.get(), result.dsrc_client_);
  191. }
  192. }
  193. // check the result with empty (broken) zones. Right now this can only
  194. // happen for in-memory caches.
  195. void emptyResult(const ClientList::FindResult& result, bool exact,
  196. const char* trace_txt)
  197. {
  198. SCOPED_TRACE(trace_txt);
  199. ASSERT_FALSE(result.finder_);
  200. EXPECT_EQ(exact, result.exact_match_);
  201. EXPECT_TRUE(dynamic_cast<InMemoryClient*>(result.dsrc_client_));
  202. }
  203. // Configure the list with multiple data sources, according to
  204. // some configuration. It uses the index as parameter, to be able to
  205. // loop through the configurations.
  206. void multiConfiguration(size_t index) {
  207. list_->getDataSources().clear();
  208. switch (index) {
  209. case 2:
  210. list_->getDataSources().push_back(ds_info_[2]);
  211. // The ds_[2] is empty. We just check that it doesn't confuse
  212. // us. Fall through to the case 0.
  213. case 0:
  214. list_->getDataSources().push_back(ds_info_[0]);
  215. list_->getDataSources().push_back(ds_info_[1]);
  216. break;
  217. case 1:
  218. // The other order
  219. list_->getDataSources().push_back(ds_info_[1]);
  220. list_->getDataSources().push_back(ds_info_[0]);
  221. break;
  222. case 3:
  223. list_->getDataSources().push_back(ds_info_[1]);
  224. list_->getDataSources().push_back(ds_info_[0]);
  225. // It is the same as ds_[1], but we take from the first one.
  226. // The first one to match is the correct one.
  227. list_->getDataSources().push_back(ds_info_[3]);
  228. break;
  229. default:
  230. FAIL() << "Unknown configuration index " << index;
  231. }
  232. }
  233. void checkDS(size_t index, const string& type, const string& params,
  234. bool cache) const
  235. {
  236. ASSERT_GT(list_->getDataSources().size(), index);
  237. MockDataSourceClient* ds(dynamic_cast<MockDataSourceClient*>(
  238. list_->getDataSources()[index].data_src_client_));
  239. // Comparing with NULL does not work
  240. ASSERT_NE(ds, static_cast<const MockDataSourceClient*>(NULL));
  241. EXPECT_EQ(type, ds->type_);
  242. EXPECT_TRUE(Element::fromJSON(params)->equals(*ds->configuration_));
  243. EXPECT_EQ(cache, list_->getDataSources()[index].cache_ !=
  244. shared_ptr<InMemoryClient>());
  245. }
  246. ConfigurableClientList::CacheStatus doReload(
  247. const Name& origin, const string& datasrc_name = "");
  248. void accessorIterate(const ConstZoneTableAccessorPtr& accessor,
  249. int numZones, const string& zoneName);
  250. const RRClass rrclass_;
  251. shared_ptr<TestedList> list_;
  252. const ClientList::FindResult negative_result_;
  253. vector<shared_ptr<MockDataSourceClient> > ds_;
  254. vector<ConfigurableClientList::DataSourceInfo> ds_info_;
  255. const ConstElementPtr config_elem_, config_elem_zones_;
  256. shared_ptr<ZoneTableSegment> ztable_segment_;
  257. };
  258. // Test the test itself
  259. TEST_F(ListTest, selfTest) {
  260. EXPECT_EQ(result::SUCCESS, ds_[0]->findZone(Name("example.org")).code);
  261. EXPECT_EQ(result::PARTIALMATCH,
  262. ds_[0]->findZone(Name("sub.example.org")).code);
  263. EXPECT_EQ(result::NOTFOUND, ds_[0]->findZone(Name("org")).code);
  264. EXPECT_EQ(result::NOTFOUND, ds_[1]->findZone(Name("example.org")).code);
  265. EXPECT_EQ(result::NOTFOUND, ds_[0]->findZone(Name("aaa")).code);
  266. EXPECT_EQ(result::NOTFOUND, ds_[0]->findZone(Name("zzz")).code);
  267. // Nothing to keep alive here.
  268. EXPECT_EQ(shared_ptr<ClientList::FindResult::LifeKeeper>(),
  269. negative_result_.life_keeper_);
  270. }
  271. // Test the list we create with empty configuration is, in fact, empty
  272. TEST_F(ListTest, emptyList) {
  273. EXPECT_TRUE(list_->getDataSources().empty());
  274. }
  275. // Check the values returned by a find on an empty list. It should be
  276. // a negative answer (nothing found) no matter if we want an exact or inexact
  277. // match.
  278. TEST_F(ListTest, emptySearch) {
  279. // No matter what we try, we don't get an answer.
  280. // Note: we don't have operator<< for the result class, so we cannot use
  281. // EXPECT_EQ. Same for other similar cases.
  282. EXPECT_TRUE(negative_result_ == list_->find(Name("example.org"), false,
  283. false));
  284. EXPECT_TRUE(negative_result_ == list_->find(Name("example.org"), false,
  285. true));
  286. EXPECT_TRUE(negative_result_ == list_->find(Name("example.org"), true,
  287. false));
  288. EXPECT_TRUE(negative_result_ == list_->find(Name("example.org"), true,
  289. true));
  290. }
  291. // Put a single data source inside the list and check it can find an
  292. // exact match if there's one.
  293. TEST_F(ListTest, singleDSExactMatch) {
  294. list_->getDataSources().push_back(ds_info_[0]);
  295. // This zone is not there
  296. EXPECT_TRUE(negative_result_ == list_->find(Name("org."), true));
  297. // But this one is, so check it.
  298. positiveResult(list_->find(Name("example.org"), true), ds_[0],
  299. Name("example.org"), true, "Exact match");
  300. // When asking for a sub zone of a zone there, we get nothing
  301. // (we want exact match, this would be partial one)
  302. EXPECT_TRUE(negative_result_ == list_->find(Name("sub.example.org."),
  303. true));
  304. }
  305. // When asking for a partial match, we get all that the exact one, but more.
  306. TEST_F(ListTest, singleDSBestMatch) {
  307. list_->getDataSources().push_back(ds_info_[0]);
  308. // This zone is not there
  309. EXPECT_TRUE(negative_result_ == list_->find(Name("org.")));
  310. // But this one is, so check it.
  311. positiveResult(list_->find(Name("example.org")), ds_[0],
  312. Name("example.org"), true, "Exact match");
  313. // When asking for a sub zone of a zone there, we get the parent
  314. // one.
  315. positiveResult(list_->find(Name("sub.example.org.")), ds_[0],
  316. Name("example.org"), false, "Subdomain match");
  317. }
  318. const char* const test_names[] = {
  319. "Sub second",
  320. "Sub first",
  321. "With empty",
  322. "With a duplicity"
  323. };
  324. TEST_F(ListTest, multiExactMatch) {
  325. // Run through all the multi-configurations
  326. for (size_t i(0); i < sizeof(test_names) / sizeof(*test_names); ++i) {
  327. SCOPED_TRACE(test_names[i]);
  328. multiConfiguration(i);
  329. // Something that is nowhere there
  330. EXPECT_TRUE(negative_result_ == list_->find(Name("org."), true));
  331. // This one is there exactly.
  332. positiveResult(list_->find(Name("example.org"), true), ds_[0],
  333. Name("example.org"), true, "Exact match");
  334. // This one too, but in a different data source.
  335. positiveResult(list_->find(Name("sub.example.org."), true), ds_[1],
  336. Name("sub.example.org"), true, "Subdomain match");
  337. // But this one is in neither data source.
  338. EXPECT_TRUE(negative_result_ ==
  339. list_->find(Name("sub.example.com."), true));
  340. }
  341. }
  342. TEST_F(ListTest, multiBestMatch) {
  343. // Run through all the multi-configurations
  344. for (size_t i(0); i < 4; ++ i) {
  345. SCOPED_TRACE(test_names[i]);
  346. multiConfiguration(i);
  347. // Something that is nowhere there
  348. EXPECT_TRUE(negative_result_ == list_->find(Name("org.")));
  349. // This one is there exactly.
  350. positiveResult(list_->find(Name("example.org")), ds_[0],
  351. Name("example.org"), true, "Exact match");
  352. // This one too, but in a different data source.
  353. positiveResult(list_->find(Name("sub.example.org.")), ds_[1],
  354. Name("sub.example.org"), true, "Subdomain match");
  355. // But this one is in neither data source. But it is a subdomain
  356. // of one of the zones in the first data source.
  357. positiveResult(list_->find(Name("sub.example.com.")), ds_[0],
  358. Name("example.com."), false, "Subdomain in com");
  359. }
  360. }
  361. // Check the configuration is empty when the list is empty
  362. TEST_F(ListTest, configureEmpty) {
  363. const ConstElementPtr elem(new ListElement);
  364. list_->configure(elem, true);
  365. EXPECT_TRUE(list_->getDataSources().empty());
  366. // Check the exact configuration is preserved
  367. EXPECT_EQ(elem, list_->getConfiguration());
  368. }
  369. // Check we can get multiple data sources and they are in the right order.
  370. TEST_F(ListTest, configureMulti) {
  371. const ConstElementPtr elem(Element::fromJSON("["
  372. "{"
  373. " \"type\": \"type1\","
  374. " \"cache-enable\": false,"
  375. " \"params\": {}"
  376. "},"
  377. "{"
  378. " \"type\": \"type2\","
  379. " \"cache-enable\": false,"
  380. " \"params\": {}"
  381. "}]"
  382. ));
  383. list_->configure(elem, true);
  384. EXPECT_EQ(2, list_->getDataSources().size());
  385. checkDS(0, "type1", "{}", false);
  386. checkDS(1, "type2", "{}", false);
  387. // Check the exact configuration is preserved
  388. EXPECT_EQ(elem, list_->getConfiguration());
  389. }
  390. // Check we can pass whatever we want to the params
  391. TEST_F(ListTest, configureParams) {
  392. const char* params[] = {
  393. "true",
  394. "false",
  395. "null",
  396. "\"hello\"",
  397. "42",
  398. "[]",
  399. "{}",
  400. NULL
  401. };
  402. for (const char** param(params); *param; ++param) {
  403. SCOPED_TRACE(*param);
  404. ConstElementPtr elem(Element::fromJSON(string("["
  405. "{"
  406. " \"type\": \"t\","
  407. " \"cache-enable\": false,"
  408. " \"params\": ") + *param +
  409. "}]"));
  410. list_->configure(elem, true);
  411. EXPECT_EQ(1, list_->getDataSources().size());
  412. checkDS(0, "t", *param, false);
  413. }
  414. }
  415. TEST_F(ListTest, status) {
  416. EXPECT_TRUE(list_->getStatus().empty());
  417. const ConstElementPtr elem(Element::fromJSON("["
  418. "{"
  419. " \"type\": \"type1\","
  420. " \"cache-enable\": false,"
  421. " \"params\": {}"
  422. "},"
  423. "{"
  424. " \"type\": \"type2\","
  425. " \"cache-enable\": true,"
  426. " \"cache-zones\": [],"
  427. " \"name\": \"Test name\","
  428. " \"params\": {}"
  429. "}]"
  430. ));
  431. list_->configure(elem, true);
  432. const vector<DataSourceStatus> statuses(list_->getStatus());
  433. ASSERT_EQ(2, statuses.size());
  434. EXPECT_EQ("type1", statuses[0].getName());
  435. EXPECT_EQ(SEGMENT_UNUSED, statuses[0].getSegmentState());
  436. EXPECT_THROW(statuses[0].getSegmentType(), isc::InvalidOperation);
  437. EXPECT_EQ("Test name", statuses[1].getName());
  438. EXPECT_EQ(SEGMENT_INUSE, statuses[1].getSegmentState());
  439. EXPECT_EQ("local", statuses[1].getSegmentType());
  440. }
  441. TEST_F(ListTest, wrongConfig) {
  442. const char* configs[] = {
  443. // A lot of stuff missing from there
  444. "[{\"type\": \"test_type\", \"params\": 13}, {}]",
  445. // Some bad types completely
  446. "{}",
  447. "true",
  448. "42",
  449. "null",
  450. "[{\"type\": \"test_type\", \"params\": 13}, true]",
  451. "[{\"type\": \"test_type\", \"params\": 13}, []]",
  452. "[{\"type\": \"test_type\", \"params\": 13}, 42]",
  453. // Bad type of type
  454. "[{\"type\": \"test_type\", \"params\": 13}, {\"type\": 42}]",
  455. "[{\"type\": \"test_type\", \"params\": 13}, {\"type\": true}]",
  456. "[{\"type\": \"test_type\", \"params\": 13}, {\"type\": null}]",
  457. "[{\"type\": \"test_type\", \"params\": 13}, {\"type\": []}]",
  458. "[{\"type\": \"test_type\", \"params\": 13}, {\"type\": {}}]",
  459. // Bad type of cache-enable
  460. "[{\"type\": \"test_type\", \"params\": 13}, "
  461. "{\"type\": \"x\", \"cache-enable\": 13, \"cache-zones\": []}]",
  462. "[{\"type\": \"test_type\", \"params\": 13}, "
  463. "{\"type\": \"x\", \"cache-enable\": \"xx\", \"cache-zones\": []}]",
  464. "[{\"type\": \"test_type\", \"params\": 13}, "
  465. "{\"type\": \"x\", \"cache-enable\": [], \"cache-zones\": []}]",
  466. "[{\"type\": \"test_type\", \"params\": 13}, "
  467. "{\"type\": \"x\", \"cache-enable\": {}, \"cache-zones\": []}]",
  468. "[{\"type\": \"test_type\", \"params\": 13}, "
  469. "{\"type\": \"x\", \"cache-enable\": null, \"cache-zones\": []}]",
  470. // Bad type of cache-zones
  471. "[{\"type\": \"test_type\", \"params\": 13}, "
  472. "{\"type\": \"x\", \"cache-enable\": true, \"cache-zones\": \"x\"}]",
  473. "[{\"type\": \"test_type\", \"params\": 13}, "
  474. "{\"type\": \"x\", \"cache-enable\": true, \"cache-zones\": true}]",
  475. "[{\"type\": \"test_type\", \"params\": 13}, "
  476. "{\"type\": \"x\", \"cache-enable\": true, \"cache-zones\": null}]",
  477. "[{\"type\": \"test_type\", \"params\": 13}, "
  478. "{\"type\": \"x\", \"cache-enable\": true, \"cache-zones\": 13}]",
  479. "[{\"type\": \"test_type\", \"params\": 13}, "
  480. "{\"type\": \"x\", \"cache-enable\": true, \"cache-zones\": {}}]",
  481. // Some bad inputs for MasterFiles special case
  482. // It must have the cache enabled
  483. "[{\"type\": \"test_type\", \"params\": 13}, "
  484. "{\"type\": \"MasterFiles\", \"cache-enable\": false,"
  485. "\"params\": {}}]",
  486. // No cache-zones allowed here
  487. "[{\"type\": \"test_type\", \"params\": 13}, "
  488. "{\"type\": \"MasterFiles\", \"cache-enable\": true,"
  489. "\"param\": {}, \"cache-zones\": []}]",
  490. // Some bad types of params
  491. "[{\"type\": \"test_type\", \"params\": 13}, "
  492. "{\"type\": \"MasterFiles\", \"cache-enable\": false,"
  493. "\"params\": []}]",
  494. "[{\"type\": \"test_type\", \"params\": 13}, "
  495. "{\"type\": \"MasterFiles\", \"cache-enable\": false,"
  496. "\"params\": 13}]",
  497. "[{\"type\": \"test_type\", \"params\": 13}, "
  498. "{\"type\": \"MasterFiles\", \"cache-enable\": false,"
  499. "\"params\": true}]",
  500. "[{\"type\": \"test_type\", \"params\": 13}, "
  501. "{\"type\": \"MasterFiles\", \"cache-enable\": false,"
  502. "\"params\": null}]",
  503. "[{\"type\": \"test_type\", \"params\": 13}, "
  504. "{\"type\": \"MasterFiles\", \"cache-enable\": false,"
  505. "\"params\": \"x\"}]",
  506. "[{\"type\": \"test_type\", \"params\": 13}, "
  507. "{\"type\": \"MasterFiles\", \"cache-enable\": false,"
  508. "\"params\": {\".\": 13}}]",
  509. "[{\"type\": \"test_type\", \"params\": 13}, "
  510. "{\"type\": \"MasterFiles\", \"cache-enable\": false,"
  511. "\"params\": {\".\": true}}]",
  512. "[{\"type\": \"test_type\", \"params\": 13}, "
  513. "{\"type\": \"MasterFiles\", \"cache-enable\": false,"
  514. "\"params\": {\".\": null}}]",
  515. "[{\"type\": \"test_type\", \"params\": 13}, "
  516. "{\"type\": \"MasterFiles\", \"cache-enable\": false,"
  517. "\"params\": {\".\": []}}]",
  518. "[{\"type\": \"test_type\", \"params\": 13}, "
  519. "{\"type\": \"MasterFiles\", \"cache-enable\": false,"
  520. "\"params\": {\".\": {}}}]",
  521. NULL
  522. };
  523. // Put something inside to see it survives the exception
  524. list_->configure(config_elem_, true);
  525. checkDS(0, "test_type", "{}", false);
  526. for (const char** config(configs); *config; ++config) {
  527. SCOPED_TRACE(*config);
  528. ConstElementPtr elem(Element::fromJSON(*config));
  529. EXPECT_THROW(list_->configure(elem, true),
  530. ConfigurableClientList::ConfigurationError);
  531. // Still untouched
  532. checkDS(0, "test_type", "{}", false);
  533. EXPECT_EQ(1, list_->getDataSources().size());
  534. }
  535. }
  536. // The param thing defaults to null. Cache is not used yet.
  537. TEST_F(ListTest, defaults) {
  538. const ConstElementPtr elem(Element::fromJSON("["
  539. "{"
  540. " \"type\": \"type1\""
  541. "}]"));
  542. list_->configure(elem, true);
  543. EXPECT_EQ(1, list_->getDataSources().size());
  544. checkDS(0, "type1", "null", false);
  545. }
  546. // Check we can call the configure multiple times, to change the configuration
  547. TEST_F(ListTest, reconfigure) {
  548. const ConstElementPtr empty(new ListElement);
  549. list_->configure(config_elem_, true);
  550. checkDS(0, "test_type", "{}", false);
  551. list_->configure(empty, true);
  552. EXPECT_TRUE(list_->getDataSources().empty());
  553. list_->configure(config_elem_, true);
  554. checkDS(0, "test_type", "{}", false);
  555. }
  556. // Make sure the data source error exception from the factory is propagated
  557. TEST_F(ListTest, dataSrcError) {
  558. const ConstElementPtr elem(Element::fromJSON("["
  559. "{"
  560. " \"type\": \"error\""
  561. "}]"));
  562. list_->configure(config_elem_, true);
  563. checkDS(0, "test_type", "{}", false);
  564. EXPECT_THROW(list_->configure(elem, true), DataSourceError);
  565. checkDS(0, "test_type", "{}", false);
  566. }
  567. // Check we can get the cache
  568. TEST_F(ListTest, configureCacheEmpty) {
  569. const ConstElementPtr elem(Element::fromJSON("["
  570. "{"
  571. " \"type\": \"type1\","
  572. " \"cache-enable\": true,"
  573. " \"cache-zones\": [],"
  574. " \"params\": {}"
  575. "},"
  576. "{"
  577. " \"type\": \"type2\","
  578. " \"cache-enable\": false,"
  579. " \"cache-zones\": [],"
  580. " \"params\": {}"
  581. "}]"
  582. ));
  583. list_->configure(elem, true);
  584. EXPECT_EQ(2, list_->getDataSources().size());
  585. checkDS(0, "type1", "{}", true);
  586. checkDS(1, "type2", "{}", false);
  587. }
  588. // But no cache if we disallow it globally
  589. TEST_F(ListTest, configureCacheDisabled) {
  590. const ConstElementPtr elem(Element::fromJSON("["
  591. "{"
  592. " \"type\": \"type1\","
  593. " \"cache-enable\": true,"
  594. " \"cache-zones\": [],"
  595. " \"params\": {}"
  596. "},"
  597. "{"
  598. " \"type\": \"type2\","
  599. " \"cache-enable\": false,"
  600. " \"cache-zones\": [],"
  601. " \"params\": {}"
  602. "}]"
  603. ));
  604. list_->configure(elem, false);
  605. EXPECT_EQ(2, list_->getDataSources().size());
  606. checkDS(0, "type1", "{}", false);
  607. checkDS(1, "type2", "{}", false);
  608. }
  609. // Put some zones into the cache
  610. TEST_F(ListTest, cacheZones) {
  611. const ConstElementPtr elem(Element::fromJSON("["
  612. "{"
  613. " \"type\": \"type1\","
  614. " \"cache-enable\": true,"
  615. " \"cache-zones\": [\"example.org\", \"example.com\"],"
  616. " \"params\": [\"example.org\", \"example.com\", \"exmaple.cz\"]"
  617. "}]"));
  618. list_->configure(elem, true);
  619. checkDS(0, "type1", "[\"example.org\", \"example.com\", \"exmaple.cz\"]",
  620. true);
  621. const shared_ptr<InMemoryClient> cache(list_->getDataSources()[0].cache_);
  622. EXPECT_EQ(2, cache->getZoneCount());
  623. EXPECT_EQ(result::SUCCESS, cache->findZone(Name("example.org")).code);
  624. EXPECT_EQ(result::SUCCESS, cache->findZone(Name("example.com")).code);
  625. EXPECT_EQ(result::NOTFOUND, cache->findZone(Name("example.cz")).code);
  626. EXPECT_EQ(RRClass::IN(),
  627. cache->findZone(Name("example.org")).zone_finder->getClass());
  628. // These are cached and answered from the cache
  629. positiveResult(list_->find(Name("example.com.")), ds_[0],
  630. Name("example.com."), true, "com", true);
  631. positiveResult(list_->find(Name("example.org.")), ds_[0],
  632. Name("example.org."), true, "org", true);
  633. positiveResult(list_->find(Name("sub.example.com.")), ds_[0],
  634. Name("example.com."), false, "Subdomain of com", true);
  635. // For now, the ones not cached are ignored.
  636. EXPECT_TRUE(negative_result_ == list_->find(Name("example.cz.")));
  637. }
  638. // Check the caching handles misbehaviour from the data source and
  639. // misconfiguration gracefully
  640. TEST_F(ListTest, badCache) {
  641. list_->configure(config_elem_, true);
  642. checkDS(0, "test_type", "{}", false);
  643. // First, the zone is not in the data source. configure() should still
  644. // succeed, and the existence zone should be cached.
  645. const ConstElementPtr elem1(Element::fromJSON("["
  646. "{"
  647. " \"type\": \"test_type\","
  648. " \"cache-enable\": true,"
  649. " \"cache-zones\": [\"example.org\", \"example.com\"],"
  650. " \"params\": [\"example.org\"]"
  651. "}]"));
  652. list_->configure(elem1, true); // shouldn't cause disruption
  653. checkDS(0, "test_type", "[\"example.org\"]", true);
  654. const shared_ptr<InMemoryClient> cache(list_->getDataSources()[0].cache_);
  655. EXPECT_EQ(1, cache->getZoneCount());
  656. EXPECT_EQ(result::SUCCESS, cache->findZone(Name("example.org")).code);
  657. // Now, the zone doesn't give an iterator
  658. const ConstElementPtr elem2(Element::fromJSON("["
  659. "{"
  660. " \"type\": \"type1\","
  661. " \"cache-enable\": true,"
  662. " \"cache-zones\": [\"noiter.org\"],"
  663. " \"params\": [\"noiter.org\"]"
  664. "}]"));
  665. EXPECT_THROW(list_->configure(elem2, true), isc::NotImplemented);
  666. checkDS(0, "test_type", "[\"example.org\"]", true);
  667. // Now, the zone returns NULL iterator
  668. const ConstElementPtr elem3(Element::fromJSON("["
  669. "{"
  670. " \"type\": \"type1\","
  671. " \"cache-enable\": true,"
  672. " \"cache-zones\": [\"null.org\"],"
  673. " \"params\": [\"null.org\"]"
  674. "}]"));
  675. EXPECT_THROW(list_->configure(elem3, true), isc::Unexpected);
  676. checkDS(0, "test_type", "[\"example.org\"]", true);
  677. // The autodetection of zones is not enabled
  678. const ConstElementPtr elem4(Element::fromJSON("["
  679. "{"
  680. " \"type\": \"type1\","
  681. " \"cache-enable\": true,"
  682. " \"params\": [\"example.org\"]"
  683. "}]"));
  684. EXPECT_THROW(list_->configure(elem4, true), isc::NotImplemented);
  685. checkDS(0, "test_type", "[\"example.org\"]", true);
  686. }
  687. // This test relies on the property of mapped type of cache.
  688. TEST_F(ListTest,
  689. #ifdef USE_SHARED_MEMORY
  690. cacheInNonWritableSegment
  691. #else
  692. DISABLED_cacheInNonWritableSegment
  693. #endif
  694. )
  695. {
  696. // Initializing data source with non writable zone table memory segment
  697. // is possible. Loading is just postponed
  698. const ConstElementPtr elem(Element::fromJSON("["
  699. "{"
  700. " \"type\": \"test_type\","
  701. " \"cache-enable\": true,"
  702. " \"cache-type\": \"mapped\","
  703. " \"cache-zones\": [\"example.org\"],"
  704. " \"params\": [\"example.org\"]"
  705. "}]"));
  706. list_->configure(elem, true); // no disruption
  707. checkDS(0, "test_type", "[\"example.org\"]", true);
  708. const shared_ptr<InMemoryClient> cache(list_->getDataSources()[0].cache_);
  709. // Likewise, reload attempt will fail.
  710. EXPECT_EQ(ConfigurableClientList::CACHE_NOT_WRITABLE,
  711. doReload(Name("example.org")));
  712. }
  713. TEST_F(ListTest, masterFiles) {
  714. const ConstElementPtr elem(Element::fromJSON("["
  715. "{"
  716. " \"type\": \"MasterFiles\","
  717. " \"cache-enable\": true,"
  718. " \"params\": {"
  719. " \".\": \"" TEST_DATA_DIR "/root.zone\""
  720. " }"
  721. "}]"));
  722. list_->configure(elem, true);
  723. // It has only the cache
  724. EXPECT_EQ(static_cast<const DataSourceClient*>(NULL),
  725. list_->getDataSources()[0].data_src_client_);
  726. // And it can search
  727. positiveResult(list_->find(Name(".")), ds_[0], Name("."), true, "root",
  728. true);
  729. // If cache is not enabled, nothing is loaded
  730. list_->configure(elem, false);
  731. EXPECT_EQ(0, list_->getDataSources().size());
  732. }
  733. // Test the names are set correctly and collission is detected.
  734. TEST_F(ListTest, names) {
  735. // Explicit name
  736. const ConstElementPtr elem1(Element::fromJSON("["
  737. "{"
  738. " \"type\": \"MasterFiles\","
  739. " \"cache-enable\": true,"
  740. " \"params\": {"
  741. " \".\": \"" TEST_DATA_DIR "/root.zone\""
  742. " },"
  743. " \"name\": \"Whatever\""
  744. "}]"));
  745. list_->configure(elem1, true);
  746. EXPECT_EQ("Whatever", list_->getDataSources()[0].name_);
  747. // Default name
  748. const ConstElementPtr elem2(Element::fromJSON("["
  749. "{"
  750. " \"type\": \"MasterFiles\","
  751. " \"cache-enable\": true,"
  752. " \"params\": {"
  753. " \".\": \"" TEST_DATA_DIR "/root.zone\""
  754. " }"
  755. "}]"));
  756. list_->configure(elem2, true);
  757. EXPECT_EQ("MasterFiles", list_->getDataSources()[0].name_);
  758. // Collission
  759. const ConstElementPtr elem3(Element::fromJSON("["
  760. "{"
  761. " \"type\": \"MasterFiles\","
  762. " \"cache-enable\": true,"
  763. " \"params\": {"
  764. " \".\": \"" TEST_DATA_DIR "/root.zone\""
  765. " }"
  766. "},"
  767. "{"
  768. " \"type\": \"MasterFiles\","
  769. " \"cache-enable\": true,"
  770. " \"params\": {"
  771. " \".\": \"" TEST_DATA_DIR "/root.zone\""
  772. " },"
  773. " \"name\": \"MasterFiles\""
  774. "}]"));
  775. EXPECT_THROW(list_->configure(elem3, true),
  776. ConfigurableClientList::ConfigurationError);
  777. }
  778. TEST_F(ListTest, BadMasterFile) {
  779. // Configuration should succeed, and the good zones in the list
  780. // below should be loaded. Bad zones won't be "loaded" in its usual sense,
  781. // but are still recognized with conceptual "empty" data.
  782. const ConstElementPtr elem(Element::fromJSON("["
  783. "{"
  784. " \"type\": \"MasterFiles\","
  785. " \"cache-enable\": true,"
  786. " \"params\": {"
  787. // good zone
  788. " \"example.com.\": \"" TEST_DATA_DIR "/example.com.flattened\","
  789. // bad zone (empty file)
  790. " \"example.net.\": \"" TEST_DATA_DIR "/example.net-empty\","
  791. // bad zone (data doesn't validate: see the file for details)
  792. " \"example.edu.\": \"" TEST_DATA_DIR "/example.edu-broken\","
  793. // bad zone (file doesn't exist)
  794. " \"example.info.\": \"" TEST_DATA_DIR "/example.info-nonexist\","
  795. // bad zone (data doesn't match the zone name)
  796. " \"foo.bar.\": \"" TEST_DATA_DIR "/example.org.nsec3-signed\","
  797. // good zone
  798. " \".\": \"" TEST_DATA_DIR "/root.zone\""
  799. " }"
  800. "}]"));
  801. EXPECT_NO_THROW({
  802. // This should not throw even if there are any zone loading
  803. // errors.
  804. list_->configure(elem, true);
  805. });
  806. positiveResult(list_->find(Name("example.com."), true), ds_[0],
  807. Name("example.com."), true, "example.com", true);
  808. // Bad cases: should result in "empty zone", whether the match is exact
  809. // or partial.
  810. emptyResult(list_->find(Name("foo.bar"), true), true, "foo.bar");
  811. emptyResult(list_->find(Name("example.net."), true), true, "example.net");
  812. emptyResult(list_->find(Name("example.edu."), true), true, "example.edu");
  813. emptyResult(list_->find(Name("example.info."), true), true,
  814. "example.info");
  815. emptyResult(list_->find(Name("www.example.edu."), false), false,
  816. "example.edu, partial");
  817. positiveResult(list_->find(Name(".")), ds_[0], Name("."), true, "root",
  818. true);
  819. // This one simply doesn't exist.
  820. EXPECT_TRUE(list_->find(Name("example.org."), true) == negative_result_);
  821. }
  822. ConfigurableClientList::CacheStatus
  823. ListTest::doReload(const Name& origin, const string& datasrc_name) {
  824. ConfigurableClientList::ZoneWriterPair
  825. result(list_->getCachedZoneWriter(origin, datasrc_name));
  826. if (result.first == ConfigurableClientList::ZONE_SUCCESS) {
  827. // Can't use ASSERT_NE here, it would want to return(), which
  828. // it can't in non-void function.
  829. if (result.second) {
  830. result.second->load();
  831. result.second->install();
  832. result.second->cleanup();
  833. } else {
  834. ADD_FAILURE() << "getCachedZoneWriter returned ZONE_SUCCESS, "
  835. "but the writer is NULL";
  836. }
  837. } else {
  838. EXPECT_EQ(static_cast<memory::ZoneWriter*>(NULL), result.second.get());
  839. }
  840. return (result.first);
  841. }
  842. // Test we can reload a zone
  843. TEST_F(ListTest, reloadSuccess) {
  844. list_->configure(config_elem_zones_, true);
  845. const Name name("example.org");
  846. prepareCache(0, name);
  847. // The cache currently contains a tweaked version of zone, which
  848. // doesn't have "tstzonedata" A record. So the lookup should result
  849. // in NXDOMAIN.
  850. EXPECT_EQ(ZoneFinder::NXDOMAIN,
  851. list_->find(name).finder_->
  852. find(Name("tstzonedata").concatenate(name),
  853. RRType::A())->code);
  854. // Now reload the full zone. It should be there now.
  855. EXPECT_EQ(ConfigurableClientList::ZONE_SUCCESS, doReload(name));
  856. EXPECT_EQ(ZoneFinder::SUCCESS,
  857. list_->find(name).finder_->
  858. find(Name("tstzonedata").concatenate(name),
  859. RRType::A())->code);
  860. }
  861. // The cache is not enabled. The load should be rejected.
  862. TEST_F(ListTest, reloadNotAllowed) {
  863. list_->configure(config_elem_zones_, false);
  864. const Name name("example.org");
  865. // We put the cache in even when not enabled. This won't confuse the thing.
  866. prepareCache(0, name);
  867. // See the reloadSuccess test. This should result in NXDOMAIN.
  868. EXPECT_EQ(ZoneFinder::NXDOMAIN,
  869. list_->find(name).finder_->
  870. find(Name("tstzonedata").concatenate(name),
  871. RRType::A())->code);
  872. // Now reload. It should reject it.
  873. EXPECT_EQ(ConfigurableClientList::CACHE_DISABLED, doReload(name));
  874. // Nothing changed here
  875. EXPECT_EQ(ZoneFinder::NXDOMAIN,
  876. list_->find(name).finder_->
  877. find(Name("tstzonedata").concatenate(name),
  878. RRType::A())->code);
  879. }
  880. // Similar to the previous case, but the cache is disabled in config.
  881. TEST_F(ListTest, reloadNotEnabled) {
  882. list_->configure(config_elem_zones_, true);
  883. const Name name("example.org");
  884. // We put the cache, actually disabling it.
  885. prepareCache(0, name, false);
  886. // In this case we cannot really look up due to the limitation of
  887. // the mock implementation. We only check reload fails.
  888. EXPECT_EQ(ConfigurableClientList::ZONE_NOT_CACHED, doReload(name));
  889. }
  890. // Test several cases when the zone does not exist
  891. TEST_F(ListTest, reloadNoSuchZone) {
  892. list_->configure(config_elem_zones_, true);
  893. const Name name("example.org");
  894. // We put the cache in even when not enabled. This won't confuse the
  895. // reload method, as that one looks at the real state of things, not
  896. // at the configuration.
  897. prepareCache(0, Name("example.com"));
  898. // Not in the data sources
  899. EXPECT_EQ(ConfigurableClientList::ZONE_NOT_FOUND,
  900. doReload(Name("exmaple.cz")));
  901. // If it's not configured to be cached, it won't be reloaded.
  902. EXPECT_EQ(ConfigurableClientList::ZONE_NOT_CACHED, doReload(name));
  903. // Partial match
  904. EXPECT_EQ(ConfigurableClientList::ZONE_NOT_FOUND,
  905. doReload(Name("sub.example.com")));
  906. // Nothing changed here - these zones don't exist
  907. EXPECT_EQ(static_cast<isc::datasrc::DataSourceClient*>(NULL),
  908. list_->find(name).dsrc_client_);
  909. EXPECT_EQ(static_cast<isc::datasrc::DataSourceClient*>(NULL),
  910. list_->find(Name("example.cz")).dsrc_client_);
  911. EXPECT_EQ(static_cast<isc::datasrc::DataSourceClient*>(NULL),
  912. list_->find(Name("sub.example.com"), true).dsrc_client_);
  913. // Not reloaded, so A record shouldn't be visible yet.
  914. EXPECT_EQ(ZoneFinder::NXDOMAIN,
  915. list_->find(Name("example.com")).finder_->
  916. find(Name("tstzonedata.example.com"),
  917. RRType::A())->code);
  918. }
  919. // Check we gracefully reject reloading (i.e. no exception) when a zone
  920. // disappeared in the underlying data source when we want to reload it
  921. TEST_F(ListTest, reloadZoneGone) {
  922. list_->configure(config_elem_zones_, true);
  923. const Name name("example.org");
  924. // We put in a cache for non-existent zone. This emulates being loaded
  925. // and then the zone disappearing. We prefill the cache, so we can check
  926. // it.
  927. prepareCache(0, name);
  928. // The (cached) zone contains zone's SOA
  929. EXPECT_EQ(ZoneFinder::SUCCESS,
  930. list_->find(name).finder_->find(name, RRType::SOA())->code);
  931. // Remove the zone from the data source.
  932. static_cast<MockDataSourceClient*>(
  933. list_->getDataSources()[0].data_src_client_)->eraseZone(name);
  934. // The zone is not there, so reload doesn't take place.
  935. EXPECT_EQ(ConfigurableClientList::ZONE_NOT_FOUND, doReload(name));
  936. // The (cached) zone is not hurt.
  937. EXPECT_EQ(ZoneFinder::SUCCESS,
  938. list_->find(name).finder_->find(name, RRType::SOA())->code);
  939. }
  940. TEST_F(ListTest, reloadNewZone) {
  941. // Test the case where a zone to be cached originally doesn't exist
  942. // in the underlying data source and is added later. reload() will
  943. // succeed once it's available in the data source.
  944. const ConstElementPtr elem(Element::fromJSON("["
  945. "{"
  946. " \"type\": \"test_type\","
  947. " \"cache-enable\": true,"
  948. " \"cache-zones\": [\"example.org\", \"example.com\"],"
  949. " \"params\": [\"example.org\"]"
  950. "}]"));
  951. list_->configure(elem, true);
  952. checkDS(0, "test_type", "[\"example.org\"]", true); // no example.com
  953. // We can't reload it either
  954. EXPECT_EQ(ConfigurableClientList::ZONE_NOT_FOUND,
  955. doReload(Name("example.com")));
  956. // If we add the zone, we can now reload it
  957. EXPECT_TRUE(static_cast<MockDataSourceClient*>(
  958. list_->getDataSources()[0].data_src_client_)->
  959. insertZone(Name("example.com")));
  960. EXPECT_EQ(ConfigurableClientList::ZONE_SUCCESS,
  961. doReload(Name("example.com")));
  962. }
  963. // The underlying data source throws. Check we don't modify the state.
  964. TEST_F(ListTest, reloadZoneThrow) {
  965. list_->configure(config_elem_zones_, true);
  966. const Name name("noiter.org");
  967. prepareCache(0, name);
  968. // The zone contains stuff now
  969. EXPECT_EQ(ZoneFinder::SUCCESS,
  970. list_->find(name).finder_->find(name, RRType::SOA())->code);
  971. // The iterator throws, so abort the reload.
  972. EXPECT_THROW(doReload(name), isc::NotImplemented);
  973. // The zone is not hurt.
  974. EXPECT_EQ(ZoneFinder::SUCCESS,
  975. list_->find(name).finder_->find(name, RRType::SOA())->code);
  976. }
  977. TEST_F(ListTest, reloadNullIterator) {
  978. list_->configure(config_elem_zones_, true);
  979. const Name name("null.org");
  980. prepareCache(0, name);
  981. // The zone contains stuff now
  982. EXPECT_EQ(ZoneFinder::SUCCESS,
  983. list_->find(name).finder_->find(name, RRType::SOA())->code);
  984. // The iterator throws, so abort the reload.
  985. EXPECT_THROW(doReload(name), isc::Unexpected);
  986. // The zone is not hurt.
  987. EXPECT_EQ(ZoneFinder::SUCCESS,
  988. list_->find(name).finder_->find(name, RRType::SOA())->code);
  989. }
  990. // Test we can reload the master files too (special-cased)
  991. TEST_F(ListTest, reloadMasterFile) {
  992. const char* const install_cmd = INSTALL_PROG " -c " TEST_DATA_DIR
  993. "/root.zone " TEST_DATA_BUILDDIR "/root.zone.copied";
  994. if (system(install_cmd) != 0) {
  995. // any exception will do, this is failure in test setup, but
  996. // nice to show the command that fails, and shouldn't be caught
  997. isc_throw(isc::Exception,
  998. "Error setting up; command failed: " << install_cmd);
  999. }
  1000. const ConstElementPtr elem(Element::fromJSON("["
  1001. "{"
  1002. " \"type\": \"MasterFiles\","
  1003. " \"cache-enable\": true,"
  1004. " \"params\": {"
  1005. " \".\": \"" TEST_DATA_BUILDDIR "/root.zone.copied\""
  1006. " }"
  1007. "}]"));
  1008. list_->configure(elem, true);
  1009. // Add a record that is not in the zone
  1010. EXPECT_EQ(ZoneFinder::NXDOMAIN,
  1011. list_->find(Name(".")).finder_->find(Name("nosuchdomain"),
  1012. RRType::TXT())->code);
  1013. ofstream f;
  1014. f.open(TEST_DATA_BUILDDIR "/root.zone.copied", ios::out | ios::app);
  1015. f << "nosuchdomain.\t\t3600\tIN\tTXT\ttest" << std::endl;
  1016. f.close();
  1017. // Do the reload.
  1018. EXPECT_EQ(ConfigurableClientList::ZONE_SUCCESS, doReload(Name(".")));
  1019. // It is here now.
  1020. EXPECT_EQ(ZoneFinder::SUCCESS,
  1021. list_->find(Name(".")).finder_->find(Name("nosuchdomain"),
  1022. RRType::TXT())->code);
  1023. }
  1024. TEST_F(ListTest, reloadByDataSourceName) {
  1025. // We use three data sources (and their clients). 2nd and 3rd have
  1026. // the same name of the zones.
  1027. const ConstElementPtr config_elem = Element::fromJSON(
  1028. "[{\"type\": \"test_type1\", \"params\": [\"example.org\"]},"
  1029. " {\"type\": \"test_type2\", \"params\": [\"example.com\"]},"
  1030. " {\"type\": \"test_type3\", \"params\": [\"example.com\"]}]");
  1031. list_->configure(config_elem, true);
  1032. // Prepare in-memory cache for the 1st and 2nd data sources.
  1033. prepareCache(0, Name("example.org"));
  1034. prepareCache(1, Name("example.com"));
  1035. // Normal case: both zone name and data source name matches.
  1036. // See the reloadSuccess test about the NXDOMAIN/SUCCESS checks.
  1037. EXPECT_EQ(ZoneFinder::NXDOMAIN,
  1038. list_->find(Name("tstzonedata.example.com")).finder_->
  1039. find(Name("tstzonedata.example.com"), RRType::A())->code);
  1040. EXPECT_EQ(ConfigurableClientList::ZONE_SUCCESS,
  1041. doReload(Name("example.com"), "test_type2"));
  1042. EXPECT_EQ(ZoneFinder::SUCCESS,
  1043. list_->find(Name("tstzonedata.example.com")).finder_->
  1044. find(Name("tstzonedata.example.com"), RRType::A())->code);
  1045. // The specified zone exists in the first entry of the list, but a
  1046. // different data source name is specified (in which the specified zone
  1047. // doesn't exist), so reloading should fail, and the cache status of the
  1048. // first data source shouldn't change.
  1049. EXPECT_EQ(ZoneFinder::NXDOMAIN,
  1050. list_->find(Name("tstzonedata.example.org")).finder_->
  1051. find(Name("tstzonedata.example.org"), RRType::A())->code);
  1052. EXPECT_EQ(ConfigurableClientList::ZONE_NOT_FOUND,
  1053. doReload(Name("example.org"), "test_type2"));
  1054. EXPECT_EQ(ZoneFinder::NXDOMAIN,
  1055. list_->find(Name("tstzonedata.example.org")).finder_->
  1056. find(Name("tstzonedata.example.org"), RRType::A())->code);
  1057. // Likewise, if a specific data source is given, normal name matching
  1058. // isn't suppressed and the 3rd data source will be used. There cache
  1059. // is disabled, so reload should fail due to "not cached".
  1060. EXPECT_EQ(ConfigurableClientList::ZONE_NOT_CACHED,
  1061. doReload(Name("example.com"), "test_type3"));
  1062. // specified name of data source doesn't exist.
  1063. EXPECT_EQ(ConfigurableClientList::DATASRC_NOT_FOUND,
  1064. doReload(Name("example.org"), "test_type4"));
  1065. }
  1066. // This takes the accessor provided by getZoneTableAccessor(), iterates
  1067. // through the table, and verifies that the expected number of zones are
  1068. // present, as well as the named zone.
  1069. void
  1070. ListTest::accessorIterate(const ConstZoneTableAccessorPtr& accessor,
  1071. int numZones, const string& zoneName="")
  1072. {
  1073. // Confirm basic iterator behavior.
  1074. ASSERT_TRUE(accessor);
  1075. ZoneTableAccessor::IteratorPtr it = accessor->getIterator();
  1076. ASSERT_TRUE(it);
  1077. // Iterator does not guarantee ordering, so we look for the target
  1078. // name anywhere in the table.
  1079. bool found = false;
  1080. int i;
  1081. for (i = 0; !it->isLast(); ++i, it->next()) {
  1082. if (Name(zoneName) == it->getCurrent().origin) {
  1083. found = true;
  1084. }
  1085. }
  1086. EXPECT_EQ(i, numZones);
  1087. if (numZones > 0) {
  1088. EXPECT_TRUE(found);
  1089. }
  1090. }
  1091. TEST_F(ListTest, zoneTableAccessor) {
  1092. // empty configuration
  1093. const ConstElementPtr elem(new ListElement);
  1094. list_->configure(elem, true);
  1095. // null pointer treated as false
  1096. EXPECT_FALSE(list_->getZoneTableAccessor("", true));
  1097. // empty list; expect it to return an empty list
  1098. list_->configure(config_elem_, true);
  1099. ConstZoneTableAccessorPtr z(list_->getZoneTableAccessor("", true));
  1100. accessorIterate(z, 0);
  1101. const ConstElementPtr elem2(Element::fromJSON("["
  1102. "{"
  1103. " \"type\": \"type1\","
  1104. " \"cache-enable\": true,"
  1105. " \"cache-zones\": [\"example.com\"],"
  1106. " \"params\": [\"example.com\"]"
  1107. "},"
  1108. "{"
  1109. " \"type\": \"type2\","
  1110. " \"cache-enable\": false,"
  1111. " \"params\": [\"example.org\"]"
  1112. "},"
  1113. "{"
  1114. " \"type\": \"type3\","
  1115. " \"cache-enable\": true,"
  1116. " \"cache-zones\": [\"example.net\", \"example.info\"],"
  1117. " \"params\": [\"example.net\", \"example.info\"]"
  1118. "}]"));
  1119. // allow_cache = false
  1120. // ask for a non-existent zone table, expect null
  1121. list_->configure(elem2, false);
  1122. EXPECT_FALSE(list_->getZoneTableAccessor("bogus", true));
  1123. // ask for any zone table, expect an empty list
  1124. z = list_->getZoneTableAccessor("", true);
  1125. accessorIterate(z, 0);
  1126. // allow_cache = true, use_cache = false
  1127. list_->configure(elem2, true);
  1128. EXPECT_THROW(list_->getZoneTableAccessor("", false), isc::NotImplemented);
  1129. EXPECT_THROW(list_->getZoneTableAccessor("type1", false),
  1130. isc::NotImplemented);
  1131. // datasrc not found, returns NULL pointer
  1132. EXPECT_FALSE(list_->getZoneTableAccessor("bogus", true));
  1133. // return first datasrc
  1134. z = list_->getZoneTableAccessor("", true);
  1135. accessorIterate(z, 1, "example.com");
  1136. // datasrc has cache disabled, returns accessor to empty list
  1137. z = list_->getZoneTableAccessor("type2", true);
  1138. accessorIterate(z, 0);
  1139. // search by name
  1140. z = list_->getZoneTableAccessor("type3", true);
  1141. accessorIterate(z, 2, "example.net");
  1142. }
  1143. // Check the status holds data
  1144. TEST(DataSourceStatus, status) {
  1145. const DataSourceStatus status("Test", SEGMENT_INUSE, "local");
  1146. EXPECT_EQ("Test", status.getName());
  1147. EXPECT_EQ(SEGMENT_INUSE, status.getSegmentState());
  1148. EXPECT_EQ("local", status.getSegmentType());
  1149. const DataSourceStatus status_unused("Unused", SEGMENT_UNUSED, "");
  1150. EXPECT_EQ("Unused", status_unused.getName());
  1151. EXPECT_EQ(SEGMENT_UNUSED, status_unused.getSegmentState());
  1152. EXPECT_THROW(status_unused.getSegmentType(), isc::InvalidOperation);
  1153. }
  1154. }