Hello community, here is the log from the commit of package azure-cli-core for openSUSE:Factory checked in at 2020-08-19 18:52:52 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/azure-cli-core (Old) and /work/SRC/openSUSE:Factory/.azure-cli-core.new.3399 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "azure-cli-core" Wed Aug 19 18:52:52 2020 rev:17 rq:827685 version:2.10.1 Changes: -------- --- /work/SRC/openSUSE:Factory/azure-cli-core/azure-cli-core.changes 2020-07-26 16:19:02.192791035 +0200 +++ /work/SRC/openSUSE:Factory/.azure-cli-core.new.3399/azure-cli-core.changes 2020-08-19 18:54:44.499780790 +0200 @@ -1,0 +2,9 @@ +Tue Aug 18 21:25:40 UTC 2020 - John Paul Adrian Glaubitz <adrian.glaub...@suse.com> + +- New upstream release (boo#1175289) + + Version 2.10.1 + + For detailed information about changes see the + HISTORY.txt file provided with this package +- Update Requires from setup.py + +------------------------------------------------------------------- Old: ---- azure-cli-core-2.9.1.tar.gz New: ---- azure-cli-core-2.10.1.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ azure-cli-core.spec ++++++ --- /var/tmp/diff_new_pack.yuF43K/_old 2020-08-19 18:54:47.419782342 +0200 +++ /var/tmp/diff_new_pack.yuF43K/_new 2020-08-19 18:54:47.423782344 +0200 @@ -17,7 +17,7 @@ Name: azure-cli-core -Version: 2.9.1 +Version: 2.10.1 Release: 0 Summary: Microsoft Azure CLI Core Module License: MIT @@ -43,14 +43,14 @@ Requires: python3-azure-mgmt-core < 2.0.0 Requires: python3-azure-mgmt-core >= 1.0.0 Requires: python3-azure-mgmt-resource < 11.0.0 -Requires: python3-azure-mgmt-resource >= 10.0.0 +Requires: python3-azure-mgmt-resource >= 10.1.0 Requires: python3-azure-nspkg >= 3.0.0 Requires: python3-colorama >= 0.4.1 Requires: python3-humanfriendly < 9.0 Requires: python3-humanfriendly >= 4.7 Requires: python3-jmespath Requires: python3-knack < 1.0.0 -Requires: python3-knack >= 0.7.1 +Requires: python3-knack >= 0.7.2 Requires: python3-msal < 2.0.0 Requires: python3-msal >= 1.0.0 Requires: python3-msal-extensions < 1.0.0 ++++++ azure-cli-core-2.9.1.tar.gz -> azure-cli-core-2.10.1.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/azure-cli-core-2.9.1/HISTORY.rst new/azure-cli-core-2.10.1/HISTORY.rst --- old/azure-cli-core-2.9.1/HISTORY.rst 2020-07-16 10:09:55.000000000 +0200 +++ new/azure-cli-core-2.10.1/HISTORY.rst 2020-08-06 10:46:59.000000000 +0200 @@ -3,6 +3,14 @@ Release History =============== +2.10.1 +++++++ +* Minor fixes + +2.10.0 +++++++ +* Minor fixes + 2.9.1 ++++++ * Minor fixes diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/azure-cli-core-2.9.1/PKG-INFO new/azure-cli-core-2.10.1/PKG-INFO --- old/azure-cli-core-2.9.1/PKG-INFO 2020-07-16 10:10:08.000000000 +0200 +++ new/azure-cli-core-2.10.1/PKG-INFO 2020-08-06 10:47:07.000000000 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: azure-cli-core -Version: 2.9.1 +Version: 2.10.1 Summary: Microsoft Azure Command-Line Tools Core Module Home-page: https://github.com/Azure/azure-cli Author: Microsoft Corporation @@ -15,6 +15,14 @@ Release History =============== + 2.10.1 + ++++++ + * Minor fixes + + 2.10.0 + ++++++ + * Minor fixes + 2.9.1 ++++++ * Minor fixes diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/azure-cli-core-2.9.1/azure/cli/core/__init__.py new/azure-cli-core-2.10.1/azure/cli/core/__init__.py --- old/azure-cli-core-2.9.1/azure/cli/core/__init__.py 2020-07-16 10:09:55.000000000 +0200 +++ new/azure-cli-core-2.10.1/azure/cli/core/__init__.py 2020-08-06 10:46:59.000000000 +0200 @@ -6,7 +6,7 @@ from __future__ import print_function -__version__ = "2.9.1" +__version__ = "2.10.1" import os import sys @@ -373,18 +373,6 @@ res.append(sup) return res - def _roughly_parse_command(args): - # Roughly parse the command part: <az vm create> --name vm1 - # Similar to knack.invocation.CommandInvoker._rudimentary_get_command, but we don't need to bother with - # positional args - nouns = [] - for arg in args: - if arg and arg[0] != '-': - nouns.append(arg) - else: - break - return ' '.join(nouns).lower() - # Clear the tables to make this method idempotent self.command_group_table.clear() self.command_table.clear() @@ -404,17 +392,32 @@ _update_command_table_from_extensions([], index_extensions) logger.debug("Loaded %d groups, %d commands.", len(self.command_group_table), len(self.command_table)) + from azure.cli.core.util import roughly_parse_command # The index may be outdated. Make sure the command appears in the loaded command table - command_str = _roughly_parse_command(args) - if command_str in self.command_table: - logger.debug("Found a match in the command table for '%s'", command_str) - return self.command_table - if command_str in self.command_group_table: - logger.debug("Found a match in the command group table for '%s'", command_str) + raw_cmd = roughly_parse_command(args) + for cmd in self.command_table: + if raw_cmd.startswith(cmd): + # For commands with positional arguments, the raw command won't match the one in the + # command table. For example, `az find vm create` won't exist in the command table, but the + # corresponding command should be `az find`. + # raw command : az find vm create + # command table: az find + # remaining : vm create + logger.debug("Found a match in the command table.") + logger.debug("Raw command : %s", raw_cmd) + logger.debug("Command table: %s", cmd) + remaining = raw_cmd[len(cmd) + 1:] + if remaining: + logger.debug("remaining : %s %s", ' ' * len(cmd), remaining) + return self.command_table + # For command group, it must be an exact match, as no positional argument is supported by + # command group operations. + if raw_cmd in self.command_group_table: + logger.debug("Found a match in the command group table for '%s'.", raw_cmd) return self.command_table - logger.debug("Could not find a match in the command table for '%s'. The index may be outdated", - command_str) + logger.debug("Could not find a match in the command or command group table for '%s'. " + "The index may be outdated.", raw_cmd) else: logger.debug("No module found from index for '%s'", args) @@ -579,7 +582,7 @@ logger.debug("Command index has been invalidated.") -class ModExtensionSuppress(object): # pylint: disable=too-few-public-methods +class ModExtensionSuppress: # pylint: disable=too-few-public-methods def __init__(self, mod_name, suppress_extension_name, suppress_up_to_version, reason=None, recommend_remove=False, recommend_update=False): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/azure-cli-core-2.9.1/azure/cli/core/_help_loaders.py new/azure-cli-core-2.10.1/azure/cli/core/_help_loaders.py --- old/azure-cli-core-2.9.1/azure/cli/core/_help_loaders.py 2020-07-16 10:09:55.000000000 +0200 +++ new/azure-cli-core-2.10.1/azure/cli/core/_help_loaders.py 2020-08-06 10:46:59.000000000 +0200 @@ -5,13 +5,14 @@ import abc import os -import yaml from azure.cli.core._help import (HelpExample, CliHelpFile) from knack.util import CLIError from knack.log import get_logger +import yaml + logger = get_logger(__name__) try: @@ -93,7 +94,7 @@ help_obj.parameters = loaded_params -class YamlLoaderMixin(object): # pylint:disable=too-few-public-methods +class YamlLoaderMixin: # pylint:disable=too-few-public-methods """A class containing helper methods for Yaml Loaders.""" # get the list of yaml help file names for the command or group diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/azure-cli-core-2.9.1/azure/cli/core/_profile.py new/azure-cli-core-2.10.1/azure/cli/core/_profile.py --- old/azure-cli-core-2.9.1/azure/cli/core/_profile.py 2020-07-16 10:09:55.000000000 +0200 +++ new/azure-cli-core-2.10.1/azure/cli/core/_profile.py 2020-08-06 10:46:59.000000000 +0200 @@ -15,14 +15,15 @@ from copy import deepcopy from enum import Enum +from knack.log import get_logger +from knack.util import CLIError + from azure.cli.core._environment import get_config_dir from azure.cli.core._session import ACCOUNT from azure.cli.core.util import get_file_json, in_cloud_console, open_page_in_browser, can_launch_browser,\ is_windows, is_wsl from azure.cli.core.cloud import get_active_cloud, set_cloud_subscription -from knack.log import get_logger -from knack.util import CLIError logger = get_logger(__name__) @@ -139,7 +140,7 @@ # pylint: disable=too-many-lines,too-many-instance-attributes -class Profile(object): +class Profile: _global_creds_cache = None @@ -745,9 +746,12 @@ endpoint_mappings['sql_management'] = 'sqlManagementEndpointUrl' endpoint_mappings['gallery'] = 'galleryEndpointUrl' endpoint_mappings['management'] = 'managementEndpointUrl' - + from azure.cli.core.cloud import CloudEndpointNotSetException for e in endpoint_mappings: - result[endpoint_mappings[e]] = getattr(get_active_cloud(self.cli_ctx).endpoints, e) + try: + result[endpoint_mappings[e]] = getattr(get_active_cloud(self.cli_ctx).endpoints, e) + except CloudEndpointNotSetException: + result[endpoint_mappings[e]] = None return result def get_installation_id(self): @@ -759,7 +763,7 @@ return installation_id -class MsiAccountTypes(object): +class MsiAccountTypes: # pylint: disable=no-method-argument,no-self-argument system_assigned = 'MSI' user_assigned_client_id = 'MSIClient' @@ -785,7 +789,7 @@ raise ValueError("unrecognized msi account name '{}'".format(cli_account_name)) -class SubscriptionFinder(object): +class SubscriptionFinder: '''finds all subscriptions for a user or service principal''' def __init__(self, cli_ctx, auth_context_factory, adal_token_cache, arm_client_factory=None): @@ -970,7 +974,7 @@ return all_subscriptions -class CredsCache(object): +class CredsCache: '''Caches AAD tokena and service principal secrets, and persistence will also be handled ''' @@ -1128,7 +1132,7 @@ _delete_file(self._token_file) -class ServicePrincipalAuth(object): +class ServicePrincipalAuth: def __init__(self, password_arg_value, use_cert_sn_issuer=None): if not password_arg_value: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/azure-cli-core-2.9.1/azure/cli/core/_session.py new/azure-cli-core-2.10.1/azure/cli/core/_session.py --- old/azure-cli-core-2.9.1/azure/cli/core/_session.py 2020-07-16 10:09:55.000000000 +0200 +++ new/azure-cli-core-2.10.1/azure/cli/core/_session.py 2020-08-06 10:46:59.000000000 +0200 @@ -113,3 +113,6 @@ # it could be lagged behind and can be used to check whether # an upgrade of azure-cli happens VERSIONS = Session() + +# EXT_CMD_TREE provides command to extension name mapping +EXT_CMD_TREE = Session() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/azure-cli-core-2.9.1/azure/cli/core/azlogging.py new/azure-cli-core-2.10.1/azure/cli/core/azlogging.py --- old/azure-cli-core-2.9.1/azure/cli/core/azlogging.py 2020-07-16 10:09:55.000000000 +0200 +++ new/azure-cli-core-2.10.1/azure/cli/core/azlogging.py 2020-08-06 10:46:59.000000000 +0200 @@ -191,7 +191,7 @@ self.command_metadata_logger = None -class CommandLoggerContext(object): +class CommandLoggerContext: def __init__(self, module_logger): self.logger = module_logger self.hdlr = logging.getLogger(AzCliLogging._COMMAND_METADATA_LOGGER) # pylint: disable=protected-access diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/azure-cli-core-2.9.1/azure/cli/core/cloud.py new/azure-cli-core-2.10.1/azure/cli/core/cloud.py --- old/azure-cli-core-2.9.1/azure/cli/core/cloud.py 2020-07-16 10:09:55.000000000 +0200 +++ new/azure-cli-core-2.10.1/azure/cli/core/cloud.py 2020-08-06 10:46:59.000000000 +0200 @@ -2,6 +2,7 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. See License.txt in the project root for license information. # -------------------------------------------------------------------------------------------- +# pylint: disable=line-too-long import os import json @@ -53,7 +54,7 @@ pass -class CloudEndpoints(object): # pylint: disable=too-few-public-methods,too-many-instance-attributes +class CloudEndpoints: # pylint: disable=too-few-public-methods,too-many-instance-attributes def __init__(self, management=None, @@ -71,7 +72,8 @@ ossrdbms_resource_id=None, log_analytics_resource_id=None, app_insights_resource_id=None, - app_insights_telemetry_channel_resource_id=None): + app_insights_telemetry_channel_resource_id=None, + synapse_analytics_resource_id=None): # Attribute names are significant. They are used when storing/retrieving clouds from config self.management = management self.resource_manager = resource_manager @@ -89,6 +91,7 @@ self.log_analytics_resource_id = log_analytics_resource_id self.app_insights_resource_id = app_insights_resource_id self.app_insights_telemetry_channel_resource_id = app_insights_telemetry_channel_resource_id + self.synapse_analytics_resource_id = synapse_analytics_resource_id def has_endpoint_set(self, endpoint_name): try: @@ -110,7 +113,7 @@ return val -class CloudSuffixes(object): # pylint: disable=too-few-public-methods,too-many-instance-attributes +class CloudSuffixes: # pylint: disable=too-few-public-methods,too-many-instance-attributes def __init__(self, storage_endpoint=None, @@ -122,7 +125,8 @@ acr_login_server_endpoint=None, mysql_server_endpoint=None, postgresql_server_endpoint=None, - mariadb_server_endpoint=None): + mariadb_server_endpoint=None, + synapse_analytics_endpoint=None): # Attribute names are significant. They are used when storing/retrieving clouds from config self.storage_endpoint = storage_endpoint self.storage_sync_endpoint = storage_sync_endpoint @@ -134,6 +138,7 @@ self.azure_datalake_store_file_system_endpoint = azure_datalake_store_file_system_endpoint self.azure_datalake_analytics_catalog_and_job_endpoint = azure_datalake_analytics_catalog_and_job_endpoint self.acr_login_server_endpoint = acr_login_server_endpoint + self.synapse_analytics_endpoint = synapse_analytics_endpoint def __getattribute__(self, name): val = object.__getattribute__(self, name) @@ -170,17 +175,90 @@ 'AzureCloud': 'afs.azure.net', 'AzureUSGovernment': 'afs.azure.us', } - return storage_sync_endpoint_mapper.get(cloud_name, 'afs.azure.net') + return storage_sync_endpoint_mapper.get(cloud_name, None) + + +def _get_synapse_analytics_endpoint(cloud_name): + synapse_analytics_endpoint_mapper = { + 'AzureCloud': 'dev.azuresynapse.net', + 'AzureChinaCloud': 'dev.azuresynapse.azure.cn' + } + return synapse_analytics_endpoint_mapper.get(cloud_name, None) + + +def _get_database_server_endpoint(sql_server_hostname, cloud_name): + def _concat_db_server_endpoint(db_prefix): + if cloud_name == 'AzureCloud': + return db_prefix + '.database.azure.com' + if not sql_server_hostname: + return None + return db_prefix + sql_server_hostname + return _concat_db_server_endpoint + + +def _get_app_insights_telemetry_channel_resource_id(cloud_name): + app_insights_telemetry_channel_resource_id_mapper = { + 'AzureCloud': 'https://dc.applicationinsights.azure.com/v2/track', + 'AzureChinaCloud': 'https://dc.applicationinsights.azure.cn/v2/track', + 'AzureUSGovernment': 'https://dc.applicationinsights.us/v2/track' + } + return app_insights_telemetry_channel_resource_id_mapper.get(cloud_name, None) + + +def _get_log_analytics_resource_id(cloud_name): + log_analytics_resource_id_mapper = { + 'AzureCloud': 'https://api.loganalytics.io', + 'AzureChinaCloud': 'https://api.loganalytics.azure.cn', + 'AzureUSGovernment': 'https://api.loganalytics.us' + } + return log_analytics_resource_id_mapper.get(cloud_name, None) + + +def _get_app_insights_resource_id(cloud_name): + app_insights_resource_id_mapper = { + 'AzureCloud': 'https://api.applicationinsights.io', + 'AzureChinaCloud': 'https://api.applicationinsights.azure.cn', + 'AzureUSGovernment': 'https://api.applicationinsights.us' + } + return app_insights_resource_id_mapper.get(cloud_name, None) + + +def _get_synapse_analytics_resource_id(cloud_name): + synapse_analytics_resource_id_mapper = { + 'AzureCloud': 'https://dev.azuresynapse.net', + 'AzureChinaCloud': 'https://dev.azuresynapse.net' + } + return synapse_analytics_resource_id_mapper.get(cloud_name, None) def _convert_arm_to_cli(arm_cloud_metadata_dict): cli_cloud_metadata_dict = {} for cloud in arm_cloud_metadata_dict: cli_cloud_metadata_dict[cloud['name']] = _arm_to_cli_mapper(cloud) + if 'AzureCloud' in cli_cloud_metadata_dict: + cli_cloud_metadata_dict['AzureCloud'].endpoints.active_directory = 'https://login.microsoftonline.com' # change once active_directory is fixed in ARM for the public cloud return cli_cloud_metadata_dict +def _add_starting_dot(suffix): + return suffix if not suffix or suffix.startswith('.') else '.' + suffix + + +def _get_arm_endpoint(arm_dict, is_suffix=False): + def _get_processed_arm_endpoint(name, add_dot=False, fallback_value=None): + if is_suffix: + return (_add_starting_dot(arm_dict['suffixes'][name]) if add_dot else arm_dict['suffixes'][name]) if name in arm_dict['suffixes'] else fallback_value + return arm_dict[name] if name in arm_dict else fallback_value + return _get_processed_arm_endpoint + + def _arm_to_cli_mapper(arm_dict): + get_endpoint = _get_arm_endpoint(arm_dict) + get_suffix = _get_arm_endpoint(arm_dict, is_suffix=True) + + sql_server_hostname = get_suffix('sqlServerHostname', add_dot=True) + get_db_server_endpoint = _get_database_server_endpoint(sql_server_hostname, arm_dict['name']) + return Cloud( arm_dict['name'], endpoints=CloudEndpoints( @@ -192,27 +270,30 @@ active_directory=arm_dict['authentication']['loginEndpoint'], active_directory_resource_id=arm_dict['authentication']['audiences'][0], active_directory_graph_resource_id=arm_dict['graphAudience'], - microsoft_graph_resource_id=_get_microsoft_graph_resource_id(arm_dict['name']), # pylint: disable=line-too-long # change once microsoft_graph_resource_id is fixed in ARM - vm_image_alias_doc=arm_dict['vmImageAliasDoc'], # pylint: disable=line-too-long + microsoft_graph_resource_id=_get_microsoft_graph_resource_id(arm_dict['name']), # change once microsoft_graph_resource_id is fixed in ARM + vm_image_alias_doc=arm_dict['vmImageAliasDoc'], media_resource_id=arm_dict['media'], - ossrdbms_resource_id=_get_ossrdbms_resource_id(arm_dict['name']), # pylint: disable=line-too-long # change once ossrdbms_resource_id is available via ARM - active_directory_data_lake_resource_id=arm_dict['activeDirectoryDataLake'] if 'activeDirectoryDataLake' in arm_dict else None, # pylint: disable=line-too-long - app_insights_resource_id=arm_dict['appInsightsResourceId'] if 'appInsightsResourceId' in arm_dict else None, - log_analytics_resource_id=arm_dict['logAnalyticsResourceId'] if 'logAnalyticsResourceId' in arm_dict else None), # pylint: disable=line-too-long + ossrdbms_resource_id=_get_ossrdbms_resource_id(arm_dict['name']), # change once ossrdbms_resource_id is available via ARM + active_directory_data_lake_resource_id=arm_dict['activeDirectoryDataLake'] if 'activeDirectoryDataLake' in arm_dict else None, + app_insights_resource_id=get_endpoint('appInsightsResourceId', fallback_value=_get_app_insights_resource_id(arm_dict['name'])), + log_analytics_resource_id=get_endpoint('logAnalyticsResourceId', fallback_value=_get_log_analytics_resource_id(arm_dict['name'])), + synapse_analytics_resource_id=get_endpoint('synapseAnalyticsResourceId', fallback_value=_get_synapse_analytics_resource_id(arm_dict['name'])), + app_insights_telemetry_channel_resource_id=get_endpoint('appInsightsTelemetryChannelResourceId', fallback_value=_get_app_insights_telemetry_channel_resource_id(arm_dict['name']))), suffixes=CloudSuffixes( - storage_endpoint=arm_dict['suffixes']['storage'], - storage_sync_endpoint=arm_dict['suffix']['storageSyncEndpointSuffix'] if 'storageSyncEndpointSuffix' in arm_dict['suffixes'] else _get_storage_sync_endpoint(arm_dict['name']), # pylint: disable=line-too-long - keyvault_dns=arm_dict['suffixes']['keyVaultDns'], - sql_server_hostname=arm_dict['suffixes']['sqlServerHostname'], - mysql_server_endpoint=arm_dict['suffixes']['mysqlServerEndpoint'], - postgresql_server_endpoint=arm_dict['suffixes']['postgresqlServerEndpoint'], - mariadb_server_endpoint=arm_dict['suffixes']['mariadbServerEndpoint'], - azure_datalake_store_file_system_endpoint=arm_dict['suffixes']['azureDataLakeStoreFileSystem'] if 'azureDataLakeStoreFileSystem' in arm_dict['suffixes'] else None, # pylint: disable=line-too-long - azure_datalake_analytics_catalog_and_job_endpoint=arm_dict['suffixes']['azureDataLakeAnalyticsCatalogAndJob'] if 'azureDataLakeAnalyticsCatalogAndJob' in arm_dict['suffixes'] else None, # pylint: disable=line-too-long - acr_login_server_endpoint=arm_dict['suffixes']['acrLoginServer'] if 'acrLoginServer' in arm_dict['suffixes'] else None)) # pylint: disable=line-too-long + storage_endpoint=get_suffix('storage'), + storage_sync_endpoint=get_suffix('storageSyncEndpointSuffix', fallback_value=_get_storage_sync_endpoint(arm_dict['name'])), + keyvault_dns=get_suffix('keyVaultDns', add_dot=True), + sql_server_hostname=sql_server_hostname, + mysql_server_endpoint=get_suffix('mysqlServerEndpoint', add_dot=True, fallback_value=get_db_server_endpoint('.mysql')), + postgresql_server_endpoint=get_suffix('postgresqlServerEndpoint', add_dot=True, fallback_value=get_db_server_endpoint('.postgres')), + mariadb_server_endpoint=get_suffix('mariadbServerEndpoint', add_dot=True, fallback_value=get_db_server_endpoint('.mariadb')), + azure_datalake_store_file_system_endpoint=get_suffix('azureDataLakeStoreFileSystem'), + azure_datalake_analytics_catalog_and_job_endpoint=get_suffix('azureDataLakeAnalyticsCatalogAndJob'), + synapse_analytics_endpoint=get_suffix('synapseAnalytics', add_dot=True, fallback_value=_get_synapse_analytics_endpoint(arm_dict['name'])), + acr_login_server_endpoint=get_suffix('acrLoginServer', add_dot=True))) -class Cloud(object): # pylint: disable=too-few-public-methods +class Cloud: # pylint: disable=too-few-public-methods """ Represents an Azure Cloud instance """ def __init__(self, @@ -251,12 +332,13 @@ active_directory_graph_resource_id='https://graph.windows.net/', microsoft_graph_resource_id='https://graph.microsoft.com/', active_directory_data_lake_resource_id='https://datalake.azure.net/', - vm_image_alias_doc='https://raw.githubusercontent.com/Azure/azure-rest-api-specs/master/arm-compute/quickstart-templates/aliases.json', # pylint: disable=line-too-long + vm_image_alias_doc='https://raw.githubusercontent.com/Azure/azure-rest-api-specs/master/arm-compute/quickstart-templates/aliases.json', media_resource_id='https://rest.media.azure.net', ossrdbms_resource_id='https://ossrdbms-aad.database.windows.net', app_insights_resource_id='https://api.applicationinsights.io', log_analytics_resource_id='https://api.loganalytics.io', - app_insights_telemetry_channel_resource_id='https://dc.applicationinsights.azure.com/v2/track'), + app_insights_telemetry_channel_resource_id='https://dc.applicationinsights.azure.com/v2/track', + synapse_analytics_resource_id='https://dev.azuresynapse.net'), suffixes=CloudSuffixes( storage_endpoint='core.windows.net', storage_sync_endpoint='afs.azure.net', @@ -267,7 +349,8 @@ mariadb_server_endpoint='.mariadb.database.azure.com', azure_datalake_store_file_system_endpoint='azuredatalakestore.net', azure_datalake_analytics_catalog_and_job_endpoint='azuredatalakeanalytics.net', - acr_login_server_endpoint='.azurecr.io')) + acr_login_server_endpoint='.azurecr.io', + synapse_analytics_endpoint='.dev.azuresynapse.net')) AZURE_CHINA_CLOUD = Cloud( 'AzureChinaCloud', @@ -281,12 +364,13 @@ active_directory_resource_id='https://management.core.chinacloudapi.cn/', active_directory_graph_resource_id='https://graph.chinacloudapi.cn/', microsoft_graph_resource_id='https://microsoftgraph.chinacloudapi.cn', - vm_image_alias_doc='https://raw.githubusercontent.com/Azure/azure-rest-api-specs/master/arm-compute/quickstart-templates/aliases.json', # pylint: disable=line-too-long + vm_image_alias_doc='https://raw.githubusercontent.com/Azure/azure-rest-api-specs/master/arm-compute/quickstart-templates/aliases.json', media_resource_id='https://rest.media.chinacloudapi.cn', ossrdbms_resource_id='https://ossrdbms-aad.database.chinacloudapi.cn', app_insights_resource_id='https://api.applicationinsights.azure.cn', log_analytics_resource_id='https://api.loganalytics.azure.cn', - app_insights_telemetry_channel_resource_id='https://dc.applicationinsights.azure.cn/v2/track'), + app_insights_telemetry_channel_resource_id='https://dc.applicationinsights.azure.cn/v2/track', + synapse_analytics_resource_id='https://dev.azuresynapse.net'), suffixes=CloudSuffixes( storage_endpoint='core.chinacloudapi.cn', keyvault_dns='.vault.azure.cn', @@ -294,7 +378,8 @@ mysql_server_endpoint='.mysql.database.chinacloudapi.cn', postgresql_server_endpoint='.postgres.database.chinacloudapi.cn', mariadb_server_endpoint='.mariadb.database.chinacloudapi.cn', - acr_login_server_endpoint='.azurecr.cn')) + acr_login_server_endpoint='.azurecr.cn', + synapse_analytics_endpoint='.dev.azuresynapse.azure.cn')) AZURE_US_GOV_CLOUD = Cloud( 'AzureUSGovernment', @@ -308,7 +393,7 @@ active_directory_resource_id='https://management.core.usgovcloudapi.net/', active_directory_graph_resource_id='https://graph.windows.net/', microsoft_graph_resource_id='https://graph.microsoft.us/', - vm_image_alias_doc='https://raw.githubusercontent.com/Azure/azure-rest-api-specs/master/arm-compute/quickstart-templates/aliases.json', # pylint: disable=line-too-long + vm_image_alias_doc='https://raw.githubusercontent.com/Azure/azure-rest-api-specs/master/arm-compute/quickstart-templates/aliases.json', media_resource_id='https://rest.media.usgovcloudapi.net', ossrdbms_resource_id='https://ossrdbms-aad.database.usgovcloudapi.net', app_insights_resource_id='https://api.applicationinsights.us', @@ -336,7 +421,7 @@ active_directory_resource_id='https://management.core.cloudapi.de/', active_directory_graph_resource_id='https://graph.cloudapi.de/', microsoft_graph_resource_id='https://graph.microsoft.de', - vm_image_alias_doc='https://raw.githubusercontent.com/Azure/azure-rest-api-specs/master/arm-compute/quickstart-templates/aliases.json', # pylint: disable=line-too-long + vm_image_alias_doc='https://raw.githubusercontent.com/Azure/azure-rest-api-specs/master/arm-compute/quickstart-templates/aliases.json', media_resource_id='https://rest.media.cloudapi.de', ossrdbms_resource_id='https://ossrdbms-aad.database.cloudapi.de'), suffixes=CloudSuffixes( @@ -354,7 +439,7 @@ arm_cloud_dict = json.loads(urlretrieve(os.getenv('ARM_CLOUD_METADATA_URL'))) cli_cloud_dict = _convert_arm_to_cli(arm_cloud_dict) if 'AzureCloud' in cli_cloud_dict: - cli_cloud_dict['AzureCloud'].endpoints.active_directory = 'https://login.microsoftonline.com' # pylint: disable=line-too-long # change once active_directory is fixed in ARM for the public cloud + cli_cloud_dict['AzureCloud'].endpoints.active_directory = 'https://login.microsoftonline.com' # change once active_directory is fixed in ARM for the public cloud KNOWN_CLOUDS = list(cli_cloud_dict.values()) except Exception as ex: # pylint: disable=broad-except logger.warning('Failed to load cloud metadata from the url specified by ARM_CLOUD_METADATA_URL') diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/azure-cli-core-2.9.1/azure/cli/core/commands/__init__.py new/azure-cli-core-2.10.1/azure/cli/core/commands/__init__.py --- old/azure-cli-core-2.9.1/azure/cli/core/commands/__init__.py 2020-07-16 10:09:55.000000000 +0200 +++ new/azure-cli-core-2.10.1/azure/cli/core/commands/__init__.py 2020-08-06 10:46:59.000000000 +0200 @@ -118,7 +118,7 @@ # pylint: disable=too-many-instance-attributes -class CacheObject(object): +class CacheObject: def path(self, args, kwargs): from azure.cli.core._environment import get_config_dir @@ -349,7 +349,7 @@ operation_group=operation_group) def update_context(self, obj_inst): - class UpdateContext(object): + class UpdateContext: def __init__(self, instance): self.instance = instance @@ -860,7 +860,7 @@ pass -class LongRunningOperation(object): # pylint: disable=too-few-public-methods +class LongRunningOperation: # pylint: disable=too-few-public-methods def __init__(self, cli_ctx, start_msg='', finish_msg='', poller_done_interval_ms=1000.0): self.cli_ctx = cli_ctx @@ -1048,7 +1048,7 @@ return _load_command_loader(loader, args, mod, 'azure.cli.command_modules.') -class ExtensionCommandSource(object): +class ExtensionCommandSource: """ Class for commands contributed by an extension """ def __init__(self, overrides_command=False, extension_name=None, preview=False, experimental=False): @@ -1129,7 +1129,7 @@ # pylint: disable=too-few-public-methods -class CliCommandType(object): +class CliCommandType: def __init__(self, overrides=None, **kwargs): if isinstance(overrides, str): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/azure-cli-core-2.9.1/azure/cli/core/commands/arm.py new/azure-cli-core-2.10.1/azure/cli/core/commands/arm.py --- old/azure-cli-core-2.9.1/azure/cli/core/commands/arm.py 2020-07-16 10:09:55.000000000 +0200 +++ new/azure-cli-core-2.10.1/azure/cli/core/commands/arm.py 2020-08-06 10:46:59.000000000 +0200 @@ -31,7 +31,7 @@ # pylint:disable=too-many-lines -class ArmTemplateBuilder(object): +class ArmTemplateBuilder: def __init__(self): template = OrderedDict() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/azure-cli-core-2.9.1/azure/cli/core/commands/client_factory.py new/azure-cli-core-2.10.1/azure/cli/core/commands/client_factory.py --- old/azure-cli-core-2.9.1/azure/cli/core/commands/client_factory.py 2020-07-16 10:09:55.000000000 +0200 +++ new/azure-cli-core-2.10.1/azure/cli/core/commands/client_factory.py 2020-08-06 10:46:59.000000000 +0200 @@ -174,7 +174,8 @@ def get_data_service_client(cli_ctx, service_type, account_name, account_key, connection_string=None, - sas_token=None, socket_timeout=None, token_credential=None, endpoint_suffix=None): + sas_token=None, socket_timeout=None, token_credential=None, endpoint_suffix=None, + location_mode=None): logger.debug('Getting data service client service_type=%s', service_type.__name__) try: client_kwargs = {'account_name': account_name, @@ -188,6 +189,8 @@ if endpoint_suffix: client_kwargs['endpoint_suffix'] = endpoint_suffix client = service_type(**client_kwargs) + if location_mode: + client.location_mode = location_mode except ValueError as exc: _ERROR_STORAGE_MISSING_INFO = get_sdk(cli_ctx, ResourceType.DATA_STORAGE, 'common._error#_ERROR_STORAGE_MISSING_INFO') diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/azure-cli-core-2.9.1/azure/cli/core/commands/progress.py new/azure-cli-core-2.10.1/azure/cli/core/commands/progress.py --- old/azure-cli-core-2.9.1/azure/cli/core/commands/progress.py 2020-07-16 10:09:55.000000000 +0200 +++ new/azure-cli-core-2.10.1/azure/cli/core/commands/progress.py 2020-08-06 10:46:59.000000000 +0200 @@ -10,7 +10,7 @@ BAR_LEN = 70 -class ProgressViewBase(object): +class ProgressViewBase: """ a view base for progress reporting """ def __init__(self, out): self.out = out @@ -28,7 +28,7 @@ pass # pylint: disable=unnecessary-pass -class ProgressReporter(object): +class ProgressReporter: """ generic progress reporter """ def __init__(self, message='', value=None, total_value=None): self.message = message @@ -45,7 +45,7 @@ total_val = kwargs.get('total_val', self.total_val) value = kwargs.get('value', self.value) if value and total_val: - assert value >= 0 and value <= total_val and total_val >= 0 + assert 0 <= value <= total_val self.closed = value == total_val self.total_val = total_val self.value = value @@ -57,7 +57,7 @@ return {'message': self.message, 'percent': percent} -class ProgressHook(object): +class ProgressHook: """ sends the progress to the view """ def __init__(self): self.reporter = ProgressReporter() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/azure-cli-core-2.9.1/azure/cli/core/decorators.py new/azure-cli-core-2.10.1/azure/cli/core/decorators.py --- old/azure-cli-core-2.9.1/azure/cli/core/decorators.py 2020-07-16 10:09:55.000000000 +0200 +++ new/azure-cli-core-2.10.1/azure/cli/core/decorators.py 2020-08-06 10:46:59.000000000 +0200 @@ -17,7 +17,7 @@ # pylint: disable=too-few-public-methods -class Completer(object): +class Completer: def __init__(self, func): self.func = func diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/azure-cli-core-2.9.1/azure/cli/core/extension/__init__.py new/azure-cli-core-2.10.1/azure/cli/core/extension/__init__.py --- old/azure-cli-core-2.9.1/azure/cli/core/extension/__init__.py 2020-07-16 10:09:55.000000000 +0200 +++ new/azure-cli-core-2.10.1/azure/cli/core/extension/__init__.py 2020-08-06 10:46:59.000000000 +0200 @@ -8,12 +8,12 @@ import traceback import json import re -import pkginfo - -from azure.cli.core._config import GLOBAL_CONFIG_DIR, ENV_VAR_PREFIX from distutils.sysconfig import get_python_lib + +import pkginfo from knack.config import CLIConfig from knack.log import get_logger +from azure.cli.core._config import GLOBAL_CONFIG_DIR, ENV_VAR_PREFIX az_config = CLIConfig(config_dir=GLOBAL_CONFIG_DIR, config_env_var_prefix=ENV_VAR_PREFIX) _CUSTOM_EXT_DIR = az_config.get('extension', 'dir', None) @@ -51,7 +51,7 @@ return "The extension {} is not installed.".format(self.extension_name) -class Extension(object): +class Extension: def __init__(self, name, ext_type, path=None): self.name = name diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/azure-cli-core-2.9.1/azure/cli/core/extension/_homebrew_patch.py new/azure-cli-core-2.10.1/azure/cli/core/extension/_homebrew_patch.py --- old/azure-cli-core-2.9.1/azure/cli/core/extension/_homebrew_patch.py 2020-07-16 10:09:55.000000000 +0200 +++ new/azure-cli-core-2.10.1/azure/cli/core/extension/_homebrew_patch.py 2020-08-06 10:46:59.000000000 +0200 @@ -18,7 +18,7 @@ # A workaround for https://github.com/Azure/azure-cli/issues/4428 -class HomebrewPipPatch(object): # pylint: disable=too-few-public-methods +class HomebrewPipPatch: # pylint: disable=too-few-public-methods CFG_FILE = os.path.expanduser(os.path.join('~', '.pydistutils.cfg')) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/azure-cli-core-2.9.1/azure/cli/core/extension/operations.py new/azure-cli-core-2.10.1/azure/cli/core/extension/operations.py --- old/azure-cli-core-2.9.1/azure/cli/core/extension/operations.py 2020-07-16 10:09:55.000000000 +0200 +++ new/azure-cli-core-2.10.1/azure/cli/core/extension/operations.py 2020-08-06 10:46:59.000000000 +0200 @@ -85,8 +85,8 @@ check_version_compatibility(azext_metadata) -def _add_whl_ext(cmd, source, ext_sha256=None, pip_extra_index_urls=None, pip_proxy=None, system=None): # pylint: disable=too-many-statements - cmd.cli_ctx.get_progress_controller().add(message='Analyzing') +def _add_whl_ext(cli_ctx, source, ext_sha256=None, pip_extra_index_urls=None, pip_proxy=None, system=None): # pylint: disable=too-many-statements + cli_ctx.get_progress_controller().add(message='Analyzing') if not source.endswith('.whl'): raise ValueError('Unknown extension type. Only Python wheels are supported.') url_parse_result = urlparse(source) @@ -108,7 +108,7 @@ logger.debug('Downloading %s to %s', source, ext_file) import requests try: - cmd.cli_ctx.get_progress_controller().add(message='Downloading') + cli_ctx.get_progress_controller().add(message='Downloading') _whl_download_from_url(url_parse_result, ext_file) except (requests.exceptions.ConnectionError, requests.exceptions.HTTPError) as err: raise CLIError('Please ensure you have network connection. Error detail: {}'.format(str(err))) @@ -130,7 +130,7 @@ raise CLIError("The checksum of the extension does not match the expected value. " "Use --debug for more information.") try: - cmd.cli_ctx.get_progress_controller().add(message='Validating') + cli_ctx.get_progress_controller().add(message='Validating') _validate_whl_extension(ext_file) except AssertionError: logger.debug(traceback.format_exc()) @@ -140,7 +140,7 @@ logger.debug('Validation successful on %s', ext_file) # Check for distro consistency check_distro_consistency() - cmd.cli_ctx.get_progress_controller().add(message='Installing') + cli_ctx.get_progress_controller().add(message='Installing') # Install with pip extension_path = build_extension_path(extension_name, system) pip_args = ['install', '--target', extension_path, ext_file] @@ -206,15 +206,15 @@ raise CLIError(min_max_msg_fmt) -def add_extension(cmd, source=None, extension_name=None, index_url=None, yes=None, # pylint: disable=unused-argument +def add_extension(cmd=None, source=None, extension_name=None, index_url=None, yes=None, # pylint: disable=unused-argument pip_extra_index_urls=None, pip_proxy=None, system=None, - version=None): + version=None, cli_ctx=None): ext_sha256 = None version = None if version == 'latest' else version - + cmd_cli_ctx = cli_ctx or cmd.cli_ctx if extension_name: - cmd.cli_ctx.get_progress_controller().add(message='Searching') + cmd_cli_ctx.get_progress_controller().add(message='Searching') ext = None try: ext = get_extension(extension_name) @@ -236,7 +236,7 @@ err = "No matching extensions for '{}'. Use --debug for more information.".format(extension_name) raise CLIError(err) - extension_name = _add_whl_ext(cmd=cmd, source=source, ext_sha256=ext_sha256, + extension_name = _add_whl_ext(cli_ctx=cmd_cli_ctx, source=source, ext_sha256=ext_sha256, pip_extra_index_urls=pip_extra_index_urls, pip_proxy=pip_proxy, system=system) try: ext = get_extension(extension_name) @@ -289,8 +289,9 @@ raise CLIError(e) -def update_extension(cmd, extension_name, index_url=None, pip_extra_index_urls=None, pip_proxy=None): +def update_extension(cmd=None, extension_name=None, index_url=None, pip_extra_index_urls=None, pip_proxy=None, cli_ctx=None): try: + cmd_cli_ctx = cli_ctx or cmd.cli_ctx ext = get_extension(extension_name, ext_type=WheelExtension) cur_version = ext.get_version() try: @@ -307,7 +308,7 @@ shutil.rmtree(extension_path) # Install newer version try: - _add_whl_ext(cmd=cmd, source=download_url, ext_sha256=ext_sha256, + _add_whl_ext(cli_ctx=cmd_cli_ctx, source=download_url, ext_sha256=ext_sha256, pip_extra_index_urls=pip_extra_index_urls, pip_proxy=pip_proxy) logger.debug('Deleting backup of old extension at %s', backup_dir) shutil.rmtree(backup_dir) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/azure-cli-core-2.9.1/azure/cli/core/local_context.py new/azure-cli-core-2.10.1/azure/cli/core/local_context.py --- old/azure-cli-core-2.9.1/azure/cli/core/local_context.py 2020-07-16 10:09:55.000000000 +0200 +++ new/azure-cli-core-2.10.1/azure/cli/core/local_context.py 2020-08-06 10:46:59.000000000 +0200 @@ -33,7 +33,7 @@ return None -class AzCLILocalContext(object): # pylint: disable=too-many-instance-attributes +class AzCLILocalContext: # pylint: disable=too-many-instance-attributes def __init__(self, cli_ctx): self.cli_ctx = cli_ctx @@ -180,7 +180,7 @@ return result -class LocalContextAttribute(object): +class LocalContextAttribute: # pylint: disable=too-few-public-methods def __init__(self, name, actions, scopes=None): """ Local Context Attribute arguments diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/azure-cli-core-2.9.1/azure/cli/core/parser.py new/azure-cli-core-2.10.1/azure/cli/core/parser.py --- old/azure-cli-core-2.9.1/azure/cli/core/parser.py 2020-07-16 10:09:55.000000000 +0200 +++ new/azure-cli-core-2.10.1/azure/cli/core/parser.py 2020-08-06 10:46:59.000000000 +0200 @@ -280,37 +280,167 @@ self._namespace, self._raw_arguments = super().parse_known_args(args=args, namespace=namespace) return self._namespace, self._raw_arguments - def _check_value(self, action, value): + def _get_extension_command_tree(self): + from azure.cli.core._session import EXT_CMD_TREE + import os + VALID_SECOND = 3600 * 24 * 10 + # self.cli_ctx is None when self.prog is beyond 'az', such as 'az iot'. + # use cli_ctx from cli_help which is not lost. + cli_ctx = self.cli_ctx or (self.cli_help.cli_ctx if self.cli_help else None) + if not cli_ctx: + return None + EXT_CMD_TREE.load(os.path.join(cli_ctx.config.config_dir, 'extensionCommandTree.json'), VALID_SECOND) + if not EXT_CMD_TREE.data: + import requests + from azure.cli.core.util import should_disable_connection_verify + try: + response = requests.get( + 'https://azurecliextensionsync.blob.core.windows.net/cmd-index/extensionCommandTree.json', + verify=(not should_disable_connection_verify()), + timeout=300) + except Exception as ex: # pylint: disable=broad-except + logger.info("Request failed for extension command tree: %s", str(ex)) + return None + if response.status_code == 200: + EXT_CMD_TREE.data = response.json() + EXT_CMD_TREE.save_with_retry() + else: + logger.info("Error when retrieving extension command tree. Response code: %s", response.status_code) + return None + return EXT_CMD_TREE + + def _search_in_extension_commands(self, command_str): + """Search the command in an extension commands dict which mimics a prefix tree. + If the value of the dict item is a string, then the key represents the end of a complete command + and the value is the name of the extension that the command belongs to. + An example of the dict read from extensionCommandTree.json: + { + "aks": { + "create": "aks-preview", + "update": "aks-preview", + "app": { + "up": "deploy-to-azure" + }, + "use-dev-spaces": "dev-spaces" + }, + ... + } + """ + + cmd_chain = self._get_extension_command_tree() + for part in command_str.split(): + try: + if isinstance(cmd_chain[part], str): + return cmd_chain[part] + cmd_chain = cmd_chain[part] + except KeyError: + return None + return None + + def _get_extension_use_dynamic_install_config(self): + cli_ctx = self.cli_ctx or (self.cli_help.cli_ctx if self.cli_help else None) + use_dynamic_install = cli_ctx.config.get( + 'extension', 'use_dynamic_install', 'no').lower() if cli_ctx else 'no' + if use_dynamic_install not in ['no', 'yes_prompt', 'yes_without_prompt']: + use_dynamic_install = 'no' + return use_dynamic_install + + def _check_value(self, action, value): # pylint: disable=too-many-statements, too-many-locals # Override to customize the error message when a argument is not among the available choices # converted value must be one of the choices (if specified) - if action.choices is not None and value not in action.choices: + if action.choices is not None and value not in action.choices: # pylint: disable=too-many-nested-blocks + caused_by_extension_not_installed = False if not self.command_source: - # parser has no `command_source`, value is part of command itself - extensions_link = 'https://docs.microsoft.com/en-us/cli/azure/azure-cli-extensions-overview' - error_msg = ("{prog}: '{value}' is not in the '{prog}' command group. See '{prog} --help'. " - "If the command is from an extension, " - "please make sure the corresponding extension is installed. " - "To learn more about extensions, please visit " - "{extensions_link}").format(prog=self.prog, value=value, extensions_link=extensions_link) + candidates = difflib.get_close_matches(value, action.choices, cutoff=0.7) + error_msg = None + # self.cli_ctx is None when self.prog is beyond 'az', such as 'az iot'. + # use cli_ctx from cli_help which is not lost. + cli_ctx = self.cli_ctx or (self.cli_help.cli_ctx if self.cli_help else None) + use_dynamic_install = self._get_extension_use_dynamic_install_config() + if use_dynamic_install != 'no' and not candidates: + # Check if the command is from an extension + from azure.cli.core.util import roughly_parse_command + cmd_list = self.prog.split() + self._raw_arguments + command_str = roughly_parse_command(cmd_list[1:]) + ext_name = self._search_in_extension_commands(command_str) + if ext_name: + caused_by_extension_not_installed = True + telemetry.set_command_details(command_str, + parameters=AzCliCommandInvoker._extract_parameter_names(cmd_list), # pylint: disable=protected-access + extension_name=ext_name) + run_after_extension_installed = cli_ctx.config.getboolean('extension', + 'run_after_dynamic_install', + False) if cli_ctx else False + if use_dynamic_install == 'yes_without_prompt': + logger.warning('The command requires the extension %s. ' + 'It will be installed first.', ext_name) + go_on = True + else: + from knack.prompting import prompt_y_n, NoTTYException + prompt_msg = 'The command requires the extension {}. ' \ + 'Do you want to install it now?'.format(ext_name) + if run_after_extension_installed: + prompt_msg = '{} The command will continue to run after the extension is installed.' \ + .format(prompt_msg) + NO_PROMPT_CONFIG_MSG = "Run 'az config set extension.use_dynamic_install=" \ + "yes_without_prompt' to allow installing extensions without prompt." + try: + go_on = prompt_y_n(prompt_msg, default='y') + if go_on: + logger.warning(NO_PROMPT_CONFIG_MSG) + except NoTTYException: + logger.warning("The command requires the extension %s.\n " + "Unable to prompt for extension install confirmation as no tty " + "available. %s", ext_name, NO_PROMPT_CONFIG_MSG) + go_on = False + if go_on: + from azure.cli.core.extension.operations import add_extension + add_extension(cli_ctx=cli_ctx, extension_name=ext_name) + if run_after_extension_installed: + import subprocess + import platform + exit_code = subprocess.call(cmd_list, shell=platform.system() == 'Windows') + telemetry.set_user_fault("Extension {} dynamically installed and commands will be " + "rerun automatically.".format(ext_name)) + self.exit(exit_code) + else: + error_msg = 'Extension {} installed. Please rerun your command.'.format(ext_name) + else: + error_msg = "The command requires the extension {ext_name}. " \ + "To install, run 'az extension add -n {ext_name}'.".format(ext_name=ext_name) + if not error_msg: + # parser has no `command_source`, value is part of command itself + error_msg = "{prog}: '{value}' is not in the '{prog}' command group. See '{prog} --help'." \ + .format(prog=self.prog, value=value) + if use_dynamic_install.lower() == 'no': + extensions_link = 'https://docs.microsoft.com/en-us/cli/azure/azure-cli-extensions-overview' + error_msg = ("{msg} " + "If the command is from an extension, " + "please make sure the corresponding extension is installed. " + "To learn more about extensions, please visit " + "{extensions_link}").format(msg=error_msg, extensions_link=extensions_link) else: # `command_source` indicates command values have been parsed, value is an argument parameter = action.option_strings[0] if action.option_strings else action.dest error_msg = "{prog}: '{value}' is not a valid value for '{param}'. See '{prog} --help'.".format( prog=self.prog, value=value, param=parameter) + candidates = difflib.get_close_matches(value, action.choices, cutoff=0.7) + telemetry.set_user_fault(error_msg) with CommandLoggerContext(logger): logger.error(error_msg) - candidates = difflib.get_close_matches(value, action.choices, cutoff=0.7) - if candidates: - print_args = { - 's': 's' if len(candidates) > 1 else '', - 'verb': 'are' if len(candidates) > 1 else 'is', - 'value': value - } - self._suggestion_msg.append("\nThe most similar choice{s} to '{value}' {verb}:".format(**print_args)) - self._suggestion_msg.append('\n'.join(['\t' + candidate for candidate in candidates])) - - failure_recovery_recommendations = self._get_failure_recovery_recommendations(action) - self._suggestion_msg.extend(failure_recovery_recommendations) - self._print_suggestion_msg(sys.stderr) + if not caused_by_extension_not_installed: + if candidates: + print_args = { + 's': 's' if len(candidates) > 1 else '', + 'verb': 'are' if len(candidates) > 1 else 'is', + 'value': value + } + self._suggestion_msg.append("\nThe most similar choice{s} to '{value}' {verb}:" + .format(**print_args)) + self._suggestion_msg.append('\n'.join(['\t' + candidate for candidate in candidates])) + + failure_recovery_recommendations = self._get_failure_recovery_recommendations(action) + self._suggestion_msg.extend(failure_recovery_recommendations) + self._print_suggestion_msg(sys.stderr) self.exit(2) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/azure-cli-core-2.9.1/azure/cli/core/profiles/_shared.py new/azure-cli-core-2.10.1/azure/cli/core/profiles/_shared.py --- old/azure-cli-core-2.9.1/azure/cli/core/profiles/_shared.py 2020-07-16 10:09:55.000000000 +0200 +++ new/azure-cli-core-2.10.1/azure/cli/core/profiles/_shared.py 2020-08-06 10:46:59.000000000 +0200 @@ -24,7 +24,7 @@ PROFILE_TYPE = object() -class CustomResourceType(object): # pylint: disable=too-few-public-methods +class CustomResourceType: # pylint: disable=too-few-public-methods def __init__(self, import_prefix, client_name): self.import_prefix = import_prefix self.client_name = client_name @@ -111,7 +111,7 @@ self.client_name = client_name -class SDKProfile(object): # pylint: disable=too-few-public-methods +class SDKProfile: # pylint: disable=too-few-public-methods def __init__(self, default_api_version, profile=None): """Constructor. @@ -136,7 +136,8 @@ 'resource_skus': '2019-04-01', 'disks': '2020-05-01', 'disk_encryption_sets': '2020-05-01', - 'snapshots': '2019-07-01', + 'disk_accesses': '2020-05-01', + 'snapshots': '2020-05-01', 'galleries': '2019-12-01', 'gallery_images': '2019-12-01', 'gallery_image_versions': '2019-12-01', @@ -146,7 +147,7 @@ ResourceType.MGMT_RESOURCE_LINKS: '2016-09-01', ResourceType.MGMT_RESOURCE_LOCKS: '2016-09-01', ResourceType.MGMT_RESOURCE_POLICY: '2019-09-01', - ResourceType.MGMT_RESOURCE_RESOURCES: '2019-07-01', + ResourceType.MGMT_RESOURCE_RESOURCES: '2020-06-01', ResourceType.MGMT_RESOURCE_SUBSCRIPTIONS: '2019-11-01', ResourceType.MGMT_RESOURCE_DEPLOYMENTSCRIPTS: '2019-10-01-preview', ResourceType.MGMT_NETWORK_DNS: '2018-05-01', @@ -281,7 +282,7 @@ } -class _ApiVersions(object): # pylint: disable=too-few-public-methods +class _ApiVersions: # pylint: disable=too-few-public-methods def __init__(self, client_type, sdk_profile, post_process): self._client_type = client_type self._sdk_profile = sdk_profile @@ -342,7 +343,7 @@ @total_ordering -class _SemVerAPIFormat(object): +class _SemVerAPIFormat: """Basic semver x.y.z API format. Supports x, or x.y, or x.y.z """ @@ -366,7 +367,7 @@ @total_ordering # pylint: disable=too-few-public-methods -class _DateAPIFormat(object): +class _DateAPIFormat: """ Class to support comparisons for API versions in YYYY-MM-DD, YYYY-MM-DD-preview, YYYY-MM-DD-profile, YYYY-MM-DD-profile-preview or any string that starts with YYYY-MM-DD format. A special case is made for 'latest'. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/azure-cli-core-2.9.1/azure/cli/core/telemetry.py new/azure-cli-core-2.10.1/azure/cli/core/telemetry.py --- old/azure-cli-core-2.9.1/azure/cli/core/telemetry.py 2020-07-16 10:09:55.000000000 +0200 +++ new/azure-cli-core-2.10.1/azure/cli/core/telemetry.py 2020-08-06 10:46:59.000000000 +0200 @@ -24,7 +24,7 @@ CORRELATION_ID_PROP_NAME = 'Reserved.DataModel.CorrelationId' -class TelemetrySession(object): # pylint: disable=too-many-instance-attributes +class TelemetrySession: # pylint: disable=too-many-instance-attributes def __init__(self, correlation_id=None, application=None): self.start_time = None self.end_time = None @@ -45,6 +45,8 @@ self.extension_management_detail = None self.raw_command = None self.mode = 'default' + self.init_time_elapsed = None + self.invoke_time_elapsed = None # A dictionary with the application insight instrumentation key # as the key and an array of telemetry events as value self.events = defaultdict(list) @@ -164,6 +166,8 @@ lambda: '{},{}'.format(locale.getdefaultlocale()[0], locale.getdefaultlocale()[1])) set_custom_properties(result, 'StartTime', str(self.start_time)) set_custom_properties(result, 'EndTime', str(self.end_time)) + set_custom_properties(result, 'InitTimeElapsed', str(self.init_time_elapsed)) + set_custom_properties(result, 'InvokeTimeElapsed', str(self.invoke_time_elapsed)) set_custom_properties(result, 'OutputType', self.output_type) set_custom_properties(result, 'RawCommand', self.raw_command) set_custom_properties(result, 'Params', ','.join(self.parameters or [])) @@ -227,6 +231,16 @@ _session.start_time = datetime.datetime.utcnow() +@decorators.suppress_all_exceptions() +def set_init_time_elapsed(init_time_elapsed): + _session.init_time_elapsed = init_time_elapsed + + +@decorators.suppress_all_exceptions() +def set_invoke_time_elapsed(invoke_time_elapsed): + _session.invoke_time_elapsed = invoke_time_elapsed + + @_user_agrees_to_telemetry @decorators.suppress_all_exceptions() def flush(): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/azure-cli-core-2.9.1/azure/cli/core/util.py new/azure-cli-core-2.10.1/azure/cli/core/util.py --- old/azure-cli-core-2.9.1/azure/cli/core/util.py 2020-07-16 10:09:55.000000000 +0200 +++ new/azure-cli-core-2.10.1/azure/cli/core/util.py 2020-08-06 10:46:59.000000000 +0200 @@ -12,10 +12,10 @@ import binascii import platform import ssl -import six import re import logging +import six from six.moves.urllib.request import urlopen # pylint: disable=import-error from knack.log import get_logger from knack.util import CLIError, to_snake_case @@ -50,6 +50,15 @@ _VERSION_CHECK_TIME = 'check_time' _VERSION_UPDATE_TIME = 'update_time' +# A list of reserved names that cannot be used as admin username of VM +DISALLOWED_USER_NAMES = [ + "administrator", "admin", "user", "user1", "test", "user2", + "test1", "user3", "admin1", "1", "123", "a", "actuser", "adm", + "admin2", "aspnet", "backup", "console", "guest", + "owner", "root", "server", "sql", "support", "support_388945a0", + "sys", "test2", "test3", "user4", "user5" +] + def handle_exception(ex): # pylint: disable=too-many-return-statements # For error code, follow guidelines at https://docs.python.org/2/library/sys.html#sys.exit, @@ -596,9 +605,13 @@ def get_default_admin_username(): try: - return getpass.getuser() + username = getpass.getuser() except KeyError: - return None + username = None + if username is None or username.lower() in DISALLOWED_USER_NAMES: + logger.warning('Default username %s is a reserved username. Use azureuser instead.', username) + username = 'azureuser' + return username def _find_child(parent, *args, **kwargs): @@ -882,7 +895,7 @@ return response -class ConfiguredDefaultSetter(object): +class ScopedConfig: def __init__(self, cli_config, use_local_config=None): self.use_local_config = use_local_config @@ -899,6 +912,9 @@ setattr(self.cli_config, 'use_local_config', self.original_use_local_config) +ConfiguredDefaultSetter = ScopedConfig + + def _ssl_context(): if sys.version_info < (3, 4) or (in_cloud_console() and platform.system() == 'Windows'): try: @@ -998,3 +1014,25 @@ release_info[k.lower()] = v.strip('"') return release_info.get('name', None), release_info.get('version_id', None) + + +def roughly_parse_command(args): + # Roughly parse the command part: <az vm create> --name vm1 + # Similar to knack.invocation.CommandInvoker._rudimentary_get_command, but we don't need to bother with + # positional args + nouns = [] + for arg in args: + if arg and arg[0] != '-': + nouns.append(arg) + else: + break + return ' '.join(nouns).lower() + + +def is_guid(guid): + import uuid + try: + uuid.UUID(guid) + return True + except ValueError: + return False diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/azure-cli-core-2.9.1/azure_cli_core.egg-info/PKG-INFO new/azure-cli-core-2.10.1/azure_cli_core.egg-info/PKG-INFO --- old/azure-cli-core-2.9.1/azure_cli_core.egg-info/PKG-INFO 2020-07-16 10:10:07.000000000 +0200 +++ new/azure-cli-core-2.10.1/azure_cli_core.egg-info/PKG-INFO 2020-08-06 10:47:07.000000000 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: azure-cli-core -Version: 2.9.1 +Version: 2.10.1 Summary: Microsoft Azure Command-Line Tools Core Module Home-page: https://github.com/Azure/azure-cli Author: Microsoft Corporation @@ -15,6 +15,14 @@ Release History =============== + 2.10.1 + ++++++ + * Minor fixes + + 2.10.0 + ++++++ + * Minor fixes + 2.9.1 ++++++ * Minor fixes diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/azure-cli-core-2.9.1/azure_cli_core.egg-info/requires.txt new/azure-cli-core-2.10.1/azure_cli_core.egg-info/requires.txt --- old/azure-cli-core-2.9.1/azure_cli_core.egg-info/requires.txt 2020-07-16 10:10:07.000000000 +0200 +++ new/azure-cli-core-2.10.1/azure_cli_core.egg-info/requires.txt 2020-08-06 10:47:07.000000000 +0200 @@ -4,7 +4,7 @@ colorama~=0.4.1 humanfriendly<9.0,>=4.7 jmespath -knack==0.7.1 +knack==0.7.2 msal~=1.0.0 msal-extensions~=0.1.3 msrest>=0.4.4 @@ -15,7 +15,7 @@ requests~=2.22 six~=1.12 pkginfo>=1.5.0.1 -azure-mgmt-resource==10.0.0 +azure-mgmt-resource==10.1.0 azure-mgmt-core==1.0.0 [:python_version<"3.0"] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/azure-cli-core-2.9.1/setup.py new/azure-cli-core-2.10.1/setup.py --- old/azure-cli-core-2.9.1/setup.py 2020-07-16 10:09:55.000000000 +0200 +++ new/azure-cli-core-2.10.1/setup.py 2020-08-06 10:46:59.000000000 +0200 @@ -17,7 +17,7 @@ logger.warn("Wheel is not available, disabling bdist_wheel hook") cmdclass = {} -VERSION = "2.9.1" +VERSION = "2.10.1" # If we have source, validate that our version numbers match # This should prevent uploading releases with mismatched versions. try: @@ -56,7 +56,7 @@ 'colorama~=0.4.1', 'humanfriendly>=4.7,<9.0', 'jmespath', - 'knack==0.7.1', + 'knack==0.7.2', 'msal~=1.0.0', 'msal-extensions~=0.1.3', 'msrest>=0.4.4', @@ -67,7 +67,7 @@ 'requests~=2.22', 'six~=1.12', 'pkginfo>=1.5.0.1', - 'azure-mgmt-resource==10.0.0', + 'azure-mgmt-resource==10.1.0', 'azure-mgmt-core==1.0.0' ]