From a11223e0b7c3c60b9206aab4bed87ff02f2b0394 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?McCoy=20Pati=C3=B1o?= Date: Wed, 9 Jun 2021 23:06:57 -0700 Subject: [PATCH 01/27] rehydration, but no .result() --- .../keyvault/administration/_backup_client.py | 6 +- .../administration/_internal/polling.py | 115 +++++++++++++++++- 2 files changed, 118 insertions(+), 3 deletions(-) diff --git a/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_backup_client.py b/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_backup_client.py index ceaf02fa69f0..3bdef326758a 100644 --- a/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_backup_client.py +++ b/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_backup_client.py @@ -9,7 +9,7 @@ from ._models import KeyVaultBackupOperation from ._internal import KeyVaultClientBase, parse_folder_url -from ._internal.polling import KeyVaultBackupClientPolling +from ._internal.polling import KeyVaultBackupClientPolling, KeyVaultBackupClientPollingMethod if TYPE_CHECKING: # pylint:disable=unused-import @@ -45,7 +45,9 @@ def begin_backup(self, blob_storage_url, sas_token, **kwargs): azure_storage_blob_container_uri=sas_parameter, cls=KeyVaultBackupOperation._from_generated, continuation_token=kwargs.pop("continuation_token", None), - polling=LROBasePolling(lro_algorithms=[KeyVaultBackupClientPolling()], timeout=polling_interval, **kwargs), + polling=KeyVaultBackupClientPollingMethod( + lro_algorithms=[KeyVaultBackupClientPolling()], timeout=polling_interval, **kwargs + ), **kwargs ) diff --git a/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_internal/polling.py b/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_internal/polling.py index b4fe58cead15..b57240e92a9a 100644 --- a/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_internal/polling.py +++ b/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_internal/polling.py @@ -2,12 +2,125 @@ # Copyright (c) Microsoft Corporation. # Licensed under the MIT License. # ------------------------------------ -from azure.core.polling.base_polling import OperationResourcePolling +from azure.core.polling.base_polling import ( + BadResponse, + BadStatus, + HttpResponseError, + LROBasePolling, + OperationFailed, + OperationResourcePolling, + _raise_if_bad_http_status_and_method +) class KeyVaultBackupClientPolling(OperationResourcePolling): def __init__(self): super(KeyVaultBackupClientPolling, self).__init__(operation_location_header="azure-asyncoperation") + def can_poll(self, pipeline_response, **kwargs): + """Answer if this polling method could be used. + """ + if kwargs.get("continuation_url"): + return True + response = pipeline_response.http_response + return self._operation_location_header in response.headers + + def set_initial_status(self, pipeline_response, **kwargs): + # type: (PipelineResponseType) -> str + """Process first response after initiating long running operation. + + :param azure.core.pipeline.PipelineResponse response: initial REST call response. + """ + self._request = pipeline_response.http_response.request + response = pipeline_response.http_response + + self._async_url = kwargs.get("continuation_url") + if self._async_url is None: + self._set_async_url_if_present(response) + + if response.status_code in {200, 201, 202, 204} and self._async_url: + return "InProgress" + raise OperationFailed("Operation failed or canceled") + def get_final_get_url(self, pipeline_response): return None + + +class KeyVaultBackupClientPollingMethod(LROBasePolling): + def initialize(self, client, initial_response, deserialization_callback): + """Set the initial status of this LRO. + + :param initial_response: The initial response of the poller + :raises: HttpResponseError if initial status is incorrect LRO state + """ + self._client = client + continuation_url = type(initial_response) == str + if continuation_url: + # get the status of the operation and a pipeline response from a URL continuation token + self._pipeline_response = self._initial_response = self.request_initial_status(initial_response) + else: + self._pipeline_response = self._initial_response = initial_response + self._deserialization_callback = deserialization_callback + + for operation in self._lro_algorithms: + if operation.can_poll(self._initial_response, continuation_url=continuation_url): + self._operation = operation + break + else: + raise BadResponse("Unable to find status link for polling.") + + try: + _raise_if_bad_http_status_and_method(self._initial_response.http_response) + self._status = self._operation.set_initial_status(self._initial_response, continuation_url=initial_response) + + except BadStatus as err: + self._status = "Failed" + raise HttpResponseError(response=self._initial_response.http_response, error=err) + except BadResponse as err: + self._status = "Failed" + raise HttpResponseError( + response=self._initial_response.http_response, message=str(err), error=err + ) + except OperationFailed as err: + raise HttpResponseError(response=self._initial_response.http_response, error=err) + + def get_continuation_token(self): + return self._operation.get_polling_url() + + def _get_request_id(self): + return self._pipeline_response.http_response.request.headers[ + "x-ms-client-request-id" + ] + + @classmethod + def from_continuation_token(cls, continuation_token, **kwargs): + """continuation_token is expected to be the URL of a pending operation""" + # type(str, Any) -> Tuple + try: + client = kwargs["client"] + except KeyError: + raise ValueError("Need kwarg 'client' to be recreated from continuation_token") + + try: + deserialization_callback = kwargs["deserialization_callback"] + except KeyError: + raise ValueError("Need kwarg 'deserialization_callback' to be recreated from continuation_token") + + return client, continuation_token, deserialization_callback + + def request_initial_status(self, status_link): + """Do a simple GET to this status link. + + This method re-inject 'x-ms-client-request-id'. + + :rtype: azure.core.pipeline.PipelineResponse + """ + if self._path_format_arguments: + status_link = self._client.format_url(status_link, **self._path_format_arguments) + request = self._client.get(status_link) + # Re-inject 'x-ms-client-request-id' while polling + # if "request_id" not in self._operation_config: + # self._operation_config["request_id"] = self._get_request_id() + return self._client._pipeline.run( # pylint: disable=protected-access + request, stream=False, **self._operation_config + ) From dd32026fc59db24837e8aa634263c7a572736766 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?McCoy=20Pati=C3=B1o?= Date: Thu, 10 Jun 2021 10:29:49 -0700 Subject: [PATCH 02/27] poll-able rehydration --- .../keyvault/administration/_internal/polling.py | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_internal/polling.py b/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_internal/polling.py index b57240e92a9a..a5617fce30db 100644 --- a/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_internal/polling.py +++ b/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_internal/polling.py @@ -85,12 +85,7 @@ def initialize(self, client, initial_response, deserialization_callback): raise HttpResponseError(response=self._initial_response.http_response, error=err) def get_continuation_token(self): - return self._operation.get_polling_url() - - def _get_request_id(self): - return self._pipeline_response.http_response.request.headers[ - "x-ms-client-request-id" - ] + return self._operation.get_polling_url().http_response.headers["azure-asyncoperation"] @classmethod def from_continuation_token(cls, continuation_token, **kwargs): @@ -109,18 +104,13 @@ def from_continuation_token(cls, continuation_token, **kwargs): return client, continuation_token, deserialization_callback def request_initial_status(self, status_link): - """Do a simple GET to this status link. - - This method re-inject 'x-ms-client-request-id'. + """Do a GET to this status link to obtain a pipeline response. :rtype: azure.core.pipeline.PipelineResponse """ if self._path_format_arguments: status_link = self._client.format_url(status_link, **self._path_format_arguments) request = self._client.get(status_link) - # Re-inject 'x-ms-client-request-id' while polling - # if "request_id" not in self._operation_config: - # self._operation_config["request_id"] = self._get_request_id() return self._client._pipeline.run( # pylint: disable=protected-access request, stream=False, **self._operation_config ) From 520ba1b84d67c833aefd4b743358f483f67a3952 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?McCoy=20Pati=C3=B1o?= Date: Thu, 10 Jun 2021 16:44:39 -0700 Subject: [PATCH 03/27] passing a/sync backup rehydration --- .../administration/_internal/async_polling.py | 117 ++++++++++++++++++ .../administration/_internal/polling.py | 103 +++++++++++---- .../administration/aio/_backup_client.py | 3 +- .../tests/test_backup_client_async.py | 10 +- 4 files changed, 204 insertions(+), 29 deletions(-) create mode 100644 sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_internal/async_polling.py diff --git a/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_internal/async_polling.py b/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_internal/async_polling.py new file mode 100644 index 000000000000..2616ebbd90e8 --- /dev/null +++ b/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_internal/async_polling.py @@ -0,0 +1,117 @@ +# ------------------------------------ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# ------------------------------------ +from azure.core.polling.async_base_polling import AsyncLROBasePolling +from azure.core.polling.base_polling import ( + BadResponse, + BadStatus, + HttpResponseError, + OperationFailed, + _raise_if_bad_http_status_and_method +) + + +class KeyVaultAsyncBackupClientPollingMethod(AsyncLROBasePolling): + def initialize(self, client, initial_response, deserialization_callback): + """Set the initial status of this LRO. + + :param initial_response: The initial pipeline response of the poller, or a URL continuation token + :raises: HttpResponseError if initial status is incorrect LRO state + """ + self._client = client + self._deserialization_callback = deserialization_callback + self._continuation_url = None + + continuation_url = type(initial_response) == str + if continuation_url: + self._continuation_url = initial_response + self._operation = self._lro_algorithms[0] + self._status = "InProgress" # assume the operation is ongoing for now + else: + self._pipeline_response = self._initial_response = initial_response + + for operation in self._lro_algorithms: + if operation.can_poll(self._initial_response, continuation_url=continuation_url): + self._operation = operation + break + else: + raise BadResponse("Unable to find status link for polling.") + + try: + _raise_if_bad_http_status_and_method(self._initial_response.http_response) + self._status = self._operation.set_initial_status( + self._initial_response, continuation_url=initial_response + ) + + except BadStatus as err: + self._status = "Failed" + raise HttpResponseError(response=self._initial_response.http_response, error=err) + except BadResponse as err: + self._status = "Failed" + raise HttpResponseError( + response=self._initial_response.http_response, message=str(err), error=err + ) + except OperationFailed as err: + raise HttpResponseError(response=self._initial_response.http_response, error=err) + + def get_continuation_token(self): + return self._operation.get_polling_url().http_response.headers["azure-asyncoperation"] + + @classmethod + def from_continuation_token(cls, continuation_token, **kwargs): + """continuation_token is expected to be the URL of a pending operation""" + # type(str, Any) -> Tuple + try: + client = kwargs["client"] + except KeyError: + raise ValueError("Need kwarg 'client' to be recreated from continuation_token") + + try: + deserialization_callback = kwargs["deserialization_callback"] + except KeyError: + raise ValueError("Need kwarg 'deserialization_callback' to be recreated from continuation_token") + + return client, continuation_token, deserialization_callback + + def _parse_resource(self, pipeline_response): + # type: (PipelineResponseType) -> Optional[Any] + """Assuming this response is a resource, use the deserialization callback to parse it. + If body is empty, assuming no resource to return. + """ + if pipeline_response is None: + return None + return super(KeyVaultAsyncBackupClientPollingMethod, self)._parse_resource(pipeline_response) + + def _extract_delay(self): + if self._continuation_url: + return 0 + return super(KeyVaultAsyncBackupClientPollingMethod, self)._extract_delay() + + async def update_status(self): + """Update the current status of the LRO. + """ + if self._continuation_url: + self._pipeline_response = await self.request_status(self._continuation_url) + else: + self._pipeline_response = await self.request_status(self._operation.get_polling_url()) + _raise_if_bad_http_status_and_method(self._pipeline_response.http_response) + self._status = self._operation.get_status(self._pipeline_response) + + async def request_status(self, status_link): + """Do a simple GET to this status link. + + This method re-inject 'x-ms-client-request-id'. + + :rtype: azure.core.pipeline.PipelineResponse + """ + if self._path_format_arguments: + status_link = self._client.format_url(status_link, **self._path_format_arguments) + request = self._client.get(status_link) + if self._continuation_url is None: + # Re-inject 'x-ms-client-request-id' while polling + if "request_id" not in self._operation_config: + self._operation_config["request_id"] = self._get_request_id() + return await self._client._pipeline.run( # pylint: disable=protected-access + request, stream=False, **self._operation_config + ) diff --git a/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_internal/polling.py b/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_internal/polling.py index a5617fce30db..613f1cd01391 100644 --- a/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_internal/polling.py +++ b/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_internal/polling.py @@ -2,6 +2,8 @@ # Copyright (c) Microsoft Corporation. # Licensed under the MIT License. # ------------------------------------ +from typing import TYPE_CHECKING + from azure.core.polling.base_polling import ( BadResponse, BadStatus, @@ -12,6 +14,18 @@ _raise_if_bad_http_status_and_method ) +if TYPE_CHECKING: + from typing import Union + from azure.core.pipeline import PipelineResponse + from azure.core.pipeline.transport import ( + HttpResponse, + AsyncHttpResponse, + HttpRequest, + ) + + ResponseType = Union[HttpResponse, AsyncHttpResponse] + PipelineResponseType = PipelineResponse[HttpRequest, ResponseType] + class KeyVaultBackupClientPolling(OperationResourcePolling): def __init__(self): @@ -50,39 +64,44 @@ class KeyVaultBackupClientPollingMethod(LROBasePolling): def initialize(self, client, initial_response, deserialization_callback): """Set the initial status of this LRO. - :param initial_response: The initial response of the poller + :param initial_response: The initial pipeline response of the poller, or a URL continuation token :raises: HttpResponseError if initial status is incorrect LRO state """ self._client = client + self._deserialization_callback = deserialization_callback + self._continuation_url = None + continuation_url = type(initial_response) == str if continuation_url: - # get the status of the operation and a pipeline response from a URL continuation token - self._pipeline_response = self._initial_response = self.request_initial_status(initial_response) + self._continuation_url = initial_response + self._operation = self._lro_algorithms[0] + self._status = "InProgress" # assume the operation is ongoing for now else: self._pipeline_response = self._initial_response = initial_response - self._deserialization_callback = deserialization_callback - - for operation in self._lro_algorithms: - if operation.can_poll(self._initial_response, continuation_url=continuation_url): - self._operation = operation - break - else: - raise BadResponse("Unable to find status link for polling.") - try: - _raise_if_bad_http_status_and_method(self._initial_response.http_response) - self._status = self._operation.set_initial_status(self._initial_response, continuation_url=initial_response) - - except BadStatus as err: - self._status = "Failed" - raise HttpResponseError(response=self._initial_response.http_response, error=err) - except BadResponse as err: - self._status = "Failed" - raise HttpResponseError( - response=self._initial_response.http_response, message=str(err), error=err - ) - except OperationFailed as err: - raise HttpResponseError(response=self._initial_response.http_response, error=err) + for operation in self._lro_algorithms: + if operation.can_poll(self._initial_response, continuation_url=continuation_url): + self._operation = operation + break + else: + raise BadResponse("Unable to find status link for polling.") + + try: + _raise_if_bad_http_status_and_method(self._initial_response.http_response) + self._status = self._operation.set_initial_status( + self._initial_response, continuation_url=initial_response + ) + + except BadStatus as err: + self._status = "Failed" + raise HttpResponseError(response=self._initial_response.http_response, error=err) + except BadResponse as err: + self._status = "Failed" + raise HttpResponseError( + response=self._initial_response.http_response, message=str(err), error=err + ) + except OperationFailed as err: + raise HttpResponseError(response=self._initial_response.http_response, error=err) def get_continuation_token(self): return self._operation.get_polling_url().http_response.headers["azure-asyncoperation"] @@ -103,14 +122,44 @@ def from_continuation_token(cls, continuation_token, **kwargs): return client, continuation_token, deserialization_callback - def request_initial_status(self, status_link): - """Do a GET to this status link to obtain a pipeline response. + def _parse_resource(self, pipeline_response): + # type: (PipelineResponseType) -> Optional[Any] + """Assuming this response is a resource, use the deserialization callback to parse it. + If body is empty, assuming no resource to return. + """ + if pipeline_response is None: + return None + return super(KeyVaultBackupClientPollingMethod, self)._parse_resource(pipeline_response) + + def _extract_delay(self): + if self._continuation_url: + return 0 + return super(KeyVaultBackupClientPollingMethod, self)._extract_delay() + + def update_status(self): + """Update the current status of the LRO. + """ + if self._continuation_url: + self._pipeline_response = self.request_status(self._continuation_url) + else: + self._pipeline_response = self.request_status(self._operation.get_polling_url()) + _raise_if_bad_http_status_and_method(self._pipeline_response.http_response) + self._status = self._operation.get_status(self._pipeline_response) + + def request_status(self, status_link): + """Do a simple GET to this status link. + + This method re-inject 'x-ms-client-request-id'. :rtype: azure.core.pipeline.PipelineResponse """ if self._path_format_arguments: status_link = self._client.format_url(status_link, **self._path_format_arguments) request = self._client.get(status_link) + if self._continuation_url is None: + # Re-inject 'x-ms-client-request-id' while polling + if "request_id" not in self._operation_config: + self._operation_config["request_id"] = self._get_request_id() return self._client._pipeline.run( # pylint: disable=protected-access request, stream=False, **self._operation_config ) diff --git a/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/aio/_backup_client.py b/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/aio/_backup_client.py index 6fb61262ad09..7142ac0bd739 100644 --- a/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/aio/_backup_client.py +++ b/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/aio/_backup_client.py @@ -8,6 +8,7 @@ from azure.core.polling.async_base_polling import AsyncLROBasePolling from .._internal import AsyncKeyVaultClientBase, parse_folder_url +from .._internal.async_polling import KeyVaultAsyncBackupClientPollingMethod from .._internal.polling import KeyVaultBackupClientPolling from .._models import KeyVaultBackupOperation @@ -45,7 +46,7 @@ async def begin_backup( azure_storage_blob_container_uri=sas_parameter, cls=KeyVaultBackupOperation._from_generated, continuation_token=kwargs.pop("continuation_token", None), - polling=AsyncLROBasePolling( + polling=KeyVaultAsyncBackupClientPollingMethod( lro_algorithms=[KeyVaultBackupClientPolling()], timeout=polling_interval, **kwargs ), **kwargs diff --git a/sdk/keyvault/azure-keyvault-administration/tests/test_backup_client_async.py b/sdk/keyvault/azure-keyvault-administration/tests/test_backup_client_async.py index 4c132d262f10..85033742fef4 100644 --- a/sdk/keyvault/azure-keyvault-administration/tests/test_backup_client_async.py +++ b/sdk/keyvault/azure-keyvault-administration/tests/test_backup_client_async.py @@ -40,7 +40,15 @@ def tearDown(self): @property def credential(self): if self.is_live: - return DefaultAzureCredential() + from dotenv import load_dotenv + from azure.identity.aio import ClientSecretCredential + import os + load_dotenv + return ClientSecretCredential( + tenant_id=os.environ["KEYVAULT_TENANT_ID"], + client_id=os.environ["KEYVAULT_CLIENT_ID"], + client_secret=os.environ["KEYVAULT_CLIENT_SECRET"] + ) async def get_token(*_, **__): return AccessToken("secret", time.time() + 3600) From 79e20c6990bcf91699ec4bda424d8036fa8eef23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?McCoy=20Pati=C3=B1o?= Date: Thu, 10 Jun 2021 22:28:23 -0700 Subject: [PATCH 04/27] cleaned up; rehydrated and regular poller success --- .../keyvault/administration/_backup_client.py | 4 +- .../administration/_internal/async_polling.py | 60 +++---------- .../administration/_internal/polling.py | 88 +++---------------- .../administration/aio/_backup_client.py | 4 +- ...p_client.test_full_backup_and_restore.yaml | 2 +- 5 files changed, 26 insertions(+), 132 deletions(-) diff --git a/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_backup_client.py b/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_backup_client.py index 3bdef326758a..5efb646f889c 100644 --- a/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_backup_client.py +++ b/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_backup_client.py @@ -5,8 +5,6 @@ import functools from typing import TYPE_CHECKING -from azure.core.polling.base_polling import LROBasePolling - from ._models import KeyVaultBackupOperation from ._internal import KeyVaultClientBase, parse_folder_url from ._internal.polling import KeyVaultBackupClientPolling, KeyVaultBackupClientPollingMethod @@ -71,7 +69,7 @@ def begin_restore(self, folder_url, sas_token, **kwargs): container_url, folder_name = parse_folder_url(folder_url) sas_parameter = self._models.SASTokenParameter(storage_resource_uri=container_url, token=sas_token) - polling = LROBasePolling( + polling = KeyVaultBackupClientPollingMethod( lro_algorithms=[KeyVaultBackupClientPolling()], timeout=kwargs.pop("_polling_interval", 5), **kwargs ) diff --git a/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_internal/async_polling.py b/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_internal/async_polling.py index 2616ebbd90e8..15c352d1cb7d 100644 --- a/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_internal/async_polling.py +++ b/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_internal/async_polling.py @@ -3,13 +3,6 @@ # Licensed under the MIT License. # ------------------------------------ from azure.core.polling.async_base_polling import AsyncLROBasePolling -from azure.core.polling.base_polling import ( - BadResponse, - BadStatus, - HttpResponseError, - OperationFailed, - _raise_if_bad_http_status_and_method -) class KeyVaultAsyncBackupClientPollingMethod(AsyncLROBasePolling): @@ -19,44 +12,23 @@ def initialize(self, client, initial_response, deserialization_callback): :param initial_response: The initial pipeline response of the poller, or a URL continuation token :raises: HttpResponseError if initial status is incorrect LRO state """ - self._client = client - self._deserialization_callback = deserialization_callback self._continuation_url = None - continuation_url = type(initial_response) == str - if continuation_url: + if type(initial_response) != str: + super(KeyVaultAsyncBackupClientPollingMethod, self).initialize( + client, initial_response, deserialization_callback + ) + + else: + self._client = client self._continuation_url = initial_response + self._deserialization_callback = deserialization_callback self._operation = self._lro_algorithms[0] - self._status = "InProgress" # assume the operation is ongoing for now - else: - self._pipeline_response = self._initial_response = initial_response - - for operation in self._lro_algorithms: - if operation.can_poll(self._initial_response, continuation_url=continuation_url): - self._operation = operation - break - else: - raise BadResponse("Unable to find status link for polling.") - - try: - _raise_if_bad_http_status_and_method(self._initial_response.http_response) - self._status = self._operation.set_initial_status( - self._initial_response, continuation_url=initial_response - ) - - except BadStatus as err: - self._status = "Failed" - raise HttpResponseError(response=self._initial_response.http_response, error=err) - except BadResponse as err: - self._status = "Failed" - raise HttpResponseError( - response=self._initial_response.http_response, message=str(err), error=err - ) - except OperationFailed as err: - raise HttpResponseError(response=self._initial_response.http_response, error=err) + self._operation._async_url = initial_response # pylint: disable=protected-access + self._status = "InProgress" # assume the operation is ongoing for now, so the actual status gets polled def get_continuation_token(self): - return self._operation.get_polling_url().http_response.headers["azure-asyncoperation"] + return self._operation.get_polling_url() @classmethod def from_continuation_token(cls, continuation_token, **kwargs): @@ -88,16 +60,6 @@ def _extract_delay(self): return 0 return super(KeyVaultAsyncBackupClientPollingMethod, self)._extract_delay() - async def update_status(self): - """Update the current status of the LRO. - """ - if self._continuation_url: - self._pipeline_response = await self.request_status(self._continuation_url) - else: - self._pipeline_response = await self.request_status(self._operation.get_polling_url()) - _raise_if_bad_http_status_and_method(self._pipeline_response.http_response) - self._status = self._operation.get_status(self._pipeline_response) - async def request_status(self, status_link): """Do a simple GET to this status link. diff --git a/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_internal/polling.py b/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_internal/polling.py index 613f1cd01391..1951188f87c8 100644 --- a/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_internal/polling.py +++ b/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_internal/polling.py @@ -4,15 +4,7 @@ # ------------------------------------ from typing import TYPE_CHECKING -from azure.core.polling.base_polling import ( - BadResponse, - BadStatus, - HttpResponseError, - LROBasePolling, - OperationFailed, - OperationResourcePolling, - _raise_if_bad_http_status_and_method -) +from azure.core.polling.base_polling import LROBasePolling, OperationResourcePolling if TYPE_CHECKING: from typing import Union @@ -31,31 +23,6 @@ class KeyVaultBackupClientPolling(OperationResourcePolling): def __init__(self): super(KeyVaultBackupClientPolling, self).__init__(operation_location_header="azure-asyncoperation") - def can_poll(self, pipeline_response, **kwargs): - """Answer if this polling method could be used. - """ - if kwargs.get("continuation_url"): - return True - response = pipeline_response.http_response - return self._operation_location_header in response.headers - - def set_initial_status(self, pipeline_response, **kwargs): - # type: (PipelineResponseType) -> str - """Process first response after initiating long running operation. - - :param azure.core.pipeline.PipelineResponse response: initial REST call response. - """ - self._request = pipeline_response.http_response.request - response = pipeline_response.http_response - - self._async_url = kwargs.get("continuation_url") - if self._async_url is None: - self._set_async_url_if_present(response) - - if response.status_code in {200, 201, 202, 204} and self._async_url: - return "InProgress" - raise OperationFailed("Operation failed or canceled") - def get_final_get_url(self, pipeline_response): return None @@ -67,44 +34,23 @@ def initialize(self, client, initial_response, deserialization_callback): :param initial_response: The initial pipeline response of the poller, or a URL continuation token :raises: HttpResponseError if initial status is incorrect LRO state """ - self._client = client - self._deserialization_callback = deserialization_callback self._continuation_url = None - continuation_url = type(initial_response) == str - if continuation_url: + if type(initial_response) != str: + super(KeyVaultBackupClientPollingMethod, self).initialize( + client, initial_response, deserialization_callback + ) + + else: + self._client = client self._continuation_url = initial_response + self._deserialization_callback = deserialization_callback self._operation = self._lro_algorithms[0] - self._status = "InProgress" # assume the operation is ongoing for now - else: - self._pipeline_response = self._initial_response = initial_response - - for operation in self._lro_algorithms: - if operation.can_poll(self._initial_response, continuation_url=continuation_url): - self._operation = operation - break - else: - raise BadResponse("Unable to find status link for polling.") - - try: - _raise_if_bad_http_status_and_method(self._initial_response.http_response) - self._status = self._operation.set_initial_status( - self._initial_response, continuation_url=initial_response - ) - - except BadStatus as err: - self._status = "Failed" - raise HttpResponseError(response=self._initial_response.http_response, error=err) - except BadResponse as err: - self._status = "Failed" - raise HttpResponseError( - response=self._initial_response.http_response, message=str(err), error=err - ) - except OperationFailed as err: - raise HttpResponseError(response=self._initial_response.http_response, error=err) + self._operation._async_url = initial_response # pylint: disable=protected-access + self._status = "InProgress" # assume the operation is ongoing for now so the actual status gets polled def get_continuation_token(self): - return self._operation.get_polling_url().http_response.headers["azure-asyncoperation"] + return self._operation.get_polling_url() @classmethod def from_continuation_token(cls, continuation_token, **kwargs): @@ -136,16 +82,6 @@ def _extract_delay(self): return 0 return super(KeyVaultBackupClientPollingMethod, self)._extract_delay() - def update_status(self): - """Update the current status of the LRO. - """ - if self._continuation_url: - self._pipeline_response = self.request_status(self._continuation_url) - else: - self._pipeline_response = self.request_status(self._operation.get_polling_url()) - _raise_if_bad_http_status_and_method(self._pipeline_response.http_response) - self._status = self._operation.get_status(self._pipeline_response) - def request_status(self, status_link): """Do a simple GET to this status link. diff --git a/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/aio/_backup_client.py b/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/aio/_backup_client.py index 7142ac0bd739..f9b51c1a4046 100644 --- a/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/aio/_backup_client.py +++ b/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/aio/_backup_client.py @@ -5,8 +5,6 @@ import functools from typing import TYPE_CHECKING -from azure.core.polling.async_base_polling import AsyncLROBasePolling - from .._internal import AsyncKeyVaultClientBase, parse_folder_url from .._internal.async_polling import KeyVaultAsyncBackupClientPollingMethod from .._internal.polling import KeyVaultBackupClientPolling @@ -72,7 +70,7 @@ async def begin_restore(self, folder_url: str, sas_token: str, **kwargs: "Any") container_url, folder_name = parse_folder_url(folder_url) sas_parameter = self._models.SASTokenParameter(storage_resource_uri=container_url, token=sas_token) - polling = AsyncLROBasePolling( + polling = KeyVaultAsyncBackupClientPollingMethod( lro_algorithms=[KeyVaultBackupClientPolling()], timeout=kwargs.pop("_polling_interval", 5), **kwargs ) diff --git a/sdk/keyvault/azure-keyvault-administration/tests/recordings/test_backup_client.test_full_backup_and_restore.yaml b/sdk/keyvault/azure-keyvault-administration/tests/recordings/test_backup_client.test_full_backup_and_restore.yaml index c117da91305c..befd4c982df1 100644 --- a/sdk/keyvault/azure-keyvault-administration/tests/recordings/test_backup_client.test_full_backup_and_restore.yaml +++ b/sdk/keyvault/azure-keyvault-administration/tests/recordings/test_backup_client.test_full_backup_and_restore.yaml @@ -113,7 +113,7 @@ interactions: cache-control: - no-cache content-length: - - '294' + - '138' content-security-policy: - default-src 'self' content-type: From e27d134267fd1f2d554d9f5bf3c179b89fba73f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?McCoy=20Pati=C3=B1o?= Date: Thu, 10 Jun 2021 22:49:26 -0700 Subject: [PATCH 05/27] base64 encoded cont tokens --- .../keyvault/administration/_internal/async_polling.py | 8 ++++++-- .../azure/keyvault/administration/_internal/polling.py | 7 +++++-- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_internal/async_polling.py b/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_internal/async_polling.py index 15c352d1cb7d..20bcbac59ccd 100644 --- a/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_internal/async_polling.py +++ b/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_internal/async_polling.py @@ -2,6 +2,8 @@ # Copyright (c) Microsoft Corporation. # Licensed under the MIT License. # ------------------------------------ +import base64 + from azure.core.polling.async_base_polling import AsyncLROBasePolling @@ -28,7 +30,8 @@ def initialize(self, client, initial_response, deserialization_callback): self._status = "InProgress" # assume the operation is ongoing for now, so the actual status gets polled def get_continuation_token(self): - return self._operation.get_polling_url() + # type() -> str + return base64.b64encode(self._operation.get_polling_url().encode()).decode("ascii") @classmethod def from_continuation_token(cls, continuation_token, **kwargs): @@ -44,7 +47,8 @@ def from_continuation_token(cls, continuation_token, **kwargs): except KeyError: raise ValueError("Need kwarg 'deserialization_callback' to be recreated from continuation_token") - return client, continuation_token, deserialization_callback + continuation_url = base64.b64decode(continuation_token.encode()).decode("ascii") + return client, continuation_url, deserialization_callback def _parse_resource(self, pipeline_response): # type: (PipelineResponseType) -> Optional[Any] diff --git a/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_internal/polling.py b/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_internal/polling.py index 1951188f87c8..16501eddfc39 100644 --- a/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_internal/polling.py +++ b/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_internal/polling.py @@ -2,6 +2,7 @@ # Copyright (c) Microsoft Corporation. # Licensed under the MIT License. # ------------------------------------ +import base64 from typing import TYPE_CHECKING from azure.core.polling.base_polling import LROBasePolling, OperationResourcePolling @@ -50,7 +51,8 @@ def initialize(self, client, initial_response, deserialization_callback): self._status = "InProgress" # assume the operation is ongoing for now so the actual status gets polled def get_continuation_token(self): - return self._operation.get_polling_url() + # type() -> str + return base64.b64encode(self._operation.get_polling_url().encode()).decode("ascii") @classmethod def from_continuation_token(cls, continuation_token, **kwargs): @@ -66,7 +68,8 @@ def from_continuation_token(cls, continuation_token, **kwargs): except KeyError: raise ValueError("Need kwarg 'deserialization_callback' to be recreated from continuation_token") - return client, continuation_token, deserialization_callback + continuation_url = base64.b64decode(continuation_token.encode()).decode("ascii") + return client, continuation_url, deserialization_callback def _parse_resource(self, pipeline_response): # type: (PipelineResponseType) -> Optional[Any] From dc742f97f15000bea7c20aeea9b08da652af0be6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?McCoy=20Pati=C3=B1o?= Date: Fri, 11 Jun 2021 09:36:14 -0700 Subject: [PATCH 06/27] cleaned up --- .../administration/_internal/async_polling.py | 27 ++++++++++--------- .../administration/_internal/polling.py | 26 +++++++++--------- 2 files changed, 26 insertions(+), 27 deletions(-) diff --git a/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_internal/async_polling.py b/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_internal/async_polling.py index 20bcbac59ccd..c46919e4d2b9 100644 --- a/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_internal/async_polling.py +++ b/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_internal/async_polling.py @@ -3,9 +3,13 @@ # Licensed under the MIT License. # ------------------------------------ import base64 +from typing import TYPE_CHECKING from azure.core.polling.async_base_polling import AsyncLROBasePolling +if TYPE_CHECKING: + from typing import Optional + class KeyVaultAsyncBackupClientPollingMethod(AsyncLROBasePolling): def initialize(self, client, initial_response, deserialization_callback): @@ -14,21 +18,18 @@ def initialize(self, client, initial_response, deserialization_callback): :param initial_response: The initial pipeline response of the poller, or a URL continuation token :raises: HttpResponseError if initial status is incorrect LRO state """ - self._continuation_url = None - - if type(initial_response) != str: - super(KeyVaultAsyncBackupClientPollingMethod, self).initialize( - client, initial_response, deserialization_callback - ) - - else: + if isinstance(initial_response, str): self._client = client - self._continuation_url = initial_response self._deserialization_callback = deserialization_callback self._operation = self._lro_algorithms[0] self._operation._async_url = initial_response # pylint: disable=protected-access self._status = "InProgress" # assume the operation is ongoing for now, so the actual status gets polled + else: + super(KeyVaultAsyncBackupClientPollingMethod, self).initialize( + client, initial_response, deserialization_callback + ) + def get_continuation_token(self): # type() -> str return base64.b64encode(self._operation.get_polling_url().encode()).decode("ascii") @@ -60,9 +61,7 @@ def _parse_resource(self, pipeline_response): return super(KeyVaultAsyncBackupClientPollingMethod, self)._parse_resource(pipeline_response) def _extract_delay(self): - if self._continuation_url: - return 0 - return super(KeyVaultAsyncBackupClientPollingMethod, self)._extract_delay() + return super(KeyVaultAsyncBackupClientPollingMethod, self)._extract_delay() or 0 async def request_status(self, status_link): """Do a simple GET to this status link. @@ -74,10 +73,12 @@ async def request_status(self, status_link): if self._path_format_arguments: status_link = self._client.format_url(status_link, **self._path_format_arguments) request = self._client.get(status_link) - if self._continuation_url is None: + try: # Re-inject 'x-ms-client-request-id' while polling if "request_id" not in self._operation_config: self._operation_config["request_id"] = self._get_request_id() + except AttributeError: + pass return await self._client._pipeline.run( # pylint: disable=protected-access request, stream=False, **self._operation_config ) diff --git a/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_internal/polling.py b/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_internal/polling.py index 16501eddfc39..4886a6ae24fb 100644 --- a/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_internal/polling.py +++ b/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_internal/polling.py @@ -3,12 +3,13 @@ # Licensed under the MIT License. # ------------------------------------ import base64 +import six from typing import TYPE_CHECKING from azure.core.polling.base_polling import LROBasePolling, OperationResourcePolling if TYPE_CHECKING: - from typing import Union + from typing import Optional, Union from azure.core.pipeline import PipelineResponse from azure.core.pipeline.transport import ( HttpResponse, @@ -35,21 +36,18 @@ def initialize(self, client, initial_response, deserialization_callback): :param initial_response: The initial pipeline response of the poller, or a URL continuation token :raises: HttpResponseError if initial status is incorrect LRO state """ - self._continuation_url = None - - if type(initial_response) != str: - super(KeyVaultBackupClientPollingMethod, self).initialize( - client, initial_response, deserialization_callback - ) - - else: + if isinstance(initial_response, six.string_types): self._client = client - self._continuation_url = initial_response self._deserialization_callback = deserialization_callback self._operation = self._lro_algorithms[0] self._operation._async_url = initial_response # pylint: disable=protected-access self._status = "InProgress" # assume the operation is ongoing for now so the actual status gets polled + else: + super(KeyVaultBackupClientPollingMethod, self).initialize( + client, initial_response, deserialization_callback + ) + def get_continuation_token(self): # type() -> str return base64.b64encode(self._operation.get_polling_url().encode()).decode("ascii") @@ -81,9 +79,7 @@ def _parse_resource(self, pipeline_response): return super(KeyVaultBackupClientPollingMethod, self)._parse_resource(pipeline_response) def _extract_delay(self): - if self._continuation_url: - return 0 - return super(KeyVaultBackupClientPollingMethod, self)._extract_delay() + return super(KeyVaultBackupClientPollingMethod, self)._extract_delay() or 0 def request_status(self, status_link): """Do a simple GET to this status link. @@ -95,10 +91,12 @@ def request_status(self, status_link): if self._path_format_arguments: status_link = self._client.format_url(status_link, **self._path_format_arguments) request = self._client.get(status_link) - if self._continuation_url is None: + try: # Re-inject 'x-ms-client-request-id' while polling if "request_id" not in self._operation_config: self._operation_config["request_id"] = self._get_request_id() + except AttributeError: + pass return self._client._pipeline.run( # pylint: disable=protected-access request, stream=False, **self._operation_config ) From afbf7eafd15c437169dd61c83801ecaadfeeb94c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?McCoy=20Pati=C3=B1o?= Date: Fri, 11 Jun 2021 10:47:37 -0700 Subject: [PATCH 07/27] poller testing --- sdk/keyvault/azure-keyvault-administration/setup.py | 2 +- ...test_backup_client_async.test_full_backup_and_restore.yaml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/sdk/keyvault/azure-keyvault-administration/setup.py b/sdk/keyvault/azure-keyvault-administration/setup.py index 307b1ef511fc..d271c634540b 100644 --- a/sdk/keyvault/azure-keyvault-administration/setup.py +++ b/sdk/keyvault/azure-keyvault-administration/setup.py @@ -80,7 +80,7 @@ "azure.keyvault", ] ), - install_requires=["azure-common~=1.1", "azure-core<2.0.0,>=1.11.0", "msrest>=0.6.21"], + install_requires=["azure-common~=1.1", "azure-core<2.0.0,>=1.11.0", "msrest>=0.6.21", "six>=1.11.0"], extras_require={ ":python_version<'3.0'": ["azure-keyvault-nspkg"], ":python_version<'3.4'": ["enum34>=1.0.4"], diff --git a/sdk/keyvault/azure-keyvault-administration/tests/recordings/test_backup_client_async.test_full_backup_and_restore.yaml b/sdk/keyvault/azure-keyvault-administration/tests/recordings/test_backup_client_async.test_full_backup_and_restore.yaml index 7f5361a2d939..b8e1036e4552 100644 --- a/sdk/keyvault/azure-keyvault-administration/tests/recordings/test_backup_client_async.test_full_backup_and_restore.yaml +++ b/sdk/keyvault/azure-keyvault-administration/tests/recordings/test_backup_client_async.test_full_backup_and_restore.yaml @@ -37,7 +37,7 @@ interactions: Accept: - application/json Content-Length: - - '235' + - '233' Content-Type: - application/json User-Agent: @@ -100,7 +100,7 @@ interactions: Accept: - application/json Content-Length: - - '319' + - '317' Content-Type: - application/json User-Agent: From b85fbe37a4228ade322a268fe72661ee2bf53709 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?McCoy=20Pati=C3=B1o?= Date: Fri, 11 Jun 2021 11:18:05 -0700 Subject: [PATCH 08/27] resolve flaky test --- ...est_backup_client.test_full_backup_and_restore.yaml | 4 ++-- .../tests/test_backup_client_async.py | 10 +--------- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/sdk/keyvault/azure-keyvault-administration/tests/recordings/test_backup_client.test_full_backup_and_restore.yaml b/sdk/keyvault/azure-keyvault-administration/tests/recordings/test_backup_client.test_full_backup_and_restore.yaml index befd4c982df1..fa826e2cd12d 100644 --- a/sdk/keyvault/azure-keyvault-administration/tests/recordings/test_backup_client.test_full_backup_and_restore.yaml +++ b/sdk/keyvault/azure-keyvault-administration/tests/recordings/test_backup_client.test_full_backup_and_restore.yaml @@ -53,7 +53,7 @@ interactions: Connection: - keep-alive Content-Length: - - '233' + - '237' Content-Type: - application/json User-Agent: @@ -150,7 +150,7 @@ interactions: Connection: - keep-alive Content-Length: - - '317' + - '321' Content-Type: - application/json User-Agent: diff --git a/sdk/keyvault/azure-keyvault-administration/tests/test_backup_client_async.py b/sdk/keyvault/azure-keyvault-administration/tests/test_backup_client_async.py index 85033742fef4..4c132d262f10 100644 --- a/sdk/keyvault/azure-keyvault-administration/tests/test_backup_client_async.py +++ b/sdk/keyvault/azure-keyvault-administration/tests/test_backup_client_async.py @@ -40,15 +40,7 @@ def tearDown(self): @property def credential(self): if self.is_live: - from dotenv import load_dotenv - from azure.identity.aio import ClientSecretCredential - import os - load_dotenv - return ClientSecretCredential( - tenant_id=os.environ["KEYVAULT_TENANT_ID"], - client_id=os.environ["KEYVAULT_CLIENT_ID"], - client_secret=os.environ["KEYVAULT_CLIENT_SECRET"] - ) + return DefaultAzureCredential() async def get_token(*_, **__): return AccessToken("secret", time.time() + 3600) From d945cd32c352fca6a1ad7d678666a02fb34ae83d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?McCoy=20Pati=C3=B1o?= Date: Fri, 11 Jun 2021 14:08:10 -0700 Subject: [PATCH 09/27] live-only sync poller test --- ...p_client.test_full_backup_and_restore.yaml | 4 +- ...t_full_backup_and_restore_rehydration.yaml | 377 ++++++++++++++++++ .../tests/test_backup_client.py | 97 +++++ .../tests/test_backup_client_async.py | 21 +- 4 files changed, 496 insertions(+), 3 deletions(-) create mode 100644 sdk/keyvault/azure-keyvault-administration/tests/recordings/test_backup_client.test_full_backup_and_restore_rehydration.yaml diff --git a/sdk/keyvault/azure-keyvault-administration/tests/recordings/test_backup_client.test_full_backup_and_restore.yaml b/sdk/keyvault/azure-keyvault-administration/tests/recordings/test_backup_client.test_full_backup_and_restore.yaml index fa826e2cd12d..befd4c982df1 100644 --- a/sdk/keyvault/azure-keyvault-administration/tests/recordings/test_backup_client.test_full_backup_and_restore.yaml +++ b/sdk/keyvault/azure-keyvault-administration/tests/recordings/test_backup_client.test_full_backup_and_restore.yaml @@ -53,7 +53,7 @@ interactions: Connection: - keep-alive Content-Length: - - '237' + - '233' Content-Type: - application/json User-Agent: @@ -150,7 +150,7 @@ interactions: Connection: - keep-alive Content-Length: - - '321' + - '317' Content-Type: - application/json User-Agent: diff --git a/sdk/keyvault/azure-keyvault-administration/tests/recordings/test_backup_client.test_full_backup_and_restore_rehydration.yaml b/sdk/keyvault/azure-keyvault-administration/tests/recordings/test_backup_client.test_full_backup_and_restore_rehydration.yaml new file mode 100644 index 000000000000..83cedcade9a8 --- /dev/null +++ b/sdk/keyvault/azure-keyvault-administration/tests/recordings/test_backup_client.test_full_backup_and_restore_rehydration.yaml @@ -0,0 +1,377 @@ +interactions: +- request: + body: null + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '0' + Content-Type: + - application/json + User-Agent: + - azsdk-python-keyvault-administration/4.0.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) + method: POST + uri: https://managedhsm/backup?api-version=7.2 + response: + body: + string: '' + headers: + cache-control: + - no-cache + content-length: + - '0' + content-security-policy: + - default-src 'self' + content-type: + - application/json; charset=utf-8 + strict-transport-security: + - max-age=31536000; includeSubDomains + www-authenticate: + - Bearer authorization="https://login.microsoftonline.com/00000000-0000-0000-0000-000000000000", + resource="https://managedhsm.azure.net" + x-content-type-options: + - nosniff + x-frame-options: + - SAMEORIGIN + x-ms-server-latency: + - '0' + status: + code: 401 + message: Unauthorized +- request: + body: '{"storageResourceUri": "https://storname.blob.core.windows.net/containerbe7dxjxuj4ise25", + "token": "redacted"}' + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '233' + Content-Type: + - application/json + User-Agent: + - azsdk-python-keyvault-administration/4.0.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) + method: POST + uri: https://managedhsm/backup?api-version=7.2 + response: + body: + string: '{"status":"InProgress","statusDetails":null,"error":null,"startTime":1623444371,"endTime":null,"jobId":"2d408948e1ec41208a7b982598998c94","azureStorageBlobContainerUri":null}' + headers: + azure-asyncoperation: + - https://managedhsm/backup/2d408948e1ec41208a7b982598998c94/pending + cache-control: + - no-cache + content-length: + - '174' + content-security-policy: + - default-src 'self' + content-type: + - application/json; charset=utf-8 + date: + - Fri, 11 Jun 2021 20:46:10 GMT + server: + - Kestrel + strict-transport-security: + - max-age=31536000; includeSubDomains + x-content-type-options: + - nosniff + x-frame-options: + - SAMEORIGIN + x-ms-keyvault-network-info: + - conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=Ipv4; + x-ms-keyvault-region: + - centralus + x-ms-server-latency: + - '2213' + status: + code: 202 + message: '' +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-keyvault-administration/4.0.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) + method: GET + uri: https://managedhsm/backup/2d408948e1ec41208a7b982598998c94/pending + response: + body: + string: '{"azureStorageBlobContainerUri":"https://storname.blob.core.windows.net/containerbe7dxjxuj4ise25/mhsm-mcpatinotesthsm-2021061120461128","endTime":1623444380,"error":null,"jobId":"2d408948e1ec41208a7b982598998c94","startTime":1623444371,"status":"Succeeded","statusDetails":null}' + headers: + cache-control: + - no-cache + content-length: + - '294' + content-security-policy: + - default-src 'self' + content-type: + - application/json; charset=utf-8 + date: + - Fri, 11 Jun 2021 20:46:23 GMT + server: + - Kestrel + strict-transport-security: + - max-age=31536000; includeSubDomains + x-content-type-options: + - nosniff + x-frame-options: + - SAMEORIGIN + x-ms-build-version: + - 1.0.20210520-1-d6634624-develop + x-ms-keyvault-network-info: + - conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=Ipv4; + x-ms-keyvault-region: + - centralus + x-ms-server-latency: + - '2088' + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-keyvault-administration/4.0.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) + method: GET + uri: https://managedhsm/backup/2d408948e1ec41208a7b982598998c94/pending + response: + body: + string: '{"azureStorageBlobContainerUri":"https://storname.blob.core.windows.net/containerbe7dxjxuj4ise25/mhsm-mcpatinotesthsm-2021061120461128","endTime":1623444380,"error":null,"jobId":"2d408948e1ec41208a7b982598998c94","startTime":1623444371,"status":"Succeeded","statusDetails":null}' + headers: + cache-control: + - no-cache + content-length: + - '294' + content-security-policy: + - default-src 'self' + content-type: + - application/json; charset=utf-8 + date: + - Fri, 11 Jun 2021 20:46:25 GMT + server: + - Kestrel + strict-transport-security: + - max-age=31536000; includeSubDomains + x-content-type-options: + - nosniff + x-frame-options: + - SAMEORIGIN + x-ms-build-version: + - 1.0.20210520-1-d6634624-develop + x-ms-keyvault-network-info: + - conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=Ipv4; + x-ms-keyvault-region: + - centralus + x-ms-server-latency: + - '1979' + status: + code: 200 + message: OK +- request: + body: '{"sasTokenParameters": {"storageResourceUri": "https://storname.blob.core.windows.net/containerbe7dxjxuj4ise25", + "token": "redacted"}, "folderToRestore": "mhsm-mcpatinotesthsm-2021061120461128"}' + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '317' + Content-Type: + - application/json + User-Agent: + - azsdk-python-keyvault-administration/4.0.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) + method: PUT + uri: https://managedhsm/restore?api-version=7.2 + response: + body: + string: '{"endTime":null,"error":null,"jobId":"f1192f26b5ee480aa7b8dc30516424b9","startTime":1623444387,"status":"InProgress","statusDetails":null}' + headers: + azure-asyncoperation: + - https://managedhsm/restore/f1192f26b5ee480aa7b8dc30516424b9/pending + cache-control: + - no-cache + content-length: + - '138' + content-security-policy: + - default-src 'self' + content-type: + - application/json; charset=utf-8 + date: + - Fri, 11 Jun 2021 20:46:27 GMT + server: + - Kestrel + strict-transport-security: + - max-age=31536000; includeSubDomains + x-content-type-options: + - nosniff + x-frame-options: + - SAMEORIGIN + x-ms-keyvault-network-info: + - conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=Ipv4; + x-ms-keyvault-region: + - centralus + x-ms-server-latency: + - '2065' + status: + code: 202 + message: '' +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-keyvault-administration/4.0.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) + method: GET + uri: https://managedhsm/restore/f1192f26b5ee480aa7b8dc30516424b9/pending + response: + body: + string: '{"endTime":null,"error":null,"jobId":"f1192f26b5ee480aa7b8dc30516424b9","startTime":1623444387,"status":"InProgress","statusDetails":null}' + headers: + cache-control: + - no-cache + content-length: + - '138' + content-security-policy: + - default-src 'self' + content-type: + - application/json; charset=utf-8 + date: + - Fri, 11 Jun 2021 20:46:39 GMT + server: + - Kestrel + strict-transport-security: + - max-age=31536000; includeSubDomains + x-content-type-options: + - nosniff + x-frame-options: + - SAMEORIGIN + x-ms-build-version: + - 1.0.20210520-1-d6634624-develop + x-ms-keyvault-network-info: + - conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=Ipv4; + x-ms-keyvault-region: + - centralus + x-ms-server-latency: + - '1976' + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-keyvault-administration/4.0.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) + method: GET + uri: https://managedhsm/restore/f1192f26b5ee480aa7b8dc30516424b9/pending + response: + body: + string: '{"endTime":1623444403,"error":null,"jobId":"f1192f26b5ee480aa7b8dc30516424b9","startTime":1623444387,"status":"Succeeded","statusDetails":null}' + headers: + cache-control: + - no-cache + content-length: + - '143' + content-security-policy: + - default-src 'self' + content-type: + - application/json; charset=utf-8 + date: + - Fri, 11 Jun 2021 20:46:47 GMT + server: + - Kestrel + strict-transport-security: + - max-age=31536000; includeSubDomains + x-content-type-options: + - nosniff + x-frame-options: + - SAMEORIGIN + x-ms-build-version: + - 1.0.20210520-1-d6634624-develop + x-ms-keyvault-network-info: + - conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=Ipv4; + x-ms-keyvault-region: + - centralus + x-ms-server-latency: + - '3084' + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-keyvault-administration/4.0.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) + method: GET + uri: https://managedhsm/restore/f1192f26b5ee480aa7b8dc30516424b9/pending + response: + body: + string: '{"endTime":1623444403,"error":null,"jobId":"f1192f26b5ee480aa7b8dc30516424b9","startTime":1623444387,"status":"Succeeded","statusDetails":null}' + headers: + cache-control: + - no-cache + content-length: + - '143' + content-security-policy: + - default-src 'self' + content-type: + - application/json; charset=utf-8 + date: + - Fri, 11 Jun 2021 20:46:49 GMT + server: + - Kestrel + strict-transport-security: + - max-age=31536000; includeSubDomains + x-content-type-options: + - nosniff + x-frame-options: + - SAMEORIGIN + x-ms-build-version: + - 1.0.20210520-1-d6634624-develop + x-ms-keyvault-network-info: + - conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=Ipv4; + x-ms-keyvault-region: + - centralus + x-ms-server-latency: + - '2168' + status: + code: 200 + message: OK +version: 1 diff --git a/sdk/keyvault/azure-keyvault-administration/tests/test_backup_client.py b/sdk/keyvault/azure-keyvault-administration/tests/test_backup_client.py index 5f9ec07314f2..32914a0e216f 100644 --- a/sdk/keyvault/azure-keyvault-administration/tests/test_backup_client.py +++ b/sdk/keyvault/azure-keyvault-administration/tests/test_backup_client.py @@ -58,6 +58,42 @@ def test_full_backup_and_restore(self, container_uri, sas_token): restore_poller = backup_client.begin_restore(backup_operation.folder_url, sas_token) restore_poller.wait() + @ResourceGroupPreparer(random_name_enabled=True, use_cache=True) + @StorageAccountPreparer(random_name_enabled=True) + @BlobContainerPreparer() + def test_full_backup_and_restore_rehydration(self, container_uri, sas_token): + # backup the vault + backup_client = KeyVaultBackupClient(self.managed_hsm["url"], self.credential) + backup_poller = backup_client.begin_backup(container_uri, sas_token) + + # create a new poller from a continuation token + token = backup_poller._polling_method.get_continuation_token() + rehydrated = backup_client.begin_backup(container_uri, sas_token, continuation_token=token) + + # check that pollers and polling methods behave as expected + assert rehydrated.status() == "InProgress" + assert not rehydrated.done() or rehydrated.polling_method().finished() + + backup_operation = rehydrated.result() + assert rehydrated.status() == "Succeeded" and rehydrated.polling_method().status() == "Succeeded" + assert backup_operation.folder_url + backup_poller.wait() + + # restore the backup + restore_poller = backup_client.begin_restore(backup_operation.folder_url, sas_token) + + # create a new poller from a continuation token + token = restore_poller._polling_method.get_continuation_token() + rehydrated = backup_client.begin_restore(backup_operation.folder_url, sas_token, continuation_token=token) + + # check that pollers and polling methods behave as expected + assert rehydrated.status() == "InProgress" + assert not rehydrated.done() or rehydrated.polling_method().finished() + + rehydrated.result() + assert rehydrated.status() == "Succeeded" and rehydrated.polling_method().status() == "Succeeded" + restore_poller.wait() + @ResourceGroupPreparer(random_name_enabled=True, use_cache=True) @StorageAccountPreparer(random_name_enabled=True) @BlobContainerPreparer() @@ -82,6 +118,67 @@ def test_selective_key_restore(self, container_uri, sas_token): delete_poller.wait() key_client.purge_deleted_key(key_name) + @ResourceGroupPreparer(random_name_enabled=True, use_cache=True) + @StorageAccountPreparer(random_name_enabled=True) + @BlobContainerPreparer() + def test_backup_client_polling(self, container_uri, sas_token): + if not self.is_live: + pytest.skip("Poller requests are incompatible with vcrpy for synchronous tests") + + # backup the vault + backup_client = KeyVaultBackupClient(self.managed_hsm["url"], self.credential) + backup_poller = backup_client.begin_backup(container_uri, sas_token) + + # create a new poller from a continuation token + token = backup_poller._polling_method.get_continuation_token() + rehydrated = backup_client.begin_backup(container_uri, sas_token, continuation_token=token) + + # check that pollers and polling methods behave as expected + assert backup_poller.status() == "InProgress" + assert not backup_poller.done() or backup_poller.polling_method().finished() + assert rehydrated.status() == "InProgress" + assert not rehydrated.done() or rehydrated.polling_method().finished() + + backup_poller.polling_method().update_status() + assert backup_poller.status() == "InProgress" + rehydrated.polling_method().update_status() + assert rehydrated.status() == "InProgress" + + backup_operation = backup_poller.result() + assert backup_poller.status() == "Succeeded" and backup_poller.polling_method().status() == "Succeeded" + rehydrated_operation = rehydrated.result() + assert rehydrated.status() == "Succeeded" and rehydrated.polling_method().status() == "Succeeded" + assert backup_operation.folder_url == rehydrated_operation.folder_url + + # rehydrate a poller with a continuation token of a completed operation + late_rehydrated = backup_client.begin_backup(container_uri, sas_token, continuation_token=token) + assert late_rehydrated.status() == "InProgress" + late_rehydrated.polling_method().update_status() + assert late_rehydrated.status() == "Succeeded" + + # restore the backup + restore_poller = backup_client.begin_restore(backup_operation.folder_url, sas_token) + + # create a new poller from a continuation token + token = restore_poller._polling_method.get_continuation_token() + rehydrated = backup_client.begin_restore(backup_operation.folder_url, sas_token, continuation_token=token) + + # check that pollers and polling methods behave as expected + assert restore_poller.status() == "InProgress" + assert not restore_poller.done() or restore_poller.polling_method().finished() + assert rehydrated.status() == "InProgress" + assert not rehydrated.done() or rehydrated.polling_method().finished() + + rehydrated.polling_method().update_status() + assert rehydrated.status() == "InProgress" + restore_poller.polling_method().update_status() + assert restore_poller.status() == "InProgress" + + rehydrated.result() + assert rehydrated.status() == "Succeeded" and rehydrated.polling_method().status() == "Succeeded" + restore_poller.result() + assert restore_poller.status() == "Succeeded" and restore_poller.polling_method().status() == "Succeeded" + def test_continuation_token(): """Methods returning pollers should accept continuation tokens""" diff --git a/sdk/keyvault/azure-keyvault-administration/tests/test_backup_client_async.py b/sdk/keyvault/azure-keyvault-administration/tests/test_backup_client_async.py index 4c132d262f10..45914d77eae3 100644 --- a/sdk/keyvault/azure-keyvault-administration/tests/test_backup_client_async.py +++ b/sdk/keyvault/azure-keyvault-administration/tests/test_backup_client_async.py @@ -58,7 +58,26 @@ async def test_full_backup_and_restore(self, container_uri, sas_token): # restore the backup restore_poller = await backup_client.begin_restore(backup_operation.folder_url, sas_token) - await restore_poller.wait() + + # create a new poller from a continuation token + token = restore_poller._polling_method.get_continuation_token() + rehydrated = await backup_client.begin_restore(backup_operation.folder_url, sas_token, continuation_token=token) + + # check that pollers and polling methods behave as expected + assert restore_poller.status() == "InProgress" + assert not restore_poller.done() or restore_poller.polling_method().finished() + assert rehydrated.status() == "InProgress" + assert not rehydrated.done() or rehydrated.polling_method().finished() + + await rehydrated.polling_method().update_status() + assert rehydrated.status() == "InProgress" + await restore_poller.polling_method().update_status() + assert restore_poller.status() == "InProgress" + + await rehydrated.result() + assert rehydrated.status() == "Succeeded" and rehydrated.polling_method().status() == "Succeeded" + await restore_poller.result() + assert restore_poller.status() == "Succeeded" and restore_poller.polling_method().status() == "Succeeded" @ResourceGroupPreparer(random_name_enabled=True, use_cache=True) @StorageAccountPreparer(random_name_enabled=True) From 1efc09e22c537caae70b9646b46388d384781193 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?McCoy=20Pati=C3=B1o?= Date: Fri, 11 Jun 2021 16:14:41 -0700 Subject: [PATCH 10/27] remove private core refs --- .../administration/_internal/async_polling.py | 58 ++++++++-- .../administration/_internal/helpers.py | 104 ++++++++++++++++++ .../administration/_internal/polling.py | 65 ++++++++--- .../tests/test_backup_client.py | 10 +- 4 files changed, 210 insertions(+), 27 deletions(-) create mode 100644 sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_internal/helpers.py diff --git a/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_internal/async_polling.py b/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_internal/async_polling.py index c46919e4d2b9..9186a58fc124 100644 --- a/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_internal/async_polling.py +++ b/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_internal/async_polling.py @@ -3,12 +3,11 @@ # Licensed under the MIT License. # ------------------------------------ import base64 -from typing import TYPE_CHECKING from azure.core.polling.async_base_polling import AsyncLROBasePolling +from azure.core.polling.base_polling import OperationFailed -if TYPE_CHECKING: - from typing import Optional +from .helpers import _failed, _get_retry_after, _raise_if_bad_http_status_and_method class KeyVaultAsyncBackupClientPollingMethod(AsyncLROBasePolling): @@ -23,7 +22,7 @@ def initialize(self, client, initial_response, deserialization_callback): self._deserialization_callback = deserialization_callback self._operation = self._lro_algorithms[0] self._operation._async_url = initial_response # pylint: disable=protected-access - self._status = "InProgress" # assume the operation is ongoing for now, so the actual status gets polled + self._status = "InProgress" # assume the operation is ongoing for now, so the actual status gets polled else: super(KeyVaultAsyncBackupClientPollingMethod, self).initialize( @@ -51,17 +50,54 @@ def from_continuation_token(cls, continuation_token, **kwargs): continuation_url = base64.b64decode(continuation_token.encode()).decode("ascii") return client, continuation_url, deserialization_callback - def _parse_resource(self, pipeline_response): - # type: (PipelineResponseType) -> Optional[Any] - """Assuming this response is a resource, use the deserialization callback to parse it. - If body is empty, assuming no resource to return. + async def _poll(self): # pylint:disable=invalid-overridden-method + """Poll status of operation so long as operation is incomplete and + we have an endpoint to query. + + :param callable update_cmd: The function to call to retrieve the + latest status of the long running operation. + :raises: OperationFailed if operation status 'Failed' or 'Canceled'. + :raises: BadStatus if response status invalid. + :raises: BadResponse if response invalid. """ - if pipeline_response is None: + + while not self.finished(): + await self._delay() + await self.update_status() + + if _failed(self.status()): + raise OperationFailed("Operation failed or canceled") + + final_get_url = self._operation.get_final_get_url(self._pipeline_response) + if final_get_url: + self._pipeline_response = await self.request_status(final_get_url) + _raise_if_bad_http_status_and_method(self._pipeline_response.http_response) + + def resource(self): + try: + return super().resource() + except AttributeError: return None - return super(KeyVaultAsyncBackupClientPollingMethod, self)._parse_resource(pipeline_response) + + async def _delay(self): # pylint:disable=invalid-overridden-method + """Check for a 'retry-after' header to set timeout, + otherwise use configured timeout. + """ + delay = self._extract_delay() + await self._sleep(delay) def _extract_delay(self): - return super(KeyVaultAsyncBackupClientPollingMethod, self)._extract_delay() or 0 + if self._pipeline_response is None: + return 0 + delay = _get_retry_after(self._pipeline_response) + if delay: + return delay + return self._timeout + + def _get_request_id(self): + return self._pipeline_response.http_response.request.headers[ + "x-ms-client-request-id" + ] async def request_status(self, status_link): """Do a simple GET to this status link. diff --git a/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_internal/helpers.py b/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_internal/helpers.py new file mode 100644 index 000000000000..9d0384900c7f --- /dev/null +++ b/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_internal/helpers.py @@ -0,0 +1,104 @@ +# ------------------------------------ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# ------------------------------------ +import datetime +import email.utils +from requests.structures import CaseInsensitiveDict +from typing import TYPE_CHECKING + +from azure.core.polling.base_polling import BadStatus + +if TYPE_CHECKING: + from typing import Union + from azure.core.pipeline.transport import HttpResponse, AsyncHttpResponse + + ResponseType = Union[HttpResponse, AsyncHttpResponse] + + +_FAILED = frozenset(["canceled", "failed"]) + + +def _failed(status): + if hasattr(status, "value"): + status = status.value + return str(status).lower() in _FAILED + + +def _get_retry_after(response): + """Get the value of Retry-After in seconds. + + :param response: The PipelineResponse object + :type response: ~azure.core.pipeline.PipelineResponse + :return: Value of Retry-After in seconds. + :rtype: float or None + """ + headers = CaseInsensitiveDict(response.http_response.headers) + retry_after = headers.get("retry-after") + if retry_after: + return _parse_retry_after(retry_after) + for ms_header in ["retry-after-ms", "x-ms-retry-after-ms"]: + retry_after = headers.get(ms_header) + if retry_after: + parsed_retry_after = _parse_retry_after(retry_after) + return parsed_retry_after / 1000.0 + return None + + +def _parse_http_date(text): + """Parse a HTTP date format into datetime.""" + parsed_date = email.utils.parsedate_tz(text) + return datetime.datetime(*parsed_date[:6], tzinfo=_FixedOffset(parsed_date[9] / 60)) + + +def _parse_retry_after(retry_after): + """Helper to parse Retry-After and get value in seconds. + + :param str retry_after: Retry-After header + :rtype: int + """ + try: + delay = int(retry_after) + except ValueError: + # Not an integer? Try HTTP date + retry_date = _parse_http_date(retry_after) + delay = (retry_date - datetime.datetime.now(retry_date.tzinfo)).total_seconds() + return max(0, delay) + + +def _raise_if_bad_http_status_and_method(response): + # type: (ResponseType) -> None + """Check response status code is valid. + + Must be 200, 201, 202, or 204. + + :raises: BadStatus if invalid status. + """ + code = response.status_code + if code in {200, 201, 202, 204}: + return + raise BadStatus("Invalid return status {!r} for {!r} operation".format(code, response.request.method)) + + +class _FixedOffset(datetime.tzinfo): + """Fixed offset in minutes east from UTC. + + Copy/pasted from Python doc + + :param int offset: offset in minutes + """ + + def __init__(self, offset): + self.__offset = datetime.timedelta(minutes=offset) + + def utcoffset(self, dt): + return self.__offset + + def tzname(self, dt): + return str(self.__offset.total_seconds() / 3600) + + def __repr__(self): + return "".format(self.tzname(None)) + + def dst(self, dt): + return datetime.timedelta(0) diff --git a/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_internal/polling.py b/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_internal/polling.py index 4886a6ae24fb..95335cbca067 100644 --- a/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_internal/polling.py +++ b/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_internal/polling.py @@ -6,16 +6,14 @@ import six from typing import TYPE_CHECKING -from azure.core.polling.base_polling import LROBasePolling, OperationResourcePolling +from azure.core.polling.base_polling import LROBasePolling, OperationFailed, OperationResourcePolling + +from .helpers import _failed, _get_retry_after, _raise_if_bad_http_status_and_method if TYPE_CHECKING: - from typing import Optional, Union + from typing import Union from azure.core.pipeline import PipelineResponse - from azure.core.pipeline.transport import ( - HttpResponse, - AsyncHttpResponse, - HttpRequest, - ) + from azure.core.pipeline.transport import HttpResponse, AsyncHttpResponse, HttpRequest ResponseType = Union[HttpResponse, AsyncHttpResponse] PipelineResponseType = PipelineResponse[HttpRequest, ResponseType] @@ -41,7 +39,7 @@ def initialize(self, client, initial_response, deserialization_callback): self._deserialization_callback = deserialization_callback self._operation = self._lro_algorithms[0] self._operation._async_url = initial_response # pylint: disable=protected-access - self._status = "InProgress" # assume the operation is ongoing for now so the actual status gets polled + self._status = "InProgress" # assume the operation is ongoing for now so the actual status gets polled else: super(KeyVaultBackupClientPollingMethod, self).initialize( @@ -69,17 +67,54 @@ def from_continuation_token(cls, continuation_token, **kwargs): continuation_url = base64.b64decode(continuation_token.encode()).decode("ascii") return client, continuation_url, deserialization_callback - def _parse_resource(self, pipeline_response): - # type: (PipelineResponseType) -> Optional[Any] - """Assuming this response is a resource, use the deserialization callback to parse it. - If body is empty, assuming no resource to return. + def _poll(self): + """Poll status of operation so long as operation is incomplete and + we have an endpoint to query. + + :param callable update_cmd: The function to call to retrieve the + latest status of the long running operation. + :raises: OperationFailed if operation status 'Failed' or 'Canceled'. + :raises: BadStatus if response status invalid. + :raises: BadResponse if response invalid. """ - if pipeline_response is None: + + while not self.finished(): + self._delay() + self.update_status() + + if _failed(self.status()): + raise OperationFailed("Operation failed or canceled") + + final_get_url = self._operation.get_final_get_url(self._pipeline_response) + if final_get_url: + self._pipeline_response = self.request_status(final_get_url) + _raise_if_bad_http_status_and_method(self._pipeline_response.http_response) + + def resource(self): + try: + return super().resource() + except AttributeError: return None - return super(KeyVaultBackupClientPollingMethod, self)._parse_resource(pipeline_response) + + def _delay(self): + """Check for a 'retry-after' header to set timeout, + otherwise use configured timeout. + """ + delay = self._extract_delay() + self._sleep(delay) def _extract_delay(self): - return super(KeyVaultBackupClientPollingMethod, self)._extract_delay() or 0 + if self._pipeline_response is None: + return 0 + delay = _get_retry_after(self._pipeline_response) + if delay: + return delay + return self._timeout + + def _get_request_id(self): + return self._pipeline_response.http_response.request.headers[ + "x-ms-client-request-id" + ] def request_status(self, status_link): """Do a simple GET to this status link. diff --git a/sdk/keyvault/azure-keyvault-administration/tests/test_backup_client.py b/sdk/keyvault/azure-keyvault-administration/tests/test_backup_client.py index 32914a0e216f..61ec635876f4 100644 --- a/sdk/keyvault/azure-keyvault-administration/tests/test_backup_client.py +++ b/sdk/keyvault/azure-keyvault-administration/tests/test_backup_client.py @@ -42,7 +42,15 @@ def tearDown(self): @property def credential(self): if self.is_live: - return DefaultAzureCredential() + from dotenv import load_dotenv + from azure.identity import ClientSecretCredential + import os + load_dotenv() + return ClientSecretCredential( + tenant_id=os.environ["KEYVAULT_TENANT_ID"], + client_id=os.environ["KEYVAULT_CLIENT_ID"], + client_secret=os.environ["KEYVAULT_CLIENT_SECRET"] + ) return mock.Mock(get_token=lambda *_, **__: AccessToken("secret", time.time() + 3600)) @ResourceGroupPreparer(random_name_enabled=True, use_cache=True) From 19a00f3cc5699f8b586b678d00fa00d3a4fbfafe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?McCoy=20Pati=C3=B1o?= Date: Fri, 11 Jun 2021 16:18:31 -0700 Subject: [PATCH 11/27] override public run() --- .../administration/_internal/async_polling.py | 22 ++++++++++++--- .../administration/_internal/polling.py | 28 ++++++++++++++++--- 2 files changed, 42 insertions(+), 8 deletions(-) diff --git a/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_internal/async_polling.py b/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_internal/async_polling.py index 9186a58fc124..9d4da6c39840 100644 --- a/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_internal/async_polling.py +++ b/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_internal/async_polling.py @@ -4,8 +4,9 @@ # ------------------------------------ import base64 +from azure.core.exceptions import HttpResponseError from azure.core.polling.async_base_polling import AsyncLROBasePolling -from azure.core.polling.base_polling import OperationFailed +from azure.core.polling.base_polling import BadResponse, BadStatus, OperationFailed from .helpers import _failed, _get_retry_after, _raise_if_bad_http_status_and_method @@ -50,6 +51,21 @@ def from_continuation_token(cls, continuation_token, **kwargs): continuation_url = base64.b64decode(continuation_token.encode()).decode("ascii") return client, continuation_url, deserialization_callback + async def run(self): # pylint:disable=invalid-overridden-method + try: + await self._poll() + + except BadStatus as err: + self._status = "Failed" + raise HttpResponseError(response=self._pipeline_response.http_response, error=err) + + except BadResponse as err: + self._status = "Failed" + raise HttpResponseError(response=self._pipeline_response.http_response, message=str(err), error=err) + + except OperationFailed as err: + raise HttpResponseError(response=self._pipeline_response.http_response, error=err) + async def _poll(self): # pylint:disable=invalid-overridden-method """Poll status of operation so long as operation is incomplete and we have an endpoint to query. @@ -95,9 +111,7 @@ def _extract_delay(self): return self._timeout def _get_request_id(self): - return self._pipeline_response.http_response.request.headers[ - "x-ms-client-request-id" - ] + return self._pipeline_response.http_response.request.headers["x-ms-client-request-id"] async def request_status(self, status_link): """Do a simple GET to this status link. diff --git a/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_internal/polling.py b/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_internal/polling.py index 95335cbca067..aaa1f9828dba 100644 --- a/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_internal/polling.py +++ b/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_internal/polling.py @@ -6,7 +6,14 @@ import six from typing import TYPE_CHECKING -from azure.core.polling.base_polling import LROBasePolling, OperationFailed, OperationResourcePolling +from azure.core.exceptions import HttpResponseError +from azure.core.polling.base_polling import ( + BadResponse, + BadStatus, + LROBasePolling, + OperationFailed, + OperationResourcePolling, +) from .helpers import _failed, _get_retry_after, _raise_if_bad_http_status_and_method @@ -67,6 +74,21 @@ def from_continuation_token(cls, continuation_token, **kwargs): continuation_url = base64.b64decode(continuation_token.encode()).decode("ascii") return client, continuation_url, deserialization_callback + def run(self): + try: + self._poll() + + except BadStatus as err: + self._status = "Failed" + raise HttpResponseError(response=self._pipeline_response.http_response, error=err) + + except BadResponse as err: + self._status = "Failed" + raise HttpResponseError(response=self._pipeline_response.http_response, message=str(err), error=err) + + except OperationFailed as err: + raise HttpResponseError(response=self._pipeline_response.http_response, error=err) + def _poll(self): """Poll status of operation so long as operation is incomplete and we have an endpoint to query. @@ -112,9 +134,7 @@ def _extract_delay(self): return self._timeout def _get_request_id(self): - return self._pipeline_response.http_response.request.headers[ - "x-ms-client-request-id" - ] + return self._pipeline_response.http_response.request.headers["x-ms-client-request-id"] def request_status(self, status_link): """Do a simple GET to this status link. From 9e0fa34f63ffde5c9f1b8a4b41f9c73a139e0ad2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?McCoy=20Pati=C3=B1o?= Date: Fri, 11 Jun 2021 16:21:53 -0700 Subject: [PATCH 12/27] pylint; remove test flakiness --- .../administration/_internal/polling.py | 2 +- .../tests/test_backup_client.py | 18 +----------------- 2 files changed, 2 insertions(+), 18 deletions(-) diff --git a/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_internal/polling.py b/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_internal/polling.py index aaa1f9828dba..07da0855a948 100644 --- a/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_internal/polling.py +++ b/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_internal/polling.py @@ -3,7 +3,6 @@ # Licensed under the MIT License. # ------------------------------------ import base64 -import six from typing import TYPE_CHECKING from azure.core.exceptions import HttpResponseError @@ -14,6 +13,7 @@ OperationFailed, OperationResourcePolling, ) +import six from .helpers import _failed, _get_retry_after, _raise_if_bad_http_status_and_method diff --git a/sdk/keyvault/azure-keyvault-administration/tests/test_backup_client.py b/sdk/keyvault/azure-keyvault-administration/tests/test_backup_client.py index 61ec635876f4..1b3112f707ad 100644 --- a/sdk/keyvault/azure-keyvault-administration/tests/test_backup_client.py +++ b/sdk/keyvault/azure-keyvault-administration/tests/test_backup_client.py @@ -42,15 +42,7 @@ def tearDown(self): @property def credential(self): if self.is_live: - from dotenv import load_dotenv - from azure.identity import ClientSecretCredential - import os - load_dotenv() - return ClientSecretCredential( - tenant_id=os.environ["KEYVAULT_TENANT_ID"], - client_id=os.environ["KEYVAULT_CLIENT_ID"], - client_secret=os.environ["KEYVAULT_CLIENT_SECRET"] - ) + return DefaultAzureCredential() return mock.Mock(get_token=lambda *_, **__: AccessToken("secret", time.time() + 3600)) @ResourceGroupPreparer(random_name_enabled=True, use_cache=True) @@ -78,10 +70,6 @@ def test_full_backup_and_restore_rehydration(self, container_uri, sas_token): token = backup_poller._polling_method.get_continuation_token() rehydrated = backup_client.begin_backup(container_uri, sas_token, continuation_token=token) - # check that pollers and polling methods behave as expected - assert rehydrated.status() == "InProgress" - assert not rehydrated.done() or rehydrated.polling_method().finished() - backup_operation = rehydrated.result() assert rehydrated.status() == "Succeeded" and rehydrated.polling_method().status() == "Succeeded" assert backup_operation.folder_url @@ -94,10 +82,6 @@ def test_full_backup_and_restore_rehydration(self, container_uri, sas_token): token = restore_poller._polling_method.get_continuation_token() rehydrated = backup_client.begin_restore(backup_operation.folder_url, sas_token, continuation_token=token) - # check that pollers and polling methods behave as expected - assert rehydrated.status() == "InProgress" - assert not rehydrated.done() or rehydrated.polling_method().finished() - rehydrated.result() assert rehydrated.status() == "Succeeded" and rehydrated.polling_method().status() == "Succeeded" restore_poller.wait() From 284d5af9558b4f00a74d1f9930fd26ca39e7b1ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?McCoy=20Pati=C3=B1o?= Date: Fri, 11 Jun 2021 18:31:26 -0700 Subject: [PATCH 13/27] pylint; clean request_status --- .../administration/_internal/async_polling.py | 6 +++--- .../keyvault/administration/_internal/helpers.py | 2 +- .../keyvault/administration/_internal/polling.py | 11 ++++++----- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_internal/async_polling.py b/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_internal/async_polling.py index 9d4da6c39840..e55bdb1dc1e9 100644 --- a/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_internal/async_polling.py +++ b/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_internal/async_polling.py @@ -120,15 +120,15 @@ async def request_status(self, status_link): :rtype: azure.core.pipeline.PipelineResponse """ - if self._path_format_arguments: - status_link = self._client.format_url(status_link, **self._path_format_arguments) - request = self._client.get(status_link) try: + if self._path_format_arguments: + status_link = self._client.format_url(status_link, **self._path_format_arguments) # Re-inject 'x-ms-client-request-id' while polling if "request_id" not in self._operation_config: self._operation_config["request_id"] = self._get_request_id() except AttributeError: pass + request = self._client.get(status_link) return await self._client._pipeline.run( # pylint: disable=protected-access request, stream=False, **self._operation_config ) diff --git a/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_internal/helpers.py b/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_internal/helpers.py index 9d0384900c7f..dcec54f9bb6f 100644 --- a/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_internal/helpers.py +++ b/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_internal/helpers.py @@ -4,8 +4,8 @@ # ------------------------------------ import datetime import email.utils -from requests.structures import CaseInsensitiveDict from typing import TYPE_CHECKING +from requests.structures import CaseInsensitiveDict from azure.core.polling.base_polling import BadStatus diff --git a/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_internal/polling.py b/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_internal/polling.py index 07da0855a948..7b5180b0027c 100644 --- a/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_internal/polling.py +++ b/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_internal/polling.py @@ -5,6 +5,8 @@ import base64 from typing import TYPE_CHECKING +import six + from azure.core.exceptions import HttpResponseError from azure.core.polling.base_polling import ( BadResponse, @@ -13,13 +15,12 @@ OperationFailed, OperationResourcePolling, ) -import six from .helpers import _failed, _get_retry_after, _raise_if_bad_http_status_and_method if TYPE_CHECKING: from typing import Union - from azure.core.pipeline import PipelineResponse + from azure.core.pipeline import PipelineResponse # pylint: disable=ungrouped-imports from azure.core.pipeline.transport import HttpResponse, AsyncHttpResponse, HttpRequest ResponseType = Union[HttpResponse, AsyncHttpResponse] @@ -143,15 +144,15 @@ def request_status(self, status_link): :rtype: azure.core.pipeline.PipelineResponse """ - if self._path_format_arguments: - status_link = self._client.format_url(status_link, **self._path_format_arguments) - request = self._client.get(status_link) try: + if self._path_format_arguments: + status_link = self._client.format_url(status_link, **self._path_format_arguments) # Re-inject 'x-ms-client-request-id' while polling if "request_id" not in self._operation_config: self._operation_config["request_id"] = self._get_request_id() except AttributeError: pass + request = self._client.get(status_link) return self._client._pipeline.run( # pylint: disable=protected-access request, stream=False, **self._operation_config ) From ea4f4c1f3ae1d4ad3f9486763322178772c49c44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?McCoy=20Pati=C3=B1o?= Date: Fri, 11 Jun 2021 18:54:52 -0700 Subject: [PATCH 14/27] tests passing after rebase --- ...nt_async.test_full_backup_and_restore.yaml | 165 +++++++++++++----- 1 file changed, 124 insertions(+), 41 deletions(-) diff --git a/sdk/keyvault/azure-keyvault-administration/tests/recordings/test_backup_client_async.test_full_backup_and_restore.yaml b/sdk/keyvault/azure-keyvault-administration/tests/recordings/test_backup_client_async.test_full_backup_and_restore.yaml index b8e1036e4552..04fb05938e6c 100644 --- a/sdk/keyvault/azure-keyvault-administration/tests/recordings/test_backup_client_async.test_full_backup_and_restore.yaml +++ b/sdk/keyvault/azure-keyvault-administration/tests/recordings/test_backup_client_async.test_full_backup_and_restore.yaml @@ -9,7 +9,7 @@ interactions: Content-Type: - application/json User-Agent: - - azsdk-python-keyvault-administration/4.0.0b4 Python/3.6.9 (Linux-5.4.72-microsoft-standard-WSL2-x86_64-with-Ubuntu-18.04-bionic) + - azsdk-python-keyvault-administration/4.0.0b4 Python/3.5.3 (Windows-10-10.0.19041-SP0) method: POST uri: https://managedhsm/backup?api-version=7.2 response: @@ -25,42 +25,41 @@ interactions: resource="https://managedhsm.azure.net" x-content-type-options: nosniff x-frame-options: SAMEORIGIN - x-ms-server-latency: '0' + x-ms-server-latency: '2' status: code: 401 message: Unauthorized url: https://mcpatinotesthsm.managedhsm.azure.net/backup?api-version=7.2 - request: - body: '{"storageResourceUri": "https://storname.blob.core.windows.net/container37sfaze3bkss4j3", - "token": "redacted"}' + body: '{"token": "redacted", "storageResourceUri": "https://storname.blob.core.windows.net/containerk7lnzcoq4emt6gf"}' headers: Accept: - application/json Content-Length: - - '233' + - '235' Content-Type: - application/json User-Agent: - - azsdk-python-keyvault-administration/4.0.0b4 Python/3.6.9 (Linux-5.4.72-microsoft-standard-WSL2-x86_64-with-Ubuntu-18.04-bionic) + - azsdk-python-keyvault-administration/4.0.0b4 Python/3.5.3 (Windows-10-10.0.19041-SP0) method: POST uri: https://managedhsm/backup?api-version=7.2 response: body: - string: '{"status":"InProgress","statusDetails":null,"error":null,"startTime":1623262396,"endTime":null,"jobId":"e1a391e096fd4462a2a79d536e17afd8","azureStorageBlobContainerUri":null}' + string: '{"status":"InProgress","statusDetails":null,"error":null,"startTime":1623462777,"endTime":null,"jobId":"c8a42738bf8643c9b7ffde9ef339d0e2","azureStorageBlobContainerUri":null}' headers: - azure-asyncoperation: https://managedhsm/backup/e1a391e096fd4462a2a79d536e17afd8/pending + azure-asyncoperation: https://managedhsm/backup/c8a42738bf8643c9b7ffde9ef339d0e2/pending cache-control: no-cache content-length: '174' content-security-policy: default-src 'self' content-type: application/json; charset=utf-8 - date: Wed, 09 Jun 2021 18:13:15 GMT + date: Sat, 12 Jun 2021 01:52:57 GMT server: Kestrel strict-transport-security: max-age=31536000; includeSubDomains x-content-type-options: nosniff x-frame-options: SAMEORIGIN - x-ms-keyvault-network-info: conn_type=Ipv4;addr=24.17.201.78;act_addr_fam=Ipv4; + x-ms-keyvault-network-info: conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=Ipv4; x-ms-keyvault-region: centralus - x-ms-server-latency: '2128' + x-ms-server-latency: '2657' status: code: 202 message: '' @@ -69,61 +68,61 @@ interactions: body: null headers: User-Agent: - - azsdk-python-keyvault-administration/4.0.0b4 Python/3.6.9 (Linux-5.4.72-microsoft-standard-WSL2-x86_64-with-Ubuntu-18.04-bionic) + - azsdk-python-keyvault-administration/4.0.0b4 Python/3.5.3 (Windows-10-10.0.19041-SP0) method: GET - uri: https://managedhsm/backup/e1a391e096fd4462a2a79d536e17afd8/pending + uri: https://managedhsm/backup/c8a42738bf8643c9b7ffde9ef339d0e2/pending response: body: - string: '{"azureStorageBlobContainerUri":"https://storname.blob.core.windows.net/container37sfaze3bkss4j3/mhsm-mcpatinotesthsm-2021060918131647","endTime":1623262405,"error":null,"jobId":"e1a391e096fd4462a2a79d536e17afd8","startTime":1623262396,"status":"Succeeded","statusDetails":null}' + string: '{"azureStorageBlobContainerUri":"https://storname.blob.core.windows.net/containerk7lnzcoq4emt6gf/mhsm-mcpatinotesthsm-2021061201525788","endTime":1623462786,"error":null,"jobId":"c8a42738bf8643c9b7ffde9ef339d0e2","startTime":1623462777,"status":"Succeeded","statusDetails":null}' headers: cache-control: no-cache content-length: '294' content-security-policy: default-src 'self' content-type: application/json; charset=utf-8 - date: Wed, 09 Jun 2021 18:13:28 GMT + date: Sat, 12 Jun 2021 01:53:09 GMT server: Kestrel strict-transport-security: max-age=31536000; includeSubDomains x-content-type-options: nosniff x-frame-options: SAMEORIGIN x-ms-build-version: 1.0.20210520-1-d6634624-develop - x-ms-keyvault-network-info: conn_type=Ipv4;addr=24.17.201.78;act_addr_fam=Ipv4; + x-ms-keyvault-network-info: conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=Ipv4; x-ms-keyvault-region: centralus - x-ms-server-latency: '1971' + x-ms-server-latency: '2005' status: code: 200 message: OK - url: https://mcpatinotesthsm.managedhsm.azure.net/backup/e1a391e096fd4462a2a79d536e17afd8/pending + url: https://mcpatinotesthsm.managedhsm.azure.net/backup/c8a42738bf8643c9b7ffde9ef339d0e2/pending - request: - body: '{"sasTokenParameters": {"storageResourceUri": "https://storname.blob.core.windows.net/container37sfaze3bkss4j3", - "token": "redacted"}, "folderToRestore": "mhsm-mcpatinotesthsm-2021060918131647"}' + body: '{"sasTokenParameters": {"token": "redacted", "storageResourceUri": "https://storname.blob.core.windows.net/containerk7lnzcoq4emt6gf"}, + "folderToRestore": "mhsm-mcpatinotesthsm-2021061201525788"}' headers: Accept: - application/json Content-Length: - - '317' + - '319' Content-Type: - application/json User-Agent: - - azsdk-python-keyvault-administration/4.0.0b4 Python/3.6.9 (Linux-5.4.72-microsoft-standard-WSL2-x86_64-with-Ubuntu-18.04-bionic) + - azsdk-python-keyvault-administration/4.0.0b4 Python/3.5.3 (Windows-10-10.0.19041-SP0) method: PUT uri: https://managedhsm/restore?api-version=7.2 response: body: - string: '{"endTime":null,"error":null,"jobId":"4fcf1426713543abb14fd9360dd40754","startTime":1623262410,"status":"InProgress","statusDetails":null}' + string: '{"endTime":null,"error":null,"jobId":"3098a29f4f5147bf86931ca9b6b75933","startTime":1623462792,"status":"InProgress","statusDetails":null}' headers: - azure-asyncoperation: https://managedhsm/restore/4fcf1426713543abb14fd9360dd40754/pending + azure-asyncoperation: https://managedhsm/restore/3098a29f4f5147bf86931ca9b6b75933/pending cache-control: no-cache content-length: '138' content-security-policy: default-src 'self' content-type: application/json; charset=utf-8 - date: Wed, 09 Jun 2021 18:13:30 GMT + date: Sat, 12 Jun 2021 01:53:12 GMT server: Kestrel strict-transport-security: max-age=31536000; includeSubDomains x-content-type-options: nosniff x-frame-options: SAMEORIGIN - x-ms-keyvault-network-info: conn_type=Ipv4;addr=24.17.201.78;act_addr_fam=Ipv4; + x-ms-keyvault-network-info: conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=Ipv4; x-ms-keyvault-region: centralus - x-ms-server-latency: '2144' + x-ms-server-latency: '2925' status: code: 202 message: '' @@ -132,56 +131,140 @@ interactions: body: null headers: User-Agent: - - azsdk-python-keyvault-administration/4.0.0b4 Python/3.6.9 (Linux-5.4.72-microsoft-standard-WSL2-x86_64-with-Ubuntu-18.04-bionic) + - azsdk-python-keyvault-administration/4.0.0b4 Python/3.5.3 (Windows-10-10.0.19041-SP0) method: GET - uri: https://managedhsm/restore/4fcf1426713543abb14fd9360dd40754/pending + uri: https://managedhsm/restore/3098a29f4f5147bf86931ca9b6b75933/pending response: body: - string: '{"endTime":null,"error":null,"jobId":"4fcf1426713543abb14fd9360dd40754","startTime":1623262410,"status":"InProgress","statusDetails":null}' + string: '{"endTime":null,"error":null,"jobId":"3098a29f4f5147bf86931ca9b6b75933","startTime":1623462792,"status":"InProgress","statusDetails":null}' headers: cache-control: no-cache content-length: '138' content-security-policy: default-src 'self' content-type: application/json; charset=utf-8 - date: Wed, 09 Jun 2021 18:13:42 GMT + date: Sat, 12 Jun 2021 01:53:14 GMT server: Kestrel strict-transport-security: max-age=31536000; includeSubDomains x-content-type-options: nosniff x-frame-options: SAMEORIGIN x-ms-build-version: 1.0.20210520-1-d6634624-develop - x-ms-keyvault-network-info: conn_type=Ipv4;addr=24.17.201.78;act_addr_fam=Ipv4; + x-ms-keyvault-network-info: conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=Ipv4; x-ms-keyvault-region: centralus - x-ms-server-latency: '2282' + x-ms-server-latency: '2214' status: code: 200 message: OK - url: https://mcpatinotesthsm.managedhsm.azure.net/restore/4fcf1426713543abb14fd9360dd40754/pending + url: https://mcpatinotesthsm.managedhsm.azure.net/restore/3098a29f4f5147bf86931ca9b6b75933/pending - request: body: null headers: User-Agent: - - azsdk-python-keyvault-administration/4.0.0b4 Python/3.6.9 (Linux-5.4.72-microsoft-standard-WSL2-x86_64-with-Ubuntu-18.04-bionic) + - azsdk-python-keyvault-administration/4.0.0b4 Python/3.5.3 (Windows-10-10.0.19041-SP0) method: GET - uri: https://managedhsm/restore/4fcf1426713543abb14fd9360dd40754/pending + uri: https://managedhsm/restore/3098a29f4f5147bf86931ca9b6b75933/pending response: body: - string: '{"endTime":1623262427,"error":null,"jobId":"4fcf1426713543abb14fd9360dd40754","startTime":1623262410,"status":"Succeeded","statusDetails":null}' + string: '{"endTime":null,"error":null,"jobId":"3098a29f4f5147bf86931ca9b6b75933","startTime":1623462792,"status":"InProgress","statusDetails":null}' + headers: + cache-control: no-cache + content-length: '138' + content-security-policy: default-src 'self' + content-type: application/json; charset=utf-8 + date: Sat, 12 Jun 2021 01:53:17 GMT + server: Kestrel + strict-transport-security: max-age=31536000; includeSubDomains + x-content-type-options: nosniff + x-frame-options: SAMEORIGIN + x-ms-build-version: 1.0.20210520-1-d6634624-develop + x-ms-keyvault-network-info: conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=Ipv4; + x-ms-keyvault-region: centralus + x-ms-server-latency: '2164' + status: + code: 200 + message: OK + url: https://mcpatinotesthsm.managedhsm.azure.net/restore/3098a29f4f5147bf86931ca9b6b75933/pending +- request: + body: null + headers: + User-Agent: + - azsdk-python-keyvault-administration/4.0.0b4 Python/3.5.3 (Windows-10-10.0.19041-SP0) + method: GET + uri: https://managedhsm/restore/3098a29f4f5147bf86931ca9b6b75933/pending + response: + body: + string: '{"endTime":null,"error":null,"jobId":"3098a29f4f5147bf86931ca9b6b75933","startTime":1623462792,"status":"InProgress","statusDetails":null}' + headers: + cache-control: no-cache + content-length: '138' + content-security-policy: default-src 'self' + content-type: application/json; charset=utf-8 + date: Sat, 12 Jun 2021 01:53:24 GMT + server: Kestrel + strict-transport-security: max-age=31536000; includeSubDomains + x-content-type-options: nosniff + x-frame-options: SAMEORIGIN + x-ms-build-version: 1.0.20210520-1-d6634624-develop + x-ms-keyvault-network-info: conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=Ipv4; + x-ms-keyvault-region: centralus + x-ms-server-latency: '2095' + status: + code: 200 + message: OK + url: https://mcpatinotesthsm.managedhsm.azure.net/restore/3098a29f4f5147bf86931ca9b6b75933/pending +- request: + body: null + headers: + User-Agent: + - azsdk-python-keyvault-administration/4.0.0b4 Python/3.5.3 (Windows-10-10.0.19041-SP0) + method: GET + uri: https://managedhsm/restore/3098a29f4f5147bf86931ca9b6b75933/pending + response: + body: + string: '{"endTime":1623462809,"error":null,"jobId":"3098a29f4f5147bf86931ca9b6b75933","startTime":1623462792,"status":"Succeeded","statusDetails":null}' + headers: + cache-control: no-cache + content-length: '143' + content-security-policy: default-src 'self' + content-type: application/json; charset=utf-8 + date: Sat, 12 Jun 2021 01:53:31 GMT + server: Kestrel + strict-transport-security: max-age=31536000; includeSubDomains + x-content-type-options: nosniff + x-frame-options: SAMEORIGIN + x-ms-build-version: 1.0.20210520-1-d6634624-develop + x-ms-keyvault-network-info: conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=Ipv4; + x-ms-keyvault-region: centralus + x-ms-server-latency: '3126' + status: + code: 200 + message: OK + url: https://mcpatinotesthsm.managedhsm.azure.net/restore/3098a29f4f5147bf86931ca9b6b75933/pending +- request: + body: null + headers: + User-Agent: + - azsdk-python-keyvault-administration/4.0.0b4 Python/3.5.3 (Windows-10-10.0.19041-SP0) + method: GET + uri: https://managedhsm/restore/3098a29f4f5147bf86931ca9b6b75933/pending + response: + body: + string: '{"endTime":1623462809,"error":null,"jobId":"3098a29f4f5147bf86931ca9b6b75933","startTime":1623462792,"status":"Succeeded","statusDetails":null}' headers: cache-control: no-cache content-length: '143' content-security-policy: default-src 'self' content-type: application/json; charset=utf-8 - date: Wed, 09 Jun 2021 18:13:49 GMT + date: Sat, 12 Jun 2021 01:53:39 GMT server: Kestrel strict-transport-security: max-age=31536000; includeSubDomains x-content-type-options: nosniff x-frame-options: SAMEORIGIN x-ms-build-version: 1.0.20210520-1-d6634624-develop - x-ms-keyvault-network-info: conn_type=Ipv4;addr=24.17.201.78;act_addr_fam=Ipv4; + x-ms-keyvault-network-info: conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=Ipv4; x-ms-keyvault-region: centralus - x-ms-server-latency: '2154' + x-ms-server-latency: '2115' status: code: 200 message: OK - url: https://mcpatinotesthsm.managedhsm.azure.net/restore/4fcf1426713543abb14fd9360dd40754/pending + url: https://mcpatinotesthsm.managedhsm.azure.net/restore/3098a29f4f5147bf86931ca9b6b75933/pending version: 1 From 2512b428115be6b2f046e3d50df2e8143df1dbc1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?McCoy=20Pati=C3=B1o?= Date: Mon, 14 Jun 2021 09:50:22 -0700 Subject: [PATCH 15/27] fix super() calls --- .../azure/keyvault/administration/_internal/async_polling.py | 4 +--- .../azure/keyvault/administration/_internal/polling.py | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_internal/async_polling.py b/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_internal/async_polling.py index e55bdb1dc1e9..32f7856e47a8 100644 --- a/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_internal/async_polling.py +++ b/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_internal/async_polling.py @@ -26,9 +26,7 @@ def initialize(self, client, initial_response, deserialization_callback): self._status = "InProgress" # assume the operation is ongoing for now, so the actual status gets polled else: - super(KeyVaultAsyncBackupClientPollingMethod, self).initialize( - client, initial_response, deserialization_callback - ) + super().initialize(client, initial_response, deserialization_callback) def get_continuation_token(self): # type() -> str diff --git a/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_internal/polling.py b/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_internal/polling.py index 7b5180b0027c..a80f1653671c 100644 --- a/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_internal/polling.py +++ b/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_internal/polling.py @@ -115,7 +115,7 @@ def _poll(self): def resource(self): try: - return super().resource() + return super(KeyVaultBackupClientPollingMethod, self).resource() except AttributeError: return None From a13138539ef9db79f3b65ef909d0daacbd1dfde8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?McCoy=20Pati=C3=B1o?= Date: Mon, 14 Jun 2021 16:50:11 -0700 Subject: [PATCH 16/27] get pipeline response w/ cont token --- .../keyvault/administration/_backup_client.py | 25 +- .../administration/_internal/async_polling.py | 119 ----- .../administration/_internal/helpers.py | 104 ---- .../administration/_internal/polling.py | 132 +---- .../administration/aio/_backup_client.py | 25 +- ...t_full_backup_and_restore_rehydration.yaml | 204 ++++++-- ...ient_async.test_backup_client_polling.yaml | 467 ++++++++++++++++++ ...t_full_backup_and_restore_rehydration.yaml | 299 +++++++++++ .../tests/test_backup_client.py | 34 +- .../tests/test_backup_client_async.py | 95 ++-- 10 files changed, 1063 insertions(+), 441 deletions(-) delete mode 100644 sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_internal/helpers.py create mode 100644 sdk/keyvault/azure-keyvault-administration/tests/recordings/test_backup_client_async.test_backup_client_polling.yaml create mode 100644 sdk/keyvault/azure-keyvault-administration/tests/recordings/test_backup_client_async.test_full_backup_and_restore_rehydration.yaml diff --git a/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_backup_client.py b/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_backup_client.py index 5efb646f889c..4121d02e8a27 100644 --- a/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_backup_client.py +++ b/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_backup_client.py @@ -2,7 +2,9 @@ # Copyright (c) Microsoft Corporation. # Licensed under the MIT License. # ------------------------------------ +import base64 import functools +import pickle from typing import TYPE_CHECKING from ._models import KeyVaultBackupOperation @@ -15,6 +17,15 @@ from azure.core.polling import LROPoller +def _get_status_from_continuation_token(token, pipeline_client): + continuation_url = base64.b64decode(token.encode()).decode("ascii") + status_response = pipeline_client._pipeline.run(pipeline_client.get(continuation_url), stream=False) + # inject the status URL if it's not in the response + if "azure-asyncoperation" not in status_response.http_response.headers: + status_response.http_response.headers["azure-asyncoperation"] = continuation_url + return base64.b64encode(pickle.dumps(status_response)).decode('ascii') + + class KeyVaultBackupClient(KeyVaultClientBase): """Performs Key Vault backup and restore operations. @@ -38,11 +49,17 @@ def begin_backup(self, blob_storage_url, sas_token, **kwargs): """ polling_interval = kwargs.pop("_polling_interval", 5) sas_parameter = self._models.SASTokenParameter(storage_resource_uri=blob_storage_url, token=sas_token) + + continuation_token = kwargs.pop("continuation_token", None) + status_response = None + if continuation_token: + status_response = _get_status_from_continuation_token(continuation_token, self._client._client) + return self._client.begin_full_backup( vault_base_url=self._vault_url, azure_storage_blob_container_uri=sas_parameter, cls=KeyVaultBackupOperation._from_generated, - continuation_token=kwargs.pop("continuation_token", None), + continuation_token=status_response, polling=KeyVaultBackupClientPollingMethod( lro_algorithms=[KeyVaultBackupClientPolling()], timeout=polling_interval, **kwargs ), @@ -67,6 +84,10 @@ def begin_restore(self, folder_url, sas_token, **kwargs): continuation_token = kwargs.pop("continuation_token", None) key_name = kwargs.pop("key_name", None) + status_response = None + if continuation_token: + status_response = _get_status_from_continuation_token(continuation_token, self._client._client) + container_url, folder_name = parse_folder_url(folder_url) sas_parameter = self._models.SASTokenParameter(storage_resource_uri=container_url, token=sas_token) polling = KeyVaultBackupClientPollingMethod( @@ -88,7 +109,7 @@ def begin_restore(self, folder_url, sas_token, **kwargs): vault_base_url=self._vault_url, restore_blob_details=restore_details, cls=lambda *_: None, # poller.result() returns None - continuation_token=continuation_token, + continuation_token=status_response, polling=polling, **kwargs ) diff --git a/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_internal/async_polling.py b/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_internal/async_polling.py index 32f7856e47a8..3ef90e117977 100644 --- a/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_internal/async_polling.py +++ b/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_internal/async_polling.py @@ -4,129 +4,10 @@ # ------------------------------------ import base64 -from azure.core.exceptions import HttpResponseError from azure.core.polling.async_base_polling import AsyncLROBasePolling -from azure.core.polling.base_polling import BadResponse, BadStatus, OperationFailed - -from .helpers import _failed, _get_retry_after, _raise_if_bad_http_status_and_method class KeyVaultAsyncBackupClientPollingMethod(AsyncLROBasePolling): - def initialize(self, client, initial_response, deserialization_callback): - """Set the initial status of this LRO. - - :param initial_response: The initial pipeline response of the poller, or a URL continuation token - :raises: HttpResponseError if initial status is incorrect LRO state - """ - if isinstance(initial_response, str): - self._client = client - self._deserialization_callback = deserialization_callback - self._operation = self._lro_algorithms[0] - self._operation._async_url = initial_response # pylint: disable=protected-access - self._status = "InProgress" # assume the operation is ongoing for now, so the actual status gets polled - - else: - super().initialize(client, initial_response, deserialization_callback) - def get_continuation_token(self): # type() -> str return base64.b64encode(self._operation.get_polling_url().encode()).decode("ascii") - - @classmethod - def from_continuation_token(cls, continuation_token, **kwargs): - """continuation_token is expected to be the URL of a pending operation""" - # type(str, Any) -> Tuple - try: - client = kwargs["client"] - except KeyError: - raise ValueError("Need kwarg 'client' to be recreated from continuation_token") - - try: - deserialization_callback = kwargs["deserialization_callback"] - except KeyError: - raise ValueError("Need kwarg 'deserialization_callback' to be recreated from continuation_token") - - continuation_url = base64.b64decode(continuation_token.encode()).decode("ascii") - return client, continuation_url, deserialization_callback - - async def run(self): # pylint:disable=invalid-overridden-method - try: - await self._poll() - - except BadStatus as err: - self._status = "Failed" - raise HttpResponseError(response=self._pipeline_response.http_response, error=err) - - except BadResponse as err: - self._status = "Failed" - raise HttpResponseError(response=self._pipeline_response.http_response, message=str(err), error=err) - - except OperationFailed as err: - raise HttpResponseError(response=self._pipeline_response.http_response, error=err) - - async def _poll(self): # pylint:disable=invalid-overridden-method - """Poll status of operation so long as operation is incomplete and - we have an endpoint to query. - - :param callable update_cmd: The function to call to retrieve the - latest status of the long running operation. - :raises: OperationFailed if operation status 'Failed' or 'Canceled'. - :raises: BadStatus if response status invalid. - :raises: BadResponse if response invalid. - """ - - while not self.finished(): - await self._delay() - await self.update_status() - - if _failed(self.status()): - raise OperationFailed("Operation failed or canceled") - - final_get_url = self._operation.get_final_get_url(self._pipeline_response) - if final_get_url: - self._pipeline_response = await self.request_status(final_get_url) - _raise_if_bad_http_status_and_method(self._pipeline_response.http_response) - - def resource(self): - try: - return super().resource() - except AttributeError: - return None - - async def _delay(self): # pylint:disable=invalid-overridden-method - """Check for a 'retry-after' header to set timeout, - otherwise use configured timeout. - """ - delay = self._extract_delay() - await self._sleep(delay) - - def _extract_delay(self): - if self._pipeline_response is None: - return 0 - delay = _get_retry_after(self._pipeline_response) - if delay: - return delay - return self._timeout - - def _get_request_id(self): - return self._pipeline_response.http_response.request.headers["x-ms-client-request-id"] - - async def request_status(self, status_link): - """Do a simple GET to this status link. - - This method re-inject 'x-ms-client-request-id'. - - :rtype: azure.core.pipeline.PipelineResponse - """ - try: - if self._path_format_arguments: - status_link = self._client.format_url(status_link, **self._path_format_arguments) - # Re-inject 'x-ms-client-request-id' while polling - if "request_id" not in self._operation_config: - self._operation_config["request_id"] = self._get_request_id() - except AttributeError: - pass - request = self._client.get(status_link) - return await self._client._pipeline.run( # pylint: disable=protected-access - request, stream=False, **self._operation_config - ) diff --git a/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_internal/helpers.py b/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_internal/helpers.py deleted file mode 100644 index dcec54f9bb6f..000000000000 --- a/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_internal/helpers.py +++ /dev/null @@ -1,104 +0,0 @@ -# ------------------------------------ -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT License. -# ------------------------------------ -import datetime -import email.utils -from typing import TYPE_CHECKING -from requests.structures import CaseInsensitiveDict - -from azure.core.polling.base_polling import BadStatus - -if TYPE_CHECKING: - from typing import Union - from azure.core.pipeline.transport import HttpResponse, AsyncHttpResponse - - ResponseType = Union[HttpResponse, AsyncHttpResponse] - - -_FAILED = frozenset(["canceled", "failed"]) - - -def _failed(status): - if hasattr(status, "value"): - status = status.value - return str(status).lower() in _FAILED - - -def _get_retry_after(response): - """Get the value of Retry-After in seconds. - - :param response: The PipelineResponse object - :type response: ~azure.core.pipeline.PipelineResponse - :return: Value of Retry-After in seconds. - :rtype: float or None - """ - headers = CaseInsensitiveDict(response.http_response.headers) - retry_after = headers.get("retry-after") - if retry_after: - return _parse_retry_after(retry_after) - for ms_header in ["retry-after-ms", "x-ms-retry-after-ms"]: - retry_after = headers.get(ms_header) - if retry_after: - parsed_retry_after = _parse_retry_after(retry_after) - return parsed_retry_after / 1000.0 - return None - - -def _parse_http_date(text): - """Parse a HTTP date format into datetime.""" - parsed_date = email.utils.parsedate_tz(text) - return datetime.datetime(*parsed_date[:6], tzinfo=_FixedOffset(parsed_date[9] / 60)) - - -def _parse_retry_after(retry_after): - """Helper to parse Retry-After and get value in seconds. - - :param str retry_after: Retry-After header - :rtype: int - """ - try: - delay = int(retry_after) - except ValueError: - # Not an integer? Try HTTP date - retry_date = _parse_http_date(retry_after) - delay = (retry_date - datetime.datetime.now(retry_date.tzinfo)).total_seconds() - return max(0, delay) - - -def _raise_if_bad_http_status_and_method(response): - # type: (ResponseType) -> None - """Check response status code is valid. - - Must be 200, 201, 202, or 204. - - :raises: BadStatus if invalid status. - """ - code = response.status_code - if code in {200, 201, 202, 204}: - return - raise BadStatus("Invalid return status {!r} for {!r} operation".format(code, response.request.method)) - - -class _FixedOffset(datetime.tzinfo): - """Fixed offset in minutes east from UTC. - - Copy/pasted from Python doc - - :param int offset: offset in minutes - """ - - def __init__(self, offset): - self.__offset = datetime.timedelta(minutes=offset) - - def utcoffset(self, dt): - return self.__offset - - def tzname(self, dt): - return str(self.__offset.total_seconds() / 3600) - - def __repr__(self): - return "".format(self.tzname(None)) - - def dst(self, dt): - return datetime.timedelta(0) diff --git a/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_internal/polling.py b/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_internal/polling.py index a80f1653671c..2b72ebbe889e 100644 --- a/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_internal/polling.py +++ b/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_internal/polling.py @@ -5,22 +5,11 @@ import base64 from typing import TYPE_CHECKING -import six - -from azure.core.exceptions import HttpResponseError -from azure.core.polling.base_polling import ( - BadResponse, - BadStatus, - LROBasePolling, - OperationFailed, - OperationResourcePolling, -) - -from .helpers import _failed, _get_retry_after, _raise_if_bad_http_status_and_method +from azure.core.polling.base_polling import LROBasePolling, OperationResourcePolling if TYPE_CHECKING: from typing import Union - from azure.core.pipeline import PipelineResponse # pylint: disable=ungrouped-imports + from azure.core.pipeline import PipelineResponse from azure.core.pipeline.transport import HttpResponse, AsyncHttpResponse, HttpRequest ResponseType = Union[HttpResponse, AsyncHttpResponse] @@ -36,123 +25,6 @@ def get_final_get_url(self, pipeline_response): class KeyVaultBackupClientPollingMethod(LROBasePolling): - def initialize(self, client, initial_response, deserialization_callback): - """Set the initial status of this LRO. - - :param initial_response: The initial pipeline response of the poller, or a URL continuation token - :raises: HttpResponseError if initial status is incorrect LRO state - """ - if isinstance(initial_response, six.string_types): - self._client = client - self._deserialization_callback = deserialization_callback - self._operation = self._lro_algorithms[0] - self._operation._async_url = initial_response # pylint: disable=protected-access - self._status = "InProgress" # assume the operation is ongoing for now so the actual status gets polled - - else: - super(KeyVaultBackupClientPollingMethod, self).initialize( - client, initial_response, deserialization_callback - ) - def get_continuation_token(self): # type() -> str return base64.b64encode(self._operation.get_polling_url().encode()).decode("ascii") - - @classmethod - def from_continuation_token(cls, continuation_token, **kwargs): - """continuation_token is expected to be the URL of a pending operation""" - # type(str, Any) -> Tuple - try: - client = kwargs["client"] - except KeyError: - raise ValueError("Need kwarg 'client' to be recreated from continuation_token") - - try: - deserialization_callback = kwargs["deserialization_callback"] - except KeyError: - raise ValueError("Need kwarg 'deserialization_callback' to be recreated from continuation_token") - - continuation_url = base64.b64decode(continuation_token.encode()).decode("ascii") - return client, continuation_url, deserialization_callback - - def run(self): - try: - self._poll() - - except BadStatus as err: - self._status = "Failed" - raise HttpResponseError(response=self._pipeline_response.http_response, error=err) - - except BadResponse as err: - self._status = "Failed" - raise HttpResponseError(response=self._pipeline_response.http_response, message=str(err), error=err) - - except OperationFailed as err: - raise HttpResponseError(response=self._pipeline_response.http_response, error=err) - - def _poll(self): - """Poll status of operation so long as operation is incomplete and - we have an endpoint to query. - - :param callable update_cmd: The function to call to retrieve the - latest status of the long running operation. - :raises: OperationFailed if operation status 'Failed' or 'Canceled'. - :raises: BadStatus if response status invalid. - :raises: BadResponse if response invalid. - """ - - while not self.finished(): - self._delay() - self.update_status() - - if _failed(self.status()): - raise OperationFailed("Operation failed or canceled") - - final_get_url = self._operation.get_final_get_url(self._pipeline_response) - if final_get_url: - self._pipeline_response = self.request_status(final_get_url) - _raise_if_bad_http_status_and_method(self._pipeline_response.http_response) - - def resource(self): - try: - return super(KeyVaultBackupClientPollingMethod, self).resource() - except AttributeError: - return None - - def _delay(self): - """Check for a 'retry-after' header to set timeout, - otherwise use configured timeout. - """ - delay = self._extract_delay() - self._sleep(delay) - - def _extract_delay(self): - if self._pipeline_response is None: - return 0 - delay = _get_retry_after(self._pipeline_response) - if delay: - return delay - return self._timeout - - def _get_request_id(self): - return self._pipeline_response.http_response.request.headers["x-ms-client-request-id"] - - def request_status(self, status_link): - """Do a simple GET to this status link. - - This method re-inject 'x-ms-client-request-id'. - - :rtype: azure.core.pipeline.PipelineResponse - """ - try: - if self._path_format_arguments: - status_link = self._client.format_url(status_link, **self._path_format_arguments) - # Re-inject 'x-ms-client-request-id' while polling - if "request_id" not in self._operation_config: - self._operation_config["request_id"] = self._get_request_id() - except AttributeError: - pass - request = self._client.get(status_link) - return self._client._pipeline.run( # pylint: disable=protected-access - request, stream=False, **self._operation_config - ) diff --git a/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/aio/_backup_client.py b/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/aio/_backup_client.py index f9b51c1a4046..10ca7f93cee6 100644 --- a/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/aio/_backup_client.py +++ b/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/aio/_backup_client.py @@ -2,7 +2,9 @@ # Copyright (c) Microsoft Corporation. # Licensed under the MIT License. # ------------------------------------ +import base64 import functools +import pickle from typing import TYPE_CHECKING from .._internal import AsyncKeyVaultClientBase, parse_folder_url @@ -16,6 +18,15 @@ from azure.core.polling import AsyncLROPoller +async def _get_status_from_continuation_token(token, pipeline_client): + continuation_url = base64.b64decode(token.encode()).decode("ascii") + status_response = await pipeline_client._pipeline.run(pipeline_client.get(continuation_url), stream=False) + # inject the status URL if it's not in the response + if "azure-asyncoperation" not in status_response.http_response.headers: + status_response.http_response.headers["azure-asyncoperation"] = continuation_url + return base64.b64encode(pickle.dumps(status_response)).decode('ascii') + + class KeyVaultBackupClient(AsyncKeyVaultClientBase): """Performs Key Vault backup and restore operations. @@ -39,11 +50,17 @@ async def begin_backup( """ polling_interval = kwargs.pop("_polling_interval", 5) sas_parameter = self._models.SASTokenParameter(storage_resource_uri=blob_storage_url, token=sas_token) + + continuation_token = kwargs.pop("continuation_token", None) + status_response = None + if continuation_token: + status_response = await _get_status_from_continuation_token(continuation_token, self._client._client) + return await self._client.begin_full_backup( vault_base_url=self._vault_url, azure_storage_blob_container_uri=sas_parameter, cls=KeyVaultBackupOperation._from_generated, - continuation_token=kwargs.pop("continuation_token", None), + continuation_token=status_response, polling=KeyVaultAsyncBackupClientPollingMethod( lro_algorithms=[KeyVaultBackupClientPolling()], timeout=polling_interval, **kwargs ), @@ -68,6 +85,10 @@ async def begin_restore(self, folder_url: str, sas_token: str, **kwargs: "Any") continuation_token = kwargs.pop("continuation_token", None) key_name = kwargs.pop("key_name", None) + status_response = None + if continuation_token: + status_response = await _get_status_from_continuation_token(continuation_token, self._client._client) + container_url, folder_name = parse_folder_url(folder_url) sas_parameter = self._models.SASTokenParameter(storage_resource_uri=container_url, token=sas_token) polling = KeyVaultAsyncBackupClientPollingMethod( @@ -89,7 +110,7 @@ async def begin_restore(self, folder_url: str, sas_token: str, **kwargs: "Any") vault_base_url=self._vault_url, restore_blob_details=restore_details, cls=lambda *_: None, # poller.result() returns None - continuation_token=continuation_token, + continuation_token=status_response, polling=polling, **kwargs ) diff --git a/sdk/keyvault/azure-keyvault-administration/tests/recordings/test_backup_client.test_full_backup_and_restore_rehydration.yaml b/sdk/keyvault/azure-keyvault-administration/tests/recordings/test_backup_client.test_full_backup_and_restore_rehydration.yaml index 83cedcade9a8..9260dfe64d92 100644 --- a/sdk/keyvault/azure-keyvault-administration/tests/recordings/test_backup_client.test_full_backup_and_restore_rehydration.yaml +++ b/sdk/keyvault/azure-keyvault-administration/tests/recordings/test_backup_client.test_full_backup_and_restore_rehydration.yaml @@ -43,7 +43,7 @@ interactions: code: 401 message: Unauthorized - request: - body: '{"storageResourceUri": "https://storname.blob.core.windows.net/containerbe7dxjxuj4ise25", + body: '{"storageResourceUri": "https://storname.blob.core.windows.net/container5tluqadlguplx4n", "token": "redacted"}' headers: Accept: @@ -53,7 +53,7 @@ interactions: Connection: - keep-alive Content-Length: - - '233' + - '235' Content-Type: - application/json User-Agent: @@ -62,10 +62,10 @@ interactions: uri: https://managedhsm/backup?api-version=7.2 response: body: - string: '{"status":"InProgress","statusDetails":null,"error":null,"startTime":1623444371,"endTime":null,"jobId":"2d408948e1ec41208a7b982598998c94","azureStorageBlobContainerUri":null}' + string: '{"status":"InProgress","statusDetails":null,"error":null,"startTime":1623714247,"endTime":null,"jobId":"712eb19999cb4e6d8a7f1477d07c0b1e","azureStorageBlobContainerUri":null}' headers: azure-asyncoperation: - - https://managedhsm/backup/2d408948e1ec41208a7b982598998c94/pending + - https://managedhsm/backup/712eb19999cb4e6d8a7f1477d07c0b1e/pending cache-control: - no-cache content-length: @@ -75,7 +75,7 @@ interactions: content-type: - application/json; charset=utf-8 date: - - Fri, 11 Jun 2021 20:46:10 GMT + - Mon, 14 Jun 2021 23:44:07 GMT server: - Kestrel strict-transport-security: @@ -89,7 +89,7 @@ interactions: x-ms-keyvault-region: - centralus x-ms-server-latency: - - '2213' + - '2448' status: code: 202 message: '' @@ -105,10 +105,56 @@ interactions: User-Agent: - azsdk-python-keyvault-administration/4.0.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) method: GET - uri: https://managedhsm/backup/2d408948e1ec41208a7b982598998c94/pending + uri: https://managedhsm/backup/712eb19999cb4e6d8a7f1477d07c0b1e/pending response: body: - string: '{"azureStorageBlobContainerUri":"https://storname.blob.core.windows.net/containerbe7dxjxuj4ise25/mhsm-mcpatinotesthsm-2021061120461128","endTime":1623444380,"error":null,"jobId":"2d408948e1ec41208a7b982598998c94","startTime":1623444371,"status":"Succeeded","statusDetails":null}' + string: '{"azureStorageBlobContainerUri":null,"endTime":null,"error":null,"jobId":"712eb19999cb4e6d8a7f1477d07c0b1e","startTime":1623714247,"status":"InProgress","statusDetails":null}' + headers: + cache-control: + - no-cache + content-length: + - '174' + content-security-policy: + - default-src 'self' + content-type: + - application/json; charset=utf-8 + date: + - Mon, 14 Jun 2021 23:44:09 GMT + server: + - Kestrel + strict-transport-security: + - max-age=31536000; includeSubDomains + x-content-type-options: + - nosniff + x-frame-options: + - SAMEORIGIN + x-ms-build-version: + - 1.0.20210520-1-d6634624-develop + x-ms-keyvault-network-info: + - conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=Ipv4; + x-ms-keyvault-region: + - centralus + x-ms-server-latency: + - '2082' + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-keyvault-administration/4.0.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) + method: GET + uri: https://managedhsm/backup/712eb19999cb4e6d8a7f1477d07c0b1e/pending + response: + body: + string: '{"azureStorageBlobContainerUri":"https://storname.blob.core.windows.net/container5tluqadlguplx4n/mhsm-mcpatinotesthsm-2021061423440753","endTime":1623714256,"error":null,"jobId":"712eb19999cb4e6d8a7f1477d07c0b1e","startTime":1623714247,"status":"Succeeded","statusDetails":null}' headers: cache-control: - no-cache @@ -119,7 +165,7 @@ interactions: content-type: - application/json; charset=utf-8 date: - - Fri, 11 Jun 2021 20:46:23 GMT + - Mon, 14 Jun 2021 23:44:17 GMT server: - Kestrel strict-transport-security: @@ -135,7 +181,7 @@ interactions: x-ms-keyvault-region: - centralus x-ms-server-latency: - - '2088' + - '2160' status: code: 200 message: OK @@ -151,10 +197,10 @@ interactions: User-Agent: - azsdk-python-keyvault-administration/4.0.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) method: GET - uri: https://managedhsm/backup/2d408948e1ec41208a7b982598998c94/pending + uri: https://managedhsm/backup/712eb19999cb4e6d8a7f1477d07c0b1e/pending response: body: - string: '{"azureStorageBlobContainerUri":"https://storname.blob.core.windows.net/containerbe7dxjxuj4ise25/mhsm-mcpatinotesthsm-2021061120461128","endTime":1623444380,"error":null,"jobId":"2d408948e1ec41208a7b982598998c94","startTime":1623444371,"status":"Succeeded","statusDetails":null}' + string: '{"azureStorageBlobContainerUri":"https://storname.blob.core.windows.net/container5tluqadlguplx4n/mhsm-mcpatinotesthsm-2021061423440753","endTime":1623714256,"error":null,"jobId":"712eb19999cb4e6d8a7f1477d07c0b1e","startTime":1623714247,"status":"Succeeded","statusDetails":null}' headers: cache-control: - no-cache @@ -165,7 +211,7 @@ interactions: content-type: - application/json; charset=utf-8 date: - - Fri, 11 Jun 2021 20:46:25 GMT + - Mon, 14 Jun 2021 23:44:19 GMT server: - Kestrel strict-transport-security: @@ -181,13 +227,13 @@ interactions: x-ms-keyvault-region: - centralus x-ms-server-latency: - - '1979' + - '2146' status: code: 200 message: OK - request: - body: '{"sasTokenParameters": {"storageResourceUri": "https://storname.blob.core.windows.net/containerbe7dxjxuj4ise25", - "token": "redacted"}, "folderToRestore": "mhsm-mcpatinotesthsm-2021061120461128"}' + body: '{"sasTokenParameters": {"storageResourceUri": "https://storname.blob.core.windows.net/container5tluqadlguplx4n", + "token": "redacted"}, "folderToRestore": "mhsm-mcpatinotesthsm-2021061423440753"}' headers: Accept: - application/json @@ -196,7 +242,7 @@ interactions: Connection: - keep-alive Content-Length: - - '317' + - '319' Content-Type: - application/json User-Agent: @@ -205,10 +251,10 @@ interactions: uri: https://managedhsm/restore?api-version=7.2 response: body: - string: '{"endTime":null,"error":null,"jobId":"f1192f26b5ee480aa7b8dc30516424b9","startTime":1623444387,"status":"InProgress","statusDetails":null}' + string: '{"endTime":null,"error":null,"jobId":"6b24e91c4cb84c0a86133807e265bbb9","startTime":1623714262,"status":"InProgress","statusDetails":null}' headers: azure-asyncoperation: - - https://managedhsm/restore/f1192f26b5ee480aa7b8dc30516424b9/pending + - https://managedhsm/restore/6b24e91c4cb84c0a86133807e265bbb9/pending cache-control: - no-cache content-length: @@ -218,7 +264,7 @@ interactions: content-type: - application/json; charset=utf-8 date: - - Fri, 11 Jun 2021 20:46:27 GMT + - Mon, 14 Jun 2021 23:44:21 GMT server: - Kestrel strict-transport-security: @@ -232,7 +278,7 @@ interactions: x-ms-keyvault-region: - centralus x-ms-server-latency: - - '2065' + - '2290' status: code: 202 message: '' @@ -248,10 +294,102 @@ interactions: User-Agent: - azsdk-python-keyvault-administration/4.0.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) method: GET - uri: https://managedhsm/restore/f1192f26b5ee480aa7b8dc30516424b9/pending + uri: https://managedhsm/restore/6b24e91c4cb84c0a86133807e265bbb9/pending + response: + body: + string: '{"endTime":null,"error":null,"jobId":"6b24e91c4cb84c0a86133807e265bbb9","startTime":1623714262,"status":"InProgress","statusDetails":null}' + headers: + cache-control: + - no-cache + content-length: + - '138' + content-security-policy: + - default-src 'self' + content-type: + - application/json; charset=utf-8 + date: + - Mon, 14 Jun 2021 23:44:23 GMT + server: + - Kestrel + strict-transport-security: + - max-age=31536000; includeSubDomains + x-content-type-options: + - nosniff + x-frame-options: + - SAMEORIGIN + x-ms-build-version: + - 1.0.20210520-1-d6634624-develop + x-ms-keyvault-network-info: + - conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=Ipv4; + x-ms-keyvault-region: + - centralus + x-ms-server-latency: + - '2069' + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-keyvault-administration/4.0.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) + method: GET + uri: https://managedhsm/restore/6b24e91c4cb84c0a86133807e265bbb9/pending + response: + body: + string: '{"endTime":null,"error":null,"jobId":"6b24e91c4cb84c0a86133807e265bbb9","startTime":1623714262,"status":"InProgress","statusDetails":null}' + headers: + cache-control: + - no-cache + content-length: + - '138' + content-security-policy: + - default-src 'self' + content-type: + - application/json; charset=utf-8 + date: + - Mon, 14 Jun 2021 23:44:31 GMT + server: + - Kestrel + strict-transport-security: + - max-age=31536000; includeSubDomains + x-content-type-options: + - nosniff + x-frame-options: + - SAMEORIGIN + x-ms-build-version: + - 1.0.20210520-1-d6634624-develop + x-ms-keyvault-network-info: + - conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=Ipv4; + x-ms-keyvault-region: + - centralus + x-ms-server-latency: + - '2148' + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-keyvault-administration/4.0.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) + method: GET + uri: https://managedhsm/restore/6b24e91c4cb84c0a86133807e265bbb9/pending response: body: - string: '{"endTime":null,"error":null,"jobId":"f1192f26b5ee480aa7b8dc30516424b9","startTime":1623444387,"status":"InProgress","statusDetails":null}' + string: '{"endTime":null,"error":null,"jobId":"6b24e91c4cb84c0a86133807e265bbb9","startTime":1623714262,"status":"InProgress","statusDetails":null}' headers: cache-control: - no-cache @@ -262,7 +400,7 @@ interactions: content-type: - application/json; charset=utf-8 date: - - Fri, 11 Jun 2021 20:46:39 GMT + - Mon, 14 Jun 2021 23:44:34 GMT server: - Kestrel strict-transport-security: @@ -278,7 +416,7 @@ interactions: x-ms-keyvault-region: - centralus x-ms-server-latency: - - '1976' + - '2192' status: code: 200 message: OK @@ -294,10 +432,10 @@ interactions: User-Agent: - azsdk-python-keyvault-administration/4.0.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) method: GET - uri: https://managedhsm/restore/f1192f26b5ee480aa7b8dc30516424b9/pending + uri: https://managedhsm/restore/6b24e91c4cb84c0a86133807e265bbb9/pending response: body: - string: '{"endTime":1623444403,"error":null,"jobId":"f1192f26b5ee480aa7b8dc30516424b9","startTime":1623444387,"status":"Succeeded","statusDetails":null}' + string: '{"endTime":1623714278,"error":null,"jobId":"6b24e91c4cb84c0a86133807e265bbb9","startTime":1623714262,"status":"Succeeded","statusDetails":null}' headers: cache-control: - no-cache @@ -308,7 +446,7 @@ interactions: content-type: - application/json; charset=utf-8 date: - - Fri, 11 Jun 2021 20:46:47 GMT + - Mon, 14 Jun 2021 23:44:38 GMT server: - Kestrel strict-transport-security: @@ -324,7 +462,7 @@ interactions: x-ms-keyvault-region: - centralus x-ms-server-latency: - - '3084' + - '2139' status: code: 200 message: OK @@ -340,10 +478,10 @@ interactions: User-Agent: - azsdk-python-keyvault-administration/4.0.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) method: GET - uri: https://managedhsm/restore/f1192f26b5ee480aa7b8dc30516424b9/pending + uri: https://managedhsm/restore/6b24e91c4cb84c0a86133807e265bbb9/pending response: body: - string: '{"endTime":1623444403,"error":null,"jobId":"f1192f26b5ee480aa7b8dc30516424b9","startTime":1623444387,"status":"Succeeded","statusDetails":null}' + string: '{"endTime":1623714278,"error":null,"jobId":"6b24e91c4cb84c0a86133807e265bbb9","startTime":1623714262,"status":"Succeeded","statusDetails":null}' headers: cache-control: - no-cache @@ -354,7 +492,7 @@ interactions: content-type: - application/json; charset=utf-8 date: - - Fri, 11 Jun 2021 20:46:49 GMT + - Mon, 14 Jun 2021 23:44:41 GMT server: - Kestrel strict-transport-security: @@ -370,7 +508,7 @@ interactions: x-ms-keyvault-region: - centralus x-ms-server-latency: - - '2168' + - '2193' status: code: 200 message: OK diff --git a/sdk/keyvault/azure-keyvault-administration/tests/recordings/test_backup_client_async.test_backup_client_polling.yaml b/sdk/keyvault/azure-keyvault-administration/tests/recordings/test_backup_client_async.test_backup_client_polling.yaml new file mode 100644 index 000000000000..cc4083f149c0 --- /dev/null +++ b/sdk/keyvault/azure-keyvault-administration/tests/recordings/test_backup_client_async.test_backup_client_polling.yaml @@ -0,0 +1,467 @@ +interactions: +- request: + body: null + headers: + Accept: + - application/json + Content-Length: + - '0' + Content-Type: + - application/json + User-Agent: + - azsdk-python-keyvault-administration/4.0.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) + method: POST + uri: https://managedhsm/backup?api-version=7.2 + response: + body: + string: '' + headers: + cache-control: no-cache + content-length: '0' + content-security-policy: default-src 'self' + content-type: application/json; charset=utf-8 + strict-transport-security: max-age=31536000; includeSubDomains + www-authenticate: Bearer authorization="https://login.microsoftonline.com/00000000-0000-0000-0000-000000000000", + resource="https://managedhsm.azure.net" + x-content-type-options: nosniff + x-frame-options: SAMEORIGIN + x-ms-server-latency: '1' + status: + code: 401 + message: Unauthorized + url: https://mcpatinotesthsm.managedhsm.azure.net/backup?api-version=7.2 +- request: + body: '{"storageResourceUri": "https://storname.blob.core.windows.net/containerbcgbx3fq2eliq2t", + "token": "redacted"}' + headers: + Accept: + - application/json + Content-Length: + - '233' + Content-Type: + - application/json + User-Agent: + - azsdk-python-keyvault-administration/4.0.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) + method: POST + uri: https://managedhsm/backup?api-version=7.2 + response: + body: + string: '{"status":"InProgress","statusDetails":null,"error":null,"startTime":1623713535,"endTime":null,"jobId":"d9bfeb26511643629937e7626eda6230","azureStorageBlobContainerUri":null}' + headers: + azure-asyncoperation: https://managedhsm/backup/d9bfeb26511643629937e7626eda6230/pending + cache-control: no-cache + content-length: '174' + content-security-policy: default-src 'self' + content-type: application/json; charset=utf-8 + date: Mon, 14 Jun 2021 23:32:14 GMT + server: Kestrel + strict-transport-security: max-age=31536000; includeSubDomains + x-content-type-options: nosniff + x-frame-options: SAMEORIGIN + x-ms-keyvault-network-info: conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=Ipv4; + x-ms-keyvault-region: centralus + x-ms-server-latency: '2329' + status: + code: 202 + message: '' + url: https://mcpatinotesthsm.managedhsm.azure.net/backup?api-version=7.2 +- request: + body: null + headers: + User-Agent: + - azsdk-python-keyvault-administration/4.0.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) + method: GET + uri: https://managedhsm/backup/d9bfeb26511643629937e7626eda6230/pending + response: + body: + string: '{"azureStorageBlobContainerUri":null,"endTime":null,"error":null,"jobId":"d9bfeb26511643629937e7626eda6230","startTime":1623713535,"status":"InProgress","statusDetails":null}' + headers: + cache-control: no-cache + content-length: '174' + content-security-policy: default-src 'self' + content-type: application/json; charset=utf-8 + date: Mon, 14 Jun 2021 23:32:16 GMT + server: Kestrel + strict-transport-security: max-age=31536000; includeSubDomains + x-content-type-options: nosniff + x-frame-options: SAMEORIGIN + x-ms-build-version: 1.0.20210520-1-d6634624-develop + x-ms-keyvault-network-info: conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=Ipv4; + x-ms-keyvault-region: centralus + x-ms-server-latency: '2050' + status: + code: 200 + message: OK + url: https://mcpatinotesthsm.managedhsm.azure.net/backup/d9bfeb26511643629937e7626eda6230/pending +- request: + body: null + headers: + User-Agent: + - azsdk-python-keyvault-administration/4.0.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) + method: GET + uri: https://managedhsm/backup/d9bfeb26511643629937e7626eda6230/pending + response: + body: + string: '{"azureStorageBlobContainerUri":null,"endTime":null,"error":null,"jobId":"d9bfeb26511643629937e7626eda6230","startTime":1623713535,"status":"InProgress","statusDetails":null}' + headers: + cache-control: no-cache + content-length: '174' + content-security-policy: default-src 'self' + content-type: application/json; charset=utf-8 + date: Mon, 14 Jun 2021 23:32:18 GMT + server: Kestrel + strict-transport-security: max-age=31536000; includeSubDomains + x-content-type-options: nosniff + x-frame-options: SAMEORIGIN + x-ms-build-version: 1.0.20210520-1-d6634624-develop + x-ms-keyvault-network-info: conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=Ipv4; + x-ms-keyvault-region: centralus + x-ms-server-latency: '2306' + status: + code: 200 + message: OK + url: https://mcpatinotesthsm.managedhsm.azure.net/backup/d9bfeb26511643629937e7626eda6230/pending +- request: + body: null + headers: + User-Agent: + - azsdk-python-keyvault-administration/4.0.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) + method: GET + uri: https://managedhsm/backup/d9bfeb26511643629937e7626eda6230/pending + response: + body: + string: '{"azureStorageBlobContainerUri":null,"endTime":null,"error":null,"jobId":"d9bfeb26511643629937e7626eda6230","startTime":1623713535,"status":"InProgress","statusDetails":null}' + headers: + cache-control: no-cache + content-length: '174' + content-security-policy: default-src 'self' + content-type: application/json; charset=utf-8 + date: Mon, 14 Jun 2021 23:32:21 GMT + server: Kestrel + strict-transport-security: max-age=31536000; includeSubDomains + x-content-type-options: nosniff + x-frame-options: SAMEORIGIN + x-ms-build-version: 1.0.20210520-1-d6634624-develop + x-ms-keyvault-network-info: conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=Ipv4; + x-ms-keyvault-region: centralus + x-ms-server-latency: '2629' + status: + code: 200 + message: OK + url: https://mcpatinotesthsm.managedhsm.azure.net/backup/d9bfeb26511643629937e7626eda6230/pending +- request: + body: null + headers: + User-Agent: + - azsdk-python-keyvault-administration/4.0.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) + method: GET + uri: https://managedhsm/backup/d9bfeb26511643629937e7626eda6230/pending + response: + body: + string: '{"azureStorageBlobContainerUri":"https://storname.blob.core.windows.net/containerbcgbx3fq2eliq2t/mhsm-mcpatinotesthsm-2021061423321511","endTime":1623713544,"error":null,"jobId":"d9bfeb26511643629937e7626eda6230","startTime":1623713535,"status":"Succeeded","statusDetails":null}' + headers: + cache-control: no-cache + content-length: '294' + content-security-policy: default-src 'self' + content-type: application/json; charset=utf-8 + date: Mon, 14 Jun 2021 23:32:29 GMT + server: Kestrel + strict-transport-security: max-age=31536000; includeSubDomains + x-content-type-options: nosniff + x-frame-options: SAMEORIGIN + x-ms-build-version: 1.0.20210520-1-d6634624-develop + x-ms-keyvault-network-info: conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=Ipv4; + x-ms-keyvault-region: centralus + x-ms-server-latency: '2308' + status: + code: 200 + message: OK + url: https://mcpatinotesthsm.managedhsm.azure.net/backup/d9bfeb26511643629937e7626eda6230/pending +- request: + body: null + headers: + User-Agent: + - azsdk-python-keyvault-administration/4.0.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) + method: GET + uri: https://managedhsm/backup/d9bfeb26511643629937e7626eda6230/pending + response: + body: + string: '{"azureStorageBlobContainerUri":"https://storname.blob.core.windows.net/containerbcgbx3fq2eliq2t/mhsm-mcpatinotesthsm-2021061423321511","endTime":1623713544,"error":null,"jobId":"d9bfeb26511643629937e7626eda6230","startTime":1623713535,"status":"Succeeded","statusDetails":null}' + headers: + cache-control: no-cache + content-length: '294' + content-security-policy: default-src 'self' + content-type: application/json; charset=utf-8 + date: Mon, 14 Jun 2021 23:32:36 GMT + server: Kestrel + strict-transport-security: max-age=31536000; includeSubDomains + x-content-type-options: nosniff + x-frame-options: SAMEORIGIN + x-ms-build-version: 1.0.20210520-1-d6634624-develop + x-ms-keyvault-network-info: conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=Ipv4; + x-ms-keyvault-region: centralus + x-ms-server-latency: '2095' + status: + code: 200 + message: OK + url: https://mcpatinotesthsm.managedhsm.azure.net/backup/d9bfeb26511643629937e7626eda6230/pending +- request: + body: null + headers: + User-Agent: + - azsdk-python-keyvault-administration/4.0.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) + method: GET + uri: https://managedhsm/backup/d9bfeb26511643629937e7626eda6230/pending + response: + body: + string: '{"azureStorageBlobContainerUri":"https://storname.blob.core.windows.net/containerbcgbx3fq2eliq2t/mhsm-mcpatinotesthsm-2021061423321511","endTime":1623713544,"error":null,"jobId":"d9bfeb26511643629937e7626eda6230","startTime":1623713535,"status":"Succeeded","statusDetails":null}' + headers: + cache-control: no-cache + content-length: '294' + content-security-policy: default-src 'self' + content-type: application/json; charset=utf-8 + date: Mon, 14 Jun 2021 23:32:38 GMT + server: Kestrel + strict-transport-security: max-age=31536000; includeSubDomains + x-content-type-options: nosniff + x-frame-options: SAMEORIGIN + x-ms-build-version: 1.0.20210520-1-d6634624-develop + x-ms-keyvault-network-info: conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=Ipv4; + x-ms-keyvault-region: centralus + x-ms-server-latency: '2238' + status: + code: 200 + message: OK + url: https://mcpatinotesthsm.managedhsm.azure.net/backup/d9bfeb26511643629937e7626eda6230/pending +- request: + body: null + headers: + User-Agent: + - azsdk-python-keyvault-administration/4.0.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) + method: GET + uri: https://managedhsm/backup/d9bfeb26511643629937e7626eda6230/pending + response: + body: + string: '{"azureStorageBlobContainerUri":"https://storname.blob.core.windows.net/containerbcgbx3fq2eliq2t/mhsm-mcpatinotesthsm-2021061423321511","endTime":1623713544,"error":null,"jobId":"d9bfeb26511643629937e7626eda6230","startTime":1623713535,"status":"Succeeded","statusDetails":null}' + headers: + cache-control: no-cache + content-length: '294' + content-security-policy: default-src 'self' + content-type: application/json; charset=utf-8 + date: Mon, 14 Jun 2021 23:32:40 GMT + server: Kestrel + strict-transport-security: max-age=31536000; includeSubDomains + x-content-type-options: nosniff + x-frame-options: SAMEORIGIN + x-ms-build-version: 1.0.20210520-1-d6634624-develop + x-ms-keyvault-network-info: conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=Ipv4; + x-ms-keyvault-region: centralus + x-ms-server-latency: '2308' + status: + code: 200 + message: OK + url: https://mcpatinotesthsm.managedhsm.azure.net/backup/d9bfeb26511643629937e7626eda6230/pending +- request: + body: '{"sasTokenParameters": {"storageResourceUri": "https://storname.blob.core.windows.net/containerbcgbx3fq2eliq2t", + "token": "redacted"}, "folderToRestore": "mhsm-mcpatinotesthsm-2021061423321511"}' + headers: + Accept: + - application/json + Content-Length: + - '317' + Content-Type: + - application/json + User-Agent: + - azsdk-python-keyvault-administration/4.0.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) + method: PUT + uri: https://managedhsm/restore?api-version=7.2 + response: + body: + string: '{"endTime":null,"error":null,"jobId":"cab800ab01da484daeac156851250628","startTime":1623713563,"status":"InProgress","statusDetails":null}' + headers: + azure-asyncoperation: https://managedhsm/restore/cab800ab01da484daeac156851250628/pending + cache-control: no-cache + content-length: '138' + content-security-policy: default-src 'self' + content-type: application/json; charset=utf-8 + date: Mon, 14 Jun 2021 23:32:43 GMT + server: Kestrel + strict-transport-security: max-age=31536000; includeSubDomains + x-content-type-options: nosniff + x-frame-options: SAMEORIGIN + x-ms-keyvault-network-info: conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=Ipv4; + x-ms-keyvault-region: centralus + x-ms-server-latency: '2385' + status: + code: 202 + message: '' + url: https://mcpatinotesthsm.managedhsm.azure.net/restore?api-version=7.2 +- request: + body: null + headers: + User-Agent: + - azsdk-python-keyvault-administration/4.0.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) + method: GET + uri: https://managedhsm/restore/cab800ab01da484daeac156851250628/pending + response: + body: + string: '{"endTime":null,"error":null,"jobId":"cab800ab01da484daeac156851250628","startTime":1623713563,"status":"InProgress","statusDetails":null}' + headers: + cache-control: no-cache + content-length: '138' + content-security-policy: default-src 'self' + content-type: application/json; charset=utf-8 + date: Mon, 14 Jun 2021 23:32:45 GMT + server: Kestrel + strict-transport-security: max-age=31536000; includeSubDomains + x-content-type-options: nosniff + x-frame-options: SAMEORIGIN + x-ms-build-version: 1.0.20210520-1-d6634624-develop + x-ms-keyvault-network-info: conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=Ipv4; + x-ms-keyvault-region: centralus + x-ms-server-latency: '2161' + status: + code: 200 + message: OK + url: https://mcpatinotesthsm.managedhsm.azure.net/restore/cab800ab01da484daeac156851250628/pending +- request: + body: null + headers: + User-Agent: + - azsdk-python-keyvault-administration/4.0.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) + method: GET + uri: https://managedhsm/restore/cab800ab01da484daeac156851250628/pending + response: + body: + string: '{"endTime":null,"error":null,"jobId":"cab800ab01da484daeac156851250628","startTime":1623713563,"status":"InProgress","statusDetails":null}' + headers: + cache-control: no-cache + content-length: '138' + content-security-policy: default-src 'self' + content-type: application/json; charset=utf-8 + date: Mon, 14 Jun 2021 23:32:48 GMT + server: Kestrel + strict-transport-security: max-age=31536000; includeSubDomains + x-content-type-options: nosniff + x-frame-options: SAMEORIGIN + x-ms-build-version: 1.0.20210520-1-d6634624-develop + x-ms-keyvault-network-info: conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=Ipv4; + x-ms-keyvault-region: centralus + x-ms-server-latency: '2364' + status: + code: 200 + message: OK + url: https://mcpatinotesthsm.managedhsm.azure.net/restore/cab800ab01da484daeac156851250628/pending +- request: + body: null + headers: + User-Agent: + - azsdk-python-keyvault-administration/4.0.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) + method: GET + uri: https://managedhsm/restore/cab800ab01da484daeac156851250628/pending + response: + body: + string: '{"endTime":null,"error":null,"jobId":"cab800ab01da484daeac156851250628","startTime":1623713563,"status":"InProgress","statusDetails":null}' + headers: + cache-control: no-cache + content-length: '138' + content-security-policy: default-src 'self' + content-type: application/json; charset=utf-8 + date: Mon, 14 Jun 2021 23:32:50 GMT + server: Kestrel + strict-transport-security: max-age=31536000; includeSubDomains + x-content-type-options: nosniff + x-frame-options: SAMEORIGIN + x-ms-build-version: 1.0.20210520-1-d6634624-develop + x-ms-keyvault-network-info: conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=Ipv4; + x-ms-keyvault-region: centralus + x-ms-server-latency: '2259' + status: + code: 200 + message: OK + url: https://mcpatinotesthsm.managedhsm.azure.net/restore/cab800ab01da484daeac156851250628/pending +- request: + body: null + headers: + User-Agent: + - azsdk-python-keyvault-administration/4.0.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) + method: GET + uri: https://managedhsm/restore/cab800ab01da484daeac156851250628/pending + response: + body: + string: '{"endTime":null,"error":null,"jobId":"cab800ab01da484daeac156851250628","startTime":1623713563,"status":"InProgress","statusDetails":null}' + headers: + cache-control: no-cache + content-length: '138' + content-security-policy: default-src 'self' + content-type: application/json; charset=utf-8 + date: Mon, 14 Jun 2021 23:32:57 GMT + server: Kestrel + strict-transport-security: max-age=31536000; includeSubDomains + x-content-type-options: nosniff + x-frame-options: SAMEORIGIN + x-ms-build-version: 1.0.20210520-1-d6634624-develop + x-ms-keyvault-network-info: conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=Ipv4; + x-ms-keyvault-region: centralus + x-ms-server-latency: '2175' + status: + code: 200 + message: OK + url: https://mcpatinotesthsm.managedhsm.azure.net/restore/cab800ab01da484daeac156851250628/pending +- request: + body: null + headers: + User-Agent: + - azsdk-python-keyvault-administration/4.0.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) + method: GET + uri: https://managedhsm/restore/cab800ab01da484daeac156851250628/pending + response: + body: + string: '{"endTime":1623713580,"error":null,"jobId":"cab800ab01da484daeac156851250628","startTime":1623713563,"status":"Succeeded","statusDetails":null}' + headers: + cache-control: no-cache + content-length: '143' + content-security-policy: default-src 'self' + content-type: application/json; charset=utf-8 + date: Mon, 14 Jun 2021 23:33:05 GMT + server: Kestrel + strict-transport-security: max-age=31536000; includeSubDomains + x-content-type-options: nosniff + x-frame-options: SAMEORIGIN + x-ms-build-version: 1.0.20210520-1-d6634624-develop + x-ms-keyvault-network-info: conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=Ipv4; + x-ms-keyvault-region: centralus + x-ms-server-latency: '2748' + status: + code: 200 + message: OK + url: https://mcpatinotesthsm.managedhsm.azure.net/restore/cab800ab01da484daeac156851250628/pending +- request: + body: null + headers: + User-Agent: + - azsdk-python-keyvault-administration/4.0.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) + method: GET + uri: https://managedhsm/restore/cab800ab01da484daeac156851250628/pending + response: + body: + string: '{"endTime":1623713580,"error":null,"jobId":"cab800ab01da484daeac156851250628","startTime":1623713563,"status":"Succeeded","statusDetails":null}' + headers: + cache-control: no-cache + content-length: '143' + content-security-policy: default-src 'self' + content-type: application/json; charset=utf-8 + date: Mon, 14 Jun 2021 23:33:13 GMT + server: Kestrel + strict-transport-security: max-age=31536000; includeSubDomains + x-content-type-options: nosniff + x-frame-options: SAMEORIGIN + x-ms-build-version: 1.0.20210520-1-d6634624-develop + x-ms-keyvault-network-info: conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=Ipv4; + x-ms-keyvault-region: centralus + x-ms-server-latency: '2176' + status: + code: 200 + message: OK + url: https://mcpatinotesthsm.managedhsm.azure.net/restore/cab800ab01da484daeac156851250628/pending +version: 1 diff --git a/sdk/keyvault/azure-keyvault-administration/tests/recordings/test_backup_client_async.test_full_backup_and_restore_rehydration.yaml b/sdk/keyvault/azure-keyvault-administration/tests/recordings/test_backup_client_async.test_full_backup_and_restore_rehydration.yaml new file mode 100644 index 000000000000..35ec88ed94e3 --- /dev/null +++ b/sdk/keyvault/azure-keyvault-administration/tests/recordings/test_backup_client_async.test_full_backup_and_restore_rehydration.yaml @@ -0,0 +1,299 @@ +interactions: +- request: + body: null + headers: + Accept: + - application/json + Content-Length: + - '0' + Content-Type: + - application/json + User-Agent: + - azsdk-python-keyvault-administration/4.0.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) + method: POST + uri: https://managedhsm/backup?api-version=7.2 + response: + body: + string: '' + headers: + cache-control: no-cache + content-length: '0' + content-security-policy: default-src 'self' + content-type: application/json; charset=utf-8 + strict-transport-security: max-age=31536000; includeSubDomains + www-authenticate: Bearer authorization="https://login.microsoftonline.com/00000000-0000-0000-0000-000000000000", + resource="https://managedhsm.azure.net" + x-content-type-options: nosniff + x-frame-options: SAMEORIGIN + x-ms-server-latency: '1' + status: + code: 401 + message: Unauthorized + url: https://mcpatinotesthsm.managedhsm.azure.net/backup?api-version=7.2 +- request: + body: '{"storageResourceUri": "https://storname.blob.core.windows.net/containerijaxozkkwimtf7e", + "token": "redacted"}' + headers: + Accept: + - application/json + Content-Length: + - '233' + Content-Type: + - application/json + User-Agent: + - azsdk-python-keyvault-administration/4.0.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) + method: POST + uri: https://managedhsm/backup?api-version=7.2 + response: + body: + string: '{"status":"InProgress","statusDetails":null,"error":null,"startTime":1623712397,"endTime":null,"jobId":"ce8d058c198e4ed68c88c75abc4f22b0","azureStorageBlobContainerUri":null}' + headers: + azure-asyncoperation: https://managedhsm/backup/ce8d058c198e4ed68c88c75abc4f22b0/pending + cache-control: no-cache + content-length: '174' + content-security-policy: default-src 'self' + content-type: application/json; charset=utf-8 + date: Mon, 14 Jun 2021 23:13:17 GMT + server: Kestrel + strict-transport-security: max-age=31536000; includeSubDomains + x-content-type-options: nosniff + x-frame-options: SAMEORIGIN + x-ms-keyvault-network-info: conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=Ipv4; + x-ms-keyvault-region: centralus + x-ms-server-latency: '2530' + status: + code: 202 + message: '' + url: https://mcpatinotesthsm.managedhsm.azure.net/backup?api-version=7.2 +- request: + body: null + headers: + User-Agent: + - azsdk-python-keyvault-administration/4.0.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) + method: GET + uri: https://managedhsm/backup/ce8d058c198e4ed68c88c75abc4f22b0/pending + response: + body: + string: '{"azureStorageBlobContainerUri":null,"endTime":null,"error":null,"jobId":"ce8d058c198e4ed68c88c75abc4f22b0","startTime":1623712397,"status":"InProgress","statusDetails":null}' + headers: + cache-control: no-cache + content-length: '174' + content-security-policy: default-src 'self' + content-type: application/json; charset=utf-8 + date: Mon, 14 Jun 2021 23:13:19 GMT + server: Kestrel + strict-transport-security: max-age=31536000; includeSubDomains + x-content-type-options: nosniff + x-frame-options: SAMEORIGIN + x-ms-build-version: 1.0.20210520-1-d6634624-develop + x-ms-keyvault-network-info: conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=Ipv4; + x-ms-keyvault-region: centralus + x-ms-server-latency: '2061' + status: + code: 200 + message: OK + url: https://mcpatinotesthsm.managedhsm.azure.net/backup/ce8d058c198e4ed68c88c75abc4f22b0/pending +- request: + body: null + headers: + User-Agent: + - azsdk-python-keyvault-administration/4.0.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) + method: GET + uri: https://managedhsm/backup/ce8d058c198e4ed68c88c75abc4f22b0/pending + response: + body: + string: '{"azureStorageBlobContainerUri":"https://storname.blob.core.windows.net/containerijaxozkkwimtf7e/mhsm-mcpatinotesthsm-2021061423131779","endTime":1623712406,"error":null,"jobId":"ce8d058c198e4ed68c88c75abc4f22b0","startTime":1623712397,"status":"Succeeded","statusDetails":null}' + headers: + cache-control: no-cache + content-length: '294' + content-security-policy: default-src 'self' + content-type: application/json; charset=utf-8 + date: Mon, 14 Jun 2021 23:13:26 GMT + server: Kestrel + strict-transport-security: max-age=31536000; includeSubDomains + x-content-type-options: nosniff + x-frame-options: SAMEORIGIN + x-ms-build-version: 1.0.20210520-1-d6634624-develop + x-ms-keyvault-network-info: conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=Ipv4; + x-ms-keyvault-region: centralus + x-ms-server-latency: '2198' + status: + code: 200 + message: OK + url: https://mcpatinotesthsm.managedhsm.azure.net/backup/ce8d058c198e4ed68c88c75abc4f22b0/pending +- request: + body: null + headers: + User-Agent: + - azsdk-python-keyvault-administration/4.0.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) + method: GET + uri: https://managedhsm/backup/ce8d058c198e4ed68c88c75abc4f22b0/pending + response: + body: + string: '{"azureStorageBlobContainerUri":"https://storname.blob.core.windows.net/containerijaxozkkwimtf7e/mhsm-mcpatinotesthsm-2021061423131779","endTime":1623712406,"error":null,"jobId":"ce8d058c198e4ed68c88c75abc4f22b0","startTime":1623712397,"status":"Succeeded","statusDetails":null}' + headers: + cache-control: no-cache + content-length: '294' + content-security-policy: default-src 'self' + content-type: application/json; charset=utf-8 + date: Mon, 14 Jun 2021 23:13:39 GMT + server: Kestrel + strict-transport-security: max-age=31536000; includeSubDomains + x-content-type-options: nosniff + x-frame-options: SAMEORIGIN + x-ms-build-version: 1.0.20210520-1-d6634624-develop + x-ms-keyvault-network-info: conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=Ipv4; + x-ms-keyvault-region: centralus + x-ms-server-latency: '2196' + status: + code: 200 + message: OK + url: https://mcpatinotesthsm.managedhsm.azure.net/backup/ce8d058c198e4ed68c88c75abc4f22b0/pending +- request: + body: '{"sasTokenParameters": {"storageResourceUri": "https://storname.blob.core.windows.net/containerijaxozkkwimtf7e", + "token": "redacted"}, "folderToRestore": "mhsm-mcpatinotesthsm-2021061423131779"}' + headers: + Accept: + - application/json + Content-Length: + - '317' + Content-Type: + - application/json + User-Agent: + - azsdk-python-keyvault-administration/4.0.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) + method: PUT + uri: https://managedhsm/restore?api-version=7.2 + response: + body: + string: '{"endTime":null,"error":null,"jobId":"b9aeb3b6db9c46c4bb0038a0bac8df0d","startTime":1623712422,"status":"InProgress","statusDetails":null}' + headers: + azure-asyncoperation: https://managedhsm/restore/b9aeb3b6db9c46c4bb0038a0bac8df0d/pending + cache-control: no-cache + content-length: '138' + content-security-policy: default-src 'self' + content-type: application/json; charset=utf-8 + date: Mon, 14 Jun 2021 23:13:41 GMT + server: Kestrel + strict-transport-security: max-age=31536000; includeSubDomains + x-content-type-options: nosniff + x-frame-options: SAMEORIGIN + x-ms-keyvault-network-info: conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=Ipv4; + x-ms-keyvault-region: centralus + x-ms-server-latency: '2509' + status: + code: 202 + message: '' + url: https://mcpatinotesthsm.managedhsm.azure.net/restore?api-version=7.2 +- request: + body: null + headers: + User-Agent: + - azsdk-python-keyvault-administration/4.0.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) + method: GET + uri: https://managedhsm/restore/b9aeb3b6db9c46c4bb0038a0bac8df0d/pending + response: + body: + string: '{"endTime":null,"error":null,"jobId":"b9aeb3b6db9c46c4bb0038a0bac8df0d","startTime":1623712422,"status":"InProgress","statusDetails":null}' + headers: + cache-control: no-cache + content-length: '138' + content-security-policy: default-src 'self' + content-type: application/json; charset=utf-8 + date: Mon, 14 Jun 2021 23:13:44 GMT + server: Kestrel + strict-transport-security: max-age=31536000; includeSubDomains + x-content-type-options: nosniff + x-frame-options: SAMEORIGIN + x-ms-build-version: 1.0.20210520-1-d6634624-develop + x-ms-keyvault-network-info: conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=Ipv4; + x-ms-keyvault-region: centralus + x-ms-server-latency: '2065' + status: + code: 200 + message: OK + url: https://mcpatinotesthsm.managedhsm.azure.net/restore/b9aeb3b6db9c46c4bb0038a0bac8df0d/pending +- request: + body: null + headers: + User-Agent: + - azsdk-python-keyvault-administration/4.0.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) + method: GET + uri: https://managedhsm/restore/b9aeb3b6db9c46c4bb0038a0bac8df0d/pending + response: + body: + string: '{"endTime":null,"error":null,"jobId":"b9aeb3b6db9c46c4bb0038a0bac8df0d","startTime":1623712422,"status":"InProgress","statusDetails":null}' + headers: + cache-control: no-cache + content-length: '138' + content-security-policy: default-src 'self' + content-type: application/json; charset=utf-8 + date: Mon, 14 Jun 2021 23:13:51 GMT + server: Kestrel + strict-transport-security: max-age=31536000; includeSubDomains + x-content-type-options: nosniff + x-frame-options: SAMEORIGIN + x-ms-build-version: 1.0.20210520-1-d6634624-develop + x-ms-keyvault-network-info: conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=Ipv4; + x-ms-keyvault-region: centralus + x-ms-server-latency: '2252' + status: + code: 200 + message: OK + url: https://mcpatinotesthsm.managedhsm.azure.net/restore/b9aeb3b6db9c46c4bb0038a0bac8df0d/pending +- request: + body: null + headers: + User-Agent: + - azsdk-python-keyvault-administration/4.0.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) + method: GET + uri: https://managedhsm/restore/b9aeb3b6db9c46c4bb0038a0bac8df0d/pending + response: + body: + string: '{"endTime":1623712438,"error":null,"jobId":"b9aeb3b6db9c46c4bb0038a0bac8df0d","startTime":1623712422,"status":"Succeeded","statusDetails":null}' + headers: + cache-control: no-cache + content-length: '143' + content-security-policy: default-src 'self' + content-type: application/json; charset=utf-8 + date: Mon, 14 Jun 2021 23:13:58 GMT + server: Kestrel + strict-transport-security: max-age=31536000; includeSubDomains + x-content-type-options: nosniff + x-frame-options: SAMEORIGIN + x-ms-build-version: 1.0.20210520-1-d6634624-develop + x-ms-keyvault-network-info: conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=Ipv4; + x-ms-keyvault-region: centralus + x-ms-server-latency: '2281' + status: + code: 200 + message: OK + url: https://mcpatinotesthsm.managedhsm.azure.net/restore/b9aeb3b6db9c46c4bb0038a0bac8df0d/pending +- request: + body: null + headers: + User-Agent: + - azsdk-python-keyvault-administration/4.0.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) + method: GET + uri: https://managedhsm/restore/b9aeb3b6db9c46c4bb0038a0bac8df0d/pending + response: + body: + string: '{"endTime":1623712438,"error":null,"jobId":"b9aeb3b6db9c46c4bb0038a0bac8df0d","startTime":1623712422,"status":"Succeeded","statusDetails":null}' + headers: + cache-control: no-cache + content-length: '143' + content-security-policy: default-src 'self' + content-type: application/json; charset=utf-8 + date: Mon, 14 Jun 2021 23:14:10 GMT + server: Kestrel + strict-transport-security: max-age=31536000; includeSubDomains + x-content-type-options: nosniff + x-frame-options: SAMEORIGIN + x-ms-build-version: 1.0.20210520-1-d6634624-develop + x-ms-keyvault-network-info: conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=Ipv4; + x-ms-keyvault-region: centralus + x-ms-server-latency: '2140' + status: + code: 200 + message: OK + url: https://mcpatinotesthsm.managedhsm.azure.net/restore/b9aeb3b6db9c46c4bb0038a0bac8df0d/pending +version: 1 diff --git a/sdk/keyvault/azure-keyvault-administration/tests/test_backup_client.py b/sdk/keyvault/azure-keyvault-administration/tests/test_backup_client.py index 1b3112f707ad..87e2d12a867c 100644 --- a/sdk/keyvault/azure-keyvault-administration/tests/test_backup_client.py +++ b/sdk/keyvault/azure-keyvault-administration/tests/test_backup_client.py @@ -2,6 +2,7 @@ # Copyright (c) Microsoft Corporation. # Licensed under the MIT License. # ------------------------------------ +import base64 from datetime import datetime from functools import partial import time @@ -42,7 +43,15 @@ def tearDown(self): @property def credential(self): if self.is_live: - return DefaultAzureCredential() + from dotenv import load_dotenv + from azure.identity import ClientSecretCredential + import os + load_dotenv() + return ClientSecretCredential( + tenant_id=os.environ["KEYVAULT_TENANT_ID"], + client_id=os.environ["KEYVAULT_CLIENT_ID"], + client_secret=os.environ["KEYVAULT_CLIENT_SECRET"] + ) return mock.Mock(get_token=lambda *_, **__: AccessToken("secret", time.time() + 3600)) @ResourceGroupPreparer(random_name_enabled=True, use_cache=True) @@ -62,6 +71,9 @@ def test_full_backup_and_restore(self, container_uri, sas_token): @StorageAccountPreparer(random_name_enabled=True) @BlobContainerPreparer() def test_full_backup_and_restore_rehydration(self, container_uri, sas_token): + if not self.is_live: + pytest.skip("Poller requests are incompatible with vcrpy for synchronous tests") + # backup the vault backup_client = KeyVaultBackupClient(self.managed_hsm["url"], self.credential) backup_poller = backup_client.begin_backup(container_uri, sas_token) @@ -71,7 +83,6 @@ def test_full_backup_and_restore_rehydration(self, container_uri, sas_token): rehydrated = backup_client.begin_backup(container_uri, sas_token, continuation_token=token) backup_operation = rehydrated.result() - assert rehydrated.status() == "Succeeded" and rehydrated.polling_method().status() == "Succeeded" assert backup_operation.folder_url backup_poller.wait() @@ -172,25 +183,6 @@ def test_backup_client_polling(self, container_uri, sas_token): assert restore_poller.status() == "Succeeded" and restore_poller.polling_method().status() == "Succeeded" -def test_continuation_token(): - """Methods returning pollers should accept continuation tokens""" - - expected_token = "token" - mock_generated_client = mock.Mock() - - backup_client = KeyVaultBackupClient("vault-url", object()) - backup_client._client = mock_generated_client - backup_client.begin_restore("storage uri", "sas", continuation_token=expected_token) - backup_client.begin_backup("storage uri", "sas", continuation_token=expected_token) - backup_client.begin_restore("storage uri", "sas", key_name="key", continuation_token=expected_token) - - for method in ("begin_full_backup", "begin_full_restore_operation", "begin_selective_key_restore_operation"): - mock_method = getattr(mock_generated_client, method) - assert mock_method.call_count == 1 - _, kwargs = mock_method.call_args - assert kwargs["continuation_token"] == expected_token - - @pytest.mark.parametrize( "url,expected_container_url,expected_folder_name", [ diff --git a/sdk/keyvault/azure-keyvault-administration/tests/test_backup_client_async.py b/sdk/keyvault/azure-keyvault-administration/tests/test_backup_client_async.py index 45914d77eae3..4ee7dda2113e 100644 --- a/sdk/keyvault/azure-keyvault-administration/tests/test_backup_client_async.py +++ b/sdk/keyvault/azure-keyvault-administration/tests/test_backup_client_async.py @@ -40,7 +40,15 @@ def tearDown(self): @property def credential(self): if self.is_live: - return DefaultAzureCredential() + from dotenv import load_dotenv + from azure.identity.aio import ClientSecretCredential + import os + load_dotenv() + return ClientSecretCredential( + tenant_id=os.environ["KEYVAULT_TENANT_ID"], + client_id=os.environ["KEYVAULT_CLIENT_ID"], + client_secret=os.environ["KEYVAULT_CLIENT_SECRET"] + ) async def get_token(*_, **__): return AccessToken("secret", time.time() + 3600) @@ -101,33 +109,60 @@ async def test_selective_key_restore(self, container_uri, sas_token): await self._poll_until_no_exception(key_client.delete_key, key_name, expected_exception=ResourceExistsError) await key_client.purge_deleted_key(key_name) + @ResourceGroupPreparer(random_name_enabled=True, use_cache=True) + @StorageAccountPreparer(random_name_enabled=True) + @BlobContainerPreparer() + async def test_backup_client_polling(self, container_uri, sas_token): + # backup the vault + backup_client = KeyVaultBackupClient(self.managed_hsm["url"], self.credential) + backup_poller = await backup_client.begin_backup(container_uri, sas_token) + + # create a new poller from a continuation token + token = backup_poller.polling_method().get_continuation_token() + rehydrated = await backup_client.begin_backup(container_uri, sas_token, continuation_token=token) + + # check that pollers and polling methods behave as expected + assert backup_poller.status() == "InProgress" + assert not backup_poller.done() or backup_poller.polling_method().finished() + assert rehydrated.status() == "InProgress" + assert not rehydrated.done() or rehydrated.polling_method().finished() + + await backup_poller.polling_method().update_status() + assert backup_poller.status() == "InProgress" + await rehydrated.polling_method().update_status() + assert rehydrated.status() == "InProgress" + + backup_operation = await backup_poller.result() + assert backup_poller.status() == "Succeeded" and backup_poller.polling_method().status() == "Succeeded" + rehydrated_operation = await rehydrated.result() + assert rehydrated.status() == "Succeeded" and rehydrated.polling_method().status() == "Succeeded" + assert backup_operation.folder_url == rehydrated_operation.folder_url + + # rehydrate a poller with a continuation token of a completed operation + late_rehydrated = await backup_client.begin_backup(container_uri, sas_token, continuation_token=token) + assert late_rehydrated.status() == "InProgress" + await late_rehydrated.polling_method().update_status() + assert late_rehydrated.status() == "Succeeded" + + # restore the backup + restore_poller = await backup_client.begin_restore(backup_operation.folder_url, sas_token) + + # create a new poller from a continuation token + token = restore_poller.polling_method().get_continuation_token() + rehydrated = await backup_client.begin_restore(backup_operation.folder_url, sas_token, continuation_token=token) -@pytest.mark.asyncio -async def test_continuation_token(): - """Methods returning pollers should accept continuation tokens""" - - expected_token = "token" - - mock_generated_client = mock.Mock() - mock_methods = [ - getattr(mock_generated_client, method_name) - for method_name in ( - "begin_full_backup", - "begin_full_restore_operation", - "begin_selective_key_restore_operation", - ) - ] - for method in mock_methods: - # the mock client's methods must return awaitables, and we don't have AsyncMock before 3.8 - method.return_value = get_completed_future() - - backup_client = KeyVaultBackupClient("vault-url", object()) - backup_client._client = mock_generated_client - await backup_client.begin_restore("storage uri", "sas", continuation_token=expected_token) - await backup_client.begin_backup("storage uri", "sas", continuation_token=expected_token) - await backup_client.begin_restore("storage uri", "sas", key_name="key", continuation_token=expected_token) - - for method in mock_methods: - assert method.call_count == 1 - _, kwargs = method.call_args - assert kwargs["continuation_token"] == expected_token + # check that pollers and polling methods behave as expected + assert restore_poller.status() == "InProgress" + assert not restore_poller.done() or restore_poller.polling_method().finished() + assert rehydrated.status() == "InProgress" + assert not rehydrated.done() or rehydrated.polling_method().finished() + + await rehydrated.polling_method().update_status() + assert rehydrated.status() == "InProgress" + await restore_poller.polling_method().update_status() + assert restore_poller.status() == "InProgress" + + await rehydrated.wait() + assert rehydrated.status() == "Succeeded" and rehydrated.polling_method().status() == "Succeeded" + await restore_poller.wait() + assert restore_poller.status() == "Succeeded" and restore_poller.polling_method().status() == "Succeeded" From ef4709220629b9ddcebac1507bdd6ab407080bbd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?McCoy=20Pati=C3=B1o?= Date: Mon, 14 Jun 2021 14:49:14 -0700 Subject: [PATCH 17/27] refine tests, add examples --- .../administration/aio/_backup_client.py | 16 + ...t_full_backup_and_restore_rehydration.yaml | 515 ------------------ ...ient_async.test_backup_client_polling.yaml | 467 ---------------- ...t_full_backup_and_restore_rehydration.yaml | 299 ---------- .../tests/test_backup_client.py | 41 +- .../tests/test_backup_client_async.py | 73 ++- .../tests/test_examples_administration.py | 79 +++ .../test_examples_administration_async.py | 83 +++ 8 files changed, 242 insertions(+), 1331 deletions(-) delete mode 100644 sdk/keyvault/azure-keyvault-administration/tests/recordings/test_backup_client.test_full_backup_and_restore_rehydration.yaml delete mode 100644 sdk/keyvault/azure-keyvault-administration/tests/recordings/test_backup_client_async.test_backup_client_polling.yaml delete mode 100644 sdk/keyvault/azure-keyvault-administration/tests/recordings/test_backup_client_async.test_full_backup_and_restore_rehydration.yaml create mode 100644 sdk/keyvault/azure-keyvault-administration/tests/test_examples_administration.py create mode 100644 sdk/keyvault/azure-keyvault-administration/tests/test_examples_administration_async.py diff --git a/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/aio/_backup_client.py b/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/aio/_backup_client.py index 10ca7f93cee6..f600cc4f1b92 100644 --- a/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/aio/_backup_client.py +++ b/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/aio/_backup_client.py @@ -47,6 +47,14 @@ async def begin_backup( :keyword str continuation_token: a continuation token to restart polling from a saved state :returns: An AsyncLROPoller. Call `result()` on this object to get a :class:`KeyVaultBackupOperation`. :rtype: ~azure.core.polling.AsyncLROPoller[~azure.keyvault.administration.KeyVaultBackupOperation] + + Example: + .. literalinclude:: ../tests/test_examples_administration_async.py + :start-after: [START begin_backup] + :end-before: [END begin_backup] + :language: python + :caption: Start a vault backup operation + :dedent: 8 """ polling_interval = kwargs.pop("_polling_interval", 5) sas_parameter = self._models.SASTokenParameter(storage_resource_uri=blob_storage_url, token=sas_token) @@ -80,6 +88,14 @@ async def begin_restore(self, folder_url: str, sas_token: str, **kwargs: "Any") :keyword str continuation_token: a continuation token to restart polling from a saved state :keyword str key_name: name of a single key in the backup. When set, only this key will be restored. :rtype: ~azure.core.polling.AsyncLROPoller + + Example: + .. literalinclude:: ../tests/test_examples_administration.py + :start-after: [START begin_backup] + :end-before: [END begin_backup] + :language: python + :caption: Start a vault backup operation + :dedent: 8 """ # AsyncLROBasePolling passes its kwargs to pipeline.run(), so we remove unexpected args before constructing it continuation_token = kwargs.pop("continuation_token", None) diff --git a/sdk/keyvault/azure-keyvault-administration/tests/recordings/test_backup_client.test_full_backup_and_restore_rehydration.yaml b/sdk/keyvault/azure-keyvault-administration/tests/recordings/test_backup_client.test_full_backup_and_restore_rehydration.yaml deleted file mode 100644 index 9260dfe64d92..000000000000 --- a/sdk/keyvault/azure-keyvault-administration/tests/recordings/test_backup_client.test_full_backup_and_restore_rehydration.yaml +++ /dev/null @@ -1,515 +0,0 @@ -interactions: -- request: - body: null - headers: - Accept: - - application/json - Accept-Encoding: - - gzip, deflate - Connection: - - keep-alive - Content-Length: - - '0' - Content-Type: - - application/json - User-Agent: - - azsdk-python-keyvault-administration/4.0.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) - method: POST - uri: https://managedhsm/backup?api-version=7.2 - response: - body: - string: '' - headers: - cache-control: - - no-cache - content-length: - - '0' - content-security-policy: - - default-src 'self' - content-type: - - application/json; charset=utf-8 - strict-transport-security: - - max-age=31536000; includeSubDomains - www-authenticate: - - Bearer authorization="https://login.microsoftonline.com/00000000-0000-0000-0000-000000000000", - resource="https://managedhsm.azure.net" - x-content-type-options: - - nosniff - x-frame-options: - - SAMEORIGIN - x-ms-server-latency: - - '0' - status: - code: 401 - message: Unauthorized -- request: - body: '{"storageResourceUri": "https://storname.blob.core.windows.net/container5tluqadlguplx4n", - "token": "redacted"}' - headers: - Accept: - - application/json - Accept-Encoding: - - gzip, deflate - Connection: - - keep-alive - Content-Length: - - '235' - Content-Type: - - application/json - User-Agent: - - azsdk-python-keyvault-administration/4.0.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) - method: POST - uri: https://managedhsm/backup?api-version=7.2 - response: - body: - string: '{"status":"InProgress","statusDetails":null,"error":null,"startTime":1623714247,"endTime":null,"jobId":"712eb19999cb4e6d8a7f1477d07c0b1e","azureStorageBlobContainerUri":null}' - headers: - azure-asyncoperation: - - https://managedhsm/backup/712eb19999cb4e6d8a7f1477d07c0b1e/pending - cache-control: - - no-cache - content-length: - - '174' - content-security-policy: - - default-src 'self' - content-type: - - application/json; charset=utf-8 - date: - - Mon, 14 Jun 2021 23:44:07 GMT - server: - - Kestrel - strict-transport-security: - - max-age=31536000; includeSubDomains - x-content-type-options: - - nosniff - x-frame-options: - - SAMEORIGIN - x-ms-keyvault-network-info: - - conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=Ipv4; - x-ms-keyvault-region: - - centralus - x-ms-server-latency: - - '2448' - status: - code: 202 - message: '' -- request: - body: null - headers: - Accept: - - '*/*' - Accept-Encoding: - - gzip, deflate - Connection: - - keep-alive - User-Agent: - - azsdk-python-keyvault-administration/4.0.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) - method: GET - uri: https://managedhsm/backup/712eb19999cb4e6d8a7f1477d07c0b1e/pending - response: - body: - string: '{"azureStorageBlobContainerUri":null,"endTime":null,"error":null,"jobId":"712eb19999cb4e6d8a7f1477d07c0b1e","startTime":1623714247,"status":"InProgress","statusDetails":null}' - headers: - cache-control: - - no-cache - content-length: - - '174' - content-security-policy: - - default-src 'self' - content-type: - - application/json; charset=utf-8 - date: - - Mon, 14 Jun 2021 23:44:09 GMT - server: - - Kestrel - strict-transport-security: - - max-age=31536000; includeSubDomains - x-content-type-options: - - nosniff - x-frame-options: - - SAMEORIGIN - x-ms-build-version: - - 1.0.20210520-1-d6634624-develop - x-ms-keyvault-network-info: - - conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=Ipv4; - x-ms-keyvault-region: - - centralus - x-ms-server-latency: - - '2082' - status: - code: 200 - message: OK -- request: - body: null - headers: - Accept: - - '*/*' - Accept-Encoding: - - gzip, deflate - Connection: - - keep-alive - User-Agent: - - azsdk-python-keyvault-administration/4.0.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) - method: GET - uri: https://managedhsm/backup/712eb19999cb4e6d8a7f1477d07c0b1e/pending - response: - body: - string: '{"azureStorageBlobContainerUri":"https://storname.blob.core.windows.net/container5tluqadlguplx4n/mhsm-mcpatinotesthsm-2021061423440753","endTime":1623714256,"error":null,"jobId":"712eb19999cb4e6d8a7f1477d07c0b1e","startTime":1623714247,"status":"Succeeded","statusDetails":null}' - headers: - cache-control: - - no-cache - content-length: - - '294' - content-security-policy: - - default-src 'self' - content-type: - - application/json; charset=utf-8 - date: - - Mon, 14 Jun 2021 23:44:17 GMT - server: - - Kestrel - strict-transport-security: - - max-age=31536000; includeSubDomains - x-content-type-options: - - nosniff - x-frame-options: - - SAMEORIGIN - x-ms-build-version: - - 1.0.20210520-1-d6634624-develop - x-ms-keyvault-network-info: - - conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=Ipv4; - x-ms-keyvault-region: - - centralus - x-ms-server-latency: - - '2160' - status: - code: 200 - message: OK -- request: - body: null - headers: - Accept: - - '*/*' - Accept-Encoding: - - gzip, deflate - Connection: - - keep-alive - User-Agent: - - azsdk-python-keyvault-administration/4.0.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) - method: GET - uri: https://managedhsm/backup/712eb19999cb4e6d8a7f1477d07c0b1e/pending - response: - body: - string: '{"azureStorageBlobContainerUri":"https://storname.blob.core.windows.net/container5tluqadlguplx4n/mhsm-mcpatinotesthsm-2021061423440753","endTime":1623714256,"error":null,"jobId":"712eb19999cb4e6d8a7f1477d07c0b1e","startTime":1623714247,"status":"Succeeded","statusDetails":null}' - headers: - cache-control: - - no-cache - content-length: - - '294' - content-security-policy: - - default-src 'self' - content-type: - - application/json; charset=utf-8 - date: - - Mon, 14 Jun 2021 23:44:19 GMT - server: - - Kestrel - strict-transport-security: - - max-age=31536000; includeSubDomains - x-content-type-options: - - nosniff - x-frame-options: - - SAMEORIGIN - x-ms-build-version: - - 1.0.20210520-1-d6634624-develop - x-ms-keyvault-network-info: - - conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=Ipv4; - x-ms-keyvault-region: - - centralus - x-ms-server-latency: - - '2146' - status: - code: 200 - message: OK -- request: - body: '{"sasTokenParameters": {"storageResourceUri": "https://storname.blob.core.windows.net/container5tluqadlguplx4n", - "token": "redacted"}, "folderToRestore": "mhsm-mcpatinotesthsm-2021061423440753"}' - headers: - Accept: - - application/json - Accept-Encoding: - - gzip, deflate - Connection: - - keep-alive - Content-Length: - - '319' - Content-Type: - - application/json - User-Agent: - - azsdk-python-keyvault-administration/4.0.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) - method: PUT - uri: https://managedhsm/restore?api-version=7.2 - response: - body: - string: '{"endTime":null,"error":null,"jobId":"6b24e91c4cb84c0a86133807e265bbb9","startTime":1623714262,"status":"InProgress","statusDetails":null}' - headers: - azure-asyncoperation: - - https://managedhsm/restore/6b24e91c4cb84c0a86133807e265bbb9/pending - cache-control: - - no-cache - content-length: - - '138' - content-security-policy: - - default-src 'self' - content-type: - - application/json; charset=utf-8 - date: - - Mon, 14 Jun 2021 23:44:21 GMT - server: - - Kestrel - strict-transport-security: - - max-age=31536000; includeSubDomains - x-content-type-options: - - nosniff - x-frame-options: - - SAMEORIGIN - x-ms-keyvault-network-info: - - conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=Ipv4; - x-ms-keyvault-region: - - centralus - x-ms-server-latency: - - '2290' - status: - code: 202 - message: '' -- request: - body: null - headers: - Accept: - - '*/*' - Accept-Encoding: - - gzip, deflate - Connection: - - keep-alive - User-Agent: - - azsdk-python-keyvault-administration/4.0.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) - method: GET - uri: https://managedhsm/restore/6b24e91c4cb84c0a86133807e265bbb9/pending - response: - body: - string: '{"endTime":null,"error":null,"jobId":"6b24e91c4cb84c0a86133807e265bbb9","startTime":1623714262,"status":"InProgress","statusDetails":null}' - headers: - cache-control: - - no-cache - content-length: - - '138' - content-security-policy: - - default-src 'self' - content-type: - - application/json; charset=utf-8 - date: - - Mon, 14 Jun 2021 23:44:23 GMT - server: - - Kestrel - strict-transport-security: - - max-age=31536000; includeSubDomains - x-content-type-options: - - nosniff - x-frame-options: - - SAMEORIGIN - x-ms-build-version: - - 1.0.20210520-1-d6634624-develop - x-ms-keyvault-network-info: - - conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=Ipv4; - x-ms-keyvault-region: - - centralus - x-ms-server-latency: - - '2069' - status: - code: 200 - message: OK -- request: - body: null - headers: - Accept: - - '*/*' - Accept-Encoding: - - gzip, deflate - Connection: - - keep-alive - User-Agent: - - azsdk-python-keyvault-administration/4.0.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) - method: GET - uri: https://managedhsm/restore/6b24e91c4cb84c0a86133807e265bbb9/pending - response: - body: - string: '{"endTime":null,"error":null,"jobId":"6b24e91c4cb84c0a86133807e265bbb9","startTime":1623714262,"status":"InProgress","statusDetails":null}' - headers: - cache-control: - - no-cache - content-length: - - '138' - content-security-policy: - - default-src 'self' - content-type: - - application/json; charset=utf-8 - date: - - Mon, 14 Jun 2021 23:44:31 GMT - server: - - Kestrel - strict-transport-security: - - max-age=31536000; includeSubDomains - x-content-type-options: - - nosniff - x-frame-options: - - SAMEORIGIN - x-ms-build-version: - - 1.0.20210520-1-d6634624-develop - x-ms-keyvault-network-info: - - conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=Ipv4; - x-ms-keyvault-region: - - centralus - x-ms-server-latency: - - '2148' - status: - code: 200 - message: OK -- request: - body: null - headers: - Accept: - - '*/*' - Accept-Encoding: - - gzip, deflate - Connection: - - keep-alive - User-Agent: - - azsdk-python-keyvault-administration/4.0.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) - method: GET - uri: https://managedhsm/restore/6b24e91c4cb84c0a86133807e265bbb9/pending - response: - body: - string: '{"endTime":null,"error":null,"jobId":"6b24e91c4cb84c0a86133807e265bbb9","startTime":1623714262,"status":"InProgress","statusDetails":null}' - headers: - cache-control: - - no-cache - content-length: - - '138' - content-security-policy: - - default-src 'self' - content-type: - - application/json; charset=utf-8 - date: - - Mon, 14 Jun 2021 23:44:34 GMT - server: - - Kestrel - strict-transport-security: - - max-age=31536000; includeSubDomains - x-content-type-options: - - nosniff - x-frame-options: - - SAMEORIGIN - x-ms-build-version: - - 1.0.20210520-1-d6634624-develop - x-ms-keyvault-network-info: - - conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=Ipv4; - x-ms-keyvault-region: - - centralus - x-ms-server-latency: - - '2192' - status: - code: 200 - message: OK -- request: - body: null - headers: - Accept: - - '*/*' - Accept-Encoding: - - gzip, deflate - Connection: - - keep-alive - User-Agent: - - azsdk-python-keyvault-administration/4.0.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) - method: GET - uri: https://managedhsm/restore/6b24e91c4cb84c0a86133807e265bbb9/pending - response: - body: - string: '{"endTime":1623714278,"error":null,"jobId":"6b24e91c4cb84c0a86133807e265bbb9","startTime":1623714262,"status":"Succeeded","statusDetails":null}' - headers: - cache-control: - - no-cache - content-length: - - '143' - content-security-policy: - - default-src 'self' - content-type: - - application/json; charset=utf-8 - date: - - Mon, 14 Jun 2021 23:44:38 GMT - server: - - Kestrel - strict-transport-security: - - max-age=31536000; includeSubDomains - x-content-type-options: - - nosniff - x-frame-options: - - SAMEORIGIN - x-ms-build-version: - - 1.0.20210520-1-d6634624-develop - x-ms-keyvault-network-info: - - conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=Ipv4; - x-ms-keyvault-region: - - centralus - x-ms-server-latency: - - '2139' - status: - code: 200 - message: OK -- request: - body: null - headers: - Accept: - - '*/*' - Accept-Encoding: - - gzip, deflate - Connection: - - keep-alive - User-Agent: - - azsdk-python-keyvault-administration/4.0.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) - method: GET - uri: https://managedhsm/restore/6b24e91c4cb84c0a86133807e265bbb9/pending - response: - body: - string: '{"endTime":1623714278,"error":null,"jobId":"6b24e91c4cb84c0a86133807e265bbb9","startTime":1623714262,"status":"Succeeded","statusDetails":null}' - headers: - cache-control: - - no-cache - content-length: - - '143' - content-security-policy: - - default-src 'self' - content-type: - - application/json; charset=utf-8 - date: - - Mon, 14 Jun 2021 23:44:41 GMT - server: - - Kestrel - strict-transport-security: - - max-age=31536000; includeSubDomains - x-content-type-options: - - nosniff - x-frame-options: - - SAMEORIGIN - x-ms-build-version: - - 1.0.20210520-1-d6634624-develop - x-ms-keyvault-network-info: - - conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=Ipv4; - x-ms-keyvault-region: - - centralus - x-ms-server-latency: - - '2193' - status: - code: 200 - message: OK -version: 1 diff --git a/sdk/keyvault/azure-keyvault-administration/tests/recordings/test_backup_client_async.test_backup_client_polling.yaml b/sdk/keyvault/azure-keyvault-administration/tests/recordings/test_backup_client_async.test_backup_client_polling.yaml deleted file mode 100644 index cc4083f149c0..000000000000 --- a/sdk/keyvault/azure-keyvault-administration/tests/recordings/test_backup_client_async.test_backup_client_polling.yaml +++ /dev/null @@ -1,467 +0,0 @@ -interactions: -- request: - body: null - headers: - Accept: - - application/json - Content-Length: - - '0' - Content-Type: - - application/json - User-Agent: - - azsdk-python-keyvault-administration/4.0.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) - method: POST - uri: https://managedhsm/backup?api-version=7.2 - response: - body: - string: '' - headers: - cache-control: no-cache - content-length: '0' - content-security-policy: default-src 'self' - content-type: application/json; charset=utf-8 - strict-transport-security: max-age=31536000; includeSubDomains - www-authenticate: Bearer authorization="https://login.microsoftonline.com/00000000-0000-0000-0000-000000000000", - resource="https://managedhsm.azure.net" - x-content-type-options: nosniff - x-frame-options: SAMEORIGIN - x-ms-server-latency: '1' - status: - code: 401 - message: Unauthorized - url: https://mcpatinotesthsm.managedhsm.azure.net/backup?api-version=7.2 -- request: - body: '{"storageResourceUri": "https://storname.blob.core.windows.net/containerbcgbx3fq2eliq2t", - "token": "redacted"}' - headers: - Accept: - - application/json - Content-Length: - - '233' - Content-Type: - - application/json - User-Agent: - - azsdk-python-keyvault-administration/4.0.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) - method: POST - uri: https://managedhsm/backup?api-version=7.2 - response: - body: - string: '{"status":"InProgress","statusDetails":null,"error":null,"startTime":1623713535,"endTime":null,"jobId":"d9bfeb26511643629937e7626eda6230","azureStorageBlobContainerUri":null}' - headers: - azure-asyncoperation: https://managedhsm/backup/d9bfeb26511643629937e7626eda6230/pending - cache-control: no-cache - content-length: '174' - content-security-policy: default-src 'self' - content-type: application/json; charset=utf-8 - date: Mon, 14 Jun 2021 23:32:14 GMT - server: Kestrel - strict-transport-security: max-age=31536000; includeSubDomains - x-content-type-options: nosniff - x-frame-options: SAMEORIGIN - x-ms-keyvault-network-info: conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=Ipv4; - x-ms-keyvault-region: centralus - x-ms-server-latency: '2329' - status: - code: 202 - message: '' - url: https://mcpatinotesthsm.managedhsm.azure.net/backup?api-version=7.2 -- request: - body: null - headers: - User-Agent: - - azsdk-python-keyvault-administration/4.0.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) - method: GET - uri: https://managedhsm/backup/d9bfeb26511643629937e7626eda6230/pending - response: - body: - string: '{"azureStorageBlobContainerUri":null,"endTime":null,"error":null,"jobId":"d9bfeb26511643629937e7626eda6230","startTime":1623713535,"status":"InProgress","statusDetails":null}' - headers: - cache-control: no-cache - content-length: '174' - content-security-policy: default-src 'self' - content-type: application/json; charset=utf-8 - date: Mon, 14 Jun 2021 23:32:16 GMT - server: Kestrel - strict-transport-security: max-age=31536000; includeSubDomains - x-content-type-options: nosniff - x-frame-options: SAMEORIGIN - x-ms-build-version: 1.0.20210520-1-d6634624-develop - x-ms-keyvault-network-info: conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=Ipv4; - x-ms-keyvault-region: centralus - x-ms-server-latency: '2050' - status: - code: 200 - message: OK - url: https://mcpatinotesthsm.managedhsm.azure.net/backup/d9bfeb26511643629937e7626eda6230/pending -- request: - body: null - headers: - User-Agent: - - azsdk-python-keyvault-administration/4.0.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) - method: GET - uri: https://managedhsm/backup/d9bfeb26511643629937e7626eda6230/pending - response: - body: - string: '{"azureStorageBlobContainerUri":null,"endTime":null,"error":null,"jobId":"d9bfeb26511643629937e7626eda6230","startTime":1623713535,"status":"InProgress","statusDetails":null}' - headers: - cache-control: no-cache - content-length: '174' - content-security-policy: default-src 'self' - content-type: application/json; charset=utf-8 - date: Mon, 14 Jun 2021 23:32:18 GMT - server: Kestrel - strict-transport-security: max-age=31536000; includeSubDomains - x-content-type-options: nosniff - x-frame-options: SAMEORIGIN - x-ms-build-version: 1.0.20210520-1-d6634624-develop - x-ms-keyvault-network-info: conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=Ipv4; - x-ms-keyvault-region: centralus - x-ms-server-latency: '2306' - status: - code: 200 - message: OK - url: https://mcpatinotesthsm.managedhsm.azure.net/backup/d9bfeb26511643629937e7626eda6230/pending -- request: - body: null - headers: - User-Agent: - - azsdk-python-keyvault-administration/4.0.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) - method: GET - uri: https://managedhsm/backup/d9bfeb26511643629937e7626eda6230/pending - response: - body: - string: '{"azureStorageBlobContainerUri":null,"endTime":null,"error":null,"jobId":"d9bfeb26511643629937e7626eda6230","startTime":1623713535,"status":"InProgress","statusDetails":null}' - headers: - cache-control: no-cache - content-length: '174' - content-security-policy: default-src 'self' - content-type: application/json; charset=utf-8 - date: Mon, 14 Jun 2021 23:32:21 GMT - server: Kestrel - strict-transport-security: max-age=31536000; includeSubDomains - x-content-type-options: nosniff - x-frame-options: SAMEORIGIN - x-ms-build-version: 1.0.20210520-1-d6634624-develop - x-ms-keyvault-network-info: conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=Ipv4; - x-ms-keyvault-region: centralus - x-ms-server-latency: '2629' - status: - code: 200 - message: OK - url: https://mcpatinotesthsm.managedhsm.azure.net/backup/d9bfeb26511643629937e7626eda6230/pending -- request: - body: null - headers: - User-Agent: - - azsdk-python-keyvault-administration/4.0.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) - method: GET - uri: https://managedhsm/backup/d9bfeb26511643629937e7626eda6230/pending - response: - body: - string: '{"azureStorageBlobContainerUri":"https://storname.blob.core.windows.net/containerbcgbx3fq2eliq2t/mhsm-mcpatinotesthsm-2021061423321511","endTime":1623713544,"error":null,"jobId":"d9bfeb26511643629937e7626eda6230","startTime":1623713535,"status":"Succeeded","statusDetails":null}' - headers: - cache-control: no-cache - content-length: '294' - content-security-policy: default-src 'self' - content-type: application/json; charset=utf-8 - date: Mon, 14 Jun 2021 23:32:29 GMT - server: Kestrel - strict-transport-security: max-age=31536000; includeSubDomains - x-content-type-options: nosniff - x-frame-options: SAMEORIGIN - x-ms-build-version: 1.0.20210520-1-d6634624-develop - x-ms-keyvault-network-info: conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=Ipv4; - x-ms-keyvault-region: centralus - x-ms-server-latency: '2308' - status: - code: 200 - message: OK - url: https://mcpatinotesthsm.managedhsm.azure.net/backup/d9bfeb26511643629937e7626eda6230/pending -- request: - body: null - headers: - User-Agent: - - azsdk-python-keyvault-administration/4.0.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) - method: GET - uri: https://managedhsm/backup/d9bfeb26511643629937e7626eda6230/pending - response: - body: - string: '{"azureStorageBlobContainerUri":"https://storname.blob.core.windows.net/containerbcgbx3fq2eliq2t/mhsm-mcpatinotesthsm-2021061423321511","endTime":1623713544,"error":null,"jobId":"d9bfeb26511643629937e7626eda6230","startTime":1623713535,"status":"Succeeded","statusDetails":null}' - headers: - cache-control: no-cache - content-length: '294' - content-security-policy: default-src 'self' - content-type: application/json; charset=utf-8 - date: Mon, 14 Jun 2021 23:32:36 GMT - server: Kestrel - strict-transport-security: max-age=31536000; includeSubDomains - x-content-type-options: nosniff - x-frame-options: SAMEORIGIN - x-ms-build-version: 1.0.20210520-1-d6634624-develop - x-ms-keyvault-network-info: conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=Ipv4; - x-ms-keyvault-region: centralus - x-ms-server-latency: '2095' - status: - code: 200 - message: OK - url: https://mcpatinotesthsm.managedhsm.azure.net/backup/d9bfeb26511643629937e7626eda6230/pending -- request: - body: null - headers: - User-Agent: - - azsdk-python-keyvault-administration/4.0.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) - method: GET - uri: https://managedhsm/backup/d9bfeb26511643629937e7626eda6230/pending - response: - body: - string: '{"azureStorageBlobContainerUri":"https://storname.blob.core.windows.net/containerbcgbx3fq2eliq2t/mhsm-mcpatinotesthsm-2021061423321511","endTime":1623713544,"error":null,"jobId":"d9bfeb26511643629937e7626eda6230","startTime":1623713535,"status":"Succeeded","statusDetails":null}' - headers: - cache-control: no-cache - content-length: '294' - content-security-policy: default-src 'self' - content-type: application/json; charset=utf-8 - date: Mon, 14 Jun 2021 23:32:38 GMT - server: Kestrel - strict-transport-security: max-age=31536000; includeSubDomains - x-content-type-options: nosniff - x-frame-options: SAMEORIGIN - x-ms-build-version: 1.0.20210520-1-d6634624-develop - x-ms-keyvault-network-info: conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=Ipv4; - x-ms-keyvault-region: centralus - x-ms-server-latency: '2238' - status: - code: 200 - message: OK - url: https://mcpatinotesthsm.managedhsm.azure.net/backup/d9bfeb26511643629937e7626eda6230/pending -- request: - body: null - headers: - User-Agent: - - azsdk-python-keyvault-administration/4.0.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) - method: GET - uri: https://managedhsm/backup/d9bfeb26511643629937e7626eda6230/pending - response: - body: - string: '{"azureStorageBlobContainerUri":"https://storname.blob.core.windows.net/containerbcgbx3fq2eliq2t/mhsm-mcpatinotesthsm-2021061423321511","endTime":1623713544,"error":null,"jobId":"d9bfeb26511643629937e7626eda6230","startTime":1623713535,"status":"Succeeded","statusDetails":null}' - headers: - cache-control: no-cache - content-length: '294' - content-security-policy: default-src 'self' - content-type: application/json; charset=utf-8 - date: Mon, 14 Jun 2021 23:32:40 GMT - server: Kestrel - strict-transport-security: max-age=31536000; includeSubDomains - x-content-type-options: nosniff - x-frame-options: SAMEORIGIN - x-ms-build-version: 1.0.20210520-1-d6634624-develop - x-ms-keyvault-network-info: conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=Ipv4; - x-ms-keyvault-region: centralus - x-ms-server-latency: '2308' - status: - code: 200 - message: OK - url: https://mcpatinotesthsm.managedhsm.azure.net/backup/d9bfeb26511643629937e7626eda6230/pending -- request: - body: '{"sasTokenParameters": {"storageResourceUri": "https://storname.blob.core.windows.net/containerbcgbx3fq2eliq2t", - "token": "redacted"}, "folderToRestore": "mhsm-mcpatinotesthsm-2021061423321511"}' - headers: - Accept: - - application/json - Content-Length: - - '317' - Content-Type: - - application/json - User-Agent: - - azsdk-python-keyvault-administration/4.0.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) - method: PUT - uri: https://managedhsm/restore?api-version=7.2 - response: - body: - string: '{"endTime":null,"error":null,"jobId":"cab800ab01da484daeac156851250628","startTime":1623713563,"status":"InProgress","statusDetails":null}' - headers: - azure-asyncoperation: https://managedhsm/restore/cab800ab01da484daeac156851250628/pending - cache-control: no-cache - content-length: '138' - content-security-policy: default-src 'self' - content-type: application/json; charset=utf-8 - date: Mon, 14 Jun 2021 23:32:43 GMT - server: Kestrel - strict-transport-security: max-age=31536000; includeSubDomains - x-content-type-options: nosniff - x-frame-options: SAMEORIGIN - x-ms-keyvault-network-info: conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=Ipv4; - x-ms-keyvault-region: centralus - x-ms-server-latency: '2385' - status: - code: 202 - message: '' - url: https://mcpatinotesthsm.managedhsm.azure.net/restore?api-version=7.2 -- request: - body: null - headers: - User-Agent: - - azsdk-python-keyvault-administration/4.0.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) - method: GET - uri: https://managedhsm/restore/cab800ab01da484daeac156851250628/pending - response: - body: - string: '{"endTime":null,"error":null,"jobId":"cab800ab01da484daeac156851250628","startTime":1623713563,"status":"InProgress","statusDetails":null}' - headers: - cache-control: no-cache - content-length: '138' - content-security-policy: default-src 'self' - content-type: application/json; charset=utf-8 - date: Mon, 14 Jun 2021 23:32:45 GMT - server: Kestrel - strict-transport-security: max-age=31536000; includeSubDomains - x-content-type-options: nosniff - x-frame-options: SAMEORIGIN - x-ms-build-version: 1.0.20210520-1-d6634624-develop - x-ms-keyvault-network-info: conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=Ipv4; - x-ms-keyvault-region: centralus - x-ms-server-latency: '2161' - status: - code: 200 - message: OK - url: https://mcpatinotesthsm.managedhsm.azure.net/restore/cab800ab01da484daeac156851250628/pending -- request: - body: null - headers: - User-Agent: - - azsdk-python-keyvault-administration/4.0.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) - method: GET - uri: https://managedhsm/restore/cab800ab01da484daeac156851250628/pending - response: - body: - string: '{"endTime":null,"error":null,"jobId":"cab800ab01da484daeac156851250628","startTime":1623713563,"status":"InProgress","statusDetails":null}' - headers: - cache-control: no-cache - content-length: '138' - content-security-policy: default-src 'self' - content-type: application/json; charset=utf-8 - date: Mon, 14 Jun 2021 23:32:48 GMT - server: Kestrel - strict-transport-security: max-age=31536000; includeSubDomains - x-content-type-options: nosniff - x-frame-options: SAMEORIGIN - x-ms-build-version: 1.0.20210520-1-d6634624-develop - x-ms-keyvault-network-info: conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=Ipv4; - x-ms-keyvault-region: centralus - x-ms-server-latency: '2364' - status: - code: 200 - message: OK - url: https://mcpatinotesthsm.managedhsm.azure.net/restore/cab800ab01da484daeac156851250628/pending -- request: - body: null - headers: - User-Agent: - - azsdk-python-keyvault-administration/4.0.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) - method: GET - uri: https://managedhsm/restore/cab800ab01da484daeac156851250628/pending - response: - body: - string: '{"endTime":null,"error":null,"jobId":"cab800ab01da484daeac156851250628","startTime":1623713563,"status":"InProgress","statusDetails":null}' - headers: - cache-control: no-cache - content-length: '138' - content-security-policy: default-src 'self' - content-type: application/json; charset=utf-8 - date: Mon, 14 Jun 2021 23:32:50 GMT - server: Kestrel - strict-transport-security: max-age=31536000; includeSubDomains - x-content-type-options: nosniff - x-frame-options: SAMEORIGIN - x-ms-build-version: 1.0.20210520-1-d6634624-develop - x-ms-keyvault-network-info: conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=Ipv4; - x-ms-keyvault-region: centralus - x-ms-server-latency: '2259' - status: - code: 200 - message: OK - url: https://mcpatinotesthsm.managedhsm.azure.net/restore/cab800ab01da484daeac156851250628/pending -- request: - body: null - headers: - User-Agent: - - azsdk-python-keyvault-administration/4.0.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) - method: GET - uri: https://managedhsm/restore/cab800ab01da484daeac156851250628/pending - response: - body: - string: '{"endTime":null,"error":null,"jobId":"cab800ab01da484daeac156851250628","startTime":1623713563,"status":"InProgress","statusDetails":null}' - headers: - cache-control: no-cache - content-length: '138' - content-security-policy: default-src 'self' - content-type: application/json; charset=utf-8 - date: Mon, 14 Jun 2021 23:32:57 GMT - server: Kestrel - strict-transport-security: max-age=31536000; includeSubDomains - x-content-type-options: nosniff - x-frame-options: SAMEORIGIN - x-ms-build-version: 1.0.20210520-1-d6634624-develop - x-ms-keyvault-network-info: conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=Ipv4; - x-ms-keyvault-region: centralus - x-ms-server-latency: '2175' - status: - code: 200 - message: OK - url: https://mcpatinotesthsm.managedhsm.azure.net/restore/cab800ab01da484daeac156851250628/pending -- request: - body: null - headers: - User-Agent: - - azsdk-python-keyvault-administration/4.0.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) - method: GET - uri: https://managedhsm/restore/cab800ab01da484daeac156851250628/pending - response: - body: - string: '{"endTime":1623713580,"error":null,"jobId":"cab800ab01da484daeac156851250628","startTime":1623713563,"status":"Succeeded","statusDetails":null}' - headers: - cache-control: no-cache - content-length: '143' - content-security-policy: default-src 'self' - content-type: application/json; charset=utf-8 - date: Mon, 14 Jun 2021 23:33:05 GMT - server: Kestrel - strict-transport-security: max-age=31536000; includeSubDomains - x-content-type-options: nosniff - x-frame-options: SAMEORIGIN - x-ms-build-version: 1.0.20210520-1-d6634624-develop - x-ms-keyvault-network-info: conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=Ipv4; - x-ms-keyvault-region: centralus - x-ms-server-latency: '2748' - status: - code: 200 - message: OK - url: https://mcpatinotesthsm.managedhsm.azure.net/restore/cab800ab01da484daeac156851250628/pending -- request: - body: null - headers: - User-Agent: - - azsdk-python-keyvault-administration/4.0.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) - method: GET - uri: https://managedhsm/restore/cab800ab01da484daeac156851250628/pending - response: - body: - string: '{"endTime":1623713580,"error":null,"jobId":"cab800ab01da484daeac156851250628","startTime":1623713563,"status":"Succeeded","statusDetails":null}' - headers: - cache-control: no-cache - content-length: '143' - content-security-policy: default-src 'self' - content-type: application/json; charset=utf-8 - date: Mon, 14 Jun 2021 23:33:13 GMT - server: Kestrel - strict-transport-security: max-age=31536000; includeSubDomains - x-content-type-options: nosniff - x-frame-options: SAMEORIGIN - x-ms-build-version: 1.0.20210520-1-d6634624-develop - x-ms-keyvault-network-info: conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=Ipv4; - x-ms-keyvault-region: centralus - x-ms-server-latency: '2176' - status: - code: 200 - message: OK - url: https://mcpatinotesthsm.managedhsm.azure.net/restore/cab800ab01da484daeac156851250628/pending -version: 1 diff --git a/sdk/keyvault/azure-keyvault-administration/tests/recordings/test_backup_client_async.test_full_backup_and_restore_rehydration.yaml b/sdk/keyvault/azure-keyvault-administration/tests/recordings/test_backup_client_async.test_full_backup_and_restore_rehydration.yaml deleted file mode 100644 index 35ec88ed94e3..000000000000 --- a/sdk/keyvault/azure-keyvault-administration/tests/recordings/test_backup_client_async.test_full_backup_and_restore_rehydration.yaml +++ /dev/null @@ -1,299 +0,0 @@ -interactions: -- request: - body: null - headers: - Accept: - - application/json - Content-Length: - - '0' - Content-Type: - - application/json - User-Agent: - - azsdk-python-keyvault-administration/4.0.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) - method: POST - uri: https://managedhsm/backup?api-version=7.2 - response: - body: - string: '' - headers: - cache-control: no-cache - content-length: '0' - content-security-policy: default-src 'self' - content-type: application/json; charset=utf-8 - strict-transport-security: max-age=31536000; includeSubDomains - www-authenticate: Bearer authorization="https://login.microsoftonline.com/00000000-0000-0000-0000-000000000000", - resource="https://managedhsm.azure.net" - x-content-type-options: nosniff - x-frame-options: SAMEORIGIN - x-ms-server-latency: '1' - status: - code: 401 - message: Unauthorized - url: https://mcpatinotesthsm.managedhsm.azure.net/backup?api-version=7.2 -- request: - body: '{"storageResourceUri": "https://storname.blob.core.windows.net/containerijaxozkkwimtf7e", - "token": "redacted"}' - headers: - Accept: - - application/json - Content-Length: - - '233' - Content-Type: - - application/json - User-Agent: - - azsdk-python-keyvault-administration/4.0.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) - method: POST - uri: https://managedhsm/backup?api-version=7.2 - response: - body: - string: '{"status":"InProgress","statusDetails":null,"error":null,"startTime":1623712397,"endTime":null,"jobId":"ce8d058c198e4ed68c88c75abc4f22b0","azureStorageBlobContainerUri":null}' - headers: - azure-asyncoperation: https://managedhsm/backup/ce8d058c198e4ed68c88c75abc4f22b0/pending - cache-control: no-cache - content-length: '174' - content-security-policy: default-src 'self' - content-type: application/json; charset=utf-8 - date: Mon, 14 Jun 2021 23:13:17 GMT - server: Kestrel - strict-transport-security: max-age=31536000; includeSubDomains - x-content-type-options: nosniff - x-frame-options: SAMEORIGIN - x-ms-keyvault-network-info: conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=Ipv4; - x-ms-keyvault-region: centralus - x-ms-server-latency: '2530' - status: - code: 202 - message: '' - url: https://mcpatinotesthsm.managedhsm.azure.net/backup?api-version=7.2 -- request: - body: null - headers: - User-Agent: - - azsdk-python-keyvault-administration/4.0.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) - method: GET - uri: https://managedhsm/backup/ce8d058c198e4ed68c88c75abc4f22b0/pending - response: - body: - string: '{"azureStorageBlobContainerUri":null,"endTime":null,"error":null,"jobId":"ce8d058c198e4ed68c88c75abc4f22b0","startTime":1623712397,"status":"InProgress","statusDetails":null}' - headers: - cache-control: no-cache - content-length: '174' - content-security-policy: default-src 'self' - content-type: application/json; charset=utf-8 - date: Mon, 14 Jun 2021 23:13:19 GMT - server: Kestrel - strict-transport-security: max-age=31536000; includeSubDomains - x-content-type-options: nosniff - x-frame-options: SAMEORIGIN - x-ms-build-version: 1.0.20210520-1-d6634624-develop - x-ms-keyvault-network-info: conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=Ipv4; - x-ms-keyvault-region: centralus - x-ms-server-latency: '2061' - status: - code: 200 - message: OK - url: https://mcpatinotesthsm.managedhsm.azure.net/backup/ce8d058c198e4ed68c88c75abc4f22b0/pending -- request: - body: null - headers: - User-Agent: - - azsdk-python-keyvault-administration/4.0.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) - method: GET - uri: https://managedhsm/backup/ce8d058c198e4ed68c88c75abc4f22b0/pending - response: - body: - string: '{"azureStorageBlobContainerUri":"https://storname.blob.core.windows.net/containerijaxozkkwimtf7e/mhsm-mcpatinotesthsm-2021061423131779","endTime":1623712406,"error":null,"jobId":"ce8d058c198e4ed68c88c75abc4f22b0","startTime":1623712397,"status":"Succeeded","statusDetails":null}' - headers: - cache-control: no-cache - content-length: '294' - content-security-policy: default-src 'self' - content-type: application/json; charset=utf-8 - date: Mon, 14 Jun 2021 23:13:26 GMT - server: Kestrel - strict-transport-security: max-age=31536000; includeSubDomains - x-content-type-options: nosniff - x-frame-options: SAMEORIGIN - x-ms-build-version: 1.0.20210520-1-d6634624-develop - x-ms-keyvault-network-info: conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=Ipv4; - x-ms-keyvault-region: centralus - x-ms-server-latency: '2198' - status: - code: 200 - message: OK - url: https://mcpatinotesthsm.managedhsm.azure.net/backup/ce8d058c198e4ed68c88c75abc4f22b0/pending -- request: - body: null - headers: - User-Agent: - - azsdk-python-keyvault-administration/4.0.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) - method: GET - uri: https://managedhsm/backup/ce8d058c198e4ed68c88c75abc4f22b0/pending - response: - body: - string: '{"azureStorageBlobContainerUri":"https://storname.blob.core.windows.net/containerijaxozkkwimtf7e/mhsm-mcpatinotesthsm-2021061423131779","endTime":1623712406,"error":null,"jobId":"ce8d058c198e4ed68c88c75abc4f22b0","startTime":1623712397,"status":"Succeeded","statusDetails":null}' - headers: - cache-control: no-cache - content-length: '294' - content-security-policy: default-src 'self' - content-type: application/json; charset=utf-8 - date: Mon, 14 Jun 2021 23:13:39 GMT - server: Kestrel - strict-transport-security: max-age=31536000; includeSubDomains - x-content-type-options: nosniff - x-frame-options: SAMEORIGIN - x-ms-build-version: 1.0.20210520-1-d6634624-develop - x-ms-keyvault-network-info: conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=Ipv4; - x-ms-keyvault-region: centralus - x-ms-server-latency: '2196' - status: - code: 200 - message: OK - url: https://mcpatinotesthsm.managedhsm.azure.net/backup/ce8d058c198e4ed68c88c75abc4f22b0/pending -- request: - body: '{"sasTokenParameters": {"storageResourceUri": "https://storname.blob.core.windows.net/containerijaxozkkwimtf7e", - "token": "redacted"}, "folderToRestore": "mhsm-mcpatinotesthsm-2021061423131779"}' - headers: - Accept: - - application/json - Content-Length: - - '317' - Content-Type: - - application/json - User-Agent: - - azsdk-python-keyvault-administration/4.0.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) - method: PUT - uri: https://managedhsm/restore?api-version=7.2 - response: - body: - string: '{"endTime":null,"error":null,"jobId":"b9aeb3b6db9c46c4bb0038a0bac8df0d","startTime":1623712422,"status":"InProgress","statusDetails":null}' - headers: - azure-asyncoperation: https://managedhsm/restore/b9aeb3b6db9c46c4bb0038a0bac8df0d/pending - cache-control: no-cache - content-length: '138' - content-security-policy: default-src 'self' - content-type: application/json; charset=utf-8 - date: Mon, 14 Jun 2021 23:13:41 GMT - server: Kestrel - strict-transport-security: max-age=31536000; includeSubDomains - x-content-type-options: nosniff - x-frame-options: SAMEORIGIN - x-ms-keyvault-network-info: conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=Ipv4; - x-ms-keyvault-region: centralus - x-ms-server-latency: '2509' - status: - code: 202 - message: '' - url: https://mcpatinotesthsm.managedhsm.azure.net/restore?api-version=7.2 -- request: - body: null - headers: - User-Agent: - - azsdk-python-keyvault-administration/4.0.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) - method: GET - uri: https://managedhsm/restore/b9aeb3b6db9c46c4bb0038a0bac8df0d/pending - response: - body: - string: '{"endTime":null,"error":null,"jobId":"b9aeb3b6db9c46c4bb0038a0bac8df0d","startTime":1623712422,"status":"InProgress","statusDetails":null}' - headers: - cache-control: no-cache - content-length: '138' - content-security-policy: default-src 'self' - content-type: application/json; charset=utf-8 - date: Mon, 14 Jun 2021 23:13:44 GMT - server: Kestrel - strict-transport-security: max-age=31536000; includeSubDomains - x-content-type-options: nosniff - x-frame-options: SAMEORIGIN - x-ms-build-version: 1.0.20210520-1-d6634624-develop - x-ms-keyvault-network-info: conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=Ipv4; - x-ms-keyvault-region: centralus - x-ms-server-latency: '2065' - status: - code: 200 - message: OK - url: https://mcpatinotesthsm.managedhsm.azure.net/restore/b9aeb3b6db9c46c4bb0038a0bac8df0d/pending -- request: - body: null - headers: - User-Agent: - - azsdk-python-keyvault-administration/4.0.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) - method: GET - uri: https://managedhsm/restore/b9aeb3b6db9c46c4bb0038a0bac8df0d/pending - response: - body: - string: '{"endTime":null,"error":null,"jobId":"b9aeb3b6db9c46c4bb0038a0bac8df0d","startTime":1623712422,"status":"InProgress","statusDetails":null}' - headers: - cache-control: no-cache - content-length: '138' - content-security-policy: default-src 'self' - content-type: application/json; charset=utf-8 - date: Mon, 14 Jun 2021 23:13:51 GMT - server: Kestrel - strict-transport-security: max-age=31536000; includeSubDomains - x-content-type-options: nosniff - x-frame-options: SAMEORIGIN - x-ms-build-version: 1.0.20210520-1-d6634624-develop - x-ms-keyvault-network-info: conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=Ipv4; - x-ms-keyvault-region: centralus - x-ms-server-latency: '2252' - status: - code: 200 - message: OK - url: https://mcpatinotesthsm.managedhsm.azure.net/restore/b9aeb3b6db9c46c4bb0038a0bac8df0d/pending -- request: - body: null - headers: - User-Agent: - - azsdk-python-keyvault-administration/4.0.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) - method: GET - uri: https://managedhsm/restore/b9aeb3b6db9c46c4bb0038a0bac8df0d/pending - response: - body: - string: '{"endTime":1623712438,"error":null,"jobId":"b9aeb3b6db9c46c4bb0038a0bac8df0d","startTime":1623712422,"status":"Succeeded","statusDetails":null}' - headers: - cache-control: no-cache - content-length: '143' - content-security-policy: default-src 'self' - content-type: application/json; charset=utf-8 - date: Mon, 14 Jun 2021 23:13:58 GMT - server: Kestrel - strict-transport-security: max-age=31536000; includeSubDomains - x-content-type-options: nosniff - x-frame-options: SAMEORIGIN - x-ms-build-version: 1.0.20210520-1-d6634624-develop - x-ms-keyvault-network-info: conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=Ipv4; - x-ms-keyvault-region: centralus - x-ms-server-latency: '2281' - status: - code: 200 - message: OK - url: https://mcpatinotesthsm.managedhsm.azure.net/restore/b9aeb3b6db9c46c4bb0038a0bac8df0d/pending -- request: - body: null - headers: - User-Agent: - - azsdk-python-keyvault-administration/4.0.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) - method: GET - uri: https://managedhsm/restore/b9aeb3b6db9c46c4bb0038a0bac8df0d/pending - response: - body: - string: '{"endTime":1623712438,"error":null,"jobId":"b9aeb3b6db9c46c4bb0038a0bac8df0d","startTime":1623712422,"status":"Succeeded","statusDetails":null}' - headers: - cache-control: no-cache - content-length: '143' - content-security-policy: default-src 'self' - content-type: application/json; charset=utf-8 - date: Mon, 14 Jun 2021 23:14:10 GMT - server: Kestrel - strict-transport-security: max-age=31536000; includeSubDomains - x-content-type-options: nosniff - x-frame-options: SAMEORIGIN - x-ms-build-version: 1.0.20210520-1-d6634624-develop - x-ms-keyvault-network-info: conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=Ipv4; - x-ms-keyvault-region: centralus - x-ms-server-latency: '2140' - status: - code: 200 - message: OK - url: https://mcpatinotesthsm.managedhsm.azure.net/restore/b9aeb3b6db9c46c4bb0038a0bac8df0d/pending -version: 1 diff --git a/sdk/keyvault/azure-keyvault-administration/tests/test_backup_client.py b/sdk/keyvault/azure-keyvault-administration/tests/test_backup_client.py index 87e2d12a867c..a0e3e4ddfdac 100644 --- a/sdk/keyvault/azure-keyvault-administration/tests/test_backup_client.py +++ b/sdk/keyvault/azure-keyvault-administration/tests/test_backup_client.py @@ -2,8 +2,6 @@ # Copyright (c) Microsoft Corporation. # Licensed under the MIT License. # ------------------------------------ -import base64 -from datetime import datetime from functools import partial import time @@ -12,7 +10,7 @@ from azure.identity import DefaultAzureCredential from azure.keyvault.keys import KeyClient from azure.keyvault.administration._internal import HttpChallengeCache -from azure.keyvault.administration import KeyVaultBackupClient, KeyVaultBackupOperation +from azure.keyvault.administration import KeyVaultBackupClient from azure.keyvault.administration._internal import parse_folder_url from devtools_testutils import ResourceGroupPreparer, StorageAccountPreparer import pytest @@ -43,15 +41,7 @@ def tearDown(self): @property def credential(self): if self.is_live: - from dotenv import load_dotenv - from azure.identity import ClientSecretCredential - import os - load_dotenv() - return ClientSecretCredential( - tenant_id=os.environ["KEYVAULT_TENANT_ID"], - client_id=os.environ["KEYVAULT_CLIENT_ID"], - client_secret=os.environ["KEYVAULT_CLIENT_SECRET"] - ) + return DefaultAzureCredential() return mock.Mock(get_token=lambda *_, **__: AccessToken("secret", time.time() + 3600)) @ResourceGroupPreparer(random_name_enabled=True, use_cache=True) @@ -62,6 +52,7 @@ def test_full_backup_and_restore(self, container_uri, sas_token): backup_client = KeyVaultBackupClient(self.managed_hsm["url"], self.credential) backup_poller = backup_client.begin_backup(container_uri, sas_token) backup_operation = backup_poller.result() + assert backup_operation.folder_url # restore the backup restore_poller = backup_client.begin_restore(backup_operation.folder_url, sas_token) @@ -72,29 +63,29 @@ def test_full_backup_and_restore(self, container_uri, sas_token): @BlobContainerPreparer() def test_full_backup_and_restore_rehydration(self, container_uri, sas_token): if not self.is_live: - pytest.skip("Poller requests are incompatible with vcrpy for synchronous tests") + pytest.skip("Poller requests are incompatible with vcrpy in playback") # backup the vault backup_client = KeyVaultBackupClient(self.managed_hsm["url"], self.credential) backup_poller = backup_client.begin_backup(container_uri, sas_token) # create a new poller from a continuation token - token = backup_poller._polling_method.get_continuation_token() + token = backup_poller.polling_method().get_continuation_token() rehydrated = backup_client.begin_backup(container_uri, sas_token, continuation_token=token) - backup_operation = rehydrated.result() - assert backup_operation.folder_url - backup_poller.wait() + rehydrated_operation = rehydrated.result() + assert rehydrated_operation.folder_url + backup_operation = backup_poller.result() + assert backup_operation.folder_url == rehydrated_operation.folder_url # restore the backup restore_poller = backup_client.begin_restore(backup_operation.folder_url, sas_token) # create a new poller from a continuation token - token = restore_poller._polling_method.get_continuation_token() + token = restore_poller.polling_method().get_continuation_token() rehydrated = backup_client.begin_restore(backup_operation.folder_url, sas_token, continuation_token=token) - rehydrated.result() - assert rehydrated.status() == "Succeeded" and rehydrated.polling_method().status() == "Succeeded" + rehydrated.wait() restore_poller.wait() @ResourceGroupPreparer(random_name_enabled=True, use_cache=True) @@ -126,14 +117,14 @@ def test_selective_key_restore(self, container_uri, sas_token): @BlobContainerPreparer() def test_backup_client_polling(self, container_uri, sas_token): if not self.is_live: - pytest.skip("Poller requests are incompatible with vcrpy for synchronous tests") + pytest.skip("Poller requests are incompatible with vcrpy in playback") # backup the vault backup_client = KeyVaultBackupClient(self.managed_hsm["url"], self.credential) backup_poller = backup_client.begin_backup(container_uri, sas_token) # create a new poller from a continuation token - token = backup_poller._polling_method.get_continuation_token() + token = backup_poller.polling_method().get_continuation_token() rehydrated = backup_client.begin_backup(container_uri, sas_token, continuation_token=token) # check that pollers and polling methods behave as expected @@ -163,7 +154,7 @@ def test_backup_client_polling(self, container_uri, sas_token): restore_poller = backup_client.begin_restore(backup_operation.folder_url, sas_token) # create a new poller from a continuation token - token = restore_poller._polling_method.get_continuation_token() + token = restore_poller.polling_method().get_continuation_token() rehydrated = backup_client.begin_restore(backup_operation.folder_url, sas_token, continuation_token=token) # check that pollers and polling methods behave as expected @@ -177,9 +168,9 @@ def test_backup_client_polling(self, container_uri, sas_token): restore_poller.polling_method().update_status() assert restore_poller.status() == "InProgress" - rehydrated.result() + rehydrated.wait() assert rehydrated.status() == "Succeeded" and rehydrated.polling_method().status() == "Succeeded" - restore_poller.result() + restore_poller.wait() assert restore_poller.status() == "Succeeded" and restore_poller.polling_method().status() == "Succeeded" diff --git a/sdk/keyvault/azure-keyvault-administration/tests/test_backup_client_async.py b/sdk/keyvault/azure-keyvault-administration/tests/test_backup_client_async.py index 4ee7dda2113e..26f58f3d442e 100644 --- a/sdk/keyvault/azure-keyvault-administration/tests/test_backup_client_async.py +++ b/sdk/keyvault/azure-keyvault-administration/tests/test_backup_client_async.py @@ -15,7 +15,6 @@ from devtools_testutils import ResourceGroupPreparer, StorageAccountPreparer import pytest -from _shared.helpers_async import get_completed_future from _shared.test_case_async import KeyVaultTestCase from blob_container_preparer import BlobContainerPreparer @@ -40,15 +39,7 @@ def tearDown(self): @property def credential(self): if self.is_live: - from dotenv import load_dotenv - from azure.identity.aio import ClientSecretCredential - import os - load_dotenv() - return ClientSecretCredential( - tenant_id=os.environ["KEYVAULT_TENANT_ID"], - client_id=os.environ["KEYVAULT_CLIENT_ID"], - client_secret=os.environ["KEYVAULT_CLIENT_SECRET"] - ) + return DefaultAzureCredential() async def get_token(*_, **__): return AccessToken("secret", time.time() + 3600) @@ -63,29 +54,41 @@ async def test_full_backup_and_restore(self, container_uri, sas_token): backup_client = KeyVaultBackupClient(self.managed_hsm["url"], self.credential) backup_poller = await backup_client.begin_backup(container_uri, sas_token) backup_operation = await backup_poller.result() + assert backup_operation.folder_url # restore the backup restore_poller = await backup_client.begin_restore(backup_operation.folder_url, sas_token) + await restore_poller.wait() + + @ResourceGroupPreparer(random_name_enabled=True, use_cache=True) + @StorageAccountPreparer(random_name_enabled=True) + @BlobContainerPreparer() + async def test_full_backup_and_restore_rehydration(self, container_uri, sas_token): + if not self.is_live: + pytest.skip("Poller requests are incompatible with vcrpy in playback") + + # backup the vault + backup_client = KeyVaultBackupClient(self.managed_hsm["url"], self.credential) + backup_poller = await backup_client.begin_backup(container_uri, sas_token) # create a new poller from a continuation token - token = restore_poller._polling_method.get_continuation_token() - rehydrated = await backup_client.begin_restore(backup_operation.folder_url, sas_token, continuation_token=token) + token = backup_poller.polling_method().get_continuation_token() + rehydrated = await backup_client.begin_backup(container_uri, sas_token, continuation_token=token) - # check that pollers and polling methods behave as expected - assert restore_poller.status() == "InProgress" - assert not restore_poller.done() or restore_poller.polling_method().finished() - assert rehydrated.status() == "InProgress" - assert not rehydrated.done() or rehydrated.polling_method().finished() + rehydrated_operation = await rehydrated.result() + assert rehydrated_operation.folder_url + backup_operation = await backup_poller.result() + assert backup_operation.folder_url == rehydrated_operation.folder_url - await rehydrated.polling_method().update_status() - assert rehydrated.status() == "InProgress" - await restore_poller.polling_method().update_status() - assert restore_poller.status() == "InProgress" + # restore the backup + restore_poller = await backup_client.begin_restore(backup_operation.folder_url, sas_token) - await rehydrated.result() - assert rehydrated.status() == "Succeeded" and rehydrated.polling_method().status() == "Succeeded" - await restore_poller.result() - assert restore_poller.status() == "Succeeded" and restore_poller.polling_method().status() == "Succeeded" + # create a new poller from a continuation token + token = restore_poller.polling_method().get_continuation_token() + rehydrated = await backup_client.begin_restore(backup_operation.folder_url, sas_token, continuation_token=token) + + await rehydrated.wait() + await restore_poller.wait() @ResourceGroupPreparer(random_name_enabled=True, use_cache=True) @StorageAccountPreparer(random_name_enabled=True) @@ -113,6 +116,9 @@ async def test_selective_key_restore(self, container_uri, sas_token): @StorageAccountPreparer(random_name_enabled=True) @BlobContainerPreparer() async def test_backup_client_polling(self, container_uri, sas_token): + if not self.is_live: + pytest.skip("Poller requests are incompatible with vcrpy in playback") + # backup the vault backup_client = KeyVaultBackupClient(self.managed_hsm["url"], self.credential) backup_poller = await backup_client.begin_backup(container_uri, sas_token) @@ -166,3 +172,20 @@ async def test_backup_client_polling(self, container_uri, sas_token): assert rehydrated.status() == "Succeeded" and rehydrated.polling_method().status() == "Succeeded" await restore_poller.wait() assert restore_poller.status() == "Succeeded" and restore_poller.polling_method().status() == "Succeeded" + + + # check that pollers and polling methods behave as expected + assert restore_poller.status() == "InProgress" + assert not restore_poller.done() or restore_poller.polling_method().finished() + assert rehydrated.status() == "InProgress" + assert not rehydrated.done() or rehydrated.polling_method().finished() + + await rehydrated.polling_method().update_status() + assert rehydrated.status() == "InProgress" + await restore_poller.polling_method().update_status() + assert restore_poller.status() == "InProgress" + + await rehydrated.wait() + assert rehydrated.status() == "Succeeded" and rehydrated.polling_method().status() == "Succeeded" + await restore_poller.wait() + assert restore_poller.status() == "Succeeded" and restore_poller.polling_method().status() == "Succeeded" diff --git a/sdk/keyvault/azure-keyvault-administration/tests/test_examples_administration.py b/sdk/keyvault/azure-keyvault-administration/tests/test_examples_administration.py new file mode 100644 index 000000000000..8a298d38f9ee --- /dev/null +++ b/sdk/keyvault/azure-keyvault-administration/tests/test_examples_administration.py @@ -0,0 +1,79 @@ +# ------------------------------------ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# ------------------------------------ +import time + +from azure.core.credentials import AccessToken +from azure.identity import DefaultAzureCredential +from azure.keyvault.administration._internal import HttpChallengeCache +from azure.keyvault.administration import KeyVaultBackupClient +from devtools_testutils import ResourceGroupPreparer, StorageAccountPreparer +import pytest +from six.moves.urllib_parse import urlparse + +from _shared.helpers import mock +from _shared.test_case import KeyVaultTestCase +from blob_container_preparer import BlobContainerPreparer + + +@pytest.mark.usefixtures("managed_hsm") +class TestExamplesTests(KeyVaultTestCase): + def __init__(self, *args, **kwargs): + super(TestExamplesTests, self).__init__(*args, match_body=False, **kwargs) + + def setUp(self, *args, **kwargs): + if self.is_live: + real = urlparse(self.managed_hsm["url"]) + playback = urlparse(self.managed_hsm["playback_url"]) + self.scrubber.register_name_pair(real.netloc, playback.netloc) + super(TestExamplesTests, self).setUp(*args, **kwargs) + + def tearDown(self): + HttpChallengeCache.clear() + assert len(HttpChallengeCache._cache) == 0 + super(KeyVaultTestCase, self).tearDown() + + @property + def credential(self): + if self.is_live: + return DefaultAzureCredential() + return mock.Mock(get_token=lambda *_, **__: AccessToken("secret", time.time() + 3600)) + + @ResourceGroupPreparer(random_name_enabled=True, use_cache=True) + @StorageAccountPreparer(random_name_enabled=True) + @BlobContainerPreparer() + def test_example_backup_and_restore(self, container_uri, sas_token): + if not self.is_live: + pytest.skip("Poller requests are incompatible with vcrpy in playback") + + backup_client = KeyVaultBackupClient(self.managed_hsm["url"], self.credential) + + # [START backup_vault] + # begin a vault backup + backup_poller = backup_client.begin_backup(container_uri, sas_token) + + # to create a new poller for the operation, use a continuation token + token = backup_poller.polling_method().get_continuation_token() + new_poller = backup_client.begin_backup(container_uri, sas_token, continuation_token=token) + + # get the final result + backup_operation = backup_poller.result() + # [END backup_vault] + + new_poller.wait() + folder_url = backup_operation.folder_url + + # [START restore_vault] + # begin a full vault restore; to restore a single key, use the key_name kwarg + restore_poller = backup_client.begin_restore(folder_url, sas_token) + + # to create a new poller for the operation, use a continuation token + token = restore_poller.polling_method().get_continuation_token() + new_poller = backup_client.begin_restore(folder_url, sas_token, continuation_token=token) + + # wait for the restore to complete + restore_poller.wait() + # [END restore_vault] + + new_poller.wait() diff --git a/sdk/keyvault/azure-keyvault-administration/tests/test_examples_administration_async.py b/sdk/keyvault/azure-keyvault-administration/tests/test_examples_administration_async.py new file mode 100644 index 000000000000..55c434b5f948 --- /dev/null +++ b/sdk/keyvault/azure-keyvault-administration/tests/test_examples_administration_async.py @@ -0,0 +1,83 @@ +# ------------------------------------ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# ------------------------------------ +import time +from unittest import mock +from urllib.parse import urlparse + +from azure.core.credentials import AccessToken +from azure.identity.aio import DefaultAzureCredential +from azure.keyvault.administration._internal import HttpChallengeCache +from azure.keyvault.administration.aio import KeyVaultBackupClient +from devtools_testutils import ResourceGroupPreparer, StorageAccountPreparer +import pytest + +from _shared.test_case_async import KeyVaultTestCase +from blob_container_preparer import BlobContainerPreparer + + +@pytest.mark.usefixtures("managed_hsm") +class TestExamplesTests(KeyVaultTestCase): + def __init__(self, *args, **kwargs): + super().__init__(*args, match_body=False, **kwargs) + + def setUp(self, *args, **kwargs): + if self.is_live: + real = urlparse(self.managed_hsm["url"]) + playback = urlparse(self.managed_hsm["playback_url"]) + self.scrubber.register_name_pair(real.netloc, playback.netloc) + super().setUp(*args, **kwargs) + + def tearDown(self): + HttpChallengeCache.clear() + assert len(HttpChallengeCache._cache) == 0 + super(KeyVaultTestCase, self).tearDown() + + @property + def credential(self): + if self.is_live: + return DefaultAzureCredential() + + async def get_token(*_, **__): + return AccessToken("secret", time.time() + 3600) + + return mock.Mock(get_token=get_token) + + @ResourceGroupPreparer(random_name_enabled=True, use_cache=True) + @StorageAccountPreparer(random_name_enabled=True) + @BlobContainerPreparer() + async def test_example_backup_and_restore(self, container_uri, sas_token): + if not self.is_live: + pytest.skip("Poller requests are incompatible with vcrpy in playback") + + backup_client = KeyVaultBackupClient(self.managed_hsm["url"], self.credential) + + # [START backup_vault] + # begin a vault backup + backup_poller = await backup_client.begin_backup(container_uri, sas_token) + + # to create a new poller for the operation, use a continuation token + token = backup_poller.polling_method().get_continuation_token() + new_poller = await backup_client.begin_backup(container_uri, sas_token, continuation_token=token) + + # get the final result + backup_operation = await backup_poller.result() + # [END backup_vault] + + await new_poller.wait() + folder_url = backup_operation.folder_url + + # [START restore_vault] + # begin a full vault restore; to restore a single key, use the key_name kwarg + restore_poller = await backup_client.begin_restore(folder_url, sas_token) + + # to create a new poller for the operation, use a continuation token + token = restore_poller.polling_method().get_continuation_token() + new_poller = await backup_client.begin_restore(folder_url, sas_token, continuation_token=token) + + # wait for the restore to complete + await restore_poller.wait() + # [END restore_vault] + + await new_poller.wait() From 0eff710f3f3adea10c46d5b5dd31629eb4d90d99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?McCoy=20Pati=C3=B1o?= Date: Mon, 14 Jun 2021 17:35:30 -0700 Subject: [PATCH 18/27] examples in docstrings --- .../keyvault/administration/_backup_client.py | 16 ++++++++++++++++ .../administration/aio/_backup_client.py | 8 ++++---- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_backup_client.py b/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_backup_client.py index 4121d02e8a27..7ecb48b9d5ce 100644 --- a/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_backup_client.py +++ b/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_backup_client.py @@ -46,6 +46,14 @@ def begin_backup(self, blob_storage_url, sas_token, **kwargs): :returns: An :class:`~azure.core.polling.LROPoller` instance. Call `result()` on this object to wait for the operation to complete and get a :class:`KeyVaultBackupOperation`. :rtype: ~azure.core.polling.LROPoller[~azure.keyvault.administration.KeyVaultBackupOperation] + + Example: + .. literalinclude:: ../tests/test_examples_administration.py + :start-after: [START begin_backup] + :end-before: [END begin_backup] + :language: python + :caption: Start a vault backup operation + :dedent: 8 """ polling_interval = kwargs.pop("_polling_interval", 5) sas_parameter = self._models.SASTokenParameter(storage_resource_uri=blob_storage_url, token=sas_token) @@ -79,6 +87,14 @@ def begin_restore(self, folder_url, sas_token, **kwargs): :keyword str continuation_token: a continuation token to restart polling from a saved state :keyword str key_name: name of a single key in the backup. When set, only this key will be restored. :rtype: ~azure.core.polling.LROPoller + + Example: + .. literalinclude:: ../tests/test_examples_administration.py + :start-after: [START begin_restore] + :end-before: [END begin_restore] + :language: python + :caption: Start a vault restore operation + :dedent: 8 """ # LROBasePolling passes its kwargs to pipeline.run(), so we remove unexpected args before constructing it continuation_token = kwargs.pop("continuation_token", None) diff --git a/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/aio/_backup_client.py b/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/aio/_backup_client.py index f600cc4f1b92..fc55dcc26fa5 100644 --- a/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/aio/_backup_client.py +++ b/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/aio/_backup_client.py @@ -90,11 +90,11 @@ async def begin_restore(self, folder_url: str, sas_token: str, **kwargs: "Any") :rtype: ~azure.core.polling.AsyncLROPoller Example: - .. literalinclude:: ../tests/test_examples_administration.py - :start-after: [START begin_backup] - :end-before: [END begin_backup] + .. literalinclude:: ../tests/test_examples_administration_async.py + :start-after: [START begin_restore] + :end-before: [END begin_restore] :language: python - :caption: Start a vault backup operation + :caption: Start a vault restore operation :dedent: 8 """ # AsyncLROBasePolling passes its kwargs to pipeline.run(), so we remove unexpected args before constructing it From 20a300c6e69bf58b2595817c9078b59c7ea59a40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?McCoy=20Pati=C3=B1o?= Date: Mon, 14 Jun 2021 18:24:21 -0700 Subject: [PATCH 19/27] doc and pylint fixes --- .../keyvault/administration/_backup_client.py | 2 ++ .../keyvault/administration/aio/_backup_client.py | 2 ++ .../tests/test_examples_administration.py | 14 ++++++++++---- .../tests/test_examples_administration_async.py | 14 ++++++++++---- 4 files changed, 24 insertions(+), 8 deletions(-) diff --git a/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_backup_client.py b/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_backup_client.py index 7ecb48b9d5ce..340d6a81296f 100644 --- a/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_backup_client.py +++ b/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_backup_client.py @@ -16,6 +16,8 @@ from typing import Any from azure.core.polling import LROPoller +# pylint: disable=W0212 + def _get_status_from_continuation_token(token, pipeline_client): continuation_url = base64.b64decode(token.encode()).decode("ascii") diff --git a/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/aio/_backup_client.py b/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/aio/_backup_client.py index fc55dcc26fa5..f4e6ccbb739e 100644 --- a/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/aio/_backup_client.py +++ b/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/aio/_backup_client.py @@ -17,6 +17,8 @@ from typing import Any from azure.core.polling import AsyncLROPoller +# pylint: disable=W0212 + async def _get_status_from_continuation_token(token, pipeline_client): continuation_url = base64.b64decode(token.encode()).decode("ascii") diff --git a/sdk/keyvault/azure-keyvault-administration/tests/test_examples_administration.py b/sdk/keyvault/azure-keyvault-administration/tests/test_examples_administration.py index 8a298d38f9ee..ec50a1a3ed27 100644 --- a/sdk/keyvault/azure-keyvault-administration/tests/test_examples_administration.py +++ b/sdk/keyvault/azure-keyvault-administration/tests/test_examples_administration.py @@ -49,7 +49,7 @@ def test_example_backup_and_restore(self, container_uri, sas_token): backup_client = KeyVaultBackupClient(self.managed_hsm["url"], self.credential) - # [START backup_vault] + # [START begin_backup] # begin a vault backup backup_poller = backup_client.begin_backup(container_uri, sas_token) @@ -57,14 +57,17 @@ def test_example_backup_and_restore(self, container_uri, sas_token): token = backup_poller.polling_method().get_continuation_token() new_poller = backup_client.begin_backup(container_uri, sas_token, continuation_token=token) + # check if the backup completed + done = new_poller.done() + # get the final result backup_operation = backup_poller.result() - # [END backup_vault] + # [END begin_backup] new_poller.wait() folder_url = backup_operation.folder_url - # [START restore_vault] + # [START begin_restore] # begin a full vault restore; to restore a single key, use the key_name kwarg restore_poller = backup_client.begin_restore(folder_url, sas_token) @@ -72,8 +75,11 @@ def test_example_backup_and_restore(self, container_uri, sas_token): token = restore_poller.polling_method().get_continuation_token() new_poller = backup_client.begin_restore(folder_url, sas_token, continuation_token=token) + # check if the restore completed + done = new_poller.done() + # wait for the restore to complete restore_poller.wait() - # [END restore_vault] + # [END begin_restore] new_poller.wait() diff --git a/sdk/keyvault/azure-keyvault-administration/tests/test_examples_administration_async.py b/sdk/keyvault/azure-keyvault-administration/tests/test_examples_administration_async.py index 55c434b5f948..d9ea11022050 100644 --- a/sdk/keyvault/azure-keyvault-administration/tests/test_examples_administration_async.py +++ b/sdk/keyvault/azure-keyvault-administration/tests/test_examples_administration_async.py @@ -53,7 +53,7 @@ async def test_example_backup_and_restore(self, container_uri, sas_token): backup_client = KeyVaultBackupClient(self.managed_hsm["url"], self.credential) - # [START backup_vault] + # [START begin_backup] # begin a vault backup backup_poller = await backup_client.begin_backup(container_uri, sas_token) @@ -61,14 +61,17 @@ async def test_example_backup_and_restore(self, container_uri, sas_token): token = backup_poller.polling_method().get_continuation_token() new_poller = await backup_client.begin_backup(container_uri, sas_token, continuation_token=token) + # check if the backup completed + done = new_poller.done() + # get the final result backup_operation = await backup_poller.result() - # [END backup_vault] + # [END begin_backup] await new_poller.wait() folder_url = backup_operation.folder_url - # [START restore_vault] + # [START begin_restore] # begin a full vault restore; to restore a single key, use the key_name kwarg restore_poller = await backup_client.begin_restore(folder_url, sas_token) @@ -76,8 +79,11 @@ async def test_example_backup_and_restore(self, container_uri, sas_token): token = restore_poller.polling_method().get_continuation_token() new_poller = await backup_client.begin_restore(folder_url, sas_token, continuation_token=token) + # check if the restore completed + done = new_poller.done() + # wait for the restore to complete await restore_poller.wait() - # [END restore_vault] + # [END begin_restore] await new_poller.wait() From cf25535d85773175683b6aa841ea6a870c6793a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?McCoy=20Pati=C3=B1o?= Date: Tue, 15 Jun 2021 13:43:20 -0700 Subject: [PATCH 20/27] use *_status; update tests --- .../keyvault/administration/_backup_client.py | 43 ++- .../administration/aio/_backup_client.py | 42 ++- ...ation.test_example_backup_and_restore.yaml | 284 ++++++++++++++++++ ...async.test_example_backup_and_restore.yaml | 186 ++++++++++++ .../tests/test_backup_client_async.py | 17 -- .../tests/test_examples_administration.py | 18 +- .../test_examples_administration_async.py | 19 +- 7 files changed, 537 insertions(+), 72 deletions(-) create mode 100644 sdk/keyvault/azure-keyvault-administration/tests/recordings/test_examples_administration.test_example_backup_and_restore.yaml create mode 100644 sdk/keyvault/azure-keyvault-administration/tests/recordings/test_examples_administration_async.test_example_backup_and_restore.yaml diff --git a/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_backup_client.py b/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_backup_client.py index 340d6a81296f..26e39dad89f7 100644 --- a/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_backup_client.py +++ b/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_backup_client.py @@ -7,6 +7,8 @@ import pickle from typing import TYPE_CHECKING +from six.moves.urllib_parse import urlparse + from ._models import KeyVaultBackupOperation from ._internal import KeyVaultClientBase, parse_folder_url from ._internal.polling import KeyVaultBackupClientPolling, KeyVaultBackupClientPollingMethod @@ -19,13 +21,10 @@ # pylint: disable=W0212 -def _get_status_from_continuation_token(token, pipeline_client): - continuation_url = base64.b64decode(token.encode()).decode("ascii") - status_response = pipeline_client._pipeline.run(pipeline_client.get(continuation_url), stream=False) - # inject the status URL if it's not in the response - if "azure-asyncoperation" not in status_response.http_response.headers: - status_response.http_response.headers["azure-asyncoperation"] = continuation_url - return base64.b64encode(pickle.dumps(status_response)).decode('ascii') +def _parse_status_url(url): + parsed = urlparse(url) + job_id = parsed.path.split("/")[2] + return job_id class KeyVaultBackupClient(KeyVaultClientBase): @@ -63,7 +62,20 @@ def begin_backup(self, blob_storage_url, sas_token, **kwargs): continuation_token = kwargs.pop("continuation_token", None) status_response = None if continuation_token: - status_response = _get_status_from_continuation_token(continuation_token, self._client._client) + try: + job_id = _parse_status_url(base64.b64decode(continuation_token.encode()).decode("ascii")) + except: # pylint: disable=broad-except + raise ValueError("The provided continuation_token is malformed. A base64-encoded URL is expected") + + pipeline_response = self._client.full_backup_status( + vault_base_url=self._vault_url, + job_id=job_id, + cls=lambda pipeline_response, _, __: pipeline_response + ) + if "azure-asyncoperation" not in pipeline_response.http_response.headers: + status_url = base64.b64decode(continuation_token.encode()).decode("ascii") + pipeline_response.http_response.headers["azure-asyncoperation"] = status_url + status_response = base64.b64encode(pickle.dumps(pipeline_response)).decode('ascii') return self._client.begin_full_backup( vault_base_url=self._vault_url, @@ -104,7 +116,20 @@ def begin_restore(self, folder_url, sas_token, **kwargs): status_response = None if continuation_token: - status_response = _get_status_from_continuation_token(continuation_token, self._client._client) + try: + job_id = _parse_status_url(base64.b64decode(continuation_token.encode()).decode("ascii")) + except: # pylint: disable=broad-except + raise ValueError("The provided continuation_token is malformed. A base64-encoded URL is expected") + + pipeline_response = self._client.restore_status( + vault_base_url=self._vault_url, + job_id=job_id, + cls=lambda pipeline_response, _, __: pipeline_response + ) + if "azure-asyncoperation" not in pipeline_response.http_response.headers: + status_url = base64.b64decode(continuation_token.encode()).decode("ascii") + pipeline_response.http_response.headers["azure-asyncoperation"] = status_url + status_response = base64.b64encode(pickle.dumps(pipeline_response)).decode('ascii') container_url, folder_name = parse_folder_url(folder_url) sas_parameter = self._models.SASTokenParameter(storage_resource_uri=container_url, token=sas_token) diff --git a/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/aio/_backup_client.py b/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/aio/_backup_client.py index f4e6ccbb739e..d452fd00a279 100644 --- a/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/aio/_backup_client.py +++ b/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/aio/_backup_client.py @@ -7,6 +7,7 @@ import pickle from typing import TYPE_CHECKING +from .._backup_client import _parse_status_url from .._internal import AsyncKeyVaultClientBase, parse_folder_url from .._internal.async_polling import KeyVaultAsyncBackupClientPollingMethod from .._internal.polling import KeyVaultBackupClientPolling @@ -17,17 +18,6 @@ from typing import Any from azure.core.polling import AsyncLROPoller -# pylint: disable=W0212 - - -async def _get_status_from_continuation_token(token, pipeline_client): - continuation_url = base64.b64decode(token.encode()).decode("ascii") - status_response = await pipeline_client._pipeline.run(pipeline_client.get(continuation_url), stream=False) - # inject the status URL if it's not in the response - if "azure-asyncoperation" not in status_response.http_response.headers: - status_response.http_response.headers["azure-asyncoperation"] = continuation_url - return base64.b64encode(pickle.dumps(status_response)).decode('ascii') - class KeyVaultBackupClient(AsyncKeyVaultClientBase): """Performs Key Vault backup and restore operations. @@ -64,7 +54,20 @@ async def begin_backup( continuation_token = kwargs.pop("continuation_token", None) status_response = None if continuation_token: - status_response = await _get_status_from_continuation_token(continuation_token, self._client._client) + try: + job_id = _parse_status_url(base64.b64decode(continuation_token.encode()).decode("ascii")) + except: # pylint: disable=broad-except + raise ValueError("The provided continuation_token is malformed. A base64-encoded URL is expected") + + pipeline_response = await self._client.full_backup_status( + vault_base_url=self._vault_url, + job_id=job_id, + cls=lambda pipeline_response, _, __: pipeline_response + ) + if "azure-asyncoperation" not in pipeline_response.http_response.headers: + status_url = base64.b64decode(continuation_token.encode()).decode("ascii") + pipeline_response.http_response.headers["azure-asyncoperation"] = status_url + status_response = base64.b64encode(pickle.dumps(pipeline_response)).decode('ascii') return await self._client.begin_full_backup( vault_base_url=self._vault_url, @@ -105,7 +108,20 @@ async def begin_restore(self, folder_url: str, sas_token: str, **kwargs: "Any") status_response = None if continuation_token: - status_response = await _get_status_from_continuation_token(continuation_token, self._client._client) + try: + job_id = _parse_status_url(base64.b64decode(continuation_token.encode()).decode("ascii")) + except: # pylint: disable=broad-except + raise ValueError("The provided continuation_token is malformed. A base64-encoded URL is expected") + + pipeline_response = await self._client.restore_status( + vault_base_url=self._vault_url, + job_id=job_id, + cls=lambda pipeline_response, _, __: pipeline_response + ) + if "azure-asyncoperation" not in pipeline_response.http_response.headers: + status_url = base64.b64decode(continuation_token.encode()).decode("ascii") + pipeline_response.http_response.headers["azure-asyncoperation"] = status_url + status_response = base64.b64encode(pickle.dumps(pipeline_response)).decode('ascii') container_url, folder_name = parse_folder_url(folder_url) sas_parameter = self._models.SASTokenParameter(storage_resource_uri=container_url, token=sas_token) diff --git a/sdk/keyvault/azure-keyvault-administration/tests/recordings/test_examples_administration.test_example_backup_and_restore.yaml b/sdk/keyvault/azure-keyvault-administration/tests/recordings/test_examples_administration.test_example_backup_and_restore.yaml new file mode 100644 index 000000000000..468f1a40c990 --- /dev/null +++ b/sdk/keyvault/azure-keyvault-administration/tests/recordings/test_examples_administration.test_example_backup_and_restore.yaml @@ -0,0 +1,284 @@ +interactions: +- request: + body: null + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '0' + Content-Type: + - application/json + User-Agent: + - azsdk-python-keyvault-administration/4.0.0b4 Python/3.5.3 (Windows-10-10.0.19041-SP0) + method: POST + uri: https://managedhsm/backup?api-version=7.2 + response: + body: + string: '' + headers: + cache-control: + - no-cache + content-length: + - '0' + content-security-policy: + - default-src 'self' + content-type: + - application/json; charset=utf-8 + strict-transport-security: + - max-age=31536000; includeSubDomains + www-authenticate: + - Bearer authorization="https://login.microsoftonline.com/00000000-0000-0000-0000-000000000000", + resource="https://managedhsm.azure.net" + x-content-type-options: + - nosniff + x-frame-options: + - SAMEORIGIN + x-ms-server-latency: + - '1' + status: + code: 401 + message: Unauthorized +- request: + body: '{"token": "redacted", "storageResourceUri": "https://storname.blob.core.windows.net/containerwex63hmamrkpesm"}' + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '235' + Content-Type: + - application/json + User-Agent: + - azsdk-python-keyvault-administration/4.0.0b4 Python/3.5.3 (Windows-10-10.0.19041-SP0) + method: POST + uri: https://managedhsm/backup?api-version=7.2 + response: + body: + string: '{"status":"InProgress","statusDetails":null,"error":null,"startTime":1623789423,"endTime":null,"jobId":"bb817c41fcea4e0dafc2bf1fa3e059c0","azureStorageBlobContainerUri":null}' + headers: + azure-asyncoperation: + - https://managedhsm/backup/bb817c41fcea4e0dafc2bf1fa3e059c0/pending + cache-control: + - no-cache + content-length: + - '174' + content-security-policy: + - default-src 'self' + content-type: + - application/json; charset=utf-8 + date: + - Tue, 15 Jun 2021 20:37:03 GMT + server: + - Kestrel + strict-transport-security: + - max-age=31536000; includeSubDomains + x-content-type-options: + - nosniff + x-frame-options: + - SAMEORIGIN + x-ms-keyvault-network-info: + - conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=Ipv4; + x-ms-keyvault-region: + - canadacentral + x-ms-server-latency: + - '2542' + status: + code: 202 + message: '' +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-keyvault-administration/4.0.0b4 Python/3.5.3 (Windows-10-10.0.19041-SP0) + method: GET + uri: https://managedhsm/backup/bb817c41fcea4e0dafc2bf1fa3e059c0/pending + response: + body: + string: '{"azureStorageBlobContainerUri":"https://storname.blob.core.windows.net/containerwex63hmamrkpesm/mhsm-mcpatinotesthsm-2021061520370374","endTime":1623789433,"error":null,"jobId":"bb817c41fcea4e0dafc2bf1fa3e059c0","startTime":1623789423,"status":"Succeeded","statusDetails":null}' + headers: + cache-control: + - no-cache + content-length: + - '294' + content-security-policy: + - default-src 'self' + content-type: + - application/json; charset=utf-8 + date: + - Tue, 15 Jun 2021 20:37:15 GMT + server: + - Kestrel + strict-transport-security: + - max-age=31536000; includeSubDomains + x-content-type-options: + - nosniff + x-frame-options: + - SAMEORIGIN + x-ms-build-version: + - 1.0.20210520-1-d6634624-develop + x-ms-keyvault-network-info: + - conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=Ipv4; + x-ms-keyvault-region: + - canadacentral + x-ms-server-latency: + - '2348' + status: + code: 200 + message: OK +- request: + body: '{"sasTokenParameters": {"token": "redacted", "storageResourceUri": "https://storname.blob.core.windows.net/containerwex63hmamrkpesm"}, + "folderToRestore": "mhsm-mcpatinotesthsm-2021061520370374"}' + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '319' + Content-Type: + - application/json + User-Agent: + - azsdk-python-keyvault-administration/4.0.0b4 Python/3.5.3 (Windows-10-10.0.19041-SP0) + method: PUT + uri: https://managedhsm/restore?api-version=7.2 + response: + body: + string: '{"endTime":null,"error":null,"jobId":"d085c52857d54d90b3c216b8d20355ca","startTime":1623789438,"status":"InProgress","statusDetails":null}' + headers: + azure-asyncoperation: + - https://managedhsm/restore/d085c52857d54d90b3c216b8d20355ca/pending + cache-control: + - no-cache + content-length: + - '138' + content-security-policy: + - default-src 'self' + content-type: + - application/json; charset=utf-8 + date: + - Tue, 15 Jun 2021 20:37:18 GMT + server: + - Kestrel + strict-transport-security: + - max-age=31536000; includeSubDomains + x-content-type-options: + - nosniff + x-frame-options: + - SAMEORIGIN + x-ms-keyvault-network-info: + - conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=Ipv4; + x-ms-keyvault-region: + - canadacentral + x-ms-server-latency: + - '2508' + status: + code: 202 + message: '' +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-keyvault-administration/4.0.0b4 Python/3.5.3 (Windows-10-10.0.19041-SP0) + method: GET + uri: https://managedhsm/restore/d085c52857d54d90b3c216b8d20355ca/pending + response: + body: + string: '{"endTime":null,"error":null,"jobId":"d085c52857d54d90b3c216b8d20355ca","startTime":1623789438,"status":"InProgress","statusDetails":null}' + headers: + cache-control: + - no-cache + content-length: + - '138' + content-security-policy: + - default-src 'self' + content-type: + - application/json; charset=utf-8 + date: + - Tue, 15 Jun 2021 20:37:30 GMT + server: + - Kestrel + strict-transport-security: + - max-age=31536000; includeSubDomains + x-content-type-options: + - nosniff + x-frame-options: + - SAMEORIGIN + x-ms-build-version: + - 1.0.20210520-1-d6634624-develop + x-ms-keyvault-network-info: + - conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=Ipv4; + x-ms-keyvault-region: + - canadacentral + x-ms-server-latency: + - '2349' + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-keyvault-administration/4.0.0b4 Python/3.5.3 (Windows-10-10.0.19041-SP0) + method: GET + uri: https://managedhsm/restore/d085c52857d54d90b3c216b8d20355ca/pending + response: + body: + string: '{"endTime":1623789455,"error":null,"jobId":"d085c52857d54d90b3c216b8d20355ca","startTime":1623789438,"status":"Succeeded","statusDetails":null}' + headers: + cache-control: + - no-cache + content-length: + - '143' + content-security-policy: + - default-src 'self' + content-type: + - application/json; charset=utf-8 + date: + - Tue, 15 Jun 2021 20:37:38 GMT + server: + - Kestrel + strict-transport-security: + - max-age=31536000; includeSubDomains + x-content-type-options: + - nosniff + x-frame-options: + - SAMEORIGIN + x-ms-build-version: + - 1.0.20210520-1-d6634624-develop + x-ms-keyvault-network-info: + - conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=Ipv4; + x-ms-keyvault-region: + - canadacentral + x-ms-server-latency: + - '2289' + status: + code: 200 + message: OK +version: 1 diff --git a/sdk/keyvault/azure-keyvault-administration/tests/recordings/test_examples_administration_async.test_example_backup_and_restore.yaml b/sdk/keyvault/azure-keyvault-administration/tests/recordings/test_examples_administration_async.test_example_backup_and_restore.yaml new file mode 100644 index 000000000000..bccbc4df2b98 --- /dev/null +++ b/sdk/keyvault/azure-keyvault-administration/tests/recordings/test_examples_administration_async.test_example_backup_and_restore.yaml @@ -0,0 +1,186 @@ +interactions: +- request: + body: null + headers: + Accept: + - application/json + Content-Length: + - '0' + Content-Type: + - application/json + User-Agent: + - azsdk-python-keyvault-administration/4.0.0b4 Python/3.5.3 (Windows-10-10.0.19041-SP0) + method: POST + uri: https://managedhsm/backup?api-version=7.2 + response: + body: + string: '' + headers: + cache-control: no-cache + content-length: '0' + content-security-policy: default-src 'self' + content-type: application/json; charset=utf-8 + strict-transport-security: max-age=31536000; includeSubDomains + www-authenticate: Bearer authorization="https://login.microsoftonline.com/00000000-0000-0000-0000-000000000000", + resource="https://managedhsm.azure.net" + x-content-type-options: nosniff + x-frame-options: SAMEORIGIN + x-ms-server-latency: '0' + status: + code: 401 + message: Unauthorized + url: https://mcpatinotesthsm.managedhsm.azure.net/backup?api-version=7.2 +- request: + body: '{"token": "redacted", "storageResourceUri": "https://storname.blob.core.windows.net/containertpuij7qwbd5ryzl"}' + headers: + Accept: + - application/json + Content-Length: + - '235' + Content-Type: + - application/json + User-Agent: + - azsdk-python-keyvault-administration/4.0.0b4 Python/3.5.3 (Windows-10-10.0.19041-SP0) + method: POST + uri: https://managedhsm/backup?api-version=7.2 + response: + body: + string: '{"status":"InProgress","statusDetails":null,"error":null,"startTime":1623789496,"endTime":null,"jobId":"3561286085204770977885ced9a31fb5","azureStorageBlobContainerUri":null}' + headers: + azure-asyncoperation: https://managedhsm/backup/3561286085204770977885ced9a31fb5/pending + cache-control: no-cache + content-length: '174' + content-security-policy: default-src 'self' + content-type: application/json; charset=utf-8 + date: Tue, 15 Jun 2021 20:38:16 GMT + server: Kestrel + strict-transport-security: max-age=31536000; includeSubDomains + x-content-type-options: nosniff + x-frame-options: SAMEORIGIN + x-ms-keyvault-network-info: conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=Ipv4; + x-ms-keyvault-region: canadacentral + x-ms-server-latency: '2590' + status: + code: 202 + message: '' + url: https://mcpatinotesthsm.managedhsm.azure.net/backup?api-version=7.2 +- request: + body: null + headers: + User-Agent: + - azsdk-python-keyvault-administration/4.0.0b4 Python/3.5.3 (Windows-10-10.0.19041-SP0) + method: GET + uri: https://managedhsm/backup/3561286085204770977885ced9a31fb5/pending + response: + body: + string: '{"azureStorageBlobContainerUri":"https://storname.blob.core.windows.net/containertpuij7qwbd5ryzl/mhsm-mcpatinotesthsm-2021061520381674","endTime":1623789505,"error":null,"jobId":"3561286085204770977885ced9a31fb5","startTime":1623789496,"status":"Succeeded","statusDetails":null}' + headers: + cache-control: no-cache + content-length: '294' + content-security-policy: default-src 'self' + content-type: application/json; charset=utf-8 + date: Tue, 15 Jun 2021 20:38:29 GMT + server: Kestrel + strict-transport-security: max-age=31536000; includeSubDomains + x-content-type-options: nosniff + x-frame-options: SAMEORIGIN + x-ms-build-version: 1.0.20210520-1-d6634624-develop + x-ms-keyvault-network-info: conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=Ipv4; + x-ms-keyvault-region: canadacentral + x-ms-server-latency: '2567' + status: + code: 200 + message: OK + url: https://mcpatinotesthsm.managedhsm.azure.net/backup/3561286085204770977885ced9a31fb5/pending +- request: + body: '{"folderToRestore": "mhsm-mcpatinotesthsm-2021061520381674", "sasTokenParameters": + {"token": "redacted", "storageResourceUri": "https://storname.blob.core.windows.net/containertpuij7qwbd5ryzl"}}' + headers: + Accept: + - application/json + Content-Length: + - '319' + Content-Type: + - application/json + User-Agent: + - azsdk-python-keyvault-administration/4.0.0b4 Python/3.5.3 (Windows-10-10.0.19041-SP0) + method: PUT + uri: https://managedhsm/restore?api-version=7.2 + response: + body: + string: '{"endTime":null,"error":null,"jobId":"5d26d34875964b1c9d400c25b82a5bf4","startTime":1623789511,"status":"InProgress","statusDetails":null}' + headers: + azure-asyncoperation: https://managedhsm/restore/5d26d34875964b1c9d400c25b82a5bf4/pending + cache-control: no-cache + content-length: '138' + content-security-policy: default-src 'self' + content-type: application/json; charset=utf-8 + date: Tue, 15 Jun 2021 20:38:31 GMT + server: Kestrel + strict-transport-security: max-age=31536000; includeSubDomains + x-content-type-options: nosniff + x-frame-options: SAMEORIGIN + x-ms-keyvault-network-info: conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=Ipv4; + x-ms-keyvault-region: canadacentral + x-ms-server-latency: '2564' + status: + code: 202 + message: '' + url: https://mcpatinotesthsm.managedhsm.azure.net/restore?api-version=7.2 +- request: + body: null + headers: + User-Agent: + - azsdk-python-keyvault-administration/4.0.0b4 Python/3.5.3 (Windows-10-10.0.19041-SP0) + method: GET + uri: https://managedhsm/restore/5d26d34875964b1c9d400c25b82a5bf4/pending + response: + body: + string: '{"endTime":null,"error":null,"jobId":"5d26d34875964b1c9d400c25b82a5bf4","startTime":1623789511,"status":"InProgress","statusDetails":null}' + headers: + cache-control: no-cache + content-length: '138' + content-security-policy: default-src 'self' + content-type: application/json; charset=utf-8 + date: Tue, 15 Jun 2021 20:38:43 GMT + server: Kestrel + strict-transport-security: max-age=31536000; includeSubDomains + x-content-type-options: nosniff + x-frame-options: SAMEORIGIN + x-ms-build-version: 1.0.20210520-1-d6634624-develop + x-ms-keyvault-network-info: conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=Ipv4; + x-ms-keyvault-region: canadacentral + x-ms-server-latency: '2203' + status: + code: 200 + message: OK + url: https://mcpatinotesthsm.managedhsm.azure.net/restore/5d26d34875964b1c9d400c25b82a5bf4/pending +- request: + body: null + headers: + User-Agent: + - azsdk-python-keyvault-administration/4.0.0b4 Python/3.5.3 (Windows-10-10.0.19041-SP0) + method: GET + uri: https://managedhsm/restore/5d26d34875964b1c9d400c25b82a5bf4/pending + response: + body: + string: '{"endTime":1623789528,"error":null,"jobId":"5d26d34875964b1c9d400c25b82a5bf4","startTime":1623789511,"status":"Succeeded","statusDetails":null}' + headers: + cache-control: no-cache + content-length: '143' + content-security-policy: default-src 'self' + content-type: application/json; charset=utf-8 + date: Tue, 15 Jun 2021 20:38:53 GMT + server: Kestrel + strict-transport-security: max-age=31536000; includeSubDomains + x-content-type-options: nosniff + x-frame-options: SAMEORIGIN + x-ms-build-version: 1.0.20210520-1-d6634624-develop + x-ms-keyvault-network-info: conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=Ipv4; + x-ms-keyvault-region: canadacentral + x-ms-server-latency: '3838' + status: + code: 200 + message: OK + url: https://mcpatinotesthsm.managedhsm.azure.net/restore/5d26d34875964b1c9d400c25b82a5bf4/pending +version: 1 diff --git a/sdk/keyvault/azure-keyvault-administration/tests/test_backup_client_async.py b/sdk/keyvault/azure-keyvault-administration/tests/test_backup_client_async.py index 26f58f3d442e..8c8d0e0f5707 100644 --- a/sdk/keyvault/azure-keyvault-administration/tests/test_backup_client_async.py +++ b/sdk/keyvault/azure-keyvault-administration/tests/test_backup_client_async.py @@ -172,20 +172,3 @@ async def test_backup_client_polling(self, container_uri, sas_token): assert rehydrated.status() == "Succeeded" and rehydrated.polling_method().status() == "Succeeded" await restore_poller.wait() assert restore_poller.status() == "Succeeded" and restore_poller.polling_method().status() == "Succeeded" - - - # check that pollers and polling methods behave as expected - assert restore_poller.status() == "InProgress" - assert not restore_poller.done() or restore_poller.polling_method().finished() - assert rehydrated.status() == "InProgress" - assert not rehydrated.done() or rehydrated.polling_method().finished() - - await rehydrated.polling_method().update_status() - assert rehydrated.status() == "InProgress" - await restore_poller.polling_method().update_status() - assert restore_poller.status() == "InProgress" - - await rehydrated.wait() - assert rehydrated.status() == "Succeeded" and rehydrated.polling_method().status() == "Succeeded" - await restore_poller.wait() - assert restore_poller.status() == "Succeeded" and restore_poller.polling_method().status() == "Succeeded" diff --git a/sdk/keyvault/azure-keyvault-administration/tests/test_examples_administration.py b/sdk/keyvault/azure-keyvault-administration/tests/test_examples_administration.py index ec50a1a3ed27..f82b82ea92ac 100644 --- a/sdk/keyvault/azure-keyvault-administration/tests/test_examples_administration.py +++ b/sdk/keyvault/azure-keyvault-administration/tests/test_examples_administration.py @@ -44,42 +44,28 @@ def credential(self): @StorageAccountPreparer(random_name_enabled=True) @BlobContainerPreparer() def test_example_backup_and_restore(self, container_uri, sas_token): - if not self.is_live: - pytest.skip("Poller requests are incompatible with vcrpy in playback") - backup_client = KeyVaultBackupClient(self.managed_hsm["url"], self.credential) # [START begin_backup] # begin a vault backup backup_poller = backup_client.begin_backup(container_uri, sas_token) - # to create a new poller for the operation, use a continuation token - token = backup_poller.polling_method().get_continuation_token() - new_poller = backup_client.begin_backup(container_uri, sas_token, continuation_token=token) - # check if the backup completed - done = new_poller.done() + done = backup_poller.done() # get the final result backup_operation = backup_poller.result() # [END begin_backup] - new_poller.wait() folder_url = backup_operation.folder_url # [START begin_restore] # begin a full vault restore; to restore a single key, use the key_name kwarg restore_poller = backup_client.begin_restore(folder_url, sas_token) - # to create a new poller for the operation, use a continuation token - token = restore_poller.polling_method().get_continuation_token() - new_poller = backup_client.begin_restore(folder_url, sas_token, continuation_token=token) - # check if the restore completed - done = new_poller.done() + done = backup_poller.done() # wait for the restore to complete restore_poller.wait() # [END begin_restore] - - new_poller.wait() diff --git a/sdk/keyvault/azure-keyvault-administration/tests/test_examples_administration_async.py b/sdk/keyvault/azure-keyvault-administration/tests/test_examples_administration_async.py index d9ea11022050..4d19bd05d472 100644 --- a/sdk/keyvault/azure-keyvault-administration/tests/test_examples_administration_async.py +++ b/sdk/keyvault/azure-keyvault-administration/tests/test_examples_administration_async.py @@ -48,42 +48,27 @@ async def get_token(*_, **__): @StorageAccountPreparer(random_name_enabled=True) @BlobContainerPreparer() async def test_example_backup_and_restore(self, container_uri, sas_token): - if not self.is_live: - pytest.skip("Poller requests are incompatible with vcrpy in playback") - backup_client = KeyVaultBackupClient(self.managed_hsm["url"], self.credential) # [START begin_backup] # begin a vault backup backup_poller = await backup_client.begin_backup(container_uri, sas_token) - # to create a new poller for the operation, use a continuation token - token = backup_poller.polling_method().get_continuation_token() - new_poller = await backup_client.begin_backup(container_uri, sas_token, continuation_token=token) - - # check if the backup completed - done = new_poller.done() + done = backup_poller.done() # get the final result backup_operation = await backup_poller.result() # [END begin_backup] - await new_poller.wait() folder_url = backup_operation.folder_url # [START begin_restore] # begin a full vault restore; to restore a single key, use the key_name kwarg restore_poller = await backup_client.begin_restore(folder_url, sas_token) - # to create a new poller for the operation, use a continuation token - token = restore_poller.polling_method().get_continuation_token() - new_poller = await backup_client.begin_restore(folder_url, sas_token, continuation_token=token) - # check if the restore completed - done = new_poller.done() + done = backup_poller.done() # wait for the restore to complete await restore_poller.wait() # [END begin_restore] - - await new_poller.wait() From 2ba1ea23435dfd3b0e1b7836f6c32b1f92272028 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?McCoy=20Pati=C3=B1o?= Date: Tue, 15 Jun 2021 15:51:41 -0700 Subject: [PATCH 21/27] Address feedback --- .../keyvault/administration/_backup_client.py | 37 +- .../administration/_internal/async_polling.py | 3 +- .../administration/_internal/polling.py | 11 +- .../administration/aio/_backup_client.py | 35 +- ...p_client.test_full_backup_and_restore.yaml | 86 ++-- ...nt_async.test_full_backup_and_restore.yaml | 165 ++------ ...on.test_example_selective_key_restore.yaml | 374 ++++++++++++++++++ ...nc.test_example_selective_key_restore.yaml | 251 ++++++++++++ .../tests/test_backup_client.py | 10 +- .../tests/test_backup_client_async.py | 10 +- .../tests/test_examples_administration.py | 41 +- .../test_examples_administration_async.py | 42 +- 12 files changed, 850 insertions(+), 215 deletions(-) create mode 100644 sdk/keyvault/azure-keyvault-administration/tests/recordings/test_examples_administration.test_example_selective_key_restore.yaml create mode 100644 sdk/keyvault/azure-keyvault-administration/tests/recordings/test_examples_administration_async.test_example_selective_key_restore.yaml diff --git a/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_backup_client.py b/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_backup_client.py index 26e39dad89f7..986f139fa4d2 100644 --- a/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_backup_client.py +++ b/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_backup_client.py @@ -18,8 +18,6 @@ from typing import Any from azure.core.polling import LROPoller -# pylint: disable=W0212 - def _parse_status_url(url): parsed = urlparse(url) @@ -53,7 +51,7 @@ def begin_backup(self, blob_storage_url, sas_token, **kwargs): :start-after: [START begin_backup] :end-before: [END begin_backup] :language: python - :caption: Start a vault backup operation + :caption: Create a vault backup :dedent: 8 """ polling_interval = kwargs.pop("_polling_interval", 5) @@ -65,17 +63,18 @@ def begin_backup(self, blob_storage_url, sas_token, **kwargs): try: job_id = _parse_status_url(base64.b64decode(continuation_token.encode()).decode("ascii")) except: # pylint: disable=broad-except - raise ValueError("The provided continuation_token is malformed. A base64-encoded URL is expected") + raise ValueError( + "The provided continuation_token is malformed. A valid token can be obtained from the operation " + + "poller's continuation_token() method" + ) pipeline_response = self._client.full_backup_status( - vault_base_url=self._vault_url, - job_id=job_id, - cls=lambda pipeline_response, _, __: pipeline_response + vault_base_url=self._vault_url, job_id=job_id, cls=lambda pipeline_response, _, __: pipeline_response ) if "azure-asyncoperation" not in pipeline_response.http_response.headers: status_url = base64.b64decode(continuation_token.encode()).decode("ascii") pipeline_response.http_response.headers["azure-asyncoperation"] = status_url - status_response = base64.b64encode(pickle.dumps(pipeline_response)).decode('ascii') + status_response = base64.b64encode(pickle.dumps(pipeline_response)).decode("ascii") return self._client.begin_full_backup( vault_base_url=self._vault_url, @@ -102,12 +101,19 @@ def begin_restore(self, folder_url, sas_token, **kwargs): :keyword str key_name: name of a single key in the backup. When set, only this key will be restored. :rtype: ~azure.core.polling.LROPoller - Example: + Examples: .. literalinclude:: ../tests/test_examples_administration.py :start-after: [START begin_restore] :end-before: [END begin_restore] :language: python - :caption: Start a vault restore operation + :caption: Restore a vault backup + :dedent: 8 + + .. literalinclude:: ../tests/test_examples_administration.py + :start-after: [START begin_selective_restore] + :end-before: [END begin_selective_restore] + :language: python + :caption: Restore a single key :dedent: 8 """ # LROBasePolling passes its kwargs to pipeline.run(), so we remove unexpected args before constructing it @@ -119,17 +125,18 @@ def begin_restore(self, folder_url, sas_token, **kwargs): try: job_id = _parse_status_url(base64.b64decode(continuation_token.encode()).decode("ascii")) except: # pylint: disable=broad-except - raise ValueError("The provided continuation_token is malformed. A base64-encoded URL is expected") + raise ValueError( + "The provided continuation_token is malformed. A valid token can be obtained from the operation " + + "poller's continuation_token() method" + ) pipeline_response = self._client.restore_status( - vault_base_url=self._vault_url, - job_id=job_id, - cls=lambda pipeline_response, _, __: pipeline_response + vault_base_url=self._vault_url, job_id=job_id, cls=lambda pipeline_response, _, __: pipeline_response ) if "azure-asyncoperation" not in pipeline_response.http_response.headers: status_url = base64.b64decode(continuation_token.encode()).decode("ascii") pipeline_response.http_response.headers["azure-asyncoperation"] = status_url - status_response = base64.b64encode(pickle.dumps(pipeline_response)).decode('ascii') + status_response = base64.b64encode(pickle.dumps(pipeline_response)).decode("ascii") container_url, folder_name = parse_folder_url(folder_url) sas_parameter = self._models.SASTokenParameter(storage_resource_uri=container_url, token=sas_token) diff --git a/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_internal/async_polling.py b/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_internal/async_polling.py index 3ef90e117977..bd77ebd79e4e 100644 --- a/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_internal/async_polling.py +++ b/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_internal/async_polling.py @@ -8,6 +8,5 @@ class KeyVaultAsyncBackupClientPollingMethod(AsyncLROBasePolling): - def get_continuation_token(self): - # type() -> str + def get_continuation_token(self) -> str: return base64.b64encode(self._operation.get_polling_url().encode()).decode("ascii") diff --git a/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_internal/polling.py b/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_internal/polling.py index 2b72ebbe889e..78f063893029 100644 --- a/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_internal/polling.py +++ b/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_internal/polling.py @@ -3,18 +3,9 @@ # Licensed under the MIT License. # ------------------------------------ import base64 -from typing import TYPE_CHECKING from azure.core.polling.base_polling import LROBasePolling, OperationResourcePolling -if TYPE_CHECKING: - from typing import Union - from azure.core.pipeline import PipelineResponse - from azure.core.pipeline.transport import HttpResponse, AsyncHttpResponse, HttpRequest - - ResponseType = Union[HttpResponse, AsyncHttpResponse] - PipelineResponseType = PipelineResponse[HttpRequest, ResponseType] - class KeyVaultBackupClientPolling(OperationResourcePolling): def __init__(self): @@ -26,5 +17,5 @@ def get_final_get_url(self, pipeline_response): class KeyVaultBackupClientPollingMethod(LROBasePolling): def get_continuation_token(self): - # type() -> str + # type: () -> str return base64.b64encode(self._operation.get_polling_url().encode()).decode("ascii") diff --git a/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/aio/_backup_client.py b/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/aio/_backup_client.py index d452fd00a279..e5f8f5a26737 100644 --- a/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/aio/_backup_client.py +++ b/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/aio/_backup_client.py @@ -45,7 +45,7 @@ async def begin_backup( :start-after: [START begin_backup] :end-before: [END begin_backup] :language: python - :caption: Start a vault backup operation + :caption: Create a vault backup :dedent: 8 """ polling_interval = kwargs.pop("_polling_interval", 5) @@ -57,17 +57,18 @@ async def begin_backup( try: job_id = _parse_status_url(base64.b64decode(continuation_token.encode()).decode("ascii")) except: # pylint: disable=broad-except - raise ValueError("The provided continuation_token is malformed. A base64-encoded URL is expected") + raise ValueError( + "The provided continuation_token is malformed. A valid token can be obtained from the operation " + + "poller's continuation_token() method" + ) pipeline_response = await self._client.full_backup_status( - vault_base_url=self._vault_url, - job_id=job_id, - cls=lambda pipeline_response, _, __: pipeline_response + vault_base_url=self._vault_url, job_id=job_id, cls=lambda pipeline_response, _, __: pipeline_response ) if "azure-asyncoperation" not in pipeline_response.http_response.headers: status_url = base64.b64decode(continuation_token.encode()).decode("ascii") pipeline_response.http_response.headers["azure-asyncoperation"] = status_url - status_response = base64.b64encode(pickle.dumps(pipeline_response)).decode('ascii') + status_response = base64.b64encode(pickle.dumps(pipeline_response)).decode("ascii") return await self._client.begin_full_backup( vault_base_url=self._vault_url, @@ -94,12 +95,19 @@ async def begin_restore(self, folder_url: str, sas_token: str, **kwargs: "Any") :keyword str key_name: name of a single key in the backup. When set, only this key will be restored. :rtype: ~azure.core.polling.AsyncLROPoller - Example: + Examples: .. literalinclude:: ../tests/test_examples_administration_async.py :start-after: [START begin_restore] :end-before: [END begin_restore] :language: python - :caption: Start a vault restore operation + :caption: Restore a vault backup + :dedent: 8 + + .. literalinclude:: ../tests/test_examples_administration_async.py + :start-after: [START begin_selective_restore] + :end-before: [END begin_selective_restore] + :language: python + :caption: Restore a single key :dedent: 8 """ # AsyncLROBasePolling passes its kwargs to pipeline.run(), so we remove unexpected args before constructing it @@ -111,17 +119,18 @@ async def begin_restore(self, folder_url: str, sas_token: str, **kwargs: "Any") try: job_id = _parse_status_url(base64.b64decode(continuation_token.encode()).decode("ascii")) except: # pylint: disable=broad-except - raise ValueError("The provided continuation_token is malformed. A base64-encoded URL is expected") + raise ValueError( + "The provided continuation_token is malformed. A valid token can be obtained from the operation " + + "poller's continuation_token() method" + ) pipeline_response = await self._client.restore_status( - vault_base_url=self._vault_url, - job_id=job_id, - cls=lambda pipeline_response, _, __: pipeline_response + vault_base_url=self._vault_url, job_id=job_id, cls=lambda pipeline_response, _, __: pipeline_response ) if "azure-asyncoperation" not in pipeline_response.http_response.headers: status_url = base64.b64decode(continuation_token.encode()).decode("ascii") pipeline_response.http_response.headers["azure-asyncoperation"] = status_url - status_response = base64.b64encode(pickle.dumps(pipeline_response)).decode('ascii') + status_response = base64.b64encode(pickle.dumps(pipeline_response)).decode("ascii") container_url, folder_name = parse_folder_url(folder_url) sas_parameter = self._models.SASTokenParameter(storage_resource_uri=container_url, token=sas_token) diff --git a/sdk/keyvault/azure-keyvault-administration/tests/recordings/test_backup_client.test_full_backup_and_restore.yaml b/sdk/keyvault/azure-keyvault-administration/tests/recordings/test_backup_client.test_full_backup_and_restore.yaml index befd4c982df1..b93e5dff6319 100644 --- a/sdk/keyvault/azure-keyvault-administration/tests/recordings/test_backup_client.test_full_backup_and_restore.yaml +++ b/sdk/keyvault/azure-keyvault-administration/tests/recordings/test_backup_client.test_full_backup_and_restore.yaml @@ -13,7 +13,7 @@ interactions: Content-Type: - application/json User-Agent: - - azsdk-python-keyvault-administration/4.0.0b4 Python/3.6.9 (Linux-5.4.72-microsoft-standard-WSL2-x86_64-with-Ubuntu-18.04-bionic) + - azsdk-python-keyvault-administration/4.0.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) method: POST uri: https://managedhsm/backup?api-version=7.2 response: @@ -38,12 +38,12 @@ interactions: x-frame-options: - SAMEORIGIN x-ms-server-latency: - - '2' + - '0' status: code: 401 message: Unauthorized - request: - body: '{"storageResourceUri": "https://storname.blob.core.windows.net/container3twjzazwmh2cwvw", + body: '{"storageResourceUri": "https://storname.blob.core.windows.net/containerebkvpbdw6vhv6bv", "token": "redacted"}' headers: Accept: @@ -53,19 +53,19 @@ interactions: Connection: - keep-alive Content-Length: - - '233' + - '235' Content-Type: - application/json User-Agent: - - azsdk-python-keyvault-administration/4.0.0b4 Python/3.6.9 (Linux-5.4.72-microsoft-standard-WSL2-x86_64-with-Ubuntu-18.04-bionic) + - azsdk-python-keyvault-administration/4.0.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) method: POST uri: https://managedhsm/backup?api-version=7.2 response: body: - string: '{"status":"InProgress","statusDetails":null,"error":null,"startTime":1623262243,"endTime":null,"jobId":"02ceebe3f890458f9dca192b3891edf9","azureStorageBlobContainerUri":null}' + string: '{"status":"InProgress","statusDetails":null,"error":null,"startTime":1623795979,"endTime":null,"jobId":"19a636ea1cd04e73b872fcd250fed223","azureStorageBlobContainerUri":null}' headers: azure-asyncoperation: - - https://managedhsm/backup/02ceebe3f890458f9dca192b3891edf9/pending + - https://managedhsm/backup/19a636ea1cd04e73b872fcd250fed223/pending cache-control: - no-cache content-length: @@ -75,7 +75,7 @@ interactions: content-type: - application/json; charset=utf-8 date: - - Wed, 09 Jun 2021 18:10:43 GMT + - Tue, 15 Jun 2021 22:26:18 GMT server: - Kestrel strict-transport-security: @@ -85,11 +85,11 @@ interactions: x-frame-options: - SAMEORIGIN x-ms-keyvault-network-info: - - conn_type=Ipv4;addr=24.17.201.78;act_addr_fam=Ipv4; + - conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=Ipv4; x-ms-keyvault-region: - - centralus + - canadacentral x-ms-server-latency: - - '2430' + - '3112' status: code: 202 message: '' @@ -103,23 +103,23 @@ interactions: Connection: - keep-alive User-Agent: - - azsdk-python-keyvault-administration/4.0.0b4 Python/3.6.9 (Linux-5.4.72-microsoft-standard-WSL2-x86_64-with-Ubuntu-18.04-bionic) + - azsdk-python-keyvault-administration/4.0.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) method: GET - uri: https://managedhsm/backup/02ceebe3f890458f9dca192b3891edf9/pending + uri: https://managedhsm/backup/19a636ea1cd04e73b872fcd250fed223/pending response: body: - string: '{"azureStorageBlobContainerUri":"https://storname.blob.core.windows.net/container3twjzazwmh2cwvw/mhsm-mcpatinotesthsm-2021060918104369","endTime":1623262252,"error":null,"jobId":"02ceebe3f890458f9dca192b3891edf9","startTime":1623262243,"status":"Succeeded","statusDetails":null}' + string: '{"azureStorageBlobContainerUri":"https://storname.blob.core.windows.net/containerebkvpbdw6vhv6bv/mhsm-mcpatinotesthsm-2021061522261918","endTime":1623795989,"error":null,"jobId":"19a636ea1cd04e73b872fcd250fed223","startTime":1623795979,"status":"Succeeded","statusDetails":null}' headers: cache-control: - no-cache content-length: - - '138' + - '294' content-security-policy: - default-src 'self' content-type: - application/json; charset=utf-8 date: - - Wed, 09 Jun 2021 18:10:57 GMT + - Tue, 15 Jun 2021 22:26:31 GMT server: - Kestrel strict-transport-security: @@ -131,17 +131,17 @@ interactions: x-ms-build-version: - 1.0.20210520-1-d6634624-develop x-ms-keyvault-network-info: - - conn_type=Ipv4;addr=24.17.201.78;act_addr_fam=Ipv4; + - conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=Ipv4; x-ms-keyvault-region: - - centralus + - canadacentral x-ms-server-latency: - - '4139' + - '2436' status: code: 200 message: OK - request: - body: '{"sasTokenParameters": {"storageResourceUri": "https://storname.blob.core.windows.net/container3twjzazwmh2cwvw", - "token": "redacted"}, "folderToRestore": "mhsm-mcpatinotesthsm-2021060918104369"}' + body: '{"sasTokenParameters": {"storageResourceUri": "https://storname.blob.core.windows.net/containerebkvpbdw6vhv6bv", + "token": "redacted"}, "folderToRestore": "mhsm-mcpatinotesthsm-2021061522261918"}' headers: Accept: - application/json @@ -150,19 +150,19 @@ interactions: Connection: - keep-alive Content-Length: - - '317' + - '319' Content-Type: - application/json User-Agent: - - azsdk-python-keyvault-administration/4.0.0b4 Python/3.6.9 (Linux-5.4.72-microsoft-standard-WSL2-x86_64-with-Ubuntu-18.04-bionic) + - azsdk-python-keyvault-administration/4.0.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) method: PUT uri: https://managedhsm/restore?api-version=7.2 response: body: - string: '{"endTime":null,"error":null,"jobId":"135e9be3e38c43f69e1d4f8b25262b52","startTime":1623262260,"status":"InProgress","statusDetails":null}' + string: '{"endTime":null,"error":null,"jobId":"a66229e63f5b4dc8864e224990a03f39","startTime":1623795994,"status":"InProgress","statusDetails":null}' headers: azure-asyncoperation: - - https://managedhsm/restore/135e9be3e38c43f69e1d4f8b25262b52/pending + - https://managedhsm/restore/a66229e63f5b4dc8864e224990a03f39/pending cache-control: - no-cache content-length: @@ -172,7 +172,7 @@ interactions: content-type: - application/json; charset=utf-8 date: - - Wed, 09 Jun 2021 18:11:00 GMT + - Tue, 15 Jun 2021 22:26:34 GMT server: - Kestrel strict-transport-security: @@ -182,11 +182,11 @@ interactions: x-frame-options: - SAMEORIGIN x-ms-keyvault-network-info: - - conn_type=Ipv4;addr=24.17.201.78;act_addr_fam=Ipv4; + - conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=Ipv4; x-ms-keyvault-region: - - centralus + - canadacentral x-ms-server-latency: - - '2245' + - '2762' status: code: 202 message: '' @@ -200,12 +200,12 @@ interactions: Connection: - keep-alive User-Agent: - - azsdk-python-keyvault-administration/4.0.0b4 Python/3.6.9 (Linux-5.4.72-microsoft-standard-WSL2-x86_64-with-Ubuntu-18.04-bionic) + - azsdk-python-keyvault-administration/4.0.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) method: GET - uri: https://managedhsm/restore/135e9be3e38c43f69e1d4f8b25262b52/pending + uri: https://managedhsm/restore/a66229e63f5b4dc8864e224990a03f39/pending response: body: - string: '{"endTime":null,"error":null,"jobId":"135e9be3e38c43f69e1d4f8b25262b52","startTime":1623262260,"status":"InProgress","statusDetails":null}' + string: '{"endTime":null,"error":null,"jobId":"a66229e63f5b4dc8864e224990a03f39","startTime":1623795994,"status":"InProgress","statusDetails":null}' headers: cache-control: - no-cache @@ -216,7 +216,7 @@ interactions: content-type: - application/json; charset=utf-8 date: - - Wed, 09 Jun 2021 18:11:12 GMT + - Tue, 15 Jun 2021 22:26:46 GMT server: - Kestrel strict-transport-security: @@ -228,11 +228,11 @@ interactions: x-ms-build-version: - 1.0.20210520-1-d6634624-develop x-ms-keyvault-network-info: - - conn_type=Ipv4;addr=24.17.201.78;act_addr_fam=Ipv4; + - conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=Ipv4; x-ms-keyvault-region: - - centralus + - canadacentral x-ms-server-latency: - - '2398' + - '2334' status: code: 200 message: OK @@ -246,12 +246,12 @@ interactions: Connection: - keep-alive User-Agent: - - azsdk-python-keyvault-administration/4.0.0b4 Python/3.6.9 (Linux-5.4.72-microsoft-standard-WSL2-x86_64-with-Ubuntu-18.04-bionic) + - azsdk-python-keyvault-administration/4.0.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) method: GET - uri: https://managedhsm/restore/135e9be3e38c43f69e1d4f8b25262b52/pending + uri: https://managedhsm/restore/a66229e63f5b4dc8864e224990a03f39/pending response: body: - string: '{"endTime":1623262276,"error":null,"jobId":"135e9be3e38c43f69e1d4f8b25262b52","startTime":1623262260,"status":"Succeeded","statusDetails":null}' + string: '{"endTime":1623796011,"error":null,"jobId":"a66229e63f5b4dc8864e224990a03f39","startTime":1623795994,"status":"Succeeded","statusDetails":null}' headers: cache-control: - no-cache @@ -262,7 +262,7 @@ interactions: content-type: - application/json; charset=utf-8 date: - - Wed, 09 Jun 2021 18:11:19 GMT + - Tue, 15 Jun 2021 22:26:54 GMT server: - Kestrel strict-transport-security: @@ -274,11 +274,11 @@ interactions: x-ms-build-version: - 1.0.20210520-1-d6634624-develop x-ms-keyvault-network-info: - - conn_type=Ipv4;addr=24.17.201.78;act_addr_fam=Ipv4; + - conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=Ipv4; x-ms-keyvault-region: - - centralus + - canadacentral x-ms-server-latency: - - '2170' + - '2689' status: code: 200 message: OK diff --git a/sdk/keyvault/azure-keyvault-administration/tests/recordings/test_backup_client_async.test_full_backup_and_restore.yaml b/sdk/keyvault/azure-keyvault-administration/tests/recordings/test_backup_client_async.test_full_backup_and_restore.yaml index 04fb05938e6c..6dcf5ba873e7 100644 --- a/sdk/keyvault/azure-keyvault-administration/tests/recordings/test_backup_client_async.test_full_backup_and_restore.yaml +++ b/sdk/keyvault/azure-keyvault-administration/tests/recordings/test_backup_client_async.test_full_backup_and_restore.yaml @@ -9,7 +9,7 @@ interactions: Content-Type: - application/json User-Agent: - - azsdk-python-keyvault-administration/4.0.0b4 Python/3.5.3 (Windows-10-10.0.19041-SP0) + - azsdk-python-keyvault-administration/4.0.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) method: POST uri: https://managedhsm/backup?api-version=7.2 response: @@ -25,41 +25,42 @@ interactions: resource="https://managedhsm.azure.net" x-content-type-options: nosniff x-frame-options: SAMEORIGIN - x-ms-server-latency: '2' + x-ms-server-latency: '1' status: code: 401 message: Unauthorized url: https://mcpatinotesthsm.managedhsm.azure.net/backup?api-version=7.2 - request: - body: '{"token": "redacted", "storageResourceUri": "https://storname.blob.core.windows.net/containerk7lnzcoq4emt6gf"}' + body: '{"storageResourceUri": "https://storname.blob.core.windows.net/containerwt3qjyb4eluro3w", + "token": "redacted"}' headers: Accept: - application/json Content-Length: - - '235' + - '233' Content-Type: - application/json User-Agent: - - azsdk-python-keyvault-administration/4.0.0b4 Python/3.5.3 (Windows-10-10.0.19041-SP0) + - azsdk-python-keyvault-administration/4.0.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) method: POST uri: https://managedhsm/backup?api-version=7.2 response: body: - string: '{"status":"InProgress","statusDetails":null,"error":null,"startTime":1623462777,"endTime":null,"jobId":"c8a42738bf8643c9b7ffde9ef339d0e2","azureStorageBlobContainerUri":null}' + string: '{"status":"InProgress","statusDetails":null,"error":null,"startTime":1623796131,"endTime":null,"jobId":"47fa9b06eeca494d901a9768bc4ecb15","azureStorageBlobContainerUri":null}' headers: - azure-asyncoperation: https://managedhsm/backup/c8a42738bf8643c9b7ffde9ef339d0e2/pending + azure-asyncoperation: https://managedhsm/backup/47fa9b06eeca494d901a9768bc4ecb15/pending cache-control: no-cache content-length: '174' content-security-policy: default-src 'self' content-type: application/json; charset=utf-8 - date: Sat, 12 Jun 2021 01:52:57 GMT + date: Tue, 15 Jun 2021 22:28:50 GMT server: Kestrel strict-transport-security: max-age=31536000; includeSubDomains x-content-type-options: nosniff x-frame-options: SAMEORIGIN x-ms-keyvault-network-info: conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=Ipv4; - x-ms-keyvault-region: centralus - x-ms-server-latency: '2657' + x-ms-keyvault-region: canadacentral + x-ms-server-latency: '2503' status: code: 202 message: '' @@ -68,61 +69,61 @@ interactions: body: null headers: User-Agent: - - azsdk-python-keyvault-administration/4.0.0b4 Python/3.5.3 (Windows-10-10.0.19041-SP0) + - azsdk-python-keyvault-administration/4.0.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) method: GET - uri: https://managedhsm/backup/c8a42738bf8643c9b7ffde9ef339d0e2/pending + uri: https://managedhsm/backup/47fa9b06eeca494d901a9768bc4ecb15/pending response: body: - string: '{"azureStorageBlobContainerUri":"https://storname.blob.core.windows.net/containerk7lnzcoq4emt6gf/mhsm-mcpatinotesthsm-2021061201525788","endTime":1623462786,"error":null,"jobId":"c8a42738bf8643c9b7ffde9ef339d0e2","startTime":1623462777,"status":"Succeeded","statusDetails":null}' + string: '{"azureStorageBlobContainerUri":"https://storname.blob.core.windows.net/containerwt3qjyb4eluro3w/mhsm-mcpatinotesthsm-2021061522285112","endTime":1623796140,"error":null,"jobId":"47fa9b06eeca494d901a9768bc4ecb15","startTime":1623796131,"status":"Succeeded","statusDetails":null}' headers: cache-control: no-cache content-length: '294' content-security-policy: default-src 'self' content-type: application/json; charset=utf-8 - date: Sat, 12 Jun 2021 01:53:09 GMT + date: Tue, 15 Jun 2021 22:29:03 GMT server: Kestrel strict-transport-security: max-age=31536000; includeSubDomains x-content-type-options: nosniff x-frame-options: SAMEORIGIN x-ms-build-version: 1.0.20210520-1-d6634624-develop x-ms-keyvault-network-info: conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=Ipv4; - x-ms-keyvault-region: centralus - x-ms-server-latency: '2005' + x-ms-keyvault-region: canadacentral + x-ms-server-latency: '2158' status: code: 200 message: OK - url: https://mcpatinotesthsm.managedhsm.azure.net/backup/c8a42738bf8643c9b7ffde9ef339d0e2/pending + url: https://mcpatinotesthsm.managedhsm.azure.net/backup/47fa9b06eeca494d901a9768bc4ecb15/pending - request: - body: '{"sasTokenParameters": {"token": "redacted", "storageResourceUri": "https://storname.blob.core.windows.net/containerk7lnzcoq4emt6gf"}, - "folderToRestore": "mhsm-mcpatinotesthsm-2021061201525788"}' + body: '{"sasTokenParameters": {"storageResourceUri": "https://storname.blob.core.windows.net/containerwt3qjyb4eluro3w", + "token": "redacted"}, "folderToRestore": "mhsm-mcpatinotesthsm-2021061522285112"}' headers: Accept: - application/json Content-Length: - - '319' + - '317' Content-Type: - application/json User-Agent: - - azsdk-python-keyvault-administration/4.0.0b4 Python/3.5.3 (Windows-10-10.0.19041-SP0) + - azsdk-python-keyvault-administration/4.0.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) method: PUT uri: https://managedhsm/restore?api-version=7.2 response: body: - string: '{"endTime":null,"error":null,"jobId":"3098a29f4f5147bf86931ca9b6b75933","startTime":1623462792,"status":"InProgress","statusDetails":null}' + string: '{"endTime":null,"error":null,"jobId":"ddd5713e65594a7da0f01a23de7216b2","startTime":1623796145,"status":"InProgress","statusDetails":null}' headers: - azure-asyncoperation: https://managedhsm/restore/3098a29f4f5147bf86931ca9b6b75933/pending + azure-asyncoperation: https://managedhsm/restore/ddd5713e65594a7da0f01a23de7216b2/pending cache-control: no-cache content-length: '138' content-security-policy: default-src 'self' content-type: application/json; charset=utf-8 - date: Sat, 12 Jun 2021 01:53:12 GMT + date: Tue, 15 Jun 2021 22:29:05 GMT server: Kestrel strict-transport-security: max-age=31536000; includeSubDomains x-content-type-options: nosniff x-frame-options: SAMEORIGIN x-ms-keyvault-network-info: conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=Ipv4; - x-ms-keyvault-region: centralus - x-ms-server-latency: '2925' + x-ms-keyvault-region: canadacentral + x-ms-server-latency: '2440' status: code: 202 message: '' @@ -131,140 +132,56 @@ interactions: body: null headers: User-Agent: - - azsdk-python-keyvault-administration/4.0.0b4 Python/3.5.3 (Windows-10-10.0.19041-SP0) + - azsdk-python-keyvault-administration/4.0.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) method: GET - uri: https://managedhsm/restore/3098a29f4f5147bf86931ca9b6b75933/pending + uri: https://managedhsm/restore/ddd5713e65594a7da0f01a23de7216b2/pending response: body: - string: '{"endTime":null,"error":null,"jobId":"3098a29f4f5147bf86931ca9b6b75933","startTime":1623462792,"status":"InProgress","statusDetails":null}' + string: '{"endTime":null,"error":null,"jobId":"ddd5713e65594a7da0f01a23de7216b2","startTime":1623796145,"status":"InProgress","statusDetails":null}' headers: cache-control: no-cache content-length: '138' content-security-policy: default-src 'self' content-type: application/json; charset=utf-8 - date: Sat, 12 Jun 2021 01:53:14 GMT + date: Tue, 15 Jun 2021 22:29:17 GMT server: Kestrel strict-transport-security: max-age=31536000; includeSubDomains x-content-type-options: nosniff x-frame-options: SAMEORIGIN x-ms-build-version: 1.0.20210520-1-d6634624-develop x-ms-keyvault-network-info: conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=Ipv4; - x-ms-keyvault-region: centralus - x-ms-server-latency: '2214' + x-ms-keyvault-region: canadacentral + x-ms-server-latency: '2234' status: code: 200 message: OK - url: https://mcpatinotesthsm.managedhsm.azure.net/restore/3098a29f4f5147bf86931ca9b6b75933/pending + url: https://mcpatinotesthsm.managedhsm.azure.net/restore/ddd5713e65594a7da0f01a23de7216b2/pending - request: body: null headers: User-Agent: - - azsdk-python-keyvault-administration/4.0.0b4 Python/3.5.3 (Windows-10-10.0.19041-SP0) + - azsdk-python-keyvault-administration/4.0.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) method: GET - uri: https://managedhsm/restore/3098a29f4f5147bf86931ca9b6b75933/pending + uri: https://managedhsm/restore/ddd5713e65594a7da0f01a23de7216b2/pending response: body: - string: '{"endTime":null,"error":null,"jobId":"3098a29f4f5147bf86931ca9b6b75933","startTime":1623462792,"status":"InProgress","statusDetails":null}' - headers: - cache-control: no-cache - content-length: '138' - content-security-policy: default-src 'self' - content-type: application/json; charset=utf-8 - date: Sat, 12 Jun 2021 01:53:17 GMT - server: Kestrel - strict-transport-security: max-age=31536000; includeSubDomains - x-content-type-options: nosniff - x-frame-options: SAMEORIGIN - x-ms-build-version: 1.0.20210520-1-d6634624-develop - x-ms-keyvault-network-info: conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=Ipv4; - x-ms-keyvault-region: centralus - x-ms-server-latency: '2164' - status: - code: 200 - message: OK - url: https://mcpatinotesthsm.managedhsm.azure.net/restore/3098a29f4f5147bf86931ca9b6b75933/pending -- request: - body: null - headers: - User-Agent: - - azsdk-python-keyvault-administration/4.0.0b4 Python/3.5.3 (Windows-10-10.0.19041-SP0) - method: GET - uri: https://managedhsm/restore/3098a29f4f5147bf86931ca9b6b75933/pending - response: - body: - string: '{"endTime":null,"error":null,"jobId":"3098a29f4f5147bf86931ca9b6b75933","startTime":1623462792,"status":"InProgress","statusDetails":null}' - headers: - cache-control: no-cache - content-length: '138' - content-security-policy: default-src 'self' - content-type: application/json; charset=utf-8 - date: Sat, 12 Jun 2021 01:53:24 GMT - server: Kestrel - strict-transport-security: max-age=31536000; includeSubDomains - x-content-type-options: nosniff - x-frame-options: SAMEORIGIN - x-ms-build-version: 1.0.20210520-1-d6634624-develop - x-ms-keyvault-network-info: conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=Ipv4; - x-ms-keyvault-region: centralus - x-ms-server-latency: '2095' - status: - code: 200 - message: OK - url: https://mcpatinotesthsm.managedhsm.azure.net/restore/3098a29f4f5147bf86931ca9b6b75933/pending -- request: - body: null - headers: - User-Agent: - - azsdk-python-keyvault-administration/4.0.0b4 Python/3.5.3 (Windows-10-10.0.19041-SP0) - method: GET - uri: https://managedhsm/restore/3098a29f4f5147bf86931ca9b6b75933/pending - response: - body: - string: '{"endTime":1623462809,"error":null,"jobId":"3098a29f4f5147bf86931ca9b6b75933","startTime":1623462792,"status":"Succeeded","statusDetails":null}' - headers: - cache-control: no-cache - content-length: '143' - content-security-policy: default-src 'self' - content-type: application/json; charset=utf-8 - date: Sat, 12 Jun 2021 01:53:31 GMT - server: Kestrel - strict-transport-security: max-age=31536000; includeSubDomains - x-content-type-options: nosniff - x-frame-options: SAMEORIGIN - x-ms-build-version: 1.0.20210520-1-d6634624-develop - x-ms-keyvault-network-info: conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=Ipv4; - x-ms-keyvault-region: centralus - x-ms-server-latency: '3126' - status: - code: 200 - message: OK - url: https://mcpatinotesthsm.managedhsm.azure.net/restore/3098a29f4f5147bf86931ca9b6b75933/pending -- request: - body: null - headers: - User-Agent: - - azsdk-python-keyvault-administration/4.0.0b4 Python/3.5.3 (Windows-10-10.0.19041-SP0) - method: GET - uri: https://managedhsm/restore/3098a29f4f5147bf86931ca9b6b75933/pending - response: - body: - string: '{"endTime":1623462809,"error":null,"jobId":"3098a29f4f5147bf86931ca9b6b75933","startTime":1623462792,"status":"Succeeded","statusDetails":null}' + string: '{"endTime":1623796163,"error":null,"jobId":"ddd5713e65594a7da0f01a23de7216b2","startTime":1623796145,"status":"Succeeded","statusDetails":null}' headers: cache-control: no-cache content-length: '143' content-security-policy: default-src 'self' content-type: application/json; charset=utf-8 - date: Sat, 12 Jun 2021 01:53:39 GMT + date: Tue, 15 Jun 2021 22:29:25 GMT server: Kestrel strict-transport-security: max-age=31536000; includeSubDomains x-content-type-options: nosniff x-frame-options: SAMEORIGIN x-ms-build-version: 1.0.20210520-1-d6634624-develop x-ms-keyvault-network-info: conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=Ipv4; - x-ms-keyvault-region: centralus - x-ms-server-latency: '2115' + x-ms-keyvault-region: canadacentral + x-ms-server-latency: '2448' status: code: 200 message: OK - url: https://mcpatinotesthsm.managedhsm.azure.net/restore/3098a29f4f5147bf86931ca9b6b75933/pending + url: https://mcpatinotesthsm.managedhsm.azure.net/restore/ddd5713e65594a7da0f01a23de7216b2/pending version: 1 diff --git a/sdk/keyvault/azure-keyvault-administration/tests/recordings/test_examples_administration.test_example_selective_key_restore.yaml b/sdk/keyvault/azure-keyvault-administration/tests/recordings/test_examples_administration.test_example_selective_key_restore.yaml new file mode 100644 index 000000000000..3e2abcc9a5b5 --- /dev/null +++ b/sdk/keyvault/azure-keyvault-administration/tests/recordings/test_examples_administration.test_example_selective_key_restore.yaml @@ -0,0 +1,374 @@ +interactions: +- request: + body: null + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '0' + Content-Type: + - application/json + User-Agent: + - azsdk-python-keyvault-keys/4.4.0 Python/3.9.0 (Windows-10-10.0.19041-SP0) + method: POST + uri: https://managedhsm/keys/selective-restore-test-key45031a2b/create?api-version=7.2 + response: + body: + string: '' + headers: + cache-control: + - no-cache + content-length: + - '0' + content-security-policy: + - default-src 'self' + content-type: + - application/json; charset=utf-8 + strict-transport-security: + - max-age=31536000; includeSubDomains + www-authenticate: + - Bearer authorization="https://login.microsoftonline.com/00000000-0000-0000-0000-000000000000", + resource="https://managedhsm.azure.net" + x-content-type-options: + - nosniff + x-frame-options: + - SAMEORIGIN + x-ms-server-latency: + - '0' + status: + code: 401 + message: Unauthorized +- request: + body: '{"kty": "RSA"}' + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '14' + Content-Type: + - application/json + User-Agent: + - azsdk-python-keyvault-keys/4.4.0 Python/3.9.0 (Windows-10-10.0.19041-SP0) + method: POST + uri: https://managedhsm/keys/selective-restore-test-key45031a2b/create?api-version=7.2 + response: + body: + string: '{"attributes":{"created":1623797343,"enabled":true,"exportable":false,"recoverableDays":90,"recoveryLevel":"Recoverable+Purgeable","updated":1623797343},"key":{"e":"AQAB","key_ops":["wrapKey","decrypt","encrypt","unwrapKey","sign","verify"],"kid":"https://managedhsm/keys/selective-restore-test-key45031a2b/755569fadcde0c7d2c2f634a76887ff1","kty":"RSA-HSM","n":"h7pkGvU2ejRfHUEoKF-ix-JjSbuaFq7Y4P4WvlnMybJo1o5B6c0adPvxDBztZt540RecfPEi4CQLkrwPtLGg_6L0nm2iIAJTIFXaUsTUKQJqgfueojpRifqSbItSPcex7sc-8gPVABW2Y1zDqsaWuTbIgfWErFF5X_A8c8-TK6aEUdeHUP0AfIyDIpoht1l84w_L8guG5k29lg8wGaT__wDcqVerhZGkRxsBoLDiJOgXTy-5Lhk14x8lRtsGudLKLggGBFAeGFbryP-q0U3FsGe2swQzGoz7xRn9ncUud-KAC6on1mdTODYXOkZgmnIDV4L5nGJCK3ukGWl6SWBP7w"}}' + headers: + cache-control: + - no-cache + content-length: + - '733' + content-security-policy: + - default-src 'self' + content-type: + - application/json; charset=utf-8 + strict-transport-security: + - max-age=31536000; includeSubDomains + x-content-type-options: + - nosniff + x-frame-options: + - SAMEORIGIN + x-ms-keyvault-network-info: + - conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=Ipv4; + x-ms-keyvault-region: + - canadacentral + x-ms-server-latency: + - '240' + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '0' + Content-Type: + - application/json + User-Agent: + - azsdk-python-keyvault-administration/4.0.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) + method: POST + uri: https://managedhsm/backup?api-version=7.2 + response: + body: + string: '' + headers: + cache-control: + - no-cache + content-length: + - '0' + content-security-policy: + - default-src 'self' + content-type: + - application/json; charset=utf-8 + strict-transport-security: + - max-age=31536000; includeSubDomains + www-authenticate: + - Bearer authorization="https://login.microsoftonline.com/00000000-0000-0000-0000-000000000000", + resource="https://managedhsm.azure.net" + x-content-type-options: + - nosniff + x-frame-options: + - SAMEORIGIN + x-ms-server-latency: + - '1' + status: + code: 401 + message: Unauthorized +- request: + body: '{"storageResourceUri": "https://storname.blob.core.windows.net/containeruu6bhqlnypomdm7", + "token": "redacted"}' + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '237' + Content-Type: + - application/json + User-Agent: + - azsdk-python-keyvault-administration/4.0.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) + method: POST + uri: https://managedhsm/backup?api-version=7.2 + response: + body: + string: '{"status":"InProgress","statusDetails":null,"error":null,"startTime":1623797347,"endTime":null,"jobId":"31b6b0454fc94f7f908474446526e57c","azureStorageBlobContainerUri":null}' + headers: + azure-asyncoperation: + - https://managedhsm/backup/31b6b0454fc94f7f908474446526e57c/pending + cache-control: + - no-cache + content-length: + - '174' + content-security-policy: + - default-src 'self' + content-type: + - application/json; charset=utf-8 + date: + - Tue, 15 Jun 2021 22:49:06 GMT + server: + - Kestrel + strict-transport-security: + - max-age=31536000; includeSubDomains + x-content-type-options: + - nosniff + x-frame-options: + - SAMEORIGIN + x-ms-keyvault-network-info: + - conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=Ipv4; + x-ms-keyvault-region: + - canadacentral + x-ms-server-latency: + - '2494' + status: + code: 202 + message: '' +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-keyvault-administration/4.0.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) + method: GET + uri: https://managedhsm/backup/31b6b0454fc94f7f908474446526e57c/pending + response: + body: + string: '{"azureStorageBlobContainerUri":"https://storname.blob.core.windows.net/containeruu6bhqlnypomdm7/mhsm-mcpatinotesthsm-2021061522490750","endTime":1623797358,"error":null,"jobId":"31b6b0454fc94f7f908474446526e57c","startTime":1623797347,"status":"Succeeded","statusDetails":null}' + headers: + cache-control: + - no-cache + content-length: + - '294' + content-security-policy: + - default-src 'self' + content-type: + - application/json; charset=utf-8 + date: + - Tue, 15 Jun 2021 22:49:19 GMT + server: + - Kestrel + strict-transport-security: + - max-age=31536000; includeSubDomains + x-content-type-options: + - nosniff + x-frame-options: + - SAMEORIGIN + x-ms-build-version: + - 1.0.20210520-1-d6634624-develop + x-ms-keyvault-network-info: + - conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=Ipv4; + x-ms-keyvault-region: + - canadacentral + x-ms-server-latency: + - '2229' + status: + code: 200 + message: OK +- request: + body: '{"sasTokenParameters": {"storageResourceUri": "https://storname.blob.core.windows.net/containeruu6bhqlnypomdm7", + "token": "redacted"}, "folder": "mhsm-mcpatinotesthsm-2021061522490750"}' + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '312' + Content-Type: + - application/json + User-Agent: + - azsdk-python-keyvault-administration/4.0.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) + method: PUT + uri: https://managedhsm/keys/selective-restore-test-key45031a2b/restore?api-version=7.2 + response: + body: + string: '{"endTime":null,"error":null,"jobId":"46ce6cd28e9441cf812298a062c427cd","startTime":1623797362,"status":"InProgress","statusDetails":null}' + headers: + azure-asyncoperation: + - https://managedhsm/restore/46ce6cd28e9441cf812298a062c427cd/pending + cache-control: + - no-cache + content-length: + - '138' + content-security-policy: + - default-src 'self' + content-type: + - application/json; charset=utf-8 + date: + - Tue, 15 Jun 2021 22:49:22 GMT + server: + - Kestrel + strict-transport-security: + - max-age=31536000; includeSubDomains + x-content-type-options: + - nosniff + x-frame-options: + - SAMEORIGIN + x-ms-keyvault-network-info: + - conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=Ipv4; + x-ms-keyvault-region: + - canadacentral + x-ms-server-latency: + - '2599' + status: + code: 202 + message: '' +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-keyvault-administration/4.0.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) + method: GET + uri: https://managedhsm/restore/46ce6cd28e9441cf812298a062c427cd/pending + response: + body: + string: '{"endTime":null,"error":null,"jobId":"46ce6cd28e9441cf812298a062c427cd","startTime":1623797362,"status":"InProgress","statusDetails":null}' + headers: + cache-control: + - no-cache + content-length: + - '138' + content-security-policy: + - default-src 'self' + content-type: + - application/json; charset=utf-8 + date: + - Tue, 15 Jun 2021 22:49:34 GMT + server: + - Kestrel + strict-transport-security: + - max-age=31536000; includeSubDomains + x-content-type-options: + - nosniff + x-frame-options: + - SAMEORIGIN + x-ms-build-version: + - 1.0.20210520-1-d6634624-develop + x-ms-keyvault-network-info: + - conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=Ipv4; + x-ms-keyvault-region: + - canadacentral + x-ms-server-latency: + - '2284' + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-keyvault-administration/4.0.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) + method: GET + uri: https://managedhsm/restore/46ce6cd28e9441cf812298a062c427cd/pending + response: + body: + string: '{"endTime":1623797379,"error":null,"jobId":"46ce6cd28e9441cf812298a062c427cd","startTime":1623797362,"status":"Succeeded","statusDetails":"Number + of successful key versions restored: 0, Number of key versions could not overwrite: + 2"}' + headers: + cache-control: + - no-cache + content-length: + - '233' + content-security-policy: + - default-src 'self' + content-type: + - application/json; charset=utf-8 + date: + - Tue, 15 Jun 2021 22:49:42 GMT + server: + - Kestrel + strict-transport-security: + - max-age=31536000; includeSubDomains + x-content-type-options: + - nosniff + x-frame-options: + - SAMEORIGIN + x-ms-build-version: + - 1.0.20210520-1-d6634624-develop + x-ms-keyvault-network-info: + - conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=Ipv4; + x-ms-keyvault-region: + - canadacentral + x-ms-server-latency: + - '2237' + status: + code: 200 + message: OK +version: 1 diff --git a/sdk/keyvault/azure-keyvault-administration/tests/recordings/test_examples_administration_async.test_example_selective_key_restore.yaml b/sdk/keyvault/azure-keyvault-administration/tests/recordings/test_examples_administration_async.test_example_selective_key_restore.yaml new file mode 100644 index 000000000000..2188861da0aa --- /dev/null +++ b/sdk/keyvault/azure-keyvault-administration/tests/recordings/test_examples_administration_async.test_example_selective_key_restore.yaml @@ -0,0 +1,251 @@ +interactions: +- request: + body: null + headers: + Accept: + - application/json + Content-Length: + - '0' + Content-Type: + - application/json + User-Agent: + - azsdk-python-keyvault-keys/4.4.0 Python/3.9.0 (Windows-10-10.0.19041-SP0) + method: POST + uri: https://managedhsm/keys/selective-restore-test-keyeb471ca8/create?api-version=7.2 + response: + body: + string: '' + headers: + cache-control: no-cache + content-length: '0' + content-security-policy: default-src 'self' + content-type: application/json; charset=utf-8 + strict-transport-security: max-age=31536000; includeSubDomains + www-authenticate: Bearer authorization="https://login.microsoftonline.com/00000000-0000-0000-0000-000000000000", + resource="https://managedhsm.azure.net" + x-content-type-options: nosniff + x-frame-options: SAMEORIGIN + x-ms-server-latency: '1' + status: + code: 401 + message: Unauthorized + url: https://mcpatinotesthsm.managedhsm.azure.net/keys/selective-restore-test-keyeb471ca8/create?api-version=7.2 +- request: + body: '{"kty": "RSA"}' + headers: + Accept: + - application/json + Content-Length: + - '14' + Content-Type: + - application/json + User-Agent: + - azsdk-python-keyvault-keys/4.4.0 Python/3.9.0 (Windows-10-10.0.19041-SP0) + method: POST + uri: https://managedhsm/keys/selective-restore-test-keyeb471ca8/create?api-version=7.2 + response: + body: + string: '{"attributes":{"created":1623797248,"enabled":true,"exportable":false,"recoverableDays":90,"recoveryLevel":"Recoverable+Purgeable","updated":1623797248},"key":{"e":"AQAB","key_ops":["wrapKey","sign","verify","encrypt","decrypt","unwrapKey"],"kid":"https://managedhsm/keys/selective-restore-test-keyeb471ca8/34023c9201ad47252856f0b3de27ed33","kty":"RSA-HSM","n":"lsBV67RlqkwhehhfOw0xPNOJt4PI6F9EnNnxzICymqJY6Kn4MmlUO2I8ahL2tBnP2s2xFU9h9cQsoIH0uulOqtUOyWt8p_vQ1D1QKKZnnFU4TUzeomTUI8o17OkgJ9uIcUh8O2Yk5igfUU2Nqkp4BFVxxwiWC0HU0wVoOEhJQ35d2Hv9ShfW2MS5b-scvsN6Gav66gj1MppmvuQ4mQgOoBPakW6HNvSEx2mlQIPJeMIGlLyxcGuknWc59Vtf2PK0lJjzZBpoPEeQ5kjWM5vy6l_a_umTxJz1jgwU1-gDWFnR5jRYRquOWTzkFmOgDdMZUvcO8PjthFen5p9d597wTQ"}}' + headers: + cache-control: no-cache + content-length: '733' + content-security-policy: default-src 'self' + content-type: application/json; charset=utf-8 + strict-transport-security: max-age=31536000; includeSubDomains + x-content-type-options: nosniff + x-frame-options: SAMEORIGIN + x-ms-keyvault-network-info: conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=Ipv4; + x-ms-keyvault-region: canadacentral + x-ms-server-latency: '396' + status: + code: 200 + message: OK + url: https://mcpatinotesthsm.managedhsm.azure.net/keys/selective-restore-test-keyeb471ca8/create?api-version=7.2 +- request: + body: null + headers: + Accept: + - application/json + Content-Length: + - '0' + Content-Type: + - application/json + User-Agent: + - azsdk-python-keyvault-administration/4.0.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) + method: POST + uri: https://managedhsm/backup?api-version=7.2 + response: + body: + string: '' + headers: + cache-control: no-cache + content-length: '0' + content-security-policy: default-src 'self' + content-type: application/json; charset=utf-8 + strict-transport-security: max-age=31536000; includeSubDomains + www-authenticate: Bearer authorization="https://login.microsoftonline.com/00000000-0000-0000-0000-000000000000", + resource="https://managedhsm.azure.net" + x-content-type-options: nosniff + x-frame-options: SAMEORIGIN + x-ms-server-latency: '0' + status: + code: 401 + message: Unauthorized + url: https://mcpatinotesthsm.managedhsm.azure.net/backup?api-version=7.2 +- request: + body: '{"storageResourceUri": "https://storname.blob.core.windows.net/containerrjm5mcjmdhlohml", + "token": "redacted"}' + headers: + Accept: + - application/json + Content-Length: + - '237' + Content-Type: + - application/json + User-Agent: + - azsdk-python-keyvault-administration/4.0.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) + method: POST + uri: https://managedhsm/backup?api-version=7.2 + response: + body: + string: '{"status":"InProgress","statusDetails":null,"error":null,"startTime":1623797252,"endTime":null,"jobId":"d2df9b7e97a64b87b457c3ab1c2af826","azureStorageBlobContainerUri":null}' + headers: + azure-asyncoperation: https://managedhsm/backup/d2df9b7e97a64b87b457c3ab1c2af826/pending + cache-control: no-cache + content-length: '174' + content-security-policy: default-src 'self' + content-type: application/json; charset=utf-8 + date: Tue, 15 Jun 2021 22:47:32 GMT + server: Kestrel + strict-transport-security: max-age=31536000; includeSubDomains + x-content-type-options: nosniff + x-frame-options: SAMEORIGIN + x-ms-keyvault-network-info: conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=Ipv4; + x-ms-keyvault-region: canadacentral + x-ms-server-latency: '2490' + status: + code: 202 + message: '' + url: https://mcpatinotesthsm.managedhsm.azure.net/backup?api-version=7.2 +- request: + body: null + headers: + User-Agent: + - azsdk-python-keyvault-administration/4.0.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) + method: GET + uri: https://managedhsm/backup/d2df9b7e97a64b87b457c3ab1c2af826/pending + response: + body: + string: '{"azureStorageBlobContainerUri":"https://storname.blob.core.windows.net/containerrjm5mcjmdhlohml/mhsm-mcpatinotesthsm-2021061522473215","endTime":1623797262,"error":null,"jobId":"d2df9b7e97a64b87b457c3ab1c2af826","startTime":1623797252,"status":"Succeeded","statusDetails":null}' + headers: + cache-control: no-cache + content-length: '294' + content-security-policy: default-src 'self' + content-type: application/json; charset=utf-8 + date: Tue, 15 Jun 2021 22:47:44 GMT + server: Kestrel + strict-transport-security: max-age=31536000; includeSubDomains + x-content-type-options: nosniff + x-frame-options: SAMEORIGIN + x-ms-build-version: 1.0.20210520-1-d6634624-develop + x-ms-keyvault-network-info: conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=Ipv4; + x-ms-keyvault-region: canadacentral + x-ms-server-latency: '2438' + status: + code: 200 + message: OK + url: https://mcpatinotesthsm.managedhsm.azure.net/backup/d2df9b7e97a64b87b457c3ab1c2af826/pending +- request: + body: '{"sasTokenParameters": {"storageResourceUri": "https://storname.blob.core.windows.net/containerrjm5mcjmdhlohml", + "token": "redacted"}, "folder": "mhsm-mcpatinotesthsm-2021061522473215"}' + headers: + Accept: + - application/json + Content-Length: + - '312' + Content-Type: + - application/json + User-Agent: + - azsdk-python-keyvault-administration/4.0.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) + method: PUT + uri: https://managedhsm/keys/selective-restore-test-keyeb471ca8/restore?api-version=7.2 + response: + body: + string: '{"endTime":null,"error":null,"jobId":"fa9cfececf4f490782962a93e67077ce","startTime":1623797267,"status":"InProgress","statusDetails":null}' + headers: + azure-asyncoperation: https://managedhsm/restore/fa9cfececf4f490782962a93e67077ce/pending + cache-control: no-cache + content-length: '138' + content-security-policy: default-src 'self' + content-type: application/json; charset=utf-8 + date: Tue, 15 Jun 2021 22:47:47 GMT + server: Kestrel + strict-transport-security: max-age=31536000; includeSubDomains + x-content-type-options: nosniff + x-frame-options: SAMEORIGIN + x-ms-keyvault-network-info: conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=Ipv4; + x-ms-keyvault-region: canadacentral + x-ms-server-latency: '2795' + status: + code: 202 + message: '' + url: https://mcpatinotesthsm.managedhsm.azure.net/keys/selective-restore-test-keyeb471ca8/restore?api-version=7.2 +- request: + body: null + headers: + User-Agent: + - azsdk-python-keyvault-administration/4.0.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) + method: GET + uri: https://managedhsm/restore/fa9cfececf4f490782962a93e67077ce/pending + response: + body: + string: '{"endTime":null,"error":null,"jobId":"fa9cfececf4f490782962a93e67077ce","startTime":1623797267,"status":"InProgress","statusDetails":null}' + headers: + cache-control: no-cache + content-length: '138' + content-security-policy: default-src 'self' + content-type: application/json; charset=utf-8 + date: Tue, 15 Jun 2021 22:47:59 GMT + server: Kestrel + strict-transport-security: max-age=31536000; includeSubDomains + x-content-type-options: nosniff + x-frame-options: SAMEORIGIN + x-ms-build-version: 1.0.20210520-1-d6634624-develop + x-ms-keyvault-network-info: conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=Ipv4; + x-ms-keyvault-region: canadacentral + x-ms-server-latency: '2378' + status: + code: 200 + message: OK + url: https://mcpatinotesthsm.managedhsm.azure.net/restore/fa9cfececf4f490782962a93e67077ce/pending +- request: + body: null + headers: + User-Agent: + - azsdk-python-keyvault-administration/4.0.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) + method: GET + uri: https://managedhsm/restore/fa9cfececf4f490782962a93e67077ce/pending + response: + body: + string: '{"endTime":1623797284,"error":null,"jobId":"fa9cfececf4f490782962a93e67077ce","startTime":1623797267,"status":"Succeeded","statusDetails":"Number + of successful key versions restored: 0, Number of key versions could not overwrite: + 3"}' + headers: + cache-control: no-cache + content-length: '233' + content-security-policy: default-src 'self' + content-type: application/json; charset=utf-8 + date: Tue, 15 Jun 2021 22:48:07 GMT + server: Kestrel + strict-transport-security: max-age=31536000; includeSubDomains + x-content-type-options: nosniff + x-frame-options: SAMEORIGIN + x-ms-build-version: 1.0.20210520-1-d6634624-develop + x-ms-keyvault-network-info: conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=Ipv4; + x-ms-keyvault-region: canadacentral + x-ms-server-latency: '2348' + status: + code: 200 + message: OK + url: https://mcpatinotesthsm.managedhsm.azure.net/restore/fa9cfececf4f490782962a93e67077ce/pending +version: 1 diff --git a/sdk/keyvault/azure-keyvault-administration/tests/test_backup_client.py b/sdk/keyvault/azure-keyvault-administration/tests/test_backup_client.py index a0e3e4ddfdac..d02ba3089ecc 100644 --- a/sdk/keyvault/azure-keyvault-administration/tests/test_backup_client.py +++ b/sdk/keyvault/azure-keyvault-administration/tests/test_backup_client.py @@ -41,7 +41,15 @@ def tearDown(self): @property def credential(self): if self.is_live: - return DefaultAzureCredential() + from dotenv import load_dotenv + from azure.identity import ClientSecretCredential + import os + load_dotenv() + return ClientSecretCredential( + tenant_id=os.environ["KEYVAULT_TENANT_ID"], + client_id=os.environ["KEYVAULT_CLIENT_ID"], + client_secret=os.environ["KEYVAULT_CLIENT_SECRET"] + ) return mock.Mock(get_token=lambda *_, **__: AccessToken("secret", time.time() + 3600)) @ResourceGroupPreparer(random_name_enabled=True, use_cache=True) diff --git a/sdk/keyvault/azure-keyvault-administration/tests/test_backup_client_async.py b/sdk/keyvault/azure-keyvault-administration/tests/test_backup_client_async.py index 8c8d0e0f5707..a4c0df8e4f12 100644 --- a/sdk/keyvault/azure-keyvault-administration/tests/test_backup_client_async.py +++ b/sdk/keyvault/azure-keyvault-administration/tests/test_backup_client_async.py @@ -39,7 +39,15 @@ def tearDown(self): @property def credential(self): if self.is_live: - return DefaultAzureCredential() + from dotenv import load_dotenv + from azure.identity.aio import ClientSecretCredential + import os + load_dotenv() + return ClientSecretCredential( + tenant_id=os.environ["KEYVAULT_TENANT_ID"], + client_id=os.environ["KEYVAULT_CLIENT_ID"], + client_secret=os.environ["KEYVAULT_CLIENT_SECRET"] + ) async def get_token(*_, **__): return AccessToken("secret", time.time() + 3600) diff --git a/sdk/keyvault/azure-keyvault-administration/tests/test_examples_administration.py b/sdk/keyvault/azure-keyvault-administration/tests/test_examples_administration.py index f82b82ea92ac..8371c70b4c11 100644 --- a/sdk/keyvault/azure-keyvault-administration/tests/test_examples_administration.py +++ b/sdk/keyvault/azure-keyvault-administration/tests/test_examples_administration.py @@ -8,6 +8,7 @@ from azure.identity import DefaultAzureCredential from azure.keyvault.administration._internal import HttpChallengeCache from azure.keyvault.administration import KeyVaultBackupClient +from azure.keyvault.keys import KeyClient from devtools_testutils import ResourceGroupPreparer, StorageAccountPreparer import pytest from six.moves.urllib_parse import urlparse @@ -37,7 +38,15 @@ def tearDown(self): @property def credential(self): if self.is_live: - return DefaultAzureCredential() + from dotenv import load_dotenv + from azure.identity import ClientSecretCredential + import os + load_dotenv() + return ClientSecretCredential( + tenant_id=os.environ["KEYVAULT_TENANT_ID"], + client_id=os.environ["KEYVAULT_CLIENT_ID"], + client_secret=os.environ["KEYVAULT_CLIENT_SECRET"] + ) return mock.Mock(get_token=lambda *_, **__: AccessToken("secret", time.time() + 3600)) @ResourceGroupPreparer(random_name_enabled=True, use_cache=True) @@ -53,14 +62,15 @@ def test_example_backup_and_restore(self, container_uri, sas_token): # check if the backup completed done = backup_poller.done() - # get the final result + # block until the backup completes + # result() returns an object with a URL of the backup backup_operation = backup_poller.result() # [END begin_backup] folder_url = backup_operation.folder_url # [START begin_restore] - # begin a full vault restore; to restore a single key, use the key_name kwarg + # begin a full vault restore restore_poller = backup_client.begin_restore(folder_url, sas_token) # check if the restore completed @@ -69,3 +79,28 @@ def test_example_backup_and_restore(self, container_uri, sas_token): # wait for the restore to complete restore_poller.wait() # [END begin_restore] + + @ResourceGroupPreparer(random_name_enabled=True, use_cache=True) + @StorageAccountPreparer(random_name_enabled=True) + @BlobContainerPreparer() + def test_example_selective_key_restore(self, container_uri, sas_token): + # create a key to selectively restore + key_client = KeyClient(self.managed_hsm["url"], self.credential) + key_name = self.get_resource_name("selective-restore-test-key") + key_client.create_rsa_key(key_name) + + backup_client = KeyVaultBackupClient(self.managed_hsm["url"], self.credential) + backup_poller = backup_client.begin_backup(container_uri, sas_token) + backup_operation = backup_poller.result() + folder_url = backup_operation.folder_url + + # [START begin_selective_restore] + # begin a restore of a single key from a backed up vault + restore_poller = backup_client.begin_restore(folder_url, sas_token, key_name=key_name) + + # check if the restore completed + done = backup_poller.done() + + # wait for the restore to complete + restore_poller.wait() + # [END begin_selective_restore] diff --git a/sdk/keyvault/azure-keyvault-administration/tests/test_examples_administration_async.py b/sdk/keyvault/azure-keyvault-administration/tests/test_examples_administration_async.py index 4d19bd05d472..a2d560935726 100644 --- a/sdk/keyvault/azure-keyvault-administration/tests/test_examples_administration_async.py +++ b/sdk/keyvault/azure-keyvault-administration/tests/test_examples_administration_async.py @@ -10,6 +10,7 @@ from azure.identity.aio import DefaultAzureCredential from azure.keyvault.administration._internal import HttpChallengeCache from azure.keyvault.administration.aio import KeyVaultBackupClient +from azure.keyvault.keys.aio import KeyClient from devtools_testutils import ResourceGroupPreparer, StorageAccountPreparer import pytest @@ -37,7 +38,15 @@ def tearDown(self): @property def credential(self): if self.is_live: - return DefaultAzureCredential() + from dotenv import load_dotenv + from azure.identity.aio import ClientSecretCredential + import os + load_dotenv() + return ClientSecretCredential( + tenant_id=os.environ["KEYVAULT_TENANT_ID"], + client_id=os.environ["KEYVAULT_CLIENT_ID"], + client_secret=os.environ["KEYVAULT_CLIENT_SECRET"] + ) async def get_token(*_, **__): return AccessToken("secret", time.time() + 3600) @@ -54,16 +63,18 @@ async def test_example_backup_and_restore(self, container_uri, sas_token): # begin a vault backup backup_poller = await backup_client.begin_backup(container_uri, sas_token) + # check if the backup completed done = backup_poller.done() - # get the final result + # block until the backup completes + # result() returns an object with a URL of the backup backup_operation = await backup_poller.result() # [END begin_backup] folder_url = backup_operation.folder_url # [START begin_restore] - # begin a full vault restore; to restore a single key, use the key_name kwarg + # begin a full vault restore restore_poller = await backup_client.begin_restore(folder_url, sas_token) # check if the restore completed @@ -72,3 +83,28 @@ async def test_example_backup_and_restore(self, container_uri, sas_token): # wait for the restore to complete await restore_poller.wait() # [END begin_restore] + + @ResourceGroupPreparer(random_name_enabled=True, use_cache=True) + @StorageAccountPreparer(random_name_enabled=True) + @BlobContainerPreparer() + async def test_example_selective_key_restore(self, container_uri, sas_token): + # create a key to selectively restore + key_client = KeyClient(self.managed_hsm["url"], self.credential) + key_name = self.get_resource_name("selective-restore-test-key") + await key_client.create_rsa_key(key_name) + + backup_client = KeyVaultBackupClient(self.managed_hsm["url"], self.credential) + backup_poller = await backup_client.begin_backup(container_uri, sas_token) + backup_operation = await backup_poller.result() + folder_url = backup_operation.folder_url + + # [START begin_selective_restore] + # begin a restore of a single key from a backed up vault + restore_poller = await backup_client.begin_restore(folder_url, sas_token, key_name=key_name) + + # check if the restore completed + done = backup_poller.done() + + # wait for the restore to complete + await restore_poller.wait() + # [END begin_selective_restore] From 86efeb687b4fa45e1aa39e96c156269088edaf97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?McCoy=20Pati=C3=B1o?= Date: Tue, 15 Jun 2021 15:56:26 -0700 Subject: [PATCH 22/27] use poller.continuation_token() --- .../tests/test_backup_client.py | 18 +++++------------- .../tests/test_backup_client_async.py | 18 +++++------------- .../tests/test_examples_administration.py | 10 +--------- .../test_examples_administration_async.py | 10 +--------- 4 files changed, 12 insertions(+), 44 deletions(-) diff --git a/sdk/keyvault/azure-keyvault-administration/tests/test_backup_client.py b/sdk/keyvault/azure-keyvault-administration/tests/test_backup_client.py index d02ba3089ecc..4d5d19926985 100644 --- a/sdk/keyvault/azure-keyvault-administration/tests/test_backup_client.py +++ b/sdk/keyvault/azure-keyvault-administration/tests/test_backup_client.py @@ -41,15 +41,7 @@ def tearDown(self): @property def credential(self): if self.is_live: - from dotenv import load_dotenv - from azure.identity import ClientSecretCredential - import os - load_dotenv() - return ClientSecretCredential( - tenant_id=os.environ["KEYVAULT_TENANT_ID"], - client_id=os.environ["KEYVAULT_CLIENT_ID"], - client_secret=os.environ["KEYVAULT_CLIENT_SECRET"] - ) + return DefaultAzureCredential() return mock.Mock(get_token=lambda *_, **__: AccessToken("secret", time.time() + 3600)) @ResourceGroupPreparer(random_name_enabled=True, use_cache=True) @@ -78,7 +70,7 @@ def test_full_backup_and_restore_rehydration(self, container_uri, sas_token): backup_poller = backup_client.begin_backup(container_uri, sas_token) # create a new poller from a continuation token - token = backup_poller.polling_method().get_continuation_token() + token = backup_poller.continuation_token() rehydrated = backup_client.begin_backup(container_uri, sas_token, continuation_token=token) rehydrated_operation = rehydrated.result() @@ -90,7 +82,7 @@ def test_full_backup_and_restore_rehydration(self, container_uri, sas_token): restore_poller = backup_client.begin_restore(backup_operation.folder_url, sas_token) # create a new poller from a continuation token - token = restore_poller.polling_method().get_continuation_token() + token = restore_poller.continuation_token() rehydrated = backup_client.begin_restore(backup_operation.folder_url, sas_token, continuation_token=token) rehydrated.wait() @@ -132,7 +124,7 @@ def test_backup_client_polling(self, container_uri, sas_token): backup_poller = backup_client.begin_backup(container_uri, sas_token) # create a new poller from a continuation token - token = backup_poller.polling_method().get_continuation_token() + token = backup_poller.continuation_token() rehydrated = backup_client.begin_backup(container_uri, sas_token, continuation_token=token) # check that pollers and polling methods behave as expected @@ -162,7 +154,7 @@ def test_backup_client_polling(self, container_uri, sas_token): restore_poller = backup_client.begin_restore(backup_operation.folder_url, sas_token) # create a new poller from a continuation token - token = restore_poller.polling_method().get_continuation_token() + token = restore_poller.continuation_token() rehydrated = backup_client.begin_restore(backup_operation.folder_url, sas_token, continuation_token=token) # check that pollers and polling methods behave as expected diff --git a/sdk/keyvault/azure-keyvault-administration/tests/test_backup_client_async.py b/sdk/keyvault/azure-keyvault-administration/tests/test_backup_client_async.py index a4c0df8e4f12..c4607f848a59 100644 --- a/sdk/keyvault/azure-keyvault-administration/tests/test_backup_client_async.py +++ b/sdk/keyvault/azure-keyvault-administration/tests/test_backup_client_async.py @@ -39,15 +39,7 @@ def tearDown(self): @property def credential(self): if self.is_live: - from dotenv import load_dotenv - from azure.identity.aio import ClientSecretCredential - import os - load_dotenv() - return ClientSecretCredential( - tenant_id=os.environ["KEYVAULT_TENANT_ID"], - client_id=os.environ["KEYVAULT_CLIENT_ID"], - client_secret=os.environ["KEYVAULT_CLIENT_SECRET"] - ) + return DefaultAzureCredential() async def get_token(*_, **__): return AccessToken("secret", time.time() + 3600) @@ -80,7 +72,7 @@ async def test_full_backup_and_restore_rehydration(self, container_uri, sas_toke backup_poller = await backup_client.begin_backup(container_uri, sas_token) # create a new poller from a continuation token - token = backup_poller.polling_method().get_continuation_token() + token = backup_poller.continuation_token() rehydrated = await backup_client.begin_backup(container_uri, sas_token, continuation_token=token) rehydrated_operation = await rehydrated.result() @@ -92,7 +84,7 @@ async def test_full_backup_and_restore_rehydration(self, container_uri, sas_toke restore_poller = await backup_client.begin_restore(backup_operation.folder_url, sas_token) # create a new poller from a continuation token - token = restore_poller.polling_method().get_continuation_token() + token = restore_poller.continuation_token() rehydrated = await backup_client.begin_restore(backup_operation.folder_url, sas_token, continuation_token=token) await rehydrated.wait() @@ -132,7 +124,7 @@ async def test_backup_client_polling(self, container_uri, sas_token): backup_poller = await backup_client.begin_backup(container_uri, sas_token) # create a new poller from a continuation token - token = backup_poller.polling_method().get_continuation_token() + token = backup_poller.continuation_token() rehydrated = await backup_client.begin_backup(container_uri, sas_token, continuation_token=token) # check that pollers and polling methods behave as expected @@ -162,7 +154,7 @@ async def test_backup_client_polling(self, container_uri, sas_token): restore_poller = await backup_client.begin_restore(backup_operation.folder_url, sas_token) # create a new poller from a continuation token - token = restore_poller.polling_method().get_continuation_token() + token = restore_poller.continuation_token() rehydrated = await backup_client.begin_restore(backup_operation.folder_url, sas_token, continuation_token=token) # check that pollers and polling methods behave as expected diff --git a/sdk/keyvault/azure-keyvault-administration/tests/test_examples_administration.py b/sdk/keyvault/azure-keyvault-administration/tests/test_examples_administration.py index 8371c70b4c11..432558aa7693 100644 --- a/sdk/keyvault/azure-keyvault-administration/tests/test_examples_administration.py +++ b/sdk/keyvault/azure-keyvault-administration/tests/test_examples_administration.py @@ -38,15 +38,7 @@ def tearDown(self): @property def credential(self): if self.is_live: - from dotenv import load_dotenv - from azure.identity import ClientSecretCredential - import os - load_dotenv() - return ClientSecretCredential( - tenant_id=os.environ["KEYVAULT_TENANT_ID"], - client_id=os.environ["KEYVAULT_CLIENT_ID"], - client_secret=os.environ["KEYVAULT_CLIENT_SECRET"] - ) + return DefaultAzureCredential() return mock.Mock(get_token=lambda *_, **__: AccessToken("secret", time.time() + 3600)) @ResourceGroupPreparer(random_name_enabled=True, use_cache=True) diff --git a/sdk/keyvault/azure-keyvault-administration/tests/test_examples_administration_async.py b/sdk/keyvault/azure-keyvault-administration/tests/test_examples_administration_async.py index a2d560935726..2b575af0de0c 100644 --- a/sdk/keyvault/azure-keyvault-administration/tests/test_examples_administration_async.py +++ b/sdk/keyvault/azure-keyvault-administration/tests/test_examples_administration_async.py @@ -38,15 +38,7 @@ def tearDown(self): @property def credential(self): if self.is_live: - from dotenv import load_dotenv - from azure.identity.aio import ClientSecretCredential - import os - load_dotenv() - return ClientSecretCredential( - tenant_id=os.environ["KEYVAULT_TENANT_ID"], - client_id=os.environ["KEYVAULT_CLIENT_ID"], - client_secret=os.environ["KEYVAULT_CLIENT_SECRET"] - ) + return DefaultAzureCredential() async def get_token(*_, **__): return AccessToken("secret", time.time() + 3600) From 78ccc8d13bfdf8ce0253561c8d30650afba944c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?McCoy=20Pati=C3=B1o?= Date: Tue, 15 Jun 2021 17:35:26 -0700 Subject: [PATCH 23/27] Thanks, Charles! --- .../keyvault/administration/_backup_client.py | 31 ++++++++++++------- .../administration/aio/_backup_client.py | 16 +++++----- .../tests/test_backup_client.py | 11 ------- .../tests/test_backup_client_async.py | 11 ------- .../test_examples_administration_async.py | 2 +- 5 files changed, 28 insertions(+), 43 deletions(-) diff --git a/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_backup_client.py b/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_backup_client.py index 986f139fa4d2..6130228a606e 100644 --- a/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_backup_client.py +++ b/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_backup_client.py @@ -7,6 +7,7 @@ import pickle from typing import TYPE_CHECKING +from six import raise_from from six.moves.urllib_parse import urlparse from ._models import KeyVaultBackupOperation @@ -60,19 +61,22 @@ def begin_backup(self, blob_storage_url, sas_token, **kwargs): continuation_token = kwargs.pop("continuation_token", None) status_response = None if continuation_token: + status_url = base64.b64decode(continuation_token.encode()).decode("ascii") try: - job_id = _parse_status_url(base64.b64decode(continuation_token.encode()).decode("ascii")) - except: # pylint: disable=broad-except - raise ValueError( - "The provided continuation_token is malformed. A valid token can be obtained from the operation " - + "poller's continuation_token() method" + job_id = _parse_status_url(status_url) + except Exception as ex: # pylint: disable=broad-except + raise_from( + ValueError( + "The provided continuation_token is malformed. A valid token can be obtained from the operation " + + "poller's continuation_token() method" + ), + ex, ) pipeline_response = self._client.full_backup_status( vault_base_url=self._vault_url, job_id=job_id, cls=lambda pipeline_response, _, __: pipeline_response ) if "azure-asyncoperation" not in pipeline_response.http_response.headers: - status_url = base64.b64decode(continuation_token.encode()).decode("ascii") pipeline_response.http_response.headers["azure-asyncoperation"] = status_url status_response = base64.b64encode(pickle.dumps(pipeline_response)).decode("ascii") @@ -122,19 +126,22 @@ def begin_restore(self, folder_url, sas_token, **kwargs): status_response = None if continuation_token: + status_url = base64.b64decode(continuation_token.encode()).decode("ascii") try: - job_id = _parse_status_url(base64.b64decode(continuation_token.encode()).decode("ascii")) - except: # pylint: disable=broad-except - raise ValueError( - "The provided continuation_token is malformed. A valid token can be obtained from the operation " - + "poller's continuation_token() method" + job_id = _parse_status_url(status_url) + except Exception as ex: # pylint: disable=broad-except + raise_from( + ValueError( + "The provided continuation_token is malformed. A valid token can be obtained from the operation " + + "poller's continuation_token() method" + ), + ex, ) pipeline_response = self._client.restore_status( vault_base_url=self._vault_url, job_id=job_id, cls=lambda pipeline_response, _, __: pipeline_response ) if "azure-asyncoperation" not in pipeline_response.http_response.headers: - status_url = base64.b64decode(continuation_token.encode()).decode("ascii") pipeline_response.http_response.headers["azure-asyncoperation"] = status_url status_response = base64.b64encode(pickle.dumps(pipeline_response)).decode("ascii") diff --git a/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/aio/_backup_client.py b/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/aio/_backup_client.py index e5f8f5a26737..60a0ed91dd16 100644 --- a/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/aio/_backup_client.py +++ b/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/aio/_backup_client.py @@ -54,19 +54,19 @@ async def begin_backup( continuation_token = kwargs.pop("continuation_token", None) status_response = None if continuation_token: + status_url = base64.b64decode(continuation_token.encode()).decode("ascii") try: - job_id = _parse_status_url(base64.b64decode(continuation_token.encode()).decode("ascii")) - except: # pylint: disable=broad-except + job_id = _parse_status_url(status_url) + except Exception as ex: # pylint: disable=broad-except raise ValueError( "The provided continuation_token is malformed. A valid token can be obtained from the operation " + "poller's continuation_token() method" - ) + ) from ex pipeline_response = await self._client.full_backup_status( vault_base_url=self._vault_url, job_id=job_id, cls=lambda pipeline_response, _, __: pipeline_response ) if "azure-asyncoperation" not in pipeline_response.http_response.headers: - status_url = base64.b64decode(continuation_token.encode()).decode("ascii") pipeline_response.http_response.headers["azure-asyncoperation"] = status_url status_response = base64.b64encode(pickle.dumps(pipeline_response)).decode("ascii") @@ -116,19 +116,19 @@ async def begin_restore(self, folder_url: str, sas_token: str, **kwargs: "Any") status_response = None if continuation_token: + status_url = base64.b64decode(continuation_token.encode()).decode("ascii") try: - job_id = _parse_status_url(base64.b64decode(continuation_token.encode()).decode("ascii")) - except: # pylint: disable=broad-except + job_id = _parse_status_url(status_url) + except Exception as ex: # pylint: disable=broad-except raise ValueError( "The provided continuation_token is malformed. A valid token can be obtained from the operation " + "poller's continuation_token() method" - ) + ) from ex pipeline_response = await self._client.restore_status( vault_base_url=self._vault_url, job_id=job_id, cls=lambda pipeline_response, _, __: pipeline_response ) if "azure-asyncoperation" not in pipeline_response.http_response.headers: - status_url = base64.b64decode(continuation_token.encode()).decode("ascii") pipeline_response.http_response.headers["azure-asyncoperation"] = status_url status_response = base64.b64encode(pickle.dumps(pipeline_response)).decode("ascii") diff --git a/sdk/keyvault/azure-keyvault-administration/tests/test_backup_client.py b/sdk/keyvault/azure-keyvault-administration/tests/test_backup_client.py index 4d5d19926985..e922a60d6e23 100644 --- a/sdk/keyvault/azure-keyvault-administration/tests/test_backup_client.py +++ b/sdk/keyvault/azure-keyvault-administration/tests/test_backup_client.py @@ -133,11 +133,6 @@ def test_backup_client_polling(self, container_uri, sas_token): assert rehydrated.status() == "InProgress" assert not rehydrated.done() or rehydrated.polling_method().finished() - backup_poller.polling_method().update_status() - assert backup_poller.status() == "InProgress" - rehydrated.polling_method().update_status() - assert rehydrated.status() == "InProgress" - backup_operation = backup_poller.result() assert backup_poller.status() == "Succeeded" and backup_poller.polling_method().status() == "Succeeded" rehydrated_operation = rehydrated.result() @@ -146,7 +141,6 @@ def test_backup_client_polling(self, container_uri, sas_token): # rehydrate a poller with a continuation token of a completed operation late_rehydrated = backup_client.begin_backup(container_uri, sas_token, continuation_token=token) - assert late_rehydrated.status() == "InProgress" late_rehydrated.polling_method().update_status() assert late_rehydrated.status() == "Succeeded" @@ -163,11 +157,6 @@ def test_backup_client_polling(self, container_uri, sas_token): assert rehydrated.status() == "InProgress" assert not rehydrated.done() or rehydrated.polling_method().finished() - rehydrated.polling_method().update_status() - assert rehydrated.status() == "InProgress" - restore_poller.polling_method().update_status() - assert restore_poller.status() == "InProgress" - rehydrated.wait() assert rehydrated.status() == "Succeeded" and rehydrated.polling_method().status() == "Succeeded" restore_poller.wait() diff --git a/sdk/keyvault/azure-keyvault-administration/tests/test_backup_client_async.py b/sdk/keyvault/azure-keyvault-administration/tests/test_backup_client_async.py index c4607f848a59..6317aa928ffd 100644 --- a/sdk/keyvault/azure-keyvault-administration/tests/test_backup_client_async.py +++ b/sdk/keyvault/azure-keyvault-administration/tests/test_backup_client_async.py @@ -133,11 +133,6 @@ async def test_backup_client_polling(self, container_uri, sas_token): assert rehydrated.status() == "InProgress" assert not rehydrated.done() or rehydrated.polling_method().finished() - await backup_poller.polling_method().update_status() - assert backup_poller.status() == "InProgress" - await rehydrated.polling_method().update_status() - assert rehydrated.status() == "InProgress" - backup_operation = await backup_poller.result() assert backup_poller.status() == "Succeeded" and backup_poller.polling_method().status() == "Succeeded" rehydrated_operation = await rehydrated.result() @@ -146,7 +141,6 @@ async def test_backup_client_polling(self, container_uri, sas_token): # rehydrate a poller with a continuation token of a completed operation late_rehydrated = await backup_client.begin_backup(container_uri, sas_token, continuation_token=token) - assert late_rehydrated.status() == "InProgress" await late_rehydrated.polling_method().update_status() assert late_rehydrated.status() == "Succeeded" @@ -163,11 +157,6 @@ async def test_backup_client_polling(self, container_uri, sas_token): assert rehydrated.status() == "InProgress" assert not rehydrated.done() or rehydrated.polling_method().finished() - await rehydrated.polling_method().update_status() - assert rehydrated.status() == "InProgress" - await restore_poller.polling_method().update_status() - assert restore_poller.status() == "InProgress" - await rehydrated.wait() assert rehydrated.status() == "Succeeded" and rehydrated.polling_method().status() == "Succeeded" await restore_poller.wait() diff --git a/sdk/keyvault/azure-keyvault-administration/tests/test_examples_administration_async.py b/sdk/keyvault/azure-keyvault-administration/tests/test_examples_administration_async.py index 2b575af0de0c..45809f90f6c3 100644 --- a/sdk/keyvault/azure-keyvault-administration/tests/test_examples_administration_async.py +++ b/sdk/keyvault/azure-keyvault-administration/tests/test_examples_administration_async.py @@ -58,7 +58,7 @@ async def test_example_backup_and_restore(self, container_uri, sas_token): # check if the backup completed done = backup_poller.done() - # block until the backup completes + # yield until the backup completes # result() returns an object with a URL of the backup backup_operation = await backup_poller.result() # [END begin_backup] From 54f6575003bc658ff2f5481ac33a5dbaf00d865d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?McCoy=20Pati=C3=B1o?= Date: Tue, 15 Jun 2021 18:08:48 -0700 Subject: [PATCH 24/27] appease pylint --- .../azure/keyvault/administration/_backup_client.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_backup_client.py b/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_backup_client.py index 6130228a606e..f9db72d1cce4 100644 --- a/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_backup_client.py +++ b/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_backup_client.py @@ -67,8 +67,8 @@ def begin_backup(self, blob_storage_url, sas_token, **kwargs): except Exception as ex: # pylint: disable=broad-except raise_from( ValueError( - "The provided continuation_token is malformed. A valid token can be obtained from the operation " - + "poller's continuation_token() method" + "The provided continuation_token is malformed. A valid token can be obtained from the " + + "operation poller's continuation_token() method" ), ex, ) @@ -132,8 +132,8 @@ def begin_restore(self, folder_url, sas_token, **kwargs): except Exception as ex: # pylint: disable=broad-except raise_from( ValueError( - "The provided continuation_token is malformed. A valid token can be obtained from the operation " - + "poller's continuation_token() method" + "The provided continuation_token is malformed. A valid token can be obtained from the " + + "operation poller's continuation_token() method" ), ex, ) From 86b4e41e5932c04a2e2aa92e12f158b777bb7b68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?McCoy=20Pati=C3=B1o?= Date: Wed, 16 Jun 2021 10:40:30 -0700 Subject: [PATCH 25/27] get status on rehydration --- .../keyvault/administration/_internal/polling.py | 15 ++++++++++++++- .../tests/test_backup_client.py | 1 - .../tests/test_backup_client_async.py | 1 - 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_internal/polling.py b/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_internal/polling.py index 78f063893029..5a7b1894b76e 100644 --- a/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_internal/polling.py +++ b/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_internal/polling.py @@ -4,7 +4,7 @@ # ------------------------------------ import base64 -from azure.core.polling.base_polling import LROBasePolling, OperationResourcePolling +from azure.core.polling.base_polling import BadResponse, LROBasePolling, OperationFailed, OperationResourcePolling class KeyVaultBackupClientPolling(OperationResourcePolling): @@ -14,6 +14,19 @@ def __init__(self): def get_final_get_url(self, pipeline_response): return None + def set_initial_status(self, pipeline_response): + self._request = pipeline_response.http_response.request + response = pipeline_response.http_response + + self._set_async_url_if_present(response) + + if response.status_code in {200, 201, 202, 204} and self._async_url: + try: + return self.get_status(pipeline_response) + except BadResponse: + return "InProgress" + raise OperationFailed("Operation failed or canceled") + class KeyVaultBackupClientPollingMethod(LROBasePolling): def get_continuation_token(self): diff --git a/sdk/keyvault/azure-keyvault-administration/tests/test_backup_client.py b/sdk/keyvault/azure-keyvault-administration/tests/test_backup_client.py index e922a60d6e23..c8ed3b942c3d 100644 --- a/sdk/keyvault/azure-keyvault-administration/tests/test_backup_client.py +++ b/sdk/keyvault/azure-keyvault-administration/tests/test_backup_client.py @@ -141,7 +141,6 @@ def test_backup_client_polling(self, container_uri, sas_token): # rehydrate a poller with a continuation token of a completed operation late_rehydrated = backup_client.begin_backup(container_uri, sas_token, continuation_token=token) - late_rehydrated.polling_method().update_status() assert late_rehydrated.status() == "Succeeded" # restore the backup diff --git a/sdk/keyvault/azure-keyvault-administration/tests/test_backup_client_async.py b/sdk/keyvault/azure-keyvault-administration/tests/test_backup_client_async.py index 6317aa928ffd..6a0f10979e88 100644 --- a/sdk/keyvault/azure-keyvault-administration/tests/test_backup_client_async.py +++ b/sdk/keyvault/azure-keyvault-administration/tests/test_backup_client_async.py @@ -141,7 +141,6 @@ async def test_backup_client_polling(self, container_uri, sas_token): # rehydrate a poller with a continuation token of a completed operation late_rehydrated = await backup_client.begin_backup(container_uri, sas_token, continuation_token=token) - await late_rehydrated.polling_method().update_status() assert late_rehydrated.status() == "Succeeded" # restore the backup From ef75b1af4a15f8f38ad08db41414a48012cb9b42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?McCoy=20Pati=C3=B1o?= Date: Wed, 16 Jun 2021 15:34:22 -0700 Subject: [PATCH 26/27] simplify set_initial_status --- .../keyvault/administration/_internal/polling.py | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_internal/polling.py b/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_internal/polling.py index 5a7b1894b76e..c34ddbedbfb0 100644 --- a/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_internal/polling.py +++ b/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_internal/polling.py @@ -15,16 +15,11 @@ def get_final_get_url(self, pipeline_response): return None def set_initial_status(self, pipeline_response): - self._request = pipeline_response.http_response.request response = pipeline_response.http_response + self._async_url = response.headers["azure-asyncoperation"] - self._set_async_url_if_present(response) - - if response.status_code in {200, 201, 202, 204} and self._async_url: - try: - return self.get_status(pipeline_response) - except BadResponse: - return "InProgress" + if response.status_code in {200, 201, 202, 204}: + return self.get_status(pipeline_response) raise OperationFailed("Operation failed or canceled") From b728a58e73125b9ddb305655992cc7cca73912d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?McCoy=20Pati=C3=B1o?= Date: Wed, 16 Jun 2021 15:45:59 -0700 Subject: [PATCH 27/27] don't depend on _async_url --- .../azure/keyvault/administration/_internal/polling.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_internal/polling.py b/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_internal/polling.py index c34ddbedbfb0..1708769f5325 100644 --- a/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_internal/polling.py +++ b/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_internal/polling.py @@ -4,19 +4,23 @@ # ------------------------------------ import base64 -from azure.core.polling.base_polling import BadResponse, LROBasePolling, OperationFailed, OperationResourcePolling +from azure.core.polling.base_polling import LROBasePolling, OperationFailed, OperationResourcePolling class KeyVaultBackupClientPolling(OperationResourcePolling): def __init__(self): + self._polling_url = None super(KeyVaultBackupClientPolling, self).__init__(operation_location_header="azure-asyncoperation") + def get_polling_url(self): + return self._polling_url + def get_final_get_url(self, pipeline_response): return None def set_initial_status(self, pipeline_response): response = pipeline_response.http_response - self._async_url = response.headers["azure-asyncoperation"] + self._polling_url = response.headers["azure-asyncoperation"] if response.status_code in {200, 201, 202, 204}: return self.get_status(pipeline_response)