65 lines
2.6 KiB
Python
65 lines
2.6 KiB
Python
# scripts/engine/identity.py
|
|
"""Node identity — fully explicit, from node.env. The framework assumes no domain
|
|
and no clustering: a node declares its real FQDN and its public host name, and the
|
|
short `name` is just the first label. The OS hostname is only a fallback for the FQDN.
|
|
|
|
node.env keys:
|
|
APEX_NODE_FQDN real FQDN (traefik Host() routing). default: OS hostname
|
|
APEX_NODE_HOST public host name (backup --host, obs/notify default: APEX_NODE_FQDN
|
|
`instance`); often the FQDN minus a cluster
|
|
segment, but that's the node's choice.
|
|
APEX_SUBNET the node's /24 for the `direct` network. required
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
import sys
|
|
from dataclasses import dataclass
|
|
from typing import Dict, List, Tuple
|
|
|
|
|
|
@dataclass
|
|
class Node:
|
|
name: str # short label (first segment of the host), e.g. "node1" — logs, branch, messages
|
|
subnet: str # the node's direct-network /24
|
|
fqdn: str # real FQDN — traefik Host() routing
|
|
host: str # public host name — backup --host, obs `instance`, notify instance
|
|
|
|
|
|
def read_env(path: str) -> Dict[str, str]:
|
|
"""Parse a KEY=value file (shell-sourceable). Ignores comments/blank lines."""
|
|
out: Dict[str, str] = {}
|
|
if not os.path.isfile(path):
|
|
return out
|
|
with open(path) as f:
|
|
for line in f:
|
|
line = line.strip()
|
|
if not line or line.startswith("#") or "=" not in line:
|
|
continue
|
|
key, _, val = line.partition("=")
|
|
out[key.strip()] = val.strip().strip('"').strip("'")
|
|
return out
|
|
|
|
|
|
def resolve(hostname: str, repo_root: str) -> Tuple[Node, List[str]]:
|
|
"""Return (Node, warnings). Exits 66 (EX_NOINPUT) if APEX_SUBNET is missing."""
|
|
warns: List[str] = []
|
|
env = read_env(os.path.join(repo_root, "node.env"))
|
|
|
|
fqdn = env.get("APEX_NODE_FQDN", "") or (hostname or "").strip()
|
|
if not fqdn:
|
|
sys.stderr.write("identity: APEX_NODE_FQDN missing from node.env and no hostname\n")
|
|
raise SystemExit(66)
|
|
if not env.get("APEX_NODE_FQDN"):
|
|
warns.append(f"APEX_NODE_FQDN not set in node.env; using hostname {fqdn!r}.")
|
|
|
|
host = env.get("APEX_NODE_HOST", "") or fqdn # node's public name; defaults to the FQDN
|
|
name = host.split(".")[0] # short label for logs/branch/messages
|
|
|
|
subnet = env.get("APEX_SUBNET", "")
|
|
if not subnet:
|
|
sys.stderr.write("identity: APEX_SUBNET missing from node.env\n")
|
|
raise SystemExit(66)
|
|
|
|
return Node(name=name, subnet=subnet, fqdn=fqdn, host=host), warns
|