Merge pull request #1893 from jwhonce/bug/1869

Refactor CLI booleans to be consistent and defined behavior
This commit is contained in:
OpenShift Merge Robot
2018-12-07 08:31:55 -08:00
committed by GitHub
32 changed files with 208 additions and 264 deletions

View File

@ -1,6 +1,6 @@
PYTHON ?= $(shell command -v python3 2>/dev/null || command -v python) PYTHON ?= $(shell command -v python3 2>/dev/null || command -v python)
DESTDIR ?= / DESTDIR ?= /
PODMAN_VERSION ?= '0.0.4' PODMAN_VERSION ?= '0.11.1.1'
.PHONY: python-podman .PHONY: python-podman
python-podman: python-podman:
@ -22,8 +22,8 @@ install:
.PHONY: upload .PHONY: upload
upload: upload:
$(PODMAN_VERSION) $(PYTHON) setup.py sdist bdist_wheel PODMAN_VERSION=$(PODMAN_VERSION) $(PYTHON) setup.py sdist bdist_wheel
twine upload --repository-url https://test.pypi.org/legacy/ dist/* twine upload --verbose --repository-url https://test.pypi.org/legacy/ dist/*
.PHONY: clobber .PHONY: clobber
clobber: uninstall clean clobber: uninstall clean

View File

@ -1,6 +1,6 @@
PYTHON ?= $(shell command -v python3 2>/dev/null || command -v python) PYTHON ?= $(shell command -v python3 2>/dev/null || command -v python)
DESTDIR := / DESTDIR := /
PODMAN_VERSION ?= '0.0.4' PODMAN_VERSION ?= '0.11.1.1'
.PHONY: python-pypodman .PHONY: python-pypodman
python-pypodman: python-pypodman:
@ -22,7 +22,7 @@ install:
.PHONY: upload .PHONY: upload
upload: upload:
$(PODMAN_VERSION) $(PYTHON) setup.py sdist bdist_wheel PODMAN_VERSION=$(PODMAN_VERSION) $(PYTHON) setup.py sdist bdist_wheel
twine upload --repository-url https://test.pypi.org/legacy/ dist/* twine upload --repository-url https://test.pypi.org/legacy/ dist/*
.PHONY: clobber .PHONY: clobber

View File

