From aba98108b09448a61bfc8d27d16ef99bdb08f242 Mon Sep 17 00:00:00 2001 From: admin Date: Mon, 3 Aug 2026 15:31:29 +0000 Subject: [PATCH] fix: indentation, docker_bin absolute path check, rename to main.py --- setup.sh | 2 +- sources/python/deploy.py | 13 +- sources/python/main.py | 252 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 258 insertions(+), 9 deletions(-) create mode 100644 sources/python/main.py diff --git a/setup.sh b/setup.sh index 99bed01..0aa4563 100644 --- a/setup.sh +++ b/setup.sh @@ -89,7 +89,7 @@ fi APP_DIR="${WALLARM_DIR}/app" mkdir -p "${APP_DIR}" echo -e "${YELLOW}Downloading deploy script...${NC}" -curl -fsSL "${REPO}/sources/python/deploy.py" -o "${APP_DIR}/main.py" +curl -fsSL "${REPO}/sources/python/main.py" -o "${APP_DIR}/main.py" curl -fsSL "${REPO}/../deploy.sh" -o "${WALLARM_DIR}/deploy.sh" 2>/dev/null || { # Fallback: create wrapper cat > "${WALLARM_DIR}/deploy.sh" << 'WRAPPER' diff --git a/sources/python/deploy.py b/sources/python/deploy.py index d0e5dff..7eb723a 100644 --- a/sources/python/deploy.py +++ b/sources/python/deploy.py @@ -127,14 +127,11 @@ def main(): return deploy_all() print(f"═══ Wallarm Node Manager v{VERSION} ═══\n") -print("Preflight checks...") -docker_bin = f"{DOCKER_DIR}/bin/docker" -if not os.path.exists(docker_bin): - print(f" ❌ Docker not found at {docker_bin}. Run setup.sh first.") - sys.exit(1) -# Add to PATH for subprocess calls -os.environ["PATH"] = f"{DOCKER_DIR}/bin:{os.environ.get('PATH', '')}" -print(" ✅ Docker ready\n") + print("Preflight checks...") + if not shutil.which("docker"): + print(" ❌ Docker not found. Run setup.sh first.") + sys.exit(1) + print(" ✅ Docker ready\n") cfg = load_config() deployed = {n["name"] for n in load_state().get("nodes", [])} diff --git a/sources/python/main.py b/sources/python/main.py new file mode 100644 index 0000000..7eb723a --- /dev/null +++ b/sources/python/main.py @@ -0,0 +1,252 @@ +#!/usr/bin/env python3 +"""Wallarm Docker Node Manager""" + +import json, os, sys, time, subprocess, shutil + +VERSION = "2.0.0" +BASE = "/opt/fw" +APP = f"{BASE}/app" +CONF = f"{APP}/fw.conf" +STATE = f"{APP}/state.json" +DOCKER_DIR = f"{BASE}/docker" +OFFLINE_DIR = f"{BASE}/offline" +WALLARM_IMAGE = os.environ.get("WALLARM_IMAGE", "wallarm/node:6.11.0-rc1") + +# Ensure Docker is in PATH +os.environ["PATH"] = f"{DOCKER_DIR}/bin:{os.environ.get('PATH', '')}" + +def run(cmd, timeout=120): + return subprocess.run(cmd, shell=True, capture_output=True, text=True, timeout=timeout) + +def load_config(): + if not os.path.exists(CONF): return {} + with open(CONF) as f: return json.load(f) + +def load_state(): + if not os.path.exists(STATE): return {"nodes": []} + with open(STATE) as f: return json.load(f) + +def save_state(s): + os.makedirs(APP, exist_ok=True) + with open(STATE, "w") as f: json.dump(s, f, indent=2) + +# ─── Docker ──────────────────────────────────────────────────────────── + +def install_docker(): + if shutil.which("docker"): + return + print("Installing Docker...") + run("apt-get update -qq && apt-get install -y -qq docker.io 2>/dev/null || yum install -y -q docker 2>/dev/null", timeout=120) + run("systemctl enable docker && systemctl start docker") + +def deploy_container(name, token, cloud, port, upstream_ip, upstream_port, labels, mode): + api_host = "us1.api.wallarm.com" if cloud == "US" else "api.wallarm.com" + + # Ensure Docker is running + install_docker() + + # Pull image if needed + if run(f"docker images -q {WALLARM_IMAGE}").stdout.strip() == "": + print(f"[{name}] Pulling Wallarm image...") + run(f"docker pull {WALLARM_IMAGE}", timeout=300) + + # Remove old container if exists + container = f"wallarm-{name}" + run(f"docker rm -f {container} 2>/dev/null", timeout=10) + + # Run container + monitoring_port = int(port) + 10 + print(f"[{name}] Starting container on port {port}...") + cmd = f"""docker run -d \ + --name {container} \ + --restart unless-stopped \ + -p {port}:{port} \ + -v wallarm-data-{name}:/opt/wallarm \ + -p {monitoring_port}:{monitoring_port} \ + -e WALLARM_API_TOKEN={token} \ + -e WALLARM_API_HOST={api_host} \ + -e WALLARM_MODE={mode} \ + -e NGINX_PORT={port}""" + if upstream_ip: + cmd += f" -e WALLARM_UPSTREAM=http://{upstream_ip}:{upstream_port}" + cmd += f" {WALLARM_IMAGE}" + + r = run(cmd, timeout=30) + if r.returncode != 0: + print(f"❌ {name}: {r.stderr}") + return False + + # Save state + s = load_state() + s.setdefault("nodes", []).append({ + "name": name, "type": "docker", "port": int(port), + "upstream_ip": upstream_ip, "upstream_port": int(upstream_port), + "status": "running", "created_at": time.strftime("%Y-%m-%dT%H:%M:%SZ") + }) + save_state(s) + print(f"✅ {name} running on port {port} (docker ps --filter name={container})") + return True + +def remove_container(name): + container = f"wallarm-{name}" + run(f"docker rm -f {container} 2>/dev/null", timeout=10) + shutil.rmtree(f"/opt/wallarm-{name}", ignore_errors=True) + s = load_state() + s["nodes"] = [n for n in s.get("nodes", []) if n["name"] != name] + save_state(s) + print(f"✅ {name} removed") + +def container_status(): + r = run("docker ps --filter 'name=wallarm-' --format '{{.Names}}\t{{.Status}}\t{{.Ports}}'") + if not r.stdout.strip(): + print("No containers running.") + return + print("\n─── Containers ───") + for line in r.stdout.strip().split("\n"): + print(f" {line}") + +# ─── Deploy All ─────────────────────────────────────────────────────── + +def deploy_all(): + cfg = load_config() + nodes = cfg.get("nodes", cfg) # support both {"nodes":{...}} and flat format + if not nodes: + print("No nodes in fw.conf") + return + for name, nc in nodes.items(): + if not isinstance(nc, dict): continue + deploy_container(name, nc["token"], nc.get("cloud","EU"), + str(nc.get("port","8081")), nc.get("upstream_ip","127.0.0.1"), + str(nc.get("upstream_port","80")), nc.get("labels",""), + nc.get("mode","monitoring")) + +# ─── Menu ───────────────────────────────────────────────────────────── + +def main(): + if len(sys.argv) > 1 and sys.argv[1] == "--deploy-all": + return deploy_all() + + print(f"═══ Wallarm Node Manager v{VERSION} ═══\n") + print("Preflight checks...") + if not shutil.which("docker"): + print(" ❌ Docker not found. Run setup.sh first.") + sys.exit(1) + print(" ✅ Docker ready\n") + + cfg = load_config() + deployed = {n["name"] for n in load_state().get("nodes", [])} + + while True: + undeployed = [k for k in cfg if k not in deployed] + + print("─── Menu ───") + if undeployed: + print(f" Nodes ready to deploy: {', '.join(undeployed)}") + else: + print(" No nodes pending deployment.") + + if undeployed: + print(" [1] Deploy all pending nodes") + print(" [2] Add a new node") + if deployed: + print(" [3] Show status") + print(" [4] Remove a node") + print(" [q] Quit") + + c = input("\nChoice: ").strip() + + if c == "1" and undeployed: + for name in undeployed: + nc = cfg[name] + print(f"\nDeploying {name}...") + deploy_container(name, nc["token"], nc.get("cloud","EU"), + str(nc.get("port","8081")), nc.get("upstream_ip",""), + str(nc.get("upstream_port","80")), "", + nc.get("mode","monitoring")) + elif c == "2": + add_node() + cfg = load_config() + deployed = {n["name"] for n in load_state().get("nodes", [])} + elif c == "3" and deployed: + container_status() + elif c == "4" and deployed: + remove_menu() + elif c.lower() == "q": + break + +def create_config(): + print("\n─── Create Node Config ───") + name = input("Node name: ").strip() + if not name: return + token = input("Wallarm token: ").strip() + cloud = input("Cloud [EU]: ").strip() or "EU" + port = input("Port [8081]: ").strip() or "8081" + upstream_ip = input("Upstream IP [127.0.0.1]: ").strip() or "127.0.0.1" + upstream_port = input("Upstream port [80]: ").strip() or "80" + mode = input("Mode [monitoring]: ").strip() or "monitoring" + + cfg = load_config() + cfg[name] = {"token": token, "cloud": cloud, "port": port, + "upstream_ip": upstream_ip, "upstream_port": upstream_port, + "labels": f"group={name}", "mode": mode} + os.makedirs(APP, exist_ok=True) + with open(CONF, "w") as f: + json.dump(cfg, f, indent=2) + print(f"✅ {name} added to fw.conf") + +def add_node(): + print("\n─── Add New Node ───") + name = input("Node name: ").strip() + if not name: return + token = input("Wallarm token: ").strip() + if not token: print("Token required."); return + cloud = input("Cloud region [EU]: ").strip() or "EU" + port = input("Port [8081]: ").strip() or "8081" + upstream_ip = input("Upstream IP [127.0.0.1]: ").strip() or "127.0.0.1" + upstream_port = input("Upstream port [80]: ").strip() or "80" + mode = input("Mode [monitoring]: ").strip() or "monitoring" + + # Save to fw.conf + cfg = load_config() + cfg[name] = {"token": token, "cloud": cloud, "port": port, + "upstream_ip": upstream_ip, "upstream_port": upstream_port, + "labels": f"group={name}", "mode": mode} + os.makedirs(APP, exist_ok=True) + with open(CONF, "w") as f: json.dump(cfg, f, indent=2) + + # Deploy immediately + print(f"\nDeploying {name}...") + deploy_container(name, token, cloud, port, upstream_ip, upstream_port, f"group={name}", mode) + +def remove_menu(): + s = load_state().get("nodes", []) + if not s: print("No nodes deployed."); return + print("\n─── Remove Node ───") + for i, n in enumerate(s): + print(f" [{i+1}] {n['name']} (port {n.get('port','')})") + c = input("\nSelect number: ").strip() + if not c.isdigit() or int(c) < 1 or int(c) > len(s): return + remove_container(s[int(c)-1]["name"]) + +def edit_menu(): + cfg = load_config() + if not cfg: print("No nodes in fw.conf"); return + print("\n─── Edit Node ───") + names = list(cfg.keys()) + for i, name in enumerate(names): + nc = cfg[name] + print(f" [{i+1}] {name} (port {nc.get('port','')})") + c = input("\nSelect number: ").strip() + if not c.isdigit() or int(c) < 1 or int(c) > len(names): return + name = names[int(c)-1] + nc = cfg[name] + print(f"\nEditing {name}. Leave blank to keep current value:") + for key, prompt in [("port","Port"), ("upstream_ip","Upstream IP"), + ("upstream_port","Upstream port"), ("mode","Mode")]: + v = input(f"{prompt} [{nc.get(key,'')}]: ").strip() + if v: nc[key] = v + with open(CONF, "w") as f: json.dump(cfg, f, indent=2) + print(f"✅ {name} updated in fw.conf. Re-deploy to apply changes.") + +if __name__ == "__main__": + main()