BIND 10 master, updated. 9281b972a87becd0f85b747d5d939f8f2e317247 [master] Merge branch 'trac2212-2'
BIND 10 source code commits
bind10-changes at lists.isc.org
Mon Oct 29 17:59:31 UTC 2012
The branch, master has been updated
via 9281b972a87becd0f85b747d5d939f8f2e317247 (commit)
via cf72f036909c786091f845a736bfd6fcfd15a7ac (commit)
via e4850b8e75f2d8fd4924b5060cbf3c4458d3513d (commit)
via aade8ac800d58c46dad9ab862cf0d6e85174b17d (commit)
via d8b60408e4843a88d15e3cd95dfeb898cea2a8fd (commit)
via 99bdc7de647ee1abb8401cc1fd8337462173e7f8 (commit)
via a6ca17646b6bdf23f36d523bb56f3bb91e1dbf39 (commit)
via 4b1c96c8a9bba522a512d07b23e36feebaec10bd (commit)
via d12b460fbb26cfbd968bf0db25c1d0d3a8d5fb4a (commit)
via 3b3b42295d636901e0647d0bba3a4f3677af50a0 (commit)
via 0bc630b2e4ab6efd3f837f2dd6f5e771e6a71b3c (commit)
via c37eaa23d920b79d3f344c8fbafbdcda37231b47 (commit)
via a2224a3c4ea8bcfc685190d78e539608e76006f4 (commit)
via eac4d4fdbc978c037d83081a3a7d99db4a8135bd (commit)
via edcbc935eb6ddc4bb5fac1e76e8eac100e783e74 (commit)
via 7ff2ddee910f9a53bd1dd47c432c99a280a6d076 (commit)
via 3519306b1c9d4bcaf8d6758ca65ad4a371296346 (commit)
via 64b2a61c6d242b974169b72542be45a57630ec8d (commit)
via 03499b185e1dca3807f4c09cd36ba066f25bc918 (commit)
from 61fc577d38615b1a25b5a94fa3bbaca9ec2f6ddf (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 9281b972a87becd0f85b747d5d939f8f2e317247
Merge: 61fc577 cf72f03
Author: JINMEI Tatuya <jinmei at isc.org>
Date: Mon Oct 29 10:50:54 2012 -0700
[master] Merge branch 'trac2212-2'
-----------------------------------------------------------------------
Summary of changes:
src/bin/auth/auth_messages.mes | 14 +
src/bin/auth/datasrc_clients_mgr.h | 126 ++++++-
.../auth/tests/datasrc_clients_builder_unittest.cc | 353 +++++++++++++++++++-
src/bin/auth/tests/test_datasrc_clients_mgr.cc | 2 +
src/bin/auth/tests/test_datasrc_clients_mgr.h | 3 +-
5 files changed, 482 insertions(+), 16 deletions(-)
-----------------------------------------------------------------------
diff --git a/src/bin/auth/auth_messages.mes b/src/bin/auth/auth_messages.mes
index bdbd2fd..163b6ba 100644
--- a/src/bin/auth/auth_messages.mes
+++ b/src/bin/auth/auth_messages.mes
@@ -61,6 +61,15 @@ the message.
A debug message, showing when the separate thread for maintaining data
source clients receives a command from the manager.
+% AUTH_DATASRC_CLIENTS_BUILDER_COMMAND_ERROR command execution failure: %1
+The separate thread for maintaining data source clients failed to complete a
+command given by the main thread. In most cases this is some kind of
+configuration or temporary error such as an attempt to load a non-existent
+zone or a temporary DB connection failure. So the event is just logged and
+the thread keeps running. In some rare cases, however, this may indicate an
+internal bug and it may be better to restart the entire program, so the log
+message should be carefully examined.
+
% AUTH_DATASRC_CLIENTS_BUILDER_FAILED data source builder thread stopped due to an exception: %1
The separate thread for maintaining data source clients has been
terminated due to some uncaught exception. When this happens, the
@@ -79,6 +88,11 @@ indicate some run time failure than program errors. As in the other
failure case, the thread terminates the entire process immediately
after logging this message.
+% AUTH_DATASRC_CLIENTS_BUILDER_LOAD_ZONE loaded zone %1/%2
+This debug message is issued when the separate thread for maintaining data
+source clients successfully loaded the named zone of the named class as a
+result of the 'loadzone' command.
+
% AUTH_DATASRC_CLIENTS_BUILDER_RECONFIGURE_CONFIG_ERROR Error in data source configuration: %1
The thread for maintaining data source clients has received a command to
reconfigure, but the parameter data (the new configuration) contains an
diff --git a/src/bin/auth/datasrc_clients_mgr.h b/src/bin/auth/datasrc_clients_mgr.h
index 48bae81..aaaf896 100644
--- a/src/bin/auth/datasrc_clients_mgr.h
+++ b/src/bin/auth/datasrc_clients_mgr.h
@@ -27,6 +27,7 @@
#include <datasrc/data_source.h>
#include <datasrc/client_list.h>
+#include <datasrc/memory/zone_writer.h>
#include <auth/auth_log.h>
#include <auth/datasrc_config.h>
@@ -37,6 +38,7 @@
#include <boost/noncopyable.hpp>
#include <exception>
+#include <cassert>
#include <list>
#include <utility>
@@ -55,6 +57,9 @@ enum CommandID {
RECONFIGURE, ///< Reconfigure the datasource client lists,
/// the argument to the command is the full new
/// datasources configuration.
+ LOADZONE, ///< Load a new version of zone into a memory,
+ /// the argument to the command is a map containing 'class'
+ /// and 'origin' elements, both should have been validated.
SHUTDOWN, ///< Shutdown the builder; no argument
NUM_COMMANDS
};
@@ -290,7 +295,23 @@ namespace datasrc_clientmgr_internal {
/// threads or locks.
template <typename MutexType, typename CondVarType>
class DataSrcClientsBuilderBase : boost::noncopyable {
+private:
+ typedef std::map<dns::RRClass,
+ boost::shared_ptr<datasrc::ConfigurableClientList> >
+ ClientListsMap;
+
public:
+ /// \brief Internal errors in handling commands.
+ ///
+ /// This exception is expected to be caught within the
+ /// \c DataSrcClientsBuilder implementation, but is defined as public
+ /// so tests can be checked it.
+ class InternalCommandError : public isc::Exception {
+ public:
+ InternalCommandError(const char* file, size_t line, const char* what) :
+ isc::Exception(file, line, what) {}
+ };
+
/// \brief Constructor.
///
/// It simply sets up a local copy of shared data with the manager.
@@ -365,6 +386,11 @@ private:
}
}
+ void doLoadZone(const isc::data::ConstElementPtr& arg);
+ boost::shared_ptr<datasrc::memory::ZoneWriter> getZoneWriter(
+ datasrc::ConfigurableClientList& client_list,
+ const dns::RRClass& rrclass, const dns::Name& origin);
+
// The following are shared with the manager
std::list<Command>* command_queue_;
CondVarType* cond_;
@@ -397,7 +423,13 @@ DataSrcClientsBuilderBase<MutexType, CondVarType>::run() {
} // the lock is released here.
while (keep_running && !current_commands.empty()) {
- keep_running = handleCommand(current_commands.front());
+ try {
+ keep_running = handleCommand(current_commands.front());;
+ } catch (const InternalCommandError& e) {
+ LOG_ERROR(auth_logger,
+ AUTH_DATASRC_CLIENTS_BUILDER_COMMAND_ERROR).
+ arg(e.what());
+ }
current_commands.pop_front();
}
}
@@ -426,7 +458,7 @@ DataSrcClientsBuilderBase<MutexType, CondVarType>::handleCommand(
}
const boost::array<const char*, NUM_COMMANDS> command_desc = {
- {"NOOP", "RECONFIGURE", "SHUTDOWN"}
+ {"NOOP", "RECONFIGURE", "LOADZONE", "SHUTDOWN"}
};
LOG_DEBUG(auth_logger, DBGLVL_TRACE_BASIC,
AUTH_DATASRC_CLIENTS_BUILDER_COMMAND).arg(command_desc.at(cid));
@@ -434,6 +466,9 @@ DataSrcClientsBuilderBase<MutexType, CondVarType>::handleCommand(
case RECONFIGURE:
doReconfigure(command.second);
break;
+ case LOADZONE:
+ doLoadZone(command.second);
+ break;
case SHUTDOWN:
return (false);
case NOOP:
@@ -444,6 +479,93 @@ DataSrcClientsBuilderBase<MutexType, CondVarType>::handleCommand(
}
return (true);
}
+
+template <typename MutexType, typename CondVarType>
+void
+DataSrcClientsBuilderBase<MutexType, CondVarType>::doLoadZone(
+ const isc::data::ConstElementPtr& arg)
+{
+ // We assume some basic level validation as this method can only be
+ // called via the manager in practice. manager is expected to do the
+ // minimal validation.
+ assert(arg);
+ assert(arg->get("class"));
+ assert(arg->get("origin"));
+
+ const dns::RRClass rrclass(arg->get("class")->stringValue());
+ const dns::Name origin(arg->get("origin")->stringValue());
+ ClientListsMap::iterator found = (*clients_map_)->find(rrclass);
+ if (found == (*clients_map_)->end()) {
+ isc_throw(InternalCommandError, "failed to load a zone " << origin <<
+ "/" << rrclass << ": not configured for the class");
+ }
+
+ boost::shared_ptr<datasrc::ConfigurableClientList> client_list =
+ found->second;
+ assert(client_list);
+
+ try {
+ boost::shared_ptr<datasrc::memory::ZoneWriter> zwriter =
+ getZoneWriter(*client_list, rrclass, origin);
+
+ zwriter->load(); // this can take time but doesn't cause a race
+ { // install() can cause a race and must be in a critical section
+ typename MutexType::Locker locker(*map_mutex_);
+ zwriter->install();
+ }
+ LOG_DEBUG(auth_logger, DBG_AUTH_OPS,
+ AUTH_DATASRC_CLIENTS_BUILDER_LOAD_ZONE)
+ .arg(origin).arg(rrclass);
+
+ // same as load(). We could let the destructor do it, but do it
+ // ourselves explicitly just in case.
+ zwriter->cleanup();
+ } catch (const InternalCommandError& ex) {
+ throw; // this comes from getZoneWriter. just let it go through.
+ } catch (const isc::Exception& ex) {
+ // We catch our internal exceptions (which will be just ignored) and
+ // propagated others (which should generally be considered fatal and
+ // will make the thread terminate)
+ isc_throw(InternalCommandError, "failed to load a zone " << origin <<
+ "/" << rrclass << ": error occurred in reload: " <<
+ ex.what());
+ }
+}
+
+// A dedicated subroutine of doLoadZone(). Separated just for keeping the
+// main method concise.
+template <typename MutexType, typename CondVarType>
+boost::shared_ptr<datasrc::memory::ZoneWriter>
+DataSrcClientsBuilderBase<MutexType, CondVarType>::getZoneWriter(
+ datasrc::ConfigurableClientList& client_list,
+ const dns::RRClass& rrclass, const dns::Name& origin)
+{
+ const datasrc::ConfigurableClientList::ZoneWriterPair writerpair =
+ client_list.getCachedZoneWriter(origin);
+
+ switch (writerpair.first) {
+ case datasrc::ConfigurableClientList::ZONE_RELOADED: // XXX misleading name
+ assert(writerpair.second);
+ return (writerpair.second);
+ case datasrc::ConfigurableClientList::ZONE_NOT_FOUND:
+ isc_throw(InternalCommandError, "failed to load zone " << origin
+ << "/" << rrclass << ": not found in any configured "
+ "data source.");
+ case datasrc::ConfigurableClientList::ZONE_NOT_CACHED:
+ isc_throw(InternalCommandError, "failed to load zone " << origin
+ << "/" << rrclass << ": not served from memory");
+ case datasrc::ConfigurableClientList::CACHE_DISABLED:
+ // This is an internal error. Auth server must have the cache
+ // enabled.
+ isc_throw(InternalCommandError, "failed to load zone " << origin
+ << "/" << rrclass << ": internal failure, in-memory cache "
+ "is somehow disabled");
+ }
+
+ // all cases above should either return or throw, but some compilers
+ // still need a return statement
+ return (boost::shared_ptr<datasrc::memory::ZoneWriter>());
+}
} // namespace datasrc_clientmgr_internal
/// \brief Shortcut type for normal data source clients manager.
diff --git a/src/bin/auth/tests/datasrc_clients_builder_unittest.cc b/src/bin/auth/tests/datasrc_clients_builder_unittest.cc
index 22d33cf..0712ef7 100644
--- a/src/bin/auth/tests/datasrc_clients_builder_unittest.cc
+++ b/src/bin/auth/tests/datasrc_clients_builder_unittest.cc
@@ -12,36 +12,62 @@
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
+#include <util/unittests/check_valgrind.h>
+
+#include <dns/name.h>
+#include <dns/rrclass.h>
+
#include <cc/data.h>
+#include <datasrc/client.h>
+#include <datasrc/factory.h>
+
#include <auth/datasrc_clients_mgr.h>
+#include <auth/datasrc_config.h>
+
+#include <testutils/dnsmessage_test.h>
+
#include "test_datasrc_clients_mgr.h"
+#include "datasrc_util.h"
#include <gtest/gtest.h>
#include <boost/function.hpp>
+#include <cstdlib>
+#include <string>
+#include <sstream>
+
using isc::data::ConstElementPtr;
+using namespace isc::dns;
+using namespace isc::data;
using namespace isc::datasrc;
using namespace isc::auth::datasrc_clientmgr_internal;
+using namespace isc::auth::unittest;
+using namespace isc::testutils;
namespace {
class DataSrcClientsBuilderTest : public ::testing::Test {
protected:
DataSrcClientsBuilderTest() :
+ clients_map(new std::map<RRClass,
+ boost::shared_ptr<ConfigurableClientList> >),
builder(&command_queue, &cond, &queue_mutex, &clients_map, &map_mutex),
- cond(command_queue, delayed_command_queue),
+ cond(command_queue, delayed_command_queue), rrclass(RRClass::IN()),
shutdown_cmd(SHUTDOWN, ConstElementPtr()),
noop_cmd(NOOP, ConstElementPtr())
{}
- TestDataSrcClientsBuilder builder;
+ void configureZones(); // used for loadzone related tests
+
+ ClientListMapPtr clients_map; // configured clients
std::list<Command> command_queue; // test command queue
std::list<Command> delayed_command_queue; // commands available after wait
- ClientListMapPtr clients_map; // configured clients
+ TestDataSrcClientsBuilder builder;
TestCondVar cond;
TestMutex queue_mutex;
TestMutex map_mutex;
+ const RRClass rrclass;
const Command shutdown_cmd;
const Command noop_cmd;
};
@@ -74,13 +100,20 @@ TEST_F(DataSrcClientsBuilderTest, exception) {
// them. Right now, we simply abort to prevent the system from running
// with half-broken state. Eventually we should introduce a better
// error handling.
- command_queue.push_back(noop_cmd);
- queue_mutex.throw_from_noop = TestMutex::EXCLASS;
- EXPECT_DEATH_IF_SUPPORTED({builder.run();}, "");
+ if (!isc::util::unittests::runningOnValgrind()) {
+ command_queue.push_back(noop_cmd);
+ queue_mutex.throw_from_noop = TestMutex::EXCLASS;
+ EXPECT_DEATH_IF_SUPPORTED({builder.run();}, "");
+
+ command_queue.push_back(noop_cmd);
+ queue_mutex.throw_from_noop = TestMutex::INTEGER;
+ EXPECT_DEATH_IF_SUPPORTED({builder.run();}, "");
+ }
command_queue.push_back(noop_cmd);
- queue_mutex.throw_from_noop = TestMutex::INTEGER;
- EXPECT_DEATH_IF_SUPPORTED({builder.run();}, "");
+ command_queue.push_back(shutdown_cmd); // we need to stop the loop
+ queue_mutex.throw_from_noop = TestMutex::INTERNAL;
+ builder.run();
}
TEST_F(DataSrcClientsBuilderTest, condWait) {
@@ -106,10 +139,10 @@ TEST_F(DataSrcClientsBuilderTest, reconfigure) {
Command reconfig_cmd(RECONFIGURE, ConstElementPtr());
// Initially, no clients should be there
- EXPECT_EQ(ClientListMapPtr(), clients_map);
+ EXPECT_TRUE(clients_map->empty());
// A config that doesn't do much except be accepted
- ConstElementPtr good_config = isc::data::Element::fromJSON(
+ ConstElementPtr good_config = Element::fromJSON(
"{"
"\"IN\": [{"
" \"type\": \"MasterFiles\","
@@ -121,7 +154,7 @@ TEST_F(DataSrcClientsBuilderTest, reconfigure) {
// A configuration that is 'correct' in the top-level, but contains
// bad data for the type it specifies
- ConstElementPtr bad_config = isc::data::Element::fromJSON(
+ ConstElementPtr bad_config = Element::fromJSON(
"{"
"\"IN\": [{"
" \"type\": \"MasterFiles\","
@@ -142,7 +175,7 @@ TEST_F(DataSrcClientsBuilderTest, reconfigure) {
// If a 'bad' command argument got here, the config validation should
// have failed already, but still, the handler should return true,
// and the clients_map should not be updated.
- reconfig_cmd.second = isc::data::Element::create("{ \"foo\": \"bar\" }");
+ reconfig_cmd.second = Element::create("{ \"foo\": \"bar\" }");
EXPECT_TRUE(builder.handleCommand(reconfig_cmd));
EXPECT_EQ(working_config_clients, clients_map);
// Building failed, so map mutex should not have been locked again
@@ -173,7 +206,7 @@ TEST_F(DataSrcClientsBuilderTest, reconfigure) {
EXPECT_EQ(2, map_mutex.lock_count);
// And finally, try an empty config to disable all datasource clients
- reconfig_cmd.second = isc::data::Element::createMap();
+ reconfig_cmd.second = Element::createMap();
EXPECT_TRUE(builder.handleCommand(reconfig_cmd));
EXPECT_EQ(0, clients_map->size());
EXPECT_EQ(3, map_mutex.lock_count);
@@ -193,4 +226,298 @@ TEST_F(DataSrcClientsBuilderTest, badCommand) {
isc::Unexpected);
}
+// A helper function commonly used for the "loadzone" command tests.
+// It configures the given data source client lists with a memory data source
+// containing two zones, and checks the zones are correctly loaded.
+void
+zoneChecks(ClientListMapPtr clients_map, RRClass rrclass) {
+ EXPECT_EQ(ZoneFinder::SUCCESS, clients_map->find(rrclass)->second->
+ find(Name("ns.test1.example")).finder_->
+ find(Name("ns.test1.example"), RRType::A())->code);
+ EXPECT_EQ(ZoneFinder::NXRRSET, clients_map->find(rrclass)->second->
+ find(Name("ns.test1.example")).finder_->
+ find(Name("ns.test1.example"), RRType::AAAA())->code);
+ EXPECT_EQ(ZoneFinder::SUCCESS, clients_map->find(rrclass)->second->
+ find(Name("ns.test2.example")).finder_->
+ find(Name("ns.test2.example"), RRType::A())->code);
+ EXPECT_EQ(ZoneFinder::NXRRSET, clients_map->find(rrclass)->second->
+ find(Name("ns.test2.example")).finder_->
+ find(Name("ns.test2.example"), RRType::AAAA())->code);
+}
+
+// Another helper that checks after completing loadzone command.
+void
+newZoneChecks(ClientListMapPtr clients_map, RRClass rrclass) {
+ EXPECT_EQ(ZoneFinder::SUCCESS, clients_map->find(rrclass)->second->
+ find(Name("ns.test1.example")).finder_->
+ find(Name("ns.test1.example"), RRType::A())->code);
+ // now test1.example should have ns/AAAA
+ EXPECT_EQ(ZoneFinder::SUCCESS, clients_map->find(rrclass)->second->
+ find(Name("ns.test1.example")).finder_->
+ find(Name("ns.test1.example"), RRType::AAAA())->code);
+
+ // test2.example shouldn't change
+ EXPECT_EQ(ZoneFinder::SUCCESS, clients_map->find(rrclass)->second->
+ find(Name("ns.test2.example")).finder_->
+ find(Name("ns.test2.example"), RRType::A())->code);
+ EXPECT_EQ(ZoneFinder::NXRRSET,
+ clients_map->find(rrclass)->second->
+ find(Name("ns.test2.example")).finder_->
+ find(Name("ns.test2.example"), RRType::AAAA())->code);
+}
+
+void
+DataSrcClientsBuilderTest::configureZones() {
+ ASSERT_EQ(0, std::system(INSTALL_PROG " -c " TEST_DATA_DIR "/test1.zone.in "
+ TEST_DATA_BUILDDIR "/test1.zone.copied"));
+ ASSERT_EQ(0, std::system(INSTALL_PROG " -c " TEST_DATA_DIR "/test2.zone.in "
+ TEST_DATA_BUILDDIR "/test2.zone.copied"));
+
+ const ConstElementPtr config(
+ Element::fromJSON(
+ "{"
+ "\"IN\": [{"
+ " \"type\": \"MasterFiles\","
+ " \"params\": {"
+ " \"test1.example\": \"" +
+ std::string(TEST_DATA_BUILDDIR "/test1.zone.copied") + "\","
+ " \"test2.example\": \"" +
+ std::string(TEST_DATA_BUILDDIR "/test2.zone.copied") + "\""
+ " },"
+ " \"cache-enable\": true"
+ "}]}"));
+ clients_map = configureDataSource(config);
+ zoneChecks(clients_map, rrclass);
+}
+
+TEST_F(DataSrcClientsBuilderTest, loadZone) {
+ // pre test condition checks
+ EXPECT_EQ(0, map_mutex.lock_count);
+ EXPECT_EQ(0, map_mutex.unlock_count);
+
+ configureZones();
+
+ EXPECT_EQ(0, system(INSTALL_PROG " -c " TEST_DATA_DIR
+ "/test1-new.zone.in "
+ TEST_DATA_BUILDDIR "/test1.zone.copied"));
+ EXPECT_EQ(0, system(INSTALL_PROG " -c " TEST_DATA_DIR
+ "/test2-new.zone.in "
+ TEST_DATA_BUILDDIR "/test2.zone.copied"));
+
+ const Command loadzone_cmd(LOADZONE, Element::fromJSON(
+ "{\"class\": \"IN\","
+ " \"origin\": \"test1.example\"}"));
+ EXPECT_TRUE(builder.handleCommand(loadzone_cmd));
+ EXPECT_EQ(1, map_mutex.lock_count); // we should have acquired the lock
+ EXPECT_EQ(1, map_mutex.unlock_count); // and released it.
+
+ newZoneChecks(clients_map, rrclass);
+}
+
+TEST_F(DataSrcClientsBuilderTest,
+#ifdef USE_STATIC_LINK
+ DISABLED_loadZoneSQLite3
+#else
+ loadZoneSQLite3
+#endif
+ )
+{
+ // Prepare the database first
+ const std::string test_db = TEST_DATA_BUILDDIR "/auth_test.sqlite3.copied";
+ std::stringstream ss("example.org. 3600 IN SOA . . 0 0 0 0 0\n");
+ createSQLite3DB(rrclass, Name("example.org"), test_db.c_str(), ss);
+ // This describes the data source in the configuration
+ const ConstElementPtr config(Element::fromJSON("{"
+ "\"IN\": [{"
+ " \"type\": \"sqlite3\","
+ " \"params\": {\"database_file\": \"" + test_db + "\"},"
+ " \"cache-enable\": true,"
+ " \"cache-zones\": [\"example.org\"]"
+ "}]}"));
+ clients_map = configureDataSource(config);
+
+ // Check that the A record at www.example.org does not exist
+ EXPECT_EQ(ZoneFinder::NXDOMAIN,
+ clients_map->find(rrclass)->second->
+ find(Name("example.org")).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);
+ sql_updater->addRRset(
+ *textToRRset("www.example.org. 60 IN A 192.0.2.1"));
+ sql_updater->commit();
+
+ EXPECT_EQ(ZoneFinder::NXDOMAIN,
+ clients_map->find(rrclass)->second->
+ find(Name("example.org")).finder_->
+ find(Name("www.example.org"), RRType::A())->code);
+
+ // Now send the command to reload it
+ const Command loadzone_cmd(LOADZONE, Element::fromJSON(
+ "{\"class\": \"IN\","
+ " \"origin\": \"example.org\"}"));
+ EXPECT_TRUE(builder.handleCommand(loadzone_cmd));
+ // And now it should be present too.
+ EXPECT_EQ(ZoneFinder::SUCCESS,
+ clients_map->find(rrclass)->second->
+ find(Name("example.org")).finder_->
+ find(Name("www.example.org"), RRType::A())->code);
+
+ // An error case: the zone has no configuration. (note .com here)
+ const Command nozone_cmd(LOADZONE, Element::fromJSON(
+ "{\"class\": \"IN\","
+ " \"origin\": \"example.com\"}"));
+ EXPECT_THROW(builder.handleCommand(nozone_cmd),
+ TestDataSrcClientsBuilder::InternalCommandError);
+ // The previous zone is not hurt in any way
+ EXPECT_EQ(ZoneFinder::SUCCESS, clients_map->find(rrclass)->second->
+ find(Name("example.org")).finder_->
+ find(Name("example.org"), RRType::SOA())->code);
+
+ // attempt of reloading a zone but in-memory cache is disabled.
+ const ConstElementPtr config2(Element::fromJSON("{"
+ "\"IN\": [{"
+ " \"type\": \"sqlite3\","
+ " \"params\": {\"database_file\": \"" + test_db + "\"},"
+ " \"cache-enable\": false,"
+ " \"cache-zones\": [\"example.org\"]"
+ "}]}"));
+ clients_map = configureDataSource(config2);
+ EXPECT_THROW(builder.handleCommand(
+ Command(LOADZONE, Element::fromJSON(
+ "{\"class\": \"IN\","
+ " \"origin\": \"example.org\"}"))),
+ TestDataSrcClientsBuilder::InternalCommandError);
+
+ // basically impossible case: in-memory cache is completely disabled.
+ // In this implementation of manager-builder, this should never happen,
+ // but it catches it like other configuration error and keeps going.
+ clients_map->clear();
+ boost::shared_ptr<ConfigurableClientList> nocache_list(
+ new ConfigurableClientList(rrclass));
+ nocache_list->configure(
+ Element::fromJSON(
+ "[{\"type\": \"sqlite3\","
+ " \"params\": {\"database_file\": \"" + test_db + "\"},"
+ " \"cache-enable\": true,"
+ " \"cache-zones\": [\"example.org\"]"
+ "}]"), false); // false = disable cache
+ (*clients_map)[rrclass] = nocache_list;
+ EXPECT_THROW(builder.handleCommand(
+ Command(LOADZONE, Element::fromJSON(
+ "{\"class\": \"IN\","
+ " \"origin\": \"example.org\"}"))),
+ TestDataSrcClientsBuilder::InternalCommandError);
+}
+
+TEST_F(DataSrcClientsBuilderTest, loadBrokenZone) {
+ configureZones();
+
+ ASSERT_EQ(0, std::system(INSTALL_PROG " -c " TEST_DATA_DIR
+ "/test1-broken.zone.in "
+ TEST_DATA_BUILDDIR "/test1.zone.copied"));
+ // there's an error in the new zone file. reload will be rejected.
+ const Command loadzone_cmd(LOADZONE, Element::fromJSON(
+ "{\"class\": \"IN\","
+ " \"origin\": \"test1.example\"}"));
+ EXPECT_THROW(builder.handleCommand(loadzone_cmd),
+ TestDataSrcClientsBuilder::InternalCommandError);
+ zoneChecks(clients_map, rrclass); // zone shouldn't be replaced
+}
+
+TEST_F(DataSrcClientsBuilderTest, loadUnreadableZone) {
+ configureZones();
+
+ // install the zone file as unreadable
+ ASSERT_EQ(0, std::system(INSTALL_PROG " -c -m 000 " TEST_DATA_DIR
+ "/test1.zone.in "
+ TEST_DATA_BUILDDIR "/test1.zone.copied"));
+ const Command loadzone_cmd(LOADZONE, Element::fromJSON(
+ "{\"class\": \"IN\","
+ " \"origin\": \"test1.example\"}"));
+ EXPECT_THROW(builder.handleCommand(loadzone_cmd),
+ TestDataSrcClientsBuilder::InternalCommandError);
+ zoneChecks(clients_map, rrclass); // zone shouldn't be replaced
+}
+
+TEST_F(DataSrcClientsBuilderTest, loadZoneWithoutDataSrc) {
+ // try to execute load command without configuring the zone beforehand.
+ // it should fail.
+ EXPECT_THROW(builder.handleCommand(
+ Command(LOADZONE,
+ Element::fromJSON(
+ "{\"class\": \"IN\", "
+ " \"origin\": \"test1.example\"}"))),
+ TestDataSrcClientsBuilder::InternalCommandError);
+}
+
+TEST_F(DataSrcClientsBuilderTest, loadZoneInvalidParams) {
+ configureZones();
+
+ if (!isc::util::unittests::runningOnValgrind()) {
+ // null arg: this causes assertion failure
+ EXPECT_DEATH_IF_SUPPORTED({
+ builder.handleCommand(Command(LOADZONE, ElementPtr()));
+ }, "");
+ }
+
+ // zone class is bogus (note that this shouldn't happen except in tests)
+ EXPECT_THROW(builder.handleCommand(
+ Command(LOADZONE,
+ Element::fromJSON(
+ "{\"origin\": \"test1.example\","
+ " \"class\": \"no_such_class\"}"))),
+ InvalidRRClass);
+
+ // not a string
+ EXPECT_THROW(builder.handleCommand(
+ Command(LOADZONE,
+ Element::fromJSON(
+ "{\"origin\": \"test1.example\","
+ " \"class\": 1}"))),
+ isc::data::TypeError);
+
+ // class or origin is missing: result in assertion failure
+ if (!isc::util::unittests::runningOnValgrind()) {
+ EXPECT_DEATH_IF_SUPPORTED({
+ builder.handleCommand(
+ Command(LOADZONE,
+ Element::fromJSON(
+ "{\"origin\": \"test1.example\"}")));
+ }, "");
+ EXPECT_DEATH_IF_SUPPORTED({
+ builder.handleCommand(Command(LOADZONE,
+ Element::fromJSON(
+ "{\"class\": \"IN\"}")));
+ }, "");
+ }
+
+ // zone doesn't exist in the data source
+ EXPECT_THROW(
+ builder.handleCommand(
+ Command(LOADZONE,
+ Element::fromJSON(
+ "{\"class\": \"IN\", \"origin\": \"xx\"}"))),
+ TestDataSrcClientsBuilder::InternalCommandError);
+
+ // origin is bogus
+ EXPECT_THROW(builder.handleCommand(
+ Command(LOADZONE,
+ Element::fromJSON(
+ "{\"class\": \"IN\", \"origin\": \"...\"}"))),
+ EmptyLabel);
+ EXPECT_THROW(builder.handleCommand(
+ Command(LOADZONE,
+ Element::fromJSON(
+ "{\"origin\": 10, \"class\": 1}"))),
+ isc::data::TypeError);
+}
+
} // unnamed namespace
diff --git a/src/bin/auth/tests/test_datasrc_clients_mgr.cc b/src/bin/auth/tests/test_datasrc_clients_mgr.cc
index 44c8b7d..82937c0 100644
--- a/src/bin/auth/tests/test_datasrc_clients_mgr.cc
+++ b/src/bin/auth/tests/test_datasrc_clients_mgr.cc
@@ -50,6 +50,8 @@ TestDataSrcClientsBuilder::doNoop() {
isc_throw(Exception, "test exception");
case TestMutex::INTEGER:
throw 42;
+ case TestMutex::INTERNAL:
+ isc_throw(InternalCommandError, "internal error, should be ignored");
}
}
} // namespace datasrc_clientmgr_internal
diff --git a/src/bin/auth/tests/test_datasrc_clients_mgr.h b/src/bin/auth/tests/test_datasrc_clients_mgr.h
index 4abffa2..9b1a367 100644
--- a/src/bin/auth/tests/test_datasrc_clients_mgr.h
+++ b/src/bin/auth/tests/test_datasrc_clients_mgr.h
@@ -43,7 +43,8 @@ public:
// None: no throw from specialized doNoop()
// EXCLASS: throw some exception class object
// INTEGER: throw an integer
- enum ExceptionFromNoop { NONE, EXCLASS, INTEGER };
+ // INTERNAL: internal error (shouldn't terminate the thread)
+ enum ExceptionFromNoop { NONE, EXCLASS, INTEGER, INTERNAL };
TestMutex() : lock_count(0), unlock_count(0), noop_count(0),
throw_from_noop(NONE)
More information about the bind10-changes
mailing list