1 /*
2   Copyright (c) 2017, 2020, 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 #include "gmock/gmock.h"
26 #include "router_component_test.h"
27 #include "tcp_port_pool.h"
28 
29 using testing::StartsWith;
30 
31 class RouterConfigTest : public RouterComponentTest {
32  protected:
33   TcpPortPool port_pool_;
34 };
35 
36 // Bug #25800863 WRONG ERRORMSG IF DIRECTORY IS PROVIDED AS CONFIGFILE
TEST_F(RouterConfigTest,RoutingDirAsMainConfigDirectory)37 TEST_F(RouterConfigTest, RoutingDirAsMainConfigDirectory) {
38   TempDirectory config_dir;
39 
40   // launch the router giving directory instead of config_name
41   auto &router = launch_router({"-c", config_dir.name()}, EXIT_FAILURE);
42 
43   check_exit_code(router, EXIT_FAILURE);
44 
45   EXPECT_TRUE(router.expect_output(
46       "The configuration file '" + config_dir.name() +
47       "' is expected to be a readable file, but it is a directory"));
48 }
49 
50 // Bug #25800863 WRONG ERRORMSG IF DIRECTORY IS PROVIDED AS CONFIGFILE
TEST_F(RouterConfigTest,RoutingDirAsExtendedConfigDirectory)51 TEST_F(RouterConfigTest, RoutingDirAsExtendedConfigDirectory) {
52   const auto router_port = port_pool_.get_next_available();
53   const auto server_port = port_pool_.get_next_available();
54 
55   const std::string routing_section =
56       "[routing:basic]\n"
57       "bind_port = " +
58       std::to_string(router_port) +
59       "\n"
60       "mode = read-write\n"
61       "destinations = 127.0.0.1:" +
62       std::to_string(server_port) + "\n";
63 
64   TempDirectory conf_dir("conf");
65   TempDirectory extra_conf_dir;
66 
67   std::string conf_file = create_config_file(conf_dir.name(), routing_section);
68 
69   // launch the router giving directory instead of an extra config name
70   auto &router = launch_router({"-c", conf_file, "-a", extra_conf_dir.name()},
71                                EXIT_FAILURE);
72 
73   check_exit_code(router, EXIT_FAILURE);
74 
75   EXPECT_TRUE(router.expect_output(
76       "The configuration file '" + extra_conf_dir.name() +
77       "' is expected to be a readable file, but it is a directory"));
78 }
79 
TEST_F(RouterConfigTest,IsExceptionThrownWhenAddTwiceTheSameSectionWithoutKey)80 TEST_F(RouterConfigTest,
81        IsExceptionThrownWhenAddTwiceTheSameSectionWithoutKey) {
82   TempDirectory conf_dir("conf");
83   const std::string conf_file =
84       create_config_file(conf_dir.name(), "[section1]\n[section1]\n");
85 
86   // run the router and wait for it to exit
87   auto &router = launch_router({"-c", conf_file}, EXIT_FAILURE);
88   check_exit_code(router, EXIT_FAILURE);
89 
90   EXPECT_THAT(
91       router.get_full_output(),
92       StartsWith(
93           "Error: Configuration error: Section 'section1' already exists"));
94 }
95 
TEST_F(RouterConfigTest,IsExceptionThrownWhenAddTwiceTheSameSectionWithKey)96 TEST_F(RouterConfigTest, IsExceptionThrownWhenAddTwiceTheSameSectionWithKey) {
97   TempDirectory conf_dir("conf");
98   const std::string conf_file =
99       create_config_file(conf_dir.name(), "[section1:key1]\n[section1:key1]\n");
100 
101   // run the router and wait for it to exit
102   auto &router = launch_router({"-c", conf_file}, EXIT_FAILURE);
103   check_exit_code(router, EXIT_FAILURE);
104 
105   EXPECT_THAT(router.get_full_output(),
106               StartsWith("Error: Configuration error: Section 'section1:key1' "
107                          "already exists"));
108 }
109 
TEST_F(RouterConfigTest,IsExceptionThrownWhenTheSameOptionsTwiceInASingleSection)110 TEST_F(RouterConfigTest,
111        IsExceptionThrownWhenTheSameOptionsTwiceInASingleSection) {
112   TempDirectory conf_dir("conf");
113   const std::string conf_file = create_config_file(
114       conf_dir.name(), "[section1]\ndynamic_state=a\ndynamic_state=b\n");
115 
116   // run the router and wait for it to exit
117   auto &router = launch_router({"-c", conf_file}, EXIT_FAILURE);
118   check_exit_code(router, EXIT_FAILURE);
119 
120   EXPECT_THAT(router.get_full_output(),
121               StartsWith("Error: Configuration error: Option 'dynamic_state' "
122                          "already defined."));
123 }
124 
125 #ifdef _WIN32
isRouterServiceInstalled()126 static bool isRouterServiceInstalled() {
127   SC_HANDLE service, scm;
128   bool result = false;
129 
130   if ((scm = OpenSCManager(0, 0, SC_MANAGER_ENUMERATE_SERVICE))) {
131     if ((service = OpenService(scm, "MySQLRouter", SERVICE_QUERY_STATUS))) {
132       CloseServiceHandle(service);
133       result = true;
134     }
135     CloseServiceHandle(scm);
136   }
137   return result;
138 }
139 
140 /**
141  * ensure that the router exits with proper error when launched with --service
142  * and the service is not installed
143  */
TEST_F(RouterConfigTest,IsErrorReturnedWhenServiceDoesNotExist)144 TEST_F(RouterConfigTest, IsErrorReturnedWhenServiceDoesNotExist) {
145   // first we need to make sure the service really is not installed on the
146   // system that the test is running on. If it is we can't do much about it and
147   // we just skip testing.
148   if (!isRouterServiceInstalled()) {
149     TempDirectory conf_dir("conf");
150     const std::string conf_file =
151         create_config_file(conf_dir.name(), "[keepalive]\ninterval = 60\n");
152 
153     // run the router and wait for it to exit
154     auto &router = launch_router({"-c", conf_file, "--service"}, EXIT_FAILURE);
155     check_exit_code(router, EXIT_FAILURE);
156 
157     EXPECT_THAT(router.get_full_output(),
158                 StartsWith("ERROR: Could not find service 'MySQLRouter'!\n"
159                            "Use --install-service or --install-service-manual "
160                            "option to install the service first.\n"));
161   }
162 }
163 #endif  // _WIN32
164 
main(int argc,char * argv[])165 int main(int argc, char *argv[]) {
166   init_windows_sockets();
167   ProcessManager::set_origin(Path(argv[0]).dirname());
168   ::testing::InitGoogleTest(&argc, argv);
169   return RUN_ALL_TESTS();
170 }
171