@ -3,18 +3,17 @@ import sys
import podman import podman
from pypodman.lib.action_base import AbstractActionBase from pypodman.lib.action_base import AbstractActionBase
from pypodman.lib.parser_actions import (BooleanAction, BooleanValidate, from pypodman.lib.parser_actions import (ChangeAction, PathAction,
ChangeAction, PathAction, PositiveIntAction, SignalAction,
PositiveIntAction, UnitAction) UnitAction)
from pypodman.lib.podman_parser import PodmanArgumentParser from pypodman.lib.podman_parser import PodmanArgumentParser
from pypodman.lib.report import Report, ReportColumn from pypodman.lib.report import Report, ReportColumn
# Silence pylint overlording... # Silence pylint overlording...
assert BooleanAction
assert BooleanValidate
assert ChangeAction assert ChangeAction
assert PathAction assert PathAction
assert PositiveIntAction assert PositiveIntAction
assert SignalAction
assert UnitAction assert UnitAction
__all__ = [ __all__ = [

View File

@ -17,29 +17,21 @@ class AbstractActionBase(abc.ABC):
Use set_defaults() to set attributes "class_" and "method". These will Use set_defaults() to set attributes "class_" and "method". These will
be invoked as class_(parsed_args).method() be invoked as class_(parsed_args).method()
""" """
parent.add_argument( parent.add_flag(
'--all', '--all',
action='store_true', help='list all items.')
help=('list all items.' parent.add_flag(
' (default: no-op, included for compatibility.)')) '--truncate',
parent.add_argument( '--trunc',
'--no-trunc',
'--notruncate',
action='store_false',
dest='truncate',
default=True, default=True,
help='Display extended information. (default: False)') help="Truncate id's and other long fields.")
parent.add_argument( parent.add_flag(
'--noheading', '--heading',
action='store_false',
dest='heading',
default=True, default=True,
help=('Omit the table headings from the output.' help='Include table headings in the output.')
' (default: False)')) parent.add_flag(
parent.add_argument(
'--quiet', '--quiet',
action='store_true', help='List only the IDs.')
help='List only the IDs. (default: %(default)s)')
def __init__(self, args): def __init__(self, args):
"""Construct class.""" """Construct class."""

View File

@ -1,6 +1,6 @@
"""Implement common create container arguments together.""" """Implement common create container arguments together."""
from pypodman.lib import BooleanAction, UnitAction from pypodman.lib import SignalAction, UnitAction
class CreateArguments(): class CreateArguments():
@ -108,11 +108,9 @@ class CreateArguments():
metavar='NODES', metavar='NODES',
help=('Memory nodes (MEMs) in which to allow execution (0-3, 0,1).' help=('Memory nodes (MEMs) in which to allow execution (0-3, 0,1).'
' Only effective on NUMA systems')) ' Only effective on NUMA systems'))
parser.add_argument( parser.add_flag(
'--detach', '--detach',
'-d', '-d',
action=BooleanAction,
default=False,
help='Detached mode: run the container in the background and' help='Detached mode: run the container in the background and'
' print the new container ID. (default: False)') ' print the new container ID. (default: False)')
parser.add_argument( parser.add_argument(
@ -218,7 +216,7 @@ class CreateArguments():
# only way for argparse to handle these options. # only way for argparse to handle these options.
vol_args = { vol_args = {
'choices': ['bind', 'tmpfs', 'ignore'], 'choices': ('bind', 'tmpfs', 'ignore'),
'metavar': 'MODE', 'metavar': 'MODE',
'type': str.lower, 'type': str.lower,
'help': 'Tells podman how to handle the builtin image volumes', 'help': 'Tells podman how to handle the builtin image volumes',
@ -228,12 +226,10 @@ class CreateArguments():
volume_group.add_argument('--image-volume', **vol_args) volume_group.add_argument('--image-volume', **vol_args)
volume_group.add_argument('--builtin-volume', **vol_args) volume_group.add_argument('--builtin-volume', **vol_args)
parser.add_argument( parser.add_flag(
'--interactive', '--interactive',
'-i', '-i',
action=BooleanAction, help='Keep STDIN open even if not attached.')
default=False,
help='Keep STDIN open even if not attached. (default: False)')
parser.add_argument('--ipc', help='Create namespace') parser.add_argument('--ipc', help='Create namespace')
parser.add_argument( parser.add_argument(
'--kernel-memory', action=UnitAction, help='Kernel memory limit') '--kernel-memory', action=UnitAction, help='Kernel memory limit')
@ -278,10 +274,9 @@ class CreateArguments():
metavar='BRIDGE', metavar='BRIDGE',
help='Set the Network mode for the container.' help='Set the Network mode for the container.'
' (format: bridge, host, container:UUID, ns:PATH, none)') ' (format: bridge, host, container:UUID, ns:PATH, none)')
parser.add_argument( parser.add_flag(
'--oom-kill-disable', '--oom-kill-disable',
action=BooleanAction, help='Whether to disable OOM Killer for the container or not.')
help='Whether to disable OOM Killer for the container or not')
parser.add_argument( parser.add_argument(
'--oom-score-adj', '--oom-score-adj',
choices=range(-1000, 1000), choices=range(-1000, 1000),
@ -298,41 +293,33 @@ class CreateArguments():
help=("Tune the container's pids limit." help=("Tune the container's pids limit."
" Set -1 to have unlimited pids for the container.")) " Set -1 to have unlimited pids for the container."))
parser.add_argument('--pod', help='Run container in an existing pod') parser.add_argument('--pod', help='Run container in an existing pod')
parser.add_argument( parser.add_flag(
'--privileged', '--privileged',
action=BooleanAction,
help='Give extended privileges to this container.') help='Give extended privileges to this container.')
parser.add_argument( parser.add_argument(
'--publish', '--publish',
'-p', '-p',
metavar='RANGE', metavar='RANGE',
help="Publish a container's port, or range of ports, to the host") help="Publish a container's port, or range of ports, to the host")
parser.add_argument( parser.add_flag(
'--publish-all', '--publish-all',
'-P', '-P',
action=BooleanAction,
help='Publish all exposed ports to random' help='Publish all exposed ports to random'
' ports on the host interfaces' ' ports on the host interfaces.')
'(default: False)') parser.add_flag(
parser.add_argument(
'--quiet', '--quiet',
'-q', '-q',
action='store_true',
help='Suppress output information when pulling images') help='Suppress output information when pulling images')
parser.add_argument( parser.add_flag(
'--read-only', '--read-only',
action=BooleanAction,
help="Mount the container's root filesystem as read only.") help="Mount the container's root filesystem as read only.")
parser.add_argument( parser.add_flag(
'--rm', '--rm',
action=BooleanAction,
default=False,
help='Automatically remove the container when it exits.') help='Automatically remove the container when it exits.')
parser.add_argument( parser.add_argument(
'--rootfs', '--rootfs',
action='store_true', help='If specified, the first argument refers to an'
help=('If specified, the first argument refers to an' ' exploded container on the file system of remote host.')
' exploded container on the file system of remote host.'))
parser.add_argument( parser.add_argument(
'--security-opt', '--security-opt',
action='append', action='append',
@ -340,15 +327,14 @@ class CreateArguments():
help='Set security options.') help='Set security options.')
parser.add_argument( parser.add_argument(
'--shm-size', action=UnitAction, help='Size of /dev/shm') '--shm-size', action=UnitAction, help='Size of /dev/shm')
parser.add_argument( parser.add_flag(
'--sig-proxy', '--sig-proxy',
action=BooleanAction,
default=True,
help='Proxy signals sent to the podman run' help='Proxy signals sent to the podman run'
' command to the container process') ' command to the container process')
parser.add_argument( parser.add_argument(
'--stop-signal', '--stop-signal',
metavar='SIGTERM', action=SignalAction,
default='TERM',
help='Signal to stop a container') help='Signal to stop a container')
parser.add_argument( parser.add_argument(
'--stop-timeout', '--stop-timeout',
@ -374,11 +360,9 @@ class CreateArguments():
metavar='MOUNT', metavar='MOUNT',
help='Create a tmpfs mount.' help='Create a tmpfs mount.'
' (default: rw,noexec,nosuid,nodev,size=65536k.)') ' (default: rw,noexec,nosuid,nodev,size=65536k.)')
parser.add_argument( parser.add_flag(
'--tty', '--tty',
'-t', '-t',
action=BooleanAction,
default=False,
help='Allocate a pseudo-TTY for standard input of container.') help='Allocate a pseudo-TTY for standard input of container.')
parser.add_argument( parser.add_argument(
'--uidmap', '--uidmap',
@ -394,15 +378,16 @@ class CreateArguments():
parser.add_argument( parser.add_argument(
'--user', '--user',
'-u', '-u',
help=('Sets the username or UID used and optionally' help='Sets the username or UID used and optionally'
' the groupname or GID for the specified command.')) ' the groupname or GID for the specified command.')
parser.add_argument( parser.add_argument(
'--userns', '--userns',
metavar='NAMESPACE', metavar='NAMESPACE',
help='Set the user namespace mode for the container') help='Set the user namespace mode for the container')
parser.add_argument( parser.add_argument(
'--uts', '--uts',
choices=['host', 'ns'], choices=('host', 'ns'),
type=str.lower,
help='Set the UTS mode for the container') help='Set the UTS mode for the container')
parser.add_argument('--volume', '-v', help='Create a bind mount.') parser.add_argument('--volume', '-v', help='Create a bind mount.')
parser.add_argument( parser.add_argument(

View File

@ -2,7 +2,7 @@
import sys import sys
import podman import podman
from pypodman.lib import AbstractActionBase, BooleanAction, ChangeAction from pypodman.lib import AbstractActionBase, ChangeAction
class Commit(AbstractActionBase): class Commit(AbstractActionBase):
@ -44,17 +44,14 @@ class Commit(AbstractActionBase):
help='Set commit message for committed image' help='Set commit message for committed image'
' (Only on docker images.)', ' (Only on docker images.)',
) )
parser.add_argument( parser.add_flag(
'--pause', '--pause',
'-p', '-p',
action=BooleanAction,
default=True,
help='Pause the container when creating an image', help='Pause the container when creating an image',
) )
parser.add_argument( parser.add_flag(
'--quiet', '--quiet',
'-q', '-q',
action='store_true',
help='Suppress output', help='Suppress output',
) )
parser.add_argument( parser.add_argument(

View File

@ -5,8 +5,7 @@ from collections import OrderedDict
import humanize import humanize
import podman import podman
from pypodman.lib import (AbstractActionBase, BooleanAction, Report, from pypodman.lib import AbstractActionBase, Report, ReportColumn
ReportColumn)
class History(AbstractActionBase): class History(AbstractActionBase):
@ -17,13 +16,10 @@ class History(AbstractActionBase):
"""Add History command to parent parser.""" """Add History command to parent parser."""
parser = parent.add_parser('history', help='report image history') parser = parent.add_parser('history', help='report image history')
super().subparser(parser) super().subparser(parser)
parser.add_argument( parser.add_flag(
'--human', '--human',
'-H', '-H',
action=BooleanAction, help='Display sizes and dates in human readable format.')
default='True',
help='Display sizes and dates in human readable format.'
' (default: %(default)s)')
parser.add_argument( parser.add_argument(
'--format', '--format',
choices=('json', 'table'), choices=('json', 'table'),

View File

@ -24,11 +24,9 @@ class Images(AbstractActionBase):
help=('Change sort ordered of displayed images.' help=('Change sort ordered of displayed images.'
' (default: %(default)s)')) ' (default: %(default)s)'))
group = parser.add_mutually_exclusive_group() parser.add_flag(
group.add_argument(
'--digests', '--digests',
action='store_true', help='Include digests with images.')
help='Include digests with images. (default: %(default)s)')
parser.set_defaults(class_=cls, method='list') parser.set_defaults(class_=cls, method='list')
def __init__(self, args): def __init__(self, args):

View File

@ -22,10 +22,6 @@ class Info(AbstractActionBase):
" (default: yaml)") " (default: yaml)")
parser.set_defaults(class_=cls, method='info') parser.set_defaults(class_=cls, method='info')
def __init__(self, args):
"""Construct Info class."""
super().__init__(args)
def info(self): def info(self):
"""Report on Podman Service.""" """Report on Podman Service."""
try: try:

View File

@ -22,12 +22,9 @@ class Inspect(AbstractActionBase):
type=str.lower, type=str.lower,
help='Type of object to inspect', help='Type of object to inspect',
) )
parser.add_argument( parser.add_flag(
'--size', '--size',
action='store_true', help='Display the total file size if the type is a container.')
default=False,
help='Display the total file size if the type is a container.'
' Always True.')
parser.add_argument( parser.add_argument(
'objects', 'objects',
nargs='+', nargs='+',
@ -35,10 +32,6 @@ class Inspect(AbstractActionBase):
) )
parser.set_defaults(class_=cls, method='inspect') parser.set_defaults(class_=cls, method='inspect')
def __init__(self, args):
"""Construct Inspect class."""
super().__init__(args)
def _get_container(self, ident): def _get_container(self, ident):
try: try:
logging.debug("Getting container %s", ident) logging.debug("Getting container %s", ident)

View File

@ -1,9 +1,8 @@
"""Remote client command for signaling podman containers.""" """Remote client command for signaling podman containers."""
import signal
import sys import sys
import podman import podman
from pypodman.lib import AbstractActionBase from pypodman.lib import AbstractActionBase, SignalAction
class Kill(AbstractActionBase): class Kill(AbstractActionBase):
@ -16,10 +15,9 @@ class Kill(AbstractActionBase):
parser.add_argument( parser.add_argument(
'--signal', '--signal',
'-s', '-s',
choices=range(1, signal.NSIG), action=SignalAction,
metavar='[1,{}]'.format(signal.NSIG),
default=9, default=9,
help='Signal to send to the container. (default: 9)') help='Signal to send to the container. (default: %(default)s)')
parser.add_argument( parser.add_argument(
'containers', 'containers',
nargs='+', nargs='+',
@ -27,10 +25,6 @@ class Kill(AbstractActionBase):
) )
parser.set_defaults(class_=cls, method='kill') parser.set_defaults(class_=cls, method='kill')
def __init__(self, args):
"""Construct Kill class."""
super().__init__(args)
def kill(self): def kill(self):
"""Signal provided containers.""" """Signal provided containers."""
try: try:

View File

@ -19,10 +19,6 @@ class Pause(AbstractActionBase):
) )
parser.set_defaults(class_=cls, method='pause') parser.set_defaults(class_=cls, method='pause')
def __init__(self, args):
"""Construct Pause class."""
super().__init__(args)
def pause(self): def pause(self):
"""Pause provided containers.""" """Pause provided containers."""
try: try:

