diff --git a/azext_edge/edge/_help.py b/azext_edge/edge/_help.py index 6a0dd0ad..a8849ac7 100644 --- a/azext_edge/edge/_help.py +++ b/azext_edge/edge/_help.py @@ -1205,6 +1205,9 @@ def load_iotops_help(): If the indicated storage account container does not exist it will be created with default settings. + + This operation will also register the Microsoft.DeviceRegistry resource provider if it is + not registered. examples: - name: Create a schema registry called 'myregistry' with minimum inputs. text: > diff --git a/azext_edge/edge/providers/orchestration/resources/schema_registries.py b/azext_edge/edge/providers/orchestration/resources/schema_registries.py index ae1bfd3d..6bfe071e 100644 --- a/azext_edge/edge/providers/orchestration/resources/schema_registries.py +++ b/azext_edge/edge/providers/orchestration/resources/schema_registries.py @@ -11,6 +11,7 @@ from knack.log import get_logger from rich.console import Console + from ....util.az_client import ( get_registry_mgmt_client, get_storage_mgmt_client, @@ -66,7 +67,11 @@ def create( custom_role_id: Optional[str] = None, **kwargs, ) -> dict: + from ..rp_namespace import register_providers, ADR_PROVIDER with console.status("Working...") as c: + # Register the schema (ADR) provider + register_providers(self.default_subscription_id, ADR_PROVIDER) + if not location: location = self.get_resource_group(name=resource_group_name)["location"] diff --git a/azext_edge/edge/providers/orchestration/rp_namespace.py b/azext_edge/edge/providers/orchestration/rp_namespace.py index 20818bfb..263f635f 100644 --- a/azext_edge/edge/providers/orchestration/rp_namespace.py +++ b/azext_edge/edge/providers/orchestration/rp_namespace.py @@ -4,6 +4,7 @@ # Licensed under the MIT License. See License file in the project root for license information. # ---------------------------------------------------------------------------------------------- +from typing import Optional from knack.log import get_logger from ...util.az_client import get_resource_client @@ -11,20 +12,22 @@ logger = get_logger(__name__) +ADR_PROVIDER = "Microsoft.DeviceRegistry" RP_NAMESPACE_SET = frozenset( [ "Microsoft.IoTOperations", - "Microsoft.DeviceRegistry", "Microsoft.SecretSyncController", + ADR_PROVIDER ] ) -def register_providers(subscription_id: str): +def register_providers(subscription_id: str, resource_provider: Optional[str] = None): resource_client = get_resource_client(subscription_id=subscription_id) providers_list = resource_client.providers.list() + required_providers = [resource_provider] if resource_provider else RP_NAMESPACE_SET for provider in providers_list: - if "namespace" in provider and provider["namespace"] in RP_NAMESPACE_SET: + if "namespace" in provider and provider["namespace"] in required_providers: if provider["registrationState"] == "Registered": logger.debug("RP %s is already registered.", provider["namespace"]) continue diff --git a/azext_edge/tests/edge/conftest.py b/azext_edge/tests/edge/conftest.py index 5cd77b80..c52f969c 100644 --- a/azext_edge/tests/edge/conftest.py +++ b/azext_edge/tests/edge/conftest.py @@ -34,6 +34,12 @@ def mocked_urlopen(mocker): yield patched +@pytest.fixture +def mocked_register_providers(mocker): + patched = mocker.patch("azext_edge.edge.providers.orchestration.rp_namespace.register_providers", autospec=True) + yield patched + + @pytest.fixture def mocked_resource_management_client(request, mocker): request_results = getattr(request, "param", {}) diff --git a/azext_edge/tests/edge/init/conftest.py b/azext_edge/tests/edge/init/conftest.py index bb80116e..8bdcf758 100644 --- a/azext_edge/tests/edge/init/conftest.py +++ b/azext_edge/tests/edge/init/conftest.py @@ -48,12 +48,6 @@ def mocked_verify_cli_client_connections(mocker): yield patched -@pytest.fixture -def mocked_register_providers(mocker): - patched = mocker.patch("azext_edge.edge.providers.orchestration.rp_namespace.register_providers", autospec=True) - yield patched - - @pytest.fixture def mocked_edge_api_keyvault_api_v1(mocker): patched = mocker.patch("azext_edge.edge.providers.edge_api.keyvault.KEYVAULT_API_V1", autospec=False) diff --git a/azext_edge/tests/edge/init/test_base_unit.py b/azext_edge/tests/edge/init/test_base_unit.py index b0c71cda..64306d44 100644 --- a/azext_edge/tests/edge/init/test_base_unit.py +++ b/azext_edge/tests/edge/init/test_base_unit.py @@ -324,7 +324,8 @@ def test_verify_custom_location_namespace( "NotRegistered", ], ) -def test_register_providers(mocker, registration_state): +@pytest.mark.parametrize("input_rp", [None, "Microsoft.DeviceRegistry"]) +def test_register_providers(mocker, registration_state, input_rp): mocked_get_resource_client: Mock = mocker.patch( "azext_edge.edge.providers.orchestration.rp_namespace.get_resource_client" ) @@ -338,13 +339,15 @@ def test_register_providers(mocker, registration_state): "Microsoft.DeviceRegistry", "Microsoft.SecretSyncController", ] + if input_rp: + iot_ops_rps = [input_rp] mocked_get_resource_client().providers.list.return_value = [ {"namespace": namespace, "registrationState": registration_state} for namespace in iot_ops_rps ] for rp in iot_ops_rps: assert rp in RP_NAMESPACE_SET - assert len(iot_ops_rps) == len(RP_NAMESPACE_SET) + assert len(iot_ops_rps) == (1 if input_rp else len(RP_NAMESPACE_SET)) register_providers(ZEROED_SUB) mocked_get_resource_client().providers.list.assert_called_once() diff --git a/azext_edge/tests/edge/orchestration/resources/test_schema_registry_unit.py b/azext_edge/tests/edge/orchestration/resources/test_schema_registry_unit.py index 3120ead9..fd1a1aa3 100644 --- a/azext_edge/tests/edge/orchestration/resources/test_schema_registry_unit.py +++ b/azext_edge/tests/edge/orchestration/resources/test_schema_registry_unit.py @@ -22,6 +22,7 @@ ROLE_DEF_FORMAT_STR, STORAGE_BLOB_DATA_CONTRIBUTOR_ROLE_ID, ) +from azext_edge.edge.providers.orchestration.rp_namespace import ADR_PROVIDER from ....generators import generate_random_string from .conftest import get_base_endpoint, get_mock_resource, get_resource_id, find_request_by_url, ZEROED_SUBSCRIPTION @@ -218,8 +219,9 @@ def test_schema_registry_delete(mocked_cmd, mocked_responses: responses): ) def test_schema_registry_create( mocked_cmd, - mocked_responses: responses, mocker, + mocked_register_providers, + mocked_responses: responses, location: Optional[str], display_name: Optional[str], description: Optional[str], @@ -343,6 +345,7 @@ def test_schema_registry_create( == f"{mock_storage_record['properties']['primaryEndpoints']['blob']}{storage_container_name}" ) + mocked_register_providers.assert_called_with(ZEROED_SUBSCRIPTION, ADR_PROVIDER) mock_permission_manager.assert_called_with(ZEROED_SUBSCRIPTION) target_role_id = custom_role_id or ROLE_DEF_FORMAT_STR.format( subscription_id=ZEROED_SUBSCRIPTION, role_id=STORAGE_BLOB_DATA_CONTRIBUTOR_ROLE_ID