Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
130 commits
Select commit Hold shift + click to select a range
fd6af49
{ARO} add __init__.py for ARO tests and update recording (#13089)
Apr 20, 2020
ad2a70f
{Profile} az login: Polish UnicodeDecodeError (#12972)
jiasli Apr 20, 2020
8937383
{Aladdin} add usage examples for module: monitor network privatedns r…
Apr 20, 2020
66fe624
[Compute] az vmss disk detach: fix data disk NoneType issue (#13069)
arrownj Apr 20, 2020
2989946
[ARO] Fixed az aro table output bug (#13066)
m1kola Apr 20, 2020
758f7e9
fix CI (#13114)
mmyyrroonn Apr 22, 2020
9abb510
[Core] fix logging file fd leaking (#13102)
Apr 22, 2020
0ae790c
[AppService] remove sleep after request.get() (#12609)
bmc-msft Apr 22, 2020
2862ce5
[Network] az network security-partner: support security partner provi…
mmyyrroonn Apr 22, 2020
89cf88c
[KeyVault] Add new parameter `--enable-rbac-authorization` during cre…
bim-msft Apr 22, 2020
21172d9
[AKS] az aks create: Add support for Windows (#13084)
AbelHu Apr 23, 2020
facd2bd
fix ci (#13132)
mmyyrroonn Apr 23, 2020
b3d6fb7
{ACR} Fix small typos (#13142)
pablomh Apr 24, 2020
fdc155c
{Compute} Delete stale test of image builder (#13134)
qwordy Apr 24, 2020
12367f8
rename (#13147)
mmyyrroonn Apr 24, 2020
4a58bcb
[Core] fix core tests failed under certain profiles (#13136)
Apr 24, 2020
b93fab1
[Privatedns] Add feature in private DNS zone to import export zone fi…
rahisshaikh Apr 26, 2020
d10679e
{Resource} Remove resource test test_resource_policyset from profile …
Apr 26, 2020
f4ff610
[Monitor] Support LA cluster CMK features (#13133)
jsntcy Apr 26, 2020
80c7bc7
{Core} support get msal accesstoken with adal refresh token for vm ss…
arrownj Apr 26, 2020
4e1ed61
{REST} az rest: Refine help (#13067)
jiasli Apr 26, 2020
dada5c0
{Compute} Update doc of image-version (#13112)
qwordy Apr 27, 2020
5fff003
[Compute] az vm availability-set list: Support showing VM list (#13090)
qwordy Apr 27, 2020
32e5102
[ACS] BREAKING CHANGE: ARO remove vnet-peer, upgrade to 2019-10-27-pr…
Apr 27, 2020
984817c
[Compute] az vm list-skus: Fix display problem of table format (#13184)
qwordy Apr 27, 2020
79964fb
[Monitor] az monitor log-analytics workspace linked-storage: supports…
mmyyrroonn Apr 27, 2020
d261ede
[CI] Onboard pytest and deprecate nose for Automation Test (#13153)
Apr 27, 2020
a638130
[ARM] az deployment sub/group what-if:Add new commands to support Wha…
shenglol Apr 27, 2020
44c3b4a
[Core] Unpin wheel version via pkginfo to query metadata for Python P…
Apr 27, 2020
06cce5b
revert wheel to 0.30.0 (#13198)
Apr 27, 2020
2349420
update azure-cli version to 2.5.0 (#13206)
Apr 28, 2020
37fcdc6
fix style (#13218)
Juliehzl Apr 28, 2020
69c9933
{CI} Fix running full test timeout on profile when in batch mode (#13…
Apr 28, 2020
21e8a2c
upgrade package (#13183)
Juliehzl Apr 28, 2020
cf7f852
[Storage] Support GZRS for storage account creation and update (#13196)
Juliehzl Apr 28, 2020
015be59
[SQL] `az sql instance-pool`: Add instance pools command group (#11721)
rohands Apr 28, 2020
c8edc08
re-run (#13224)
mmyyrroonn Apr 28, 2020
d60ceda
[Compute] vm list-ip-addresses: Error handling (#13186)
qwordy Apr 28, 2020
35c140c
{Network} fix duplicated quotes for private-endpoint example (#13227)
Apr 28, 2020
91f91cc
[Extension] fix loading wrong metadata for wheel type extension (#13222)
Apr 28, 2020
3315616
[Cosmos DB] az cosmosdb create/update: add --enable-public-network su…
soeom Apr 28, 2020
16de64f
[ACR] `az acr check-health`: Fix "DOCKER_PULL_ERROR" on Windows (#13158)
Wwwsylvia Apr 29, 2020
b302f30
[Compute] Fix a bug of vm create if endpoint_vm_image_alias_doc is no…
qwordy Apr 29, 2020
868decc
[Packaging] Add az script for Git Bash/Cygwin on Windows (#13197)
fengzhou-msft Apr 29, 2020
154ecaa
[Compute] vmss create: Add --os-disk-size-gb (#13180)
qwordy Apr 29, 2020
1d64349
{Packaging} Remove wheel as a runtime dependency (#13230)
fengzhou-msft Apr 29, 2020
bd8a963
[Storage] `az storage account failover`: Add support for grs/gzrs sto…
Juliehzl Apr 30, 2020
4acf761
{Extension} Enhance the ability to filter extension's dist-info in or…
Apr 30, 2020
6fc125b
[Storage] `az storage blob upload`: Add --encryption-scope parameter …
Juliehzl Apr 30, 2020
c8bb769
upgarde version (#13265)
Juliehzl Apr 30, 2020
ba399c7
{Release} Upgrade to Azure CLI 2.5.1 (#13263)
Apr 30, 2020
527267b
{Profile} az login: In WSL use powershell.exe to open the default bro…
jiasli Apr 30, 2020
2aa71a7
pass test for connection string (#13273)
Juliehzl Apr 30, 2020
44c092a
{Config} Add endpoint suffix for mysql and postgre sql (#13115)
handsomesun May 6, 2020
f4f44bd
[AppService] functionapp: AzureWebJobsDashboard will only be set if A…
gzuber May 6, 2020
b7a41a3
fix in enable protection for fileshare cmd (#13268)
May 6, 2020
066ca6e
Fix in restore flow (#13348)
May 6, 2020
2325d7c
{Docs} Quote code in help messages with backticks (#13060)
jiasli May 6, 2020
7674236
[Extension] Support installing extensions in a system directory (#12856)
fengzhou-msft May 6, 2020
2343260
[AppService] fix #10664- VNet Integration - Location Check Issue & fi…
Kotasudhakarreddy May 6, 2020
7a087b1
[IoT] Update the IoT command module first run extension awareness mes…
digimaun May 7, 2020
949aaa0
[Network] fix ttl value changed unintended for dns add-record (#13243)
May 7, 2020
e6a19f2
[Compute] New command az vm auto-shutdown (#13199)
qwordy May 7, 2020
a2ab44e
refine help message (#13372)
mmyyrroonn May 7, 2020
cd405fa
ACS: remove fields cleanup that no longer exist (#13315)
May 7, 2020
da047a1
[AKS] update uptime-sla command help context (#13300)
zqingqing1 May 7, 2020
a6fc7da
[Extension] support .egg-info to store wheel type extension metadata …
May 7, 2020
26e341b
[AppConfig] Bug fix for list key values with fields (#13326)
shenmuxiaosen May 7, 2020
df10af0
{AppService}: Updating websites to use the latest SDK version (#13328)
panchagnula May 7, 2020
cef3cf4
{CI} temporary disable CredScan since ADO bug (#13388)
May 8, 2020
f2fa95a
acr: add timeout of 5 minutes (#13349)
yugangw-msft May 8, 2020
08328cd
{Packaging} Clean some azure-cli dependencies (#13253)
fengzhou-msft May 8, 2020
c3f32ed
[ACR] Support disable public network access (#13347)
yugangw-msft May 8, 2020
aa9c4c1
[ARM] az deployment: Fix the problem that the templateLink will not b…
zhoxing-ms May 8, 2020
2de661c
{Storage} Use get_login_credentials from Core for --auth-mode login i…
Juliehzl May 8, 2020
28cc8f4
Bump ADAL version to 1.2.3 (#13371)
jiasli May 8, 2020
4b495b0
[Network] az network public-ip create: Inform customers of a coming b…
jsntcy May 8, 2020
a15c836
[ARO] Add examples to az aro create, list, list-credentials, show, de…
troy0820 May 9, 2020
f04507a
Update python SDK (#13287)
soeom May 9, 2020
14ecf9e
[Storage] az storage account file-service-properties update/show: Add…
Juliehzl May 9, 2020
05d6f12
[ACR]: bug fixes on token create and image import commands (#13392)
yugangw-msft May 9, 2020
137fcc8
{CI} Allow CI to pass on fork repos (#13398)
jiasli May 9, 2020
193c17c
{Docs} Documentation update for configuring machine in VS Code (#13239)
niswitze May 9, 2020
3c8bf29
Fixes az openshift show command (#13358)
m1kola May 9, 2020
5f3c3da
Fix the problem that deployment does not support specially encoded ch…
zhoxing-ms May 9, 2020
70bedda
{Compute} az vm auto-shutdown: remove a validation (#13409)
qwordy May 11, 2020
938bbaf
[RBAC] `az ad sp credential reset`: fix weak credential generation (#…
qianwens May 11, 2020
0caf144
[Network] support generic commands for private link scenario (#13225)
mmyyrroonn May 11, 2020
bd3dfd5
[Network] az network private-endpoint-connection: Support mysql, post…
mmyyrroonn May 11, 2020
197fcf6
acr: keep show-endpoints command in preview (#13430)
yugangw-msft May 11, 2020
0fc82ed
[AMS] Change az ams transform create and az ams content-key-policy cr…
hivyas May 12, 2020
827774b
[Packaging] Build MSI/Homebrew packages with dependecies resolved fro…
fengzhou-msft May 12, 2020
fce911e
[CI] Support flake8 3.8.0 (#13454)
May 12, 2020
b465b2a
{Docs} Increase visibility of doc/use_cli_effectively.md (#13249)
jiasli May 13, 2020
50a18e8
[Network] az network private-endpoint-connection: Support cosmosdb ty…
mmyyrroonn May 13, 2020
0ce0448
[Core] Update local context on/off status to global user level (#13277)
arrownj May 13, 2020
830672d
remove range check for updating min count for autoscaler (#13215)
marwanad May 13, 2020
c07a398
{CI} Code style check use latest azdev (#13450)
May 13, 2020
fe18bc1
[AppService] az webapp|functionapp config ssl import: Lookup key vaul…
madsd May 14, 2020
df5cfde
{CI} Re-enable CredScan (#13464)
May 14, 2020
39c0d01
[ARM] az deployment sub/group what-if: Fix array alignment and error …
shenglol May 14, 2020
3d1fc80
change sappservice code (#13412)
Juliehzl May 14, 2020
f629608
{ACR} Use azure.multiapi.storage for storage blob service (#13411)
Juliehzl May 14, 2020
bcb9921
{batchai} Change storage dependency (#13414)
Juliehzl May 14, 2020
a6a469f
{TestSDK} Delete local context file when local context scenario test …
arrownj May 14, 2020
8f149eb
[ARO]Add generate_random_id function (#13482)
troy0820 May 14, 2020
863517e
{Resource} Remove unused f-string to fix code style (#13493)
May 14, 2020
8027e06
{Doc} Add quoting-issues-with-powershell.md (#13419)
jiasli May 14, 2020
989924e
{Core} Load vnet/subnet global definition for local context by defaul…
arrownj May 14, 2020
a70d7db
Added "MAB" backupmanagementtype to item list command (#13449)
May 14, 2020
ca63e5d
[Backup] Add support for retrying policy update for failed items. (#1…
May 14, 2020
28b1422
[Backup] Add Resume Protection functionality for Azure Virtual Machin…
May 14, 2020
2c3097b
[Backup] Add support to specify ResourceGroup for storing instantRP d…
May 14, 2020
73ee5ce
[Storage] `az storage container create`: Fix #13373 by adding validat…
Juliehzl May 14, 2020
e6e437a
[Output] Show update instruction in find, feedback and --help (#13345)
fengzhou-msft May 14, 2020
2439cc8
[Storage] Add ADLS Gen2 track2 support (#12729)
Juliehzl May 15, 2020
50cedea
{ACR}: expose --expiration from token commands (#13451)
yugangw-msft May 15, 2020
f7e6bba
Fix that cli doe not fail when user only specifies Windows password (…
AbelHu May 15, 2020
cb927d5
[Compute] az vm list-skus: Update --zone behavior, return all type sk…
qwordy May 15, 2020
58198af
[IoT Hub] Support for 2020-03-01 API and Network Isolation commands (…
c-ryan-k May 15, 2020
2e850ea
{Monitor} az monitor metrics alert: refine severity explanation (#13512)
mmyyrroonn May 15, 2020
80457eb
[AppService] Onboard local context for app service (#12984)
arrownj May 15, 2020
4d73f0f
Modify the deprecate information for deployment operation (#13390)
zhoxing-ms May 15, 2020
08432df
Support --connect-string for az storage blob sync (#13135)
zhoxing-ms May 15, 2020
9b71e8a
[Storage] az storage blob sync: Fix the incorrect error message when …
zhoxing-ms May 15, 2020
bae215b
{Network} az network nic create: Refine help message (#13513)
mmyyrroonn May 15, 2020
ee84afb
[NetAppFiles] Anf 5207 Bugfix - add missing snapshot restore function…
audunn May 15, 2020
fe1e042
{Resource} Add example to tell how to pass array to --parameters (#13…
May 16, 2020
530cd07
fix (#13511)
mmyyrroonn May 18, 2020
029e73b
{Release} Upgrade to Azure CLI 2.6.0 (#13542)
May 18, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
[Extension] Support installing extensions in a system directory (#12856)
  • Loading branch information
fengzhou-msft authored May 6, 2020
commit 76742362fc9c44ca968859f748ec3bb594d75271
2 changes: 1 addition & 1 deletion .github/CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
/src/azure-cli/azure/cli/command_modules/container/ @samkreter
/src/azure-cli/azure/cli/command_modules/consumption/ @sandeepnl
/src/azure-cli/azure/cli/command_modules/dls/ @lewu-msft
/src/azure-cli/azure/cli/command_modules/extension/ @zikalino
/src/azure-cli/azure/cli/command_modules/extension/ @fengzhou-msft @haroldrandom
/src/azure-cli/azure/cli/command_modules/keyvault/ @bim-msft @fengzhou-msft
/src/azure-cli/azure/cli/command_modules/monitor/ @MyronFanQiu
/src/azure-cli/azure/cli/command_modules/natgateway/ @khannarheams @MyronFanQiu @haroldrandom
Expand Down
25 changes: 17 additions & 8 deletions src/azure-cli-core/azure/cli/core/extension/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@
# 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 traceback
import json
import re
import sys
import pkginfo

from azure.cli.core._config import GLOBAL_CONFIG_DIR, ENV_VAR_PREFIX
Expand All @@ -18,10 +18,11 @@
az_config = CLIConfig(config_dir=GLOBAL_CONFIG_DIR, config_env_var_prefix=ENV_VAR_PREFIX)
_CUSTOM_EXT_DIR = az_config.get('extension', 'dir', None)
_DEV_EXTENSION_SOURCES = az_config.get('extension', 'dev_sources', None)
_CUSTOM_EXT_SYS_DIR = az_config.get('extension', 'sys_dir', None)
EXTENSIONS_DIR = os.path.expanduser(_CUSTOM_EXT_DIR) if _CUSTOM_EXT_DIR else os.path.join(GLOBAL_CONFIG_DIR,
'cliextensions')
DEV_EXTENSION_SOURCES = _DEV_EXTENSION_SOURCES.split(',') if _DEV_EXTENSION_SOURCES else []
EXTENSIONS_SYS_DIR = os.path.join(get_python_lib(), 'azure-cli-extensions') if sys.platform.startswith('linux') else ""
EXTENSIONS_SYS_DIR = os.path.expanduser(_CUSTOM_EXT_SYS_DIR) if _CUSTOM_EXT_SYS_DIR else os.path.join(get_python_lib(), 'azure-cli-extensions')

EXTENSIONS_MOD_PREFIX = 'azext_'

Expand Down Expand Up @@ -134,10 +135,11 @@ def get_version(self):

def get_metadata(self):
from glob import glob
if not extension_exists(self.name):
return None
metadata = {}
ext_dir = self.path or get_extension_path(self.name)

if not ext_dir or not os.path.isdir(ext_dir):
return None
info_dirs = glob(os.path.join(ext_dir, self.name.replace('-', '_') + '-' + '*.dist-info'))

azext_metadata = WheelExtension.get_azext_metadata(ext_dir)
Expand Down Expand Up @@ -199,11 +201,10 @@ def get_version(self):
return self.metadata.get('version')

def get_metadata(self):

if not extension_exists(self.name):
return None
metadata = {}
ext_dir = self.path
if not ext_dir or not os.path.isdir(ext_dir):
return None
egg_info_dirs = [f for f in os.listdir(ext_dir) if f.endswith('.egg-info')]
azext_metadata = DevExtension.get_azext_metadata(ext_dir)
if azext_metadata:
Expand Down Expand Up @@ -284,8 +285,16 @@ def get_extension_modname(ext_name=None, ext_dir=None):


def get_extension_path(ext_name):
# This will return the path for a WHEEL extension if exists.
ext_sys_path = os.path.join(EXTENSIONS_SYS_DIR, ext_name)
ext_path = os.path.join(EXTENSIONS_DIR, ext_name)
return ext_path if os.path.isdir(ext_path) else (
ext_sys_path if os.path.isdir(ext_sys_path) else None)


def build_extension_path(ext_name, system=None):
# This will simply form the path for a WHEEL extension.
return os.path.join(EXTENSIONS_DIR, ext_name)
return os.path.join(EXTENSIONS_SYS_DIR, ext_name) if system else os.path.join(EXTENSIONS_DIR, ext_name)


def get_extensions(ext_type=None):
Expand Down
41 changes: 24 additions & 17 deletions src/azure-cli-core/azure/cli/core/extension/operations.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 collections import OrderedDict
import sys
import os
Expand All @@ -17,7 +19,7 @@
from pkg_resources import parse_version

from azure.cli.core.util import CLIError, reload_module
from azure.cli.core.extension import (extension_exists, get_extension_path, get_extensions, get_extension_modname,
from azure.cli.core.extension import (extension_exists, build_extension_path, get_extensions, get_extension_modname,
get_extension, ext_compat_with_cli,
EXT_METADATA_ISPREVIEW, EXT_METADATA_ISEXPERIMENTAL,
WheelExtension, DevExtension, ExtensionNotInstalledException, WHEEL_INFO_RE)
Expand All @@ -37,13 +39,14 @@
OUT_KEY_METADATA = 'metadata'
OUT_KEY_PREVIEW = 'preview'
OUT_KEY_EXPERIMENTAL = 'experimental'
OUT_KEY_PATH = 'path'

IS_WINDOWS = sys.platform.lower() in ['windows', 'win32']
LIST_FILE_PATH = os.path.join(os.sep, 'etc', 'apt', 'sources.list.d', 'azure-cli.list')
LSB_RELEASE_FILE = os.path.join(os.sep, 'etc', 'lsb-release')


def _run_pip(pip_exec_args):
def _run_pip(pip_exec_args, extension_path):
cmd = [sys.executable, '-m', 'pip'] + pip_exec_args + ['-vv', '--disable-pip-version-check', '--no-cache-dir']
logger.debug('Running: %s', cmd)
try:
Expand All @@ -53,6 +56,8 @@ def _run_pip(pip_exec_args):
except CalledProcessError as e:
logger.debug(e.output)
logger.debug(e)
if "PermissionError: [WinError 5]" in e.output:
logger.warning("You do not have the permission to add extensions in the target directory: %s. You may need to rerun on a shell as administrator.", os.path.split(extension_path)[0])
returncode = e.returncode
return returncode

Expand All @@ -79,7 +84,7 @@ def _validate_whl_extension(ext_file):
check_version_compatibility(azext_metadata)


def _add_whl_ext(cmd, source, ext_sha256=None, pip_extra_index_urls=None, pip_proxy=None): # pylint: disable=too-many-statements
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')
if not source.endswith('.whl'):
raise ValueError('Unknown extension type. Only Python wheels are supported.')
Expand Down Expand Up @@ -135,7 +140,7 @@ def _add_whl_ext(cmd, source, ext_sha256=None, pip_extra_index_urls=None, pip_pr
check_distro_consistency()
cmd.cli_ctx.get_progress_controller().add(message='Installing')
# Install with pip
extension_path = get_extension_path(extension_name)
extension_path = build_extension_path(extension_name, system)
pip_args = ['install', '--target', extension_path, ext_file]

if pip_proxy:
Expand All @@ -146,7 +151,7 @@ def _add_whl_ext(cmd, source, ext_sha256=None, pip_extra_index_urls=None, pip_pr

logger.debug('Executing pip with args: %s', pip_args)
with HomebrewPipPatch():
pip_status_code = _run_pip(pip_args)
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)
shutil.rmtree(extension_path, ignore_errors=True)
Expand All @@ -168,12 +173,12 @@ def is_valid_sha256sum(a_file, expected_sum):
return expected_sum == computed_hash, computed_hash


def _augment_telemetry_with_ext_info(extension_name):
def _augment_telemetry_with_ext_info(extension_name, ext=None):
# The extension must be available before calling this otherwise we can't get the version from metadata
if not extension_name:
return
try:
ext = get_extension(extension_name)
ext = ext or get_extension(extension_name)
ext_version = ext.version
set_extension_management_detail(extension_name, ext_version)
except Exception: # nopa pylint: disable=broad-except
Expand All @@ -200,7 +205,7 @@ def check_version_compatibility(azext_metadata):


def add_extension(cmd, source=None, extension_name=None, index_url=None, yes=None, # pylint: disable=unused-argument
pip_extra_index_urls=None, pip_proxy=None):
pip_extra_index_urls=None, pip_proxy=None, system=None):
ext_sha256 = None
if extension_name:
cmd.cli_ctx.get_progress_controller().add(message='Searching')
Expand All @@ -220,13 +225,14 @@ def add_extension(cmd, source=None, extension_name=None, index_url=None, yes=Non
logger.debug(err)
raise CLIError("No matching extensions for '{}'. Use --debug for more information.".format(extension_name))
extension_name = _add_whl_ext(cmd=cmd, source=source, ext_sha256=ext_sha256,
pip_extra_index_urls=pip_extra_index_urls, pip_proxy=pip_proxy)
_augment_telemetry_with_ext_info(extension_name)
pip_extra_index_urls=pip_extra_index_urls, pip_proxy=pip_proxy, system=system)
try:
if extension_name and get_extension(extension_name).experimental:
ext = get_extension(extension_name)
_augment_telemetry_with_ext_info(extension_name, ext)
if extension_name and ext.experimental:
logger.warning("The installed extension '%s' is experimental and not covered by customer support. "
"Please use with discretion.", extension_name)
elif extension_name and get_extension(extension_name).preview:
elif extension_name and ext.preview:
logger.warning("The installed extension '%s' is in preview.", extension_name)
except ExtensionNotInstalledException:
pass
Expand All @@ -245,15 +251,15 @@ def log_err(func, path, exc_info):
"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)
shutil.rmtree(get_extension_path(extension_name), onerror=log_err)
_augment_telemetry_with_ext_info(extension_name, ext)
shutil.rmtree(ext.path, onerror=log_err)
except ExtensionNotInstalledException as e:
raise CLIError(e)


def list_extensions():
return [{OUT_KEY_NAME: ext.name, OUT_KEY_VERSION: ext.version, OUT_KEY_TYPE: ext.ext_type,
OUT_KEY_PREVIEW: ext.preview, OUT_KEY_EXPERIMENTAL: ext.experimental}
OUT_KEY_PREVIEW: ext.preview, OUT_KEY_EXPERIMENTAL: ext.experimental, OUT_KEY_PATH: ext.path}
for ext in get_extensions()]


Expand All @@ -263,7 +269,8 @@ def show_extension(extension_name):
return {OUT_KEY_NAME: extension.name,
OUT_KEY_VERSION: extension.version,
OUT_KEY_TYPE: extension.ext_type,
OUT_KEY_METADATA: extension.metadata}
OUT_KEY_METADATA: extension.metadata,
OUT_KEY_PATH: extension.path}
except ExtensionNotInstalledException as e:
raise CLIError(e)

Expand All @@ -279,7 +286,7 @@ def update_extension(cmd, extension_name, index_url=None, pip_extra_index_urls=N
raise CLIError("No updates available for '{}'. Use --debug for more information.".format(extension_name))
# 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 = get_extension_path(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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@
import mock

from azure.cli.core.util import CLIError
from azure.cli.core.extension import build_extension_path
from azure.cli.core.extension.operations import (list_extensions, add_extension, show_extension,
remove_extension, update_extension,
list_available_extensions, OUT_KEY_NAME, OUT_KEY_VERSION, OUT_KEY_METADATA)
list_available_extensions, OUT_KEY_NAME, OUT_KEY_VERSION,
OUT_KEY_METADATA, OUT_KEY_PATH)
from azure.cli.core.extension._resolve import NoExtensionCandidatesError
from azure.cli.core.mock import DummyCli

Expand All @@ -39,13 +41,18 @@ class TestExtensionCommands(unittest.TestCase):

def setUp(self):
self.ext_dir = tempfile.mkdtemp()
self.patcher = mock.patch('azure.cli.core.extension.EXTENSIONS_DIR', self.ext_dir)
self.patcher.start()
self.ext_sys_dir = tempfile.mkdtemp()
self.patchers = [mock.patch('azure.cli.core.extension.EXTENSIONS_DIR', self.ext_dir),
mock.patch('azure.cli.core.extension.EXTENSIONS_SYS_DIR', self.ext_sys_dir)]
for patcher in self.patchers:
patcher.start()
self.cmd = self._setup_cmd()

def tearDown(self):
self.patcher.stop()
for patcher in self.patchers:
patcher.stop()
shutil.rmtree(self.ext_dir, ignore_errors=True)
shutil.rmtree(self.ext_sys_dir, ignore_errors=True)

def test_no_extensions_dir(self):
shutil.rmtree(self.ext_dir)
Expand All @@ -66,6 +73,34 @@ def test_add_list_show_remove_extension(self):
num_exts = len(list_extensions())
self.assertEqual(num_exts, 0)

def test_add_list_show_remove_system_extension(self):
add_extension(cmd=self.cmd, source=MY_EXT_SOURCE, system=True)
actual = list_extensions()
self.assertEqual(len(actual), 1)
ext = show_extension(MY_EXT_NAME)
self.assertEqual(ext[OUT_KEY_NAME], MY_EXT_NAME)
remove_extension(MY_EXT_NAME)
num_exts = len(list_extensions())
self.assertEqual(num_exts, 0)

def test_add_list_show_remove_user_system_extensions(self):
add_extension(cmd=self.cmd, source=MY_EXT_SOURCE)
add_extension(cmd=self.cmd, source=MY_SECOND_EXT_SOURCE_DASHES, system=True)
actual = list_extensions()
self.assertEqual(len(actual), 2)
ext = show_extension(MY_EXT_NAME)
self.assertEqual(ext[OUT_KEY_NAME], MY_EXT_NAME)
self.assertEqual(ext[OUT_KEY_PATH], build_extension_path(MY_EXT_NAME))
second_ext = show_extension(MY_SECOND_EXT_NAME_DASHES)
self.assertEqual(second_ext[OUT_KEY_NAME], MY_SECOND_EXT_NAME_DASHES)
self.assertEqual(second_ext[OUT_KEY_PATH], build_extension_path(MY_SECOND_EXT_NAME_DASHES, system=True))
remove_extension(MY_EXT_NAME)
num_exts = len(list_extensions())
self.assertEqual(num_exts, 1)
remove_extension(MY_SECOND_EXT_NAME_DASHES)
num_exts = len(list_extensions())
self.assertEqual(num_exts, 0)

def test_add_list_show_remove_extension_with_dashes(self):
add_extension(cmd=self.cmd, source=MY_SECOND_EXT_SOURCE_DASHES)
actual = list_extensions()
Expand All @@ -85,6 +120,13 @@ def test_add_extension_twice(self):
with self.assertRaises(CLIError):
add_extension(cmd=self.cmd, source=MY_EXT_SOURCE)

def test_add_same_extension_user_system(self):
add_extension(cmd=self.cmd, source=MY_EXT_SOURCE)
num_exts = len(list_extensions())
self.assertEqual(num_exts, 1)
with self.assertRaises(CLIError):
add_extension(cmd=self.cmd, source=MY_EXT_SOURCE, system=True)

def test_add_extension_invalid(self):
with self.assertRaises(ValueError):
add_extension(cmd=self.cmd, source=MY_BAD_EXT_SOURCE)
Expand Down
Binary file not shown.
Loading