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

Worm feature and listing usage by region #181

Merged
merged 7 commits into from
May 15, 2018
Merged
Show file tree
Hide file tree
Changes from all 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
8 changes: 4 additions & 4 deletions src/index.json
Original file line number Diff line number Diff line change
Expand Up @@ -829,9 +829,9 @@
],
"storage-preview": [
{
"filename": "storage_preview-0.1.0-py2.py3-none-any.whl",
"sha256Digest": "36768962d09c65b9668581f5bc01f1ad252acf832f22afdb04ed00c3333379cc",
"downloadUrl": "https://azurecliprod.blob.core.windows.net/cli-extensions/storage_preview-0.1.0-py2.py3-none-any.whl",
"filename": "storage_preview-0.1.1-py2.py3-none-any.whl",
"sha256Digest": "4447f8b825559e2b94bbdcaa2a1af1723a2151fad670ff88162e8d2567e715cf",
"downloadUrl": "https://azurecliprod.blob.core.windows.net/cli-extensions/storage_preview-0.1.1-py2.py3-none-any.whl",
"metadata": {
"azext.isPreview": true,
"azext.minCliCoreVersion": "2.0.32.dev0",
Expand Down Expand Up @@ -870,7 +870,7 @@
"metadata_version": "2.0",
"name": "storage-preview",
"summary": "Provides a preview for upcoming storage features.",
"version": "0.1.0"
"version": "0.1.1"
}
}
]
Expand Down
15 changes: 10 additions & 5 deletions src/storage-preview/azext_storage_preview/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,23 @@
# --------------------------------------------------------------------------------------------

from azure.cli.core import AzCommandsLoader
from azure.cli.core.profiles import ResourceType, register_resource_type
from azure.cli.core.profiles import register_resource_type
from azure.cli.core.commands import AzCommandGroup, AzArgumentContext

import azext_storage_preview._help # pylint: disable=unused-import
from .profiles import CUSTOM_DATA_STORAGE
from .profiles import CUSTOM_DATA_STORAGE, CUSTOM_MGMT_STORAGE


class StorageCommandsLoader(AzCommandsLoader):
def __init__(self, cli_ctx=None):
from azure.cli.core.commands import CliCommandType

register_resource_type('latest', CUSTOM_DATA_STORAGE, '2017-11-09')
register_resource_type('latest', CUSTOM_MGMT_STORAGE, '2018-02-01')
storage_custom = CliCommandType(operations_tmpl='azext_storage_preview.custom#{}')

super(StorageCommandsLoader, self).__init__(cli_ctx=cli_ctx,
min_profile='2017-03-10-profile',
resource_type=CUSTOM_DATA_STORAGE,
custom_command_type=storage_custom,
command_group_cls=StorageCommandGroup,
Expand Down Expand Up @@ -107,12 +109,12 @@ def register_common_storage_account_options(self):
from ._validators import validate_encryption_services

t_access_tier, t_sku_name, t_encryption_services = self.command_loader.get_models(
'AccessTier', 'SkuName', 'EncryptionServices', resource_type=ResourceType.MGMT_STORAGE)
'AccessTier', 'SkuName', 'EncryptionServices', resource_type=CUSTOM_MGMT_STORAGE)

