Skip to content

Commit

Permalink
Update show commands to exit with code 3 upon a 404. (#6740)
Browse files Browse the repository at this point in the history
* added custom flag for generic commands

* added custom_command arg to each generic cmd registering method

* add custom flag to resolve_operations hidden method

* check for 'client' as a client_arg_name

* converted all custom show commands

* added description loader for generic_show

* bug fix for 'cmd' in command signature

* added catch for sys.exit in test base

* convert show commands registered with 'command()'

* fixed failing tests since show will now fail on missing resources

* removed exception_handler=empty_on_404 for commands using generic_show

* fix style

* bump versions

* updated history

update with minor version bump

* billing history version bump

* updated generic show/wait to custom_command/command pattern

* pep8

style

* fixes

called wrong method

* addressed comments

* removed getter_type arg from wait/show

* added documentation for new command registration methods

* addressed more comments
  • Loading branch information
williexu authored Jul 11, 2018
1 parent 008b199 commit 663db88
Show file tree
Hide file tree
Showing 117 changed files with 505 additions and 482 deletions.
33 changes: 27 additions & 6 deletions doc/authoring_command_modules/authoring_commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,8 +99,9 @@ with self.command_group('mymod', mymod_sdk) as g:
# (3) Registering different types of commands
g.command('command1', 'do_something_1')
g.custom_command('command2', 'do_something_2')
g.generic_update('update', custom_function_name='my_custom_update')
g.generic_wait('wait')
g.generic_update_command('update', custom_function_name='my_custom_update')
g.wait_command('wait')
g.show_command('show')
```

At this point, you should be able to access your command using `az [name]` and access the built-in help with `az [name] -h/--help`. Your command will automatically be 'wired up' with the global parameters. See below for amplifying information.
Expand Down Expand Up @@ -141,24 +142,44 @@ The signature for `custom_command` is exactly the same as `command`. The only di

See the section on "Suppporting Generic Update"

***generic_wait_command***
***wait_command***

The generic wait command provides a templated solution for polling Azure resources until specific conditions are met.

```Python
generic_wait_command(self, name, getter_name='get', getter_type=None, **kwargs)
wait_command(self, name, getter_name='get', **kwargs)
```

- `name`: The name of the command within the command group. Commonly called 'wait'.
- `getter_name`: The name of the method for the object getter, relative to the path specified in `operations_tmpl`.
- `getter_type`: A `CliCommandType` object to apply to this command (optional).
- `kwargs`: any supported kwarg.

Since most wait commands rely on a simple GET call from the SDK, most of these entries simply look like:
```Python
g.generic_wait_command('wait')
g.wait_command('wait')
```

***custom_wait_command***

Similar to `custom_command` and `command`, the signature for `custom_wait_command` is exactly the same as `wait_command` but uses `custom_command_type` as the fallback for missings kwargs.

***show_command***

The generic show command ensures a consistent behavior when encountering a missing Azure resource.
With little exception, all `show` commands should be registered using this method or `custom_show_command` to ensure consistency.

```Python
show_command(self, name, getter_name='get', **kwargs)
```

- `name`: The name of the command within the command group. Commonly called 'show'.
- `getter_name`: The name of the method for the object getter, relative to the path specified in `operations_tmpl`.
- `kwargs`: any supported kwarg.

***custom_show_command***

Similar to `custom_command` and `command`, the signature for `custom_show_command` is exactly the same as `show_command` but uses `custom_command_type` as the fallback for missings kwargs.

**(4) Supporting --no-wait**

When registering a command, the boolean `supports_no_wait` property can be used to specify that the command supports `--no-wait`.
Expand Down
81 changes: 40 additions & 41 deletions src/azure-cli-core/azure/cli/core/commands/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
from azure.cli.core.commands.parameters import (
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
import azure.cli.core.telemetry as telemetry

logger = get_logger(__name__)
Expand Down Expand Up @@ -60,7 +61,6 @@ def _explode_list_args(args):

def _expand_file_prefixed_files(args):
def _load_file(path):
from azure.cli.core.util import read_file_content
if path == '-':
content = sys.stdin.read()
else:
Expand Down Expand Up @@ -380,7 +380,6 @@ def execute(self, args):
is_query_active=self.data['query_active'])

def _build_kwargs(self, func, ns): # pylint: disable=no-self-use
from azure.cli.core.util import get_arg_list
arg_list = get_arg_list(func)
kwargs = {}
if 'cmd' in arg_list:
Expand Down Expand Up @@ -573,7 +572,6 @@ def __call__(self, poller):
class DeploymentOutputLongRunningOperation(LongRunningOperation):
def __call__(self, result):
from msrest.pipeline import ClientRawResponse
from azure.cli.core.util import poller_classes

if isinstance(result, poller_classes()):
# most deployment operations return a poller
Expand Down Expand Up @@ -677,7 +675,6 @@ def _is_paged(obj):
def _is_poller(obj):
# Since loading msrest is expensive, we avoid it until we have to
if obj.__class__.__name__ in ['AzureOperationPoller', 'LROPoller']:
from azure.cli.core.util import poller_classes
return isinstance(obj, poller_classes())
return False

Expand Down Expand Up @@ -750,7 +747,7 @@ def _flatten_kwargs(self, kwargs, default_source_name):
# pylint: disable=arguments-differ
def command(self, name, method_name=None, **kwargs):
"""
Register a CLI command
Register a CLI command.
:param name: Name of the command as it will be called on the command line
:type name: str
:param method_name: Name of the method the command maps to
Expand All @@ -762,7 +759,7 @@ def command(self, name, method_name=None, **kwargs):
- exception_handler: Exception handler for handling non-standard exceptions (function)
- supports_no_wait: The command supports no wait. (bool)
- no_wait_param: [deprecated] The name of a boolean parameter that will be exposed as `--no-wait`
to skip long-running operation polling. (string)
to skip long running operation polling. (string)
- transform: Transform function for transforming the output of the command (function)
- table_transformer: Transform function or JMESPath query to be applied to table output to create a
better output format for tables. (function or string)
Expand All @@ -771,20 +768,11 @@ def command(self, name, method_name=None, **kwargs):
- max_api: Maximum API version required for commands within the group (string)
:rtype: None
"""
self._check_stale()
merged_kwargs = self._flatten_kwargs(kwargs, 'command_type')
# don't inherit deprecation info from command group
merged_kwargs['deprecate_info'] = kwargs.get('deprecate_info', None)
operations_tmpl = merged_kwargs['operations_tmpl']
command_name = '{} {}'.format(self.group_name, name) if self.group_name else name
operation = operations_tmpl.format(method_name) if operations_tmpl else None
self.command_loader._cli_command(command_name, operation, **merged_kwargs) # pylint: disable=protected-access

return command_name
return self._command(name, method_name=method_name, **kwargs)

def custom_command(self, name, method_name, **kwargs):
def custom_command(self, name, method_name=None, **kwargs):
"""
Register a custom CLI command.
Register a CLI command.
:param name: Name of the command as it will be called on the command line
:type name: str
:param method_name: Name of the method the command maps to
Expand All @@ -805,8 +793,11 @@ def custom_command(self, name, method_name, **kwargs):
- max_api: Maximum API version required for commands within the group (string)
:rtype: None
"""
return self._command(name, method_name=method_name, custom_command=True, **kwargs)

def _command(self, name, method_name, custom_command=False, **kwargs):
self._check_stale()
merged_kwargs = self._flatten_kwargs(kwargs, 'custom_command_type')
merged_kwargs = self._flatten_kwargs(kwargs, get_command_type_kwarg(custom_command))
# don't inherit deprecation info from command group
merged_kwargs['deprecate_info'] = kwargs.get('deprecate_info', None)

Expand All @@ -819,12 +810,8 @@ def custom_command(self, name, method_name, **kwargs):
return command_name

# pylint: disable=no-self-use
def _resolve_operation(self, kwargs, name, command_type=None, source_kwarg='command_type'):

allowed_source_kwargs = ['command_type', 'custom_command_type']
if source_kwarg not in allowed_source_kwargs:
raise ValueError("command authoring error: 'source_kwarg' value '{}'. Allowed values: {}".format(
source_kwarg, ' '.join(allowed_source_kwargs)))
def _resolve_operation(self, kwargs, name, command_type=None, custom_command=False):
source_kwarg = get_command_type_kwarg(custom_command)

operations_tmpl = None
if command_type:
Expand Down Expand Up @@ -860,7 +847,7 @@ def generic_update_command(self, name,
getter_op = self._resolve_operation(merged_kwargs, getter_name, getter_type)
setter_op = self._resolve_operation(merged_kwargs, setter_name, setter_type)
custom_func_op = self._resolve_operation(merged_kwargs, custom_func_name, custom_func_type,
source_kwarg='custom_command_type') if custom_func_name else None
custom_command=True) if custom_func_name else None
_cli_generic_update_command(
self.command_loader,
'{} {}'.format(self.group_name, name),
Expand All @@ -873,31 +860,43 @@ def generic_update_command(self, name,
child_arg_name=child_arg_name,
**merged_kwargs)

def wait_command(self, name, getter_name='get', **kwargs):
self._wait_command(name, getter_name=getter_name, custom_command=False, **kwargs)

def custom_wait_command(self, name, getter_name='get', **kwargs):
self._wait_command(name, getter_name=getter_name, custom_command=True, **kwargs)

def generic_wait_command(self, name, getter_name='get', getter_type=None, **kwargs):
from azure.cli.core.commands.arm import _cli_generic_wait_command
self._wait_command(name, getter_name=getter_name, getter_type=getter_type, **kwargs)

def _wait_command(self, name, getter_name='get', getter_type=None, custom_command=False, **kwargs):
from azure.cli.core.commands.arm import _cli_wait_command
self._check_stale()
merged_kwargs = _merge_kwargs(kwargs, self.group_kwargs, CLI_COMMAND_KWARGS)
# don't inherit deprecation info from command group
merged_kwargs['deprecate_info'] = kwargs.get('deprecate_info', None)

if getter_type:
merged_kwargs = _merge_kwargs(getter_type.settings, merged_kwargs, CLI_COMMAND_KWARGS)
getter_op = self._resolve_operation(merged_kwargs, getter_name, getter_type)
_cli_generic_wait_command(
self.command_loader,
'{} {}'.format(self.group_name, name),
getter_op=getter_op,
**merged_kwargs)
getter_op = self._resolve_operation(merged_kwargs, getter_name, getter_type, custom_command=custom_command)
_cli_wait_command(self.command_loader, '{} {}'.format(self.group_name, name), getter_op=getter_op,
custom_command=custom_command, **merged_kwargs)

def generic_show_command(self, name, getter_name='get', getter_type=None, **kwargs):
from azure.cli.core.commands.arm import _cli_generic_show_command
def show_command(self, name, getter_name='get', **kwargs):
self._show_command(name, getter_name=getter_name, custom_command=False, **kwargs)

def custom_show_command(self, name, getter_name='get', **kwargs):
self._show_command(name, getter_name=getter_name, custom_command=True, **kwargs)

def _show_command(self, name, getter_name='get', getter_type=None, custom_command=False, **kwargs):
from azure.cli.core.commands.arm import _cli_show_command
self._check_stale()
merged_kwargs = _merge_kwargs(kwargs, self.group_kwargs, CLI_COMMAND_KWARGS)
# don't inherit deprecation info from command group
merged_kwargs['deprecate_info'] = kwargs.get('deprecate_info', None)

if getter_type:
merged_kwargs = _merge_kwargs(getter_type.settings, merged_kwargs, CLI_COMMAND_KWARGS)
getter_op = self._resolve_operation(merged_kwargs, getter_name, getter_type)
_cli_generic_show_command(
self.command_loader,
'{} {}'.format(self.group_name, name),
getter_op=getter_op,
**merged_kwargs)
getter_op = self._resolve_operation(merged_kwargs, getter_name, getter_type, custom_command=custom_command)
_cli_show_command(self.command_loader, '{} {}'.format(self.group_name, name), getter_op=getter_op,
custom_command=custom_command, **merged_kwargs)
Loading

0 comments on commit 663db88

Please sign in to comment.