From patchwork Sun Apr 19 18:11:02 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Fr=C3=A9d=C3=A9ric_Mangano-Tarumi?= X-Patchwork-Id: 1598 Return-Path: Delivered-To: patchwork@archlinux.org Received: from apollo.archlinux.org (localhost [127.0.0.1]) by apollo.archlinux.org (Postfix) with ESMTP id D59A01849B80C for ; Sun, 19 Apr 2020 18:11:12 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on apollo.archlinux.org X-Spam-Level: X-Spam-Status: No, score=-0.6 required=5.0 tests=DKIM_INVALID=1, DKIM_SIGNED=0.1,MAILING_LIST_MULTI=-1,RCVD_IN_DNSWL_LOW=-0.7, SPF_HELO_NONE=0.001 autolearn=ham autolearn_force=no version=3.4.4 X-Spam-BL-Results: [127.0.9.1] Received: from orion.archlinux.org (orion.archlinux.org [IPv6:2a01:4f8:160:6087::1]) by apollo.archlinux.org (Postfix) with ESMTPS for ; Sun, 19 Apr 2020 18:11:12 +0000 (UTC) Received: from orion.archlinux.org (localhost [127.0.0.1]) by orion.archlinux.org (Postfix) with ESMTP id E407D1B19C913C; Sun, 19 Apr 2020 18:11:10 +0000 (UTC) Received: from luna.archlinux.org (luna.archlinux.org [IPv6:2a01:4f8:160:3033::2]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange ECDHE (P-384) server-signature RSA-PSS (4096 bits)) (No client certificate requested) (Authenticated sender: luna) by orion.archlinux.org (Postfix) with ESMTPSA id C4F4D1B19C9139; Sun, 19 Apr 2020 18:11:10 +0000 (UTC) Authentication-Results: orion.archlinux.org; dkim=fail reason="signature verification failed" (1024-bit key) header.d=mg0.fr header.i=@mg0.fr header.b=MKL2pHvj Received: from luna.archlinux.org (luna.archlinux.org [127.0.0.1]) by luna.archlinux.org (Postfix) with ESMTP id B73F92A93F; Sun, 19 Apr 2020 18:11:10 +0000 (UTC) Authentication-Results: luna.archlinux.org; dkim=fail reason="signature verification failed" (1024-bit key) header.d=mg0.fr header.i=@mg0.fr header.b=MKL2pHvj Received: from luna.archlinux.org (luna.archlinux.org [127.0.0.1]) by luna.archlinux.org (Postfix) with ESMTP id 318ED2A8F1 for ; Sun, 19 Apr 2020 18:11:08 +0000 (UTC) Received: from orion.archlinux.org (orion.archlinux.org [88.198.91.70]) by luna.archlinux.org (Postfix) with ESMTPS for ; Sun, 19 Apr 2020 18:11:08 +0000 (UTC) Received: from orion.archlinux.org (localhost [127.0.0.1]) by orion.archlinux.org (Postfix) with ESMTP id 291B41B19C9135 for ; Sun, 19 Apr 2020 18:11:04 +0000 (UTC) Received: from tsubame.mg0.fr (tsubame.mg0.fr [IPv6:2001:41d0:401:3100::402b]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange ECDHE (P-384) server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by orion.archlinux.org (Postfix) with ESMTPS for ; Sun, 19 Apr 2020 18:11:04 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=mg0.fr; s=tsubame; h=Content-Transfer-Encoding:Content-Type:MIME-Version:Message-ID: Subject:To:From:Date:Sender:Reply-To:Cc:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: In-Reply-To:References:List-Id:List-Help:List-Unsubscribe:List-Subscribe: List-Post:List-Owner:List-Archive; bh=scDKjKHyR+3HglvOlaz8WxSz5BjY0sqgwP+/wju5TYo=; b=MKL2pHvjFmFD+GzyFpLkV46W3U FfNUGRLUwxuyynxsPq9JJLproIXpT3FsZeaSMGeATBoKTEcovRuZPr9u/XzA50sqt2V1q+g74Ip/m 8gNFvE7cAkHMbLeLTYcsbmI+af1GF80L2C3P7g9eRwdvxuOWt/zJeV8GPIbIr5Pzm5Nw=; Received: from fmang by tsubame.mg0.fr with local (Exim 4.93) (envelope-from ) id 1jQEPG-00EV3Y-Du for aur-dev@archlinux.org; Sun, 19 Apr 2020 20:11:02 +0200 Date: Sun, 19 Apr 2020 20:11:02 +0200 From: =?utf-8?b?RnLDqWTDqXJpYw==?= Mangano-Tarumi To: aur-dev@archlinux.org Subject: [PATCH] Create aurweb.spawn for spawing the test server Message-ID: <20200419181102.GA3455958@tsubame.mg0.fr> MIME-Version: 1.0 Content-Disposition: inline X-BeenThere: aur-dev@archlinux.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: "Arch User Repository \(AUR\) Development" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: aur-dev-bounces@archlinux.org Sender: "aur-dev" This program makes it easier for developers to spawn the PHP server since it fetches automatically what it needs from the configuration file, rather than having the user explicitly pass arguments to the php executable. When the setup gets more complicated as we introduce Python, aurweb.spawn will keep providing the same interface, while under the hood it is planned to support running multiple sub-processes. Its Python interface provides an way for the test suite to spawn the test server when it needs to perform HTTP requests to the test server. The current implementation is somewhat weak as it doesn’t detect when a child process dies, but this is not supposed to happen often, and it is only meant for aurweb developers. In the long term, aurweb.spawn will eventually become obsolete, and replaced by Docker or Flask’s tools. --- TESTING | 7 +-- aurweb/spawn.py | 107 +++++++++++++++++++++++++++++++++++++++++++ conf/config.defaults | 3 ++ 3 files changed, 114 insertions(+), 3 deletions(-) create mode 100644 aurweb/spawn.py diff --git a/TESTING b/TESTING index 4a1e6f4c..a5e08cb8 100644 --- a/TESTING +++ b/TESTING @@ -17,7 +17,8 @@ INSTALL. Ensure to enable the pdo_sqlite extension in php.ini. 3) Copy conf/config.defaults to conf/config and adjust the configuration - (pay attention to disable_http_login, enable_maintenance and aur_location). + Pay attention to disable_http_login, enable_maintenance, aur_location and + htmldir. Be sure to change backend to sqlite and name to the file location of your created test database. @@ -31,6 +32,6 @@ INSTALL. $ ./gendummydata.py out.sql $ sqlite3 path/to/aurweb.sqlite3 < out.sql -5) Run the PHP built-in web server: +5) Run the test server: - $ AUR_CONFIG='/path/to/aurweb/conf/config' php -S localhost:8080 -t /path/to/aurweb/web/html + $ AUR_CONFIG='/path/to/aurweb/conf/config' python -m aurweb.spawn diff --git a/aurweb/spawn.py b/aurweb/spawn.py new file mode 100644 index 00000000..5fa646b5 --- /dev/null +++ b/aurweb/spawn.py @@ -0,0 +1,107 @@ +""" +Provide an automatic way of spawing an HTTP test server running aurweb. + +It can be called from the command-line or from another Python module. + +This module uses a global state, since you can’t open two servers with the same +configuration anyway. +""" + + +import atexit +import argparse +import subprocess +import sys +import time +import urllib + +import aurweb.config +import aurweb.schema + + +children = [] +verbosity = 0 + + +class ProcessExceptions(Exception): + """ + Compound exception used by stop() to list all the errors that happened when + terminating child processes. + """ + def __init__(self, message, exceptions): + self.message = message + self.exceptions = exceptions + messages = [message] + [str(e) for e in exceptions] + super().__init__("\n- ".join(messages)) + + +def spawn_child(args): + """Open a subprocess and add it to the global state.""" + if verbosity >= 1: + print(f"Spawning {args}", file=sys.stderr) + children.append(subprocess.Popen(args)) + + +def start(): + """ + Spawn the test server. If it is already running, do nothing. + + The server can be stopped with stop(), or is automatically stopped when the + Python process ends using atexit. + """ + if children: + return + atexit.register(stop) + aur_location = aurweb.config.get("options", "aur_location") + aur_location_parts = urllib.parse.urlsplit(aur_location) + htmldir = aurweb.config.get("options", "htmldir") + spawn_child(["php", "-S", aur_location_parts.netloc, "-t", htmldir]) + + +def stop(): + """ + Stop all the child processes. + + If an exception occurs during the process, the process continues anyway + because we don’t want to leave runaway processes around, and all the + exceptions are finally raised as a single ProcessExceptions. + """ + global children + atexit.unregister(stop) + exceptions = [] + for p in children: + try: + p.terminate() + if verbosity >= 1: + print(f"Sent SIGTERM to {p.args}", file=sys.stderr) + except Exception as e: + exceptions.append(e) + for p in children: + try: + rc = p.wait() + if rc != 0 and rc != -15: + # rc = -15 indicates the process was terminated with SIGTERM, + # which is to be expected since we called terminate on them. + raise Exception(f"Process {p.args} exited with {rc}") + except Exception as e: + exceptions.append(e) + children = [] + if exceptions: + raise ProcessExceptions("Errors terminating the child processes:", + exceptions) + + +if __name__ == '__main__': + parser = argparse.ArgumentParser( + prog='python -m aurweb.spawn', + description='Start aurweb\'s test server.') + parser.add_argument('-v', '--verbose', action='count', default=0, + help='increase verbosity') + args = parser.parse_args() + verbosity = args.verbose + start() + try: + while True: + time.sleep(60) + except KeyboardInterrupt: + stop() diff --git a/conf/config.defaults b/conf/config.defaults index 447dacac..86fe765c 100644 --- a/conf/config.defaults +++ b/conf/config.defaults @@ -41,6 +41,9 @@ cache = none cache_pkginfo_ttl = 86400 memcache_servers = 127.0.0.1:11211 +; Directory containing aurweb's PHP code, required by aurweb.spawn. +;htmldir = /path/to/web/html + [ratelimit] request_limit = 4000 window_length = 86400