fix(delegate): admission covers a rowless target and the installed parent of an incidental .mam (Issue #72, review round 2)
R1: a target with no registry row skipped the target gate. Its cwd is now read from the live agent on the server delivery uses (resolve_herdr_session), through the real herdr only when that server is running, so no server is started; a herdr that cannot answer refuses before registration. R2: any .mam counted as a workspace and stopped the walk, so B/sub/.mam hid an installed B and the row's control_plane root was dropped. Workspaces are now update.sh's installed trees (.mam_deploy/remove.sh or remove.sh): every installed ancestor of the cwd, plus the recorded control_plane root. lib_py.update target-workspaces prints them one per line; the wrapper holds each on fds 20.. until the record is written. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
ba75718ac3
commit
253a0aa56e
4 files changed
+115
-46
No files matched your search
@@ -16,11 +16,13 @@ this module what needs parsing. Standard library only, Python 3.9.
|
||||
python -m lib_py.update monitor --workspace W
|
||||
JSON {"pids": [...], "lock": "free"|"held"}: the monitor daemons
|
||||
daemon.find_running recognises, and whether .mam/monitor.lock is held.
|
||||
python -m lib_py.update target-workspace --workspace W --agent-session S
|
||||
The installed workspace a delegate target `herdr:<name>` belongs to
|
||||
(decision 8), when it is not W: the nearest directory holding a .mam
|
||||
at or above its row's pane.cwd, else the row's control_plane
|
||||
workspace_root. Nothing for a rowless or non-herdr target.
|
||||
python -m lib_py.update target-workspaces --workspace W --agent-session S --server V --herdr H
|
||||
The workspaces other than W whose update admission a delegate submit
|
||||
to `herdr:<name>` must take (decision 8), one per line: every installed
|
||||
workspace at or above the target's cwd (its row's pane.cwd; with no row
|
||||
the live agent's on server V) and its row's control_plane
|
||||
workspace_root. Nothing for a non-herdr target. rc 1: herdr cannot
|
||||
answer for a rowless target.
|
||||
python -m lib_py.update backup-registry --workspace W --dest D
|
||||
Copies the registry into D under the registry's own write lock
|
||||
(agent-sessions.db.lock, as atomic_yaml takes it): the DB through the
|
||||
@@ -264,27 +266,46 @@ def precheck(workspace: str, state: dict, herdr: str, env: Mapping[str, str]) ->
|
||||
|
||||
# --------------------------------------------------------------------------- delegate target
|
||||
|
||||
def target_workspace(workspace: str, state: dict, agent_session: str) -> str:
|
||||
def _installed(d: str) -> bool:
|
||||
"""update.sh's own test of an installed workspace."""
|
||||
return os.path.isfile(os.path.join(d, ".mam_deploy", "remove.sh")) or os.path.isfile(os.path.join(d, "remove.sh"))
|
||||
|
||||
|
||||
def target_workspaces(workspace: str, state: dict, agent_session: str, server: str,
|
||||
herdr: str, env: Mapping[str, str]) -> List[str]:
|
||||
"""Every installed workspace at or above the target's cwd - an update of
|
||||
any of them counts that agent (decision 1) - and its row's
|
||||
control_plane.workspace_root; the caller's own workspace left out. With
|
||||
no row the cwd is the live agent's on <server> (the one delivery uses),
|
||||
read with the real herdr. No herdr, a server that is not running, or no
|
||||
such agent gives nothing (delivery then fails on its own); a herdr that
|
||||
cannot answer raises HerdrError."""
|
||||
if not agent_session.startswith("herdr:"):
|
||||
return ""
|
||||
return []
|
||||
name = agent_session[len("herdr:"):]
|
||||
row = next((s for s in state.get("herdr_sessions") or []
|
||||
if isinstance(s, dict) and s.get("name") == name), None)
|
||||
if row is None:
|
||||
return ""
|
||||
found = ""
|
||||
cwd = (row.get("pane") or {}).get("cwd") or ""
|
||||
d = _real(cwd) if cwd else ""
|
||||
while d and not found:
|
||||
if os.path.isdir(os.path.join(d, ".mam")):
|
||||
found = d
|
||||
elif os.path.dirname(d) == d:
|
||||
break
|
||||
d = os.path.dirname(d)
|
||||
if not found:
|
||||
cwds, out = [], []
|
||||
if row is not None:
|
||||
cwds.append((row.get("pane") or {}).get("cwd") or "")
|
||||
root = (row.get("control_plane") or {}).get("workspace_root") or ""
|
||||
found = _real(root) if root else ""
|
||||
return "" if found == _real(workspace) else found
|
||||
if root:
|
||||
out.append(_real(root))
|
||||
elif herdr and server in running_servers(herdr, env):
|
||||
for a in server_agents(herdr, server, env):
|
||||
if a.get("name") in (name, sanitize_herdr_agent_name(name)):
|
||||
cwds += [a.get("cwd") or "", a.get("foreground_cwd") or ""]
|
||||
break
|
||||
for cwd in cwds:
|
||||
d = _real(cwd) if cwd else ""
|
||||
while d:
|
||||
if _installed(d):
|
||||
out.append(d)
|
||||
if os.path.dirname(d) == d:
|
||||
break
|
||||
d = os.path.dirname(d)
|
||||
me = _real(workspace)
|
||||
return [d for i, d in enumerate(out) if d != me and d not in out[:i]]
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------- monitor
|
||||
@@ -344,9 +365,11 @@ def main(argv: Optional[List[str]] = None) -> int:
|
||||
s.add_argument("--herdr", required=True)
|
||||
s = sub.add_parser("monitor")
|
||||
s.add_argument("--workspace", required=True)
|
||||
s = sub.add_parser("target-workspace")
|
||||
s = sub.add_parser("target-workspaces")
|
||||
s.add_argument("--workspace", required=True)
|
||||
s.add_argument("--agent-session", required=True)
|
||||
s.add_argument("--server", default="default")
|
||||
s.add_argument("--herdr", default="")
|
||||
s = sub.add_parser("backup-registry")
|
||||
s.add_argument("--workspace", required=True)
|
||||
s.add_argument("--dest", required=True)
|
||||
@@ -362,12 +385,18 @@ def main(argv: Optional[List[str]] = None) -> int:
|
||||
for line in lines:
|
||||
print(line)
|
||||
return 1 if lines else 0
|
||||
if a.cmd == "target-workspace":
|
||||
if a.cmd == "target-workspaces":
|
||||
try:
|
||||
state = _state()
|
||||
except ValueError:
|
||||
state = {}
|
||||
print(target_workspace(ws, state, a.agent_session))
|
||||
try:
|
||||
roots = target_workspaces(ws, state, a.agent_session, a.server, a.herdr, os.environ)
|
||||
except HerdrError as exc:
|
||||
print("cannot find %s live: %s" % (a.agent_session, exc), file=sys.stderr)
|
||||
return 1
|
||||
for root in roots:
|
||||
print(root)
|
||||
return 0
|
||||
if a.cmd == "monitor":
|
||||
print(json.dumps(monitor_state(ws)))
|
||||
|
||||
Reference in new issue
Block a user