diff --git a/TODO b/TODO index ba9a0b34377e603f60bf14b81582d7e250b14066..1db4b41c304355430b6d7025142d9c1484374f28 100644 --- a/TODO +++ b/TODO @@ -1,3 +1,9 @@ - colors - status: do not exit on non-zero return code - "shc CONTAINER start" vs. "shc CONTAINER shell" +- exec -i + +Albireo cleanup: +- prerouting rule +- shc@.service +- containers diff --git a/shipcat.py b/shipcat.py index d3c965bbd2c631d2aaecf960bb27e81c5d1cfc29..8ddd883c885c998594267ab549524bda81dc4169 100755 --- a/shipcat.py +++ b/shipcat.py @@ -42,12 +42,12 @@ def load_config() -> GlobalConfig: sys.exit(1) -def setup_container(args: argparse.Namespace, require_root: bool) -> ContainerConfig: +def setup_container(args: argparse.Namespace, require_root: bool, container: str = None) -> ContainerConfig: if os.geteuid() != 0: die('This program must be run setuid root') try: - cc = ContainerConfig.load(config, args.name) + cc = ContainerConfig.load(config, container or args.name) except ConfigError as e: die(str(e)) @@ -277,6 +277,36 @@ def cmd_exec(args: argparse.Namespace) -> None: run_command(cmd) +def cmd_rsync(args: argparse.Namespace) -> None: + rsync = args.arg + if verbose: + print('rsync passed:', rsync, file=sys.stderr) + + if len(rsync) >= 2 and rsync[0] == '-l': + user = rsync.pop(0) + rsync = rsync.pop(0) + else: + user = None + + if not rsync or rsync[0].startswith('-'): + die('Cannot parse rsync arguments') + + machine = rsync.pop(0) + + if not rsync or rsync[0] != 'rsync': + die('Cannot parse rsync arguments') + + cc = setup_container(args, False, container=machine) + + cmd = ['podman', 'exec', '-i'] + if user is not None: + cmd.extend(['--user', user]) + cmd.append(cc.container) + cmd.extend(rsync) + + run_command(cmd) + + def parse_int_list(s: str) -> List[int]: return list(map(int, s.split(','))) @@ -307,6 +337,15 @@ create_parser.add_argument('arg', nargs='+', help='command and its arguments') create_parser.add_argument('--tty', '-t', default=False, action='store_true', help='stdio will be attached to a pseudo-terminal') create_parser.add_argument('--user', '-u', metavar='USER[:GROUP]', help='user/group to run the command under (default: root)') +start_parser = subparsers.add_parser('rsync', help='rsync connection wrapper', description=""" +Connection wrapper for rsync. + +If rsync is invoked with "--rsh='shc rsync --'", it connects to local containers +instead of remote machines. Please note that rsync must be installed inside the +container, too. +""") +start_parser.add_argument('arg', nargs='+', help='arguments passed by rsync') + start_parser = subparsers.add_parser('shell', help='run a shell inside a container', description='Run a shell inside a container') start_parser.add_argument('name', help='name of the container') @@ -328,6 +367,7 @@ actions = { 'enable': cmd_enable, 'exec': cmd_exec, 'init': cmd_init, + 'rsync': cmd_rsync, 'shell': cmd_shell, 'start': cmd_start, 'status': cmd_status, diff --git a/shipcat/config.py b/shipcat/config.py index 94be7ecc5c8225adfe82282ce1b37a2804c5d075..b8a6bdbcfd1f849de3092fe28239b2731532244b 100644 --- a/shipcat/config.py +++ b/shipcat/config.py @@ -64,16 +64,16 @@ class ContainerConfig: with open(path, 'rb') as f: root = tomllib.load(f) except FileNotFoundError: - raise ConfigError(f'Cannot load {path}') + raise ConfigError(f'Cannot load container configuration {path}') except tomllib.TOMLDecodeError as e: - raise ConfigError(f'Cannot parse {path}: {e}') + raise ConfigError(f'Cannot parse container configuration {path}: {e}') cc = ContainerConfig() cc.container = name try: cc.parse(Walker(root)) except WalkerError as e: - raise ConfigError(f'Cannot parse {path}: {e}') + raise ConfigError(f'Cannot parse container configuration {path}: {e}') return cc