Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
ISC Open Source Projects
Kea
Commits
9281b972
Commit
9281b972
authored
Oct 29, 2012
by
JINMEI Tatuya
Browse files
[master] Merge branch 'trac2212-2'
parents
61fc577d
cf72f036
Changes
5
Hide whitespace changes
Inline
Side-by-side
src/bin/auth/auth_messages.mes
View file @
9281b972
...
...
@@ -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
...
...
src/bin/auth/datasrc_clients_mgr.h
View file @
9281b972
...
...
@@ -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.
...
...
src/bin/auth/tests/datasrc_clients_builder_unittest.cc
View file @
9281b972
...
...
@@ -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
\"
}"
))),