Assistant: codex Assistant-Model: gpt-6-astra Assistant-Session: 01a06ecb-456a-71c2-b41e-0755d336e883
85 lines
4.2 KiB
Python
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())
|