forked from wangbo/easyai
fix(deploy): 通过更新脚本恢复受控仓库镜像定义
This commit is contained in:
@@ -0,0 +1,95 @@
|
||||
#!/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)}))
|
||||
Reference in New Issue
Block a user