Skip to content

Commit

Permalink
Merge branch 'state_endpoint_fix' of https://github.com/vilit1/azure-…
Browse files Browse the repository at this point in the history
…iot-cli-extension into state_endpoint_fix
  • Loading branch information
vilit1 committed Jun 6, 2024
2 parents cbfe2f9 + 77d6453 commit 43661a8
Show file tree
Hide file tree
Showing 16 changed files with 135 additions and 64 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/tox.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ jobs:
python-version: ${{ matrix.py }}
- uses: actions/checkout@v4
- name: Install tox-gh
run: python -m pip install tox-gh
run: python -m pip install tox-gh tox==4.12.1
- name: Setup test suite
run: tox r -vv --notest
- name: Run test suite
Expand All @@ -53,7 +53,7 @@ jobs:
- uses: actions/checkout@v4
- name: Run code coverage
run: |
python -m pip install tox
python -m pip install tox==4.12.1
tox -e coverage
coverage=$(jq .totals.percent_covered coverage.json | cut -c1-4)
echo "Code coverage: $coverage%" >> $GITHUB_STEP_SUMMARY
Expand Down
3 changes: 3 additions & 0 deletions HISTORY.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ Release History
0.24.0
+++++++++++++++

**General updates**

* Updated the minimum core CLI version to 2.46.0

**IoT Product**

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ Please refer to the official `az iot` reference on [Microsoft Docs](https://docs
## Installation
1. Install the [Azure CLI](https://docs.microsoft.com/en-us/cli/azure/install-azure-cli)
- You must have at least `v2.32.0` for the latest versions of `azure-iot`, which you can verify with `az --version`
- You must have at least `v2.46.0` for the latest versions of `azure-iot`, which you can verify with `az --version`
1. Add, Update or Remove the IoT extension with the following commands:
- Add: `az extension add --name azure-iot`
- Update: `az extension update --name azure-iot`
Expand Down
4 changes: 2 additions & 2 deletions azext_iot/_help.py
Original file line number Diff line number Diff line change
Expand Up @@ -1114,7 +1114,7 @@
text: >
az iot dps enrollment create -g {resource_group_name} --dps-name {dps_name}
--enrollment-id {enrollment_id} --attestation-type tpm --allocation-policy hashed
--endorsement-key 14963E8F3BA5B3984110B3C1CA8E8B89 --iot-hubs "{iot_hub_host_name1} {iot_hub_host_name2}"
--endorsement-key 14963E8F3BA5B3984110B3C1CA8E8B89 --iot-hubs {iot_hub_host_name1} {iot_hub_host_name2}
- name: Create an enrollment 'MyEnrollment' with custom allocation policy,
text: >
az iot dps enrollment create -g {resource_group_name} --dps-name {dps_name}
Expand Down Expand Up @@ -1161,7 +1161,7 @@
text: >
az iot dps enrollment update -g {resource_group_name} --dps-name {dps_name}
--enrollment-id {enrollment_id} --allocation-policy geolatency
--etag AAAAAAAAAAA= --iot-hubs "{iot_hub_host_name1} {iot_hub_host_name2} {iot_hub_host_name3}"
--etag AAAAAAAAAAA= --iot-hubs {iot_hub_host_name1} {iot_hub_host_name2} {iot_hub_host_name3}
- name: Update enrollment '{enrollment_id}' in the Azure IoT Device Provisioning Service '{dps_name}'
in the resource group '{resource_group_name}' with
initial twin properties '{"location":{"region":"USA"}}', initial twin tags '{"version":"2"}',
Expand Down
2 changes: 2 additions & 0 deletions azext_iot/_params.py
Original file line number Diff line number Diff line change
Expand Up @@ -1006,6 +1006,8 @@ def load_arguments(self, _):
options_list=["--iot-hubs", "--ih"],
help="Host name of target IoT Hub associated with the allocation policy. Use space-separated "
"list for multiple IoT Hubs.",
nargs="+",
action="extend",
arg_group="Allocation Policy"
)
context.argument(
Expand Down
2 changes: 1 addition & 1 deletion azext_iot/azext_metadata.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
"azext.minCliCoreVersion": "2.37.0"
"azext.minCliCoreVersion": "2.46.0"
}
8 changes: 2 additions & 6 deletions azext_iot/deviceupdate/commands_update.py
Original file line number Diff line number Diff line change
Expand Up @@ -236,8 +236,8 @@ def manifest_init_v5(
):
from datetime import datetime
from pathlib import PurePath
from azure.cli.core.azclierror import ArgumentUsageError, InvalidArgumentValueError
from azext_iot.deviceupdate.common import FP_HANDLERS, FP_HANDLERS_REQUIRE_CRITERIA
from azure.cli.core.azclierror import ArgumentUsageError
from azext_iot.deviceupdate.common import FP_HANDLERS_REQUIRE_CRITERIA
from azext_iot.deviceupdate.providers.utility import parse_manifest_json

def _sanitize_safe_params(safe_params: list, keep: list) -> list:
Expand Down Expand Up @@ -323,10 +323,6 @@ def _associate_related(sanitized_params: list, key: str) -> dict:
"handler": assembled_step["handler"],
}

