diff --git a/src/azure-cli-core/azure/cli/core/__init__.py b/src/azure-cli-core/azure/cli/core/__init__.py index 17464c0cf8d..1a757cd7fc1 100644 --- a/src/azure-cli-core/azure/cli/core/__init__.py +++ b/src/azure-cli-core/azure/cli/core/__init__.py @@ -2,6 +2,8 @@ # 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" @@ -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): @@ -96,9 +108,36 @@ def show_version(self): 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.') diff --git a/src/azure-cli-core/azure/cli/core/_environment.py b/src/azure-cli-core/azure/cli/core/_environment.py index 228addf0ba9..b27d29b4e3b 100644 --- a/src/azure-cli-core/azure/cli/core/_environment.py +++ b/src/azure-cli-core/azure/cli/core/_environment.py @@ -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 --git a/src/azure-cli-core/azure/cli/core/telemetry.py b/src/azure-cli-core/azure/cli/core/telemetry.py index 95d114d4004..56594e4349b 100644 --- a/src/azure-cli-core/azure/cli/core/telemetry.py +++ b/src/azure-cli-core/azure/cli/core/telemetry.py @@ -173,7 +173,8 @@ def _get_azure_cli_properties(self): 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 --git a/src/azure-cli-core/azure/cli/core/tests/test_util.py b/src/azure-cli-core/azure/cli/core/tests/test_util.py index af7efb0c8ab..178cd8a567d 100644 --- a/src/azure-cli-core/azure/cli/core/tests/test_util.py +++ b/src/azure-cli-core/azure/cli/core/tests/test_util.py @@ -213,7 +213,8 @@ def test_configured_default_setter(self): @mock.patch('azure.cli.core.__version__', '7.8.9') def test_get_az_user_agent(self): - with mock.patch.dict('os.environ', {'AZ_INSTALLER': 'PIP'}): + from azure.cli.core._environment import _ENV_AZ_INSTALLER + with mock.patch.dict('os.environ', {_ENV_AZ_INSTALLER: 'PIP'}): actual = get_az_user_agent() self.assertEqual(actual, 'AZURECLI/7.8.9 (PIP)') diff --git a/src/azure-cli-core/azure/cli/core/util.py b/src/azure-cli-core/azure/cli/core/util.py index b4b4d14ffd4..7cfdff55bb6 100644 --- a/src/azure-cli-core/azure/cli/core/util.py +++ b/src/azure-cli-core/azure/cli/core/util.py @@ -200,9 +200,6 @@ def _get_version_string(name, version_dict): _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 @@ -805,8 +802,8 @@ def get_az_user_agent(): 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 +825,22 @@ def user_confirmation(message, yes=False): 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 --git a/src/azure-cli/azure/cli/command_modules/feedback/custom.py b/src/azure-cli/azure/cli/command_modules/feedback/custom.py index 4383498eae0..a322c3db120 100644 --- a/src/azure-cli/azure/cli/command_modules/feedback/custom.py +++ b/src/azure-cli/azure/cli/command_modules/feedback/custom.py @@ -515,7 +515,8 @@ def _build_issue_info_tup(command_log_file=None): format_dict["python_info"] = "Python {}".format(platform.python_version()) format_dict["platform"] = "{}".format(platform.platform()) format_dict["auto_gen_comment"] = _AUTO_GEN_COMMENT - format_dict["installer"] = "Installer: {}".format(os.getenv('AZ_INSTALLER') or '') + from azure.cli.core._environment import _ENV_AZ_INSTALLER + format_dict["installer"] = "Installer: {}".format(os.getenv(_ENV_AZ_INSTALLER) or '') pretty_url_name = _get_extension_repo_url(ext_name) if is_ext else _CLI_ISSUES_URL