mirror of
https://github.com/containers/podman.git
synced 2025-06-19 08:09:12 +08:00
Implement pypodman start command
* Improve error messages from argparse Actions * Silence more pylint errors when supporting a given API * Refactor BooleanAction to support lower and mixed case input * Remove spurious print() Signed-off-by: Jhon Honce <jhonce@redhat.com>
This commit is contained in:
@ -19,9 +19,13 @@ class Mixin:
|
|||||||
"""
|
"""
|
||||||
if stdin is None:
|
if stdin is None:
|
||||||
stdin = sys.stdin.fileno()
|
stdin = sys.stdin.fileno()
|
||||||
|
elif hasattr(stdin, 'fileno'):
|
||||||
|
stdin = stdin.fileno()
|
||||||
|
|
||||||
if stdout is None:
|
if stdout is None:
|
||||||
stdout = sys.stdout.fileno()
|
stdout = sys.stdout.fileno()
|
||||||
|
elif hasattr(stdout, 'fileno'):
|
||||||
|
stdout = stdout.fileno()
|
||||||
|
|
||||||
with self._client() as podman:
|
with self._client() as podman:
|
||||||
attach = podman.GetAttachSockets(self._id)
|
attach = podman.GetAttachSockets(self._id)
|
||||||
@ -49,7 +53,7 @@ class Mixin:
|
|||||||
def resize_handler(self):
|
def resize_handler(self):
|
||||||
"""Send the new window size to conmon."""
|
"""Send the new window size to conmon."""
|
||||||
|
|
||||||
def wrapped(signum, frame):
|
def wrapped(signum, frame): # pylint: disable=unused-argument
|
||||||
packed = fcntl.ioctl(self.pseudo_tty.stdout, termios.TIOCGWINSZ,
|
packed = fcntl.ioctl(self.pseudo_tty.stdout, termios.TIOCGWINSZ,
|
||||||
struct.pack('HHHH', 0, 0, 0, 0))
|
struct.pack('HHHH', 0, 0, 0, 0))
|
||||||
rows, cols, _, _ = struct.unpack('HHHH', packed)
|
rows, cols, _, _ = struct.unpack('HHHH', packed)
|
||||||
@ -67,7 +71,7 @@ class Mixin:
|
|||||||
def log_handler(self):
|
def log_handler(self):
|
||||||
"""Send command to reopen log to conmon."""
|
"""Send command to reopen log to conmon."""
|
||||||
|
|
||||||
def wrapped(signum, frame):
|
def wrapped(signum, frame): # pylint: disable=unused-argument
|
||||||
with open(self.pseudo_tty.control_socket, 'w') as skt:
|
with open(self.pseudo_tty.control_socket, 'w') as skt:
|
||||||
# send conmon reopen log message
|
# send conmon reopen log message
|
||||||
skt.write('2\n')
|
skt.write('2\n')
|
||||||
|
@ -22,6 +22,7 @@ from pypodman.lib.actions.rm_action import Rm
|
|||||||
from pypodman.lib.actions.rmi_action import Rmi
|
from pypodman.lib.actions.rmi_action import Rmi
|
||||||
from pypodman.lib.actions.run_action import Run
|
from pypodman.lib.actions.run_action import Run
|
||||||
from pypodman.lib.actions.search_action import Search
|
from pypodman.lib.actions.search_action import Search
|
||||||
|
from pypodman.lib.actions.start_action import Start
|
||||||
from pypodman.lib.actions.version_action import Version
|
from pypodman.lib.actions.version_action import Version
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
@ -48,5 +49,6 @@ __all__ = [
|
|||||||
'Rmi',
|
'Rmi',
|
||||||
'Run',
|
'Run',
|
||||||
'Search',
|
'Search',
|
||||||
|
'Start',
|
||||||
'Version',
|
'Version',
|
||||||
]
|
]
|
||||||
|
76
contrib/python/pypodman/pypodman/lib/actions/start_action.py
Normal file
76
contrib/python/pypodman/pypodman/lib/actions/start_action.py
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
"""Remote client command for starting containers."""
|
||||||
|
import sys
|
||||||
|
|
||||||
|
import podman
|
||||||
|
from pypodman.lib import AbstractActionBase, BooleanAction
|
||||||
|
|
||||||
|
|
||||||
|
class Start(AbstractActionBase):
|
||||||
|
"""Class for starting container."""
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def subparser(cls, parent):
|
||||||
|
"""Add Start command to parent parser."""
|
||||||
|
parser = parent.add_parser('start', help='start container')
|
||||||
|
parser.add_argument(
|
||||||
|
'--attach',
|
||||||
|
'-a',
|
||||||
|
action=BooleanAction,
|
||||||
|
default=False,
|
||||||
|
help="Attach container's STDOUT and STDERR (default: %(default)s)")
|
||||||
|
parser.add_argument(
|
||||||
|
'--detach-keys',
|
||||||
|
metavar='KEY(s)',
|
||||||
|
default=4,
|
||||||
|
help='Override the key sequence for detaching a container.'
|
||||||
|
' (format: a single character [a-Z] or ctrl-<value> where'
|
||||||
|
' <value> is one of: a-z, @, ^, [, , or _) (default: ^D)')
|
||||||
|
parser.add_argument(
|
||||||
|
'--interactive',
|
||||||
|
'-i',
|
||||||
|
action=BooleanAction,
|
||||||
|
default=False,
|
||||||
|
help="Attach container's STDIN (default: %(default)s)")
|
||||||
|
# TODO: Implement sig-proxy
|
||||||
|
parser.add_argument(
|
||||||
|
'--sig-proxy',
|
||||||
|
action=BooleanAction,
|
||||||
|
default=False,
|
||||||
|
help="Proxy received signals to the process (default: %(default)s)"
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'containers',
|
||||||
|
nargs='+',
|
||||||
|
help='containers to start',
|
||||||
|
)
|
||||||
|
parser.set_defaults(class_=cls, method='start')
|
||||||
|
|
||||||
|
def start(self):
|
||||||
|
"""Start provided containers."""
|
||||||
|
stdin = sys.stdin if self.opts['interactive'] else None
|
||||||
|
stdout = sys.stdout if self.opts['attach'] else None
|
||||||
|
|
||||||
|
try:
|
||||||
|
for ident in self._args.containers:
|
||||||
|
try:
|
||||||
|
ctnr = self.client.containers.get(ident)
|
||||||
|
ctnr.attach(
|
||||||
|
eot=self.opts['detach_keys'],
|
||||||
|
stdin=stdin,
|
||||||
|
stdout=stdout)
|
||||||
|
ctnr.start()
|
||||||
|
except podman.ContainerNotFound as e:
|
||||||
|
sys.stdout.flush()
|
||||||
|
print(
|
||||||
|
'Container "{}" not found'.format(e.name),
|
||||||
|
file=sys.stderr,
|
||||||
|
flush=True)
|
||||||
|
else:
|
||||||
|
print(ident)
|
||||||
|
except podman.ErrorOccurred as e:
|
||||||
|
sys.stdout.flush()
|
||||||
|
print(
|
||||||
|
'{}'.format(e.reason).capitalize(),
|
||||||
|
file=sys.stderr,
|
||||||
|
flush=True)
|
||||||
|
return 1
|
@ -37,7 +37,7 @@ class BooleanAction(argparse.Action):
|
|||||||
const=None,
|
const=None,
|
||||||
default=None,
|
default=None,
|
||||||
type=None,
|
type=None,
|
||||||
choices=('True', 'False'),
|
choices=None,
|
||||||
required=False,
|
required=False,
|
||||||
help=None,
|
help=None,
|
||||||
metavar='{True,False}'):
|
metavar='{True,False}'):
|
||||||
@ -59,7 +59,7 @@ class BooleanAction(argparse.Action):
|
|||||||
try:
|
try:
|
||||||
val = BooleanValidate()(values)
|
val = BooleanValidate()(values)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
parser.error('{} must be True or False.'.format(self.dest))
|
parser.error('"{}" must be True or False.'.format(option_string))
|
||||||
else:
|
else:
|
||||||
setattr(namespace, self.dest, val)
|
setattr(namespace, self.dest, val)
|
||||||
|
|
||||||
@ -96,7 +96,6 @@ class ChangeAction(argparse.Action):
|
|||||||
|
|
||||||
def __call__(self, parser, namespace, values, option_string=None):
|
def __call__(self, parser, namespace, values, option_string=None):
|
||||||
"""Convert and Validate input."""
|
"""Convert and Validate input."""
|
||||||
print(self.dest)
|
|
||||||
items = getattr(namespace, self.dest, None) or []
|
items = getattr(namespace, self.dest, None) or []
|
||||||
items = copy.copy(items)
|
items = copy.copy(items)
|
||||||
|
|
||||||
@ -105,9 +104,9 @@ class ChangeAction(argparse.Action):
|
|||||||
|
|
||||||
opt, val = values.split('=', 1)
|
opt, val = values.split('=', 1)
|
||||||
if opt not in choices:
|
if opt not in choices:
|
||||||
parser.error('{} is not a supported "--change" option,'
|
parser.error('Option "{}" is not supported by argument "{}",'
|
||||||
' valid options are: {}'.format(
|
' valid options are: {}'.format(
|
||||||
opt, ', '.join(choices)))
|
opt, option_string, ', '.join(choices)))
|
||||||
items.append(values)
|
items.append(values)
|
||||||
setattr(namespace, self.dest, items)
|
setattr(namespace, self.dest, items)
|
||||||
|
|
||||||
@ -127,8 +126,8 @@ class UnitAction(argparse.Action):
|
|||||||
help=None,
|
help=None,
|
||||||
metavar='UNIT'):
|
metavar='UNIT'):
|
||||||
"""Create UnitAction object."""
|
"""Create UnitAction object."""
|
||||||
help = (help or metavar or dest
|
help = (help or metavar or dest)\
|
||||||
) + ' (format: <number>[<unit>], where unit = b, k, m or g)'
|
+ ' (format: <number>[<unit>], where unit = b, k, m or g)'
|
||||||
super().__init__(
|
super().__init__(
|
||||||
option_strings=option_strings,
|
option_strings=option_strings,
|
||||||
dest=dest,
|
dest=dest,
|
||||||
@ -148,15 +147,15 @@ class UnitAction(argparse.Action):
|
|||||||
except ValueError:
|
except ValueError:
|
||||||
if not values[:-1].isdigit():
|
if not values[:-1].isdigit():
|
||||||
msg = ('{} must be a positive integer,'
|
msg = ('{} must be a positive integer,'
|
||||||
' with optional suffix').format(self.dest)
|
' with optional suffix').format(option_string)
|
||||||
parser.error(msg)
|
parser.error(msg)
|
||||||
if not values[-1] in ('b', 'k', 'm', 'g'):
|
if not values[-1] in ('b', 'k', 'm', 'g'):
|
||||||
msg = '{} only supports suffices of: b, k, m, g'.format(
|
msg = '{} only supports suffices of: b, k, m, g'.format(
|
||||||
self.dest)
|
option_string)
|
||||||
parser.error(msg)
|
parser.error(msg)
|
||||||
else:
|
else:
|
||||||
if val <= 0:
|
if val <= 0:
|
||||||
msg = '{} must be a positive integer'.format(self.dest)
|
msg = '{} must be a positive integer'.format(option_string)
|
||||||
parser.error(msg)
|
parser.error(msg)
|
||||||
|
|
||||||
setattr(namespace, self.dest, values)
|
setattr(namespace, self.dest, values)
|
||||||
@ -174,19 +173,16 @@ class PositiveIntAction(argparse.Action):
|
|||||||
type=int,
|
type=int,
|
||||||
choices=None,
|
choices=None,
|
||||||
required=False,
|
required=False,
|
||||||
help=None,
|
help='Must be a positive integer.',
|
||||||
metavar=None):
|
metavar=None):
|
||||||
"""Create PositiveIntAction object."""
|
"""Create PositiveIntAction object."""
|
||||||
self.message = '{} must be a positive integer'.format(dest)
|
|
||||||
help = help or self.message
|
|
||||||
|
|
||||||
super().__init__(
|
super().__init__(
|
||||||
option_strings=option_strings,
|
option_strings=option_strings,
|
||||||
dest=dest,
|
dest=dest,
|
||||||
nargs=nargs,
|
nargs=nargs,
|
||||||
const=const,
|
const=const,
|
||||||
default=default,
|
default=default,
|
||||||
type=int,
|
type=type,
|
||||||
choices=choices,
|
choices=choices,
|
||||||
required=required,
|
required=required,
|
||||||
help=help,
|
help=help,
|
||||||
@ -198,7 +194,8 @@ class PositiveIntAction(argparse.Action):
|
|||||||
setattr(namespace, self.dest, values)
|
setattr(namespace, self.dest, values)
|
||||||
return
|
return
|
||||||
|
|
||||||
parser.error(self.message)
|
msg = '{} must be a positive integer'.format(option_string)
|
||||||
|
parser.error(msg)
|
||||||
|
|
||||||
|
|
||||||
class PathAction(argparse.Action):
|
class PathAction(argparse.Action):
|
||||||
|
Reference in New Issue
Block a user