fix: indentation, docker_bin absolute path check, rename to main.py
This commit is contained in:
parent
d6fdbbe8db
commit
aba98108b0
3 changed files with 258 additions and 9 deletions
2
setup.sh
2
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'
|
||||
|
|
|
|||
|
|
@ -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.")
|
||||
print("Preflight checks...")
|
||||
if not shutil.which("docker"):
|
||||
print(" ❌ Docker not found. 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(" ✅ Docker ready\n")
|
||||
|
||||
cfg = load_config()
|
||||
deployed = {n["name"] for n in load_state().get("nodes", [])}
|
||||
|
|
|
|||
252
sources/python/main.py
Normal file
252
sources/python/main.py
Normal file
|
|
@ -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()
|
||||
Loading…
Reference in a new issue