diff --git a/src/log-analytics/HISTORY.rst b/src/log-analytics/HISTORY.rst index fd420d07543..62afff4cb01 100644 --- a/src/log-analytics/HISTORY.rst +++ b/src/log-analytics/HISTORY.rst @@ -1,3 +1,7 @@ +1.0.0b2 +++++++++++++++++++ +* Add AzureChinaCloud and AzureUSGovernment host templates for log-analytics data-plane client. + 1.0.0b1 ++++++++++++++++++ * Migrate to AAZ codegen. diff --git a/src/log-analytics/azext_loganalytics/aaz/latest/_clients.py b/src/log-analytics/azext_loganalytics/aaz/latest/_clients.py index e95651254d6..0a42506beb4 100644 --- a/src/log-analytics/azext_loganalytics/aaz/latest/_clients.py +++ b/src/log-analytics/azext_loganalytics/aaz/latest/_clients.py @@ -15,8 +15,10 @@ class AAZMicrosoftOperationalinsightsDataPlaneClient(AAZBaseClient): _CLOUD_HOST_TEMPLATES = { CloudNameEnum.AzureCloud: "https://api.loganalytics.io", + CloudNameEnum.AzureChinaCloud: "https://api.loganalytics.azure.cn", + CloudNameEnum.AzureUSGovernment: "https://api.loganalytics.us", } - _CLOUD_HOST_METADATA_INDEX = "logAnalyticslogAnalyticsResourceId" + _CLOUD_HOST_METADATA_INDEX = "logAnalyticsResourceId" _AAD_CREDENTIAL_SCOPES = [ "https://api.loganalytics.io/.default", @@ -27,7 +29,7 @@ def _build_base_url(cls, ctx, **kwargs): endpoint = cls.get_cloud_endpoint(ctx, cls._CLOUD_HOST_METADATA_INDEX) if not endpoint: endpoint = cls._CLOUD_HOST_TEMPLATES.get(ctx.cli_ctx.cloud.name, None) - return endpoint + return endpoint.rstrip('/') if endpoint else endpoint @classmethod def _build_configuration(cls, ctx, credential, **kwargs): diff --git a/src/log-analytics/azext_loganalytics/tests/latest/recordings/test_query.yaml b/src/log-analytics/azext_loganalytics/tests/latest/recordings/test_query.yaml index 654630fe5c8..303d285f3c8 100644 --- a/src/log-analytics/azext_loganalytics/tests/latest/recordings/test_query.yaml +++ b/src/log-analytics/azext_loganalytics/tests/latest/recordings/test_query.yaml @@ -1,119 +1,4 @@ interactions: -- request: - body: '{"location": "eastus", "properties": {"publicNetworkAccessForIngestion": - "Enabled", "publicNetworkAccessForQuery": "Enabled", "retentionInDays": 30, - "sku": {"capacityReservationLevel": 100, "name": "CapacityReservation"}, "workspaceCapping": - {"dailyQuotaGb": 1.0}}}' - headers: - Accept: - - application/json - Accept-Encoding: - - gzip, deflate - CommandName: - - monitor log-analytics workspace create - Connection: - - keep-alive - Content-Length: - - '265' - Content-Type: - - application/json - ParameterSetName: - - -g -n --location --quota --level --sku - User-Agent: - - AZURECLI/2.64.0 azsdk-python-core/1.28.0 Python/3.12.6 (Windows-11-10.0.22631-SP0) - method: PUT - uri: https://management.azure.com/subscriptions/00000000-0000-0000-0000-000000000000/resourcegroups/cli_test_log_analytics000001/providers/Microsoft.OperationalInsights/workspaces/clitest000002?api-version=2023-09-01 - response: - body: - string: '{"properties":{"customerId":"062c153f-f7b2-4a8b-b57d-05b9f5cadfdc","provisioningState":"Creating","sku":{"name":"CapacityReservation","capacityReservationLevel":100,"lastSkuUpdate":"2024-09-14T04:50:29.9279239Z"},"retentionInDays":30,"features":{"legacy":0,"searchVersion":1,"enableLogAccessUsingOnlyResourcePermissions":true},"workspaceCapping":{"dailyQuotaGb":1.0,"quotaNextResetTime":"2024-09-14T20:00:00Z","dataIngestionStatus":"RespectQuota"},"publicNetworkAccessForIngestion":"Enabled","publicNetworkAccessForQuery":"Enabled","createdDate":"2024-09-14T04:50:29.9279239Z","modifiedDate":"2024-09-14T04:50:29.9279239Z"},"location":"eastus","id":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/cli_test_log_analytics000001/providers/Microsoft.OperationalInsights/workspaces/clitest000002","name":"clitest000002","type":"Microsoft.OperationalInsights/workspaces","etag":"\"18004b51-0000-0100-0000-66e516150000\""}' - headers: - access-control-allow-origin: - - '*' - api-supported-versions: - - 2015-03-20, 2020-08-01, 2020-10-01, 2021-06-01, 2022-10-01, 2023-09-01 - cache-control: - - no-cache - content-length: - - '935' - content-type: - - application/json; charset=utf-8 - date: - - Sat, 14 Sep 2024 04:50:29 GMT - expires: - - '-1' - location: - - https://management.azure.com/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/cli_test_log_analytics000001/providers/Microsoft.OperationalInsights/workspaces/clitest000002?api-version=2023-09-01 - pragma: - - no-cache - request-context: - - appId=cid-v1:e6336c63-aab2-45f0-996a-e5dbab2a1508 - strict-transport-security: - - max-age=31536000; includeSubDomains - x-cache: - - CONFIG_NOCACHE - x-content-type-options: - - nosniff - x-ms-ratelimit-remaining-subscription-global-writes: - - '5999' - x-ms-ratelimit-remaining-subscription-writes: - - '399' - x-msedge-ref: - - 'Ref A: 969F1114F8B045CE99F05F0392FCBD71 Ref B: MAA201060513039 Ref C: 2024-09-14T04:50:26Z' - status: - code: 201 - message: Created -- request: - body: null - headers: - Accept: - - '*/*' - Accept-Encoding: - - gzip, deflate - CommandName: - - monitor log-analytics workspace create - Connection: - - keep-alive - ParameterSetName: - - -g -n --location --quota --level --sku - User-Agent: - - AZURECLI/2.64.0 azsdk-python-core/1.28.0 Python/3.12.6 (Windows-11-10.0.22631-SP0) - method: GET - uri: https://management.azure.com/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/cli_test_log_analytics000001/providers/Microsoft.OperationalInsights/workspaces/clitest000002?api-version=2023-09-01 - response: - body: - string: '{"properties":{"customerId":"062c153f-f7b2-4a8b-b57d-05b9f5cadfdc","provisioningState":"Succeeded","sku":{"name":"CapacityReservation","capacityReservationLevel":100,"lastSkuUpdate":"2024-09-14T04:50:29.9279239Z"},"retentionInDays":30,"features":{"legacy":0,"searchVersion":1,"enableLogAccessUsingOnlyResourcePermissions":true},"workspaceCapping":{"dailyQuotaGb":1.0,"quotaNextResetTime":"2024-09-14T20:00:00Z","dataIngestionStatus":"RespectQuota"},"publicNetworkAccessForIngestion":"Enabled","publicNetworkAccessForQuery":"Enabled","createdDate":"2024-09-14T04:50:29.9279239Z","modifiedDate":"2024-09-14T04:50:30.9494306Z"},"location":"eastus","id":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/cli_test_log_analytics000001/providers/Microsoft.OperationalInsights/workspaces/clitest000002","name":"clitest000002","type":"Microsoft.OperationalInsights/workspaces","etag":"\"18004c51-0000-0100-0000-66e516160000\""}' - headers: - access-control-allow-origin: - - '*' - api-supported-versions: - - 2015-03-20, 2020-08-01, 2020-10-01, 2021-06-01, 2022-10-01, 2023-09-01 - cache-control: - - no-cache - content-length: - - '936' - content-type: - - application/json; charset=utf-8 - date: - - Sat, 14 Sep 2024 04:50:30 GMT - expires: - - '-1' - pragma: - - no-cache - request-context: - - appId=cid-v1:e6336c63-aab2-45f0-996a-e5dbab2a1508 - strict-transport-security: - - max-age=31536000; includeSubDomains - x-cache: - - CONFIG_NOCACHE - x-content-type-options: - - nosniff - x-ms-ratelimit-remaining-subscription-global-reads: - - '7499' - x-msedge-ref: - - 'Ref A: 7BFCFDBF1A6F44838E333CA298740C12 Ref B: MAA201060513039 Ref C: 2024-09-14T04:50:30Z' - status: - code: 200 - message: OK - request: body: '{"query": "Heartbeat | getschema"}' headers: diff --git a/src/log-analytics/azext_loganalytics/tests/latest/test_loganalytics_commands.py b/src/log-analytics/azext_loganalytics/tests/latest/test_loganalytics_commands.py index 64d164c68ec..8a2b7fbcbae 100644 --- a/src/log-analytics/azext_loganalytics/tests/latest/test_loganalytics_commands.py +++ b/src/log-analytics/azext_loganalytics/tests/latest/test_loganalytics_commands.py @@ -4,25 +4,21 @@ # -------------------------------------------------------------------------------------------- # pylint: disable=line-too-long -from azure.cli.testsdk import (ScenarioTest, ResourceGroupPreparer) +import unittest +from unittest.mock import MagicMock, patch + +from azure.cli.testsdk import ScenarioTest class LogAnalyticsDataClientTests(ScenarioTest): """Test class for Log Analytics data client.""" - @ResourceGroupPreparer(name_prefix='cli_test_log_analytics') - def test_query(self, resource_group): + def test_query(self): """Tests data plane query capabilities for Log Analytics.""" self.kwargs.update({ - 'workspace_name': self.create_random_name('clitest', 20), - 'location': "eastus", + 'workspace_customerId': '062c153f-f7b2-4a8b-b57d-05b9f5cadfdc', }) - workspace_json = self.cmd( - "monitor log-analytics workspace create -g {rg} -n {workspace_name} --location {location} --quota 1 " - "--level 100 --sku CapacityReservation").get_output_in_json() - self.kwargs['workspace_customerId'] = workspace_json['customerId'] - self.cmd( 'az monitor log-analytics query -w {workspace_customerId} ' '--analytics-query "Heartbeat | getschema"', @@ -32,3 +28,72 @@ def test_query(self, resource_group): 'DataType': 'System.String', 'TableName': 'getschema'}) ] ) + + +class LogAnalyticsClientEndpointMappingTests(unittest.TestCase): + """Unit tests for cloud endpoint mapping in the Log Analytics data-plane client.""" + + def _make_ctx(self, cloud_name, log_analytics_resource_id=None, raise_endpoint_not_set=False): + """Build a minimal mock context with the given cloud name and endpoint.""" + from unittest.mock import PropertyMock + from azure.cli.core.cloud import CloudEndpointNotSetException + + endpoints = MagicMock() + if raise_endpoint_not_set: + type(endpoints).log_analytics_resource_id = PropertyMock( + side_effect=CloudEndpointNotSetException("log_analytics_resource_id not set") + ) + else: + endpoints.log_analytics_resource_id = log_analytics_resource_id + + cloud = MagicMock() + cloud.name = cloud_name + cloud.endpoints = endpoints + + cli_ctx = MagicMock() + cli_ctx.cloud = cloud + + ctx = MagicMock() + ctx.cli_ctx = cli_ctx + return ctx + + def _get_base_url(self, ctx): + from azext_loganalytics.aaz.latest._clients import AAZMicrosoftOperationalinsightsDataPlaneClient + with patch.object( + AAZMicrosoftOperationalinsightsDataPlaneClient, + '_retrieve_value_in_arm_cloud_metadata', + return_value=None + ): + return AAZMicrosoftOperationalinsightsDataPlaneClient._build_base_url(ctx) + + def test_azure_public_cloud_endpoint(self): + """AzureCloud should resolve to the public Log Analytics endpoint.""" + ctx = self._make_ctx('AzureCloud', 'https://api.loganalytics.io') + self.assertEqual(self._get_base_url(ctx), 'https://api.loganalytics.io') + + def test_azure_public_cloud_endpoint_trailing_slash(self): + """AzureCloud endpoint with trailing slash should be stripped.""" + ctx = self._make_ctx('AzureCloud', 'https://api.loganalytics.io/') + self.assertEqual(self._get_base_url(ctx), 'https://api.loganalytics.io') + + def test_azure_china_cloud_endpoint(self): + """AzureChinaCloud should resolve to the China sovereign endpoint.""" + ctx = self._make_ctx('AzureChinaCloud', 'https://api.loganalytics.azure.cn') + self.assertEqual(self._get_base_url(ctx), 'https://api.loganalytics.azure.cn') + + def test_azure_us_government_cloud_endpoint(self): + """AzureUSGovernment should resolve to the US Government endpoint.""" + ctx = self._make_ctx('AzureUSGovernment', 'https://api.loganalytics.us') + self.assertEqual(self._get_base_url(ctx), 'https://api.loganalytics.us') + + def test_fallback_to_host_templates_when_endpoint_not_set(self): + """When cloud endpoint is not configured, fall back to _CLOUD_HOST_TEMPLATES.""" + expected_by_cloud = { + 'AzureCloud': 'https://api.loganalytics.io', + 'AzureChinaCloud': 'https://api.loganalytics.azure.cn', + 'AzureUSGovernment': 'https://api.loganalytics.us', + } + for cloud_name, expected_url in expected_by_cloud.items(): + with self.subTest(cloud_name=cloud_name): + ctx = self._make_ctx(cloud_name, raise_endpoint_not_set=True) + self.assertEqual(self._get_base_url(ctx), expected_url) diff --git a/src/log-analytics/setup.py b/src/log-analytics/setup.py index dcd1f2ecc91..bd9ef287da6 100644 --- a/src/log-analytics/setup.py +++ b/src/log-analytics/setup.py @@ -8,7 +8,7 @@ from codecs import open from setuptools import setup, find_packages -VERSION = "1.0.0b1" +VERSION = "1.0.0b2" CLASSIFIERS = [ 'Development Status :: 4 - Beta',