railiance-platform/scripts/telemetry_primary_archive.py
codex 805e0e5a2b
All checks were successful
CI Smoke / host-smoke (push) Successful in 1s
CI Smoke / container-smoke (push) Successful in 2s
Verify telemetry essentials recovery from versioned Scaleway archive
Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a06ecb-456a-71c2-b41e-0755d336e883
2026-09-06 23:46:20 +02:00

85 lines
4.2 KiB
Python

#!/usr/bin/env python3
"""Create and re-fetch a bounded encrypted telemetry object; no delete/list operations."""
import argparse
import base64
from datetime import datetime, timezone
import hashlib
import json
import os
from pathlib import Path
import secrets
from state_hub_preflight_lane import assert_cluster, command, data
ENDPOINT = 'https://s3.nl-ams.scw.cloud'
BUCKET = 'railiance-platform-pg-backup'
PREFIX = 'platform-pg/application-archives/telemetry/'
LIMIT = 256 * 1024**2
def digest(path):
with path.open('rb') as stream:
return hashlib.file_digest(stream, 'sha256').hexdigest()
def run(args, receipt):
source = json.loads(args.source_receipt.read_text())
if source.get('status') != 'captured' or not source.get('production_resumed'):
raise ValueError('verified_capture_required')
if not 0 < args.source.stat().st_size <= LIMIT or digest(args.source) != source['ciphertext_sha256']:
raise ValueError('source_mismatch')
with args.source.open('rb') as stream:
if stream.read(22) != b'age-encryption.org/v1\n':
raise ValueError('encrypted_artifact_required')
k = ['kubectl', '--kubeconfig', args.kubeconfig]
assert_cluster(k)
values = data(command(k + ['-n', 'databases', 'get', 'secret', 'platform-pg-backup-s3', '-o', 'json']))['data']
import boto3
from botocore.config import Config
client = boto3.client('s3', endpoint_url=ENDPOINT, region_name='nl-ams', aws_access_key_id=base64.b64decode(values['ACCESS_KEY_ID']).decode(), aws_secret_access_key=base64.b64decode(values['ACCESS_SECRET_KEY']).decode(), config=Config(signature_version='s3v4', connect_timeout=15, read_timeout=60, retries={'max_attempts': 2}, request_checksum_calculation='when_required', response_checksum_validation='when_required', s3={'addressing_style': 'path'}))
key = PREFIX + datetime.now(timezone.utc).strftime('%Y/%m/%d/%H%M%S-') + secrets.token_hex(16) + '.tar.age'
receipt.update(destination='s3://' + BUCKET + '/' + key, ciphertext_sha256=source['ciphertext_sha256'], ciphertext_bytes=args.source.stat().st_size)
with args.source.open('rb') as stream:
uploaded = client.put_object(Bucket=BUCKET, Key=key, Body=stream, ContentLength=args.source.stat().st_size, ContentType='application/octet-stream', Metadata={'sha256': source['ciphertext_sha256'], 'profile': 'telemetry-essentials-v1'})
version = uploaded.get('VersionId')
receipt['version_id'] = version
if not version or version == 'null':
raise ValueError('version_pin_missing')
response = client.get_object(Bucket=BUCKET, Key=key, VersionId=version)
if response['ContentLength'] != args.source.stat().st_size:
raise ValueError('download_length_mismatch')
with response['Body'] as body, args.output.open('xb') as output:
args.output.chmod(0o600)
total = 0
while chunk := body.read(1024 * 1024):
total += len(chunk)
if total > LIMIT:
raise ValueError('download_limit_exceeded')
output.write(chunk)
if digest(args.output) != source['ciphertext_sha256']:
raise ValueError('download_digest_mismatch')
receipt.update(status='fetched_pending_restore', version_pinned=True, download_hash_matches=True)
def main():
p = argparse.ArgumentParser(description=__doc__)
p.add_argument('--kubeconfig', required=True)
for name in ['source', 'source-receipt', 'output', 'receipt']:
p.add_argument('--' + name, type=Path, required=True)
args = p.parse_args()
if args.output.exists():
p.error('output already exists')
fd = os.open(args.receipt, os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o600)
receipt = {'schema': 'platform.telemetry-primary-archive.v1', 'status': 'failed', 'started_at': datetime.now(timezone.utc).isoformat()}
try:
run(args, receipt)
except Exception as error:
receipt['error_type'] = type(error).__name__
finally:
receipt['finished_at'] = datetime.now(timezone.utc).isoformat()
with os.fdopen(fd, 'w') as output:
json.dump(receipt, output, indent=2)
return int(receipt['status'] != 'fetched_pending_restore')
if __name__ == '__main__':
raise SystemExit(main())