66 lines
3.3 KiB
Python
66 lines
3.3 KiB
Python
"""Boot the rendered image/configuration via Docker, including remote Docker sockets."""
|
|
import secrets
|
|
import subprocess
|
|
import sys
|
|
import tempfile
|
|
import time
|
|
from pathlib import Path
|
|
import yaml
|
|
|
|
docs = list(yaml.safe_load_all(Path(sys.argv[1]).read_text()))
|
|
d = next(d for d in docs if d['kind'] == 'Deployment')
|
|
c = next(c for c in d['spec']['template']['spec']['containers'] if c['name'] == 'factorio')
|
|
cms = {d['metadata']['name']: d['data'] for d in docs if d['kind'] == 'ConfigMap'}
|
|
run = lambda *a, **kw: subprocess.run(a, check=True, **kw)
|
|
volume = 'factorio-smoke-' + secrets.token_hex(6)
|
|
run('docker', 'volume', 'create', volume, stdout=subprocess.DEVNULL)
|
|
container = None
|
|
try:
|
|
with tempfile.TemporaryDirectory() as directory:
|
|
root = Path(directory)
|
|
for name in ['scripts', 'settings', 'credentials']:
|
|
(root / name).mkdir()
|
|
for folder, name in [('scripts', 'factorio-scripts'), ('settings', 'factorio-settings')]:
|
|
for key, value in cms[name].items():
|
|
file = root / folder / key
|
|
file.write_text(value)
|
|
file.chmod(0o755 if key.endswith('.sh') else 0o644)
|
|
(root / 'credentials/rcon-password').write_text(secrets.token_urlsafe(32))
|
|
args = ['docker', 'create']
|
|
for env in c['env']:
|
|
args += ['--env', env['name'] + '=' + env['value']]
|
|
data_mount = next(m['mountPath'] for m in c['volumeMounts'] if m['name'] == 'data')
|
|
args += ['--volume', f'{volume}:{data_mount}']
|
|
args += ['--entrypoint', c['command'][0], c['image'], *c['command'][1:]]
|
|
container = subprocess.check_output(args, text=True).strip()
|
|
# docker cp works when the daemon is outside the CI job container;
|
|
# bind-mounting the job container's /tmp would silently mount a different directory.
|
|
for mount in c['volumeMounts']:
|
|
if mount['name'] != 'data':
|
|
run('docker', 'cp', str(root / mount['name']), container + ':' + mount['mountPath'])
|
|
run('docker', 'start', container, stdout=subprocess.DEVNULL)
|
|
|
|
def ready():
|
|
command = c['readinessProbe']['exec']['command']
|
|
for _ in range(90):
|
|
if subprocess.run(['docker', 'exec', container, *command], stdout=subprocess.DEVNULL,
|
|
stderr=subprocess.DEVNULL, timeout=15).returncode == 0:
|
|
return
|
|
time.sleep(2)
|
|
raise RuntimeError('Server did not pass the production readiness probe')
|
|
|
|
ready()
|
|
run('docker', 'exec', container, 'rcon', '/server-save')
|
|
run('docker', 'stop', '--time', str(d['spec']['template']['spec']['terminationGracePeriodSeconds']),
|
|
container, stdout=subprocess.DEVNULL)
|
|
run('docker', 'start', container, stdout=subprocess.DEVNULL)
|
|
ready()
|
|
run('docker', 'exec', container, 'sh', '-c',
|
|
'test -n "$(find "$1/saves" -name "*.zip")" && test -n "$(find "$1/upgrade-backups" -name "*.zip")"',
|
|
'sh', data_mount)
|
|
print('Actual image passed RCON readiness, persistent save, restart, and upgrade-backup checks')
|
|
finally:
|
|
if container:
|
|
run('docker', 'rm', '--force', container, stdout=subprocess.DEVNULL)
|
|
run('docker', 'volume', 'rm', volume, stdout=subprocess.DEVNULL)
|