diff options
Diffstat (limited to 'tests')
-rw-r--r-- | tests/integration_tests/modules/test_user_events.py | 5 | ||||
-rw-r--r-- | tests/integration_tests/test_networking.py | 93 | ||||
-rw-r--r-- | tests/unittests/net/test_netplan.py | 41 | ||||
-rw-r--r-- | tests/unittests/test_net.py | 4 | ||||
-rw-r--r-- | tests/unittests/test_util.py | 21 |
5 files changed, 159 insertions, 5 deletions
diff --git a/tests/integration_tests/modules/test_user_events.py b/tests/integration_tests/modules/test_user_events.py index 79d88022..810f8727 100644 --- a/tests/integration_tests/modules/test_user_events.py +++ b/tests/integration_tests/modules/test_user_events.py @@ -28,8 +28,7 @@ def _add_dummy_bridge_to_netplan(client: IntegrationInstance): @pytest.mark.skipif( - PLATFORM - not in ["lxd_container", "lxd_vm", "ec2", "gce", "oci", "openstack"], + PLATFORM not in ["lxd_container", "lxd_vm", "ec2", "oci", "openstack"], reason="Default boot events testing is datasource specific", ) def test_boot_event_disabled_by_default(client: IntegrationInstance): @@ -93,7 +92,7 @@ def _test_network_config_applied_on_reboot(client: IntegrationInstance): @pytest.mark.skipif( - PLATFORM != "azure", + PLATFORM not in ("azure", "gce"), reason=( f"{PLATFORM} doesn't support updates every boot event by default " "(or hasn't been testing for it)." diff --git a/tests/integration_tests/test_networking.py b/tests/integration_tests/test_networking.py new file mode 100644 index 00000000..cde69afc --- /dev/null +++ b/tests/integration_tests/test_networking.py @@ -0,0 +1,93 @@ +"""Networking-related tests.""" +import pytest +import yaml + +from tests.integration_tests.instances import IntegrationInstance +from tests.integration_tests.integration_settings import PLATFORM + + +def _add_dummy_bridge_to_netplan(client: IntegrationInstance): + # Update netplan configuration to ensure it doesn't change on reboot + netplan = yaml.safe_load( + client.execute("cat /etc/netplan/50-cloud-init.yaml") + ) + # Just a dummy bridge to do nothing + try: + netplan["network"]["bridges"]["dummy0"] = {"dhcp4": False} + except KeyError: + netplan["network"]["bridges"] = {"dummy0": {"dhcp4": False}} + + dumped_netplan = yaml.dump(netplan) + client.write_to_file("/etc/netplan/50-cloud-init.yaml", dumped_netplan) + + +USER_DATA = """\ +#cloud-config +updates: + network: + when: [boot] +""" + + +@pytest.mark.skipif( + PLATFORM not in ("lxd_container", "lxd_vm"), + reason=( + f"{PLATFORM} could make nic changes in a reboot event invalidating" + f" these tests." + ), +) +@pytest.mark.user_data(USER_DATA) +class TestNetplanGenerateBehaviorOnReboot: + def test_skip(self, client: IntegrationInstance): + log = client.read_from_file("/var/log/cloud-init.log") + assert "Applying network configuration" in log + assert "Selected renderer 'netplan'" in log + client.execute( + "mv /var/log/cloud-init.log /var/log/cloud-init.log.bak" + ) + netplan = yaml.safe_load( + client.execute("cat /etc/netplan/50-cloud-init.yaml") + ) + + client.restart() + + log = client.read_from_file("/var/log/cloud-init.log") + assert "Event Allowed: scope=network EventType=boot" in log + assert "Applying network configuration" in log + assert "Running command ['netplan', 'generate']" not in log + assert ( + "skipping call to `netplan generate`." + " reason: identical netplan config" + ) in log + netplan_new = yaml.safe_load( + client.execute("cat /etc/netplan/50-cloud-init.yaml") + ) + assert netplan == netplan_new, "no changes expected in netplan config" + + def test_applied(self, client: IntegrationInstance): + log = client.read_from_file("/var/log/cloud-init.log") + assert "Applying network configuration" in log + assert "Selected renderer 'netplan'" in log + client.execute( + "mv /var/log/cloud-init.log /var/log/cloud-init.log.bak" + ) + # fake a change in the rendered network config file + _add_dummy_bridge_to_netplan(client) + netplan = yaml.safe_load( + client.execute("cat /etc/netplan/50-cloud-init.yaml") + ) + + client.restart() + + log = client.read_from_file("/var/log/cloud-init.log") + assert "Event Allowed: scope=network EventType=boot" in log + assert "Applying network configuration" in log + assert ( + "skipping call to `netplan generate`." + " reason: identical netplan config" + ) not in log + assert "Running command ['netplan', 'generate']" in log + netplan_new = yaml.safe_load( + client.execute("cat /etc/netplan/50-cloud-init.yaml") + ) + assert netplan != netplan_new, "changes expected in netplan config" diff --git a/tests/unittests/net/test_netplan.py b/tests/unittests/net/test_netplan.py new file mode 100644 index 00000000..28b0891d --- /dev/null +++ b/tests/unittests/net/test_netplan.py @@ -0,0 +1,41 @@ +import os +from unittest import mock + +import pytest + +from cloudinit import util +from cloudinit.net import netplan + + +@pytest.fixture +def renderer(tmp_path): + config = { + "netplan_path": str(tmp_path / "netplan/50-cloud-init.yaml"), + "postcmds": True, + } + yield netplan.Renderer(config) + + +class TestNetplanRenderer: + @pytest.mark.parametrize("write_config", [True, False]) + def test_skip_netplan_generate(self, renderer, write_config, mocker): + """Check `netplan generate` is called if netplan config has changed.""" + header = "\n" + content = "foo" + renderer_mocks = mocker.patch.multiple( + renderer, + _render_content=mocker.Mock(return_value=content), + _netplan_generate=mocker.DEFAULT, + _net_setup_link=mocker.DEFAULT, + ) + if write_config: + util.ensure_dir(os.path.dirname(renderer.netplan_path)) + with open(renderer.netplan_path, "w") as f: + f.write(header) + f.write(content) + + renderer.render_network_state(mocker.Mock()) + + assert renderer_mocks["_netplan_generate"].call_args_list == [ + mock.call(run=True, same_content=write_config) + ] diff --git a/tests/unittests/test_net.py b/tests/unittests/test_net.py index d7640d70..a8b18ee2 100644 --- a/tests/unittests/test_net.py +++ b/tests/unittests/test_net.py @@ -6470,7 +6470,7 @@ class TestNetplanPostcommands(CiTestCase): @mock.patch.object(netplan.Renderer, "_net_setup_link") @mock.patch("cloudinit.subp.subp") def test_netplan_render_calls_postcmds( - self, mock_subp, mock_netplan_generate, mock_net_setup_link + self, mock_subp, mock_net_setup_link, mock_netplan_generate ): tmp_dir = self.tmp_dir() ns = network_state.parse_net_config_data(self.mycfg, skip_broken=False) @@ -6485,7 +6485,7 @@ class TestNetplanPostcommands(CiTestCase): mock_subp.side_effect = iter([subp.ProcessExecutionError]) renderer.render_network_state(ns, target=render_dir) - mock_netplan_generate.assert_called_with(run=True) + mock_netplan_generate.assert_called_with(run=True, same_content=False) mock_net_setup_link.assert_called_with(run=True) @mock.patch("cloudinit.util.SeLinuxGuard") diff --git a/tests/unittests/test_util.py b/tests/unittests/test_util.py index e5243ef3..c23f6399 100644 --- a/tests/unittests/test_util.py +++ b/tests/unittests/test_util.py @@ -3043,3 +3043,24 @@ class TestResolvable: assert util.is_resolvable("http://169.254.169.254/") is True assert util.is_resolvable("http://[fd00:ec2::254]/") is True assert not m_getaddr.called + + +class TestHashBuffer: + def test_in_memory(self): + buf = io.BytesIO(b"hola") + assert ( + util.hash_buffer(buf) + == b"\x99\x80\x0b\x85\xd38>:/\xb4^\xb7\xd0\x06jHy\xa9\xda\xd0" + ) + + def test_file(self, tmp_path): + content = b"hola" + file = tmp_path / "file.txt" + with file.open("wb") as f: + f.write(content) + + with file.open("rb") as f: + assert ( + util.hash_buffer(f) + == b"\x99\x80\x0b\x85\xd38>:/\xb4^\xb7\xd0\x06jHy\xa9\xda\xd0" + ) |