Files
jobtrackingapp/docs/redesign/phase-9/p9-requal-01/mariadb-lane.py
T
cesnimda 5ac11549b2
MariaDB persistence qualification / mariadb (push) Failing after 7s
test: harden database qualification startup and cleanup evidence
2026-10-24 14:04:57 +02:00

232 lines
14 KiB
Python

"""Explicit isolated MariaDB browser lanes; self-check never touches a server."""
import argparse
import base64
import datetime
import hashlib
import json
import os
from pathlib import Path
import re
import socket
import subprocess
import sys
import time
import uuid
ROOT = Path(__file__).resolve().parents[4]
OUT = ROOT / '.artifacts/p9-requal-01'
UI = ROOT / 'job-tracker-ui'
CLIENT = ROOT / '.artifacts/mariadb/runtime/mariadb-12.2.2-winx64/bin/mariadb.exe'
CONTAINER = 'jobtracker-it-p9-requal-mariadb-test-1'
IMAGE = 'mariadb:12.2.2@sha256:42a27baaba3e8f3f0159d16381d5a7156661aa91dcc65f663cabb6a6f9503c00'
CONFIGS = {
'mariadb-browser': ('playwright.mariadb.config.ts', 1),
'mariadb-observations': ('../docs/redesign/phase-9/p9-requal-01/observations/playwright.config.ts', 2),
'performance': ('../docs/redesign/phase-9/p9-requal-01/performance/playwright.config.ts', 1),
}
def utc():
return datetime.datetime.now(datetime.timezone.utc).isoformat()
def scrub(value):
if isinstance(value, dict):
return {k: '[REDACTED]' if re.search(r'password|connectionstring', k, re.I) else scrub(v) for k, v in value.items()}
if isinstance(value, list): return [scrub(v) for v in value]
if not isinstance(value, str): return value
value = re.sub(r'Server=127\.0\.0\.1;Port=33308;Database=jt_it_[a-f0-9]{32};User ID=root;Password=[^;\r\n]+;SslMode=None;?', '[REDACTED CONNECTION]', value)
for secret in ('jt-integration-only', 'E2ePassword123!'):
value = value.replace(secret, '[REDACTED]')
return re.sub(r'(Password\s*=\s*)[^;\s]+', r'\1[REDACTED]', value, flags=re.I)
def write(path, value):
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text(json.dumps(scrub(value), indent=2) + '\n', encoding='utf-8')
def schema_sql(schema, drop=False):
if not re.fullmatch(r'jt_it_[a-f0-9]{32}', schema): raise ValueError('Invalid generated schema')
return f'DROP DATABASE `{schema}`;' if drop else f'CREATE DATABASE `{schema}` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;'
def ownership(data):
labels = data['Config'].get('Labels', {})
ports = data['NetworkSettings']['Ports'].get('3306/tcp', [])
if (data['Name'] != '/' + CONTAINER or not data['State']['Running'] or data['Config']['Image'] != IMAGE
or labels.get('com.docker.compose.project') != 'jobtracker-it-p9-requal'
or labels.get('com.docker.compose.service') != 'mariadb-test'
or ports != [{'HostIp': '127.0.0.1', 'HostPort': '33308'}]
or data['HostConfig'].get('Privileged') or data['HostConfig'].get('NetworkMode') == 'host'
or any(mount.get('Destination') == '/var/lib/mysql' and mount.get('Type') != 'tmpfs' for mount in data.get('Mounts', []))
or '/var/lib/mysql' not in data['HostConfig'].get('Tmpfs', {})):
raise ValueError('Exact task-owned pinned loopback disposable container proof required')
return {'name': CONTAINER, 'id': data['Id'], 'imageReference': IMAGE, 'imageId': data['Image'],
'project': labels['com.docker.compose.project'], 'service': 'mariadb-test', 'port': 33308,
'bindHost': '127.0.0.1', 'disposableData': 'tmpfs', 'privileged': False}
def cases(report):
result = []
def walk(suite):
for spec in suite.get('specs', []):
for test in spec.get('tests', []): result.append(test)
for child in suite.get('suites', []): walk(child)
for suite in report.get('suites', []): walk(suite)
return result
def extract_performance(report, process_code):
tests = cases(report)
attachments = [a for test in tests for run in test.get('results', []) for a in run.get('attachments', [])
if a.get('name') == 'p9-requal-01-mariadb-performance']
if len(attachments) != 1: raise ValueError('Expected exactly one performance evidence attachment')
attachment = attachments[0]
if attachment.get('body'):
evidence = json.loads(base64.b64decode(attachment['body']))
elif attachment.get('path'):
path = Path(attachment['path'])
if not path.is_absolute(): path = UI / path
path = path.resolve()
if not path.is_relative_to(OUT.resolve()): raise ValueError('Attachment outside task artifacts')
evidence = json.loads(path.read_text(encoding='utf-8'))
else: raise ValueError('Performance attachment has no body/path')
passed = process_code == 0 and len(tests) == 1 and not report.get('errors') and tests[0].get('status') == 'expected'
passed = passed and len(tests[0].get('results', [])) == 1 and tests[0]['results'][0].get('status') == 'passed'
passed = passed and evidence.get('phase') == 'complete' and evidence.get('actualDurationMs', 0) >= 900000
return {**evidence, 'status': 'PASS' if passed else 'FAIL', 'processExitCode': process_code,
'attachmentName': 'p9-requal-01-mariadb-performance'}
def self_check():
schema = 'jt_it_' + 'a' * 32
assert schema_sql(schema, True) == f'DROP DATABASE `{schema}`;'
for bad in ('main', 'jt_it_' + 'a' * 31, schema + ';DROP DATABASE main'):
try: schema_sql(bad, True)
except ValueError: pass
else: raise AssertionError('Invalid schema accepted')
fixture = {'Name': '/' + CONTAINER, 'Id': 'test', 'Image': 'sha256:test', 'State': {'Running': True},
'Config': {'Image': IMAGE, 'Labels': {'com.docker.compose.project': 'jobtracker-it-p9-requal', 'com.docker.compose.service': 'mariadb-test'}},
'NetworkSettings': {'Ports': {'3306/tcp': [{'HostIp': '127.0.0.1', 'HostPort': '33308'}]}},
'HostConfig': {'Privileged': False, 'NetworkMode': 'bridge', 'Tmpfs': {'/var/lib/mysql': ''}}, 'Mounts': []}
assert ownership(fixture)['port'] == 33308
fixture['HostConfig']['NetworkMode'] = 'host'
try: ownership(fixture)
except ValueError: pass
else: raise AssertionError('Host networking accepted')
fixture['HostConfig']['NetworkMode'] = 'bridge'
fixture['Config']['Image'] = 'mariadb:latest'
try: ownership(fixture)
except ValueError: pass
else: raise AssertionError('Unpinned image accepted')
fixture['Config']['Image'] = IMAGE
fixture['NetworkSettings']['Ports']['3306/tcp'][0]['HostIp'] = '0.0.0.0'
try: ownership(fixture)
except ValueError: pass
else: raise AssertionError('Public database binding accepted')
evidence = {'phase': 'complete', 'actualDurationMs': 900000}
result = {'status': 'passed', 'attachments': [{'name': 'p9-requal-01-mariadb-performance', 'body': base64.b64encode(json.dumps(evidence).encode()).decode()}]}
report = {'suites': [{'specs': [{'tests': [{'status': 'expected', 'results': [result]}]}]}]}
assert extract_performance(report, 0)['status'] == 'PASS'
assert extract_performance(report, 1)['status'] == 'FAIL'
assert 'jt-integration-only' not in scrub('Server=127.0.0.1;Port=33308;Database=' + schema + ';User ID=root;Password=jt-integration-only;SslMode=None')
print('PASS: schema ownership/drop guards, pinned container/network guards, performance extraction/status and redaction; no services touched')
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('mode', choices=list(CONFIGS) + ['self-check'])
args = parser.parse_args()
if args.mode == 'self-check': self_check(); return 0
lane = args.mode
config, expected = CONFIGS[lane]
report_path = OUT / (lane + '.json')
exit_path = OUT / (lane + '-exit.json')
results_path = OUT / (lane + '-results')
if any(path.exists() for path in (report_path, exit_path, results_path)) or (lane == 'performance' and (OUT / 'performance/result.json').exists()):
raise ValueError('Existing evidence present; archive it explicitly before a new invocation')
OUT.mkdir(parents=True, exist_ok=True)
env = {**os.environ, 'MYSQL_PWD': 'jt-integration-only', 'DOTNET_ROOT': 'C:/Users/Connor/.dotnet',
'DOTNET_HOST_PATH': 'C:/Users/Connor/.dotnet/dotnet.exe', 'PLAYWRIGHT_STATIC_EXPORT': 'true',
'PLAYWRIGHT_PREBUILT_EXPORT': 'true', 'PLAYWRIGHT_REUSE_SERVERS': 'false',
'PLAYWRIGHT_JSON_OUTPUT_NAME': str(report_path), 'JOBTRACKER_MARIADB_BROWSER_PORT': '33308',
'JOBTRACKER_MARIADB_CLIENT': str(CLIENT), 'NEXT_PUBLIC_API_BASE_URL': 'http://localhost:5302/api',
'SKIP_MODEL_LOAD': '1', 'AI_SERVICE_SKIP_MODEL_LOAD': '1', 'PYTHONIOENCODING': 'utf-8'}
schema = 'jt_it_' + uuid.uuid4().hex
created = False
process = None
record = {'lane': lane, 'startedUtc': utc(), 'generatedSchema': schema, 'commands': [], 'exitCode': 1, 'cleanup': {'attempted': False}}
started = time.monotonic()
def execute(command, name):
nonlocal created
completed = subprocess.run(['rtk', 'proxy', *command], cwd=ROOT, env=env, capture_output=True, text=True, encoding='utf-8', errors='replace', timeout=30)
# Cleanup ownership must survive a diagnostic write failing after successful CREATE.
if name == 'create-schema' and completed.returncode == 0: created = True
log = OUT / (lane + '-' + name + '.log')
log.write_text(scrub(completed.stdout + completed.stderr), encoding='utf-8')
record['commands'].append({'operation': name, 'exitCode': completed.returncode, 'log': str(log.relative_to(ROOT))})
if completed.returncode: raise RuntimeError(name + ' failed; inspect sanitized operation log')
return completed.stdout.strip()
def sql(query, name):
return execute([str(CLIENT), '--host=127.0.0.1', '--port=33308', '--user=root', '--batch', '--raw', '--skip-column-names', '--execute=' + query], name)
try:
for port in (5302, 3300):
for family, address in ((socket.AF_INET, '127.0.0.1'), (socket.AF_INET6, '::1')):
with socket.socket(family) as probe:
probe.settimeout(0.2)
if probe.connect_ex((address, port)) == 0: raise ValueError(f'Port {port} occupied; exclusive lane required')
# Inspect only this exact task container; full inspection stays in memory, never a report/log.
inspected = subprocess.run(['rtk', 'proxy', 'docker', 'inspect', CONTAINER], capture_output=True, text=True, timeout=20)
if inspected.returncode: raise ValueError('Task container inspection unavailable')
record['ownership'] = ownership(json.loads(inspected.stdout)[0])
version = sql('SELECT VERSION();', 'version')
record['serverVersion'] = version
if not re.fullmatch(r'12\.2\.2-MariaDB(?:-[A-Za-z0-9.]+)?', version): raise ValueError('Unexpected actual server version')
sql(schema_sql(schema), 'create-schema')
env['JOBTRACKER_MARIADB_BROWSER_CONNECTION'] = f'Server=127.0.0.1;Port=33308;Database={schema};User ID=root;Password=jt-integration-only;SslMode=None'
command = ['node', 'node_modules/@playwright/test/cli.js', 'test', '--config=' + config, '--retries=0', '--reporter=list,json', '--output=' + str(results_path)]
record['command'] = command
with (OUT / (lane + '.log')).open('w', encoding='utf-8') as log:
process = subprocess.Popen(['rtk', 'proxy', *command], cwd=UI, env=env, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, encoding='utf-8', errors='replace')
record['processId'] = process.pid
for line in process.stdout: log.write(scrub(line)); log.flush()
record['exitCode'] = process.wait()
report = json.loads(report_path.read_text(encoding='utf-8'))
write(report_path, report)
tests = cases(report)
record['caseCount'] = len(tests)
if len(tests) != expected or any(test.get('status') != 'expected' or len(test.get('results', [])) != 1 or test['results'][0].get('status') != 'passed' for test in tests) or report.get('errors'):
record['exitCode'] = record['exitCode'] or 1
if lane == 'performance': write(OUT / 'performance/result.json', extract_performance(report, record['exitCode']))
except Exception as error:
record['error'] = scrub(type(error).__name__ + ': ' + str(error))
record['exitCode'] = record['exitCode'] or 1
if lane == 'performance' and not (OUT / 'performance/result.json').exists():
write(OUT / 'performance/result.json', {'status': 'FAIL', 'reason': record['error'], 'processExitCode': record['exitCode']})
finally:
# Stop only this invocation's process tree before dropping its schema after an interrupted run.
if process is not None and process.poll() is None:
stopped = subprocess.run(['rtk', 'proxy', 'taskkill', '/PID', str(process.pid), '/T', '/F'], capture_output=True, text=True, timeout=20)
record['processTreeStopExitCode'] = stopped.returncode
try: process.wait(timeout=20)
except subprocess.TimeoutExpired:
created = False
record['cleanup']['error'] = 'Owned process tree still running; schema retained for operator cleanup'
record['exitCode'] = record['exitCode'] or 1
if created:
record['cleanup']['attempted'] = True
try:
sql(schema_sql(schema, True), 'drop-schema')
remaining = sql(f"SELECT COUNT(*) FROM information_schema.SCHEMATA WHERE SCHEMA_NAME='{schema}';", 'verify-drop')
if remaining != '0': raise ValueError('Generated schema remains after cleanup')
record['cleanup'].update(exitCode=0, schemaAbsent=True)
except Exception as error:
record['cleanup'].update(exitCode=1, error=scrub(str(error)))
record['exitCode'] = record['exitCode'] or 1
record.update(finishedUtc=utc(), durationSeconds=time.monotonic() - started,
helperSha256=hashlib.sha256(Path(__file__).read_bytes()).hexdigest())
if lane == 'performance' and (OUT / 'performance/result.json').exists():
result = json.loads((OUT / 'performance/result.json').read_text())
if record['exitCode']: result['status'] = 'FAIL'
result['cleanup'] = record['cleanup']
write(OUT / 'performance/result.json', result)
write(exit_path, record)
print(json.dumps({'lane': lane, 'exitCode': record['exitCode'], 'processRecord': str(exit_path)}))
return record['exitCode']
if __name__ == '__main__':
try: sys.exit(main())
except ValueError as error: raise SystemExit(scrub(str(error)))