View File

@ -2,7 +2,7 @@
import sys import sys
import podman import podman
from pypodman.lib import AbstractActionBase, BooleanAction from pypodman.lib import AbstractActionBase
class CreatePod(AbstractActionBase): class CreatePod(AbstractActionBase):
@ -20,12 +20,9 @@ class CreatePod(AbstractActionBase):
type=str, type=str,
help='Path to cgroups under which the' help='Path to cgroups under which the'
' cgroup for the pod will be created.') ' cgroup for the pod will be created.')
parser.add_argument( parser.add_flag(
'--infra', '--infra',
action=BooleanAction, help='Create an infra container and associate it with the pod.')
default=True,
help='Create an infra container and associate it with the pod'
'(default: %(default)s)')
parser.add_argument( parser.add_argument(
'-l', '-l',
'--label', '--label',

View File

@ -3,7 +3,7 @@ import signal
import sys import sys
import podman import podman
from pypodman.lib import AbstractActionBase from pypodman.lib import AbstractActionBase, SignalAction
from pypodman.lib import query_model as query_pods from pypodman.lib import query_model as query_pods
@ -15,18 +15,16 @@ class KillPod(AbstractActionBase):
"""Add Pod Kill command to parent parser.""" """Add Pod Kill command to parent parser."""
parser = parent.add_parser('kill', help='signal containers in pod') parser = parent.add_parser('kill', help='signal containers in pod')
parser.add_argument( parser.add_flag(
'-a',
'--all', '--all',
action='store_true', '-a',
help='Sends signal to all pods') help='Sends signal to all pods.')
parser.add_argument( parser.add_argument(
'-s', '-s',
'--signal', '--signal',
choices=range(1, signal.NSIG), action=SignalAction,
metavar='[1,{}]'.format(signal.NSIG),
default=9, default=9,
help='Signal to send to the pod. (default: 9)') help='Signal to send to the pod. (default: %(default)s)')
parser.add_argument('pod', nargs='*', help='pod(s) to signal') parser.add_argument('pod', nargs='*', help='pod(s) to signal')
parser.set_defaults(class_=cls, method='kill') parser.set_defaults(class_=cls, method='kill')

View File

@ -13,8 +13,10 @@ class PausePod(AbstractActionBase):
def subparser(cls, parent): def subparser(cls, parent):
"""Add Pod Pause command to parent parser.""" """Add Pod Pause command to parent parser."""
parser = parent.add_parser('pause', help='pause containers in pod') parser = parent.add_parser('pause', help='pause containers in pod')
parser.add_argument( parser.add_flag(
'-a', '--all', action='store_true', help='Pause all pods') '--all',
'-a',
help='Pause all pods.')
parser.add_argument('pod', nargs='*', help='pod(s) to pause.') parser.add_argument('pod', nargs='*', help='pod(s) to pause.')
parser.set_defaults(class_=cls, method='pause') parser.set_defaults(class_=cls, method='pause')

View File

@ -14,18 +14,15 @@ class ProcessesPod(AbstractActionBase):
parser = parent.add_parser('ps', help='list processes of pod') parser = parent.add_parser('ps', help='list processes of pod')
super().subparser(parser) super().subparser(parser)
parser.add_argument( parser.add_flag(
'--ctr-names', '--ctr-names',
action='store_true', help='Include container name in the info field.')
help='Include container name in the info field') parser.add_flag(
parser.add_argument(
'--ctr-ids', '--ctr-ids',
action='store_true', help='Include container ID in the info field.')
help='Include container ID in the info field') parser.add_flag(
parser.add_argument(
'--ctr-status', '--ctr-status',
action='store_true', help='Include container status in the info field.')
help='Include container status in the info field')
parser.add_argument( parser.add_argument(
'--format', '--format',
choices=('json'), choices=('json'),

View File

@ -13,13 +13,14 @@ class RemovePod(AbstractActionBase):
def subparser(cls, parent): def subparser(cls, parent):
"""Add Pod Rm command to parent parser.""" """Add Pod Rm command to parent parser."""
parser = parent.add_parser('rm', help='Delete pod and container(s)') parser = parent.add_parser('rm', help='Delete pod and container(s)')
parser.add_argument( parser.add_flag(
'-a', '--all', action='store_true', help='Remove all pods') '--all',
parser.add_argument( '-a',
'-f', help='Remove all pods.')
parser.add_flag(
'--force', '--force',
action='store_true', '-f',
help='Stop and remove container(s) then delete pod') help='Stop and remove container(s) then delete pod.')
parser.add_argument( parser.add_argument(
'pod', nargs='*', help='Pod to remove. Or, use --all') 'pod', nargs='*', help='Pod to remove. Or, use --all')
parser.set_defaults(class_=cls, method='remove') parser.set_defaults(class_=cls, method='remove')

View File

@ -13,8 +13,10 @@ class RestartPod(AbstractActionBase):
def subparser(cls, parent): def subparser(cls, parent):
"""Add Pod Restart command to parent parser.""" """Add Pod Restart command to parent parser."""
parser = parent.add_parser('restart', help='restart containers in pod') parser = parent.add_parser('restart', help='restart containers in pod')
parser.add_argument( parser.add_flag(
'-a', '--all', action='store_true', help='Restart all pods') '--all',
'-a',
help='Restart all pods.')
parser.add_argument( parser.add_argument(
'pod', nargs='*', help='Pod to restart. Or, use --all') 'pod', nargs='*', help='Pod to restart. Or, use --all')
parser.set_defaults(class_=cls, method='restart') parser.set_defaults(class_=cls, method='restart')

View File

@ -14,8 +14,10 @@ class StartPod(AbstractActionBase):
def subparser(cls, parent): def subparser(cls, parent):
"""Add Pod Start command to parent parser.""" """Add Pod Start command to parent parser."""
parser = parent.add_parser('start', help='start pod') parser = parent.add_parser('start', help='start pod')
parser.add_argument( parser.add_flag(
'-a', '--all', action='store_true', help='Start all pods') '--all',
'-a',
help='Start all pods.')
parser.add_argument( parser.add_argument(
'pod', nargs='*', help='Pod to start. Or, use --all') 'pod', nargs='*', help='Pod to start. Or, use --all')
parser.set_defaults(class_=cls, method='start') parser.set_defaults(class_=cls, method='start')

View File

@ -13,8 +13,10 @@ class StopPod(AbstractActionBase):
def subparser(cls, parent): def subparser(cls, parent):
"""Add Pod Stop command to parent parser.""" """Add Pod Stop command to parent parser."""
parser = parent.add_parser('stop', help='stop pod') parser = parent.add_parser('stop', help='stop pod')
parser.add_argument( parser.add_flag(
'-a', '--all', action='store_true', help='Stop all pods') '--all',
'-a',
help='Stop all pods.')
parser.add_argument( parser.add_argument(
'pod', nargs='*', help='Pod to stop. Or, use --all') 'pod', nargs='*', help='Pod to stop. Or, use --all')
parser.set_defaults(class_=cls, method='stop') parser.set_defaults(class_=cls, method='stop')

View File

@ -13,8 +13,10 @@ class UnpausePod(AbstractActionBase):
def subparser(cls, parent): def subparser(cls, parent):
"""Add Pod Unpause command to parent parser.""" """Add Pod Unpause command to parent parser."""
parser = parent.add_parser('unpause', help='unpause pod') parser = parent.add_parser('unpause', help='unpause pod')
parser.add_argument( parser.add_flag(
'-a', '--all', action='store_true', help='Unpause all pods') '--all',
'-a',
help='Unpause all pods.')
parser.add_argument( parser.add_argument(
'pod', nargs='*', help='Pod to unpause. Or, use --all') 'pod', nargs='*', help='Pod to unpause. Or, use --all')
parser.set_defaults(class_=cls, method='unpause') parser.set_defaults(class_=cls, method='unpause')

View File

@ -5,6 +5,8 @@ import sys
from pypodman.lib import AbstractActionBase from pypodman.lib import AbstractActionBase
# pylint: disable=wildcard-import
# pylint: disable=unused-wildcard-import
from .pod import * from .pod import *

View File

@ -13,16 +13,13 @@ class Port(AbstractActionBase):
"""Add Port command to parent parser.""" """Add Port command to parent parser."""
parser = parent.add_parser( parser = parent.add_parser(
'port', help='retrieve ports from containers') 'port', help='retrieve ports from containers')
parser.add_argument( parser.add_flag(
'--all', '--all',
'-a', '-a',
action='store_true',
default=False,
help='List all known port mappings for running containers') help='List all known port mappings for running containers')
parser.add_argument( parser.add_argument(
'containers', 'containers',
nargs='*', nargs='*',
default=None,
help='containers to list ports', help='containers to list ports',
) )
parser.set_defaults(class_=cls, method='port') parser.set_defaults(class_=cls, method='port')
@ -61,3 +58,4 @@ class Port(AbstractActionBase):
file=sys.stderr, file=sys.stderr,
flush=True) flush=True)
return 1 return 1
return 0

View File

@ -15,12 +15,10 @@ class Push(AbstractActionBase):
'push', 'push',
help='push image elsewhere', help='push image elsewhere',
) )
parser.add_argument( parser.add_flag(
'--tlsverify', '--tlsverify',
action='store_true',
default=True,
help='Require HTTPS and verify certificates when' help='Require HTTPS and verify certificates when'
' contacting registries (default: %(default)s)') ' contacting registries.')
parser.add_argument( parser.add_argument(
'image', nargs=1, help='name or id of image to push') 'image', nargs=1, help='name or id of image to push')
parser.add_argument( parser.add_argument(
@ -30,10 +28,6 @@ class Push(AbstractActionBase):
) )
parser.set_defaults(class_=cls, method='push') parser.set_defaults(class_=cls, method='push')
def __init__(self, args):
"""Construct Push class."""
super().__init__(args)
def pull(self): def pull(self):
"""Store image elsewhere.""" """Store image elsewhere."""
try: try:

