Files
Agent-Fleet/manager/manager.py
T
2026-07-03 19:54:40 +02:00

112 lines
3.5 KiB
Python

#!/usr/bin/env python3
import getpass
import os
import shutil
import subprocess
from pathlib import Path
import click
from lib.config import DEFAULT_CONFIG, load_yaml, get_profile_dir
from lib.functions import deep_update
from lib.run import compose_exec, run
from lib.profiles import profile_exists, ensure_profile, set_state
from lib.profiles import apply_settings, copy_seed
def merged_profile(cfg, name):
profile = cfg["profiles"][name]
template_name = profile.get("template")
template = cfg.get("templates", {}).get(template_name, {}) if template_name else {}
merged = dict(template)
merged = deep_update(merged, profile)
merged["name"] = name
return merged
@click.group()
@click.option("--config", "config_path", default=DEFAULT_CONFIG, show_default=True)
@click.pass_context
def cli(ctx, config_path):
ctx.obj = {
"config_path": config_path,
"config": load_yaml(config_path),
}
@cli.command()
@click.pass_context
def reconcile(ctx):
"""Create missing profiles and apply desired state."""
cfg = ctx.obj["config"]
for name in cfg.get("profiles", {}):
profile = merged_profile(cfg, name)
container = profile["container"]
description = profile.get("description")
click.echo(f"\n==> Reconciling {name} in {container}")
existed_before = profile_exists(container, name)
ensure_profile(container, name, description)
if not existed_before:
# Set profile dir as write-able by current user
profile_path = str(get_profile_dir(container, name).parent.parent)
user = getpass.getuser()
cmd = ["sudo", "setfacl", "-R", "-m", f"u:{user}:rwx",
"-m", f"d:u:{user}:rwx", profile_path]
run(cmd, capture=False)
# If a seed directory is set, use its files as default
if profile.get("seed"):
copy_seed(container, name, profile["seed"])
# Apply settings from hermes-profiles file
apply_settings(container, name, profile)
set_state(container, name, profile.get("state", "existing"))
@cli.command()
@click.argument("name")
@click.pass_context
def start(ctx, name):
"""Start one configured profile."""
profile = merged_profile(ctx.obj["config"], name)
compose_exec(profile["container"], "hermes", "-p", name, "gateway", "start")
@cli.command()
@click.argument("name")
@click.pass_context
def stop(ctx, name):
"""Stop one configured profile."""
profile = merged_profile(ctx.obj["config"], name)
compose_exec(profile["container"], "hermes", "-p", name, "gateway", "stop")
@cli.command()
@click.argument("name")
@click.pass_context
def restart(ctx, name):
"""Restart one configured profile."""
profile = merged_profile(ctx.obj["config"], name)
compose_exec(profile["container"], "hermes", "-p", name, "gateway", "restart")
@cli.command()
@click.pass_context
def list(ctx):
"""List configured profiles."""
cfg = ctx.obj["config"]
padding = max([len(x) for x in cfg.get("profiles", {})])
for name in cfg.get("profiles", {}):
profile = merged_profile(cfg, name)
state = profile.get('state', 'existing')
if state == "running": state = click.style(state, fg='green')
elif state == "stopped": state = click.style(state, fg='red')
click.echo(f"{name.ljust(padding)}\t{profile['container']}\t{state}")
if __name__ == "__main__":
cli()