Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Preview extension notation & Simple default view for extension list-a… #5882

Merged
merged 4 commits into from
Mar 22, 2018
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
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
5 changes: 5 additions & 0 deletions doc/extensions/metadata.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,9 @@ Type: `string`

Example: `"azext.maxCliCoreVersion": "2.0.15"`

### azext.isPreview
Description: Indicate that the extension is in preview.

Type: `boolean`

Example: `"azext.isPreview": true`
2 changes: 1 addition & 1 deletion src/azure-cli-core/HISTORY.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Release History

2.0.30
++++++
* Minor fixes
* Show message for extensions marked as preview on -h.

2.0.29
++++++
Expand Down
6 changes: 4 additions & 2 deletions src/azure-cli-core/azure/cli/core/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,8 @@ def _handle_extension_suppressions(extensions):
if extensions:
logger.debug("Found %s extensions: %s", len(extensions), [e.name for e in extensions])
allowed_extensions = _handle_extension_suppressions(extensions)
for ext_name in [e.name for e in allowed_extensions]:
for ext in allowed_extensions:
ext_name = ext.name
ext_dir = get_extension_path(ext_name)
sys.path.append(ext_dir)
try:
Expand All @@ -177,7 +178,8 @@ def _handle_extension_suppressions(extensions):
for cmd_name, cmd in extension_command_table.items():
cmd.command_source = ExtensionCommandSource(
extension_name=ext_name,
overrides_command=cmd_name in cmd_to_mod_map)
overrides_command=cmd_name in cmd_to_mod_map,
preview=ext.preview)

self.command_table.update(extension_command_table)
elapsed_time = timeit.default_timer() - start_time
Expand Down
2 changes: 2 additions & 0 deletions src/azure-cli-core/azure/cli/core/_help.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ def _print_extensions_msg(help_file):
return
if help_file.command_source and isinstance(help_file.command_source, ExtensionCommandSource):
logger.warning(help_file.command_source.get_command_warn_msg())
if help_file.command_source.preview:
logger.warning(help_file.command_source.get_preview_warn_msg())

@classmethod
def print_detailed_help(cls, cli_name, help_file):
Expand Down
8 changes: 7 additions & 1 deletion src/azure-cli-core/azure/cli/core/commands/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -568,11 +568,12 @@ def _load_module_command_loader(loader, args, mod):
class ExtensionCommandSource(object):
""" Class for commands contributed by an extension """

def __init__(self, overrides_command=False, extension_name=None):
def __init__(self, overrides_command=False, extension_name=None, preview=False):
super(ExtensionCommandSource, self).__init__()
# True if the command overrides a CLI command
self.overrides_command = overrides_command
self.extension_name = extension_name
self.preview = preview

def get_command_warn_msg(self):
if self.overrides_command:
Expand All @@ -585,6 +586,11 @@ def get_command_warn_msg(self):
return "This command is from the following extension: {}".format(self.extension_name)
return "This command is from an extension."

def get_preview_warn_msg(self):
if self.preview:
return "The extension is in preview"
return None


def _load_client_exception_class():
# Since loading msrest is expensive, we avoid it until we have to
Expand Down
15 changes: 15 additions & 0 deletions src/azure-cli-core/azure/cli/core/extension.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

EXT_METADATA_MINCLICOREVERSION = 'azext.minCliCoreVersion'
EXT_METADATA_MAXCLICOREVERSION = 'azext.maxCliCoreVersion'
EXT_METADATA_ISPREVIEW = 'azext.isPreview'

logger = get_logger(__name__)

Expand All @@ -42,6 +43,7 @@ def __init__(self, name, ext_type):
self.ext_type = ext_type
self._version = None
self._metadata = None
self._preview = None

@property
def version(self):
Expand All @@ -67,6 +69,19 @@ def metadata(self):
logger.debug("Unable to get extension metadata: %s", traceback.format_exc())
return self._metadata

@property
def preview(self):
"""
Lazy load preview status.
Returns the preview status of the extension.
"""
try:
if not isinstance(self._preview, bool):
self._preview = bool(self.metadata.get(EXT_METADATA_ISPREVIEW))
except Exception: # pylint: disable=broad-except
logger.debug("Unable to get extension preview status: %s", traceback.format_exc())
return self._preview

def get_version(self):
raise NotImplementedError()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -152,9 +152,9 @@ def _mock_extension_modname(ext_name, ext_dir):
return ext_name

def _mock_get_extensions():
MockExtension = namedtuple('Extension', ['name'])
return [MockExtension(name=__name__ + '.ExtCommandsLoader'),
MockExtension(name=__name__ + '.Ext2CommandsLoader')]
MockExtension = namedtuple('Extension', ['name', 'preview'])
return [MockExtension(name=__name__ + '.ExtCommandsLoader', preview=False),
MockExtension(name=__name__ + '.Ext2CommandsLoader', preview=False)]

def _mock_load_command_loader(loader, args, name, prefix):

