mirror of
https://github.com/ansible-collections/community.general.git
synced 2024-09-14 20:13:21 +02:00
Fix lxc plugin options (#7369)
* Fixture for liblxc
Add a fixture to allow testing the lxc connection plugin both with and
without liblxc being present.
Also change the test from unittest to pytest.
* Update liblxc error message
The error is not specific to python2, so remove the version. Also add
a test for it.
* Migrate to options
Because the lxc plugin was only using PlayContext properties, using host
vars like `ansible_lxc_host` didn't work. This is fixed by instead using
the `get_option` method inherited from `AnsiblePlugin`.
The options are not yet available in the `__init__` function, so the
determination of the container name is moved to the `_connect` method,
which is the first time it is actually needed.
The default for the `remote_addr` option is removed, because the string
`inventory_hostname` is not very useful. At all. This seams to have been
spread with copy&paste and a bit of cargo culting. The variable priority
already takes care of setting the value.
* Add changelog fragment
* Fix for Py2.7
`TypeError: super() takes at least 1 argument (0 given)`
* Add plugin type to changelog fragment.
* Restore untemplated default
This partially reverts commit 429d8c8cfb
.
---------
Co-authored-by: Felix Fontein <felix@fontein.de>
This commit is contained in:
parent
8a51a3c30c
commit
1bf5a44a77
3 changed files with 77 additions and 11 deletions
3
changelogs/fragments/7369-fix-lxc-options.yml
Normal file
3
changelogs/fragments/7369-fix-lxc-options.yml
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
---
|
||||||
|
bugfixes:
|
||||||
|
- lxc connection plugin - properly evaluate options (https://github.com/ansible-collections/community.general/pull/7369).
|
|
@ -60,7 +60,7 @@ class Connection(ConnectionBase):
|
||||||
def __init__(self, play_context, new_stdin, *args, **kwargs):
|
def __init__(self, play_context, new_stdin, *args, **kwargs):
|
||||||
super(Connection, self).__init__(play_context, new_stdin, *args, **kwargs)
|
super(Connection, self).__init__(play_context, new_stdin, *args, **kwargs)
|
||||||
|
|
||||||
self.container_name = self._play_context.remote_addr
|
self.container_name = None
|
||||||
self.container = None
|
self.container = None
|
||||||
|
|
||||||
def _connect(self):
|
def _connect(self):
|
||||||
|
@ -68,12 +68,14 @@ class Connection(ConnectionBase):
|
||||||
super(Connection, self)._connect()
|
super(Connection, self)._connect()
|
||||||
|
|
||||||
if not HAS_LIBLXC:
|
if not HAS_LIBLXC:
|
||||||
msg = "lxc bindings for python2 are not installed"
|
msg = "lxc python bindings are not installed"
|
||||||
raise errors.AnsibleError(msg)
|
raise errors.AnsibleError(msg)
|
||||||
|
|
||||||
if self.container:
|
if self.container:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
self.container_name = self.get_option('remote_addr')
|
||||||
|
|
||||||
self._display.vvv("THIS IS A LOCAL LXC DIR", host=self.container_name)
|
self._display.vvv("THIS IS A LOCAL LXC DIR", host=self.container_name)
|
||||||
self.container = _lxc.Container(self.container_name)
|
self.container = _lxc.Container(self.container_name)
|
||||||
if self.container.state == "STOPPED":
|
if self.container.state == "STOPPED":
|
||||||
|
@ -118,7 +120,7 @@ class Connection(ConnectionBase):
|
||||||
super(Connection, self).exec_command(cmd, in_data=in_data, sudoable=sudoable)
|
super(Connection, self).exec_command(cmd, in_data=in_data, sudoable=sudoable)
|
||||||
|
|
||||||
# python2-lxc needs bytes. python3-lxc needs text.
|
# python2-lxc needs bytes. python3-lxc needs text.
|
||||||
executable = to_native(self._play_context.executable, errors='surrogate_or_strict')
|
executable = to_native(self.get_option('executable'), errors='surrogate_or_strict')
|
||||||
local_cmd = [executable, '-c', to_native(cmd, errors='surrogate_or_strict')]
|
local_cmd = [executable, '-c', to_native(cmd, errors='surrogate_or_strict')]
|
||||||
|
|
||||||
read_stdout, write_stdout = None, None
|
read_stdout, write_stdout = None, None
|
||||||
|
|
|
@ -6,20 +6,81 @@
|
||||||
from __future__ import (absolute_import, division, print_function)
|
from __future__ import (absolute_import, division, print_function)
|
||||||
__metaclass__ = type
|
__metaclass__ = type
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
import sys
|
||||||
|
|
||||||
from io import StringIO
|
from io import StringIO
|
||||||
|
|
||||||
from ansible_collections.community.general.tests.unit.compat import unittest
|
from ansible.errors import AnsibleError
|
||||||
from ansible_collections.community.general.plugins.connection import lxc
|
|
||||||
from ansible.playbook.play_context import PlayContext
|
from ansible.playbook.play_context import PlayContext
|
||||||
|
from ansible.plugins.loader import connection_loader
|
||||||
|
from ansible_collections.community.general.tests.unit.compat import mock
|
||||||
|
|
||||||
|
|
||||||
class TestLXCConnectionClass(unittest.TestCase):
|
@pytest.fixture(autouse=True)
|
||||||
|
def lxc(request):
|
||||||
|
"""Fixture to import/load the lxc plugin module.
|
||||||
|
|
||||||
def test_lxc_connection_module(self):
|
The fixture parameter is used to determine the presence of liblxc.
|
||||||
|
When true (default), a mocked liblxc module is injected. If False,
|
||||||
|
no liblxc will be present.
|
||||||
|
"""
|
||||||
|
liblxc_present = getattr(request, 'param', True)
|
||||||
|
|
||||||
|
class ContainerMock(mock.MagicMock):
|
||||||
|
def __init__(self, name):
|
||||||
|
super(ContainerMock, self).__init__()
|
||||||
|
self.name = name
|
||||||
|
self.state = 'STARTED'
|
||||||
|
|
||||||
|
liblxc_module_mock = mock.MagicMock()
|
||||||
|
liblxc_module_mock.Container = ContainerMock
|
||||||
|
|
||||||
|
with mock.patch.dict('sys.modules'):
|
||||||
|
if liblxc_present:
|
||||||
|
sys.modules['lxc'] = liblxc_module_mock
|
||||||
|
elif 'lxc' in sys.modules:
|
||||||
|
del sys.modules['lxc']
|
||||||
|
|
||||||
|
from ansible_collections.community.general.plugins.connection import lxc as lxc_plugin_module
|
||||||
|
|
||||||
|
assert lxc_plugin_module.HAS_LIBLXC == liblxc_present
|
||||||
|
assert bool(getattr(lxc_plugin_module, '_lxc', None)) == liblxc_present
|
||||||
|
|
||||||
|
yield lxc_plugin_module
|
||||||
|
|
||||||
|
|
||||||
|
class TestLXCConnectionClass():
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('lxc', [True, False], indirect=True)
|
||||||
|
def test_lxc_connection_module(self, lxc):
|
||||||
|
"""Test that a connection can be created with the plugin."""
|
||||||
play_context = PlayContext()
|
play_context = PlayContext()
|
||||||
play_context.prompt = (
|
|
||||||
'[sudo via ansible, key=ouzmdnewuhucvuaabtjmweasarviygqq] password: '
|
|
||||||
)
|
|
||||||
in_stream = StringIO()
|
in_stream = StringIO()
|
||||||
|
|
||||||
self.assertIsInstance(lxc.Connection(play_context, in_stream), lxc.Connection)
|
conn = connection_loader.get('lxc', play_context, in_stream)
|
||||||
|
assert conn
|
||||||
|
assert isinstance(conn, lxc.Connection)
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('lxc', [False], indirect=True)
|
||||||
|
def test_lxc_connection_liblxc_error(self, lxc):
|
||||||
|
"""Test that on connect an error is thrown if liblxc is not present."""
|
||||||
|
play_context = PlayContext()
|
||||||
|
in_stream = StringIO()
|
||||||
|
conn = connection_loader.get('lxc', play_context, in_stream)
|
||||||
|
|
||||||
|
with pytest.raises(AnsibleError, match='lxc python bindings are not installed'):
|
||||||
|
conn._connect()
|
||||||
|
|
||||||
|
def test_remote_addr_option(self):
|
||||||
|
"""Test that the remote_addr option is used"""
|
||||||
|
play_context = PlayContext()
|
||||||
|
in_stream = StringIO()
|
||||||
|
conn = connection_loader.get('lxc', play_context, in_stream)
|
||||||
|
|
||||||
|
container_name = 'my-container'
|
||||||
|
conn.set_option('remote_addr', container_name)
|
||||||
|
assert conn.get_option('remote_addr') == container_name
|
||||||
|
|
||||||
|
conn._connect()
|
||||||
|
assert conn.container_name == container_name
|
||||||
|
|
Loading…
Reference in a new issue