View File

@ -23,10 +23,6 @@ class Restart(AbstractActionBase):
'targets', nargs='+', help='container id(s) to restart') 'targets', nargs='+', help='container id(s) to restart')
parser.set_defaults(class_=cls, method='restart') parser.set_defaults(class_=cls, method='restart')
def __init__(self, args):
"""Construct Restart class."""
super().__init__(args)
def restart(self): def restart(self):
"""Restart container(s).""" """Restart container(s)."""
try: try:

View File

@ -12,20 +12,14 @@ class Rm(AbstractActionBase):
def subparser(cls, parent): def subparser(cls, parent):
"""Add Rm command to parent parser.""" """Add Rm command to parent parser."""
parser = parent.add_parser('rm', help='delete container(s)') parser = parent.add_parser('rm', help='delete container(s)')
parser.add_argument( parser.add_flag(
'-f',
'--force', '--force',
action='store_true', '-f',
help=('force delete of running container(s).' help='force delete of running container(s).')
' (default: %(default)s)'))
parser.add_argument( parser.add_argument(
'targets', nargs='+', help='container id(s) to delete') 'targets', nargs='+', help='container id(s) to delete')
parser.set_defaults(class_=cls, method='remove') parser.set_defaults(class_=cls, method='remove')
def __init__(self, args):
"""Construct Rm class."""
super().__init__(args)
def remove(self): def remove(self):
"""Remove container(s).""" """Remove container(s)."""
for ident in self._args.targets: for ident in self._args.targets:

