Skip to content

Commit

Permalink
Set of testing tweaks & min CLI increment (#643)
Browse files Browse the repository at this point in the history
- assign_role_assignment() will  take a set of service principal KPIs into account. Useful when user principal names or service principal names are used for role assignment.
  - This operation will no longer return anything and works on a best-attempt basis.
  - Fixes an unhandled exception case if a role assignment already exists and `output` is still None.
- Raise minCLIVersion to 2.37.0 to have consistent MS graph experience.
- Refactors how an assertion works for ADU deployment creation, investigating change in existing model.
- Revert az dt rbac test change.
  • Loading branch information
digimaun authored Mar 13, 2023
1 parent 0c302f0 commit 76c8e81
Show file tree
Hide file tree
Showing 9 changed files with 80 additions and 41 deletions.
1 change: 1 addition & 0 deletions .azure-devops/templates/set-testenv-sentinel.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ steps:
f.writelines(envvars_sentinel)
f.write("markers =\n")
f.write(" adu_infrastructure:\n")
f.write(" hub_infrastructure:\n")
f.close()
env:
Expand Down
16 changes: 14 additions & 2 deletions HISTORY.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,25 @@
Release History
===============


unreleased
+++++++++++++++


0.21.0
+++++++++++++++

**General updates**

* The Azure IoT CLI extension min core CLI version incremented to `2.37.0`.

**Digital Twins updates**

* Addition of new parameter `--max-models-per-batch` for `az dt model create` to let user adjust batch size when directory exceeds
250 models. The parameter will be removed once the DTDLParserError is fixed when models created exceed single page API limit in ubuntu.
* Fix to ensure policy key retreival during Digital Twin endpoint creation works. Affected commands are: - `az dt endpoint create *`.

* Addition of new temporary experimental parameter `--max-models-per-batch` for `az dt model create` to let user adjust batch size when directory exceeds
250 models.


0.20.0
+++++++++++++++
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.32.0"
"azext.minCliCoreVersion": "2.37.0"
}
2 changes: 1 addition & 1 deletion azext_iot/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

import os

VERSION = "0.20.0"
VERSION = "0.21.0"
EXTENSION_NAME = "azure-iot"
EXTENSION_ROOT = os.path.dirname(os.path.abspath(__file__))
EXTENSION_CONFIG_ROOT_KEY = "iotext"
Expand Down
2 changes: 1 addition & 1 deletion azext_iot/tests/deviceupdate/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@

ACCOUNT_RG = settings.env.azext_iot_testrg
VALID_IDENTITY_MAP = {"system": 1, "user": 1}
DEFAULT_ADU_RBAC_SLEEP_SEC = 105
DEFAULT_ADU_RBAC_SLEEP_SEC = 90

# Manifest v4 will work with deviceUpdateModel;[1|2] but v5 only with deviceUpdateModel;2
ADU_CLIENT_DTMI = "dtmi:azure:iot:deviceUpdateModel;2"
Expand Down
9 changes: 8 additions & 1 deletion azext_iot/tests/deviceupdate/test_adu_update_int.py
Original file line number Diff line number Diff line change
Expand Up @@ -330,7 +330,14 @@ def test_instance_update_lifecycle(provisioned_instances_module: Dict[str, dict]
f"--group-id {device_group_id} --start-time {start_date_time_iso} --update-name {simple_manifest_id['name']} "
f"--update-provider {simple_manifest_id['provider']} --update-version {simple_manifest_id['version']}").as_json()
assert basic_create_deployment["deploymentId"] == basic_deployment_id
assert basic_create_deployment["update"]["updateId"] == simple_manifest_id

assert basic_create_deployment["update"]["updateId"]
for update_kpi in ["name", "provider", "version"]:
assert (
update_kpi in basic_create_deployment["update"]["updateId"]
and basic_create_deployment["update"]["updateId"][update_kpi] == simple_manifest_id[update_kpi]
)

