summaryrefslogtreecommitdiff
path: root/contrib
diff options
context:
space:
mode:
Diffstat (limited to 'contrib')
-rw-r--r--contrib/cirrus/README.md82
-rwxr-xr-xcontrib/cirrus/build_vm_images.sh4
-rw-r--r--contrib/cirrus/lib.sh46
-rw-r--r--contrib/cirrus/packer/fedora_setup.sh3
-rw-r--r--contrib/cirrus/packer/ubuntu_setup.sh11
-rwxr-xr-xcontrib/cirrus/setup_environment.sh2
-rwxr-xr-xcontrib/cirrus/system_test.sh33
-rw-r--r--contrib/gate/Dockerfile69
-rw-r--r--contrib/gate/README.md4
-rwxr-xr-xcontrib/gate/entrypoint.sh15
-rw-r--r--contrib/python/podman/Makefile2
-rw-r--r--contrib/python/podman/podman/libs/_containers_attach.py8
-rw-r--r--contrib/python/podman/podman/libs/containers.py83
-rw-r--r--contrib/python/podman/podman/libs/images.py9
-rw-r--r--contrib/python/podman/test/test_containers.py2
-rw-r--r--contrib/python/podman/test/test_images.py2
-rw-r--r--contrib/python/podman/test/test_pods_ctnrs.py3
-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
-rw-r--r--contrib/spec/podman.spec.in12
36 files changed, 604 insertions, 192 deletions
diff --git a/contrib/cirrus/README.md b/contrib/cirrus/README.md
new file mode 100644
index 000000000..fa233a2cb
--- /dev/null
+++ b/contrib/cirrus/README.md
@@ -0,0 +1,82 @@
+![PODMAN logo](../../logo/podman-logo-source.svg)
+
+# Cirrus-CI
+
+Similar to other integrated github CI/CD services, Cirrus utilizes a simple
+YAML-based configuration/description file: ``.cirrus.yml``. Ref: https://cirrus-ci.org/
+
+## Workflow
+
+All tasks execute in parallel, unless there are conditions or dependencies
+which alter this behavior. Within each task, each script executes in sequence,
+so long as any previous script exited successfully. The overall state of each
+task (pass or fail) is set based on the exit status of the last script to execute.
+
+### ``full_vm_testing`` Task
+
+1. Unconditionally, spin up one VM per ``matrix: image_name`` item defined
+ in ``.cirrus.yml``. Once accessible, ``ssh`` into each VM and run the following
+ scripts.
+
+2. ``setup_environment.sh``: Configure root's ``.bash_profile``
+ for all subsequent scripts (each run in a new shell). Any
+ distribution-specific environment variables are also defined
+ here. For example, setting tags/flags to use compiling.
+
+3. ``verify_source.sh``: Perform per-distribution source
+ verification, lint-checking, etc. This acts as a minimal
+ gate, blocking extended use of VMs when a PR's code or commits
+ would otherwise not be accepted. Should run for less than a minute.
+
+4. ``unit_test.sh``: Execute unit-testing, as defined by the ``Makefile``.
+ This should execute within 10-minutes, but often much faster.
+
+5. ``integration_test.sh``: Execute integration-testing. This is
+ much more involved, and relies on access to external
+ resources like container images and code from other repositories.
+ Total execution time is capped at 2-hours (includes all the above)
+ but this script normally completes in less than an hour.
+
+### ``optional_system_testing`` Task
+
+1. Optionally executes in parallel with ``full_vm_testing``. Requires
+ **prior** to job-start, the magic string ``***CIRRUS: SYSTEM TEST***``
+ is found in the pull-request *description*. The *description* is the first
+ text-box under the main *summary* line in the github WebUI.
+
+2. ``setup_environment.sh``: Same as for other tasks.
+
+3. ``system_test.sh``: Build both dependencies and libpod, install them,
+ then execute `make localsystem` from the repository root.
+
+### ``build_vm_images`` Task
+
+1. When a PR is merged (``$CIRRUS_BRANCH`` == ``master``), Cirrus
+ checks the last commit message. If it contains the magic string
+ ``***CIRRUS: REBUILD IMAGES***``, then this task continues.
+
+2. Execute run another round of the ``full_vm_testing`` task (above).
+ After the tests pass (post-merge), spin up a special VM
+ (from the `image-builder-image`) capable of communicating with the
+ GCE API. Once accessible, ``ssh`` into the VM and run the following scripts.
+
+3. ``setup_environment.sh``: Same as for other tasks.
+
+4. ``build_vm_images.sh``: Utilize [the packer tool](http://packer.io/docs/)
+ to produce new VM images. Create a new VM from each base-image, connect
+ to them with ``ssh``, and perform the steps as defined by the
+ ``$PACKER_BASE/libpod_images.json`` file:
+
+ 1. On a base-image VM, as root, copy the current state of the repository
+ into ``/tmp/libpod``.
+ 2. Execute distribution-specific scripts to prepare the image for
+ use by the ``full_vm_testing`` task (above). These scripts all
+ end with the suffix `_setup.sh` within the `$PACKER_BASE` directory.
+ 3. If successful, shut down each VM and create a new GCE Image
+ named after the base image and the commit sha of the merge.
+
+***Note:*** The ``.cirrus.yml`` file must be manually updated with the new
+images names, then the change sent in via a secondary pull-request. This
+ensures that all the ``full_vm_testing`` tasks can pass with the new images,
+before subjecting all future PRs to them. A workflow to automate this
+process is described in comments at the end of the ``.cirrus.yml`` file.
diff --git a/contrib/cirrus/build_vm_images.sh b/contrib/cirrus/build_vm_images.sh
index 80c689a6c..c8ff55445 100755
--- a/contrib/cirrus/build_vm_images.sh
+++ b/contrib/cirrus/build_vm_images.sh
@@ -22,10 +22,6 @@ SCRIPT_BASE $SCRIPT_BASE
PACKER_BASE $PACKER_BASE
"
-# TODO: Skip building images if $CIRRUS_BRANCH =~ "master" and
-# commit message of $CIRRUS_CHANGE_IN_REPO contains a magic word
-# produced by 'commit_and_create_upstream_pr.sh' script (see .cirrus.yml)
-
show_env_vars
# Everything here is running on the 'image-builder-image' GCE image
diff --git a/contrib/cirrus/lib.sh b/contrib/cirrus/lib.sh
index 1e0052a65..04314e5fe 100644
--- a/contrib/cirrus/lib.sh
+++ b/contrib/cirrus/lib.sh
@@ -106,7 +106,10 @@ ircmsg() {
SCRIPT="$GOSRC/$SCRIPT_BASE/podbot.py"
NICK="podbot_$CIRRUS_TASK_ID"
NICK="${NICK:0:15}" # Any longer will break things
+ set +e
$SCRIPT $NICK $1
+ echo "Ignoring exit($?)"
+ set -e
}
# Run sudo in directory with GOPATH set
@@ -117,7 +120,6 @@ cdsudo() {
sudo --preserve-env=GOPATH --non-interactive bash -c "$CMD"
}
-
# Helper/wrapper script to only show stderr/stdout on non-zero exit
install_ooe() {
req_env_var "SCRIPT_BASE $SCRIPT_BASE"
@@ -142,8 +144,8 @@ EOF
install_cni_plugins() {
echo "Installing CNI Plugins from commit $CNI_COMMIT"
req_env_var "
- GOPATH $GOPATH
- CNI_COMMIT $CNI_COMMIT
+ GOPATH $GOPATH
+ CNI_COMMIT $CNI_COMMIT
"
DEST="$GOPATH/src/github.com/containernetworking/plugins"
rm -rf "$DEST"
@@ -155,14 +157,27 @@ install_cni_plugins() {
sudo cp bin/* /usr/libexec/cni
}
+install_runc_from_git(){
+ wd=$(pwd)
+ DEST="$GOPATH/src/github.com/opencontainers/runc"
+ rm -rf "$DEST"
+ ooe.sh git clone https://github.com/opencontainers/runc.git "$DEST"
+ cd "$DEST"
+ ooe.sh git fetch origin --tags
+ ooe.sh git checkout -q "$RUNC_COMMIT"
+ ooe.sh make static BUILDTAGS="seccomp selinux"
+ sudo install -m 755 runc /usr/bin/runc
+ cd $wd
+}
+
install_runc(){
OS_RELEASE_ID=$(os_release_id)
echo "Installing RunC from commit $RUNC_COMMIT"
echo "Platform is $OS_RELEASE_ID"
req_env_var "
- GOPATH $GOPATH
- RUNC_COMMIT $RUNC_COMMIT
- OS_RELEASE_ID $OS_RELEASE_ID
+ GOPATH $GOPATH
+ RUNC_COMMIT $RUNC_COMMIT
+ OS_RELEASE_ID $OS_RELEASE_ID
"
if [[ "$OS_RELEASE_ID" =~ "ubuntu" ]]; then
echo "Running make install.libseccomp.sudo for ubuntu"
@@ -177,14 +192,7 @@ install_runc(){
cd "$GOPATH/src/github.com/containers/libpod"
ooe.sh sudo make install.libseccomp.sudo
fi
- DEST="$GOPATH/src/github.com/opencontainers/runc"
- rm -rf "$DEST"
- ooe.sh git clone https://github.com/opencontainers/runc.git "$DEST"
- cd "$DEST"
- ooe.sh git fetch origin --tags
- ooe.sh git checkout -q "$RUNC_COMMIT"
- ooe.sh make static BUILDTAGS="seccomp selinux"
- sudo install -m 755 runc /usr/bin/runc
+ install_runc_from_git
}
install_buildah() {
@@ -202,8 +210,8 @@ install_buildah() {
install_conmon(){
echo "Installing conmon from commit $CRIO_COMMIT"
req_env_var "
- GOPATH $GOPATH
- CRIO_COMMIT $CRIO_COMMIT
+ GOPATH $GOPATH
+ CRIO_COMMIT $CRIO_COMMIT
"
DEST="$GOPATH/src/github.com/kubernetes-sigs/cri-o.git"
rm -rf "$DEST"
@@ -234,8 +242,8 @@ install_criu(){
install_testing_dependencies() {
echo "Installing ginkgo, gomega, and easyjson into \$GOPATH=$GOPATH"
req_env_var "
- GOPATH $GOPATH
- GOSRC $GOSRC
+ GOPATH $GOPATH
+ GOSRC $GOSRC
"
cd "$GOSRC"
ooe.sh go get -u github.com/onsi/ginkgo/ginkgo
@@ -263,7 +271,7 @@ install_varlink(){
_finalize(){
echo "Removing leftover giblets from cloud-init"
cd /
- sudo rm -rf /var/lib/cloud
+ sudo rm -rf /var/lib/cloud/instance?
sudo rm -rf /root/.ssh/*
sudo rm -rf /home/*
}
diff --git a/contrib/cirrus/packer/fedora_setup.sh b/contrib/cirrus/packer/fedora_setup.sh
index 16b6e4e6b..f9fea04a7 100644
--- a/contrib/cirrus/packer/fedora_setup.sh
+++ b/contrib/cirrus/packer/fedora_setup.sh
@@ -21,8 +21,7 @@ install_ooe
export GOPATH="$(mktemp -d)"
trap "sudo rm -rf $GOPATH" EXIT
-# breaks networking on f28/29 in GCE
-# ooe.sh sudo dnf update -y
+ooe.sh sudo dnf update -y
ooe.sh sudo dnf install -y \
atomic-registries \
diff --git a/contrib/cirrus/packer/ubuntu_setup.sh b/contrib/cirrus/packer/ubuntu_setup.sh
index ff20944dc..ef209a4a4 100644
--- a/contrib/cirrus/packer/ubuntu_setup.sh
+++ b/contrib/cirrus/packer/ubuntu_setup.sh
@@ -21,10 +21,13 @@ install_ooe
export GOPATH="$(mktemp -d)"
trap "sudo rm -rf $GOPATH" EXIT
-ooe.sh sudo apt-get -qq update
-ooe.sh sudo apt-get -qq update # sometimes it needs to get it twice :S
-ooe.sh sudo apt-get -qq upgrade
-ooe.sh sudo apt-get -qq install --no-install-recommends \
+export DEBIAN_FRONTEND=noninteractive
+
+# Try twice as workaround for minor networking problems
+echo "Updating system and installing package dependencies"
+ooe.sh sudo -E apt-get -qq update || sudo -E apt-get -qq update
+ooe.sh sudo -E apt-get -qq upgrade || sudo -E apt-get -qq upgrade
+ooe.sh sudo -E apt-get -qq install --no-install-recommends \
apparmor \
autoconf \
automake \
diff --git a/contrib/cirrus/setup_environment.sh b/contrib/cirrus/setup_environment.sh
index 167db127f..2563b5f43 100755
--- a/contrib/cirrus/setup_environment.sh
+++ b/contrib/cirrus/setup_environment.sh
@@ -53,6 +53,8 @@ then
# Some setup needs to vary between distros
case "${OS_RELEASE_ID}-${OS_RELEASE_VER}" in
ubuntu-18)
+ # Always install runc on Ubuntu
+ install_runc_from_git
envstr='export BUILDTAGS="seccomp $($GOSRC/hack/btrfs_tag.sh) $($GOSRC/hack/btrfs_installed_tag.sh) $($GOSRC/hack/ostree_tag.sh) varlink exclude_graphdriver_devicemapper"'
;;
fedora-28) ;& # Continue to the next item
diff --git a/contrib/cirrus/system_test.sh b/contrib/cirrus/system_test.sh
new file mode 100755
index 000000000..7c727d336
--- /dev/null
+++ b/contrib/cirrus/system_test.sh
@@ -0,0 +1,33 @@
+#!/bin/bash
+
+set -e
+source $(dirname $0)/lib.sh
+
+req_env_var "
+GOSRC $GOSRC
+OS_RELEASE_ID $OS_RELEASE_ID
+OS_RELEASE_VER $OS_RELEASE_VER
+"
+
+show_env_vars
+
+set -x
+cd "$GOSRC"
+
+case "${OS_RELEASE_ID}-${OS_RELEASE_VER}" in
+ ubuntu-18)
+ make install.tools "BUILDTAGS=$BUILDTAGS"
+ make "BUILDTAGS=$BUILDTAGS"
+ make test-binaries "BUILDTAGS=$BUILDTAGS"
+ ;;
+ fedora-28) ;&
+ centos-7) ;&
+ rhel-7)
+ make install.tools
+ make
+ make test-binaries
+ ;;
+ *) bad_os_id_ver ;;
+esac
+
+make localsystem
diff --git a/contrib/gate/Dockerfile b/contrib/gate/Dockerfile
new file mode 100644
index 000000000..0c0e4aaf9
--- /dev/null
+++ b/contrib/gate/Dockerfile
@@ -0,0 +1,69 @@
+FROM fedora:28
+RUN dnf -y install \
+ atomic-registries \
+ btrfs-progs-devel \
+ buildah \
+ bzip2 \
+ conmon \
+ container-selinux \
+ containernetworking-cni \
+ containernetworking-cni-devel \
+ device-mapper-devel \
+ findutils \
+ git \
+ glib2-devel \
+ glibc-static \
+ gnupg \
+ golang \
+ gpgme-devel \
+ iptables \
+ libassuan-devel \
+ libseccomp-devel \
+ libselinux-devel \
+ lsof \
+ make \
+ nmap-ncat \
+ ostree-devel \
+ procps-ng \
+ python \
+ python3-dateutil \
+ python3-psutil \
+ python3-pytoml \
+ python3-varlink \
+ skopeo-containers \
+ slirp4netns \
+ rsync \
+ which \
+ xz \
+ && dnf clean all
+
+ENV GOPATH="/go" \
+ PATH="/go/bin:/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin" \
+ SRCPATH="/usr/src/libpod" \
+ GOSRC="/go/src/github.com/containers/libpod"
+
+# Only needed for installing build-time dependencies
+COPY / $GOSRC
+
+WORKDIR $GOSRC
+
+# Install dependencies
+RUN set -x && \
+ go get -u github.com/mailru/easyjson/... && \
+ install -D -m 755 "$GOPATH"/bin/easyjson /usr/bin/ && \
+ make install.tools && \
+ install -D -m 755 $GOSRC/contrib/gate/entrypoint.sh /usr/local/bin/ && \
+ rm -rf "$GOSRC"
+
+# Install cni config
+#RUN make install.cni
+RUN mkdir -p /etc/cni/net.d/
+COPY cni/87-podman-bridge.conflist /etc/cni/net.d/87-podman-bridge.conflist
+
+# Make sure we have some policy for pulling images
+RUN mkdir -p /etc/containers
+COPY test/policy.json /etc/containers/policy.json
+COPY test/redhat_sigstore.yaml /etc/containers/registries.d/registry.access.redhat.com.yaml
+
+VOLUME ["/usr/src/libpod"]
+ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
diff --git a/contrib/gate/README.md b/contrib/gate/README.md
new file mode 100644
index 000000000..709e6035f
--- /dev/null
+++ b/contrib/gate/README.md
@@ -0,0 +1,4 @@
+![PODMAN logo](../../logo/podman-logo-source.svg)
+
+A standard container image for `gofmt` and lint-checking the libpod
+repository. The [contributors guide contains the documentation for usage.](https://github.com/containers/libpod/blob/master/CONTRIBUTING.md#go-format-and-lint)
diff --git a/contrib/gate/entrypoint.sh b/contrib/gate/entrypoint.sh
new file mode 100755
index 000000000..e16094cc0
--- /dev/null
+++ b/contrib/gate/entrypoint.sh
@@ -0,0 +1,15 @@
+#!/bin/bash
+
+[[ -n "$SRCPATH" ]] || \
+ ( echo "ERROR: \$SRCPATH must be non-empty" && exit 1 )
+[[ -n "$GOSRC" ]] || \
+ ( echo "ERROR: \$GOSRC must be non-empty" && exit 2 )
+[[ -r "${SRCPATH}/contrib/gate/Dockerfile" ]] || \
+ ( echo "ERROR: Expecting libpod repository root at $SRCPATH" && exit 3 )
+
+# Working from a copy avoids needing to perturb the actual source files
+mkdir -p "$GOSRC"
+/usr/bin/rsync --recursive --links --quiet --safe-links \
+ --perms --times "${SRCPATH}/" "${GOSRC}/"
+cd "$GOSRC"
+make "$@"
diff --git a/contrib/python/podman/Makefile b/contrib/python/podman/Makefile
index 6ec4159f2..11a7568d1 100644
--- a/contrib/python/podman/Makefile
+++ b/contrib/python/podman/Makefile
@@ -4,6 +4,7 @@ PODMAN_VERSION ?= '0.0.4'
.PHONY: python-podman
python-podman:
+ 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/podman/podman/libs/_containers_attach.py b/contrib/python/podman/podman/libs/_containers_attach.py
index f2dad573b..94247d349 100644
--- a/contrib/python/podman/podman/libs/_containers_attach.py
+++ b/contrib/python/podman/podman/libs/_containers_attach.py
@@ -19,9 +19,13 @@ class Mixin:
"""
if stdin is None:
stdin = sys.stdin.fileno()
+ elif hasattr(stdin, 'fileno'):
+ stdin = stdin.fileno()
if stdout is None:
stdout = sys.stdout.fileno()
+ elif hasattr(stdout, 'fileno'):
+ stdout = stdout.fileno()
with self._client() as podman:
attach = podman.GetAttachSockets(self._id)
@@ -49,7 +53,7 @@ class Mixin:
def resize_handler(self):
"""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,
struct.pack('HHHH', 0, 0, 0, 0))
rows, cols, _, _ = struct.unpack('HHHH', packed)
@@ -67,7 +71,7 @@ class Mixin:
def log_handler(self):
"""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:
# send conmon reopen log message
skt.write('2\n')
diff --git a/contrib/python/podman/podman/libs/containers.py b/contrib/python/podman/podman/libs/containers.py
index e211a284e..7adecea8f 100644
--- a/contrib/python/podman/podman/libs/containers.py
+++ b/contrib/python/podman/podman/libs/containers.py
@@ -1,12 +1,12 @@
"""Models for manipulating containers and storage."""
import collections
-import functools
import getpass
import json
import logging
import signal
import time
+from . import fold_keys
from ._containers_attach import Mixin as AttachMixin
from ._containers_start import Mixin as StartMixin
@@ -14,25 +14,27 @@ from ._containers_start import Mixin as StartMixin
class Container(AttachMixin, StartMixin, collections.UserDict):
"""Model for a container."""
- def __init__(self, client, id, data):
+ def __init__(self, client, ident, data, refresh=True):
"""Construct Container Model."""
super(Container, self).__init__(data)
-
self._client = client
- self._id = id
+ self._id = ident
- with client() as podman:
- self._refresh(podman)
+ if refresh:
+ with client() as podman:
+ self._refresh(podman)
+ else:
+ for k, v in self.data.items():
+ setattr(self, k, v)
+ if 'containerrunning' in self.data:
+ setattr(self, 'running', self.data['containerrunning'])
+ self.data['running'] = self.data['containerrunning']
assert self._id == data['id'],\
'Requested container id({}) does not match store id({})'.format(
self._id, data['id']
)
- def __getitem__(self, key):
- """Get items from parent dict."""
- return super().__getitem__(key)
-
def _refresh(self, podman, tries=1):
try:
ctnr = podman.GetContainer(self._id)
@@ -71,18 +73,18 @@ class Container(AttachMixin, StartMixin, collections.UserDict):
results = podman.ListContainerChanges(self._id)
return results['container']
- def kill(self, signal=signal.SIGTERM, wait=25):
+ def kill(self, sig=signal.SIGTERM, wait=25):
"""Send signal to container.
default signal is signal.SIGTERM.
wait n of seconds, 0 waits forever.
"""
with self._client() as podman:
- podman.KillContainer(self._id, signal)
+ podman.KillContainer(self._id, sig)
timeout = time.time() + wait
while True:
self._refresh(podman)
- if self.status != 'running':
+ if self.status != 'running': # pylint: disable=no-member
return self
if wait and timeout < time.time():
@@ -90,20 +92,11 @@ class Container(AttachMixin, StartMixin, collections.UserDict):
time.sleep(0.5)
- def _lower_hook(self):
- """Convert all keys to lowercase."""
-
- @functools.wraps(self._lower_hook)
- def wrapped(input_):
- return {k.lower(): v for (k, v) in input_.items()}
-
- return wrapped
-
def inspect(self):
"""Retrieve details about containers."""
with self._client() as podman:
results = podman.InspectContainer(self._id)
- obj = json.loads(results['container'], object_hook=self._lower_hook())
+ obj = json.loads(results['container'], object_hook=fold_keys())
return collections.namedtuple('ContainerInspect', obj.keys())(**obj)
def export(self, target):
@@ -115,19 +108,16 @@ class Container(AttachMixin, StartMixin, collections.UserDict):
results = podman.ExportContainer(self._id, target)
return results['tarfile']
- def commit(self,
- image_name,
- *args,
- changes=[],
- message='',
- pause=True,
- **kwargs):
+ def commit(self, image_name, **kwargs):
"""Create image from container.
- All changes overwrite existing values.
- See inspect() to obtain current settings.
+ Keyword arguments:
+ author -- change image's author
+ message -- change image's message, docker format only.
+ pause -- pause container during commit
+ change -- Additional properties to change
- Changes:
+ Change examples:
CMD=/usr/bin/zsh
ENTRYPOINT=/bin/sh date
ENV=TEST=test_containers.TestContainers.test_commit
@@ -136,21 +126,23 @@ class Container(AttachMixin, StartMixin, collections.UserDict):
USER=bozo:circus
VOLUME=/data
WORKDIR=/data/application
+
+ All changes overwrite existing values.
+ See inspect() to obtain current settings.
"""
- # TODO: Clean up *args, **kwargs after Commit() is complete
- try:
- author = kwargs.get('author', getpass.getuser())
- except Exception: # pylint: disable=broad-except
- author = ''
+ author = kwargs.get('author', None) or getpass.getuser()
+ change = kwargs.get('change', None) or []
+ message = kwargs.get('message', None) or ''
+ pause = kwargs.get('pause', None) or True
- for c in changes:
+ for c in change:
if c.startswith('LABEL=') and c.count('=') < 2:
raise ValueError(
'LABEL should have the format: LABEL=label=value, not {}'.
format(c))
with self._client() as podman:
- results = podman.Commit(self._id, image_name, changes, author,
+ results = podman.Commit(self._id, image_name, change, author,
message, pause)
return results['image']
@@ -175,7 +167,7 @@ class Container(AttachMixin, StartMixin, collections.UserDict):
podman.RestartContainer(self._id, timeout)
return self._refresh(podman)
- def rename(self, target):
+ def rename(self, target): # pylint: disable=unused-argument
"""Rename container, return id on success."""
with self._client() as podman:
# TODO: Need arguments
@@ -183,7 +175,7 @@ class Container(AttachMixin, StartMixin, collections.UserDict):
# TODO: fixup objects cached information
return results['container']
- def resize_tty(self, width, height):
+ def resize_tty(self, width, height): # pylint: disable=unused-argument
"""Resize container tty."""
with self._client() as podman:
# TODO: magic re: attach(), arguments
@@ -201,7 +193,8 @@ class Container(AttachMixin, StartMixin, collections.UserDict):
podman.UnpauseContainer(self._id)
return self._refresh(podman)
- def update_container(self, *args, **kwargs):
+ def update_container(self, *args, **kwargs): \
+ # pylint: disable=unused-argument
"""TODO: Update container..., return id on success."""
with self._client() as podman:
podman.UpdateContainer()
@@ -220,7 +213,7 @@ class Container(AttachMixin, StartMixin, collections.UserDict):
obj = results['container']
return collections.namedtuple('StatDetail', obj.keys())(**obj)
- def logs(self, *args, **kwargs):
+ def logs(self, *args, **kwargs): # pylint: disable=unused-argument
"""Retrieve container logs."""
with self._client() as podman:
results = podman.GetContainerLogs(self._id)
@@ -239,7 +232,7 @@ class Containers():
with self._client() as podman:
results = podman.ListContainers()
for cntr in results['containers']:
- yield Container(self._client, cntr['id'], cntr)
+ yield Container(self._client, cntr['id'], cntr, refresh=False)
def delete_stopped(self):
"""Delete all stopped containers."""
diff --git a/contrib/python/podman/podman/libs/images.py b/contrib/python/podman/podman/libs/images.py
index 982546cd2..ae1b86390 100644
--- a/contrib/python/podman/podman/libs/images.py
+++ b/contrib/python/podman/podman/libs/images.py
@@ -27,9 +27,10 @@ class Image(collections.UserDict):
@staticmethod
def _split_token(values=None, sep='='):
+ if not values:
+ return {}
return {
- k: v1
- for k, v1 in (v0.split(sep, 1) for v0 in values if values)
+ k: v1 for k, v1 in (v0.split(sep, 1) for v0 in values)
}
def create(self, *args, **kwargs):
@@ -74,7 +75,7 @@ class Image(collections.UserDict):
obj = json.loads(results['image'], object_hook=fold_keys())
return collections.namedtuple('ImageInspect', obj.keys())(**obj)
- def push(self, target, tlsverify=False):
+ def push(self, target, tlsverify=True):
"""Copy image to target, return id on success."""
with self._client() as podman:
results = podman.PushImage(self._id, target, tlsverify)
@@ -137,7 +138,7 @@ class Images():
results = podman.DeleteUnusedImages()
return results['images']
- def import_image(self, source, reference, message=None, changes=None):
+ def import_image(self, source, reference, message='', changes=None):
"""Read image tarball from source and save in image store."""
with self._client() as podman:
results = podman.ImportImage(source, reference, message, changes)
diff --git a/contrib/python/podman/test/test_containers.py b/contrib/python/podman/test/test_containers.py
index 3de1e54bc..a7a6ac304 100644
--- a/contrib/python/podman/test/test_containers.py
+++ b/contrib/python/podman/test/test_containers.py
@@ -152,7 +152,7 @@ class TestContainers(PodmanTestCase):
changes.append('WORKDIR=/data/application')
id = self.alpine_ctnr.commit(
- 'alpine3', author='Bozo the clown', changes=changes, pause=True)
+ 'alpine3', author='Bozo the clown', change=changes, pause=True)
img = self.pclient.images.get(id)
self.assertIsNotNone(img)
diff --git a/contrib/python/podman/test/test_images.py b/contrib/python/podman/test/test_images.py
index f97e13b4c..45f0a2964 100644
--- a/contrib/python/podman/test/test_images.py
+++ b/contrib/python/podman/test/test_images.py
@@ -102,7 +102,7 @@ class TestImages(PodmanTestCase):
def test_push(self):
path = '{}/alpine_push'.format(self.tmpdir)
target = 'dir:{}'.format(path)
- self.alpine_image.push(target)
+ self.alpine_image.push(target, tlsverify=False)
self.assertTrue(os.path.isfile(os.path.join(path, 'manifest.json')))
self.assertTrue(os.path.isfile(os.path.join(path, 'version')))
diff --git a/contrib/python/podman/test/test_pods_ctnrs.py b/contrib/python/podman/test/test_pods_ctnrs.py
index 14ce95c8a..009e30720 100644
--- a/contrib/python/podman/test/test_pods_ctnrs.py
+++ b/contrib/python/podman/test/test_pods_ctnrs.py
@@ -52,7 +52,8 @@ class TestPodsCtnrs(PodmanTestCase):
status = FoldedString(pod.containersinfo[0]['status'])
self.assertIn(status, ('stopped', 'exited', 'running'))
- killed = pod.kill()
+ # Pod kill is broken, so use stop for now
+ killed = pod.stop()
self.assertEqual(pod, killed)
def test_999_remove(self):
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)
diff --git a/contrib/spec/podman.spec.in b/contrib/spec/podman.spec.in
index c2d8fc59d..3192cbfed 100644
--- a/contrib/spec/podman.spec.in
+++ b/contrib/spec/podman.spec.in
@@ -39,7 +39,7 @@
%global shortcommit_conmon %(c=%{commit_conmon}; echo ${c:0:7})
Name: podman
-Version: 0.10.2
+Version: 0.11.2
Release: #COMMITDATE#.git%{shortcommit0}%{?dist}
Summary: Manage Pods, Containers and Container Images
License: ASL 2.0
@@ -378,10 +378,6 @@ providing packages with %{import_path} prefix.
%prep
%autosetup -Sgit -n %{repo}-%{shortcommit0}
-sed -i '/\/bin\/env/d' completions/bash/%{name}
-sed -i 's/0.0.0/%{version}/' contrib/python/%{name}/setup.py
-sed -i 's/0.0.0/%{version}/' contrib/python/py%{name}/setup.py
-mv pkg/hooks/README.md pkg/hooks/README-hooks.md
# untar cri-o
tar zxf %{SOURCE1}
@@ -416,15 +412,17 @@ popd
%install
install -dp %{buildroot}%{_unitdir}
-%{__make} PREFIX=%{buildroot}%{_prefix} ETCDIR=%{buildroot}%{_sysconfdir} \
+PODMAN_VERSION=%{version} %{__make} PREFIX=%{buildroot}%{_prefix} ETCDIR=%{buildroot}%{_sysconfdir} \
install.bin \
install.man \
install.cni \
install.systemd \
install.completions
+mv pkg/hooks/README.md pkg/hooks/README-hooks.md
+
%if %{with varlink}
-%{__make} DESTDIR=%{buildroot} install.python
+PODMAN_VERSION=%{version} %{__make} DESTDIR=%{buildroot} install.python
%endif # varlink
# install libpod.conf