|
| 1 | +import os |
| 2 | +import argparse |
| 3 | +import pathlib |
| 4 | +import tempfile |
| 5 | +import shutil |
| 6 | +import sys |
| 7 | +import subprocess |
| 8 | +import importlib.resources as pkg_resources |
| 9 | + |
| 10 | +PACKAGE_NAME = "DashML" |
| 11 | +DB_SUBDIR = "db" |
| 12 | +INIT_SQL_NAME = "init.sql" |
| 13 | +COMPOSE_FILE = "docker-compose.yml" |
| 14 | + |
| 15 | + |
| 16 | +def check_docker_access(): |
| 17 | + # Check if docker is installed |
| 18 | + if not shutil.which("docker"): |
| 19 | + print("❌ Docker is not installed or not in PATH.") |
| 20 | + sys.exit(1) |
| 21 | + |
| 22 | + try: |
| 23 | + subprocess.run(["docker", "info"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, check=True) |
| 24 | + except subprocess.CalledProcessError: |
| 25 | + print("❌ Docker is running but your user lacks permission to access the Docker daemon.") |
| 26 | + print("➡️ Try running:") |
| 27 | + print(" sudo usermod -aG docker $USER") |
| 28 | + print(" newgrp docker # or log out and back in") |
| 29 | + sys.exit(1) |
| 30 | + except Exception as e: |
| 31 | + print(f"❌ Unexpected error trying to access Docker: {e}") |
| 32 | + sys.exit(1) |
| 33 | + |
| 34 | + |
| 35 | + |
| 36 | +def extract_resource(filename): |
| 37 | + tmp_dir = pathlib.Path(tempfile.mkdtemp(prefix="dashml_")) |
| 38 | + output_path = tmp_dir / filename |
| 39 | + |
| 40 | + with pkg_resources.files(f"{PACKAGE_NAME}.{DB_SUBDIR}").joinpath(filename).open("rb") as src: |
| 41 | + with open(output_path, "wb") as dst: |
| 42 | + shutil.copyfileobj(src, dst) |
| 43 | + |
| 44 | + return output_path |
| 45 | + |
| 46 | + |
| 47 | +def run_docker_compose(command_args, extra_env=None): |
| 48 | + compose_path = pathlib.Path(__file__).parent.parent / DB_SUBDIR / COMPOSE_FILE |
| 49 | + |
| 50 | + if not compose_path.exists(): |
| 51 | + print(f"❌ Could not find docker-compose.yml at {compose_path}") |
| 52 | + return |
| 53 | + |
| 54 | + env = os.environ.copy() |
| 55 | + if extra_env: |
| 56 | + env.update(extra_env) |
| 57 | + |
| 58 | + cmd = ["docker", "compose", "-f", str(compose_path)] + command_args |
| 59 | + subprocess.run(cmd, env=env) |
| 60 | + |
| 61 | +def main(): |
| 62 | + check_docker_access() |
| 63 | + |
| 64 | + parser = argparse.ArgumentParser(prog="dt_db", description="DashML DB manager") |
| 65 | + subparsers = parser.add_subparsers(dest="command", required=True) |
| 66 | + |
| 67 | + subparsers.add_parser("up", help="Start the database container") |
| 68 | + subparsers.add_parser("reset", help="Warning: Deletes the data!") |
| 69 | + subparsers.add_parser("down", help="Stop and remove the container") |
| 70 | + subparsers.add_parser("logs", help="Show container logs") |
| 71 | + subparsers.add_parser("status", help="Show container status") |
| 72 | + |
| 73 | + args = parser.parse_args() |
| 74 | + |
| 75 | + if args.command == "up": |
| 76 | + init_sql_path = extract_resource("init.sql") |
| 77 | + my_cnf_path = extract_resource("my.cnf") |
| 78 | + |
| 79 | + print(f"📦 Using init.sql: {init_sql_path}") |
| 80 | + print(f"⚙️ Using my.cnf: {my_cnf_path}") |
| 81 | + |
| 82 | + run_docker_compose(["up", "--build", "-d"], { |
| 83 | + "INIT_SQL_PATH": str(init_sql_path), |
| 84 | + "MY_CNF_PATH": str(my_cnf_path) |
| 85 | + }) |
| 86 | + |
| 87 | + elif args.command == "down": |
| 88 | + run_docker_compose(["down"]) |
| 89 | + |
| 90 | + elif args.command == "del": |
| 91 | + run_docker_compose(["down", "-v"]) |
| 92 | + |
| 93 | + elif args.command == "logs": |
| 94 | + run_docker_compose(["logs"]) |
| 95 | + |
| 96 | + elif args.command == "status": |
| 97 | + run_docker_compose(["ps"]) |
| 98 | + |
| 99 | + else: |
| 100 | + parser.print_help() |
0 commit comments