Why: probing identity by running the subcommand still launched old RDM builds, and ensure on a missing database created an empty one. A batch update also inherited the caller's AGENT_SYSTEM_* variables. Evidence: python3 -m unittest tests.test_svc_identity tests.test_svc_recover passed (41 tests, 1 skipped). tests.test_svc_lifecycle_isolation caller-env and restart cases passed. Impact: capability probes use version --json only. Established instances refuse a missing identity row until adopt --confirm. update and recover run each service in a clean environment. serve reads a 0600 serve.env and does not inherit the caller environment. Backend and documents pins move to the D1 identity and recovery-plan commits. Refs: recovery plan r3.1 D1; Backend feature/d1-identity-open; RDM feature/d1-version-json Co-authored-by: Cursor <cursoragent@cursor.com>
55 lines
2.3 KiB
Python
55 lines
2.3 KiB
Python
"""生命周期隔离:调用方的 AGENT_SYSTEM_* 不得进入 serve,serve.env 为 0600。"""
|
||
import importlib.util
|
||
import json
|
||
import shutil
|
||
from pathlib import Path
|
||
|
||
SOURCE = Path(__file__).resolve().parents[1]
|
||
_spec = importlib.util.spec_from_file_location("tsr", SOURCE / "tests" / "test_svc_recover.py")
|
||
tsr = importlib.util.module_from_spec(_spec)
|
||
_spec.loader.exec_module(tsr)
|
||
|
||
|
||
class LifecycleIsolationTests(tsr.SvcAutoRecoverTests):
|
||
def setUp(self):
|
||
super().setUp()
|
||
shutil.copy2(SOURCE / "scripts" / "svc_manifest.py",
|
||
self.root / "scripts" / "svc_manifest.py")
|
||
|
||
def test_caller_identity_env_does_not_reach_serve(self):
|
||
self.seed_agent("iso", 9461)
|
||
env = dict(self.env)
|
||
env["AGENT_SYSTEM_DSN"] = "postgres://leak-user:leak-pass@127.0.0.1/leak"
|
||
env["AGENT_SYSTEM_DATA"] = "/tmp/leak-data"
|
||
env["AGENT_SYSTEM_INSTANCE_ID"] = "svc-foreign-leak"
|
||
self.svc("iso", "start", env=env)
|
||
pid = self.pid_of("iso")
|
||
self.assertIsNotNone(pid)
|
||
raw = Path("/proc/%d/environ" % pid).read_bytes()
|
||
self.assertNotIn(b"leak-pass", raw)
|
||
self.assertNotIn(b"/tmp/leak-data", raw)
|
||
self.assertNotIn(b"svc-foreign-leak", raw)
|
||
serve_env = self.runtime("iso") / "serve.env"
|
||
self.assertTrue(serve_env.is_file())
|
||
self.assertEqual(serve_env.stat().st_mode & 0o777, 0o600)
|
||
text = serve_env.read_text()
|
||
self.assertNotIn("AGENT_SYSTEM_", text)
|
||
self.assertNotIn("leak-pass", text)
|
||
self.svc("iso", "stop")
|
||
|
||
def test_two_bare_restarts_keep_identity(self):
|
||
self.seed_agent("iso2", 9462)
|
||
self.svc("iso2", "start")
|
||
manifest = json.loads((self.runtime("iso2") / "manifest.json").read_text())
|
||
iid = manifest["instance_id"]
|
||
data = manifest["identity"]["data_root"]
|
||
self.svc("iso2", "stop")
|
||
self.svc("iso2", "start")
|
||
self.svc("iso2", "stop")
|
||
self.svc("iso2", "start")
|
||
again = json.loads((self.runtime("iso2") / "manifest.json").read_text())
|
||
self.assertEqual(again["instance_id"], iid)
|
||
self.assertEqual(again["identity"]["data_root"], data)
|
||
self.assertEqual(again["identity"].get("db_marker"), "established")
|
||
self.svc("iso2", "stop")
|