View File

@ -12,19 +12,13 @@ class Rmi(AbstractActionBase):
def subparser(cls, parent): def subparser(cls, parent):
"""Add Rmi command to parent parser.""" """Add Rmi command to parent parser."""
parser = parent.add_parser('rmi', help='delete image(s)') parser = parent.add_parser('rmi', help='delete image(s)')
parser.add_argument( parser.add_flag(
'-f',
'--force', '--force',
action='store_true', '-f',
help=('force delete of image(s) and associated containers.' help='force delete of image(s) and associated containers.')
' (default: %(default)s)'))
parser.add_argument('targets', nargs='+', help='image id(s) to delete') parser.add_argument('targets', nargs='+', help='image id(s) to delete')
parser.set_defaults(class_=cls, method='remove') parser.set_defaults(class_=cls, method='remove')
def __init__(self, args):
"""Construct Rmi class."""
super().__init__(args)
def remove(self): def remove(self):
"""Remove image(s).""" """Remove image(s)."""
for ident in self._args.targets: for ident in self._args.targets:

View File

@ -4,8 +4,8 @@ import sys
from collections import OrderedDict from collections import OrderedDict
import podman import podman
from pypodman.lib import (AbstractActionBase, BooleanValidate, from pypodman.lib import (AbstractActionBase, PositiveIntAction, Report,
PositiveIntAction, Report, ReportColumn) ReportColumn)
class FilterAction(argparse.Action): class FilterAction(argparse.Action):
@ -58,16 +58,16 @@ class FilterAction(argparse.Action):
if val < 0: if val < 0:
parser.error(msg) parser.error(msg)
elif opt == 'is-automated': elif opt == 'is-automated':
try: if val.capitalize() in ('True', 'False'):
val = BooleanValidate()(val) val = bool(val)
except ValueError: else:
msg = ('{} option "is-automated"' msg = ('{} option "is-automated"'
' must be True or False.'.format(self.dest)) ' must be True or False.'.format(self.dest))
parser.error(msg) parser.error(msg)
elif opt == 'is-official': elif opt == 'is-official':
try: if val.capitalize() in ('True', 'False'):
val = BooleanValidate()(val) val = bool(val)
except ValueError: else:
msg = ('{} option "is-official"' msg = ('{} option "is-official"'
' must be True or False.'.format(self.dest)) ' must be True or False.'.format(self.dest))
parser.error(msg) parser.error(msg)

