| OLD | NEW |
| 1 # Copyright (C) 2011 Google Inc. All rights reserved. | 1 # Copyright (C) 2011 Google Inc. All rights reserved. |
| 2 # | 2 # |
| 3 # Redistribution and use in source and binary forms, with or without | 3 # Redistribution and use in source and binary forms, with or without |
| 4 # modification, are permitted provided that the following conditions are | 4 # modification, are permitted provided that the following conditions are |
| 5 # met: | 5 # met: |
| 6 # | 6 # |
| 7 # * Redistributions of source code must retain the above copyright | 7 # * Redistributions of source code must retain the above copyright |
| 8 # notice, this list of conditions and the following disclaimer. | 8 # notice, this list of conditions and the following disclaimer. |
| 9 # * Redistributions in binary form must reproduce the above | 9 # * Redistributions in binary form must reproduce the above |
| 10 # copyright notice, this list of conditions and the following disclaimer | 10 # copyright notice, this list of conditions and the following disclaimer |
| (...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 82 def start(self): | 82 def start(self): |
| 83 """Starts the server. It is an error to start an already started server. | 83 """Starts the server. It is an error to start an already started server. |
| 84 | 84 |
| 85 This method also stops any stale servers started by a previous instance.
""" | 85 This method also stops any stale servers started by a previous instance.
""" |
| 86 assert not self._pid, '%s server is already running' % self._name | 86 assert not self._pid, '%s server is already running' % self._name |
| 87 | 87 |
| 88 # Stop any stale servers left over from previous instances. | 88 # Stop any stale servers left over from previous instances. |
| 89 if self._filesystem.exists(self._pid_file): | 89 if self._filesystem.exists(self._pid_file): |
| 90 try: | 90 try: |
| 91 self._pid = int(self._filesystem.read_text_file(self._pid_file)) | 91 self._pid = int(self._filesystem.read_text_file(self._pid_file)) |
| 92 _log.debug('stale %s pid file, pid %d' % (self._name, self._pid)
) | 92 _log.debug('stale %s pid file, pid %d', self._name, self._pid) |
| 93 self._stop_running_server() | 93 self._stop_running_server() |
| 94 except (ValueError, UnicodeDecodeError): | 94 except (ValueError, UnicodeDecodeError): |
| 95 # These could be raised if the pid file is corrupt. | 95 # These could be raised if the pid file is corrupt. |
| 96 self._remove_pid_file() | 96 self._remove_pid_file() |
| 97 self._pid = None | 97 self._pid = None |
| 98 | 98 |
| 99 self._remove_stale_logs() | 99 self._remove_stale_logs() |
| 100 self._prepare_config() | 100 self._prepare_config() |
| 101 self._check_that_all_ports_are_available() | 101 self._check_that_all_ports_are_available() |
| 102 | 102 |
| 103 self._pid = self._spawn_process() | 103 self._pid = self._spawn_process() |
| 104 | 104 |
| 105 if self._wait_for_action(self._is_server_running_on_all_ports): | 105 if self._wait_for_action(self._is_server_running_on_all_ports): |
| 106 _log.debug("%s successfully started (pid = %d)" % (self._name, self.
_pid)) | 106 _log.debug("%s successfully started (pid = %d)", self._name, self._p
id) |
| 107 else: | 107 else: |
| 108 self._log_errors_from_subprocess() | 108 self._log_errors_from_subprocess() |
| 109 self._stop_running_server() | 109 self._stop_running_server() |
| 110 raise ServerError('Failed to start %s server' % self._name) | 110 raise ServerError('Failed to start %s server' % self._name) |
| 111 | 111 |
| 112 def stop(self): | 112 def stop(self): |
| 113 """Stops the server. Stopping a server that isn't started is harmless.""
" | 113 """Stops the server. Stopping a server that isn't started is harmless.""
" |
| 114 actual_pid = None | 114 actual_pid = None |
| 115 try: | 115 try: |
| 116 if self._filesystem.exists(self._pid_file): | 116 if self._filesystem.exists(self._pid_file): |
| 117 try: | 117 try: |
| 118 actual_pid = int(self._filesystem.read_text_file(self._pid_f
ile)) | 118 actual_pid = int(self._filesystem.read_text_file(self._pid_f
ile)) |
| 119 except (ValueError, UnicodeDecodeError): | 119 except (ValueError, UnicodeDecodeError): |
| 120 # These could be raised if the pid file is corrupt. | 120 # These could be raised if the pid file is corrupt. |
| 121 pass | 121 pass |
| 122 if not self._pid: | 122 if not self._pid: |
| 123 self._pid = actual_pid | 123 self._pid = actual_pid |
| 124 | 124 |
| 125 if not self._pid: | 125 if not self._pid: |
| 126 return | 126 return |
| 127 | 127 |
| 128 if not actual_pid: | 128 if not actual_pid: |
| 129 _log.warning('Failed to stop %s: pid file is missing' % self._na
me) | 129 _log.warning('Failed to stop %s: pid file is missing', self._nam
e) |
| 130 return | 130 return |
| 131 if self._pid != actual_pid: | 131 if self._pid != actual_pid: |
| 132 _log.warning('Failed to stop %s: pid file contains %d, not %d' % | 132 _log.warning('Failed to stop %s: pid file contains %d, not %d', |
| 133 (self._name, actual_pid, self._pid)) | 133 self._name, actual_pid, self._pid) |
| 134 # Try to kill the existing pid, anyway, in case it got orphaned. | 134 # Try to kill the existing pid, anyway, in case it got orphaned. |
| 135 self._executive.kill_process(self._pid) | 135 self._executive.kill_process(self._pid) |
| 136 self._pid = None | 136 self._pid = None |
| 137 return | 137 return |
| 138 | 138 |
| 139 _log.debug("Attempting to shut down %s server at pid %d" % (self._na
me, self._pid)) | 139 _log.debug("Attempting to shut down %s server at pid %d", self._name
, self._pid) |
| 140 self._stop_running_server() | 140 self._stop_running_server() |
| 141 _log.debug("%s server at pid %d stopped" % (self._name, self._pid)) | 141 _log.debug("%s server at pid %d stopped", self._name, self._pid) |
| 142 self._pid = None | 142 self._pid = None |
| 143 finally: | 143 finally: |
| 144 # Make sure we delete the pid file no matter what happens. | 144 # Make sure we delete the pid file no matter what happens. |
| 145 self._remove_pid_file() | 145 self._remove_pid_file() |
| 146 | 146 |
| 147 def _prepare_config(self): | 147 def _prepare_config(self): |
| 148 """This routine can be overridden by subclasses to do any sort | 148 """This routine can be overridden by subclasses to do any sort |
| 149 of initialization required prior to starting the server that may fail.""
" | 149 of initialization required prior to starting the server that may fail.""
" |
| 150 | 150 |
| 151 def _remove_stale_logs(self): | 151 def _remove_stale_logs(self): |
| 152 """This routine can be overridden by subclasses to try and remove logs | 152 """This routine can be overridden by subclasses to try and remove logs |
| 153 left over from a prior run. This routine should log warnings if the | 153 left over from a prior run. This routine should log warnings if the |
| 154 files cannot be deleted, but should not fail unless failure to | 154 files cannot be deleted, but should not fail unless failure to |
| 155 delete the logs will actually cause start() to fail.""" | 155 delete the logs will actually cause start() to fail.""" |
| 156 # Sometimes logs are open in other processes but they should clear event
ually. | 156 # Sometimes logs are open in other processes but they should clear event
ually. |
| 157 for log_prefix in self._log_prefixes: | 157 for log_prefix in self._log_prefixes: |
| 158 try: | 158 try: |
| 159 self._remove_log_files(self._output_dir, log_prefix) | 159 self._remove_log_files(self._output_dir, log_prefix) |
| 160 except OSError: | 160 except OSError: |
| 161 _log.warning('Failed to remove old %s %s files' % (self._name, l
og_prefix)) | 161 _log.warning('Failed to remove old %s %s files', self._name, log
_prefix) |
| 162 | 162 |
| 163 def _spawn_process(self): | 163 def _spawn_process(self): |
| 164 _log.debug('Starting %s server, cmd="%s"' % (self._name, self._start_cmd
)) | 164 _log.debug('Starting %s server, cmd="%s"', self._name, self._start_cmd) |
| 165 self._process = self._executive.popen(self._start_cmd, | 165 self._process = self._executive.popen(self._start_cmd, |
| 166 env=self._env, | 166 env=self._env, |
| 167 cwd=self._cwd, | 167 cwd=self._cwd, |
| 168 stdout=self._stdout, | 168 stdout=self._stdout, |
| 169 stderr=self._stderr) | 169 stderr=self._stderr) |
| 170 pid = self._process.pid | 170 pid = self._process.pid |
| 171 self._filesystem.write_text_file(self._pid_file, str(pid)) | 171 self._filesystem.write_text_file(self._pid_file, str(pid)) |
| 172 return pid | 172 return pid |
| 173 | 173 |
| 174 def _stop_running_server(self): | 174 def _stop_running_server(self): |
| 175 self._wait_for_action(self._check_and_kill) | 175 self._wait_for_action(self._check_and_kill) |
| 176 if self._filesystem.exists(self._pid_file): | 176 if self._filesystem.exists(self._pid_file): |
| 177 self._filesystem.remove(self._pid_file) | 177 self._filesystem.remove(self._pid_file) |
| 178 | 178 |
| 179 def _check_and_kill(self): | 179 def _check_and_kill(self): |
| 180 if self._executive.check_running_pid(self._pid): | 180 if self._executive.check_running_pid(self._pid): |
| 181 _log.debug('pid %d is running, killing it' % self._pid) | 181 _log.debug('pid %d is running, killing it', self._pid) |
| 182 self._executive.kill_process(self._pid) | 182 self._executive.kill_process(self._pid) |
| 183 return False | 183 return False |
| 184 else: | 184 else: |
| 185 _log.debug('pid %d is not running' % self._pid) | 185 _log.debug('pid %d is not running', self._pid) |
| 186 | 186 |
| 187 return True | 187 return True |
| 188 | 188 |
| 189 def _remove_pid_file(self): | 189 def _remove_pid_file(self): |
| 190 if self._filesystem.exists(self._pid_file): | 190 if self._filesystem.exists(self._pid_file): |
| 191 self._filesystem.remove(self._pid_file) | 191 self._filesystem.remove(self._pid_file) |
| 192 | 192 |
| 193 def _remove_log_files(self, folder, starts_with): | 193 def _remove_log_files(self, folder, starts_with): |
| 194 files = self._filesystem.listdir(folder) | 194 files = self._filesystem.listdir(folder) |
| 195 for file in files: | 195 for file in files: |
| 196 if file.startswith(starts_with): | 196 if file.startswith(starts_with): |
| 197 full_path = self._filesystem.join(folder, file) | 197 full_path = self._filesystem.join(folder, file) |
| 198 self._filesystem.remove(full_path) | 198 self._filesystem.remove(full_path) |
| 199 | 199 |
| 200 def _log_errors_from_subprocess(self): | 200 def _log_errors_from_subprocess(self): |
| 201 _log.error('logging %s errors, if any' % self._name) | 201 _log.error('logging %s errors, if any', self._name) |
| 202 if self._process: | 202 if self._process: |
| 203 _log.error('%s returncode %s' % (self._name, str(self._process.retur
ncode))) | 203 _log.error('%s returncode %s', self._name, str(self._process.returnc
ode)) |
| 204 if self._process.stderr: | 204 if self._process.stderr: |
| 205 stderr_text = self._process.stderr.read() | 205 stderr_text = self._process.stderr.read() |
| 206 if stderr_text: | 206 if stderr_text: |
| 207 _log.error('%s stderr:' % self._name) | 207 _log.error('%s stderr:', self._name) |
| 208 for line in stderr_text.splitlines(): | 208 for line in stderr_text.splitlines(): |
| 209 _log.error(' %s' % line) | 209 _log.error(' %s', line) |
| 210 else: | 210 else: |
| 211 _log.error('%s no stderr' % self._name) | 211 _log.error('%s no stderr', self._name) |
| 212 else: | 212 else: |
| 213 _log.error('%s no stderr handle' % self._name) | 213 _log.error('%s no stderr handle', self._name) |
| 214 else: | 214 else: |
| 215 _log.error('%s no process' % self._name) | 215 _log.error('%s no process', self._name) |
| 216 if self._error_log_path and self._filesystem.exists(self._error_log_path
): | 216 if self._error_log_path and self._filesystem.exists(self._error_log_path
): |
| 217 error_log_text = self._filesystem.read_text_file(self._error_log_pat
h) | 217 error_log_text = self._filesystem.read_text_file(self._error_log_pat
h) |
| 218 if error_log_text: | 218 if error_log_text: |
| 219 _log.error('%s error log (%s) contents:' % (self._name, self._er
ror_log_path)) | 219 _log.error('%s error log (%s) contents:', self._name, self._erro
r_log_path) |
| 220 for line in error_log_text.splitlines(): | 220 for line in error_log_text.splitlines(): |
| 221 _log.error(' %s' % line) | 221 _log.error(' %s', line) |
| 222 else: | 222 else: |
| 223 _log.error('%s error log empty' % self._name) | 223 _log.error('%s error log empty', self._name) |
| 224 _log.error('') | 224 _log.error('') |
| 225 else: | 225 else: |
| 226 _log.error('%s no error log' % self._name) | 226 _log.error('%s no error log', self._name) |
| 227 | 227 |
| 228 def _wait_for_action(self, action, wait_secs=20.0, sleep_secs=1.0): | 228 def _wait_for_action(self, action, wait_secs=20.0, sleep_secs=1.0): |
| 229 """Repeat the action for wait_sec or until it succeeds, sleeping for sle
ep_secs | 229 """Repeat the action for wait_sec or until it succeeds, sleeping for sle
ep_secs |
| 230 in between each attempt. Returns whether it succeeded.""" | 230 in between each attempt. Returns whether it succeeded.""" |
| 231 start_time = time.time() | 231 start_time = time.time() |
| 232 while time.time() - start_time < wait_secs: | 232 while time.time() - start_time < wait_secs: |
| 233 if action(): | 233 if action(): |
| 234 return True | 234 return True |
| 235 _log.debug("Waiting for action: %s" % action) | 235 _log.debug("Waiting for action: %s", action) |
| 236 time.sleep(sleep_secs) | 236 time.sleep(sleep_secs) |
| 237 | 237 |
| 238 return False | 238 return False |
| 239 | 239 |
| 240 def _is_server_running_on_all_ports(self): | 240 def _is_server_running_on_all_ports(self): |
| 241 """Returns whether the server is running on all the desired ports.""" | 241 """Returns whether the server is running on all the desired ports.""" |
| 242 | 242 |
| 243 # TODO(dpranke): crbug/378444 maybe pid is unreliable on win? | 243 # TODO(dpranke): crbug/378444 maybe pid is unreliable on win? |
| 244 if not self._platform.is_win() and not self._executive.check_running_pid
(self._pid): | 244 if not self._platform.is_win() and not self._executive.check_running_pid
(self._pid): |
| 245 _log.debug("Server isn't running at all") | 245 _log.debug("Server isn't running at all") |
| 246 self._log_errors_from_subprocess() | 246 self._log_errors_from_subprocess() |
| 247 raise ServerError("Server exited") | 247 raise ServerError("Server exited") |
| 248 | 248 |
| 249 for mapping in self._mappings: | 249 for mapping in self._mappings: |
| 250 s = socket.socket() | 250 s = socket.socket() |
| 251 port = mapping['port'] | 251 port = mapping['port'] |
| 252 try: | 252 try: |
| 253 s.connect(('localhost', port)) | 253 s.connect(('localhost', port)) |
| 254 _log.debug("Server running on %d" % port) | 254 _log.debug("Server running on %d", port) |
| 255 except IOError as e: | 255 except IOError as e: |
| 256 if e.errno not in (errno.ECONNREFUSED, errno.ECONNRESET): | 256 if e.errno not in (errno.ECONNREFUSED, errno.ECONNRESET): |
| 257 raise | 257 raise |
| 258 _log.debug("Server NOT running on %d: %s" % (port, e)) | 258 _log.debug("Server NOT running on %d: %s", port, e) |
| 259 return False | 259 return False |
| 260 finally: | 260 finally: |
| 261 s.close() | 261 s.close() |
| 262 return True | 262 return True |
| 263 | 263 |
| 264 def _check_that_all_ports_are_available(self): | 264 def _check_that_all_ports_are_available(self): |
| 265 for mapping in self._mappings: | 265 for mapping in self._mappings: |
| 266 s = socket.socket() | 266 s = socket.socket() |
| 267 if not self._platform.is_win(): | 267 if not self._platform.is_win(): |
| 268 s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) | 268 s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) |
| 269 port = mapping['port'] | 269 port = mapping['port'] |
| 270 try: | 270 try: |
| 271 s.bind(('localhost', port)) | 271 s.bind(('localhost', port)) |
| 272 except IOError as e: | 272 except IOError as e: |
| 273 if e.errno in (errno.EALREADY, errno.EADDRINUSE): | 273 if e.errno in (errno.EALREADY, errno.EADDRINUSE): |
| 274 raise ServerError('Port %d is already in use.' % port) | 274 raise ServerError('Port %d is already in use.' % port) |
| 275 elif self._platform.is_win() and e.errno in (errno.WSAEACCES,):
# pylint: disable=E1101 | 275 elif self._platform.is_win() and e.errno in (errno.WSAEACCES,):
# pylint: disable=E1101 |
| 276 raise ServerError('Port %d is already in use.' % port) | 276 raise ServerError('Port %d is already in use.' % port) |
| 277 else: | 277 else: |
| 278 raise | 278 raise |
| 279 finally: | 279 finally: |
| 280 s.close() | 280 s.close() |
| 281 _log.debug('all ports are available') | 281 _log.debug('all ports are available') |
| OLD | NEW |