fix(qa): isolate legacy database clients with owned transport guard
This commit is contained in:
1 parent
65c567152e
commit
01e949093a
9 files changed
+577
-23
No files matched your search
+20
-3
@@ -17,6 +17,7 @@ import sys
|
||||
import time
|
||||
import urllib.parse
|
||||
import xml.etree.ElementTree as ET
|
||||
import qa_isolation
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
DATABASE = 'railwaysystem_fusion_qa_20261013'
|
||||
@@ -50,6 +51,8 @@ def environment(config):
|
||||
raise ValueError('Refusing non-target database URL')
|
||||
if any(k.lower() in ('user', 'password') for k in urllib.parse.parse_qs(parsed.query)):
|
||||
raise ValueError('URL credentials forbidden')
|
||||
if parsed.port == qa_isolation.LEGACY_PORT:
|
||||
raise ValueError('Legacy database endpoint is forbidden')
|
||||
password = config.get('FUSION_QA_APP_PASSWORD', '')
|
||||
if not password:
|
||||
raise ValueError('Missing FUSION_QA_APP_PASSWORD')
|
||||
@@ -115,11 +118,14 @@ def launch(name, command, env):
|
||||
child = subprocess.Popen(command, cwd=ROOT, env=env, stdout=log, stderr=subprocess.STDOUT, start_new_session=True)
|
||||
log.close()
|
||||
started = subprocess.run(['ps', '-p', str(child.pid), '-o', 'lstart='], text=True, capture_output=True).stdout.strip()
|
||||
ledger.write_text(json.dumps({'pid': child.pid, 'command': command, 'cwd': str(ROOT), 'started': started}))
|
||||
ledger.write_text(json.dumps({'pid': child.pid, 'command': command, 'cwd': str(ROOT), 'started': started,
|
||||
'isolation_id': env.get('FUSION_QA_ISOLATION_ID')}))
|
||||
print(name + ' PID=' + str(child.pid) + ' LOG=' + str(RUNTIME / (name + '.log')))
|
||||
|
||||
|
||||
def start(config, env, backend_port, redis_port):
|
||||
lease = qa_isolation.require(env.get('QA_DB_URL'))
|
||||
env['FUSION_QA_ISOLATION_ID'] = lease['isolation_id']
|
||||
if inspect(env, ['--require-migrated']):
|
||||
raise ValueError('Backend not started: QA migration preflight failed')
|
||||
available(backend_port)
|
||||
@@ -189,13 +195,15 @@ def wait_for_exit(pid):
|
||||
raise ValueError('Task-owned process did not stop; ledger retained, no forced kill')
|
||||
|
||||
|
||||
def stop():
|
||||
def stop(isolation_id=None):
|
||||
private_directory()
|
||||
for name in ['frontend', 'backend', 'redis']:
|
||||
path = RUNTIME / (name + '.json')
|
||||
if not path.exists():
|
||||
continue
|
||||
item = json.loads(path.read_text())
|
||||
if isolation_id is not None and item.get('isolation_id') != isolation_id:
|
||||
raise ValueError('Other isolation lease owns this runtime ledger; refusing to signal it')
|
||||
actual = subprocess.run(['ps', '-p', str(item['pid']), '-o', 'command='], text=True, capture_output=True).stdout.strip()
|
||||
if actual:
|
||||
# macOS Python wrappers exec Python.app; Redis rewrites its process title.
|
||||
@@ -233,11 +241,20 @@ def main():
|
||||
stop()
|
||||
return 0
|
||||
if args.action == 'frontend':
|
||||
lease = qa_isolation.require()
|
||||
backend = json.loads((RUNTIME / 'backend.json').read_text())
|
||||
if backend.get('isolation_id') != lease['isolation_id'] or '--server.port=' + str(args.backend_port) not in backend.get('command', []):
|
||||
raise ValueError('Frontend backend belongs to another isolation lease')
|
||||
available(args.frontend_port)
|
||||
env = os.environ.copy()
|
||||
env['FUSION_QA_ISOLATION_ID'] = lease['isolation_id']
|
||||
launch('frontend', [sys.executable, str(ROOT / 'scripts/qa/serve_frontend.py'),
|
||||
'--port', str(args.frontend_port), '--backend-port', str(args.backend_port)], os.environ.copy())
|
||||
'--port', str(args.frontend_port), '--backend-port', str(args.backend_port)], env)
|
||||
return 0
|
||||
config = load_config(args.config)
|
||||
if args.action == 'start' or (RUNTIME / qa_isolation.LEDGER).exists():
|
||||
lease = qa_isolation.require()
|
||||
config.setdefault('FUSION_QA_DB_URL', qa_isolation.url_for(lease))
|
||||
env = environment(config)
|
||||
if args.action == 'inspect':
|
||||
return inspect(env, [])
|
||||
|
||||
Reference in new issue
Block a user