|
9 | 9 |
|
10 | 10 | from lib.colorer import color_stdout
|
11 | 11 | from lib.colorer import color_log
|
| 12 | +from lib.colorer import qa_notice |
12 | 13 | from lib.options import Options
|
13 | 14 | from lib.preprocessor import TestState
|
14 | 15 | from lib.server import Server
|
|
18 | 19 | from lib.tarantool_server import TarantoolStartError
|
19 | 20 | from lib.utils import find_port
|
20 | 21 | from lib.utils import format_process
|
| 22 | +from lib.utils import signame |
21 | 23 | from lib.utils import warn_unix_socket
|
22 | 24 | from test import TestRunGreenlet, TestExecutionError
|
23 | 25 | from threading import Timer
|
@@ -72,15 +74,25 @@ def execute(self, server):
|
72 | 74 | # A non-default server failed to start.
|
73 | 75 | raise TestExecutionError
|
74 | 76 | finally:
|
75 |
| - # Stop any servers created by the test, except the |
76 |
| - # default one. |
77 |
| - # |
78 |
| - # See a comment in LuaTest.execute() for motivation of |
79 |
| - # SIGKILL usage. |
80 |
| - ts.stop_nondefault(signal=signal.SIGKILL) |
| 77 | + self.teardown(server, ts) |
81 | 78 | if retval.get('returncode', None) != 0:
|
82 | 79 | raise TestExecutionError
|
83 | 80 |
|
| 81 | + def teardown(self, server, ts): |
| 82 | + # Stop any servers created by the test, except the |
| 83 | + # default one. |
| 84 | + # |
| 85 | + # See a comment in LuaTest.execute() for motivation of |
| 86 | + # SIGKILL usage. |
| 87 | + ts.stop_nondefault(signal=signal.SIGKILL) |
| 88 | + |
| 89 | + # When a supplementary (non-default) server fails, we |
| 90 | + # should not leave the process that executes an app test. |
| 91 | + # Let's kill it. |
| 92 | + # |
| 93 | + # Reuse AppServer.stop() code for convenience. |
| 94 | + server.stop(signal=signal.SIGKILL) |
| 95 | + |
84 | 96 |
|
85 | 97 | class AppServer(Server):
|
86 | 98 | """A dummy server implementation for application server tests"""
|
@@ -153,16 +165,60 @@ def deploy(self, vardir=None, silent=True, need_init=True):
|
153 | 165 | # cannot check length of path of *.control unix socket created by it.
|
154 | 166 | # So for 'app' tests type we don't check *.control unix sockets paths.
|
155 | 167 |
|
156 |
| - def stop(self, silent): |
| 168 | + def stop(self, silent=True, signal=signal.SIGTERM): |
| 169 | + # FIXME: Extract common parts of AppServer.stop() and |
| 170 | + # TarantoolServer.stop() to an utility function. |
| 171 | + |
| 172 | + color_log('DEBUG: [app server] Stopping the server...\n', |
| 173 | + schema='info') |
| 174 | + |
157 | 175 | if not self.process:
|
| 176 | + color_log(' | Nothing to do: the process does not exist\n', |
| 177 | + schema='info') |
158 | 178 | return
|
159 |
| - color_log('AppServer.stop(): stopping the %s\n' % |
160 |
| - format_process(self.process.pid), schema='test_var') |
| 179 | + |
| 180 | + if self.process.returncode: |
| 181 | + if self.process.returncode < 0: |
| 182 | + signaled_by = -self.process.returncode |
| 183 | + color_log(' | Nothing to do: the process was terminated by ' |
| 184 | + 'signal {} ({})\n'.format(signaled_by, |
| 185 | + signame(signaled_by)), |
| 186 | + schema='info') |
| 187 | + else: |
| 188 | + color_log(' | Nothing to do: the process was exited with code ' |
| 189 | + '{}\n'.format(self.process.returncode), |
| 190 | + schema='info') |
| 191 | + return |
| 192 | + |
| 193 | + color_log(' | Sending signal {0} ({1}) to {2}\n'.format( |
| 194 | + signal, signame(signal), |
| 195 | + format_process(self.process.pid))) |
161 | 196 | try:
|
162 |
| - self.process.terminate() |
| 197 | + self.process.send_signal(signal) |
163 | 198 | except OSError:
|
164 | 199 | pass
|
165 | 200 |
|
| 201 | + # Waiting for stopping the server. If the timeout |
| 202 | + # reached, send SIGKILL. |
| 203 | + timeout = 5 |
| 204 | + |
| 205 | + def kill(): |
| 206 | + qa_notice('The app server does not stop during {} ' |
| 207 | + 'seconds after the {} ({}) signal.\n' |
| 208 | + 'Info: {}\n' |
| 209 | + 'Sending SIGKILL...'.format( |
| 210 | + timeout, signal, signame(signal), |
| 211 | + format_process(self.process.pid))) |
| 212 | + try: |
| 213 | + self.process.kill() |
| 214 | + except OSError: |
| 215 | + pass |
| 216 | + |
| 217 | + timer = Timer(timeout, kill) |
| 218 | + timer.start() |
| 219 | + self.process.wait() |
| 220 | + timer.cancel() |
| 221 | + |
166 | 222 | @classmethod
|
167 | 223 | def find_exe(cls, builddir):
|
168 | 224 | cls.builddir = builddir
|
|
0 commit comments