From 84c567e2650dfee761ecb39486b6c506f0cbcafe Mon Sep 17 00:00:00 2001 From: Denis Zalevskiy Date: Fri, 22 Oct 2021 11:31:55 +0300 Subject: [PATCH] Add podman connection support Shameless copy of buildah connection with modifications of invocation to fit podman CLI. Signed-off-by: Denis Zalevskiy --- docs/api.rst | 14 +++++++ docs/changelog.rst | 1 + mitogen/core.py | 1 + mitogen/parent.py | 3 ++ mitogen/podman.py | 74 +++++++++++++++++++++++++++++++++ tests/data/stubs/stub-podman.py | 8 ++++ tests/podman_test.py | 52 +++++++++++++++++++++++ 7 files changed, 153 insertions(+) create mode 100644 mitogen/podman.py create mode 100755 tests/data/stubs/stub-podman.py create mode 100644 tests/podman_test.py diff --git a/docs/api.rst b/docs/api.rst index 9bc32861..3dab62c1 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -367,6 +367,20 @@ Connection Methods Filename or complete path to the ``lxc`` binary. ``PATH`` will be searched if given as a filename. Defaults to ``lxc``. +.. currentmodule:: mitogen.parent +.. method:: Router.podman (container=None, podman_path=None, username=None, \**kwargs) + + Construct a context on the local machine over a ``podman`` invocation. + Accepts all parameters accepted by :meth:`local`, in addition to: + + :param str container: + The name of the Podman container to connect to. + :param str podman_path: + Filename or complete path to the ``podman`` binary. ``PATH`` will be + searched if given as a filename. Defaults to ``podman``. + :param str username: + Username to use, defaults to unset. + .. method:: Router.setns (container, kind, username=None, docker_path=None, lxc_info_path=None, machinectl_path=None, \**kwargs) Construct a context in the style of :meth:`local`, but change the diff --git a/docs/changelog.rst b/docs/changelog.rst index 5d617432..1dbe99a8 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -23,6 +23,7 @@ v0.3.1.dev0 (unreleased) * :gh:issue:`869` Continuous Integration tests are now run with Tox * :gh:issue:`869` Continuous Integration tests now cover CentOS 6 & 8, Debian 9 & 11, Ubuntu 16.04 & 20.04 +* :gh:issue:`860` Add initial support for podman connection (w/o Ansible support yet) v0.3.0 (2021-11-24) diff --git a/mitogen/core.py b/mitogen/core.py index 802ac45e..e796129d 100644 --- a/mitogen/core.py +++ b/mitogen/core.py @@ -1254,6 +1254,7 @@ class Importer(object): 'minify', 'os_fork', 'parent', + 'podman', 'select', 'service', 'setns', diff --git a/mitogen/parent.py b/mitogen/parent.py index 3b4dca8a..7ca90ab4 100644 --- a/mitogen/parent.py +++ b/mitogen/parent.py @@ -2505,6 +2505,9 @@ class Router(mitogen.core.Router): def ssh(self, **kwargs): return self.connect(u'ssh', **kwargs) + def podman(self, **kwargs): + return self.connect(u'podman', **kwargs) + class Reaper(object): """ diff --git a/mitogen/podman.py b/mitogen/podman.py new file mode 100644 index 00000000..01e02e94 --- /dev/null +++ b/mitogen/podman.py @@ -0,0 +1,74 @@ +# Copyright 2019, David Wilson +# Copyright 2021, Mitogen contributors +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its contributors +# may be used to endorse or promote products derived from this software without +# specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +# !mitogen: minify_safe + +import logging + +import mitogen.core +import mitogen.parent + + +LOG = logging.getLogger(__name__) + + +class Options(mitogen.parent.Options): + container = None + username = None + podman_path = 'podman' + + def __init__(self, container=None, podman_path=None, username=None, + **kwargs): + super(Options, self).__init__(**kwargs) + assert container is not None + self.container = container + if podman_path: + self.podman_path = podman_path + if username: + self.username = username + + +class Connection(mitogen.parent.Connection): + options_class = Options + child_is_immediate_subprocess = False + + # TODO: better way of capturing errors such as "No such container." + create_child_args = { + 'merge_stdio': True + } + + def _get_name(self): + return u'podman.' + self.options.container + + def get_boot_command(self): + args = [self.options.podman_path, 'exec'] + if self.options.username: + args += ['--user=' + self.options.username] + args += ["--interactive", "--", self.options.container] + return args + super(Connection, self).get_boot_command() diff --git a/tests/data/stubs/stub-podman.py b/tests/data/stubs/stub-podman.py new file mode 100755 index 00000000..28f55189 --- /dev/null +++ b/tests/data/stubs/stub-podman.py @@ -0,0 +1,8 @@ +#!/usr/bin/env python + +import sys +import os + +os.environ['ORIGINAL_ARGV'] = repr(sys.argv) +os.environ['THIS_IS_STUB_PODMAN'] = '1' +os.execv(sys.executable, sys.argv[sys.argv.index('--') + 2:]) diff --git a/tests/podman_test.py b/tests/podman_test.py new file mode 100644 index 00000000..8ee2d1cd --- /dev/null +++ b/tests/podman_test.py @@ -0,0 +1,52 @@ +import os +import unittest2 + +import mitogen + +import testlib + + +class ConstructorTest(testlib.RouterMixin, testlib.TestCase): + def test_okay(self): + stub_path = testlib.data_path('stubs/stub-podman.py') + + context = self.router.podman( + container='container_name', + podman_path=stub_path, + ) + stream = self.router.stream_by_id(context.context_id) + + argv = eval(context.call(os.getenv, 'ORIGINAL_ARGV')) + expected_call = [ + stub_path, + 'exec', + '--interactive', + '--', + 'container_name', + stream.conn.options.python_path + ] + self.assertEquals(argv[:len(expected_call)], expected_call) + + context = self.router.podman( + container='container_name', + podman_path=stub_path, + username='some_user', + ) + stream = self.router.stream_by_id(context.context_id) + + argv = eval(context.call(os.getenv, 'ORIGINAL_ARGV')) + expected_call = [ + stub_path, + 'exec', + '--user=some_user', + '--interactive', + '--', + 'container_name', + stream.conn.options.python_path + ] + self.assertEquals(argv[:len(expected_call)], expected_call) + + + +if __name__ == '__main__': + unittest2.main()