#!/usr/bin/env python3 """Render a validated S3 report as Prometheus text; no listener or delivery.""" import argparse from datetime import datetime, timezone import json from pathlib import Path import sys from platform_event import translate from receiver import STATES, contract_check, instant, read_json def labels(values): def escape(value): return value.replace('\\', '\\\\').replace('\n', '\\n').replace('"', '\\"') return '{' + ','.join(f'{key}="{escape(value)}"' for key, value in values.items()) + '}' def render(report, contract, now): contract_check(contract) event = translate(report, contract) observed = instant(event['observed_at']).timestamp() if not 0 <= now.timestamp() - observed <= contract['max_event_age_seconds']: raise ValueError('stale or future report') identity = dict(stream=contract['stream'], producer=contract['producer']) lines = ['# HELP railiance_assurance_state Producer classification, one hot per signal.', '# TYPE railiance_assurance_state gauge'] for signal, state in sorted(event['states'].items()): for candidate in sorted(STATES): key = labels(dict(identity, signal=signal, state=candidate)) lines.append(f'railiance_assurance_state{key} {int(state == candidate)}') lines.extend([ '# HELP railiance_assurance_observed_timestamp_seconds Original producer evaluation time.', '# TYPE railiance_assurance_observed_timestamp_seconds gauge', f'railiance_assurance_observed_timestamp_seconds{labels(identity)} {observed}', ]) return '\n'.join(lines) + '\n' def main(): parser = argparse.ArgumentParser(description=__doc__) parser.add_argument('--contract', required=True, type=Path) parser.add_argument('report', type=Path) args = parser.parse_args() try: output = render(read_json(args.report), read_json(args.contract), datetime.now(timezone.utc)) except (OSError, ValueError, KeyError, TypeError, AttributeError): print(json.dumps({'status': 'rejected', 'error': 'invalid-or-expired-report'}), file=sys.stderr) return 2 sys.stdout.write(output) return 0 if __name__ == '__main__': sys.exit(main())