Expand Down
4 changes: 3 additions & 1 deletion src/command_modules/azure-cli-extension/HISTORY.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ Release History

0.0.11
++++++
* Minor fixes
* Preview extensions: Show message on `az extension add` if extension is in preview
* BC: `az extension list-available` - The full extension data is now available with `--show-details`
* `az extension list-available` - A simplified view of the extensions available is now shown by default

0.0.10
+++++++
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,10 @@ def ext_add_has_confirmed(command_args):
return bool(not command_args.get('source') or prompt_y_n('Are you sure you want to install this extension?'))

def transform_extension_list_available(results):
return [OrderedDict([('Name', r)]) for r in results]
if isinstance(results, dict):
# For --show-details, transform the table
return [OrderedDict([('Name', r)]) for r in results]
return results

def validate_extension_add(namespace):
if (namespace.extension_name and namespace.source) or (not namespace.extension_name and not namespace.source):
Expand Down Expand Up @@ -67,5 +70,8 @@ def load_arguments(self, command):
c.argument('source', options_list=['--source', '-s'], help='Filepath or URL to an extension', completer=FilesCompleter())
c.argument('yes', options_list=['--yes', '-y'], action='store_true', help='Do not prompt for confirmation.')

with self.argument_context('extension list-available') as c:
c.argument('show_details', options_list=['--show-details', '-d'], action='store_true', help='Show the raw data from the extension index.')


COMMAND_LOADER_CLS = ExtensionCommandsLoader
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,17 @@
import hashlib
from subprocess import check_output, STDOUT, CalledProcessError
from six.moves.urllib.parse import urlparse # pylint: disable=import-error
from collections import OrderedDict

import requests
from wheel.install import WHEEL_INFO_RE
from pkg_resources import parse_version

from knack.log import get_logger

from azure.cli.core.util import CLIError
from azure.cli.core.extension import (extension_exists, get_extension_path, get_extensions,
get_extension, ext_compat_with_cli,
get_extension, ext_compat_with_cli, EXT_METADATA_ISPREVIEW,
WheelExtension, ExtensionNotInstalledException)
from azure.cli.core.telemetry import set_extension_management_detail

Expand Down Expand Up @@ -200,6 +203,11 @@ def add_extension(source=None, extension_name=None, index_url=None, yes=None, #
raise CLIError("No matching extensions for '{}'. Use --debug for more information.".format(extension_name))
_add_whl_ext(source, ext_sha256=ext_sha256, pip_extra_index_urls=pip_extra_index_urls, pip_proxy=pip_proxy)
_augment_telemetry_with_ext_info(extension_name)
try:
if extension_name and get_extension(extension_name).preview:
logger.warning("The installed extension '%s' is in preview.", extension_name)
except ExtensionNotInstalledException:
pass


def remove_extension(extension_name):
Expand Down Expand Up @@ -263,8 +271,28 @@ def update_extension(extension_name, index_url=None, pip_extra_index_urls=None,
raise CLIError(e)


def list_available_extensions(index_url=None):
return get_index_extensions(index_url=index_url)
def list_available_extensions(index_url=None, show_details=False):
index_data = get_index_extensions(index_url=index_url)
if show_details:
return index_data
installed_extensions = get_extensions()
installed_extension_names = [e.name for e in installed_extensions]
results = []
for name, items in OrderedDict(sorted(index_data.items())).items():
latest = sorted(items, key=lambda c: parse_version(c['metadata']['version']), reverse=True)[0]
installed = False
if name in installed_extension_names:
installed = True
if parse_version(latest['metadata']['version']) > parse_version(get_extension(name).version):
installed = str(True) + ' (upgrade available)'
results.append({
'name': name,
'version': latest['metadata']['version'],
'summary': latest['metadata']['summary'],
'preview': latest['metadata'].get(EXT_METADATA_ISPREVIEW, False),
'installed': installed
})
return results
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we have a test for this?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do have some tests but not for these changes...
I will take a look at adding tests... 👍



def get_lsb_release():
Expand Down Expand Up @@ -296,14 +324,14 @@ def check_distro_consistency():
except Exception as err: # pylint: disable=broad-except
current_linux_dist_name = None
stored_linux_dist_name = None
logger.debug('Linux distro check: An error occurred while checking \
linux distribution version source list consistency.')
logger.debug('Linux distro check: An error occurred while checking '
'linux distribution version source list consistency.')
logger.debug(err)

if current_linux_dist_name != stored_linux_dist_name:
logger.warning("Linux distro check: Mismatch distribution \
name in %s file", LIST_FILE_PATH)
logger.warning("Linux distro check: If command fails, install the appropriate package \
for your distribution or change the above file accordingly.")
logger.warning("Linux distro check: Mismatch distribution "
"name in %s file", LIST_FILE_PATH)
logger.warning("Linux distro check: If command fails, install the appropriate package "
"for your distribution or change the above file accordingly.")
logger.warning("Linux distro check: %s has '%s', current distro is '%s'",
LIST_FILE_PATH, stored_linux_dist_name, current_linux_dist_name)