irctest/irctest/basecontrollers.py

115 lines
4.2 KiB
Python
Raw Normal View History

2015-12-19 22:09:06 +00:00
import os
import shutil
import socket
import tempfile
import time
2015-12-19 22:09:06 +00:00
import subprocess
from .runner import NotImplementedByController
2015-12-20 12:12:54 +00:00
2015-12-19 00:11:57 +00:00
class _BaseController:
2015-12-20 12:47:30 +00:00
"""Base class for software controllers.
A software controller is an object that handles configuring and running
a process (eg. a server or a client), as well as sending it instructions
that are not part of the IRC specification."""
def __init__(self, test_config):
self.test_config = test_config
2015-12-19 00:11:57 +00:00
2015-12-19 22:09:06 +00:00
class DirectoryBasedController(_BaseController):
2015-12-20 12:47:30 +00:00
"""Helper for controllers whose software configuration is based on an
arbitrary directory."""
def __init__(self, test_config):
super().__init__(test_config)
2015-12-19 22:09:06 +00:00
self.directory = None
self.proc = None
2015-12-20 00:48:56 +00:00
def kill_proc(self):
2015-12-20 12:47:30 +00:00
"""Terminates the controlled process, waits for it to exit, and
eventually kills it."""
2015-12-20 00:48:56 +00:00
self.proc.terminate()
try:
self.proc.wait(5)
except subprocess.TimeoutExpired:
self.proc.kill()
self.proc = None
2015-12-19 22:09:06 +00:00
def kill(self):
2015-12-20 12:47:30 +00:00
"""Calls `kill_proc` and cleans the configuration."""
2015-12-19 22:09:06 +00:00
if self.proc:
2015-12-20 00:48:56 +00:00
self.kill_proc()
2015-12-19 22:09:06 +00:00
if self.directory:
shutil.rmtree(self.directory)
2019-12-08 20:26:21 +00:00
def terminate(self):
"""Stops the process gracefully, and does not clean its config."""
self.proc.terminate()
self.proc.wait()
self.proc = None
2015-12-19 22:09:06 +00:00
def open_file(self, name, mode='a'):
2015-12-20 12:47:30 +00:00
"""Open a file in the configuration directory."""
2015-12-19 22:09:06 +00:00
assert self.directory
if os.sep in name:
dir_ = os.path.join(self.directory, os.path.dirname(name))
if not os.path.isdir(dir_):
os.makedirs(dir_)
assert os.path.isdir(dir_)
return open(os.path.join(self.directory, name), mode)
def create_config(self):
2019-12-08 20:26:21 +00:00
"""If there is no config dir, creates it and returns True.
Else returns False."""
if self.directory:
return False
else:
self.directory = tempfile.mkdtemp()
return True
2015-12-19 22:09:06 +00:00
2015-12-25 14:45:06 +00:00
def gen_ssl(self):
self.csr_path = os.path.join(self.directory, 'ssl.csr')
self.key_path = os.path.join(self.directory, 'ssl.key')
self.pem_path = os.path.join(self.directory, 'ssl.pem')
self.dh_path = os.path.join(self.directory, 'dh.pem')
subprocess.check_output([self.openssl_bin, 'req', '-new', '-newkey', 'rsa',
2015-12-25 14:45:06 +00:00
'-nodes', '-out', self.csr_path, '-keyout', self.key_path,
'-batch'],
stderr=subprocess.DEVNULL)
subprocess.check_output([self.openssl_bin, 'x509', '-req',
2015-12-25 14:45:06 +00:00
'-in', self.csr_path, '-signkey', self.key_path,
'-out', self.pem_path],
stderr=subprocess.DEVNULL)
subprocess.check_output([self.openssl_bin, 'dhparam',
2015-12-25 14:45:06 +00:00
'-out', self.dh_path, '128'],
stderr=subprocess.DEVNULL)
2015-12-19 00:11:57 +00:00
class BaseClientController(_BaseController):
2015-12-20 12:47:30 +00:00
"""Base controller for IRC clients."""
2015-12-19 16:52:38 +00:00
def run(self, hostname, port, auth):
2015-12-19 00:11:57 +00:00
raise NotImplementedError()
class BaseServerController(_BaseController):
2015-12-20 12:47:30 +00:00
"""Base controller for IRC server."""
_port_wait_interval = .1
port_open = False
2015-12-22 21:33:23 +00:00
def run(self, hostname, port, password,
valid_metadata_keys, invalid_metadata_keys):
2015-12-20 12:12:54 +00:00
raise NotImplementedError()
2015-12-20 14:11:56 +00:00
def registerUser(self, case, username, password=None):
raise NotImplementedByController('account registration')
def wait_for_port(self):
while not self.port_open:
time.sleep(self._port_wait_interval)
2020-09-13 10:47:50 +00:00
try:
c = socket.create_connection(('localhost', self.port), timeout=1.0)
c.setsockopt(socket.SOL_TCP, socket.TCP_NODELAY, 1)
# Make sure the server properly processes the disconnect.
# Otherwise, it may still count it in LUSER and fail tests in
# test_lusers.py (eg. this happens with Charybdis 3.5.0)
c.send(b"QUIT :chkport\r\n")
data = b""
while b"chkport" not in data:
data += c.recv(1024)
2020-09-13 10:47:50 +00:00
c.close()
self.port_open = True
except Exception as e:
continue