BIND 10 master, updated. 42d8e0ec9a379888f3013a3458ea6f97d38d5332 [master] Merge branch 'trac1207'
BIND 10 source code commits
bind10-changes at lists.isc.org
Mon May 21 09:37:48 UTC 2012
The branch, master has been updated
via 42d8e0ec9a379888f3013a3458ea6f97d38d5332 (commit)
via bb745745967720d590e39b970861a40d2f7efa56 (commit)
via 4c63a321442273e8acd3414585b1b8d775ceb83c (commit)
via 34ed44e230ee4939303a988a50a50874a0e5b4c9 (commit)
via 61b38bf68fb9db29513d2d1d26b914338e690741 (commit)
via ea91956a36df0eb95d1ffeaeea57ecc72839e00b (commit)
via 2d63768bd9243803dea66aca4c6a84213692723b (commit)
via daa24fa2dd5c2013d5ae2ebf4b84d7c56d6c2d26 (commit)
via 5da5594d9b80f3b92d0700fdd45c145de56a46d2 (commit)
via 63bb58065a7136106c24e09f14a1f61177aa6d7f (commit)
via 014746f188ea8de6931b9ec7496303db994c092d (commit)
via f8848254849ec37587250cf9f6660aa20264495a (commit)
via d19d162c3e89784b4e013e6892e7274c4bc0f861 (commit)
via abcaba769071a7e93cd40f55fc2b44e216991163 (commit)
via 393bf630eca3e1735e5d2c2dbf79bd79df6d9790 (commit)
via 1d668020a0e6e3f44a5037b55d8e2391ffd1b049 (commit)
via afcfd91930470d545841306b9d7dfb60863f6385 (commit)
via ba087ed8be288925fd4578efe89eefe1ea07ff31 (commit)
via 34561bdb8ee2ec934df4ff957839cd4e7d53e7be (commit)
via c17d922ffe421fbbcbffab695cfeb01cdc316bca (commit)
via 2a57f8bbd3c0b7fff3b5ee1f95c83c3435daa2c7 (commit)
via 17eb0c775e8242e4b7eb7b531bb97b9519e4ef1f (commit)
via 188b12ea5823239e860f7726c258c07c4ac07ae2 (commit)
via 0b5f0023e7cde8924891f75686e93c0c5fead7ad (commit)
via bf1717f38c4292fcd19d31ba69f810079f80664d (commit)
via 18cb352fef0dc9343fbffad36539bb7f1cce5214 (commit)
via 446e309a0456d8fbf3c53a66d7cc0f556bda9b6b (commit)
via 9a1f442d5067ecc75eecf2678cc90d74d4a5e597 (commit)
from a5c587cc3fd3208a875cfaad163cb2cded3309dc (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
- Log -----------------------------------------------------------------
commit 42d8e0ec9a379888f3013a3458ea6f97d38d5332
Merge: a5c587cc3fd3208a875cfaad163cb2cded3309dc bb745745967720d590e39b970861a40d2f7efa56
Author: Jelte Jansen <jelte at isc.org>
Date: Mon May 21 10:57:41 2012 +0200
[master] Merge branch 'trac1207'
-----------------------------------------------------------------------
Summary of changes:
src/bin/auth/Makefile.am | 6 +-
src/bin/auth/auth_config.cc | 195 ++++++++---------------------
src/bin/auth/auth_srv.cc | 38 ++++--
src/bin/auth/auth_srv.h | 65 ++++++----
src/bin/auth/command.cc | 11 +-
src/bin/auth/tests/Makefile.am | 6 +-
src/bin/auth/tests/auth_srv_unittest.cc | 179 +++++++++++++++++++++------
src/bin/auth/tests/command_unittest.cc | 97 +++++++++++----
src/bin/auth/tests/config_unittest.cc | 119 ++++++++++++++----
src/lib/datasrc/client.h | 16 +++
src/lib/datasrc/factory.cc | 2 +
src/lib/datasrc/factory.h | 23 +++-
src/lib/datasrc/memory_datasrc.h | 2 +-
src/lib/datasrc/memory_datasrc_link.cc | 136 ++++++++++++++++++---
src/lib/datasrc/tests/client_unittest.cc | 4 +
src/lib/datasrc/tests/factory_unittest.cc | 7 +-
16 files changed, 603 insertions(+), 303 deletions(-)
-----------------------------------------------------------------------
diff --git a/src/bin/auth/Makefile.am b/src/bin/auth/Makefile.am
index 66abbe2..257d8eb 100644
--- a/src/bin/auth/Makefile.am
+++ b/src/bin/auth/Makefile.am
@@ -51,9 +51,9 @@ b10_auth_SOURCES += statistics.cc statistics.h
b10_auth_SOURCES += main.cc
# This is a temporary workaround for #1206, where the InMemoryClient has been
# moved to an ldopened library. We could add that library to LDADD, but that
-# is nonportable. When #1207 is done this becomes moot anyway, and the
-# specific workaround is not needed anymore, so we can then remove this
-# line again.
+# is nonportable. This should've been moot after #1207, but there is still
+# one dependency; the in-memory-specific zone loader call is still in
+# auth.
b10_auth_SOURCES += ${top_srcdir}/src/lib/datasrc/memory_datasrc.cc
nodist_b10_auth_SOURCES = auth_messages.h auth_messages.cc
diff --git a/src/bin/auth/auth_config.cc b/src/bin/auth/auth_config.cc
index 3a04dc8..c85a4ee 100644
--- a/src/bin/auth/auth_config.cc
+++ b/src/bin/auth/auth_config.cc
@@ -43,22 +43,19 @@ using namespace isc::datasrc;
using namespace isc::server_common::portconfig;
namespace {
-// Forward declaration
-AuthConfigParser*
-createAuthConfigParser(AuthSrv& server, const std::string& config_id,
- bool internal);
-
/// A derived \c AuthConfigParser class for the "datasources" configuration
/// identifier.
class DatasourcesConfig : public AuthConfigParser {
public:
- DatasourcesConfig(AuthSrv& server) : server_(server) {}
+ DatasourcesConfig(AuthSrv& server) : server_(server)
+ {}
virtual void build(ConstElementPtr config_value);
virtual void commit();
private:
AuthSrv& server_;
vector<boost::shared_ptr<AuthConfigParser> > datasources_;
set<string> configured_sources_;
+ vector<pair<RRClass, DataSourceClientContainerPtr> > clients_;
};
/// A derived \c AuthConfigParser for the version value
@@ -86,137 +83,60 @@ DatasourcesConfig::build(ConstElementPtr config_value) {
isc_throw(AuthConfigError, "Data source type '" <<
datasrc_type->stringValue() << "' already configured");
}
-
- boost::shared_ptr<AuthConfigParser> datasrc_config =
- boost::shared_ptr<AuthConfigParser>(
- createAuthConfigParser(server_, string("datasources/") +
- datasrc_type->stringValue(),
- true));
- datasrc_config->build(datasrc_elem);
- datasources_.push_back(datasrc_config);
- configured_sources_.insert(datasrc_type->stringValue());
- }
-}
+ // Apart from that it's not really easy to get at the default
+ // class value for the class here, it should probably really
+ // be a property of the instantiated data source. For now
+ // use hardcoded default IN.
+ const RRClass rrclass =
+ datasrc_elem->contains("class") ?
+ RRClass(datasrc_elem->get("class")->stringValue()) : RRClass::IN();
+
+ // Right now, we only support the in-memory data source for the
+ // RR class of IN. We reject other cases explicitly by hardcoded
+ // checks. This will soon be generalized, at which point these
+ // checks will also have to be cleaned up.
+ if (rrclass != RRClass::IN()) {
+ isc_throw(isc::InvalidParameter, "Unsupported data source class: "
+ << rrclass);
+ }
+ if (datasrc_type->stringValue() != "memory") {
+ isc_throw(AuthConfigError, "Unsupported data source type: "
+ << datasrc_type->stringValue());
+ }
-void
-DatasourcesConfig::commit() {
- // XXX a short term workaround: clear all data sources and then reset
- // to new ones so that we can remove data sources that don't exist in
- // the new configuration and have been used in the server.
- // This could be inefficient and requires knowledge about
- // server implementation details, and isn't scalable wrt the number of
- // data source types, and should eventually be improved.
- // Currently memory data source for class IN is the only possibility.
- server_.setInMemoryClient(RRClass::IN(), AuthSrv::InMemoryClientPtr());
+ // Create a new client for the specified data source and store it
+ // in the local vector. For now, we always build a new client
+ // from the scratch, and replace any existing ones with the new ones.
+ // We might eventually want to optimize building zones (in case of
+ // reloading) by selectively loading fresh zones for data source
+ // where zone loading is expensive (such as in-memory).
+ clients_.push_back(
+ pair<RRClass, DataSourceClientContainerPtr>(
+ rrclass,
+ DataSourceClientContainerPtr(new DataSourceClientContainer(
+ datasrc_type->stringValue(),
+ datasrc_elem))));
- BOOST_FOREACH(boost::shared_ptr<AuthConfigParser> datasrc_config,
- datasources_) {
- datasrc_config->commit();
+ configured_sources_.insert(datasrc_type->stringValue());
}
}
-/// A derived \c AuthConfigParser class for the memory type datasource
-/// configuration. It does not correspond to the configuration syntax;
-/// it's instantiated for internal use.
-class MemoryDatasourceConfig : public AuthConfigParser {
-public:
- MemoryDatasourceConfig(AuthSrv& server) :
- server_(server),
- rrclass_(0) // XXX: dummy initial value
- {}
- virtual void build(ConstElementPtr config_value);
- virtual void commit() {
- server_.setInMemoryClient(rrclass_, memory_client_);
- }
-private:
- AuthSrv& server_;
- RRClass rrclass_;
- AuthSrv::InMemoryClientPtr memory_client_;
-};
-
void
-MemoryDatasourceConfig::build(ConstElementPtr config_value) {
- // XXX: apparently we cannot retrieve the default RR class from the
- // module spec. As a temporary workaround we hardcode the default value.
- ConstElementPtr rrclass_elem = config_value->get("class");
- rrclass_ = RRClass(rrclass_elem ? rrclass_elem->stringValue() : "IN");
-
- // We'd eventually optimize building zones (in case of reloading) by
- // selectively loading fresh zones. Right now we simply check the
- // RR class is supported by the server implementation.
- server_.getInMemoryClient(rrclass_);
- memory_client_ = AuthSrv::InMemoryClientPtr(new InMemoryClient());
-
- ConstElementPtr zones_config = config_value->get("zones");
- if (!zones_config) {
- // XXX: Like the RR class, we cannot retrieve the default value here,
- // so we assume an empty zone list in this case.
- return;
- }
-
- BOOST_FOREACH(ConstElementPtr zone_config, zones_config->listValue()) {
- ConstElementPtr origin = zone_config->get("origin");
- const string origin_txt = origin ? origin->stringValue() : "";
- if (origin_txt.empty()) {
- isc_throw(AuthConfigError, "Missing zone origin");
- }
- ConstElementPtr file = zone_config->get("file");
- const string file_txt = file ? file->stringValue() : "";
- if (file_txt.empty()) {
- isc_throw(AuthConfigError, "Missing zone file for zone: "
- << origin_txt);
- }
-
- // We support the traditional text type and SQLite3 backend. For the
- // latter we create a client for the underlying SQLite3 data source,
- // and build the in-memory zone using an iterator of the underlying
- // zone.
- ConstElementPtr filetype = zone_config->get("filetype");
- const string filetype_txt = filetype ? filetype->stringValue() :
- "text";
- boost::scoped_ptr<DataSourceClientContainer> container;
- if (filetype_txt == "sqlite3") {
- container.reset(new DataSourceClientContainer(
- "sqlite3",
- Element::fromJSON("{\"database_file\": \"" +
- file_txt + "\"}")));
- } else if (filetype_txt != "text") {
- isc_throw(AuthConfigError, "Invalid filetype for zone "
- << origin_txt << ": " << filetype_txt);
- }
-
- // Note: we don't want to have such small try-catch blocks for each
- // specific error. We may eventually want to introduce some unified
- // error handling framework as we have more configuration parameters.
- // See bug #1627 for the relevant discussion.
- InMemoryZoneFinder* imzf = NULL;
- try {
- imzf = new InMemoryZoneFinder(rrclass_, Name(origin_txt));
- } catch (const isc::dns::NameParserException& ex) {
- isc_throw(AuthConfigError, "unable to parse zone's origin: " <<
- ex.what());
- }
-
- boost::shared_ptr<InMemoryZoneFinder> zone_finder(imzf);
- const result::Result result = memory_client_->addZone(zone_finder);
- if (result == result::EXIST) {
- isc_throw(AuthConfigError, "zone "<< origin->str()
- << " already exists");
- }
-
- /*
- * TODO: Once we have better reloading of configuration (something
- * else than throwing everything away and loading it again), we will
- * need the load method to be split into some kind of build and
- * commit/abort parts.
- */
- if (filetype_txt == "text") {
- zone_finder->load(file_txt);
- } else {
- zone_finder->load(*container->getInstance().getIterator(
- Name(origin_txt)));
- }
+DatasourcesConfig::commit() {
+ // As noted in build(), the current implementation only supports the
+ // in-memory data source for class IN, and build() should have ensured
+ // it. So, depending on the vector is empty or not, we either clear
+ // or install an in-memory data source for the server.
+ //
+ // When we generalize it, we'll somehow install all data source clients
+ // built in the vector, clearing deleted ones from the server.
+ if (clients_.empty()) {
+ server_.setInMemoryClient(RRClass::IN(),
+ DataSourceClientContainerPtr());
+ } else {
+ server_.setInMemoryClient(clients_.front().first,
+ clients_.front().second);
}
}
@@ -314,13 +234,10 @@ private:
*/
AddrListPtr rollbackAddresses_;
};
+} // end of unnamed namespace
-// This is a generalized version of create function that can create
-// an AuthConfigParser object for "internal" use.
AuthConfigParser*
-createAuthConfigParser(AuthSrv& server, const std::string& config_id,
- bool internal)
-{
+createAuthConfigParser(AuthSrv& server, const std::string& config_id) {
// For the initial implementation we use a naive if-else blocks for
// simplicity. In future we'll probably generalize it using map-like
// data structure, and may even provide external register interface so
@@ -329,8 +246,6 @@ createAuthConfigParser(AuthSrv& server, const std::string& config_id,
return (new DatasourcesConfig(server));
} else if (config_id == "statistics-interval") {
return (new StatisticsIntervalConfig(server));
- } else if (internal && config_id == "datasources/memory") {
- return (new MemoryDatasourceConfig(server));
} else if (config_id == "listen_on") {
return (new ListenAddressConfig(server));
} else if (config_id == "_commit_throw") {
@@ -351,12 +266,6 @@ createAuthConfigParser(AuthSrv& server, const std::string& config_id,
config_id);
}
}
-} // end of unnamed namespace
-
-AuthConfigParser*
-createAuthConfigParser(AuthSrv& server, const std::string& config_id) {
- return (createAuthConfigParser(server, config_id, false));
-}
void
configureAuthServer(AuthSrv& server, ConstElementPtr config_set) {
diff --git a/src/bin/auth/auth_srv.cc b/src/bin/auth/auth_srv.cc
index 9f5642e..50eb931 100644
--- a/src/bin/auth/auth_srv.cc
+++ b/src/bin/auth/auth_srv.cc
@@ -141,7 +141,7 @@ public:
/// In-memory data source. Currently class IN only for simplicity.
const RRClass memory_client_class_;
- AuthSrv::InMemoryClientPtr memory_client_;
+ isc::datasrc::DataSourceClientContainerPtr memory_client_container_;
/// Hot spot cache
isc::datasrc::HotCache cache_;
@@ -389,34 +389,46 @@ AuthSrv::getConfigSession() const {
return (impl_->config_session_);
}
-AuthSrv::InMemoryClientPtr
-AuthSrv::getInMemoryClient(const RRClass& rrclass) {
- // XXX: for simplicity, we only support the IN class right now.
+isc::datasrc::DataSourceClientContainerPtr
+AuthSrv::getInMemoryClientContainer(const RRClass& rrclass) {
if (rrclass != impl_->memory_client_class_) {
isc_throw(InvalidParameter,
"Memory data source is not supported for RR class "
<< rrclass);
}
- return (impl_->memory_client_);
+ return (impl_->memory_client_container_);
+}
+
+isc::datasrc::DataSourceClient*
+AuthSrv::getInMemoryClient(const RRClass& rrclass) {
+ if (hasInMemoryClient()) {
+ return (&getInMemoryClientContainer(rrclass)->getInstance());
+ } else {
+ return (NULL);
+ }
+}
+
+bool
+AuthSrv::hasInMemoryClient() const {
+ return (impl_->memory_client_container_);
}
void
AuthSrv::setInMemoryClient(const isc::dns::RRClass& rrclass,
- InMemoryClientPtr memory_client)
+ DataSourceClientContainerPtr memory_client)
{
- // XXX: see above
if (rrclass != impl_->memory_client_class_) {
isc_throw(InvalidParameter,
"Memory data source is not supported for RR class "
<< rrclass);
- } else if (!impl_->memory_client_ && memory_client) {
+ } else if (!impl_->memory_client_container_ && memory_client) {
LOG_DEBUG(auth_logger, DBG_AUTH_OPS, AUTH_MEM_DATASRC_ENABLED)
.arg(rrclass);
- } else if (impl_->memory_client_ && !memory_client) {
+ } else if (impl_->memory_client_container_ && !memory_client) {
LOG_DEBUG(auth_logger, DBG_AUTH_OPS, AUTH_MEM_DATASRC_DISABLED)
.arg(rrclass);
}
- impl_->memory_client_ = memory_client;
+ impl_->memory_client_container_ = memory_client;
}
uint32_t
@@ -585,10 +597,12 @@ AuthSrvImpl::processNormalQuery(const IOMessage& io_message, Message& message,
// If a memory data source is configured call the separate
// Query::process()
const ConstQuestionPtr question = *message.beginQuestion();
- if (memory_client_ && memory_client_class_ == question->getClass()) {
+ if (memory_client_container_ &&
+ memory_client_class_ == question->getClass()) {
const RRType& qtype = question->getType();
const Name& qname = question->getName();
- query_.process(*memory_client_, qname, qtype, message, dnssec_ok);
+ query_.process(memory_client_container_->getInstance(),
+ qname, qtype, message, dnssec_ok);
} else {
datasrc::Query query(message, cache_, dnssec_ok);
data_sources_.doQuery(query);
diff --git a/src/bin/auth/auth_srv.h b/src/bin/auth/auth_srv.h
index 3be711b..d74a42f 100644
--- a/src/bin/auth/auth_srv.h
+++ b/src/bin/auth/auth_srv.h
@@ -17,12 +17,9 @@
#include <string>
-// For InMemoryClientPtr below. This should be a temporary definition until
-// we reorganize the data source framework.
-#include <boost/shared_ptr.hpp>
-
#include <cc/data.h>
#include <config/ccsession.h>
+#include <datasrc/factory.h>
#include <dns/message.h>
#include <dns/opcode.h>
#include <util/buffer.h>
@@ -40,9 +37,6 @@
#include <auth/statistics.h>
namespace isc {
-namespace datasrc {
-class InMemoryClient;
-}
namespace xfr {
class AbstractXfroutClient;
}
@@ -235,19 +229,14 @@ public:
///
void setXfrinSession(isc::cc::AbstractSession* xfrin_session);
- /// A shared pointer type for \c InMemoryClient.
- ///
- /// This is defined inside the \c AuthSrv class as it's supposed to be
- /// a short term interface until we integrate the in-memory and other
- /// data source frameworks.
- typedef boost::shared_ptr<isc::datasrc::InMemoryClient> InMemoryClientPtr;
-
- /// An immutable shared pointer type for \c InMemoryClient.
- typedef boost::shared_ptr<const isc::datasrc::InMemoryClient>
- ConstInMemoryClientPtr;
-
/// Returns the in-memory data source configured for the \c AuthSrv,
- /// if any.
+ /// if any, as a pointer.
+ ///
+ /// This is mostly a convenience function around
+ /// \c getInMemoryClientContainer, which saves the caller the step
+ /// of having to call getInstance().
+ /// The pointer is of course only valid as long as the container
+ /// exists.
///
/// The in-memory data source is configured per RR class. However,
/// the data source may not be available for all RR classes.
@@ -262,24 +251,48 @@ public:
/// \param rrclass The RR class of the requested in-memory data source.
/// \return A pointer to the in-memory data source, if configured;
/// otherwise NULL.
- InMemoryClientPtr getInMemoryClient(const isc::dns::RRClass& rrclass);
+ isc::datasrc::DataSourceClient* getInMemoryClient(
+ const isc::dns::RRClass& rrclass);
+
+ /// Returns the DataSourceClientContainer of the in-memory datasource
+ ///
+ /// \exception InvalidParameter if the given class does not match
+ /// the one in the memory data source, or if the memory
+ /// datasource has not been set (callers can check with
+ /// \c hasMemoryDataSource())
+ ///
+ /// \param rrclass The RR class of the requested in-memory data source.
+ /// \return A shared pointer to the in-memory data source, if configured;
+ /// otherwise an empty shared pointer.
+ isc::datasrc::DataSourceClientContainerPtr getInMemoryClientContainer(
+ const isc::dns::RRClass& rrclass);
+
+ /// Checks if the in-memory data source has been set.
+ ///
+ /// Right now, only one datasource at a time is effectively supported.
+ /// This is a helper method to check whether it is the in-memory one.
+ /// This is mostly useful for current testing, and is expected to be
+ /// removed (or changed in behaviour) soon, when the general
+ /// multi-data-source framework is completed.
+ ///
+ /// \return True if the in-memory datasource has been set.
+ bool hasInMemoryClient() const;
/// Sets or replaces the in-memory data source of the specified RR class.
///
- /// As noted in \c getInMemoryClient(), some RR classes may not be
- /// supported, in which case an exception of class \c InvalidParameter
- /// will be thrown.
+ /// Some RR classes may not be supported, in which case an exception
+ /// of class \c InvalidParameter will be thrown.
/// This method never throws an exception otherwise.
///
/// If there is already an in memory data source configured, it will be
/// replaced with the newly specified one.
- /// \c memory_datasrc can be NULL, in which case it will (re)disable the
- /// in-memory data source.
+ /// \c memory_client can be an empty shared pointer, in which case it
+ /// will (re)disable the in-memory data source.
///
/// \param rrclass The RR class of the in-memory data source to be set.
/// \param memory_client A (shared) pointer to \c InMemoryClient to be set.
void setInMemoryClient(const isc::dns::RRClass& rrclass,
- InMemoryClientPtr memory_client);
+ isc::datasrc::DataSourceClientContainerPtr memory_client);
/// \brief Set the communication session with Statistics.
///
diff --git a/src/bin/auth/command.cc b/src/bin/auth/command.cc
index 055c73a..750ea28 100644
--- a/src/bin/auth/command.cc
+++ b/src/bin/auth/command.cc
@@ -210,8 +210,8 @@ private:
const RRClass zone_class =
class_elem ? RRClass(class_elem->stringValue()) : RRClass::IN();
- AuthSrv::InMemoryClientPtr datasrc(server.
- getInMemoryClient(zone_class));
+ isc::datasrc::DataSourceClient* datasrc(
+ server.getInMemoryClient(zone_class));
if (datasrc == NULL) {
isc_throw(AuthCommandError, "Memory data source is disabled");
}
@@ -223,13 +223,16 @@ private:
const Name origin = Name(origin_elem->stringValue());
// Get the current zone
- const InMemoryClient::FindResult result = datasrc->findZone(origin);
+ const DataSourceClient::FindResult result = datasrc->findZone(origin);
if (result.code != result::SUCCESS) {
isc_throw(AuthCommandError, "Zone " << origin <<
" is not found in data source");
}
- old_zone_finder_ = boost::dynamic_pointer_cast<InMemoryZoneFinder>(
+ // It would appear that dynamic_cast does not work on all systems;
+ // it seems to confuse the RTTI system, resulting in NULL return
+ // values. So we use the more dangerous static_pointer_cast here.
+ old_zone_finder_ = boost::static_pointer_cast<InMemoryZoneFinder>(
result.zone_finder);
return (true);
diff --git a/src/bin/auth/tests/Makefile.am b/src/bin/auth/tests/Makefile.am
index f9fac2f..3a7c54b 100644
--- a/src/bin/auth/tests/Makefile.am
+++ b/src/bin/auth/tests/Makefile.am
@@ -44,9 +44,9 @@ run_unittests_SOURCES += statistics_unittest.cc
run_unittests_SOURCES += run_unittests.cc
# This is a temporary workaround for #1206, where the InMemoryClient has been
# moved to an ldopened library. We could add that library to LDADD, but that
-# is nonportable. When #1207 is done this becomes moot anyway, and the
-# specific workaround is not needed anymore, so we can then remove this
-# line again.
+# is nonportable. This should've been moot after #1207, but there is still
+# one dependency; the in-memory-specific zone loader call is still in
+# auth.
run_unittests_SOURCES += ${top_srcdir}/src/lib/datasrc/memory_datasrc.cc
diff --git a/src/bin/auth/tests/auth_srv_unittest.cc b/src/bin/auth/tests/auth_srv_unittest.cc
index a8815da..17a9312 100644
--- a/src/bin/auth/tests/auth_srv_unittest.cc
+++ b/src/bin/auth/tests/auth_srv_unittest.cc
@@ -17,6 +17,7 @@
#include <vector>
#include <boost/shared_ptr.hpp>
+#include <boost/scoped_ptr.hpp>
#include <gtest/gtest.h>
@@ -89,6 +90,13 @@ protected:
server.setStatisticsSession(&statistics_session);
}
+ ~AuthSrvTest() {
+ // Clear the message now; depending on the RTTI implementation,
+ // type information may be lost if the message is cleared
+ // automatically later, so as a precaution we do it now.
+ parse_message->clear(Message::PARSE);
+ }
+
virtual void processMessage() {
// If processMessage has been called before, parse_message needs
// to be reset. If it hasn't, there's no harm in doing so
@@ -830,16 +838,23 @@ TEST_F(AuthSrvTest, updateConfigFail) {
QR_FLAG | AA_FLAG, 1, 1, 1, 0);
}
-TEST_F(AuthSrvTest, updateWithInMemoryClient) {
+TEST_F(AuthSrvTest,
+#ifdef USE_STATIC_LINK
+ DISABLED_updateWithInMemoryClient
+#else
+ updateWithInMemoryClient
+#endif
+ )
+{
// Test configuring memory data source. Detailed test cases are covered
// in the configuration tests. We only check the AuthSrv interface here.
// By default memory data source isn't enabled
- EXPECT_EQ(AuthSrv::InMemoryClientPtr(), server.getInMemoryClient(rrclass));
+ EXPECT_FALSE(server.hasInMemoryClient());
updateConfig(&server,
"{\"datasources\": [{\"type\": \"memory\"}]}", true);
// after successful configuration, we should have one (with empty zoneset).
- ASSERT_NE(AuthSrv::InMemoryClientPtr(), server.getInMemoryClient(rrclass));
+ EXPECT_TRUE(server.hasInMemoryClient());
EXPECT_EQ(0, server.getInMemoryClient(rrclass)->getZoneCount());
// The memory data source is empty, should return REFUSED rcode.
@@ -851,13 +866,20 @@ TEST_F(AuthSrvTest, updateWithInMemoryClient) {
opcode.getCode(), QR_FLAG, 1, 0, 0, 0);
}
-TEST_F(AuthSrvTest, queryWithInMemoryClientNoDNSSEC) {
+TEST_F(AuthSrvTest,
+#ifdef USE_STATIC_LINK
+ DISABLED_queryWithInMemoryClientNoDNSSEC
+#else
+ queryWithInMemoryClientNoDNSSEC
+#endif
+ )
+{
// In this example, we do simple check that query is handled from the
// query handler class, and confirm it returns no error and a non empty
// answer section. Detailed examination on the response content
// for various types of queries are tested in the query tests.
updateConfig(&server, CONFIG_INMEMORY_EXAMPLE, true);
- ASSERT_NE(AuthSrv::InMemoryClientPtr(), server.getInMemoryClient(rrclass));
+ EXPECT_TRUE(server.hasInMemoryClient());
EXPECT_EQ(1, server.getInMemoryClient(rrclass)->getZoneCount());
createDataFromFile("nsec3query_nodnssec_fromWire.wire");
@@ -869,12 +891,19 @@ TEST_F(AuthSrvTest, queryWithInMemoryClientNoDNSSEC) {
opcode.getCode(), QR_FLAG | AA_FLAG, 1, 1, 2, 1);
}
-TEST_F(AuthSrvTest, queryWithInMemoryClientDNSSEC) {
+TEST_F(AuthSrvTest,
+#ifdef USE_STATIC_LINK
+ DISABLED_queryWithInMemoryClientDNSSEC
+#else
+ queryWithInMemoryClientDNSSEC
+#endif
+ )
+{
// Similar to the previous test, but the query has the DO bit on.
// The response should contain RRSIGs, and should have more RRs than
// the previous case.
updateConfig(&server, CONFIG_INMEMORY_EXAMPLE, true);
- ASSERT_NE(AuthSrv::InMemoryClientPtr(), server.getInMemoryClient(rrclass));
+ EXPECT_TRUE(server.hasInMemoryClient());
EXPECT_EQ(1, server.getInMemoryClient(rrclass)->getZoneCount());
createDataFromFile("nsec3query_fromWire.wire");
@@ -886,7 +915,14 @@ TEST_F(AuthSrvTest, queryWithInMemoryClientDNSSEC) {
opcode.getCode(), QR_FLAG | AA_FLAG, 1, 2, 3, 3);
}
-TEST_F(AuthSrvTest, chQueryWithInMemoryClient) {
+TEST_F(AuthSrvTest,
+#ifdef USE_STATIC_LINK
+ DISABLED_chQueryWithInMemoryClient
+#else
+ chQueryWithInMemoryClient
+#endif
+ )
+{
// Configure memory data source for class IN
updateConfig(&server, "{\"datasources\": "
"[{\"class\": \"IN\", \"type\": \"memory\"}]}", true);
@@ -1108,7 +1144,7 @@ TEST_F(AuthSrvTest, processNormalQuery_reuseRenderer2) {
//
namespace {
-/// A the possible methods to throw in, either in FakeInMemoryClient or
+/// The possible methods to throw in, either in FakeClient or
/// FakeZoneFinder
enum ThrowWhen {
THROW_NEVER,
@@ -1132,10 +1168,10 @@ checkThrow(ThrowWhen method, ThrowWhen throw_at, bool isc_exception) {
}
}
-/// \brief proxy class for the ZoneFinder returned by the InMemoryClient
-/// proxied by FakeInMemoryClient
+/// \brief proxy class for the ZoneFinder returned by the Client
+/// proxied by FakeClient
///
-/// See the documentation for FakeInMemoryClient for more information,
+/// See the documentation for FakeClient for more information,
/// all methods simply check whether they should throw, and if not, call
/// their proxied equivalent.
class FakeZoneFinder : public isc::datasrc::ZoneFinder {
@@ -1208,15 +1244,15 @@ private:
ConstRRsetPtr fake_rrset_;
};
-/// \brief Proxy InMemoryClient that can throw exceptions at specified times
+/// \brief Proxy FakeClient that can throw exceptions at specified times
///
-/// It is based on the memory client since that one is easy to override
-/// (with setInMemoryClient) with the current design of AuthSrv.
-class FakeInMemoryClient : public isc::datasrc::InMemoryClient {
+/// Currently it is used as an 'InMemoryClient' using setInMemoryClient,
+/// but it is in effect a general datasource client.
+class FakeClient : public isc::datasrc::DataSourceClient {
public:
/// \brief Create a proxy memory client
///
- /// \param real_client The real in-memory client to proxy
+ /// \param real_client The real (in-memory) client to proxy
/// \param throw_when if set to any value other than never, that is
/// the method that will throw an exception (either in this
/// class or the related FakeZoneFinder)
@@ -1224,10 +1260,10 @@ public:
/// throw std::exception
/// \param fake_rrset If non NULL, it will be used as an answer to
/// find() for that name and type.
- FakeInMemoryClient(AuthSrv::InMemoryClientPtr real_client,
- ThrowWhen throw_when, bool isc_exception,
- ConstRRsetPtr fake_rrset = ConstRRsetPtr()) :
- real_client_(real_client),
+ FakeClient(isc::datasrc::DataSourceClientContainerPtr real_client,
+ ThrowWhen throw_when, bool isc_exception,
+ ConstRRsetPtr fake_rrset = ConstRRsetPtr()) :
+ real_client_ptr_(real_client),
throw_when_(throw_when),
isc_exception_(isc_exception),
fake_rrset_(fake_rrset)
@@ -1242,7 +1278,8 @@ public:
virtual FindResult
findZone(const isc::dns::Name& name) const {
checkThrow(THROW_AT_FIND_ZONE, throw_when_, isc_exception_);
- const FindResult result = real_client_->findZone(name);
+ const FindResult result =
+ real_client_ptr_->getInstance().findZone(name);
return (FindResult(result.code, isc::datasrc::ZoneFinderPtr(
new FakeZoneFinder(result.zone_finder,
throw_when_,
@@ -1250,28 +1287,74 @@ public:
fake_rrset_))));
}
+ isc::datasrc::ZoneUpdaterPtr
+ getUpdater(const isc::dns::Name&, bool, bool) const {
+ isc_throw(isc::NotImplemented,
+ "Update attempt on in fake data source");
+ }
+ std::pair<isc::datasrc::ZoneJournalReader::Result,
+ isc::datasrc::ZoneJournalReaderPtr>
+ getJournalReader(const isc::dns::Name&, uint32_t, uint32_t) const {
+ isc_throw(isc::NotImplemented, "Journaling isn't supported for "
+ "fake data source");
+ }
private:
- AuthSrv::InMemoryClientPtr real_client_;
+ const isc::datasrc::DataSourceClientContainerPtr real_client_ptr_;
ThrowWhen throw_when_;
bool isc_exception_;
ConstRRsetPtr fake_rrset_;
};
+class FakeContainer : public isc::datasrc::DataSourceClientContainer {
+public:
+ /// \brief Creates a fake container for the given in-memory client
+ ///
+ /// The initializer creates a fresh instance of a memory datasource,
+ /// which is ignored for the rest (but we do not allow 'null' containers
+ /// atm, and this is only needed in these tests, this may be changed
+ /// if we generalize the container class a bit more)
+ ///
+ /// It will also create a FakeClient, with the given arguments, which
+ /// is actually used when the instance is requested.
+ FakeContainer(isc::datasrc::DataSourceClientContainerPtr real_client,
+ ThrowWhen throw_when, bool isc_exception,
+ ConstRRsetPtr fake_rrset = ConstRRsetPtr()) :
+ DataSourceClientContainer("memory",
+ Element::fromJSON("{\"type\": \"memory\"}")),
+ client_(new FakeClient(real_client, throw_when, isc_exception,
+ fake_rrset))
+ {}
+
+ isc::datasrc::DataSourceClient& getInstance() {
+ return (*client_);
+ }
+
+private:
+ const boost::scoped_ptr<isc::datasrc::DataSourceClient> client_;
+};
+
} // end anonymous namespace for throwing proxy classes
// Test for the tests
//
// Set the proxies to never throw, this should have the same result as
// queryWithInMemoryClientNoDNSSEC, and serves to test the two proxy classes
-TEST_F(AuthSrvTest, queryWithInMemoryClientProxy) {
+TEST_F(AuthSrvTest,
+#ifdef USE_STATIC_LINK
+ DISABLED_queryWithInMemoryClientProxy
+#else
+ queryWithInMemoryClientProxy
+#endif
+ )
+{
// Set real inmem client to proxy
updateConfig(&server, CONFIG_INMEMORY_EXAMPLE, true);
+ EXPECT_TRUE(server.hasInMemoryClient());
- AuthSrv::InMemoryClientPtr fake_client(
- new FakeInMemoryClient(server.getInMemoryClient(rrclass),
- THROW_NEVER, false));
- ASSERT_NE(AuthSrv::InMemoryClientPtr(), server.getInMemoryClient(rrclass));
- server.setInMemoryClient(rrclass, fake_client);
+ isc::datasrc::DataSourceClientContainerPtr fake_client_container(
+ new FakeContainer(server.getInMemoryClientContainer(rrclass),
+ THROW_NEVER, false));
+ server.setInMemoryClient(rrclass, fake_client_container);
createDataFromFile("nsec3query_nodnssec_fromWire.wire");
server.processMessage(*io_message, *parse_message, *response_obuffer,
@@ -1297,17 +1380,23 @@ setupThrow(AuthSrv* server, const char *config, ThrowWhen throw_when,
// Set it to throw on findZone(), this should result in
// SERVFAIL on any exception
- AuthSrv::InMemoryClientPtr fake_client(
- new FakeInMemoryClient(
- server->getInMemoryClient(isc::dns::RRClass::IN()),
+ isc::datasrc::DataSourceClientContainerPtr fake_client_container(
+ new FakeContainer(
+ server->getInMemoryClientContainer(isc::dns::RRClass::IN()),
throw_when, isc_exception, rrset));
- ASSERT_NE(AuthSrv::InMemoryClientPtr(),
- server->getInMemoryClient(isc::dns::RRClass::IN()));
- server->setInMemoryClient(isc::dns::RRClass::IN(), fake_client);
+ ASSERT_TRUE(server->hasInMemoryClient());
+ server->setInMemoryClient(isc::dns::RRClass::IN(), fake_client_container);
}
-TEST_F(AuthSrvTest, queryWithThrowingProxyServfails) {
+TEST_F(AuthSrvTest,
+#ifdef USE_STATIC_LINK
+ DISABLED_queryWithThrowingProxyServfails
+#else
+ queryWithThrowingProxyServfails
+#endif
+ )
+{
// Test the common cases, all of which should simply return SERVFAIL
// Use THROW_NEVER as end marker
ThrowWhen throws[] = { THROW_AT_FIND_ZONE,
@@ -1331,7 +1420,14 @@ TEST_F(AuthSrvTest, queryWithThrowingProxyServfails) {
// Throw isc::Exception in getClass(). (Currently?) getClass is not called
// in the processMessage path, so this should result in a normal answer
-TEST_F(AuthSrvTest, queryWithInMemoryClientProxyGetClass) {
+TEST_F(AuthSrvTest,
+#ifdef USE_STATIC_LINK
+ DISABLED_queryWithInMemoryClientProxyGetClass
+#else
+ queryWithInMemoryClientProxyGetClass
+#endif
+ )
+{
createDataFromFile("nsec3query_nodnssec_fromWire.wire");
setupThrow(&server, CONFIG_INMEMORY_EXAMPLE, THROW_AT_GET_CLASS, true);
@@ -1344,7 +1440,14 @@ TEST_F(AuthSrvTest, queryWithInMemoryClientProxyGetClass) {
opcode.getCode(), QR_FLAG | AA_FLAG, 1, 1, 2, 1);
}
-TEST_F(AuthSrvTest, queryWithThrowingInToWire) {
+TEST_F(AuthSrvTest,
+#ifdef USE_STATIC_LINK
+ DISABLED_queryWithThrowingInToWire
+#else
+ queryWithThrowingInToWire
+#endif
+ )
+{
// Set up a faked data source. It will return an empty RRset for the
// query.
ConstRRsetPtr empty_rrset(new RRset(Name("foo.example"),
diff --git a/src/bin/auth/tests/command_unittest.cc b/src/bin/auth/tests/command_unittest.cc
index bcaf4b1..f134d40 100644
--- a/src/bin/auth/tests/command_unittest.cc
+++ b/src/bin/auth/tests/command_unittest.cc
@@ -23,6 +23,7 @@
#include <dns/name.h>
#include <dns/rrclass.h>
#include <dns/rrtype.h>
+#include <dns/rrttl.h>
#include <cc/data.h>
@@ -68,7 +69,7 @@ protected:
}
void checkAnswer(const int expected_code) {
parseAnswer(rcode_, result_);
- EXPECT_EQ(expected_code, rcode_);
+ EXPECT_EQ(expected_code, rcode_) << result_->str();
}
MockSession statistics_session_;
MockXfroutClient xfrout_;
@@ -233,7 +234,14 @@ newZoneChecks(AuthSrv& server) {
find(Name("ns.test2.example"), RRType::AAAA())->code);
}
-TEST_F(AuthCommandTest, loadZone) {
+TEST_F(AuthCommandTest,
+#ifdef USE_STATIC_LINK
+ DISABLED_loadZone
+#else
+ loadZone
+#endif
+ )
+{
configureZones(server_);
ASSERT_EQ(0, system(INSTALL_PROG " -c " TEST_DATA_DIR
@@ -250,8 +258,6 @@ TEST_F(AuthCommandTest, loadZone) {
newZoneChecks(server_);
}
-// This test uses dynamic load of a data source module, and won't work when
-// statically linked.
TEST_F(AuthCommandTest,
#ifdef USE_STATIC_LINK
DISABLED_loadZoneSQLite3
@@ -289,36 +295,57 @@ TEST_F(AuthCommandTest,
" }"
" ]"
" }"
- "]}"));
+ "],"
+ " \"database_file\": \"" + test_db + "\""
+ "}"));
module_session.setLocalConfig(map);
server_.setConfigSession(&module_session);
- // The loadzone command needs the zone to be already loaded, because
- // it is used for reloading only
- AuthSrv::InMemoryClientPtr dsrc(new InMemoryClient());
- dsrc->addZone(ZoneFinderPtr(new InMemoryZoneFinder(RRClass::IN(),
- Name("example.org"))));
- server_.setInMemoryClient(RRClass::IN(), dsrc);
+ server_.updateConfig(map);
+
+ // Check that the A record at www.example.org does not exist
+ ASSERT_TRUE(server_.hasInMemoryClient());
+ EXPECT_EQ(ZoneFinder::NXDOMAIN, server_.getInMemoryClient(RRClass::IN())->
+ findZone(Name("example.org")).zone_finder->
+ find(Name("www.example.org"), RRType::A())->code);
+
+ // Add the record to the underlying sqlite database, by loading
+ // it as a separate datasource, and updating it
+ ConstElementPtr sql_cfg = Element::fromJSON("{ \"type\": \"sqlite3\","
+ "\"database_file\": \""
+ + test_db + "\"}");
+ DataSourceClientContainer sql_ds("sqlite3", sql_cfg);
+ ZoneUpdaterPtr sql_updater =
+ sql_ds.getInstance().getUpdater(Name("example.org"), false);
+ RRsetPtr rrset(new RRset(Name("www.example.org."), RRClass::IN(),
+ RRType::A(), RRTTL(60)));
+ rrset->addRdata(rdata::createRdata(rrset->getType(),
+ rrset->getClass(),
+ "192.0.2.1"));
+ sql_updater->addRRset(*rrset);
+ sql_updater->commit();
+
+ // This new record is in the database now, but should not be in the
+ // memory-datasource yet, so check again
+ EXPECT_EQ(ZoneFinder::NXDOMAIN, server_.getInMemoryClient(RRClass::IN())->
+ findZone(Name("example.org")).zone_finder->
+ find(Name("www.example.org"), RRType::A())->code);
// Now send the command to reload it
result_ = execAuthServerCommand(server_, "loadzone",
- Element::fromJSON("{\"origin\": \"example.org\"}"));
+ Element::fromJSON(
+ "{\"origin\": \"example.org\"}"));
checkAnswer(0);
- // Get the zone and look if there are data in it (the original one was
- // empty)
- ASSERT_TRUE(server_.getInMemoryClient(RRClass::IN()));
+ // And now it should be present too.
EXPECT_EQ(ZoneFinder::SUCCESS, server_.getInMemoryClient(RRClass::IN())->
findZone(Name("example.org")).zone_finder->
- find(Name("example.org"), RRType::SOA())->code);
+ find(Name("www.example.org"), RRType::A())->code);
- // Some error cases. First, the zone has no configuration.
- dsrc->addZone(ZoneFinderPtr(new InMemoryZoneFinder(RRClass::IN(),
- Name("example.com"))));
+ // Some error cases. First, the zone has no configuration. (note .com here)
result_ = execAuthServerCommand(server_, "loadzone",
Element::fromJSON("{\"origin\": \"example.com\"}"));
checkAnswer(1);
-
// The previous zone is not hurt in any way
EXPECT_EQ(ZoneFinder::SUCCESS, server_.getInMemoryClient(RRClass::IN())->
findZone(Name("example.org")).zone_finder->
@@ -326,7 +353,8 @@ TEST_F(AuthCommandTest,
module_session.setLocalConfig(Element::fromJSON("{\"datasources\": []}"));
result_ = execAuthServerCommand(server_, "loadzone",
- Element::fromJSON("{\"origin\": \"example.org\"}"));
+ Element::fromJSON(
+ "{\"origin\": \"example.org\"}"));
checkAnswer(1);
// The previous zone is not hurt in any way
@@ -373,7 +401,14 @@ TEST_F(AuthCommandTest,
find(Name("example.org"), RRType::SOA())->code);
}
-TEST_F(AuthCommandTest, loadBrokenZone) {
+TEST_F(AuthCommandTest,
+#ifdef USE_STATIC_LINK
+ DISABLED_loadBrokenZone
+#else
+ loadBrokenZone
+#endif
+ )
+{
configureZones(server_);
ASSERT_EQ(0, system(INSTALL_PROG " -c " TEST_DATA_DIR
@@ -386,7 +421,14 @@ TEST_F(AuthCommandTest, loadBrokenZone) {
zoneChecks(server_); // zone shouldn't be replaced
}
-TEST_F(AuthCommandTest, loadUnreadableZone) {
+TEST_F(AuthCommandTest,
+#ifdef USE_STATIC_LINK
+ DISABLED_loadUnreadableZone
+#else
+ loadUnreadableZone
+#endif
+ )
+{
configureZones(server_);
// install the zone file as unreadable
@@ -419,7 +461,14 @@ TEST_F(AuthCommandTest, loadSqlite3DataSrc) {
checkAnswer(0);
}
-TEST_F(AuthCommandTest, loadZoneInvalidParams) {
+TEST_F(AuthCommandTest,
+#ifdef USE_STATIC_LINK
+ DISABLED_loadZoneInvalidParams
+#else
+ loadZoneInvalidParams
+#endif
+ )
+{
configureZones(server_);
// null arg
diff --git a/src/bin/auth/tests/config_unittest.cc b/src/bin/auth/tests/config_unittest.cc
index d471a53..3ff9324 100644
--- a/src/bin/auth/tests/config_unittest.cc
+++ b/src/bin/auth/tests/config_unittest.cc
@@ -69,13 +69,20 @@ private:
isc::testutils::TestSocketRequestor sock_requestor_;
};
-TEST_F(AuthConfigTest, datasourceConfig) {
+TEST_F(AuthConfigTest,
+#ifdef USE_STATIC_LINK
+ DISABLED_datasourceConfig
+#else
+ datasourceConfig
+#endif
+ )
+{
// By default, we don't have any in-memory data source.
- EXPECT_EQ(AuthSrv::InMemoryClientPtr(), server.getInMemoryClient(rrclass));
+ EXPECT_FALSE(server.hasInMemoryClient());
configureAuthServer(server, Element::fromJSON(
"{\"datasources\": [{\"type\": \"memory\"}]}"));
// after successful configuration, we should have one (with empty zoneset).
- ASSERT_NE(AuthSrv::InMemoryClientPtr(), server.getInMemoryClient(rrclass));
+ EXPECT_TRUE(server.hasInMemoryClient());
EXPECT_EQ(0, server.getInMemoryClient(rrclass)->getZoneCount());
}
@@ -96,7 +103,7 @@ TEST_F(AuthConfigTest, versionConfig) {
}
TEST_F(AuthConfigTest, exceptionGuarantee) {
- EXPECT_EQ(AuthSrv::InMemoryClientPtr(), server.getInMemoryClient(rrclass));
+ EXPECT_FALSE(server.hasInMemoryClient());
// This configuration contains an invalid item, which will trigger
// an exception.
EXPECT_THROW(configureAuthServer(
@@ -106,7 +113,7 @@ TEST_F(AuthConfigTest, exceptionGuarantee) {
" \"no_such_config_var\": 1}")),
AuthConfigError);
// The server state shouldn't change
- EXPECT_EQ(AuthSrv::InMemoryClientPtr(), server.getInMemoryClient(rrclass));
+ EXPECT_FALSE(server.hasInMemoryClient());
}
TEST_F(AuthConfigTest, exceptionConversion) {
@@ -176,25 +183,46 @@ protected:
TEST_F(MemoryDatasrcConfigTest, addZeroDataSrc) {
parser->build(Element::fromJSON("[]"));
parser->commit();
- EXPECT_EQ(AuthSrv::InMemoryClientPtr(), server.getInMemoryClient(rrclass));
+ EXPECT_FALSE(server.hasInMemoryClient());
}
-TEST_F(MemoryDatasrcConfigTest, addEmpty) {
+TEST_F(MemoryDatasrcConfigTest,
+#ifdef USE_STATIC_LINK
+ DISABLED_addEmpty
+#else
+ addEmpty
+#endif
+ )
+{
// By default, we don't have any in-memory data source.
- EXPECT_EQ(AuthSrv::InMemoryClientPtr(), server.getInMemoryClient(rrclass));
+ EXPECT_FALSE(server.hasInMemoryClient());
parser->build(Element::fromJSON("[{\"type\": \"memory\"}]"));
parser->commit();
EXPECT_EQ(0, server.getInMemoryClient(rrclass)->getZoneCount());
}
-TEST_F(MemoryDatasrcConfigTest, addZeroZone) {
+TEST_F(MemoryDatasrcConfigTest,
+#ifdef USE_STATIC_LINK
+ DISABLED_addZeroZone
+#else
+ addZeroZone
+#endif
+ )
+{
parser->build(Element::fromJSON("[{\"type\": \"memory\","
" \"zones\": []}]"));
parser->commit();
EXPECT_EQ(0, server.getInMemoryClient(rrclass)->getZoneCount());
}
-TEST_F(MemoryDatasrcConfigTest, addOneZone) {
+TEST_F(MemoryDatasrcConfigTest,
+#ifdef USE_STATIC_LINK
+ DISABLED_addOneZone
+#else
+ addOneZone
+#endif
+ )
+{
EXPECT_NO_THROW(parser->build(Element::fromJSON(
"[{\"type\": \"memory\","
" \"zones\": [{\"origin\": \"example.com\","
@@ -245,7 +273,14 @@ TEST_F(MemoryDatasrcConfigTest,
DataSourceError);
}
-TEST_F(MemoryDatasrcConfigTest, addOneWithFiletypeText) {
+TEST_F(MemoryDatasrcConfigTest,
+#ifdef USE_STATIC_LINK
+ DISABLED_addOneWithFiletypeText
+#else
+ addOneWithFiletypeText
+#endif
+ )
+{
// Explicitly specifying "text" is okay.
parser->build(Element::fromJSON(
"[{\"type\": \"memory\","
@@ -257,7 +292,14 @@ TEST_F(MemoryDatasrcConfigTest, addOneWithFiletypeText) {
EXPECT_EQ(1, server.getInMemoryClient(rrclass)->getZoneCount());
}
-TEST_F(MemoryDatasrcConfigTest, addMultiZones) {
+TEST_F(MemoryDatasrcConfigTest,
+#ifdef USE_STATIC_LINK
+ DISABLED_addMultiZones
+#else
+ addMultiZones
+#endif
+ )
+{
EXPECT_NO_THROW(parser->build(Element::fromJSON(
"[{\"type\": \"memory\","
" \"zones\": [{\"origin\": \"example.com\","
@@ -273,7 +315,14 @@ TEST_F(MemoryDatasrcConfigTest, addMultiZones) {
EXPECT_EQ(3, server.getInMemoryClient(rrclass)->getZoneCount());
}
-TEST_F(MemoryDatasrcConfigTest, replace) {
+TEST_F(MemoryDatasrcConfigTest,
+#ifdef USE_STATIC_LINK
+ DISABLED_replace
+#else
+ replace
+#endif
+ )
+{
EXPECT_NO_THROW(parser->build(Element::fromJSON(
"[{\"type\": \"memory\","
" \"zones\": [{\"origin\": \"example.com\","
@@ -304,7 +353,14 @@ TEST_F(MemoryDatasrcConfigTest, replace) {
Name("example.com")).code);
}
-TEST_F(MemoryDatasrcConfigTest, exception) {
+TEST_F(MemoryDatasrcConfigTest,
+#ifdef USE_STATIC_LINK
+ DISABLED_exception
+#else
+ exception
+#endif
+ )
+{
// Load a zone
EXPECT_NO_THROW(parser->build(Element::fromJSON(
"[{\"type\": \"memory\","
@@ -328,7 +384,8 @@ TEST_F(MemoryDatasrcConfigTest, exception) {
"/example.org.zone\"},"
" {\"origin\": \"example.net\","
" \"file\": \"" TEST_DATA_DIR
- "/nonexistent.zone\"}]}]")), isc::dns::MasterLoadError);
+ "/nonexistent.zone\"}]}]")),
+ isc::datasrc::DataSourceError);
// As that one throwed exception, it is not expected from us to
// commit it
@@ -339,7 +396,14 @@ TEST_F(MemoryDatasrcConfigTest, exception) {
Name("example.com")).code);
}
-TEST_F(MemoryDatasrcConfigTest, remove) {
+TEST_F(MemoryDatasrcConfigTest,
+#ifdef USE_STATIC_LINK
+ DISABLED_remove
+#else
+ remove
+#endif
+ )
+{
EXPECT_NO_THROW(parser->build(Element::fromJSON(
"[{\"type\": \"memory\","
" \"zones\": [{\"origin\": \"example.com\","
@@ -352,7 +416,7 @@ TEST_F(MemoryDatasrcConfigTest, remove) {
parser = createAuthConfigParser(server, "datasources");
EXPECT_NO_THROW(parser->build(Element::fromJSON("[]")));
EXPECT_NO_THROW(parser->commit());
- EXPECT_EQ(AuthSrv::InMemoryClientPtr(), server.getInMemoryClient(rrclass));
+ EXPECT_FALSE(server.hasInMemoryClient());
}
TEST_F(MemoryDatasrcConfigTest, addDuplicateZones) {
@@ -365,7 +429,7 @@ TEST_F(MemoryDatasrcConfigTest, addDuplicateZones) {
" {\"origin\": \"example.com\","
" \"file\": \"" TEST_DATA_DIR
"/example.com.zone\"}]}]")),
- AuthConfigError);
+ DataSourceError);
}
TEST_F(MemoryDatasrcConfigTest, addBadZone) {
@@ -374,35 +438,35 @@ TEST_F(MemoryDatasrcConfigTest, addBadZone) {
Element::fromJSON(
"[{\"type\": \"memory\","
" \"zones\": [{}]}]")),
- AuthConfigError);
+ DataSourceError);
// origin is missing
EXPECT_THROW(parser->build(
Element::fromJSON(
"[{\"type\": \"memory\","
" \"zones\": [{\"file\": \"example.zone\"}]}]")),
- AuthConfigError);
+ DataSourceError);
// file is missing
EXPECT_THROW(parser->build(
Element::fromJSON(
"[{\"type\": \"memory\","
" \"zones\": [{\"origin\": \"example.com\"}]}]")),
- AuthConfigError);
+ DataSourceError);
// missing zone file
EXPECT_THROW(parser->build(
Element::fromJSON(
"[{\"type\": \"memory\","
" \"zones\": [{\"origin\": \"example.com\"}]}]")),
- AuthConfigError);
+ DataSourceError);
// bogus origin name
EXPECT_THROW(parser->build(Element::fromJSON(
"[{\"type\": \"memory\","
" \"zones\": [{\"origin\": \"example..com\","
" \"file\": \"example.zone\"}]}]")),
- AuthConfigError);
+ DataSourceError);
// bogus RR class name
EXPECT_THROW(parser->build(
@@ -423,7 +487,14 @@ TEST_F(MemoryDatasrcConfigTest, addBadZone) {
isc::InvalidParameter);
}
-TEST_F(MemoryDatasrcConfigTest, badDatasrcType) {
+TEST_F(MemoryDatasrcConfigTest,
+#ifdef USE_STATIC_LINK
+ DISABLED_badDatasrcType
+#else
+ badDatasrcType
+#endif
+ )
+{
EXPECT_THROW(parser->build(Element::fromJSON("[{\"type\": \"badsrc\"}]")),
AuthConfigError);
EXPECT_THROW(parser->build(Element::fromJSON("[{\"notype\": \"memory\"}]")),
diff --git a/src/lib/datasrc/client.h b/src/lib/datasrc/client.h
index 24c8850..dab081f 100644
--- a/src/lib/datasrc/client.h
+++ b/src/lib/datasrc/client.h
@@ -362,6 +362,22 @@ public:
virtual std::pair<ZoneJournalReader::Result, ZoneJournalReaderPtr>
getJournalReader(const isc::dns::Name& zone, uint32_t begin_serial,
uint32_t end_serial) const = 0;
+
+ /// Return the number of zones currently known to this datasource
+ ///
+ /// This is an optional convenience method, currently only implemented
+ /// by the InMemory datasource. By default, it throws NotImplemented
+ ///
+ /// \exception NotImplemented Thrown if this method is not supported
+ /// by the datasource
+ ///
+ /// \note This is a tentative API, and this method may likely to be
+ /// removed in the near future.
+ /// \return The number of zones known to this datasource
+ virtual unsigned int getZoneCount() const {
+ isc_throw(isc::NotImplemented,
+ "Data source doesn't support getZoneCount");
+ }
};
}
}
diff --git a/src/lib/datasrc/factory.cc b/src/lib/datasrc/factory.cc
index 35a79fe..82b4df9 100644
--- a/src/lib/datasrc/factory.cc
+++ b/src/lib/datasrc/factory.cc
@@ -23,6 +23,8 @@
#include <datasrc/logger.h>
+#include <exceptions/exceptions.h>
+
#include <dlfcn.h>
#include <cstdlib>
diff --git a/src/lib/datasrc/factory.h b/src/lib/datasrc/factory.h
index f3ca397..2731f58 100644
--- a/src/lib/datasrc/factory.h
+++ b/src/lib/datasrc/factory.h
@@ -15,14 +15,14 @@
#ifndef __DATA_SOURCE_FACTORY_H
#define __DATA_SOURCE_FACTORY_H 1
-#include <boost/noncopyable.hpp>
-
#include <datasrc/data_source.h>
#include <datasrc/client.h>
-#include <exceptions/exceptions.h>
#include <cc/data.h>
+#include <boost/noncopyable.hpp>
+#include <boost/shared_ptr.hpp>
+
namespace isc {
namespace datasrc {
@@ -134,6 +134,13 @@ private:
///
/// extern "C" void destroyInstance(isc::data::DataSourceClient* instance);
/// \endcode
+///
+/// \note This class is relatively recent, and its design is not yet fully
+/// formed. We may want to split this into an abstract base container
+/// class, and a derived 'dyload' class, and perhaps then add non-dynamic
+/// derived classes as well. Currently, the class is actually derived in some
+/// of the tests, which is rather unclean (as this class as written is really
+/// intended to be used directly).
class DataSourceClientContainer : boost::noncopyable {
public:
/// \brief Constructor
@@ -157,13 +164,13 @@ public:
isc::data::ConstElementPtr config);
/// \brief Destructor
- ~DataSourceClientContainer();
+ virtual ~DataSourceClientContainer();
/// \brief Accessor to the instance
///
/// \return Reference to the DataSourceClient instance contained in this
/// container.
- DataSourceClient& getInstance() { return (*instance_); }
+ virtual DataSourceClient& getInstance() { return (*instance_); }
private:
DataSourceClient* instance_;
@@ -171,6 +178,12 @@ private:
LibraryContainer ds_lib_;
};
+///
+/// Shared pointer type for datasource client containers
+///
+typedef boost::shared_ptr<DataSourceClientContainer>
+ DataSourceClientContainerPtr;
+
} // end namespace datasrc
} // end namespace isc
#endif // DATA_SOURCE_FACTORY_H
diff --git a/src/lib/datasrc/memory_datasrc.h b/src/lib/datasrc/memory_datasrc.h
index f4e99a8..b7c45bc 100644
--- a/src/lib/datasrc/memory_datasrc.h
+++ b/src/lib/datasrc/memory_datasrc.h
@@ -282,7 +282,7 @@ public:
/// This method never throws an exception.
///
/// \return The number of zones stored in the client.
- unsigned int getZoneCount() const;
+ virtual unsigned int getZoneCount() const;
/// Add a zone (in the form of \c ZoneFinder) to the \c InMemoryClient.
///
diff --git a/src/lib/datasrc/memory_datasrc_link.cc b/src/lib/datasrc/memory_datasrc_link.cc
index a0b4bf6..cbbc6db 100644
--- a/src/lib/datasrc/memory_datasrc_link.cc
+++ b/src/lib/datasrc/memory_datasrc_link.cc
@@ -17,9 +17,13 @@
#include <dns/rrclass.h>
#include <datasrc/client.h>
+#include <datasrc/factory.h>
#include <datasrc/memory_datasrc.h>
+#include <exceptions/exceptions.h>
+
#include <boost/foreach.hpp>
+#include <boost/scoped_ptr.hpp>
#include <string>
@@ -29,6 +33,14 @@ using namespace isc::data;
namespace isc {
namespace datasrc {
+/// This exception is raised if there is an error in the configuration
+/// that has been passed; missing information, duplicate values, etc.
+class InMemoryConfigError : public isc::Exception {
+public:
+ InMemoryConfigError(const char* file, size_t line, const char* what) :
+ isc::Exception(file, line, what) {}
+};
+
namespace {
// convencience function to add an error message to a list of those
// (TODO: move functions like these to some util lib?)
@@ -49,14 +61,14 @@ checkConfigElementString(ConstElementPtr config, const std::string& name,
"Config for memory backend does not contain a '"
+name+
"' value");
- return false;
+ return (false);
} else if (!config->get(name) ||
config->get(name)->getType() != Element::string) {
addError(errors, "value of " + name +
" in memory backend config is not a string");
- return false;
+ return (false);
} else {
- return true;
+ return (true);
}
}
@@ -112,24 +124,26 @@ checkConfig(ConstElementPtr config, ElementPtr errors) {
result = false;
}
}
- if (!checkConfigElementString(config, "class", errors)) {
- result = false;
- } else {
- try {
- RRClass rrc(config->get("class")->stringValue());
- } catch (const isc::Exception& rrce) {
- addError(errors,
- "Error parsing class config for memory backend: " +
- std::string(rrce.what()));
+ if (config->contains("class")) {
+ if (!checkConfigElementString(config, "class", errors)) {
result = false;
+ } else {
+ try {
+ RRClass rrc(config->get("class")->stringValue());
+ } catch (const isc::Exception& rrce) {
+ addError(errors,
+ "Error parsing class config for memory backend: " +
+ std::string(rrce.what()));
+ result = false;
+ }
}
}
if (!config->contains("zones")) {
- addError(errors, "No 'zones' element in memory backend config");
- result = false;
+ // Assume empty list of zones
} else if (!config->get("zones") ||
config->get("zones")->getType() != Element::list) {
- addError(errors, "'zones' element in memory backend config is not a list");
+ addError(errors,
+ "'zones' element in memory backend config is not a list");
result = false;
} else {
BOOST_FOREACH(ConstElementPtr zone_config,
@@ -144,6 +158,91 @@ checkConfig(ConstElementPtr config, ElementPtr errors) {
return (result);
}
+// Apply the given config to the just-initialized client
+// client must be freshly allocated, and config_value should have been
+// checked by the caller
+void
+applyConfig(isc::datasrc::InMemoryClient& client,
+ isc::data::ConstElementPtr config_value)
+{
+ // XXX: We have lost the context to get to the default values here,
+ // as a temporary workaround we hardcode the IN class here.
+ isc::dns::RRClass rrclass = RRClass::IN();
+ if (config_value->contains("class")) {
+ rrclass = RRClass(config_value->get("class")->stringValue());
+ }
+ ConstElementPtr zones_config = config_value->get("zones");
+ if (!zones_config) {
+ // XXX: Like the RR class, we cannot retrieve the default value here,
+ // so we assume an empty zone list in this case.
+ return;
+ }
+
+ BOOST_FOREACH(ConstElementPtr zone_config, zones_config->listValue()) {
+ ConstElementPtr origin = zone_config->get("origin");
+ const std::string origin_txt = origin ? origin->stringValue() : "";
+ if (origin_txt.empty()) {
+ isc_throw(InMemoryConfigError, "Missing zone origin");
+ }
+ ConstElementPtr file = zone_config->get("file");
+ const std::string file_txt = file ? file->stringValue() : "";
+ if (file_txt.empty()) {
+ isc_throw(InMemoryConfigError, "Missing zone file for zone: "
+ << origin_txt);
+ }
+
+ // We support the traditional text type and SQLite3 backend. For the
+ // latter we create a client for the underlying SQLite3 data source,
+ // and build the in-memory zone using an iterator of the underlying
+ // zone.
+ ConstElementPtr filetype = zone_config->get("filetype");
+ const std::string filetype_txt = filetype ? filetype->stringValue() :
+ "text";
+ boost::scoped_ptr<DataSourceClientContainer> container;
+ if (filetype_txt == "sqlite3") {
+ container.reset(new DataSourceClientContainer(
+ "sqlite3",
+ Element::fromJSON("{\"database_file\": \"" +
+ file_txt + "\"}")));
+ } else if (filetype_txt != "text") {
+ isc_throw(InMemoryConfigError, "Invalid filetype for zone "
+ << origin_txt << ": " << filetype_txt);
+ }
+
+ // Note: we don't want to have such small try-catch blocks for each
+ // specific error. We may eventually want to introduce some unified
+ // error handling framework as we have more configuration parameters.
+ // See bug #1627 for the relevant discussion.
+ InMemoryZoneFinder* imzf = NULL;
+ try {
+ imzf = new InMemoryZoneFinder(rrclass, Name(origin_txt));
+ } catch (const isc::dns::NameParserException& ex) {
+ isc_throw(InMemoryConfigError, "unable to parse zone's origin: " <<
+ ex.what());
+ }
+
+ boost::shared_ptr<InMemoryZoneFinder> zone_finder(imzf);
+ const result::Result result = client.addZone(zone_finder);
+ if (result == result::EXIST) {
+ isc_throw(InMemoryConfigError, "zone "<< origin->str()
+ << " already exists");
+ }
+
+ /*
+ * TODO: Once we have better reloading of configuration (something
+ * else than throwing everything away and loading it again), we will
+ * need the load method to be split into some kind of build and
+ * commit/abort parts.
+ */
+ if (filetype_txt == "text") {
+ zone_finder->load(file_txt);
+ } else {
+ zone_finder->load(*container->getInstance().getIterator(
+ Name(origin_txt)));
+ }
+ }
+}
+
} // end unnamed namespace
DataSourceClient *
@@ -154,7 +253,12 @@ createInstance(isc::data::ConstElementPtr config, std::string& error) {
return (NULL);
}
try {
- return (new isc::datasrc::InMemoryClient());
+ std::auto_ptr<InMemoryClient> client(new isc::datasrc::InMemoryClient());
+ applyConfig(*client, config);
+ return (client.release());
+ } catch (const isc::Exception& isce) {
+ error = isce.what();
+ return (NULL);
} catch (const std::exception& exc) {
error = std::string("Error creating memory datasource: ") + exc.what();
return (NULL);
diff --git a/src/lib/datasrc/tests/client_unittest.cc b/src/lib/datasrc/tests/client_unittest.cc
index 64ad25f..87ab5e0 100644
--- a/src/lib/datasrc/tests/client_unittest.cc
+++ b/src/lib/datasrc/tests/client_unittest.cc
@@ -56,4 +56,8 @@ TEST_F(ClientTest, defaultIterator) {
EXPECT_THROW(client_.getIterator(Name(".")), isc::NotImplemented);
}
+TEST_F(ClientTest, defaultGetZoneCount) {
+ EXPECT_THROW(client_.getZoneCount(), isc::NotImplemented);
+}
+
}
diff --git a/src/lib/datasrc/tests/factory_unittest.cc b/src/lib/datasrc/tests/factory_unittest.cc
index e98f9bc..58afd7b 100644
--- a/src/lib/datasrc/tests/factory_unittest.cc
+++ b/src/lib/datasrc/tests/factory_unittest.cc
@@ -188,8 +188,8 @@ TEST(FactoryTest, memoryClient) {
DataSourceError);
config->set("type", Element::create("memory"));
- ASSERT_THROW(DataSourceClientContainer("memory", config),
- DataSourceError);
+ // no config at all should result in a default empty memory client
+ ASSERT_NO_THROW(DataSourceClientContainer("memory", config));
config->set("class", ElementPtr());
ASSERT_THROW(DataSourceClientContainer("memory", config),
@@ -204,8 +204,7 @@ TEST(FactoryTest, memoryClient) {
DataSourceError);
config->set("class", Element::create("IN"));
- ASSERT_THROW(DataSourceClientContainer("memory", config),
- DataSourceError);
+ ASSERT_NO_THROW(DataSourceClientContainer("memory", config));
config->set("zones", ElementPtr());
ASSERT_THROW(DataSourceClientContainer("memory", config),
More information about the bind10-changes
mailing list