Files
anonymous_github/backend/scripts/test-deployment.py
T

97 lines
5.0 KiB
Python

#!/usr/bin/env python3
"""Smoke-test the root Go Compose model with disposable data and random ports.
Build first: docker build -t anonymous-github-go:deployment-test .
Run: python3 backend/scripts/test-deployment.py [image]
"""
import base64
import json
import os
from pathlib import Path
import secrets
import subprocess
import sys
import tempfile
import urllib.request
ROOT = Path(__file__).resolve().parents[2]
IMAGE = sys.argv[1] if len(sys.argv) > 1 else 'anonymous-github-go:deployment-test'
PROJECT = 'go-deployment-test-' + secrets.token_hex(6)
ENV = {key: os.environ[key] for key in ('PATH', 'HOME', 'DOCKER_HOST', 'DOCKER_CONTEXT', 'DOCKER_CONFIG') if key in os.environ}
ENV.update(DB_USERNAME='smoke', DB_PASSWORD=secrets.token_hex(20), EXPOSED_PORT='5000')
def run(*args, capture=True):
return subprocess.run(args, cwd=ROOT, env=ENV, check=True, text=True,
stdout=subprocess.PIPE if capture else None).stdout
with tempfile.TemporaryDirectory(prefix=PROJECT) as directory:
envfile = Path(directory) / 'app.env'
envfile.write_text('\n'.join([
'SESSION_SECRET=' + secrets.token_hex(32),
'CREDENTIAL_KEYS=' + json.dumps({'smoke': base64.b64encode(secrets.token_bytes(32)).decode()}),
'CREDENTIAL_ACTIVE_KEY_ID=smoke', 'GITHUB_OAUTH_ENABLED=false',
'GITHUB_APP_ENABLED=false', 'COOKIE_SECURE=false',
'SHARED_CACHE_ENABLED=true', 'SHARED_CACHE_FREE_BYTES=0',
'DB_USERNAME=' + ENV['DB_USERNAME'], 'DB_PASSWORD=' + ENV['DB_PASSWORD'],
]) + '\n')
envfile.chmod(0o600)
ENV['APP_ENV_FILE'] = str(envfile)
base = ['docker', 'compose', '--env-file', '/dev/null']
model = json.loads(run(*base, '-f', 'docker-compose.yml', 'config', '--format', 'json'))
assert set(model['services']) == {'anonymous_github', 'mongodb', 'redis', 'mongodb-backup'}
for files, expected in [(['docker-compose.yml', 'docker-compose.github-app.yml'], 4),
(['docker-compose.node.yml', 'docker-compose.node.github-app.yml'], 5)]:
flags = [arg for file in files for arg in ('-f', file)]
other = json.loads(run(*base, *flags, 'config', '--no-env-resolution', '--format', 'json'))
assert len(other['services']) == expected
app = model['services']['anonymous_github']
assert app['read_only'] and app['init']
assert app['healthcheck']['test'] == ['CMD', '/usr/local/bin/anonymous-server', 'healthcheck']
assert app['pids_limit'] == 128 and app['cap_drop'] == ['ALL']
assert 'streamer' not in app['depends_on']
# Replace all persisted data and host bindings before starting any container.
del model['services']['mongodb-backup']
app.pop('build', None)
app['image'] = IMAGE
app['ports'] = [{'target': 5000, 'published': '0', 'host_ip': '127.0.0.1', 'protocol': 'tcp'}]
app['volumes'] = [{'type': 'volume', 'source': 'test_repositories', 'target': '/app/repositories'}]
mongo = model['services']['mongodb']
mongo['image'] = 'mongo:7'
mongo.pop('ports', None)
mongo['volumes'] = [{'type': 'volume', 'source': 'test_mongo', 'target': '/data/db'}]
model['services']['redis'].pop('ports', None)
model['volumes'] = {'test_repositories': {}, 'test_mongo': {}}
model['networks'] = {'default': {}}
model['name'] = PROJECT
for service in model['services'].values():
service['restart'] = 'no'
assert all(v['type'] == 'volume' for v in service.get('volumes', []))
config = Path(directory) / 'compose.json'
config.write_text(json.dumps(model))
config.chmod(0o600)
compose = base + ['-p', PROJECT, '-f', str(config)]
try:
run(*compose, 'up', '-d', '--no-build', '--wait', '--wait-timeout', '120', capture=False)
cid = run(*compose, 'ps', '-q', 'anonymous_github').strip()
info = json.loads(run('docker', 'inspect', cid))[0]
assert info['Config']['User'] == '10001:10001'
assert info['HostConfig']['ReadonlyRootfs']
run(*compose, 'exec', '-T', 'anonymous_github', 'sh', '-ec',
'test "$(id -u)" = 10001; touch /tmp/smoke /app/repositories/smoke; '
'test ! -w /app/public; test ! -e /usr/local/bin/node; '
'rm /tmp/smoke /app/repositories/smoke')
address = run(*compose, 'port', 'anonymous_github', '5000').strip()
for path in ('/healthcheck', '/', '/llms.txt', '/maintenance-openapi.json'):
with urllib.request.urlopen('http://' + address + path, timeout=10) as response:
assert response.status == 200
assert response.read(), path
run(*compose, 'stop', 'anonymous_github', capture=False)
state = json.loads(run('docker', 'inspect', cid))[0]['State']
assert state['ExitCode'] == 0 and not state['OOMKilled'], state
print('PASS: Go default, Node rollback models, isolated startup, non-root writes, static routes, graceful shutdown')
finally:
# Only this randomly named test project owns these disposable volumes.
run(*compose, 'down', '--volumes', '--timeout', '45', capture=False)