d2_controller_unittests.cc 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321
  1. // Copyright (C) 2013-2015 Internet Systems Consortium, Inc. ("ISC")
  2. //
  3. // This Source Code Form is subject to the terms of the Mozilla Public
  4. // License, v. 2.0. If a copy of the MPL was not distributed with this
  5. // file, You can obtain one at http://mozilla.org/MPL/2.0/.
  6. #include <config.h>
  7. #include <cc/command_interpreter.h>
  8. #include <d_test_stubs.h>
  9. #include <d2/d2_controller.h>
  10. #include <d2/d2_process.h>
  11. #include <d2/spec_config.h>
  12. #include <boost/pointer_cast.hpp>
  13. #include <boost/date_time/posix_time/posix_time.hpp>
  14. #include <gtest/gtest.h>
  15. #include <sstream>
  16. using namespace boost::posix_time;
  17. namespace isc {
  18. namespace d2 {
  19. /// @brief Test fixture class for testing D2Controller class. This class
  20. /// derives from DControllerTest and wraps a D2Controller. Much of the
  21. /// underlying functionality is in the DControllerBase class which has an
  22. /// extensive set of unit tests that are independent of DHCP-DDNS.
  23. /// @TODO Currently These tests are relatively light and duplicate some of
  24. /// the testing done on the base class. These tests are sufficient to ensure
  25. /// that D2Controller properly derives from its base class and to test the
  26. /// logic that is unique to D2Controller. These tests will be augmented and
  27. /// or new tests added as additional functionality evolves.
  28. /// Unlike the stub testing, there is no use of SimFailure to induce error
  29. /// conditions as this is production code.
  30. class D2ControllerTest : public DControllerTest {
  31. public:
  32. /// @brief Constructor
  33. /// Note the constructor passes in the static D2Controller instance
  34. /// method.
  35. D2ControllerTest() : DControllerTest(D2Controller::instance) {
  36. }
  37. /// @brief Fetches the D2Controller's D2Process
  38. ///
  39. /// @return A pointer to the process which may be null if it has not yet
  40. /// been instantiated.
  41. D2ProcessPtr getD2Process() {
  42. return (boost::dynamic_pointer_cast<D2Process>(getProcess()));
  43. }
  44. /// @brief Fetches the D2Process's D2Configuration manager
  45. ///
  46. /// @return A pointer to the manager which may be null if it has not yet
  47. /// been instantiated.
  48. D2CfgMgrPtr getD2CfgMgr() {
  49. D2CfgMgrPtr p;
  50. if (getD2Process()) {
  51. p = getD2Process()->getD2CfgMgr();
  52. }
  53. return (p);
  54. }
  55. /// @brief Fetches the D2Configuration manager's D2CfgContext
  56. ///
  57. /// @return A pointer to the context which may be null if it has not yet
  58. /// been instantiated.
  59. D2CfgContextPtr getD2CfgContext() {
  60. D2CfgContextPtr p;
  61. if (getD2CfgMgr()) {
  62. p = getD2CfgMgr()->getD2CfgContext();
  63. }
  64. return (p);
  65. }
  66. };
  67. /// @brief Basic Controller instantiation testing.
  68. /// Verifies that the controller singleton gets created and that the
  69. /// basic derivation from the base class is intact.
  70. TEST_F(D2ControllerTest, basicInstanceTesting) {
  71. // Verify the we can the singleton instance can be fetched and that
  72. // it is the correct type.
  73. DControllerBasePtr& controller = DControllerTest::getController();
  74. ASSERT_TRUE(controller);
  75. ASSERT_NO_THROW(boost::dynamic_pointer_cast<D2Controller>(controller));
  76. // Verify that controller's app name is correct.
  77. EXPECT_TRUE(checkAppName(D2Controller::d2_app_name_));
  78. // Verify that controller's bin name is correct.
  79. EXPECT_TRUE(checkBinName(D2Controller::d2_bin_name_));
  80. // Verify that controller's spec file name is correct.
  81. EXPECT_TRUE(checkSpecFileName(D2_SPECFILE_LOCATION));
  82. // Verify that controller's IOService exists.
  83. EXPECT_TRUE(checkIOService());
  84. // Verify that the Process does NOT exist.
  85. EXPECT_FALSE(checkProcess());
  86. }
  87. /// @brief Tests basic command line processing.
  88. /// Verifies that:
  89. /// 1. Standard command line options are supported.
  90. /// 2. Invalid options are detected.
  91. TEST_F(D2ControllerTest, commandLineArgs) {
  92. char* argv[] = { const_cast<char*>("progName"),
  93. const_cast<char*>("-c"),
  94. const_cast<char*>(DControllerTest::CFG_TEST_FILE),
  95. const_cast<char*>("-d") };
  96. int argc = 4;
  97. // Verify that verbose flag is false initially.
  98. EXPECT_TRUE(checkVerbose(false));
  99. // Verify that standard options can be parsed without error.
  100. EXPECT_NO_THROW(parseArgs(argc, argv));
  101. // Verify that verbose flag is true.
  102. EXPECT_TRUE(checkVerbose(true));
  103. // Verify configuration file name is correct.
  104. EXPECT_TRUE(checkConfigFileName(DControllerTest::CFG_TEST_FILE));
  105. // Verify that an unknown option is detected.
  106. char* argv2[] = { const_cast<char*>("progName"),
  107. const_cast<char*>("-x") };
  108. argc = 2;
  109. EXPECT_THROW(parseArgs(argc, argv2), InvalidUsage);
  110. }
  111. /// @brief Tests application process creation and initialization.
  112. /// Verifies that the process can be successfully created and initialized.
  113. TEST_F(D2ControllerTest, initProcessTesting) {
  114. ASSERT_NO_THROW(initProcess());
  115. EXPECT_TRUE(checkProcess());
  116. }
  117. /// @brief Tests launch and normal shutdown (stand alone mode).
  118. /// This creates an interval timer to generate a normal shutdown and then
  119. /// launches with a valid, stand-alone command line and no simulated errors.
  120. TEST_F(D2ControllerTest, launchNormalShutdown) {
  121. // Write valid_d2_config and then run launch() for 1000 ms.
  122. time_duration elapsed_time;
  123. runWithConfig(valid_d2_config, 1000, elapsed_time);
  124. // Give a generous margin to accomodate slower test environs.
  125. EXPECT_TRUE(elapsed_time.total_milliseconds() >= 800 &&
  126. elapsed_time.total_milliseconds() <= 1300);
  127. }
  128. /// @brief Configuration update event testing.
  129. /// This really tests just the ability of the handlers to invoke the necessary
  130. /// chain of methods and handle error conditions. Configuration parsing and
  131. /// retrieval should be tested as part of the d2 configuration management
  132. /// implementation.
  133. /// This test verifies that:
  134. /// 1. A valid configuration yields a successful parse result.
  135. /// 2. That an application process error in configuration updating is handled
  136. /// properly.
  137. TEST_F(D2ControllerTest, configUpdateTests) {
  138. int rcode = -1;
  139. isc::data::ConstElementPtr answer;
  140. // Initialize the application process.
  141. ASSERT_NO_THROW(initProcess());
  142. EXPECT_TRUE(checkProcess());
  143. // Create a configuration set using a small, valid D2 configuration.
  144. isc::data::ElementPtr config_set =
  145. isc::data::Element::fromJSON(valid_d2_config);
  146. // Verify that given a valid config we get a successful update result.
  147. answer = updateConfig(config_set);
  148. isc::config::parseAnswer(rcode, answer);
  149. EXPECT_EQ(0, rcode);
  150. // Use an invalid configuration to verify parsing error return.
  151. std::string config = "{ \"bogus\": 1000 } ";
  152. config_set = isc::data::Element::fromJSON(config);
  153. answer = updateConfig(config_set);
  154. isc::config::parseAnswer(rcode, answer);
  155. EXPECT_EQ(1, rcode);
  156. }
  157. /// @brief Command execution tests.
  158. /// This really tests just the ability of the handler to invoke the necessary
  159. /// chain of methods and to handle error conditions.
  160. /// This test verifies that:
  161. /// 1. That an unrecognized command is detected and returns a status of
  162. /// d2::COMMAND_INVALID.
  163. /// 2. Shutdown command is recognized and returns a d2::COMMAND_SUCCESS status.
  164. TEST_F(D2ControllerTest, executeCommandTests) {
  165. int rcode = -1;
  166. isc::data::ConstElementPtr answer;
  167. isc::data::ElementPtr arg_set;
  168. // Initialize the application process.
  169. ASSERT_NO_THROW(initProcess());
  170. EXPECT_TRUE(checkProcess());
  171. // Verify that an unknown command returns an COMMAND_INVALID response.
  172. std::string bogus_command("bogus");
  173. answer = executeCommand(bogus_command, arg_set);
  174. isc::config::parseAnswer(rcode, answer);
  175. EXPECT_EQ(COMMAND_INVALID, rcode);
  176. // Verify that shutdown command returns COMMAND_SUCCESS response.
  177. //answer = executeCommand(SHUT_DOWN_COMMAND, isc::data::ElementPtr());
  178. answer = executeCommand(SHUT_DOWN_COMMAND, arg_set);
  179. isc::config::parseAnswer(rcode, answer);
  180. EXPECT_EQ(COMMAND_SUCCESS, rcode);
  181. }
  182. // Tests that the original configuration is retained after a SIGHUP triggered
  183. // reconfiguration fails due to invalid config content.
  184. TEST_F(D2ControllerTest, invalidConfigReload) {
  185. // Schedule to replace the configuration file after launch. This way the
  186. // file is updated after we have done the initial configuration.
  187. scheduleTimedWrite("{ \"string_test\": BOGUS JSON }", 100);
  188. // Setup to raise SIGHUP in 200 ms.
  189. TimedSignal sighup(*getIOService(), SIGHUP, 200);
  190. // Write valid_d2_config and then run launch() for a maximum of 500 ms.
  191. time_duration elapsed_time;
  192. runWithConfig(valid_d2_config, 500, elapsed_time);
  193. // Context is still available post launch.
  194. // Check to see that our configuration matches the original per
  195. // valid_d2_config (see d_test_stubs.cc)
  196. D2CfgMgrPtr d2_cfg_mgr = getD2CfgMgr();
  197. D2ParamsPtr d2_params = d2_cfg_mgr->getD2Params();
  198. ASSERT_TRUE(d2_params);
  199. EXPECT_EQ("127.0.0.1", d2_params->getIpAddress().toText());
  200. EXPECT_EQ(5031, d2_params->getPort());
  201. EXPECT_TRUE(d2_cfg_mgr->forwardUpdatesEnabled());
  202. EXPECT_TRUE(d2_cfg_mgr->reverseUpdatesEnabled());
  203. /// @todo add a way to trap log file and search it
  204. }
  205. // Tests that the original configuration is replaced after a SIGHUP triggered
  206. // reconfiguration succeeds.
  207. TEST_F(D2ControllerTest, validConfigReload) {
  208. // Define a replacement config.
  209. const char* second_cfg =
  210. "{"
  211. " \"ip-address\": \"192.168.77.1\" , "
  212. " \"port\": 777 , "
  213. "\"tsig-keys\": [], "
  214. "\"forward-ddns\" : {}, "
  215. "\"reverse-ddns\" : {} "
  216. "}";
  217. // Schedule to replace the configuration file after launch. This way the
  218. // file is updated after we have done the initial configuration.
  219. scheduleTimedWrite(second_cfg, 100);
  220. // Setup to raise SIGHUP in 200 ms.
  221. TimedSignal sighup(*getIOService(), SIGHUP, 200);
  222. // Write valid_d2_config and then run launch() for a maximum of 500ms.
  223. time_duration elapsed_time;
  224. runWithConfig(valid_d2_config, 500, elapsed_time);
  225. // Context is still available post launch.
  226. // Check to see that our configuration matches the replacement config.
  227. D2CfgMgrPtr d2_cfg_mgr = getD2CfgMgr();
  228. D2ParamsPtr d2_params = d2_cfg_mgr->getD2Params();
  229. ASSERT_TRUE(d2_params);
  230. EXPECT_EQ("192.168.77.1", d2_params->getIpAddress().toText());
  231. EXPECT_EQ(777, d2_params->getPort());
  232. EXPECT_FALSE(d2_cfg_mgr->forwardUpdatesEnabled());
  233. EXPECT_FALSE(d2_cfg_mgr->reverseUpdatesEnabled());
  234. /// @todo add a way to trap log file and search it
  235. }
  236. // Tests that the SIGINT triggers a normal shutdown.
  237. TEST_F(D2ControllerTest, sigintShutdown) {
  238. // Setup to raise SIGHUP in 1 ms.
  239. TimedSignal sighup(*getIOService(), SIGINT, 1);
  240. // Write valid_d2_config and then run launch() for a maximum of 1000 ms.
  241. time_duration elapsed_time;
  242. runWithConfig(valid_d2_config, 1000, elapsed_time);
  243. // Signaled shutdown should make our elapsed time much smaller than
  244. // the maximum run time. Give generous margin to accomodate slow
  245. // test environs.
  246. EXPECT_TRUE(elapsed_time.total_milliseconds() < 300);
  247. /// @todo add a way to trap log file and search it
  248. }
  249. // Tests that the SIGTERM triggers a normal shutdown.
  250. TEST_F(D2ControllerTest, sigtermShutdown) {
  251. // Setup to raise SIGHUP in 1 ms.
  252. TimedSignal sighup(*getIOService(), SIGTERM, 1);
  253. // Write valid_d2_config and then run launch() for a maximum of 1 s.
  254. time_duration elapsed_time;
  255. runWithConfig(valid_d2_config, 1000, elapsed_time);
  256. // Signaled shutdown should make our elapsed time much smaller than
  257. // the maximum run time. Give generous margin to accomodate slow
  258. // test environs.
  259. EXPECT_TRUE(elapsed_time.total_milliseconds() < 300);
  260. /// @todo add a way to trap log file and search it
  261. }
  262. }; // end of isc::d2 namespace
  263. }; // end of isc namespace