From 846c890527bd8e008d7556310f312a4d746018cb Mon Sep 17 00:00:00 2001 From: jenkins Date: Sat, 15 Aug 2026 03:44:00 -0300 Subject: [PATCH] hermes: load tool credentials before s6 --- services/hermes/agent-deployment.yaml | 32 +++---------- services/hermes/kustomization.yaml | 1 - services/hermes/scripts/patch_main_wrapper.py | 48 ------------------- testing/tests/test_hermes_cli_lanes.py | 6 ++- testing/tests/test_hermes_main_wrapper.py | 42 +++------------- 5 files changed, 18 insertions(+), 111 deletions(-) delete mode 100644 services/hermes/scripts/patch_main_wrapper.py diff --git a/services/hermes/agent-deployment.yaml b/services/hermes/agent-deployment.yaml index 4bcf0be0..fcb1a63e 100644 --- a/services/hermes/agent-deployment.yaml +++ b/services/hermes/agent-deployment.yaml @@ -255,26 +255,6 @@ spec: resources: requests: {cpu: 25m, memory: 64Mi} limits: {cpu: 250m, memory: 256Mi} - - name: patch-main-wrapper - image: registry.bstein.dev/bstein/hermes-agent@sha256:81970563e542f0720773e72297810b3a844b83e381e278f25c0916c78d930107 - imagePullPolicy: IfNotPresent - command: - - /opt/hermes/.venv/bin/python - - /opt/coordinator/patch_main_wrapper.py - - /opt/hermes/docker/main-wrapper.sh - - /patched/main-wrapper.sh - securityContext: - allowPrivilegeEscalation: false - runAsUser: 10000 - runAsGroup: 10000 - seccompProfile: - type: RuntimeDefault - volumeMounts: - - {name: coordinator, mountPath: /opt/coordinator, readOnly: true} - - {name: main-wrapper-patch, mountPath: /patched} - resources: - requests: {cpu: 25m, memory: 32Mi} - limits: {cpu: 100m, memory: 64Mi} - name: patch-auth image: registry.bstein.dev/bstein/hermes-agent@sha256:81970563e542f0720773e72297810b3a844b83e381e278f25c0916c78d930107 imagePullPolicy: IfNotPresent @@ -476,8 +456,13 @@ spec: - name: hermes image: registry.bstein.dev/bstein/hermes-agent@sha256:81970563e542f0720773e72297810b3a844b83e381e278f25c0916c78d930107 imagePullPolicy: IfNotPresent - command: [/init, /opt/hermes/docker/main-wrapper.sh] - args: [gateway, run] + command: [/bin/sh, -ec] + args: + - | + set -a + . /opt/data/.env + set +a + exec /init /opt/hermes/docker/main-wrapper.sh gateway run ports: - {name: api, containerPort: 8642, protocol: TCP} - {name: dashboard, containerPort: 9119, protocol: TCP} @@ -512,7 +497,6 @@ spec: - {name: home, mountPath: /opt/data} - {name: provider-auth, mountPath: /shared-auth} - {name: coordinator, mountPath: /opt/coordinator, readOnly: true} - - {name: main-wrapper-patch, mountPath: /opt/hermes/docker/main-wrapper.sh, subPath: main-wrapper.sh, readOnly: true} - {name: auth-patch, mountPath: /opt/hermes/hermes_cli/auth.py, subPath: auth.py} - {name: codex-runtime-patch, mountPath: /opt/hermes/hermes_cli/runtime_provider.py, subPath: runtime_provider.py} - {name: codex-runtime-patch, mountPath: /opt/hermes/agent/transports/codex_app_server_session.py, subPath: codex_app_server_session.py} @@ -962,8 +946,6 @@ spec: defaultMode: 0444 - name: auth-patch emptyDir: {} - - name: main-wrapper-patch - emptyDir: {} - name: tui-gateway-patch emptyDir: {} - name: api-server-patch diff --git a/services/hermes/kustomization.yaml b/services/hermes/kustomization.yaml index e12c42f7..5afa5965 100644 --- a/services/hermes/kustomization.yaml +++ b/services/hermes/kustomization.yaml @@ -76,7 +76,6 @@ configMapGenerator: - migrate_api_session_lineage.py=scripts/migrate_api_session_lineage.py - patch_api_server_sessions.py=scripts/patch_api_server_sessions.py - patch_hermes_auth.py=scripts/patch_hermes_auth.py - - patch_main_wrapper.py=scripts/patch_main_wrapper.py - patch_codex_runtime.py=scripts/patch_codex_runtime.py - patch_stream_recovery.py=scripts/patch_stream_recovery.py - patch_tui_gateway.py=scripts/patch_tui_gateway.py diff --git a/services/hermes/scripts/patch_main_wrapper.py b/services/hermes/scripts/patch_main_wrapper.py deleted file mode 100644 index c613384a..00000000 --- a/services/hermes/scripts/patch_main_wrapper.py +++ /dev/null @@ -1,48 +0,0 @@ -#!/usr/bin/env python3 -"""Load the Vault-derived tool environment in Hermes's main gateway.""" - -from __future__ import annotations - -import argparse -import shutil -from pathlib import Path - - -BEFORE = """# HOME comes through with-contenv as /root (the /init context). Override -""" -AFTER = """# The init container writes Vault-derived tool credentials here. Load them -# after with-contenv restores the container environment so dashboard-spawned -# terminal tools receive the same Git identity as durable CLI workers. -if [ -r /opt/data/.env ]; then - set -a - # shellcheck disable=SC1091 - . /opt/data/.env - set +a -fi - -# HOME comes through with-contenv as /root (the /init context). Override -""" - - -def patch(source: Path, destination: Path) -> None: - """Apply the narrow environment load and fail on upstream drift.""" - content = source.read_text(encoding="utf-8") - if BEFORE not in content: - raise RuntimeError("Hermes main-wrapper patch context changed") - destination.parent.mkdir(parents=True, exist_ok=True) - destination.write_text(content.replace(BEFORE, AFTER, 1), encoding="utf-8") - shutil.copymode(source, destination) - - -def main() -> int: - """Patch the wrapper path supplied by the deployment init container.""" - parser = argparse.ArgumentParser(description=__doc__) - parser.add_argument("source", type=Path) - parser.add_argument("destination", type=Path) - args = parser.parse_args() - patch(args.source, args.destination) - return 0 - - -if __name__ == "__main__": - raise SystemExit(main()) diff --git a/testing/tests/test_hermes_cli_lanes.py b/testing/tests/test_hermes_cli_lanes.py index 08c5742b..cbe30fc5 100644 --- a/testing/tests/test_hermes_cli_lanes.py +++ b/testing/tests/test_hermes_cli_lanes.py @@ -639,8 +639,10 @@ def test_agent_root_is_stock_dashboard_and_terminal_is_a_separate_path(): assert "dashboard" not in containers hermes = containers["hermes"] - assert hermes["command"] == ["/init", "/opt/hermes/docker/main-wrapper.sh"] - assert hermes["args"] == ["gateway", "run"] + assert hermes["command"] == ["/bin/sh", "-ec"] + startup = hermes["args"][0] + assert ". /opt/data/.env" in startup + assert "exec /init /opt/hermes/docker/main-wrapper.sh gateway run" in startup hermes_env = {item["name"]: item["value"] for item in hermes["env"]} assert hermes_env["HERMES_DASHBOARD"] == "1" assert hermes_env["HERMES_DASHBOARD_HOST"] == "127.0.0.1" diff --git a/testing/tests/test_hermes_main_wrapper.py b/testing/tests/test_hermes_main_wrapper.py index 5165413c..f02edad8 100644 --- a/testing/tests/test_hermes_main_wrapper.py +++ b/testing/tests/test_hermes_main_wrapper.py @@ -1,9 +1,7 @@ -"""Hermes gateway tool-environment patch and manifest tests.""" +"""Hermes gateway tool-environment and recovery manifest tests.""" from __future__ import annotations -import importlib.util -import stat from pathlib import Path import yaml @@ -11,46 +9,20 @@ import yaml ROOT = Path(__file__).resolve().parents[2] HERMES = ROOT / "services/hermes" -SCRIPT = HERMES / "scripts/patch_main_wrapper.py" - - -def _patch_module(): - spec = importlib.util.spec_from_file_location("patch_main_wrapper", SCRIPT) - assert spec and spec.loader - module = importlib.util.module_from_spec(spec) - spec.loader.exec_module(module) - return module def _deployment() -> dict: return yaml.safe_load((HERMES / "agent-deployment.yaml").read_text()) -def test_wrapper_loads_tool_environment_after_s6_restore(tmp_path: Path) -> None: - module = _patch_module() - source = tmp_path / "main-wrapper.sh" - destination = tmp_path / "patched.sh" - source.write_text("#!/bin/sh\n" + module.BEFORE + "export HOME=/opt/data\n") - source.chmod(0o755) - - module.patch(source, destination) - - patched = destination.read_text() - assert ". /opt/data/.env" in patched - assert patched.index(". /opt/data/.env") < patched.index("export HOME=/opt/data") - assert destination.stat().st_mode & stat.S_IXUSR - - -def test_gateway_retains_stock_entrypoint_with_patched_wrapper() -> None: +def test_gateway_loads_tool_environment_before_s6_entrypoint() -> None: pod = _deployment()["spec"]["template"]["spec"] init = {item["name"]: item for item in pod["initContainers"]} containers = {item["name"]: item for item in pod["containers"]} assert "repair-cassandra-kanban" in init - assert "patch-main-wrapper" in init - assert containers["hermes"]["command"] == [ - "/init", - "/opt/hermes/docker/main-wrapper.sh", - ] - mounts = {item["name"]: item for item in containers["hermes"]["volumeMounts"]} - assert mounts["main-wrapper-patch"]["subPath"] == "main-wrapper.sh" + hermes = containers["hermes"] + assert hermes["command"] == ["/bin/sh", "-ec"] + startup = hermes["args"][0] + assert startup.index(". /opt/data/.env") < startup.index("exec /init") + assert "exec /init /opt/hermes/docker/main-wrapper.sh gateway run" in startup