"""Disposable local Linux renderer; no host daemon is exposed to the API container.""" import datetime, json, os, re, subprocess, sys, time, urllib.request, uuid from pathlib import Path ROOT = Path(__file__).resolve().parents[4] OUT = ROOT / '.artifacts/p9-requal-01/linux' IMAGE = 'jobtracker-p9-requal-api:security-check' DB = 'jobtracker-it-p9-requal-mariadb-test-1' PIN = 'mariadb:12.2.2@sha256:42a27baaba3e8f3f0159d16381d5a7156661aa91dcc65f663cabb6a6f9503c00' VERSION = 'p9-requal-linux-2b26953' COMMIT = '2b26953' CONFIG = '../docs/redesign/phase-9/p9-requal-01/linux/playwright.config.ts' def guard_schema(name): if not re.fullmatch(r'jt_it_[0-9a-f]{32}', name): raise ValueError('Only a generated qualification schema is allowed.') return name def command(args, **kwargs): return subprocess.run(['rtk', 'proxy', *args], check=True, capture_output=True, text=True, encoding='utf-8', **kwargs).stdout.strip() def sql(query): return command(['docker', 'exec', '-e', 'MYSQL_PWD=jt-integration-only', DB, 'mariadb', '-uroot', '--batch', '--skip-column-names', '-e', query]) def safe_error(error): # CalledProcessError includes command arguments, including development-only credentials. return str(error).replace('jt-integration-only','[test-password]').replace('E2ePassword123!','[test-password]').replace('e2e-only-key-at-least-thirty-two-characters-long','[test-key]') def main(): if '--self-test' in sys.argv: assert guard_schema('jt_it_'+'a'*32) for name in ['jobtracker', 'jt_it_abc', 'jt_it_'+'a'*32+';DROP DATABASE x']: try: guard_schema(name) except ValueError: continue raise AssertionError(name) print('Linux qualification schema guards: PASS') return OUT.mkdir(parents=True, exist_ok=True) if (OUT/'process.json').exists() or (OUT.parent/'linux-renderer.json').exists(): raise ValueError('Retain prior evidence; select a fresh run directory.') owned = json.loads(command(['docker','inspect',DB]))[0] assert owned['Config']['Image'] == PIN assert owned['Config']['Labels']['com.docker.compose.project'] == 'jobtracker-it-p9-requal' assert owned['Config']['Labels']['com.docker.compose.service'] == 'mariadb-test' assert '/var/lib/mysql' in owned['HostConfig'].get('Tmpfs',{}) assert not owned['HostConfig'].get('Privileged') and owned['HostConfig'].get('NetworkMode')!='host' assert owned['State']['Running'] and owned['State']['Health']['Status']=='healthy' assert owned['HostConfig']['PortBindings']['3306/tcp'] == [{'HostIp':'127.0.0.1','HostPort':'33308'}] actual = sql('SELECT VERSION()') assert actual.startswith('12.2.2-MariaDB'), actual name=guard_schema('jt_it_'+uuid.uuid4().hex) suffix=uuid.uuid4().hex[:12] api='jobtracker-p9-requal-linux-'+suffix network='jobtracker-p9-requal-linux-'+suffix record={'startedUtc':datetime.datetime.now(datetime.timezone.utc).isoformat(), 'schema':name, 'serverVersion':actual,'imageTag':IMAGE,'apiContainer':api,'network':network,'cleanup':{},'exitCode':1} created=attached=network_created=api_created=False try: assert sql(f"SELECT COUNT(*) FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME='{name}'")=='0' sql(f'CREATE DATABASE `{name}` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci') created=True assert sql(f"SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA='{name}'")=='0' record['imageId']=command(['docker','image','inspect','--format','{{.Id}}',IMAGE]) record['chromium']=command(['docker','run','--rm','--network','none','--entrypoint','/usr/bin/chromium',IMAGE,'--version']) record['runtime']=command(['docker','run','--rm','--network','none','--entrypoint','dotnet',IMAGE,'--info']) command(['docker','network','create','--label','task=p9-requal-01',network]); network_created=True command(['docker','network','connect','--alias','mariadb',network,DB]); attached=True variables={ 'ASPNETCORE_ENVIRONMENT':'Development','Database__Provider':'mariadb','Database__ServerVersion':'12.2.2', 'ConnectionStrings__JobTracker':f'Server=mariadb;Port=3306;Database={name};User ID=root;Password=jt-integration-only;SslMode=None', 'Auth__Require':'true','Auth__JwtKey':'e2e-only-key-at-least-thirty-two-characters-long', 'Auth__AdminEmail':'e2e@example.test','Auth__AdminPassword':'E2ePassword123!', 'Auth__AllowRegistration':'false', 'RateLimits__AuthLoginPermitLimit':'1000','Cors__Origins__0':'http://localhost:3300', 'Workers__AiOperationsEnabled':'false','Workers__SavedJobSearchAlertsEnabled':'false', 'HttpsRedirection__Enabled':'false','App__Version':VERSION,'App__CommitSha':COMMIT, 'Data__Root':'/data','Logging__LogLevel__Default':'Warning'} args=['docker','create','--name',api,'--label','task=p9-requal-01','--network',network, '-p','127.0.0.1:5302:8080','--tmpfs','/data:rw,nosuid,size=512m'] for key,value in variables.items(): args+=['-e',key+'='+value] command(args+[IMAGE]); api_created=True assert command(['docker','inspect','--format','{{.Image}}',api])==record['imageId'] command(['docker','start',api]) deadline=time.monotonic()+120 while True: try: with urllib.request.urlopen('http://localhost:5302/health',timeout=3) as response: health=json.load(response) assert health['version']==VERSION, health with urllib.request.urlopen('http://localhost:5302/ready',timeout=3) as response: assert json.load(response)['status']=='ready' break except Exception: if time.monotonic()>=deadline: raise time.sleep(1) record['health']=health record['migrationCount']=int(sql(f'SELECT COUNT(*) FROM `{name}`.`__EFMigrationsHistory`')) assert record['migrationCount']>0 env={**os.environ,'P9_REQUAL_LINUX_READY':VERSION,'PLAYWRIGHT_STATIC_EXPORT':'true', 'PLAYWRIGHT_PREBUILT_EXPORT':'true','NEXT_PUBLIC_API_BASE_URL':'http://localhost:5302/api', 'CV_FIDELITY_OUTPUT':str(OUT/'fidelity'),'CV_SETTINGS_FIDELITY_OUTPUT':str(OUT/'settings-fidelity'), 'PLAYWRIGHT_JSON_OUTPUT_NAME':str(OUT.parent/'linux-renderer.json')} test=['node','node_modules/@playwright/test/cli.js','test', '--config='+CONFIG, '--retries=0','--reporter=list,json','--output='+str(OUT/'test-results')] with (OUT/'playwright.log').open('w',encoding='utf-8') as log: record['browserExitCode']=subprocess.run(['rtk','proxy',*test],cwd=ROOT/'job-tracker-ui',env=env,stdout=log,stderr=subprocess.STDOUT).returncode record['exitCode']=record['browserExitCode'] record['migrationCountAfter']=int(sql(f'SELECT COUNT(*) FROM `{name}`.`__EFMigrationsHistory`')) assert record['migrationCountAfter']==record['migrationCount'] except Exception as error: record['exitCode']=1 record['error']=safe_error(error) finally: if api_created: try: (OUT/'api.log').write_text(safe_error(command(['docker','logs',api])),encoding='utf-8') except Exception as error: record['cleanup']['logError']=safe_error(error) try: command(['docker','rm','--force',api]); record['cleanup']['apiRemoved']=True except Exception as error: record['cleanup']['apiError']=str(error) if attached: try: command(['docker','network','disconnect',network,DB]); record['cleanup']['databaseDisconnected']=True except Exception as error: record['cleanup']['disconnectError']=str(error) if network_created: try: command(['docker','network','rm',network]); record['cleanup']['networkRemoved']=True except Exception as error: record['cleanup']['networkError']=str(error) if created and (not api_created or record['cleanup'].get('apiRemoved')): try: guard_schema(name); sql(f'DROP DATABASE `{name}`') assert sql(f"SELECT COUNT(*) FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME='{name}'")=='0' record['cleanup']['schemaRemoved']=True except Exception as error: record['cleanup']['schemaError']=str(error) elif created: record['cleanup']['schemaError']='Schema retained because API removal was not confirmed.' if any(key.endswith('Error') for key in record['cleanup']): record['exitCode']=1 record['finishedUtc']=datetime.datetime.now(datetime.timezone.utc).isoformat() (OUT/'process.json').write_text(json.dumps(record,indent=2)+'\n',encoding='utf-8') (OUT.parent/'linux-renderer-exit.json').write_text(json.dumps(record,indent=2)+'\n',encoding='utf-8') print(json.dumps(record)) if record['exitCode'] or any(key.endswith('Error') for key in record['cleanup']): raise SystemExit(1) if __name__=='__main__': main()