acme_account_info: retrieve orders (#59697)
* Add retrieve_orders option. * Run acme_certificate tests also for acme_account_info; use acme_account_info to get list of orders. * Doing some quoting. * Improve returned description.
This commit is contained in:
parent
14974f5fc2
commit
039123ec6b
4 changed files with 220 additions and 2 deletions
|
@ -28,6 +28,21 @@ notes:
|
||||||
- "The M(acme_account) module allows to modify, create and delete ACME accounts."
|
- "The M(acme_account) module allows to modify, create and delete ACME accounts."
|
||||||
- "This module was called C(acme_account_facts) before Ansible 2.8. The usage
|
- "This module was called C(acme_account_facts) before Ansible 2.8. The usage
|
||||||
did not change."
|
did not change."
|
||||||
|
options:
|
||||||
|
retrieve_orders:
|
||||||
|
description:
|
||||||
|
- "Whether to retrieve the list of order URLs or order objects, if provided
|
||||||
|
by the ACME server."
|
||||||
|
- "A value of C(ignore) will not fetch the list of orders."
|
||||||
|
- "Currently, Let's Encrypt does not return orders, so the C(orders) result
|
||||||
|
will always be empty."
|
||||||
|
type: str
|
||||||
|
choices:
|
||||||
|
- ignore
|
||||||
|
- url_list
|
||||||
|
- object_list
|
||||||
|
default: ignore
|
||||||
|
version_added: "2.9"
|
||||||
seealso:
|
seealso:
|
||||||
- module: acme_account
|
- module: acme_account
|
||||||
description: Allows to create, modify or delete an ACME account.
|
description: Allows to create, modify or delete an ACME account.
|
||||||
|
@ -90,7 +105,10 @@ account:
|
||||||
choices: ['valid', 'deactivated', 'revoked']
|
choices: ['valid', 'deactivated', 'revoked']
|
||||||
sample: valid
|
sample: valid
|
||||||
orders:
|
orders:
|
||||||
description: a URL where a list of orders can be retrieved for this account
|
description:
|
||||||
|
- A URL where a list of orders can be retrieved for this account.
|
||||||
|
- Use the I(retrieve_orders) option to query this URL and retrieve the
|
||||||
|
complete list of orders.
|
||||||
returned: always
|
returned: always
|
||||||
type: str
|
type: str
|
||||||
sample: https://example.ca/account/1/orders
|
sample: https://example.ca/account/1/orders
|
||||||
|
@ -99,15 +117,126 @@ account:
|
||||||
returned: always
|
returned: always
|
||||||
type: str
|
type: str
|
||||||
sample: https://example.ca/account/1/orders
|
sample: https://example.ca/account/1/orders
|
||||||
|
|
||||||
|
orders:
|
||||||
|
description:
|
||||||
|
- "The list of orders."
|
||||||
|
- "If I(retrieve_orders) is C(url_list), this will be a list of URLs."
|
||||||
|
- "If I(retrieve_orders) is C(object_list), this will be a list of objects."
|
||||||
|
type: list
|
||||||
|
returned: if account exists, I(retrieve_orders) is not C(ignore), and server supports order listing
|
||||||
|
contains:
|
||||||
|
status:
|
||||||
|
description: The order's status.
|
||||||
|
type: str
|
||||||
|
choices:
|
||||||
|
- pending
|
||||||
|
- ready
|
||||||
|
- processing
|
||||||
|
- valid
|
||||||
|
- invalid
|
||||||
|
expires:
|
||||||
|
description:
|
||||||
|
- When the order expires.
|
||||||
|
- Timestamp should be formatted as described in RFC3339.
|
||||||
|
- Only required to be included in result when I(status) is C(pending) or C(valid).
|
||||||
|
type: str
|
||||||
|
returned: when server gives expiry date
|
||||||
|
identifiers:
|
||||||
|
description:
|
||||||
|
- List of identifiers this order is for.
|
||||||
|
type: list
|
||||||
|
contains:
|
||||||
|
type:
|
||||||
|
description: Type of identifier. C(dns) or C(ip).
|
||||||
|
type: str
|
||||||
|
value:
|
||||||
|
description: Name of identifier. Hostname or IP address.
|
||||||
|
type: str
|
||||||
|
wildcard:
|
||||||
|
description: "Whether I(value) is actually a wildcard. The wildcard
|
||||||
|
prefix C(*.) is not included in I(value) if this is C(true)."
|
||||||
|
type: bool
|
||||||
|
returned: required to be included if the identifier is wildcarded
|
||||||
|
notBefore:
|
||||||
|
description:
|
||||||
|
- The requested value of the C(notBefore) field in the certificate.
|
||||||
|
- Date should be formatted as described in RFC3339.
|
||||||
|
- Server is not required to return this.
|
||||||
|
type: str
|
||||||
|
returned: when server returns this
|
||||||
|
notAfter:
|
||||||
|
description:
|
||||||
|
- The requested value of the C(notAfter) field in the certificate.
|
||||||
|
- Date should be formatted as described in RFC3339.
|
||||||
|
- Server is not required to return this.
|
||||||
|
type: str
|
||||||
|
returned: when server returns this
|
||||||
|
error:
|
||||||
|
description:
|
||||||
|
- In case an error occured during processing, this contains information about the error.
|
||||||
|
- The field is structured as a problem document (RFC7807).
|
||||||
|
type: complex
|
||||||
|
returned: when an error occurred
|
||||||
|
authorizations:
|
||||||
|
description:
|
||||||
|
- A list of URLs for authorizations for this order.
|
||||||
|
type: list
|
||||||
|
finalize:
|
||||||
|
description:
|
||||||
|
- A URL used for finalizing an ACME order.
|
||||||
|
type: str
|
||||||
|
certificate:
|
||||||
|
description:
|
||||||
|
- The URL for retrieving the certificate.
|
||||||
|
type: str
|
||||||
|
returned: when certificate was issued
|
||||||
'''
|
'''
|
||||||
|
|
||||||
from ansible.module_utils.acme import (
|
from ansible.module_utils.acme import (
|
||||||
ModuleFailException, ACMEAccount, set_crypto_backend,
|
ModuleFailException, ACMEAccount, set_crypto_backend, process_links,
|
||||||
)
|
)
|
||||||
|
|
||||||
from ansible.module_utils.basic import AnsibleModule
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
|
|
||||||
|
|
||||||
|
def get_orders_list(module, account, orders_url):
|
||||||
|
'''
|
||||||
|
Retrieves orders list (handles pagination).
|
||||||
|
'''
|
||||||
|
orders = []
|
||||||
|
while orders_url:
|
||||||
|
# Get part of orders list
|
||||||
|
res, info = account.get_request(orders_url, parse_json_result=True, fail_on_error=True)
|
||||||
|
if not res.get('orders'):
|
||||||
|
if orders:
|
||||||
|
module.warn('When retrieving orders list part {0}, got empty result list'.format(orders_url))
|
||||||
|
break
|
||||||
|
# Add order URLs to result list
|
||||||
|
orders.extend(res['orders'])
|
||||||
|
# Extract URL of next part of results list
|
||||||
|
new_orders_url = []
|
||||||
|
|
||||||
|
def f(link, relation):
|
||||||
|
if relation == 'next':
|
||||||
|
new_orders_url.append(link)
|
||||||
|
|
||||||
|
process_links(info, f)
|
||||||
|
new_orders_url.append(None)
|
||||||
|
previous_orders_url, orders_url = orders_url, new_orders_url.pop(0)
|
||||||
|
if orders_url == previous_orders_url:
|
||||||
|
# Prevent infinite loop
|
||||||
|
orders_url = None
|
||||||
|
return orders
|
||||||
|
|
||||||
|
|
||||||
|
def get_order(account, order_url):
|
||||||
|
'''
|
||||||
|
Retrieve order data.
|
||||||
|
'''
|
||||||
|
return account.get_request(order_url, parse_json_result=True, fail_on_error=True)[0]
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
module = AnsibleModule(
|
module = AnsibleModule(
|
||||||
argument_spec=dict(
|
argument_spec=dict(
|
||||||
|
@ -118,6 +247,7 @@ def main():
|
||||||
acme_version=dict(type='int', default=1, choices=[1, 2]),
|
acme_version=dict(type='int', default=1, choices=[1, 2]),
|
||||||
validate_certs=dict(type='bool', default=True),
|
validate_certs=dict(type='bool', default=True),
|
||||||
select_crypto_backend=dict(type='str', default='auto', choices=['auto', 'openssl', 'cryptography']),
|
select_crypto_backend=dict(type='str', default='auto', choices=['auto', 'openssl', 'cryptography']),
|
||||||
|
retrieve_orders=dict(type='str', default='ignore', choices=['ignore', 'url_list', 'object_list']),
|
||||||
),
|
),
|
||||||
required_one_of=(
|
required_one_of=(
|
||||||
['account_key_src', 'account_key_content'],
|
['account_key_src', 'account_key_content'],
|
||||||
|
@ -159,6 +289,13 @@ def main():
|
||||||
account_data['contact'] = []
|
account_data['contact'] = []
|
||||||
account_data['public_account_key'] = account.key_data['jwk']
|
account_data['public_account_key'] = account.key_data['jwk']
|
||||||
result['account'] = account_data
|
result['account'] = account_data
|
||||||
|
# Retrieve orders list
|
||||||
|
if account_data.get('orders') and module.params['retrieve_orders'] != 'ignore':
|
||||||
|
orders = get_orders_list(module, account, account_data['orders'])
|
||||||
|
if module.params['retrieve_orders'] == 'url_list':
|
||||||
|
result['orders'] = orders
|
||||||
|
else:
|
||||||
|
result['orders'] = [get_order(account, order) for order in orders]
|
||||||
module.exit_json(**result)
|
module.exit_json(**result)
|
||||||
except ModuleFailException as e:
|
except ModuleFailException as e:
|
||||||
e.do_fail(module)
|
e.do_fail(module)
|
||||||
|
|
|
@ -1,2 +1,3 @@
|
||||||
shippable/cloud/group1
|
shippable/cloud/group1
|
||||||
cloud/acme
|
cloud/acme
|
||||||
|
acme_account_info
|
||||||
|
|
|
@ -299,3 +299,49 @@
|
||||||
- name: Dumping cert 8
|
- name: Dumping cert 8
|
||||||
command: openssl x509 -in "{{ output_dir }}/cert-8.pem" -noout -text
|
command: openssl x509 -in "{{ output_dir }}/cert-8.pem" -noout -text
|
||||||
register: cert_8_text
|
register: cert_8_text
|
||||||
|
## GET ACCOUNT ORDERS #########################################################################
|
||||||
|
- name: Don't retrieve orders
|
||||||
|
acme_account_info:
|
||||||
|
select_crypto_backend: "{{ select_crypto_backend }}"
|
||||||
|
account_key_src: "{{ output_dir }}/account-ec256.pem"
|
||||||
|
acme_version: 2
|
||||||
|
acme_directory: https://{{ acme_host }}:14000/dir
|
||||||
|
validate_certs: no
|
||||||
|
retrieve_orders: ignore
|
||||||
|
register: account_orders_not
|
||||||
|
- name: Retrieve orders as URL list (1/2)
|
||||||
|
acme_account_info:
|
||||||
|
select_crypto_backend: "{{ select_crypto_backend }}"
|
||||||
|
account_key_src: "{{ output_dir }}/account-ec256.pem"
|
||||||
|
acme_version: 2
|
||||||
|
acme_directory: https://{{ acme_host }}:14000/dir
|
||||||
|
validate_certs: no
|
||||||
|
retrieve_orders: url_list
|
||||||
|
register: account_orders_urls
|
||||||
|
- name: Retrieve orders as URL list (2/2)
|
||||||
|
acme_account_info:
|
||||||
|
select_crypto_backend: "{{ select_crypto_backend }}"
|
||||||
|
account_key_src: "{{ output_dir }}/account-ec384.pem"
|
||||||
|
acme_version: 2
|
||||||
|
acme_directory: https://{{ acme_host }}:14000/dir
|
||||||
|
validate_certs: no
|
||||||
|
retrieve_orders: url_list
|
||||||
|
register: account_orders_urls2
|
||||||
|
- name: Retrieve orders as object list (1/2)
|
||||||
|
acme_account_info:
|
||||||
|
select_crypto_backend: "{{ select_crypto_backend }}"
|
||||||
|
account_key_src: "{{ output_dir }}/account-ec256.pem"
|
||||||
|
acme_version: 2
|
||||||
|
acme_directory: https://{{ acme_host }}:14000/dir
|
||||||
|
validate_certs: no
|
||||||
|
retrieve_orders: object_list
|
||||||
|
register: account_orders_full
|
||||||
|
- name: Retrieve orders as object list (2/2)
|
||||||
|
acme_account_info:
|
||||||
|
select_crypto_backend: "{{ select_crypto_backend }}"
|
||||||
|
account_key_src: "{{ output_dir }}/account-ec384.pem"
|
||||||
|
acme_version: 2
|
||||||
|
acme_directory: https://{{ acme_host }}:14000/dir
|
||||||
|
validate_certs: no
|
||||||
|
retrieve_orders: object_list
|
||||||
|
register: account_orders_full2
|
||||||
|
|
|
@ -83,3 +83,37 @@
|
||||||
assert:
|
assert:
|
||||||
that:
|
that:
|
||||||
- "'DNS:example.org' in cert_6_text.stdout"
|
- "'DNS:example.org' in cert_6_text.stdout"
|
||||||
|
|
||||||
|
- name: Validate that orders were not retrieved
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- "'account' in account_orders_not"
|
||||||
|
- "'orders' not in account_orders_not"
|
||||||
|
|
||||||
|
- name: Validate that orders were retrieved as list of URLs (1/2)
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- "'account' in account_orders_urls"
|
||||||
|
- "'orders' in account_orders_urls"
|
||||||
|
- "account_orders_urls.orders[0] is string"
|
||||||
|
|
||||||
|
- name: Validate that orders were retrieved as list of URLs (2/2)
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- "'account' in account_orders_urls2"
|
||||||
|
- "'orders' in account_orders_urls2"
|
||||||
|
- "account_orders_urls2.orders[0] is string"
|
||||||
|
|
||||||
|
- name: Validate that orders were retrieved as list of objects (1/2)
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- "'account' in account_orders_full"
|
||||||
|
- "'orders' in account_orders_full"
|
||||||
|
- "account_orders_full.orders[0].status is string"
|
||||||
|
|
||||||
|
- name: Validate that orders were retrieved as list of objects (2/2)
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- "'account' in account_orders_full2"
|
||||||
|
- "'orders' in account_orders_full2"
|
||||||
|
- "account_orders_full2.orders[0].status is string"
|
||||||
|
|
Loading…
Reference in a new issue