From 7687f916f7173adee48fc49acbdc7022189d5d47 Mon Sep 17 00:00:00 2001 From: Zunli Hu Date: Wed, 3 Feb 2021 11:52:01 +0800 Subject: [PATCH] {storage-blob-preview} Support blob service properties with track 2 SDK (#2878) * delete rention support * support service propeties * add release note * enable logging * enable logging * fix linter --- src/storage-blob-preview/HISTORY.rst | 4 + .../_client_factory.py | 42 +- .../azext_storage_blob_preview/_params.py | 20 + .../_transformers.py | 17 + .../azext_storage_blob_preview/_validators.py | 5 +- .../azext_metadata.json | 2 +- .../azext_storage_blob_preview/commands.py | 18 + .../operations/blob.py | 48 +- .../test_storage_blob_soft_delete.yaml | 465 ++++++++++++++++++ ...torage_blob_update_service_properties.yaml | 340 +++++++++++++ .../latest/test_storage_blob_scenarios.py | 66 +++ src/storage-blob-preview/setup.py | 2 +- 12 files changed, 958 insertions(+), 71 deletions(-) create mode 100644 src/storage-blob-preview/azext_storage_blob_preview/tests/latest/recordings/test_storage_blob_soft_delete.yaml create mode 100644 src/storage-blob-preview/azext_storage_blob_preview/tests/latest/recordings/test_storage_blob_update_service_properties.yaml diff --git a/src/storage-blob-preview/HISTORY.rst b/src/storage-blob-preview/HISTORY.rst index f29bef47352..c8e3dc2aeb1 100644 --- a/src/storage-blob-preview/HISTORY.rst +++ b/src/storage-blob-preview/HISTORY.rst @@ -2,6 +2,10 @@ Release History =============== +0.4.1 +++++++ +* `az storage blob service-properties`: Adopt new api version with track2 SDK + 0.4.0 ++++++ * Support blob url for blob related commands diff --git a/src/storage-blob-preview/azext_storage_blob_preview/_client_factory.py b/src/storage-blob-preview/azext_storage_blob_preview/_client_factory.py index 10a75ab404c..0bc26148af9 100644 --- a/src/storage-blob-preview/azext_storage_blob_preview/_client_factory.py +++ b/src/storage-blob-preview/azext_storage_blob_preview/_client_factory.py @@ -5,6 +5,7 @@ from azure.cli.core.commands.client_factory import get_mgmt_service_client from azure.cli.core.profiles import ResourceType, get_sdk +from azure.cli.core.commands.client_factory import _prepare_client_kwargs_track2 from .profiles import CUSTOM_DATA_STORAGE_BLOB, CUSTOM_MGMT_STORAGE MISSING_CREDENTIALS_ERROR_MESSAGE = """ @@ -55,7 +56,7 @@ def get_credential(kwargs): def cf_blob_service(cli_ctx, kwargs): from knack.util import CLIError - client_args = {} + client_kwargs = {} t_blob_service = get_sdk(cli_ctx, CUSTOM_DATA_STORAGE_BLOB, '_blob_service_client#BlobServiceClient') connection_string = kwargs.pop('connection_string', None) @@ -63,16 +64,16 @@ def cf_blob_service(cli_ctx, kwargs): location_mode = kwargs.pop('location_mode', None) if location_mode: - client_args['_location_mode'] = location_mode - + client_kwargs['_location_mode'] = location_mode + client_kwargs.update(_prepare_client_kwargs_track2(cli_ctx)) if connection_string: - return t_blob_service.from_connection_string(conn_str=connection_string) + return t_blob_service.from_connection_string(conn_str=connection_string, **client_kwargs) account_url = get_account_url(cli_ctx, account_name=account_name, service='blob') credential = get_credential(kwargs) if account_url and credential: - return t_blob_service(account_url=account_url, credential=credential, **client_args) + return t_blob_service(account_url=account_url, credential=credential, **client_kwargs) raise CLIError("Please provide valid connection string, or account name with account key, " "sas token or login auth mode.") @@ -121,34 +122,3 @@ def cf_blob_sas(cli_ctx, kwargs): return t_blob_sas(account_name=kwargs.pop('account_name', None), account_key=kwargs.pop('account_key', None)) - - -def cf_adls_service(cli_ctx, kwargs): - t_adls_service = get_sdk(cli_ctx, ResourceType.DATA_STORAGE_FILEDATALAKE, - '_data_lake_service_client#DataLakeServiceClient') - connection_string = kwargs.pop('connection_string', None) - account_key = kwargs.pop('account_key', None) - token_credential = kwargs.pop('token_credential', None) - sas_token = kwargs.pop('sas_token', None) - if connection_string: - return t_adls_service.from_connection_string(connection_string=connection_string) - - account_url = get_account_url(cli_ctx, account_name=kwargs.pop('account_name', None), service='dfs') - credential = account_key or sas_token or token_credential - - if account_url and credential: - return t_adls_service(account_url=account_url, credential=credential) - return None - - -def cf_adls_file_system(cli_ctx, kwargs): - return cf_adls_service(cli_ctx, kwargs).get_file_system_client(file_system=kwargs.pop('file_system_name')) - - -def cf_adls_directory(cli_ctx, kwargs): - return cf_adls_file_system(cli_ctx, kwargs).get_directory_client(directory=kwargs.pop('directory_path')) - - -def cf_adls_file(cli_ctx, kwargs): - return cf_adls_service(cli_ctx, kwargs).get_file_client(file_system=kwargs.pop('file_system_name', None), - file_path=kwargs.pop('path', None)) diff --git a/src/storage-blob-preview/azext_storage_blob_preview/_params.py b/src/storage-blob-preview/azext_storage_blob_preview/_params.py index 3b237fa8a19..2ec2fc1b0d7 100644 --- a/src/storage-blob-preview/azext_storage_blob_preview/_params.py +++ b/src/storage-blob-preview/azext_storage_blob_preview/_params.py @@ -368,6 +368,26 @@ def load_arguments(self, _): # pylint: disable=too-many-locals, too-many-statem c.extra('snapshot', snapshot_type) c.extra('if_tags_match_condition', tags_condition_type) + with self.argument_context('storage blob service-properties delete-policy update') as c: + c.argument('enable', arg_type=get_enum_type(['true', 'false']), help='Enables/disables soft-delete.') + c.argument('days_retained', type=int, + help='Number of days that soft-deleted blob will be retained. Must be in range [1,365].') + + with self.argument_context('storage blob service-properties update', min_api='2018-03-28') as c: + c.argument('delete_retention', arg_type=get_three_state_flag(), arg_group='Soft Delete', + help='Enables soft-delete.') + c.argument('delete_retention_period', type=int, arg_group='Soft Delete', + help='Number of days that soft-deleted blob will be retained. Must be in range [1,365].') + c.argument('static_website', arg_group='Static Website', arg_type=get_three_state_flag(), + help='Enables static-website.') + c.argument('index_document', help='The default name of the index page under each directory.', + arg_group='Static Website') + c.argument('error_document_404_path', options_list=['--404-document'], arg_group='Static Website', + help='The absolute path of the custom 404 page.') + c.argument('default_index_document_path', options_list='--default-index-path', is_preview=True, + help='Absolute path of the default index page.', + arg_group='Static Website') + with self.argument_context('storage blob set-tier', resource_type=CUSTOM_DATA_STORAGE_BLOB) as c: c.register_blob_arguments() diff --git a/src/storage-blob-preview/azext_storage_blob_preview/_transformers.py b/src/storage-blob-preview/azext_storage_blob_preview/_transformers.py index 6a945277356..831f1e3a7bd 100644 --- a/src/storage-blob-preview/azext_storage_blob_preview/_transformers.py +++ b/src/storage-blob-preview/azext_storage_blob_preview/_transformers.py @@ -234,6 +234,23 @@ def transform_blob_json_output(result): return new_result +def transform_blob_service_properties(result): + from azure.cli.core.commands.arm import make_camel_case + static_website = todict(result.pop("static_website", None)) + static_website["errorDocument_404Path"] = static_website.pop("errorDocument404Path", None) + new_result = { + "cors": result.pop("cors", None), + "deleteRetentionPolicy": result.pop("delete_retention_policy", None), + "hourMetrics": result.pop("hour_metrics", None), + "logging": result.pop("analytics_logging", None), + "minuteMetrics": result.pop("minute_metrics", None), + "staticWebsite": static_website + } + for key in result: + new_result[make_camel_case(key)] = result[key] + return new_result + + def transform_container_list_output(result): for i, item in enumerate(result): if isinstance(item, dict) and 'nextMarker' in item: diff --git a/src/storage-blob-preview/azext_storage_blob_preview/_validators.py b/src/storage-blob-preview/azext_storage_blob_preview/_validators.py index dfd940566b3..92c78033207 100644 --- a/src/storage-blob-preview/azext_storage_blob_preview/_validators.py +++ b/src/storage-blob-preview/azext_storage_blob_preview/_validators.py @@ -35,12 +35,11 @@ def _query_account_key(cli_ctx, account_name): t_storage_account_keys = get_sdk( cli_ctx, ResourceType.MGMT_STORAGE, 'models.storage_account_keys#StorageAccountKeys') - scf.config.enable_http_logger = False logger.debug('Disable HTTP logging to avoid having storage keys in debug logs') if t_storage_account_keys: - return scf.storage_accounts.list_keys(rg, account_name).key1 + return scf.storage_accounts.list_keys(rg, account_name, logging_enable=False).key1 # of type: models.storage_account_list_keys_result#StorageAccountListKeysResult - return scf.storage_accounts.list_keys(rg, account_name).keys[0].value # pylint: disable=no-member + return scf.storage_accounts.list_keys(rg, account_name, logging_enable=False).keys[0].value # pylint: disable=no-member def _query_account_rg(cli_ctx, account_name): diff --git a/src/storage-blob-preview/azext_storage_blob_preview/azext_metadata.json b/src/storage-blob-preview/azext_storage_blob_preview/azext_metadata.json index bf919d8cb78..1f7e4963bc4 100644 --- a/src/storage-blob-preview/azext_storage_blob_preview/azext_metadata.json +++ b/src/storage-blob-preview/azext_storage_blob_preview/azext_metadata.json @@ -1,4 +1,4 @@ { "azext.isPreview": true, - "azext.minCliCoreVersion": "2.14.0" + "azext.minCliCoreVersion": "2.16.0" } \ No newline at end of file diff --git a/src/storage-blob-preview/azext_storage_blob_preview/commands.py b/src/storage-blob-preview/azext_storage_blob_preview/commands.py index 5bb030c75a0..09705caa372 100644 --- a/src/storage-blob-preview/azext_storage_blob_preview/commands.py +++ b/src/storage-blob-preview/azext_storage_blob_preview/commands.py @@ -131,6 +131,24 @@ def get_custom_sdk(custom_module, client_factory, resource_type=ResourceType.DAT g.storage_custom_command_oauth('renew', 'renew_blob_lease') g.storage_command_oauth('release', 'release') + with self.command_group('storage blob service-properties delete-policy', command_type=blob_service_sdk, + min_api='2019-02-02', resource_type=CUSTOM_DATA_STORAGE_BLOB, + custom_command_type=get_custom_sdk('blob', cf_blob_service)) as g: + g.storage_command_oauth('show', 'get_service_properties', + transform=lambda x: x.get('delete_retention_policy', x), + exception_handler=show_exception_handler) + g.storage_custom_command_oauth('update', 'set_delete_policy') + + with self.command_group('storage blob service-properties', command_type=blob_service_sdk, + custom_command_type=get_custom_sdk('blob', cf_blob_service), + min_api='2019-02-02', resource_type=CUSTOM_DATA_STORAGE_BLOB) as g: + from ._transformers import transform_blob_service_properties + g.storage_command_oauth( + 'show', 'get_service_properties', exception_handler=show_exception_handler, + transform=transform_blob_service_properties) + g.storage_custom_command_oauth('update', 'set_service_properties', + transform=transform_blob_service_properties) + # --auth-mode login need to verify with self.command_group('storage blob tag', command_type=blob_client_sdk, custom_command_type=get_custom_sdk('blob', cf_blob_client), diff --git a/src/storage-blob-preview/azext_storage_blob_preview/operations/blob.py b/src/storage-blob-preview/azext_storage_blob_preview/operations/blob.py index 173e9b90e4d..2f8f7532ac9 100644 --- a/src/storage-blob-preview/azext_storage_blob_preview/operations/blob.py +++ b/src/storage-blob-preview/azext_storage_blob_preview/operations/blob.py @@ -56,7 +56,7 @@ def set_blob_tier(client, container_name, blob_name, tier, blob_type='block', ti def set_delete_policy(client, enable=None, days_retained=None): - policy = client.get_blob_service_properties().delete_retention_policy + policy = client.get_service_properties()['delete_retention_policy'] if enable is not None: policy.enabled = enable == 'true' @@ -66,47 +66,35 @@ def set_delete_policy(client, enable=None, days_retained=None): if policy.enabled and not policy.days: raise CLIError("must specify days-retained") - client.set_blob_service_properties(delete_retention_policy=policy) - return client.get_blob_service_properties().delete_retention_policy + client.set_service_properties(delete_retention_policy=policy) + return client.get_service_properties()['delete_retention_policy'] -def set_service_properties(client, parameters, delete_retention=None, delete_retention_period=None, - static_website=None, index_document=None, error_document_404_path=None): +def set_service_properties(client, delete_retention=None, delete_retention_period=None, + static_website=None, index_document=None, error_document_404_path=None, + default_index_document_path=None, timeout=None): + properties = client.get_service_properties() + # update - kwargs = {} - if hasattr(parameters, 'delete_retention_policy'): - kwargs['delete_retention_policy'] = parameters.delete_retention_policy if delete_retention is not None: - parameters.delete_retention_policy.enabled = delete_retention + properties['delete_retention_policy'].enabled = delete_retention if delete_retention_period is not None: - parameters.delete_retention_policy.days = delete_retention_period + properties['delete_retention_policy'].days = delete_retention_period - if hasattr(parameters, 'static_website'): - kwargs['static_website'] = parameters.static_website - elif any(param is not None for param in [static_website, index_document, error_document_404_path]): - raise CLIError('Static websites are only supported for StorageV2 (general-purpose v2) accounts.') if static_website is not None: - parameters.static_website.enabled = static_website + properties['static_website'].enabled = static_website if index_document is not None: - parameters.static_website.index_document = index_document + properties['static_website'].index_document = index_document if error_document_404_path is not None: - parameters.static_website.error_document_404_path = error_document_404_path - if hasattr(parameters, 'hour_metrics'): - kwargs['hour_metrics'] = parameters.hour_metrics - if hasattr(parameters, 'logging'): - kwargs['logging'] = parameters.logging - if hasattr(parameters, 'minute_metrics'): - kwargs['minute_metrics'] = parameters.minute_metrics - if hasattr(parameters, 'cors'): - kwargs['cors'] = parameters.cors - - # checks - policy = kwargs.get('delete_retention_policy', None) + properties['static_website'].error_document404_path = error_document_404_path + if default_index_document_path is not None: + properties['static_website'].default_index_document_path = default_index_document_path + policy = properties.get('delete_retention_policy', None) if policy and policy.enabled and not policy.days: raise CLIError("must specify days-retained") - client.set_blob_service_properties(**kwargs) - return client.get_blob_service_properties() + client.set_service_properties(timeout=timeout, **properties) + return client.get_service_properties() def storage_blob_copy_batch(cmd, client, source_client, container_name=None, diff --git a/src/storage-blob-preview/azext_storage_blob_preview/tests/latest/recordings/test_storage_blob_soft_delete.yaml b/src/storage-blob-preview/azext_storage_blob_preview/tests/latest/recordings/test_storage_blob_soft_delete.yaml new file mode 100644 index 00000000000..5cb54e1e222 --- /dev/null +++ b/src/storage-blob-preview/azext_storage_blob_preview/tests/latest/recordings/test_storage_blob_soft_delete.yaml @@ -0,0 +1,465 @@ +interactions: +- request: + body: null + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + CommandName: + - storage account keys list + Connection: + - keep-alive + Content-Length: + - '0' + ParameterSetName: + - -n -g --query -o + User-Agent: + - AZURECLI/2.17.1 azsdk-python-azure-mgmt-storage/16.0.0 Python/3.7.4 (Windows-10-10.0.19041-SP0) + method: POST + uri: https://management.azure.com/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/clitest.rg000001/providers/Microsoft.Storage/storageAccounts/clitest000002/listKeys?api-version=2019-06-01&$expand=kerb + response: + body: + string: '{"keys":[{"keyName":"key1","value":"veryFakedStorageAccountKey==","permissions":"FULL"},{"keyName":"key2","value":"veryFakedStorageAccountKey==","permissions":"FULL"}]}' + headers: + cache-control: + - no-cache + content-length: + - '288' + content-type: + - application/json + date: + - Sat, 09 Jan 2021 08:51:22 GMT + expires: + - '-1' + pragma: + - no-cache + server: + - Microsoft-Azure-Storage-Resource-Provider/1.0,Microsoft-HTTPAPI/2.0 Microsoft-HTTPAPI/2.0 + strict-transport-security: + - max-age=31536000; includeSubDomains + transfer-encoding: + - chunked + vary: + - Accept-Encoding + x-content-type-options: + - nosniff + x-ms-ratelimit-remaining-subscription-resource-requests: + - '11999' + status: + code: 200 + message: OK +- request: + body: null + headers: + Connection: + - keep-alive + Content-Length: + - '0' + User-Agent: + - Azure-Storage/2.0.0-2.0.1 (Python CPython 3.7.4; Windows 10) AZURECLI/2.17.1 + x-ms-date: + - Sat, 09 Jan 2021 08:51:23 GMT + x-ms-version: + - '2018-11-09' + method: PUT + uri: https://clitest000002.blob.core.windows.net/cont000003?restype=container + response: + body: + string: '' + headers: + content-length: + - '0' + date: + - Sat, 09 Jan 2021 08:51:25 GMT + etag: + - '"0x8D8B47BBF476EC6"' + last-modified: + - Sat, 09 Jan 2021 08:51:25 GMT + server: + - Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0 + x-ms-version: + - '2018-11-09' + status: + code: 201 + message: Created +- request: + body: "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '1024' + Content-Type: + - application/octet-stream + If-None-Match: + - '*' + User-Agent: + - azsdk-python-storage-blob/12.6.0 Python/3.7.4 (Windows-10-10.0.19041-SP0) + x-ms-blob-type: + - BlockBlob + x-ms-date: + - Sat, 09 Jan 2021 08:51:25 GMT + x-ms-version: + - '2020-02-10' + method: PUT + uri: https://clitest000002.blob.core.windows.net/cont000003/blob000004 + response: + body: + string: '' + headers: + content-length: + - '0' + content-md5: + - DzQ7CTESaiDxM9Z8KwGKOw== + date: + - Sat, 09 Jan 2021 08:51:26 GMT + etag: + - '"0x8D8B47BC0107710"' + last-modified: + - Sat, 09 Jan 2021 08:51:26 GMT + server: + - Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0 + x-ms-content-crc64: + - iknlm7CyG2k= + x-ms-request-server-encrypted: + - 'true' + x-ms-version: + - '2020-02-10' + status: + code: 201 + message: Created +- request: + body: null + headers: + Accept: + - application/xml + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-storage-blob/12.6.0 Python/3.7.4 (Windows-10-10.0.19041-SP0) + x-ms-date: + - Sat, 09 Jan 2021 08:51:27 GMT + x-ms-version: + - '2020-02-10' + method: GET + uri: https://clitest000002.blob.core.windows.net/cont000003?maxresults=5000&restype=container&comp=list + response: + body: + string: "\uFEFF5000blob000004Sat, + 09 Jan 2021 08:51:26 GMTSat, 09 Jan 2021 08:51:26 + GMT0x8D8B47BC01077101024application/octet-streamDzQ7CTESaiDxM9Z8KwGKOw==BlockBlobHottrueunlockedavailabletrue" + headers: + content-type: + - application/xml + date: + - Sat, 09 Jan 2021 08:51:27 GMT + server: + - Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0 + transfer-encoding: + - chunked + x-ms-version: + - '2020-02-10' + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - application/xml + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-storage-blob/12.6.0 Python/3.7.4 (Windows-10-10.0.19041-SP0) + x-ms-date: + - Sat, 09 Jan 2021 08:51:28 GMT + x-ms-version: + - '2020-02-10' + method: GET + uri: https://clitest000002.blob.core.windows.net/?restype=service&comp=properties + response: + body: + string: "\uFEFF1.0falsefalsefalsefalse1.0truetruetrue71.0falsefalsefalsefalsefalse" + headers: + content-type: + - application/xml + date: + - Sat, 09 Jan 2021 08:51:28 GMT + server: + - Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0 + transfer-encoding: + - chunked + x-ms-version: + - '2020-02-10' + status: + code: 200 + message: OK +- request: + body: ' + + true2' + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '176' + Content-Type: + - application/xml; charset=utf-8 + User-Agent: + - azsdk-python-storage-blob/12.6.0 Python/3.7.4 (Windows-10-10.0.19041-SP0) + x-ms-date: + - Sat, 09 Jan 2021 08:51:29 GMT + x-ms-version: + - '2020-02-10' + method: PUT + uri: https://clitest000002.blob.core.windows.net/?restype=service&comp=properties + response: + body: + string: '' + headers: + content-length: + - '0' + date: + - Sat, 09 Jan 2021 08:51:28 GMT + server: + - Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0 + x-ms-version: + - '2020-02-10' + status: + code: 202 + message: Accepted +- request: + body: null + headers: + Accept: + - application/xml + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-storage-blob/12.6.0 Python/3.7.4 (Windows-10-10.0.19041-SP0) + x-ms-date: + - Sat, 09 Jan 2021 08:51:29 GMT + x-ms-version: + - '2020-02-10' + method: GET + uri: https://clitest000002.blob.core.windows.net/?restype=service&comp=properties + response: + body: + string: "\uFEFF1.0falsefalsefalsefalse1.0truetruetrue71.0falsefalsetrue2falsefalse" + headers: + content-type: + - application/xml + date: + - Sat, 09 Jan 2021 08:51:29 GMT + server: + - Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0 + transfer-encoding: + - chunked + x-ms-version: + - '2020-02-10' + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - application/xml + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-storage-blob/12.6.0 Python/3.7.4 (Windows-10-10.0.19041-SP0) + x-ms-date: + - Sat, 09 Jan 2021 08:51:30 GMT + x-ms-version: + - '2020-02-10' + method: GET + uri: https://clitest000002.blob.core.windows.net/?restype=service&comp=properties + response: + body: + string: "\uFEFF1.0falsefalsefalsefalse1.0truetruetrue71.0falsefalsetrue2falsefalse" + headers: + content-type: + - application/xml + date: + - Sat, 09 Jan 2021 08:51:30 GMT + server: + - Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0 + transfer-encoding: + - chunked + x-ms-version: + - '2020-02-10' + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '0' + User-Agent: + - azsdk-python-storage-blob/12.6.0 Python/3.7.4 (Windows-10-10.0.19041-SP0) + x-ms-date: + - Sat, 09 Jan 2021 08:51:42 GMT + x-ms-version: + - '2020-02-10' + method: DELETE + uri: https://clitest000002.blob.core.windows.net/cont000003/blob000004 + response: + body: + string: '' + headers: + content-length: + - '0' + date: + - Sat, 09 Jan 2021 08:51:42 GMT + server: + - Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0 + x-ms-delete-type-permanent: + - 'false' + x-ms-version: + - '2020-02-10' + status: + code: 202 + message: Accepted +- request: + body: null + headers: + Accept: + - application/xml + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-storage-blob/12.6.0 Python/3.7.4 (Windows-10-10.0.19041-SP0) + x-ms-date: + - Sat, 09 Jan 2021 08:51:43 GMT + x-ms-version: + - '2020-02-10' + method: GET + uri: https://clitest000002.blob.core.windows.net/cont000003?maxresults=5000&restype=container&comp=list + response: + body: + string: "\uFEFF5000" + headers: + content-type: + - application/xml + date: + - Sat, 09 Jan 2021 08:51:43 GMT + server: + - Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0 + transfer-encoding: + - chunked + x-ms-version: + - '2020-02-10' + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '0' + User-Agent: + - azsdk-python-storage-blob/12.6.0 Python/3.7.4 (Windows-10-10.0.19041-SP0) + x-ms-date: + - Sat, 09 Jan 2021 08:51:44 GMT + x-ms-version: + - '2020-02-10' + method: PUT + uri: https://clitest000002.blob.core.windows.net/cont000003/blob000004?comp=undelete + response: + body: + string: '' + headers: + content-length: + - '0' + date: + - Sat, 09 Jan 2021 08:51:45 GMT + server: + - Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0 + x-ms-version: + - '2020-02-10' + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - application/xml + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-storage-blob/12.6.0 Python/3.7.4 (Windows-10-10.0.19041-SP0) + x-ms-date: + - Sat, 09 Jan 2021 08:51:45 GMT + x-ms-version: + - '2020-02-10' + method: GET + uri: https://clitest000002.blob.core.windows.net/cont000003?maxresults=5000&restype=container&comp=list + response: + body: + string: "\uFEFF5000blob000004Sat, + 09 Jan 2021 08:51:26 GMTSat, 09 Jan 2021 08:51:26 + GMT0x8D8B47BC01077101024application/octet-streamDzQ7CTESaiDxM9Z8KwGKOw==BlockBlobHottrueunlockedavailabletrue" + headers: + content-type: + - application/xml + date: + - Sat, 09 Jan 2021 08:51:46 GMT + server: + - Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0 + transfer-encoding: + - chunked + x-ms-version: + - '2020-02-10' + status: + code: 200 + message: OK +version: 1 diff --git a/src/storage-blob-preview/azext_storage_blob_preview/tests/latest/recordings/test_storage_blob_update_service_properties.yaml b/src/storage-blob-preview/azext_storage_blob_preview/tests/latest/recordings/test_storage_blob_update_service_properties.yaml new file mode 100644 index 00000000000..cf92bdfb5e8 --- /dev/null +++ b/src/storage-blob-preview/azext_storage_blob_preview/tests/latest/recordings/test_storage_blob_update_service_properties.yaml @@ -0,0 +1,340 @@ +interactions: +- request: + body: null + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + CommandName: + - storage account keys list + Connection: + - keep-alive + Content-Length: + - '0' + ParameterSetName: + - -n -g --query -o + User-Agent: + - AZURECLI/2.17.1 azsdk-python-azure-mgmt-storage/16.0.0 Python/3.7.4 (Windows-10-10.0.19041-SP0) + method: POST + uri: https://management.azure.com/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/clitest.rg000001/providers/Microsoft.Storage/storageAccounts/clitest000002/listKeys?api-version=2019-06-01&$expand=kerb + response: + body: + string: '{"keys":[{"keyName":"key1","value":"veryFakedStorageAccountKey==","permissions":"FULL"},{"keyName":"key2","value":"veryFakedStorageAccountKey==","permissions":"FULL"}]}' + headers: + cache-control: + - no-cache + content-length: + - '288' + content-type: + - application/json + date: + - Sat, 09 Jan 2021 11:40:34 GMT + expires: + - '-1' + pragma: + - no-cache + server: + - Microsoft-Azure-Storage-Resource-Provider/1.0,Microsoft-HTTPAPI/2.0 Microsoft-HTTPAPI/2.0 + strict-transport-security: + - max-age=31536000; includeSubDomains + transfer-encoding: + - chunked + vary: + - Accept-Encoding + x-content-type-options: + - nosniff + x-ms-ratelimit-remaining-subscription-resource-requests: + - '11999' + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - application/xml + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-storage-blob/12.6.0 Python/3.7.4 (Windows-10-10.0.19041-SP0) + x-ms-date: + - Sat, 09 Jan 2021 11:40:34 GMT + x-ms-version: + - '2020-02-10' + method: GET + uri: https://clitest000002.blob.core.windows.net/?restype=service&comp=properties + response: + body: + string: "\uFEFF1.0falsefalsefalsefalse1.0truetruetrue71.0falsefalsefalsefalsefalse" + headers: + content-type: + - application/xml + date: + - Sat, 09 Jan 2021 11:40:37 GMT + server: + - Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0 + transfer-encoding: + - chunked + x-ms-version: + - '2020-02-10' + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - application/xml + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-storage-blob/12.6.0 Python/3.7.4 (Windows-10-10.0.19041-SP0) + x-ms-date: + - Sat, 09 Jan 2021 11:40:37 GMT + x-ms-version: + - '2020-02-10' + method: GET + uri: https://clitest000002.blob.core.windows.net/?restype=service&comp=properties + response: + body: + string: "\uFEFF1.0falsefalsefalsefalse1.0truetruetrue71.0falsefalsefalsefalsefalse" + headers: + content-type: + - application/xml + date: + - Sat, 09 Jan 2021 11:40:37 GMT + server: + - Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0 + transfer-encoding: + - chunked + x-ms-version: + - '2020-02-10' + status: + code: 200 + message: OK +- request: + body: ' + + 1.0falsefalsefalsefalse1.0truetruetrue71.0falsefalsefalsetrueindex.htmlerror.html' + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '792' + Content-Type: + - application/xml; charset=utf-8 + User-Agent: + - azsdk-python-storage-blob/12.6.0 Python/3.7.4 (Windows-10-10.0.19041-SP0) + x-ms-date: + - Sat, 09 Jan 2021 11:40:38 GMT + x-ms-version: + - '2020-02-10' + method: PUT + uri: https://clitest000002.blob.core.windows.net/?restype=service&comp=properties + response: + body: + string: '' + headers: + content-length: + - '0' + date: + - Sat, 09 Jan 2021 11:40:38 GMT + server: + - Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0 + x-ms-version: + - '2020-02-10' + status: + code: 202 + message: Accepted +- request: + body: null + headers: + Accept: + - application/xml + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-storage-blob/12.6.0 Python/3.7.4 (Windows-10-10.0.19041-SP0) + x-ms-date: + - Sat, 09 Jan 2021 11:40:39 GMT + x-ms-version: + - '2020-02-10' + method: GET + uri: https://clitest000002.blob.core.windows.net/?restype=service&comp=properties + response: + body: + string: "\uFEFF1.0falsefalsefalsefalse1.0truetruetrue71.0falsefalsefalsefalsetrueindex.htmlerror.html" + headers: + content-type: + - application/xml + date: + - Sat, 09 Jan 2021 11:40:38 GMT + server: + - Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0 + transfer-encoding: + - chunked + x-ms-version: + - '2020-02-10' + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - application/xml + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-storage-blob/12.6.0 Python/3.7.4 (Windows-10-10.0.19041-SP0) + x-ms-date: + - Sat, 09 Jan 2021 11:40:39 GMT + x-ms-version: + - '2020-02-10' + method: GET + uri: https://clitest000002.blob.core.windows.net/?restype=service&comp=properties + response: + body: + string: "\uFEFF1.0falsefalsefalsefalse1.0truetruetrue71.0falsefalsefalsefalsetrueindex.htmlerror.html" + headers: + content-type: + - application/xml + date: + - Sat, 09 Jan 2021 11:40:39 GMT + server: + - Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0 + transfer-encoding: + - chunked + x-ms-version: + - '2020-02-10' + status: + code: 200 + message: OK +- request: + body: ' + + 1.0falsefalsefalsefalse1.0truetruetrue71.0falsefalsetrue1trueindex.htmlerror.html' + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '805' + Content-Type: + - application/xml; charset=utf-8 + User-Agent: + - azsdk-python-storage-blob/12.6.0 Python/3.7.4 (Windows-10-10.0.19041-SP0) + x-ms-date: + - Sat, 09 Jan 2021 11:40:40 GMT + x-ms-version: + - '2020-02-10' + method: PUT + uri: https://clitest000002.blob.core.windows.net/?restype=service&comp=properties + response: + body: + string: '' + headers: + content-length: + - '0' + date: + - Sat, 09 Jan 2021 11:40:39 GMT + server: + - Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0 + x-ms-version: + - '2020-02-10' + status: + code: 202 + message: Accepted +- request: + body: null + headers: + Accept: + - application/xml + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-storage-blob/12.6.0 Python/3.7.4 (Windows-10-10.0.19041-SP0) + x-ms-date: + - Sat, 09 Jan 2021 11:40:40 GMT + x-ms-version: + - '2020-02-10' + method: GET + uri: https://clitest000002.blob.core.windows.net/?restype=service&comp=properties + response: + body: + string: "\uFEFF1.0falsefalsefalsefalse1.0truetruetrue71.0falsefalsetrue1falsetrueindex.htmlerror.html" + headers: + content-type: + - application/xml + date: + - Sat, 09 Jan 2021 11:40:40 GMT + server: + - Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0 + transfer-encoding: + - chunked + x-ms-version: + - '2020-02-10' + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - application/xml + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-storage-blob/12.6.0 Python/3.7.4 (Windows-10-10.0.19041-SP0) + x-ms-date: + - Sat, 09 Jan 2021 11:40:41 GMT + x-ms-version: + - '2020-02-10' + method: GET + uri: https://clitest000002.blob.core.windows.net/?restype=service&comp=properties + response: + body: + string: "\uFEFF1.0falsefalsefalsefalse1.0truetruetrue71.0falsefalsetrue1falsetrueindex.htmlerror.html" + headers: + content-type: + - application/xml + date: + - Sat, 09 Jan 2021 11:40:41 GMT + server: + - Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0 + transfer-encoding: + - chunked + x-ms-version: + - '2020-02-10' + status: + code: 200 + message: OK +version: 1 diff --git a/src/storage-blob-preview/azext_storage_blob_preview/tests/latest/test_storage_blob_scenarios.py b/src/storage-blob-preview/azext_storage_blob_preview/tests/latest/test_storage_blob_scenarios.py index edb1896ec40..e0df3199b0a 100644 --- a/src/storage-blob-preview/azext_storage_blob_preview/tests/latest/test_storage_blob_scenarios.py +++ b/src/storage-blob-preview/azext_storage_blob_preview/tests/latest/test_storage_blob_scenarios.py @@ -149,6 +149,72 @@ def is_block_put_req(request): 'The expected number of block put requests is {} but the actual ' 'number is {}.'.format(block_count, len(put_blocks))) + @ResourceGroupPreparer() + @StorageAccountPreparer(kind='StorageV2') + def test_storage_blob_soft_delete(self, resource_group, storage_account): + account_info = self.get_account_info(resource_group, storage_account) + container = self.create_container(account_info) + import time + + # create a blob + local_file = self.create_temp_file(1) + blob_name = self.create_random_name(prefix='blob', length=24) + + self.storage_cmd('storage blob upload -c {} -f "{}" -n {} --type block', account_info, + container, local_file, blob_name) + self.assertEqual(len(self.storage_cmd('storage blob list -c {}', + account_info, container).get_output_in_json()), 1) + + # set delete-policy to enable soft-delete + self.storage_cmd('storage blob service-properties delete-policy update --enable true --days-retained 2', + account_info) + self.storage_cmd('storage blob service-properties delete-policy show', + account_info).assert_with_checks(JMESPathCheck('enabled', True), + JMESPathCheck('days', 2)) + time.sleep(10) + # soft-delete and check + self.storage_cmd('storage blob delete -c {} -n {}', account_info, container, blob_name) + self.assertEqual(len(self.storage_cmd('storage blob list -c {}', + account_info, container).get_output_in_json()), 0) + + # undelete and check + self.storage_cmd('storage blob undelete -c {} -n {}', account_info, container, blob_name) + self.assertEqual(len(self.storage_cmd('storage blob list -c {}', + account_info, container).get_output_in_json()), 1) + + @ResourceGroupPreparer() + @StorageAccountPreparer(kind='StorageV2') + def test_storage_blob_update_service_properties(self, resource_group, storage_account): + account_info = self.get_account_info(resource_group, storage_account) + + self.storage_cmd('storage blob service-properties show', account_info) \ + .assert_with_checks(JMESPathCheck('staticWebsite.enabled', False), + JMESPathCheck('hourMetrics.enabled', True), + JMESPathCheck('minuteMetrics.enabled', False), + JMESPathCheck('minuteMetrics.includeApis', None), + JMESPathCheck('logging.delete', False)) + + self.storage_cmd('storage blob service-properties update --static-website --index-document index.html ' + '--404-document error.html', account_info) \ + .assert_with_checks(JMESPathCheck('staticWebsite.enabled', True), + JMESPathCheck('staticWebsite.errorDocument_404Path', 'error.html'), + JMESPathCheck('staticWebsite.indexDocument', 'index.html')) + + self.storage_cmd('storage blob service-properties update --delete-retention --delete-retention-period 1', + account_info) \ + .assert_with_checks(JMESPathCheck('staticWebsite.enabled', True), + JMESPathCheck('staticWebsite.errorDocument_404Path', 'error.html'), + JMESPathCheck('staticWebsite.indexDocument', 'index.html'), + JMESPathCheck('deleteRetentionPolicy.enabled', True), + JMESPathCheck('deleteRetentionPolicy.days', 1)) + + self.storage_cmd('storage blob service-properties show', account_info) \ + .assert_with_checks(JMESPathCheck('staticWebsite.enabled', True), + JMESPathCheck('staticWebsite.errorDocument_404Path', 'error.html'), + JMESPathCheck('staticWebsite.indexDocument', 'index.html'), + JMESPathCheck('deleteRetentionPolicy.enabled', True), + JMESPathCheck('deleteRetentionPolicy.days', 1)) + if __name__ == '__main__': unittest.main() diff --git a/src/storage-blob-preview/setup.py b/src/storage-blob-preview/setup.py index fa8b94ae2a6..4b05e8efd03 100644 --- a/src/storage-blob-preview/setup.py +++ b/src/storage-blob-preview/setup.py @@ -16,7 +16,7 @@ # TODO: Confirm this is the right version number you want and it matches your # HISTORY.rst entry. -VERSION = '0.4.0' +VERSION = '0.4.1' # The full list of classifiers is available at # https://pypi.python.org/pypi?%3Aaction=list_classifiers