AC_CONFIG_FILES([src/bin/d2/tests/Makefile])
AC_CONFIG_FILES([src/bin/d2/tests/d2_process_tests.sh],
[chmod +x src/bin/d2/tests/d2_process_tests.sh])
-AC_CONFIG_FILES([src/bin/d2/tests/test_libraries.h])
+AC_CONFIG_FILES([src/bin/d2/tests/test_callout_libraries.h])
+AC_CONFIG_FILES([src/bin/d2/tests/test_configured_libraries.h])
AC_CONFIG_FILES([src/bin/d2/tests/test_data_files_config.h])
AC_CONFIG_FILES([src/bin/dhcp4/Makefile])
AC_CONFIG_FILES([src/bin/dhcp4/tests/Makefile])
// The config can be rejected by a hook.
if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_DROP) {
callout_handle->getArgument("error", error);
+ LOG_ERROR(d2_logger, DHCP_DDNS_CONFIGURED_CALLOUT_DROP)
+ .arg(error);
reconf_queue_flag_ = false;
answer = isc::config::createAnswer(1, error);
return (answer);
/d2_process_tests.sh
/d2_unittests
/test_data_files_config.h
-/test_libraries.h
+/test_callout_libraries.h
+/test_configured_libraries.h
libcallout_la_LIBADD += $(top_builddir)/src/lib/log/libkea-log.la
libcallout_la_LDFLAGS = -avoid-version -export-dynamic -module -rpath /nowhere
-noinst_LTLIBRARIES = libcallout.la
+# The d2_srv_configured callout library
+libconfigured_la_SOURCES = configured_library.cc
+libconfigured_la_CXXFLAGS = $(AM_CXXFLAGS)
+libconfigured_la_CPPFLAGS = $(AM_CPPFLAGS)
+libconfigured_la_LIBADD = $(top_builddir)/src/lib/hooks/libkea-hooks.la
+libconfigured_la_LIBADD += $(top_builddir)/src/lib/cc/libkea-cc.la
+libconfigured_la_LIBADD += $(top_builddir)/src/lib/asiolink/libkea-asiolink.la
+libconfigured_la_LIBADD += $(top_builddir)/src/lib/log/libkea-log.la
+libconfigured_la_LIBADD += $(top_builddir)/src/lib/util/libkea-util.la
+libconfigured_la_LIBADD += $(top_builddir)/src/lib/exceptions/libkea-exceptions.la
+libconfigured_la_LIBADD += $(LOG4CPLUS_LIBS) $(BOOST_LIBS)
+libconfigured_la_LDFLAGS = -avoid-version -export-dynamic -module -rpath /nowhere
+
+noinst_LTLIBRARIES = libcallout.la libconfigured.la
nodist_d2_unittests_SOURCES =
nodist_d2_unittests_SOURCES += test_data_files_config.h
-nodist_d2_unittests_SOURCES += test_libraries.h
+nodist_d2_unittests_SOURCES += test_callout_libraries.h
+nodist_d2_unittests_SOURCES += test_configured_libraries.h
# Run C++ tests on "make check".
TESTS += $(PROGRAM_TESTS)
# "make distclean", but not on "make clean".
DISTCLEANFILES += d2_process_tests.sh
DISTCLEANFILES += test_data_files_config.h
-DISTCLEANFILES += test_libraries.h
+DISTCLEANFILES += test_callout_libraries.h
+DISTCLEANFILES += test_configured_libraries.h
# Don't install C++ tests.
noinst_PROGRAMS = $(PROGRAM_TESTS)
--- /dev/null
+// Copyright (C) 2021 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+/// @brief d2_srv_configured callout testing library
+
+#include <config.h>
+#include <cc/data.h>
+#include <hooks/hooks.h>
+
+using namespace isc::data;
+using namespace isc::hooks;
+using namespace std;
+
+namespace {
+
+extern "C" {
+
+// d2_srv_configured callout.
+int
+d2_srv_configured(CalloutHandle& handle) {
+ // Get the parameters.
+ ConstElementPtr cfg;
+ string error;
+ handle.getArgument("json_config", cfg);
+ handle.getArgument("error", error);
+
+ if (cfg) {
+ ConstElementPtr uc = cfg->get("user-context");
+ if (uc) {
+ ConstElementPtr msg = uc->get("error");
+ if (msg && (msg->getType() == Element::string)) {
+ error = msg->stringValue();
+ }
+ }
+ }
+
+ if (!error.empty()) {
+ handle.setArgument("error", error);
+ handle.setStatus(CalloutHandle::NEXT_STEP_DROP);
+ }
+ return (0);
+}
+
+// Framework functions.
+int
+version() {
+ return (KEA_HOOKS_VERSION);
+}
+
+// load() initializes the user library if the main image was statically linked.
+int
+load(isc::hooks::LibraryHandle&) {
+#ifdef USE_STATIC_LINK
+ hooksStaticLinkInit();
+#endif
+ return (0);
+}
+
+}
+}
#include <d2/parser_context.h>
#include <d2/tests/parser_unittest.h>
-#include <d2/tests/test_libraries.h>
+#include <d2/tests/test_callout_libraries.h>
#include <d2srv/d2_cfg_mgr.h>
#include <d2srv/d2_config.h>
#include <d2srv/d2_simple_parser.h>
#include <dhcp_ddns/ncr_io.h>
#include <process/testutils/d_test_stubs.h>
#include <d2/tests/nc_test_utils.h>
+#include <d2/tests/test_configured_libraries.h>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <gtest/gtest.h>
using namespace isc;
using namespace isc::config;
using namespace isc::d2;
+using namespace isc::data;
using namespace isc::process;
using namespace boost::posix_time;
/// @brief Callback that will invoke shutdown method.
void genShutdownCallback() {
- shutdown(isc::data::ConstElementPtr());
+ shutdown(ConstElementPtr());
}
/// @brief Callback that throws an exception.
return res;
}
- isc::data::ConstElementPtr answer = configure(config_set_, false);
- isc::data::ConstElementPtr comment;
+ ConstElementPtr answer = configure(config_set_, false);
+ ConstElementPtr comment;
comment = isc::config::parseAnswer(rcode, answer);
if (rcode) {
setShutdownType(shutdown_type);
return (canShutdown());
}
+
+ /// @brief Replaces %LIBRARY% with specified library name.
+ ///
+ /// @param config input config text (should contain "%LIBRARY%" string).
+ /// @param lib_name %LIBRARY% will be replaced with that name.
+ /// @return configuration text with library name replaced.
+ string pathReplacer(const char* config, const char* lib_name) {
+ string txt(config);
+ txt.replace(txt.find("%LIBRARY%"), strlen("%LIBRARY%"), string(lib_name));
+ return (txt);
+ }
};
/// @brief Verifies D2Process construction behavior.
ASSERT_TRUE(fromJSON(valid_d2_config));
// Invoke configure() with a valid D2 configuration.
- isc::data::ConstElementPtr answer = configure(config_set_, false);
+ ConstElementPtr answer = configure(config_set_, false);
// Verify that configure result is success and reconfigure queue manager
// flag is true.
ASSERT_EQ(D2QueueMgr::STOPPED, queue_mgr->getMgrState());
}
-
/// @brief Tests checkQueueStatus() logic for recovering from queue full
/// This test manually creates a receive queue full condition and then
/// "drains" the queue until the queue manager resumes listening. This
// Invoke configure() with a valid config that contains an unusable IP
ASSERT_TRUE(fromJSON(bad_ip_d2_config));
- isc::data::ConstElementPtr answer = configure(config_set_, false);
+ ConstElementPtr answer = configure(config_set_, false);
// Verify that configure result is success and reconfigure queue manager
// flag is true.
/// success response; and for invalid values: sets the shutdown flag to false
/// and returns a failure response.
TEST_F(D2ProcessTest, shutdownArgs) {
- isc::data::ElementPtr args;
- isc::data::ConstElementPtr answer;
+ ElementPtr args;
+ ConstElementPtr answer;
const char* default_args = "{}";
const char* normal_args = "{ \"type\" : \"normal\" }";
const char* drain_args = "{ \"type\" : \"drain_first\" }";
const char* bogus_args = "{ \"type\" : \"bogus\" }";
// Verify defaulting to SD_NORMAL if no argument is given.
- ASSERT_NO_THROW(args = isc::data::Element::fromJSON(default_args));
+ ASSERT_NO_THROW(args = Element::fromJSON(default_args));
EXPECT_NO_THROW(answer = shutdown(args));
ASSERT_TRUE(checkAnswer(answer, 0));
EXPECT_EQ(SD_NORMAL, getShutdownType());
EXPECT_TRUE(shouldShutdown());
// Verify argument value "normal".
- ASSERT_NO_THROW(args = isc::data::Element::fromJSON(normal_args));
+ ASSERT_NO_THROW(args = Element::fromJSON(normal_args));
EXPECT_NO_THROW(answer = shutdown(args));
ASSERT_TRUE(checkAnswer(answer, 0));
EXPECT_EQ(SD_NORMAL, getShutdownType());
EXPECT_TRUE(shouldShutdown());
// Verify argument value "drain_first".
- ASSERT_NO_THROW(args = isc::data::Element::fromJSON(drain_args));
+ ASSERT_NO_THROW(args = Element::fromJSON(drain_args));
EXPECT_NO_THROW(answer = shutdown(args));
ASSERT_TRUE(checkAnswer(answer, 0));
EXPECT_EQ(SD_DRAIN_FIRST, getShutdownType());
EXPECT_TRUE(shouldShutdown());
// Verify argument value "now".
- ASSERT_NO_THROW(args = isc::data::Element::fromJSON(now_args));
+ ASSERT_NO_THROW(args = Element::fromJSON(now_args));
EXPECT_NO_THROW(answer = shutdown(args));
ASSERT_TRUE(checkAnswer(answer, 0));
EXPECT_EQ(SD_NOW, getShutdownType());
EXPECT_TRUE(shouldShutdown());
// Verify correct handling of an invalid value.
- ASSERT_NO_THROW(args = isc::data::Element::fromJSON(bogus_args));
+ ASSERT_NO_THROW(args = Element::fromJSON(bogus_args));
EXPECT_NO_THROW(answer = shutdown(args));
ASSERT_TRUE(checkAnswer(answer, 1));
EXPECT_FALSE(shouldShutdown());
EXPECT_TRUE(checkCanShutdown(SD_NOW));
}
-
/// @brief Verifies that an "external" call to shutdown causes the run method
/// to exit gracefully.
TEST_F(D2ProcessTest, normalShutdown) {
elapsed.total_milliseconds() <= 2200);
}
-
/// @brief Verifies that an "uncaught" exception thrown during event loop
/// execution is treated as a fatal error.
TEST_F(D2ProcessTest, fatalErrorShutdown) {
runWithConfig(config);
}
-
/// @brief Used to permit visual inspection of logs to ensure
/// DHCP_DDNS_NOT_ON_LOOPBACK is not issued.
TEST_F(D2ProcessTest, v4LoopbackTest) {
ASSERT_TRUE(runWithConfig(config));
}
+/// @brief Check the configured callout (positive case).
+TEST_F(D2ProcessTest, configuredNoFail) {
+ const char* config = "{\n"
+ "\"hooks-libraries\": [ {\n"
+ " \"library\": \"%LIBRARY%\",\n"
+ " \"parameters\": {\n"
+ " } } ] }\n";
+ string cfg = pathReplacer(config, CONFIGURED_LIBRARY);
+
+ ConstElementPtr json;
+ ASSERT_NO_THROW(json = Element::fromJSON(cfg));
+ ConstElementPtr answer;
+ ASSERT_NO_THROW(answer = configure(json, false));
+ int rcode = -1;
+ ConstElementPtr comment;
+ comment = isc::config::parseAnswer(rcode, answer);
+ EXPECT_EQ(0, rcode) << comment->str();
+}
+
+/// @brief Check the configured callout (negative case).
+TEST_F(D2ProcessTest, configuredFail) {
+ const char* config = "{\n"
+ "\"user-context\": { \"error\": \"Fail!\" },\n"
+ "\"hooks-libraries\": [ {\n"
+ " \"library\": \"%LIBRARY%\",\n"
+ " \"parameters\": {\n"
+ " } } ] }\n";
+ string cfg = pathReplacer(config, CONFIGURED_LIBRARY);
+
+ ConstElementPtr json;
+ ASSERT_NO_THROW(json = Element::fromJSON(cfg));
+ ConstElementPtr answer;
+ ASSERT_NO_THROW(answer = configure(json, false));
+ int rcode = -1;
+ ConstElementPtr comment;
+ comment = isc::config::parseAnswer(rcode, answer);
+ EXPECT_EQ(1, rcode);
+ ASSERT_TRUE(comment);
+ ASSERT_EQ(Element::string, comment->getType());
+ EXPECT_EQ("Fail!", comment->stringValue());
+}
+
} // end of anonymous namespace
#include <sstream>
#include "test_data_files_config.h"
-#include "test_libraries.h"
+#include "test_callout_libraries.h"
using namespace isc::config;
using namespace isc::d2;
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
-#ifndef D2_TEST_LIBRARIES_H
-#define D2_TEST_LIBRARIES_H
+#ifndef D2_TEST_CALLOUT_LIBRARIES_H
+#define D2_TEST_CALLOUT_LIBRARIES_H
#include <config.h>
} // anonymous namespace
-#endif // D2_TEST_LIBRARIES_H
+#endif // D2_TEST_CALLOUT_LIBRARIES_H
--- /dev/null
+// Copyright (C) 2021 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#ifndef D2_TEST_CONFIGURED_LIBRARIES_H
+#define D2_TEST_CONFIGURED_LIBRARIES_H
+
+#include <config.h>
+
+namespace {
+
+// Names of the libraries used in these tests. These libraries are built using
+// libtool, so we need to look in the hidden ".libs" directory to locate the
+// .so file. Note that we access the .so file - libtool creates this as a
+// like to the real shared library.
+
+// Configured library with d2_srv_configured testing: if there is a toplevel
+// user context with an error entry the returned status is DROP with the
+// content of the error entry.
+static const char* CONFIGURED_LIBRARY = "@abs_builddir@/.libs/libconfigured.so";
+
+} // anonymous namespace
+
+#endif // D2_TEST_CONFIGURED_LIBRARIES_H
extern const isc::log::MessageID DHCP_DDNS_CLEARED_FOR_SHUTDOWN = "DHCP_DDNS_CLEARED_FOR_SHUTDOWN";
extern const isc::log::MessageID DHCP_DDNS_COMMAND = "DHCP_DDNS_COMMAND";
extern const isc::log::MessageID DHCP_DDNS_CONFIGURE = "DHCP_DDNS_CONFIGURE";
+extern const isc::log::MessageID DHCP_DDNS_CONFIGURED_CALLOUT_DROP = "DHCP_DDNS_CONFIGURED_CALLOUT_DROP";
extern const isc::log::MessageID DHCP_DDNS_CONFIG_CHECK_FAIL = "DHCP_DDNS_CONFIG_CHECK_FAIL";
extern const isc::log::MessageID DHCP_DDNS_CONFIG_FAIL = "DHCP_DDNS_CONFIG_FAIL";
extern const isc::log::MessageID DHCP_DDNS_FAILED = "DHCP_DDNS_FAILED";
"DHCP_DDNS_CLEARED_FOR_SHUTDOWN", "application has met shutdown criteria for shutdown type: %1",
"DHCP_DDNS_COMMAND", "command directive received, command: %1 - args: %2",
"DHCP_DDNS_CONFIGURE", "configuration %1 received: %2",
+ "DHCP_DDNS_CONFIGURED_CALLOUT_DROP", "configuration was rejected because a callout set the next step to 'drop': %1",
"DHCP_DDNS_CONFIG_CHECK_FAIL", "DHCP-DDNS server configuration check failed: %1",
"DHCP_DDNS_CONFIG_FAIL", "DHCP-DDNS server configuration failed: %1",
"DHCP_DDNS_FAILED", "application experienced a fatal error: %1",
extern const isc::log::MessageID DHCP_DDNS_CLEARED_FOR_SHUTDOWN;
extern const isc::log::MessageID DHCP_DDNS_COMMAND;
extern const isc::log::MessageID DHCP_DDNS_CONFIGURE;
+extern const isc::log::MessageID DHCP_DDNS_CONFIGURED_CALLOUT_DROP;
extern const isc::log::MessageID DHCP_DDNS_CONFIG_CHECK_FAIL;
extern const isc::log::MessageID DHCP_DDNS_CONFIG_FAIL;
extern const isc::log::MessageID DHCP_DDNS_FAILED;
This is a debug message issued when the DHCP-DDNS application configure method
has been invoked.
+% DHCP_DDNS_CONFIGURED_CALLOUT_DROP configuration was rejected because a callout set the next step to 'drop': %1
+This error message indicates that the DHCP-DDNS had failed configuration
+attempt because the next stop of the configured callout was set to 'drop'
+by a hook library. The error message provided by the hook library is displayed.
+
% DHCP_DDNS_CONFIG_CHECK_FAIL DHCP-DDNS server configuration check failed: %1
This error message indicates that the DHCP-DDNS had failed configuration
check. Details are provided. Additional details may be available