email-connect/deploy/k8s/railiance/deploy-verification-2026-10-07.py
tegwick bc303639f4
Some checks are pending
CI Smoke / host-smoke (push) Waiting to run
CI Smoke / container-smoke (push) Waiting to run
Record approved retry release, private recovery proof and completed workplan
Assistant: codex
Assistant-Model: gpt-6.1-sol
Assistant-Session: 01a11538-05ea-7b63-b3c7-a2df5f38898d
2026-10-07 22:42:45 +02:00

233 lines
11 KiB
Python

#!/usr/bin/env python3
"""Exact founder-approved email-connect verification release, 2026-10-07.
Default is server dry-run. Never prints SQLite bytes, credentials or child errors.
"""
import argparse
import copy
import hashlib
import json
import os
import signal
import sqlite3
import subprocess
import sys
import time
from pathlib import Path
NS = 'email-connect'
UID = 'a553c742-0115-43d4-99a4-a5ca56fe0786'
OLD = 'forgejo.coulomb.social/coulomb/email-connect@sha256:e095d7e51c68a29ff6b82d735c734d5d27e0041b937dc958ecee5bcb5ddf5e8b'
NEW = 'forgejo.coulomb.social/coulomb/email-connect@sha256:1e613d43734c44c5cd32c6083fda8b4da5d01ef43d10c6ca9c29a33c788cf75c'
DECISION = '8da41538-8482-4c72-b83d-e0fce1b662f0'
class Refused(RuntimeError):
pass
def k(*args, body=None, timeout=180):
result = subprocess.run(['kubectl', *args], input=body, capture_output=True, timeout=timeout)
if result.returncode:
raise Refused('command_failed')
return result.stdout
def document(*args):
return json.loads(k(*args, '-o', 'json'))
def image(dep):
return dep['spec']['template']['spec']['containers'][0]['image']
def private_write(path, data):
fd = os.open(path, os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o600)
with os.fdopen(fd, 'wb') as stream:
stream.write(data)
stream.flush()
os.fsync(stream.fileno())
def backup_pod(name):
return {'apiVersion': 'v1', 'kind': 'Pod',
'metadata': {'name': name, 'namespace': NS, 'labels': {'email-connect-recovery': name}},
'spec': {'restartPolicy': 'Never', 'automountServiceAccountToken': False,
'securityContext': {'runAsNonRoot': True, 'runAsUser': 10001,
'runAsGroup': 10001, 'fsGroup': 10001, 'seccompProfile': {'type': 'RuntimeDefault'}},
'containers': [{'name': 'backup', 'image': NEW,
'command': ['python', '-c', 'import time;time.sleep(600)'],
'resources': {'requests': {'cpu': '10m', 'memory': '32Mi'},
'limits': {'cpu': '100m', 'memory': '128Mi'}},
'securityContext': {'allowPrivilegeEscalation': False,
'readOnlyRootFilesystem': True, 'capabilities': {'drop': ['ALL']}},
'volumeMounts': [{'name': 'data', 'mountPath': '/data', 'readOnly': True},
{'name': 'tmp', 'mountPath': '/tmp'}]}],
'volumes': [{'name': 'data', 'persistentVolumeClaim':
{'claimName': 'email-connect-data', 'readOnly': True}},
{'name': 'tmp', 'emptyDir': {}}]}}
def patch():
return json.dumps([
{'op': 'test', 'path': '/spec/template/spec/containers/0/image', 'value': OLD},
{'op': 'replace', 'path': '/spec/template/spec/containers/0/image', 'value': NEW},
])
def capture(name, destination):
code = '''import os,sqlite3,sys
src=sqlite3.connect('file:/data/email-connect.db?mode=ro',uri=True)
dst=sqlite3.connect('/tmp/verification-pre-release.db')
src.backup(dst)
assert dst.execute('PRAGMA integrity_check').fetchone()[0]=='ok'
dst.close();src.close()
os.chmod('/tmp/verification-pre-release.db',0o600)
with open('/tmp/verification-pre-release.db','rb') as stream:sys.stdout.buffer.write(stream.read())
'''
data = k('exec', '-n', NS, name, '--', 'python', '-c', code)
if not data.startswith(b'SQLite format 3\x00'):
raise Refused('invalid_sqlite_backup')
path = destination / 'database.sqlite3'
private_write(path, data)
with sqlite3.connect('file:' + str(path) + '?mode=ro', uri=True) as database:
if database.execute('PRAGMA integrity_check').fetchone()[0] != 'ok':
raise Refused('invalid_sqlite_backup')
return {'database_sha256': hashlib.sha256(data).hexdigest(),
'database_bytes': len(data), 'sqlite_integrity': 'ok',
'source_pvc_read_only': True, 'writers_quiesced': True}
def verify():
code = '''import json,os,sqlite3,urllib.request,urllib.error
base='http://127.0.0.1:8080'
results={}
for path in ['/healthz','/readyz']:
with urllib.request.urlopen(base+path,timeout=5) as response:results[path]=response.status
for label,path,payload,token,key,expected,reason in [
('unauthenticated','/v1/send',{},'synthetic-invalid',None,401,'unauthorized'),
('template_denial','/v1/send',{'id':'verification-release-denial','source':'user-engine','type':'arbitrary.send'},os.environ['EMAIL_CONNECT_INGEST_TOKEN'].strip(),'verification-release-denial',400,'template_not_allowed'),
('invalid_verification','/v1/registration-verifications',{},os.environ['EMAIL_CONNECT_INGEST_TOKEN'].strip(),None,400,'invalid_verification_request')]:
headers={'Content-Type':'application/json','Authorization':'Bearer '+token}
if key:headers['Idempotency-Key']=key
request=urllib.request.Request(base+path,data=json.dumps(payload).encode(),headers=headers,method='POST')
try:urllib.request.urlopen(request,timeout=5);raise RuntimeError('expected_denial')
except urllib.error.HTTPError as error:
body=json.load(error)
assert error.code==expected and body.get('error')==reason
results[label]=error.code
with sqlite3.connect('file:/data/email-connect.db?mode=ro',uri=True) as database:
assert database.execute('PRAGMA integrity_check').fetchone()[0]=='ok'
assert database.execute("SELECT 1 FROM sqlite_master WHERE type='table' AND name='verification_attempts'").fetchone()
results.update(sqlite_integrity='ok',reservation_schema_present=True,mail_sent=False)
print(json.dumps(results))
'''
return json.loads(k('exec', '-n', NS, 'deployment/email-connect', '--', 'python', '-c', code))
def deploy(before, body, destination):
destination.mkdir(mode=0o700)
receipt = {'status': 'incomplete', 'decision': DECISION, 'old_image': OLD,
'new_image': NEW, 'started_at': time.strftime('%Y-%m-%dT%H:%M:%SZ', time.gmtime())}
started = time.monotonic()
paused = False
helper = False
succeeded = False
name = json.loads(body)['metadata']['name']
try:
paused = True
receipt['phase'] = 'pause'
k('scale', 'deployment/email-connect', '-n', NS, '--replicas=0')
k('wait', '--for=delete', 'pod', '-n', NS,
'-l', 'app.kubernetes.io/name=email-connect', '--timeout=120s')
if document('get', 'deployment', 'email-connect', '-n', NS)['spec']['replicas'] != 0:
raise Refused('writers_not_quiesced')
helper = True
k('create', '-f', '-', body=body)
k('wait', '--for=condition=Ready', 'pod/' + name, '-n', NS, '--timeout=180s', timeout=240)
receipt['phase'] = 'capture'
receipt.update(capture(name, destination))
k('delete', 'pod', name, '-n', NS, '--wait=true', '--timeout=120s')
helper = False
receipt['phase'] = 'image_patch'
k('patch', 'deployment', 'email-connect', '-n', NS, '--type=json', '-p', patch())
k('scale', 'deployment/email-connect', '-n', NS, '--replicas=1')
k('rollout', 'status', 'deployment/email-connect', '-n', NS, '--timeout=180s', timeout=240)
actual = document('get', 'deployment', 'email-connect', '-n', NS)
expected = copy.deepcopy(before['spec'])
expected['template']['spec']['containers'][0]['image'] = NEW
if actual['spec'] != expected or actual['status'].get('readyReplicas') != 1:
raise Refused('unexpected_release_spec')
receipt['phase'] = 'verification'
receipt['checks'] = verify()
receipt.update(status='released_verified', image_only_spec_change=True, ready_replicas=1)
succeeded = True
except BaseException as exc:
receipt.update(status='failed', failure_type=type(exc).__name__)
raise
finally:
try:
if helper:
k('delete', 'pod', name, '-n', NS, '--ignore-not-found=true', '--wait=true', '--timeout=120s')
except Exception:
receipt['helper_cleanup_failed'] = True
if paused and not succeeded:
try:
current = image(document('get', 'deployment', 'email-connect', '-n', NS))
replicas = '1' if current == OLD else '0'
k('scale', 'deployment/email-connect', '-n', NS, '--replicas=' + replicas)
receipt['failure_replicas'] = int(replicas)
if replicas == '1':
k('rollout', 'status', 'deployment/email-connect', '-n', NS, '--timeout=180s', timeout=240)
receipt['old_receiver_resumed'] = True
except Exception:
receipt['receiver_state_requires_check'] = True
receipt['elapsed_seconds'] = round(time.monotonic() - started, 2)
private_write(destination / 'release-receipt.json', json.dumps(receipt, indent=2).encode())
print(json.dumps(receipt))
return receipt
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--execute', action='store_true')
parser.add_argument('--destination', type=Path, required=True)
args = parser.parse_args()
os.environ['KUBECONFIG'] = '/etc/rancher/k3s/k3s.yaml'
if document('get', 'namespace', 'kube-system')['metadata']['uid'] != UID:
raise Refused('wrong_cluster')
before = document('get', 'deployment', 'email-connect', '-n', NS)
if image(before) != OLD or before['spec']['replicas'] != 1 or before['status'].get('readyReplicas') != 1:
raise Refused('unexpected_release')
if document('get', 'pvc', 'email-connect-data', '-n', NS)['status']['phase'] != 'Bound':
raise Refused('pvc_not_bound')
config = document('get', 'configmap', 'email-connect-config', '-n', NS)['data']
if config.get('EMAIL_CONNECT_DATABASE_PATH') != '/data/email-connect.db':
raise Refused('unexpected_database_path')
name = 'email-connect-verification-backup-' + time.strftime('%Y%m%d%H%M%S', time.gmtime())
body = json.dumps(backup_pod(name)).encode()
k('create', '--dry-run=server', '-f', '-', body=body)
projected = document('patch', 'deployment', 'email-connect', '-n', NS,
'--type=json', '-p', patch(), '--dry-run=server')
expected = copy.deepcopy(before['spec'])
expected['template']['spec']['containers'][0]['image'] = NEW
if projected['spec'] != expected:
raise Refused('unexpected_dry_run_spec')
if not args.execute:
print(json.dumps({'status': 'reviewed', 'live_changes': False,
'image_only_spec_diff': True, 'readonly_backup_helper': True}))
return
if not args.destination.is_absolute() or args.destination.exists():
raise Refused('destination_must_be_new')
def terminate(signum, frame):
raise Refused('release_interrupted')
signal.signal(signal.SIGTERM, terminate)
deploy(before, body, args.destination)
if __name__ == '__main__':
try:
main()
except Exception as exc:
print(json.dumps({'status': 'failed', 'failure_type': type(exc).__name__}))
sys.exit(1)