diff --git a/Do-not-write-NM_CONTROLLED-no-in-generated-interface-config.patch b/Do-not-write-NM_CONTROLLED-no-in-generated-interface-config.patch deleted file mode 100644 index 643091fe8f4eb4613fdcb8746c99577f40b7307d..0000000000000000000000000000000000000000 --- a/Do-not-write-NM_CONTROLLED-no-in-generated-interface-config.patch +++ /dev/null @@ -1,544 +0,0 @@ -From 8256852de570a0c6b237c75abd134ddbafee5c1f Mon Sep 17 00:00:00 2001 -From: Eduardo Otubo -Date: Thu, 3 Dec 2020 12:31:50 +0100 -Subject: [PATCH] Do not write NM_CONTROLLED=no in generated interface config - files - -Conflicts 20.3: - - Not appplying patch on cloudinit/net/sysconfig.py since it now has a -mechanism to identify if cloud-init is running on RHEL, having the -correct settings for NM_CONTROLLED. - -X-downstream-only: true -Signed-off-by: Eduardo Otubo -Signed-off-by: Ryan McCabe ---- - cloudinit/net/sysconfig.py | 1 - - tests/unittests/cmd/devel/test_net_convert.py | 1 - - tests/unittests/distros/test_netconfig.py | 8 --- - tests/unittests/test_net.py | 53 ------------------- - 4 files changed, 63 deletions(-) - -diff --git a/cloudinit/net/sysconfig.py b/cloudinit/net/sysconfig.py -index cf55e83..030e6aa 100644 ---- a/cloudinit/net/sysconfig.py -+++ b/cloudinit/net/sysconfig.py -@@ -317,7 +317,6 @@ class Renderer(renderer.Renderer): - "rhel": { - "ONBOOT": True, - "USERCTL": False, -- "NM_CONTROLLED": False, - "BOOTPROTO": "none", - }, - "suse": {"BOOTPROTO": "static", "STARTMODE": "auto"}, -diff --git a/tests/unittests/cmd/devel/test_net_convert.py b/tests/unittests/cmd/devel/test_net_convert.py -index fb72963..7b9121b 100644 ---- a/tests/unittests/cmd/devel/test_net_convert.py -+++ b/tests/unittests/cmd/devel/test_net_convert.py -@@ -62,7 +62,6 @@ SAMPLE_SYSCONFIG_CONTENT = """\ - # - BOOTPROTO=dhcp - DEVICE=eth0 --NM_CONTROLLED=no - ONBOOT=yes - TYPE=Ethernet - USERCTL=no -diff --git a/tests/unittests/distros/test_netconfig.py b/tests/unittests/distros/test_netconfig.py -index 7ba430f..962ff7f 100644 ---- a/tests/unittests/distros/test_netconfig.py -+++ b/tests/unittests/distros/test_netconfig.py -@@ -723,7 +723,6 @@ class TestNetCfgDistroRedhat(TestNetCfgDistroBase): - GATEWAY=192.168.1.254 - IPADDR=192.168.1.5 - NETMASK=255.255.255.0 -- NM_CONTROLLED=no - ONBOOT=yes - TYPE=Ethernet - USERCTL=no -@@ -733,7 +732,6 @@ class TestNetCfgDistroRedhat(TestNetCfgDistroBase): - """\ - BOOTPROTO=dhcp - DEVICE=eth1 -- NM_CONTROLLED=no - ONBOOT=yes - TYPE=Ethernet - USERCTL=no -@@ -764,7 +762,6 @@ class TestNetCfgDistroRedhat(TestNetCfgDistroBase): - IPV6_AUTOCONF=no - IPV6_DEFAULTGW=2607:f0d0:1002:0011::1 - IPV6_FORCE_ACCEPT_RA=no -- NM_CONTROLLED=no - ONBOOT=yes - TYPE=Ethernet - USERCTL=no -@@ -774,7 +771,6 @@ class TestNetCfgDistroRedhat(TestNetCfgDistroBase): - """\ - BOOTPROTO=dhcp - DEVICE=eth1 -- NM_CONTROLLED=no - ONBOOT=yes - TYPE=Ethernet - USERCTL=no -@@ -821,7 +817,6 @@ class TestNetCfgDistroRedhat(TestNetCfgDistroBase): - HWADDR=00:16:3e:60:7c:df - IPADDR=192.10.1.2 - NETMASK=255.255.255.0 -- NM_CONTROLLED=no - ONBOOT=yes - TYPE=Ethernet - USERCTL=no -@@ -833,7 +828,6 @@ class TestNetCfgDistroRedhat(TestNetCfgDistroBase): - DEVICE=infra0 - IPADDR=10.0.1.2 - NETMASK=255.255.0.0 -- NM_CONTROLLED=no - ONBOOT=yes - PHYSDEV=eth0 - USERCTL=no -@@ -869,7 +863,6 @@ class TestNetCfgDistroRedhat(TestNetCfgDistroBase): - DEVICE=eth0 - IPADDR=192.10.1.2 - NETMASK=255.255.255.0 -- NM_CONTROLLED=no - ONBOOT=yes - TYPE=Ethernet - USERCTL=no -@@ -881,7 +874,6 @@ class TestNetCfgDistroRedhat(TestNetCfgDistroBase): - DEVICE=eth0.1001 - IPADDR=10.0.1.2 - NETMASK=255.255.0.0 -- NM_CONTROLLED=no - ONBOOT=yes - PHYSDEV=eth0 - USERCTL=no -diff --git a/tests/unittests/test_net.py b/tests/unittests/test_net.py -index c550953..052b067 100644 ---- a/tests/unittests/test_net.py -+++ b/tests/unittests/test_net.py -@@ -585,7 +585,6 @@ GATEWAY=172.19.3.254 - HWADDR=fa:16:3e:ed:9a:59 - IPADDR=172.19.1.34 - NETMASK=255.255.252.0 --NM_CONTROLLED=no - ONBOOT=yes - TYPE=Ethernet - USERCTL=no -@@ -750,7 +749,6 @@ IPADDR=172.19.1.34 - IPADDR1=10.0.0.10 - NETMASK=255.255.252.0 - NETMASK1=255.255.255.0 --NM_CONTROLLED=no - ONBOOT=yes - TYPE=Ethernet - USERCTL=no -@@ -912,7 +910,6 @@ IPV6_AUTOCONF=no - IPV6_DEFAULTGW=2001:DB8::1 - IPV6_FORCE_ACCEPT_RA=no - NETMASK=255.255.252.0 --NM_CONTROLLED=no - ONBOOT=yes - TYPE=Ethernet - USERCTL=no -@@ -1143,7 +1140,6 @@ NETWORK_CONFIGS = { - BOOTPROTO=none - DEVICE=eth1 - HWADDR=cf:d6:af:48:e8:80 -- NM_CONTROLLED=no - ONBOOT=yes - TYPE=Ethernet - USERCTL=no""" -@@ -1162,7 +1158,6 @@ NETWORK_CONFIGS = { - IPADDR=192.168.21.3 - NETMASK=255.255.255.0 - METRIC=10000 -- NM_CONTROLLED=no - ONBOOT=yes - TYPE=Ethernet - USERCTL=no""" -@@ -1319,7 +1314,6 @@ NETWORK_CONFIGS = { - BOOTPROTO=none - DEVICE=eth1 - HWADDR=cf:d6:af:48:e8:80 -- NM_CONTROLLED=no - ONBOOT=yes - TYPE=Ethernet - USERCTL=no""" -@@ -1338,7 +1332,6 @@ NETWORK_CONFIGS = { - IPADDR=192.168.21.3 - NETMASK=255.255.255.0 - METRIC=10000 -- NM_CONTROLLED=no - ONBOOT=yes - TYPE=Ethernet - USERCTL=no""" -@@ -1581,7 +1574,6 @@ NETWORK_CONFIGS = { - IPV6_AUTOCONF=no - IPV6_FORCE_ACCEPT_RA=no - NETMASK=255.255.255.0 -- NM_CONTROLLED=no - ONBOOT=yes - TYPE=Ethernet - USERCTL=no -@@ -1725,7 +1717,6 @@ NETWORK_CONFIGS = { - DHCPV6C=yes - IPV6INIT=yes - DEVICE=iface0 -- NM_CONTROLLED=no - ONBOOT=yes - TYPE=Ethernet - USERCTL=no -@@ -1816,7 +1807,6 @@ NETWORK_CONFIGS = { - IPV6INIT=yes - IPV6_FORCE_ACCEPT_RA=yes - DEVICE=iface0 -- NM_CONTROLLED=no - ONBOOT=yes - TYPE=Ethernet - USERCTL=no -@@ -1892,7 +1882,6 @@ NETWORK_CONFIGS = { - IPV6INIT=yes - IPV6_FORCE_ACCEPT_RA=no - DEVICE=iface0 -- NM_CONTROLLED=no - ONBOOT=yes - TYPE=Ethernet - USERCTL=no -@@ -1956,7 +1945,6 @@ NETWORK_CONFIGS = { - IPV6_AUTOCONF=yes - IPV6INIT=yes - DEVICE=iface0 -- NM_CONTROLLED=no - ONBOOT=yes - TYPE=Ethernet - USERCTL=no -@@ -2014,7 +2002,6 @@ NETWORK_CONFIGS = { - IPV6_AUTOCONF=no - IPV6_FORCE_ACCEPT_RA=no - DEVICE=iface0 -- NM_CONTROLLED=no - ONBOOT=yes - TYPE=Ethernet - USERCTL=no -@@ -2071,7 +2058,6 @@ NETWORK_CONFIGS = { - IPV6_AUTOCONF=yes - IPV6INIT=yes - DEVICE=iface0 -- NM_CONTROLLED=no - ONBOOT=yes - TYPE=Ethernet - USERCTL=no -@@ -2157,7 +2143,6 @@ NETWORK_CONFIGS = { - IPV6_FAILURE_FATAL=yes - IPV6_FORCE_ACCEPT_RA=yes - DEVICE=iface0 -- NM_CONTROLLED=no - ONBOOT=yes - TYPE=Ethernet - USERCTL=no -@@ -2198,7 +2183,6 @@ NETWORK_CONFIGS = { - """\ - BOOTPROTO=dhcp - DEVICE=iface0 -- NM_CONTROLLED=no - ONBOOT=yes - TYPE=Ethernet - USERCTL=no -@@ -2275,7 +2259,6 @@ NETWORK_CONFIGS = { - BOOTPROTO=dhcp - DEVICE=iface0 - ETHTOOL_OPTS="wol g" -- NM_CONTROLLED=no - ONBOOT=yes - TYPE=Ethernet - USERCTL=no -@@ -2619,7 +2602,6 @@ pre-down route del -net 10.0.0.0/8 gw 11.0.0.1 metric 3 || true - DHCPV6C=yes - IPV6INIT=yes - MACADDR=aa:bb:cc:dd:ee:ff -- NM_CONTROLLED=no - ONBOOT=yes - TYPE=Bond - USERCTL=no""" -@@ -2629,7 +2611,6 @@ pre-down route del -net 10.0.0.0/8 gw 11.0.0.1 metric 3 || true - BOOTPROTO=dhcp - DEVICE=bond0.200 - DHCLIENT_SET_DEFAULT_ROUTE=no -- NM_CONTROLLED=no - ONBOOT=yes - PHYSDEV=bond0 - USERCTL=no -@@ -2649,7 +2630,6 @@ pre-down route del -net 10.0.0.0/8 gw 11.0.0.1 metric 3 || true - IPV6_DEFAULTGW=2001:4800:78ff:1b::1 - MACADDR=bb:bb:bb:bb:bb:aa - NETMASK=255.255.255.0 -- NM_CONTROLLED=no - ONBOOT=yes - PRIO=22 - STP=no -@@ -2661,7 +2641,6 @@ pre-down route del -net 10.0.0.0/8 gw 11.0.0.1 metric 3 || true - BOOTPROTO=none - DEVICE=eth0 - HWADDR=c0:d6:9f:2c:e8:80 -- NM_CONTROLLED=no - ONBOOT=yes - TYPE=Ethernet - USERCTL=no""" -@@ -2680,7 +2659,6 @@ pre-down route del -net 10.0.0.0/8 gw 11.0.0.1 metric 3 || true - MTU=1500 - NETMASK=255.255.255.0 - NETMASK1=255.255.255.0 -- NM_CONTROLLED=no - ONBOOT=yes - PHYSDEV=eth0 - USERCTL=no -@@ -2692,7 +2670,6 @@ pre-down route del -net 10.0.0.0/8 gw 11.0.0.1 metric 3 || true - DEVICE=eth1 - HWADDR=aa:d6:9f:2c:e8:80 - MASTER=bond0 -- NM_CONTROLLED=no - ONBOOT=yes - SLAVE=yes - TYPE=Ethernet -@@ -2704,7 +2681,6 @@ pre-down route del -net 10.0.0.0/8 gw 11.0.0.1 metric 3 || true - DEVICE=eth2 - HWADDR=c0:bb:9f:2c:e8:80 - MASTER=bond0 -- NM_CONTROLLED=no - ONBOOT=yes - SLAVE=yes - TYPE=Ethernet -@@ -2716,7 +2692,6 @@ pre-down route del -net 10.0.0.0/8 gw 11.0.0.1 metric 3 || true - BRIDGE=br0 - DEVICE=eth3 - HWADDR=66:bb:9f:2c:e8:80 -- NM_CONTROLLED=no - ONBOOT=yes - TYPE=Ethernet - USERCTL=no""" -@@ -2727,7 +2702,6 @@ pre-down route del -net 10.0.0.0/8 gw 11.0.0.1 metric 3 || true - BRIDGE=br0 - DEVICE=eth4 - HWADDR=98:bb:9f:2c:e8:80 -- NM_CONTROLLED=no - ONBOOT=yes - TYPE=Ethernet - USERCTL=no""" -@@ -2738,7 +2712,6 @@ pre-down route del -net 10.0.0.0/8 gw 11.0.0.1 metric 3 || true - DEVICE=eth5 - DHCLIENT_SET_DEFAULT_ROUTE=no - HWADDR=98:bb:9f:2c:e8:8a -- NM_CONTROLLED=no - ONBOOT=no - TYPE=Ethernet - USERCTL=no""" -@@ -2751,7 +2724,6 @@ pre-down route del -net 10.0.0.0/8 gw 11.0.0.1 metric 3 || true - IPADDR=192.168.200.7 - MTU=9000 - NETMASK=255.255.255.0 -- NM_CONTROLLED=no - ONBOOT=yes - TYPE=InfiniBand - USERCTL=no""" -@@ -3473,7 +3445,6 @@ iface bond0 inet6 static - MTU=9000 - NETMASK=255.255.255.0 - NETMASK1=255.255.255.0 -- NM_CONTROLLED=no - ONBOOT=yes - TYPE=Bond - USERCTL=no -@@ -3485,7 +3456,6 @@ iface bond0 inet6 static - DEVICE=bond0s0 - HWADDR=aa:bb:cc:dd:e8:00 - MASTER=bond0 -- NM_CONTROLLED=no - ONBOOT=yes - SLAVE=yes - TYPE=Ethernet -@@ -3513,7 +3483,6 @@ iface bond0 inet6 static - DEVICE=bond0s1 - HWADDR=aa:bb:cc:dd:e8:01 - MASTER=bond0 -- NM_CONTROLLED=no - ONBOOT=yes - SLAVE=yes - TYPE=Ethernet -@@ -3662,7 +3631,6 @@ iface bond0 inet6 static - BOOTPROTO=none - DEVICE=en0 - HWADDR=aa:bb:cc:dd:e8:00 -- NM_CONTROLLED=no - ONBOOT=yes - TYPE=Ethernet - USERCTL=no""" -@@ -3683,7 +3651,6 @@ iface bond0 inet6 static - MTU=2222 - NETMASK=255.255.255.0 - NETMASK1=255.255.255.0 -- NM_CONTROLLED=no - ONBOOT=yes - PHYSDEV=en0 - USERCTL=no -@@ -3811,7 +3778,6 @@ iface bond0 inet6 static - DEVICE=br0 - IPADDR=192.168.2.2 - NETMASK=255.255.255.0 -- NM_CONTROLLED=no - ONBOOT=yes - PRIO=22 - STP=no -@@ -3829,7 +3795,6 @@ iface bond0 inet6 static - IPV6INIT=yes - IPV6_AUTOCONF=no - IPV6_FORCE_ACCEPT_RA=no -- NM_CONTROLLED=no - ONBOOT=yes - TYPE=Ethernet - USERCTL=no -@@ -3845,7 +3810,6 @@ iface bond0 inet6 static - IPV6INIT=yes - IPV6_AUTOCONF=no - IPV6_FORCE_ACCEPT_RA=no -- NM_CONTROLLED=no - ONBOOT=yes - TYPE=Ethernet - USERCTL=no -@@ -4030,7 +3994,6 @@ iface bond0 inet6 static - HWADDR=52:54:00:12:34:00 - IPADDR=192.168.1.2 - NETMASK=255.255.255.0 -- NM_CONTROLLED=no - ONBOOT=no - TYPE=Ethernet - USERCTL=no -@@ -4042,7 +4005,6 @@ iface bond0 inet6 static - DEVICE=eth1 - HWADDR=52:54:00:12:34:aa - MTU=1480 -- NM_CONTROLLED=no - ONBOOT=yes - TYPE=Ethernet - USERCTL=no -@@ -4053,7 +4015,6 @@ iface bond0 inet6 static - BOOTPROTO=none - DEVICE=eth2 - HWADDR=52:54:00:12:34:ff -- NM_CONTROLLED=no - ONBOOT=no - TYPE=Ethernet - USERCTL=no -@@ -4138,7 +4099,6 @@ iface bond0 inet6 static - BOOTPROTO=none - DEVICE=eth0 - HWADDR=cf:d6:af:48:e8:80 -- NM_CONTROLLED=no - ONBOOT=yes - TYPE=Ethernet - USERCTL=no""" -@@ -4736,7 +4696,6 @@ class TestRhelSysConfigRendering(CiTestCase): - BOOTPROTO=dhcp - DEVICE=eth1000 - HWADDR=07-1c-c6-75-a4-be --NM_CONTROLLED=no - ONBOOT=yes - TYPE=Ethernet - USERCTL=no -@@ -4948,7 +4907,6 @@ GATEWAY=10.0.2.2 - HWADDR=52:54:00:12:34:00 - IPADDR=10.0.2.15 - NETMASK=255.255.255.0 --NM_CONTROLLED=no - ONBOOT=yes - TYPE=Ethernet - USERCTL=no -@@ -4979,7 +4937,6 @@ HWADDR=fa:16:3e:25:b4:59 - IPADDR=51.68.89.122 - MTU=1500 - NETMASK=255.255.240.0 --NM_CONTROLLED=no - ONBOOT=yes - TYPE=Ethernet - USERCTL=no -@@ -4993,7 +4950,6 @@ DEVICE=eth1 - DHCLIENT_SET_DEFAULT_ROUTE=no - HWADDR=fa:16:3e:b1:ca:29 - MTU=9000 --NM_CONTROLLED=no - ONBOOT=yes - TYPE=Ethernet - USERCTL=no -@@ -5018,7 +4974,6 @@ USERCTL=no - # - BOOTPROTO=dhcp - DEVICE=eth0 --NM_CONTROLLED=no - ONBOOT=yes - TYPE=Ethernet - USERCTL=no -@@ -5251,7 +5206,6 @@ USERCTL=no - IPV6_FORCE_ACCEPT_RA=no - IPV6_DEFAULTGW=2001:db8::1 - NETMASK=255.255.255.0 -- NM_CONTROLLED=no - ONBOOT=yes - TYPE=Ethernet - USERCTL=no -@@ -5283,7 +5237,6 @@ USERCTL=no - """\ - BOOTPROTO=none - DEVICE=eno1 -- NM_CONTROLLED=no - ONBOOT=yes - TYPE=Ethernet - USERCTL=no -@@ -5296,7 +5249,6 @@ USERCTL=no - IPADDR=192.6.1.9 - MTU=1495 - NETMASK=255.255.255.0 -- NM_CONTROLLED=no - ONBOOT=yes - PHYSDEV=eno1 - USERCTL=no -@@ -5332,7 +5284,6 @@ USERCTL=no - IPADDR=10.101.8.65 - MTU=1334 - NETMASK=255.255.255.192 -- NM_CONTROLLED=no - ONBOOT=yes - TYPE=Bond - USERCTL=no -@@ -5344,7 +5295,6 @@ USERCTL=no - BOOTPROTO=none - DEVICE=enp0s0 - MASTER=bond0 -- NM_CONTROLLED=no - ONBOOT=yes - SLAVE=yes - TYPE=Bond -@@ -5357,7 +5307,6 @@ USERCTL=no - BOOTPROTO=none - DEVICE=enp0s1 - MASTER=bond0 -- NM_CONTROLLED=no - ONBOOT=yes - SLAVE=yes - TYPE=Bond -@@ -5388,7 +5337,6 @@ USERCTL=no - DEVICE=eno1 - HWADDR=07-1c-c6-75-a4-be - METRIC=100 -- NM_CONTROLLED=no - ONBOOT=yes - TYPE=Ethernet - USERCTL=no -@@ -5479,7 +5427,6 @@ USERCTL=no - IPV6_FORCE_ACCEPT_RA=no - MTU=1400 - NETMASK=255.255.248.0 -- NM_CONTROLLED=no - ONBOOT=yes - TYPE=Ethernet - USERCTL=no --- -2.27.0 - diff --git a/backport-bug-tests-mock-reads-of-host-s-sys-class-net-via-get.patch b/backport-bug-tests-mock-reads-of-host-s-sys-class-net-via-get.patch deleted file mode 100644 index 932fdb7557f920108c97f4da6756b3557f717f15..0000000000000000000000000000000000000000 --- a/backport-bug-tests-mock-reads-of-host-s-sys-class-net-via-get.patch +++ /dev/null @@ -1,73 +0,0 @@ -From 463d36cc237435084e5ddb4d7468f5546e7ece28 Mon Sep 17 00:00:00 2001 -From: Chad Smith -Date: Wed, 6 Mar 2024 07:51:34 -0700 -Subject: [PATCH] bug(tests): mock reads of host's /sys/class/net via - get_sys_class_path - -Avoid leaking reads to the underlying host's /sys/class/net files. -Some test environments contain virtual network hardware and -configuration such as Calico network devices which provide -duplicated MAC addresses for each device in /sys/class/net. This -results in errors from unittests calling cloudinit.net.get_interfaces. - -Provide a disable_sysfs_net` fixture and use it in net-related -modules or functions to avoid unrelated test failures due to -environmental differences. - -Provide the ability to turn off this fixture for tests which -need to write to the mocked sysfs tmp directory so test artifacts -do not pollute other tests. - -This fixture can be disabled by passing False to the disable_sysfs_net -via request.param. We want to avoid polluting tox.ini with pytest.marks -for infrequently used cases like this. - -Also fix whitespace in tox.ini ---- - tests/unittests/conftest.py | 20 ++++++++++++++++++++ - tox.ini | 2 +- - 2 files changed, 21 insertions(+), 1 deletion(-) - -diff --git a/tests/unittests/conftest.py b/tests/unittests/conftest.py -index 91dbd06..22bc189 100644 ---- a/tests/unittests/conftest.py -+++ b/tests/unittests/conftest.py -@@ -61,6 +61,26 @@ def fake_filesystem(mocker, tmpdir): - mocker.patch.object(mod, f, trap_func) - - -+@pytest.fixture(scope="session", autouse=True) -+def disable_sysfs_net(request, tmpdir_factory): -+ """Avoid tests which read the undertying host's /syc/class/net. -+ -+ To allow unobscured reads of /sys/class/net on the host we can -+ parametrize the fixture with: -+ -+ @pytest.mark.parametrize("disable_sysfs_net", [False], indirect=True) -+ """ -+ if hasattr(request, "param") and getattr(request, "param") is False: -+ # Test disabled this fixture, perform no mocks. -+ yield -+ return -+ mock_sysfs = f"{tmpdir_factory.mktemp('sysfs')}/" -+ with mock.patch( -+ "cloudinit.net.get_sys_class_path", return_value=mock_sysfs -+ ): -+ yield mock_sysfs -+ -+ - @pytest.fixture(autouse=True) - def disable_dns_lookup(request): - if "allow_dns_lookup" in request.keywords: -diff --git a/tox.ini b/tox.ini -index 5f01a9a..cd261f3 100644 ---- a/tox.ini -+++ b/tox.ini -@@ -323,4 +323,4 @@ markers = - serial: tests that do not work in parallel, skipped with py3-fast - unstable: skip this test because it is flakey - user_data: the user data to be passed to the test instance -- allow_dns_lookup: disable autochecking for host network configuration -+ allow_dns_lookup: disable autochecking for host network configuration --- -2.33.0 diff --git a/backport-chore-set-recursive-False-for-ensure_dir-if-parent-p.patch b/backport-chore-set-recursive-False-for-ensure_dir-if-parent-p.patch deleted file mode 100644 index 1571fd3d6d258ea4d69fe5021702cfdd430cfae1..0000000000000000000000000000000000000000 --- a/backport-chore-set-recursive-False-for-ensure_dir-if-parent-p.patch +++ /dev/null @@ -1,30 +0,0 @@ -From b3120f7fefbb772b8fd5f5e8d32ee5377d4aa5cf Mon Sep 17 00:00:00 2001 -From: sxt1001 -Date: Wed, 13 Nov 2024 23:15:39 +0800 -Subject: [PATCH] chore: set recursive=False for ensure_dir if parent path is - "/" (#5816) - ---- - cloudinit/util.py | 6 +++++- - 1 file changed, 5 insertions(+), 1 deletion(-) - -diff --git a/cloudinit/util.py b/cloudinit/util.py -index 8025f4d51..e2f04a402 100644 ---- a/cloudinit/util.py -+++ b/cloudinit/util.py -@@ -1884,7 +1884,11 @@ def ensure_dir(path, mode=None, user=None, group=None): - # Get non existed parent dir first before they are created. - non_existed_parent_dir = get_non_exist_parent_dir(path) - # Make the dir and adjust the mode -- with SeLinuxGuard(os.path.dirname(path), recursive=True): -+ dir_name = os.path.dirname(path) -+ selinux_recursive = True -+ if dir_name == "/": -+ selinux_recursive = False -+ with SeLinuxGuard(dir_name, recursive=selinux_recursive): - os.makedirs(path) - chmod(path, mode) - # Change the ownership --- -2.27.0 - diff --git a/backport-ec2-Do-not-enable-dhcp6-on-EC2.patch b/backport-ec2-Do-not-enable-dhcp6-on-EC2.patch deleted file mode 100644 index 02ecf8bbcceb2a99648841c4820ec1e1b6d342e3..0000000000000000000000000000000000000000 --- a/backport-ec2-Do-not-enable-dhcp6-on-EC2.patch +++ /dev/null @@ -1,117 +0,0 @@ -From f0fb841883b80c71618582e43e1b3cd87a0dcb58 Mon Sep 17 00:00:00 2001 -From: Major Hayden -Date: Mon, 1 Apr 2024 18:28:12 +0000 -Subject: [PATCH] ec2: Do not enable dhcp6 on EC2 (#5104) - -When cloud-init finds any ipv6 information in the instance metadata, it -automatically enables dhcp6 for the network interface. However, this -brings up the instance with a broken IPv6 configuration because SLAAC -should be used for almost all situations on EC2. - -Red Hat BZ: https://bugzilla.redhat.com/show_bug.cgi?id=2092459 -Fedora Pagure: https://pagure.io/cloud-sig/issue/382 -Upstream: https://bugs.launchpad.net/cloud-init/+bug/1976526 - -Fixes GH-3980 - -Signed-off-by: Major Hayden ---- - cloudinit/sources/DataSourceEc2.py | 5 ----- - tests/unittests/sources/test_ec2.py | 15 +++++++-------- - 2 files changed, 7 insertions(+), 13 deletions(-) - -diff --git a/cloudinit/sources/DataSourceEc2.py b/cloudinit/sources/DataSourceEc2.py -index 9e6bfbd..fbc7761 100644 ---- a/cloudinit/sources/DataSourceEc2.py -+++ b/cloudinit/sources/DataSourceEc2.py -@@ -921,8 +921,6 @@ def convert_ec2_metadata_network_config( - "set-name": nic_name, - } - nic_metadata = macs_metadata.get(mac) -- if nic_metadata.get("ipv6s"): # Any IPv6 addresses configured -- dev_config["dhcp6"] = True - netcfg["ethernets"][nic_name] = dev_config - return netcfg - # Apply network config for all nics and any secondary IPv4/v6 addresses -@@ -942,9 +940,6 @@ def convert_ec2_metadata_network_config( - "match": {"macaddress": mac.lower()}, - "set-name": nic_name, - } -- if nic_metadata.get("ipv6s"): # Any IPv6 addresses configured -- dev_config["dhcp6"] = True -- dev_config["dhcp6-overrides"] = dhcp_override - dev_config["addresses"] = get_secondary_addresses(nic_metadata, mac) - if not dev_config["addresses"]: - dev_config.pop("addresses") # Since we found none configured -diff --git a/tests/unittests/sources/test_ec2.py b/tests/unittests/sources/test_ec2.py -index ea8621a..a6801fa 100644 ---- a/tests/unittests/sources/test_ec2.py -+++ b/tests/unittests/sources/test_ec2.py -@@ -432,7 +432,7 @@ class TestEc2(test_helpers.ResponsesTestCase): - "match": {"macaddress": "06:17:04:d7:26:09"}, - "set-name": "eth9", - "dhcp4": True, -- "dhcp6": True, -+ "dhcp6": False, - } - }, - } -@@ -513,7 +513,7 @@ class TestEc2(test_helpers.ResponsesTestCase): - "2600:1f16:292:100:f153:12a3:c37c:11f9/128", - ], - "dhcp4": True, -- "dhcp6": True, -+ "dhcp6": False, - } - }, - } -@@ -593,7 +593,7 @@ class TestEc2(test_helpers.ResponsesTestCase): - "match": {"macaddress": mac1}, - "set-name": "eth9", - "dhcp4": True, -- "dhcp6": True, -+ "dhcp6": False, - } - }, - } -@@ -1001,7 +1001,7 @@ class TestConvertEc2MetadataNetworkConfig(test_helpers.CiTestCase): - "match": {"macaddress": self.mac1}, - "set-name": "eth9", - "dhcp4": True, -- "dhcp6": True, -+ "dhcp6": False, - } - }, - } -@@ -1078,7 +1078,7 @@ class TestConvertEc2MetadataNetworkConfig(test_helpers.CiTestCase): - "match": {"macaddress": self.mac1}, - "set-name": "eth9", - "dhcp4": True, -- "dhcp6": True, -+ "dhcp6": False, - } - }, - } -@@ -1108,8 +1108,7 @@ class TestConvertEc2MetadataNetworkConfig(test_helpers.CiTestCase): - "set-name": "eth9", - "dhcp4": True, - "dhcp4-overrides": {"route-metric": 100}, -- "dhcp6": True, -- "dhcp6-overrides": {"route-metric": 100}, -+ "dhcp6": False, - }, - "eth10": { - "match": {"macaddress": mac2}, -@@ -1140,7 +1139,7 @@ class TestConvertEc2MetadataNetworkConfig(test_helpers.CiTestCase): - "match": {"macaddress": self.mac1}, - "set-name": "eth9", - "dhcp4": True, -- "dhcp6": True, -+ "dhcp6": False, - } - }, - } --- -2.33.0 - - diff --git a/backport-feat-Ensure-random-passwords-contain-multiple-charac.patch b/backport-feat-Ensure-random-passwords-contain-multiple-charac.patch deleted file mode 100644 index 2b0bb4e29e6ffdc934502b2953996f9694b27ede..0000000000000000000000000000000000000000 --- a/backport-feat-Ensure-random-passwords-contain-multiple-charac.patch +++ /dev/null @@ -1,140 +0,0 @@ -From 879945f56103d937a7fee84bfe7662dc2a5be708 Mon Sep 17 00:00:00 2001 -From: sxt1001 -Date: Thu, 17 Oct 2024 20:45:07 +0800 -Subject: [PATCH] feat: Ensure random passwords contain multiple character - types (#5815) - -Reference:https://github.com/canonical/cloud-init/commit/879945f56103d937a7fee84bfe7662dc2a5be708 -Conflict:NA - -The complexity of the random password generated by the -rand_user_password() method may not meet the security configuration -requirements of the system authentication module. This can cause -chpasswd to fail. - -This commit ensures we generate a password using 4 different character -classes. - -Fixes GH-5814 - -Co-authored-by: James Falcon ---- - cloudinit/config/cc_set_passwords.py | 33 +++++++++++++--- - .../unittests/config/test_cc_set_passwords.py | 38 +++++++++++++++++++ - 2 files changed, 66 insertions(+), 5 deletions(-) - -diff --git a/cloudinit/config/cc_set_passwords.py b/cloudinit/config/cc_set_passwords.py -index 24d8267..d46c7f2 100644 ---- a/cloudinit/config/cc_set_passwords.py -+++ b/cloudinit/config/cc_set_passwords.py -@@ -9,7 +9,8 @@ - - import logging - import re --from string import ascii_letters, digits -+import random -+import string - from textwrap import dedent - from typing import List - -@@ -89,9 +90,6 @@ __doc__ = get_meta_doc(meta) - - LOG = logging.getLogger(__name__) - --# We are removing certain 'painful' letters/numbers --PW_SET = "".join([x for x in ascii_letters + digits if x not in "loLOI01"]) -- - - def get_users_by_type(users_list: list, pw_type: str) -> list: - """either password or type: RANDOM is required, user is always required""" -@@ -307,4 +305,29 @@ def handle(name: str, cfg: Config, cloud: Cloud, args: list) -> None: - - - def rand_user_password(pwlen=20): -- return util.rand_str(pwlen, select_from=PW_SET) -+ if pwlen < 4: -+ raise ValueError("Password length must be at least 4 characters.") -+ -+ # There are often restrictions on the minimum number of character -+ # classes required in a password, so ensure we at least one character -+ # from each class. -+ res_rand_list = [ -+ random.choice(string.digits), -+ random.choice(string.ascii_lowercase), -+ random.choice(string.ascii_uppercase), -+ random.choice(string.punctuation), -+ ] -+ -+ res_rand_list.extend( -+ list( -+ util.rand_str( -+ pwlen - len(res_rand_list), -+ select_from=string.digits -+ + string.ascii_lowercase -+ + string.ascii_uppercase -+ + string.punctuation, -+ ) -+ ) -+ ) -+ random.shuffle(res_rand_list) -+ return "".join(res_rand_list) -diff --git a/tests/unittests/config/test_cc_set_passwords.py b/tests/unittests/config/test_cc_set_passwords.py -index ef34a8c..b5d561c 100644 ---- a/tests/unittests/config/test_cc_set_passwords.py -+++ b/tests/unittests/config/test_cc_set_passwords.py -@@ -1,6 +1,7 @@ - # This file is part of cloud-init. See LICENSE file for license information. - - import logging -+import string - from unittest import mock - - import pytest -@@ -555,6 +556,43 @@ class TestExpire: - assert "Expired passwords" not in caplog.text - - -+class TestRandUserPassword: -+ def _get_str_class_num(self, str): -+ return sum( -+ [ -+ any(c.islower() for c in str), -+ any(c.isupper() for c in str), -+ any(c.isupper() for c in str), -+ any(c in string.punctuation for c in str), -+ ] -+ ) -+ -+ @pytest.mark.parametrize( -+ "strlen, expected_result", -+ [ -+ (1, ValueError), -+ (2, ValueError), -+ (3, ValueError), -+ (4, 4), -+ (5, 4), -+ (5, 4), -+ (6, 4), -+ (20, 4), -+ ], -+ ) -+ def test_rand_user_password(self, strlen, expected_result): -+ if expected_result is ValueError: -+ with pytest.raises( -+ expected_result, -+ match="Password length must be at least 4 characters.", -+ ): -+ setpass.rand_user_password(strlen) -+ else: -+ rand_password = setpass.rand_user_password(strlen) -+ assert len(rand_password) == strlen -+ assert self._get_str_class_num(rand_password) == expected_result -+ -+ - class TestSetPasswordsSchema: - @pytest.mark.parametrize( - "config, expectation", --- -2.33.0 - - diff --git a/backport-fix-Ensure-properties-for-bonded-interfaces-are-prop.patch b/backport-fix-Ensure-properties-for-bonded-interfaces-are-prop.patch deleted file mode 100644 index 726e9cb177f8d9b366c8ea842e825bc98ab4c1c9..0000000000000000000000000000000000000000 --- a/backport-fix-Ensure-properties-for-bonded-interfaces-are-prop.patch +++ /dev/null @@ -1,59 +0,0 @@ -From 371b2362bbd78ce53cd1b8f69d55db5855434e61 Mon Sep 17 00:00:00 2001 -From: Curt Moore -Date: Tue, 4 Jun 2024 12:45:32 -0500 -Subject: [PATCH] fix: Ensure properties for bonded interfaces are properly - translated (#5367) - -There is a discrepancy between the properties key name formatting in -the OpenStack network_data.json and cloudinit network-config.json -specifications. Ensure `bond_` is translated to `bond-` when the -OpenStack configuration is parsed by cloudinit. - -Fixes GH-5366 - -Co-authored-by: Alberto Contreras ---- - cloudinit/sources/helpers/openstack.py | 9 ++++++++- - tests/unittests/sources/helpers/test_openstack.py | 6 +++--- - 2 files changed, 11 insertions(+), 4 deletions(-) - -diff --git a/cloudinit/sources/helpers/openstack.py b/cloudinit/sources/helpers/openstack.py -index 69a35db72..70998dda2 100644 ---- a/cloudinit/sources/helpers/openstack.py -+++ b/cloudinit/sources/helpers/openstack.py -@@ -672,7 +672,14 @@ def convert_net_json(network_json=None, known_macs=None): - if k == "bond_links": - continue - elif k.startswith("bond"): -- params.update({k: v}) -+ # There is a difference in key name formatting for -+ # bond parameters in the cloudinit and OpenStack -+ # network schemas. The keys begin with 'bond-' in the -+ # cloudinit schema but 'bond_' in OpenStack -+ # network_data.json schema. Translate them to what -+ # is expected by cloudinit. -+ translated_key = "bond-{}".format(k.split("bond_", 1)[-1]) -+ params.update({translated_key: v}) - - # openstack does not provide a name for the bond. - # they do provide an 'id', but that is possibly non-sensical. -diff --git a/tests/unittests/sources/helpers/test_openstack.py b/tests/unittests/sources/helpers/test_openstack.py -index 312d66a01..663f6c2db 100644 ---- a/tests/unittests/sources/helpers/test_openstack.py -+++ b/tests/unittests/sources/helpers/test_openstack.py -@@ -192,9 +192,9 @@ class TestConvertNetJson: - "name": "bond0", - "mac_address": "xx:xx:xx:xx:xx:00", - "params": { -- "bond_miimon": 100, -- "bond_mode": "802.3ad", -- "bond_xmit_hash_policy": "layer3+4", -+ "bond-miimon": 100, -+ "bond-mode": "802.3ad", -+ "bond-xmit_hash_policy": "layer3+4", - }, - "subnets": [], - "type": "bond", --- -2.27.0 - diff --git a/backport-fix-Fall-back-to-cached-local-ds-if-no-valid-ds-foun.patch b/backport-fix-Fall-back-to-cached-local-ds-if-no-valid-ds-foun.patch deleted file mode 100644 index cc6d8b1559a9b9859fee8c03f21931b9362e7c78..0000000000000000000000000000000000000000 --- a/backport-fix-Fall-back-to-cached-local-ds-if-no-valid-ds-foun.patch +++ /dev/null @@ -1,141 +0,0 @@ -From 9929a00580d50afc60bf4e0fb9f2f39d4f797b4b Mon Sep 17 00:00:00 2001 -From: PengpengSun <40026211+PengpengSun@users.noreply.github.com> -Date: Fri, 29 Mar 2024 22:39:13 +0800 -Subject: [PATCH] fix: Fall back to cached local ds if no valid ds found - (#4997) - -Rebooting an instance which has finished VMware guest -customization with DataSourceVMware will load -DataSourceNone due to metadata is NOT available. - -This is mostly a re-post of PR#229, few differences are: -1. Let ds decide if fallback is allowed, not always fall back - to previous cached LOCAL ds. -2. No comparing instance-id of cached ds with previous instance-id - due to I think they are always identical. - -Fixes GH-3402 ---- - cloudinit/sources/DataSourceVMware.py | 14 ++++++++++++- - cloudinit/sources/__init__.py | 14 +++++++++++++ - cloudinit/stages.py | 40 ++++++++++++++++++++++++------------- - 3 files changed, 53 insertions(+), 15 deletions(-) - -diff --git a/cloudinit/sources/DataSourceVMware.py b/cloudinit/sources/DataSourceVMware.py -index 1591121..2d5d42e 100644 ---- a/cloudinit/sources/DataSourceVMware.py -+++ b/cloudinit/sources/DataSourceVMware.py -@@ -197,7 +197,7 @@ class DataSourceVMware(sources.DataSource): - break - - if not self.data_access_method: -- LOG.error("failed to find a valid data access method") -+ LOG.debug("failed to find a valid data access method") - return False - - LOG.info("using data access method %s", self._get_subplatform()) -@@ -291,6 +291,18 @@ class DataSourceVMware(sources.DataSource): - self.metadata["instance-id"] = str(id_file.read()).rstrip().lower() - return self.metadata["instance-id"] - -+ def check_if_fallback_is_allowed(self): -+ if ( -+ self.data_access_method -+ and self.data_access_method == DATA_ACCESS_METHOD_IMC -+ and is_vmware_platform() -+ ): -+ LOG.debug( -+ "Cache fallback is allowed for : %s", self._get_subplatform() -+ ) -+ return True -+ return False -+ - def get_public_ssh_keys(self): - for key_name in ( - "public-keys-data", -diff --git a/cloudinit/sources/__init__.py b/cloudinit/sources/__init__.py -index c207b5e..453801b 100644 ---- a/cloudinit/sources/__init__.py -+++ b/cloudinit/sources/__init__.py -@@ -312,6 +312,10 @@ class DataSource(CloudInitPickleMixin, metaclass=abc.ABCMeta): - self.vendordata2_raw = None - if not hasattr(self, "skip_hotplug_detect"): - self.skip_hotplug_detect = False -+ -+ if not hasattr(self, "check_if_fallback_is_allowed"): -+ setattr(self, "check_if_fallback_is_allowed", lambda: False) -+ - if hasattr(self, "userdata") and self.userdata is not None: - # If userdata stores MIME data, on < python3.6 it will be - # missing the 'policy' attribute that exists on >=python3.6. -@@ -914,6 +918,16 @@ class DataSource(CloudInitPickleMixin, metaclass=abc.ABCMeta): - # quickly (local check only) if self.instance_id is still - return False - -+ def check_if_fallback_is_allowed(self): -+ """check_if_fallback_is_allowed() -+ Checks if a cached ds is allowed to be restored when no valid ds is -+ found in local mode by checking instance-id and searching valid data -+ through ds list. -+ -+ @return True if a ds allows fallback, False otherwise. -+ """ -+ return False -+ - @staticmethod - def _determine_dsmode(candidates, default=None, valid=None): - # return the first candidate that is non None, warn if not valid -diff --git a/cloudinit/stages.py b/cloudinit/stages.py -index 3b6405f..0b79562 100644 ---- a/cloudinit/stages.py -+++ b/cloudinit/stages.py -@@ -353,20 +353,32 @@ class Init: - LOG.debug(myrep.description) - - if not ds: -- util.del_file(self.paths.instance_link) -- (cfg_list, pkg_list) = self._get_datasources() -- # Deep copy so that user-data handlers can not modify -- # (which will affect user-data handlers down the line...) -- (ds, dsname) = sources.find_source( -- self.cfg, -- self.distro, -- self.paths, -- copy.deepcopy(self.ds_deps), -- cfg_list, -- pkg_list, -- self.reporter, -- ) -- LOG.info("Loaded datasource %s - %s", dsname, ds) -+ try: -+ cfg_list, pkg_list = self._get_datasources() -+ # Deep copy so that user-data handlers can not modify -+ # (which will affect user-data handlers down the line...) -+ ds, dsname = sources.find_source( -+ self.cfg, -+ self.distro, -+ self.paths, -+ copy.deepcopy(self.ds_deps), -+ cfg_list, -+ pkg_list, -+ self.reporter, -+ ) -+ util.del_file(self.paths.instance_link) -+ LOG.info("Loaded datasource %s - %s", dsname, ds) -+ except sources.DataSourceNotFoundException as e: -+ if existing != "check": -+ raise e -+ ds = self._restore_from_cache() -+ if ds and ds.check_if_fallback_is_allowed(): -+ LOG.info( -+ "Restored fallback datasource from checked cache: %s", -+ ds, -+ ) -+ else: -+ raise e - self.datasource = ds - # Ensure we adjust our path members datasource - # now that we have one (thus allowing ipath to be used) --- -2.43.0 - diff --git a/backport-fix-Logging-sensitive-data.patch b/backport-fix-Logging-sensitive-data.patch deleted file mode 100644 index 0e31d45b63a34d7ecfab4794a10a486fece8be80..0000000000000000000000000000000000000000 --- a/backport-fix-Logging-sensitive-data.patch +++ /dev/null @@ -1,56 +0,0 @@ -From 2f9812e805f8e66feaf2689384ea6d669305d9a5 Mon Sep 17 00:00:00 2001 -From: Brett Holman -Date: Wed, 3 Apr 2024 13:51:25 -0600 -Subject: [PATCH] fix: Logging sensitive data - -Don't log sensitive data. - -Since /var/log/cloud-init.log is a priviledged file, this does not expose a -secure system (no CVE). However, we don't want to log this information so that -users can file reports without having to manually redact logs. - -Standardize log messages so that redacted and non-redacted logs match. - -Reference:https://github.com/canonical/cloud-init/commit/2f9812e8 ---- - cloudinit/subp.py | 24 +++++++++--------------- - 1 file changed, 9 insertions(+), 15 deletions(-) - -diff --git a/cloudinit/subp.py b/cloudinit/subp.py -index 85a970f..9347f4f 100644 ---- a/cloudinit/subp.py -+++ b/cloudinit/subp.py -@@ -229,21 +229,15 @@ def subp( - if status_cb: - command = " ".join(args) if isinstance(args, list) else args - status_cb("Begin run command: {command}\n".format(command=command)) -- if not logstring: -- LOG.debug( -- "Running command %s with allowed return codes %s" -- " (shell=%s, capture=%s)", -- args, -- rcs, -- shell, -- "combine" if combine_capture else capture, -- ) -- else: -- LOG.debug( -- "Running hidden command to protect sensitive " -- "input/output logstring: %s", -- logstring, -- ) -+ -+ LOG.debug( -+ "Running command %s with allowed return codes %s" -+ " (shell=%s, capture=%s)", -+ logstring if logstring else args, -+ rcs, -+ shell, -+ "combine" if combine_capture else capture, -+ ) - - stdin: Union[TextIOWrapper, int] - stdout = None --- -2.27.0 - diff --git a/backport-fix-azure-disable-use-dns-for-secondary-nics-5314.patch b/backport-fix-azure-disable-use-dns-for-secondary-nics-5314.patch deleted file mode 100644 index d9959c24e41230948476f5805c1fb66140cfbb8c..0000000000000000000000000000000000000000 --- a/backport-fix-azure-disable-use-dns-for-secondary-nics-5314.patch +++ /dev/null @@ -1,112 +0,0 @@ -From 42930d8459b59b22cab3f76d85d170871174b479 Mon Sep 17 00:00:00 2001 -From: Alberto Contreras -Date: Wed, 29 May 2024 09:10:53 +0200 -Subject: [PATCH] fix(azure): disable use-dns for secondary nics (#5314) - -DNS resolution through secondary NICs is not supported on Azure. Disable -it. - -Without this, we see seconds of delay resolving urls in cloud-init logs -from Jammy+, see SF ticket. - -Per cjp256's comment, the first NIC under metadata.imds.network is -ensured -to be the primary one. We use this to determine primary NICs instead of -relying on fragile driver and/or NIC names. - -Fixes: SF: #00380708 - -Co-authored-by: Calvin Mwadime ---- - cloudinit/sources/DataSourceAzure.py | 3 +++ - tests/unittests/sources/test_azure.py | 20 +++++++++++++------- - 2 files changed, 16 insertions(+), 7 deletions(-) - -diff --git a/cloudinit/sources/DataSourceAzure.py b/cloudinit/sources/DataSourceAzure.py -index 11c14e2..e384b32 100644 ---- a/cloudinit/sources/DataSourceAzure.py -+++ b/cloudinit/sources/DataSourceAzure.py -@@ -1965,6 +1965,9 @@ def generate_network_config_from_instance_network_metadata( - # addresses. - nicname = "eth{idx}".format(idx=idx) - dhcp_override = {"route-metric": (idx + 1) * 100} -+ # DNS resolution through secondary NICs is not supported, disable it. -+ if idx > 0: -+ dhcp_override["use-dns"] = False - dev_config: Dict[str, Any] = { - "dhcp4": True, - "dhcp4-overrides": dhcp_override, -diff --git a/tests/unittests/sources/test_azure.py b/tests/unittests/sources/test_azure.py -index 2a477f8..3a36418 100644 ---- a/tests/unittests/sources/test_azure.py -+++ b/tests/unittests/sources/test_azure.py -@@ -718,14 +718,20 @@ class TestGenerateNetworkConfig: - "match": {"macaddress": "00:0d:3a:04:75:98"}, - "dhcp6": False, - "dhcp4": True, -- "dhcp4-overrides": {"route-metric": 200}, -+ "dhcp4-overrides": { -+ "route-metric": 200, -+ "use-dns": False, -+ }, - }, - "eth2": { - "set-name": "eth2", - "match": {"macaddress": "00:0d:3a:04:75:98"}, - "dhcp6": False, - "dhcp4": True, -- "dhcp4-overrides": {"route-metric": 300}, -+ "dhcp4-overrides": { -+ "route-metric": 300, -+ "use-dns": False, -+ }, - }, - }, - "version": 2, -@@ -952,7 +958,7 @@ class TestNetworkConfig: - "dhcp6": False, - "match": {"macaddress": "00:0d:3a:04:75:98"}, - "set-name": "eth0", -- } -+ }, - }, - "version": 2, - } -@@ -1534,7 +1540,7 @@ scbus-1 on xpt0 bus 0 - "dhcp6": False, - "dhcp4": True, - "dhcp4-overrides": {"route-metric": 100}, -- } -+ }, - }, - "version": 2, - } -@@ -1563,14 +1569,14 @@ scbus-1 on xpt0 bus 0 - "match": {"macaddress": "22:0d:3a:04:75:98"}, - "dhcp6": False, - "dhcp4": True, -- "dhcp4-overrides": {"route-metric": 200}, -+ "dhcp4-overrides": {"route-metric": 200, "use-dns": False}, - }, - "eth2": { - "set-name": "eth2", - "match": {"macaddress": "33:0d:3a:04:75:98"}, - "dhcp6": False, - "dhcp4": True, -- "dhcp4-overrides": {"route-metric": 300}, -+ "dhcp4-overrides": {"route-metric": 300, "use-dns": False}, - }, - }, - "version": 2, -@@ -1603,7 +1609,7 @@ scbus-1 on xpt0 bus 0 - "dhcp6": False, - "dhcp4": True, - "dhcp4-overrides": {"route-metric": 100}, -- } -+ }, - }, - "version": 2, - } --- -2.27.0 - diff --git a/backport-fix-growpart-race-4618.patch b/backport-fix-growpart-race-4618.patch deleted file mode 100644 index ce87a060ad7b67e4ea8069725882d39d552aa489..0000000000000000000000000000000000000000 --- a/backport-fix-growpart-race-4618.patch +++ /dev/null @@ -1,99 +0,0 @@ -From 598e0560d64f949369962ebbce2c53207763f5d2 Mon Sep 17 00:00:00 2001 -From: Brett Holman -Date: Fri, 5 Jan 2024 13:10:01 -0700 -Subject: [PATCH] fix: fix growpart race (#4618) - -Fixes GH-4613 ---- - cloudinit/config/cc_growpart.py | 23 +++++++++++++++++----- - tests/unittests/config/test_cc_growpart.py | 16 +++++++++++++++ - 2 files changed, 34 insertions(+), 5 deletions(-) - -diff --git a/cloudinit/config/cc_growpart.py b/cloudinit/config/cc_growpart.py -index f2e847e..f00e2e9 100644 ---- a/cloudinit/config/cc_growpart.py -+++ b/cloudinit/config/cc_growpart.py -@@ -19,7 +19,7 @@ from abc import ABC, abstractmethod - from contextlib import suppress - from pathlib import Path - from textwrap import dedent --from typing import Tuple -+from typing import Optional, Tuple - - from cloudinit import subp, temp_utils, util - from cloudinit.cloud import Cloud -@@ -283,12 +283,16 @@ class ResizeGpart(Resizer): - return (before, get_size(partdev)) - - --def get_size(filename): -- fd = os.open(filename, os.O_RDONLY) -+def get_size(filename) -> Optional[int]: -+ fd = None - try: -+ fd = os.open(filename, os.O_RDONLY) - return os.lseek(fd, 0, os.SEEK_END) -+ except FileNotFoundError: -+ return None - finally: -- os.close(fd) -+ if fd: -+ os.close(fd) - - - def device_part_info(devpath): -@@ -571,7 +575,7 @@ def resize_devices(resizer, devices): - continue - - try: -- (old, new) = resizer.resize(disk, ptnum, blockdev) -+ old, new = resizer.resize(disk, ptnum, blockdev) - if old == new: - info.append( - ( -@@ -580,6 +584,15 @@ def resize_devices(resizer, devices): - "no change necessary (%s, %s)" % (disk, ptnum), - ) - ) -+ elif new is None or old is None: -+ info.append( -+ ( -+ devent, -+ RESIZE.CHANGED, -+ "changed (%s, %s) size, new size is unknown" -+ % (disk, ptnum), -+ ) -+ ) - else: - info.append( - ( -diff --git a/tests/unittests/config/test_cc_growpart.py b/tests/unittests/config/test_cc_growpart.py -index 5b97f7b..85a4759 100644 ---- a/tests/unittests/config/test_cc_growpart.py -+++ b/tests/unittests/config/test_cc_growpart.py -@@ -389,6 +389,22 @@ class TestResize(unittest.TestCase): - os.stat = real_stat - - -+class TestGetSize: -+ @pytest.mark.parametrize( -+ "file_exists, expected", -+ ( -+ (False, None), -+ (True, 1), -+ ), -+ ) -+ def test_get_size_behaves(self, file_exists, expected, tmp_path): -+ """Ensure that get_size() doesn't raise exception""" -+ tmp_file = tmp_path / "tmp.txt" -+ if file_exists: -+ tmp_file.write_bytes(b"0") -+ assert expected == cc_growpart.get_size(tmp_file) -+ -+ - class TestEncrypted: - """Attempt end-to-end scenarios using encrypted devices. - --- -2.27.0 - diff --git a/backport-fix-net-Make-duplicate-route-add-succeed.-5343.patch b/backport-fix-net-Make-duplicate-route-add-succeed.-5343.patch deleted file mode 100644 index a9a9142f7da3486a11801689d8c208b9cf8a0e02..0000000000000000000000000000000000000000 --- a/backport-fix-net-Make-duplicate-route-add-succeed.-5343.patch +++ /dev/null @@ -1,52 +0,0 @@ -From e432a31d6ea4263027c327559bb08adf3a91ad6d Mon Sep 17 00:00:00 2001 -From: Brett Holman -Date: Wed, 29 May 2024 16:03:46 -0600 -Subject: [PATCH] fix(net): Make duplicate route add succeed. (#5343) - -This behaves the same but doesn't fail when adding an existing route. - -Fixes GH-3441 -Fixes GH-3595 ---- - cloudinit/net/netops/iproute2.py | 2 +- - tests/unittests/net/test_init.py | 4 ++-- - 2 files changed, 3 insertions(+), 3 deletions(-) - -diff --git a/cloudinit/net/netops/iproute2.py b/cloudinit/net/netops/iproute2.py -index 08d79b1..25ccbc1 100644 ---- a/cloudinit/net/netops/iproute2.py -+++ b/cloudinit/net/netops/iproute2.py -@@ -30,7 +30,7 @@ class Iproute2(netops.NetOps): - source_address: Optional[str] = None, - ): - subp.subp( -- ["ip", "-4", "route", "add", route] -+ ["ip", "-4", "route", "replace", route] - + (["via", gateway] if gateway and gateway != "0.0.0.0" else []) - + [ - "dev", -diff --git a/tests/unittests/net/test_init.py b/tests/unittests/net/test_init.py -index 51e54d0..16fe8e9 100644 ---- a/tests/unittests/net/test_init.py -+++ b/tests/unittests/net/test_init.py -@@ -1084,7 +1084,7 @@ class TestEphemeralIPV4Network(CiTestCase): - "ip", - "-4", - "route", -- "add", -+ "replace", - "192.168.2.1", - "dev", - "eth0", -@@ -1097,7 +1097,7 @@ class TestEphemeralIPV4Network(CiTestCase): - "ip", - "-4", - "route", -- "add", -+ "replace", - "default", - "via", - "192.168.2.1", --- -2.27.0 - diff --git a/backport-fix-net-klibc-ipconfig-PROTO-compatibility-5437.patch b/backport-fix-net-klibc-ipconfig-PROTO-compatibility-5437.patch deleted file mode 100644 index 5d2ac84c172f36539e3080126360f00ae9b0eb5d..0000000000000000000000000000000000000000 --- a/backport-fix-net-klibc-ipconfig-PROTO-compatibility-5437.patch +++ /dev/null @@ -1,94 +0,0 @@ -From 2c09f69173d448118b02e013518bf5f1674d3c1f Mon Sep 17 00:00:00 2001 -From: Alexsander de Souza <61709370+alexsander-souza@users.noreply.github.com> -Date: Thu, 27 Jun 2024 12:24:50 -0300 -Subject: [PATCH] fix(net): klibc ipconfig PROTO compatibility (#5437) - -klibc's ipconfig format [1] states that PROTO values 'none', 'off', -'static' and blank all mean no autoconfiguration, but cloud-init parser -is too strict and accepts only the first. - -Reference:https://github.com/canonical/cloud-init/commit/2c09f69173d448118b02e013518bf5f1674d3c1f -Conflict:not change tools/.github-cla-signers - -LP: #2065787 - -[1] https://git.kernel.org/pub/scm/libs/klibc/klibc.git/plain/usr/kinit/ipconfig/README.ipconfig ---- - cloudinit/net/cmdline.py | 3 +++ - tests/unittests/test_net.py | 39 +++++++++++++++++++++++++++++++++++-- - 2 files changed, 40 insertions(+), 2 deletions(-) - -diff --git a/cloudinit/net/cmdline.py b/cloudinit/net/cmdline.py -index 48714e9..23febfe 100644 ---- a/cloudinit/net/cmdline.py -+++ b/cloudinit/net/cmdline.py -@@ -127,6 +127,9 @@ def _klibc_to_config_entry(content, mac_addrs=None): - else: - proto = "none" - -+ if proto in ("static", "off"): -+ proto = "none" -+ - if proto not in ("none", "dhcp", "dhcp6"): - raise ValueError("Unexpected value for PROTO: %s" % proto) - -diff --git a/tests/unittests/test_net.py b/tests/unittests/test_net.py -index 73a4c91..68e2e94 100644 ---- a/tests/unittests/test_net.py -+++ b/tests/unittests/test_net.py -@@ -134,6 +134,37 @@ STATIC_EXPECTED_1 = { - ], - } - -+STATIC_CONTENT_2 = """ -+DEVICE='eth1' -+PROTO='static' -+IPV4ADDR='10.0.0.2' -+IPV4BROADCAST='10.0.0.255' -+IPV4NETMASK='255.255.255.0' -+IPV4GATEWAY='10.0.0.1' -+IPV4DNS0='10.0.1.1' -+IPV4DNS1='0.0.0.0' -+HOSTNAME='foohost' -+UPTIME='21' -+DHCPLEASETIME='3600' -+DOMAINSEARCH='foo.com' -+""" -+ -+STATIC_CONTENT_3 = """ -+DEVICE='eth1' -+PROTO='off' -+IPV4ADDR='10.0.0.2' -+IPV4BROADCAST='10.0.0.255' -+IPV4NETMASK='255.255.255.0' -+IPV4GATEWAY='10.0.0.1' -+IPV4DNS0='10.0.1.1' -+IPV4DNS1='0.0.0.0' -+HOSTNAME='foohost' -+UPTIME='21' -+DHCPLEASETIME='3600' -+DOMAINSEARCH='foo.com' -+""" -+ -+ - V1_NAMESERVER_ALIAS = """ - config: - - id: eno1 -@@ -6891,8 +6922,12 @@ class TestCmdlineConfigParsing(CiTestCase): - self.assertEqual(found, ("eno1", DHCP6_EXPECTED_1)) - - def test_cmdline_convert_static(self): -- found = cmdline._klibc_to_config_entry(STATIC_CONTENT_1) -- self.assertEqual(found, ("eth1", STATIC_EXPECTED_1)) -+ found1 = cmdline._klibc_to_config_entry(STATIC_CONTENT_1) -+ assert found1 == ("eth1", STATIC_EXPECTED_1) -+ found2 = cmdline._klibc_to_config_entry(STATIC_CONTENT_2) -+ assert found2 == ("eth1", STATIC_EXPECTED_1) -+ found3 = cmdline._klibc_to_config_entry(STATIC_CONTENT_3) -+ assert found3 == ("eth1", STATIC_EXPECTED_1) - - def test_config_from_cmdline_net_cfg(self): - files = [] --- -2.43.0 - diff --git a/backport-fix-netplan-Fix-predictable-interface-rename-issue-5.patch b/backport-fix-netplan-Fix-predictable-interface-rename-issue-5.patch deleted file mode 100644 index 62e70897339266ba2832e385c6346cba7d00bb40..0000000000000000000000000000000000000000 --- a/backport-fix-netplan-Fix-predictable-interface-rename-issue-5.patch +++ /dev/null @@ -1,56 +0,0 @@ -From 2856f4c8a440eba1127ac09f2b411d436c62e777 Mon Sep 17 00:00:00 2001 -From: Brett Holman -Date: Wed, 29 May 2024 16:08:35 -0600 -Subject: [PATCH] fix(netplan): Fix predictable interface rename issue -(#5339) - -When predictable naming is disabled, the following command may exit with -a non-zero exit code. - -udevadm test-builtin net_setup_link - -This code only ran to check for udev rename races, which cannot happen -when systemd renaming is disabled. Skip when disabled. - -Fixes GH-3950 ---- - cloudinit/net/netplan.py | 3 +++ - tests/unittests/test_net.py | 5 ++++- - 2 files changed, 7 insertions(+), 1 deletion(-) - -diff --git a/cloudinit/net/netplan.py b/cloudinit/net/netplan.py -index 0b8419a..aea8a67 100644 ---- a/cloudinit/net/netplan.py -+++ b/cloudinit/net/netplan.py -@@ -329,6 +329,9 @@ class Renderer(renderer.Renderer): - if not run: - LOG.debug("netplan net_setup_link postcmd disabled") - return -+ elif "net.ifnames=0" in util.get_cmdline(): -+ LOG.debug("Predictable interface names disabled.") -+ return - setup_lnk = ["udevadm", "test-builtin", "net_setup_link"] - - # It's possible we can race a udev rename and attempt to run -diff --git a/tests/unittests/test_net.py b/tests/unittests/test_net.py -index 052b067..73a4c91 100644 ---- a/tests/unittests/test_net.py -+++ b/tests/unittests/test_net.py -@@ -6782,10 +6782,13 @@ class TestNetplanPostcommands(CiTestCase): - mock_netplan_generate.assert_called_with(run=True, same_content=False) - mock_net_setup_link.assert_called_with(run=True) - -+ @mock.patch("cloudinit.util.get_cmdline") - @mock.patch("cloudinit.util.SeLinuxGuard") - @mock.patch.object(netplan, "get_devicelist") - @mock.patch("cloudinit.subp.subp") -- def test_netplan_postcmds(self, mock_subp, mock_devlist, mock_sel): -+ def test_netplan_postcmds( -+ self, mock_subp, mock_devlist, mock_sel, m_get_cmdline -+ ): - mock_sel.__enter__ = mock.Mock(return_value=False) - mock_sel.__exit__ = mock.Mock() - mock_devlist.side_effect = [["lo"]] --- -2.27.0 - diff --git a/backport-fix-openstack-Fix-bond-mac_address-5369.patch b/backport-fix-openstack-Fix-bond-mac_address-5369.patch deleted file mode 100644 index 8f0be4808f76dadab983b4be89f5b5be0cd83035..0000000000000000000000000000000000000000 --- a/backport-fix-openstack-Fix-bond-mac_address-5369.patch +++ /dev/null @@ -1,29 +0,0 @@ -From 12f1198e8e9e884363b14eeaaf6eb69b7199c36a Mon Sep 17 00:00:00 2001 -From: Curt Moore -Date: Tue, 4 Jun 2024 14:37:43 -0500 -Subject: [PATCH] fix(openstack): Fix bond mac_address (#5369) - -Reference:https://github.com/canonical/cloud-init/commit/12f1198e8e9e884363b14eeaaf6eb69b7199c36a -Conflict:tools/.github-cla-signers not change. - -Fixes GH-5368 ---- - cloudinit/sources/helpers/openstack.py | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/cloudinit/sources/helpers/openstack.py b/cloudinit/sources/helpers/openstack.py -index d2260ba..ef29eb7 100644 ---- a/cloudinit/sources/helpers/openstack.py -+++ b/cloudinit/sources/helpers/openstack.py -@@ -663,7 +663,7 @@ def convert_net_json(network_json=None, known_macs=None): - if link["type"] in ["bond"]: - params = {} - if link_mac_addr: -- params["mac_address"] = link_mac_addr -+ cfg.update({"mac_address": link_mac_addr}) - for k, v in link.items(): - if k == "bond_links": - continue --- -2.43.0 - diff --git a/backport-fix-properly-handle-blank-lines-in-fstab-5643.patch b/backport-fix-properly-handle-blank-lines-in-fstab-5643.patch deleted file mode 100644 index 75f6a52a39be77de1f2f91474b4e0a7f1cd0e051..0000000000000000000000000000000000000000 --- a/backport-fix-properly-handle-blank-lines-in-fstab-5643.patch +++ /dev/null @@ -1,33 +0,0 @@ -From 93f30bbfcb073fd8213c18c2e7eb7f857234fc8a Mon Sep 17 00:00:00 2001 -From: James Falcon -Date: Thu, 29 Aug 2024 18:22:23 -0400 -Subject: [PATCH] fix: properly handle blank lines in fstab (#5643) - -Reference:https://github.com/canonical/cloud-init/commit/93f30bbfcb073fd8213c18c2e7eb7f857234fc8a -Conflict:(1)not change test, the corresponding test case does not exist. -(2)change handle() not parse_fstab(), diff commit is d15a770. - ---- - cloudinit/config/cc_mounts.py | 5 +++-- - 1 file changed, 3 insertions(+), 2 deletions(-) - -diff --git a/cloudinit/config/cc_mounts.py b/cloudinit/config/cc_mounts.py -index 4efa2a2..1cd53ef 100644 ---- a/cloudinit/config/cc_mounts.py -+++ b/cloudinit/config/cc_mounts.py -@@ -459,8 +459,9 @@ def handle(name: str, cfg: Config, cloud: Cloud, args: list) -> None: - toks = WS.split(line) - except Exception: - pass -- fstab_devs[toks[0]] = line -- fstab_lines.append(line) -+ if toks: -+ fstab_devs[toks[0]] = line -+ fstab_lines.append(line) - - device_aliases = cfg.get("device_aliases", {}) - --- -2.33.0 - - diff --git a/backport-fix-unpin-jsonschema-and-update-tests.patch b/backport-fix-unpin-jsonschema-and-update-tests.patch deleted file mode 100644 index 1d1990faac7991e4e0a7862f104d439a747708e9..0000000000000000000000000000000000000000 --- a/backport-fix-unpin-jsonschema-and-update-tests.patch +++ /dev/null @@ -1,457 +0,0 @@ -From c948e4182b9557f2befeb6210a055d0aa1c2df21 Mon Sep 17 00:00:00 2001 -From: James Falcon -Date: Wed, 14 Feb 2024 16:25:11 -0600 -Subject: [PATCH] fix: unpin jsonschema and update tests (#4882) - -In 034a5cd , we pinned jsonschema version due to failing tests. Test -failures were due to jsonschema library changing error messages. -This commit unpins the version and updates tests accordingly. - -Fixes GH-4783 - -Co-authored-by: dermotbradley ---- - .../unittests/config/test_cc_apk_configure.py | 5 +++-- - .../unittests/config/test_cc_apt_configure.py | 14 ++++++------- - tests/unittests/config/test_cc_bootcmd.py | 17 +++++++++++----- - tests/unittests/config/test_cc_ca_certs.py | 10 +++++++--- - tests/unittests/config/test_cc_chef.py | 5 +++-- - tests/unittests/config/test_cc_lxd.py | 2 +- - tests/unittests/config/test_cc_mounts.py | 10 ++++++++-- - .../test_cc_package_update_upgrade_install.py | 3 ++- - tests/unittests/config/test_cc_runcmd.py | 3 ++- - .../unittests/config/test_cc_set_passwords.py | 9 +++++++-- - tests/unittests/config/test_cc_snap.py | 20 +++++++++++++------ - tests/unittests/config/test_cc_write_files.py | 6 +++++- - .../unittests/config/test_cc_yum_add_repo.py | 2 +- - tests/unittests/helpers.py | 8 ++++++++ - tests/unittests/test_merging.py | 2 +- - 15 files changed, 81 insertions(+), 35 deletions(-) - -diff --git a/tests/unittests/config/test_cc_apk_configure.py b/tests/unittests/config/test_cc_apk_configure.py -index 8854670..e9533a1 100644 ---- a/tests/unittests/config/test_cc_apk_configure.py -+++ b/tests/unittests/config/test_cc_apk_configure.py -@@ -18,6 +18,7 @@ from cloudinit.config.schema import ( - validate_cloudconfig_schema, - ) - from tests.unittests.helpers import ( -+ SCHEMA_EMPTY_ERROR, - FilesystemMockingTestCase, - mock, - skipUnlessJsonSchema, -@@ -355,7 +356,7 @@ class TestApkConfigureSchema: - ( - {"apk_repos": {"alpine_repo": {}}}, - "apk_repos.alpine_repo: 'version' is a required property," -- " apk_repos.alpine_repo: {} does not have enough properties", -+ f" apk_repos.alpine_repo: {{}} {SCHEMA_EMPTY_ERROR}", - ), - ( - {"apk_repos": {"alpine_repo": True}}, -@@ -368,7 +369,7 @@ class TestApkConfigureSchema: - ), - ( - {"apk_repos": {}}, -- "apk_repos: {} does not have enough properties", -+ f"apk_repos: {{}} {SCHEMA_EMPTY_ERROR}", - ), - ( - {"apk_repos": {"local_repo_base_url": None}}, -diff --git a/tests/unittests/config/test_cc_apt_configure.py b/tests/unittests/config/test_cc_apt_configure.py -index 18e6663..4fff316 100644 ---- a/tests/unittests/config/test_cc_apt_configure.py -+++ b/tests/unittests/config/test_cc_apt_configure.py -@@ -12,7 +12,7 @@ from cloudinit.config.schema import ( - get_schema, - validate_cloudconfig_schema, - ) --from tests.unittests.helpers import skipUnlessJsonSchema -+from tests.unittests.helpers import SCHEMA_EMPTY_ERROR, skipUnlessJsonSchema - from tests.unittests.util import get_cloud - - -@@ -34,7 +34,7 @@ class TestAPTConfigureSchema: - " ('boguskey' was unexpected)" - ), - ), -- ({"apt": {}}, "apt: {} does not have enough properties"), -+ ({"apt": {}}, f"apt: {{}} {SCHEMA_EMPTY_ERROR}"), - ( - {"apt": {"preserve_sources_list": 1}}, - "apt.preserve_sources_list: 1 is not of type 'boolean'", -@@ -45,7 +45,7 @@ class TestAPTConfigureSchema: - ), - ( - {"apt": {"disable_suites": []}}, -- re.escape("apt.disable_suites: [] is too short"), -+ re.escape("apt.disable_suites: [] ") + SCHEMA_EMPTY_ERROR, - ), - ( - {"apt": {"disable_suites": [1]}}, -@@ -65,7 +65,7 @@ class TestAPTConfigureSchema: - ), - ( - {"apt": {"primary": []}}, -- re.escape("apt.primary: [] is too short"), -+ re.escape("apt.primary: [] ") + SCHEMA_EMPTY_ERROR, - ), - ( - {"apt": {"primary": ["nonobj"]}}, -@@ -102,7 +102,7 @@ class TestAPTConfigureSchema: - ), - ( - {"apt": {"primary": [{"arches": ["amd64"], "search": []}]}}, -- re.escape("apt.primary.0.search: [] is too short"), -+ re.escape("apt.primary.0.search: [] ") + SCHEMA_EMPTY_ERROR, - ), - ( - { -@@ -134,7 +134,7 @@ class TestAPTConfigureSchema: - ), - ( - {"apt": {"debconf_selections": {}}}, -- "apt.debconf_selections: {} does not have enough properties", -+ f"apt.debconf_selections: {{}} {SCHEMA_EMPTY_ERROR}", - ), - ( - {"apt": {"sources_list": True}}, -@@ -170,7 +170,7 @@ class TestAPTConfigureSchema: - ), - ( - {"apt": {"sources": {"opaquekey": {}}}}, -- "apt.sources.opaquekey: {} does not have enough properties", -+ f"apt.sources.opaquekey: {{}} {SCHEMA_EMPTY_ERROR}", - ), - ( - {"apt": {"sources": {"opaquekey": {"boguskey": True}}}}, -diff --git a/tests/unittests/config/test_cc_bootcmd.py b/tests/unittests/config/test_cc_bootcmd.py -index f6772df..b938732 100644 ---- a/tests/unittests/config/test_cc_bootcmd.py -+++ b/tests/unittests/config/test_cc_bootcmd.py -@@ -11,7 +11,12 @@ from cloudinit.config.schema import ( - get_schema, - validate_cloudconfig_schema, - ) --from tests.unittests.helpers import CiTestCase, mock, skipUnlessJsonSchema -+from tests.unittests.helpers import ( -+ SCHEMA_EMPTY_ERROR, -+ CiTestCase, -+ mock, -+ skipUnlessJsonSchema, -+) - from tests.unittests.util import get_cloud - - -@@ -128,12 +133,14 @@ class TestBootCMDSchema: - "Cloud config schema errors: bootcmd: 1 is not of type" - " 'array'", - ), -- ({"bootcmd": []}, re.escape("bootcmd: [] is too short")), - ( - {"bootcmd": []}, -- re.escape( -- "Cloud config schema errors: bootcmd: [] is too short" -- ), -+ re.escape("bootcmd: [] ") + SCHEMA_EMPTY_ERROR, -+ ), -+ ( -+ {"bootcmd": []}, -+ re.escape("Cloud config schema errors: bootcmd: [] ") -+ + SCHEMA_EMPTY_ERROR, - ), - ( - { -diff --git a/tests/unittests/config/test_cc_ca_certs.py b/tests/unittests/config/test_cc_ca_certs.py -index b93fda7..cb5712b 100644 ---- a/tests/unittests/config/test_cc_ca_certs.py -+++ b/tests/unittests/config/test_cc_ca_certs.py -@@ -17,7 +17,11 @@ from cloudinit.config.schema import ( - get_schema, - validate_cloudconfig_schema, - ) --from tests.unittests.helpers import TestCase, skipUnlessJsonSchema -+from tests.unittests.helpers import ( -+ SCHEMA_EMPTY_ERROR, -+ TestCase, -+ skipUnlessJsonSchema, -+) - from tests.unittests.util import get_cloud - - -@@ -398,7 +402,7 @@ class TestCACertsSchema: - ), - ( - {"ca_certs": {}}, -- re.escape("ca_certs: {} does not have enough properties"), -+ re.escape("ca_certs: {} ") + SCHEMA_EMPTY_ERROR, - ), - ( - {"ca_certs": {"boguskey": 1}}, -@@ -417,7 +421,7 @@ class TestCACertsSchema: - ), - ( - {"ca_certs": {"trusted": []}}, -- re.escape("ca_certs.trusted: [] is too short"), -+ re.escape("ca_certs.trusted: [] ") + SCHEMA_EMPTY_ERROR, - ), - ), - ) -diff --git a/tests/unittests/config/test_cc_chef.py b/tests/unittests/config/test_cc_chef.py -index 9d8ba1f..6fad6a7 100644 ---- a/tests/unittests/config/test_cc_chef.py -+++ b/tests/unittests/config/test_cc_chef.py -@@ -15,6 +15,7 @@ from cloudinit.config.schema import ( - validate_cloudconfig_schema, - ) - from tests.unittests.helpers import ( -+ SCHEMA_EMPTY_ERROR, - FilesystemMockingTestCase, - ResponsesTestCase, - cloud_init_project_dir, -@@ -306,7 +307,7 @@ class TestBootCMDSchema: - ), - ( - {"chef": {}}, -- re.escape(" chef: {} does not have enough properties"), -+ re.escape(" chef: {} ") + SCHEMA_EMPTY_ERROR, - ), - ( - {"chef": {"boguskey": True}}, -@@ -321,7 +322,7 @@ class TestBootCMDSchema: - ), - ( - {"chef": {"directories": []}}, -- re.escape("chef.directories: [] is too short"), -+ re.escape("chef.directories: [] ") + SCHEMA_EMPTY_ERROR, - ), - ( - {"chef": {"directories": [1]}}, -diff --git a/tests/unittests/config/test_cc_lxd.py b/tests/unittests/config/test_cc_lxd.py -index 648fc33..320bfdb 100644 ---- a/tests/unittests/config/test_cc_lxd.py -+++ b/tests/unittests/config/test_cc_lxd.py -@@ -385,7 +385,7 @@ class TestLXDSchema: - # Require bridge.mode - ({"lxd": {"bridge": {}}}, "bridge: 'mode' is a required property"), - # Require init or bridge keys -- ({"lxd": {}}, "lxd: {} does not have enough properties"), -+ ({"lxd": {}}, f"lxd: {{}} {t_help.SCHEMA_EMPTY_ERROR}"), - # Require some non-empty preseed config of type string - ({"lxd": {"preseed": {}}}, "not of type 'string'"), - ({"lxd": {"preseed": ""}}, None), -diff --git a/tests/unittests/config/test_cc_mounts.py b/tests/unittests/config/test_cc_mounts.py -index a2dada8..4795357 100644 ---- a/tests/unittests/config/test_cc_mounts.py -+++ b/tests/unittests/config/test_cc_mounts.py -@@ -583,9 +583,15 @@ class TestMountsSchema: - "config, error_msg", - [ - # We expect to see one mount if provided in user-data. -- ({"mounts": []}, re.escape("mounts: [] is too short")), -+ ( -+ {"mounts": []}, -+ re.escape("mounts: [] ") + test_helpers.SCHEMA_EMPTY_ERROR, -+ ), - # Disallow less than 1 item per mount entry -- ({"mounts": [[]]}, re.escape("mounts.0: [] is too short")), -+ ( -+ {"mounts": [[]]}, -+ re.escape("mounts.0: [] ") + test_helpers.SCHEMA_EMPTY_ERROR, -+ ), - # Disallow more than 6 items per mount entry - ({"mounts": [["1"] * 7]}, "mounts.0:.* is too long"), - # Disallow mount_default_fields will anything other than 6 items -diff --git a/tests/unittests/config/test_cc_package_update_upgrade_install.py b/tests/unittests/config/test_cc_package_update_upgrade_install.py -index 9ba7f17..b3d43a1 100644 ---- a/tests/unittests/config/test_cc_package_update_upgrade_install.py -+++ b/tests/unittests/config/test_cc_package_update_upgrade_install.py -@@ -14,6 +14,7 @@ from cloudinit.config.schema import ( - from cloudinit.distros import PackageInstallerError - from cloudinit.subp import SubpResult - from tests.unittests.helpers import skipUnlessJsonSchema -+from tests.unittests.helpers import SCHEMA_EMPTY_ERROR - from tests.unittests.util import get_cloud - - -@@ -188,7 +189,7 @@ class TestPackageUpdateUpgradeSchema: - # packages list with three entries (2 required) - ({"packages": ["p1", ["p2", "p3", "p4"]]}, ""), - # empty packages list -- ({"packages": []}, "is too short"), -+ ({"packages": []}, SCHEMA_EMPTY_ERROR), - ( - {"apt_update": False}, - ( -diff --git a/tests/unittests/config/test_cc_runcmd.py b/tests/unittests/config/test_cc_runcmd.py -index a8636bb..f69cd9d 100644 ---- a/tests/unittests/config/test_cc_runcmd.py -+++ b/tests/unittests/config/test_cc_runcmd.py -@@ -14,6 +14,7 @@ from cloudinit.config.schema import ( - validate_cloudconfig_schema, - ) - from tests.unittests.helpers import ( -+ SCHEMA_EMPTY_ERROR, - FilesystemMockingTestCase, - skipUnlessJsonSchema, - ) -@@ -90,7 +91,7 @@ class TestRunCmdSchema: - ({"runcmd": ["echo bye", "echo bye"]}, None), - # Invalid schemas - ({"runcmd": 1}, "1 is not of type 'array'"), -- ({"runcmd": []}, r"runcmd: \[\] is too short"), -+ ({"runcmd": []}, rf"runcmd: \[\] {SCHEMA_EMPTY_ERROR}"), - ( - { - "runcmd": [ -diff --git a/tests/unittests/config/test_cc_set_passwords.py b/tests/unittests/config/test_cc_set_passwords.py -index 1a9fcd3..ef34a8c 100644 ---- a/tests/unittests/config/test_cc_set_passwords.py -+++ b/tests/unittests/config/test_cc_set_passwords.py -@@ -12,7 +12,11 @@ from cloudinit.config.schema import ( - get_schema, - validate_cloudconfig_schema, - ) --from tests.unittests.helpers import does_not_raise, skipUnlessJsonSchema -+from tests.unittests.helpers import ( -+ SCHEMA_EMPTY_ERROR, -+ does_not_raise, -+ skipUnlessJsonSchema, -+) - from tests.unittests.util import get_cloud - - MODPATH = "cloudinit.config.cc_set_passwords." -@@ -718,7 +722,8 @@ class TestSetPasswordsSchema: - ( - {"chpasswd": {"list": []}}, - pytest.raises( -- SchemaValidationError, match=r"\[\] is too short" -+ SchemaValidationError, -+ match=rf"\[\] {SCHEMA_EMPTY_ERROR}", - ), - ), - ], -diff --git a/tests/unittests/config/test_cc_snap.py b/tests/unittests/config/test_cc_snap.py -index 65088dd..573ade9 100644 ---- a/tests/unittests/config/test_cc_snap.py -+++ b/tests/unittests/config/test_cc_snap.py -@@ -14,7 +14,12 @@ from cloudinit.config.schema import ( - get_schema, - validate_cloudconfig_schema, - ) --from tests.unittests.helpers import CiTestCase, mock, skipUnlessJsonSchema -+from tests.unittests.helpers import ( -+ SCHEMA_EMPTY_ERROR, -+ CiTestCase, -+ mock, -+ skipUnlessJsonSchema, -+) - from tests.unittests.util import get_cloud - - M_PATH = "cloudinit.config.cc_snap." -@@ -288,15 +293,18 @@ class TestSnapSchema: - {"snap": {"commands": ["ls"], "invalid-key": ""}}, - "Additional properties are not allowed", - ), -- ({"snap": {}}, "{} does not have enough properties"), -+ ({"snap": {}}, f"{{}} {SCHEMA_EMPTY_ERROR}"), - ( - {"snap": {"commands": "broken"}}, - "'broken' is not of type 'object', 'array'", - ), -- ({"snap": {"commands": []}}, r"snap.commands: \[\] is too short"), -+ ( -+ {"snap": {"commands": []}}, -+ rf"snap.commands: \[\] {SCHEMA_EMPTY_ERROR}", -+ ), - ( - {"snap": {"commands": {}}}, -- r"snap.commands: {} does not have enough properties", -+ rf"snap.commands: {{}} {SCHEMA_EMPTY_ERROR}", - ), - ({"snap": {"commands": [123]}}, ""), - ({"snap": {"commands": {"01": 123}}}, ""), -@@ -311,10 +319,10 @@ class TestSnapSchema: - {"snap": {"assertions": "broken"}}, - "'broken' is not of type 'object', 'array'", - ), -- ({"snap": {"assertions": []}}, r"\[\] is too short"), -+ ({"snap": {"assertions": []}}, rf"\[\] {SCHEMA_EMPTY_ERROR}"), - ( - {"snap": {"assertions": {}}}, -- r"\{} does not have enough properties", -+ rf"\{{}} {SCHEMA_EMPTY_ERROR}", - ), - ], - ) -diff --git a/tests/unittests/config/test_cc_write_files.py b/tests/unittests/config/test_cc_write_files.py -index 210edf7..ea9cf8a 100644 ---- a/tests/unittests/config/test_cc_write_files.py -+++ b/tests/unittests/config/test_cc_write_files.py -@@ -18,6 +18,7 @@ from cloudinit.config.schema import ( - validate_cloudconfig_schema, - ) - from tests.unittests.helpers import ( -+ SCHEMA_EMPTY_ERROR, - CiTestCase, - FilesystemMockingTestCase, - skipUnlessJsonSchema, -@@ -222,7 +223,10 @@ class TestWriteFilesSchema: - [ - # Top-level write_files type validation - ({"write_files": 1}, "write_files: 1 is not of type 'array'"), -- ({"write_files": []}, re.escape("write_files: [] is too short")), -+ ( -+ {"write_files": []}, -+ re.escape("write_files: [] ") + SCHEMA_EMPTY_ERROR, -+ ), - ( - {"write_files": [{}]}, - "write_files.0: 'path' is a required property", -diff --git a/tests/unittests/config/test_cc_yum_add_repo.py b/tests/unittests/config/test_cc_yum_add_repo.py -index d2c2912..e7392f2 100644 ---- a/tests/unittests/config/test_cc_yum_add_repo.py -+++ b/tests/unittests/config/test_cc_yum_add_repo.py -@@ -139,7 +139,7 @@ class TestAddYumRepoSchema: - ), - ( - {"yum_repos": {}}, -- re.escape("yum_repos: {} does not have enough properties"), -+ re.escape("yum_repos: {} ") + helpers.SCHEMA_EMPTY_ERROR, - ), - # baseurl required - ( -diff --git a/tests/unittests/helpers.py b/tests/unittests/helpers.py -index 96f407f..42cc2e9 100644 ---- a/tests/unittests/helpers.py -+++ b/tests/unittests/helpers.py -@@ -47,6 +47,14 @@ except ImportError: - HAS_APT_PKG = False - - -+# Used by tests to verify the error message when a jsonschema structure -+# is empty but should not be. -+# Version 4.20.0 of jsonschema changed the error messages for empty structures. -+SCHEMA_EMPTY_ERROR = ( -+ "(is too short|should be non-empty|does not have enough properties)" -+) -+ -+ - # Makes the old path start - # with new base instead of whatever - # it previously had -diff --git a/tests/unittests/test_merging.py b/tests/unittests/test_merging.py -index 891031e..52abaa7 100644 ---- a/tests/unittests/test_merging.py -+++ b/tests/unittests/test_merging.py -@@ -270,7 +270,7 @@ class TestMergingSchema: - [ - ({"merge_how": "list()+dict()+str()"}, None), - ({"merge_type": "list()+dict()+str()"}, None), -- ({"merge_how": []}, "\\[\\] is too short"), -+ ({"merge_how": []}, f"\\[\\] {helpers.SCHEMA_EMPTY_ERROR}"), - ( - {"merge_how": {"name": "list", "settings": ["append"]}}, - "is not of type", --- -2.27.0 diff --git a/backport-handle-error-when-log-file-is-empty-4859.patch b/backport-handle-error-when-log-file-is-empty-4859.patch deleted file mode 100644 index 153f8bab780601e50f13f36d36c2dd3a7c60f244..0000000000000000000000000000000000000000 --- a/backport-handle-error-when-log-file-is-empty-4859.patch +++ /dev/null @@ -1,67 +0,0 @@ -From ee79940717e354d26954fc4401dc5b0c38980509 Mon Sep 17 00:00:00 2001 -From: Hasan -Date: Tue, 13 Feb 2024 19:34:11 +0400 -Subject: [PATCH] feat: handle error when log file is empty (#4859) - -Fixes GH-4686 ---- - cloudinit/analyze/show.py | 4 ++++ - tests/unittests/analyze/test_show.py | 24 ++++++++++++++++++++++++ - 2 files changed, 28 insertions(+) - create mode 100644 tests/unittests/analyze/test_show.py - -diff --git a/cloudinit/analyze/show.py b/cloudinit/analyze/show.py -index 8d5866e..7938252 100644 ---- a/cloudinit/analyze/show.py -+++ b/cloudinit/analyze/show.py -@@ -7,6 +7,7 @@ - import datetime - import json - import os -+import sys - import time - - from cloudinit import subp, util -@@ -370,6 +371,9 @@ def load_events_infile(infile): - :return: json version of logfile, raw file - """ - data = infile.read() -+ if not data.strip(): -+ sys.stderr.write("Empty file %s\n" % infile.name) -+ sys.exit(1) - try: - return json.loads(data), data - except ValueError: -diff --git a/tests/unittests/analyze/test_show.py b/tests/unittests/analyze/test_show.py -new file mode 100644 -index 0000000..0984e90 ---- /dev/null -+++ b/tests/unittests/analyze/test_show.py -@@ -0,0 +1,24 @@ -+from collections import namedtuple -+ -+import pytest -+ -+from cloudinit.analyze import analyze_show -+ -+ -+@pytest.fixture -+def mock_io(tmp_path): -+ """Mock args for configure_io function""" -+ infile = tmp_path / "infile" -+ outfile = tmp_path / "outfile" -+ return namedtuple("MockIO", ["infile", "outfile"])(infile, outfile) -+ -+ -+class TestAnalyzeShow: -+ """Test analyze_show (and/or helpers) in cloudinit/analyze/__init__.py""" -+ -+ def test_empty_logfile(self, mock_io, capsys): -+ """Test analyze_show with an empty logfile""" -+ mock_io.infile.write_text("") -+ with pytest.raises(SystemExit): -+ analyze_show("dontcare", mock_io) -+ assert capsys.readouterr().err == f"Empty file {mock_io.infile}\n" --- -2.27.0 - diff --git a/backport-test-Fix-duplicate-judgment-conditions-in-password-g.patch b/backport-test-Fix-duplicate-judgment-conditions-in-password-g.patch deleted file mode 100644 index af2961b41bfa0b86a1ca1bf5f0bd87f64ca1f7cb..0000000000000000000000000000000000000000 --- a/backport-test-Fix-duplicate-judgment-conditions-in-password-g.patch +++ /dev/null @@ -1,31 +0,0 @@ -From 4c156a80375c01433cdd00546c6278edb0bb6025 Mon Sep 17 00:00:00 2001 -From: sxt1001 -Date: Mon, 21 Oct 2024 23:40:25 +0800 -Subject: [PATCH] test: Fix duplicate judgment conditions in password - generation (#5835) - -Reference:https://github.com/canonical/cloud-init/commit/4c156a80375c01433cdd00546c6278edb0bb6025 -Conflict:NA - -The problem was introduced by commit 879945f ---- - tests/unittests/config/test_cc_set_passwords.py | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/tests/unittests/config/test_cc_set_passwords.py b/tests/unittests/config/test_cc_set_passwords.py -index 73cb3d490..c068f62d8 100644 ---- a/tests/unittests/config/test_cc_set_passwords.py -+++ b/tests/unittests/config/test_cc_set_passwords.py -@@ -566,7 +566,7 @@ class TestRandUserPassword: - [ - any(c.islower() for c in str), - any(c.isupper() for c in str), -- any(c.isupper() for c in str), -+ any(c.isdigit() for c in str), - any(c in string.punctuation for c in str), - ] - ) --- -2.33.0 - - diff --git a/backport-test-fix-disable_sysfs_net-mock.patch b/backport-test-fix-disable_sysfs_net-mock.patch deleted file mode 100644 index 1f334a630fb01898e289215455593d8995d45391..0000000000000000000000000000000000000000 --- a/backport-test-fix-disable_sysfs_net-mock.patch +++ /dev/null @@ -1,72 +0,0 @@ -From dc0eafbc7d88be99e11301081adb41ad7b50338e Mon Sep 17 00:00:00 2001 -From: James Falcon -Date: Mon, 11 Mar 2024 19:37:48 -0500 -Subject: [PATCH] test: fix `disable_sysfs_net` mock (#5065) - -The fixture parametrization ability added in 9baf31c doesn't work -as expected. When you have a session-wide fixture, the setup is run -once, then further invocations of the fixture (including autouse) uses a -cached version of the fixture. Teardown for the session fixture happens -at the end of all test runs. This also applies to mock patching. Since -the mock patching happens only once, parametrizing the fixture to yield -without patching doesn't undo the initial mock setup; the -parametrization of `disable_sys_net` effectively does nothing. - -The good news is that patches stack, so current tests that patch -`get_sys_class_path` differently will still work fine. If we need to -disable the patching entirely, that is also possible by saving the -original `get_sys_class_path` before applying the global disable mock, -then having a separate mock that has a side effect of calling -the original function. - -Reference:https://github.com/canonical/cloud-init/commit/dc0eafbc7d88be99e11301081adb41ad7b50338e -Conflict:NA ---- - tests/unittests/conftest.py | 14 ++------------ - tests/unittests/net/test_init.py | 5 +---- - 2 files changed, 3 insertions(+), 16 deletions(-) - -diff --git a/tests/unittests/conftest.py b/tests/unittests/conftest.py -index 22bc189..bdd21c3 100644 ---- a/tests/unittests/conftest.py -+++ b/tests/unittests/conftest.py -@@ -62,18 +62,8 @@ def fake_filesystem(mocker, tmpdir): - - - @pytest.fixture(scope="session", autouse=True) --def disable_sysfs_net(request, tmpdir_factory): -- """Avoid tests which read the undertying host's /syc/class/net. -- -- To allow unobscured reads of /sys/class/net on the host we can -- parametrize the fixture with: -- -- @pytest.mark.parametrize("disable_sysfs_net", [False], indirect=True) -- """ -- if hasattr(request, "param") and getattr(request, "param") is False: -- # Test disabled this fixture, perform no mocks. -- yield -- return -+def disable_sysfs_net(tmpdir_factory): -+ """Avoid tests which read the underlying host's /syc/class/net.""" - mock_sysfs = f"{tmpdir_factory.mktemp('sysfs')}/" - with mock.patch( - "cloudinit.net.get_sys_class_path", return_value=mock_sysfs -diff --git a/tests/unittests/net/test_init.py b/tests/unittests/net/test_init.py -index a7b75ab..51e54d0 100644 ---- a/tests/unittests/net/test_init.py -+++ b/tests/unittests/net/test_init.py -@@ -42,10 +42,7 @@ class TestSysDevPath: - - class TestReadSysNet: - @pytest.fixture(autouse=True) -- @pytest.mark.parametrize( -- "disable_sysfs_net", [False], indirect=["disable_sysfs_net"] -- ) -- def setup(self, disable_sysfs_net, tmpdir_factory): -+ def setup(self, tmpdir_factory): - # We mock invididual numbered tmpdirs here because these tests write - # to the sysfs directory and stale test artifacts break later tests. - mock_sysfs = f"{tmpdir_factory.mktemp('sysfs', numbered=True)}/" --- -2.43.0 - diff --git a/backport-test-fix-mocking-leaks-4815.patch b/backport-test-fix-mocking-leaks-4815.patch deleted file mode 100644 index 1db43d39b055487f92e7791d7c1e0adcd8e457ab..0000000000000000000000000000000000000000 --- a/backport-test-fix-mocking-leaks-4815.patch +++ /dev/null @@ -1,110 +0,0 @@ -From ef2e48e9344e4849c10475fa9a823d50fb241512 Mon Sep 17 00:00:00 2001 -From: Alberto Contreras -Date: Mon, 29 Jan 2024 18:30:56 +0100 -Subject: [PATCH] test: fix mocking leaks (#4815) - -In pytest==8.0.0, the collection algorithm changed which results in -unittests being executed in a different order. This is shows some -mocking leaks that are fixed in this commit, as: - -- super().tearDown not called -- super().setUp doubly called -- Distro.default_updates_event not mocked ---- - tests/unittests/config/test_cc_growpart.py | 1 + - tests/unittests/config/test_cc_seed_random.py | 1 + - tests/unittests/sources/test_altcloud.py | 4 ++++ - tests/unittests/sources/test_azure.py | 1 - - tests/unittests/test_stages.py | 4 ++++ - 5 files changed, 10 insertions(+), 1 deletion(-) - -diff --git a/tests/unittests/config/test_cc_growpart.py b/tests/unittests/config/test_cc_growpart.py -index 85a4759..79c46e2 100644 ---- a/tests/unittests/config/test_cc_growpart.py -+++ b/tests/unittests/config/test_cc_growpart.py -@@ -135,6 +135,7 @@ class TestConfig(TestCase): - def tearDown(self): - self.tmpfile.close() - os.remove(self.tmppath) -+ super().tearDown() - - @mock.patch.object(os.path, "isfile", return_value=False) - @mock.patch.dict("os.environ", clear=True) -diff --git a/tests/unittests/config/test_cc_seed_random.py b/tests/unittests/config/test_cc_seed_random.py -index 3ba2a96..b9b1250 100644 ---- a/tests/unittests/config/test_cc_seed_random.py -+++ b/tests/unittests/config/test_cc_seed_random.py -@@ -42,6 +42,7 @@ class TestRandomSeed(TestCase): - def tearDown(self): - apply_patches([i for i in reversed(self.unapply)]) - util.del_file(self._seed_file) -+ super().tearDown() - - def apply_patches(self, patches): - ret = apply_patches(patches) -diff --git a/tests/unittests/sources/test_altcloud.py b/tests/unittests/sources/test_altcloud.py -index b4bc44b..dba0f6a 100644 ---- a/tests/unittests/sources/test_altcloud.py -+++ b/tests/unittests/sources/test_altcloud.py -@@ -92,6 +92,7 @@ class TestGetCloudType(CiTestCase): - # Reset - dmi.read_dmi_data = self.dmi_data - force_arch() -+ super().tearDown() - - def test_cloud_info_file_ioerror(self): - """Return UNKNOWN when /etc/sysconfig/cloud-info exists but errors.""" -@@ -230,6 +231,7 @@ class TestGetDataNoCloudInfoFile(CiTestCase): - dmi.read_dmi_data = self.dmi_data - # Return back to original arch - force_arch() -+ super().tearDown() - - def test_rhev_no_cloud_file(self): - """Test No cloud info file module get_data() forcing RHEV.""" -@@ -346,6 +348,7 @@ class TestUserDataVsphere(CiTestCase): - pass - - dsac.CLOUD_INFO_FILE = "/etc/sysconfig/cloud-info" -+ super().tearDown() - - @mock.patch("cloudinit.sources.DataSourceAltCloud.util.find_devs_with") - @mock.patch("cloudinit.sources.DataSourceAltCloud.util.mount_cb") -@@ -409,6 +412,7 @@ class TestReadUserDataCallback(CiTestCase): - shutil.rmtree(self.mount_dir) - except OSError: - pass -+ super().tearDown() - - def test_callback_both(self): - """Test read_user_data_callback() with both files.""" -diff --git a/tests/unittests/sources/test_azure.py b/tests/unittests/sources/test_azure.py -index 3a36418..c4be540 100644 ---- a/tests/unittests/sources/test_azure.py -+++ b/tests/unittests/sources/test_azure.py -@@ -1064,7 +1064,6 @@ class TestAzureDataSource(CiTestCase): - mock.MagicMock(), - ) - ) -- super(TestAzureDataSource, self).setUp() - - def apply_patches(self, patches): - for module, name, new in patches: -diff --git a/tests/unittests/test_stages.py b/tests/unittests/test_stages.py -index 7730608..4c54257 100644 ---- a/tests/unittests/test_stages.py -+++ b/tests/unittests/test_stages.py -@@ -441,6 +441,10 @@ class TestInit: - assert not self.tmpdir.join(path).exists() - - @mock.patch("cloudinit.distros.ubuntu.Distro") -+ @mock.patch.dict( -+ sources.DataSource.default_update_events, -+ {EventScope.NETWORK: {EventType.BOOT_NEW_INSTANCE}}, -+ ) - def test_apply_network_on_same_instance_id(self, m_ubuntu, caplog): - """Only call distro.networking.apply_network_config_names on same - instance id.""" --- -2.27.0 - diff --git a/backport-test-fix-tmpdir-in-test_cc_apk_configure.patch b/backport-test-fix-tmpdir-in-test_cc_apk_configure.patch deleted file mode 100644 index f7672aeb118603b6ea633b16f9e0bb5f2dd3c705..0000000000000000000000000000000000000000 --- a/backport-test-fix-tmpdir-in-test_cc_apk_configure.patch +++ /dev/null @@ -1,37 +0,0 @@ -From 4ed78b116774dcbf78cfc556c823bdb7f8384068 Mon Sep 17 00:00:00 2001 -From: James Falcon -Date: Tue, 20 Feb 2024 11:08:13 -0600 -Subject: [PATCH] test: fix tmpdir in test_cc_apk_configure (#4914) - -cc_apk_configure uses temp_utils.py, which has special logic to return -if the user is root that can't be retargeted using our current fixtures. -Fix it by setting that tmpdir in the test setup. ---- - tests/unittests/config/test_cc_apk_configure.py | 7 ++++++- - 1 file changed, 6 insertions(+), 1 deletion(-) - -diff --git a/tests/unittests/config/test_cc_apk_configure.py b/tests/unittests/config/test_cc_apk_configure.py -index 72ed82242ee..47777b4701a 100644 ---- a/tests/unittests/config/test_cc_apk_configure.py -+++ b/tests/unittests/config/test_cc_apk_configure.py -@@ -10,7 +10,7 @@ - - import pytest - --from cloudinit import cloud, helpers, util -+from cloudinit import cloud, helpers, temp_utils, util - from cloudinit.config import cc_apk_configure - from cloudinit.config.schema import ( - SchemaValidationError, -@@ -60,6 +60,11 @@ def setUp(self): - self.name = "apk_configure" - self.cloud = cloud.Cloud(None, self.paths, None, None, None) - self.args = [] -+ temp_utils._TMPDIR = self.new_root -+ -+ def tearDown(self): -+ super().tearDown() -+ temp_utils._TMPDIR = None - - @mock.patch(CC_APK + "._write_repositories_file") - def test_no_repo_settings(self, m_write_repos): diff --git a/backport-test-openstack-Test-bond-mac-address.patch b/backport-test-openstack-Test-bond-mac-address.patch deleted file mode 100644 index a956a487b848b5809aa88d92a9fe4c4c3757204f..0000000000000000000000000000000000000000 --- a/backport-test-openstack-Test-bond-mac-address.patch +++ /dev/null @@ -1,140 +0,0 @@ -From f8f9d19409fcbda32e119a5514fd5185bcd88b79 Mon Sep 17 00:00:00 2001 -From: Brett Holman -Date: Thu, 27 Jun 2024 11:56:58 -0600 -Subject: [PATCH] test(openstack): Test bond mac address (#5369) - ---- - .../sources/helpers/test_openstack.py | 120 ++++++++++++++++++ - 1 file changed, 120 insertions(+) - -diff --git a/tests/unittests/sources/helpers/test_openstack.py b/tests/unittests/sources/helpers/test_openstack.py -index 4d85ec3c6..312d66a01 100644 ---- a/tests/unittests/sources/helpers/test_openstack.py -+++ b/tests/unittests/sources/helpers/test_openstack.py -@@ -112,3 +112,123 @@ class TestConvertNetJson: - assert expected == openstack.convert_net_json( - network_json=net_json, known_macs=macs - ) -+ -+ def test_bond_mac(self): -+ """Verify the bond mac address is assigned correctly.""" -+ network_json = { -+ "links": [ -+ { -+ "id": "ens1f0np0", -+ "name": "ens1f0np0", -+ "type": "phy", -+ "ethernet_mac_address": "xx:xx:xx:xx:xx:00", -+ "mtu": 9000, -+ }, -+ { -+ "id": "ens1f1np1", -+ "name": "ens1f1np1", -+ "type": "phy", -+ "ethernet_mac_address": "xx:xx:xx:xx:xx:01", -+ "mtu": 9000, -+ }, -+ { -+ "id": "bond0", -+ "name": "bond0", -+ "type": "bond", -+ "bond_links": ["ens1f0np0", "ens1f1np1"], -+ "mtu": 9000, -+ "ethernet_mac_address": "xx:xx:xx:xx:xx:00", -+ "bond_mode": "802.3ad", -+ "bond_xmit_hash_policy": "layer3+4", -+ "bond_miimon": 100, -+ }, -+ { -+ "id": "bond0.123", -+ "name": "bond0.123", -+ "type": "vlan", -+ "vlan_link": "bond0", -+ "vlan_id": 123, -+ "vlan_mac_address": "xx:xx:xx:xx:xx:00", -+ }, -+ ], -+ "networks": [ -+ { -+ "id": "publicnet-ipv4", -+ "type": "ipv4", -+ "link": "bond0.123", -+ "ip_address": "x.x.x.x", -+ "netmask": "255.255.255.0", -+ "routes": [ -+ { -+ "network": "0.0.0.0", -+ "netmask": "0.0.0.0", -+ "gateway": "x.x.x.1", -+ } -+ ], -+ "network_id": "00000000-0000-0000-0000-000000000000", -+ } -+ ], -+ "services": [{"type": "dns", "address": "1.1.1.1"}], -+ } -+ expected = { -+ "config": [ -+ { -+ "mac_address": "xx:xx:xx:xx:xx:00", -+ "mtu": 9000, -+ "name": "ens1f0np0", -+ "subnets": [], -+ "type": "physical", -+ }, -+ { -+ "mac_address": "xx:xx:xx:xx:xx:01", -+ "mtu": 9000, -+ "name": "ens1f1np1", -+ "subnets": [], -+ "type": "physical", -+ }, -+ { -+ "bond_interfaces": ["ens1f0np0", "ens1f1np1"], -+ "mtu": 9000, -+ "name": "bond0", -+ "mac_address": "xx:xx:xx:xx:xx:00", -+ "params": { -+ "bond_miimon": 100, -+ "bond_mode": "802.3ad", -+ "bond_xmit_hash_policy": "layer3+4", -+ }, -+ "subnets": [], -+ "type": "bond", -+ }, -+ { -+ "mac_address": "xx:xx:xx:xx:xx:00", -+ "name": "bond0.123", -+ "subnets": [ -+ { -+ "address": "x.x.x.x", -+ "ipv4": True, -+ "netmask": "255.255.255.0", -+ "routes": [ -+ { -+ "gateway": "x.x.x.1", -+ "netmask": "0.0.0.0", -+ "network": "0.0.0.0", -+ } -+ ], -+ "type": "static", -+ } -+ ], -+ "type": "vlan", -+ "vlan_id": 123, -+ "vlan_link": "bond0", -+ }, -+ {"address": "1.1.1.1", "type": "nameserver"}, -+ ], -+ "version": 1, -+ } -+ macs = { -+ "xx:xx:xx:xx:xx:00": "ens1f0np0", -+ "xx:xx:xx:xx:xx:01": "ens1f1np1", -+ } -+ assert expected == openstack.convert_net_json( -+ network_json=network_json, known_macs=macs -+ ) --- -2.27.0 - diff --git a/backport-tests-drop-CiTestCase-and-convert-to-pytest.patch b/backport-tests-drop-CiTestCase-and-convert-to-pytest.patch deleted file mode 100644 index 62b350552d7e1235fe9b492645f65ac924c98052..0000000000000000000000000000000000000000 --- a/backport-tests-drop-CiTestCase-and-convert-to-pytest.patch +++ /dev/null @@ -1,672 +0,0 @@ -From 9baf31c7108b031d469e6476b949e042aba249ea Mon Sep 17 00:00:00 2001 -From: Chad Smith -Date: Wed, 6 Mar 2024 09:47:36 -0700 -Subject: [PATCH] tests: drop CiTestCase and convert to pytest - -Prevent disable_sysfs_net fixture from impacting TestSysDevPath -and TestReadSysNet in order to avoid shared mocks of /sys/class/net. -This avoid test artifact pollution for TestReadSysNet. - -Adapt the following tests, dropping CiTestCase to use pytest: - TestDHCPDiscoveryClean, TestSysDevPath, TestReadSysNet, - TestGenerateFallbackConfig, TestNetFailOver, TestConvertNetJson - -Fixes GH-4973 - -Reference:https://github.com/canonical/cloud-init/commit/9baf31c7108b031d469e6476b949e042aba249ea -Confilct:don't change test_dhcp.py, otherwise, it will introduce many test case failures (eg. -test_provided_nic_does_not_exist). Some failed tests will be deleted in -21b2b6e4423b0fec325b32042c05ef4274cdd301. ---- - tests/unittests/net/test_init.py | 298 +++++++++--------- - .../sources/helpers/test_openstack.py | 17 +- - 2 files changed, 149 insertions(+), 166 deletions(-) - -diff --git a/tests/unittests/net/test_init.py b/tests/unittests/net/test_init.py -index 561d515..a7b75ab 100644 ---- a/tests/unittests/net/test_init.py -+++ b/tests/unittests/net/test_init.py -@@ -17,49 +17,55 @@ from cloudinit import subp - from cloudinit.net.ephemeral import EphemeralIPv4Network, EphemeralIPv6Network - from cloudinit.subp import ProcessExecutionError - from cloudinit.util import ensure_file, write_file --from tests.unittests.helpers import CiTestCase, ResponsesTestCase -+from tests.unittests.helpers import ( -+ CiTestCase, -+ ResponsesTestCase, -+ random_string, -+) - from tests.unittests.util import MockDistro - - --class TestSysDevPath(CiTestCase): -+class TestSysDevPath: - def test_sys_dev_path(self): - """sys_dev_path returns a path under SYS_CLASS_NET for a device.""" - dev = "something" - path = "attribute" -- expected = net.SYS_CLASS_NET + dev + "/" + path -- self.assertEqual(expected, net.sys_dev_path(dev, path)) -+ expected = net.get_sys_class_path() + dev + "/" + path -+ assert expected == net.sys_dev_path(dev, path) - - def test_sys_dev_path_without_path(self): - """When path param isn't provided it defaults to empty string.""" - dev = "something" -- expected = net.SYS_CLASS_NET + dev + "/" -- self.assertEqual(expected, net.sys_dev_path(dev)) -- -+ expected = net.get_sys_class_path() + dev + "/" -+ assert expected == net.sys_dev_path(dev) - --class TestReadSysNet(CiTestCase): -- with_logs = True - -- def setUp(self): -- super(TestReadSysNet, self).setUp() -- sys_mock = mock.patch("cloudinit.net.get_sys_class_path") -- self.m_sys_path = sys_mock.start() -- self.sysdir = self.tmp_dir() + "/" -- self.m_sys_path.return_value = self.sysdir -- self.addCleanup(sys_mock.stop) -+class TestReadSysNet: -+ @pytest.fixture(autouse=True) -+ @pytest.mark.parametrize( -+ "disable_sysfs_net", [False], indirect=["disable_sysfs_net"] -+ ) -+ def setup(self, disable_sysfs_net, tmpdir_factory): -+ # We mock invididual numbered tmpdirs here because these tests write -+ # to the sysfs directory and stale test artifacts break later tests. -+ mock_sysfs = f"{tmpdir_factory.mktemp('sysfs', numbered=True)}/" -+ with mock.patch( -+ "cloudinit.net.get_sys_class_path", return_value=mock_sysfs -+ ): -+ self.sysdir = mock_sysfs -+ yield - - def test_read_sys_net_strips_contents_of_sys_path(self): - """read_sys_net strips whitespace from the contents of a sys file.""" - content = "some stuff with trailing whitespace\t\r\n" - write_file(os.path.join(self.sysdir, "dev", "attr"), content) -- self.assertEqual(content.strip(), net.read_sys_net("dev", "attr")) -+ assert content.strip() == net.read_sys_net("dev", "attr") - - def test_read_sys_net_reraises_oserror(self): - """read_sys_net raises OSError/IOError when file doesn't exist.""" - # Non-specific Exception because versions of python OSError vs IOError. -- with self.assertRaises(Exception) as context_manager: # noqa: H202 -+ with pytest.raises(Exception, match="No such file or directory"): - net.read_sys_net("dev", "attr") -- error = context_manager.exception -- self.assertIn("No such file or directory", str(error)) - - def test_read_sys_net_handles_error_with_on_enoent(self): - """read_sys_net handles OSError/IOError with on_enoent if provided.""" -@@ -70,30 +76,27 @@ class TestReadSysNet(CiTestCase): - - net.read_sys_net("dev", "attr", on_enoent=on_enoent) - error = handled_errors[0] -- self.assertIsInstance(error, Exception) -- self.assertIn("No such file or directory", str(error)) -+ assert isinstance(error, Exception) -+ assert "No such file or directory" in str(error) - - def test_read_sys_net_translates_content(self): - """read_sys_net translates content when translate dict is provided.""" - content = "you're welcome\n" - write_file(os.path.join(self.sysdir, "dev", "attr"), content) - translate = {"you're welcome": "de nada"} -- self.assertEqual( -- "de nada", net.read_sys_net("dev", "attr", translate=translate) -+ assert "de nada" == net.read_sys_net( -+ "dev", "attr", translate=translate - ) - -- def test_read_sys_net_errors_on_translation_failures(self): -+ def test_read_sys_net_errors_on_translation_failures(self, caplog): - """read_sys_net raises a KeyError and logs details on failure.""" - content = "you're welcome\n" - write_file(os.path.join(self.sysdir, "dev", "attr"), content) -- with self.assertRaises(KeyError) as context_manager: -+ with pytest.raises(KeyError, match='"you\'re welcome"'): - net.read_sys_net("dev", "attr", translate={}) -- error = context_manager.exception -- self.assertEqual('"you\'re welcome"', str(error)) -- self.assertIn( -+ assert ( - "Found unexpected (not translatable) value 'you're welcome' in " -- "'{0}dev/attr".format(self.sysdir), -- self.logs.getvalue(), -+ "'{0}dev/attr".format(self.sysdir) in caplog.text - ) - - def test_read_sys_net_handles_handles_with_onkeyerror(self): -@@ -107,63 +110,63 @@ class TestReadSysNet(CiTestCase): - - net.read_sys_net("dev", "attr", translate={}, on_keyerror=on_keyerror) - error = handled_errors[0] -- self.assertIsInstance(error, KeyError) -- self.assertEqual('"you\'re welcome"', str(error)) -+ assert isinstance(error, KeyError) -+ assert '"you\'re welcome"' == str(error) - - def test_read_sys_net_safe_false_on_translate_failure(self): - """read_sys_net_safe returns False on translation failures.""" - content = "you're welcome\n" - write_file(os.path.join(self.sysdir, "dev", "attr"), content) -- self.assertFalse(net.read_sys_net_safe("dev", "attr", translate={})) -+ assert not net.read_sys_net_safe("dev", "attr", translate={}) - - def test_read_sys_net_safe_returns_false_on_noent_failure(self): - """read_sys_net_safe returns False on file not found failures.""" -- self.assertFalse(net.read_sys_net_safe("dev", "attr")) -+ assert not net.read_sys_net_safe("dev", "attr") - - def test_read_sys_net_int_returns_none_on_error(self): - """read_sys_net_safe returns None on failures.""" -- self.assertFalse(net.read_sys_net_int("dev", "attr")) -+ assert not net.read_sys_net_int("dev", "attr") - - def test_read_sys_net_int_returns_none_on_valueerror(self): - """read_sys_net_safe returns None when content is not an int.""" - write_file(os.path.join(self.sysdir, "dev", "attr"), "NOTINT\n") -- self.assertFalse(net.read_sys_net_int("dev", "attr")) -+ assert not net.read_sys_net_int("dev", "attr") - - def test_read_sys_net_int_returns_integer_from_content(self): - """read_sys_net_safe returns None on failures.""" - write_file(os.path.join(self.sysdir, "dev", "attr"), "1\n") -- self.assertEqual(1, net.read_sys_net_int("dev", "attr")) -+ assert 1 == net.read_sys_net_int("dev", "attr") - - def test_is_up_true(self): - """is_up is True if sys/net/devname/operstate is 'up' or 'unknown'.""" - for state in ["up", "unknown"]: - write_file(os.path.join(self.sysdir, "eth0", "operstate"), state) -- self.assertTrue(net.is_up("eth0")) -+ assert net.is_up("eth0") - - def test_is_up_false(self): - """is_up is False if sys/net/devname/operstate is 'down' or invalid.""" - for state in ["down", "incomprehensible"]: - write_file(os.path.join(self.sysdir, "eth0", "operstate"), state) -- self.assertFalse(net.is_up("eth0")) -+ assert not net.is_up("eth0") - - def test_is_bridge(self): - """is_bridge is True when /sys/net/devname/bridge exists.""" -- self.assertFalse(net.is_bridge("eth0")) -+ assert not net.is_bridge("eth0") - ensure_file(os.path.join(self.sysdir, "eth0", "bridge")) -- self.assertTrue(net.is_bridge("eth0")) -+ assert net.is_bridge("eth0") - - def test_is_bond(self): - """is_bond is True when /sys/net/devname/bonding exists.""" -- self.assertFalse(net.is_bond("eth0")) -+ assert not net.is_bond("eth0") - ensure_file(os.path.join(self.sysdir, "eth0", "bonding")) -- self.assertTrue(net.is_bond("eth0")) -+ assert net.is_bond("eth0") - - def test_get_master(self): - """get_master returns the path when /sys/net/devname/master exists.""" -- self.assertIsNone(net.get_master("enP1s1")) -+ assert net.get_master("enP1s1") is None - master_path = os.path.join(self.sysdir, "enP1s1", "master") - ensure_file(master_path) -- self.assertEqual(master_path, net.get_master("enP1s1")) -+ assert master_path == net.get_master("enP1s1") - - def test_master_is_bridge_or_bond(self): - bridge_mac = "aa:bb:cc:aa:bb:cc" -@@ -173,8 +176,8 @@ class TestReadSysNet(CiTestCase): - write_file(os.path.join(self.sysdir, "eth1", "address"), bridge_mac) - write_file(os.path.join(self.sysdir, "eth2", "address"), bond_mac) - -- self.assertFalse(net.master_is_bridge_or_bond("eth1")) -- self.assertFalse(net.master_is_bridge_or_bond("eth2")) -+ assert not net.master_is_bridge_or_bond("eth1") -+ assert not net.master_is_bridge_or_bond("eth2") - - # masters without bridge/bonding => False - write_file(os.path.join(self.sysdir, "br0", "address"), bridge_mac) -@@ -183,15 +186,15 @@ class TestReadSysNet(CiTestCase): - os.symlink("../br0", os.path.join(self.sysdir, "eth1", "master")) - os.symlink("../bond0", os.path.join(self.sysdir, "eth2", "master")) - -- self.assertFalse(net.master_is_bridge_or_bond("eth1")) -- self.assertFalse(net.master_is_bridge_or_bond("eth2")) -+ assert not net.master_is_bridge_or_bond("eth1") -+ assert not net.master_is_bridge_or_bond("eth2") - - # masters with bridge/bonding => True - write_file(os.path.join(self.sysdir, "br0", "bridge"), "") - write_file(os.path.join(self.sysdir, "bond0", "bonding"), "") - -- self.assertTrue(net.master_is_bridge_or_bond("eth1")) -- self.assertTrue(net.master_is_bridge_or_bond("eth2")) -+ assert net.master_is_bridge_or_bond("eth1") -+ assert net.master_is_bridge_or_bond("eth2") - - def test_master_is_openvswitch(self): - ovs_mac = "bb:cc:aa:bb:cc:aa" -@@ -199,7 +202,7 @@ class TestReadSysNet(CiTestCase): - # No master => False - write_file(os.path.join(self.sysdir, "eth1", "address"), ovs_mac) - -- self.assertFalse(net.master_is_bridge_or_bond("eth1")) -+ assert not net.master_is_bridge_or_bond("eth1") - - # masters without ovs-system => False - write_file(os.path.join(self.sysdir, "ovs-system", "address"), ovs_mac) -@@ -208,7 +211,7 @@ class TestReadSysNet(CiTestCase): - "../ovs-system", os.path.join(self.sysdir, "eth1", "master") - ) - -- self.assertFalse(net.master_is_openvswitch("eth1")) -+ assert not net.master_is_openvswitch("eth1") - - # masters with ovs-system => True - os.symlink( -@@ -216,15 +219,15 @@ class TestReadSysNet(CiTestCase): - os.path.join(self.sysdir, "eth1", "upper_ovs-system"), - ) - -- self.assertTrue(net.master_is_openvswitch("eth1")) -+ assert net.master_is_openvswitch("eth1") - - def test_is_vlan(self): - """is_vlan is True when /sys/net/devname/uevent has DEVTYPE=vlan.""" - ensure_file(os.path.join(self.sysdir, "eth0", "uevent")) -- self.assertFalse(net.is_vlan("eth0")) -+ assert not net.is_vlan("eth0") - content = "junk\nDEVTYPE=vlan\njunk\n" - write_file(os.path.join(self.sysdir, "eth0", "uevent"), content) -- self.assertTrue(net.is_vlan("eth0")) -+ assert net.is_vlan("eth0") - - - class TestGenerateFallbackConfig(CiTestCase): -@@ -1453,134 +1456,121 @@ class TestExtractPhysdevs(CiTestCase): - net.extract_physdevs({"version": 3, "awesome_config": []}) - - --class TestNetFailOver(CiTestCase): -- def setUp(self): -- super(TestNetFailOver, self).setUp() -- self.add_patch("cloudinit.net.util", "m_util") -- self.add_patch("cloudinit.net.read_sys_net", "m_read_sys_net") -- self.add_patch("cloudinit.net.device_driver", "m_device_driver") -+class TestNetFailOver: -+ @pytest.fixture(autouse=True) -+ def setup(self, mocker): -+ mocker.patch("cloudinit.net.util") -+ self.device_driver = mocker.patch("cloudinit.net.device_driver") -+ self.read_sys_net = mocker.patch("cloudinit.net.read_sys_net") - - def test_get_dev_features(self): -- devname = self.random_string() -- features = self.random_string() -- self.m_read_sys_net.return_value = features -+ devname = random_string() -+ features = random_string() -+ self.read_sys_net.return_value = features - -- self.assertEqual(features, net.get_dev_features(devname)) -- self.assertEqual(1, self.m_read_sys_net.call_count) -- self.assertEqual( -- mock.call(devname, "device/features"), -- self.m_read_sys_net.call_args_list[0], -- ) -+ assert features == net.get_dev_features(devname) -+ assert 1 == self.read_sys_net.call_count -+ self.read_sys_net.assert_called_once_with(devname, "device/features") - - def test_get_dev_features_none_returns_empty_string(self): -- devname = self.random_string() -- self.m_read_sys_net.side_effect = Exception("error") -- self.assertEqual("", net.get_dev_features(devname)) -- self.assertEqual(1, self.m_read_sys_net.call_count) -- self.assertEqual( -- mock.call(devname, "device/features"), -- self.m_read_sys_net.call_args_list[0], -- ) -+ devname = random_string() -+ self.read_sys_net.side_effect = Exception("error") -+ assert "" == net.get_dev_features(devname) -+ assert 1 == self.read_sys_net.call_count -+ self.read_sys_net.assert_called_once_with(devname, "device/features") - - @mock.patch("cloudinit.net.get_dev_features") - def test_has_netfail_standby_feature(self, m_dev_features): -- devname = self.random_string() -+ devname = random_string() - standby_features = ("0" * 62) + "1" + "0" - m_dev_features.return_value = standby_features -- self.assertTrue(net.has_netfail_standby_feature(devname)) -+ assert net.has_netfail_standby_feature(devname) - - @mock.patch("cloudinit.net.get_dev_features") - def test_has_netfail_standby_feature_short_is_false(self, m_dev_features): -- devname = self.random_string() -- standby_features = self.random_string() -+ devname = random_string() -+ standby_features = random_string() - m_dev_features.return_value = standby_features -- self.assertFalse(net.has_netfail_standby_feature(devname)) -+ assert not net.has_netfail_standby_feature(devname) - - @mock.patch("cloudinit.net.get_dev_features") - def test_has_netfail_standby_feature_not_present_is_false( - self, m_dev_features - ): -- devname = self.random_string() -+ devname = random_string() - standby_features = "0" * 64 - m_dev_features.return_value = standby_features -- self.assertFalse(net.has_netfail_standby_feature(devname)) -+ assert not net.has_netfail_standby_feature(devname) - - @mock.patch("cloudinit.net.get_dev_features") - def test_has_netfail_standby_feature_no_features_is_false( - self, m_dev_features - ): -- devname = self.random_string() -+ devname = random_string() - standby_features = None - m_dev_features.return_value = standby_features -- self.assertFalse(net.has_netfail_standby_feature(devname)) -+ assert not net.has_netfail_standby_feature(devname) - - @mock.patch("cloudinit.net.has_netfail_standby_feature") - @mock.patch("cloudinit.net.os.path.exists") - def test_is_netfail_master(self, m_exists, m_standby): -- devname = self.random_string() -+ devname = random_string() - driver = "virtio_net" - m_exists.return_value = False # no master sysfs attr - m_standby.return_value = True # has standby feature flag -- self.assertTrue(net.is_netfail_master(devname, driver)) -+ assert net.is_netfail_master(devname, driver) - - @mock.patch("cloudinit.net.sys_dev_path") - def test_is_netfail_master_checks_master_attr(self, m_sysdev): -- devname = self.random_string() -+ devname = random_string() - driver = "virtio_net" -- m_sysdev.return_value = self.random_string() -- self.assertFalse(net.is_netfail_master(devname, driver)) -- self.assertEqual(1, m_sysdev.call_count) -- self.assertEqual( -- mock.call(devname, path="master"), m_sysdev.call_args_list[0] -- ) -+ m_sysdev.return_value = random_string() -+ assert not net.is_netfail_master(devname, driver) -+ assert 1 == m_sysdev.call_count -+ m_sysdev.assert_called_once_with(devname, path="master") - - @mock.patch("cloudinit.net.has_netfail_standby_feature") - @mock.patch("cloudinit.net.os.path.exists") - def test_is_netfail_master_wrong_driver(self, m_exists, m_standby): -- devname = self.random_string() -- driver = self.random_string() -- self.assertFalse(net.is_netfail_master(devname, driver)) -+ devname = random_string() -+ driver = random_string() -+ assert not net.is_netfail_master(devname, driver) - - @mock.patch("cloudinit.net.has_netfail_standby_feature") - @mock.patch("cloudinit.net.os.path.exists") - def test_is_netfail_master_has_master_attr(self, m_exists, m_standby): -- devname = self.random_string() -+ devname = random_string() - driver = "virtio_net" - m_exists.return_value = True # has master sysfs attr -- self.assertFalse(net.is_netfail_master(devname, driver)) -+ assert not net.is_netfail_master(devname, driver) - - @mock.patch("cloudinit.net.has_netfail_standby_feature") - @mock.patch("cloudinit.net.os.path.exists") - def test_is_netfail_master_no_standby_feat(self, m_exists, m_standby): -- devname = self.random_string() -+ devname = random_string() - driver = "virtio_net" - m_exists.return_value = False # no master sysfs attr - m_standby.return_value = False # no standby feature flag -- self.assertFalse(net.is_netfail_master(devname, driver)) -+ assert not net.is_netfail_master(devname, driver) - - @mock.patch("cloudinit.net.has_netfail_standby_feature") - @mock.patch("cloudinit.net.os.path.exists") - @mock.patch("cloudinit.net.sys_dev_path") - def test_is_netfail_primary(self, m_sysdev, m_exists, m_standby): -- devname = self.random_string() -- driver = self.random_string() # device not virtio_net -- master_devname = self.random_string() -+ devname = random_string() -+ driver = random_string() # device not virtio_net -+ master_devname = random_string() - m_sysdev.return_value = "%s/%s" % ( -- self.random_string(), -+ random_string(), - master_devname, - ) - m_exists.return_value = True # has master sysfs attr -- self.m_device_driver.return_value = "virtio_net" # master virtio_net -+ self.device_driver.return_value = "virtio_net" # master virtio_net - m_standby.return_value = True # has standby feature flag -- self.assertTrue(net.is_netfail_primary(devname, driver)) -- self.assertEqual(1, self.m_device_driver.call_count) -- self.assertEqual( -- mock.call(master_devname), self.m_device_driver.call_args_list[0] -- ) -- self.assertEqual(1, m_standby.call_count) -- self.assertEqual( -- mock.call(master_devname), m_standby.call_args_list[0] -- ) -+ assert net.is_netfail_primary(devname, driver) -+ self.device_driver.assert_called_once_with(master_devname) -+ assert 1 == m_standby.call_count -+ m_standby.assert_called_once_with(master_devname) - - @mock.patch("cloudinit.net.has_netfail_standby_feature") - @mock.patch("cloudinit.net.os.path.exists") -@@ -1588,18 +1578,18 @@ class TestNetFailOver(CiTestCase): - def test_is_netfail_primary_wrong_driver( - self, m_sysdev, m_exists, m_standby - ): -- devname = self.random_string() -+ devname = random_string() - driver = "virtio_net" -- self.assertFalse(net.is_netfail_primary(devname, driver)) -+ assert not net.is_netfail_primary(devname, driver) - - @mock.patch("cloudinit.net.has_netfail_standby_feature") - @mock.patch("cloudinit.net.os.path.exists") - @mock.patch("cloudinit.net.sys_dev_path") - def test_is_netfail_primary_no_master(self, m_sysdev, m_exists, m_standby): -- devname = self.random_string() -- driver = self.random_string() # device not virtio_net -+ devname = random_string() -+ driver = random_string() # device not virtio_net - m_exists.return_value = False # no master sysfs attr -- self.assertFalse(net.is_netfail_primary(devname, driver)) -+ assert not net.is_netfail_primary(devname, driver) - - @mock.patch("cloudinit.net.has_netfail_standby_feature") - @mock.patch("cloudinit.net.os.path.exists") -@@ -1607,16 +1597,16 @@ class TestNetFailOver(CiTestCase): - def test_is_netfail_primary_bad_master( - self, m_sysdev, m_exists, m_standby - ): -- devname = self.random_string() -- driver = self.random_string() # device not virtio_net -- master_devname = self.random_string() -+ devname = random_string() -+ driver = random_string() # device not virtio_net -+ master_devname = random_string() - m_sysdev.return_value = "%s/%s" % ( -- self.random_string(), -+ random_string(), - master_devname, - ) - m_exists.return_value = True # has master sysfs attr -- self.m_device_driver.return_value = "XXXX" # master not virtio_net -- self.assertFalse(net.is_netfail_primary(devname, driver)) -+ self.device_driver.return_value = "XXXX" # master not virtio_net -+ assert not net.is_netfail_primary(devname, driver) - - @mock.patch("cloudinit.net.has_netfail_standby_feature") - @mock.patch("cloudinit.net.os.path.exists") -@@ -1624,77 +1614,77 @@ class TestNetFailOver(CiTestCase): - def test_is_netfail_primary_no_standby( - self, m_sysdev, m_exists, m_standby - ): -- devname = self.random_string() -- driver = self.random_string() # device not virtio_net -- master_devname = self.random_string() -+ devname = random_string() -+ driver = random_string() # device not virtio_net -+ master_devname = random_string() - m_sysdev.return_value = "%s/%s" % ( -- self.random_string(), -+ random_string(), - master_devname, - ) - m_exists.return_value = True # has master sysfs attr -- self.m_device_driver.return_value = "virtio_net" # master virtio_net -+ self.device_driver.return_value = "virtio_net" # master virtio_net - m_standby.return_value = False # master has no standby feature flag -- self.assertFalse(net.is_netfail_primary(devname, driver)) -+ assert not net.is_netfail_primary(devname, driver) - - @mock.patch("cloudinit.net.has_netfail_standby_feature") - @mock.patch("cloudinit.net.os.path.exists") - def test_is_netfail_standby(self, m_exists, m_standby): -- devname = self.random_string() -+ devname = random_string() - driver = "virtio_net" - m_exists.return_value = True # has master sysfs attr - m_standby.return_value = True # has standby feature flag -- self.assertTrue(net.is_netfail_standby(devname, driver)) -+ assert net.is_netfail_standby(devname, driver) - - @mock.patch("cloudinit.net.has_netfail_standby_feature") - @mock.patch("cloudinit.net.os.path.exists") - def test_is_netfail_standby_wrong_driver(self, m_exists, m_standby): -- devname = self.random_string() -- driver = self.random_string() -- self.assertFalse(net.is_netfail_standby(devname, driver)) -+ devname = random_string() -+ driver = random_string() -+ assert not net.is_netfail_standby(devname, driver) - - @mock.patch("cloudinit.net.has_netfail_standby_feature") - @mock.patch("cloudinit.net.os.path.exists") - def test_is_netfail_standby_no_master(self, m_exists, m_standby): -- devname = self.random_string() -+ devname = random_string() - driver = "virtio_net" - m_exists.return_value = False # has master sysfs attr -- self.assertFalse(net.is_netfail_standby(devname, driver)) -+ assert not net.is_netfail_standby(devname, driver) - - @mock.patch("cloudinit.net.has_netfail_standby_feature") - @mock.patch("cloudinit.net.os.path.exists") - def test_is_netfail_standby_no_standby_feature(self, m_exists, m_standby): -- devname = self.random_string() -+ devname = random_string() - driver = "virtio_net" - m_exists.return_value = True # has master sysfs attr - m_standby.return_value = False # has standby feature flag -- self.assertFalse(net.is_netfail_standby(devname, driver)) -+ assert not net.is_netfail_standby(devname, driver) - - @mock.patch("cloudinit.net.is_netfail_standby") - @mock.patch("cloudinit.net.is_netfail_primary") - def test_is_netfailover_primary(self, m_primary, m_standby): -- devname = self.random_string() -- driver = self.random_string() -+ devname = random_string() -+ driver = random_string() - m_primary.return_value = True - m_standby.return_value = False -- self.assertTrue(net.is_netfailover(devname, driver)) -+ assert net.is_netfailover(devname, driver) - - @mock.patch("cloudinit.net.is_netfail_standby") - @mock.patch("cloudinit.net.is_netfail_primary") - def test_is_netfailover_standby(self, m_primary, m_standby): -- devname = self.random_string() -- driver = self.random_string() -+ devname = random_string() -+ driver = random_string() - m_primary.return_value = False - m_standby.return_value = True -- self.assertTrue(net.is_netfailover(devname, driver)) -+ assert net.is_netfailover(devname, driver) - - @mock.patch("cloudinit.net.is_netfail_standby") - @mock.patch("cloudinit.net.is_netfail_primary") - def test_is_netfailover_returns_false(self, m_primary, m_standby): -- devname = self.random_string() -- driver = self.random_string() -+ devname = random_string() -+ driver = random_string() - m_primary.return_value = False - m_standby.return_value = False -- self.assertFalse(net.is_netfailover(devname, driver)) -+ assert not net.is_netfailover(devname, driver) - - - class TestOpenvswitchIsInstalled: -diff --git a/tests/unittests/sources/helpers/test_openstack.py b/tests/unittests/sources/helpers/test_openstack.py -index ac8e2a3..4d85ec3 100644 ---- a/tests/unittests/sources/helpers/test_openstack.py -+++ b/tests/unittests/sources/helpers/test_openstack.py -@@ -3,14 +3,13 @@ - from unittest import mock - - from cloudinit.sources.helpers import openstack --from tests.unittests import helpers as test_helpers - - - @mock.patch( - "cloudinit.net.is_openvswitch_internal_interface", - mock.Mock(return_value=False), - ) --class TestConvertNetJson(test_helpers.CiTestCase): -+class TestConvertNetJson: - def test_phy_types(self): - """Verify the different known physical types are handled.""" - # network_data.json example from -@@ -54,11 +53,8 @@ class TestConvertNetJson(test_helpers.CiTestCase): - - for t in openstack.KNOWN_PHYSICAL_TYPES: - net_json["links"][0]["type"] = t -- self.assertEqual( -- expected, -- openstack.convert_net_json( -- network_json=net_json, known_macs=macs -- ), -+ assert expected == openstack.convert_net_json( -+ network_json=net_json, known_macs=macs - ) - - def test_subnet_dns(self): -@@ -113,9 +109,6 @@ class TestConvertNetJson(test_helpers.CiTestCase): - - for t in openstack.KNOWN_PHYSICAL_TYPES: - net_json["links"][0]["type"] = t -- self.assertEqual( -- expected, -- openstack.convert_net_json( -- network_json=net_json, known_macs=macs -- ), -+ assert expected == openstack.convert_net_json( -+ network_json=net_json, known_macs=macs - ) --- -2.43.0 - diff --git a/cloud-init-22.1-no-override-default-network.patch b/cloud-init-22.1-no-override-default-network.patch deleted file mode 100644 index ed102815b6406f555c07992d8a73b312d2bf2fd1..0000000000000000000000000000000000000000 --- a/cloud-init-22.1-no-override-default-network.patch +++ /dev/null @@ -1,36 +0,0 @@ -From 5514d5922cbc92278868bfea587c4207619d81fc Mon Sep 17 00:00:00 2001 -From: Eduardo Otubo -Date: Thu, 3 Dec 2020 12:34:01 +0100 -Subject: [PATCH 3/3] Don't override default network configuration - -Signed-off-by: Eduardo Otubo ---- - cloudinit/net/sysconfig.py | 12 +++++++++++- - 1 file changed, 11 insertions(+), 1 deletion(-) - -diff --git a/cloudinit/net/sysconfig.py b/cloudinit/net/sysconfig.py -index d934f66..8a60c95 100644 ---- a/cloudinit/net/sysconfig.py -+++ b/cloudinit/net/sysconfig.py -@@ -1025,7 +1025,17 @@ class Renderer(renderer.Renderer): - # Distros configuring /etc/sysconfig/network as a file e.g. Centos - if sysconfig_path.endswith("network"): - util.ensure_dir(os.path.dirname(sysconfig_path)) -- netcfg = [_make_header(), "NETWORKING=yes"] -+ # Make sure that existing lines, other than overriding ones, remain -+ netcfg = [] -+ for line in util.load_file(sysconfig_path, quiet=True).split('\n'): -+ if 'cloud-init' in line: -+ break -+ if not line.startswith(('NETWORKING=', -+ 'IPV6_AUTOCONF=', -+ 'NETWORKING_IPV6=')): -+ netcfg.append(line) -+ # Now generate the cloud-init portion of sysconfig/network -+ netcfg.extend([_make_header(), 'NETWORKING=yes']) - if network_state.use_ipv6: - netcfg.append("NETWORKING_IPV6=yes") - netcfg.append("IPV6_AUTOCONF=no") --- -2.27.0 - diff --git a/cloud-init-23.4.1.tar.gz b/cloud-init-23.4.1.tar.gz deleted file mode 100644 index c2c6a65b397166fd11dd6c71911d8cf44bd94682..0000000000000000000000000000000000000000 Binary files a/cloud-init-23.4.1.tar.gz and /dev/null differ diff --git a/cloud-init-25.1.tar.gz b/cloud-init-25.1.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..bd85cf2c223c1cc7d23d6d717e706771f7221aa6 Binary files /dev/null and b/cloud-init-25.1.tar.gz differ diff --git a/cloud-init.spec b/cloud-init.spec index 796c99819e6bfa9c9d38d5e39ceb472adf2b1f65..9e043953efb1f287a7e48571f58934560669a956 100644 --- a/cloud-init.spec +++ b/cloud-init.spec @@ -1,6 +1,6 @@ Name: cloud-init -Version: 23.4.1 -Release: 12 +Version: 25.1 +Release: 1 Summary: the defacto multi-distribution package that handles early initialization of a cloud instance. License: ASL 2.0 or GPLv3 URL: http://launchpad.net/cloud-init @@ -8,36 +8,10 @@ Source0: https://launchpad.net/%{name}/trunk/%{version}/+download/%{name}-%{vers Source1: cloud-init-tmpfiles.conf -Patch0: cloud-init-22.1-no-override-default-network.patch -Patch2: bugfix-sort-requirements.patch -Patch3: add-variable-to-forbid-tmp-dir.patch -Patch5: Do-not-write-NM_CONTROLLED-no-in-generated-interface-config.patch -Patch6: delete-config-nopasswd-all.patch - -Patch6000: backport-fix-unpin-jsonschema-and-update-tests.patch -Patch6001: backport-test-fix-tmpdir-in-test_cc_apk_configure.patch -Patch6002: backport-bug-tests-mock-reads-of-host-s-sys-class-net-via-get.patch -Patch6003: backport-tests-drop-CiTestCase-and-convert-to-pytest.patch -Patch6004: backport-test-fix-disable_sysfs_net-mock.patch -Patch6005: backport-fix-Logging-sensitive-data.patch -Patch6006: backport-fix-growpart-race-4618.patch -Patch6007: backport-handle-error-when-log-file-is-empty-4859.patch -Patch6008: backport-ec2-Do-not-enable-dhcp6-on-EC2.patch -Patch6009: backport-fix-azure-disable-use-dns-for-secondary-nics-5314.patch -Patch6010: backport-fix-net-Make-duplicate-route-add-succeed.-5343.patch -Patch6011: backport-fix-netplan-Fix-predictable-interface-rename-issue-5.patch -Patch6012: backport-fix-Fall-back-to-cached-local-ds-if-no-valid-ds-foun.patch -Patch6013: backport-fix-openstack-Fix-bond-mac_address-5369.patch -Patch6014: backport-fix-net-klibc-ipconfig-PROTO-compatibility-5437.patch -Patch6015: backport-test-fix-mocking-leaks-4815.patch -Patch6016: backport-feat-Ensure-random-passwords-contain-multiple-charac.patch -Patch6017: backport-test-Fix-duplicate-judgment-conditions-in-password-g.patch -Patch6018: backport-fix-properly-handle-blank-lines-in-fstab-5643.patch -Patch6019: backport-chore-set-recursive-False-for-ensure_dir-if-parent-p.patch -Patch6020: backport-test-openstack-Test-bond-mac-address.patch -Patch6021: backport-fix-Ensure-properties-for-bonded-interfaces-are-prop.patch - -Patch9000: do-not-generate-dsa.patch +Patch1: bugfix-sort-requirements.patch +Patch2: add-variable-to-forbid-tmp-dir.patch +Patch3: delete-config-nopasswd-all.patch +Patch4: skip-test_ntp_custom_client_overrides_installed_clie.patch BuildRequires: pkgconfig(systemd) python3-devel python3-setuptools systemd BuildRequires: iproute python3-configobj python3-responses @@ -138,18 +112,18 @@ fi %dir %{_sysconfdir}/cloud/cloud.cfg.d %config(noreplace) %{_sysconfdir}/cloud/cloud.cfg.d/*.cfg %doc %{_sysconfdir}/cloud/cloud.cfg.d/README -%doc %{_sysconfdir}/cloud/clean.d/README %dir %{_sysconfdir}/rsyslog.d %config(noreplace) %{_sysconfdir}/rsyslog.d/21-cloudinit.conf %{_udevrulesdir}/66-azure-ephemeral.rules %{_unitdir}/cloud-config.service %{_unitdir}/cloud-final.service -%{_unitdir}/cloud-init.service +%{_unitdir}/cloud-init-main.service %{_unitdir}/cloud-init-local.service +%{_unitdir}/cloud-init-network.service %{_unitdir}/cloud-config.target %{_unitdir}/cloud-init.target -/usr/lib/systemd/system-generators/cloud-init-generator -%{_sysconfdir}/systemd/system/sshd-keygen@.service.d/disable-sshd-keygen-if-cloud-init-active.conf +%{_prefix}/lib/systemd/system-generators/cloud-init-generator +%{_unitdir}/sshd-keygen@.service.d/disable-sshd-keygen-if-cloud-init-active.conf %{_unitdir}/cloud-init-hotplugd.service %{_unitdir}/cloud-init-hotplugd.socket %{_tmpfilesdir}/%{name}.conf @@ -168,6 +142,12 @@ fi %exclude /usr/share/doc/* %changelog +* Tue Feb 25 2025 shixuantong - 25.1-1 +- Type:bugfix +- CVE:NA +- SUG:NA +- DESC:upgrade version to 25.1 + * Fri Dec 06 2024 shixuantong - 23.4.1-12 - Type:bugfix - CVE:NA diff --git a/do-not-generate-dsa.patch b/do-not-generate-dsa.patch deleted file mode 100644 index a91c41ab1d04410d4f2d3ade16fb49783ab93f29..0000000000000000000000000000000000000000 --- a/do-not-generate-dsa.patch +++ /dev/null @@ -1,26 +0,0 @@ -From 5f121b085119d7eb694b5ee09f4183175cda2678 Mon Sep 17 00:00:00 2001 -From: shixuantong -Date: Sat, 20 Jul 2024 15:04:30 +0800 -Subject: [PATCH] do not generate dsa - ---- - config/cloud.cfg.tmpl | 3 +++ - 1 file changed, 3 insertions(+) - -diff --git a/config/cloud.cfg.tmpl b/config/cloud.cfg.tmpl -index f096595..37571fc 100644 ---- a/config/cloud.cfg.tmpl -+++ b/config/cloud.cfg.tmpl -@@ -109,6 +109,9 @@ syslog_fix_perms: ~ - disable_vmware_customization: false - {% endif -%} - -+# do not generate dsa -+ssh_genkeytypes: ['rsa', 'ecdsa', 'ed25519'] -+ - # The modules that run in the 'init' stage - cloud_init_modules: - - migrator --- -2.27.0 - diff --git a/skip-test_ntp_custom_client_overrides_installed_clie.patch b/skip-test_ntp_custom_client_overrides_installed_clie.patch new file mode 100644 index 0000000000000000000000000000000000000000..19f9799b5441132f74d94095494af8f13b18c2a6 --- /dev/null +++ b/skip-test_ntp_custom_client_overrides_installed_clie.patch @@ -0,0 +1,42 @@ +From 8f8cccc92ec4de0ea3a044111da7a5e2faace95e Mon Sep 17 00:00:00 2001 +From: shixuantong +Date: Wed, 26 Feb 2025 09:03:46 +0800 +Subject: [PATCH] skip test_ntp_custom_client_overrides_installed_clients if + the build user does not have the root permission + +error info: +tests/unittests/config/test_cc_ntp.py:598: +------------------------------------------------------------- +cloudinit/config/cc_ntp.py:561:in handle +rename_ntp_conf(confpath=ntp_client_config.get("confpath")) +cloudinit/config/cc_ntp.py:357: in rename_ntp_conf +util.rename(confpath, confpath + ".dist") +------------------------------------------------------------- +src = '/etc/ntp.conf', dest = '/etc/ntp.conf.dist' + +def rename(src, dest): + LOG.debug("Renaming %s to %s", src, dest) + # TODO(harlowja) use a se guard here?? +> os.rename(src, dest) +E PermissionError: [Errno 13] Permission denied: '/etc/ntp.conf' -> '/etc/ntp.conf.dist' + +cloudinit/util.py:1834: PermissionError +--- + tests/unittests/config/test_cc_ntp.py | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/tests/unittests/config/test_cc_ntp.py b/tests/unittests/config/test_cc_ntp.py +index c28da73..db6db52 100644 +--- a/tests/unittests/config/test_cc_ntp.py ++++ b/tests/unittests/config/test_cc_ntp.py +@@ -577,6 +577,7 @@ class TestNtp: + m_which.assert_has_calls(expected_calls) + assert sorted(expected_cfg) == sorted(cfg) + ++ @pytest.mark.skipif(os.geteuid() != 0, reason = "the root permission is required") + @mock.patch("cloudinit.config.cc_ntp.write_ntp_config_template") + @mock.patch("cloudinit.cloud.Cloud.get_template_filename") + @mock.patch("cloudinit.config.cc_ntp.subp.which") +-- +2.27.0 +