self.argument('https_only', help='Allows https traffic only to storage service.',
arg_type=get_three_state_flag())
self.argument('sku', help='The storage account SKU.', arg_type=get_enum_type(t_sku_name))
self.argument('assign_identity', action='store_true', resource_type=ResourceType.MGMT_STORAGE,
self.argument('assign_identity', action='store_true', resource_type=CUSTOM_MGMT_STORAGE,
min_api='2017-06-01',
help='Generate and assign a new Storage Account Identity for this storage account for use '
'with key management services like Azure KeyVault.')
Expand All @@ -125,7 +127,7 @@ def register_common_storage_account_options(self):
encryption_choices = list(
t_encryption_services._attribute_map.keys()) # pylint: disable=protected-access
self.argument('encryption_services', arg_type=get_enum_type(encryption_choices),
resource_type=ResourceType.MGMT_STORAGE, min_api='2016-12-01', nargs='+',
resource_type=CUSTOM_MGMT_STORAGE, min_api='2016-12-01', nargs='+',
validator=validate_encryption_services, help='Specifies which service(s) to encrypt.')


Expand Down Expand Up @@ -196,14 +198,17 @@ def handler(ex):

def _register_data_plane_account_arguments(self, command_name):
""" Add parameters required to create a storage client """
from azure.cli.core.commands.parameters import get_resource_name_completion_list
from ._validators import validate_client_parameters
command = self.command_loader.command_table.get(command_name, None)
if not command:
return

group_name = 'Storage Account'

command.add_argument('account_name', '--account-name', required=False, default=None,
arg_group=group_name,
completer=get_resource_name_completion_list('Microsoft.Storage/storageAccounts'),
help='Storage account name. Related environment variable: AZURE_STORAGE_ACCOUNT. Must be '
'used in conjunction with either storage account key or a SAS token. If neither are '
'present, the command will try to query the storage account key using the '
Expand Down
10 changes: 7 additions & 3 deletions src/storage-preview/azext_storage_preview/_client_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@
# --------------------------------------------------------------------------------------------

from azure.cli.core.commands.client_factory import get_mgmt_service_client, _get_add_headers_callback
from azure.cli.core.profiles import ResourceType, get_sdk
from azure.cli.core.profiles import get_sdk
from knack.util import CLIError
from knack.log import get_logger

from .sdkutil import get_table_data_type
from .profiles import CUSTOM_DATA_STORAGE
from .profiles import CUSTOM_DATA_STORAGE, CUSTOM_MGMT_STORAGE

NO_CREDENTIALS_ERROR_MESSAGE = """
No credentials specified to access storage service. Please provide any of the following:
Expand Down Expand Up @@ -75,7 +75,7 @@ def generic_data_service_factory(cli_ctx, service, name=None, key=None, connecti


def storage_client_factory(cli_ctx, **_):
return get_mgmt_service_client(cli_ctx, ResourceType.MGMT_STORAGE)
return get_mgmt_service_client(cli_ctx, CUSTOM_MGMT_STORAGE)


def file_data_service_factory(cli_ctx, kwargs):
Expand Down Expand Up @@ -164,3 +164,7 @@ def get_creator(name, service_type):

def cf_sa(cli_ctx, _):
return storage_client_factory(cli_ctx).storage_accounts


def cf_blob_container_mgmt(cli_ctx, _):
return storage_client_factory(cli_ctx).blob_containers
8 changes: 8 additions & 0 deletions src/storage-preview/azext_storage_preview/_format.py
Original file line number Diff line number Diff line change
Expand Up @@ -148,3 +148,11 @@ def transformer(result):

return return_list
return transformer


def transform_immutability_policy(result):
# service returns policy with period value of "0" after it has been deleted
# this only shows the policy if the property value is greater than 0
if result.immutability_period_since_creation_in_days:
return result
return None
15 changes: 15 additions & 0 deletions src/storage-preview/azext_storage_preview/_help.py
Original file line number Diff line number Diff line change
Expand Up @@ -403,6 +403,21 @@
short-summary: Manage container stored access policies.
"""

helps['storage container immutability-policy'] = """
type: group
short-summary: Manage container immutability policies.
"""

helps['storage container legal-hold'] = """
type: group
short-summary: Manage container legal holds.
"""

helps['storage container legal-hold show'] = """
type: command
short-summary: Get the legal hold properties of a container.
"""

helps['storage cors'] = """
type: group
short-summary: Manage storage service Cross-Origin Resource Sharing (CORS).
Expand Down
26 changes: 18 additions & 8 deletions src/storage-preview/azext_storage_preview/_params.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
# Licensed under the MIT License. See License.txt in the project root for license information.
# --------------------------------------------------------------------------------------------

from azure.cli.core.profiles import ResourceType
from azure.cli.core.commands.validators import get_default_location_from_resource_group
from azure.cli.core.commands.parameters import (tags_type, file_type, get_location_type, get_enum_type)

Expand All @@ -13,6 +12,7 @@
validate_table_payload_format, validate_key, add_progress_callback,
storage_account_key_options, process_file_download_namespace, process_metric_update_namespace,
get_char_options_validator, validate_bypass, validate_encryption_source)
from .profiles import CUSTOM_MGMT_STORAGE


def load_arguments(self, _): # pylint: disable=too-many-locals, too-many-statements
Expand All @@ -24,7 +24,7 @@ def load_arguments(self, _): # pylint: disable=too-many-locals, too-many-statem
from azure.cli.core.commands.parameters import get_resource_name_completion_list

from .sdkutil import get_table_data_type
from .completers import get_storage_name_completion_list
from .completers import get_storage_name_completion_list, get_container_name_completions

t_base_blob_service = self.get_sdk('blob.baseblobservice#BaseBlobService')
t_file_service = self.get_sdk('file#FileService')
Expand All @@ -39,8 +39,7 @@ def load_arguments(self, _): # pylint: disable=too-many-locals, too-many-statem
parent='container_name'))

container_name_type = CLIArgumentType(options_list=['--container-name', '-c'], help='The container name.',
completer=get_storage_name_completion_list(t_base_blob_service,
'list_containers'))
completer=get_container_name_completions)
directory_type = CLIArgumentType(options_list=['--directory-name', '-d'], help='The directory name.',
completer=get_storage_name_completion_list(t_file_service,
'list_directories_and_files',
Expand Down Expand Up @@ -84,7 +83,7 @@ def load_arguments(self, _): # pylint: disable=too-many-locals, too-many-statem
c.argument('if_match')
c.argument('if_none_match')

for item in ['delete', 'list', 'show', 'show-usage', 'update', 'keys']:
for item in ['delete', 'list', 'show', 'update', 'keys']:
with self.argument_context('storage account {}'.format(item)) as c:
c.argument('account_name', acct_name_type, options_list=['--name', '-n'])

Expand All @@ -93,7 +92,7 @@ def load_arguments(self, _): # pylint: disable=too-many-locals, too-many-statem

with self.argument_context('storage account create') as c:
t_account_type, t_sku_name, t_kind = self.get_models('AccountType', 'SkuName', 'Kind',
resource_type=ResourceType.MGMT_STORAGE)
resource_type=CUSTOM_MGMT_STORAGE)

c.register_common_storage_account_options()
c.argument('location', get_location_type(self.cli_ctx), validator=get_default_location_from_resource_group)
Expand Down Expand Up @@ -126,10 +125,10 @@ def load_arguments(self, _): # pylint: disable=too-many-locals, too-many-statem
c.ignore('encryption_key_vault_properties')

for scope in ['storage account create', 'storage account update']:
with self.argument_context(scope, resource_type=ResourceType.MGMT_STORAGE, min_api='2017-06-01',
with self.argument_context(scope, resource_type=CUSTOM_MGMT_STORAGE, min_api='2017-06-01',
arg_group='Network Rule') as c:
t_bypass, t_default_action = self.get_models('Bypass', 'DefaultAction',
resource_type=ResourceType.MGMT_STORAGE)
resource_type=CUSTOM_MGMT_STORAGE)

c.argument('bypass', nargs='+', validator=validate_bypass, arg_type=get_enum_type(t_bypass),
help='Bypass traffic for space-separated uses.')
Expand Down Expand Up @@ -417,6 +416,17 @@ def load_arguments(self, _): # pylint: disable=too-many-locals, too-many-statem
c.argument('lease_duration', type=int)
c.argument('lease_break_period', type=int)

with self.argument_context('storage container immutability-policy') as c:
c.argument('immutability_period_since_creation_in_days', options_list='--period')
c.argument('container_name', container_name_type)
c.argument('account_name', completer=get_resource_name_completion_list('Microsoft.Storage/storageAccounts'))

with self.argument_context('storage container legal-hold') as c:
c.argument('container_name', container_name_type)
c.argument('account_name', completer=get_resource_name_completion_list('Microsoft.Storage/storageAccounts'))
c.argument('tags', nargs='+', help='Each tag should be 3 to 23 alphanumeric characters and is '
'normalized to lower case')

with self.argument_context('storage share') as c:
c.argument('share_name', share_name_type, options_list=('--name', '-n'))

Expand Down
11 changes: 6 additions & 5 deletions src/storage-preview/azext_storage_preview/_validators.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,14 @@
# pylint: disable=protected-access
from azure.cli.core.commands.client_factory import get_mgmt_service_client
from azure.cli.core.commands.validators import validate_key_value_pairs
from azure.cli.core.profiles import ResourceType, get_sdk
from azure.cli.core.profiles import get_sdk

from ._client_factory import get_storage_data_service_client
from .util import glob_files_locally, guess_content_type
from .sdkutil import get_table_data_type
from .url_quote_util import encode_for_url
from .oauth_token_util import TokenUpdater
from .profiles import CUSTOM_MGMT_STORAGE

storage_account_key_options = {'primary': 'key1', 'secondary': 'key2'}

Expand All @@ -23,15 +24,15 @@
# pylint: disable=inconsistent-return-statements
def _query_account_key(cli_ctx, account_name):
"""Query the storage account key. This is used when the customer doesn't offer account key but name."""
scf = get_mgmt_service_client(cli_ctx, ResourceType.MGMT_STORAGE)
scf = get_mgmt_service_client(cli_ctx, CUSTOM_MGMT_STORAGE)
acc = next((x for x in scf.storage_accounts.list() if x.name == account_name), None)
if acc:
from msrestazure.tools import parse_resource_id
rg = parse_resource_id(acc.id)['resource_group']

t_storage_account_keys, t_storage_account_list_keys_results = get_sdk(
cli_ctx,
ResourceType.MGMT_STORAGE,
CUSTOM_MGMT_STORAGE,
'models.storage_account_keys#StorageAccountKeys',
'models.storage_account_list_keys_result#StorageAccountListKeysResult')

Expand Down Expand Up @@ -398,7 +399,7 @@ def validate_encryption_services(cmd, namespace):
Builds up the encryption services object for storage account operations based on the list of services passed in.
"""
if namespace.encryption_services:
t_encryption_services, t_encryption_service = get_sdk(cmd.cli_ctx, ResourceType.MGMT_STORAGE,
t_encryption_services, t_encryption_service = get_sdk(cmd.cli_ctx, CUSTOM_MGMT_STORAGE,
'EncryptionServices', 'EncryptionService', mod='models')
services = {service: t_encryption_service(enabled=True) for service in namespace.encryption_services}

Expand All @@ -420,7 +421,7 @@ def validate_encryption_source(cmd, namespace):
if namespace.encryption_key_source != 'Microsoft.Keyvault':
raise ValueError('--encryption-key-name, --encryption-key-vault, and --encryption-key-version are not '
'applicable when --encryption-key-source=Microsoft.Keyvault is not specified.')
KeyVaultProperties = get_sdk(cmd.cli_ctx, ResourceType.MGMT_STORAGE, 'KeyVaultProperties',
KeyVaultProperties = get_sdk(cmd.cli_ctx, CUSTOM_MGMT_STORAGE, 'KeyVaultProperties',
mod='models')
if not KeyVaultProperties:
return
Expand Down
30 changes: 25 additions & 5 deletions src/storage-preview/azext_storage_preview/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,21 @@

from azure.cli.core.commands import CliCommandType
from azure.cli.core.profiles import ResourceType
from ._client_factory import (cf_sa, blob_data_service_factory,
from ._client_factory import (cf_sa, cf_blob_container_mgmt, blob_data_service_factory,
page_blob_service_factory, file_data_service_factory,
queue_data_service_factory, table_data_service_factory,
cloud_storage_account_service_factory,
multi_service_properties_factory)
from .sdkutil import cosmosdb_table_exists
from .profiles import CUSTOM_DATA_STORAGE
from .profiles import CUSTOM_DATA_STORAGE, CUSTOM_MGMT_STORAGE
from ._format import transform_immutability_policy


def load_command_table(self, _): # pylint: disable=too-many-locals, too-many-statements
storage_account_sdk = CliCommandType(
operations_tmpl='azure.mgmt.storage.operations.storage_accounts_operations#StorageAccountsOperations.{}',
client_factory=cf_sa,
resource_type=ResourceType.MGMT_STORAGE
resource_type=CUSTOM_MGMT_STORAGE
)

storage_account_custom_type = CliCommandType(
Expand All @@ -40,7 +41,7 @@ def get_custom_sdk(custom_module, client_factory, resource_type=CUSTOM_DATA_STOR
resource_type=resource_type
)

with self.command_group('storage account', storage_account_sdk, resource_type=ResourceType.MGMT_STORAGE,
with self.command_group('storage account', storage_account_sdk, resource_type=CUSTOM_MGMT_STORAGE,
custom_command_type=storage_account_custom_type) as g:
g.command('check-name', 'check_name_availability')
g.custom_command('create', 'create_storage_account', min_api='2016-01-01')
Expand All @@ -57,7 +58,7 @@ def get_custom_sdk(custom_module, client_factory, resource_type=CUSTOM_DATA_STOR

with self.command_group('storage account network-rule', storage_account_sdk,
custom_command_type=storage_account_custom_type,
resource_type=ResourceType.MGMT_STORAGE, min_api='2017-06-01') as g:
resource_type=CUSTOM_MGMT_STORAGE, min_api='2017-06-01') as g:
g.custom_command('add', 'add_network_rule')
g.custom_command('list', 'list_network_rules')
g.custom_command('remove', 'remove_network_rule')
Expand Down Expand Up @@ -190,6 +191,25 @@ def get_custom_sdk(custom_module, client_factory, resource_type=CUSTOM_DATA_STOR
g.storage_custom_command_oauth('policy show', 'get_acl_policy', exception_handler=g.get_handler_suppress_404())
g.storage_custom_command_oauth('policy list', 'list_acl_policies', table_transformer=transform_acl_list_output)

blob_container_mgmt_sdk = CliCommandType(
operations_tmpl='azext_storage_preview.vendored_sdks.azure_mgmt_storage.operations.blob_containers_operations'
'#BlobContainersOperations.{}',
client_factory=cf_blob_container_mgmt,
resource_type=CUSTOM_MGMT_STORAGE
)

with self.command_group('storage container immutability-policy', command_type=blob_container_mgmt_sdk) as g:
g.command('show', 'get_immutability_policy', transform=transform_immutability_policy)
g.command('create', 'create_or_update_immutability_policy')
g.command('delete', 'delete_immutability_policy', transform=lambda x: None)
g.command('lock', 'lock_immutability_policy')
g.command('extend', 'extend_immutability_policy')

with self.command_group('storage container legal-hold', command_type=blob_container_mgmt_sdk) as g:
g.command('set', 'set_legal_hold')
g.command('clear', 'clear_legal_hold')
g.command('show', 'get', transform=lambda x: getattr(x, 'legal_hold', x))

file_sdk = CliCommandType(
operations_tmpl='azure.multiapi.storage.file.fileservice#FileService.{}',
client_factory=file_data_service_factory,
Expand Down
Loading