1 /*
2   Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved.
3 
4   This program is free software; you can redistribute it and/or modify
5   it under the terms of the GNU General Public License, version 2.0,
6   as published by the Free Software Foundation.
7 
8   This program is also distributed with certain software (including
9   but not limited to OpenSSL) that is licensed under separate terms,
10   as designated in a particular file or component or in included license
11   documentation.  The authors of MySQL hereby grant you an additional
12   permission to link the program and your derivative works with the
13   separately licensed software that they have included with MySQL.
14 
15   This program is distributed in the hope that it will be useful,
16   but WITHOUT ANY WARRANTY; without even the implied warranty of
17   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18   GNU General Public License for more details.
19 
20   You should have received a copy of the GNU General Public License
21   along with this program; if not, write to the Free Software
22   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
23 */
24 
25 #ifndef _PROCESS_MANAGER_H_
26 #define _PROCESS_MANAGER_H_
27 
28 #include "process_wrapper.h"
29 
30 #include <gmock/gmock.h>
31 #include <chrono>
32 #include <cstring>
33 #include <functional>
34 #include <iostream>
35 #include <list>
36 #include <map>
37 #include <sstream>
38 #include <stdexcept>
39 #include <streambuf>
40 #include <vector>
41 #ifndef _WIN32
42 #include <unistd.h>
43 #endif
44 
45 #include "router_test_helpers.h"
46 #include "temp_dir.h"
47 
48 using mysql_harness::Path;
49 
50 /** @class ProcessManager
51  *
52  * Manages collecion of the processes
53  * Enables creating, shutting down etc.
54  *
55  **/
56 class ProcessManager {
57  public:
58   /**
59    * set origin path.
60    */
61   static void set_origin(const Path &dir);
62 
63  protected:
~ProcessManager()64   virtual ~ProcessManager() {}
65 
66   /**
67    * shutdown all managed processes.
68    */
69   void shutdown_all();
70 
71   /**
72    * ensures all processes exited and checks for crashes.
73    */
74   void ensure_clean_exit();
75 
76   /**
77    * ensures given process exited with expected return value and checks for
78    * crashes.
79    */
80   void check_exit_code(
81       ProcessWrapper &process, int expected_exit_code = EXIT_SUCCESS,
82       std::chrono::milliseconds timeout = kDefaultWaitForExitTimeout);
83 
84   void dump_all();
85 
86   /**
87    * ensures given port is ready for accepting connections, prints some debug
88    * data otherwise.
89    *
90    * @param process       process that should be listening on that port
91    * @param port          TCP port number to check
92    * @param timeout       maximum timeout to wait for the port
93    * @param hostname      name/IP address of the network host to check
94    */
95   void check_port_ready(
96       ProcessWrapper &process, uint16_t port,
97       std::chrono::milliseconds timeout = kDefaultPortReadyTimeout,
98       const std::string &hostname = "127.0.0.1");
99 
100   /**
101    * ensures given port is NOT ready for accepting connections, prints some
102    * debug data otherwise.
103    *
104    * @param process       process that should be listening on that port
105    * @param port          TCP port number to check
106    * @param timeout       maximum timeout to wait for the port
107    * @param hostname      name/IP address of the network host to check
108    */
109   void check_port_not_ready(
110       ProcessWrapper &process, uint16_t port,
111       std::chrono::milliseconds timeout = kDefaultPortReadyTimeout,
112       const std::string &hostname = "127.0.0.1");
113 
114   /** @brief Launches the MySQLRouter process.
115    *
116    * @param   params vector<string> containing command line parameters to pass
117    * to process
118    * @param expected_exit_code expected exit-code for ensure_clean_exit()
119    * @param   catch_stderr bool flag indicating if the process' error output
120    * stream should be included in the output caught from the process
121    * @param   with_sudo    bool flag indicating if the process' should be
122    * execute with sudo priviledges
123    *
124    * @returns handle to the launched proccess
125    */
126   ProcessWrapper &launch_router(const std::vector<std::string> &params,
127                                 int expected_exit_code = 0,
128                                 bool catch_stderr = true,
129                                 bool with_sudo = false);
130 
131   /** @brief Launches the MySQLServerMock process.
132    *
133    * @param json_file  path to the json file containing expected queries
134    * definitions
135    * @param   port       number of the port where the mock server will accept
136    * the client connections
137    * @param expected_exit_code expected exit-code for ensure_clean_exit()
138    * @param debug_mode if true all the queries and result get printed on the
139    *                     standard output
140    * @param http_port  port number where the http_server module of the mock
141    * server will accept REST client requests
142    * @param x_port  port number where the mock server will accept x client
143    *                  connections
144    * @param module_prefix base-path for javascript modules used by the tests
145    * @param bind_address listen address for the mock server to bind to
146    *
147    * @returns handle to the launched proccess
148    */
149   ProcessWrapper &launch_mysql_server_mock(
150       const std::string &json_file, unsigned port, int expected_exit_code = 0,
151       bool debug_mode = false, uint16_t http_port = 0, uint16_t x_port = 0,
152       const std::string &module_prefix = "",
153       const std::string &bind_address = "0.0.0.0");
154 
155   /** @brief Launches a process.
156    *
157    * @param command       path to executable
158    * @param params        array of commanline parameters to pass to the
159    * executable
160    * @param catch_stderr  if true stderr will also be captured (combined with
161    * stdout)
162    *
163    * @returns handle to the launched proccess
164    */
165   ProcessWrapper &launch_command(const std::string &command,
166                                  const std::vector<std::string> &params,
167                                  int expected_exit_code = 0,
168                                  bool catch_stderr = true);
169 
170   /** @brief Gets path to the directory containing testing data
171    *         (conf files, json files).
172    */
get_data_dir()173   const Path &get_data_dir() const { return data_dir_; }
174 
175   /** @brief Gets path to the directory used as log output directory
176    */
get_logging_dir()177   Path get_logging_dir() const { return Path(logging_dir_.name()); }
178 
179   /** @brief returns a map with default [DEFAULT] section parameters
180    *
181    * @return default parameters for [DEFAULT] section
182    */
183   std::map<std::string, std::string> get_DEFAULT_defaults() const;
184 
185   /** @brief create config file
186    *
187    * @param directory directory in which the config file will be created
188    * @param sections text to follow [DEFAULT] section (typically all other
189    * sections)
190    * @param default_section [DEFAULT] section parameters
191    * @param name config file name
192    * @param extra_defaults addional parameters to add to [DEFAULT]
193    * @param enable_debug_logging add a logger section with debug level
194    *
195    * @return path to the created file
196    */
197   std::string create_config_file(
198       const std::string &directory, const std::string &sections = "",
199       const std::map<std::string, std::string> *default_section = nullptr,
200       const std::string &name = "mysqlrouter.conf",
201       const std::string &extra_defaults = "",
202       bool enable_debug_logging = true) const;
203 
204   // returns full path to the file
205   std::string create_state_file(const std::string &dir_name,
206                                 const std::string &content);
207 
get_origin()208   static const Path &get_origin() { return origin_dir_; }
209 
get_mysqlrouter_exec()210   const Path &get_mysqlrouter_exec() const { return mysqlrouter_exec_; }
211 
212   /**
213    * get Path to mysql_server_mock inside the build-dir.
214    *
215    * valid after SetUp() got called.
216    */
get_mysqlserver_mock_exec()217   const Path &get_mysqlserver_mock_exec() const {
218     return mysqlserver_mock_exec_;
219   }
220 
set_mysqlrouter_exec(const Path & path)221   void set_mysqlrouter_exec(const Path &path) { mysqlrouter_exec_ = path; }
222 
223  protected:
224   /** @brief returns a [DEFAULT] section as string
225    *
226    * @param params map of [DEFAULT] section parameters
227    * @returns [DEFAULT] section text
228    */
229   std::string make_DEFAULT_section(
230       const std::map<std::string, std::string> *params) const;
231 
232  private:
233   void check_port(bool should_be_ready, ProcessWrapper &process, uint16_t port,
234                   std::chrono::milliseconds timeout,
235                   const std::string &hostname);
236 
237   static Path origin_dir_;
238   static Path data_dir_;
239   static Path plugin_dir_;
240   static Path mysqlrouter_exec_;
241   static Path mysqlserver_mock_exec_;
242 
243   TempDirectory logging_dir_;
244 
245   std::list<std::tuple<ProcessWrapper, int>> processes_;
246 };
247 
248 #endif  // _PROCESS_MANAGER_H_
249