#!/usr/bin/env python3 """Run the Docker container demo of nprintML""" import argparse import os import pathlib import platform import re import subprocess DEBUG = os.getenv('NPRINTML_DOCKER_DEBUG', '0') == '1' CONTAINER_REMOVE = os.getenv('NPRINTML_DOCKER_REMOVE', '1') == '1' CONTAINER_REPO = os.getenv('NPRINTML_DOCKER_REPOSITORY', 'ghcr.io/nprint/nprintml') CONTAINER_VERSION = os.getenv('NPRINTML_DOCKER_VERSION', 'latest') CHOWN_OUTPUT = os.getenv('NPRINTML_DOCKER_CHOWN', '1') == '1' MOUNT_PATH = '/mnt/nprintml-docker/' RUN_PATH = '/run/nprintml-docker/' COMMAND_BASE = ( 'docker', 'run', '--workdir', RUN_PATH, ) if CONTAINER_REMOVE: COMMAND_BASE += ( '--rm', ) CURRENT_UID = os.getuid() CURRENT_GID = os.getgid() SUDO_UID = os.getenv('SUDO_UID') SUDO_GID = os.getenv('SUDO_GID') def map_path(command, args, flag, path): abspath = os.path.abspath(path) mount_path = os.path.join(MOUNT_PATH, abspath.lstrip(os.path.sep)) command1 = command + [ '--volume', f'{abspath}:{mount_path}', ] if flag is None: args1 = args + [ mount_path, ] else: args1 = args + [ flag, mount_path, ] return (command1, args1) def flag_match(arg): return arg in flag_match.words or flag_match.pattern.fullmatch(arg) flag_match.words = {'learn', '-C', '-L', '-I', '-N', '-P'} flag_match.pattern = re.compile(r'-.*\b((file)|(dir))\b.*') def map_paths(command, args): command1 = command args1 = [] for (index, arg1) in enumerate(args): if index > 0 and flag_match(args[index - 1]) and os.path.exists(arg1): (command1, args1) = map_path(command1, args1, None, arg1) else: args1.append(arg1) return (command1, args1) def log(*args, start='\n'): print(f'{start}[nprintml-docker]', *args) def main(args, outdir=None): command = list(COMMAND_BASE) (command, args) = map_paths(command, args) if outdir: (command, args) = map_path(command, args, '--output', outdir) else: outdir = pathlib.Path.cwd() / 'nprintml' outdir_mount = pathlib.Path(RUN_PATH) / 'nprintml' command.extend([ '--volume', f'{outdir}:{outdir_mount}', ]) command.append(f'{CONTAINER_REPO}:{CONTAINER_VERSION}') command.extend(args) if DEBUG: log('command:', *command, start='') command_proc = subprocess.run(command) if CHOWN_OUTPUT and (CURRENT_UID or SUDO_UID) and not command_proc.returncode: # 1. use subprocess to make use of "sudo" # 2. use pipe rather than "chown --from" to support darwin chown_command = ['chown'] if CURRENT_UID: chown_command.insert(0, 'sudo') chown_command.append(f'{CURRENT_UID}:{CURRENT_GID}') else: chown_command.append(f'{SUDO_UID}:{SUDO_GID}') if platform.system() == 'Linux': chown_command.insert(-1, '--recursive') chown_command.insert(-1, '--from') chown_command.insert(-1, '0') chown_command.append(outdir) log('clean-up:', *chown_command) chown_proc = subprocess.run(chown_command) else: chown_index = chown_command.index('chown') chown_command.insert(chown_index, 'xargs') chown_command.insert(chown_index + 1, '-0') find_command = ['find', outdir, '-user', '0', '-print0'] log('clean-up:', *find_command, '|', *chown_command) find_proc = subprocess.Popen(find_command, stdout=subprocess.PIPE) chown_proc = subprocess.run(chown_command, stdin=find_proc.stdout) return (command_proc, chown_proc) return (command_proc,) if __name__ == '__main__': parser = argparse.ArgumentParser( add_help=False, usage="%(prog)s [options]", ) parser.add_argument( '-o', '--output', dest='outdir', type=pathlib.Path, ) (args, remainder) = parser.parse_known_args() processes = main(args=remainder, **vars(args)) raise SystemExit(max(proc.returncode for proc in processes))