Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Key Vault: making poller return Certificate or CertificateOperation #7349

Merged
merged 9 commits into from
Sep 26, 2019
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 create Certificate if creation is successful,
iscai-msft marked this conversation as resolved.
Show resolved Hide resolved
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,52 @@
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, unknown_issuer=False, 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._unknown_issuer = unknown_issuer
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._unknown_issuer:
iscai-msft marked this conversation as resolved.
Show resolved Hide resolved
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,47 @@
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, unknown_issuer=False, 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._unknown_issuer = unknown_issuer
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._unknown_issuer:
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,8 @@ 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.
iscai-msft marked this conversation as resolved.
Show resolved Hide resolved
:raises: :class:`~azure.core.exceptions.HttpResponseError`

Example:
Expand Down Expand Up @@ -118,17 +118,24 @@ async def create_certificate(
)

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'))
get_certificate_command=get_certificate_command,
unknown_issuer=(policy.issuer_name.lower() == 'unknown')
)
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,7 +74,8 @@ 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
:returns: An LROPoller for the create certificate operation. Waiting on the poller
iscai-msft marked this conversation as resolved.
Show resolved Hide resolved
gives you the certificate if creation is successful, the CertificateOperation if not.
:rtype: ~azure.core.polling.LROPoller
:raises: :class:`~azure.core.exceptions.HttpResponseError`

Expand Down Expand Up @@ -127,16 +128,24 @@ def create_certificate(
)

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

create_certificate_polling = CreateCertificatePoller(unknown_issuer=(policy.issuer_name.lower() == 'unknown'))
get_certificate_command = partial(
self.get_certificate_with_policy,
name=name,
**kwargs
)

create_certificate_polling = CreateCertificatePoller(
get_certificate_command=get_certificate_command,
unknown_issuer=(policy.issuer_name.lower() == 'unknown')
)
return LROPoller(
command,
create_certificate_operation.status.lower(),
create_certificate_operation,
None,
create_certificate_polling
)
Expand Down Expand Up @@ -342,7 +351,7 @@ def import_certificate(
"""Imports a certificate into a specified key vault.

Imports an existing valid certificate, containing a private key, into
Azure Key Vault. The certificate to be imported can be in either PFX or
Azure Key Vault. The certificate to be imported can be in either PFX orHi
iscai-msft marked this conversation as resolved.
Show resolved Hide resolved
PEM format. If the certificate is in PEM format the PEM file must
contain the key as well as x509 certificates. This operation requires
the certificates/import permission.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,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
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
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,11 @@
storage_certificate_poller = client.create_certificate(name=storage_cert_name)

# await the creation of the bank and storage certificate
bank_certificate_poller.wait()
storage_certificate_poller.wait()
bank_certificate = bank_certificate_poller.result()
storage_certificate = storage_certificate_poller.wait()
iscai-msft marked this conversation as resolved.
Show resolved Hide resolved

print("Certificate with name '{0}' was created.".format(bank_cert_name))
print("Certificate with name '{0}' was created.".format(storage_cert_name))
print("Certificate with name '{0}' was created.".format(bank_certificate.name))
print("Certificate with name '{0}' was created.".format(storage_certificate.name))

# Let's list the certificates.
print("\n.. List certificates from the Key Vault")
Expand All @@ -65,11 +65,11 @@
# certificate creates a new version of the certificate in the Key Vault with the new value.

tags = {"a": "b"}
client.create_certificate(name=bank_cert_name, tags=tags).wait()
bank_certificate = client.create_certificate(name=bank_cert_name, tags=tags).wait()
print(
"Certificate with name '{0}' was created again with tags '{1}'".format(
bank_cert_name,
tags
bank_certificate.name,
bank_certificate.tags
)
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,11 @@ async def run_sample():
storage_certificate_poller = await client.create_certificate(name=storage_cert_name)

# await the creation of the bank and storage certificate
await bank_certificate_poller
await storage_certificate_poller
bank_certificate = await bank_certificate_poller
storage_certificate = await storage_certificate_poller

print("Certificate with name '{0}' was created.".format(bank_cert_name))
print("Certificate with name '{0}' was created.".format(storage_cert_name))
print("Certificate with name '{0}' was created.".format(bank_certificate.name))
print("Certificate with name '{0}' was created.".format(storage_certificate.name))

# Let's list the certificates.
print("\n.. List certificates from the Key Vault")
Expand All @@ -67,9 +67,12 @@ async def run_sample():
tags = {"a": "b"}

updated_bank_certificate_poller = await client.create_certificate(name=bank_cert_name, tags=tags)
await updated_bank_certificate_poller
bank_certificate = await updated_bank_certificate_poller
print(
"Certificate with name '{0}' was created again with tags '{1}'".format(bank_cert_name, tags)
"Certificate with name '{0}' was created again with tags '{1}'".format(
bank_certificate.name,
bank_certificate.tags
)
)

# You need to check all the different tags your bank account certificate had previously. Lets print all the versions of this certificate.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,10 @@
bank_certificate_poller = client.create_certificate(name=bank_cert_name)
storage_certificate_poller = client.create_certificate(name=storage_cert_name)

bank_certificate_poller.wait()
storage_certificate_poller.wait()
print("Certificate with name '{0}' was created.".format(bank_cert_name))
print("Certificate with name '{0}' was created.".format(storage_cert_name))
bank_certificate = bank_certificate_poller.result()
storage_certificate = storage_certificate_poller.result()
print("Certificate with name '{0}' was created.".format(bank_certificate.name))
print("Certificate with name '{0}' was created.".format(storage_certificate.name))

# The storage account was closed, need to delete its credentials from the Key Vault.
print("\n.. Delete a Certificate")
Expand Down
Loading