123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993 |
- // Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
- //
- // Permission to use, copy, modify, and/or distribute this software for any
- // purpose with or without fee is hereby granted, provided that the above
- // copyright notice and this permission notice appear in all copies.
- //
- // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
- // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
- // AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
- // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
- // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
- // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
- // PERFORMANCE OF THIS SOFTWARE.
- #include <datasrc/client_list.h>
- #include <datasrc/client.h>
- #include <datasrc/iterator.h>
- #include <datasrc/data_source.h>
- #include <datasrc/memory/memory_client.h>
- #include <datasrc/memory/zone_table_segment.h>
- #include <datasrc/memory/zone_finder.h>
- #include <dns/rrclass.h>
- #include <dns/rrttl.h>
- #include <dns/rdataclass.h>
- #include <gtest/gtest.h>
- #include <set>
- #include <fstream>
- using namespace isc::datasrc;
- using isc::datasrc::memory::InMemoryClient;
- using isc::datasrc::memory::ZoneTableSegment;
- using isc::datasrc::memory::InMemoryZoneFinder;
- using namespace isc::data;
- using namespace isc::dns;
- using namespace boost;
- using namespace std;
- namespace {
- // A test data source. It pretends it has some zones.
- class MockDataSourceClient : public DataSourceClient {
- public:
- class Finder : public ZoneFinder {
- public:
- Finder(const Name& origin) :
- origin_(origin)
- {}
- Name getOrigin() const { return (origin_); }
- // The rest is not to be called, so just have them
- RRClass getClass() const {
- isc_throw(isc::NotImplemented, "Not implemented");
- }
- shared_ptr<Context> find(const Name&, const RRType&,
- const FindOptions)
- {
- isc_throw(isc::NotImplemented, "Not implemented");
- }
- shared_ptr<Context> findAll(const Name&,
- vector<ConstRRsetPtr>&,
- const FindOptions)
- {
- isc_throw(isc::NotImplemented, "Not implemented");
- }
- FindNSEC3Result findNSEC3(const Name&, bool) {
- isc_throw(isc::NotImplemented, "Not implemented");
- }
- private:
- Name origin_;
- };
- class Iterator : public ZoneIterator {
- public:
- Iterator(const Name& origin, bool include_ns) :
- origin_(origin),
- soa_(new RRset(origin_, RRClass::IN(), RRType::SOA(),
- RRTTL(3600)))
- {
- // The RData here is bogus, but it is not used to anything. There
- // just needs to be some.
- soa_->addRdata(rdata::generic::SOA(Name::ROOT_NAME(),
- Name::ROOT_NAME(),
- 0, 0, 0, 0, 0));
- rrsets_.push_back(soa_);
- if (include_ns) {
- ns_.reset(new RRset(origin_, RRClass::IN(), RRType::NS(),
- RRTTL(3600)));
- ns_->addRdata(rdata::generic::NS(Name::ROOT_NAME()));
- rrsets_.push_back(ns_);
- }
- rrsets_.push_back(ConstRRsetPtr());
- it_ = rrsets_.begin();
- }
- virtual isc::dns::ConstRRsetPtr getNextRRset() {
- ConstRRsetPtr result = *it_;
- ++it_;
- return (result);
- }
- virtual isc::dns::ConstRRsetPtr getSOA() const {
- return (soa_);
- }
- private:
- const Name origin_;
- const RRsetPtr soa_;
- RRsetPtr ns_;
- std::vector<ConstRRsetPtr> rrsets_;
- std::vector<ConstRRsetPtr>::const_iterator it_;
- };
- // Constructor from a list of zones.
- MockDataSourceClient(const char* zone_names[]) :
- have_ns_(true), use_baditerator_(true)
- {
- for (const char** zone(zone_names); *zone; ++zone) {
- zones.insert(Name(*zone));
- }
- }
- // Constructor from configuration. The list of zones will be empty, but
- // it will keep the configuration inside for further inspection.
- MockDataSourceClient(const string& type,
- const ConstElementPtr& configuration) :
- type_(type),
- configuration_(configuration),
- have_ns_(true), use_baditerator_(true)
- {
- EXPECT_NE("MasterFiles", type) << "MasterFiles is a special case "
- "and it never should be created as a data source client";
- if (configuration_->getType() == Element::list) {
- for (size_t i(0); i < configuration_->size(); ++i) {
- zones.insert(Name(configuration_->get(i)->stringValue()));
- }
- }
- }
- virtual FindResult findZone(const Name& name) const {
- if (zones.empty()) {
- return (FindResult(result::NOTFOUND, ZoneFinderPtr()));
- }
- set<Name>::const_iterator it(zones.upper_bound(name));
- if (it == zones.begin()) {
- return (FindResult(result::NOTFOUND, ZoneFinderPtr()));
- }
- --it;
- NameComparisonResult compar(it->compare(name));
- const ZoneFinderPtr finder(new Finder(*it));
- switch (compar.getRelation()) {
- case NameComparisonResult::EQUAL:
- return (FindResult(result::SUCCESS, finder));
- case NameComparisonResult::SUPERDOMAIN:
- return (FindResult(result::PARTIALMATCH, finder));
- default:
- return (FindResult(result::NOTFOUND, ZoneFinderPtr()));
- }
- }
- // These methods are not used. They just need to be there to have
- // complete vtable.
- virtual ZoneUpdaterPtr getUpdater(const Name&, bool, bool) const {
- isc_throw(isc::NotImplemented, "Not implemented");
- }
- virtual pair<ZoneJournalReader::Result, ZoneJournalReaderPtr>
- getJournalReader(const Name&, uint32_t, uint32_t) const
- {
- isc_throw(isc::NotImplemented, "Not implemented");
- }
- virtual ZoneIteratorPtr getIterator(const Name& name, bool) const {
- if (use_baditerator_ && name == Name("noiter.org")) {
- isc_throw(isc::NotImplemented, "Asked not to be implemented");
- } else if (use_baditerator_ && name == Name("null.org")) {
- return (ZoneIteratorPtr());
- } else {
- FindResult result(findZone(name));
- if (result.code == isc::datasrc::result::SUCCESS) {
- return (ZoneIteratorPtr(new Iterator(name, have_ns_)));
- } else {
- isc_throw(DataSourceError, "No such zone");
- }
- }
- }
- void disableNS() { have_ns_ = false; }
- void disableBadIterator() { use_baditerator_ = false; }
- const string type_;
- const ConstElementPtr configuration_;
- private:
- set<Name> zones;
- bool have_ns_; // control the iterator behavior wrt whether to include NS
- bool use_baditerator_; // whether to use bogus zone iterators for tests
- };
- // The test version is the same as the normal version. We, however, add
- // some methods to dig directly in the internals, for the tests.
- class TestedList : public ConfigurableClientList {
- public:
- TestedList(const RRClass& rrclass) :
- ConfigurableClientList(rrclass)
- {}
- DataSources& getDataSources() { return (data_sources_); }
- // Overwrite the list's method to get a data source with given type
- // and configuration. We mock the data source and don't create the
- // container. This is just to avoid some complexity in the tests.
- virtual DataSourcePair getDataSourceClient(const string& type,
- const ConstElementPtr&
- configuration)
- {
- if (type == "error") {
- isc_throw(DataSourceError, "The error data source type");
- }
- shared_ptr<MockDataSourceClient>
- ds(new MockDataSourceClient(type, configuration));
- // Make sure it is deleted when the test list is deleted.
- to_delete_.push_back(ds);
- return (DataSourcePair(ds.get(), DataSourceClientContainerPtr()));
- }
- private:
- // Hold list of data sources created internally, so they are preserved
- // until the end of the test and then deleted.
- vector<shared_ptr<MockDataSourceClient> > to_delete_;
- };
- const char* ds_zones[][3] = {
- {
- "example.org.",
- "example.com.",
- NULL
- },
- {
- "sub.example.org.",
- NULL, NULL
- },
- {
- NULL, NULL, NULL
- },
- {
- "sub.example.org.",
- NULL, NULL
- }
- };
- const size_t ds_count = (sizeof(ds_zones) / sizeof(*ds_zones));
- class ListTest : public ::testing::Test {
- public:
- ListTest() :
- rrclass_(RRClass::IN()),
- // The empty list corresponds to a list with no elements inside
- list_(new TestedList(rrclass_)),
- config_elem_(Element::fromJSON("["
- "{"
- " \"type\": \"test_type\","
- " \"params\": {}"
- "}]")),
- config_elem_zones_(Element::fromJSON("["
- "{"
- " \"type\": \"test_type\","
- " \"params\": [\"example.org\", \"example.com\", "
- " \"noiter.org\", \"null.org\"]"
- "}]")),
- config_(Element::fromJSON("{}")),
- segment_(ZoneTableSegment::create(*config_))
- {
- for (size_t i(0); i < ds_count; ++ i) {
- shared_ptr<MockDataSourceClient>
- ds(new MockDataSourceClient(ds_zones[i]));
- ds_.push_back(ds);
- ds_info_.push_back(ConfigurableClientList::DataSourceInfo(
- ds.get(), DataSourceClientContainerPtr(),
- false, rrclass_, segment_));
- }
- }
- // Install a "fake" cached zone using a temporary underlying data source
- // client.
- void prepareCache(size_t index, const Name& zone) {
- // Prepare the temporary data source client
- const char* zones[2];
- const std::string zonename_txt = zone.toText();
- zones[0] = zonename_txt.c_str();
- zones[1] = NULL;
- MockDataSourceClient mock_client(zones);
- // Disable some default features of the mock to distinguish the
- // temporary case from normal case.
- mock_client.disableNS();
- mock_client.disableBadIterator();
- // Create cache from the temporary data source, and push it to the
- // client list.
- const shared_ptr<InMemoryClient> cache(new InMemoryClient(segment_,
- rrclass_));
- cache->load(zone, *mock_client.getIterator(zone, false));
- ConfigurableClientList::DataSourceInfo& dsrc_info =
- list_->getDataSources()[index];
- dsrc_info.cache_ = cache;
- }
- // Check the positive result is as we expect it.
- void positiveResult(const ClientList::FindResult& result,
- const shared_ptr<MockDataSourceClient>& dsrc,
- const Name& name, bool exact,
- const char* test, bool from_cache = false)
- {
- SCOPED_TRACE(test);
- ASSERT_NE(ZoneFinderPtr(), result.finder_);
- EXPECT_EQ(name, result.finder_->getOrigin());
- EXPECT_EQ(exact, result.exact_match_);
- // If it is a positive result, there's something to keep
- // alive, even when we don't know what it is.
- // Any better idea how to test it actually keeps the thing
- // alive?
- EXPECT_NE(shared_ptr<ClientList::FindResult::LifeKeeper>(),
- result.life_keeper_);
- if (from_cache) {
- EXPECT_NE(shared_ptr<InMemoryZoneFinder>(),
- dynamic_pointer_cast<InMemoryZoneFinder>(
- result.finder_)) << "Finder is not from cache";
- EXPECT_TRUE(NULL !=
- dynamic_cast<InMemoryClient*>(result.dsrc_client_));
- } else {
- EXPECT_EQ(dsrc.get(), result.dsrc_client_);
- }
- }
- // Configure the list with multiple data sources, according to
- // some configuration. It uses the index as parameter, to be able to
- // loop through the configurations.
- void multiConfiguration(size_t index) {
- list_->getDataSources().clear();
- switch (index) {
- case 2:
- list_->getDataSources().push_back(ds_info_[2]);
- // The ds_[2] is empty. We just check that it doesn't confuse
- // us. Fall through to the case 0.
- case 0:
- list_->getDataSources().push_back(ds_info_[0]);
- list_->getDataSources().push_back(ds_info_[1]);
- break;
- case 1:
- // The other order
- list_->getDataSources().push_back(ds_info_[1]);
- list_->getDataSources().push_back(ds_info_[0]);
- break;
- case 3:
- list_->getDataSources().push_back(ds_info_[1]);
- list_->getDataSources().push_back(ds_info_[0]);
- // It is the same as ds_[1], but we take from the first one.
- // The first one to match is the correct one.
- list_->getDataSources().push_back(ds_info_[3]);
- break;
- default:
- FAIL() << "Unknown configuration index " << index;
- }
- }
- void checkDS(size_t index, const string& type, const string& params,
- bool cache) const
- {
- ASSERT_GT(list_->getDataSources().size(), index);
- MockDataSourceClient* ds(dynamic_cast<MockDataSourceClient*>(
- list_->getDataSources()[index].data_src_client_));
- // Comparing with NULL does not work
- ASSERT_NE(ds, static_cast<const MockDataSourceClient*>(NULL));
- EXPECT_EQ(type, ds->type_);
- EXPECT_TRUE(Element::fromJSON(params)->equals(*ds->configuration_));
- EXPECT_EQ(cache, list_->getDataSources()[index].cache_ !=
- shared_ptr<InMemoryClient>());
- }
- const RRClass rrclass_;
- shared_ptr<TestedList> list_;
- const ClientList::FindResult negative_result_;
- vector<shared_ptr<MockDataSourceClient> > ds_;
- vector<ConfigurableClientList::DataSourceInfo> ds_info_;
- const ConstElementPtr config_elem_, config_elem_zones_, config_;
- shared_ptr<ZoneTableSegment> segment_;
- };
- // Test the test itself
- TEST_F(ListTest, selfTest) {
- EXPECT_EQ(result::SUCCESS, ds_[0]->findZone(Name("example.org")).code);
- EXPECT_EQ(result::PARTIALMATCH,
- ds_[0]->findZone(Name("sub.example.org")).code);
- EXPECT_EQ(result::NOTFOUND, ds_[0]->findZone(Name("org")).code);
- EXPECT_EQ(result::NOTFOUND, ds_[1]->findZone(Name("example.org")).code);
- EXPECT_EQ(result::NOTFOUND, ds_[0]->findZone(Name("aaa")).code);
- EXPECT_EQ(result::NOTFOUND, ds_[0]->findZone(Name("zzz")).code);
- // Nothing to keep alive here.
- EXPECT_EQ(shared_ptr<ClientList::FindResult::LifeKeeper>(),
- negative_result_.life_keeper_);
- }
- // Test the list we create with empty configuration is, in fact, empty
- TEST_F(ListTest, emptyList) {
- EXPECT_TRUE(list_->getDataSources().empty());
- }
- // Check the values returned by a find on an empty list. It should be
- // a negative answer (nothing found) no matter if we want an exact or inexact
- // match.
- TEST_F(ListTest, emptySearch) {
- // No matter what we try, we don't get an answer.
- // Note: we don't have operator<< for the result class, so we cannot use
- // EXPECT_EQ. Same for other similar cases.
- EXPECT_TRUE(negative_result_ == list_->find(Name("example.org"), false,
- false));
- EXPECT_TRUE(negative_result_ == list_->find(Name("example.org"), false,
- true));
- EXPECT_TRUE(negative_result_ == list_->find(Name("example.org"), true,
- false));
- EXPECT_TRUE(negative_result_ == list_->find(Name("example.org"), true,
- true));
- }
- // Put a single data source inside the list and check it can find an
- // exact match if there's one.
- TEST_F(ListTest, singleDSExactMatch) {
- list_->getDataSources().push_back(ds_info_[0]);
- // This zone is not there
- EXPECT_TRUE(negative_result_ == list_->find(Name("org."), true));
- // But this one is, so check it.
- positiveResult(list_->find(Name("example.org"), true), ds_[0],
- Name("example.org"), true, "Exact match");
- // When asking for a sub zone of a zone there, we get nothing
- // (we want exact match, this would be partial one)
- EXPECT_TRUE(negative_result_ == list_->find(Name("sub.example.org."),
- true));
- }
- // When asking for a partial match, we get all that the exact one, but more.
- TEST_F(ListTest, singleDSBestMatch) {
- list_->getDataSources().push_back(ds_info_[0]);
- // This zone is not there
- EXPECT_TRUE(negative_result_ == list_->find(Name("org.")));
- // But this one is, so check it.
- positiveResult(list_->find(Name("example.org")), ds_[0],
- Name("example.org"), true, "Exact match");
- // When asking for a sub zone of a zone there, we get the parent
- // one.
- positiveResult(list_->find(Name("sub.example.org.")), ds_[0],
- Name("example.org"), false, "Subdomain match");
- }
- const char* const test_names[] = {
- "Sub second",
- "Sub first",
- "With empty",
- "With a duplicity"
- };
- TEST_F(ListTest, multiExactMatch) {
- // Run through all the multi-configurations
- for (size_t i(0); i < sizeof(test_names) / sizeof(*test_names); ++i) {
- SCOPED_TRACE(test_names[i]);
- multiConfiguration(i);
- // Something that is nowhere there
- EXPECT_TRUE(negative_result_ == list_->find(Name("org."), true));
- // This one is there exactly.
- positiveResult(list_->find(Name("example.org"), true), ds_[0],
- Name("example.org"), true, "Exact match");
- // This one too, but in a different data source.
- positiveResult(list_->find(Name("sub.example.org."), true), ds_[1],
- Name("sub.example.org"), true, "Subdomain match");
- // But this one is in neither data source.
- EXPECT_TRUE(negative_result_ ==
- list_->find(Name("sub.example.com."), true));
- }
- }
- TEST_F(ListTest, multiBestMatch) {
- // Run through all the multi-configurations
- for (size_t i(0); i < 4; ++ i) {
- SCOPED_TRACE(test_names[i]);
- multiConfiguration(i);
- // Something that is nowhere there
- EXPECT_TRUE(negative_result_ == list_->find(Name("org.")));
- // This one is there exactly.
- positiveResult(list_->find(Name("example.org")), ds_[0],
- Name("example.org"), true, "Exact match");
- // This one too, but in a different data source.
- positiveResult(list_->find(Name("sub.example.org.")), ds_[1],
- Name("sub.example.org"), true, "Subdomain match");
- // But this one is in neither data source. But it is a subdomain
- // of one of the zones in the first data source.
- positiveResult(list_->find(Name("sub.example.com.")), ds_[0],
- Name("example.com."), false, "Subdomain in com");
- }
- }
- // Check the configuration is empty when the list is empty
- TEST_F(ListTest, configureEmpty) {
- const ConstElementPtr elem(new ListElement);
- list_->configure(elem, true);
- EXPECT_TRUE(list_->getDataSources().empty());
- // Check the exact configuration is preserved
- EXPECT_EQ(elem, list_->getConfiguration());
- }
- // Check we can get multiple data sources and they are in the right order.
- TEST_F(ListTest, configureMulti) {
- const ConstElementPtr elem(Element::fromJSON("["
- "{"
- " \"type\": \"type1\","
- " \"cache\": \"off\","
- " \"params\": {}"
- "},"
- "{"
- " \"type\": \"type2\","
- " \"cache\": \"off\","
- " \"params\": {}"
- "}]"
- ));
- list_->configure(elem, true);
- EXPECT_EQ(2, list_->getDataSources().size());
- checkDS(0, "type1", "{}", false);
- checkDS(1, "type2", "{}", false);
- // Check the exact configuration is preserved
- EXPECT_EQ(elem, list_->getConfiguration());
- }
- // Check we can pass whatever we want to the params
- TEST_F(ListTest, configureParams) {
- const char* params[] = {
- "true",
- "false",
- "null",
- "\"hello\"",
- "42",
- "[]",
- "{}",
- NULL
- };
- for (const char** param(params); *param; ++param) {
- SCOPED_TRACE(*param);
- ConstElementPtr elem(Element::fromJSON(string("["
- "{"
- " \"type\": \"t\","
- " \"cache\": \"off\","
- " \"params\": ") + *param +
- "}]"));
- list_->configure(elem, true);
- EXPECT_EQ(1, list_->getDataSources().size());
- checkDS(0, "t", *param, false);
- }
- }
- TEST_F(ListTest, wrongConfig) {
- const char* configs[] = {
- // A lot of stuff missing from there
- "[{\"type\": \"test_type\", \"params\": 13}, {}]",
- // Some bad types completely
- "{}",
- "true",
- "42",
- "null",
- "[{\"type\": \"test_type\", \"params\": 13}, true]",
- "[{\"type\": \"test_type\", \"params\": 13}, []]",
- "[{\"type\": \"test_type\", \"params\": 13}, 42]",
- // Bad type of type
- "[{\"type\": \"test_type\", \"params\": 13}, {\"type\": 42}]",
- "[{\"type\": \"test_type\", \"params\": 13}, {\"type\": true}]",
- "[{\"type\": \"test_type\", \"params\": 13}, {\"type\": null}]",
- "[{\"type\": \"test_type\", \"params\": 13}, {\"type\": []}]",
- "[{\"type\": \"test_type\", \"params\": 13}, {\"type\": {}}]",
- // Bad type of cache-enable
- "[{\"type\": \"test_type\", \"params\": 13}, "
- "{\"type\": \"x\", \"cache-enable\": 13, \"cache-zones\": []}]",
- "[{\"type\": \"test_type\", \"params\": 13}, "
- "{\"type\": \"x\", \"cache-enable\": \"xx\", \"cache-zones\": []}]",
- "[{\"type\": \"test_type\", \"params\": 13}, "
- "{\"type\": \"x\", \"cache-enable\": [], \"cache-zones\": []}]",
- "[{\"type\": \"test_type\", \"params\": 13}, "
- "{\"type\": \"x\", \"cache-enable\": {}, \"cache-zones\": []}]",
- "[{\"type\": \"test_type\", \"params\": 13}, "
- "{\"type\": \"x\", \"cache-enable\": null, \"cache-zones\": []}]",
- // Bad type of cache-zones
- "[{\"type\": \"test_type\", \"params\": 13}, "
- "{\"type\": \"x\", \"cache-enable\": true, \"cache-zones\": \"x\"}]",
- "[{\"type\": \"test_type\", \"params\": 13}, "
- "{\"type\": \"x\", \"cache-enable\": true, \"cache-zones\": true}]",
- "[{\"type\": \"test_type\", \"params\": 13}, "
- "{\"type\": \"x\", \"cache-enable\": true, \"cache-zones\": null}]",
- "[{\"type\": \"test_type\", \"params\": 13}, "
- "{\"type\": \"x\", \"cache-enable\": true, \"cache-zones\": 13}]",
- "[{\"type\": \"test_type\", \"params\": 13}, "
- "{\"type\": \"x\", \"cache-enable\": true, \"cache-zones\": {}}]",
- // Some bad inputs for MasterFiles special case
- // It must have the cache enabled
- "[{\"type\": \"test_type\", \"params\": 13}, "
- "{\"type\": \"MasterFiles\", \"cache-enable\": false,"
- "\"params\": {}}]",
- // No cache-zones allowed here
- "[{\"type\": \"test_type\", \"params\": 13}, "
- "{\"type\": \"MasterFiles\", \"cache-enable\": true,"
- "\"param\": {}, \"cache-zones\": []}]",
- // Some bad types of params
- "[{\"type\": \"test_type\", \"params\": 13}, "
- "{\"type\": \"MasterFiles\", \"cache-enable\": false,"
- "\"params\": []}]",
- "[{\"type\": \"test_type\", \"params\": 13}, "
- "{\"type\": \"MasterFiles\", \"cache-enable\": false,"
- "\"params\": 13}]",
- "[{\"type\": \"test_type\", \"params\": 13}, "
- "{\"type\": \"MasterFiles\", \"cache-enable\": false,"
- "\"params\": true}]",
- "[{\"type\": \"test_type\", \"params\": 13}, "
- "{\"type\": \"MasterFiles\", \"cache-enable\": false,"
- "\"params\": null}]",
- "[{\"type\": \"test_type\", \"params\": 13}, "
- "{\"type\": \"MasterFiles\", \"cache-enable\": false,"
- "\"params\": \"x\"}]",
- "[{\"type\": \"test_type\", \"params\": 13}, "
- "{\"type\": \"MasterFiles\", \"cache-enable\": false,"
- "\"params\": {\".\": 13}}]",
- "[{\"type\": \"test_type\", \"params\": 13}, "
- "{\"type\": \"MasterFiles\", \"cache-enable\": false,"
- "\"params\": {\".\": true}}]",
- "[{\"type\": \"test_type\", \"params\": 13}, "
- "{\"type\": \"MasterFiles\", \"cache-enable\": false,"
- "\"params\": {\".\": null}}]",
- "[{\"type\": \"test_type\", \"params\": 13}, "
- "{\"type\": \"MasterFiles\", \"cache-enable\": false,"
- "\"params\": {\".\": []}}]",
- "[{\"type\": \"test_type\", \"params\": 13}, "
- "{\"type\": \"MasterFiles\", \"cache-enable\": false,"
- "\"params\": {\".\": {}}}]",
- NULL
- };
- // Put something inside to see it survives the exception
- list_->configure(config_elem_, true);
- checkDS(0, "test_type", "{}", false);
- for (const char** config(configs); *config; ++config) {
- SCOPED_TRACE(*config);
- ConstElementPtr elem(Element::fromJSON(*config));
- EXPECT_THROW(list_->configure(elem, true),
- ConfigurableClientList::ConfigurationError);
- // Still untouched
- checkDS(0, "test_type", "{}", false);
- EXPECT_EQ(1, list_->getDataSources().size());
- }
- }
- // The param thing defaults to null. Cache is not used yet.
- TEST_F(ListTest, defaults) {
- const ConstElementPtr elem(Element::fromJSON("["
- "{"
- " \"type\": \"type1\""
- "}]"));
- list_->configure(elem, true);
- EXPECT_EQ(1, list_->getDataSources().size());
- checkDS(0, "type1", "null", false);
- }
- // Check we can call the configure multiple times, to change the configuration
- TEST_F(ListTest, reconfigure) {
- const ConstElementPtr empty(new ListElement);
- list_->configure(config_elem_, true);
- checkDS(0, "test_type", "{}", false);
- list_->configure(empty, true);
- EXPECT_TRUE(list_->getDataSources().empty());
- list_->configure(config_elem_, true);
- checkDS(0, "test_type", "{}", false);
- }
- // Make sure the data source error exception from the factory is propagated
- TEST_F(ListTest, dataSrcError) {
- const ConstElementPtr elem(Element::fromJSON("["
- "{"
- " \"type\": \"error\""
- "}]"));
- list_->configure(config_elem_, true);
- checkDS(0, "test_type", "{}", false);
- EXPECT_THROW(list_->configure(elem, true), DataSourceError);
- checkDS(0, "test_type", "{}", false);
- }
- // Check we can get the cache
- TEST_F(ListTest, configureCacheEmpty) {
- const ConstElementPtr elem(Element::fromJSON("["
- "{"
- " \"type\": \"type1\","
- " \"cache-enable\": true,"
- " \"cache-zones\": [],"
- " \"params\": {}"
- "},"
- "{"
- " \"type\": \"type2\","
- " \"cache-enable\": false,"
- " \"cache-zones\": [],"
- " \"params\": {}"
- "}]"
- ));
- list_->configure(elem, true);
- EXPECT_EQ(2, list_->getDataSources().size());
- checkDS(0, "type1", "{}", true);
- checkDS(1, "type2", "{}", false);
- }
- // But no cache if we disallow it globally
- TEST_F(ListTest, configureCacheDisabled) {
- const ConstElementPtr elem(Element::fromJSON("["
- "{"
- " \"type\": \"type1\","
- " \"cache-enable\": true,"
- " \"cache-zones\": [],"
- " \"params\": {}"
- "},"
- "{"
- " \"type\": \"type2\","
- " \"cache-enable\": false,"
- " \"cache-zones\": [],"
- " \"params\": {}"
- "}]"
- ));
- list_->configure(elem, false);
- EXPECT_EQ(2, list_->getDataSources().size());
- checkDS(0, "type1", "{}", false);
- checkDS(1, "type2", "{}", false);
- }
- // Put some zones into the cache
- TEST_F(ListTest, cacheZones) {
- const ConstElementPtr elem(Element::fromJSON("["
- "{"
- " \"type\": \"type1\","
- " \"cache-enable\": true,"
- " \"cache-zones\": [\"example.org\", \"example.com\"],"
- " \"params\": [\"example.org\", \"example.com\", \"exmaple.cz\"]"
- "}]"));
- list_->configure(elem, true);
- checkDS(0, "type1", "[\"example.org\", \"example.com\", \"exmaple.cz\"]",
- true);
- const shared_ptr<InMemoryClient> cache(list_->getDataSources()[0].cache_);
- EXPECT_EQ(2, cache->getZoneCount());
- EXPECT_EQ(result::SUCCESS, cache->findZone(Name("example.org")).code);
- EXPECT_EQ(result::SUCCESS, cache->findZone(Name("example.com")).code);
- EXPECT_EQ(result::NOTFOUND, cache->findZone(Name("example.cz")).code);
- EXPECT_EQ(RRClass::IN(),
- cache->findZone(Name("example.org")).zone_finder->getClass());
- // These are cached and answered from the cache
- positiveResult(list_->find(Name("example.com.")), ds_[0],
- Name("example.com."), true, "com", true);
- positiveResult(list_->find(Name("example.org.")), ds_[0],
- Name("example.org."), true, "org", true);
- positiveResult(list_->find(Name("sub.example.com.")), ds_[0],
- Name("example.com."), false, "Subdomain of com", true);
- // For now, the ones not cached are ignored.
- EXPECT_TRUE(negative_result_ == list_->find(Name("example.cz.")));
- }
- // Check the caching handles misbehaviour from the data source and
- // misconfiguration gracefully
- TEST_F(ListTest, badCache) {
- list_->configure(config_elem_, true);
- checkDS(0, "test_type", "{}", false);
- // First, the zone is not in the data source
- const ConstElementPtr elem1(Element::fromJSON("["
- "{"
- " \"type\": \"type1\","
- " \"cache-enable\": true,"
- " \"cache-zones\": [\"example.org\"],"
- " \"params\": []"
- "}]"));
- EXPECT_THROW(list_->configure(elem1, true),
- ConfigurableClientList::ConfigurationError);
- checkDS(0, "test_type", "{}", false);
- // Now, the zone doesn't give an iterator
- const ConstElementPtr elem2(Element::fromJSON("["
- "{"
- " \"type\": \"type1\","
- " \"cache-enable\": true,"
- " \"cache-zones\": [\"noiter.org\"],"
- " \"params\": [\"noiter.org\"]"
- "}]"));
- EXPECT_THROW(list_->configure(elem2, true), isc::NotImplemented);
- checkDS(0, "test_type", "{}", false);
- // Now, the zone returns NULL iterator
- const ConstElementPtr elem3(Element::fromJSON("["
- "{"
- " \"type\": \"type1\","
- " \"cache-enable\": true,"
- " \"cache-zones\": [\"null.org\"],"
- " \"params\": [\"null.org\"]"
- "}]"));
- EXPECT_THROW(list_->configure(elem3, true), isc::Unexpected);
- checkDS(0, "test_type", "{}", false);
- // The autodetection of zones is not enabled
- const ConstElementPtr elem4(Element::fromJSON("["
- "{"
- " \"type\": \"type1\","
- " \"cache-enable\": true,"
- " \"params\": [\"example.org\"]"
- "}]"));
- EXPECT_THROW(list_->configure(elem4, true), isc::NotImplemented);
- checkDS(0, "test_type", "{}", false);
- }
- TEST_F(ListTest, masterFiles) {
- const ConstElementPtr elem(Element::fromJSON("["
- "{"
- " \"type\": \"MasterFiles\","
- " \"cache-enable\": true,"
- " \"params\": {"
- " \".\": \"" TEST_DATA_DIR "/root.zone\""
- " }"
- "}]"));
- list_->configure(elem, true);
- // It has only the cache
- EXPECT_EQ(static_cast<const DataSourceClient*>(NULL),
- list_->getDataSources()[0].data_src_client_);
- // And it can search
- positiveResult(list_->find(Name(".")), ds_[0], Name("."), true, "com",
- true);
- // If cache is not enabled, nothing is loaded
- list_->configure(elem, false);
- EXPECT_EQ(0, list_->getDataSources().size());
- }
- TEST_F(ListTest, BadMasterFile) {
- // Configure two zone correctly, and one with the wrong origin
- // (resulting in an out-of-zone data error)
- // Configuration should succeed, and the correct zones should
- // be loaded. Neither the 'bad' origin or the zone it used
- // should be loaded
- const ConstElementPtr elem(Element::fromJSON("["
- "{"
- " \"type\": \"MasterFiles\","
- " \"cache-enable\": true,"
- " \"params\": {"
- " \"example.com.\": \"" TEST_DATA_DIR "/example.com.flattened\","
- " \"foo.bar.\": \"" TEST_DATA_DIR "/example.org.nsec3-signed\","
- " \".\": \"" TEST_DATA_DIR "/root.zone\""
- " }"
- "}]"));
- list_->configure(elem, true);
- positiveResult(list_->find(Name("example.com."), true), ds_[0],
- Name("example.com."), true, "example.com", true);
- EXPECT_TRUE(negative_result_ == list_->find(Name("example.org."), true));
- EXPECT_TRUE(negative_result_ == list_->find(Name("foo.bar"), true));
- positiveResult(list_->find(Name(".")), ds_[0], Name("."), true, "root",
- true);
- }
- // Test we can reload a zone
- TEST_F(ListTest, reloadSuccess) {
- list_->configure(config_elem_zones_, true);
- const Name name("example.org");
- prepareCache(0, name);
- // The cache currently contains a tweaked version of zone, which doesn't
- // have apex NS. So the lookup should result in NXRRSET.
- EXPECT_EQ(ZoneFinder::NXRRSET,
- list_->find(name).finder_->find(name, RRType::NS())->code);
- // Now reload the full zone. It should be there now.
- EXPECT_EQ(ConfigurableClientList::ZONE_RELOADED, list_->reload(name));
- EXPECT_EQ(ZoneFinder::SUCCESS,
- list_->find(name).finder_->find(name, RRType::NS())->code);
- }
- // The cache is not enabled. The load should be rejected.
- TEST_F(ListTest, reloadNotEnabled) {
- list_->configure(config_elem_zones_, false);
- const Name name("example.org");
- // We put the cache in even when not enabled. This won't confuse the thing.
- prepareCache(0, name);
- // See the reloadSuccess test. This should result in NXRRSET.
- EXPECT_EQ(ZoneFinder::NXRRSET,
- list_->find(name).finder_->find(name, RRType::NS())->code);
- // Now reload. It should reject it.
- EXPECT_EQ(ConfigurableClientList::CACHE_DISABLED, list_->reload(name));
- // Nothing changed here
- EXPECT_EQ(ZoneFinder::NXRRSET,
- list_->find(name).finder_->find(name, RRType::NS())->code);
- }
- // Test several cases when the zone does not exist
- TEST_F(ListTest, reloadNoSuchZone) {
- list_->configure(config_elem_zones_, true);
- const Name name("example.org");
- // We put the cache in even when not enabled. This won't confuse the
- // reload method, as that one looks at the real state of things, not
- // at the configuration.
- prepareCache(0, Name("example.com"));
- // Not in the data sources
- EXPECT_EQ(ConfigurableClientList::ZONE_NOT_FOUND,
- list_->reload(Name("example.cz")));
- // Not cached
- EXPECT_EQ(ConfigurableClientList::ZONE_NOT_FOUND, list_->reload(name));
- // Partial match
- EXPECT_EQ(ConfigurableClientList::ZONE_NOT_FOUND,
- list_->reload(Name("sub.example.com")));
- // Nothing changed here - these zones don't exist
- EXPECT_EQ(static_cast<isc::datasrc::DataSourceClient*>(NULL),
- list_->find(name).dsrc_client_);
- EXPECT_EQ(static_cast<isc::datasrc::DataSourceClient*>(NULL),
- list_->find(Name("example.cz")).dsrc_client_);
- EXPECT_EQ(static_cast<isc::datasrc::DataSourceClient*>(NULL),
- list_->find(Name("sub.example.com"), true).dsrc_client_);
- // Not reloaded, so NS shouldn't be visible yet.
- EXPECT_EQ(ZoneFinder::NXRRSET,
- list_->find(Name("example.com")).finder_->
- find(Name("example.com"), RRType::NS())->code);
- }
- // Check we gracefuly throw an exception when a zone disappeared in
- // the underlying data source when we want to reload it
- TEST_F(ListTest, reloadZoneGone) {
- list_->configure(config_elem_, true);
- const Name name("example.org");
- // We put in a cache for non-existant zone. This emulates being loaded
- // and then the zone disappearing. We prefill the cache, so we can check
- // it.
- prepareCache(0, name);
- // The (cached) zone contains zone's SOA
- EXPECT_EQ(ZoneFinder::SUCCESS,
- list_->find(name).finder_->find(name, RRType::SOA())->code);
- // The zone is not there, so abort the reload.
- EXPECT_THROW(list_->reload(name), DataSourceError);
- // The (cached) zone is not hurt.
- EXPECT_EQ(ZoneFinder::SUCCESS,
- list_->find(name).finder_->find(name, RRType::SOA())->code);
- }
- // The underlying data source throws. Check we don't modify the state.
- TEST_F(ListTest, reloadZoneThrow) {
- list_->configure(config_elem_zones_, true);
- const Name name("noiter.org");
- prepareCache(0, name);
- // The zone contains stuff now
- EXPECT_EQ(ZoneFinder::SUCCESS,
- list_->find(name).finder_->find(name, RRType::SOA())->code);
- // The iterator throws, so abort the reload.
- EXPECT_THROW(list_->reload(name), isc::NotImplemented);
- // The zone is not hurt.
- EXPECT_EQ(ZoneFinder::SUCCESS,
- list_->find(name).finder_->find(name, RRType::SOA())->code);
- }
- TEST_F(ListTest, reloadNullIterator) {
- list_->configure(config_elem_zones_, true);
- const Name name("null.org");
- prepareCache(0, name);
- // The zone contains stuff now
- EXPECT_EQ(ZoneFinder::SUCCESS,
- list_->find(name).finder_->find(name, RRType::SOA())->code);
- // The iterator throws, so abort the reload.
- EXPECT_THROW(list_->reload(name), isc::Unexpected);
- // The zone is not hurt.
- EXPECT_EQ(ZoneFinder::SUCCESS,
- list_->find(name).finder_->find(name, RRType::SOA())->code);
- }
- // Test we can reload the master files too (special-cased)
- TEST_F(ListTest, reloadMasterFile) {
- const char* const install_cmd = INSTALL_PROG " -c " TEST_DATA_DIR
- "/root.zone " TEST_DATA_BUILDDIR "/root.zone.copied";
- if (system(install_cmd) != 0) {
- // any exception will do, this is failure in test setup, but
- // nice to show the command that fails, and shouldn't be caught
- isc_throw(isc::Exception,
- "Error setting up; command failed: " << install_cmd);
- }
- const ConstElementPtr elem(Element::fromJSON("["
- "{"
- " \"type\": \"MasterFiles\","
- " \"cache-enable\": true,"
- " \"params\": {"
- " \".\": \"" TEST_DATA_BUILDDIR "/root.zone.copied\""
- " }"
- "}]"));
- list_->configure(elem, true);
- // Add a record that is not in the zone
- EXPECT_EQ(ZoneFinder::NXDOMAIN,
- list_->find(Name(".")).finder_->find(Name("nosuchdomain"),
- RRType::TXT())->code);
- ofstream f;
- f.open(TEST_DATA_BUILDDIR "/root.zone.copied", ios::out | ios::app);
- f << "nosuchdomain.\t\t3600\tIN\tTXT\ttest" << std::endl;
- f.close();
- // Do the reload.
- EXPECT_EQ(ConfigurableClientList::ZONE_RELOADED, list_->reload(Name(".")));
- // It is here now.
- EXPECT_EQ(ZoneFinder::SUCCESS,
- list_->find(Name(".")).finder_->find(Name("nosuchdomain"),
- RRType::TXT())->code);
- }
- }
|