Files
apex.ermnvldmr.com/engine/context.py
T
deytenit bd973d783d refactor: make engine domain-agnostic — identity from node.env, no hardcoded domain
Drop the hardcoded-domain FQDN regex and every hardcoded domain from commons.
Node identity is now explicit in node.env: APEX_NODE_FQDN (real FQDN,
traefik Host()), APEX_NODE_HOST (public host name — backup --host,
obs/notify instance), APEX_SUBNET. The short name (log prefix, sync/<node>
branch, messages) is derived as the first host label.

notify instance is just the host (no hardcoded prefix); the git bot identity
moves to APEX_GIT_AUTHOR_NAME/EMAIL (node.env) with generic defaults. Alloy
node+instance labels use APEX_NODE_HOST. Fallbacks keep a node working if it
omits a var (host<-fqdn<-hostname).

BREAKING CHANGE: removes APEX_NODE and APEX_CLUSTER env vars; node name is now derived from APEX_NODE_FQDN
2026-07-08 09:32:20 +03:00

114 lines
4.1 KiB
Python

# engine/context.py
"""Context (ctx) — the single seam between an action and the world (SP2 §2.1)."""
from __future__ import annotations
import os
from engine.lib.log import Log
from engine.lib.sys import Sys
from engine.lib.tpl import Tpl
from engine.lib.host import Host
from engine.lib.ufw import Ufw
from engine.lib.crowdsec import Crowdsec
from engine.lib.systemd import Systemd
from engine.lib.notify import Notify
class Paths:
def __init__(self, repo_root: str, commons_dir: str):
self.repo_root = repo_root
self.commons = commons_dir
self.proprietaries = os.path.join(repo_root, "proprietaries")
self.configs = os.path.join(repo_root, "configs")
self.compositions = os.path.join(repo_root, "compositions")
self.core = os.path.join(self.compositions, "apex")
def _tier(self, n: int) -> str:
return os.path.realpath(os.path.join(self.core, f"@tier{n}"))
def _stier(self, n: int) -> str:
return os.path.realpath(os.path.join(self.core, f"@tier{n}", "shared"))
def _root_tier(self, n: int) -> str:
return os.path.realpath(os.path.join(self.compositions, f"@tier{n}"))
@property
def root_tier1(self): return self._root_tier(1)
@property
def root_tier2(self): return self._root_tier(2)
@property
def root_tier3(self): return self._root_tier(3)
@property
def tier1(self): return self._tier(1)
@property
def tier2(self): return self._tier(2)
@property
def tier3(self): return self._tier(3)
@property
def stier1(self): return self._stier(1)
@property
def stier2(self): return self._stier(2)
@property
def stier3(self): return self._stier(3)
class _CommonsWrap:
"""ctx.commons — lazily loads and runs the shadowed commons action (opt-in wrap)."""
def __init__(self, ctx, shadowed_path):
self._ctx = ctx
self._path = shadowed_path
self._mod = None
def run(self, args):
if not self._path:
raise RuntimeError("ctx.commons is only available when a commons action is shadowed")
from engine import overlay
if self._mod is None:
self._mod = overlay.load_module(self._path, "apex_commons_shadowed")
return self._mod.run(self._ctx, args)
class Context:
def __init__(self, node, paths: Paths, action_name: str, shadowed=None):
self.node = node
self.paths = paths
self.action = action_name
name = node.name if node else "global"
self.log = Log(name, action_name)
self.sys = Sys(self.log)
self.tpl = Tpl(paths.configs, self.log)
self.host = Host(self.log, self.sys)
self.ufw = Ufw(self.log, self.sys, self.host)
self.crowdsec = Crowdsec(self.log, self.sys, self.tpl, self.host)
self.systemd = Systemd(self.log, self.sys, self.tpl, self.host)
self.notify = Notify(self.log, node.host if node else "global")
self._shadowed = shadowed
self._commons = None
@property
def commons(self) -> _CommonsWrap:
if self._commons is None:
self._commons = _CommonsWrap(self, self._shadowed)
return self._commons
def vars(self) -> dict:
"""Substitution variable set for templating: os.environ + apex identity/paths."""
v = dict(os.environ)
v["APEX_COMMONS"] = self.paths.commons # absolute launcher dir (for crontab/systemd)
v["APEX_REPO_ROOT"] = self.paths.repo_root
if self.node:
v.update({
"APEX_NODE_HOST": self.node.host,
"APEX_NODE_FQDN": self.node.fqdn,
"APEX_SUBNET": self.node.subnet,
"APEX_TIER1": self.paths.tier1, "APEX_TIER2": self.paths.tier2,
"APEX_TIER3": self.paths.tier3,
"APEX_TIER1_SHARED": self.paths.stier1,
"APEX_TIER2_SHARED": self.paths.stier2,
"APEX_TIER3_SHARED": self.paths.stier3,
"APEX_TIER_ROOT1": self.paths.root_tier1,
"APEX_TIER_ROOT2": self.paths.root_tier2,
"APEX_TIER_ROOT3": self.paths.root_tier3,
})
return v