99 lines
3.8 KiB
Python
99 lines
3.8 KiB
Python
# actions/compose.py
|
|
"""Orchestrate docker compose across the node.
|
|
|
|
Supports targeting all compositions in sequence (default: apex core first, services next;
|
|
reverse on down) or targeting one with `apex compose -p apex restart traefik`.
|
|
Wrapper options precede the action. Exports derived APEX_* vars and layers env files.
|
|
"""
|
|
import os
|
|
import shlex
|
|
|
|
from engine.descriptor import Flag, Meta, Opt, Rest
|
|
|
|
METADATA = Meta(
|
|
summary="Orchestrate docker compose across the node or on a specific composition.",
|
|
args=[
|
|
Opt("-p", "Target composition name or 'all'.", default=None),
|
|
Flag("--dry-run", "Print commands only."),
|
|
Rest("command", "ACTION [COMPOSE_ARGS...]; wrapper options must precede ACTION."),
|
|
],
|
|
)
|
|
|
|
|
|
def run(ctx, args):
|
|
log, comp = ctx.log, ctx.paths.compositions
|
|
command = list(args.command)
|
|
if command and command[0] == "--":
|
|
command.pop(0)
|
|
if not command:
|
|
log.error("ACTION is required (up, down, restart, logs, ...).")
|
|
raise SystemExit(64)
|
|
action, extra = command[0], command[1:]
|
|
dry_run = args.dry_run
|
|
before_separator = extra[:extra.index("--")] if "--" in extra else extra
|
|
if any(arg in ("-p", "--dry-run") or arg.startswith("-p=")
|
|
or arg.startswith("--dry-run=") for arg in before_separator):
|
|
log.error("Wrapper options -p and --dry-run must precede ACTION; use -- for passthrough.")
|
|
raise SystemExit(64)
|
|
env = dict(os.environ)
|
|
env.update({k: v for k, v in ctx.vars().items() if k.startswith("APEX_")})
|
|
|
|
available = []
|
|
if os.path.isdir(comp):
|
|
available = [
|
|
d for d in os.listdir(comp)
|
|
if os.path.isdir(os.path.join(comp, d))
|
|
and not d.startswith((".", "@"))
|
|
and os.path.isfile(os.path.join(comp, d, "docker-compose.yml"))
|
|
]
|
|
|
|
target = getattr(args, "p", None)
|
|
if target is None and extra:
|
|
if extra[0] in ["all", *available]:
|
|
target, extra = extra[0], extra[1:]
|
|
elif action == "up" and len(extra) >= 2 and extra[0] == "-d":
|
|
# Preserve the documented `up -d apex` spelling without scanning flag values.
|
|
if extra[1] in ["all", *available]:
|
|
if len(extra) != 2:
|
|
log.error("Use -p COMPOSITION before up when adding service arguments.")
|
|
raise SystemExit(64)
|
|
target, extra = extra[1], ["-d"]
|
|
|
|
if target is None:
|
|
target = "all"
|
|
|
|
if target != "all" and target not in available:
|
|
names = ", ".join(sorted(available)) if available else "none"
|
|
log.error(f"Unknown composition '{target}'. Available: {names}")
|
|
raise SystemExit(1)
|
|
|
|
def sequence():
|
|
if target != "all":
|
|
return [os.path.join(comp, target)]
|
|
core = os.path.join(comp, "apex")
|
|
others = sorted(os.path.join(comp, d) for d in available if d != "apex")
|
|
seq = ([core] if "apex" in available else []) + others
|
|
return list(reversed(seq)) if action == "down" else seq
|
|
|
|
def run_one(dirpath):
|
|
name = os.path.basename(dirpath)
|
|
base = ["docker", "compose"]
|
|
for ef in (".env", "apex.env"):
|
|
if os.path.isfile(os.path.join(dirpath, ef)):
|
|
base += ["--env-file", ef]
|
|
if action == "up":
|
|
up_flags = [] if any(flag in extra for flag in ("-d", "--detach")) else ["-d"]
|
|
cmd = base + ["up"] + up_flags + extra
|
|
else:
|
|
cmd = base + [action] + extra
|
|
|
|
if dry_run:
|
|
log.info(f"[DRY-RUN] (cd {shlex.quote(dirpath)} && {shlex.join(cmd)})")
|
|
else:
|
|
log.info(f"{action} {name}...")
|
|
ctx.sys.run(cmd, cwd=dirpath, env=env, check=True)
|
|
|
|
for d in sequence():
|
|
run_one(d)
|
|
log.success(f"Orchestrated {action} ({target}).")
|