From: Michal 'vorner' Vaner Date: Thu, 5 Jul 2012 14:48:23 +0000 (+0200) Subject: [1976] Reload method for the client list X-Git-Tag: trac2351_base~97^2~7^2~2^2~21^2~1^2~3 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=899ef652280b56e0974fd78c5f0b160c538ae952;p=thirdparty%2Fkea.git [1976] Reload method for the client list --- diff --git a/src/lib/datasrc/client_list.cc b/src/lib/datasrc/client_list.cc index e3e2948ce9..d1ff8de594 100644 --- a/src/lib/datasrc/client_list.cc +++ b/src/lib/datasrc/client_list.cc @@ -157,6 +157,7 @@ ConfigurableClientList::configure(const ConstElementPtr& config, // the scope. data_sources_.swap(new_data_sources); configuration_ = config; + allow_cache_ = allow_cache; } catch (const TypeError& te) { isc_throw(ConfigurationError, "Malformed configuration at data source " "no. " << i << ": " << te.what()); @@ -258,6 +259,43 @@ ConfigurableClientList::findInternal(MutableResult& candidate, // match, this surely contains the original empty result. } +ConfigurableClientList::ReloadResult +ConfigurableClientList::reload(const Name& name) { + if (!allow_cache_) { + return (CACHE_DISABLED); + } + // Try to find the correct zone. + MutableResult result; + findInternal(result, name, true, true); + if (!result.finder) { + return (ZONE_NOT_FOUND); + } + // Try to convert the finder to in-memory one. If it is the cache, + // it should work. + shared_ptr + finder(dynamic_pointer_cast(result.finder)); + const DataSourceInfo* info(result.info); + // It is of a different type or there's no cache. + if (!info->cache_ || !finder) { + return (ZONE_NOT_CACHED); + } + DataSourceClient* client(info->data_src_client_); + if (!client) { + isc_throw(isc::NotImplemented, + "Reloading of master files not implemented yet. " + "Next commit or so."); + } + // Now do the final reload. If it does not exist in client, + // DataSourceError is thrown, which is exactly the result what we + // want, so no need to handle it. + ZoneIteratorPtr iterator(client->getIterator(name)); + if (!iterator) { + isc_throw(isc::Unexpected, "Null iterator from " << name); + } + finder->load(*iterator); + return (ZONE_RELOADED); +} + // NOTE: This function is not tested, it would be complicated. However, the // purpose of the function is to provide a very thin wrapper to be able to // replace the call to DataSourceClientContainer constructor in tests. diff --git a/src/lib/datasrc/client_list.h b/src/lib/datasrc/client_list.h index e4aff6ee93..4006f12c56 100644 --- a/src/lib/datasrc/client_list.h +++ b/src/lib/datasrc/client_list.h @@ -241,8 +241,8 @@ public: /// \brief Result of the reload() method. enum ReloadResult { CACHE_DISABLED, ///< The cache is not enabled in this list. - ZONE_NOT_CACHED, ///< Zone exists, but is not cached. - ZONE_NOT_FOUND, ///< No data source in the list provides the zone. + ZONE_NOT_CACHED, ///< Zone is served directly, not from cache. + ZONE_NOT_FOUND, ///< Zone does not exist or not cached. ZONE_RELOADED ///< The zone was successfully reloaded. }; @@ -335,6 +335,8 @@ private: const isc::dns::RRClass rrclass_; /// \brief Currently active configuration. isc::data::ConstElementPtr configuration_; + /// \brief The last set value of allow_cache. + bool allow_cache_; }; } // namespace datasrc diff --git a/src/lib/datasrc/tests/client_list_unittest.cc b/src/lib/datasrc/tests/client_list_unittest.cc index 84a1fe8e94..9999297d33 100644 --- a/src/lib/datasrc/tests/client_list_unittest.cc +++ b/src/lib/datasrc/tests/client_list_unittest.cc @@ -225,6 +225,12 @@ public: "{" " \"type\": \"test_type\"," " \"params\": {}" + "}]")), + config_elem_zones_(Element::fromJSON("[" + "{" + " \"type\": \"test_type\"," + " \"params\": [\"example.org\", \"example.com\", " + " \"noiter.org\", \"null.org\"]" "}]")) { for (size_t i(0); i < ds_count; ++ i) { @@ -235,6 +241,24 @@ public: DataSourceClientContainerPtr(), false)); } } + void prepareCache(size_t index, const Name& zone, bool prefill = false) { + const shared_ptr cache(new InMemoryClient()); + const shared_ptr + finder(new InMemoryZoneFinder(RRClass::IN(), zone)); + if (prefill) { + RRsetPtr soa(new RRset(zone, 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)); + finder->add(soa); + } + // We leave the zone empty, so we can check it was reloaded. + cache->addZone(finder); + list_->getDataSources()[index].cache_ = cache; + } // Check the positive result is as we expect it. void positiveResult(const ClientList::FindResult& result, const shared_ptr& dsrc, @@ -303,7 +327,7 @@ public: const ClientList::FindResult negativeResult_; vector > ds_; vector ds_info_; - const ConstElementPtr config_elem_; + const ConstElementPtr config_elem_, config_elem_zones_; }; // Test the test itself @@ -750,4 +774,108 @@ TEST_F(ListTest, masterFiles) { EXPECT_EQ(0, list_->getDataSources().size()); } +// Test we can reload a zone +TEST_F(ListTest, reloadSuccess) { + list_->configure(config_elem_zones_, true); + Name name("example.org"); + prepareCache(0, name); + // Not there yet. It would be NXDOMAIN, but it is in apex and + // it returns NXRRSET instead. + EXPECT_EQ(ZoneFinder::NXRRSET, + list_->find(name).finder_->find(name, RRType::SOA())->code); + // Now reload. It should be there now. + EXPECT_EQ(ConfigurableClientList::ZONE_RELOADED, list_->reload(name)); + EXPECT_EQ(ZoneFinder::SUCCESS, + list_->find(name).finder_->find(name, RRType::SOA())->code); +} + +// The cache is not enabled. The load should be rejected. +TEST_F(ListTest, reloadNotEnabled) { + list_->configure(config_elem_zones_, false); + Name name("example.org"); + // We put the cache in even when not enabled. This won't confuse the thing. + prepareCache(0, name); + // Not there yet. It would be NXDOMAIN, but it is in apex and + // it returns NXRRSET instead. + EXPECT_EQ(ZoneFinder::NXRRSET, + list_->find(name).finder_->find(name, RRType::SOA())->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::SOA())->code); +} + +// Test several cases when the zone does not exist +TEST_F(ListTest, reloadNoSuchZone) { + list_->configure(config_elem_zones_, true); + Name name("example.org"); + // We put the cache in even when not enabled. This won't confuse the thing. + 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(NULL, list_->find(name).dsrc_client_); + EXPECT_EQ(NULL, list_->find(Name("example.cz")).dsrc_client_); + EXPECT_EQ(NULL, list_->find(Name("sub.example.com"), true).dsrc_client_); + // Not reloaded + EXPECT_EQ(ZoneFinder::NXRRSET, + list_->find(Name("example.com")).finder_-> + find(Name("example.com"), RRType::SOA())->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); + 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, true); + // The zone contains something + 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 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); + Name name("noiter.org"); + prepareCache(0, name, true); + // 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); + Name name("null.org"); + prepareCache(0, name, true); + // 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); +} + }