test of `_kill_srv` added, don't kill server if foreground;

pid_exists already available in Utils;
small bug and stability fixing;
pull/1483/head
sebres 2016-07-12 12:06:48 +02:00
parent 7242c9cbde
commit 0dc3db1f43
1 changed files with 54 additions and 27 deletions

View File

@ -98,7 +98,7 @@ INTERACT = []
def _test_input_command(*args): def _test_input_command(*args):
if len(INTERACT): if len(INTERACT):
#logSys.debug('interact command: %r', INTERACT[0]) #logSys.debug('--- interact command: %r', INTERACT[0])
return INTERACT.pop(0) return INTERACT.pop(0)
else: else:
return "exit" return "exit"
@ -179,15 +179,6 @@ def _start_params(tmp, use_stock=False, logtarget="/dev/null"):
) )
def _pid_exists(pid):
"""Check if PID exists by sending 0 signal to the PID process"""
try:
os.kill(pid, 0)
return True
except OSError:
return False
def _kill_srv(pidfile): def _kill_srv(pidfile):
logSys.debug("cleanup: %r", (pidfile, isdir(pidfile))) logSys.debug("cleanup: %r", (pidfile, isdir(pidfile)))
if isdir(pidfile): if isdir(pidfile):
@ -204,25 +195,32 @@ def _kill_srv(pidfile):
try: try:
logSys.debug("cleanup pidfile: %r", pidfile) logSys.debug("cleanup pidfile: %r", pidfile)
f = open(pidfile) f = open(pidfile)
pid = f.read().split()[1] pid = f.read()
pid = re.match(r'\S+', pid).group()
pid = int(pid) pid = int(pid)
logSys.debug("cleanup pid: %r", pid) except Exception as e: # pragma: no cover
if pid <= 0:
raise ValueError('pid %s of %s is invalid' % (pid, pidfile))
if not _pid_exists(pid):
return True
## try to prepare stop (have signal handler):
os.kill(pid, signal.SIGTERM)
## check still exists after small timeout:
if not Utils.wait_for(lambda: not _pid_exists(pid), 1):
## try to kill hereafter:
os.kill(pid, signal.SIGKILL)
return not _pid_exists(pid)
except Exception as e:
logSys.debug(e) logSys.debug(e)
return False
finally: finally:
if f is not None: if f is not None:
f.close() f.close()
try:
logSys.debug("cleanup pid: %r", pid)
if pid <= 0 or pid == os.getpid(): # pragma: no cover
raise ValueError('pid %s of %s is invalid' % (pid, pidfile))
if not Utils.pid_exists(pid):
return True
## try to properly stop (have signal handler):
os.kill(pid, signal.SIGTERM)
## check still exists after small timeout:
if not Utils.wait_for(lambda: not Utils.pid_exists(pid), 1):
## try to kill hereafter:
os.kill(pid, signal.SIGKILL)
logSys.debug("cleanup: kill ready")
return not Utils.pid_exists(pid)
except Exception as e: # pragma: no cover
logSys.exception(e)
return True return True
@ -310,11 +308,11 @@ class Fail2banClientServerBase(LogCaptureTestCase):
def testStartForeground(self, tmp): def testStartForeground(self, tmp):
# intended to be ran only in subclasses # intended to be ran only in subclasses
th = None th = None
phase = dict()
try: try:
# started directly here, so prevent overwrite test cases logger with "INHERITED" # started directly here, so prevent overwrite test cases logger with "INHERITED"
startparams = _start_params(tmp, logtarget="INHERITED") startparams = _start_params(tmp, logtarget="INHERITED")
# because foreground block execution - start it in thread: # because foreground block execution - start it in thread:
phase = dict()
th = Thread( th = Thread(
name="_TestCaseWorker", name="_TestCaseWorker",
target=self._testStartForeground, target=self._testStartForeground,
@ -342,9 +340,11 @@ class Fail2banClientServerBase(LogCaptureTestCase):
self.assertTrue(phase.get('end', None)) self.assertTrue(phase.get('end', None))
self.assertLogged("Shutdown successful", "Exiting Fail2ban") self.assertLogged("Shutdown successful", "Exiting Fail2ban")
finally: finally:
_kill_srv(tmp)
if th: if th:
th.join() # we start client/server directly in current process (new thread),
# so don't kill (same process) - if success, just wait for end of worker:
if phase.get('end', None):
th.join()
class Fail2banClientTest(Fail2banClientServerBase): class Fail2banClientTest(Fail2banClientServerBase):
@ -586,3 +586,30 @@ class Fail2banServerTest(Fail2banClientServerBase):
self.assertLogged("Fail2ban seems to be in unexpected state (not running but the socket exists)") self.assertLogged("Fail2ban seems to be in unexpected state (not running but the socket exists)")
self.pruneLog() self.pruneLog()
os.remove(pjoin(tmp, "f2b.sock")) os.remove(pjoin(tmp, "f2b.sock"))
@with_tmpdir
def testKillAfterStart(self, tmp):
try:
# to prevent fork of test-cases process, start server in background via command:
startparams = _start_params(tmp, logtarget=pjoin(tmp, "f2b.log"))
# start (in new process, using the same python version):
cmd = (sys.executable, pjoin(BIN, SERVER))
logSys.debug('Start %s ...', cmd)
cmd = cmd + startparams + ("-b",)
ret = Utils.executeCmd(cmd, timeout=MAX_WAITTIME, shell=False, output=True)
self.assertTrue(len(ret) and ret[0])
# wait for server (socket and ready):
self._wait_for_srv(tmp, True, startparams=cmd)
self.assertLogged("Server ready")
self.pruneLog()
logSys.debug('Kill server ... %s', tmp)
finally:
self.assertTrue(_kill_srv(tmp))
# wait for end (kill was successful):
Utils.wait_for(lambda: not isfile(pjoin(tmp, "f2b.pid")), MAX_WAITTIME)
self.assertFalse(isfile(pjoin(tmp, "f2b.pid")))
self.assertLogged("cleanup: kill ready")
self.pruneLog()
# again:
self.assertTrue(_kill_srv(tmp))
self.assertLogged("cleanup: no pidfile for")