Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[PREVIEW] Show the possibly listening process in the AlreadyRunning exception message #158

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 39 additions & 1 deletion src/mirakuru/exceptions.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
"""Mirakuru exceptions."""

import psutil


class ExecutorError(Exception):
"""Base exception for executor failures."""
Expand Down Expand Up @@ -45,8 +47,43 @@ class AlreadyRunning(ExecutorError):

When some other process (not necessary executor) seems to be started with
same configuration we can't bind to same port.

The exception message is less useful for executors not using TCP ports.
"""

def __init__(self, executor):
"""
Initialize and detect what service is listening on the configured port.

:param mirakuru.base.Executor executor: for which exception occurred
"""
super(AlreadyRunning, self).__init__(executor)
try:
self.port = getattr(executor, 'port')
except AttributeError:
self.port = None
if self.port:
# The pid field contains an integer process ID or None if the
# process belongs to a different user. Multiple processes can
# listen on the same port.
pids = [
sconn.pid
for sconn in psutil.net_connections(kind='tcp')
if sconn.pid and sconn.laddr[1] == self.port]
processes = []
for pid in pids:
try:
processes.append(
'%r[%d]' % (psutil.Process(pid).cmdline(), pid))
except psutil.NoSuchProcess:
processes.append('exited[%d]' % pid)
if not processes:
self.listening_service = '[unknown]'
else:
self.listening_service = ' '.join(processes)
else:
self.listening_service = '[unknown]'

def __str__(self):
"""
Return Exception's string representation.
Expand All @@ -57,7 +94,8 @@ def __str__(self):
return ("Executor {exc.executor} seems to be already running. "
"It looks like the previous executor process hasn't been "
"terminated or killed. Also there might be some completely "
"different service listening on {exc.executor.port} port."
"different service listening on {exc.port} port. Services "
"found running on that port: {exc.listening_service}."
.format(exc=self))


Expand Down
4 changes: 4 additions & 0 deletions tests/executors/test_http_executor.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import sys
import socket
from functools import partial
import shlex

import pytest
from mock import patch
Expand Down Expand Up @@ -124,6 +125,9 @@ def test_fail_if_other_executor_running():
with executor2:
pass
assert 'seems to be already running' in str(exc)
expected_process = '%r[%d]' % (
shlex.split(http_server_cmd), executor.process.pid)
assert expected_process in str(exc)


@patch.object(HTTPExecutor, 'DEFAULT_PORT', PORT)
Expand Down