Skip to content

Commit

Permalink
Key Vault: making poller return Certificate or CertificateOperation (A…
Browse files Browse the repository at this point in the history
…zure#7349)

* async_request failing in sync client during playback

* switched to returning certificate, all tests passing

* updated create_certificate retval in docstrings

* added Charles' comments

* implemented Charles' comments
  • Loading branch information
iscai-msft authored and yijxie committed Oct 9, 2019
1 parent c702234 commit b912181
Show file tree
Hide file tree
Showing 55 changed files with 14,712 additions and 6,064 deletions.
2 changes: 2 additions & 0 deletions sdk/keyvault/azure-keyvault-certificates/HISTORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
## 4.0.0b4
### Breaking changes
- Enums 'JsonWebKeyCurveName' and 'JsonWebKeyType' have been renamed to 'KeyCurveName' and 'KeyType', respectively.
- Both async and sync versions of create_certificate now return pollers that return the created Certificate if creation is successful,
and the CertificateOperation if not.

## 4.0.0b3 (2019-09-11)
Version 4.0.0b3 is the first preview of our efforts to create a user-friendly and Pythonic client library for Azure Key Vault's certificates.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,53 +6,51 @@
import time

from azure.core.polling import PollingMethod
from azure.keyvault.certificates._shared import parse_vault_id

logger = logging.getLogger(__name__)


class CreateCertificatePoller(PollingMethod):
def __init__(self, interval=5, unknown_issuer=False):
def __init__(self, get_certificate_command, interval=5):
self._command = None
self._status = None
self._certificate_id = None
self.polling_interval = interval
self.unknown_issuer = unknown_issuer
self._resource = None
self._pending_certificate_op = None
self._get_certificate_command = get_certificate_command
self._polling_interval = interval

def _update_status(self):
# type: () -> None
pending_certificate = self._command()
self._status = pending_certificate.status.lower()
if not self._certificate_id:
self._certificate_id = parse_vault_id(pending_certificate.id)
self._pending_certificate_op = self._command()

def initialize(self, client, initial_response, _):
# type: (Any, Any, Callable) -> None
self._command = client
self._status = initial_response
self._pending_certificate_op = initial_response

def run(self):
# type: () -> None
try:
while not self.finished():
self._update_status()
time.sleep(self.polling_interval)
time.sleep(self._polling_interval)
if self._pending_certificate_op.status.lower() == 'completed':
self._resource = self._get_certificate_command()
else:
self._resource = self._pending_certificate_op
except Exception as e:
logger.warning(str(e))
raise

def finished(self):
# type: () -> bool
if self.unknown_issuer:
if self._pending_certificate_op.issuer_name.lower() == 'unknown':
return True
return self._status in ('completed', 'cancelled', 'failed')
return self._pending_certificate_op.status.lower() != 'inprogress'

def resource(self):
# type: () -> str
if not self.finished():
self._update_status()
return self._status
# type: () -> Any
return self._resource

def status(self):
# type: () ->str
return self._status
return self._pending_certificate_op.status.lower()
Original file line number Diff line number Diff line change
Expand Up @@ -8,46 +8,46 @@
from typing import Any, Callable

from azure.core.polling import AsyncPollingMethod
from azure.keyvault.certificates._shared import parse_vault_id


logger = logging.getLogger(__name__)


class CreateCertificatePollerAsync(AsyncPollingMethod):
def __init__(self, interval=5, unknown_issuer=False):
def __init__(self, get_certificate_command, interval=5):
self._command = None
self._status = None
self._certificate_id = None
self.polling_interval = interval
self.unknown_issuer = unknown_issuer
self._resource = None
self._pending_certificate_op = None
self._get_certificate_command = get_certificate_command
self._polling_interval = interval

async def _update_status(self) -> None:
pending_certificate = await self._command()
self._status = pending_certificate.status.lower()
if not self._certificate_id:
self._certificate_id = parse_vault_id(pending_certificate.id)
self._pending_certificate_op = await self._command()

def initialize(self, client: Any, initial_response: Any, _: Callable) -> None:
self._command = client
self._status = initial_response
self._pending_certificate_op = initial_response

async def run(self) -> None:
try:
while not self.finished():
await self._update_status()
await asyncio.sleep(self.polling_interval)
await asyncio.sleep(self._polling_interval)
if self._pending_certificate_op.status.lower() == 'completed':
self._resource = await self._get_certificate_command()
else:
self._resource = self._pending_certificate_op
except Exception as e:
logger.warning(str(e))
raise

def finished(self) -> bool:
if self.unknown_issuer:
if self._pending_certificate_op.issuer_name.lower() == 'unknown':
return True
return self._status in ('completed', 'cancelled', 'failed')
return self._pending_certificate_op.status.lower() != 'inprogress'

def resource(self) -> Any:
return self._status
return self._resource

def status(self) -> str:
return self._status
return self._pending_certificate_op.status.lower()
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
# pylint:disable=too-many-lines,too-many-public-methods
import base64
import uuid
from typing import Any, AsyncIterable, Optional, Iterable, List, Dict
from typing import Any, AsyncIterable, Optional, Iterable, List, Dict, Coroutine
from functools import partial

from azure.core.tracing.decorator import distributed_trace
Expand Down Expand Up @@ -53,7 +53,7 @@ async def create_certificate(
enabled: Optional[bool] = None,
tags: Optional[Dict[str, str]] = None,
**kwargs: "**Any"
) -> CertificateOperation:
) -> Coroutine:
"""Creates a new certificate.
If this is the first version, the certificate resource is created. This
Expand All @@ -66,8 +66,10 @@ async def create_certificate(
:param bool enabled: Determines whether the object is enabled.
:param tags: Application specific metadata in the form of key-value pairs.
:type tags: dict(str, str)
:returns: The created CertificateOperation
:rtype: coroutine
:returns: A coroutine for the creation of the certificate. Awaiting the coroutine
returns the created Certificate if creation is successful, the CertificateOperation if not.
:rtype: coroutine[~azure.keyvault.certificates.models.Certificate or
~azure.keyvault.certificates.models.CertificateOperation]
:raises: :class:`~azure.core.exceptions.HttpResponseError`
Example:
Expand Down Expand Up @@ -107,28 +109,32 @@ async def create_certificate(
content_type=SecretContentType.PKCS12,
subject_name="CN=DefaultPolicy",
validity_in_months=12)

create_certificate_operation = await self._client.create_certificate(
cert_bundle = await self._client.create_certificate(
vault_base_url=self.vault_url,
certificate_name=name,
certificate_policy=policy._to_certificate_policy_bundle(),
certificate_attributes=attributes,
tags=tags,
**kwargs
)
create_certificate_operation = CertificateOperation._from_certificate_operation_bundle(cert_bundle)

command = partial(
self._client.get_certificate_operation,
vault_base_url=self.vault_url,
certificate_name=name,
self.get_certificate_operation,
name=name,
**kwargs
)

get_certificate_command = partial(
self.get_certificate_with_policy,
name=name,
**kwargs
)

create_certificate_polling = CreateCertificatePollerAsync(
unknown_issuer=(policy.issuer_name.lower() == 'unknown'))
create_certificate_polling = CreateCertificatePollerAsync(get_certificate_command=get_certificate_command)
return async_poller(
command,
create_certificate_operation.status.lower(),
create_certificate_operation,
None,
create_certificate_polling
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ def create_certificate(
tags=None, # type: Optional[Dict[str, str]]
**kwargs # type: Any
):
# type: (...) -> CertificateOperation
# type: (...) -> LROPoller
"""Creates a new certificate.
If this is the first version, the certificate resource is created. This
Expand All @@ -74,8 +74,10 @@ def create_certificate(
:param bool enabled: Determines whether the object is enabled.
:param tags: Application specific metadata in the form of key-value pairs.
:type tags: dict(str, str)
:returns: The created CertificateOperation
:rtype: ~azure.core.polling.LROPoller
:returns: An LROPoller for the create certificate operation. Waiting on the poller
gives you the certificate if creation is successful, the CertificateOperation if not.
:rtype: ~azure.core.polling.LROPoller[~azure.keyvault.certificates.models.Certificate or
~azure.keyvault.certificates.models.CertificateOperation]
:raises: :class:`~azure.core.exceptions.HttpResponseError`
Example:
Expand Down Expand Up @@ -117,7 +119,7 @@ def create_certificate(
subject_name="CN=DefaultPolicy",
validity_in_months=12)

create_certificate_operation = self._client.create_certificate(
cert_bundle = self._client.create_certificate(
vault_base_url=self.vault_url,
certificate_name=name,
certificate_policy=policy._to_certificate_policy_bundle(),
Expand All @@ -126,17 +128,24 @@ def create_certificate(
**kwargs
)

create_certificate_operation = CertificateOperation._from_certificate_operation_bundle(cert_bundle)

command = partial(
self._client.get_certificate_operation,
vault_base_url=self.vault_url,
certificate_name=name,
self.get_certificate_operation,
name=name,
**kwargs
)

get_certificate_command = partial(
self.get_certificate_with_policy,
name=name,
**kwargs
)

create_certificate_polling = CreateCertificatePoller(unknown_issuer=(policy.issuer_name.lower() == 'unknown'))
create_certificate_polling = CreateCertificatePoller(get_certificate_command=get_certificate_command)
return LROPoller(
command,
create_certificate_operation.status.lower(),
create_certificate_operation,
None,
create_certificate_polling
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,9 @@
# A long running poller is returned for the create certificate operation.
create_certificate_poller = client.create_certificate(name=cert_name)

# The wait call awaits the completion of the create certificate operation
create_certificate_poller.wait()
# The result call awaits the completion of the create certificate operation and returns the final result.
# It will return a certificate if creation is successful, and will return the CertificateOperation if not.
certificate = create_certificate_poller.result()
print("Certificate with name '{0}' created.".format(cert_name))

# Backups are good to have, if in case certificates gets deleted accidentally.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,11 @@ async def run_sample():

# Let's create a certificate for your key vault.
# if the certificate already exists in the Key Vault, then a new version of the certificate is created.
# An async poller is returned
# An async poller is returned.
create_certificate_poller = await client.create_certificate(name=cert_name)

# Awaiting the poller will return a certificate if creation is successful, and will return the failed
# CertificateOperation if not.
await create_certificate_poller
print("Certificate with name '{0}' created.".format(cert_name))

Expand Down Expand Up @@ -91,4 +94,4 @@ async def run_sample():
loop.close()

except Exception as e:
print("Top level Error: {0}".format(str(e)))
print("Top level Error: {0}".format(str(e)))
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,4 @@
print("\nrun_sample has caught an error. {0}".format(e.message))

finally:
print("\nrun_sample done")
print("\nrun_sample done")
Original file line number Diff line number Diff line change
Expand Up @@ -72,4 +72,4 @@ async def run_sample():
loop.close()

except Exception as e:
print("Top level Error: {0}".format(str(e)))
print("Top level Error: {0}".format(str(e)))
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,12 @@
san_dns_names=['sdk.azure-int.net']
)
cert_name = "HelloWorldCertificate"
create_certificate_poller = client.create_certificate(name=cert_name, policy=cert_policy)
create_certificate_poller.wait()
print("Certificate with name '{0}' created".format(cert_name))

# create_certificate returns a poller. Calling result() on the poller will return the certificate
# if creation is successful, and the CertificateOperation if not. The wait() call on the poller will
# wait until the long running operation is complete.
certificate = client.create_certificate(name=cert_name, policy=cert_policy).result()
print("Certificate with name '{0}' created".format(certificate.name))

# Let's get the bank certificate using its name
print("\n.. Get a Certificate by name")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,12 @@ async def run_sample():
san_dns_names=['sdk.azure-int.net']
)
cert_name = "HelloWorldCertificate"

# create_certificate returns a poller. Awaiting the poller will return the certificate
# if creation is successful, and the CertificateOperation if not.
create_certificate_poller = await client.create_certificate(name=cert_name, policy=cert_policy)
await create_certificate_poller
print("Certificate with name '{0}' created".format(cert_name))
certificate = await create_certificate_poller
print("Certificate with name '{0}' created".format(certificate.name))

# Let's get the bank certificate using its name
print("\n.. Get a Certificate by name")
Expand Down Expand Up @@ -104,4 +107,4 @@ async def run_sample():
loop.close()

except Exception as e:
print("Top level Error: {0}".format(str(e)))
print("Top level Error: {0}".format(str(e)))
Original file line number Diff line number Diff line change
Expand Up @@ -92,4 +92,3 @@

finally:
print("\nrun_sample done")

Original file line number Diff line number Diff line change
Expand Up @@ -102,4 +102,4 @@ async def run_sample():
loop.close()

except Exception as e:
print("Top level Error: {0}".format(str(e)))
print("Top level Error: {0}".format(str(e)))
Loading

0 comments on commit b912181

Please sign in to comment.