View File

@ -2,7 +2,7 @@
import sys import sys
import podman import podman
from pypodman.lib import AbstractActionBase, BooleanAction from pypodman.lib import AbstractActionBase
class Start(AbstractActionBase): class Start(AbstractActionBase):
@ -12,12 +12,10 @@ class Start(AbstractActionBase):
def subparser(cls, parent): def subparser(cls, parent):
"""Add Start command to parent parser.""" """Add Start command to parent parser."""
parser = parent.add_parser('start', help='start container') parser = parent.add_parser('start', help='start container')
parser.add_argument( parser.add_flag(
'--attach', '--attach',
'-a', '-a',
action=BooleanAction, help="Attach container's STDOUT and STDERR.")
default=False,
help="Attach container's STDOUT and STDERR (default: %(default)s)")
parser.add_argument( parser.add_argument(
'--detach-keys', '--detach-keys',
metavar='KEY(s)', metavar='KEY(s)',
@ -25,18 +23,14 @@ class Start(AbstractActionBase):
help='Override the key sequence for detaching a container.' help='Override the key sequence for detaching a container.'
' (format: a single character [a-Z] or ctrl-<value> where' ' (format: a single character [a-Z] or ctrl-<value> where'
' <value> is one of: a-z, @, ^, [, , or _) (default: ^D)') ' <value> is one of: a-z, @, ^, [, , or _) (default: ^D)')
parser.add_argument( parser.add_flag(
'--interactive', '--interactive',
'-i', '-i',
action=BooleanAction, help="Attach container's STDIN.")
default=False,
help="Attach container's STDIN (default: %(default)s)")
# TODO: Implement sig-proxy # TODO: Implement sig-proxy
parser.add_argument( parser.add_flag(
'--sig-proxy', '--sig-proxy',
action=BooleanAction, help="Proxy received signals to the process."
default=False,
help="Proxy received signals to the process (default: %(default)s)"
) )
parser.add_argument( parser.add_argument(
'containers', 'containers',
@ -74,3 +68,4 @@ class Start(AbstractActionBase):
file=sys.stderr, file=sys.stderr,
flush=True) flush=True)
return 1 return 1
return 0

