From 782b29693643891a63e4727f5f169a1ffa7feba6 Mon Sep 17 00:00:00 2001 From: Ganga Mahesh Siddem Date: Thu, 30 Jun 2022 00:00:07 -0700 Subject: [PATCH 1/7] aks monitoring msi auth onboarding updates --- .../azext_aks_preview/addonconfiguration.py | 267 ++++++++++-------- .../tests/latest/test_aks_commands.py | 29 +- 2 files changed, 160 insertions(+), 136 deletions(-) diff --git a/src/aks-preview/azext_aks_preview/addonconfiguration.py b/src/aks-preview/azext_aks_preview/addonconfiguration.py index 8671e52ac73..1fe49985add 100644 --- a/src/aks-preview/azext_aks_preview/addonconfiguration.py +++ b/src/aks-preview/azext_aks_preview/addonconfiguration.py @@ -9,7 +9,7 @@ from azure.cli.core.azclierror import ArgumentUsageError, ClientRequestError from azure.cli.core.commands import LongRunningOperation from azure.cli.core.commands.client_factory import get_subscription_id -from azure.cli.core.util import sdk_no_wait +from azure.cli.core.util import sdk_no_wait, send_raw_request from azext_aks_preview._client_factory import CUSTOM_MGMT_AKS_PREVIEW from azext_aks_preview._client_factory import cf_resources, cf_resource_groups from azext_aks_preview._resourcegroup import get_rg_location @@ -497,16 +497,39 @@ def sanitize_loganalytics_ws_resource_id(workspace_resource_id): return workspace_resource_id -def ensure_container_insights_for_monitoring(cmd, - addon, - cluster_subscription, - cluster_resource_group_name, - cluster_name, - cluster_region, - remove_monitoring=False, - aad_route=False, - create_dcr=False, - create_dcra=False): +def get_existing_container_insights_extension_dcr_tags(cmd, dcr_url): + tags = {} + _MAX_RETRY_TIMES = 3 + for retry_count in range(0, _MAX_RETRY_TIMES): + try: + resp = send_raw_request( + cmd.cli_ctx, "GET", dcr_url + ) + json_response = json.loads(resp.text) + if ("tags" in json_response) and (json_response["tags"] is not None): + tags = json_response["tags"] + break + except CLIError as e: + if "ResourceNotFound" in str(e): + break + if retry_count >= (_MAX_RETRY_TIMES - 1): + raise e + return tags + + +# pylint: disable=too-many-locals,too-many-branches,too-many-statements,line-too-long +def ensure_container_insights_for_monitoring( + cmd, + addon, + cluster_subscription, + cluster_resource_group_name, + cluster_name, + cluster_region, + remove_monitoring=False, + aad_route=False, + create_dcr=False, + create_dcra=False, +): """ Either adds the ContainerInsights solution to a LA Workspace OR sets up a DCR (Data Collection Rule) and DCRA (Data Collection Rule Association). Both let the monitoring addon send data to a Log Analytics Workspace. @@ -520,48 +543,53 @@ def ensure_container_insights_for_monitoring(cmd, """ if not addon.enabled: return None - # workaround for this addon key which has been seen lowercased in the wild for key in list(addon.config): - if key.lower() == CONST_MONITORING_LOG_ANALYTICS_WORKSPACE_RESOURCE_ID.lower() and key != CONST_MONITORING_LOG_ANALYTICS_WORKSPACE_RESOURCE_ID: - addon.config[CONST_MONITORING_LOG_ANALYTICS_WORKSPACE_RESOURCE_ID] = addon.config.pop( - key) - - workspace_resource_id = addon.config[CONST_MONITORING_LOG_ANALYTICS_WORKSPACE_RESOURCE_ID].strip( + if ( + key.lower() == CONST_MONITORING_LOG_ANALYTICS_WORKSPACE_RESOURCE_ID.lower() and + key != CONST_MONITORING_LOG_ANALYTICS_WORKSPACE_RESOURCE_ID + ): + addon.config[ + CONST_MONITORING_LOG_ANALYTICS_WORKSPACE_RESOURCE_ID + ] = addon.config.pop(key) + + workspace_resource_id = addon.config[ + CONST_MONITORING_LOG_ANALYTICS_WORKSPACE_RESOURCE_ID + ] + workspace_resource_id = sanitize_loganalytics_ws_resource_id( + workspace_resource_id ) - if not workspace_resource_id.startswith('/'): - workspace_resource_id = '/' + workspace_resource_id - - if workspace_resource_id.endswith('/'): - workspace_resource_id = workspace_resource_id.rstrip('/') # extract subscription ID and resource group from workspace_resource_id URL try: - subscription_id = workspace_resource_id.split('/')[2] - resource_group = workspace_resource_id.split('/')[4] - workspace_name = workspace_resource_id.split('/')[8] + subscription_id = workspace_resource_id.split("/")[2] + resource_group = workspace_resource_id.split("/")[4] except IndexError: - raise CLIError( - 'Could not locate resource group in workspace-resource-id URL.') + raise AzCLIError( + "Could not locate resource group in workspace-resource-id URL." + ) # region of workspace can be different from region of RG so find the location of the workspace_resource_id if not remove_monitoring: resources = cf_resources(cmd.cli_ctx, subscription_id) - from azure.core.exceptions import HttpResponseError try: resource = resources.get_by_id( - workspace_resource_id, '2015-11-01-preview') + workspace_resource_id, "2015-11-01-preview" + ) location = resource.location except HttpResponseError as ex: raise ex if aad_route: - cluster_resource_id = f"/subscriptions/{cluster_subscription}/resourceGroups/{cluster_resource_group_name}/providers/Microsoft.ContainerService/managedClusters/{cluster_name}" - dataCollectionRuleName = f"MSCI-{workspace_name}" - dcr_resource_id = f"/subscriptions/{subscription_id}/resourceGroups/{resource_group}/providers/Microsoft.Insights/dataCollectionRules/{dataCollectionRuleName}" - from azure.cli.core.util import send_raw_request - from azure.cli.core.profiles import ResourceType - + cluster_resource_id = ( + f"/subscriptions/{cluster_subscription}/resourceGroups/{cluster_resource_group_name}/" + f"providers/Microsoft.ContainerService/managedClusters/{cluster_name}" + ) + dataCollectionRuleName = f"MSCI-{cluster_name}-{cluster_region}" + dcr_resource_id = ( + f"/subscriptions/{subscription_id}/resourceGroups/{resource_group}/" + f"providers/Microsoft.Insights/dataCollectionRules/{dataCollectionRuleName}" + ) if create_dcr: # first get the association between region display names and region IDs (because for some reason # the "which RPs are available in which regions" check returns region display names) @@ -569,130 +597,127 @@ def ensure_container_insights_for_monitoring(cmd, # retry the request up to two times for _ in range(3): try: - location_list_url = f"https://management.azure.com/subscriptions/{subscription_id}/locations?api-version=2019-11-01" + location_list_url = cmd.cli_ctx.cloud.endpoints.resource_manager + \ + f"/subscriptions/{subscription_id}/locations?api-version=2019-11-01" r = send_raw_request(cmd.cli_ctx, "GET", location_list_url) - # this is required to fool the static analyzer. The else statement will only run if an exception # is thrown, but flake8 will complain that e is undefined if we don't also define it here. error = None break - except CLIError as e: + except AzCLIError as e: error = e else: # This will run if the above for loop was not broken out of. This means all three requests failed raise error json_response = json.loads(r.text) for region_data in json_response["value"]: - region_names_to_id[region_data["displayName"] - ] = region_data["name"] + region_names_to_id[region_data["displayName"]] = region_data[ + "name" + ] # check if region supports DCRs and DCR-A for _ in range(3): try: - feature_check_url = f"https://management.azure.com/subscriptions/{subscription_id}/providers/Microsoft.Insights?api-version=2020-10-01" + feature_check_url = cmd.cli_ctx.cloud.endpoints.resource_manager + \ + f"/subscriptions/{subscription_id}/providers/Microsoft.Insights?api-version=2020-10-01" r = send_raw_request(cmd.cli_ctx, "GET", feature_check_url) error = None break - except CLIError as e: + except AzCLIError as e: error = e else: raise error json_response = json.loads(r.text) for resource in json_response["resourceTypes"]: - region_ids = map(lambda x: region_names_to_id[x], - resource["locations"]) # map is lazy, so doing this for every region isn't slow - if resource["resourceType"].lower() == "datacollectionrules" and location not in region_ids: - raise ClientRequestError( - f'Data Collection Rules are not supported for LA workspace region {location}') - elif resource[ - "resourceType"].lower() == "datacollectionruleassociations" and cluster_region not in region_ids: - raise ClientRequestError( - f'Data Collection Rule Associations are not supported for cluster region {location}') - + if resource["resourceType"].lower() == "datacollectionrules": + region_ids = map( + lambda x: region_names_to_id[x], resource["locations"]) + if location not in region_ids: + raise ClientRequestError( + f"Data Collection Rules are not supported for LA workspace region {location}") + if resource["resourceType"].lower() == "datacollectionruleassociations": + region_ids = map( + lambda x: region_names_to_id[x], resource["locations"]) + if cluster_region not in region_ids: + raise ClientRequestError( + f"Data Collection Rule Associations are not supported for cluster region {cluster_region}") + dcr_url = cmd.cli_ctx.cloud.endpoints.resource_manager + \ + f"{dcr_resource_id}?api-version=2021-04-01" + # get existing tags on the container insights extension DCR if the customer added any + existing_tags = get_existing_container_insights_extension_dcr_tags( + cmd, dcr_url) # create the DCR - dcr_creation_body = json.dumps({"location": location, - "properties": { - "dataSources": { - "extensions": [ - { - "name": "ContainerInsightsExtension", - "streams": [ - "Microsoft-Perf", - "Microsoft-ContainerInventory", - "Microsoft-ContainerLog", - "Microsoft-ContainerLogV2", - "Microsoft-ContainerNodeInventory", - "Microsoft-KubeEvents", - "Microsoft-KubeHealth", - "Microsoft-KubeMonAgentEvents", - "Microsoft-KubeNodeInventory", - "Microsoft-KubePodInventory", - "Microsoft-KubePVInventory", - "Microsoft-KubeServices", - "Microsoft-InsightsMetrics" - ], - "extensionName": "ContainerInsights" - } - ] - }, - "dataFlows": [ - { - "streams": [ - "Microsoft-Perf", - "Microsoft-ContainerInventory", - "Microsoft-ContainerLog", - "Microsoft-ContainerLogV2", - "Microsoft-ContainerNodeInventory", - "Microsoft-KubeEvents", - "Microsoft-KubeHealth", - "Microsoft-KubeMonAgentEvents", - "Microsoft-KubeNodeInventory", - "Microsoft-KubePodInventory", - "Microsoft-KubePVInventory", - "Microsoft-KubeServices", - "Microsoft-InsightsMetrics" - ], - "destinations": [ - "la-workspace" - ] - } - ], - "destinations": { - "logAnalytics": [ - { - "workspaceResourceId": workspace_resource_id, - "name": "la-workspace" - } - ] - } - }}) - dcr_url = f"https://management.azure.com/{dcr_resource_id}?api-version=2019-11-01-preview" + dcr_creation_body = json.dumps( + { + "location": location, + "tags": existing_tags, + "properties": { + "dataSources": { + "extensions": [ + { + "name": "ContainerInsightsExtension", + "streams": [ + "Microsoft-ContainerInsights-Group-Default" + ], + "extensionName": "ContainerInsights", + } + ] + }, + "dataFlows": [ + { + "streams": [ + "Microsoft-ContainerInsights-Group-Default" + ], + "destinations": ["la-workspace"], + } + ], + "destinations": { + "logAnalytics": [ + { + "workspaceResourceId": workspace_resource_id, + "name": "la-workspace", + } + ] + }, + }, + } + ) for _ in range(3): try: - send_raw_request(cmd.cli_ctx, "PUT", - dcr_url, body=dcr_creation_body) + send_raw_request( + cmd.cli_ctx, "PUT", dcr_url, body=dcr_creation_body + ) error = None break - except CLIError as e: + except AzCLIError as e: error = e else: raise error if create_dcra: # only create or delete the association between the DCR and cluster - association_body = json.dumps({"location": cluster_region, - "properties": { - "dataCollectionRuleId": dcr_resource_id, - "description": "routes monitoring data to a Log Analytics workspace" - }}) - association_url = f"https://management.azure.com/{cluster_resource_id}/providers/Microsoft.Insights/dataCollectionRuleAssociations/send-to-{workspace_name}?api-version=2019-11-01-preview" + association_body = json.dumps( + { + "location": cluster_region, + "properties": { + "dataCollectionRuleId": dcr_resource_id, + "description": "routes monitoring data to a Log Analytics workspace", + }, + } + ) + association_url = cmd.cli_ctx.cloud.endpoints.resource_manager + \ + f"{cluster_resource_id}/providers/Microsoft.Insights/dataCollectionRuleAssociations/ContainerInsightsExtension?api-version=2021-04-01" for _ in range(3): try: - send_raw_request(cmd.cli_ctx, "PUT" if not remove_monitoring else "DELETE", association_url, - body=association_body) + send_raw_request( + cmd.cli_ctx, + "PUT" if not remove_monitoring else "DELETE", + association_url, + body=association_body, + ) error = None break - except CLIError as e: + except AzCLIError as e: error = e else: raise error diff --git a/src/aks-preview/azext_aks_preview/tests/latest/test_aks_commands.py b/src/aks-preview/azext_aks_preview/tests/latest/test_aks_commands.py index 7306429e239..c7aa793c9fd 100644 --- a/src/aks-preview/azext_aks_preview/tests/latest/test_aks_commands.py +++ b/src/aks-preview/azext_aks_preview/tests/latest/test_aks_commands.py @@ -2286,17 +2286,17 @@ def create_new_cluster_with_monitoring_aad_auth(self, resource_group, resource_g workspace_resource_group = workspace_resource_id.split("/")[4] # check that the DCR was created - dataCollectionRuleName = f"MSCI-{workspace_name}" + dataCollectionRuleName = f"MSCI-{aks_name}-{resource_group_location}" dcr_resource_id = f"/subscriptions/{subscription}/resourceGroups/{workspace_resource_group}/providers/Microsoft.Insights/dataCollectionRules/{dataCollectionRuleName}" - get_cmd = f'rest --method get --url https://management.azure.com{dcr_resource_id}?api-version=2019-11-01-preview' + get_cmd = f'rest --method get --url https://management.azure.com{dcr_resource_id}?api-version=2021-04-01' self.cmd(get_cmd, checks=[ self.check( 'properties.destinations.logAnalytics[0].workspaceResourceId', f'{workspace_resource_id}') ]) # check that the DCR-A was created - dcra_resource_id = f"{cluster_resource_id}/providers/Microsoft.Insights/dataCollectionRuleAssociations/send-to-{workspace_name}" - get_cmd = f'rest --method get --url https://management.azure.com{dcra_resource_id}?api-version=2019-11-01-preview' + dcra_resource_id = f"{cluster_resource_id}/providers/Microsoft.Insights/dataCollectionRuleAssociations/ContainerInsightsExtension" + get_cmd = f'rest --method get --url https://management.azure.com{dcra_resource_id}?api-version=2021-04-01' self.cmd(get_cmd, checks=[ self.check('properties.dataCollectionRuleId', f'{dcr_resource_id}') ]) @@ -2364,17 +2364,17 @@ def enable_monitoring_existing_cluster_aad_atuh(self, resource_group, resource_g workspace_resource_group = workspace_resource_id.split("/")[4] # check that the DCR was created - dataCollectionRuleName = f"MSCI-{workspace_name}" + dataCollectionRuleName = f"MSCI-{aks_name}-{resource_group_location}" dcr_resource_id = f"/subscriptions/{subscription}/resourceGroups/{workspace_resource_group}/providers/Microsoft.Insights/dataCollectionRules/{dataCollectionRuleName}" - get_cmd = f'rest --method get --url https://management.azure.com{dcr_resource_id}?api-version=2019-11-01-preview' + get_cmd = f'rest --method get --url https://management.azure.com{dcr_resource_id}?api-version=2021-04-01' self.cmd(get_cmd, checks=[ self.check( 'properties.destinations.logAnalytics[0].workspaceResourceId', f'{workspace_resource_id}') ]) # check that the DCR-A was created - dcra_resource_id = f"{cluster_resource_id}/providers/Microsoft.Insights/dataCollectionRuleAssociations/send-to-{workspace_name}" - get_cmd = f'rest --method get --url https://management.azure.com{dcra_resource_id}?api-version=2019-11-01-preview' + dcra_resource_id = f"{cluster_resource_id}/providers/Microsoft.Insights/dataCollectionRuleAssociations/ContainerInsightsExtension" + get_cmd = f'rest --method get --url https://management.azure.com{dcra_resource_id}?api-version=2021-04-01' self.cmd(get_cmd, checks=[ self.check('properties.dataCollectionRuleId', f'{dcr_resource_id}') ]) @@ -2417,14 +2417,13 @@ def test_aks_create_with_monitoring_legacy_auth(self, resource_group, resource_g cluster_resource_id = response["id"] subscription = cluster_resource_id.split("/")[2] workspace_resource_id = response["addonProfiles"]["omsagent"]["config"]["logAnalyticsWorkspaceResourceID"] - workspace_name = workspace_resource_id.split("/")[-1] workspace_resource_group = workspace_resource_id.split("/")[4] try: # check that the DCR was created - dataCollectionRuleName = f"MSCI-{workspace_name}" + dataCollectionRuleName = f"MSCI-{aks_name}-{resource_group_location}" dcr_resource_id = f"/subscriptions/{subscription}/resourceGroups/{workspace_resource_group}/providers/Microsoft.Insights/dataCollectionRules/{dataCollectionRuleName}" - get_cmd = f'rest --method get --url https://management.azure.com{dcr_resource_id}?api-version=2019-11-01-preview' + get_cmd = f'rest --method get --url https://management.azure.com{dcr_resource_id}?api-version=2021-04-01' self.cmd(get_cmd, checks=[ self.check( 'properties.destinations.logAnalytics[0].workspaceResourceId', f'{workspace_resource_id}') @@ -4403,7 +4402,7 @@ def test_aks_create_and_update_with_blob_csi_driver(self, resource_group, resour 'name': aks_name, 'ssh_key_value': self.generate_ssh_keys() }) - + # create aks with blob driver create_cmd = 'aks create --resource-group={resource_group} --name={name} --ssh-key-value={ssh_key_value} -o json \ --enable-blob-driver \ @@ -4412,7 +4411,7 @@ def test_aks_create_and_update_with_blob_csi_driver(self, resource_group, resour self.check('provisioningState', 'Succeeded'), self.check('storageProfile.blobCsiDriver.enabled', True), ]) - + # check standard reconcile scenario update_cmd = 'aks update --resource-group={resource_group} --name={name} -y -o json \ --aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/EnableBlobCSIDriver' @@ -4578,7 +4577,7 @@ def test_aks_create_with_web_application_routing(self, resource_group, resource_ @AllowLargeResponse() @AKSCustomResourceGroupPreparer(random_name_length=17, name_prefix='clitest', location='westus2') def test_aks_create_web_application_routing_dns_zone_not_exist(self, resource_group, resource_group_location): - # Test creation failure when using an non-existing dns zone resource ID. + # Test creation failure when using an non-existing dns zone resource ID. aks_name = self.create_random_name('cliakstest', 16) self.kwargs.update({ 'resource_group': resource_group, @@ -4759,7 +4758,7 @@ def test_aks_draft_with_manifest(self): abs_file_path = os.path.join(script_dir, create_config) with tempfile.TemporaryDirectory() as tmp_dir: - + create_cmd = f'aks draft create --path={tmp_dir} --create-config={abs_file_path} --destination={tmp_dir}' self.cmd(create_cmd) assert os.path.isdir(f'{tmp_dir}/manifests') and os.path.isfile(f'{tmp_dir}/Dockerfile') From 363f8b3daeddf6e260cfe5b53135e64025d763c6 Mon Sep 17 00:00:00 2001 From: Ganga Mahesh Siddem Date: Thu, 30 Jun 2022 00:01:54 -0700 Subject: [PATCH 2/7] bump aks-preview version --- src/aks-preview/setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/aks-preview/setup.py b/src/aks-preview/setup.py index 2f857df4765..9d6418e0cc2 100644 --- a/src/aks-preview/setup.py +++ b/src/aks-preview/setup.py @@ -9,7 +9,7 @@ from setuptools import setup, find_packages -VERSION = "0.5.86" +VERSION = "0.5.87" CLASSIFIERS = [ "Development Status :: 4 - Beta", "Intended Audience :: Developers", From 540c714dd256a462d8f9a4c2a43ecdb15d911cf0 Mon Sep 17 00:00:00 2001 From: Ganga Mahesh Siddem Date: Thu, 30 Jun 2022 08:55:25 -0700 Subject: [PATCH 3/7] add missing imports --- src/aks-preview/azext_aks_preview/addonconfiguration.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/aks-preview/azext_aks_preview/addonconfiguration.py b/src/aks-preview/azext_aks_preview/addonconfiguration.py index 1fe49985add..be4377e525d 100644 --- a/src/aks-preview/azext_aks_preview/addonconfiguration.py +++ b/src/aks-preview/azext_aks_preview/addonconfiguration.py @@ -6,10 +6,11 @@ import json from knack.log import get_logger from knack.util import CLIError -from azure.cli.core.azclierror import ArgumentUsageError, ClientRequestError +from azure.cli.core.azclierror import AzCLIError, ArgumentUsageError, ClientRequestError from azure.cli.core.commands import LongRunningOperation from azure.cli.core.commands.client_factory import get_subscription_id from azure.cli.core.util import sdk_no_wait, send_raw_request +from azure.core.exceptions import HttpResponseError from azext_aks_preview._client_factory import CUSTOM_MGMT_AKS_PREVIEW from azext_aks_preview._client_factory import cf_resources, cf_resource_groups from azext_aks_preview._resourcegroup import get_rg_location From 9407a1659caf28f04e1e0aa1ce522c0ff2f42aef Mon Sep 17 00:00:00 2001 From: Ganga Mahesh Siddem Date: Wed, 6 Jul 2022 12:36:29 -0700 Subject: [PATCH 4/7] import the code from azure cli --- .../azext_aks_preview/addonconfiguration.py | 240 +----------------- src/aks-preview/azext_aks_preview/custom.py | 11 +- 2 files changed, 12 insertions(+), 239 deletions(-) diff --git a/src/aks-preview/azext_aks_preview/addonconfiguration.py b/src/aks-preview/azext_aks_preview/addonconfiguration.py index ddd6442d93f..fdd29bc8cb8 100644 --- a/src/aks-preview/azext_aks_preview/addonconfiguration.py +++ b/src/aks-preview/azext_aks_preview/addonconfiguration.py @@ -11,6 +11,11 @@ from azure.cli.core.commands.client_factory import get_subscription_id from azure.cli.core.util import sdk_no_wait, send_raw_request from azure.core.exceptions import HttpResponseError +from azure.cli.command_modules.acs.addonconfiguration import ( + ensure_container_insights_for_monitoring, + sanitize_loganalytics_ws_resource_id, + ensure_default_log_analytics_workspace_for_monitoring +) from azext_aks_preview._client_factory import CUSTOM_MGMT_AKS_PREVIEW from azext_aks_preview._client_factory import get_resources_client, get_resource_groups_client from azext_aks_preview._resourcegroup import get_rg_location @@ -495,241 +500,6 @@ def ensure_default_log_analytics_workspace_for_monitoring(cmd, subscription_id, return ws_resource_id -def sanitize_loganalytics_ws_resource_id(workspace_resource_id): - workspace_resource_id = workspace_resource_id.strip() - if not workspace_resource_id.startswith('/'): - workspace_resource_id = '/' + workspace_resource_id - if workspace_resource_id.endswith('/'): - workspace_resource_id = workspace_resource_id.rstrip('/') - return workspace_resource_id - - -def get_existing_container_insights_extension_dcr_tags(cmd, dcr_url): - tags = {} - _MAX_RETRY_TIMES = 3 - for retry_count in range(0, _MAX_RETRY_TIMES): - try: - resp = send_raw_request( - cmd.cli_ctx, "GET", dcr_url - ) - json_response = json.loads(resp.text) - if ("tags" in json_response) and (json_response["tags"] is not None): - tags = json_response["tags"] - break - except CLIError as e: - if "ResourceNotFound" in str(e): - break - if retry_count >= (_MAX_RETRY_TIMES - 1): - raise e - return tags - - -# pylint: disable=too-many-locals,too-many-branches,too-many-statements,line-too-long -def ensure_container_insights_for_monitoring( - cmd, - addon, - cluster_subscription, - cluster_resource_group_name, - cluster_name, - cluster_region, - remove_monitoring=False, - aad_route=False, - create_dcr=False, - create_dcra=False, -): - """ - Either adds the ContainerInsights solution to a LA Workspace OR sets up a DCR (Data Collection Rule) and DCRA - (Data Collection Rule Association). Both let the monitoring addon send data to a Log Analytics Workspace. - - Set aad_route == True to set up the DCR data route. Otherwise the solution route will be used. Create_dcr and - create_dcra have no effect if aad_route == False. - - Set remove_monitoring to True and create_dcra to True to remove the DCRA from a cluster. The association makes - it very hard to delete either the DCR or cluster. (It is not obvious how to even navigate to the association from - the portal, and it prevents the cluster and DCR from being deleted individually). - """ - if not addon.enabled: - return None - # workaround for this addon key which has been seen lowercased in the wild - for key in list(addon.config): - if ( - key.lower() == CONST_MONITORING_LOG_ANALYTICS_WORKSPACE_RESOURCE_ID.lower() and - key != CONST_MONITORING_LOG_ANALYTICS_WORKSPACE_RESOURCE_ID - ): - addon.config[ - CONST_MONITORING_LOG_ANALYTICS_WORKSPACE_RESOURCE_ID - ] = addon.config.pop(key) - - workspace_resource_id = addon.config[ - CONST_MONITORING_LOG_ANALYTICS_WORKSPACE_RESOURCE_ID - ] - workspace_resource_id = sanitize_loganalytics_ws_resource_id( - workspace_resource_id - ) - - # extract subscription ID and resource group from workspace_resource_id URL - try: - subscription_id = workspace_resource_id.split("/")[2] - resource_group = workspace_resource_id.split("/")[4] - except IndexError: - raise AzCLIError( - "Could not locate resource group in workspace-resource-id URL." - ) - - # region of workspace can be different from region of RG so find the location of the workspace_resource_id - if not remove_monitoring: - resources = cf_resources(cmd.cli_ctx, subscription_id) - try: - resource = resources.get_by_id( - workspace_resource_id, "2015-11-01-preview" - ) - location = resource.location - except HttpResponseError as ex: - raise ex - - if aad_route: - cluster_resource_id = ( - f"/subscriptions/{cluster_subscription}/resourceGroups/{cluster_resource_group_name}/" - f"providers/Microsoft.ContainerService/managedClusters/{cluster_name}" - ) - dataCollectionRuleName = f"MSCI-{cluster_name}-{cluster_region}" - dcr_resource_id = ( - f"/subscriptions/{subscription_id}/resourceGroups/{resource_group}/" - f"providers/Microsoft.Insights/dataCollectionRules/{dataCollectionRuleName}" - ) - if create_dcr: - # first get the association between region display names and region IDs (because for some reason - # the "which RPs are available in which regions" check returns region display names) - region_names_to_id = {} - # retry the request up to two times - for _ in range(3): - try: - location_list_url = cmd.cli_ctx.cloud.endpoints.resource_manager + \ - f"/subscriptions/{subscription_id}/locations?api-version=2019-11-01" - r = send_raw_request(cmd.cli_ctx, "GET", location_list_url) - # this is required to fool the static analyzer. The else statement will only run if an exception - # is thrown, but flake8 will complain that e is undefined if we don't also define it here. - error = None - break - except AzCLIError as e: - error = e - else: - # This will run if the above for loop was not broken out of. This means all three requests failed - raise error - json_response = json.loads(r.text) - for region_data in json_response["value"]: - region_names_to_id[region_data["displayName"]] = region_data[ - "name" - ] - - # check if region supports DCRs and DCR-A - for _ in range(3): - try: - feature_check_url = cmd.cli_ctx.cloud.endpoints.resource_manager + \ - f"/subscriptions/{subscription_id}/providers/Microsoft.Insights?api-version=2020-10-01" - r = send_raw_request(cmd.cli_ctx, "GET", feature_check_url) - error = None - break - except AzCLIError as e: - error = e - else: - raise error - json_response = json.loads(r.text) - for resource in json_response["resourceTypes"]: - if resource["resourceType"].lower() == "datacollectionrules": - region_ids = map( - lambda x: region_names_to_id[x], resource["locations"]) - if location not in region_ids: - raise ClientRequestError( - f"Data Collection Rules are not supported for LA workspace region {location}") - if resource["resourceType"].lower() == "datacollectionruleassociations": - region_ids = map( - lambda x: region_names_to_id[x], resource["locations"]) - if cluster_region not in region_ids: - raise ClientRequestError( - f"Data Collection Rule Associations are not supported for cluster region {cluster_region}") - dcr_url = cmd.cli_ctx.cloud.endpoints.resource_manager + \ - f"{dcr_resource_id}?api-version=2021-04-01" - # get existing tags on the container insights extension DCR if the customer added any - existing_tags = get_existing_container_insights_extension_dcr_tags( - cmd, dcr_url) - # create the DCR - dcr_creation_body = json.dumps( - { - "location": location, - "tags": existing_tags, - "properties": { - "dataSources": { - "extensions": [ - { - "name": "ContainerInsightsExtension", - "streams": [ - "Microsoft-ContainerInsights-Group-Default" - ], - "extensionName": "ContainerInsights", - } - ] - }, - "dataFlows": [ - { - "streams": [ - "Microsoft-ContainerInsights-Group-Default" - ], - "destinations": ["la-workspace"], - } - ], - "destinations": { - "logAnalytics": [ - { - "workspaceResourceId": workspace_resource_id, - "name": "la-workspace", - } - ] - }, - }, - } - ) - for _ in range(3): - try: - send_raw_request( - cmd.cli_ctx, "PUT", dcr_url, body=dcr_creation_body - ) - error = None - break - except AzCLIError as e: - error = e - else: - raise error - - if create_dcra: - # only create or delete the association between the DCR and cluster - association_body = json.dumps( - { - "location": cluster_region, - "properties": { - "dataCollectionRuleId": dcr_resource_id, - "description": "routes monitoring data to a Log Analytics workspace", - }, - } - ) - association_url = cmd.cli_ctx.cloud.endpoints.resource_manager + \ - f"{cluster_resource_id}/providers/Microsoft.Insights/dataCollectionRuleAssociations/ContainerInsightsExtension?api-version=2021-04-01" - for _ in range(3): - try: - send_raw_request( - cmd.cli_ctx, - "PUT" if not remove_monitoring else "DELETE", - association_url, - body=association_body, - ) - error = None - break - except AzCLIError as e: - error = e - else: - raise error - - def add_monitoring_role_assignment(result, cluster_resource_id, cmd): service_principal_msi_id = None # Check if service principal exists, if it does, assign permissions to service principal diff --git a/src/aks-preview/azext_aks_preview/custom.py b/src/aks-preview/azext_aks_preview/custom.py index 00399b82cb1..dac9b67dafc 100644 --- a/src/aks-preview/azext_aks_preview/custom.py +++ b/src/aks-preview/azext_aks_preview/custom.py @@ -43,6 +43,12 @@ from six.moves.urllib.error import URLError from six.moves.urllib.request import urlopen +from azure.cli.command_modules.acs.addonconfiguration import ( + ensure_container_insights_for_monitoring, + sanitize_loganalytics_ws_resource_id, + ensure_default_log_analytics_workspace_for_monitoring +) + from azext_aks_preview._client_factory import ( CUSTOM_MGMT_AKS_PREVIEW, cf_agent_pools, @@ -97,10 +103,7 @@ add_ingress_appgw_addon_role_assignment, add_monitoring_role_assignment, add_virtual_node_role_assignment, - enable_addons, - ensure_container_insights_for_monitoring, - ensure_default_log_analytics_workspace_for_monitoring, - sanitize_loganalytics_ws_resource_id, + enable_addons ) from azext_aks_preview.aks_draft.commands import ( aks_draft_cmd_create, From 88a3c01bcf001c0f0c682f32c71089e71556b287 Mon Sep 17 00:00:00 2001 From: Ganga Mahesh Siddem Date: Wed, 6 Jul 2022 18:55:10 -0700 Subject: [PATCH 5/7] remove redefined code --- .../azext_aks_preview/addonconfiguration.py | 178 ------------------ 1 file changed, 178 deletions(-) diff --git a/src/aks-preview/azext_aks_preview/addonconfiguration.py b/src/aks-preview/azext_aks_preview/addonconfiguration.py index fdd29bc8cb8..a82094e3499 100644 --- a/src/aks-preview/azext_aks_preview/addonconfiguration.py +++ b/src/aks-preview/azext_aks_preview/addonconfiguration.py @@ -322,184 +322,6 @@ def update_addons(cmd, # pylint: disable=too-many-branches,too-many-statements return instance -def ensure_default_log_analytics_workspace_for_monitoring(cmd, subscription_id, resource_group_name): - # mapping for azure public cloud - # log analytics workspaces cannot be created in WCUS region due to capacity limits - # so mapped to EUS per discussion with log analytics team - AzureCloudLocationToOmsRegionCodeMap = { - "australiasoutheast": "ASE", - "australiaeast": "EAU", - "australiacentral": "CAU", - "canadacentral": "CCA", - "centralindia": "CIN", - "centralus": "CUS", - "eastasia": "EA", - "eastus": "EUS", - "eastus2": "EUS2", - "eastus2euap": "EAP", - "francecentral": "PAR", - "japaneast": "EJP", - "koreacentral": "SE", - "northeurope": "NEU", - "southcentralus": "SCUS", - "southeastasia": "SEA", - "uksouth": "SUK", - "usgovvirginia": "USGV", - "westcentralus": "EUS", - "westeurope": "WEU", - "westus": "WUS", - "westus2": "WUS2", - "brazilsouth": "CQ", - "brazilsoutheast": "BRSE", - "norwayeast": "NOE", - "southafricanorth": "JNB", - "northcentralus": "NCUS", - "uaenorth": "DXB", - "germanywestcentral": "DEWC", - "ukwest": "WUK", - "switzerlandnorth": "CHN", - "switzerlandwest": "CHW", - "uaecentral": "AUH" - } - AzureCloudRegionToOmsRegionMap = { - "australiacentral": "australiacentral", - "australiacentral2": "australiacentral", - "australiaeast": "australiaeast", - "australiasoutheast": "australiasoutheast", - "brazilsouth": "brazilsouth", - "canadacentral": "canadacentral", - "canadaeast": "canadacentral", - "centralus": "centralus", - "centralindia": "centralindia", - "eastasia": "eastasia", - "eastus": "eastus", - "eastus2": "eastus2", - "francecentral": "francecentral", - "francesouth": "francecentral", - "japaneast": "japaneast", - "japanwest": "japaneast", - "koreacentral": "koreacentral", - "koreasouth": "koreacentral", - "northcentralus": "northcentralus", - "northeurope": "northeurope", - "southafricanorth": "southafricanorth", - "southafricawest": "southafricanorth", - "southcentralus": "southcentralus", - "southeastasia": "southeastasia", - "southindia": "centralindia", - "uksouth": "uksouth", - "ukwest": "ukwest", - "westcentralus": "eastus", - "westeurope": "westeurope", - "westindia": "centralindia", - "westus": "westus", - "westus2": "westus2", - "norwayeast": "norwayeast", - "norwaywest": "norwayeast", - "switzerlandnorth": "switzerlandnorth", - "switzerlandwest": "switzerlandwest", - "uaenorth": "uaenorth", - "germanywestcentral": "germanywestcentral", - "germanynorth": "germanywestcentral", - "uaecentral": "uaecentral", - "eastus2euap": "eastus2euap", - "brazilsoutheast": "brazilsoutheast" - } - - # mapping for azure china cloud - # log analytics only support China East2 region - AzureChinaLocationToOmsRegionCodeMap = { - "chinaeast": "EAST2", - "chinaeast2": "EAST2", - "chinanorth": "EAST2", - "chinanorth2": "EAST2" - } - AzureChinaRegionToOmsRegionMap = { - "chinaeast": "chinaeast2", - "chinaeast2": "chinaeast2", - "chinanorth": "chinaeast2", - "chinanorth2": "chinaeast2" - } - - # mapping for azure us governmner cloud - AzureFairfaxLocationToOmsRegionCodeMap = { - "usgovvirginia": "USGV", - "usgovarizona": "PHX" - } - AzureFairfaxRegionToOmsRegionMap = { - "usgovvirginia": "usgovvirginia", - "usgovtexas": "usgovvirginia", - "usgovarizona": "usgovarizona" - } - - rg_location = get_rg_location(cmd.cli_ctx, resource_group_name) - cloud_name = cmd.cli_ctx.cloud.name - - if cloud_name.lower() == 'azurecloud': - workspace_region = AzureCloudRegionToOmsRegionMap.get( - rg_location, "eastus") - workspace_region_code = AzureCloudLocationToOmsRegionCodeMap.get( - workspace_region, "EUS") - elif cloud_name.lower() == 'azurechinacloud': - workspace_region = AzureChinaRegionToOmsRegionMap.get( - rg_location, "chinaeast2") - workspace_region_code = AzureChinaLocationToOmsRegionCodeMap.get( - workspace_region, "EAST2") - elif cloud_name.lower() == 'azureusgovernment': - workspace_region = AzureFairfaxRegionToOmsRegionMap.get( - rg_location, "usgovvirginia") - workspace_region_code = AzureFairfaxLocationToOmsRegionCodeMap.get( - workspace_region, "USGV") - else: - logger.error( - "AKS Monitoring addon not supported in cloud : %s", cloud_name) - - default_workspace_resource_group = 'DefaultResourceGroup-' + workspace_region_code - default_workspace_name = 'DefaultWorkspace-{0}-{1}'.format( - subscription_id, workspace_region_code) - - default_workspace_resource_id = '/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.OperationalInsights' \ - '/workspaces/{2}'.format(subscription_id, - default_workspace_resource_group, default_workspace_name) - resource_groups = get_resource_groups_client(cmd.cli_ctx, subscription_id) - resources = get_resources_client(cmd.cli_ctx, subscription_id) - - from azure.cli.core.profiles import ResourceType - # check if default RG exists - if resource_groups.check_existence(default_workspace_resource_group): - from azure.core.exceptions import HttpResponseError - try: - resource = resources.get_by_id( - default_workspace_resource_id, '2015-11-01-preview') - return resource.id - except HttpResponseError as ex: - if ex.status_code != 404: - raise ex - else: - ResourceGroup = cmd.get_models( - 'ResourceGroup', resource_type=ResourceType.MGMT_RESOURCE_RESOURCES) - resource_group = ResourceGroup(location=workspace_region) - resource_groups.create_or_update( - default_workspace_resource_group, resource_group) - - GenericResource = cmd.get_models( - 'GenericResource', resource_type=ResourceType.MGMT_RESOURCE_RESOURCES) - generic_resource = GenericResource(location=workspace_region, properties={ - 'sku': {'name': 'standalone'}}) - - async_poller = resources.begin_create_or_update_by_id(default_workspace_resource_id, '2015-11-01-preview', - generic_resource) - - ws_resource_id = '' - while True: - result = async_poller.result(15) - if async_poller.done(): - ws_resource_id = result.id - break - - return ws_resource_id - - def add_monitoring_role_assignment(result, cluster_resource_id, cmd): service_principal_msi_id = None # Check if service principal exists, if it does, assign permissions to service principal From 28743f646695cb5fbba6f3af79f7104e6b29ad4a Mon Sep 17 00:00:00 2001 From: Ganga Mahesh Siddem Date: Wed, 6 Jul 2022 20:32:40 -0700 Subject: [PATCH 6/7] remove unused imports --- src/aks-preview/azext_aks_preview/addonconfiguration.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/aks-preview/azext_aks_preview/addonconfiguration.py b/src/aks-preview/azext_aks_preview/addonconfiguration.py index a82094e3499..8d4e966d007 100644 --- a/src/aks-preview/azext_aks_preview/addonconfiguration.py +++ b/src/aks-preview/azext_aks_preview/addonconfiguration.py @@ -6,19 +6,16 @@ import json from knack.log import get_logger from knack.util import CLIError -from azure.cli.core.azclierror import AzCLIError, ArgumentUsageError, ClientRequestError +from azure.cli.core.azclierror import ArgumentUsageError from azure.cli.core.commands import LongRunningOperation from azure.cli.core.commands.client_factory import get_subscription_id -from azure.cli.core.util import sdk_no_wait, send_raw_request -from azure.core.exceptions import HttpResponseError +from azure.cli.core.util import sdk_no_wait from azure.cli.command_modules.acs.addonconfiguration import ( ensure_container_insights_for_monitoring, sanitize_loganalytics_ws_resource_id, ensure_default_log_analytics_workspace_for_monitoring ) from azext_aks_preview._client_factory import CUSTOM_MGMT_AKS_PREVIEW -from azext_aks_preview._client_factory import get_resources_client, get_resource_groups_client -from azext_aks_preview._resourcegroup import get_rg_location from azext_aks_preview._roleassignments import add_role_assignment from azext_aks_preview._consts import ( ADDONS, From 250a6a9ae927173347c5df97547a59cae9293ca2 Mon Sep 17 00:00:00 2001 From: Ganga Mahesh Siddem Date: Thu, 7 Jul 2022 08:28:00 -0700 Subject: [PATCH 7/7] bump aks-preview version --- src/aks-preview/HISTORY.rst | 4 ++++ src/aks-preview/setup.py | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/aks-preview/HISTORY.rst b/src/aks-preview/HISTORY.rst index eb668103637..d888a32334d 100644 --- a/src/aks-preview/HISTORY.rst +++ b/src/aks-preview/HISTORY.rst @@ -11,6 +11,10 @@ To release a new version, please select a new version number (usually plus 1 to Pending +++++++ +0.5.88 +++++++ + +* AKS Monitoring MSI Auth related code imported from Azure CLI to reuse the code between aks-preview and Azure CLI 0.5.87 ++++++ diff --git a/src/aks-preview/setup.py b/src/aks-preview/setup.py index 9d6418e0cc2..844f66f117d 100644 --- a/src/aks-preview/setup.py +++ b/src/aks-preview/setup.py @@ -9,7 +9,7 @@ from setuptools import setup, find_packages -VERSION = "0.5.87" +VERSION = "0.5.88" CLASSIFIERS = [ "Development Status :: 4 - Beta", "Intended Audience :: Developers",