From b078c5e0a829829d18d42eb1fe9f2cea9272a3da Mon Sep 17 00:00:00 2001 From: avagraw <51140335+avagraw@users.noreply.github.com> Date: Thu, 10 Jun 2021 16:01:35 -0700 Subject: [PATCH] Merging changes from Dev branch into Device client integration (#372) * Introducing IoT Hub dataplane RBAC support + various improvements (#341) * Introducing IoT Hub dataplane RBAC support. * IoT Hub rbac support for most commands. * Removed deprecated nested IoT edge artifacts. * Removed deprecated show-connection-string artifacts. * --auth-type supports configurable defaults. * Improved IoT Hub test infrastructure. * Significant refactor of IoT Hub tests. * Various additional improvements. * All warnings from tests are now shown. * Uamqp integration with jwt auth. * Modify HISTORY.rst * Iotc command versioning (#340) * add support for v1 and preview routes * update history file * address review comments * lint fixes * Add warning for qos deprecation and update contributing guide (#342) * Add warning for qos deprecation and update contributing guide * removing version from deprecate_info * Integration tests command update in Contributing guide * Integrate TQDM to show progress bar when simulator sends d2c messages * remove unused loop varable * Update progress bar descriptionb * Iotc command ga (#348) * remove preview tags * history updates * Use enum value instead of literal str. (#349) * Managed identity support for device-identity import and export (#344) * Fix for new identity parameter format * test updates * better differentiation of storage vars * updated to remove *all* user-identitites after storage tests until CLI core is patched * Update azext_metadata.json (#351) * Increment version to v0.10.13 * Update README.md * Update HISTORY.rst * update twin reported properties during simulation * update unit tests * Add dataplane reset (#352) * pr comments * update dt reset to only support deleting both * Remove unused import * styling updates * C2D messaging improvements. (#354) * Remove hiding of warnings from sample pytest.ini * Digital Twin wait commands (#345) * update to use wait * fix error status code * int testing * update help * Add Identity Storage Account ID param to sentinel values (#355) * Using SDK Listener for Twin properties update * Module identity renew key (#356) * initial changes * update params, help * add missing module to test * Word change * Update README.md * merge from remote * Pipeline updates (#359) * Pipeline updates Added nightly build pipeline Added template to run tests against minimum supported AZ CLI * Pipeline parameter refactoring * Moved deprecated ubuntu images to `ubuntu-latest` Co-authored-by: Paymaun * Structured mqtt formatting and eliminate dependency on six * remove indent property not needed any more * Check for conditionals before running test jobs (#363) * Update d2c and simulate commands to return errors for non SaS devices - update help to reflect the change (#346) * Add warning for qos deprecation and update contributing guide * removing version from deprecate_info * Integration tests command update in Contributing guide * Update help message for d2c command and mqtt simulation to indicate only sas auth is supported * Checks and error messages when non SAS devices are used for MQTT operations * Indentation update in help file * fix styling * Adding enum and updating test * Addressed comments * Support C2D Message decoding and Add TQDM for HTTP simulation * Update MQTT operations to run on web sockets * Remove duplicate test already in dev branch * Device connection is automatic now * Styling update Co-authored-by: Paymaun Co-authored-by: valluriraj Co-authored-by: Ryan K Co-authored-by: Paymaun Heidari Co-authored-by: vilit1 <73560279+vilit1@users.noreply.github.com> --- .azure-devops/create-release.yml | 6 +- .azure-devops/merge.yml | 10 +-- .azure-devops/nightly.yml | 6 +- CONTRIBUTING.md | 4 +- HISTORY.rst | 25 +----- azext_iot/_help.py | 3 +- azext_iot/central/command_map.py | 11 --- azext_iot/central/commands_device.py | 4 +- azext_iot/central/models/__init__.py | 4 - azext_iot/central/params.py | 7 -- .../central/providers/preview/__init__.py | 9 -- .../preview/device_provider_preview.py | 37 ++------ azext_iot/central/services/device.py | 47 ---------- azext_iot/constants.py | 2 +- azext_iot/operations/hub.py | 86 ++++++------------- .../json/device_template_int_test.json | 9 -- .../tests/central/test_iot_central_int.py | 48 ++--------- .../tests/central/test_iot_central_unit.py | 4 - azext_iot/tests/conftest.py | 66 ++++++++++++++ azext_iot/tests/iothub/test_iot_ext_unit.py | 2 +- .../tests/iothub/test_iothub_utilities_int.py | 11 --- dev_requirements | 3 +- 22 files changed, 125 insertions(+), 279 deletions(-) diff --git a/.azure-devops/create-release.yml b/.azure-devops/create-release.yml index 5ad9a05c0..d558f5a2e 100644 --- a/.azure-devops/create-release.yml +++ b/.azure-devops/create-release.yml @@ -32,7 +32,7 @@ stages: - job: 'Build_Publish_Azure_IoT_CLI_Extension' pool: - vmImage: 'Ubuntu-16.04' + vmImage: 'ubuntu-latest' steps: - task: UsePythonVersion@0 @@ -46,7 +46,7 @@ stages: - job: 'Build_Publish_Azure_CLI_Test_SDK' pool: - vmImage: 'Ubuntu-16.04' + vmImage: 'ubuntu-latest' steps: - task: UsePythonVersion@0 @@ -72,7 +72,7 @@ stages: - stage: 'test' displayName: 'Run tests' pool: - vmImage: 'Ubuntu-16.04' + vmImage: 'ubuntu-latest' dependsOn: build jobs: - job: 'testCentral' diff --git a/.azure-devops/merge.yml b/.azure-devops/merge.yml index b375250a2..5e9a25a83 100644 --- a/.azure-devops/merge.yml +++ b/.azure-devops/merge.yml @@ -22,7 +22,7 @@ jobs: - job: 'build_and_publish_azure_iot_cli_ext' pool: - vmImage: 'Ubuntu-16.04' + vmImage: 'ubuntu-latest' steps: - task: UsePythonVersion@0 @@ -35,7 +35,7 @@ jobs: - job: 'build_and_publish_azure_cli_test_sdk' pool: - vmImage: 'Ubuntu-16.04' + vmImage: 'ubuntu-latest' steps: - task: UsePythonVersion@0 @@ -49,7 +49,7 @@ jobs: - job: 'run_unit_tests_ubuntu' dependsOn: [ 'build_and_publish_azure_iot_cli_ext', 'build_and_publish_azure_cli_test_sdk'] pool: - vmImage: 'Ubuntu-16.04' + vmImage: 'ubuntu-latest' strategy: matrix: Python36: @@ -103,7 +103,7 @@ jobs: - job: 'run_style_check' dependsOn: ['build_and_publish_azure_iot_cli_ext', 'build_and_publish_azure_cli_test_sdk'] pool: - vmImage: 'Ubuntu-16.04' + vmImage: 'ubuntu-latest' steps: - task: UsePythonVersion@0 @@ -128,7 +128,7 @@ jobs: dependsOn: ['build_and_publish_azure_iot_cli_ext'] displayName: 'Evaluate IoT extension command table' pool: - vmImage: 'Ubuntu-16.04' + vmImage: 'ubuntu-latest' steps: - task: UsePythonVersion@0 diff --git a/.azure-devops/nightly.yml b/.azure-devops/nightly.yml index c58fe3050..34ce2b14d 100644 --- a/.azure-devops/nightly.yml +++ b/.azure-devops/nightly.yml @@ -17,7 +17,7 @@ stages: - job: 'Build_Publish_Azure_IoT_CLI_Extension' pool: - vmImage: 'Ubuntu-16.04' + vmImage: 'ubuntu-latest' steps: - task: UsePythonVersion@0 @@ -31,7 +31,7 @@ stages: - job: 'Build_Publish_Azure_CLI_Test_SDK' pool: - vmImage: 'Ubuntu-16.04' + vmImage: 'ubuntu-latest' steps: - task: UsePythonVersion@0 @@ -57,7 +57,7 @@ stages: - stage: 'test' displayName: 'Run all tests' pool: - vmImage: 'Ubuntu-16.04' + vmImage: 'ubuntu-latest' dependsOn: build jobs: - job: 'azEdge' diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4b199d06c..8301c0439 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -140,13 +140,13 @@ You can either manually set the environment variables or use the `pytest.ini.exa Execute the following command to run the IoT Hub integration tests: -`pytest azext_iot/tests/iothub/test_iot_ext_int.py` +`pytest azext_iot/tests/iothub/ -k "_int"` ##### Device Provisioning Service Execute the following command to run the IoT Hub DPS integration tests: -`pytest azext_iot/tests/dps/test_iot_dps_int.py` +`pytest azext_iot/tests/dps/ -k "_int"` #### Unit and Integration Tests Single Command diff --git a/HISTORY.rst b/HISTORY.rst index 1ae89c7a3..397899c48 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -3,41 +3,20 @@ Release History =============== -0.10.15 -+++++++++++++++ - -**IoT Central updates** - -* Adds support for listing device groups -* Adds support for listing roles and get role by id - 0.10.14 +++++++++++++++ -**IoT Central updates** - -* Adds support to run root/interface level device commands. -* Adds support to get command history for root/interface level device commands. -* The --interface-id parameter for commands "device command run" , "device command history" changed to optional. - **IoT Hub updates** * Fix for "az iot hub c2d-message receive" - the command will use the "ContentEncoding" header value (which indicates the message body encoding) or fallback to utf-8 to decode the received message body. -* Addition for "az iot hub generate-sas-token" - the command will allow offline generation of a SAS Token using a connection string. - -* Changes to Edge validation for set-modules and edge deployment creation: - - By default only properties of system modules $edgeAgent and $edgeHub are validated against schemas installed with the IoT extension. - This can be disabled by using the --no-validation switch. - **Azure Digital Twins updates** * Addition of the following commands * az dt reset - Preview command which deletes all data entities from the target instance (models, twins, twin relationships). - + 0.10.13 +++++++++++++++ @@ -55,7 +34,7 @@ Release History * Public API GA update * Remove preview tag for api-token, device, device-template, user routes. Default routes use central GA API's. - * Add support for preview and 1.0 routes. + * Add support for preview and 1.0 routes. * Addition of the optional '--av' argument to specify the version of API for the requested operation. **IoT Hub updates** diff --git a/azext_iot/_help.py b/azext_iot/_help.py index c1d2f17a5..6ddd4cee7 100644 --- a/azext_iot/_help.py +++ b/azext_iot/_help.py @@ -878,7 +878,8 @@ While the device simulation is running, the device will automatically receive and acknowledge cloud-to-device (c2d) messages. For mqtt simulation, all c2d messages will be acknowledged with completion. For http simulation c2d acknowledgement is based on user - selection which can be complete, reject or abandon. The mqtt simulation also supports direct + selection which can be complete, reject or abandon. Additionally, mqtt simulation is only + supported for symmetric key auth (SAS) based devices. The mqtt simulation also supports direct method invocation which can be acknowledged by a response status code and response payload Note: The command by default will set content-type to application/json and content-encoding diff --git a/azext_iot/central/command_map.py b/azext_iot/central/command_map.py index 1d8b185eb..52da2a4b5 100644 --- a/azext_iot/central/command_map.py +++ b/azext_iot/central/command_map.py @@ -111,17 +111,6 @@ def load_central_commands(self, _): cmd_group.command("create", "create_device_template") cmd_group.command("delete", "delete_device_template") - with self.command_group( - "iot central device-group", command_type=central_device_groups_ops, is_preview=True - ) as cmd_group: - cmd_group.command("list", "list_device_groups") - - with self.command_group( - "iot central role", command_type=central_roles_ops, is_preview=True - ) as cmd_group: - cmd_group.show_command("show", "get_role") - cmd_group.command("list", "list_roles") - with self.command_group( "iot central device twin", command_type=central_device_twin_ops, ) as cmd_group: diff --git a/azext_iot/central/commands_device.py b/azext_iot/central/commands_device.py index 8e20f0aa7..0a134a9eb 100644 --- a/azext_iot/central/commands_device.py +++ b/azext_iot/central/commands_device.py @@ -122,7 +122,7 @@ def run_command( else: provider = CentralDeviceProviderV1(cmd=cmd, app_id=app_id, token=token) - return provider.run_command( + return provider.run_component_command( device_id=device_id, interface_id=interface_id, command_name=command_name, @@ -174,7 +174,7 @@ def get_command_history( else: provider = CentralDeviceProviderV1(cmd=cmd, app_id=app_id, token=token) - return provider.get_command_history( + return provider.get_component_command_history( device_id=device_id, interface_id=interface_id, command_name=command_name, diff --git a/azext_iot/central/models/__init__.py b/azext_iot/central/models/__init__.py index 475d899bf..cc1abd64c 100644 --- a/azext_iot/central/models/__init__.py +++ b/azext_iot/central/models/__init__.py @@ -8,16 +8,12 @@ from azext_iot.central.models.devicetwin import DeviceTwin from azext_iot.central.models.templatepreview import TemplatePreview from azext_iot.central.models.templatev1 import TemplateV1 -from azext_iot.central.models.deviceGroupPreview import DeviceGroupPreview -from azext_iot.central.models.rolePreview import RolePreview __all__ = [ "DevicePreview", - "DeviceGroupPreview", "DeviceV1", "DeviceTwin", "TemplatePreview", "TemplateV1", - "RolePreview" ] diff --git a/azext_iot/central/params.py b/azext_iot/central/params.py index e865d07a8..d6f5d7e56 100644 --- a/azext_iot/central/params.py +++ b/azext_iot/central/params.py @@ -215,10 +215,3 @@ def load_central_arguments(self, _): options_list=["--module-id", "-m"], help="Provide IoT Edge Module ID if the device type is IoT Edge.", ) - - with self.argument_context("iot central role") as context: - context.argument( - "role_id", - options_list=["--role-id", "-r"], - help="Provide a unique identifier for the role" - ) diff --git a/azext_iot/central/providers/preview/__init__.py b/azext_iot/central/providers/preview/__init__.py index db856a08e..f47cdd416 100644 --- a/azext_iot/central/providers/preview/__init__.py +++ b/azext_iot/central/providers/preview/__init__.py @@ -17,19 +17,10 @@ from azext_iot.central.providers.preview.api_token_provider_preview import ( CentralApiTokenProviderPreview, ) -from azext_iot.central.providers.preview.device_group_provider_preview import ( - CentralDeviceGroupProviderPreview -) -from azext_iot.central.providers.preview.role_provider_preview import ( - CentralRoleProviderPreview -) __all__ = [ "CentralDeviceProviderPreview", "CentralDeviceTemplateProviderPreview", "CentralUserProviderPreview", "CentralApiTokenProviderPreview", - "CentralDeviceGroupProviderPreview", - "CentralRoleProviderPreview" - ] diff --git a/azext_iot/central/providers/preview/device_provider_preview.py b/azext_iot/central/providers/preview/device_provider_preview.py index c016a4dfd..49368258e 100644 --- a/azext_iot/central/providers/preview/device_provider_preview.py +++ b/azext_iot/central/providers/preview/device_provider_preview.py @@ -126,7 +126,7 @@ def delete_device(self, device_id, central_dns_suffix=CENTRAL_ENDPOINT,) -> dict return result - def run_command( + def run_component_command( self, device_id: str, interface_id: str, @@ -134,56 +134,31 @@ def run_command( payload: dict, central_dns_suffix=CENTRAL_ENDPOINT, ) -> dict: - - if interface_id: - return central_services.device.run_component_command( - cmd=self._cmd, - app_id=self._app_id, - token=self._token, - device_id=device_id, - interface_id=interface_id, - command_name=command_name, - payload=payload, - central_dns_suffix=central_dns_suffix, - api_version=ApiVersion.preview.value, - ) - - return central_services.device.run_command( + return central_services.device.run_component_command( cmd=self._cmd, app_id=self._app_id, token=self._token, device_id=device_id, + interface_id=interface_id, command_name=command_name, payload=payload, central_dns_suffix=central_dns_suffix, api_version=ApiVersion.preview.value, ) - def get_command_history( + def get_component_command_history( self, device_id: str, interface_id: str, command_name: str, central_dns_suffix=CENTRAL_ENDPOINT, ) -> dict: - - if interface_id: - return central_services.device.get_component_command_history( - cmd=self._cmd, - app_id=self._app_id, - token=self._token, - device_id=device_id, - interface_id=interface_id, - command_name=command_name, - central_dns_suffix=central_dns_suffix, - api_version=ApiVersion.preview.value, - ) - - return central_services.device.get_command_history( + return central_services.device.get_component_command_history( cmd=self._cmd, app_id=self._app_id, token=self._token, device_id=device_id, + interface_id=interface_id, command_name=command_name, central_dns_suffix=central_dns_suffix, api_version=ApiVersion.preview.value, diff --git a/azext_iot/central/services/device.py b/azext_iot/central/services/device.py index 60170f350..7186ca163 100644 --- a/azext_iot/central/services/device.py +++ b/azext_iot/central/services/device.py @@ -303,53 +303,6 @@ def get_device_credentials( return _utility.try_extract_result(response) -def run_command( - cmd, - app_id: str, - token: str, - device_id: str, - command_name: str, - payload: dict, - central_dns_suffix=CENTRAL_ENDPOINT, - api_version=ApiVersion.v1.value, -): - """ - Execute a direct method on a device - - Args: - cmd: command passed into az - app_id: name of app (used for forming request URL) - device_id: unique case-sensitive device id - command_name: name of command to execute - payload: params for command - token: (OPTIONAL) authorization token to fetch device details from IoTC. - MUST INCLUDE type (e.g. 'SharedAccessToken ...', 'Bearer ...') - central_dns_suffix: {centralDnsSuffixInPath} as found in docs - - Returns: - result (currently a 201) - """ - url = "https://{}.{}/{}/{}/commands/{}".format( - app_id, central_dns_suffix, BASE_PATH, device_id, command_name - ) - headers = _utility.get_headers(token, cmd) - - # Construct parameters - query_parameters = {} - query_parameters["api-version"] = api_version - - response = requests.post( - url, headers=headers, json=payload, params=query_parameters - ) - - # execute command response has caveats in it due to Async/Sync device methods - # return the response if we get 201, otherwise try to apply generic logic - if response.status_code == 201: - return response.json() - - return _utility.try_extract_result(response) - - def run_component_command( cmd, app_id: str, diff --git a/azext_iot/constants.py b/azext_iot/constants.py index f00235ede..b3e2dcf63 100644 --- a/azext_iot/constants.py +++ b/azext_iot/constants.py @@ -7,7 +7,7 @@ import os -VERSION = "0.10.15" +VERSION = "0.10.14" EXTENSION_NAME = "azure-iot" EXTENSION_ROOT = os.path.dirname(os.path.abspath(__file__)) EXTENSION_CONFIG_ROOT_KEY = "iotext" diff --git a/azext_iot/operations/hub.py b/azext_iot/operations/hub.py index 295321fcf..c9de605f1 100644 --- a/azext_iot/operations/hub.py +++ b/azext_iot/operations/hub.py @@ -27,7 +27,6 @@ RenewKeyType, IoTHubStateType, DeviceAuthApiType, - ConnectionStringParser, ) from azext_iot.iothub.providers.discovery import IotHubDiscovery from azext_iot.common.utility import ( @@ -257,18 +256,11 @@ def _assemble_auth(auth_method, pk, sk): ) auth = None - if auth_method in [ - DeviceAuthType.shared_private_key.name, - DeviceAuthApiType.sas.value, - ]: + if auth_method in [DeviceAuthType.shared_private_key.name, DeviceAuthApiType.sas.value]: auth = AuthenticationMechanism( - symmetric_key=SymmetricKey(primary_key=pk, secondary_key=sk), - type=DeviceAuthApiType.sas.value, + symmetric_key=SymmetricKey(primary_key=pk, secondary_key=sk), type=DeviceAuthApiType.sas.value ) - elif auth_method in [ - DeviceAuthType.x509_thumbprint.name, - DeviceAuthApiType.selfSigned.value, - ]: + elif auth_method in [DeviceAuthType.x509_thumbprint.name, DeviceAuthApiType.selfSigned.value]: if not pk: raise ValueError("primary thumbprint required with selfSigned auth") auth = AuthenticationMechanism( @@ -277,13 +269,8 @@ def _assemble_auth(auth_method, pk, sk): ), type=DeviceAuthApiType.selfSigned.value, ) - elif auth_method in [ - DeviceAuthType.x509_ca.name, - DeviceAuthApiType.certificateAuthority.value, - ]: - auth = AuthenticationMechanism( - type=DeviceAuthApiType.certificateAuthority.value - ) + elif auth_method in [DeviceAuthType.x509_ca.name, DeviceAuthApiType.certificateAuthority.value]: + auth = AuthenticationMechanism(type=DeviceAuthApiType.certificateAuthority.value) else: raise ValueError("Authorization method {} invalid.".format(auth_method)) return auth @@ -877,11 +864,7 @@ def _handle_module_update_params(parameters): def _parse_auth(parameters): - valid_auth = [ - DeviceAuthApiType.sas.value, - DeviceAuthApiType.selfSigned.value, - DeviceAuthApiType.certificateAuthority.value, - ] + valid_auth = [DeviceAuthApiType.sas.value, DeviceAuthApiType.selfSigned.value, DeviceAuthApiType.certificateAuthority.value] auth = parameters["authentication"].get("type") if auth not in valid_auth: raise CLIError("authentication.type must be one of {}".format(valid_auth)) @@ -1956,7 +1939,6 @@ def iot_get_sas_token( login=None, module_id=None, auth_type_dataplane=None, - connection_string=None, ): key_type = key_type.lower() policy_name = policy_name.lower() @@ -2126,10 +2108,7 @@ def _build_device_or_module_connection_string(entity, key_type="primary"): if key_type == "primary" else auth["symmetricKey"]["secondaryKey"] ) - elif auth_type in [ - DeviceAuthApiType.certificateAuthority.value.lower(), - DeviceAuthApiType.selfSigned.value.lower(), - ]: + elif auth_type in [DeviceAuthApiType.certificateAuthority.value.lower(), DeviceAuthApiType.selfSigned.value.lower()]: key = "x509=true" else: raise CLIError("Unable to form target connection string") @@ -2426,7 +2405,7 @@ def _iot_c2d_message_receive(target, device_id, lock_timeout=60, ack=None): if result.content: target_encoding = result.headers.get("ContentEncoding", "utf-8") logger.info(f"Decoding message data encoded with: {target_encoding}") - payload["data"] = result.content.decode(encoding=target_encoding) + payload["data"] = result.content.decode(target_encoding) return payload return @@ -2592,6 +2571,10 @@ def http_wrap(target, device_id, generator, msg_interval, msg_count): if protocol_type == ProtocolType.mqtt.name: device = _iot_device_show(target, device_id) device_connection_string = _build_device_or_module_connection_string(device, KeyType.primary.value) + + if device and device.get("authentication", {}).get("type", "") != DeviceAuthApiType.sas.value: + raise CLIError('MQTT simulation is only supported for symmetric key auth (SAS) based devices') + client_mqtt = mqtt_client( target=target, device_conn_string=device_connection_string, @@ -2677,7 +2660,6 @@ def iot_device_export( resource_group_name=None, ): from azext_iot._factory import iot_hub_service_factory - client = iot_hub_service_factory(cmd.cli_ctx) discovery = IotHubDiscovery(cmd) target = discovery.get_target( @@ -2703,31 +2685,21 @@ def iot_device_export( authentication_type=storage_authentication_type, ) - user_identity = identity not in [None, "[system]"] - if ( - user_identity - and storage_authentication_type != AuthenticationType.identityBased.name - ): + user_identity = identity not in [None, '[system]'] + if user_identity and storage_authentication_type != AuthenticationType.identityBased.name: raise CLIError( "Device export with user-assigned identities requires identity-based authentication [--storage-auth-type]" ) # Track 2 CLI SDKs provide support for user-assigned identity objects - if ( - ensure_iothub_sdk_min_version(IOTHUB_TRACK_2_SDK_MIN_VERSION) - and user_identity - ): - from azure.mgmt.iothub.models import ( - ManagedIdentity, - ) # pylint: disable=no-name-in-module - + if ensure_iothub_sdk_min_version(IOTHUB_TRACK_2_SDK_MIN_VERSION) and user_identity: + from azure.mgmt.iothub.models import ManagedIdentity # pylint: disable=no-name-in-module export_request.identity = ManagedIdentity(user_assigned_identity=identity) # if the user supplied a user-assigned identity, let them know they need a new CLI/SDK elif user_identity: raise CLIError( - "Device export with user-assigned identities requires a dependency of azure-mgmt-iothub>={}".format( - IOTHUB_TRACK_2_SDK_MIN_VERSION - ) + "Device export with user-assigned identities requires a dependency of azure-mgmt-iothub>={}" + .format(IOTHUB_TRACK_2_SDK_MIN_VERSION) ) return client.export_devices( @@ -2789,30 +2761,20 @@ def iot_device_import( authentication_type=storage_authentication_type, ) - user_identity = identity not in [None, "[system]"] - if ( - user_identity - and storage_authentication_type != AuthenticationType.identityBased.name - ): + user_identity = identity not in [None, '[system]'] + if user_identity and storage_authentication_type != AuthenticationType.identityBased.name: raise CLIError( "Device import with user-assigned identities requires identity-based authentication [--storage-auth-type]" ) # Track 2 CLI SDKs provide support for user-assigned identity objects - if ( - ensure_iothub_sdk_min_version(IOTHUB_TRACK_2_SDK_MIN_VERSION) - and user_identity - ): - from azure.mgmt.iothub.models import ( - ManagedIdentity, - ) # pylint: disable=no-name-in-module - + if ensure_iothub_sdk_min_version(IOTHUB_TRACK_2_SDK_MIN_VERSION) and user_identity: + from azure.mgmt.iothub.models import ManagedIdentity # pylint: disable=no-name-in-module import_request.identity = ManagedIdentity(user_assigned_identity=identity) # if the user supplied a user-assigned identity, let them know they need a new CLI/SDK elif user_identity: raise CLIError( - "Device import with user-assigned identities requires a dependency of azure-mgmt-iothub>={}".format( - IOTHUB_TRACK_2_SDK_MIN_VERSION - ) + "Device import with user-assigned identities requires a dependency of azure-mgmt-iothub>={}" + .format(IOTHUB_TRACK_2_SDK_MIN_VERSION) ) return client.import_devices( diff --git a/azext_iot/tests/central/json/device_template_int_test.json b/azext_iot/tests/central/json/device_template_int_test.json index bb0e0b5db..1e238e4ba 100644 --- a/azext_iot/tests/central/json/device_template_int_test.json +++ b/azext_iot/tests/central/json/device_template_int_test.json @@ -30,15 +30,6 @@ ], "displayName": "Component" } - }, - { - "@id": "urn:rigado:RS40_Occupancy_Sensor:testRootCommand:9", - "@type": "Command", - "commandType": "synchronous", - "displayName": { - "en": "testRootCommand" - }, - "name": "testRootCommand" } ], "displayName": "larger-telemetry-device", diff --git a/azext_iot/tests/central/test_iot_central_int.py b/azext_iot/tests/central/test_iot_central_int.py index a332ee726..9d4fef075 100644 --- a/azext_iot/tests/central/test_iot_central_int.py +++ b/azext_iot/tests/central/test_iot_central_int.py @@ -392,43 +392,7 @@ def test_central_device_registration_info_registered(self): assert device_registration_info.get("status") is None assert dps_state.get("error") == "Device is not yet provisioned." - def test_central_run_command_root_level(self): - command_name = "testRootCommand" - (template_id, _) = self._create_device_template() - (device_id, _) = self._create_device(template=template_id, simulated=True) - - self._wait_for_provisioned(device_id) - - run_command_result = self.cmd( - "iot central device command run" - " -n {}" - " -d {}" - " --cn {}" - " -k '{}'" - "".format(APP_ID, device_id, command_name, sync_command_params) - ) - - show_command_result = self.cmd( - "iot central device command history" - " -n {}" - " -d {}" - " --cn {}" - "".format(APP_ID, device_id, command_name) - ) - - self._delete_device(device_id) - self._delete_device_template(template_id) - - run_result = run_command_result.get_output_in_json() - show_result = show_command_result.get_output_in_json() - - # from file indicated by `sync_command_params` - assert run_result["request"] == {"argument": "value"} - - # check that run result and show result indeed match - assert run_result["response"] == show_result["value"][0]["response"] - - def test_central_run_command_component(self): + def test_central_run_command(self): interface_id = "dtmiIntTestDeviceTemplateV33jl" command_name = "testCommand" (template_id, _) = self._create_device_template() @@ -709,12 +673,12 @@ def _create_device_template(self): template_name = template["displayName"] template_id = template_name + "id" - command = "iot central device-template create --app-id {} --device-template-id {} -k '{}'".format( - APP_ID, template_id, device_template_path + result = self.cmd( + "iot central device-template create --app-id {} --device-template-id {} -k '{}'".format( + APP_ID, template_id, device_template_path + ), + checks=[self.check("displayName", template_name)], ) - command = self._appendOptionalArgsToCommand(command, TOKEN, DNS_SUFFIX) - - result = self.cmd(command, checks=[self.check("displayName", template_name), ],) json_result = result.get_output_in_json() assert json_result["@id"] == template_id diff --git a/azext_iot/tests/central/test_iot_central_unit.py b/azext_iot/tests/central/test_iot_central_unit.py index 717a343d1..f93438e40 100644 --- a/azext_iot/tests/central/test_iot_central_unit.py +++ b/azext_iot/tests/central/test_iot_central_unit.py @@ -19,10 +19,6 @@ CentralDeviceProviderV1, CentralDeviceTemplateProviderV1, ) -from azext_iot.central.providers.preview import ( - CentralDeviceGroupProviderPreview, - CentralRoleProviderPreview -) from azext_iot.central.models.devicetwin import DeviceTwin from azext_iot.central import models as central_models from azext_iot.monitor.property import PropertyMonitor diff --git a/azext_iot/tests/conftest.py b/azext_iot/tests/conftest.py index 6da919da7..aded184ab 100644 --- a/azext_iot/tests/conftest.py +++ b/azext_iot/tests/conftest.py @@ -171,6 +171,72 @@ def fixture_monitor_events_entrypoint(mocker): return mocker.patch(path_iot_hub_monitor_events_entrypoint) +@pytest.fixture() +def fixture_iot_device_show_sas(mocker): + device = mocker.patch(path_iot_device_show) + device.return_value = { + "authentication": { + "symmetricKey": { + "primaryKey": "test_pk", + "secondaryKey": "test_sk" + }, + "type": DeviceAuthApiType.sas.value, + "x509Thumbprint": { + "primaryThumbprint": None, + "secondaryThumbprint": None + } + }, + "capabilities": { + "iotEdge": False + }, + "cloudToDeviceMessageCount": 0, + "connectionState": "Disconnected", + "connectionStateUpdatedTime": "2021-05-27T00:36:11.2861732Z", + "deviceId": "Test_Device_1", + "etag": "ODgxNTgwOA==", + "generationId": "637534345627501371", + "hub": "test-iot-hub.azure-devices.net", + "lastActivityTime": "2021-05-27T00:18:16.3154299Z", + "status": "enabled", + "statusReason": None, + "statusUpdatedTime": "0001-01-01T00:00:00Z" + } + return device + + +@pytest.fixture() +def fixture_self_signed_device_show_self_signed(mocker): + device = mocker.patch(path_iot_device_show) + device.return_value = { + "authentication": { + "symmetricKey": { + "primaryKey": "test_pk", + "secondaryKey": "test_sk" + }, + "type": DeviceAuthApiType.selfSigned.value, + "x509Thumbprint": { + "primaryThumbprint": None, + "secondaryThumbprint": None + } + }, + "capabilities": { + "iotEdge": False + }, + "cloudToDeviceMessageCount": 0, + "connectionState": "Disconnected", + "connectionStateUpdatedTime": "2021-05-27T00:36:11.2861732Z", + "deviceId": "Test_Device_1", + "etag": "ODgxNTgwOA==", + "generationId": "637534345627501371", + "hub": "test-iot-hub.azure-devices.net", + "lastActivityTime": "2021-05-27T00:18:16.3154299Z", + "status": "enabled", + "statusReason": None, + "statusUpdatedTime": "0001-01-01T00:00:00Z" + } + return device + + # TODO: To be deprecated asap. Leverage mocked_response fixture for this functionality. def build_mock_response( mocker=None, status_code=200, payload=None, headers=None, **kwargs diff --git a/azext_iot/tests/iothub/test_iot_ext_unit.py b/azext_iot/tests/iothub/test_iot_ext_unit.py index d1bc5b9ba..a668dce82 100644 --- a/azext_iot/tests/iothub/test_iot_ext_unit.py +++ b/azext_iot/tests/iothub/test_iot_ext_unit.py @@ -1998,7 +1998,7 @@ def test_generate_sas_token(self): class TestDeviceSimulate: @pytest.fixture(params=[204]) - def serviceclient(self, mocker, fixture_ghcs, fixture_sas, request, fixture_device): + def serviceclient(self, mocker, fixture_ghcs, fixture_sas, request, fixture_device, fixture_iot_device_show_sas): service_client = mocker.patch(path_service_client) service_client.return_value = build_mock_response(mocker, request.param, {}) return service_client diff --git a/azext_iot/tests/iothub/test_iothub_utilities_int.py b/azext_iot/tests/iothub/test_iothub_utilities_int.py index 2158a1b99..992bd0c60 100644 --- a/azext_iot/tests/iothub/test_iothub_utilities_int.py +++ b/azext_iot/tests/iothub/test_iothub_utilities_int.py @@ -62,17 +62,6 @@ def test_iothub_generate_sas_token(self): expect_failure=True, ) - # Offline SAS token generation - self.cmd( - f"iot hub generate-sas-token --connection-string {self.connection_string}", - checks=[self.exists("sas")], - ) - - self.cmd( - f"iot hub generate-sas-token --connection-string {self.connection_string} --du 1000", - checks=[self.exists("sas")], - ) - def test_iothub_connection_string_show(self): conn_str_pattern = r"^HostName={0}.azure-devices.net;SharedAccessKeyName=iothubowner;SharedAccessKey=".format( LIVE_HUB diff --git a/dev_requirements b/dev_requirements index 18aa1e562..407530160 100644 --- a/dev_requirements +++ b/dev_requirements @@ -4,7 +4,8 @@ pytest-cov pytest-env uamqp~=1.2 responses -black;python_version>='3.6' +urllib3[secure]>=1.21.1,<=1.25 +black wheel==0.30.0 pre-commit pylint