forked from wangbo/easyai
96 lines
5.0 KiB
Python
96 lines
5.0 KiB
Python
#!/usr/bin/env python3
|
|
"""Exercise reviewed image-pin adoption with real Git and Compose parsing only."""
|
|
import json
|
|
import os
|
|
from pathlib import Path
|
|
import subprocess
|
|
import tempfile
|
|
import time
|
|
|
|
source = Path(__file__).resolve().parent.parent
|
|
prepare = Path(os.environ.get('EASYAI_PREPARE_UPDATE_SCRIPT', source / 'scripts/prepare-update-repository.py'))
|
|
started = time.monotonic()
|
|
pin = 'repository-web@sha256:' + 'a' * 64
|
|
|
|
|
|
def run(args, root, env=None):
|
|
return subprocess.run(args, cwd=root, env=env, capture_output=True, text=True, timeout=30)
|
|
|
|
|
|
def checked(args, root, env=None):
|
|
result = run(args, root, env)
|
|
assert result.returncode == 0, result.stdout + result.stderr
|
|
return result.stdout
|
|
|
|
|
|
for restore in ('', 'easyai-web', 'redis', 'ws-gateway'):
|
|
with tempfile.TemporaryDirectory(prefix='easyai-image-restore-') as directory:
|
|
base = Path(directory)
|
|
root = base / 'easyai'
|
|
root.mkdir()
|
|
compose = '''services:
|
|
easyai-web:
|
|
image: repository-web:${VERSION}
|
|
environment:
|
|
- EXISTING=keep
|
|
easyai-server:
|
|
image: repository-server:${VERSION}
|
|
environment:
|
|
- LOCAL_SETTING=original
|
|
redis:
|
|
image: redis:7
|
|
'''
|
|
(root / 'docker-compose.yml').write_text(compose)
|
|
(root / '.gitignore').write_text('.env\ndata/\n')
|
|
env_original = b'VERSION=latest\nCONFIG_JWT_SECRET=test-private-value\n'
|
|
(root / '.env').write_bytes(env_original)
|
|
for args in (['git', 'init', '-q', '-b', 'main'], ['git', 'config', 'user.name', 'Update test'], ['git', 'config', 'user.email', 'test@example.invalid'], ['git', 'add', '.'], ['git', 'commit', '-qm', 'test: 旧部署基准']):
|
|
checked(args, root)
|
|
remote = base / 'remote.git'
|
|
seed = base / 'seed'
|
|
checked(['git', 'clone', '-q', '--bare', str(root), str(remote)], base)
|
|
checked(['git', 'remote', 'add', 'origin', str(remote)], root)
|
|
checked(['git', 'fetch', '-q'], root)
|
|
checked(['git', 'branch', '--set-upstream-to', 'origin/main'], root)
|
|
checked(['git', 'clone', '-q', str(remote), str(seed)], base)
|
|
(seed / 'docker-compose.yml').write_text(compose.replace(' - EXISTING=keep', ' - EXISTING=keep\n - NEW_SETTING=from-repository'))
|
|
for args in (['git', 'config', 'user.name', 'Update test'], ['git', 'config', 'user.email', 'test@example.invalid'], ['git', 'add', '.'], ['git', 'commit', '-qm', 'test: 新部署配置'], ['git', 'push', '-q']):
|
|
checked(args, seed)
|
|
original = compose.replace('repository-web:${VERSION}', pin).replace('LOCAL_SETTING=original', 'LOCAL_SETTING=custom').replace('redis:7', 'redis:6.2')
|
|
(root / 'docker-compose.yml').write_text(original)
|
|
env = {**os.environ, 'UPDATE_RESTORE_REPOSITORY_IMAGES': restore, 'UPDATE_SERVICES': 'easyai-server easyai-web'}
|
|
result = run(['python3', str(prepare), str(root)], root, env)
|
|
if restore in ('redis', 'ws-gateway'):
|
|
assert result.returncode != 0, result.stdout
|
|
assert (root / 'docker-compose.yml').read_text() == original
|
|
assert (root / '.env').read_bytes() == env_original
|
|
assert not (root / 'data').exists()
|
|
continue
|
|
assert result.returncode == 0, result.stdout + result.stderr
|
|
checked(['git', 'pull', '--ff-only'], root)
|
|
config = json.loads(checked(['docker', 'compose', '--env-file', '.env', 'config', '--format', 'json'], root))
|
|
services = config['services']
|
|
assert services['easyai-web']['image'] == ('repository-web:latest' if restore else pin), services['easyai-web']['image']
|
|
assert services['easyai-web']['environment']['NEW_SETTING'] == 'from-repository'
|
|
assert services['easyai-server']['environment']['LOCAL_SETTING'] == 'custom'
|
|
assert services['redis']['image'] == 'redis:6.2'
|
|
assert 'CONFIG_JWT_SECRET=test-private-value' in (root / '.env').read_text()
|
|
backups = list((root / 'data/deployment-backups').glob('legacy-update-*'))
|
|
assert len(backups) == 1
|
|
assert (backups[0] / 'docker-compose.yml').read_text() == original
|
|
assert (backups[0] / '.env').read_bytes() == env_original
|
|
assert (backups[0] / '.env').stat().st_mode & 0o077 == 0
|
|
metadata = json.loads((backups[0] / 'migration.json').read_text())
|
|
if restore:
|
|
assert metadata['restoredRepositoryImages'] == ['easyai-web']
|
|
# A second adoption preserves the result and creates no extra backup.
|
|
checked(['python3', str(prepare), str(root)], root, env)
|
|
assert len(list((root / 'data/deployment-backups').glob('legacy-update-*'))) == 1
|
|
|
|
for mode, adopt in (('image', '1'), ('full', '0')):
|
|
env = {**os.environ, 'UPDATE_MODE': mode, 'UPDATE_ADOPT_LEGACY_CONFIG': adopt, 'UPDATE_RESTORE_REPOSITORY_IMAGES': 'easyai-web'}
|
|
result = run(['bash', str(source / 'update.sh')], source, env)
|
|
assert result.returncode != 0 and '尚未修改配置或容器' in result.stderr
|
|
|
|
print(json.dumps({'passed': True, 'cases': 6, 'durationSeconds': round(time.monotonic() - started, 3)}))
|