Assistant: codex Assistant-Model: gpt-6.1-sol Assistant-Session: 01a11538-05ea-7b63-b3c7-a2df5f38898d
233 lines
11 KiB
Python
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)
|