mirror of
https://github.com/containers/podman.git
synced 2025-07-15 03:02:52 +08:00

* Add support for pod -- create, inspect, kill, pause, ps, rm, restart, start, stop, top, unpause * Update pylintrc to better reflect pep8 code standards * Fix various pylint reported errors * Refactor code that determines screen width to no longer require initializing curses. Improved start up time and pushing data blob down ssh tunnel. * Correct pod-create man page, cgroupparent not boolean * Abort integration tests if podman service fails to start Signed-off-by: Jhon Honce <jhonce@redhat.com>
241 lines
8.1 KiB
Python
241 lines
8.1 KiB
Python
"""Parse configuration while building subcommands."""
|
|
import argparse
|
|
import getpass
|
|
import inspect
|
|
import logging
|
|
import os
|
|
import shutil
|
|
import sys
|
|
from contextlib import suppress
|
|
from pathlib import Path
|
|
|
|
import pkg_resources
|
|
import pytoml
|
|
|
|
from .parser_actions import PathAction, PositiveIntAction
|
|
|
|
# TODO: setup.py and obtain __version__ from rpm.spec
|
|
try:
|
|
__version__ = pkg_resources.get_distribution('pypodman').version
|
|
except Exception: # pylint: disable=broad-except
|
|
__version__ = '0.0.0'
|
|
|
|
|
|
class HelpFormatter(argparse.RawDescriptionHelpFormatter):
|
|
"""Set help width to screen size."""
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
"""Construct HelpFormatter using screen width."""
|
|
if 'width' not in kwargs:
|
|
try:
|
|
size = shutil.get_terminal_size()
|
|
kwargs['width'] = size.columns
|
|
except Exception: # pylint: disable=broad-except
|
|
kwargs['width'] = 80
|
|
|
|
super().__init__(*args, **kwargs)
|
|
|
|
|
|
class PodmanArgumentParser(argparse.ArgumentParser):
|
|
"""Default remote podman configuration."""
|
|
|
|
def __init__(self, **kwargs):
|
|
"""Construct the parser."""
|
|
kwargs['add_help'] = True
|
|
kwargs['description'] = ('Portable and simple management'
|
|
' tool for containers and images')
|
|
kwargs['formatter_class'] = HelpFormatter
|
|
|
|
super().__init__(**kwargs)
|
|
|
|
def initialize_parser(self):
|
|
"""Initialize parser without causing recursion meltdown."""
|
|
self.add_argument(
|
|
'--version',
|
|
action='version',
|
|
version='%(prog)s v. ' + __version__)
|
|
self.add_argument(
|
|
'--log-level',
|
|
choices=['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'],
|
|
default='WARNING',
|
|
type=str.upper,
|
|
help='set logging level for events. (default: %(default)s)',
|
|
)
|
|
self.add_argument(
|
|
'--run-dir',
|
|
metavar='DIRECTORY',
|
|
help=('directory to place local socket bindings.'
|
|
' (default: XDG_RUNTIME_DIR/pypodman)'))
|
|
self.add_argument(
|
|
'--username',
|
|
'-l',
|
|
help='Authenicating user on remote host. (default: {})'.format(
|
|
getpass.getuser()))
|
|
self.add_argument(
|
|
'--host', help='name of remote host. (default: None)')
|
|
self.add_argument(
|
|
'--port',
|
|
'-p',
|
|
action=PositiveIntAction,
|
|
help='port for ssh tunnel to remote host. (default: 22)')
|
|
self.add_argument(
|
|
'--remote-socket-path',
|
|
metavar='PATH',
|
|
help=('path of podman socket on remote host'
|
|
' (default: /run/podman/io.podman)'))
|
|
self.add_argument(
|
|
'--identity-file',
|
|
'-i',
|
|
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)'))
|
|
|
|
actions_parser = self.add_subparsers(
|
|
dest='subparser_name', help='commands')
|
|
|
|
# import buried here to prevent import loops
|
|
import pypodman.lib.actions # pylint: disable=cyclic-import
|
|
assert pypodman.lib.actions
|
|
|
|
# pull in plugin(s) code for each subcommand
|
|
for name, obj in inspect.getmembers(
|
|
sys.modules['pypodman.lib.actions'],
|
|
predicate=inspect.isclass):
|
|
if hasattr(obj, 'subparser'):
|
|
try:
|
|
obj.subparser(actions_parser)
|
|
except NameError as e:
|
|
logging.critical(e)
|
|
logging.warning(
|
|
'See subparser configuration for Class "%s"', name)
|
|
sys.exit(3)
|
|
|
|
def parse_args(self, args=None, namespace=None):
|
|
"""Parse command line arguments, backed by env var and config_file."""
|
|
self.initialize_parser()
|
|
cooked = super().parse_args(args, namespace)
|
|
return self.resolve_configuration(cooked)
|
|
|
|
def resolve_configuration(self, args):
|
|
"""Find and fill in any arguments not passed on command line."""
|
|
args.xdg_runtime_dir = os.environ.get('XDG_RUNTIME_DIR', '/tmp')
|
|
args.xdg_config_home = os.environ.get('XDG_CONFIG_HOME',
|
|
os.path.expanduser('~/.config'))
|
|
args.xdg_config_dirs = os.environ.get('XDG_CONFIG_DIRS', '/etc/xdg')
|
|
|
|
# Configuration file(s) are optional,
|
|
# required arguments may be provided elsewhere
|
|
config = {'default': {}}
|
|
dirs = args.xdg_config_dirs.split(':')
|
|
dirs.extend((args.xdg_config_home, args.config_home))
|
|
for dir_ in dirs:
|
|
if dir_ is None:
|
|
continue
|
|
with suppress(OSError):
|
|
cnf = Path(dir_, 'pypodman', 'pypodman.conf')
|
|
with cnf.open() as stream:
|
|
config.update(pytoml.load(stream))
|
|
|
|
def reqattr(name, value):
|
|
"""Raise an error if value is unset."""
|
|
if value:
|
|
setattr(args, name, value)
|
|
return value
|
|
return self.error(
|
|
'Required argument "{}" is not configured.'.format(name))
|
|
|
|
reqattr(
|
|
'run_dir',
|
|
getattr(args, 'run_dir')
|
|
or os.environ.get('RUN_DIR')
|
|
or config['default'].get('run_dir')
|
|
or Path(args.xdg_runtime_dir, 'pypodman')
|
|
) # yapf: disable
|
|
|
|
setattr(
|
|
args,
|
|
'host',
|
|
getattr(args, 'host')
|
|
or os.environ.get('HOST')
|
|
or config['default'].get('host')
|
|
) # yapf:disable
|
|
|
|
reqattr(
|
|
'username',
|
|
getattr(args, 'username')
|
|
or os.environ.get('USER')
|
|
or os.environ.get('LOGNAME')
|
|
or config['default'].get('username')
|
|
or getpass.getuser()
|
|
) # yapf:disable
|
|
|
|
reqattr(
|
|
'port',
|
|
getattr(args, 'port')
|
|
or os.environ.get('PORT')
|
|
or config['default'].get('port', None)
|
|
or 22
|
|
) # yapf:disable
|
|
|
|
reqattr(
|
|
'remote_socket_path',
|
|
getattr(args, 'remote_socket_path')
|
|
or os.environ.get('REMOTE_SOCKET_PATH')
|
|
or config['default'].get('remote_socket_path')
|
|
or '/run/podman/io.podman'
|
|
) # yapf:disable
|
|
|
|
reqattr(
|
|
'log_level',
|
|
getattr(args, 'log_level')
|
|
or os.environ.get('LOG_LEVEL')
|
|
or config['default'].get('log_level')
|
|
or logging.WARNING
|
|
) # yapf:disable
|
|
|
|
setattr(
|
|
args,
|
|
'identity_file',
|
|
getattr(args, 'identity_file')
|
|
or os.environ.get('IDENTITY_FILE')
|
|
or config['default'].get('identity_file')
|
|
or os.path.expanduser('~{}/.ssh/id_dsa'.format(args.username))
|
|
) # yapf:disable
|
|
|
|
if not os.path.isfile(args.identity_file):
|
|
args.identity_file = None
|
|
|
|
if args.host:
|
|
args.local_socket_path = Path(args.run_dir, 'podman.socket')
|
|
else:
|
|
args.local_socket_path = args.remote_socket_path
|
|
|
|
args.local_uri = 'unix:{}'.format(args.local_socket_path)
|
|
|
|
if args.host:
|
|
components = ['ssh://', args.username, '@', 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):
|
|
"""Capture message and route to logger."""
|
|
if message:
|
|
log = logging.info if status == 0 else logging.error
|
|
log(message)
|
|
super().exit(status)
|
|
|
|
def error(self, message):
|
|
"""Capture message and route to logger."""
|
|
logging.error('%s: %s', self.prog, message)
|
|
logging.error("Try '%s --help' for more information.", self.prog)
|
|
super().exit(2)
|