From 4695078c84e4a00fa93eef67c18e5b86712674b2 Mon Sep 17 00:00:00 2001 From: Olivier Gayot Date: Thu, 25 Apr 2024 15:53:39 +0200 Subject: [PATCH 1/3] ubuntu-advantage: mention that fromisoformat can handle Z suffix in Python >= 3.11 Signed-off-by: Olivier Gayot --- subiquity/server/ubuntu_advantage.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/subiquity/server/ubuntu_advantage.py b/subiquity/server/ubuntu_advantage.py index 48abc8fd..0b43c207 100644 --- a/subiquity/server/ubuntu_advantage.py +++ b/subiquity/server/ubuntu_advantage.py @@ -328,8 +328,8 @@ class UAInterface: info = await self.get_subscription_status(token) # Sometimes, a time zone offset of 0 is replaced by the letter Z. This - # is specified in RFC 3339 but not supported by fromisoformat. - # See https://bugs.python.org/issue35829 + # is specified in RFC 3339 but not supported by fromisoformat before + # Python 3.11. See https://bugs.python.org/issue35829 expiration = dt.fromisoformat(info["expires"].replace("Z", "+00:00")) if expiration.timestamp() <= dt.utcnow().timestamp(): raise ExpiredTokenError(token, expires=info["expires"]) From fa8e49457b6975e8ee15438a1bc3fc54c23c1f46 Mon Sep 17 00:00:00 2001 From: Olivier Gayot Date: Thu, 25 Apr 2024 15:59:56 +0200 Subject: [PATCH 2/3] ubuntu-advantage: compare date from two /aware/ datetime objects The current implementation attempts to compare two datetime objects by comparing the value returned by .timestamp(). However, one of the objects is an /aware/ datetime and the other is a /naive/ datetime. When calling .timestamp() on a naive datetime object, the function expects the datetime object to represent local time. However, in our scenario, it was UTC time. Therefore, the comparison is slightly inaccurate and can end up rejecting a token that is not yet expired ; or accepting one that is about to expire. In practice, this should rarely be a problem (a few hours is not a big deal). But we now compare two /aware/ datetime objects to make the comparison more correct. Also, datetime.datetime.utcnow() (which returns a naive datetime object) is deprecated in favor of datetime.datetime.now() (which can return an aware datetime object if timezone information is passed). Signed-off-by: Olivier Gayot --- subiquity/server/ubuntu_advantage.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/subiquity/server/ubuntu_advantage.py b/subiquity/server/ubuntu_advantage.py index 0b43c207..640d4637 100644 --- a/subiquity/server/ubuntu_advantage.py +++ b/subiquity/server/ubuntu_advantage.py @@ -17,6 +17,7 @@ helper. """ import asyncio import contextlib +import datetime import json import logging import os @@ -331,7 +332,7 @@ class UAInterface: # is specified in RFC 3339 but not supported by fromisoformat before # Python 3.11. See https://bugs.python.org/issue35829 expiration = dt.fromisoformat(info["expires"].replace("Z", "+00:00")) - if expiration.timestamp() <= dt.utcnow().timestamp(): + if expiration <= dt.now(datetime.timezone.utc): raise ExpiredTokenError(token, expires=info["expires"]) def is_activable_service(service: dict) -> bool: From c18d434439e5b3785a59e845cac1a111869ff522 Mon Sep 17 00:00:00 2001 From: Olivier Gayot Date: Thu, 25 Apr 2024 16:18:56 +0200 Subject: [PATCH 3/3] file-util: replace use of deprecate datetime.datetime.utcnow() Signed-off-by: Olivier Gayot --- subiquity/models/tests/test_subiquity.py | 11 +++++++---- subiquitycore/file_util.py | 2 +- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/subiquity/models/tests/test_subiquity.py b/subiquity/models/tests/test_subiquity.py index 042ae3dd..b2ded689 100644 --- a/subiquity/models/tests/test_subiquity.py +++ b/subiquity/models/tests/test_subiquity.py @@ -13,6 +13,7 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . +import datetime import fnmatch import json import re @@ -264,11 +265,13 @@ class TestSubiquityModel(unittest.IsolatedAsyncioTestCase): self.assertEqual(expected_error, str(ctx.exception)) @mock.patch("subiquity.models.subiquity.lsb_release") - @mock.patch("subiquitycore.file_util.datetime.datetime") + @mock.patch("subiquitycore.file_util.datetime.datetime", wraps=datetime.datetime) def test_cloud_init_files_emits_datasource_config_and_clean_script( - self, datetime, lsb_release + self, m_datetime, lsb_release ): - datetime.utcnow.return_value = "2004-03-05 ..." + m_datetime.now.return_value = datetime.datetime( + 2004, 3, 5, tzinfo=datetime.timezone.utc + ) main_user = IdentityData( username="mainuser", crypted_password="sample_pass", hostname="somehost" ) @@ -296,7 +299,7 @@ grub_dpkg: # NETPLAN_CONFIG_ROOT_READ_ONLY is True "/etc/cloud/cloud.cfg.d/90-installer-network.cfg" ) - header = "# Autogenerated by Subiquity: 2004-03-05 ... UTC\n" + header = "# Autogenerated by Subiquity: 2004-03-05 00:00:00 UTC\n" with self.subTest( "Stable releases Jammy do not disable cloud-init." " NETPLAN_ROOT_READ_ONLY=True uses cloud-init networking" diff --git a/subiquitycore/file_util.py b/subiquitycore/file_util.py index c1e48e50..f31509d1 100644 --- a/subiquitycore/file_util.py +++ b/subiquitycore/file_util.py @@ -70,7 +70,7 @@ def write_file(filename, content, **kwargs): def generate_timestamped_header() -> str: - now = datetime.datetime.utcnow() + now = datetime.datetime.now(datetime.timezone.utc).replace(tzinfo=None) return f"# Autogenerated by Subiquity: {now} UTC\n"