#!/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()