diff --git a/cloud-init-dont-assume-ordering-of-ThreadPoolExecutor.patch b/cloud-init-dont-assume-ordering-of-ThreadPoolExecutor.patch new file mode 100644 index 0000000..7d7cf35 --- /dev/null +++ b/cloud-init-dont-assume-ordering-of-ThreadPoolExecutor.patch @@ -0,0 +1,100 @@ +From 4060bed98d2637418955fdb33fc43623c8b95235 Mon Sep 17 00:00:00 2001 +From: Brett Holman +Date: Tue, 12 Mar 2024 22:20:06 -0600 +Subject: [PATCH 1/4] fix: Don't assume ordering of ThreadPoolExecutor + submissions + +--- + tests/unittests/test_url_helper.py | 52 +++++++++++++++++++++++++++--- + 1 file changed, 47 insertions(+), 5 deletions(-) + +Index: cloud-init-23.3/tests/unittests/test_url_helper.py +=================================================================== +--- cloud-init-23.3.orig/tests/unittests/test_url_helper.py ++++ cloud-init-23.3/tests/unittests/test_url_helper.py +@@ -4,6 +4,7 @@ import logging + from functools import partial + from threading import Event + from time import process_time ++from unittest.mock import ANY, call + + import pytest + import requests +@@ -465,20 +466,72 @@ class TestDualStack: + """Assert expected call intervals occur""" + stagger = 0.1 + with mock.patch(M_PATH + "_run_func_with_delay") as delay_func: ++ ++ def identity_of_first_arg(x, _): ++ return x ++ + dual_stack( +- lambda x, _y: x, ++ identity_of_first_arg, + ["you", "and", "me", "and", "dog"], + stagger_delay=stagger, + timeout=1, + ) + +- # ensure that stagger delay for each subsequent call is: ++ # ensure that stagger delay for each call is made with args: + # [ 0 * N, 1 * N, 2 * N, 3 * N, 4 * N, 5 * N] where N = stagger + # it appears that without an explicit wait/join we can't assert + # number of calls +- for delay, call_item in enumerate(delay_func.call_args_list): +- _, kwargs = call_item +- assert stagger * delay == kwargs.get("delay") ++ calls = [ ++ call( ++ func=identity_of_first_arg, ++ addr="you", ++ timeout=1, ++ event=ANY, ++ delay=stagger * 0, ++ ), ++ call( ++ func=identity_of_first_arg, ++ addr="and", ++ timeout=1, ++ event=ANY, ++ delay=stagger * 1, ++ ), ++ call( ++ func=identity_of_first_arg, ++ addr="me", ++ timeout=1, ++ event=ANY, ++ delay=stagger * 2, ++ ), ++ call( ++ func=identity_of_first_arg, ++ addr="and", ++ timeout=1, ++ event=ANY, ++ delay=stagger * 3, ++ ), ++ call( ++ func=identity_of_first_arg, ++ addr="dog", ++ timeout=1, ++ event=ANY, ++ delay=stagger * 4, ++ ), ++ ] ++ num_calls = 0 ++ for call_instance in calls: ++ if call_instance in delay_func.call_args_list: ++ num_calls += 1 ++ ++ # we can't know the order of the submitted functions' execution ++ # we can't know how many of the submitted functions get called ++ # in advance ++ # ++ # we _do_ know what the possible arg combinations are ++ # we _do_ know from the mocked function how many got called ++ # assert that all calls that occurred had known valid arguments ++ # by checking for the correct number of matches ++ assert num_calls == len(delay_func.call_args_list) + + + ADDR1 = "https://addr1/" diff --git a/cloud-init-fix-python313.patch b/cloud-init-fix-python313.patch new file mode 100644 index 0000000..df6fe80 --- /dev/null +++ b/cloud-init-fix-python313.patch @@ -0,0 +1,22 @@ +From bed5ae7f777e0e4bcb5609622385ee94751c03ce Mon Sep 17 00:00:00 2001 +From: Brett Holman +Date: Tue, 5 Dec 2023 13:41:13 -0700 +Subject: [PATCH] fix(python3.13): Fix import error for passlib on Python 3.13 + +--- + cloudinit/sources/DataSourceAzure.py | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +Index: cloud-init-23.3/cloudinit/sources/DataSourceAzure.py +=================================================================== +--- cloud-init-23.3.orig/cloudinit/sources/DataSourceAzure.py ++++ cloud-init-23.3/cloudinit/sources/DataSourceAzure.py +@@ -55,7 +55,7 @@ try: + ) + except (ImportError, AttributeError): + try: +- import passlib ++ import passlib.hash + + blowfish_hash = passlib.hash.sha512_crypt.hash + except ImportError: diff --git a/cloud-init.changes b/cloud-init.changes index c28675f..17f8525 100644 --- a/cloud-init.changes +++ b/cloud-init.changes @@ -1,3 +1,11 @@ +------------------------------------------------------------------- +Mon Jan 20 14:21:55 UTC 2025 - Daniel Garcia + +- Support python 3.13: + + pep-594-drop-pipes.patch, gh#canonical/cloud-init#4392 + + cloud-init-fix-python313.patch, gh#canonical/cloud-init#4669 + + cloud-init-dont-assume-ordering-of-ThreadPoolExecutor.patch gh#canonical/cloud-init#5052 + ------------------------------------------------------------------- Mon Jan 6 13:17:05 UTC 2025 - Robert Schweikert diff --git a/cloud-init.spec b/cloud-init.spec index 2ccfebc..9cb0042 100644 --- a/cloud-init.spec +++ b/cloud-init.spec @@ -55,6 +55,12 @@ Patch14: cloud-init-usr-sudoers.patch Patch15: cloud-init-skip-rename.patch # FIXME https://github.com/canonical/cloud-init/pull/5947 Patch16: cloud-init-wait-for-net.patch +# FIXME https://github.com/canonical/cloud-init/pull/4392 +Patch17: pep-594-drop-pipes.patch +# FIXME https://github.com/canonical/cloud-init/pull/4669 +Patch18: cloud-init-fix-python313.patch +# FIXME https://github.com/canonical/cloud-init/pull/5052 +Patch19: cloud-init-dont-assume-ordering-of-ThreadPoolExecutor.patch BuildRequires: fdupes BuildRequires: filesystem # pkg-config is needed to find correct systemd unit dir @@ -169,6 +175,9 @@ Documentation and examples for cloud-init tools %patch -P 14 %patch -P 15 %patch -P 16 +%patch -p1 -P 17 +%patch -p1 -P 18 +%patch -p1 -P 19 # patch in the full version to version.py version_pys=$(find . -name version.py -type f) diff --git a/pep-594-drop-pipes.patch b/pep-594-drop-pipes.patch new file mode 100644 index 0000000..2d186d9 --- /dev/null +++ b/pep-594-drop-pipes.patch @@ -0,0 +1,63 @@ +From c76f9eb0df30ab7c288e5050ed1df85d132c0202 Mon Sep 17 00:00:00 2001 +From: Chad Smith +Date: Mon, 28 Aug 2023 10:22:04 -0600 +Subject: [PATCH] pep-594: drop deprecated pipes module import + +python3.11 will deprecated pipes module 3.13 will drop it from main. + +cloud-init only used the undocumented pipes.quote function, +which is actually only wrapper around shlex.quote[1]. + +Use shlex.quote instead. + +[1] https://github.com/python/cpython/blob/3.11/Lib/pipes.py#L64-L66 +--- + cloudinit/distros/parsers/sys_conf.py | 6 +++--- + tests/unittests/distros/test_sysconfig.py | 2 +- + 2 files changed, 4 insertions(+), 4 deletions(-) + +diff --git a/cloudinit/distros/parsers/sys_conf.py b/cloudinit/distros/parsers/sys_conf.py +index cb6e583e789..1d519ce4ef7 100644 +--- a/cloudinit/distros/parsers/sys_conf.py ++++ b/cloudinit/distros/parsers/sys_conf.py +@@ -4,8 +4,8 @@ + # + # This file is part of cloud-init. See LICENSE file for license information. + +-import pipes + import re ++import shlex + from io import StringIO + + # This library is used to parse/write +@@ -82,7 +82,7 @@ def _quote(self, value, multiline=False): + if re.search(r"[\t\r\n ]", value): + if _contains_shell_variable(value): + # If it contains shell variables then we likely want to +- # leave it alone since the pipes.quote function likes ++ # leave it alone since the shlex.quote function likes + # to use single quotes which won't get expanded... + if re.search(r"[\n\"']", value): + quot_func = ( +@@ -93,7 +93,7 @@ def _quote(self, value, multiline=False): + lambda x: self._get_single_quote(x) % x + ) # noqa: E731 + else: +- quot_func = pipes.quote ++ quot_func = shlex.quote + if not quot_func: + return value + return quot_func(value) +diff --git a/tests/unittests/distros/test_sysconfig.py b/tests/unittests/distros/test_sysconfig.py +index 9c3a2018edf..9f8d0cc8003 100644 +--- a/tests/unittests/distros/test_sysconfig.py ++++ b/tests/unittests/distros/test_sysconfig.py +@@ -65,7 +65,7 @@ def test_parse_adjust(self): + conf["IPV6TO4_ROUTING"] = "blah \tblah" + contents2 = str(conf).strip() + # Should be requoted due to whitespace +- self.assertRegex(contents2, r"IPV6TO4_ROUTING=[\']blah\s+blah[\']") ++ self.assertRegex(contents2, r"IPV6TO4_ROUTING='blah\s+blah'") + + def test_parse_no_adjust_shell(self): + conf = SysConf("".splitlines())