Hello community, here is the log from the commit of package azure-cli-core for openSUSE:Factory checked in at 2020-05-08 23:06:24 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/azure-cli-core (Old) and /work/SRC/openSUSE:Factory/.azure-cli-core.new.2738 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "azure-cli-core" Fri May 8 23:06:24 2020 rev:14 rq:801209 version:2.5.1 Changes: -------- --- /work/SRC/openSUSE:Factory/azure-cli-core/azure-cli-core.changes 2020-05-03 22:45:59.471002910 +0200 +++ /work/SRC/openSUSE:Factory/.azure-cli-core.new.2738/azure-cli-core.changes 2020-05-08 23:06:30.922004879 +0200 @@ -1,0 +2,9 @@ +Thu May 7 07:46:57 UTC 2020 - John Paul Adrian Glaubitz <[email protected]> + +- New upstream release + + Version 2.5.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.3.1.tar.gz New: ---- azure-cli-core-2.5.1.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ azure-cli-core.spec ++++++ --- /var/tmp/diff_new_pack.clQA0g/_old 2020-05-08 23:06:31.466005991 +0200 +++ /var/tmp/diff_new_pack.clQA0g/_new 2020-05-08 23:06:31.466005991 +0200 @@ -17,7 +17,7 @@ Name: azure-cli-core -Version: 2.3.1 +Version: 2.5.1 Release: 0 Summary: Microsoft Azure CLI Core Module License: MIT @@ -40,21 +40,25 @@ Requires: python3-adal >= 1.2 Requires: python3-argcomplete < 2.0 Requires: python3-argcomplete >= 1.8 -Requires: python3-azure-mgmt-resource < 9.0.0 -Requires: python3-azure-mgmt-resource >= 8.0.1 +Requires: python3-azure-mgmt-core < 2.0.0 +Requires: python3-azure-mgmt-core >= 1.0.0 +Requires: python3-azure-mgmt-resource < 10.0.0 +Requires: python3-azure-mgmt-resource >= 9.0.0 Requires: python3-azure-nspkg >= 3.0.0 Requires: python3-colorama >= 0.3.9 Requires: python3-humanfriendly < 9.0 Requires: python3-humanfriendly >= 4.7 Requires: python3-jmespath Requires: python3-knack < 1.0.0 -Requires: python3-knack >= 0.7.0~rc1 +Requires: python3-knack >= 0.7.0~rc4 +Requires: python3-msal < 2.0.0 +Requires: python3-msal >= 1.0.0 Requires: python3-msrest >= 0.4.4 -Requires: python3-msrestazure >= 0.6.2 +Requires: python3-msrestazure >= 0.6.3 Requires: python3-paramiko < 3.0.0 -Requires: python3-paramiko >= 2.0. +Requires: python3-paramiko >= 2.0.8 Requires: python3-pip -Requires: python3-pkginfo +Requires: python3-pkginfo >= 1.5.0.1 Requires: python3-pyOpenSSL >= 17.1.0 Requires: python3-requests < 3.0.0 Requires: python3-requests >= 2.20 ++++++ azure-cli-core-2.3.1.tar.gz -> azure-cli-core-2.5.1.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/azure-cli-core-2.3.1/HISTORY.rst new/azure-cli-core-2.5.1/HISTORY.rst --- old/azure-cli-core-2.3.1/HISTORY.rst 2020-04-01 09:16:17.000000000 +0200 +++ new/azure-cli-core-2.5.1/HISTORY.rst 2020-04-30 08:18:16.000000000 +0200 @@ -3,6 +3,27 @@ Release History =============== +2.5.1 +++++++ +* Minor fixes + +2.5.0 +++++++ +* Fix logging file fd leaking (#13102) +* Fix core tests failed under certain profiles (#13136) +* Use pkginfo to query metadata for Python Package (#12782) + +2.4.0 +++++++ +* Upgrade msrestazure to 0.6.3 (#12839) +* Output more error details of exceptions if possible (#12945) +* Support track1 and track2 mgmt SDK side by side (#12952) +* Fix generic remove property NoneType exception (#12950) +* `az --version`: Show command instruction and detailed instruction link when updates available (#12981) +* Fix bug in core test test_parser that will affect exception stack while existing if argument parse raise Exception +* Make CLI respect file logging configuration option: enable_log_file and AZURE_LOGGING_ENABLE_LOG_FILE env variable +* Fix the name of extension of dev type is None if it has incompatibility problem with core + 2.3.1 ++++++ * No changes diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/azure-cli-core-2.3.1/PKG-INFO new/azure-cli-core-2.5.1/PKG-INFO --- old/azure-cli-core-2.3.1/PKG-INFO 2020-04-01 09:16:27.000000000 +0200 +++ new/azure-cli-core-2.5.1/PKG-INFO 2020-04-30 08:18:30.000000000 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: azure-cli-core -Version: 2.3.1 +Version: 2.5.1 Summary: Microsoft Azure Command-Line Tools Core Module Home-page: https://github.com/Azure/azure-cli Author: Microsoft Corporation @@ -15,6 +15,27 @@ Release History =============== + 2.5.1 + ++++++ + * Minor fixes + + 2.5.0 + ++++++ + * Fix logging file fd leaking (#13102) + * Fix core tests failed under certain profiles (#13136) + * Use pkginfo to query metadata for Python Package (#12782) + + 2.4.0 + ++++++ + * Upgrade msrestazure to 0.6.3 (#12839) + * Output more error details of exceptions if possible (#12945) + * Support track1 and track2 mgmt SDK side by side (#12952) + * Fix generic remove property NoneType exception (#12950) + * `az --version`: Show command instruction and detailed instruction link when updates available (#12981) + * Fix bug in core test test_parser that will affect exception stack while existing if argument parse raise Exception + * Make CLI respect file logging configuration option: enable_log_file and AZURE_LOGGING_ENABLE_LOG_FILE env variable + * Fix the name of extension of dev type is None if it has incompatibility problem with core + 2.3.1 ++++++ * No changes diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/azure-cli-core-2.3.1/azure/cli/core/__init__.py new/azure-cli-core-2.5.1/azure/cli/core/__init__.py --- old/azure-cli-core-2.3.1/azure/cli/core/__init__.py 2020-04-01 09:16:17.000000000 +0200 +++ new/azure-cli-core-2.5.1/azure/cli/core/__init__.py 2020-04-30 08:18:16.000000000 +0200 @@ -2,9 +2,11 @@ # 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 + from __future__ import print_function -__version__ = "2.3.1" +__version__ = "2.5.1" import os import sys @@ -21,7 +23,7 @@ from knack.experimental import ExperimentalItem from knack.util import CLIError from knack.arguments import ArgumentsContext, CaseInsensitiveList # pylint: disable=unused-import -from .local_context import AzCLILocalContext, STORE +from .local_context import AzCLILocalContext, SET logger = get_logger(__name__) @@ -29,6 +31,16 @@ 'content_version', 'kwargs', 'client', 'no_wait'] EVENT_FAILED_EXTENSION_LOAD = 'MainLoader.OnFailedExtensionLoad' +_PACKAGE_UPGRADE_INSTRUCTIONS = {"YUM": ("sudo yum update -y azure-cli", "https://aka.ms/doc/UpdateAzureCliYum"), + "ZYPPER": ("sudo zypper refresh && sudo zypper update -y azure-cli", "https://aka.ms/doc/UpdateAzureCliZypper"), + "DEB": ("sudo apt-get update && sudo apt-get install --only-upgrade -y azure-cli", "https://aka.ms/doc/UpdateAzureCliApt"), + "HOMEBREW": ("brew update && brew upgrade azure-cli", "https://aka.ms/doc/UpdateAzureCliHomebrew"), + "PIP": ("curl -L https://aka.ms/InstallAzureCli | bash", "https://aka.ms/doc/UpdateAzureCliLinux"), + "MSI": ("https://aka.ms/installazurecliwindows", "https://aka.ms/doc/UpdateAzureCliMsi"), + "DOCKER": ("docker pull mcr.microsoft.com/azure-cli", "https://aka.ms/doc/UpdateAzureCliDocker")} + +_GENERAL_UPGRADE_INSTRUCTION = 'Instructions can be found at https://aka.ms/doc/InstallAzureCli' + class AzCli(CLI): @@ -89,20 +101,50 @@ def show_version(self): from azure.cli.core.util import get_az_version_string - from azure.cli.core.commands.constants import SURVEY_PROMPT, SURVEY_PROMPT_COLOR + from azure.cli.core.commands.constants import (SURVEY_PROMPT, SURVEY_PROMPT_COLOR, + UX_SURVEY_PROMPT, UX_SURVEY_PROMPT_COLOR) ver_string, updates_available = get_az_version_string() print(ver_string) if updates_available == -1: logger.warning('Unable to check if your CLI is up-to-date. Check your internet connection.') elif updates_available: - logger.warning('You have %i updates available. Consider updating your CLI installation. ' - 'Instructions can be found at https://docs.microsoft.com/en-us/cli/azure/install-azure-cli', - updates_available) + warning_msg = 'You have %i updates available. Consider updating your CLI installation' + from azure.cli.core._environment import _ENV_AZ_INSTALLER + installer = os.getenv(_ENV_AZ_INSTALLER) + instruction_msg = '' + if installer in _PACKAGE_UPGRADE_INSTRUCTIONS: + if installer == 'RPM': + from azure.cli.core.util import get_linux_distro + distname, _ = get_linux_distro() + if not distname: + instruction_msg = '. {}'.format(_GENERAL_UPGRADE_INSTRUCTION) + else: + distname = distname.lower().strip() + if any(x in distname for x in ['centos', 'rhel', 'red hat', 'fedora']): + installer = 'YUM' + elif any(x in distname for x in ['opensuse', 'suse', 'sles']): + installer = 'ZYPPER' + else: + instruction_msg = '. {}'.format(_GENERAL_UPGRADE_INSTRUCTION) + elif installer == 'PIP': + import platform + system = platform.system() + alternative_command = " or '{}' if you used our script for installation. Detailed instructions can be found at {}".format(_PACKAGE_UPGRADE_INSTRUCTIONS[installer][0], _PACKAGE_UPGRADE_INSTRUCTIONS[installer][1]) if system != 'Windows' else '' + instruction_msg = " with 'pip install --upgrade azure-cli'{}".format(alternative_command) + if instruction_msg: + warning_msg += instruction_msg + else: + warning_msg += " with '{}'. Detailed instructions can be found at {}".format(_PACKAGE_UPGRADE_INSTRUCTIONS[installer][0], _PACKAGE_UPGRADE_INSTRUCTIONS[installer][1]) + else: + warning_msg += '. {}'.format(_GENERAL_UPGRADE_INSTRUCTION) + logger.warning(warning_msg, updates_available) else: print('Your CLI is up-to-date.') - - print('\n' + (SURVEY_PROMPT_COLOR if self.enable_color else SURVEY_PROMPT)) + show_link = self.config.getboolean('output', 'show_survey_link', True) + if show_link: + print('\n' + (SURVEY_PROMPT_COLOR if self.enable_color else SURVEY_PROMPT)) + print(UX_SURVEY_PROMPT_COLOR if self.enable_color else UX_SURVEY_PROMPT) def exception_handler(self, ex): # pylint: disable=no-self-use from azure.cli.core.util import handle_exception @@ -111,7 +153,7 @@ def save_local_context(self, parsed_args, argument_definitions, specified_arguments): """ Local Context Attribute arguments - Save argument value to local context if it is defined as STORE and user specify a value for it. + Save argument value to local context if it is defined as SET and user specify a value for it. :param parsed_args: Parsed args which return by AzCliCommandParser parse_args :type parsed_args: Namespace @@ -122,12 +164,12 @@ """ for argument_name in specified_arguments: - # make sure STORE is defined + # make sure SET is defined if argument_name not in argument_definitions: continue argtype = argument_definitions[argument_name].type lca = argtype.settings.get('local_context_attribute', None) - if not lca or not lca.actions or STORE not in lca.actions: + if not lca or not lca.actions or SET not in lca.actions: continue # get the specified value value = getattr(parsed_args, argument_name) @@ -228,6 +270,7 @@ continue ext_name = ext.name ext_dir = ext.path or get_extension_path(ext_name) + logger.debug("Extensions directory: '%s'", ext_dir) sys.path.append(ext_dir) try: ext_mod = get_extension_modname(ext_name, ext_dir=ext_dir) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/azure-cli-core-2.3.1/azure/cli/core/_debug.py new/azure-cli-core-2.5.1/azure/cli/core/_debug.py --- old/azure-cli-core-2.3.1/azure/cli/core/_debug.py 2020-04-01 09:16:17.000000000 +0200 +++ new/azure-cli-core-2.5.1/azure/cli/core/_debug.py 2020-04-30 08:18:16.000000000 +0200 @@ -31,6 +31,22 @@ return client +def change_ssl_cert_verification_track2(): + client_kwargs = {} + if should_disable_connection_verify(): + logger.warning("Connection verification disabled by environment variable %s", + DISABLE_VERIFY_VARIABLE_NAME) + os.environ[ADAL_PYTHON_SSL_NO_VERIFY] = '1' + client_kwargs['connection_verify'] = False + elif REQUESTS_CA_BUNDLE in os.environ: + ca_bundle_file = os.environ[REQUESTS_CA_BUNDLE] + if not os.path.isfile(ca_bundle_file): + raise CLIError('REQUESTS_CA_BUNDLE environment variable is specified with an invalid file path') + logger.debug("Using CA bundle file at '%s'.", ca_bundle_file) + client_kwargs['connection_verify'] = ca_bundle_file + return client_kwargs + + def allow_debug_adal_connection(): if should_disable_connection_verify(): os.environ[ADAL_PYTHON_SSL_NO_VERIFY] = '1' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/azure-cli-core-2.3.1/azure/cli/core/_environment.py new/azure-cli-core-2.5.1/azure/cli/core/_environment.py --- old/azure-cli-core-2.3.1/azure/cli/core/_environment.py 2020-04-01 09:16:17.000000000 +0200 +++ new/azure-cli-core-2.5.1/azure/cli/core/_environment.py 2020-04-30 08:18:16.000000000 +0200 @@ -4,6 +4,9 @@ # -------------------------------------------------------------------------------------------- +_ENV_AZ_INSTALLER = 'AZ_INSTALLER' + + def get_config_dir(): import os return os.getenv('AZURE_CONFIG_DIR', None) or os.path.expanduser(os.path.join('~', '.azure')) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/azure-cli-core-2.3.1/azure/cli/core/_help.py new/azure-cli-core-2.5.1/azure/cli/core/_help.py --- old/azure-cli-core-2.3.1/azure/cli/core/_help.py 2020-04-01 09:16:17.000000000 +0200 +++ new/azure-cli-core-2.5.1/azure/cli/core/_help.py 2020-04-30 08:18:16.000000000 +0200 @@ -7,7 +7,8 @@ import argparse from azure.cli.core.commands import ExtensionCommandSource -from azure.cli.core.commands.constants import SURVEY_PROMPT, SURVEY_PROMPT_COLOR +from azure.cli.core.commands.constants import (SURVEY_PROMPT, SURVEY_PROMPT_COLOR, + UX_SURVEY_PROMPT, UX_SURVEY_PROMPT_COLOR) from knack.help import (HelpFile as KnackHelpFile, CommandHelpFile as KnackCommandHelpFile, GroupHelpFile as KnackGroupHelpFile, ArgumentGroupRegistry as KnackArgumentGroupRegistry, @@ -172,8 +173,11 @@ else: AzCliHelp.update_examples(help_file) self._print_detailed_help(cli_name, help_file) - - print(SURVEY_PROMPT_COLOR if self.cli_ctx.enable_color else SURVEY_PROMPT) + show_link = self.cli_ctx.config.getboolean('output', 'show_survey_link', True) + if show_link: + print(SURVEY_PROMPT_COLOR if self.cli_ctx.enable_color else SURVEY_PROMPT) + if not nouns: + print(UX_SURVEY_PROMPT_COLOR if self.cli_ctx.enable_color else UX_SURVEY_PROMPT) def _register_help_loaders(self): import azure.cli.core._help_loaders as help_loaders diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/azure-cli-core-2.3.1/azure/cli/core/_msal.py new/azure-cli-core-2.5.1/azure/cli/core/_msal.py --- old/azure-cli-core-2.3.1/azure/cli/core/_msal.py 1970-01-01 01:00:00.000000000 +0100 +++ new/azure-cli-core-2.5.1/azure/cli/core/_msal.py 2020-04-30 08:18:16.000000000 +0200 @@ -0,0 +1,34 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- +from knack.util import CLIError +from msal import ClientApplication + + +class AdalRefreshTokenBasedClientApplication(ClientApplication): + """ + This is added only for vmssh feature. + It is a temporary solution and will deprecate after MSAL adopted completely. + """ + def _acquire_token_silent_by_finding_rt_belongs_to_me_or_my_family( + self, authority, scopes, account, **kwargs): + # pylint: disable=line-too-long + return self._acquire_token_silent_by_finding_specific_refresh_token( + authority, scopes, None, **kwargs) + + def _acquire_token_silent_by_finding_specific_refresh_token( + self, authority, scopes, query, + rt_remover=None, break_condition=lambda response: False, **kwargs): + refresh_token = kwargs.get('refresh_token', None) + client = self._build_client(self.client_credential, authority) + if 'refresh_token' in kwargs: + kwargs.pop('refresh_token') + if 'force_refresh' in kwargs: + kwargs.pop('force_refresh') + if 'correlation_id' in kwargs: + kwargs.pop('correlation_id') + response = client.obtain_token_by_refresh_token(refresh_token, scope=scopes, **kwargs) + if "error" in response: + raise CLIError(response["error"]) + return response diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/azure-cli-core-2.3.1/azure/cli/core/_profile.py new/azure-cli-core-2.5.1/azure/cli/core/_profile.py --- old/azure-cli-core-2.3.1/azure/cli/core/_profile.py 2020-04-01 09:16:17.000000000 +0200 +++ new/azure-cli-core-2.5.1/azure/cli/core/_profile.py 2020-04-30 08:18:16.000000000 +0200 @@ -593,6 +593,18 @@ str(account[_SUBSCRIPTION_ID]), str(account[_TENANT_ID])) + def get_msal_token(self, scopes, data): + """ + This is added only for vmssh feature. + It is a temporary solution and will deprecate after MSAL adopted completely. + """ + account = self.get_subscription() + username = account[_USER_ENTITY][_USER_NAME] + tenant = account[_TENANT_ID] or 'common' + _, refresh_token, _, _ = self.get_refresh_token() + certificate = self._creds_cache.retrieve_msal_token(tenant, scopes, data, refresh_token) + return username, certificate + def get_refresh_token(self, resource=None, subscription=None): account = self.get_subscription(subscription) @@ -1008,6 +1020,19 @@ self.persist_cached_creds() return (token_entry[_TOKEN_ENTRY_TOKEN_TYPE], token_entry[_ACCESS_TOKEN], token_entry) + def retrieve_msal_token(self, tenant, scopes, data, refresh_token): + """ + This is added only for vmssh feature. + It is a temporary solution and will deprecate after MSAL adopted completely. + """ + from azure.cli.core._msal import AdalRefreshTokenBasedClientApplication + tenant = tenant or 'organizations' + authority = self._ctx.cloud.endpoints.active_directory + '/' + tenant + app = AdalRefreshTokenBasedClientApplication(_CLIENT_ID, authority=authority) + result = app.acquire_token_silent(scopes, None, data=data, refresh_token=refresh_token) + + return result["access_token"] + def retrieve_token_for_service_principal(self, sp_id, resource, tenant, use_cert_sn_issuer=False): self.load_adal_token_cache() matched = [x for x in self._service_principal_creds if sp_id == x[_SERVICE_PRINCIPAL_ID]] @@ -1202,6 +1227,11 @@ break except socket.error as ex: logger.warning("Port '%s' is taken with error '%s'. Trying with the next one", port, ex) + except UnicodeDecodeError: + logger.warning("Please make sure there is no international (Unicode) character in the computer name " + r"or C:\Windows\System32\drivers\etc\hosts file's 127.0.0.1 entries. " + "For more details, please see https://github.com/Azure/azure-cli/issues/12957") + break if reply_url is None: logger.warning("Error: can't reserve a port for authentication reply url") diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/azure-cli-core-2.3.1/azure/cli/core/adal_authentication.py new/azure-cli-core-2.5.1/azure/cli/core/adal_authentication.py --- old/azure-cli-core-2.3.1/azure/cli/core/adal_authentication.py 2020-04-01 09:16:17.000000000 +0200 +++ new/azure-cli-core-2.5.1/azure/cli/core/adal_authentication.py 2020-04-30 08:18:16.000000000 +0200 @@ -3,11 +3,12 @@ # Licensed under the MIT License. See License.txt in the project root for license information. # -------------------------------------------------------------------------------------------- +import time import requests import adal from msrest.authentication import Authentication - +from azure.core.credentials import AccessToken from azure.cli.core.util import in_cloud_console from knack.util import CLIError @@ -19,11 +20,10 @@ self._token_retriever = token_retriever self._external_tenant_token_retriever = external_tenant_token_retriever - def signed_session(self, session=None): # pylint: disable=arguments-differ - session = session or super(AdalAuthentication, self).signed_session() + def _get_token(self): external_tenant_tokens = None try: - scheme, token, _ = self._token_retriever() + scheme, token, full_token = self._token_retriever() if self._external_tenant_token_retriever: external_tenant_tokens = self._external_tenant_token_retriever() except CLIError as err: @@ -55,6 +55,20 @@ except requests.exceptions.ConnectionError as err: raise CLIError('Please ensure you have network connection. Error detail: ' + str(err)) + return scheme, token, full_token, external_tenant_tokens + + # This method is exposed for Azure Core. + def get_token(self, *scopes, **kwargs): # pylint:disable=unused-argument + _, token, full_token, _ = self._get_token() + + return AccessToken(token, int(full_token['expiresIn'] + time.time())) + + # This method is exposed for msrest. + def signed_session(self, session=None): # pylint: disable=arguments-differ + session = session or super(AdalAuthentication, self).signed_session() + + scheme, token, _, external_tenant_tokens = self._get_token() + header = "{} {}".format(scheme, token) session.headers['Authorization'] = header if external_tenant_tokens: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/azure-cli-core-2.3.1/azure/cli/core/azlogging.py new/azure-cli-core-2.5.1/azure/cli/core/azlogging.py --- old/azure-cli-core-2.3.1/azure/cli/core/azlogging.py 2020-04-01 09:16:17.000000000 +0200 +++ new/azure-cli-core-2.5.1/azure/cli/core/azlogging.py 2020-04-30 08:18:16.000000000 +0200 @@ -29,6 +29,7 @@ from azure.cli.core.commands.events import EVENT_INVOKER_PRE_CMD_TBL_TRUNCATE +from knack.events import EVENT_CLI_POST_EXECUTE from knack.log import CLILogging, get_logger from knack.util import ensure_dir @@ -46,6 +47,7 @@ self.command_logger_handler = None self.command_metadata_logger = None self.cli_ctx.register_event(EVENT_INVOKER_PRE_CMD_TBL_TRUNCATE, AzCliLogging.init_command_file_logging) + self.cli_ctx.register_event(EVENT_CLI_POST_EXECUTE, AzCliLogging.deinit_cmd_metadata_logging) def get_command_log_dir(self): return self.command_log_dir @@ -77,10 +79,15 @@ cmd_logger = logging.getLogger(AzCliLogging._COMMAND_METADATA_LOGGER) - self._init_command_logfile_handlers(cmd_logger, args) # pylint: disable=protected-access - get_logger(__name__).debug("metadata file logging enabled - writing logs to '%s'.", self.command_log_dir) + # overwrite CLILogging._is_file_log_enabled() from knack + self.file_log_enabled = cli_ctx.config.getboolean('logging', 'enable_log_file', fallback=True) - _delete_old_logs(self.command_log_dir) + if self.file_log_enabled: + self._init_command_logfile_handlers(cmd_logger, args) # pylint: disable=protected-access + get_logger(__name__).debug("metadata file logging enabled - writing logs to '%s'.", + self.command_log_dir) + + _delete_old_logs(self.command_log_dir) def _init_command_logfile_handlers(self, command_metadata_logger, args): @@ -168,13 +175,18 @@ self.command_metadata_logger.info("extension name: %s", extension_name) self.command_metadata_logger.info("extension version: %s", extension_version) - def end_cmd_metadata_logging(self, exit_code): + @staticmethod + def deinit_cmd_metadata_logging(cli_ctx): + cli_ctx.logging.end_cmd_metadata_logging(cli_ctx.result.exit_code if cli_ctx.result else 128) + + def end_cmd_metadata_logging(self, exit_code): # leave it non '-' prefix to not to break user if self.command_metadata_logger: self.command_metadata_logger.info("exit code: %s", exit_code) # We have finished metadata logging, remove handler and set command_metadata_handler to None. # crucial to remove handler as in python logger objects are shared which can affect testing of this logger # we do not want duplicate handlers to be added in subsequent calls of _init_command_logfile_handlers + self.command_logger_handler.close() self.command_metadata_logger.removeHandler(self.command_logger_handler) self.command_metadata_logger = None diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/azure-cli-core-2.3.1/azure/cli/core/commands/__init__.py new/azure-cli-core-2.5.1/azure/cli/core/commands/__init__.py --- old/azure-cli-core-2.3.1/azure/cli/core/commands/__init__.py 2020-04-01 09:16:17.000000000 +0200 +++ new/azure-cli-core-2.5.1/azure/cli/core/commands/__init__.py 2020-04-30 08:18:16.000000000 +0200 @@ -27,7 +27,7 @@ AzArgumentContext, patch_arg_make_required, patch_arg_make_optional) from azure.cli.core.extension import get_extension from azure.cli.core.util import get_command_type_kwarg, read_file_content, get_arg_list, poller_classes -from azure.cli.core.local_context import USE +from azure.cli.core.local_context import GET import azure.cli.core.telemetry as telemetry @@ -295,7 +295,7 @@ def _resolve_default_value_from_local_context(self, arg, overrides): if self.cli_ctx.local_context.is_on(): lca = overrides.settings.get('local_context_attribute', None) - if not lca or not lca.actions or USE not in lca.actions: + if not lca or not lca.actions or GET not in lca.actions: return if lca.name: local_context = self.cli_ctx.local_context @@ -421,15 +421,21 @@ return _get_operation() -def cached_put(cmd_obj, operation, parameters, *args, **kwargs): - +def cached_put(cmd_obj, operation, parameters, *args, setter_arg_name='parameters', **kwargs): + """ + setter_arg_name: The name of the argument in the setter which corresponds to the object being updated. + In track2, unknown kwargs will raise, so we should not pass 'parameters" for operation when the name of the argument + in the setter which corresponds to the object being updated is not 'parameters'. + """ def _put_operation(): result = None if args: extended_args = args + (parameters,) result = operation(*extended_args) elif kwargs is not None: - result = operation(parameters=parameters, **kwargs) + kwargs[setter_arg_name] = parameters + result = operation(**kwargs) + del kwargs[setter_arg_name] return result # early out if the command does not use the cache @@ -1067,7 +1073,8 @@ and not isinstance(obj, list) \ and not isinstance(obj, dict): from msrest.paging import Paged - return isinstance(obj, Paged) + from azure.core.paging import ItemPaged as AzureCorePaged + return isinstance(obj, (AzureCorePaged, Paged)) return False diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/azure-cli-core-2.3.1/azure/cli/core/commands/arm.py new/azure-cli-core-2.5.1/azure/cli/core/commands/arm.py --- old/azure-cli-core-2.3.1/azure/cli/core/commands/arm.py 2020-04-01 09:16:17.000000000 +0200 +++ new/azure-cli-core-2.5.1/azure/cli/core/commands/arm.py 2020-04-30 08:18:16.000000000 +0200 @@ -566,7 +566,7 @@ if setter_arg_name == 'parameters': result = cached_put(cmd, setter, **setterargs) else: - result = cached_put(cmd, setter, setterargs[setter_arg_name], **setterargs) + result = cached_put(cmd, setter, setterargs[setter_arg_name], setter_arg_name=setter_arg_name, **setterargs) if supports_no_wait and no_wait_enabled: return None @@ -925,6 +925,8 @@ except IndexError: raise CLIError('index {} doesn\'t exist on {}' .format(list_index, list_attribute_path[-1])) + except AttributeError: + raise CLIError('{} doesn\'t exist'.format(list_attribute_path[-1])) def throw_and_show_options(instance, part, path): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/azure-cli-core-2.3.1/azure/cli/core/commands/client_factory.py new/azure-cli-core-2.5.1/azure/cli/core/commands/client_factory.py --- old/azure-cli-core-2.3.1/azure/cli/core/commands/client_factory.py 2020-04-01 09:16:17.000000000 +0200 +++ new/azure-cli-core-2.5.1/azure/cli/core/commands/client_factory.py 2020-04-30 08:18:16.000000000 +0200 @@ -7,7 +7,7 @@ from azure.cli.core.extension import EXTENSIONS_MOD_PREFIX from azure.cli.core.profiles._shared import get_client_class, SDKProfile from azure.cli.core.profiles import ResourceType, CustomResourceType, get_api_version, get_sdk -from azure.cli.core.util import get_az_user_agent +from azure.cli.core.util import get_az_user_agent, is_track2 from knack.log import get_logger from knack.util import CLIError @@ -102,6 +102,34 @@ client.config.generate_client_request_id = 'x-ms-client-request-id' not in cli_ctx.data['headers'] +def configure_common_settings_track2(cli_ctx): + client_kwargs = {} + + client_kwargs.update(_debug.change_ssl_cert_verification_track2()) + + client_kwargs['logging_enable'] = True + client_kwargs['user_agent'] = get_az_user_agent() + + try: + command_ext_name = cli_ctx.data['command_extension_name'] + if command_ext_name: + client_kwargs['user_agent'] += "CliExtension/{}".format(command_ext_name) + except KeyError: + pass + + headers = dict(cli_ctx.data['headers']) + command_name_suffix = ';completer-request' if cli_ctx.data['completer_active'] else '' + headers['CommandName'] = "{}{}".format(cli_ctx.data['command'], command_name_suffix) + if cli_ctx.data.get('safe_params'): + headers['ParameterSetName'] = ' '.join(cli_ctx.data['safe_params']) + client_kwargs['headers'] = headers + + if 'x-ms-client-request-id' in cli_ctx.data['headers']: + client_kwargs['request_id'] = cli_ctx.data['headers']['x-ms-client-request-id'] + + return client_kwargs + + def _get_mgmt_service_client(cli_ctx, client_type, subscription_bound=True, @@ -131,12 +159,16 @@ if kwargs: client_kwargs.update(kwargs) + if is_track2(client_type): + client_kwargs.update(configure_common_settings_track2(cli_ctx)) + if subscription_bound: client = client_type(cred, subscription_id, **client_kwargs) else: client = client_type(cred, **client_kwargs) - configure_common_settings(cli_ctx, client) + if not is_track2(client): + configure_common_settings(cli_ctx, client) return client, subscription_id diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/azure-cli-core-2.3.1/azure/cli/core/commands/constants.py new/azure-cli-core-2.5.1/azure/cli/core/commands/constants.py --- old/azure-cli-core-2.3.1/azure/cli/core/commands/constants.py 2020-04-01 09:16:17.000000000 +0200 +++ new/azure-cli-core-2.5.1/azure/cli/core/commands/constants.py 2020-04-30 08:18:16.000000000 +0200 @@ -35,3 +35,7 @@ SURVEY_PROMPT = 'Please let us know how we are doing: https://aka.ms/clihats' SURVEY_PROMPT_COLOR = Fore.YELLOW + Style.BRIGHT + 'Please let us know how we are doing: ' + Fore.BLUE + \ 'https://aka.ms/clihats' + Style.RESET_ALL +UX_SURVEY_PROMPT = 'and let us know if you\'re interested in trying out our newest features: https://aka.ms/CLIUXstudy' +UX_SURVEY_PROMPT_COLOR = Fore.YELLOW + Style.BRIGHT + \ + 'and let us know if you\'re interested in trying out our newest features: ' \ + + Fore.BLUE + 'https://aka.ms/CLIUXstudy' + Style.RESET_ALL diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/azure-cli-core-2.3.1/azure/cli/core/commands/parameters.py new/azure-cli-core-2.5.1/azure/cli/core/commands/parameters.py --- old/azure-cli-core-2.3.1/azure/cli/core/commands/parameters.py 2020-04-01 09:16:17.000000000 +0200 +++ new/azure-cli-core-2.5.1/azure/cli/core/commands/parameters.py 2020-04-30 08:18:16.000000000 +0200 @@ -12,7 +12,7 @@ from azure.cli.core.commands.validators import validate_tag, validate_tags, generate_deployment_name from azure.cli.core.decorators import Completer from azure.cli.core.profiles import ResourceType -from azure.cli.core.local_context import LocalContextAttribute, STORE, USE, ALL +from azure.cli.core.local_context import LocalContextAttribute, SET, GET, ALL from knack.arguments import ( CLIArgumentType, CaseInsensitiveList, ignore_type, ArgumentsContext) @@ -242,7 +242,7 @@ configured_default='group', local_context_attribute=LocalContextAttribute( name='resource_group_name', - actions=[STORE, USE], + actions=[SET, GET], scopes=[ALL] )) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/azure-cli-core-2.3.1/azure/cli/core/extension/__init__.py new/azure-cli-core-2.5.1/azure/cli/core/extension/__init__.py --- old/azure-cli-core-2.3.1/azure/cli/core/extension/__init__.py 2020-04-01 09:16:17.000000000 +0200 +++ new/azure-cli-core-2.5.1/azure/cli/core/extension/__init__.py 2020-04-30 08:18:16.000000000 +0200 @@ -138,24 +138,19 @@ return None metadata = {} ext_dir = self.path or get_extension_path(self.name) - info_dirs = glob(os.path.join(ext_dir, '*.*-info')) + info_dirs = glob(os.path.join(ext_dir, self.name.replace('-', '_') + '-' + '*.dist-info')) + azext_metadata = WheelExtension.get_azext_metadata(ext_dir) if azext_metadata: metadata.update(azext_metadata) for dist_info_dirname in info_dirs: - parsed_dist_info_dir = WHEEL_INFO_RE(dist_info_dirname) - if not parsed_dist_info_dir: - continue - - parsed_dist_info_dir = parsed_dist_info_dir.groupdict().get('name') - if os.path.split(parsed_dist_info_dir)[-1] == self.name.replace('-', '_'): - whl_metadata_filepath = os.path.join(dist_info_dirname, WHL_METADATA_FILENAME) - if os.path.isfile(whl_metadata_filepath): - with open(whl_metadata_filepath) as f: - metadata.update(json.loads(f.read())) - elif os.path.isfile(os.path.join(dist_info_dirname, 'PKG-INFO')): - metadata.update(pkginfo.Develop(dist_info_dirname).__dict__) + try: + ext_whl_metadata = pkginfo.Wheel(dist_info_dirname) + if self.name == ext_whl_metadata.name: + metadata.update(vars(ext_whl_metadata)) + except ValueError: + logger.warning('extension % contains invalid metadata for Python Package', self.name) return metadata @@ -182,13 +177,13 @@ if os.path.isdir(EXTENSIONS_DIR): for ext_name in os.listdir(EXTENSIONS_DIR): ext_path = os.path.join(EXTENSIONS_DIR, ext_name) - pattern = os.path.join(ext_path, '*.*-info') + pattern = os.path.join(ext_path, '*.dist-info') if os.path.isdir(ext_path) and glob(pattern): exts.append(WheelExtension(ext_name, ext_path)) if os.path.isdir(EXTENSIONS_SYS_DIR): for ext_name in os.listdir(EXTENSIONS_SYS_DIR): ext_path = os.path.join(EXTENSIONS_SYS_DIR, ext_name) - pattern = os.path.join(ext_path, '*.*-info') + pattern = os.path.join(ext_path, '*.dist-info') if os.path.isdir(ext_path) and glob(pattern): ext = WheelExtension(ext_name, ext_path) if ext not in exts: @@ -214,27 +209,14 @@ if azext_metadata: metadata.update(azext_metadata) - def _apply_egginfo_metadata(filename): - # extract version info for dev extensions from PKG-INFO - if os.path.isfile(filename): - with open(filename) as f: - for line in f.readlines(): - try: - key, val = line.split(':', 1) - key = key.lower() - if key == 'version': - metadata[key] = '{}'.format(val.strip()) - elif key == 'name': - # temporary fix extension name is None - # until wheel upgrade and metadata structure in azure-cli-extensions has upgraded too. - # https://github.com/Azure/azure-cli/pull/12583 - metadata[key] = val.strip() - except ValueError: - continue - for egg_info_dirname in egg_info_dirs: - egg_metadata_filepath = os.path.join(ext_dir, egg_info_dirname, EGG_INFO_METADATA_FILE_NAME) - _apply_egginfo_metadata(egg_metadata_filepath) + egg_metadata_path = os.path.join(ext_dir, egg_info_dirname, ) + try: + ext_whl_metadata = pkginfo.Develop(egg_metadata_path) + if self.name == ext_whl_metadata.name: + metadata.update(vars(ext_whl_metadata)) + except ValueError: + logger.warning('extension % contains invalid metadata for Python Package', self.name) return metadata @@ -307,7 +289,6 @@ def get_extensions(ext_type=None): - logger.debug("Extensions directory: '%s'", EXTENSIONS_DIR) extensions = [] if not ext_type: ext_type = EXTENSION_TYPES diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/azure-cli-core-2.3.1/azure/cli/core/local_context.py new/azure-cli-core-2.5.1/azure/cli/core/local_context.py --- old/azure-cli-core-2.3.1/azure/cli/core/local_context.py 2020-04-01 09:16:17.000000000 +0200 +++ new/azure-cli-core-2.5.1/azure/cli/core/local_context.py 2020-04-30 08:18:16.000000000 +0200 @@ -12,8 +12,8 @@ from knack.config import _ConfigFile from knack.util import ensure_dir, CLIError -STORE = 'STORE' # action for a parameter in local context, STORE means its value will be saved to local context -USE = 'USE' # action for a parameter in local context, USE means will read value from local context for this parameter +SET = 'SET' # action for a parameter in local context, SET means its value will be saved to local context +GET = 'GET' # action for a parameter in local context, GET means will read value from local context for this parameter ALL = 'ALL' # effective level of local context, ALL means all commands can share this parameter value logger = get_logger(__name__) @@ -104,9 +104,9 @@ def __init__(self, name, actions, scopes=None): """ Local Context Attribute arguments - :param name: Argument name in local context. Make sure it is consistent for STORE and USE. + :param name: Argument name in local context. Make sure it is consistent for SET and GET. :type name: str - :param actions: Which action should be taken for local context. Allowed values: STORE, USE + :param actions: Which action should be taken for local context. Allowed values: SET, GET :type actions: list :param scopes: The effective commands or command groups of this argument when saved to local context. :type scopes: list @@ -119,6 +119,6 @@ if isinstance(scopes, str): scopes = [scopes] - if scopes is None and STORE in actions: + if scopes is None and SET in actions: scopes = [ALL] self.scopes = scopes diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/azure-cli-core-2.3.1/azure/cli/core/parser.py new/azure-cli-core-2.5.1/azure/cli/core/parser.py --- old/azure-cli-core-2.3.1/azure/cli/core/parser.py 2020-04-01 09:16:17.000000000 +0200 +++ new/azure-cli-core-2.5.1/azure/cli/core/parser.py 2020-04-30 08:18:16.000000000 +0200 @@ -15,6 +15,7 @@ from azure.cli.core.azlogging import CommandLoggerContext from azure.cli.core.extension import get_extension from azure.cli.core.commands import ExtensionCommandSource +from azure.cli.core.commands import AzCliCommandInvoker from azure.cli.core.commands.events import EVENT_INVOKER_ON_TAB_COMPLETION from knack.log import get_logger @@ -52,8 +53,17 @@ class AzCliCommandParser(CLICommandParser): """ArgumentParser implementation specialized for the Azure CLI utility.""" + @staticmethod + def recommendation_provider(version, command, parameters, extension): # pylint: disable=unused-argument + logger.debug("recommendation_provider: version: %s, command: %s, parameters: %s, extension: %s", + version, command, parameters, extension) + return [] + def __init__(self, cli_ctx=None, cli_help=None, **kwargs): self.command_source = kwargs.pop('_command_source', None) + self._raw_arguments = None + self._namespace = None + self._suggestion_msg = [] self.subparser_map = {} self.specified_arguments = [] super(AzCliCommandParser, self).__init__(cli_ctx, cli_help=cli_help, **kwargs) @@ -141,6 +151,9 @@ with CommandLoggerContext(logger): logger.error('%(prog)s: error: %(message)s', args) self.print_usage(sys.stderr) + failure_recovery_recommendations = self._get_failure_recovery_recommendations() + self._suggestion_msg.extend(failure_recovery_recommendations) + self._print_suggestion_msg(sys.stderr) self.exit(2) def format_help(self): @@ -165,12 +178,96 @@ argcomplete.autocomplete(self, validator=lambda c, p: c.lower().startswith(p.lower()), default_completer=lambda _: ()) + def _get_failure_recovery_arguments(self, action=None): + # Strip the leading "az " and any extraneous whitespace. + command = self.prog[3:].strip() + parameters = [] + parameter_set = set() + raw_arguments = None + extension = None + + # Extract only parameter names to ensure GPDR compliance + def extract_safe_params(parameters): + return AzCliCommandInvoker._extract_parameter_names(parameters) # pylint: disable=protected-access + + # Check for extension name attribute + def has_extension_name(command_source): + is_extension_command_source = isinstance(command_source, ExtensionCommandSource) + has_extension_name = False + + if is_extension_command_source: + has_extension_name = hasattr(command_source, 'extension_name') + + return is_extension_command_source and has_extension_name + + # If the arguments have been processed into a namespace... + if self._namespace: + # Select the parsed command. + if hasattr(self._namespace, 'command'): + command = self._namespace.command + # Parse parameter names from user input. + if self._raw_arguments: + raw_arguments = self._raw_arguments + parameters = extract_safe_params(self._raw_arguments) + + for parameter in parameters: + parameter_set.add(parameter) + + # If we can retrieve the extension from the current parser's command source... + if has_extension_name(self.command_source): + extension = self.command_source.extension_name + # Otherwise, the command may have not been in a command group. The command source will not be + # set in this case. + elif action and action.dest == '_subcommand': + # Get all parsers in the set of possible actions. + parsers = list(action.choices.values()) + parser = parsers[0] if parsers else None + # If the first parser comes from an extension... + if parser and has_extension_name(parser.command_source): + # We're looking for a subcommand under an extension command group. Set the + # extension to reflect this. + extension = parser.command_source.extension_name + # Extend the command if the first raw argument is not a parameter. + if raw_arguments and raw_arguments[0] not in parameter_set: + command = '{cmd} {arg}'.format(cmd=command, arg=raw_arguments[0]) + # Otherwise, only set the extension if every subparser comes from an extension. This occurs + # when an unrecognized argument is passed to a command from an extension. + elif isinstance(self.subparser_map, dict): + for _, subparser in self.subparser_map.items(): + if isinstance(subparser.command_source, ExtensionCommandSource): + extension = subparser.command_source.extension_name + else: + extension = None + break + + return command, parameters, extension + + def _get_failure_recovery_recommendations(self, action=None, **kwargs): + # Gets failure recovery recommendations + from azure.cli.core import __version__ as core_version + failure_recovery_arguments = self._get_failure_recovery_arguments(action) + recommendations = AzCliCommandParser.recommendation_provider(core_version, + *failure_recovery_arguments, + **kwargs) + return recommendations + def _get_values(self, action, arg_strings): value = super(AzCliCommandParser, self)._get_values(action, arg_strings) if action.dest and isinstance(action.dest, str) and not action.dest.startswith('_'): self.specified_arguments.append(action.dest) return value + def _print_suggestion_msg(self, file=None): + if self._suggestion_msg: + print('\n'.join(self._suggestion_msg), file=file) + + def parse_known_args(self, args=None, namespace=None): + # retrieve the raw argument list in case parsing known arguments fails. + self._raw_arguments = args + # if parsing known arguments succeeds, get the command namespace and the argument list + 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): # 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) @@ -198,8 +295,10 @@ 'verb': 'are' if len(candidates) > 1 else 'is', 'value': value } - suggestion_msg = "\nThe most similar choice{s} to '{value}' {verb}:\n".format(**print_args) - suggestion_msg += '\n'.join(['\t' + candidate for candidate in candidates]) - print(suggestion_msg, file=sys.stderr) + 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.3.1/azure/cli/core/profiles/_shared.py new/azure-cli-core-2.5.1/azure/cli/core/profiles/_shared.py --- old/azure-cli-core-2.3.1/azure/cli/core/profiles/_shared.py 2020-04-01 09:16:17.000000000 +0200 +++ new/azure-cli-core-2.5.1/azure/cli/core/profiles/_shared.py 2020-04-30 08:18:16.000000000 +0200 @@ -47,15 +47,18 @@ MGMT_RESOURCE_POLICY = ('azure.mgmt.resource.policy', 'PolicyClient') MGMT_RESOURCE_RESOURCES = ('azure.mgmt.resource.resources', 'ResourceManagementClient') MGMT_RESOURCE_SUBSCRIPTIONS = ('azure.mgmt.resource.subscriptions', 'SubscriptionClient') + MGMT_RESOURCE_DEPLOYMENTSCRIPTS = ('azure.mgmt.resource.deploymentscripts', 'DeploymentScriptsClient') MGMT_MONITOR = ('azure.mgmt.monitor', 'MonitorManagementClient') DATA_KEYVAULT = ('azure.keyvault', 'KeyVaultClient') MGMT_EVENTHUB = ('azure.mgmt.eventhub', 'EventHubManagementClient') MGMT_APPSERVICE = ('azure.mgmt.web', 'WebSiteManagementClient') MGMT_IOTHUB = ('azure.mgmt.iothub', 'IotHubClient') + MGMT_ARO = ('azure.mgmt.redhatopenshift', 'AzureRedHatOpenShiftClient') # the "None" below will stay till a command module fills in the type so "get_mgmt_service_client" # can be provided with "ResourceType.XXX" to initialize the client object. This usually happens # when related commands start to support Multi-API DATA_STORAGE = ('azure.multiapi.storage', None) + DATA_STORAGE_BLOB = ('azure.multiapi.storagev2.blob', None) DATA_COSMOS_TABLE = ('azure.multiapi.cosmosdb', None) MGMT_CONTAINERSERVICE = ('azure.mgmt.containerservice', None) MGMT_ADVISOR = ('azure.mgmt.advisor', None) @@ -124,12 +127,14 @@ AZURE_API_PROFILES = { 'latest': { ResourceType.MGMT_STORAGE: '2019-06-01', - ResourceType.MGMT_NETWORK: '2019-11-01', + ResourceType.MGMT_NETWORK: '2020-03-01', ResourceType.MGMT_COMPUTE: SDKProfile('2019-07-01', { 'resource_skus': '2019-04-01', 'disks': '2019-11-01', 'snapshots': '2019-07-01', - 'galleries': '2019-07-01', + 'galleries': '2019-12-01', + 'gallery_images': '2019-12-01', + 'gallery_image_versions': '2019-12-01', 'virtual_machine_scale_sets': '2019-12-01' }), ResourceType.MGMT_RESOURCE_FEATURES: '2015-12-01', @@ -138,6 +143,7 @@ ResourceType.MGMT_RESOURCE_POLICY: '2019-09-01', ResourceType.MGMT_RESOURCE_RESOURCES: '2019-07-01', ResourceType.MGMT_RESOURCE_SUBSCRIPTIONS: '2019-06-01', + ResourceType.MGMT_RESOURCE_DEPLOYMENTSCRIPTS: '2019-10-01-preview', ResourceType.MGMT_NETWORK_DNS: '2018-05-01', ResourceType.MGMT_KEYVAULT: '2019-09-01', ResourceType.MGMT_AUTHORIZATION: SDKProfile('2018-09-01-preview', { @@ -148,6 +154,7 @@ ResourceType.MGMT_CONTAINERREGISTRY: '2019-12-01-preview', ResourceType.DATA_KEYVAULT: '7.0', ResourceType.DATA_STORAGE: '2018-11-09', + ResourceType.DATA_STORAGE_BLOB: '2019-07-07', ResourceType.DATA_COSMOS_TABLE: '2017-04-17', ResourceType.MGMT_EVENTHUB: '2017-04-01', ResourceType.MGMT_MONITOR: SDKProfile('2019-06-01', { @@ -174,10 +181,16 @@ 'scheduled_query_rules': '2018-04-16', 'service_diagnostic_settings': '2016-09-01', 'tenant_activity_logs': '2015-04-01', - 'vm_insights': '2018-11-27-preview' + 'vm_insights': '2018-11-27-preview', + 'private_link_resources': '2019-10-17-preview', + 'private_link_scoped_resources': '2019-10-17-preview', + 'private_link_scope_operation_status': '2019-10-17-preview', + 'private_link_scopes': '2019-10-17-preview', + 'private_endpoint_connections': '2019-10-17-preview' }), ResourceType.MGMT_APPSERVICE: '2019-08-01', - ResourceType.MGMT_IOTHUB: '2019-07-01-preview' + ResourceType.MGMT_IOTHUB: '2019-07-01-preview', + ResourceType.MGMT_ARO: '2020-04-30' }, '2019-03-01-hybrid': { ResourceType.MGMT_STORAGE: '2017-10-01', diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/azure-cli-core-2.3.1/azure/cli/core/telemetry.py new/azure-cli-core-2.5.1/azure/cli/core/telemetry.py --- old/azure-cli-core-2.3.1/azure/cli/core/telemetry.py 2020-04-01 09:16:17.000000000 +0200 +++ new/azure-cli-core-2.5.1/azure/cli/core/telemetry.py 2020-04-30 08:18:16.000000000 +0200 @@ -173,7 +173,8 @@ set_custom_properties(result, 'Feedback', self.feedback) set_custom_properties(result, 'ExtensionManagementDetail', self.extension_management_detail) set_custom_properties(result, 'Mode', self.mode) - set_custom_properties(result, 'Installer', os.getenv('AZ_INSTALLER')) + from azure.cli.core._environment import _ENV_AZ_INSTALLER + set_custom_properties(result, 'Installer', os.getenv(_ENV_AZ_INSTALLER)) return result diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/azure-cli-core-2.3.1/azure/cli/core/util.py new/azure-cli-core-2.5.1/azure/cli/core/util.py --- old/azure-cli-core-2.3.1/azure/cli/core/util.py 2020-04-01 09:16:17.000000000 +0200 +++ new/azure-cli-core-2.5.1/azure/cli/core/util.py 2020-04-30 08:18:16.000000000 +0200 @@ -16,9 +16,12 @@ import logging from six.moves.urllib.request import urlopen # pylint: disable=import-error + +from azure.common import AzureException +from azure.core.exceptions import AzureError from knack.log import get_logger from knack.util import CLIError, to_snake_case -from azure.common import AzureException +from inspect import getfullargspec as get_arg_spec logger = get_logger(__name__) @@ -51,8 +54,15 @@ logger.error("To learn more about --query, please visit: " "https://docs.microsoft.com/cli/azure/query-azure-cli?view=azure-cli-latest") return 1 - if isinstance(ex, (CLIError, CloudError, AzureException)): + if isinstance(ex, (CLIError, CloudError, AzureException, AzureError)): logger.error(ex.args[0]) + try: + for detail in ex.args[0].error.details: + logger.error(detail) + except (AttributeError, TypeError): + pass + except: # pylint: disable=bare-except + pass return ex.args[1] if len(ex.args) >= 2 else 1 if isinstance(ex, ValidationError): logger.error('validation error: %s', ex) @@ -200,9 +210,6 @@ _print() _print('Legal docs and information: aka.ms/AzureCliLegal') _print() - if sys.version.startswith('2.7'): - _print("* DEPRECATION: Python 2.7 will reach the end of its life on January 1st, 2020. \nA future version of Azure CLI will drop support for Python 2.7.") - _print() version_string = output.getvalue() # if unable to query PyPI, use sentinel value to flag that @@ -358,6 +365,14 @@ return sig.args +def is_track2(client_class): + """ IS this client a autorestv3/track2 one?. + Could be refined later if necessary. + """ + args = get_arg_spec(client_class.__init__).args + return "credential" in args + + DISABLE_VERIFY_VARIABLE_NAME = "AZURE_CLI_DISABLE_CONNECTION_VERIFICATION" @@ -369,7 +384,8 @@ def poller_classes(): from msrestazure.azure_operation import AzureOperationPoller from msrest.polling.poller import LROPoller - return (AzureOperationPoller, LROPoller) + from azure.core.polling import LROPoller as AzureCoreLROPoller + return (AzureOperationPoller, LROPoller, AzureCoreLROPoller) def augment_no_wait_handler_args(no_wait_enabled, handler, handler_args): @@ -387,7 +403,7 @@ def sdk_no_wait(no_wait, func, *args, **kwargs): if no_wait: - kwargs.update({'raw': True, 'polling': False}) + kwargs.update({'polling': False}) return func(*args, **kwargs) @@ -537,7 +553,7 @@ return success -def send_raw_request(cli_ctx, method, uri, headers=None, uri_parameters=None, # pylint: disable=too-many-locals,too-many-branches,too-many-statements +def send_raw_request(cli_ctx, method, url, headers=None, uri_parameters=None, # pylint: disable=too-many-locals,too-many-branches,too-many-statements body=None, skip_authorization_header=False, resource=None, output_file=None, generated_client_request_id_name='x-ms-client-request-id'): import uuid @@ -600,26 +616,26 @@ result[key] = value uri_parameters = result or None - # If uri is an ARM resource ID, like /subscriptions/xxx/resourcegroups/xxx?api-version=2019-07-01, + # If url is an ARM resource ID, like /subscriptions/xxx/resourcegroups/xxx?api-version=2019-07-01, # default to Azure Resource Manager. # https://management.azure.com/ + subscriptions/xxx/resourcegroups/xxx?api-version=2019-07-01 - if '://' not in uri: - uri = cli_ctx.cloud.endpoints.resource_manager + uri.lstrip('/') + if '://' not in url: + url = cli_ctx.cloud.endpoints.resource_manager + url.lstrip('/') # Replace common tokens with real values. It is for smooth experience if users copy and paste the url from # Azure Rest API doc from azure.cli.core._profile import Profile profile = Profile() - if '{subscriptionId}' in uri: - uri = uri.replace('{subscriptionId}', profile.get_subscription_id()) + if '{subscriptionId}' in url: + url = url.replace('{subscriptionId}', profile.get_subscription_id()) - if not skip_authorization_header and uri.lower().startswith('https://'): + if not skip_authorization_header and url.lower().startswith('https://'): if not resource: endpoints = cli_ctx.cloud.endpoints - # If uri starts with ARM endpoint, like https://management.azure.com/, + # If url starts with ARM endpoint, like https://management.azure.com/, # use active_directory_resource_id for resource. # This follows the same behavior as azure.cli.core.commands.client_factory._get_mgmt_service_client - if uri.lower().startswith(endpoints.resource_manager.rstrip('/')): + if url.lower().startswith(endpoints.resource_manager.rstrip('/')): resource = endpoints.active_directory_resource_id else: from azure.cli.core.cloud import CloudEndpointNotSetException @@ -628,7 +644,7 @@ value = getattr(endpoints, p) except CloudEndpointNotSetException: continue - if isinstance(value, six.string_types) and uri.lower().startswith(value.lower()): + if isinstance(value, six.string_types) and url.lower().startswith(value.lower()): resource = value break if resource: @@ -643,7 +659,7 @@ try: # https://requests.readthedocs.io/en/latest/user/advanced/#prepared-requests s = Session() - req = Request(method=method, url=uri, headers=headers, params=uri_parameters, data=body) + req = Request(method=method, url=url, headers=headers, params=uri_parameters, data=body) prepped = s.prepare_request(req) # Merge environment settings into session @@ -805,8 +821,8 @@ agents = ["AZURECLI/{}".format(core_version)] - _ENV_AZ_INSTALLER = 'AZ_INSTALLER' import os + from azure.cli.core._environment import _ENV_AZ_INSTALLER if _ENV_AZ_INSTALLER in os.environ: agents.append('({})'.format(os.environ[_ENV_AZ_INSTALLER])) @@ -828,3 +844,22 @@ except NoTTYException: raise CLIError( 'Unable to prompt for confirmation as no tty available. Use --yes.') + + +def get_linux_distro(): + if platform.system() != 'Linux': + return None, None + + try: + with open('/etc/os-release') as lines: + tokens = [line.strip() for line in lines] + except Exception: # pylint: disable=broad-except + return None, None + + release_info = {} + for token in tokens: + if '=' in token: + k, v = token.split('=', 1) + release_info[k.lower()] = v.strip('"') + + return release_info.get('name', None), release_info.get('version_id', None) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/azure-cli-core-2.3.1/azure_cli_core.egg-info/PKG-INFO new/azure-cli-core-2.5.1/azure_cli_core.egg-info/PKG-INFO --- old/azure-cli-core-2.3.1/azure_cli_core.egg-info/PKG-INFO 2020-04-01 09:16:27.000000000 +0200 +++ new/azure-cli-core-2.5.1/azure_cli_core.egg-info/PKG-INFO 2020-04-30 08:18:30.000000000 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: azure-cli-core -Version: 2.3.1 +Version: 2.5.1 Summary: Microsoft Azure Command-Line Tools Core Module Home-page: https://github.com/Azure/azure-cli Author: Microsoft Corporation @@ -15,6 +15,27 @@ Release History =============== + 2.5.1 + ++++++ + * Minor fixes + + 2.5.0 + ++++++ + * Fix logging file fd leaking (#13102) + * Fix core tests failed under certain profiles (#13136) + * Use pkginfo to query metadata for Python Package (#12782) + + 2.4.0 + ++++++ + * Upgrade msrestazure to 0.6.3 (#12839) + * Output more error details of exceptions if possible (#12945) + * Support track1 and track2 mgmt SDK side by side (#12952) + * Fix generic remove property NoneType exception (#12950) + * `az --version`: Show command instruction and detailed instruction link when updates available (#12981) + * Fix bug in core test test_parser that will affect exception stack while existing if argument parse raise Exception + * Make CLI respect file logging configuration option: enable_log_file and AZURE_LOGGING_ENABLE_LOG_FILE env variable + * Fix the name of extension of dev type is None if it has incompatibility problem with core + 2.3.1 ++++++ * No changes diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/azure-cli-core-2.3.1/azure_cli_core.egg-info/SOURCES.txt new/azure-cli-core-2.5.1/azure_cli_core.egg-info/SOURCES.txt --- old/azure-cli-core-2.3.1/azure_cli_core.egg-info/SOURCES.txt 2020-04-01 09:16:27.000000000 +0200 +++ new/azure-cli-core-2.5.1/azure_cli_core.egg-info/SOURCES.txt 2020-04-30 08:18:30.000000000 +0200 @@ -13,6 +13,7 @@ azure/cli/core/_environment.py azure/cli/core/_help.py azure/cli/core/_help_loaders.py +azure/cli/core/_msal.py azure/cli/core/_output.py azure/cli/core/_profile.py azure/cli/core/_session.py diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/azure-cli-core-2.3.1/azure_cli_core.egg-info/requires.txt new/azure-cli-core-2.5.1/azure_cli_core.egg-info/requires.txt --- old/azure-cli-core-2.3.1/azure_cli_core.egg-info/requires.txt 2020-04-01 09:16:27.000000000 +0200 +++ new/azure-cli-core-2.5.1/azure_cli_core.egg-info/requires.txt 2020-04-30 08:18:30.000000000 +0200 @@ -4,18 +4,19 @@ colorama>=0.3.9 humanfriendly<9.0,>=4.7 jmespath -knack==0.7.0rc1 +knack==0.7.0rc4 +msal~=1.0.0 +msal-extensions~=0.1.3 msrest>=0.4.4 -msrestazure>=0.6.2 +msrestazure>=0.6.3 paramiko<3.0.0,>=2.0.8 -pkginfo PyJWT pyopenssl>=17.1.0 -pyyaml~=5.2 requests~=2.20 six~=1.12 -wheel==0.30.0 -azure-mgmt-resource==8.0.1 +pkginfo>=1.5.0.1 +azure-mgmt-resource==9.0.0 +azure-mgmt-core==1.0.0 [:python_version<"3.0"] futures @@ -25,14 +26,8 @@ ndg-httpsclient pyasn1 -[:python_version<'3.0'] -antlr4-python2-runtime - [:python_version<'3.4'] enum34 -[:python_version>='3.0'] -antlr4-python3-runtime - [test] mock diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/azure-cli-core-2.3.1/setup.py new/azure-cli-core-2.5.1/setup.py --- old/azure-cli-core-2.3.1/setup.py 2020-04-01 09:16:17.000000000 +0200 +++ new/azure-cli-core-2.5.1/setup.py 2020-04-30 08:18:16.000000000 +0200 @@ -17,7 +17,7 @@ logger.warn("Wheel is not available, disabling bdist_wheel hook") cmdclass = {} -VERSION = "2.3.1" +VERSION = "2.5.1" # If we have source, validate that our version numbers match # This should prevent uploading releases with mismatched versions. try: @@ -56,18 +56,19 @@ 'colorama>=0.3.9', 'humanfriendly>=4.7,<9.0', 'jmespath', - 'knack==0.7.0rc1', + 'knack==0.7.0rc4', + 'msal~=1.0.0', + 'msal-extensions~=0.1.3', 'msrest>=0.4.4', - 'msrestazure>=0.6.2', + 'msrestazure>=0.6.3', 'paramiko>=2.0.8,<3.0.0', - 'pkginfo', 'PyJWT', 'pyopenssl>=17.1.0', # https://github.com/pyca/pyopenssl/pull/612 - 'pyyaml~=5.2', 'requests~=2.20', 'six~=1.12', - 'wheel==0.30.0', - 'azure-mgmt-resource==8.0.1', + 'pkginfo>=1.5.0.1', + 'azure-mgmt-resource==9.0.0', + 'azure-mgmt-core==1.0.0' ] TESTS_REQUIRE = [ @@ -103,8 +104,6 @@ ":python_version<'3.4'": ['enum34'], ":python_version<'2.7.9'": ['pyopenssl', 'ndg-httpsclient', 'pyasn1'], ':python_version<"3.0"': ['futures'], - ":python_version<'3.0'": ['antlr4-python2-runtime'], - ":python_version>='3.0'": ['antlr4-python3-runtime'], "test": TESTS_REQUIRE, }, tests_require=TESTS_REQUIRE,
