Initial server source import
This commit is contained in:
@@ -0,0 +1,668 @@
|
||||
"""Real PG16 regression; run only in the trusted, offline disposable test image.
|
||||
|
||||
Main provisions a fresh truf-projection-loss-test-<32 hex> volume separately.
|
||||
Run python -u -I -S -B /opt/truf/tests/container_projection_recovery_e2e.py
|
||||
--budget-seconds 300. The budget is cooperative: it NEVER kills PostgreSQL or
|
||||
releases uncertain lifecycle authority. A FAILED_HOLD may outlive that budget.
|
||||
|
||||
The actual schema/migration helpers, queries, transactions and locks are used.
|
||||
Only the recovery module's manifest pin is substituted in memory for this fresh
|
||||
fixture. Fault adapters raise only AFTER real SQL execution or real commit.
|
||||
No accepted journal is deleted, no old output is rebuilt, and no application
|
||||
initialized marker, supervisor, scanner, ingester or provider is started.
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import contextlib
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
import re
|
||||
import runpy
|
||||
import signal
|
||||
import socket
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
from types import SimpleNamespace
|
||||
|
||||
|
||||
OUTPUT = sys.stdout
|
||||
STOPPED = False
|
||||
IMAGE_PATH = Path('/opt/truf/tests/container_projection_recovery_e2e.py')
|
||||
STAMP = '2026-09-16T00:00:00+00:00'
|
||||
APPENDS = 38024
|
||||
OLD_OFFSET = 97783145
|
||||
|
||||
|
||||
def require(value, check):
|
||||
if not value:
|
||||
raise AssertionError(check)
|
||||
|
||||
|
||||
def emit(**values):
|
||||
try:
|
||||
print(json.dumps(values, sort_keys=True), file=OUTPUT, flush=True)
|
||||
except BaseException:
|
||||
pass
|
||||
|
||||
|
||||
def safe_error(error):
|
||||
try:
|
||||
line, state, current = 0, None, error
|
||||
for _ in range(8):
|
||||
tb = current.__traceback__
|
||||
while tb is not None:
|
||||
if tb.tb_frame.f_code.co_filename == str(IMAGE_PATH):
|
||||
line = tb.tb_lineno
|
||||
tb = tb.tb_next
|
||||
candidate = getattr(current, 'sqlstate', None)
|
||||
if state is None and isinstance(candidate, str) and re.fullmatch(r'[A-Z0-9]{5}', candidate):
|
||||
state = candidate
|
||||
current = current.__cause__ or current.__context__
|
||||
if current is None:
|
||||
break
|
||||
name = type(error).__name__
|
||||
if not re.fullmatch(r'[A-Za-z_][A-Za-z0-9_]{0,63}', name or ''):
|
||||
name = 'other'
|
||||
return {'error_type': next((i for i, kind in enumerate(
|
||||
(TimeoutError, OSError, ValueError, RuntimeError, KeyboardInterrupt, AssertionError,
|
||||
TypeError, KeyError, AttributeError, ImportError, LookupError), 1)
|
||||
if isinstance(error, kind)), 0), 'line': line, 'sqlstate': state, 'error_class': name}
|
||||
except BaseException:
|
||||
return {'error_type': 0, 'line': 0, 'sqlstate': None, 'error_class': 'other'}
|
||||
|
||||
|
||||
def hold_pause():
|
||||
try:
|
||||
time.sleep(2)
|
||||
except BaseException:
|
||||
pass
|
||||
|
||||
|
||||
def stop_confirmed(backend, pg):
|
||||
"""Retain this exact backend and the caller's two locks until positive stop."""
|
||||
global STOPPED
|
||||
attempts = 0
|
||||
while True:
|
||||
try:
|
||||
attempts += 1
|
||||
result = backend.stop()
|
||||
require(result.completed is True and result.stopped is True, 'stop_result')
|
||||
require(backend.probe().kind == pg.ProbeKind.STOPPED, 'stopped_probe')
|
||||
backend.close()
|
||||
STOPPED = True
|
||||
emit(stage='stop', stopped=True, attempts=attempts)
|
||||
return
|
||||
except BaseException as error:
|
||||
emit(stage='stop', failed_hold=True, attempts=attempts, **safe_error(error))
|
||||
hold_pause()
|
||||
|
||||
|
||||
def initialize_empty(runtime, config_path):
|
||||
"""The real lifecycle child owns initialization compensation; never kill it."""
|
||||
try:
|
||||
child = subprocess.Popen(runtime._bootstrap_command(
|
||||
'postgres-runtime', 'initialize-empty', '--config', str(config_path)),
|
||||
stdin=subprocess.DEVNULL, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
||||
except BaseException as error:
|
||||
emit(stage='initialize_empty', failed_hold=True, **safe_error(error))
|
||||
return None
|
||||
attempts = 0
|
||||
while True:
|
||||
try:
|
||||
code = child.wait(timeout=10)
|
||||
emit(stage='initialize_empty', completed=code == 0, exit_code=code)
|
||||
return code
|
||||
except BaseException as error:
|
||||
attempts += 1
|
||||
emit(stage='initialize_empty', failed_hold=True, attempts=attempts, **safe_error(error))
|
||||
hold_pause()
|
||||
|
||||
|
||||
def checkpoint(runtime, deadline):
|
||||
require(runtime._shutdown_requested is False, 'shutdown_requested')
|
||||
if time.monotonic() >= deadline:
|
||||
raise TimeoutError('driver_budget')
|
||||
|
||||
|
||||
def identifier(name):
|
||||
require(isinstance(name, str) and re.fullmatch(r'[a-z_][a-z0-9_]*', name), 'fixture_identifier')
|
||||
return '"' + name + '"'
|
||||
|
||||
|
||||
def digest(payload):
|
||||
return hashlib.sha256(payload).hexdigest()
|
||||
|
||||
|
||||
def metadata(connection):
|
||||
row = connection.execute("""SELECT pg_catalog.row_to_json(s) AS stream,
|
||||
pg_catalog.row_to_json(c) AS cursor FROM public.projection_streams s
|
||||
JOIN public.projection_cursors c USING (stream_name)
|
||||
WHERE s.stream_name = 'found_secrets'""").fetchone()
|
||||
parsed = {}
|
||||
for key in ('stream', 'cursor'):
|
||||
value = row[key]
|
||||
if isinstance(value, str):
|
||||
value = json.loads(value)
|
||||
require(isinstance(value, dict), 'metadata_object')
|
||||
parsed[key] = value
|
||||
return parsed
|
||||
|
||||
|
||||
def proof(connection, check):
|
||||
"""Independent, bounded-fixture whole-row digests; never return raw rows."""
|
||||
tables = connection.execute("""SELECT c.relname AS name FROM pg_catalog.pg_class c
|
||||
JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
|
||||
WHERE n.nspname = 'public' AND c.relkind = 'r' ORDER BY c.relname""").fetchall()
|
||||
result = {'tables': {}, 'sequences': {'public': {}}}
|
||||
for table in tables:
|
||||
check()
|
||||
name = table['name']
|
||||
where = " WHERE t.stream_name <> 'found_secrets'" if name in ('projection_streams', 'projection_cursors') else ''
|
||||
row = connection.execute("""SELECT count(*) AS rows, pg_catalog.encode(pg_catalog.sha256(
|
||||
pg_catalog.convert_to(COALESCE(string_agg(h, '' ORDER BY h), ''), 'UTF8')), 'hex') AS sha256
|
||||
FROM (SELECT pg_catalog.encode(pg_catalog.sha256(pg_catalog.convert_to(
|
||||
pg_catalog.row_to_json(t)::text, 'UTF8')), 'hex') COLLATE "C" AS h FROM public."""
|
||||
+ identifier(name) + ' AS t' + where + ') AS hashes').fetchone()
|
||||
require(0 <= row['rows'] <= 100000, 'bounded_fixture')
|
||||
result['tables'][name] = row
|
||||
sequences = connection.execute("""SELECT c.relname AS name FROM pg_catalog.pg_class c
|
||||
JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
|
||||
WHERE n.nspname = 'public' AND c.relkind = 'S' ORDER BY c.relname""").fetchall()
|
||||
for row in sequences:
|
||||
result['sequences']['public'][row['name']] = connection.execute(
|
||||
'SELECT last_value, is_called FROM public.' + identifier(row['name'])).fetchone()
|
||||
return result
|
||||
|
||||
|
||||
def journal_snapshot(runtime, recovery):
|
||||
from container_import import _input, _json
|
||||
|
||||
path = runtime.DATA / 'config' / recovery.JOURNAL_NAME
|
||||
with _input(path, runtime) as (handle, before):
|
||||
require(0 < before[4] <= recovery.MAX_JOURNAL, 'journal_bound')
|
||||
raw = handle.read(recovery.MAX_JOURNAL + 1)
|
||||
value = _json(raw)
|
||||
require(raw == recovery._encoded(value) and value['record']['state'] == 'PREPARED'
|
||||
and value['sha256'] == digest(recovery._encoded(value['record'])), 'durable_journal')
|
||||
return raw, before, value
|
||||
|
||||
|
||||
class ObservedConnection:
|
||||
"""Delegate everything to psycopg; inject only after a real operation."""
|
||||
def __init__(self, connection, runtime, recovery, check, *, after_update=False, lost_ack=False):
|
||||
self.connection, self.runtime, self.recovery, self.check = connection, runtime, recovery, check
|
||||
self.after_update, self.lost_ack = after_update, lost_ack
|
||||
self.updates, self.commits, self.injected = 0, 0, False
|
||||
|
||||
def __getattr__(self, name):
|
||||
return getattr(self.connection, name)
|
||||
|
||||
def execute(self, query, *args, **kwargs):
|
||||
result = self.connection.execute(query, *args, **kwargs)
|
||||
sql = ' '.join(query.split()).upper() if isinstance(query, str) else ''
|
||||
if sql.startswith('UPDATE '):
|
||||
self.updates += 1
|
||||
if self.after_update and sql.startswith('UPDATE PUBLIC.PROJECTION_STREAMS '):
|
||||
require(result.rowcount == 1, 'real_first_update')
|
||||
journal_snapshot(self.runtime, self.recovery)
|
||||
self.after_update, self.injected = False, True
|
||||
raise OSError('synthetic_transport_after_real_update')
|
||||
self.check()
|
||||
return result
|
||||
|
||||
@contextlib.contextmanager
|
||||
def transaction(self, *args, **kwargs):
|
||||
with self.connection.transaction(*args, **kwargs) as transaction:
|
||||
yield transaction
|
||||
self.commits += 1
|
||||
if self.lost_ack:
|
||||
self.lost_ack, self.injected = False, True
|
||||
raise OSError('synthetic_ack_loss_after_real_commit')
|
||||
|
||||
|
||||
def seed_history(connection, runtime, importer):
|
||||
"""Seed real production tables; historical byte proofs need no old files."""
|
||||
with connection.transaction():
|
||||
connection.execute("""INSERT INTO public.target_scans(
|
||||
id, scan_event_id, scan_event_hash, source, target, normalized_target, scan_type,
|
||||
status, ended_at, findings_count, raw_result_storage, created_at)
|
||||
SELECT n, lpad(to_hex(n), 32, '0'), encode(sha256(convert_to('event-' || n, 'UTF8')), 'hex'),
|
||||
'fixture', 'fixture:' || n, 'fixture:' || n, 'fixture', 'found', %s, 1, 'normalized_v2', %s
|
||||
FROM generate_series(1, 38024) AS g(n)""", (STAMP, STAMP))
|
||||
connection.execute("""INSERT INTO public.findings(
|
||||
id, target_scan_id, source, target, detector_name, detector_type, verified,
|
||||
raw_secret, redacted_secret, secret_hash, finding_uid, created_at)
|
||||
SELECT id, id, 'fixture', target, 'Fixture', 'fixture', 0,
|
||||
'synthetic-only-' || id, 'fixture', encode(sha256(convert_to('synthetic-only-' || id, 'UTF8')), 'hex'),
|
||||
encode(sha256(convert_to('finding-' || id, 'UTF8')), 'hex'), %s FROM public.target_scans""", (STAMP,))
|
||||
connection.execute("""INSERT INTO public.scan_result_compat(
|
||||
target_scan_id, schema_version, metadata_json, metadata_sha256, metadata_bytes, reconstruction_status, created_at)
|
||||
SELECT id, 2, '{}', encode(sha256(convert_to('{}', 'UTF8')), 'hex'), 2, 'exact', %s
|
||||
FROM public.target_scans""", (STAMP,))
|
||||
connection.execute("""INSERT INTO public.finding_compat_payloads(
|
||||
finding_id, raw_value, extension_json, payload_sha256, payload_bytes, payload_omitted, created_at)
|
||||
SELECT id, raw_secret, '{}', encode(sha256(convert_to(raw_secret, 'UTF8')), 'hex'),
|
||||
octet_length(raw_secret), 0, %s FROM public.findings""", (STAMP,))
|
||||
connection.execute("""INSERT INTO public.projection_jobs(
|
||||
id, job_kind, event_id, event_hash, target_scan_id, status, required_stream_mask,
|
||||
capacity_items, capacity_bytes, capacity_released, attempts, created_at, updated_at, completed_at)
|
||||
SELECT id, 'scan_event', scan_event_id, scan_event_hash, id, 'completed', 2,
|
||||
1, 65536, 1, 1, %s, %s, %s FROM public.target_scans""", (STAMP, STAMP, STAMP))
|
||||
connection.execute("""WITH positions AS (
|
||||
SELECT id, event_id, event_hash, (id - 1) / 2716 AS generation,
|
||||
CASE WHEN id > 35308 THEN 97783145::bigint ELSE 134217728::bigint END AS bytes,
|
||||
(id - 1) %% 2716 AS ordinal FROM public.projection_jobs)
|
||||
INSERT INTO public.projection_appends(id, job_id, stream_name, event_id, event_hash,
|
||||
generation, byte_offset, byte_length, payload_sha256, record_count, state, prepared_at, appended_at)
|
||||
SELECT id, id, 'found_secrets', event_id, event_hash, generation,
|
||||
bytes * ordinal / 2716, bytes * (ordinal + 1) / 2716 - bytes * ordinal / 2716,
|
||||
encode(sha256(convert_to('historical-output-' || id, 'UTF8')), 'hex'),
|
||||
1, 'appended', %s, %s FROM positions""", (STAMP, STAMP))
|
||||
connection.execute("""INSERT INTO public.projection_rotations(
|
||||
id, stream_name, from_generation, to_generation, source_bytes, segment_relative_path, state, created_at, completed_at)
|
||||
SELECT n + 1, 'found_secrets', n, n + 1, 134217728,
|
||||
'found_secrets.g' || lpad(n::text, 6, '0') || '.jsonl', 'completed', %s, %s
|
||||
FROM generate_series(0, 12) AS g(n)""", (STAMP, STAMP))
|
||||
for i in range(18):
|
||||
provider = f'p{i:02d}'
|
||||
for suffix, filename in (('results', 'Results.jsonl'), ('status', 'Checked.txt')):
|
||||
if suffix == 'status' and i >= 13:
|
||||
continue
|
||||
name = f'keycheck:{provider}:{suffix}'
|
||||
connection.execute("""INSERT INTO public.projection_streams(
|
||||
stream_name, base_relative_path, current_generation, rotation_bytes, max_generations, created_at, updated_at)
|
||||
VALUES (%s, %s, 0, 33554432, 16, %s, %s)""", (name, f'{provider}/{provider}{filename}', STAMP, STAMP))
|
||||
connection.execute("""INSERT INTO public.projection_cursors(stream_name, generation, committed_offset, updated_at)
|
||||
VALUES (%s, 0, %s, %s)""", (name, 1000 + i if suffix == 'status' else 0, STAMP))
|
||||
connection.execute("UPDATE public.projection_streams SET current_generation = 13 WHERE stream_name = 'found_secrets'")
|
||||
connection.execute("""UPDATE public.projection_cursors SET generation = 13, committed_offset = 97783145,
|
||||
last_append_id = 38024, last_job_id = 38024,
|
||||
last_event_id = (SELECT event_id FROM public.projection_jobs WHERE id = 38024),
|
||||
last_event_hash = (SELECT event_hash FROM public.projection_jobs WHERE id = 38024)
|
||||
WHERE stream_name = 'found_secrets'""")
|
||||
for worker in ('result_ingester', 'jsonl_projector'):
|
||||
connection.execute("""INSERT INTO public.pipeline_leases(worker_name, generation, lease_token,
|
||||
supervisor_instance_id, owner_pid, owner_creation_time, owner_executable, state,
|
||||
acquired_at, heartbeat_at, lease_expires_at, updated_at)
|
||||
VALUES (%s, 7, 'synthetic-expired', 'synthetic-expired', 999999, 'synthetic',
|
||||
'/synthetic/expired', 'ready', %s, %s, %s, %s)""", (worker, STAMP, STAMP, STAMP, STAMP))
|
||||
for table in ('target_scans', 'findings', 'projection_jobs', 'projection_appends', 'projection_rotations'):
|
||||
connection.execute('SELECT pg_catalog.setval(pg_catalog.pg_get_serial_sequence(%s, %s), '
|
||||
+ '(SELECT max(id) FROM public.' + identifier(table) + '), true)', ('public.' + table, 'id'))
|
||||
status_files = []
|
||||
for i in range(13):
|
||||
provider = f'p{i:02d}'
|
||||
directory = runtime.DATA / 'runtime-linux/keychecks' / provider
|
||||
directory.mkdir(mode=0o700)
|
||||
path = directory / f'{provider}Checked.txt'
|
||||
importer._write(runtime, path, b'synthetic status snapshot\n')
|
||||
status_files.append(path)
|
||||
return status_files
|
||||
|
||||
|
||||
def fixture_manifest(connection, runtime, importer, recovery, baseline, status_files, system_identifier):
|
||||
# Required source labels are inert format metadata, never opened as paths.
|
||||
paths = list(status_files)
|
||||
for name in sorted(importer.REQUIRED_FILES):
|
||||
path = runtime.DATA / name
|
||||
if not os.path.lexists(path):
|
||||
path.parent.mkdir(mode=0o700, parents=True, exist_ok=True)
|
||||
importer._write(runtime, path, b'')
|
||||
paths.append(path)
|
||||
files = []
|
||||
for path in paths:
|
||||
with importer._input(path, runtime) as (handle, before):
|
||||
raw = handle.read(4096)
|
||||
require(len(raw) == before[4], 'status_fixture_bound')
|
||||
files.append({'path': path.relative_to(runtime.DATA).as_posix(), 'size': len(raw), 'sha256': digest(raw)})
|
||||
# Historical bytes are intentionally not materialized; this is not a restore test.
|
||||
files.append({'path': 'runtime-linux/results/found_secrets.jsonl', 'size': OLD_OFFSET,
|
||||
'sha256': digest(b'intentionally-unavailable-synthetic-output')})
|
||||
value = {'format': 'truf-windows-snapshot-v1',
|
||||
'source': {'root': r'D:\truf', 'postgres_data_dir': r'S:\postgres-data',
|
||||
'supervisor_stopped': True, 'postgres_stopped': True},
|
||||
'database': {'version_num': connection.execute("SELECT current_setting('server_version_num')::int AS version").fetchone()['version'],
|
||||
'system_identifier': '1' if system_identifier != '1' else '2',
|
||||
'database_name': 'synthetic_source', 'user_name': 'synthetic_source', 'port': 15432,
|
||||
'data_directory': r'S:\postgres-data', 'bytes': 6, 'sha256': digest(b'PGDMPx'),
|
||||
'table_counts': {name: item['rows'] + int(name in ('projection_streams', 'projection_cursors'))
|
||||
for name, item in baseline['tables'].items()},
|
||||
'sequence_states': baseline['sequences'], 'sequence_count': len(baseline['sequences']['public'])},
|
||||
'archive': {'bytes': sum(item['size'] for item in files) + 10240,
|
||||
'sha256': digest(b'synthetic archive identity')}, 'files': files}
|
||||
raw = importer._encoded(value)
|
||||
pin = digest(raw)
|
||||
importer._manifest(raw, pin)
|
||||
importer._write(runtime, runtime.DATA / 'config/windows-import-manifest.json', raw)
|
||||
recovery.APPROVED_MANIFEST_SHA256 = pin
|
||||
return pin
|
||||
|
||||
|
||||
def exercise_projector(connection, db, runtime, config, check):
|
||||
from jsonl_projector import JsonlProjector
|
||||
from migrate_runtime_safety import initialize_projection_cursors_from_existing_files, require_legacy_cutover_clear
|
||||
|
||||
# This is a real offline cutover after recovery, not a mocked readiness gate.
|
||||
db.require_runtime_safety_schema()
|
||||
cursors = initialize_projection_cursors_from_existing_files(db, config)
|
||||
legacy = require_legacy_cutover_clear(db, config)
|
||||
migrations = db.conn.execute('SELECT version, code_sha256 FROM runtime_schema_migrations ORDER BY version').fetchall()
|
||||
db.conn.commit()
|
||||
db.record_final_cutover({'legacy': legacy, 'projection_cursors': cursors,
|
||||
'schema_migrations': [dict(row) for row in migrations]})
|
||||
db.require_final_cutover()
|
||||
check()
|
||||
event_id, event_hash, finding_uid = 'f' * 32, digest(b'future-event'), digest(b'future-finding')
|
||||
with connection.transaction():
|
||||
scan_id = connection.execute("""INSERT INTO public.target_scans(
|
||||
scan_event_id, scan_event_hash, target, normalized_target, scan_type, status, ended_at,
|
||||
findings_count, raw_result_storage, created_at)
|
||||
VALUES (%s, %s, 'fixture:future', 'fixture:future', 'fixture', 'found', %s, 1, 'normalized_v2', %s)
|
||||
RETURNING id""", (event_id, event_hash, STAMP, STAMP)).fetchone()['id']
|
||||
connection.execute("""INSERT INTO public.scan_result_compat(target_scan_id, schema_version,
|
||||
metadata_json, metadata_sha256, metadata_bytes, reconstruction_status, created_at)
|
||||
VALUES (%s, 2, '{}', %s, 2, 'exact', %s)""", (scan_id, digest(b'{}'), STAMP))
|
||||
finding_id = connection.execute("""INSERT INTO public.findings(target_scan_id, detector_name,
|
||||
detector_type, verified, raw_secret, redacted_secret, finding_uid, created_at)
|
||||
VALUES (%s, 'Fixture', 'fixture', 0, 'synthetic-future', 'fixture', %s, %s) RETURNING id""",
|
||||
(scan_id, finding_uid, STAMP)).fetchone()['id']
|
||||
connection.execute("""INSERT INTO public.finding_compat_payloads(finding_id, raw_value,
|
||||
extension_json, payload_sha256, payload_bytes, payload_omitted, created_at)
|
||||
VALUES (%s, 'synthetic-future', '{}', %s, 16, 0, %s)""", (finding_id, digest(b'synthetic-future'), STAMP))
|
||||
job_id = connection.execute("""INSERT INTO public.projection_jobs(job_kind, event_id, event_hash,
|
||||
target_scan_id, status, required_stream_mask, capacity_items, capacity_bytes, created_at, updated_at)
|
||||
VALUES ('scan_event', %s, %s, %s, 'pending', 2, 1, 65536, %s, %s) RETURNING id""",
|
||||
(event_id, event_hash, scan_id, STAMP, STAMP)).fetchone()['id']
|
||||
connection.execute("""UPDATE public.pipeline_capacity SET projection_items = projection_items + 1,
|
||||
projection_bytes = projection_bytes + 65536 WHERE id = 1""")
|
||||
worker = JsonlProjector(db, str(runtime.DATA / 'runtime-linux/results'), 'projection-loss-e2e',
|
||||
keycheck_dir=str(runtime.DATA / 'runtime-linux/keychecks'))
|
||||
try:
|
||||
worker.start()
|
||||
require(worker.process_one() is True, 'projector_processed')
|
||||
require(worker.process_one() is False, 'projector_idle')
|
||||
row = connection.execute("""SELECT a.generation, a.byte_offset, a.byte_length,
|
||||
a.payload_sha256, a.state, j.status, j.capacity_released
|
||||
FROM public.projection_appends a JOIN public.projection_jobs j ON j.id = a.job_id
|
||||
WHERE a.job_id = %s AND a.stream_name = 'found_secrets'""", (job_id,)).fetchone()
|
||||
path = runtime.DATA / 'runtime-linux/results/found_secrets.jsonl'
|
||||
runtime.private_path(path)
|
||||
require(path.stat().st_size < 65536, 'bounded_new_output')
|
||||
raw = path.read_bytes()
|
||||
require(row and row['generation'] == 14 and row['byte_offset'] == 0
|
||||
and row['byte_length'] == len(raw) and row['payload_sha256'] == digest(raw)
|
||||
and row['state'] == 'appended' and row['status'] == 'completed' and row['capacity_released'] == 1,
|
||||
'projector_fenced_append')
|
||||
require(json.loads(raw)['finding_uid'] == finding_uid, 'projector_real_payload')
|
||||
cursor = metadata(connection)['cursor']
|
||||
require(cursor['generation'] == 14 and cursor['committed_offset'] == len(raw)
|
||||
and cursor['last_job_id'] == job_id, 'projector_cursor')
|
||||
capacity = connection.execute('SELECT projection_items, projection_bytes FROM public.pipeline_capacity WHERE id = 1').fetchone()
|
||||
require(capacity == {'projection_items': 0, 'projection_bytes': 0}, 'projector_capacity')
|
||||
emit(stage='projector', passed=True, generation=14, records=1, bytes=len(raw))
|
||||
finally:
|
||||
worker.stop()
|
||||
|
||||
|
||||
def run_cases(connection, connect, resources, runtime, config, identity, initialize_lock, authority_lock, check):
|
||||
import container_import as importer
|
||||
import container_projection_recovery as recovery
|
||||
from runtime_security import PrivateFileLock
|
||||
from scanner_db import ScannerDB, migrate_runtime_safety_schema
|
||||
|
||||
schema_db = ScannerDB(db_url=os.environ['SCANNER_DB_URL'], initialize=False)
|
||||
resources.append(schema_db)
|
||||
require(schema_db.enabled and schema_db.conn.is_postgres, 'real_schema_connection')
|
||||
try:
|
||||
migrate_runtime_safety_schema(schema_db, initialize_base=True)
|
||||
schema_db.require_runtime_safety_schema()
|
||||
finally:
|
||||
schema_db.close()
|
||||
emit(stage='schema', passed=True)
|
||||
check()
|
||||
status_files = seed_history(connection, runtime, importer)
|
||||
emit(stage='seed', passed=True)
|
||||
baseline = proof(connection, check)
|
||||
emit(stage='proof', passed=True, tables=len(baseline['tables']), sequences=len(baseline['sequences']['public']))
|
||||
before = metadata(connection)
|
||||
require(before['stream']['current_generation'] == before['cursor']['generation'] == 13
|
||||
and before['cursor']['committed_offset'] == OLD_OFFSET, 'reviewed_before')
|
||||
require(baseline['tables']['projection_appends']['rows'] == APPENDS
|
||||
and baseline['tables']['projection_rotations']['rows'] == 13
|
||||
and baseline['tables']['projection_append_audit']['rows'] == 0, 'reviewed_history')
|
||||
original_pin = recovery.APPROVED_MANIFEST_SHA256
|
||||
pin = fixture_manifest(connection, runtime, importer, recovery, baseline, status_files, identity['system_identifier'])
|
||||
journal_path = runtime.DATA / 'config' / recovery.JOURNAL_NAME
|
||||
status_before = {path: (path.stat().st_ino, path.read_bytes()) for path in status_files}
|
||||
emit(stage='fixture', passed=True, streams=34, appends=APPENDS, rotations=13,
|
||||
tables=len(baseline['tables']), sequences=len(baseline['sequences']['public']))
|
||||
|
||||
def recover(observed):
|
||||
return recovery.recover_found_secrets_projection(runtime, observed,
|
||||
system_identifier=identity['system_identifier'], manifest_sha256=pin,
|
||||
initialize_lock=initialize_lock, authority_lock=authority_lock)
|
||||
|
||||
def refused(observed):
|
||||
try:
|
||||
recover(observed)
|
||||
except recovery.ProjectionRecoveryError as error:
|
||||
if not observed.injected and (observed.after_update or observed.lost_ack):
|
||||
emit(stage='fault_not_reached', passed=False, **safe_error(error))
|
||||
return
|
||||
raise AssertionError('expected_recovery_refusal')
|
||||
|
||||
def alone():
|
||||
while True:
|
||||
check()
|
||||
connection.execute('SELECT pg_catalog.pg_stat_clear_snapshot()')
|
||||
count = connection.execute("""SELECT count(*) AS count FROM pg_catalog.pg_stat_activity
|
||||
WHERE backend_type = 'client backend' AND pid <> pg_backend_pid()""").fetchone()['count']
|
||||
if count == 0:
|
||||
return
|
||||
time.sleep(0.05)
|
||||
|
||||
try:
|
||||
alone()
|
||||
contender = connect()
|
||||
resources.append(contender)
|
||||
try:
|
||||
observed = ObservedConnection(connection, runtime, recovery, check)
|
||||
refused(observed)
|
||||
require(observed.updates == 0, 'other_client_no_updates')
|
||||
with contender.transaction():
|
||||
contender.execute('LOCK TABLE public.projection_streams IN ROW EXCLUSIVE MODE')
|
||||
observed = ObservedConnection(connection, runtime, recovery, check)
|
||||
refused(observed)
|
||||
require(observed.updates == 0, 'writer_contention_no_updates')
|
||||
finally:
|
||||
contender.close()
|
||||
alone()
|
||||
with PrivateFileLock(str(runtime.DATA / 'runtime-linux/results/.jsonl-projector.lock')):
|
||||
observed = ObservedConnection(connection, runtime, recovery, check)
|
||||
refused(observed)
|
||||
require(observed.updates == 0, 'projector_file_lock_no_updates')
|
||||
require(metadata(connection) == before and proof(connection, check) == baseline
|
||||
and not os.path.lexists(journal_path), 'contention_unchanged')
|
||||
emit(stage='contention', passed=True, cases=3, updates=0)
|
||||
|
||||
observed = ObservedConnection(connection, runtime, recovery, check, after_update=True)
|
||||
refused(observed)
|
||||
require(observed.injected and observed.updates == 1 and observed.commits == 0, 'rollback_fault_window')
|
||||
require(int(connection.info.transaction_status) == 0 and metadata(connection) == before
|
||||
and proof(connection, check) == baseline, 'both_rows_rolled_back')
|
||||
journal = journal_snapshot(runtime, recovery)
|
||||
require(journal[2]['record']['before'] == before, 'journal_before')
|
||||
emit(stage='rollback', passed=True, updates=1, commits=0, journal_retained=True)
|
||||
|
||||
observed = ObservedConnection(connection, runtime, recovery, check, lost_ack=True)
|
||||
refused(observed)
|
||||
require(observed.injected and observed.updates == 2 and observed.commits == 1, 'real_commit_before_ack_loss')
|
||||
after = metadata(connection)
|
||||
require(after == journal[2]['record']['after'] and proof(connection, check) == baseline
|
||||
and journal_snapshot(runtime, recovery) == journal, 'committed_despite_ack_loss')
|
||||
emit(stage='lost_ack', passed=True, updates=2, commits=1, history_unchanged=True)
|
||||
|
||||
observed = ObservedConnection(connection, runtime, recovery, check)
|
||||
result = recover(observed)
|
||||
require(result['status'] == 'already-committed' and observed.updates == 0
|
||||
and result['journal_sha256'] == digest(journal[0]) and metadata(connection) == after
|
||||
and proof(connection, check) == baseline and journal_snapshot(runtime, recovery) == journal,
|
||||
'idempotent_readonly_retry')
|
||||
require({path: (path.stat().st_ino, path.read_bytes()) for path in status_files} == status_before,
|
||||
'other_output_unchanged')
|
||||
emit(stage='retry', passed=True, updates=0, journal_unchanged=True, history_unchanged=True, sequences_unchanged=True)
|
||||
|
||||
writer_db = ScannerDB(db_url=os.environ['SCANNER_DB_URL'], initialize=False)
|
||||
resources.append(writer_db)
|
||||
require(writer_db.enabled and writer_db.conn.is_postgres, 'real_writer_connection')
|
||||
try:
|
||||
exercise_projector(connection, writer_db, runtime, config, check)
|
||||
finally:
|
||||
writer_db.close()
|
||||
alone()
|
||||
advanced, advanced_proof = metadata(connection), proof(connection, check)
|
||||
future_path = runtime.DATA / 'runtime-linux/results/found_secrets.jsonl'
|
||||
future_bytes, future_inode = future_path.read_bytes(), future_path.stat().st_ino
|
||||
observed = ObservedConnection(connection, runtime, recovery, check)
|
||||
refused(observed)
|
||||
require(observed.updates == 0 and advanced['cursor']['committed_offset'] > 0
|
||||
and metadata(connection) == advanced and proof(connection, check) == advanced_proof
|
||||
and (future_path.read_bytes(), future_path.stat().st_ino) == (future_bytes, future_inode)
|
||||
and journal_snapshot(runtime, recovery) == journal, 'future_output_not_rewound')
|
||||
emit(stage='future_output', passed=True, updates=0, advanced_cursor_retained=True, journal_unchanged=True)
|
||||
finally:
|
||||
recovery.APPROVED_MANIFEST_SHA256 = original_pin
|
||||
|
||||
|
||||
def main():
|
||||
global OUTPUT
|
||||
# Native pg_ctl also inherits fd 1/2: Python stream redirection alone is insufficient.
|
||||
OUTPUT = os.fdopen(os.dup(1), 'w', encoding='utf-8', buffering=1)
|
||||
sink = os.open(os.devnull, os.O_WRONLY)
|
||||
try:
|
||||
os.dup2(sink, 1)
|
||||
os.dup2(sink, 2)
|
||||
finally:
|
||||
os.close(sink)
|
||||
parser = argparse.ArgumentParser(allow_abbrev=False)
|
||||
parser.add_argument('--budget-seconds', type=int, default=300)
|
||||
args = parser.parse_args()
|
||||
require(30 <= args.budget_seconds <= 3600, 'budget')
|
||||
started = time.monotonic()
|
||||
deadline = started + args.budget_seconds
|
||||
require(sys.platform == 'linux' and os.geteuid() == os.getuid() == 10001
|
||||
and os.getegid() == os.getgid() == 10001, 'test_identity')
|
||||
require(Path(__file__) == IMAGE_PATH and sys.flags.isolated and sys.flags.no_site
|
||||
and sys.flags.dont_write_bytecode, 'test_image')
|
||||
require({name for _, name in socket.if_nameindex()} == {'lo'}, 'offline_test')
|
||||
raw_mounts = Path('/proc/self/mountinfo').read_bytes()
|
||||
require(len(raw_mounts) <= 1024 * 1024, 'mount_bound')
|
||||
mounts = [line.split() for line in raw_mounts.decode('utf-8', errors='strict').splitlines()]
|
||||
data = [row for row in mounts if len(row) > 6 and (row[4] == '/data' or row[4].startswith('/data/'))]
|
||||
require(len(data) == 1 and data[0][4] == '/data' and '-' in data[0]
|
||||
and re.fullmatch(r'/var/lib/docker/volumes/truf-projection-loss-test-[a-f0-9]{32}/_data', data[0][3])
|
||||
and data[0][data[0].index('-') + 1] == 'ext4', 'fresh_test_volume')
|
||||
runtime = SimpleNamespace(**runpy.run_path('/opt/truf/app/container_runtime.py'))
|
||||
runtime.require_container()
|
||||
runtime.private_path(IMAGE_PATH.parent, directory=True)
|
||||
runtime.private_path(IMAGE_PATH)
|
||||
runtime._shutdown_requested = False
|
||||
for sig in (signal.SIGTERM, signal.SIGINT, signal.SIGHUP):
|
||||
signal.signal(sig, lambda *_: setattr(runtime, '_shutdown_requested', True))
|
||||
|
||||
def fresh():
|
||||
runtime.private_path(runtime.DATA / 'postgres-linux', directory=True)
|
||||
require(not any((runtime.DATA / 'postgres-linux').iterdir()), 'empty_pgdata')
|
||||
require(not any((runtime.DATA / 'runtime-linux/results').iterdir()), 'empty_results')
|
||||
for name in ('runtime-linux/postgres/cluster_identity.json', 'initialized.json',
|
||||
'config/windows-import-manifest.json', 'config/found-secrets-loss-g13-g14.prepared.json'):
|
||||
require(not os.path.lexists(runtime.DATA / name), 'fresh_fixture')
|
||||
|
||||
fresh()
|
||||
config_path = runtime.DEFAULT_CONFIG
|
||||
config = runtime.prepare_environment(config_path)
|
||||
os.environ.update(TRUF_DB_STATEMENT_TIMEOUT_MS='120000', TRUF_DB_LOCK_TIMEOUT_MS='5000',
|
||||
TRUF_DB_IDLE_TRANSACTION_TIMEOUT_MS='300000')
|
||||
import postgres_runtime as pg
|
||||
import psycopg
|
||||
from psycopg.rows import dict_row
|
||||
from runtime_security import ClusterAuthorityLock, PrivateFileLock
|
||||
|
||||
def connect():
|
||||
return psycopg.connect(os.environ['SCANNER_DB_URL'], autocommit=True, row_factory=dict_row,
|
||||
connect_timeout=5, application_name='truf-projection-loss-e2e', tcp_user_timeout=30000,
|
||||
options='-c search_path=public -c statement_timeout=120000 -c lock_timeout=5000 '
|
||||
'-c idle_in_transaction_session_timeout=300000 -c row_security=off '
|
||||
'-c log_min_error_statement=panic -c log_min_messages=panic '
|
||||
'-c log_statement=none -c log_min_duration_statement=-1')
|
||||
|
||||
resources = []
|
||||
check = lambda: checkpoint(runtime, deadline)
|
||||
with PrivateFileLock(str(runtime.INITIALIZE_LOCK)) as initialize_lock:
|
||||
fresh()
|
||||
authority_lock = ClusterAuthorityLock(config, endpoint_dsn=os.environ['SCANNER_DB_URL'])
|
||||
code = initialize_empty(runtime, config_path)
|
||||
while True:
|
||||
try:
|
||||
authority_lock.acquire()
|
||||
break
|
||||
except BaseException as error:
|
||||
emit(stage='authority', failed_hold=True, **safe_error(error))
|
||||
hold_pause()
|
||||
try:
|
||||
backend = None
|
||||
while backend is None:
|
||||
try:
|
||||
backend = pg.PostgresBackend(config, stop_timeout_sec=60)
|
||||
except BaseException as error:
|
||||
emit(stage='backend', failed_hold=True, **safe_error(error))
|
||||
hold_pause()
|
||||
try:
|
||||
require(code == 0, 'initialize_empty_exit')
|
||||
check()
|
||||
require(backend.probe().kind == pg.ProbeKind.STOPPED, 'initial_stopped_probe')
|
||||
identity = pg.verify_cluster_identity(config)
|
||||
require(identity['pg_major'] == 16 and identity['data_directory'] == '/data/postgres-linux', 'bound_fixture')
|
||||
require(backend.start().accepted is True, 'direct_backend_start')
|
||||
while True:
|
||||
check()
|
||||
probe = backend.probe()
|
||||
if probe.kind == pg.ProbeKind.READY:
|
||||
break
|
||||
require(probe.kind == pg.ProbeKind.RECOVERING, 'authenticated_start')
|
||||
time.sleep(0.1)
|
||||
emit(stage='start', ready=True)
|
||||
connection = connect()
|
||||
resources.append(connection)
|
||||
count = connection.execute("""SELECT count(*) AS count FROM pg_catalog.pg_class c
|
||||
JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
|
||||
WHERE n.nspname = 'public' AND c.relkind IN ('r','p','f')""").fetchone()['count']
|
||||
require(count == 0, 'virgin_schema')
|
||||
run_cases(connection, connect, resources, runtime, config, identity, initialize_lock, authority_lock, check)
|
||||
require(not os.path.lexists(runtime.INITIALIZED), 'no_application_marker')
|
||||
finally:
|
||||
try:
|
||||
for resource in reversed(resources):
|
||||
try:
|
||||
resource.close()
|
||||
except BaseException as error:
|
||||
emit(stage='client_close', failed_hold=True, **safe_error(error))
|
||||
finally:
|
||||
stop_confirmed(backend, pg)
|
||||
finally:
|
||||
authority_lock.release()
|
||||
emit(stage='finished', passed=True, stopped=STOPPED, application_initialized=False,
|
||||
elapsed_ms=round((time.monotonic() - started) * 1000))
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
try:
|
||||
result = main()
|
||||
except BaseException as error:
|
||||
emit(stage='finished', passed=False, stopped=STOPPED, **safe_error(error))
|
||||
result = 1
|
||||
raise SystemExit(result)
|
||||
Reference in New Issue
Block a user