summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--contrib/python/podman/libs/__init__.py6
-rw-r--r--contrib/python/podman/libs/_containers_attach.py111
-rw-r--r--contrib/python/podman/libs/containers.py10
-rw-r--r--contrib/python/test/podman_testcase.py3
-rw-r--r--contrib/python/test/test_containers.py20
-rwxr-xr-xhack/release.sh80
6 files changed, 215 insertions, 15 deletions
diff --git a/contrib/python/podman/libs/__init__.py b/contrib/python/podman/libs/__init__.py
index 9db5dacf5..3a8a35021 100644
--- a/contrib/python/podman/libs/__init__.py
+++ b/contrib/python/podman/libs/__init__.py
@@ -1,4 +1,5 @@
"""Support files for podman API implementation."""
+import collections
import datetime
import functools
@@ -12,12 +13,11 @@ __all__ = [
def cached_property(fn):
- """Cache return to save future expense."""
+ """Decorate property to cache return value."""
return property(functools.lru_cache(maxsize=8)(fn))
-# Cannot subclass collections.UserDict, breaks varlink
-class Config(dict):
+class Config(collections.UserDict):
"""Silently ignore None values, only take key once."""
def __init__(self, **kwargs):
diff --git a/contrib/python/podman/libs/_containers_attach.py b/contrib/python/podman/libs/_containers_attach.py
new file mode 100644
index 000000000..54e6a009e
--- /dev/null
+++ b/contrib/python/podman/libs/_containers_attach.py
@@ -0,0 +1,111 @@
+"""Exported method Container.attach()."""
+
+import fcntl
+import os
+import select
+import signal
+import socket
+import struct
+import sys
+import termios
+import tty
+
+CONMON_BUFSZ = 8192
+
+
+class Mixin:
+ """Publish attach() for inclusion in Container class."""
+
+ def attach(self, eot=4, stdin=None, stdout=None):
+ """Attach to container's PID1 stdin and stdout.
+
+ stderr is ignored.
+ """
+ if stdin is None:
+ stdin = sys.stdin.fileno()
+
+ if stdout is None:
+ stdout = sys.stdout.fileno()
+
+ with self._client() as podman:
+ attach = podman.GetAttachSockets(self._id)
+
+ # This is the UDS where all the IO goes
+ io_socket = attach['sockets']['io_socket']
+ assert len(io_socket) <= 107,\
+ 'Path length for sockets too long. {} > 107'.format(
+ len(io_socket)
+ )
+
+ # This is the control socket where resizing events are sent to conmon
+ ctl_socket = attach['sockets']['control_socket']
+
+ def resize_handler(signum, frame):
+ """Send the new window size to conmon.
+
+ The method arguments are not used.
+ """
+ packed = fcntl.ioctl(stdout, termios.TIOCGWINSZ,
+ struct.pack('HHHH', 0, 0, 0, 0))
+ rows, cols, _, _ = struct.unpack('HHHH', packed)
+
+ with open(ctl_socket, 'w') as skt:
+ # send conmon window resize message
+ skt.write('1 {} {}\n'.format(rows, cols))
+
+ def log_handler(signum, frame):
+ """Send command to reopen log to conmon.
+
+ The method arguments are not used.
+ """
+ with open(ctl_socket, 'w') as skt:
+ # send conmon reopen log message
+ skt.write('2\n')
+
+ try:
+ # save off the old settings for terminal
+ original_attr = termios.tcgetattr(stdout)
+ tty.setraw(stdin)
+
+ # initialize containers window size
+ resize_handler(None, sys._getframe(0))
+
+ # catch any resizing events and send the resize info
+ # to the control fifo "socket"
+ signal.signal(signal.SIGWINCH, resize_handler)
+ except termios.error:
+ original_attr = None
+
+ try:
+ # Prepare socket for communicating with conmon/container
+ with socket.socket(socket.AF_UNIX, socket.SOCK_SEQPACKET) as skt:
+ skt.connect(io_socket)
+ skt.sendall(b'\n')
+
+ sources = [skt, stdin]
+ while sources:
+ readable, _, _ = select.select(sources, [], [])
+ for r in readable:
+ if r is skt:
+ data = r.recv(CONMON_BUFSZ)
+ if not data:
+ sources.remove(skt)
+
+ # Remove source marker when writing
+ os.write(stdout, data[1:])
+ elif r is stdin:
+ data = os.read(stdin, CONMON_BUFSZ)
+ if not data:
+ sources.remove(stdin)
+
+ skt.sendall(data)
+
+ if eot in data:
+ sources.clear()
+ break
+ else:
+ raise ValueError('Unknown source in select')
+ finally:
+ if original_attr:
+ termios.tcsetattr(stdout, termios.TCSADRAIN, original_attr)
+ signal.signal(signal.SIGWINCH, signal.SIG_DFL)
diff --git a/contrib/python/podman/libs/containers.py b/contrib/python/podman/libs/containers.py
index 96ec6be37..a350a128a 100644
--- a/contrib/python/podman/libs/containers.py
+++ b/contrib/python/podman/libs/containers.py
@@ -6,8 +6,10 @@ import json
import signal
import time
+from ._containers_attach import Mixin as AttachMixin
-class Container(collections.UserDict):
+
+class Container(collections.UserDict, AttachMixin):
"""Model for a container."""
def __init__(self, client, id, data):
@@ -46,12 +48,6 @@ class Container(collections.UserDict):
with self._client() as podman:
return self._refresh(podman)
- def attach(self, detach_key=None, no_stdin=False, sig_proxy=True):
- """Attach to running container."""
- with self._client() as podman:
- # TODO: streaming and port magic occur, need arguments
- podman.AttachToContainer()
-
def processes(self):
"""Show processes running in container."""
with self._client() as podman:
diff --git a/contrib/python/test/podman_testcase.py b/contrib/python/test/podman_testcase.py
index fc99f26ce..f96a3a013 100644
--- a/contrib/python/test/podman_testcase.py
+++ b/contrib/python/test/podman_testcase.py
@@ -62,7 +62,8 @@ class PodmanTestCase(unittest.TestCase):
cmd = ['podman']
cmd.extend(podman_args)
- cmd.extend(['run', '-d', 'alpine', 'sleep', '500'])
+ # cmd.extend(['run', '-d', 'alpine', 'sleep', '500'])
+ cmd.extend(['run', '-dt', 'alpine', '/bin/sh'])
PodmanTestCase.alpine_process = subprocess.Popen(
cmd,
stdout=PodmanTestCase.alpine_log,
diff --git a/contrib/python/test/test_containers.py b/contrib/python/test/test_containers.py
index 5c7c9934b..87d43adb4 100644
--- a/contrib/python/test/test_containers.py
+++ b/contrib/python/test/test_containers.py
@@ -1,11 +1,9 @@
import os
import signal
-import time
import unittest
from test.podman_testcase import PodmanTestCase
import podman
-from podman import datetime_parse
class TestContainers(PodmanTestCase):
@@ -65,8 +63,22 @@ class TestContainers(PodmanTestCase):
self.pclient.containers.get("bozo")
def test_attach(self):
- with self.assertRaisesNotImplemented():
- self.alpine_ctnr.attach()
+ # StringIO does not support fileno() so we had to go old school
+ input = os.path.join(self.tmpdir, 'test_attach.stdin')
+ output = os.path.join(self.tmpdir, 'test_attach.stdout')
+
+ with open(input, 'w+') as mock_in, open(output, 'w+') as mock_out:
+ # double quote is indeed in the expected place
+ mock_in.write('echo H"ello, World"; exit\n')
+ mock_in.seek(0, 0)
+
+ self.alpine_ctnr.attach(
+ stdin=mock_in.fileno(), stdout=mock_out.fileno())
+
+ mock_out.flush()
+ mock_out.seek(0, 0)
+ output = mock_out.read()
+ self.assertIn('Hello', output)
def test_processes(self):
actual = list(self.alpine_ctnr.processes())
diff --git a/hack/release.sh b/hack/release.sh
new file mode 100755
index 000000000..01d2e81a9
--- /dev/null
+++ b/hack/release.sh
@@ -0,0 +1,80 @@
+#!/bin/sh
+#
+# Cut a libpod release. Usage:
+#
+# $ hack/release.sh <version> <next-version>
+#
+# For example:
+#
+# $ hack/release.sh 1.2.3 1.3.0
+#
+# for "I'm cutting 1.2.3, and want to use 1.3.0-dev for future work".
+
+VERSION="$1"
+NEXT_VERSION="$2"
+DATE=$(date '+%Y-%m-%d')
+LAST_TAG=$(git describe --tags --abbrev=0)
+
+write_go_version()
+{
+ LOCAL_VERSION="$1"
+ sed -i "s/^\(const Version = \"\).*/\1${LOCAL_VERSION}\"/" version/version.go
+}
+
+write_python_version()
+{
+ LOCAL_VERSION="$1"
+ sed -i "s/^\( *version='*\).*/\1${LOCAL_VERSION}'/" contrib/python/setup.py
+}
+
+write_spec_version()
+{
+ LOCAL_VERSION="$1"
+ sed -i "s/^\(Version: *\).*/\1${LOCAL_VERSION}/" contrib/spec/podman.spec.in
+}
+
+write_makefile_epoch()
+{
+ LOCAL_EPOCH="$1"
+ sed -i "s/^\(EPOCH_TEST_COMMIT ?= \).*/\1${LOCAL_EPOCH}/" Makefile
+}
+
+write_changelog()
+{
+ echo "- Changelog for v${VERSION} (${DATE})" >.changelog.txt &&
+ git log --no-merges --format=' * %s' "${LAST_TAG}..HEAD" >>.changelog.txt &&
+ echo >>.changelog.txt &&
+ cat changelog.txt >>.changelog.txt &&
+ mv -f .changelog.txt changelog.txt
+}
+
+release_commit()
+{
+ write_go_version "${VERSION}" &&
+ write_python_version "${VERSION}" &&
+ write_changelog &&
+ git commit -asm "Bump to v${VERSION}"
+}
+
+dev_version_commit()
+{
+ write_go_version "${NEXT_VERSION}-dev" &&
+ write_python_version "${NEXT_VERSION}" &&
+ write_spec_version "${VERSION}" &&
+ git commit -asm "Bump to v${NEXT_VERSION}-dev"
+}
+
+epoch_commit()
+{
+ LOCAL_EPOCH="$1"
+ write_makefile_epoch "${LOCAL_EPOCH}" &&
+ git commit -asm 'Bump gitvalidation epoch'
+}
+
+git fetch origin &&
+git checkout -b "bump-${VERSION}" origin/master &&
+EPOCH=$(git rev-parse HEAD) &&
+release_commit &&
+git tag -s -m "version ${VERSION}" "v${VERSION}" &&
+dev_version_commit &&
+epoch_commit "${EPOCH}"