client_list_unittest.cc 46 KB

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