From 47620961fe8eb9ec859b33bd0480a698e655af69 Mon Sep 17 00:00:00 2001 From: Jhon Honce Date: Fri, 27 Jul 2018 15:09:07 -0700 Subject: Port to MacOS * Refactor Tunnel to support selecting port for remote sshd * Refactor ssh tunnel to support MacOS version of ssh * Refactor Tunnel.close() to find and kill off zombie siblings * Add psutil dependency * Add logging setup, letting library produce debugging records * Clean up Tunnel API * Fix test_runner.sh to propagate returncode to caller Signed-off-by: Jhon Honce Closes: #1199 Approved by: rhatdan --- contrib/python/pypodman/docs/man1/pypodman.1 | 5 +++ contrib/python/pypodman/pypodman/lib/config.py | 46 +++++++++++++++++++++++--- contrib/python/pypodman/pypodman/main.py | 6 ++-- 3 files changed, 51 insertions(+), 6 deletions(-) (limited to 'contrib/python/pypodman') diff --git a/contrib/python/pypodman/docs/man1/pypodman.1 b/contrib/python/pypodman/docs/man1/pypodman.1 index 50d88f84d..7c4816062 100644 --- a/contrib/python/pypodman/docs/man1/pypodman.1 +++ b/contrib/python/pypodman/docs/man1/pypodman.1 @@ -46,6 +46,11 @@ user. Name of remote host. There is no default, if not given \f[C]pypodman\f[] attempts to connect to \f[C]\-\-remote\-socket\-path\f[] on local host. .PP +\f[B]\[en]port\f[] +.PP +The optional port for \f[C]ssh\f[] to connect tunnel to on remote host. +Default is None and will allow \f[C]ssh\f[] to follow it's default configuration. +.PP \f[B]\[en]remote\-socket\-path\f[] .PP Path on remote host for podman service's \f[C]AF_UNIX\f[] socket. The default is diff --git a/contrib/python/pypodman/pypodman/lib/config.py b/contrib/python/pypodman/pypodman/lib/config.py index 75059fc15..137ffdb34 100644 --- a/contrib/python/pypodman/pypodman/lib/config.py +++ b/contrib/python/pypodman/pypodman/lib/config.py @@ -33,6 +33,28 @@ class HelpFormatter(argparse.RawDescriptionHelpFormatter): super().__init__(*args, **kwargs) +class PortAction(argparse.Action): + """Validate port number given is positive integer.""" + + def __call__(self, parser, namespace, values, option_string=None): + """Validate input.""" + if values > 0: + setattr(namespace, self.dest, values) + return + + msg = 'port numbers must be a positive integer.' + raise argparse.ArgumentError(self, msg) + + +class PathAction(argparse.Action): + """Expand user- and relative-paths.""" + + def __call__(self, parser, namespace, values, option_string=None): + """Resolve full path value.""" + setattr(namespace, self.dest, + os.path.abspath(os.path.expanduser(values))) + + class PodmanArgumentParser(argparse.ArgumentParser): """Default remote podman configuration.""" @@ -64,10 +86,17 @@ class PodmanArgumentParser(argparse.ArgumentParser): ' (default: XDG_RUNTIME_DIR/pypodman')) self.add_argument( '--user', + '-l', default=getpass.getuser(), help='Authenicating user on remote host. (default: %(default)s)') self.add_argument( '--host', help='name of remote host. (default: None)') + self.add_argument( + '--port', + '-p', + type=int, + action=PortAction, + help='port for ssh tunnel to remote host. (default: 22)') self.add_argument( '--remote-socket-path', metavar='PATH', @@ -75,11 +104,14 @@ class PodmanArgumentParser(argparse.ArgumentParser): ' (default: /run/podman/io.projectatomic.podman)')) self.add_argument( '--identity-file', + '-i', metavar='PATH', + action=PathAction, help=('path to ssh identity file. (default: ~user/.ssh/id_dsa)')) self.add_argument( '--config-home', metavar='DIRECTORY', + action=PathAction, help=('home of configuration "pypodman.conf".' ' (default: XDG_CONFIG_HOME/pypodman')) @@ -125,8 +157,8 @@ class PodmanArgumentParser(argparse.ArgumentParser): if dir_ is None: continue with suppress(OSError): - with open(os.path.join(dir_, 'pypodman/pypodman.conf'), - 'r') as stream: + with open(os.path.join(dir_, + 'pypodman/pypodman.conf')) as stream: config.update(pytoml.load(stream)) def reqattr(name, value): @@ -156,6 +188,7 @@ class PodmanArgumentParser(argparse.ArgumentParser): 'user', getattr(args, 'user') or os.environ.get('USER') + or os.environ.get('LOGNAME') or config['default'].get('user') or getpass.getuser() ) # yapf:disable @@ -195,8 +228,13 @@ class PodmanArgumentParser(argparse.ArgumentParser): args.local_socket_path = args.remote_socket_path args.local_uri = "unix:{}".format(args.local_socket_path) - args.remote_uri = "ssh://{}@{}{}".format(args.user, args.host, - args.remote_socket_path) + + components = ['ssh://', args.user, '@', args.host] + if args.port: + components.extend((':', str(args.port))) + components.append(args.remote_socket_path) + + args.remote_uri = ''.join(components) return args def exit(self, status=0, message=None): diff --git a/contrib/python/pypodman/pypodman/main.py b/contrib/python/pypodman/pypodman/main.py index e7055d611..5e0ef0750 100755 --- a/contrib/python/pypodman/pypodman/main.py +++ b/contrib/python/pypodman/pypodman/main.py @@ -2,6 +2,7 @@ import logging import os import sys +from subprocess import CalledProcessError from pypodman.lib import PodmanArgumentParser @@ -49,14 +50,15 @@ def main(): try: returncode = getattr(obj, args.method)() + except KeyboardInterrupt: + pass except AttributeError as e: logging.critical(e, exc_info=want_tb()) logging.warning('See subparser "%s" configuration.', args.subparser_name) returncode = 3 - except KeyboardInterrupt: - pass except ( + CalledProcessError, ConnectionRefusedError, ConnectionResetError, TimeoutError, -- cgit v1.2.3-54-g00ecf