zone_loader_unittest.cc 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441
  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/zone_loader.h>
  15. #include <datasrc/data_source.h>
  16. #include <datasrc/memory/zone_table_segment.h>
  17. #include <datasrc/memory/memory_client.h>
  18. #include <dns/rrclass.h>
  19. #include <dns/name.h>
  20. #include <dns/rrset.h>
  21. #include <util/memory_segment_local.h>
  22. #include <exceptions/exceptions.h>
  23. #include <gtest/gtest.h>
  24. #include <boost/shared_ptr.hpp>
  25. #include <string>
  26. #include <vector>
  27. using isc::dns::RRClass;
  28. using isc::dns::Name;
  29. using isc::dns::RRType;
  30. using isc::dns::ConstRRsetPtr;
  31. using std::string;
  32. using std::vector;
  33. using boost::shared_ptr;
  34. using namespace isc::datasrc;
  35. namespace {
  36. class MockClient : public DataSourceClient {
  37. public:
  38. MockClient() :
  39. commit_called_(false),
  40. missing_zone_(false),
  41. rrclass_(RRClass::IN())
  42. {}
  43. virtual FindResult findZone(const Name&) const {
  44. isc_throw(isc::NotImplemented, "Method not used in tests");
  45. };
  46. virtual std::pair<ZoneJournalReader::Result, ZoneJournalReaderPtr>
  47. getJournalReader(const Name&, uint32_t, uint32_t) const
  48. {
  49. isc_throw(isc::NotImplemented, "Method not used in tests");
  50. }
  51. virtual ZoneUpdaterPtr getUpdater(const Name& name, bool replace,
  52. bool journaling) const;
  53. // We store some information about what was happening here.
  54. // It is publicly accessible, since this is private testing class
  55. // anyway, so no need to dress it fancy into getters. Some are mutable,
  56. // since many client methods are const, but we still want to know they
  57. // were called.
  58. mutable vector<Name> provided_updaters_;
  59. // We store string representations of the RRsets. This is simpler than
  60. // copying them and we can't really put them into shared pointers, because
  61. // we get them as references.
  62. vector<string> rrsets_;
  63. bool commit_called_;
  64. // If set to true, getUpdater returns NULL
  65. bool missing_zone_;
  66. // The pretended class of the client. Usualy IN, but can be overriden.
  67. RRClass rrclass_;
  68. };
  69. // The updater isn't really correct according to the API. For example,
  70. // the whole client can be committed only once in its lifetime. The
  71. // updaters would influence each other if there were more. But we
  72. // don't need more updaters in the same test, so it doesn't matter
  73. // and this way, it is much simpler.
  74. class Updater : public ZoneUpdater {
  75. public:
  76. Updater(MockClient* client) :
  77. client_(client),
  78. finder_(client_->rrclass_)
  79. {}
  80. virtual ZoneFinder& getFinder() {
  81. return (finder_);
  82. }
  83. virtual void addRRset(const isc::dns::AbstractRRset& rrset) {
  84. if (client_->commit_called_) {
  85. isc_throw(DataSourceError, "Add after commit");
  86. }
  87. client_->rrsets_.push_back(rrset.toText());
  88. }
  89. virtual void deleteRRset(const isc::dns::AbstractRRset&) {
  90. isc_throw(isc::NotImplemented, "Method not used in tests");
  91. }
  92. virtual void commit() {
  93. client_->commit_called_ = true;
  94. }
  95. private:
  96. MockClient* client_;
  97. class Finder : public ZoneFinder {
  98. public:
  99. Finder(const RRClass& rrclass) :
  100. class_(rrclass)
  101. {}
  102. virtual RRClass getClass() const {
  103. return (class_);
  104. }
  105. virtual Name getOrigin() const {
  106. isc_throw(isc::NotImplemented, "Method not used in tests");
  107. }
  108. virtual shared_ptr<Context> find(const Name&, const RRType&,
  109. const FindOptions)
  110. {
  111. isc_throw(isc::NotImplemented, "Method not used in tests");
  112. }
  113. virtual shared_ptr<Context> findAll(const Name&,
  114. vector<ConstRRsetPtr>&,
  115. const FindOptions)
  116. {
  117. isc_throw(isc::NotImplemented, "Method not used in tests");
  118. }
  119. virtual FindNSEC3Result findNSEC3(const Name&, bool) {
  120. isc_throw(isc::NotImplemented, "Method not used in tests");
  121. }
  122. private:
  123. const RRClass class_;
  124. } finder_;
  125. };
  126. ZoneUpdaterPtr
  127. MockClient::getUpdater(const Name& name, bool replace, bool journaling) const {
  128. if (missing_zone_) {
  129. return (ZoneUpdaterPtr());
  130. }
  131. EXPECT_TRUE(replace);
  132. EXPECT_FALSE(journaling);
  133. provided_updaters_.push_back(name);
  134. // const_cast is bad. But the const on getUpdater seems wrong in the first
  135. // place, since updater will be modifying the data there. And the updater
  136. // wants to store data into the client so we can examine it later.
  137. return (ZoneUpdaterPtr(new Updater(const_cast<MockClient*>(this))));
  138. }
  139. class ZoneLoaderTest : public ::testing::Test {
  140. protected:
  141. ZoneLoaderTest() :
  142. rrclass_(RRClass::IN()),
  143. ztable_segment_(memory::ZoneTableSegment::
  144. create(isc::data::NullElement(), rrclass_)),
  145. source_client_(ztable_segment_, rrclass_)
  146. {}
  147. void prepareSource(const Name& zone, const char* filename) {
  148. // TODO:
  149. // Currently, load uses an urelated implementation. In the long term,
  150. // the method will probably be deprecated. At that time, we should
  151. // probably prepare the data in some other way (using sqlite3 or
  152. // something). This is simpler for now.
  153. source_client_.load(zone, string(TEST_DATA_DIR) + "/" + filename);
  154. }
  155. private:
  156. const RRClass rrclass_;
  157. // This is because of the in-memory client. We use it to read data
  158. // from. It is still easier than setting up sqlite3 client, since
  159. // we have this one in the linked library.
  160. // FIXME: We should be destroying it by ZoneTableSegment::destroy.
  161. // But the shared pointer won't let us, will it?
  162. shared_ptr<memory::ZoneTableSegment> ztable_segment_;
  163. protected:
  164. memory::InMemoryClient source_client_;
  165. // This one is mocked. It will help us see what is happening inside.
  166. // Also, mocking it is simpler than setting up an sqlite3 client.
  167. MockClient destination_client_;
  168. };
  169. // Use the loader to load an unsigned zone.
  170. TEST_F(ZoneLoaderTest , copyUnsigned) {
  171. prepareSource(Name::ROOT_NAME(), "root.zone");
  172. ZoneLoader loader(destination_client_, Name::ROOT_NAME(), source_client_);
  173. // It gets the updater directly in the constructor
  174. ASSERT_EQ(1, destination_client_.provided_updaters_.size());
  175. EXPECT_EQ(Name::ROOT_NAME(), destination_client_.provided_updaters_[0]);
  176. // Counter is initialized to 0, progress is "unknown" in case of copy.
  177. EXPECT_EQ(0, loader.getRRCount());
  178. EXPECT_EQ(ZoneLoader::PROGRESS_UNKNOWN, loader.getProgress());
  179. // Now load the whole zone
  180. loader.load();
  181. EXPECT_TRUE(destination_client_.commit_called_);
  182. // We don't check the whole zone. We check the first and last and the
  183. // count, which should be enough.
  184. // The count is 34 because we expect the RRs to be separated.
  185. EXPECT_EQ(34, destination_client_.rrsets_.size());
  186. // Check various counters. getRRCount should be identical of the RRs
  187. // we've seen. progress is still "unknown" in the copy operation.
  188. EXPECT_EQ(destination_client_.rrsets_.size(), loader.getRRCount());
  189. EXPECT_EQ(ZoneLoader::PROGRESS_UNKNOWN, loader.getProgress());
  190. // Ensure known order.
  191. std::sort(destination_client_.rrsets_.begin(),
  192. destination_client_.rrsets_.end());
  193. EXPECT_EQ(". 518400 IN NS a.root-servers.net.\n",
  194. destination_client_.rrsets_.front());
  195. EXPECT_EQ("m.root-servers.net. 3600000 IN AAAA 2001:dc3::35\n",
  196. destination_client_.rrsets_.back());
  197. // It isn't possible to try again now
  198. EXPECT_THROW(loader.load(), isc::InvalidOperation);
  199. EXPECT_THROW(loader.loadIncremental(1), isc::InvalidOperation);
  200. // Even 0, which should load nothing, returns the error
  201. EXPECT_THROW(loader.loadIncremental(0), isc::InvalidOperation);
  202. }
  203. // Try loading incrementally.
  204. TEST_F(ZoneLoaderTest, copyUnsignedIncremental) {
  205. prepareSource(Name::ROOT_NAME(), "root.zone");
  206. ZoneLoader loader(destination_client_, Name::ROOT_NAME(), source_client_);
  207. // Try loading few RRs first.
  208. loader.loadIncremental(10);
  209. // We should get the 10 we asked for
  210. EXPECT_EQ(10, destination_client_.rrsets_.size());
  211. // Not committed yet, we didn't complete the loading
  212. EXPECT_FALSE(destination_client_.commit_called_);
  213. // Check we can get intermediate counters. progress is always "unknown"
  214. // in case of copy.
  215. EXPECT_EQ(destination_client_.rrsets_.size(), loader.getRRCount());
  216. EXPECT_EQ(ZoneLoader::PROGRESS_UNKNOWN, loader.getProgress());
  217. // This is unusual, but allowed. Check it doesn't do anything
  218. loader.loadIncremental(0);
  219. EXPECT_EQ(10, destination_client_.rrsets_.size());
  220. EXPECT_FALSE(destination_client_.commit_called_);
  221. // We can finish the rest
  222. loader.loadIncremental(30);
  223. EXPECT_EQ(34, destination_client_.rrsets_.size());
  224. EXPECT_TRUE(destination_client_.commit_called_);
  225. // No more loading now
  226. EXPECT_THROW(loader.load(), isc::InvalidOperation);
  227. EXPECT_THROW(loader.loadIncremental(1), isc::InvalidOperation);
  228. EXPECT_THROW(loader.loadIncremental(0), isc::InvalidOperation);
  229. }
  230. // Check we can load RRSIGs and NSEC3 (which could break due to them being
  231. // in separate namespace)
  232. TEST_F(ZoneLoaderTest, copySigned) {
  233. prepareSource(Name("example.org"), "example.org.nsec3-signed");
  234. ZoneLoader loader(destination_client_, Name("example.org"),
  235. source_client_);
  236. loader.load();
  237. // All the RRs are there, including the ones in NSEC3 namespace
  238. EXPECT_EQ(14, destination_client_.rrsets_.size());
  239. EXPECT_TRUE(destination_client_.commit_called_);
  240. // Same trick with sorting to know where they are
  241. std::sort(destination_client_.rrsets_.begin(),
  242. destination_client_.rrsets_.end());
  243. // Due to the R at the beginning, this one should be last
  244. EXPECT_EQ("09GM5T42SMIMT7R8DF6RTG80SFMS1NLU.example.org. 1200 IN NSEC3 "
  245. "1 0 10 AABBCCDD RKOF8QMFRB5F2V9EJHFBVB2JPVSA0DJD A RRSIG\n",
  246. destination_client_.rrsets_[0]);
  247. EXPECT_EQ("09GM5T42SMIMT7R8DF6RTG80SFMS1NLU.example.org. 1200 IN RRSIG "
  248. "NSEC3 7 3 1200 20120301040838 20120131040838 19562 example.org."
  249. " EdwMeepLf//lV+KpCAN+213Scv1rrZyj4i2OwoCP4XxxS3CWGSuvYuKOyfZc8w"
  250. "KRcrD/4YG6nZVXE0s5O8NahjBJmDIyVt4WkfZ6QthxGg8ggLVvcD3dFksPyiKHf"
  251. "/WrTOZPSsxvN5m/i1Ey6+YWS01Gf3WDCMWDauC7Nmh3CTM=\n",
  252. destination_client_.rrsets_[1]);
  253. }
  254. // If the destination zone does not exist, it throws
  255. TEST_F(ZoneLoaderTest, copyMissingDestination) {
  256. destination_client_.missing_zone_ = true;
  257. prepareSource(Name::ROOT_NAME(), "root.zone");
  258. EXPECT_THROW(ZoneLoader(destination_client_, Name::ROOT_NAME(),
  259. source_client_), DataSourceError);
  260. }
  261. // If the source zone does not exist, it throws
  262. TEST_F(ZoneLoaderTest, copyMissingSource) {
  263. EXPECT_THROW(ZoneLoader(destination_client_, Name::ROOT_NAME(),
  264. source_client_), DataSourceError);
  265. }
  266. // The class of the source and destination are different
  267. TEST_F(ZoneLoaderTest, classMismatch) {
  268. destination_client_.rrclass_ = RRClass::CH();
  269. prepareSource(Name::ROOT_NAME(), "root.zone");
  270. EXPECT_THROW(ZoneLoader(destination_client_, Name::ROOT_NAME(),
  271. source_client_), isc::InvalidParameter);
  272. }
  273. // Load an unsigned zone, all at once
  274. TEST_F(ZoneLoaderTest, loadUnsigned) {
  275. ZoneLoader loader(destination_client_, Name::ROOT_NAME(),
  276. TEST_DATA_DIR "/root.zone");
  277. // Counter and progress are initialized to 0.
  278. EXPECT_EQ(0, loader.getRRCount());
  279. EXPECT_EQ(0, loader.getProgress());
  280. // It gets the updater directly in the constructor
  281. ASSERT_EQ(1, destination_client_.provided_updaters_.size());
  282. EXPECT_EQ(Name::ROOT_NAME(), destination_client_.provided_updaters_[0]);
  283. // Now load the whole zone
  284. loader.load();
  285. EXPECT_TRUE(destination_client_.commit_called_);
  286. // We don't check the whole zone. We check the first and last and the
  287. // count, which should be enough.
  288. // The count is 34 because we expect the RRs to be separated.
  289. EXPECT_EQ(34, destination_client_.rrsets_.size());
  290. // getRRCount should be identical of the RRs we've seen. progress
  291. // should reach 100% (= 1).
  292. EXPECT_EQ(destination_client_.rrsets_.size(), loader.getRRCount());
  293. EXPECT_EQ(1, loader.getProgress());
  294. // Ensure known order.
  295. std::sort(destination_client_.rrsets_.begin(),
  296. destination_client_.rrsets_.end());
  297. EXPECT_EQ(". 518400 IN NS a.root-servers.net.\n",
  298. destination_client_.rrsets_.front());
  299. EXPECT_EQ("m.root-servers.net. 3600000 IN AAAA 2001:dc3::35\n",
  300. destination_client_.rrsets_.back());
  301. // It isn't possible to try again now
  302. EXPECT_THROW(loader.load(), isc::InvalidOperation);
  303. EXPECT_THROW(loader.loadIncremental(1), isc::InvalidOperation);
  304. // Even 0, which should load nothing, returns the error
  305. EXPECT_THROW(loader.loadIncremental(0), isc::InvalidOperation);
  306. }
  307. // Try loading from master file incrementally.
  308. TEST_F(ZoneLoaderTest, loadUnsignedIncremental) {
  309. ZoneLoader loader(destination_client_, Name::ROOT_NAME(),
  310. TEST_DATA_DIR "/root.zone");
  311. // Counters are initialized to 0.
  312. EXPECT_EQ(0, loader.getRRCount());
  313. EXPECT_EQ(0, loader.getProgress());
  314. // Try loading few RRs first.
  315. loader.loadIncremental(10);
  316. // We should get the 10 we asked for
  317. EXPECT_EQ(10, destination_client_.rrsets_.size());
  318. // Not committed yet, we didn't complete the loading
  319. EXPECT_FALSE(destination_client_.commit_called_);
  320. EXPECT_EQ(10, destination_client_.rrsets_.size());
  321. EXPECT_FALSE(destination_client_.commit_called_);
  322. // Check we can get intermediate counters. expected progress is calculated
  323. // based on the size of the zone file and the offset to the end of 10th RR
  324. // (subject to future changes to the file, but we assume it's a rare
  325. // event.). The expected value should be the exact expression that
  326. // getProgress() should do internally, so EXPECT_EQ() should work here,
  327. // but floating-point comparison can be always tricky we use
  328. // EXPECT_DOUBLE_EQ just in case.
  329. EXPECT_EQ(destination_client_.rrsets_.size(), loader.getRRCount());
  330. // file size = 1541, offset = 428 (27.77%).
  331. EXPECT_DOUBLE_EQ(static_cast<double>(428) / 1541, loader.getProgress());
  332. // We can finish the rest
  333. loader.loadIncremental(30);
  334. EXPECT_EQ(34, destination_client_.rrsets_.size());
  335. EXPECT_TRUE(destination_client_.commit_called_);
  336. // Counters are updated accordingly. progress should reach 100%.
  337. EXPECT_EQ(destination_client_.rrsets_.size(), loader.getRRCount());
  338. EXPECT_EQ(1, loader.getProgress());
  339. // No more loading now
  340. EXPECT_THROW(loader.load(), isc::InvalidOperation);
  341. EXPECT_THROW(loader.loadIncremental(1), isc::InvalidOperation);
  342. EXPECT_THROW(loader.loadIncremental(0), isc::InvalidOperation);
  343. }
  344. // If the destination zone does not exist, it throws
  345. TEST_F(ZoneLoaderTest, loadMissingDestination) {
  346. destination_client_.missing_zone_ = true;
  347. EXPECT_THROW(ZoneLoader(destination_client_, Name::ROOT_NAME(),
  348. TEST_DATA_DIR "/root.zone"), DataSourceError);
  349. }
  350. // Check we can load RRSIGs and NSEC3 (which could break due to them being
  351. // in separate namespace)
  352. TEST_F(ZoneLoaderTest, loadSigned) {
  353. ZoneLoader loader(destination_client_, Name("example.org"),
  354. TEST_DATA_DIR "/example.org.nsec3-signed");
  355. loader.load();
  356. // All the RRs are there, including the ones in NSEC3 namespace
  357. EXPECT_EQ(14, destination_client_.rrsets_.size());
  358. EXPECT_TRUE(destination_client_.commit_called_);
  359. // Same trick with sorting to know where they are
  360. std::sort(destination_client_.rrsets_.begin(),
  361. destination_client_.rrsets_.end());
  362. // Due to the R at the beginning, this one should be last
  363. EXPECT_EQ("09GM5T42SMIMT7R8DF6RTG80SFMS1NLU.example.org. 1200 IN NSEC3 "
  364. "1 0 10 AABBCCDD RKOF8QMFRB5F2V9EJHFBVB2JPVSA0DJD A RRSIG\n",
  365. destination_client_.rrsets_[0]);
  366. EXPECT_EQ("09GM5T42SMIMT7R8DF6RTG80SFMS1NLU.example.org. 1200 IN RRSIG "
  367. "NSEC3 7 3 1200 20120301040838 20120131040838 19562 example.org."
  368. " EdwMeepLf//lV+KpCAN+213Scv1rrZyj4i2OwoCP4XxxS3CWGSuvYuKOyfZc8w"
  369. "KRcrD/4YG6nZVXE0s5O8NahjBJmDIyVt4WkfZ6QthxGg8ggLVvcD3dFksPyiKHf"
  370. "/WrTOZPSsxvN5m/i1Ey6+YWS01Gf3WDCMWDauC7Nmh3CTM=\n",
  371. destination_client_.rrsets_[1]);
  372. }
  373. // Test it throws when there's no such file
  374. TEST_F(ZoneLoaderTest, loadNoSuchFile) {
  375. ZoneLoader loader(destination_client_, Name::ROOT_NAME(),
  376. "This file does not exist");
  377. EXPECT_THROW(loader.load(), MasterFileError);
  378. EXPECT_FALSE(destination_client_.commit_called_);
  379. }
  380. // And it also throws when there's a syntax error in the master file
  381. TEST_F(ZoneLoaderTest, loadSyntaxError) {
  382. ZoneLoader loader(destination_client_, Name::ROOT_NAME(),
  383. // This is not a master file for sure
  384. // (misusing a file that happens to be there
  385. // already).
  386. TEST_DATA_DIR "/example.org.sqlite3");
  387. EXPECT_THROW(loader.load(), MasterFileError);
  388. EXPECT_FALSE(destination_client_.commit_called_);
  389. }
  390. }