if step["handler"].lower().startswith("microsoft") and step["handler"] not in FP_HANDLERS:
if not no_validation:
raise InvalidArgumentValueError(f"Valid Microsoft handlers: {', '.join(FP_HANDLERS)}")

step["files"] = (
list(set([f.strip() for f in assembled_step["files"].split(",")])) if "files" in assembled_step else []
)
Expand Down
70 changes: 48 additions & 22 deletions azext_iot/operations/dps.py
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ def iot_dps_device_enrollment_create(
)
reprovision = _get_reprovision_policy(reprovision_policy)
initial_twin = _get_initial_twin(initial_twin_tags, initial_twin_properties)
iot_hub_list = iot_hubs.split() if iot_hubs else iot_hubs
iot_hub_list = iot_hubs.split() if isinstance(iot_hubs, str) else iot_hubs
_validate_allocation_policy_for_enrollment(
allocation_policy, iot_hub_host_name, iot_hub_list, webhook_url, api_version
)
Expand Down Expand Up @@ -303,18 +303,27 @@ def iot_dps_device_enrollment_update(
enrollment_record.initial_twin = _get_updated_inital_twin(
enrollment_record, initial_twin_tags, initial_twin_properties
)
iot_hub_list = iot_hubs.split() if iot_hubs else iot_hubs
iot_hub_list = iot_hubs.split() if isinstance(iot_hubs, str) else iot_hubs
_validate_allocation_policy_for_enrollment(
allocation_policy, iot_hub_host_name, iot_hub_list, webhook_url, api_version
allocation_policy,
iot_hub_host_name,
iot_hub_list,
webhook_url,
api_version,
current_enrollment=enrollment_record
)
if allocation_policy:
enrollment_record.allocation_policy = allocation_policy
if iot_hub_list:
enrollment_record.iot_hubs = iot_hub_list
enrollment_record.iot_hub_host_name = None
if allocation_policy == AllocationType.custom.value:
enrollment_record.custom_allocation_definition = CustomAllocationDefinition(
webhook_url=webhook_url, api_version=api_version
)
if allocation_policy:
enrollment_record.allocation_policy = allocation_policy
if enrollment_record.allocation_policy == AllocationType.custom.value and any([
webhook_url, api_version
]):
enrollment_record.custom_allocation_definition = CustomAllocationDefinition(
webhook_url=webhook_url or enrollment_record.custom_allocation_definition.webhook_url,
api_version=api_version or enrollment_record.custom_allocation_definition.api_version
)
if edge_enabled is not None:
enrollment_record.capabilities = DeviceCapabilities(iot_edge=edge_enabled)
if device_information:
Expand Down Expand Up @@ -481,7 +490,7 @@ def iot_dps_device_enrollment_group_create(
)
reprovision = _get_reprovision_policy(reprovision_policy)
initial_twin = _get_initial_twin(initial_twin_tags, initial_twin_properties)
iot_hub_list = iot_hubs.split() if iot_hubs else iot_hubs
iot_hub_list = iot_hubs.split() if isinstance(iot_hubs, str) else iot_hubs
_validate_allocation_policy_for_enrollment(
allocation_policy, iot_hub_host_name, iot_hub_list, webhook_url, api_version
)
Expand Down Expand Up @@ -621,18 +630,27 @@ def iot_dps_device_enrollment_group_update(
enrollment_record.initial_twin = _get_updated_inital_twin(
enrollment_record, initial_twin_tags, initial_twin_properties
)
iot_hub_list = iot_hubs.split() if iot_hubs else iot_hubs
iot_hub_list = iot_hubs.split() if isinstance(iot_hubs, str) else iot_hubs
_validate_allocation_policy_for_enrollment(
allocation_policy, iot_hub_host_name, iot_hub_list, webhook_url, api_version
allocation_policy,
iot_hub_host_name,
iot_hub_list,
webhook_url,
api_version,
current_enrollment=enrollment_record
)
if allocation_policy:
enrollment_record.allocation_policy = allocation_policy
if iot_hub_list:
enrollment_record.iot_hubs = iot_hub_list
enrollment_record.iot_hub_host_name = None
if allocation_policy == AllocationType.custom.value:
enrollment_record.custom_allocation_definition = CustomAllocationDefinition(
webhook_url=webhook_url, api_version=api_version
)
if allocation_policy:
enrollment_record.allocation_policy = allocation_policy
if enrollment_record.allocation_policy == AllocationType.custom.value and any([
webhook_url, api_version
]):
enrollment_record.custom_allocation_definition = CustomAllocationDefinition(
webhook_url=webhook_url or enrollment_record.custom_allocation_definition.webhook_url,
api_version=api_version or enrollment_record.custom_allocation_definition.api_version
)
if edge_enabled is not None:
enrollment_record.capabilities = DeviceCapabilities(iot_edge=edge_enabled)
return sdk.enrollment_group.create_or_update(
Expand Down Expand Up @@ -1137,8 +1155,16 @@ def _validate_arguments_for_attestation_mechanism(


def _validate_allocation_policy_for_enrollment(
allocation_policy, iot_hub_host_name, iot_hub_list, webhook_url, api_version
allocation_policy, iot_hub_host_name, iot_hub_list, webhook_url, api_version, current_enrollment=None
):
# get the enrollment values if not provided but present
if current_enrollment:
iot_hub_list = iot_hub_list or current_enrollment.iot_hubs
allocation_policy = allocation_policy or current_enrollment.allocation_policy
if current_enrollment.allocation_policy == AllocationType.custom.value:
webhook_url = webhook_url or current_enrollment.custom_allocation_definition.webhook_url
api_version = api_version or current_enrollment.custom_allocation_definition.api_version

if allocation_policy:
if iot_hub_host_name is not None:
raise MutuallyExclusiveArgumentError(
Expand All @@ -1149,6 +1175,7 @@ def _validate_allocation_policy_for_enrollment(
allocation_policy == allocation.value for allocation in AllocationType
):
raise RequiredArgumentMissingError("Please provide valid allocation policy.")

if allocation_policy == AllocationType.static.value:
if iot_hub_list is None:
raise RequiredArgumentMissingError("Please provide a hub to be assigned with device.")
Expand All @@ -1160,6 +1187,5 @@ def _validate_allocation_policy_for_enrollment(
"Please provide both the Azure function webhook url and provisioning"
" service api-version when the allocation-policy is defined as Custom."
)
else:
if iot_hub_list:
raise RequiredArgumentMissingError("Please provide allocation policy.")
elif iot_hub_list and not current_enrollment:
raise RequiredArgumentMissingError("Please provide allocation policy.")
56 changes: 40 additions & 16 deletions azext_iot/tests/deviceupdate/test_adu_manifest_int.py
Original file line number Diff line number Diff line change
Expand Up @@ -424,6 +424,46 @@
"manifestVersion": "5.0",
},
),
(
"--update-provider digimaun0 --update-name customhandler --update-version 0.1 "
"--compat manufacturer=Contoso model=Vacuum "
"--compat ring=0 tier=test "
"--step handler=microsoft/customhandler:2 "
f"--file path=\"{get_context_path(__file__, 'manifests', 'libcurl4-doc-apt-manifest.json')}\" "
f"--related-file path=\"{get_context_path(__file__, 'manifests', 'surface15', 'parent.importmanifest.json')}\" ",
{
"updateId": {"provider": "digimaun0", "name": "customhandler", "version": "0.1"},
"compatibility": [
{"manufacturer": "Contoso", "model": "Vacuum"},
{"ring": "0", "tier": "test"},
],
"instructions": {
# no first party handler validation
"steps": [
{
"handler": "microsoft/customhandler:2",
"files": ["libcurl4-doc-apt-manifest.json"],
"type": "inline",
},
]
},
"files": [
{
"filename": "libcurl4-doc-apt-manifest.json",
"sizeInBytes": 163,
"hashes": {"sha256": "iFWTIaxp33tf5BR1w0fMmnnHpjsUjLRQ9eZFjw74LbU="},
"relatedFiles": [
{
"filename": "parent.importmanifest.json",
"sizeInBytes": 1390,
"hashes": {"sha256": "hos1UvCk66WmtL/SPNUmub+k302BM4gtWYtAF7tOCb4="},
}
],
}
],
"manifestVersion": "5.0",
},
),
],
)
def test_adu_manifest_init_v5(options, expected):
Expand Down Expand Up @@ -514,22 +554,6 @@ def test_adu_manifest_init_v5_invalid_path_required(options):
"downloadHandler=abc",
True,
),
(
# If content handler starts with microsoft (case-insensitive) enforce valid value.
"--update-provider digimaun --update-name invalid --update-version 1.0 "
"--compat deviceManufacturer=Contoso deviceModel=Vacuum "
"--step handler=microsoft/fake:1 "
f"--file path=\"{get_context_path(__file__, 'manifests', 'libcurl4-doc-apt-manifest.json')}\" ",
False,
),
(
# Same as prior test case but ensure escape hatch with --no-validation
"--update-provider digimaun --update-name invalid --update-version 1.0 "
"--compat deviceManufacturer=Contoso deviceModel=Vacuum "
"--step handler=microsoft/fake:1 "
f"--file path=\"{get_context_path(__file__, 'manifests', 'libcurl4-doc-apt-manifest.json')}\" ",
False,
),
],
)
def test_adu_manifest_init_v5_validate_errors(options, no_validation):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -298,7 +298,7 @@ def test_dps_enrollment_symmetrickey_lifecycle(provisioned_iot_dps_module):
assert update_enrollment["customAllocationDefinition"]["webhookUrl"] == WEBHOOK_URL
assert update_enrollment["customAllocationDefinition"]["apiVersion"] == API_VERSION
assert update_enrollment["deviceId"] == device_id
assert update_enrollment["iotHubs"] is None
assert update_enrollment["iotHubs"] == [hub_hostname]
assert update_enrollment["initialTwin"]["tags"]
assert update_enrollment["initialTwin"]["properties"]["desired"]
assert update_enrollment["optionalDeviceInformation"] == generic_dict
Expand Down
18 changes: 15 additions & 3 deletions azext_iot/tests/dps/enrollment/test_iot_dps_enrollment_unit.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,12 @@ def serviceclient_generic_error(self, mocked_response, fixture_gdcs, fixture_dps
reprovision_policy='never',
allocation_policy='hashed',
iot_hubs='hub1 hub2')),
(generate_enrollment_create_req(attestation_type='symmetricKey',
primary_key='primarykey',
secondary_key='secondarykey',
reprovision_policy='never',
allocation_policy='hashed',
iot_hubs=['hub1', 'hub2'])),
(generate_enrollment_create_req(attestation_type='symmetricKey',
primary_key='primarykey',
secondary_key='secondarykey',
Expand Down Expand Up @@ -227,7 +233,9 @@ def test_enrollment_create(self, serviceclient, fixture_cmd, req):
assert body['customAllocationDefinition']['webhookUrl'] == req['webhook_url']
assert body['customAllocationDefinition']['apiVersion'] == req['api_version']
if req['iot_hubs']:
assert body['iotHubs'] == req['iot_hubs'].split()
assert body['iotHubs'] == (
req['iot_hubs'].split() if isinstance(req['iot_hubs'], str) else req['iot_hubs']
)
if req['edge_enabled']:
assert body['capabilities']['iotEdge']

Expand Down Expand Up @@ -376,14 +384,16 @@ def serviceclient(self, mocked_response, fixture_gdcs, fixture_dps_sas, patch_ce
(generate_enrollment_update_req(reprovision_policy='never')),
(generate_enrollment_update_req(allocation_policy='static', iot_hubs='hub1')),
(generate_enrollment_update_req(allocation_policy='hashed', iot_hubs='hub1 hub2')),
(generate_enrollment_update_req(allocation_policy='hashed', iot_hubs=['hub1', 'hub2'])),
(generate_enrollment_update_req(allocation_policy='geoLatency')),
(generate_enrollment_update_req(allocation_policy='custom',
webhook_url="https://www.test.test",
api_version="2019-03-31")),
(generate_enrollment_update_req(edge_enabled=True)),
(generate_enrollment_update_req(edge_enabled=False))
])
def test_enrollment_update(self, serviceclient, fixture_cmd, req):
def test_enrollment_update(self, mocker, serviceclient, fixture_cmd, req):
mocker.patch("azext_iot.operations.dps._validate_allocation_policy_for_enrollment")
subject.iot_dps_device_enrollment_update(
cmd=fixture_cmd,
enrollment_id=req['enrollment_id'],
Expand Down Expand Up @@ -457,7 +467,9 @@ def test_enrollment_update(self, serviceclient, fixture_cmd, req):
assert body['customAllocationDefinition']['webhookUrl'] == req['webhook_url']
assert body['customAllocationDefinition']['apiVersion'] == req['api_version']
if req['iot_hubs']:
assert body['iotHubs'] == req['iot_hubs'].split()
assert body['iotHubs'] == (
req['iot_hubs'].split() if isinstance(req['iot_hubs'], str) else req['iot_hubs']
)
if req['edge_enabled'] is not None:
assert body['capabilities']['iotEdge'] == req['edge_enabled']

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,7 @@ def test_dps_enrollment_group_symmetrickey_lifecycle(provisioned_iot_dps_module)
assert enrollment_update["customAllocationDefinition"]["webhookUrl"] == WEBHOOK_URL
assert enrollment_update["customAllocationDefinition"]["apiVersion"] == API_VERSION
assert enrollment_update["enrollmentGroupId"] == enrollment_id
assert enrollment_update["iotHubs"] is None
assert enrollment_update["iotHubs"] == [hub_hostname]
assert enrollment_update["initialTwin"]["tags"]
assert enrollment_update["initialTwin"]["properties"]["desired"]
assert enrollment_update["provisioningStatus"] == EntityStatusType.disabled.value
Expand Down
Loading

0 comments on commit 43661a8

Please sign in to comment.