railiance-platform/scripts/service_assurance.py
codex 234b1b559f
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Implement S3 service assurance and admission checks
Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a06ecb-456a-71c2-b41e-0755d336e883
2026-09-05 11:43:55 +02:00

183 lines
8.8 KiB
Python

#!/usr/bin/env python3
"""Read-only S3 admission and bounded evidence evaluation. Never fetch Secrets."""
from __future__ import annotations
import argparse
from datetime import datetime, timezone
import hashlib
import importlib.util
import json
import math
from pathlib import Path
import sys
import yaml
ROOT = Path(__file__).resolve().parents[1]
def load_module(name, path):
spec = importlib.util.spec_from_file_location(name, path)
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
return module
def timestamp(value):
if not isinstance(value, str):
raise ValueError('invalid timestamp')
result = datetime.fromisoformat(value.replace('Z', '+00:00'))
if result.tzinfo is None:
raise ValueError('timezone required')
return result.astimezone(timezone.utc)
def digest(path):
return hashlib.sha256(path.read_bytes()).hexdigest()
def admission(root=ROOT, package=None):
package = package or root.parent / 'rapp-postgres'
local = load_module('apps_capacity', root / 'tools/verify_apps_pg_capacity.py')
native = load_module('postgres_consumers', package / 'scripts/render_consumers.py')
manifests = [root / 'helm' / (name + '-cluster.yaml') for name in local.REVIEWED_CELLS]
local.verify(manifests)
paths = sorted((package / 'consumers').glob('*.yaml'))
if not paths:
raise ValueError('package consumers missing')
consumers = [native.load(path) for path in paths]
native.validate_cluster_capacity(consumers)
registry = json.loads((root / 'assurance/placement-owners.json').read_text())
cells = {}
source_files = manifests + paths + [root / 'tools/verify_apps_pg_capacity.py',
root / 'assurance/placement-owners.json', package / 'scripts/render_consumers.py']
for manifest in manifests:
doc = yaml.safe_load(manifest.read_text())
name, spec = doc['metadata']['name'], doc['spec']
roles = spec.get('managed', {}).get('roles', [])
names = sorted(r['name'] for r in roles)
cells[name] = {'consumers': names, 'ceiling': local.MAX_CONSUMERS,
'retention_days': int(spec['backup']['retentionPolicy'].removesuffix('d')),
'instances': spec['instances'], 'memory_limit': spec['resources']['limits']['memory'],
'max_connections': int(spec['postgresql']['parameters']['max_connections']),
'service_classes': {n: registry[n]['service_class'] for n in names},
'connection_limits': {r['name']: r['connectionLimit'] for r in roles}}
if cells[name]['retention_days'] != 30:
raise ValueError('apps-pg retention differs from disclosed 30-day contract')
for name in sorted(native.ALLOWED_CLUSTERS):
manifest = package / 'helm' / (name + '-cluster.yaml')
spec = yaml.safe_load(manifest.read_text())['spec']
source_files.append(manifest)
selected = [d for d in consumers if native.cluster_name(d) == name]
horizon = native.erasure_horizon(selected)
retention = int(spec['backup']['retentionPolicy'].removesuffix('d'))
if retention != horizon['instanceRetentionDays']:
raise ValueError('package retention differs from effective consumer horizon')
if any(not d['honoured'] for d in horizon['consumers'].values()):
raise ValueError('consumer retention request cannot be honoured on shared cell')
cells[name] = {'consumers': sorted(horizon['consumers']),
'ceiling': native.MAX_CONSUMERS_PER_CLUSTER, 'retention_days': retention,
'instances': spec['instances'], 'memory_limit': spec['resources']['limits']['memory'],
'max_connections': int(spec['postgresql']['parameters']['max_connections']),
'service_classes': horizon['serviceClasses']}
names = {n for c in cells.values() for n in c['consumers']}
if names != set(registry) or any(not r.get('owner') or not r.get('evidence') for r in registry.values()):
raise ValueError('missing or stale placement owner')
for name, cell in cells.items():
for consumer in cell['consumers']:
if registry[consumer]['cell'] != name:
raise ValueError('placement owner cell mismatch')
cell['owners'] = {n: registry[n]['owner'] for n in cell['consumers']}
return {'schema': 'railiance-platform.admission.v1', 'basis': 'source-declarations',
'cells': cells, 'sources': {str(p.relative_to(root.parent)): digest(p) for p in source_files}}
def evaluate(contract, observation, now):
"""Classify a value-safe observation, not certify its underlying truth."""
if set(observation) != {'schema', 'cluster_uid', 'captured_at', 'signals'}:
raise ValueError('invalid observation envelope')
if observation['schema'] != 'railiance-platform.observation.v1':
raise ValueError('unsupported observation version')
if observation['cluster_uid'] != contract['cluster_uid']:
raise ValueError('wrong cluster')
capture = timestamp(observation['captured_at'])
capture_age = (now - capture).total_seconds()
if capture_age < 0:
raise ValueError('future capture')
specs = contract['signals']
if not isinstance(observation['signals'], dict) or set(observation['signals']) - set(specs):
raise ValueError('unknown signals')
results = {}
for name, spec in specs.items():
sample = observation['signals'].get(name)
state = 'missing'
if sample is not None:
if not isinstance(sample, dict) or set(sample) != {'observed_at', 'result'}:
raise ValueError('invalid signal fields')
if sample['result'] not in ('pass', 'fail', 'unavailable'):
raise ValueError('invalid result')
observed = timestamp(sample['observed_at'])
age = (now - observed).total_seconds()
if observed > capture:
raise ValueError('signal newer than capture')
if sample['result'] == 'unavailable':
state = 'unavailable'
elif sample['result'] == 'fail':
state = 'failed'
elif capture_age > contract['capture_max_age_seconds']:
state = 'stale'
elif age > spec['max_age_seconds']:
state = 'stale'
else:
state = 'healthy'
results[name] = {'state': state, 'owner': spec['owner']}
return {'schema': 'railiance-platform.assurance-signal.v1',
'cluster_uid': contract['cluster_uid'], 'evaluated_at': now.isoformat(),
'signals': results, 'transport': 'unmonitored',
'guarantees': 'unsupported', 'threshold_status': 'local-diagnostic-only',
'healthy': all(r['state'] == 'healthy' for r in results.values())}
def read_contract(path):
doc = json.loads(path.read_text())
if doc['schema'] != 'railiance-platform.assurance-contract.v1' or not doc['signals']:
raise ValueError('invalid contract')
for budget in [doc['capture_max_age_seconds'], *(s['max_age_seconds'] for s in doc['signals'].values())]:
if type(budget) not in (int, float) or not math.isfinite(budget) or budget <= 0:
raise ValueError('invalid freshness budget')
if any(not s.get('owner') for s in doc['signals'].values()):
raise ValueError('signal owner missing')
return doc
def main(argv=None):
parser = argparse.ArgumentParser(description=__doc__)
sub = parser.add_subparsers(dest='command', required=True)
a = sub.add_parser('admission')
a.add_argument('--package', type=Path, default=ROOT.parent / 'rapp-postgres')
a.add_argument('--check', type=Path)
e = sub.add_parser('evaluate')
e.add_argument('observation', type=Path)
e.add_argument('--contract', type=Path, default=ROOT / 'assurance/service-contract.json')
e.add_argument('--now', help='Explicit UTC time for deterministic replay')
args = parser.parse_args(argv)
try:
if args.command == 'admission':
report = admission(package=args.package)
if args.check and report != json.loads(args.check.read_text()):
raise ValueError('admission source or disclosure drift; review before refreshing')
code = 0
else:
now = timestamp(args.now) if args.now else datetime.now(timezone.utc)
report = evaluate(read_contract(args.contract), json.loads(args.observation.read_text()), now)
code = 0 if report['healthy'] else 1
except (OSError, ValueError, KeyError, TypeError, AttributeError, yaml.YAMLError):
# Do not print parser errors, raw input, values, source paths or subprocess stderr.
print(json.dumps({'schema': 'railiance-platform.assurance-error.v1', 'error': 'invalid-or-unavailable-input'}))
return 2
print(json.dumps(report, indent=2, allow_nan=False))
return code
if __name__ == '__main__':
sys.exit(main())