From cda332622e2c9d95c347fd61b131d84510f1d669 Mon Sep 17 00:00:00 2001 From: Ryan Date: Tue, 3 Dec 2024 13:43:06 -0800 Subject: [PATCH 1/6] various test patches wheel/setuptools/azdev updates to command table linter fix for test_import_and_version removal of system properties contentEncoding and contentType in tests as they're no longer returned fix for using a target hub in int tests --- .../templates/evaluate-command-table.yml | 3 ++ azext_iot/common/utility.py | 3 +- azext_iot/tests/iothub/__init__.py | 10 ++-- .../iothub/core/test_iot_messaging_int.py | 48 +++++++++++-------- .../devices/test_iothub_device_tracing.py | 2 +- .../messaging/test_iothub_c2d_messages_int.py | 9 ++-- setup.py | 5 +- 7 files changed, 47 insertions(+), 33 deletions(-) diff --git a/.azure-devops/templates/evaluate-command-table.yml b/.azure-devops/templates/evaluate-command-table.yml index eec3ee92b..956150b27 100644 --- a/.azure-devops/templates/evaluate-command-table.yml +++ b/.azure-devops/templates/evaluate-command-table.yml @@ -20,6 +20,7 @@ steps: source ./venv/bin/activate git clone --single-branch -b dev https://github.com/Azure/azure-cli.git ../azure-cli pip install azdev + pip install wheel==0.30.0 setuptools==70.0.0 azdev --version azdev setup -c ../azure-cli -r ./ AZURE_EXTENSION_DIR=~/.azure/cliextensions @@ -30,4 +31,6 @@ steps: az extension add --source $i -y --debug done cp ./linter_exclusions.yml $AZURE_EXTENSION_DIR/azure-iot/ + # temp fix for newest azdev v0.1.65 + cp .pylintrc pylintrc azdev linter --include-whl-extensions azure-iot --min-severity medium diff --git a/azext_iot/common/utility.py b/azext_iot/common/utility.py index ee4aa5677..6f2175604 100644 --- a/azext_iot/common/utility.py +++ b/azext_iot/common/utility.py @@ -366,9 +366,10 @@ def test_import_and_version(package, expected_version): that are installed without metadata. """ from importlib import metadata + from packaging.version import parse try: - return metadata.version(package) >= expected_version + return parse(metadata.version(package)) >= parse(expected_version) except metadata.PackageNotFoundError: return False diff --git a/azext_iot/tests/iothub/__init__.py b/azext_iot/tests/iothub/__init__.py index 2b97c5e41..924f516b5 100644 --- a/azext_iot/tests/iothub/__init__.py +++ b/azext_iot/tests/iothub/__init__.py @@ -93,12 +93,12 @@ def __init__(self, test_scenario, add_data_contributor=True): ) sleep(ROLE_ASSIGNMENT_REFRESH_TIME) - target_hub = self.cmd( - "iot hub show -n {} -g {}".format(self.entity_name, self.entity_rg) - ).get_output_in_json() + target_hub = self.cmd( + "iot hub show -n {} -g {}".format(self.entity_name, self.entity_rg) + ).get_output_in_json() - if add_data_contributor: - self._add_data_contributor(target_hub) + if add_data_contributor: + self._add_data_contributor(target_hub) self.host_name = target_hub["properties"]["hostName"] self.region = self.get_region() diff --git a/azext_iot/tests/iothub/core/test_iot_messaging_int.py b/azext_iot/tests/iothub/core/test_iot_messaging_int.py index e9740dbdc..9f108c214 100644 --- a/azext_iot/tests/iothub/core/test_iot_messaging_int.py +++ b/azext_iot/tests/iothub/core/test_iot_messaging_int.py @@ -5,6 +5,7 @@ # -------------------------------------------------------------------------------------------- import os +from azure.cli.core.azclierror import AzureResponseError from azext_iot.iothub.common import NON_DECODABLE_PAYLOAD from azext_iot.tests.conftest import get_context_path import pytest @@ -101,8 +102,9 @@ def test_uamqp_device_messaging(self): assert result["data"] == test_body system_props = result["properties"]["system"] - assert system_props["ContentEncoding"] == test_ce - assert system_props["ContentType"] == test_ct + # TODO - @c-ryan-k - no system properties + # assert system_props["ContentEncoding"] == test_ce + # assert system_props["ContentType"] == test_ct assert system_props["iothub-correlationid"] == test_cid assert system_props["iothub-messageid"] == test_mid assert system_props["iothub-expiry"] @@ -157,8 +159,8 @@ def test_uamqp_device_messaging(self): self._remove_newlines_spaces(payload=self.kwargs["messaging_data"]) system_props = result["properties"]["system"] - assert system_props["ContentEncoding"] == test_ce - assert system_props["ContentType"] == 'application/json' + # assert system_props["ContentEncoding"] == test_ce + # assert system_props["ContentType"] == 'application/json' assert system_props["iothub-correlationid"] == test_cid assert system_props["iothub-messageid"] == test_mid assert system_props["iothub-expiry"] @@ -209,8 +211,8 @@ def test_uamqp_device_messaging(self): assert result["data"] == self.kwargs["messaging_unicodable_data"] system_props = result["properties"]["system"] - assert system_props["ContentEncoding"] == test_ce - assert system_props["ContentType"] == 'application/octet-stream' + # assert system_props["ContentEncoding"] == test_ce + # assert system_props["ContentType"] == 'application/octet-stream' assert system_props["iothub-correlationid"] == test_cid assert system_props["iothub-messageid"] == test_mid assert system_props["iothub-expiry"] @@ -261,8 +263,8 @@ def test_uamqp_device_messaging(self): assert result["data"] == self.kwargs["messaging_non_unicodable_data"] system_props = result["properties"]["system"] - assert system_props["ContentEncoding"] == test_ce - assert system_props["ContentType"] == 'application/octet-stream' + # assert system_props["ContentEncoding"] == test_ce + # assert system_props["ContentType"] == 'application/octet-stream' assert system_props["iothub-correlationid"] == test_cid assert system_props["iothub-messageid"] == test_mid assert system_props["iothub-expiry"] @@ -310,11 +312,12 @@ def test_uamqp_device_messaging(self): ) ).get_output_in_json() - assert result["data"] == self.kwargs["messaging_non_unicodable_data"] + # no data in this result + # assert result["data"] == self.kwargs["messaging_non_unicodable_data"] system_props = result["properties"]["system"] - assert system_props["ContentEncoding"] == 'gzip' - assert system_props["ContentType"] == 'application/octet-stream' + # assert system_props["ContentEncoding"] == 'gzip' + # assert system_props["ContentType"] == 'application/octet-stream' assert system_props["iothub-correlationid"] == test_cid assert system_props["iothub-messageid"] == test_mid assert system_props["iothub-expiry"] @@ -331,12 +334,15 @@ def test_uamqp_device_messaging(self): # Implicit etag assertion etag = result["etag"] - self.cmd( - "iot device c2d-message complete -d {} --hub-name {} -g {} --etag {}".format( - device_ids[0], self.entity_name, self.entity_rg, etag - ), - checks=self.is_empty(), - ) + try: + self.cmd( + "iot device c2d-message complete -d {} --hub-name {} -g {} --etag {}".format( + device_ids[0], self.entity_name, self.entity_rg, etag + ), + checks=self.is_empty(), + ) + except AzureResponseError as e: + logger.warning("Error completing message: %s", e) # Error - Send C2D message with non existed file path self.cmd( @@ -386,8 +392,8 @@ def test_uamqp_device_messaging(self): assert result["data"] == self.kwargs["c2d_json_send_data"] system_props = result["properties"]["system"] - assert system_props["ContentEncoding"] == test_ce - assert system_props["ContentType"] == test_ct + # assert system_props["ContentEncoding"] == test_ce + # assert system_props["ContentType"] == test_ct assert system_props["iothub-correlationid"] == test_cid assert system_props["iothub-messageid"] == test_mid assert system_props["iothub-expiry"] @@ -771,8 +777,10 @@ def test_mqtt_device_simulation_x509(self): # x509 CA device simulation and include model Id upon connection model_id_simulate_x509ca = "dtmi:com:example:simulatex509ca;1" + # not sure why this needs a timer but it seems to help avoid unauthorized errors + import time; time.sleep(60) self.cmd( - "iot device simulate -d {} -n {} -g {} --da '{}' --mc 1 --mi 1 --cp {} --kp {} --pass {} --model-id {}".format( + "iot device simulate -d {} -n {} -g {} --da '{}' --mc 1 --mi 1 --cp {} --kp {} --pass {} --model-id '{}'".format( device_ids[1], self.entity_name, self.entity_rg, simulate_msg, f"{device_ids[1]}-cert.pem", f"{device_ids[1]}-key.pem", fake_pass, model_id_simulate_x509ca ) diff --git a/azext_iot/tests/iothub/devices/test_iothub_device_tracing.py b/azext_iot/tests/iothub/devices/test_iothub_device_tracing.py index 46e9f3aa2..5311adbb6 100644 --- a/azext_iot/tests/iothub/devices/test_iothub_device_tracing.py +++ b/azext_iot/tests/iothub/devices/test_iothub_device_tracing.py @@ -51,7 +51,7 @@ def test_iothub_device_distributed_tracing(self): result = self.cmd( self.set_cmd_auth_type( f"iot hub distributed-tracing update " - f"-d {device_ids[0]} -n {self.host_name} -g { self.entity_rg} --sm on --sr 50", + f"-d {device_ids[0]} -n {self.host_name} -g {self.entity_rg} --sm on --sr 50", auth_type=auth_phase, ) ).get_output_in_json() diff --git a/azext_iot/tests/iothub/messaging/test_iothub_c2d_messages_int.py b/azext_iot/tests/iothub/messaging/test_iothub_c2d_messages_int.py index d5f7fe3c1..ca261c25b 100644 --- a/azext_iot/tests/iothub/messaging/test_iothub_c2d_messages_int.py +++ b/azext_iot/tests/iothub/messaging/test_iothub_c2d_messages_int.py @@ -8,6 +8,7 @@ from time import sleep from uuid import uuid4 +from azext_iot.iothub.common import NON_DECODABLE_PAYLOAD from azext_iot.tests.iothub import IoTLiveScenarioTest from azext_iot.common.shared import AuthenticationTypeDataplane from azext_iot.tests.iothub import DATAPLANE_AUTH_TYPES @@ -60,12 +61,14 @@ def test_iothub_c2d_messages(self): f"iot device c2d-message receive -d {device_ids[0]} --hub-name {self.entity_name} -g {self.entity_rg} --complete", ).get_output_in_json() - assert c2d_receive_result["data"] == test_body + # TODO - @c-ryan-k - when using login auth type, the payload is not decoded correctly + assert c2d_receive_result["data"] == (NON_DECODABLE_PAYLOAD if auth_phase == AuthenticationTypeDataplane.login.value else test_body) # Assert system properties received_system_props = c2d_receive_result["properties"]["system"] - assert received_system_props["ContentEncoding"] == test_ce - assert received_system_props["ContentType"] == test_ct + # TODO - @c-ryan-k - no system props returned + # assert received_system_props["ContentEncoding"] == test_ce + # assert received_system_props["ContentType"] == test_ct assert received_system_props["iothub-correlationid"] == test_cid assert received_system_props["iothub-messageid"] == test_mid assert received_system_props["iothub-expiry"] diff --git a/setup.py b/setup.py index a27502a37..de9354b56 100644 --- a/setup.py +++ b/setup.py @@ -46,7 +46,7 @@ DEPENDENCIES = [ "azure-core>=1.24.0,<2.0.0", "azure-mgmt-core>=1.3.0,<2.0.0", - "azure-identity>=1.6.1,<2.0.0", + "azure-identity>=1.6.1,<1.18.0", "azure-storage-blob>=12.14.0,<13.0.0", "msrest>=0.6.21", "msrestazure>=0.6.3,<2.0.0", @@ -67,7 +67,6 @@ "Programming Language :: Python", "Programming Language :: Python :: 3 :: Only", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", @@ -80,7 +79,7 @@ setup( name=PACKAGE_NAME, version=VERSION, - python_requires=">=3.8", + python_requires=">=3.9", description=short_description, long_description="{} Intended for power users and/or automation of IoT solutions at scale.".format( short_description From c1f11679e501674b27d8911f6b25d88825671995 Mon Sep 17 00:00:00 2001 From: Ryan Date: Thu, 12 Dec 2024 12:28:51 -0800 Subject: [PATCH 2/6] contenttype and contentencoding property name change --- HISTORY.rst | 17 +++++++++++++ azext_iot/constants.py | 4 +-- azext_iot/iothub/_help.py | 4 +-- .../iothub/providers/device_messaging.py | 2 +- .../iothub/core/test_iot_messaging_int.py | 25 +++++++++---------- .../messaging/test_iothub_c2d_messages_int.py | 7 +++--- 6 files changed, 37 insertions(+), 22 deletions(-) diff --git a/HISTORY.rst b/HISTORY.rst index 5222b17cd..cd68223ae 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -3,6 +3,23 @@ Release History =============== +0.26.0 ++++++++++++++++ + +**General updates** + +* We have dropped support for Python 3.8 +* This extension now supports Python 3.12 as the CLI core is packaging newer releases with this python version, +* **[Breaking Change]** Older versions of `uamqp` (below `1.6.6`) are not compatible with Python 3.12 + * If you update your packaged AZ CLI version (`2.66.0` or later) or otherwise change your environment's Python version to 3.12, you will need to update your `uamqp` dependency to `1.6.6` or above in order to use commands like `az iot hub monitor-events` + * You can repair this dependency at command runtime by utilizing the `--repair` / `-r` argument in `az iot hub monitor-events` + +**IoT device updates** + +* **[Breaking Change]** Device c2d messages (`az iot device c2d-message`) have been updated to support the following service-side changes: + * `ContentEncoding` system property is now `content-encoding` + * `ContentType` system property is now `content-type` + 0.25.0 +++++++++++++++ diff --git a/azext_iot/constants.py b/azext_iot/constants.py index be3cbe583..1af287ae1 100644 --- a/azext_iot/constants.py +++ b/azext_iot/constants.py @@ -23,8 +23,8 @@ "iothub-expiry", "iothub-deliverycount", "iothub-enqueuedtime", - "ContentType", - "ContentEncoding", + "content-type", + "content-encoding", ] METHOD_INVOKE_MAX_TIMEOUT_SEC = 300 METHOD_INVOKE_MIN_TIMEOUT_SEC = 10 diff --git a/azext_iot/iothub/_help.py b/azext_iot/iothub/_help.py index 569be8626..b9db5553b 100644 --- a/azext_iot/iothub/_help.py +++ b/azext_iot/iothub/_help.py @@ -270,8 +270,8 @@ def load_iothub_help(): to `application/octet-stream`. Note: The command only works for symmetric key auth (SAS) based devices. - To enable querying on a message body in message routing, the contentType - system property must be application/JSON and the contentEncoding system + To enable querying on a message body in message routing, the content-type + system property must be application/JSON and the content-encoding system property must be one of the UTF encoding values supported by that system property(UTF-8, UTF-16 or UTF-32). If the content encoding isn't set when Azure Storage is used as routing endpoint, then IoT Hub writes the messages diff --git a/azext_iot/iothub/providers/device_messaging.py b/azext_iot/iothub/providers/device_messaging.py index d2a2774fd..0772639e4 100644 --- a/azext_iot/iothub/providers/device_messaging.py +++ b/azext_iot/iothub/providers/device_messaging.py @@ -219,7 +219,7 @@ def _c2d_message_receive(self, lock_timeout: int = 60, ack: Optional[str] = None payload["properties"]["system"] = sys_props if result.content: - target_encoding = result.headers.get("ContentEncoding", "utf-8") + target_encoding = result.headers.get("content-encoding", "utf-8") payload["data"] = NON_DECODABLE_PAYLOAD if target_encoding in ["utf-8", "utf8", "utf-16", "utf16", "utf-32", "utf32"]: logger.info(f"Decoding message data encoded with: {target_encoding}") diff --git a/azext_iot/tests/iothub/core/test_iot_messaging_int.py b/azext_iot/tests/iothub/core/test_iot_messaging_int.py index 9f108c214..55a8846a1 100644 --- a/azext_iot/tests/iothub/core/test_iot_messaging_int.py +++ b/azext_iot/tests/iothub/core/test_iot_messaging_int.py @@ -102,9 +102,8 @@ def test_uamqp_device_messaging(self): assert result["data"] == test_body system_props = result["properties"]["system"] - # TODO - @c-ryan-k - no system properties - # assert system_props["ContentEncoding"] == test_ce - # assert system_props["ContentType"] == test_ct + assert system_props["content-encoding"] == test_ce + assert system_props["content-type"] == test_ct assert system_props["iothub-correlationid"] == test_cid assert system_props["iothub-messageid"] == test_mid assert system_props["iothub-expiry"] @@ -159,8 +158,8 @@ def test_uamqp_device_messaging(self): self._remove_newlines_spaces(payload=self.kwargs["messaging_data"]) system_props = result["properties"]["system"] - # assert system_props["ContentEncoding"] == test_ce - # assert system_props["ContentType"] == 'application/json' + assert system_props["content-encoding"] == test_ce + assert system_props["content-type"] == 'application/json' assert system_props["iothub-correlationid"] == test_cid assert system_props["iothub-messageid"] == test_mid assert system_props["iothub-expiry"] @@ -211,8 +210,8 @@ def test_uamqp_device_messaging(self): assert result["data"] == self.kwargs["messaging_unicodable_data"] system_props = result["properties"]["system"] - # assert system_props["ContentEncoding"] == test_ce - # assert system_props["ContentType"] == 'application/octet-stream' + assert system_props["content-encoding"] == test_ce + assert system_props["content-type"] == 'application/octet-stream' assert system_props["iothub-correlationid"] == test_cid assert system_props["iothub-messageid"] == test_mid assert system_props["iothub-expiry"] @@ -263,8 +262,8 @@ def test_uamqp_device_messaging(self): assert result["data"] == self.kwargs["messaging_non_unicodable_data"] system_props = result["properties"]["system"] - # assert system_props["ContentEncoding"] == test_ce - # assert system_props["ContentType"] == 'application/octet-stream' + assert system_props["content-encoding"] == test_ce + assert system_props["content-type"] == 'application/octet-stream' assert system_props["iothub-correlationid"] == test_cid assert system_props["iothub-messageid"] == test_mid assert system_props["iothub-expiry"] @@ -316,8 +315,8 @@ def test_uamqp_device_messaging(self): # assert result["data"] == self.kwargs["messaging_non_unicodable_data"] system_props = result["properties"]["system"] - # assert system_props["ContentEncoding"] == 'gzip' - # assert system_props["ContentType"] == 'application/octet-stream' + assert system_props["content-encoding"] == 'gzip' + assert system_props["content-type"] == 'application/octet-stream' assert system_props["iothub-correlationid"] == test_cid assert system_props["iothub-messageid"] == test_mid assert system_props["iothub-expiry"] @@ -392,8 +391,8 @@ def test_uamqp_device_messaging(self): assert result["data"] == self.kwargs["c2d_json_send_data"] system_props = result["properties"]["system"] - # assert system_props["ContentEncoding"] == test_ce - # assert system_props["ContentType"] == test_ct + assert system_props["content-encoding"] == test_ce + assert system_props["content-type"] == test_ct assert system_props["iothub-correlationid"] == test_cid assert system_props["iothub-messageid"] == test_mid assert system_props["iothub-expiry"] diff --git a/azext_iot/tests/iothub/messaging/test_iothub_c2d_messages_int.py b/azext_iot/tests/iothub/messaging/test_iothub_c2d_messages_int.py index ca261c25b..a17e3993b 100644 --- a/azext_iot/tests/iothub/messaging/test_iothub_c2d_messages_int.py +++ b/azext_iot/tests/iothub/messaging/test_iothub_c2d_messages_int.py @@ -62,13 +62,12 @@ def test_iothub_c2d_messages(self): ).get_output_in_json() # TODO - @c-ryan-k - when using login auth type, the payload is not decoded correctly - assert c2d_receive_result["data"] == (NON_DECODABLE_PAYLOAD if auth_phase == AuthenticationTypeDataplane.login.value else test_body) + assert c2d_receive_result["data"] == test_body # Assert system properties received_system_props = c2d_receive_result["properties"]["system"] - # TODO - @c-ryan-k - no system props returned - # assert received_system_props["ContentEncoding"] == test_ce - # assert received_system_props["ContentType"] == test_ct + assert received_system_props["content-encoding"] == test_ce + assert received_system_props["content-type"] == test_ct assert received_system_props["iothub-correlationid"] == test_cid assert received_system_props["iothub-messageid"] == test_mid assert received_system_props["iothub-expiry"] From 6c2800188cc3f75ce51cc45a8d7c93850b17e0c2 Mon Sep 17 00:00:00 2001 From: Ryan Date: Thu, 12 Dec 2024 13:10:10 -0800 Subject: [PATCH 3/6] more fixes --- .../iothub/core/test_iot_messaging_int.py | 25 ++++++++----------- .../messaging/test_iothub_c2d_messages_int.py | 2 -- 2 files changed, 11 insertions(+), 16 deletions(-) diff --git a/azext_iot/tests/iothub/core/test_iot_messaging_int.py b/azext_iot/tests/iothub/core/test_iot_messaging_int.py index 55a8846a1..904db7fa2 100644 --- a/azext_iot/tests/iothub/core/test_iot_messaging_int.py +++ b/azext_iot/tests/iothub/core/test_iot_messaging_int.py @@ -5,14 +5,13 @@ # -------------------------------------------------------------------------------------------- import os -from azure.cli.core.azclierror import AzureResponseError -from azext_iot.iothub.common import NON_DECODABLE_PAYLOAD -from azext_iot.tests.conftest import get_context_path import pytest import json +import time -from time import time from uuid import uuid4 +from azext_iot.iothub.common import NON_DECODABLE_PAYLOAD +from azext_iot.tests.conftest import get_context_path from azext_iot.tests.helpers import CERT_ENDING, KEY_ENDING from azext_iot.tests.iothub import IoTLiveScenarioTest, PREFIX_DEVICE from azext_iot.common.utility import ( @@ -333,15 +332,12 @@ def test_uamqp_device_messaging(self): # Implicit etag assertion etag = result["etag"] - try: - self.cmd( - "iot device c2d-message complete -d {} --hub-name {} -g {} --etag {}".format( - device_ids[0], self.entity_name, self.entity_rg, etag - ), - checks=self.is_empty(), - ) - except AzureResponseError as e: - logger.warning("Error completing message: %s", e) + self.cmd( + "iot device c2d-message complete -d {} --hub-name {} -g {} --etag {}".format( + device_ids[0], self.entity_name, self.entity_rg, etag + ), + checks=self.is_empty(), + ) # Error - Send C2D message with non existed file path self.cmd( @@ -776,8 +772,9 @@ def test_mqtt_device_simulation_x509(self): # x509 CA device simulation and include model Id upon connection model_id_simulate_x509ca = "dtmi:com:example:simulatex509ca;1" + # not sure why this needs a timer but it seems to help avoid unauthorized errors - import time; time.sleep(60) + time.sleep(60) self.cmd( "iot device simulate -d {} -n {} -g {} --da '{}' --mc 1 --mi 1 --cp {} --kp {} --pass {} --model-id '{}'".format( device_ids[1], self.entity_name, self.entity_rg, simulate_msg, diff --git a/azext_iot/tests/iothub/messaging/test_iothub_c2d_messages_int.py b/azext_iot/tests/iothub/messaging/test_iothub_c2d_messages_int.py index a17e3993b..f4f06af7c 100644 --- a/azext_iot/tests/iothub/messaging/test_iothub_c2d_messages_int.py +++ b/azext_iot/tests/iothub/messaging/test_iothub_c2d_messages_int.py @@ -8,7 +8,6 @@ from time import sleep from uuid import uuid4 -from azext_iot.iothub.common import NON_DECODABLE_PAYLOAD from azext_iot.tests.iothub import IoTLiveScenarioTest from azext_iot.common.shared import AuthenticationTypeDataplane from azext_iot.tests.iothub import DATAPLANE_AUTH_TYPES @@ -61,7 +60,6 @@ def test_iothub_c2d_messages(self): f"iot device c2d-message receive -d {device_ids[0]} --hub-name {self.entity_name} -g {self.entity_rg} --complete", ).get_output_in_json() - # TODO - @c-ryan-k - when using login auth type, the payload is not decoded correctly assert c2d_receive_result["data"] == test_body # Assert system properties From 446a051aba2815dc909a619fd377bf47d618254c Mon Sep 17 00:00:00 2001 From: Ryan Date: Thu, 12 Dec 2024 14:40:54 -0800 Subject: [PATCH 4/6] fix import --- azext_iot/tests/iothub/core/test_iot_messaging_int.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/azext_iot/tests/iothub/core/test_iot_messaging_int.py b/azext_iot/tests/iothub/core/test_iot_messaging_int.py index 904db7fa2..b9f055ca0 100644 --- a/azext_iot/tests/iothub/core/test_iot_messaging_int.py +++ b/azext_iot/tests/iothub/core/test_iot_messaging_int.py @@ -7,7 +7,7 @@ import os import pytest import json -import time +from time import time, sleep from uuid import uuid4 from azext_iot.iothub.common import NON_DECODABLE_PAYLOAD @@ -774,7 +774,7 @@ def test_mqtt_device_simulation_x509(self): model_id_simulate_x509ca = "dtmi:com:example:simulatex509ca;1" # not sure why this needs a timer but it seems to help avoid unauthorized errors - time.sleep(60) + sleep(60) self.cmd( "iot device simulate -d {} -n {} -g {} --da '{}' --mc 1 --mi 1 --cp {} --kp {} --pass {} --model-id '{}'".format( device_ids[1], self.entity_name, self.entity_rg, simulate_msg, From 40e4fe65b8457cbe24c6af9c0b7438e770c60ea2 Mon Sep 17 00:00:00 2001 From: Ryan Date: Fri, 13 Dec 2024 12:50:31 -0800 Subject: [PATCH 5/6] Bump uamqp dev dependency to ~=1.6.6 and update ensure/test code --- azext_iot/common/deps.py | 10 +++--- azext_iot/constants.py | 4 +-- .../tests/utility/test_iot_utility_unit.py | 36 +++++++++++++++++-- dev_requirements | 2 +- 4 files changed, 41 insertions(+), 11 deletions(-) diff --git a/azext_iot/common/deps.py b/azext_iot/common/deps.py index 4ea65c5a4..61f358c89 100644 --- a/azext_iot/common/deps.py +++ b/azext_iot/common/deps.py @@ -8,17 +8,17 @@ from os import linesep from azure.cli.core.azclierror import CLIInternalError -from azext_iot.constants import EVENT_LIB, VERSION +from azext_iot.constants import UAMQP_DEP_NAME, UAMQP_COMPAT_VERSION, VERSION from azext_iot.common.utility import test_import_and_version from azext_iot.common.pip import install from azext_iot.common._homebrew_patch import HomebrewPipPatch def ensure_uamqp(config, yes=False, repair=False): - if repair or not test_import_and_version(EVENT_LIB[0], EVENT_LIB[1]): + if repair or not test_import_and_version(UAMQP_DEP_NAME, UAMQP_COMPAT_VERSION): if not yes: input_txt = ('Dependency update ({} {}) required for IoT extension version: {}. {}' - 'Continue? (y/n) -> ').format(EVENT_LIB[0], EVENT_LIB[1], VERSION, linesep) + 'Continue? (y/n) -> ').format(UAMQP_DEP_NAME, UAMQP_COMPAT_VERSION, VERSION, linesep) i = input(input_txt) if i.lower() != 'y': sys.exit('User has declined update...') @@ -27,8 +27,8 @@ def ensure_uamqp(config, yes=False, repair=False): with HomebrewPipPatch(): # The version range defined in this custom_version parameter should be stable try: - install(EVENT_LIB[0], compatible_version='{}'.format(EVENT_LIB[1])) + install(UAMQP_DEP_NAME, compatible_version=UAMQP_COMPAT_VERSION) print('Update complete. Executing command...') except RuntimeError as e: - print('Failure updating {}. Aborting...'.format(EVENT_LIB[0])) + print('Failure updating {}. Aborting...'.format(UAMQP_DEP_NAME)) raise CLIInternalError(e) diff --git a/azext_iot/constants.py b/azext_iot/constants.py index 1af287ae1..019c21aef 100644 --- a/azext_iot/constants.py +++ b/azext_iot/constants.py @@ -47,6 +47,6 @@ IOTHUB_THROTTLE_SLEEP_SEC = 20 THROTTLE_HTTP_STATUS_CODE = 429 IOTHUB_RENEW_KEY_BATCH_SIZE = 100 -# (Lib name, minimum version (including), maximum version (excluding)) -EVENT_LIB = ("uamqp", "1.2", "1.3") +UAMQP_DEP_NAME = "uamqp" +UAMQP_COMPAT_VERSION = "1.6.6" PNP_DTDLV2_COMPONENT_MARKER = "__t" diff --git a/azext_iot/tests/utility/test_iot_utility_unit.py b/azext_iot/tests/utility/test_iot_utility_unit.py index aea961d5d..1f03e9b80 100644 --- a/azext_iot/tests/utility/test_iot_utility_unit.py +++ b/azext_iot/tests/utility/test_iot_utility_unit.py @@ -11,6 +11,7 @@ from unittest import mock from knack.util import CLIError +from importlib.metadata import PackageNotFoundError from azure.cli.core.azclierror import CLIInternalError from azure.cli.core.extension import get_extension_path from azext_iot.common.utility import ( @@ -24,7 +25,7 @@ ) from azext_iot.operations.generic import _process_top from azext_iot.common.deps import ensure_uamqp -from azext_iot.constants import EVENT_LIB, EXTENSION_NAME +from azext_iot.constants import EXTENSION_NAME, UAMQP_DEP_NAME, UAMQP_COMPAT_VERSION from azext_iot._validators import mode2_iot_login_handler from azext_iot.common.embedded_cli import EmbeddedCLI @@ -153,8 +154,8 @@ def test_ensure_uamqp_version( assert uamqp_scenario["exit"].call_args else: install_args = uamqp_scenario["installer"].call_args - assert install_args[0][0] == EVENT_LIB[0] - assert install_args[1]["compatible_version"] == EVENT_LIB[1] + assert install_args[0][0] == UAMQP_DEP_NAME + assert install_args[1]["compatible_version"] == UAMQP_COMPAT_VERSION class TestInstallPipPackage(object): @@ -320,6 +321,35 @@ def test_ensure_iotdps_sdk_min_version(self, mocker, current, minimum, expected) assert ensure_iotdps_sdk_min_version(minimum) == expected + @pytest.mark.parametrize( + "installed, expected, result", + [ + # nothing installed, check for compat version + (None, UAMQP_COMPAT_VERSION, False), + # 1.2, check for compat version + ("1.2", UAMQP_COMPAT_VERSION, False), + # 1.6.5, check for compat version, + ("1.6.5", UAMQP_COMPAT_VERSION, False), + # compat version installed + ("1.6.6", UAMQP_COMPAT_VERSION, True), + # compat++ version installed + ("1.6.7", UAMQP_COMPAT_VERSION, True), + # 1.9 installed, 1.10 expected + ("1.9.9", "1.10.0", False), + ] + + ) + def test_test_import_and_version(self, mocker, installed, expected, result): + from azext_iot.common.utility import test_import_and_version + + mocked_version = mocker.patch("importlib.metadata.version") + if installed: + mocked_version.return_value = installed + else: + mocked_version.side_effect = [PackageNotFoundError] + + assert test_import_and_version(package=UAMQP_DEP_NAME, expected_version=expected) == result + class TestEmbeddedCli(object): @pytest.fixture(params=[0, 1, 2]) diff --git a/dev_requirements b/dev_requirements index b206dc23f..9d36afa81 100644 --- a/dev_requirements +++ b/dev_requirements @@ -2,7 +2,7 @@ pytest==8.1.1 pytest-mock==3.12.0 pytest-cov pytest-env -uamqp>=1.2,<=1.6.8 +uamqp~=1.6.6 responses==0.22.0 black setuptools==70.0.0 From 34f93a70c8518203f0046a8444ae79f55fa97311 Mon Sep 17 00:00:00 2001 From: Ryan Date: Fri, 13 Dec 2024 12:55:46 -0800 Subject: [PATCH 6/6] bump version to 0.26.0 --- azext_iot/constants.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azext_iot/constants.py b/azext_iot/constants.py index 019c21aef..c2f603862 100644 --- a/azext_iot/constants.py +++ b/azext_iot/constants.py @@ -7,7 +7,7 @@ import os -VERSION = "0.25.0" +VERSION = "0.26.0" EXTENSION_NAME = "azure-iot" EXTENSION_ROOT = os.path.dirname(os.path.abspath(__file__)) EXTENSION_CONFIG_ROOT_KEY = "iotext"