View File

@ -1,9 +1,7 @@
"""Remote client command for reporting on Podman service.""" """Remote client command for reporting on Podman service."""
import json
import sys import sys
import podman import podman
import yaml
from pypodman.lib import AbstractActionBase from pypodman.lib import AbstractActionBase
@ -17,10 +15,6 @@ class Version(AbstractActionBase):
'version', help='report version on podman service') 'version', help='report version on podman service')
parser.set_defaults(class_=cls, method='version') parser.set_defaults(class_=cls, method='version')
def __init__(self, args):
"""Construct Version class."""
super().__init__(args)
def version(self): def version(self):
"""Report on Podman Service.""" """Report on Podman Service."""
try: try:

View File

@ -6,6 +6,7 @@ The constructors are very verbose but remain for IDE support.
import argparse import argparse
import copy import copy
import os import os
import signal
# API defined by argparse.Action therefore shut up pylint # API defined by argparse.Action therefore shut up pylint
# pragma pylint: disable=redefined-builtin # pragma pylint: disable=redefined-builtin
@ -13,22 +14,8 @@ import os
# pragma pylint: disable=too-many-arguments # pragma pylint: disable=too-many-arguments
class BooleanValidate(): class ChangeAction(argparse.Action):
"""Validate value is boolean string.""" """Convert and validate change argument."""
def __call__(self, value):
"""Return True, False or raise ValueError."""
val = value.capitalize()
if val == 'False':
return False
elif val == 'True':
return True
else:
raise ValueError('"{}" is not True or False'.format(value))
class BooleanAction(argparse.Action):
"""Convert and validate bool argument."""
def __init__(self, def __init__(self,
option_strings, option_strings,
@ -40,47 +27,12 @@ class BooleanAction(argparse.Action):
choices=None, choices=None,
required=False, required=False,
help=None, help=None,
metavar='{True,False}'):
"""Create BooleanAction object."""
super().__init__(
option_strings=option_strings,
dest=dest,
nargs=nargs,
const=const,
default=default,
type=type,
choices=choices,
required=required,
help=help,
metavar=metavar)
def __call__(self, parser, namespace, values, option_string=None):
"""Convert and Validate input."""
try:
val = BooleanValidate()(values)
except ValueError:
parser.error('"{}" must be True or False.'.format(option_string))
else:
setattr(namespace, self.dest, val)
class ChangeAction(argparse.Action):
"""Convert and validate change argument."""
def __init__(self,
option_strings,
dest,
nargs=None,
const=None,
default=[],
type=None,
choices=None,
required=False,
help=None,
metavar='OPT=VALUE'): metavar='OPT=VALUE'):
"""Create ChangeAction object.""" """Create ChangeAction object."""
help = (help or '') + ('Apply change(s) to the new image.' help = (help or '') + ('Apply change(s) to the new image.'
' May be given multiple times.') ' May be given multiple times.')
if default is None:
default = []
super().__init__( super().__init__(
option_strings=option_strings, option_strings=option_strings,
@ -102,7 +54,7 @@ class ChangeAction(argparse.Action):
choices = ('CMD', 'ENTRYPOINT', 'ENV', 'EXPOSE', 'LABEL', 'ONBUILD', choices = ('CMD', 'ENTRYPOINT', 'ENV', 'EXPOSE', 'LABEL', 'ONBUILD',
'STOPSIGNAL', 'USER', 'VOLUME', 'WORKDIR') 'STOPSIGNAL', 'USER', 'VOLUME', 'WORKDIR')
opt, val = values.split('=', 1) opt, _ = values.split('=', 1)
if opt not in choices: if opt not in choices:
parser.error('Option "{}" is not supported by argument "{}",' parser.error('Option "{}" is not supported by argument "{}",'
' valid options are: {}'.format( ' valid options are: {}'.format(
@ -111,6 +63,70 @@ class ChangeAction(argparse.Action):
setattr(namespace, self.dest, items) setattr(namespace, self.dest, items)
class SignalAction(argparse.Action):
"""Validate input as a signal."""
def __init__(self,
option_strings,
dest,
nargs=None,
const=None,
default=None,
type=str,
choices=None,
required=False,
help='The signal to send.'
' It may be given as a name or a number.',
metavar='SIGNAL'):
"""Create SignalAction object."""
super().__init__(
option_strings=option_strings,
dest=dest,
nargs=nargs,
const=const,
default=default,
type=type,
choices=choices,
required=required,
help=help,
metavar=metavar)
if hasattr(signal, "Signals"):
def _signal_number(signame):
cooked = 'SIG{}'.format(signame)
try:
return signal.Signals[cooked].value
except ValueError:
pass
else:
def _signal_number(signame):
cooked = 'SIG{}'.format(signame)
for n, v in sorted(signal.__dict__.items()):
if n != cooked:
continue
if n.startswith("SIG") and not n.startswith("SIG_"):
return v
self._signal_number = _signal_number
def __call__(self, parser, namespace, values, option_string=None):
"""Validate input is a signal for platform."""
if values.isdigit():
signum = int(values)
if signal.SIGRTMIN <= signum >= signal.SIGRTMAX:
raise ValueError('"{}" is not a valid signal. {}-{}'.format(
values, signal.SIGRTMIN, signal.SIGRTMAX))
else:
signum = self._signal_number(values)
if signum is None:
parser.error(
'"{}" is not a valid signal,'
' see your platform documentation.'.format(values))
setattr(namespace, self.dest, signum)
class UnitAction(argparse.Action): class UnitAction(argparse.Action):
"""Validate number given is positive integer, with optional suffix.""" """Validate number given is positive integer, with optional suffix."""

View File

@ -48,6 +48,18 @@ class PodmanArgumentParser(argparse.ArgumentParser):
super().__init__(**kwargs) super().__init__(**kwargs)
def add_flag(self, *args, **kwargs):
"""Add flag to parser."""
flags = [a for a in args if a[0] in self.prefix_chars]
dest = flags[0].lstrip(self.prefix_chars)
no_flag = '{0}{0}no-{1}'.format(self.prefix_chars, dest)
group = self.add_mutually_exclusive_group(required=False)
group.add_argument(*flags, action='store_true', dest=dest, **kwargs)
group.add_argument(no_flag, action='store_false', dest=dest, **kwargs)
default = kwargs.get('default', False)
self.set_defaults(**{dest: default})
def initialize_parser(self): def initialize_parser(self):
"""Initialize parser without causing recursion meltdown.""" """Initialize parser without causing recursion meltdown."""
self.add_argument( self.add_argument(