# Copyright (c) 2020 Red Hat Inc. # GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) # SPDX-License-Identifier: GPL-3.0-or-later # Make coding more python3-ish from __future__ import (absolute_import, division, print_function) __metaclass__ = type import pytest import sys from io import StringIO from ansible.errors import AnsibleError from ansible.playbook.play_context import PlayContext from ansible.plugins.loader import connection_loader from ansible_collections.community.general.tests.unit.compat import mock @pytest.fixture(autouse=True) def lxc(request): """Fixture to import/load the lxc plugin module. 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(): # dict of container name to its state _container_states = {} def __init__(self, name): super(ContainerMock, self).__init__() self.name = name @property def state(self): return ContainerMock._container_states.get(self.name, '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() in_stream = StringIO() 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 def test_error_when_stopped(self, lxc): """Test that on connect an error is thrown if the container is stopped.""" play_context = PlayContext() in_stream = StringIO() conn = connection_loader.get('lxc', play_context, in_stream) conn.set_option('remote_addr', 'my-container') lxc._lxc.Container._container_states['my-container'] = 'STOPPED' with pytest.raises(AnsibleError, match='my-container is not running'): conn._connect() def test_container_name_change(self): """Test connect method reconnects when remote_addr changes""" play_context = PlayContext() in_stream = StringIO() conn = connection_loader.get('lxc', play_context, in_stream) # setting the option does nothing container1_name = 'my-container' conn.set_option('remote_addr', container1_name) assert conn.container_name is None assert conn.container is None # first call initializes the connection conn._connect() assert conn.container_name is container1_name assert conn.container is not None assert conn.container.name == container1_name container1 = conn.container # second call is basically a no-op conn._connect() assert conn.container_name is container1_name assert conn.container is container1 assert conn.container.name == container1_name # setting the option does again nothing container2_name = 'my-other-container' conn.set_option('remote_addr', container2_name) assert conn.container_name == container1_name assert conn.container is container1 assert conn.container.name == container1_name # first call with a different remote_addr changes the connection conn._connect() assert conn.container_name == container2_name assert conn.container is not None assert conn.container is not container1 assert conn.container.name == container2_name