-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
initial extension commit (rebased previous commits)
- Loading branch information
Showing
282 changed files
with
30,177 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -106,3 +106,6 @@ ENV/ | |
|
||
# Sphinx | ||
_build/ | ||
|
||
# Jet Brains | ||
.idea/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
|
||
# Key Vault Azure CLI Extension | ||
|
||
The Azure CLI extension for Key Vault is an extension which previews unreleased functionality in the keyvault command module. | ||
|
||
__NOTE__: The code for this extension is automatedly pulled from the [azure-sdk-for-python](https://github.com/azure/azure-sdk-for-python) and the [azure-cli](https://github.com/azure/azure-cli) repos using update_extension.py, and updated to run as an Azure CLI extension. Changes may cause incorrect behavior and will be lost if the code is regenerated. | ||
|
||
## Manually updating the Extension | ||
|
||
Clone the [azure-sdk-for-python](https://github.com/azure/azure-sdk-for-python) and the [azure-cli](https://github.com/azure/azure-cli) repos: | ||
|
||
$ git clone https://github.com/azure/azure-sdk-for-python.git | ||
$ git clone https://github.com/azure/azure-cli | ||
|
||
Using python 3.* run update_extension.py: | ||
|
||
$ python update_extension.py --sdk <azure-sdk-for-python clone root> --cli <azure-cli clone root> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
# pylint: skip-file | ||
# --------------------------------------------------------------------------- | ||
# The code for this extension file is pulled from the azure-cli repo. Changes may | ||
# cause incorrect behavior and will be lost if the code is regenerated. | ||
# Please see the readme.md at the base of the keyvault extension for details. | ||
# --------------------------------------------------------------------------- | ||
# -------------------------------------------------------------------------------------------- | ||
# Copyright (c) Microsoft Corporation. All rights reserved. | ||
# Licensed under the MIT License. See License.txt in the project root for license information. | ||
# -------------------------------------------------------------------------------------------- | ||
|
||
from azure.cli.core import AzCommandsLoader | ||
|
||
import azext_keyvault._help # pylint: disable=unused-import | ||
|
||
|
||
class KeyVaultCommandsLoader(AzCommandsLoader): | ||
|
||
def __init__(self, cli_ctx=None): | ||
from azure.cli.core.commands import CliCommandType | ||
from ._client_factory import keyvault_client_factory | ||
from ._command_type import KeyVaultCommandGroup, KeyVaultArgumentContext | ||
keyvault_custom = CliCommandType( | ||
operations_tmpl='azext_keyvault.custom#{}', | ||
client_factory=keyvault_client_factory | ||
) | ||
super(KeyVaultCommandsLoader, self).__init__(cli_ctx=cli_ctx, | ||
custom_command_type=keyvault_custom, | ||
command_group_cls=KeyVaultCommandGroup, | ||
argument_context_cls=KeyVaultArgumentContext) | ||
|
||
def load_command_table(self, args): | ||
from .commands import load_command_table | ||
load_command_table(self, args) | ||
return self.command_table | ||
|
||
def load_arguments(self, command): | ||
from ._params import load_arguments | ||
load_arguments(self, command) | ||
|
||
|
||
COMMAND_LOADER_CLS = KeyVaultCommandsLoader |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
# pylint: skip-file | ||
# --------------------------------------------------------------------------- | ||
# The code for this extension file is pulled from the azure-cli repo. Changes may | ||
# cause incorrect behavior and will be lost if the code is regenerated. | ||
# Please see the readme.md at the base of the keyvault extension for details. | ||
# --------------------------------------------------------------------------- | ||
# -------------------------------------------------------------------------------------------- | ||
# Copyright (c) Microsoft Corporation. All rights reserved. | ||
# Licensed under the MIT License. See License.txt in the project root for license information. | ||
# -------------------------------------------------------------------------------------------- | ||
|
||
|
||
def keyvault_client_factory(cli_ctx, **_): | ||
from azure.cli.core.commands.client_factory import get_mgmt_service_client | ||
from azext_keyvault.mgmt.keyvault import KeyVaultManagementClient | ||
return get_mgmt_service_client(cli_ctx, KeyVaultManagementClient) | ||
|
||
|
||
def keyvault_client_vaults_factory(cli_ctx, _): | ||
return keyvault_client_factory(cli_ctx).vaults | ||
|
||
|
||
def keyvault_data_plane_factory(cli_ctx, _): | ||
from azext_keyvault.keyvault import KeyVaultClient, KeyVaultAuthentication | ||
|
||
def get_token(server, resource, scope): # pylint: disable=unused-argument | ||
import adal | ||
from azure.cli.core._profile import Profile | ||
try: | ||
return Profile(cli_ctx=cli_ctx).get_login_credentials(resource)[0]._token_retriever() # pylint: disable=protected-access | ||
except adal.AdalError as err: | ||
from knack.util import CLIError | ||
# pylint: disable=no-member | ||
if (hasattr(err, 'error_response') and | ||
('error_description' in err.error_response) and | ||
('AADSTS70008:' in err.error_response['error_description'])): | ||
raise CLIError( | ||
"Credentials have expired due to inactivity. Please run 'az login'") | ||
raise CLIError(err) | ||
|
||
return KeyVaultClient(KeyVaultAuthentication(get_token)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,164 @@ | ||
# pylint: skip-file | ||
# --------------------------------------------------------------------------- | ||
# The code for this extension file is pulled from the azure-cli repo. Changes may | ||
# cause incorrect behavior and will be lost if the code is regenerated. | ||
# Please see the readme.md at the base of the keyvault extension for details. | ||
# --------------------------------------------------------------------------- | ||
# -------------------------------------------------------------------------------------------- | ||
# Copyright (c) Microsoft Corporation. All rights reserved. | ||
# Licensed under the MIT License. See License.txt in the project root for license information. | ||
# -------------------------------------------------------------------------------------------- | ||
|
||
import base64 | ||
|
||
from knack.introspection import extract_full_summary_from_signature, extract_args_from_signature | ||
from knack.util import CLIError | ||
|
||
from azure.cli.core.commands import LongRunningOperation, AzCommandGroup, AzArgumentContext | ||
|
||
|
||
def _encode_hex(item): | ||
""" Recursively crawls the object structure and converts bytes or bytearrays to base64 | ||
encoded strings. """ | ||
if isinstance(item, list): | ||
return [_encode_hex(x) for x in item] | ||
elif hasattr(item, '__dict__'): | ||
for key, val in item.__dict__.items(): | ||
if not key.startswith('_'): | ||
try: | ||
setattr(item, key, _encode_hex(val)) | ||
except TypeError: | ||
item.__dict__[key] = _encode_hex(val) | ||
return item | ||
elif isinstance(item, (bytes, bytearray)): | ||
return base64.b64encode(item).decode('utf-8') | ||
return item | ||
|
||
|
||
def keyvault_exception_handler(ex): | ||
from msrest.exceptions import ValidationError, ClientRequestError | ||
from azext_keyvault.keyvault.models import KeyVaultErrorException | ||
if isinstance(ex, (ValidationError, KeyVaultErrorException)): | ||
try: | ||
raise CLIError(ex.inner_exception.error.message) | ||
except AttributeError: | ||
raise CLIError(ex) | ||
elif isinstance(ex, ClientRequestError): | ||
if 'Failed to establish a new connection' in str(ex.inner_exception): | ||
raise CLIError('Max retries exceeded attempting to connect to vault. ' | ||
'The vault may not exist or you may need to flush your DNS cache ' | ||
'and try again later.') | ||
raise CLIError(ex) | ||
|
||
|
||
class KeyVaultCommandGroup(AzCommandGroup): | ||
|
||
def __init__(self, command_loader, group_name, **kwargs): | ||
from ._client_factory import keyvault_data_plane_factory | ||
# all regular and custom commands should use the keyvault data plane client | ||
merged_kwargs = self._merge_kwargs(kwargs, base_kwargs=command_loader.module_kwargs) | ||
merged_kwargs['custom_command_type'].settings['client_factory'] = keyvault_data_plane_factory | ||
super(KeyVaultCommandGroup, self).__init__(command_loader, group_name, **kwargs) | ||
|
||
def _create_keyvault_command(self, name, method_name=None, command_type_name=None, **kwargs): | ||
self._check_stale() | ||
|
||
merged_kwargs = self._flatten_kwargs(kwargs, command_type_name) | ||
operations_tmpl = merged_kwargs['operations_tmpl'] | ||
command_name = '{} {}'.format(self.group_name, name) if self.group_name else name | ||
|
||
def get_op_handler(): | ||
return self.command_loader.get_op_handler(operations_tmpl.format(method_name)) | ||
|
||
def keyvault_arguments_loader(): | ||
op = get_op_handler() | ||
self.command_loader._apply_doc_string(op, merged_kwargs) # pylint: disable=protected-access | ||
cmd_args = list( | ||
extract_args_from_signature(op, excluded_params=self.command_loader.excluded_command_handler_args)) | ||
return cmd_args | ||
|
||
def keyvault_description_loader(): | ||
op = get_op_handler() | ||
self.command_loader._apply_doc_string(op, merged_kwargs) # pylint: disable=protected-access | ||
return extract_full_summary_from_signature(op) | ||
|
||
def keyvault_command_handler(command_args): | ||
from azure.cli.core.util import get_arg_list | ||
from azure.cli.core.commands.client_factory import resolve_client_arg_name | ||
from msrest.paging import Paged | ||
from azure.cli.core.util import poller_classes | ||
|
||
op = get_op_handler() | ||
op_args = get_arg_list(op) | ||
command_type = merged_kwargs.get('command_type', None) | ||
client_factory = command_type.settings.get('client_factory', None) if command_type \ | ||
else merged_kwargs.get('client_factory', None) | ||
|
||
client_arg_name = resolve_client_arg_name(operations_tmpl.format(method_name), kwargs) | ||
if client_arg_name in op_args: | ||
client = client_factory(self.command_loader.cli_ctx, command_args) | ||
command_args[client_arg_name] = client | ||
try: | ||
result = op(**command_args) | ||
# apply results transform if specified | ||
transform_result = merged_kwargs.get('transform', None) | ||
if transform_result: | ||
return _encode_hex(transform_result(result)) | ||
|
||
# otherwise handle based on return type of results | ||
if isinstance(result, poller_classes()): | ||
return _encode_hex( | ||
LongRunningOperation(self.command_loader.cli_ctx, 'Starting {}'.format(name))(result)) | ||
elif isinstance(result, Paged): | ||
try: | ||
return _encode_hex(list(result)) | ||
except TypeError: | ||
# TODO: Workaround for an issue in either KeyVault server-side or msrest | ||
# See https://github.com/Azure/autorest/issues/1309 | ||
return [] | ||
else: | ||
return _encode_hex(result) | ||
except Exception as ex: # pylint: disable=broad-except | ||
return keyvault_exception_handler(ex) | ||
|
||
self.command_loader._cli_command(command_name, handler=keyvault_command_handler, # pylint: disable=protected-access | ||
argument_loader=keyvault_arguments_loader, | ||
description_loader=keyvault_description_loader, | ||
**merged_kwargs) | ||
|
||
def keyvault_command(self, name, method_name=None, command_type=None, **kwargs): | ||
""" Registers an Azure CLI KeyVault Data Plane command. These commands must respond to a | ||
challenge from the service when they make requests. """ | ||
command_type_name = 'command_type' | ||
if command_type: | ||
kwargs[command_type_name] = command_type | ||
self._create_keyvault_command(name, method_name, command_type_name, **kwargs) | ||
|
||
def keyvault_custom(self, name, method_name=None, command_type=None, **kwargs): | ||
command_type_name = 'custom_command_type' | ||
if command_type: | ||
kwargs[command_type_name] = command_type | ||
self._create_keyvault_command(name, method_name, command_type_name, **kwargs) | ||
|
||
|
||
class KeyVaultArgumentContext(AzArgumentContext): | ||
|
||
def attributes_argument(self, name, attr_class, create=False, ignore=None): | ||
from ._validators import get_attribute_validator, datetime_type | ||
from azure.cli.core.commands.parameters import get_three_state_flag | ||
|
||
from knack.arguments import ignore_type | ||
|
||
ignore = ignore or [] | ||
self.argument('{}_attributes'.format(name), ignore_type, | ||
validator=get_attribute_validator(name, attr_class, create)) | ||
if create: | ||
self.extra('disabled', help='Create {} in disabled state.'.format(name), arg_type=get_three_state_flag()) | ||
else: | ||
self.extra('enabled', help='Enable the {}.'.format(name), arg_type=get_three_state_flag()) | ||
if 'expires' not in ignore: | ||
self.extra('expires', default=None, help='Expiration UTC datetime (Y-m-d\'T\'H:M:S\'Z\').', | ||
type=datetime_type) | ||
if 'not_before' not in ignore: | ||
self.extra('not_before', default=None, type=datetime_type, | ||
help='Key not usable before the provided UTC datetime (Y-m-d\'T\'H:M:S\'Z\').') |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
# pylint: skip-file | ||
# --------------------------------------------------------------------------- | ||
# The code for this extension file is pulled from the azure-cli repo. Changes may | ||
# cause incorrect behavior and will be lost if the code is regenerated. | ||
# Please see the readme.md at the base of the keyvault extension for details. | ||
# --------------------------------------------------------------------------- | ||
# -------------------------------------------------------------------------------------------- | ||
# Copyright (c) Microsoft Corporation. All rights reserved. | ||
# Licensed under the MIT License. See License.txt in the project root for license information. | ||
# -------------------------------------------------------------------------------------------- | ||
|
||
from azure.cli.core.decorators import Completer | ||
from azure.cli.core._profile import Profile | ||
|
||
|
||
def _get_token(cli_ctx, server, resource, scope): # pylint: disable=unused-argument | ||
return Profile(cli_ctx=cli_ctx).get_login_credentials(resource)[0]._token_retriever() # pylint: disable=protected-access | ||
|
||
|
||
def get_keyvault_name_completion_list(resource_name): | ||
|
||
@Completer | ||
def completer(cmd, prefix, namespace, **kwargs): # pylint: disable=unused-argument | ||
from azext_keyvault.keyvault import KeyVaultClient, KeyVaultAuthentication | ||
client = KeyVaultClient(KeyVaultAuthentication(_get_token)) | ||
func_name = 'get_{}s'.format(resource_name) | ||
vault = namespace.vault_base_url | ||
items = [] | ||
for y in list(getattr(client, func_name)(vault)): | ||
id_val = getattr(y, 'id', None) or getattr(y, 'kid', None) | ||
items.append(id_val.rsplit('/', 1)[1]) | ||
return items | ||
|
||
return completer | ||
|
||
|
||
def get_keyvault_version_completion_list(resource_name): | ||
|
||
@Completer | ||
def completer(cmd, prefix, namespace, **kwargs): # pylint: disable=unused-argument | ||
from azext_keyvault.keyvault import KeyVaultClient, KeyVaultAuthentication | ||
client = KeyVaultClient(KeyVaultAuthentication(_get_token)) | ||
func_name = 'get_{}_versions'.format(resource_name) | ||
vault = namespace.vault_base_url | ||
name = getattr(namespace, '{}_name'.format(resource_name)) | ||
items = [] | ||
for y in list(getattr(client, func_name)(vault, name)): | ||
id_val = getattr(y, 'id', None) or getattr(y, 'kid', None) | ||
items.append(id_val.rsplit('/', 1)[1]) | ||
return items | ||
|
||
return completer |
Oops, something went wrong.