"""Fixed original-image clients. No user-provided Docker flags or GPU access.""" import json from pathlib import Path, PurePosixPath import re import subprocess def name(identity): if not re.fullmatch(r'[a-f0-9]{32}', identity): raise ValueError('原环境任务编号不合法') return 'adamwen-native-run-' + identity def mounts(profile): values = profile['docker']['mounts'] for mount in values: for key in ('source', 'target'): value = mount[key] if not value.startswith('/') or ',' in value or '\x00' in value or '..' in PurePosixPath(value).parts: raise ValueError('原镜像挂载路径不合法') if mount['target'] in ('/', '/out', '/tmp'): raise ValueError('原镜像挂载不能覆盖任务输出目录') return values def command(profile, source, arguments, directory, identity): image = profile['docker']['image'] if not re.fullmatch(r'sha256:[a-f0-9]{64}', image): raise ValueError('原镜像必须登记不可变摘要') container = name(identity) if identity != '{run_id}' else 'adamwen-native-run-{run_id}' args = ['/usr/bin/docker', 'run', '--rm', '--init', '--name', container, '--label', 'xas.native-run=' + identity, '--runtime', 'runc', '--network', 'host', '--user', '0:0', '--cap-drop', 'ALL', '--security-opt', 'no-new-privileges', '--read-only', '--memory', '4g', '--cpus', '2', '--pids-limit', '128', '--tmpfs', '/tmp:rw,nosuid,nodev,size=268435456', '--env', 'HOME=/tmp', '--env', 'PYTHONUNBUFFERED=1', '--env', 'PYTHONDONTWRITEBYTECODE=1', '--env', 'OMP_NUM_THREADS=2', '--env', 'OPENBLAS_NUM_THREADS=2', '--env', 'MKL_NUM_THREADS=2', '--env', 'NUMBA_NUM_THREADS=2', '--env', 'NUMBA_CACHE_DIR=/tmp/numba'] entry = None for mount in mounts(profile): args.extend(['--mount', f"type=bind,src={mount['source']},dst={mount['target']},readonly"]) try: relative = PurePosixPath(Path(source).as_posix()).relative_to(PurePosixPath(mount['source'])) entry = str(PurePosixPath(mount['target']) / relative) except ValueError: pass if entry is None: raise ValueError('原入口未包含在只读挂载中') output = str(PurePosixPath(str(directory)) / 'work') args += ['--mount', f'type=bind,src={output},dst=/out', '--workdir', '/out', '--entrypoint', profile['interpreter'], image, '-u', entry, *arguments] return args def checks(profile): result = [] for mount in mounts(profile): path = Path(mount['source']) valid = path.exists() and not any(p.is_symlink() for p in (path, *path.parents)) result.append({'name': '原镜像只读依赖', 'ok': valid, 'message': mount['source']}) try: value = subprocess.run(['/usr/bin/docker', 'image', 'inspect', '--format', '{{.Id}}', profile['docker']['image']], capture_output=True, text=True, timeout=5) valid = value.returncode == 0 and value.stdout.strip() == profile['docker']['image'] except (OSError, subprocess.TimeoutExpired): valid = False result.append({'name': '原依赖镜像', 'ok': valid, 'message': '使用已登记镜像,不拉取或升级环境'}) return result def cleanup(identity): container = name(identity) value = subprocess.run(['/usr/bin/docker', 'inspect', container], capture_output=True, text=True, timeout=8) if value.returncode: if 'no such object:' in value.stderr.lower() or 'no such container:' in value.stderr.lower(): return raise OSError('本次原镜像客户端状态未知') item = json.loads(value.stdout)[0] if item.get('Name') != '/' + container or item.get('Config', {}).get('Labels', {}).get('xas.native-run') != identity: raise ValueError('原镜像客户端归属核验失败') identity_value = item.get('Id', '') if not re.fullmatch(r'[a-f0-9]{64}', identity_value): raise ValueError('原镜像客户端标识不合法') value = subprocess.run(['/usr/bin/docker', 'rm', '-f', identity_value], capture_output=True, timeout=15) if value.returncode: # --rm may delete the verified container between inspect and removal. remaining = subprocess.run(['/usr/bin/docker', 'inspect', identity_value], capture_output=True, text=True, timeout=8) if remaining.returncode and ('no such object:' in remaining.stderr.lower() or 'no such container:' in remaining.stderr.lower()): return raise OSError('本次原镜像客户端收尾未确认')