d_controller_unittests.cc 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470
  1. // Copyright (C) 2013-2014 Internet Systems Consortium, Inc. ("ISC")
  2. //
  3. // Permission to use, copy, modify, and/or distribute this software for any
  4. // purpose with or without fee is hereby granted, provided that the above
  5. // copyright notice and this permission notice appear in all copies.
  6. //
  7. // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
  8. // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
  9. // AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
  10. // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  11. // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
  12. // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  13. // PERFORMANCE OF THIS SOFTWARE.
  14. #include <config/ccsession.h>
  15. #include <d_test_stubs.h>
  16. #include <d2/spec_config.h>
  17. #include <boost/date_time/posix_time/posix_time.hpp>
  18. #include <gtest/gtest.h>
  19. #include <config.h>
  20. #include <sstream>
  21. using namespace boost::posix_time;
  22. namespace isc {
  23. namespace d2 {
  24. /// @brief Test fixture class for testing DControllerBase class. This class
  25. /// derives from DControllerTest and wraps a DStubController. DStubController
  26. /// has been constructed to exercise DControllerBase.
  27. class DStubControllerTest : public DControllerTest {
  28. public:
  29. /// @brief Constructor.
  30. /// Note the constructor passes in the static DStubController instance
  31. /// method.
  32. DStubControllerTest() : DControllerTest (DStubController::instance) {
  33. controller_ = boost::dynamic_pointer_cast<DStubController>
  34. (DControllerTest::
  35. getController());
  36. }
  37. virtual ~DStubControllerTest() {
  38. }
  39. DStubControllerPtr controller_;
  40. };
  41. /// @brief Basic Controller instantiation testing.
  42. /// Verfies that the controller singleton gets created and that the
  43. /// basic derivation from the base class is intact.
  44. TEST_F(DStubControllerTest, basicInstanceTesting) {
  45. // Verify that the singleton exists and it is the correct type.
  46. DControllerBasePtr& controller = DControllerTest::getController();
  47. ASSERT_TRUE(controller);
  48. ASSERT_NO_THROW(boost::dynamic_pointer_cast<DStubController>(controller));
  49. // Verify that controller's app name is correct.
  50. EXPECT_TRUE(checkAppName(DStubController::stub_app_name_));
  51. // Verify that controller's bin name is correct.
  52. EXPECT_TRUE(checkBinName(DStubController::stub_bin_name_));
  53. // Verify that controller's spec file name is correct.
  54. EXPECT_TRUE(checkSpecFileName(D2_SPECFILE_LOCATION));
  55. // Verify that controller's IOService exists.
  56. EXPECT_TRUE(checkIOService());
  57. // Verify that the Process does NOT exist.
  58. EXPECT_FALSE(checkProcess());
  59. }
  60. /// @brief Tests basic command line processing.
  61. /// Verifies that:
  62. /// 1. Standard command line options are supported.
  63. /// 2. Custom command line options are supported.
  64. /// 3. Invalid options are detected.
  65. /// 4. Extraneous command line information is detected.
  66. TEST_F(DStubControllerTest, commandLineArgs) {
  67. // Verify that verbose flag is false initially.
  68. EXPECT_TRUE(checkVerbose(false));
  69. // Verify that standard options can be parsed without error.
  70. char* argv[] = { const_cast<char*>("progName"),
  71. const_cast<char*>("-c"),
  72. const_cast<char*>("cfgName"),
  73. const_cast<char*>("-d") };
  74. int argc = 4;
  75. EXPECT_NO_THROW(parseArgs(argc, argv));
  76. // Verify that verbose is true.
  77. EXPECT_TRUE(checkVerbose(true));
  78. // Verify configuration file name is correct
  79. EXPECT_TRUE(checkConfigFileName("cfgName"));
  80. // Verify that the custom command line option is parsed without error.
  81. char xopt[3] = "- ";
  82. xopt[1] = *DStubController::stub_option_x_;
  83. char* argv1[] = { const_cast<char*>("progName"), xopt};
  84. argc = 2;
  85. EXPECT_NO_THROW (parseArgs(argc, argv1));
  86. // Verify that an unknown option is detected.
  87. char* argv2[] = { const_cast<char*>("progName"),
  88. const_cast<char*>("-bs") };
  89. argc = 2;
  90. EXPECT_THROW (parseArgs(argc, argv2), InvalidUsage);
  91. // Verify that extraneous information is detected.
  92. char* argv3[] = { const_cast<char*>("progName"),
  93. const_cast<char*>("extra"),
  94. const_cast<char*>("information") };
  95. argc = 3;
  96. EXPECT_THROW (parseArgs(argc, argv3), InvalidUsage);
  97. }
  98. /// @brief Tests application process creation and initialization.
  99. /// Verifies that:
  100. /// 1. An error during process creation is handled.
  101. /// 2. A NULL returned by process creation is handled.
  102. /// 3. An error during process initialization is handled.
  103. /// 4. Process can be successfully created and initialized.
  104. TEST_F(DStubControllerTest, initProcessTesting) {
  105. // Verify that a failure during process creation is caught.
  106. SimFailure::set(SimFailure::ftCreateProcessException);
  107. EXPECT_THROW(initProcess(), DControllerBaseError);
  108. EXPECT_FALSE(checkProcess());
  109. // Verify that a NULL returned by process creation is handled.
  110. SimFailure::set(SimFailure::ftCreateProcessNull);
  111. EXPECT_THROW(initProcess(), DControllerBaseError);
  112. EXPECT_FALSE(checkProcess());
  113. // Re-create controller, verify that we are starting clean
  114. resetController();
  115. EXPECT_FALSE(checkProcess());
  116. // Verify that an error during process initialization is handled.
  117. SimFailure::set(SimFailure::ftProcessInit);
  118. EXPECT_THROW(initProcess(), DProcessBaseError);
  119. // Re-create controller, verify that we are starting clean
  120. resetController();
  121. EXPECT_FALSE(checkProcess());
  122. // Verify that the application process can created and initialized.
  123. ASSERT_NO_THROW(initProcess());
  124. EXPECT_TRUE(checkProcess());
  125. }
  126. /// @brief Tests launch handling of invalid command line.
  127. /// This test launches with an invalid command line which should throw
  128. /// an InvalidUsage.
  129. TEST_F(DStubControllerTest, launchInvalidUsage) {
  130. // Command line to run integrated
  131. char* argv[] = { const_cast<char*>("progName"),
  132. const_cast<char*>("-z") };
  133. int argc = 2;
  134. // Launch the controller in integrated mode.
  135. EXPECT_THROW(launch(argc, argv), InvalidUsage);
  136. }
  137. /// @brief Tests launch handling of failure in application process
  138. /// initialization. This test launches with a valid command line but with
  139. /// SimFailure set to fail during process creation. Launch should throw
  140. /// ProcessInitError.
  141. TEST_F(DStubControllerTest, launchProcessInitError) {
  142. // Command line to run integrated
  143. char* argv[] = { const_cast<char*>("progName"),
  144. const_cast<char*>("-c"),
  145. const_cast<char*>(DControllerTest::CFG_TEST_FILE),
  146. const_cast<char*>("-d") };
  147. int argc = 4;
  148. // Launch the controller in stand alone mode.
  149. SimFailure::set(SimFailure::ftCreateProcessException);
  150. EXPECT_THROW(launch(argc, argv), ProcessInitError);
  151. }
  152. /// @brief Tests launch and normal shutdown (stand alone mode).
  153. /// This creates an interval timer to generate a normal shutdown and then
  154. /// launches with a valid, command line, with a valid configuration file
  155. /// and no simulated errors.
  156. TEST_F(DStubControllerTest, launchNormalShutdown) {
  157. // Write the valid, empty, config and then run launch() for 1000 ms
  158. time_duration elapsed_time;
  159. ASSERT_NO_THROW(runWithConfig("{}", 2000, elapsed_time));
  160. // Verify that duration of the run invocation is the same as the
  161. // timer duration. This demonstrates that the shutdown was driven
  162. // by an io_service event and callback.
  163. EXPECT_TRUE(elapsed_time.total_milliseconds() >= 1900 &&
  164. elapsed_time.total_milliseconds() <= 2300);
  165. }
  166. /// @brief Tests launch with an nonexistant configuration file.
  167. TEST_F(DStubControllerTest, nonexistantConfigFile) {
  168. // command line to run standalone
  169. char* argv[] = { const_cast<char*>("progName"),
  170. const_cast<char*>("-c"),
  171. const_cast<char*>("bogus-file"),
  172. const_cast<char*>("-d") };
  173. int argc = 4;
  174. // Record start time, and invoke launch().
  175. EXPECT_THROW(launch(argc, argv), ProcessInitError);
  176. }
  177. /// @brief Tests launch with configuration file argument but no file name
  178. TEST_F(DStubControllerTest, missingConfigFileName) {
  179. // command line to run standalone
  180. char* argv[] = { const_cast<char*>("progName"),
  181. const_cast<char*>("-c"),
  182. const_cast<char*>("-d") };
  183. int argc = 3;
  184. // Record start time, and invoke launch().
  185. EXPECT_THROW(launch(argc, argv), ProcessInitError);
  186. }
  187. /// @brief Tests launch with no configuration file argument
  188. TEST_F(DStubControllerTest, missingConfigFileArgument) {
  189. // command line to run standalone
  190. char* argv[] = { const_cast<char*>("progName"),
  191. const_cast<char*>("-d") };
  192. int argc = 2;
  193. // Record start time, and invoke launch().
  194. EXPECT_THROW(launch(argc, argv), ProcessInitError);
  195. }
  196. /// @brief Tests launch with an operational error during application execution.
  197. /// This test creates an interval timer to generate a runtime exception during
  198. /// the process event loop. It launches wih a valid, stand-alone command line
  199. /// and no simulated errors. Launch should throw ProcessRunError.
  200. TEST_F(DStubControllerTest, launchRuntimeError) {
  201. // Use an asiolink IntervalTimer and callback to generate the
  202. // shutdown invocation. (Note IntervalTimer setup is in milliseconds).
  203. isc::asiolink::IntervalTimer timer(*getIOService());
  204. timer.setup(genFatalErrorCallback, 2000);
  205. // Write the valid, empty, config and then run launch() for 1000 ms
  206. time_duration elapsed_time;
  207. EXPECT_THROW(runWithConfig("{}", 2000, elapsed_time), ProcessRunError);
  208. // Verify that duration of the run invocation is the same as the
  209. // timer duration. This demonstrates that the shutdown was driven
  210. // by an io_service event and callback.
  211. EXPECT_TRUE(elapsed_time.total_milliseconds() >= 1900 &&
  212. elapsed_time.total_milliseconds() <= 2300);
  213. }
  214. /// @brief Configuration update event testing.
  215. /// This really tests just the ability of the handlers to invoke the necessary
  216. /// chain of methods and handle error conditions. Configuration parsing and
  217. /// retrieval should be tested as part of the d2 configuration management
  218. /// implementation.
  219. /// This test verifies that:
  220. /// 1. That a valid configuration update results in successful status return.
  221. /// 2. That an application process error in configuration updating is handled
  222. /// properly.
  223. TEST_F(DStubControllerTest, configUpdateTests) {
  224. int rcode = -1;
  225. isc::data::ConstElementPtr answer;
  226. // Initialize the application process.
  227. ASSERT_NO_THROW(initProcess());
  228. EXPECT_TRUE(checkProcess());
  229. // Create a configuration set. Content is arbitrary, just needs to be
  230. // valid JSON.
  231. std::string config = "{ \"test-value\": 1000 } ";
  232. isc::data::ElementPtr config_set = isc::data::Element::fromJSON(config);
  233. // Verify that a valid config gets a successful update result.
  234. answer = updateConfig(config_set);
  235. isc::config::parseAnswer(rcode, answer);
  236. EXPECT_EQ(0, rcode);
  237. // Verify that an error in process configure method is handled.
  238. SimFailure::set(SimFailure::ftProcessConfigure);
  239. answer = updateConfig(config_set);
  240. isc::config::parseAnswer(rcode, answer);
  241. EXPECT_EQ(1, rcode);
  242. }
  243. /// @brief Command execution tests.
  244. /// This really tests just the ability of the handler to invoke the necessary
  245. /// chain of methods and to handle error conditions.
  246. /// This test verifies that:
  247. /// 1. That an unrecognized command is detected and returns a status of
  248. /// d2::COMMAND_INVALID.
  249. /// 2. Shutdown command is recognized and returns a d2::COMMAND_SUCCESS status.
  250. /// 3. A valid, custom controller command is recognized a d2::COMMAND_SUCCESS
  251. /// status.
  252. /// 4. A valid, custom process command is recognized a d2::COMMAND_SUCCESS
  253. /// status.
  254. /// 5. That a valid controller command that fails returns a d2::COMMAND_ERROR.
  255. /// 6. That a valid process command that fails returns a d2::COMMAND_ERROR.
  256. TEST_F(DStubControllerTest, executeCommandTests) {
  257. int rcode = -1;
  258. isc::data::ConstElementPtr answer;
  259. isc::data::ElementPtr arg_set;
  260. // Initialize the application process.
  261. ASSERT_NO_THROW(initProcess());
  262. EXPECT_TRUE(checkProcess());
  263. // Verify that an unknown command returns an d2::COMMAND_INVALID response.
  264. std::string bogus_command("bogus");
  265. answer = executeCommand(bogus_command, arg_set);
  266. isc::config::parseAnswer(rcode, answer);
  267. EXPECT_EQ(COMMAND_INVALID, rcode);
  268. // Verify that shutdown command returns d2::COMMAND_SUCCESS response.
  269. answer = executeCommand(SHUT_DOWN_COMMAND, arg_set);
  270. isc::config::parseAnswer(rcode, answer);
  271. EXPECT_EQ(COMMAND_SUCCESS, rcode);
  272. // Verify that a valid custom controller command returns
  273. // d2::COMMAND_SUCCESS response.
  274. answer = executeCommand(DStubController::stub_ctl_command_, arg_set);
  275. isc::config::parseAnswer(rcode, answer);
  276. EXPECT_EQ(COMMAND_SUCCESS, rcode);
  277. // Verify that a valid custom process command returns d2::COMMAND_SUCCESS
  278. // response.
  279. answer = executeCommand(DStubProcess::stub_proc_command_, arg_set);
  280. isc::config::parseAnswer(rcode, answer);
  281. EXPECT_EQ(COMMAND_SUCCESS, rcode);
  282. // Verify that a valid custom controller command that fails returns
  283. // a d2::COMMAND_ERROR.
  284. SimFailure::set(SimFailure::ftControllerCommand);
  285. answer = executeCommand(DStubController::stub_ctl_command_, arg_set);
  286. isc::config::parseAnswer(rcode, answer);
  287. EXPECT_EQ(COMMAND_ERROR, rcode);
  288. // Verify that a valid custom process command that fails returns
  289. // a d2::COMMAND_ERROR.
  290. SimFailure::set(SimFailure::ftProcessCommand);
  291. answer = executeCommand(DStubProcess::stub_proc_command_, arg_set);
  292. isc::config::parseAnswer(rcode, answer);
  293. EXPECT_EQ(COMMAND_ERROR, rcode);
  294. }
  295. // Tests that registered signals are caught and handled.
  296. TEST_F(DStubControllerTest, ioSignals) {
  297. // Tell test controller just to record the signals, don't call the
  298. // base class signal handler.
  299. controller_->recordSignalOnly(true);
  300. // Setup to raise SIGHUP in 10 ms.
  301. TimedSignal sighup(*getIOService(), SIGHUP, 10);
  302. TimedSignal sigint(*getIOService(), SIGINT, 10);
  303. TimedSignal sigterm(*getIOService(), SIGTERM, 10);
  304. // Write the valid, empty, config and then run launch() for 500 ms
  305. time_duration elapsed_time;
  306. runWithConfig("{}", 500, elapsed_time);
  307. // Verify that we caught the signals as expected.
  308. std::vector<int>& signals = controller_->getProcessedSignals();
  309. ASSERT_EQ(3, signals.size());
  310. EXPECT_EQ(SIGHUP, signals[0]);
  311. EXPECT_EQ(SIGINT, signals[1]);
  312. EXPECT_EQ(SIGTERM, signals[2]);
  313. }
  314. // Tests that the original configuration is retained after a SIGHUP triggered
  315. // reconfiguration fails due to invalid config content.
  316. TEST_F(DStubControllerTest, invalidConfigReload) {
  317. // Schedule to rewrite the configuration file after launch. This way the
  318. // file is updated after we have done the initial configuration. The
  319. // new content is invalid JSON which will cause the config parse to fail.
  320. scheduleTimedWrite("{ \"string_test\": BOGUS JSON }", 100);
  321. // Setup to raise SIGHUP in 200 ms.
  322. TimedSignal sighup(*getIOService(), SIGHUP, 200);
  323. // Write the config and then run launch() for 500 ms
  324. // After startup, which will load the initial configuration this enters
  325. // the process's runIO() loop. We will first rewrite the config file.
  326. // Next we process the SIGHUP signal which should cause us to reconfigure.
  327. time_duration elapsed_time;
  328. runWithConfig("{ \"string_test\": \"first value\" }", 500, elapsed_time);
  329. // Context is still available post launch. Check to see that our
  330. // configuration value is still the original value.
  331. std::string actual_value = "";
  332. ASSERT_NO_THROW(getContext()->getParam("string_test", actual_value));
  333. EXPECT_EQ("first value", actual_value);
  334. // Verify that we saw the signal.
  335. std::vector<int>& signals = controller_->getProcessedSignals();
  336. ASSERT_EQ(1, signals.size());
  337. EXPECT_EQ(SIGHUP, signals[0]);
  338. }
  339. // Tests that the original configuration is replaced after a SIGHUP triggered
  340. // reconfiguration succeeds.
  341. TEST_F(DStubControllerTest, validConfigReload) {
  342. // Schedule to rewrite the configuration file after launch. This way the
  343. // file is updated after we have done the initial configuration.
  344. scheduleTimedWrite("{ \"string_test\": \"second value\" }", 100);
  345. // Setup to raise SIGHUP in 200 ms and another at 400 ms.
  346. TimedSignal sighup(*getIOService(), SIGHUP, 200);
  347. TimedSignal sighup2(*getIOService(), SIGHUP, 400);
  348. // Write the config and then run launch() for 800 ms
  349. time_duration elapsed_time;
  350. runWithConfig("{ \"string_test\": \"first value\" }", 800, elapsed_time);
  351. // Context is still available post launch.
  352. // Check to see that our configuration value is what we expect.
  353. std::string actual_value = "";
  354. ASSERT_NO_THROW(getContext()->getParam("string_test", actual_value));
  355. EXPECT_EQ("second value", actual_value);
  356. // Verify that we saw two occurrences of the signal.
  357. std::vector<int>& signals = controller_->getProcessedSignals();
  358. ASSERT_EQ(2, signals.size());
  359. EXPECT_EQ(SIGHUP, signals[0]);
  360. EXPECT_EQ(SIGHUP, signals[1]);
  361. }
  362. // Tests that the SIGINT triggers a normal shutdown.
  363. TEST_F(DStubControllerTest, sigintShutdown) {
  364. // Setup to raise SIGHUP in 1 ms.
  365. TimedSignal sighup(*getIOService(), SIGINT, 1);
  366. // Write the config and then run launch() for 1000 ms
  367. time_duration elapsed_time;
  368. runWithConfig("{ \"string_test\": \"first value\" }", 1000, elapsed_time);
  369. // Verify that we saw the signal.
  370. std::vector<int>& signals = controller_->getProcessedSignals();
  371. ASSERT_EQ(1, signals.size());
  372. EXPECT_EQ(SIGINT, signals[0]);
  373. // Duration should be significantly less than our max run time.
  374. EXPECT_TRUE(elapsed_time.total_milliseconds() < 300);
  375. }
  376. // Tests that the SIGTERM triggers a normal shutdown.
  377. TEST_F(DStubControllerTest, sigtermShutdown) {
  378. // Setup to raise SIGHUP in 1 ms.
  379. TimedSignal sighup(*getIOService(), SIGTERM, 1);
  380. // Write the config and then run launch() for 1000 ms
  381. time_duration elapsed_time;
  382. runWithConfig("{ \"string_test\": \"first value\" }", 1000, elapsed_time);
  383. // Verify that we saw the signal.
  384. std::vector<int>& signals = controller_->getProcessedSignals();
  385. ASSERT_EQ(1, signals.size());
  386. EXPECT_EQ(SIGTERM, signals[0]);
  387. // Duration should be significantly less than our max run time.
  388. EXPECT_TRUE(elapsed_time.total_milliseconds() < 300);
  389. }
  390. }; // end of isc::d2 namespace
  391. }; // end of isc namespace