diff --git a/script_creator.py b/script_creator.py index 5016279..0f8b8f2 100644 --- a/script_creator.py +++ b/script_creator.py @@ -3,33 +3,84 @@ import os import subprocess import base64 import requests +from redlock import Redlock +from threading import Thread, Lock +import time app = Flask(__name__) -# Configuration MinIO depuis les variables d'environnement +# Variables d'environnement MINIO_BUCKET = os.getenv("MINIO_BUCKET", "nextcloud") MINIO_HOST = os.getenv("MINIO_HOST", "minio.minio.svc.cluster.local:9000") MINIO_REGION = os.getenv("MINIO_REGION", "us-east-1") -S3_ACCESS_KEY = os.getenv("S3_ACCESS_KEY") -S3_SECRET_KEY = os.getenv("S3_SECRET_KEY") MINIO_MOUNT_PATH = "/mnt" N8N_WEBHOOK_URL = os.getenv("N8N_WEBHOOK_URL", "https://n8n.n8n.svc.kube.ia86.cc/webhook/7950310f-e526-475a-82d1-63818da79339") +DEBUG = bool(os.getenv("DEBUG", False)) -DEBUG = bool(os.getenv("DEBUG")) +S3_ACCESS_KEY = os.getenv("S3_ACCESS_KEY") +S3_SECRET_KEY = os.getenv("S3_SECRET_KEY") +if not S3_ACCESS_KEY or not S3_SECRET_KEY: + raise ValueError("❌ ERREUR: S3_ACCESS_KEY ou S3_SECRET_KEY manquantes") -def log_debug(message): +redis_host = os.getenv("REDIS_HOST", "redis.redis.svc.cluster.local") +redis_port = int(os.getenv("REDIS_PORT", 6379)) +dlm = Redlock([{"host": redis_host, "port": redis_port, "db": 0}]) + +S3_UNMOUNT_CHECK_INTERVAL = int(os.getenv("S3_UNMOUNT_CHECK_INTERVAL", 300)) # 5 minutes par défaut +lock_prefix = "lock:mkdocs:" +local_lock = Lock() + +def log_debug(msg): if DEBUG: - print(f"🔍 DEBUG: {message}") + print(f"🔍 DEBUG: {msg}") def mount_s3(): - if not os.path.ismount(MINIO_MOUNT_PATH): - os.makedirs(MINIO_MOUNT_PATH, exist_ok=True) - credentials_file = "/etc/passwd-s3fs" - with open(credentials_file, "w") as f: - f.write(f"{S3_ACCESS_KEY}:{S3_SECRET_KEY}\n") - os.chmod(credentials_file, 0o600) - cmd = f"s3fs {MINIO_BUCKET} {MINIO_MOUNT_PATH} -o passwd_file={credentials_file} -o url=https://{MINIO_HOST} -o use_path_request_style -o allow_other" - subprocess.run(cmd, shell=True, capture_output=True, text=True) + with local_lock: + if not os.path.ismount(MINIO_MOUNT_PATH): + os.makedirs(MINIO_MOUNT_PATH, exist_ok=True) + credentials_file = "/etc/passwd-s3fs" + with open(credentials_file, "w") as f: + f.write(f"{S3_ACCESS_KEY}:{S3_SECRET_KEY}\n") + os.chmod(credentials_file, 0o600) + + cmd = ( + f"s3fs {MINIO_BUCKET} {MINIO_MOUNT_PATH} " + f"-o passwd_file={credentials_file} " + f"-o url=https://{MINIO_HOST} " + "-o use_path_request_style " + "-o allow_other" + ) + + log_debug(f"🚀 Montage S3: {cmd}") + result = subprocess.run(cmd, shell=True, capture_output=True, text=True) + if result.returncode != 0: + log_debug(f"❌ Erreur montage: {result.stderr}") + raise RuntimeError(f"Erreur montage: {result.stderr}") + log_debug("✅ Montage réussi.") + else: + log_debug("✅ Montage S3 déjà actif.") + +def unmount_s3(): + with local_lock: + if os.path.ismount(MINIO_MOUNT_PATH): + cmd = f"fusermount -u {MINIO_MOUNT_PATH}" + result = subprocess.run(cmd, shell=True, capture_output=True, text=True) + if result.returncode == 0: + log_debug("✅ Démontage S3 réussi.") + else: + log_debug(f"⚠️ Échec démontage S3: {result.stderr}") + +def unmount_checker(): + while True: + time.sleep(S3_UNMOUNT_CHECK_INTERVAL) + log_debug("⏰ Vérification périodique pour démontage S3...") + # Vérification si aucun build actif + active_locks = dlm.redis_clients[0].keys(f"{lock_prefix}*") + if not active_locks: + log_debug("🟢 Aucun build actif, démontage S3...") + unmount_s3() + else: + log_debug("🔴 Builds en cours détectés, S3 reste monté.") @app.route("/build", methods=["POST"]) def build_mkdocs(): @@ -38,53 +89,44 @@ def build_mkdocs(): error_callback = data.get("ERROR_CALLBACK", N8N_WEBHOOK_URL) if not website: - log_debug("❌ Paramètre WEBSITE manquant.") - return jsonify({"error": "Paramètre WEBSITE manquant"}), 400 + return jsonify({"error": "WEBSITE manquant"}), 400 - lock_file = f"/tmp/build_{website}.lock" - - if os.path.exists(lock_file): - log_debug(f"⚠️ Un build est déjà en cours pour {website}.") - return jsonify({ - "status": "busy", - "message": f"Un build est déjà en cours pour {website}." - }), 429 - - # Création du verrou - open(lock_file, 'w').close() - log_debug(f"🔐 Verrou activé pour {website}.") + lock_key = f"{lock_prefix}{website}" + lock = dlm.lock(lock_key, 60000) + if not lock: + log_debug(f"⚠️ Build déjà actif: {website}") + return jsonify({"status": "busy", "message": f"Build déjà actif: {website}"}), 429 try: mount_s3() - src = f"/mnt/files/sites/@{website}/mkdocs.yml" + src = f"{MINIO_MOUNT_PATH}/files/sites/@{website}/mkdocs.yml" tmp = f"/srv/{website}" if not os.path.exists(src): - log_debug(f"❌ Le fichier de configuration {src} n'existe pas.") + log_debug(f"❌ Config introuvable: {src}") return jsonify({"error": f"{src} introuvable"}), 404 cmd = f"mkdocs build --quiet --no-strict --config-file {src} --site-dir {tmp}" - log_debug(f"🚀 Exécution: {cmd}") - + log_debug(f"🚀 Build MkDocs: {cmd}") result = subprocess.run(cmd, shell=True, capture_output=True, text=True) if result.returncode != 0: build_error = base64.b64encode(result.stderr.encode()).decode() - json_payload = {"site": tmp, "erreur": build_error} + json_payload = {"site": website, "erreur": build_error} + log_debug(f"❌ Échec build: {result.stderr}") requests.post(error_callback, json=json_payload, headers={"Content-Type": "application/json"}) - - log_debug(f"❌ Échec du build {website}: {result.stderr}") return jsonify({"status": "error", "message": "Build échoué", "error": result.stderr}), 500 - log_debug(f"✅ Build réussi pour {website}.") + log_debug(f"✅ Build réussi: {website}") return jsonify({"status": "success", "message": "Build réussi"}), 200 finally: - if os.path.exists(lock_file): - os.remove(lock_file) - log_debug(f"🔓 Verrou supprimé pour {website}.") + dlm.unlock(lock) + log_debug(f"🔓 Verrou libéré: {website}") if __name__ == "__main__": - log_debug("🚀 Serveur Flask démarré.") + log_debug("🚀 Flask démarré (0.0.0.0:80)") + checker_thread = Thread(target=unmount_checker, daemon=True) + checker_thread.start() app.run(host="0.0.0.0", port=80) \ No newline at end of file