summaryrefslogtreecommitdiff
path: root/contrib/python/pypodman
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/python/pypodman')
-rw-r--r--contrib/python/pypodman/Makefile2
-rw-r--r--contrib/python/pypodman/docs/man1/pypodman.12
-rw-r--r--contrib/python/pypodman/pypodman/lib/__init__.py5
-rw-r--r--contrib/python/pypodman/pypodman/lib/actions/__init__.py4
-rw-r--r--contrib/python/pypodman/pypodman/lib/actions/commit_action.py46
-rw-r--r--contrib/python/pypodman/pypodman/lib/actions/create_action.py2
-rw-r--r--contrib/python/pypodman/pypodman/lib/actions/export_action.py22
-rw-r--r--contrib/python/pypodman/pypodman/lib/actions/history_action.py2
-rw-r--r--contrib/python/pypodman/pypodman/lib/actions/images_action.py8
-rw-r--r--contrib/python/pypodman/pypodman/lib/actions/import_action.py41
-rw-r--r--contrib/python/pypodman/pypodman/lib/actions/inspect_action.py14
-rw-r--r--contrib/python/pypodman/pypodman/lib/actions/ps_action.py19
-rw-r--r--contrib/python/pypodman/pypodman/lib/actions/run_action.py2
-rw-r--r--contrib/python/pypodman/pypodman/lib/actions/start_action.py76
-rw-r--r--contrib/python/pypodman/pypodman/lib/actions/version_action.py41
-rw-r--r--contrib/python/pypodman/pypodman/lib/parser_actions.py74
-rw-r--r--contrib/python/pypodman/pypodman/lib/podman_parser.py17
-rw-r--r--contrib/python/pypodman/pypodman/lib/report.py29
18 files changed, 304 insertions, 102 deletions
diff --git a/contrib/python/pypodman/Makefile b/contrib/python/pypodman/Makefile
index cd0fcf1de..272145c5d 100644
--- a/contrib/python/pypodman/Makefile
+++ b/contrib/python/pypodman/Makefile
@@ -4,6 +4,7 @@ PODMAN_VERSION ?= '0.0.4'
.PHONY: python-pypodman
python-pypodman:
+ PODMAN_VERSION=$(PODMAN_VERSION) \
$(PYTHON) setup.py sdist bdist
.PHONY: lint
@@ -16,6 +17,7 @@ integration:
.PHONY: install
install:
+ PODMAN_VERSION=$(PODMAN_VERSION) \
$(PYTHON) setup.py install --root ${DESTDIR}
.PHONY: upload
diff --git a/contrib/python/pypodman/docs/man1/pypodman.1 b/contrib/python/pypodman/docs/man1/pypodman.1
index 09acb205b..45472dab0 100644
--- a/contrib/python/pypodman/docs/man1/pypodman.1
+++ b/contrib/python/pypodman/docs/man1/pypodman.1
@@ -85,7 +85,7 @@ overwriting earlier. Any missing items are ignored.
.IP \[bu] 2
From \f[C]\-\-config\-home\f[] command line option + \f[C]pypodman/pypodman.conf\f[]
.IP \[bu] 2
-From environment variable, for example: RUN_DIR
+From environment variable prefixed with PODMAN_, for example: PODMAN_RUN_DIR
.IP \[bu] 2
From command line option, for example: \[en]run\-dir
.PP
diff --git a/contrib/python/pypodman/pypodman/lib/__init__.py b/contrib/python/pypodman/pypodman/lib/__init__.py
index 5525ddaef..be1b5f467 100644
--- a/contrib/python/pypodman/pypodman/lib/__init__.py
+++ b/contrib/python/pypodman/pypodman/lib/__init__.py
@@ -4,14 +4,15 @@ import sys
import podman
from pypodman.lib.action_base import AbstractActionBase
from pypodman.lib.parser_actions import (BooleanAction, BooleanValidate,
- PathAction, PositiveIntAction,
- UnitAction)
+ ChangeAction, PathAction,
+ PositiveIntAction, UnitAction)
from pypodman.lib.podman_parser import PodmanArgumentParser
from pypodman.lib.report import Report, ReportColumn
# Silence pylint overlording...
assert BooleanAction
assert BooleanValidate
+assert ChangeAction
assert PathAction
assert PositiveIntAction
assert UnitAction
diff --git a/contrib/python/pypodman/pypodman/lib/actions/__init__.py b/contrib/python/pypodman/pypodman/lib/actions/__init__.py
index 2668cd8ff..c0d77ddb1 100644
--- a/contrib/python/pypodman/pypodman/lib/actions/__init__.py
+++ b/contrib/python/pypodman/pypodman/lib/actions/__init__.py
@@ -22,6 +22,8 @@ from pypodman.lib.actions.rm_action import Rm
from pypodman.lib.actions.rmi_action import Rmi
from pypodman.lib.actions.run_action import Run
from pypodman.lib.actions.search_action import Search
+from pypodman.lib.actions.start_action import Start
+from pypodman.lib.actions.version_action import Version
__all__ = [
'Attach',
@@ -47,4 +49,6 @@ __all__ = [
'Rmi',
'Run',
'Search',
+ 'Start',
+ 'Version',
]
diff --git a/contrib/python/pypodman/pypodman/lib/actions/commit_action.py b/contrib/python/pypodman/pypodman/lib/actions/commit_action.py
index 0da6a2078..21924e938 100644
--- a/contrib/python/pypodman/pypodman/lib/actions/commit_action.py
+++ b/contrib/python/pypodman/pypodman/lib/actions/commit_action.py
@@ -2,7 +2,7 @@
import sys
import podman
-from pypodman.lib import AbstractActionBase, BooleanAction
+from pypodman.lib import AbstractActionBase, BooleanAction, ChangeAction
class Commit(AbstractActionBase):
@@ -12,7 +12,9 @@ class Commit(AbstractActionBase):
def subparser(cls, parent):
"""Add Commit command to parent parser."""
parser = parent.add_parser(
- 'commit', help='create image from container')
+ 'commit',
+ help='create image from container',
+ )
parser.add_argument(
'--author',
help='Set the author for the committed image',
@@ -20,11 +22,7 @@ class Commit(AbstractActionBase):
parser.add_argument(
'--change',
'-c',
- choices=('CMD', 'ENTRYPOINT', 'ENV', 'EXPOSE', 'LABEL', 'ONBUILD',
- 'STOPSIGNAL', 'USER', 'VOLUME', 'WORKDIR'),
- action='append',
- type=str.upper,
- help='Apply the following possible changes to the created image',
+ action=ChangeAction,
)
parser.add_argument(
'--format',
@@ -32,7 +30,8 @@ class Commit(AbstractActionBase):
choices=('oci', 'docker'),
default='oci',
type=str.lower,
- help='Set the format of the image manifest and metadata',
+ help='Set the format of the image manifest and metadata.'
+ ' (Ignored.)',
)
parser.add_argument(
'--iidfile',
@@ -42,7 +41,8 @@ class Commit(AbstractActionBase):
parser.add_argument(
'--message',
'-m',
- help='Set commit message for committed image',
+ help='Set commit message for committed image'
+ ' (Only on docker images.)',
)
parser.add_argument(
'--pause',
@@ -69,27 +69,11 @@ class Commit(AbstractActionBase):
)
parser.set_defaults(class_=cls, method='commit')
- def __init__(self, args):
- """Construct Commit class."""
- if not args.container:
- raise ValueError('You must supply one container id'
- ' or name to be used as source.')
- if not args.image:
- raise ValueError('You must supply one image id'
- ' or name to be created.')
- super().__init__(args)
-
- # used only on client
- del self.opts['image']
- del self.opts['container']
-
def commit(self):
"""Create image from container."""
try:
try:
ctnr = self.client.containers.get(self._args.container[0])
- ident = ctnr.commit(**self.opts)
- print(ident)
except podman.ContainerNotFound as e:
sys.stdout.flush()
print(
@@ -97,6 +81,17 @@ class Commit(AbstractActionBase):
file=sys.stderr,
flush=True)
return 1
+ else:
+ ident = ctnr.commit(
+ self.opts['image'][0],
+ change=self.opts.get('change', None),
+ message=self.opts.get('message', None),
+ pause=self.opts['pause'],
+ author=self.opts.get('author', None),
+ )
+
+ if not self.opts['quiet']:
+ print(ident)
except podman.ErrorOccurred as e:
sys.stdout.flush()
print(
@@ -104,3 +99,4 @@ class Commit(AbstractActionBase):
file=sys.stderr,
flush=True)
return 1
+ return 0
diff --git a/contrib/python/pypodman/pypodman/lib/actions/create_action.py b/contrib/python/pypodman/pypodman/lib/actions/create_action.py
index d9631763a..26a312bb1 100644
--- a/contrib/python/pypodman/pypodman/lib/actions/create_action.py
+++ b/contrib/python/pypodman/pypodman/lib/actions/create_action.py
@@ -21,7 +21,7 @@ class Create(AbstractActionBase):
parser.add_argument('image', nargs=1, help='source image id')
parser.add_argument(
'command',
- nargs='*',
+ nargs=parent.REMAINDER,
help='command and args to run.',
)
parser.set_defaults(class_=cls, method='create')
diff --git a/contrib/python/pypodman/pypodman/lib/actions/export_action.py b/contrib/python/pypodman/pypodman/lib/actions/export_action.py
index f62cd3535..7ef178c4c 100644
--- a/contrib/python/pypodman/pypodman/lib/actions/export_action.py
+++ b/contrib/python/pypodman/pypodman/lib/actions/export_action.py
@@ -12,13 +12,16 @@ class Export(AbstractActionBase):
def subparser(cls, parent):
"""Add Export command to parent parser."""
parser = parent.add_parser(
- 'export', help='export container to tarball')
+ 'export',
+ help='export container to tarball',
+ )
parser.add_argument(
'--output',
'-o',
metavar='PATH',
nargs=1,
- help='Write to a file',
+ required=True,
+ help='Write to this file on host',
)
parser.add_argument(
'container',
@@ -27,23 +30,11 @@ class Export(AbstractActionBase):
)
parser.set_defaults(class_=cls, method='export')
- def __init__(self, args):
- """Construct Export class."""
- if not args.container:
- raise ValueError('You must supply one container id'
- ' or name to be used as source.')
-
- if not args.output:
- raise ValueError('You must supply one filename'
- ' to be created as tarball using --output.')
- super().__init__(args)
-
def export(self):
"""Create tarball from container filesystem."""
try:
try:
ctnr = self.client.containers.get(self._args.container[0])
- ctnr.export(self._args.output[0])
except podman.ContainerNotFound as e:
sys.stdout.flush()
print(
@@ -51,6 +42,8 @@ class Export(AbstractActionBase):
file=sys.stderr,
flush=True)
return 1
+ else:
+ ctnr.export(self._args.output[0])
except podman.ErrorOccurred as e:
sys.stdout.flush()
print(
@@ -58,3 +51,4 @@ class Export(AbstractActionBase):
file=sys.stderr,
flush=True)
return 1
+ return 0
diff --git a/contrib/python/pypodman/pypodman/lib/actions/history_action.py b/contrib/python/pypodman/pypodman/lib/actions/history_action.py
index 3e3f539fc..f9aaa54f6 100644
--- a/contrib/python/pypodman/pypodman/lib/actions/history_action.py
+++ b/contrib/python/pypodman/pypodman/lib/actions/history_action.py
@@ -60,7 +60,7 @@ class History(AbstractActionBase):
if self._args.human:
fields.update({
'size':
- humanize.naturalsize(details.size, binary=True),
+ humanize.naturalsize(details.size),
'created':
humanize.naturaldate(
podman.datetime_parse(details.created)),
diff --git a/contrib/python/pypodman/pypodman/lib/actions/images_action.py b/contrib/python/pypodman/pypodman/lib/actions/images_action.py
index d28e32db9..29bf90dd2 100644
--- a/contrib/python/pypodman/pypodman/lib/actions/images_action.py
+++ b/contrib/python/pypodman/pypodman/lib/actions/images_action.py
@@ -37,7 +37,7 @@ class Images(AbstractActionBase):
self.columns = OrderedDict({
'name':
- ReportColumn('name', 'REPOSITORY', 40),
+ ReportColumn('name', 'REPOSITORY', 0),
'tag':
ReportColumn('tag', 'TAG', 10),
'id':
@@ -65,18 +65,18 @@ class Images(AbstractActionBase):
'created':
humanize.naturaldate(podman.datetime_parse(image.created)),
'size':
- humanize.naturalsize(int(image.size), binary=True),
+ humanize.naturalsize(int(image.size)),
'repoDigests':
' '.join(image.repoDigests),
})
for r in image.repoTags:
- name, tag = r.split(':', 1)
+ name, tag = r.rsplit(':', 1)
fields.update({
'name': name,
'tag': tag,
})
- rows.append(fields)
+ rows.append(fields)
if not self._args.digests:
del self.columns['repoDigests']
diff --git a/contrib/python/pypodman/pypodman/lib/actions/import_action.py b/contrib/python/pypodman/pypodman/lib/actions/import_action.py
index 49b8a5a57..43448144a 100644
--- a/contrib/python/pypodman/pypodman/lib/actions/import_action.py
+++ b/contrib/python/pypodman/pypodman/lib/actions/import_action.py
@@ -2,7 +2,7 @@
import sys
import podman
-from pypodman.lib import AbstractActionBase
+from pypodman.lib import AbstractActionBase, ChangeAction
class Import(AbstractActionBase):
@@ -12,18 +12,19 @@ class Import(AbstractActionBase):
def subparser(cls, parent):
"""Add Import command to parent parser."""
parser = parent.add_parser(
- 'import', help='import tarball as image filesystem')
+ 'import',
+ help='import tarball as image filesystem',
+ )
parser.add_argument(
'--change',
'-c',
- action='append',
- choices=('CMD', 'ENTRYPOINT', 'ENV', 'EXPOSE', 'LABEL',
- 'STOPSIGNAL', 'USER', 'VOLUME', 'WORKDIR'),
- type=str.upper,
- help='Apply the following possible instructions',
+ action=ChangeAction,
)
parser.add_argument(
- '--message', '-m', help='Set commit message for imported image.')
+ '--message',
+ '-m',
+ help='Set commit message for imported image.',
+ )
parser.add_argument(
'source',
metavar='PATH',
@@ -38,18 +39,25 @@ class Import(AbstractActionBase):
)
parser.set_defaults(class_=cls, method='import_')
- def __init__(self, args):
- """Construct Import class."""
- super().__init__(args)
-
def import_(self):
"""Import tarball as image filesystem."""
+ # ImportImage() validates it's parameters therefore we need to create
+ # pristine dict() for keywords
+ options = {}
+ if 'message' in self.opts:
+ options['message'] = self.opts['message']
+ if 'change' in self.opts and self.opts['change']:
+ options['changes'] = self.opts['change']
+
+ reference = self.opts['reference'][0] if 'reference' in self.opts\
+ else None
+
try:
ident = self.client.images.import_image(
- self.opts.source,
- self.opts.reference,
- message=self.opts.message,
- changes=self.opts.change)
+ self.opts['source'][0],
+ reference,
+ **options,
+ )
print(ident)
except podman.ErrorOccurred as e:
sys.stdout.flush()
@@ -58,3 +66,4 @@ class Import(AbstractActionBase):
file=sys.stderr,
flush=True)
return 1
+ return 0
diff --git a/contrib/python/pypodman/pypodman/lib/actions/inspect_action.py b/contrib/python/pypodman/pypodman/lib/actions/inspect_action.py
index 514b4702a..a581e7e4e 100644
--- a/contrib/python/pypodman/pypodman/lib/actions/inspect_action.py
+++ b/contrib/python/pypodman/pypodman/lib/actions/inspect_action.py
@@ -23,9 +23,9 @@ class Inspect(AbstractActionBase):
help='Type of object to inspect',
)
parser.add_argument(
- 'size',
+ '--size',
action='store_true',
- default=True,
+ default=False,
help='Display the total file size if the type is a container.'
' Always True.')
parser.add_argument(
@@ -59,7 +59,7 @@ class Inspect(AbstractActionBase):
def inspect(self):
"""Inspect provided podman objects."""
- output = {}
+ output = []
try:
for ident in self._args.objects:
obj = None
@@ -78,7 +78,13 @@ class Inspect(AbstractActionBase):
msg = 'Object "{}" not found'.format(ident)
print(msg, file=sys.stderr, flush=True)
else:
- output.update(obj._asdict())
+ fields = obj._asdict()
+ if not self._args.size:
+ try:
+ del fields['sizerootfs']
+ except KeyError:
+ pass
+ output.append(fields)
except podman.ErrorOccurred as e:
sys.stdout.flush()
print(
diff --git a/contrib/python/pypodman/pypodman/lib/actions/ps_action.py b/contrib/python/pypodman/pypodman/lib/actions/ps_action.py
index cd7a7947d..62ceb2e67 100644
--- a/contrib/python/pypodman/pypodman/lib/actions/ps_action.py
+++ b/contrib/python/pypodman/pypodman/lib/actions/ps_action.py
@@ -16,6 +16,7 @@ class Ps(AbstractActionBase):
"""Add Images command to parent parser."""
parser = parent.add_parser('ps', help='list containers')
super().subparser(parser)
+
parser.add_argument(
'--sort',
choices=('createdat', 'id', 'image', 'names', 'runningfor', 'size',
@@ -32,9 +33,9 @@ class Ps(AbstractActionBase):
self.columns = OrderedDict({
'id':
- ReportColumn('id', 'CONTAINER ID', 14),
+ ReportColumn('id', 'CONTAINER ID', 12),
'image':
- ReportColumn('image', 'IMAGE', 30),
+ ReportColumn('image', 'IMAGE', 31),
'command':
ReportColumn('column', 'COMMAND', 20),
'createdat':
@@ -49,10 +50,15 @@ class Ps(AbstractActionBase):
def list(self):
"""List containers."""
+ if self._args.all:
+ ictnrs = self.client.containers.list()
+ else:
+ ictnrs = filter(
+ lambda c: podman.FoldedString(c['status']) == 'running',
+ self.client.containers.list())
+
# TODO: Verify sorting on dates and size
- ctnrs = sorted(
- self.client.containers.list(),
- key=operator.attrgetter(self._args.sort))
+ ctnrs = sorted(ictnrs, key=operator.attrgetter(self._args.sort))
if not ctnrs:
return
@@ -65,9 +71,6 @@ class Ps(AbstractActionBase):
'createdat':
humanize.naturaldate(podman.datetime_parse(ctnr.createdat)),
})
-
- if self._args.truncate:
- fields.update({'image': ctnr.image[-30:]})
rows.append(fields)
with Report(self.columns, heading=self._args.heading) as report:
diff --git a/contrib/python/pypodman/pypodman/lib/actions/run_action.py b/contrib/python/pypodman/pypodman/lib/actions/run_action.py
index a63eb7917..6a6b3cb2c 100644
--- a/contrib/python/pypodman/pypodman/lib/actions/run_action.py
+++ b/contrib/python/pypodman/pypodman/lib/actions/run_action.py
@@ -21,7 +21,7 @@ class Run(AbstractActionBase):
parser.add_argument('image', nargs=1, help='source image id.')
parser.add_argument(
'command',
- nargs='*',
+ nargs=parent.REMAINDER,
help='command and args to run.',
)
parser.set_defaults(class_=cls, method='run')
diff --git a/contrib/python/pypodman/pypodman/lib/actions/start_action.py b/contrib/python/pypodman/pypodman/lib/actions/start_action.py
new file mode 100644
index 000000000..f312fb3fa
--- /dev/null
+++ b/contrib/python/pypodman/pypodman/lib/actions/start_action.py
@@ -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
diff --git a/contrib/python/pypodman/pypodman/lib/actions/version_action.py b/contrib/python/pypodman/pypodman/lib/actions/version_action.py
new file mode 100644
index 000000000..12b6dc576
--- /dev/null
+++ b/contrib/python/pypodman/pypodman/lib/actions/version_action.py
@@ -0,0 +1,41 @@
+"""Remote client command for reporting on Podman service."""
+import json
+import sys
+
+import podman
+import yaml
+from pypodman.lib import AbstractActionBase
+
+
+class Version(AbstractActionBase):
+ """Class for reporting on Podman Service."""
+
+ @classmethod
+ def subparser(cls, parent):
+ """Add Version command to parent parser."""
+ parser = parent.add_parser(
+ 'version', help='report version on podman service')
+ parser.set_defaults(class_=cls, method='version')
+
+ def __init__(self, args):
+ """Construct Version class."""
+ super().__init__(args)
+
+ def version(self):
+ """Report on Podman Service."""
+ try:
+ info = self.client.system.info()
+ except podman.ErrorOccurred as e:
+ sys.stdout.flush()
+ print(
+ '{}'.format(e.reason).capitalize(),
+ file=sys.stderr,
+ flush=True)
+ return 1
+ else:
+ version = info._asdict()['podman']
+ host = info._asdict()['host']
+ print("Version {}".format(version['podman_version']))
+ print("Go Version {}".format(version['go_version']))
+ print("Git Commit {}".format(version['git_commit']))
+ print("OS/Arch {}/{}".format(host["os"], host["arch"]))
diff --git a/contrib/python/pypodman/pypodman/lib/parser_actions.py b/contrib/python/pypodman/pypodman/lib/parser_actions.py
index 2a5859e47..77ee14761 100644
--- a/contrib/python/pypodman/pypodman/lib/parser_actions.py
+++ b/contrib/python/pypodman/pypodman/lib/parser_actions.py
@@ -4,9 +4,10 @@ Supplimental argparse.Action converters and validaters.
The constructors are very verbose but remain for IDE support.
"""
import argparse
+import copy
import os
-# API defined by argparse.Action shut up pylint
+# API defined by argparse.Action therefore shut up pylint
# pragma pylint: disable=redefined-builtin
# pragma pylint: disable=too-few-public-methods
# pragma pylint: disable=too-many-arguments
@@ -36,7 +37,7 @@ class BooleanAction(argparse.Action):
const=None,
default=None,
type=None,
- choices=('True', 'False'),
+ choices=None,
required=False,
help=None,
metavar='{True,False}'):
@@ -58,11 +59,58 @@ class BooleanAction(argparse.Action):
try:
val = BooleanValidate()(values)
except ValueError:
- parser.error('{} must be True or False.'.format(self.dest))
+ 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'):
+ """Create ChangeAction object."""
+ help = (help or '') + ('Apply change(s) to the new image.'
+ ' May be given multiple times.')
+
+ 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."""
+ items = getattr(namespace, self.dest, None) or []
+ items = copy.copy(items)
+
+ choices = ('CMD', 'ENTRYPOINT', 'ENV', 'EXPOSE', 'LABEL', 'ONBUILD',
+ 'STOPSIGNAL', 'USER', 'VOLUME', 'WORKDIR')
+
+ opt, val = values.split('=', 1)
+ if opt not in choices:
+ parser.error('Option "{}" is not supported by argument "{}",'
+ ' valid options are: {}'.format(
+ opt, option_string, ', '.join(choices)))
+ items.append(values)
+ setattr(namespace, self.dest, items)
+
+
class UnitAction(argparse.Action):
"""Validate number given is positive integer, with optional suffix."""
@@ -78,8 +126,8 @@ class UnitAction(argparse.Action):
help=None,
metavar='UNIT'):
"""Create UnitAction object."""
- help = (help or metavar or dest
- ) + ' (format: <number>[<unit>], where unit = b, k, m or g)'
+ help = (help or metavar or dest)\
+ + ' (format: <number>[<unit>], where unit = b, k, m or g)'
super().__init__(
option_strings=option_strings,
dest=dest,
@@ -99,15 +147,15 @@ class UnitAction(argparse.Action):
except ValueError:
if not values[:-1].isdigit():
msg = ('{} must be a positive integer,'
- ' with optional suffix').format(self.dest)
+ ' with optional suffix').format(option_string)
parser.error(msg)
if not values[-1] in ('b', 'k', 'm', 'g'):
msg = '{} only supports suffices of: b, k, m, g'.format(
- self.dest)
+ option_string)
parser.error(msg)
else:
if val <= 0:
- msg = '{} must be a positive integer'.format(self.dest)
+ msg = '{} must be a positive integer'.format(option_string)
parser.error(msg)
setattr(namespace, self.dest, values)
@@ -125,19 +173,16 @@ class PositiveIntAction(argparse.Action):
type=int,
choices=None,
required=False,
- help=None,
+ help='Must be a positive integer.',
metavar=None):
"""Create PositiveIntAction object."""
- self.message = '{} must be a positive integer'.format(dest)
- help = help or self.message
-
super().__init__(
option_strings=option_strings,
dest=dest,
nargs=nargs,
const=const,
default=default,
- type=int,
+ type=type,
choices=choices,
required=required,
help=help,
@@ -149,7 +194,8 @@ class PositiveIntAction(argparse.Action):
setattr(namespace, self.dest, values)
return
- parser.error(self.message)
+ msg = '{} must be a positive integer'.format(option_string)
+ parser.error(msg)
class PathAction(argparse.Action):
diff --git a/contrib/python/pypodman/pypodman/lib/podman_parser.py b/contrib/python/pypodman/pypodman/lib/podman_parser.py
index d3c84224f..412c8c8fd 100644
--- a/contrib/python/pypodman/pypodman/lib/podman_parser.py
+++ b/contrib/python/pypodman/pypodman/lib/podman_parser.py
@@ -97,6 +97,8 @@ class PodmanArgumentParser(argparse.ArgumentParser):
actions_parser = self.add_subparsers(
dest='subparser_name', help='commands')
+ # For create/exec/run: don't process options intended for subcommand
+ actions_parser.REMAINDER = argparse.REMAINDER
# import buried here to prevent import loops
import pypodman.lib.actions # pylint: disable=cyclic-import
@@ -152,7 +154,7 @@ class PodmanArgumentParser(argparse.ArgumentParser):
reqattr(
'run_dir',
getattr(args, 'run_dir')
- or os.environ.get('RUN_DIR')
+ or os.environ.get('PODMAN_RUN_DIR')
or config['default'].get('run_dir')
or str(Path(args.xdg_runtime_dir, 'pypodman'))
) # yapf: disable
@@ -161,23 +163,24 @@ class PodmanArgumentParser(argparse.ArgumentParser):
args,
'host',
getattr(args, 'host')
- or os.environ.get('HOST')
+ or os.environ.get('PODMAN_HOST')
or config['default'].get('host')
) # yapf:disable
reqattr(
'username',
getattr(args, 'username')
+ or os.environ.get('PODMAN_USER')
+ or config['default'].get('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 os.environ.get('PODMAN_PORT')
or config['default'].get('port', None)
or 22
) # yapf:disable
@@ -185,7 +188,7 @@ class PodmanArgumentParser(argparse.ArgumentParser):
reqattr(
'remote_socket_path',
getattr(args, 'remote_socket_path')
- or os.environ.get('REMOTE_SOCKET_PATH')
+ or os.environ.get('PODMAN_REMOTE_SOCKET_PATH')
or config['default'].get('remote_socket_path')
or '/run/podman/io.podman'
) # yapf:disable
@@ -193,7 +196,7 @@ class PodmanArgumentParser(argparse.ArgumentParser):
reqattr(
'log_level',
getattr(args, 'log_level')
- or os.environ.get('LOG_LEVEL')
+ or os.environ.get('PODMAN_LOG_LEVEL')
or config['default'].get('log_level')
or logging.WARNING
) # yapf:disable
@@ -202,7 +205,7 @@ class PodmanArgumentParser(argparse.ArgumentParser):
args,
'identity_file',
getattr(args, 'identity_file')
- or os.environ.get('IDENTITY_FILE')
+ or os.environ.get('PODMAN_IDENTITY_FILE')
or config['default'].get('identity_file')
or os.path.expanduser('~{}/.ssh/id_dsa'.format(args.username))
) # yapf:disable
diff --git a/contrib/python/pypodman/pypodman/lib/report.py b/contrib/python/pypodman/pypodman/lib/report.py
index 1db4268da..b689390fd 100644
--- a/contrib/python/pypodman/pypodman/lib/report.py
+++ b/contrib/python/pypodman/pypodman/lib/report.py
@@ -1,8 +1,23 @@
"""Report Manager."""
+import string
import sys
from collections import namedtuple
+class ReportFormatter(string.Formatter):
+ """Custom formatter to default missing keys to '<none>'."""
+
+ def get_value(self, key, args, kwargs):
+ """Map missing key to value '<none>'."""
+ try:
+ if isinstance(key, int):
+ return args[key]
+ else:
+ return kwargs[key]
+ except KeyError:
+ return '<none>'
+
+
class ReportColumn(namedtuple('ReportColumn', 'key display width default')):
"""Hold attributes of output column."""
@@ -26,18 +41,24 @@ class Report():
"""
self._columns = columns
self._file = file
+ self._format_string = None
+ self._formatter = ReportFormatter()
self._heading = heading
self.epilog = epilog
- self._format = None
def row(self, **fields):
"""Print row for report."""
if self._heading:
hdrs = {k: v.display for (k, v) in self._columns.items()}
- print(self._format.format(**hdrs), flush=True, file=self._file)
+ print(
+ self._formatter.format(self._format_string, **hdrs),
+ flush=True,
+ file=self._file,
+ )
self._heading = False
+
fields = {k: str(v) for k, v in fields.items()}
- print(self._format.format(**fields))
+ print(self._formatter.format(self._format_string, **fields))
def __enter__(self):
"""Return `self` upon entering the runtime context."""
@@ -63,4 +84,4 @@ class Report():
display_len = info.width
fmt.append('{{{0}:{1}.{1}}}'.format(key, display_len))
- self._format = ' '.join(fmt)
+ self._format_string = ' '.join(fmt)