def _validate_whl_extension(ext_file): tmp_dir = tempfile.mkdtemp() zip_ref = zipfile.ZipFile(ext_file, 'r') zip_ref.extractall(tmp_dir) zip_ref.close() azext_metadata = WheelExtension.get_azext_metadata(tmp_dir) rmtree_with_retry(tmp_dir) check_version_compatibility(azext_metadata)
def update_extension(cmd=None, extension_name=None, index_url=None, pip_extra_index_urls=None, pip_proxy=None, cli_ctx=None, version=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: download_url, ext_sha256 = resolve_from_index( extension_name, cur_version=cur_version, index_url=index_url, target_version=version, cli_ctx=cmd_cli_ctx) _, ext_version = _get_extension_info_from_source(download_url) set_extension_management_detail(extension_name, ext_version) except NoExtensionCandidatesError as err: logger.debug(err) msg = "Extension {} with version {} not found.".format( extension_name, version ) if version else "No updates available for '{}'. Use --debug for more information.".format( extension_name) logger.warning(msg) return # Copy current version of extension to tmp directory in case we need to restore it after a failed install. backup_dir = os.path.join(tempfile.mkdtemp(), extension_name) extension_path = ext.path logger.debug('Backing up the current extension: %s to %s', extension_path, backup_dir) shutil.copytree(extension_path, backup_dir) # Remove current version of the extension rmtree_with_retry(extension_path) # Install newer version try: _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) rmtree_with_retry(backup_dir) except Exception as err: logger.error('An error occurred whilst updating.') logger.error(err) logger.debug('Copying %s to %s', backup_dir, extension_path) shutil.copytree(backup_dir, extension_path) raise CLIError('Failed to update. Rolled {} back to {}.'.format( extension_name, cur_version)) CommandIndex().invalidate() except ExtensionNotInstalledException as e: raise CLIError(e)
def remove_extension(extension_name): try: # Get the extension and it will raise an error if it doesn't exist ext = get_extension(extension_name) if ext and isinstance(ext, DevExtension): raise CLIError( "Extension '{name}' was installed in development mode. Remove using " "`azdev extension remove {name}`".format(name=extension_name)) # We call this just before we remove the extension so we can get the metadata before it is gone _augment_telemetry_with_ext_info(extension_name, ext) rmtree_with_retry(ext.path) CommandIndex().invalidate() except ExtensionNotInstalledException as e: raise CLIError(e)
def _upgrade_on_windows(): """Download MSI to a temp folder and install it with msiexec.exe. Directly installing from URL may be blocked by policy: https://github.com/Azure/azure-cli/issues/19171 This also gives the user a chance to manually install the MSI in case of msiexec.exe failure. """ logger.warning( "Updating Azure CLI with MSI from https://aka.ms/installazurecliwindows" ) tmp_dir, msi_path = _download_from_url( 'https://aka.ms/installazurecliwindows') logger.warning("Installing MSI") import subprocess exit_code = subprocess.call(['msiexec.exe', '/i', msi_path]) if exit_code: logger.warning("Installation Failed. You may manually install %s", msi_path) else: from azure.cli.core.util import rmtree_with_retry logger.warning("Succeeded. Deleting %s", tmp_dir) rmtree_with_retry(tmp_dir) return exit_code
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) is_url = (url_parse_result.scheme == 'http' or url_parse_result.scheme == 'https') logger.debug('Extension source is url? %s', is_url) whl_filename = os.path.basename(url_parse_result.path) if is_url else os.path.basename(source) parsed_filename = WHEEL_INFO_RE(whl_filename) # Extension names can have - but .whl format changes it to _ (PEP 0427). Undo this. extension_name = parsed_filename.groupdict().get('name').replace('_', '-') if parsed_filename else None if not extension_name: raise CLIError('Unable to determine extension name from {}. Is the file name correct?'.format(source)) if extension_exists(extension_name, ext_type=WheelExtension): raise CLIError('The extension {} already exists.'.format(extension_name)) if extension_name == 'rdbms-connect': _install_deps_for_psycopg2() ext_file = None if is_url: # Download from URL tmp_dir = tempfile.mkdtemp() ext_file = os.path.join(tmp_dir, whl_filename) logger.debug('Downloading %s to %s', source, ext_file) import requests try: 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))) logger.debug('Downloaded to %s', ext_file) else: # Get file path ext_file = os.path.realpath(os.path.expanduser(source)) if not os.path.isfile(ext_file): raise CLIError("File {} not found.".format(source)) # Validate the extension logger.debug('Validating the extension %s', ext_file) if ext_sha256: valid_checksum, computed_checksum = is_valid_sha256sum(ext_file, ext_sha256) if valid_checksum: logger.debug("Checksum of %s is OK", ext_file) else: logger.debug("Invalid checksum for %s. Expected '%s', computed '%s'.", ext_file, ext_sha256, computed_checksum) raise CLIError("The checksum of the extension does not match the expected value. " "Use --debug for more information.") try: cli_ctx.get_progress_controller().add(message='Validating') _validate_whl_extension(ext_file) except AssertionError: logger.debug(traceback.format_exc()) raise CLIError('The extension is invalid. Use --debug for more information.') except CLIError as e: raise e logger.debug('Validation successful on %s', ext_file) # Check for distro consistency check_distro_consistency() 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] if pip_proxy: pip_args = pip_args + ['--proxy', pip_proxy] if pip_extra_index_urls: for extra_index_url in pip_extra_index_urls: pip_args = pip_args + ['--extra-index-url', extra_index_url] logger.debug('Executing pip with args: %s', pip_args) with HomebrewPipPatch(): pip_status_code = _run_pip(pip_args, extension_path) if pip_status_code > 0: logger.debug('Pip failed so deleting anything we might have installed at %s', extension_path) rmtree_with_retry(extension_path) raise CLIError('An error occurred. Pip failed with status code {}. ' 'Use --debug for more information.'.format(pip_status_code)) # Save the whl we used to install the extension in the extension dir. dst = os.path.join(extension_path, whl_filename) shutil.copyfile(ext_file, dst) logger.debug('Saved the whl to %s', dst) return extension_name