2023-02-11 17:23:57 +01:00
|
|
|
#!./venv/bin/python3
|
|
|
|
# Copyright 2023 Julian Müller (ChaoticByte)
|
|
|
|
|
|
|
|
import os
|
|
|
|
|
|
|
|
from argparse import ArgumentParser
|
|
|
|
from atexit import register as register_exithandler
|
|
|
|
from pathlib import Path
|
|
|
|
from signal import SIGINT
|
|
|
|
from subprocess import Popen
|
|
|
|
from sys import path as sys_path
|
2023-02-11 18:04:52 +01:00
|
|
|
from sys import stdout, stderr
|
2023-02-11 17:23:57 +01:00
|
|
|
from time import sleep
|
|
|
|
|
|
|
|
from yaml import safe_load
|
|
|
|
|
|
|
|
|
|
|
|
base_directory = Path(__file__).parent.parent
|
|
|
|
data_directory = base_directory / "data"
|
|
|
|
logfile_directory = data_directory / "logs"
|
|
|
|
configuration_file = data_directory / "config.yml"
|
|
|
|
caddyfile = data_directory / "Caddyfile"
|
|
|
|
logfile_caddy = logfile_directory / "caddy.log"
|
|
|
|
logfile_app = logfile_directory / "app.log"
|
|
|
|
|
|
|
|
|
|
|
|
class MonitoredSubprocess:
|
|
|
|
def __init__(
|
|
|
|
self,
|
|
|
|
name: str,
|
|
|
|
commandline: list,
|
|
|
|
logfile: Path,
|
|
|
|
environment: dict = os.environ,
|
|
|
|
max_tries: int = 5,
|
|
|
|
):
|
|
|
|
self.name = name
|
|
|
|
self.commandline = commandline
|
|
|
|
self.logfile = logfile
|
|
|
|
self.environment = environment
|
|
|
|
self.max_tries = max_tries
|
|
|
|
self.s = None # the subprocess object
|
|
|
|
self._tries = 0
|
|
|
|
self._stopped = False
|
|
|
|
|
|
|
|
def try_start(self):
|
|
|
|
if self._tries < self.max_tries:
|
|
|
|
self._tries += 1
|
|
|
|
print(f"Starting {self.name}...")
|
2023-02-11 18:04:52 +01:00
|
|
|
if self.logfile is None:
|
2023-02-11 17:23:57 +01:00
|
|
|
self.s = Popen(
|
|
|
|
self.commandline,
|
2023-02-11 18:04:52 +01:00
|
|
|
stdout=stdout.buffer,
|
|
|
|
stderr=stderr.buffer,
|
2023-02-11 17:23:57 +01:00
|
|
|
env=self.environment)
|
2023-02-11 18:04:52 +01:00
|
|
|
else:
|
|
|
|
with self.logfile.open("ab") as l:
|
|
|
|
self.s = Popen(
|
|
|
|
self.commandline,
|
|
|
|
stdout=l,
|
|
|
|
stderr=l,
|
|
|
|
env=self.environment)
|
2023-02-11 17:23:57 +01:00
|
|
|
return True
|
|
|
|
else:
|
|
|
|
print(f"Max. tries exceeded ({self.name})!")
|
|
|
|
# the process must already be stopped at this
|
|
|
|
# point, so we can set the variable accordingly
|
|
|
|
self._stopped = True
|
|
|
|
return False
|
|
|
|
|
|
|
|
def stop(self):
|
|
|
|
if not self._stopped:
|
|
|
|
print(f"Stopping {self.name}...")
|
|
|
|
self.s.terminate()
|
|
|
|
self._stopped = True
|
|
|
|
|
|
|
|
|
|
|
|
def cleanup_procs(processes):
|
|
|
|
for p in processes:
|
|
|
|
p.stop()
|
|
|
|
|
|
|
|
|
2023-02-11 18:04:52 +01:00
|
|
|
def start_and_monitor(monitored_subprocesses: list):
|
|
|
|
# start processes
|
|
|
|
for p in monitored_subprocesses:
|
|
|
|
p.try_start()
|
|
|
|
register_exithandler(cleanup_procs, monitored_subprocesses)
|
|
|
|
# monitor processes
|
|
|
|
try:
|
|
|
|
while True:
|
|
|
|
sleep(1)
|
|
|
|
for p in monitored_subprocesses:
|
|
|
|
returncode = p.s.poll()
|
|
|
|
if returncode is None:
|
|
|
|
continue
|
|
|
|
else:
|
|
|
|
print(f"{p.name} stopped with exit code {returncode}.")
|
|
|
|
if p.try_start() is False:
|
|
|
|
# stop everything if the process
|
|
|
|
# has exceeded max. tries
|
|
|
|
exit()
|
|
|
|
except KeyboardInterrupt:
|
|
|
|
print("Received KeyboardInterrupt, exiting...")
|
|
|
|
exit()
|
|
|
|
|
|
|
|
|
2023-02-11 17:23:57 +01:00
|
|
|
if __name__ == "__main__":
|
|
|
|
argp = ArgumentParser()
|
|
|
|
argp.add_argument("--devel", help="Start development server", action="store_true")
|
|
|
|
args = argp.parse_args()
|
|
|
|
# Load configuration
|
|
|
|
with configuration_file.open("r") as f:
|
|
|
|
config = safe_load(f)
|
|
|
|
# Prepare
|
|
|
|
os.chdir(str(base_directory))
|
|
|
|
Popen(
|
|
|
|
["./venv/bin/python3", "./manage.py", "collectstatic", "--noinput"], env=os.environ).wait()
|
|
|
|
Popen(
|
|
|
|
["./venv/bin/python3", "./manage.py", "migrate", "--noinput"], env=os.environ).wait()
|
2023-02-11 18:04:52 +01:00
|
|
|
# Caddy configuration via env
|
|
|
|
environment_caddy = os.environ
|
|
|
|
environment_caddy["DATADIR"] = str(data_directory.absolute())
|
|
|
|
environment_caddy["CADDY_HOST"] = str(config["caddy"]["host"])
|
|
|
|
environment_caddy["HTTP_PORT"] = str(config["caddy"]["http_port"])
|
|
|
|
environment_caddy["HTTPS_PORT"] = str(config["caddy"]["https_port"])
|
|
|
|
environment_caddy["APPLICATION_PORT"] = str(config["app"]["application_port"])
|
|
|
|
environment_caddy["ACCESS_LOG"] = config["logs"]["http_access"]
|
2023-02-11 17:23:57 +01:00
|
|
|
# Start
|
|
|
|
if args.devel:
|
2023-02-11 18:04:52 +01:00
|
|
|
procs = [
|
|
|
|
MonitoredSubprocess(
|
|
|
|
"Caddy Webserver",
|
|
|
|
["caddy", "run", "--config", str(caddyfile)],
|
|
|
|
None,
|
2023-02-11 21:22:52 +01:00
|
|
|
environment=environment_caddy),
|
2023-02-11 18:04:52 +01:00
|
|
|
MonitoredSubprocess(
|
|
|
|
"Django Development Server",
|
|
|
|
["./venv/bin/python3", "./manage.py", "runserver", str(config["app"]["application_port"])],
|
2023-02-11 21:22:52 +01:00
|
|
|
None),
|
|
|
|
MonitoredSubprocess(
|
|
|
|
"Session Autocleaner",
|
|
|
|
["./scripts/_session-autocleaner.py", str(config["app"]["session_clear_interval"])],
|
|
|
|
None)
|
2023-02-11 18:04:52 +01:00
|
|
|
]
|
|
|
|
start_and_monitor(procs)
|
2023-02-11 17:23:57 +01:00
|
|
|
else:
|
|
|
|
# Application configuration via env
|
|
|
|
environment_app = os.environ
|
|
|
|
environment_app["APP_PROD"] = "1"
|
|
|
|
print("\nRunning in production mode.\n")
|
|
|
|
# define processes
|
|
|
|
procs = [
|
|
|
|
MonitoredSubprocess(
|
|
|
|
"Caddy Webserver",
|
|
|
|
["caddy", "run", "--config", str(caddyfile)],
|
|
|
|
logfile_caddy,
|
2023-02-11 21:22:52 +01:00
|
|
|
environment=environment_caddy),
|
2023-02-11 17:23:57 +01:00
|
|
|
MonitoredSubprocess(
|
|
|
|
"Drinks-Manager",
|
|
|
|
[
|
|
|
|
"./venv/bin/python3",
|
|
|
|
"-m",
|
|
|
|
"uvicorn",
|
|
|
|
"--host",
|
|
|
|
"127.0.0.1",
|
|
|
|
"--port",
|
|
|
|
str(config["app"]["application_port"]),
|
|
|
|
"project.asgi:application",
|
|
|
|
],
|
|
|
|
logfile_app,
|
2023-02-11 21:22:52 +01:00
|
|
|
environment=environment_app),
|
|
|
|
MonitoredSubprocess(
|
|
|
|
"Session Autocleaner",
|
|
|
|
["./scripts/_session-autocleaner.py", str(config["app"]["session_clear_interval"])],
|
|
|
|
logfile_app)
|
2023-02-11 17:23:57 +01:00
|
|
|
]
|
2023-02-11 18:04:52 +01:00
|
|
|
start_and_monitor(procs)
|