2022-09-07 22:12:55 +00:00
|
|
|
#!/usr/bin/env python3
|
2022-07-31 02:33:31 +00:00
|
|
|
|
|
|
|
import subprocess # nosec 404
|
|
|
|
import sys
|
|
|
|
from argparse import ArgumentParser
|
|
|
|
from pathlib import Path
|
|
|
|
|
2022-08-02 11:39:44 +00:00
|
|
|
# Variables
|
|
|
|
# You might want to change some.
|
|
|
|
|
|
|
|
# Volumes
|
2022-07-31 02:33:31 +00:00
|
|
|
VOLUMES_DIR = Path.home() / "volumes"
|
2022-08-02 11:39:44 +00:00
|
|
|
# AdvLabDB
|
2022-08-18 14:46:10 +00:00
|
|
|
ADVLABDB_REPO_LINK = "https://codeberg.org/Mo8it/AdvLabDB.git"
|
2022-07-31 02:33:31 +00:00
|
|
|
ADVLABDB_VOLUMES_DIR = VOLUMES_DIR / "advlabdb"
|
|
|
|
ADVLABDB_REPO_DIR = ADVLABDB_VOLUMES_DIR / "repo"
|
|
|
|
ADVLABDB_LOGS_DIR = ADVLABDB_VOLUMES_DIR / "logs"
|
2022-08-18 01:20:52 +00:00
|
|
|
ADVLABDB_DATA_DIR = ADVLABDB_VOLUMES_DIR / "data"
|
2022-09-10 16:35:57 +00:00
|
|
|
# Nginx
|
|
|
|
NGINX_CONF_D_DIR = ADVLABDB_VOLUMES_DIR / "nginx/conf.d"
|
2022-08-02 11:39:44 +00:00
|
|
|
# Traefik
|
2022-07-31 02:33:31 +00:00
|
|
|
TRAEFIK_VOLUMES_DIR = VOLUMES_DIR / "traefik"
|
|
|
|
TRAEFIK_ETC_DIR = TRAEFIK_VOLUMES_DIR / "etc"
|
|
|
|
TRAEFIK_LOGS_DIR = TRAEFIK_VOLUMES_DIR / "logs"
|
|
|
|
TRAEFIK_CERTS_DIR = TRAEFIK_VOLUMES_DIR / "certs"
|
2022-08-02 11:39:44 +00:00
|
|
|
# Systemd
|
2022-07-31 02:33:31 +00:00
|
|
|
SYSTEMD_USER_DIR = Path.home() / ".config/systemd/user"
|
|
|
|
|
|
|
|
|
2022-08-02 11:39:44 +00:00
|
|
|
# Parse script arguments
|
|
|
|
|
|
|
|
parser = ArgumentParser(
|
|
|
|
description="Build the AdvLabDB image and deploy related containers.",
|
|
|
|
)
|
|
|
|
parser.add_argument(
|
|
|
|
"--skip-traefik",
|
|
|
|
action="store_true",
|
|
|
|
help="Do not deploy the Traefik container.",
|
|
|
|
)
|
|
|
|
parser.add_argument(
|
|
|
|
"-n",
|
|
|
|
"--network",
|
|
|
|
default="traefik",
|
|
|
|
help="Traefik's network. Default: traefik.",
|
|
|
|
)
|
|
|
|
parser.add_argument(
|
|
|
|
"-v",
|
|
|
|
"--verbose",
|
|
|
|
action="store_true",
|
|
|
|
help="Show more information while running.",
|
|
|
|
)
|
|
|
|
|
|
|
|
args = parser.parse_args()
|
|
|
|
|
|
|
|
# Functions
|
|
|
|
|
|
|
|
|
2022-07-31 02:33:31 +00:00
|
|
|
def run(command: str, **kwargs):
|
2022-08-18 01:58:40 +00:00
|
|
|
print("\n\nRunning command:")
|
|
|
|
print(f"\t{command}\n")
|
2022-07-31 02:33:31 +00:00
|
|
|
return subprocess.run(command, shell=True, **kwargs) # nosec B602
|
|
|
|
|
|
|
|
|
2022-08-02 11:39:44 +00:00
|
|
|
# Needed for create_container
|
|
|
|
if args.verbose:
|
|
|
|
print(f"Making sure that the systemd directory for user services {SYSTEMD_USER_DIR} exists.")
|
2022-07-31 02:33:31 +00:00
|
|
|
SYSTEMD_USER_DIR.mkdir(parents=True, exist_ok=True)
|
|
|
|
|
|
|
|
|
|
|
|
def create_container(container_name: str, podman_args: str):
|
2022-08-02 11:39:44 +00:00
|
|
|
"""
|
|
|
|
Create a container with a systemd service.
|
|
|
|
"""
|
|
|
|
print(f"Creating container: {container_name}.")
|
2022-07-31 02:33:31 +00:00
|
|
|
run(
|
|
|
|
f"""podman create \
|
|
|
|
--name {container_name} \
|
2022-08-02 12:33:20 +00:00
|
|
|
--tz local \
|
2022-07-31 02:33:31 +00:00
|
|
|
{podman_args}""",
|
|
|
|
check=True,
|
|
|
|
)
|
|
|
|
|
2022-08-02 11:39:44 +00:00
|
|
|
print(f"Generating a systemd service then enabling and starting it for the container {container_name}.")
|
2022-08-18 01:20:52 +00:00
|
|
|
print(f"The service is a user service named container-{container_name}.")
|
2022-08-02 11:39:44 +00:00
|
|
|
print("You can check its status with the following command:")
|
|
|
|
print(f"\tsystemctl --user status container-{container_name}")
|
2022-07-31 02:33:31 +00:00
|
|
|
run(
|
|
|
|
f"podman generate systemd --new --files --name {container_name}",
|
|
|
|
cwd=SYSTEMD_USER_DIR,
|
|
|
|
check=True,
|
|
|
|
)
|
|
|
|
run(
|
|
|
|
f"systemctl --user enable --now container-{container_name}",
|
|
|
|
check=True,
|
|
|
|
)
|
|
|
|
|
|
|
|
|
2022-08-18 01:20:52 +00:00
|
|
|
def pull_or_clone_repo():
|
|
|
|
if ADVLABDB_REPO_DIR.is_dir():
|
|
|
|
print("Pulling AdvLabDB repository.")
|
2022-07-31 02:33:31 +00:00
|
|
|
run(
|
2022-08-18 01:20:52 +00:00
|
|
|
"git pull --rebase origin main",
|
|
|
|
cwd=ADVLABDB_REPO_DIR,
|
2022-07-31 02:33:31 +00:00
|
|
|
check=True,
|
|
|
|
)
|
2022-08-18 01:20:52 +00:00
|
|
|
else:
|
|
|
|
if args.verbose:
|
|
|
|
print(f"Making sure that the volumes directory {ADVLABDB_VOLUMES_DIR} exists.")
|
|
|
|
ADVLABDB_VOLUMES_DIR.mkdir(parents=True, exist_ok=True)
|
2022-07-31 02:33:31 +00:00
|
|
|
|
2022-08-18 01:20:52 +00:00
|
|
|
print("Cloning AdvLabDB repository")
|
|
|
|
print(f"From:\t{ADVLABDB_REPO_LINK}")
|
|
|
|
print(f"Into:\t{ADVLABDB_REPO_DIR}")
|
|
|
|
run(
|
|
|
|
f"git clone {ADVLABDB_REPO_LINK} {ADVLABDB_REPO_DIR}",
|
|
|
|
check=True,
|
|
|
|
)
|
2022-08-02 11:39:44 +00:00
|
|
|
|
2022-07-31 02:33:31 +00:00
|
|
|
|
2022-08-18 01:20:52 +00:00
|
|
|
def check_requirements():
|
|
|
|
settings_file = ADVLABDB_DATA_DIR / "settings.ini"
|
|
|
|
if not settings_file.is_file():
|
|
|
|
sys.exit(f"{settings_file} missing!")
|
2022-07-31 02:33:31 +00:00
|
|
|
|
2022-09-10 16:35:57 +00:00
|
|
|
if not NGINX_CONF_D_DIR.is_dir():
|
|
|
|
sys.exit(f"{NGINX_CONF_D_DIR} missing!")
|
|
|
|
|
2022-08-18 01:20:52 +00:00
|
|
|
if not args.skip_traefik:
|
|
|
|
if not TRAEFIK_ETC_DIR.is_dir():
|
|
|
|
sys.exit(f"{TRAEFIK_ETC_DIR} missing!")
|
2022-07-31 02:33:31 +00:00
|
|
|
|
2022-08-18 01:20:52 +00:00
|
|
|
if run(f"podman network exists {args.network}").returncode != 0:
|
|
|
|
if args.skip_traefik:
|
|
|
|
sys.exit(f"Skipped Traefik's deployment although Traefik's network {args.network} does not exist!")
|
|
|
|
else:
|
|
|
|
print(f"Creating network {args.network}.")
|
|
|
|
run(
|
|
|
|
f"podman network create {args.network}",
|
2022-08-18 01:55:05 +00:00
|
|
|
# Commented out because old buildah versions do not support "network exists"
|
|
|
|
# check=True,
|
2022-08-18 01:20:52 +00:00
|
|
|
)
|
2022-07-31 02:33:31 +00:00
|
|
|
|
2022-08-02 11:39:44 +00:00
|
|
|
|
2022-09-10 16:35:57 +00:00
|
|
|
def create_nginx_container():
|
|
|
|
create_container(
|
|
|
|
"advlabdb-nginx",
|
|
|
|
f"""--network {args.network} \
|
|
|
|
-v {NGINX_CONF_D_DIR}:/etc/nginx/conf.d:Z,ro \
|
|
|
|
--label "io.containers.autoupdate=registry" \
|
|
|
|
docker.io/library/nginx:alpine""",
|
|
|
|
)
|
|
|
|
|
|
|
|
|
2022-08-18 01:20:52 +00:00
|
|
|
def create_traefik_container():
|
2022-08-02 11:39:44 +00:00
|
|
|
if args.verbose:
|
|
|
|
print(f"Making sure that the logs directory {TRAEFIK_LOGS_DIR} exists.")
|
2022-07-31 02:33:31 +00:00
|
|
|
TRAEFIK_LOGS_DIR.mkdir(parents=True, exist_ok=True)
|
2022-08-02 11:39:44 +00:00
|
|
|
|
|
|
|
if args.verbose:
|
|
|
|
print(f"Making sure that the certificates directory {TRAEFIK_CERTS_DIR} exists.")
|
2022-07-31 02:33:31 +00:00
|
|
|
TRAEFIK_CERTS_DIR.mkdir(parents=True, exist_ok=True)
|
|
|
|
|
|
|
|
create_container(
|
|
|
|
"traefik",
|
2022-08-18 01:40:30 +00:00
|
|
|
f"""--network {args.network} \
|
2022-07-31 02:33:31 +00:00
|
|
|
-p 80:80 \
|
|
|
|
-p 443:443 \
|
2022-08-18 02:27:43 +00:00
|
|
|
-v {TRAEFIK_ETC_DIR}:/etc/traefik:Z,ro \
|
2022-07-31 02:33:31 +00:00
|
|
|
-v {TRAEFIK_LOGS_DIR}:/volumes/logs:Z \
|
|
|
|
-v {TRAEFIK_CERTS_DIR}:/volumes/certs:Z \
|
2022-08-18 01:40:30 +00:00
|
|
|
--label "io.containers.autoupdate=registry" \
|
2022-07-31 02:33:31 +00:00
|
|
|
docker.io/library/traefik:latest""",
|
|
|
|
)
|
|
|
|
|
2022-08-02 11:39:44 +00:00
|
|
|
|
2022-08-18 17:05:00 +00:00
|
|
|
# Needed for main and create_advlabdb_container
|
|
|
|
advlabdb_container_args = f"""--network {args.network} \
|
|
|
|
-e ADVLABDB_DATA_DIR=/volumes/data \
|
|
|
|
-v {ADVLABDB_REPO_DIR}:/volumes/repo:Z \
|
|
|
|
-v {ADVLABDB_DATA_DIR}:/volumes/data:Z \
|
|
|
|
-v {ADVLABDB_LOGS_DIR}:/volumes/logs:Z"""
|
|
|
|
|
|
|
|
|
|
|
|
def create_advlabdb_container():
|
|
|
|
create_container(
|
|
|
|
"advlabdb",
|
|
|
|
f"{advlabdb_container_args} localhost/advlabdb:latest",
|
|
|
|
)
|
|
|
|
|
|
|
|
|
2022-08-18 01:20:52 +00:00
|
|
|
def main():
|
|
|
|
pull_or_clone_repo()
|
|
|
|
|
|
|
|
# Checking requirements
|
|
|
|
|
|
|
|
check_requirements()
|
|
|
|
|
|
|
|
# Create/update the AdvLabDB image and container
|
|
|
|
|
|
|
|
# Make sure that the builder container does not exist.
|
|
|
|
run(
|
|
|
|
"buildah rm builder",
|
|
|
|
stderr=subprocess.DEVNULL,
|
|
|
|
)
|
|
|
|
|
|
|
|
requirements_file = ADVLABDB_REPO_DIR / "requirements.txt"
|
2022-08-18 02:21:30 +00:00
|
|
|
container_cmd = (
|
|
|
|
"\"gunicorn --bind 0.0.0.0:80 --workers 5 --log-file /volumes/logs/gunicorn.log 'run:create_app()'\""
|
|
|
|
)
|
2022-08-18 01:20:52 +00:00
|
|
|
|
|
|
|
print("Creating AdvLabDB image.")
|
|
|
|
commands = (
|
|
|
|
# Start building from a base image
|
|
|
|
"buildah from --pull --name builder docker.io/library/python:3.10-slim",
|
|
|
|
# Install Python requirements in the container
|
2022-08-18 01:56:06 +00:00
|
|
|
f"buildah copy builder {requirements_file} /root/requirements.txt",
|
2022-08-18 01:20:52 +00:00
|
|
|
"buildah run builder -- pip3 install -r /root/requirements.txt",
|
|
|
|
"buildah run builder -- rm /root/requirements.txt",
|
|
|
|
# Set the working directory of the container
|
|
|
|
"buildah config --workingdir /volumes/repo builder",
|
|
|
|
# Set the command that will run after starting the container
|
2022-08-18 02:21:30 +00:00
|
|
|
f"buildah config --cmd {container_cmd} builder",
|
2022-08-18 01:20:52 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
for command in commands:
|
|
|
|
run(command, check=True)
|
|
|
|
|
|
|
|
if run("systemctl --user is-enabled container-advlabdb").returncode == 0:
|
|
|
|
print("Disabling and deleting existing container advlabdb.")
|
|
|
|
run(
|
|
|
|
"systemctl --user disable --now container-advlabdb",
|
|
|
|
check=True,
|
|
|
|
)
|
|
|
|
|
|
|
|
if run("podman image exists localhost/advlabdb:latest").returncode == 0:
|
|
|
|
print("Deleting existing image advlabdb.")
|
|
|
|
run(
|
|
|
|
"podman rmi localhost/advlabdb:latest",
|
|
|
|
check=True,
|
|
|
|
)
|
|
|
|
|
|
|
|
# Save new image
|
|
|
|
run(
|
|
|
|
"buildah commit --rm builder advlabdb",
|
|
|
|
check=True,
|
|
|
|
)
|
|
|
|
|
|
|
|
if args.verbose:
|
|
|
|
print(f"Making sure that the logs directory {ADVLABDB_LOGS_DIR} exists.")
|
|
|
|
ADVLABDB_LOGS_DIR.mkdir(parents=True, exist_ok=True)
|
|
|
|
|
|
|
|
# Running setup commands (if needed)
|
2022-08-18 17:05:00 +00:00
|
|
|
run_manage = f"podman run -it --rm {advlabdb_container_args} localhost/advlabdb:latest python3 manage.py"
|
2022-08-18 01:20:52 +00:00
|
|
|
commands = (
|
|
|
|
# Generate secret keys if secrets.ini does not exist yet
|
|
|
|
f"{run_manage} setup generate-secrets",
|
|
|
|
# Initialize a database if none exists
|
|
|
|
f"{run_manage} setup init-db",
|
|
|
|
)
|
|
|
|
|
|
|
|
for command in commands:
|
|
|
|
run(command, check=True)
|
|
|
|
|
2022-08-18 17:05:00 +00:00
|
|
|
create_advlabdb_container()
|
2022-08-18 01:20:52 +00:00
|
|
|
|
|
|
|
# Create Traefik container if needed
|
|
|
|
|
|
|
|
if not args.skip_traefik and run("systemctl --user is-enabled container-traefik").returncode != 0:
|
|
|
|
create_traefik_container()
|
|
|
|
|
|
|
|
# Create Nginx container if needed
|
|
|
|
|
2022-09-10 16:35:57 +00:00
|
|
|
if run("systemctl --user is-enabled container-advlabdb-nginx").returncode != 0:
|
2022-08-18 01:20:52 +00:00
|
|
|
create_nginx_container()
|
2022-09-10 16:35:57 +00:00
|
|
|
else:
|
|
|
|
run("systemctl --user restart container-advlabdb-nginx")
|
2022-08-18 01:20:52 +00:00
|
|
|
|
|
|
|
print("\nDone!\n")
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
main()
|