assert device_class_id in basic_create_deployment["deviceClassSubgroups"]
assert basic_create_deployment["groupId"] == device_group_id
assert basic_create_deployment["isRetried"] is None
Expand Down
64 changes: 38 additions & 26 deletions azext_iot/tests/digitaltwins/test_dt_resource_lifecycle_int.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
from azext_iot.common.utility import unpack_msrest_error
from azext_iot.digitaltwins.common import ADTEndpointAuthType, ADTEndpointType, IdentityType
from azext_iot.tests.digitaltwins.dt_helpers import assert_system_data_attributes
from azext_iot.tests.helpers import assign_role_assignment, delete_role_assignment, get_role_assignments
from azext_iot.tests.helpers import assign_role_assignment, get_role_assignments
from . import DTLiveScenarioTest
from . import (
EP_RG,
Expand Down Expand Up @@ -252,62 +252,74 @@ def test_dt_rbac(self):

assert (
len(
get_role_assignments(scope=rbac_instance["id"])
self.cmd(
"dt role-assignment list -n {}".format(rbac_instance_name)
).get_output_in_json()
)
== 0
)

assign_output = assign_role_assignment(
role=self.role_map["owner"],
scope=rbac_instance["id"],
assignee=rbac_assignee_owner)

assert assign_output and assign_output.success()
assign_output = self.cmd(
"dt role-assignment create -n {} --assignee {} --role '{}'".format(
rbac_instance_name, rbac_assignee_owner, self.role_map["owner"]
)
).get_output_in_json()

assert_common_rbac_attributes(
assign_output.as_json(),
assign_output,
rbac_instance_name,
"owner",
rbac_assignee_owner,
)

assign_output = assign_role_assignment(
role=self.role_map["reader"],
scope=rbac_instance["id"],
assignee=rbac_assignee_reader)

assert assign_output and assign_output.success()
assign_output = self.cmd(
"dt role-assignment create -n {} --assignee {} --role '{}' -g {}".format(
rbac_instance_name,
rbac_assignee_reader,
self.role_map["reader"],
self.rg,
)
).get_output_in_json()

assert_common_rbac_attributes(
assign_output.as_json(),
assign_output,
rbac_instance_name,
"reader",
rbac_assignee_reader,
)

list_assigned_output = get_role_assignments(scope=rbac_instance["id"])
list_assigned_output = self.cmd(
"dt role-assignment list -n {}".format(rbac_instance_name)
).get_output_in_json()

assert len(list_assigned_output) == 2

# Remove specific role assignment (reader) for assignee
# Role-assignment delete does not currently return output
delete_role_assignment(
scope=rbac_instance["id"],
assignee=rbac_assignee_reader,
role=self.role_map["reader"]
self.cmd(
"dt role-assignment delete -n {} --assignee {} --role '{}'".format(
rbac_instance_name,
rbac_assignee_owner,
self.role_map["reader"],
)
)

list_assigned_output = get_role_assignments(scope=rbac_instance["id"])
list_assigned_output = self.cmd(
"dt role-assignment list -n {} -g {}".format(rbac_instance_name, self.rg)
).get_output_in_json()

assert len(list_assigned_output) == 1

# Remove all role assignments for assignee
delete_role_assignment(
scope=rbac_instance["id"],
assignee=rbac_assignee_owner
self.cmd(
"dt role-assignment delete -n {} --assignee {}".format(
rbac_instance_name, rbac_assignee_reader
)
)

list_assigned_output = get_role_assignments(scope=rbac_instance["id"])
list_assigned_output = self.cmd(
"dt role-assignment list -n {} -g {}".format(rbac_instance_name, self.rg)
).get_output_in_json()

assert len(list_assigned_output) == 0

Expand Down
24 changes: 15 additions & 9 deletions azext_iot/tests/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
from azext_iot.common.utility import ensure_azure_namespace_path
from azext_iot.common.utility import read_file_content
from azext_iot.tests.settings import DynamoSettings
from typing import TypeVar
from typing import TypeVar, List

ensure_azure_namespace_path()

Expand Down Expand Up @@ -166,7 +166,7 @@ def get_role_assignments(
scope: str,
assignee: str = None,
role: str = None,
) -> json:
) -> List[dict]:
"""
Get rbac permissions of resource.
"""
Expand All @@ -189,28 +189,34 @@ def assign_role_assignment(
scope: str,
assignee: str,
max_tries=10,
wait=10
wait=10,
):
"""
Assign rbac permissions to resource.
"""
output = None
tries = 0
principal_kpis = ["name", "principalId", "principalName"]
while tries < max_tries:
flat_assignment_kpis = []
role_assignments = get_role_assignments(scope=scope, role=role)
role_assignment_principal_ids = [assignment["principalId"] for assignment in role_assignments]
if assignee in role_assignment_principal_ids:
logger.info(f"Role assignments for the role of '{role}' against scope '{scope}': {role_assignments}")
for role_assignment in role_assignments:
for principal_kpi in principal_kpis:
if principal_kpi in role_assignment and role_assignment[principal_kpi]:
flat_assignment_kpis.append(role_assignment[principal_kpi])
if assignee in flat_assignment_kpis:
break
# else assign role to scope and check again
output = cli.invoke(
f'role assignment create --assignee "{assignee}" --role "{role}" --scope "{scope}"'
)
if not output.success():
logger.warning(f"Failed to assign '{assignee}' the role of '{role}' against scope '{scope}'.")
break

sleep(wait)
tries += 1
if not output.success():
logger.warning(f"Failed to assign '{assignee}' the role of '{role}' against scope '{scope}'.")

return output


def delete_role_assignment(
Expand Down
1 change: 1 addition & 0 deletions pytest.ini.example
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,4 @@ env =

markers =
adu_infrastructure: desired customizations for ADU integration tests.
hub_infrastructure: desired customizations for IoT Hub integration tests.

0 comments on commit 76c8e81

Please sign in to comment.