Improve env var management for LEXICON vars
continuous-integration/drone/push Build is passing Details

This commit is contained in:
Luke Murphy 2020-03-29 09:07:36 +02:00
parent d0289ad5b3
commit d430e3ffbb
No known key found for this signature in database
GPG Key ID: 5E2EF5A63E3718CC
6 changed files with 76 additions and 49 deletions

View File

@ -14,7 +14,7 @@ Ansible libraries for managing Gandi resources.
tasks: tasks:
- name: Create foobar.autonomic.zone - name: Create foobar.autonomic.zone
gandi_dns: gandi_dns:
rest_api_key: "{{ lookup('env', 'LEXICON_GANDI_AUTH_TOKEN') }}" gandi_rest_token: abc...
domain: foobar.autonomic.zone domain: foobar.autonomic.zone
ipv4: 94.130.105.60 ipv4: 94.130.105.60
state: present state: present

View File

@ -1,6 +1,7 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import json import json
import os
import traceback import traceback
from subprocess import CalledProcessError, check_output from subprocess import CalledProcessError, check_output
@ -28,13 +29,13 @@ options:
description: The IP v4 address that the domain refers to. description: The IP v4 address that the domain refers to.
type: str type: str
required: true required: true
rest_api_key: gandi_rest_token:
description: description:
- The Gandi REST API key. It may also be specified by the C(LEXICON_GANDI_AUTH_TOKEN) - The Gandi REST API key. It may also be specified by the C(LEXICON_GANDI_AUTH_TOKEN)
environment variable. See U(https://github.com/AnalogJ/lexicon/blob/ce168132880558415c8c755e65f8e2f9b46cff62/lexicon/providers/gandi.py). environment variable. See U(https://github.com/AnalogJ/lexicon/blob/ce168132880558415c8c755e65f8e2f9b46cff62/lexicon/providers/gandi.py).
for more. for more.
type: str type: str
required: true required: false
state: state:
description: description:
- The desired instance state. - The desired instance state.
@ -48,7 +49,6 @@ options:
EXAMPLES = """ EXAMPLES = """
- name: Create a new Gandi DNS entry - name: Create a new Gandi DNS entry
gandi_dns: gandi_dns:
rest_api_key: "{{ lookup('env', 'LEXICON_GANDI_AUTH_TOKEN') }}"
domain: foobar.autonomic.zone domain: foobar.autonomic.zone
ipv4: 192.168.1.2 ipv4: 192.168.1.2
state: present state: present
@ -64,30 +64,46 @@ except ImportError:
HAS_DNS_LEXICON_DEPENDENCY = False HAS_DNS_LEXICON_DEPENDENCY = False
def error_msg(domain):
"""Better error message since check_output output is not helpful."""
return (
"Unable to retrieve domain info. Did you export "
"the LEXICON_GANDI_AUTH_TOKEN environment variable? "
"Does running the following command work? lexicon "
"gandi list {domain} A"
).format(domain=domain)
def get_env():
"""Build environment for running command-line commands."""
env = os.environ.copy()
env["PROVIDER"] = "gandi"
env["LEXICON_GANDI_API_PROTOCOL"] = "rest"
return env
def retrieve_domain_info(module): def retrieve_domain_info(module):
"""Retrieve all information about a specific domain.""" """Retrieve all information about a specific domain."""
domain = module.params["domain"]
env = get_env()
try: try:
return json.loads( return json.loads(
check_output( check_output(
[ ["lexicon", "gandi", "list", domain, "A", "--output", "JSON"],
"lexicon", env=env,
"gandi",
"list",
module.params["domain"],
"A",
"--output",
"JSON",
]
) )
) )
except CalledProcessError as exception: except CalledProcessError:
module.fail_json( module.fail_json(msg=error_msg(domain))
msg="Unable to retrieve domain info. Saw: %s" % str(exception)
)
def create_domain(module): def create_domain(module):
"""Create a new DNS entry.""" """Create a new DNS entry."""
domain = module.params["domain"]
ipv4 = module.params["ipv4"]
env = get_env()
try: try:
return json.loads( return json.loads(
check_output( check_output(
@ -95,25 +111,28 @@ def create_domain(module):
"lexicon", "lexicon",
"gandi", "gandi",
"create", "create",
module.params["domain"], domain,
"A", "A",
"--name", "--name",
module.params["domain"], domain,
"--content", "--content",
module.params["ipv4"], ipv4,
"--output", "--output",
"JSON", "JSON",
] ],
env=env,
) )
) )
except Exception as exception: except Exception:
module.fail_json( module.fail_json(msg=error_msg(domain))
msg="Unable to create domain entry. Saw: %s" % str(exception)
)
def delete_domain(module): def delete_domain(module):
"""Delete an existing DNS entry.""" """Delete an existing DNS entry."""
domain = module.params["domain"]
ipv4 = module.params["ipv4"]
env = get_env()
try: try:
return json.loads( return json.loads(
check_output( check_output(
@ -121,59 +140,58 @@ def delete_domain(module):
"lexicon", "lexicon",
"gandi", "gandi",
"delete", "delete",
module.params["domain"], domain,
"A", "A",
"--name", "--name",
module.params["domain"], domain,
"--content", "--content",
module.params["ipv4"], ipv4,
"--output", "--output",
"JSON", "JSON",
] ],
env=env,
) )
) )
except Exception as exception: except Exception:
module.fail_json( module.fail_json(msg=error_msg(domain))
msg="Unable to delete domain entry. Saw: %s" % str(exception)
)
def main(): def main():
"""Module entrypoint.""" """Module entrypoint."""
module = AnsibleModule( module = AnsibleModule(
argument_spec=dict( argument_spec=dict(
domain=dict(type='str', required=True), domain=dict(type="str", required=True),
ipv4=dict(type='str', required=True), ipv4=dict(type="str", required=True),
state=dict( state=dict(
type='str', required=True, choices=['present', 'absent'] type="str", required=True, choices=["present", "absent"]
), ),
rest_api_key=dict( gandi_rest_token=dict(
type='str', type="str",
required=True, required=False,
no_log=True, no_log=True,
fallback=(env_fallback, ['LEXICON_GANDI_AUTH_TOKEN']), fallback=(env_fallback, ["LEXICON_GANDI_AUTH_TOKEN"]),
), ),
), ),
supports_check_mode=False, supports_check_mode=False,
required_together=(['domain', 'ipv4']), required_together=(["domain", "ipv4"]),
) )
if not HAS_DNS_LEXICON_DEPENDENCY: if not HAS_DNS_LEXICON_DEPENDENCY:
msg = missing_required_lib('lexicon') msg = missing_required_lib("lexicon")
module.fail_json(msg=msg, exception=DNS_LEXICON_IMP_ERR) module.fail_json(msg=msg, exception=DNS_LEXICON_IMP_ERR)
domains = retrieve_domain_info(module) domains = retrieve_domain_info(module)
existing_domain = module.params['domain'] in [ existing_domain = module.params["domain"] in [
domain['name'] for domain in domains domain["name"] for domain in domains
] ]
if module.params['state'] == 'present': if module.params["state"] == "present":
if existing_domain: if existing_domain:
module.exit_json(changed=False) module.exit_json(changed=False)
create_domain(module) create_domain(module)
module.exit_json(changed=True) module.exit_json(changed=True)
if module.params['state'] == 'absent': if module.params["state"] == "absent":
if not existing_domain: if not existing_domain:
module.exit_json(changed=False) module.exit_json(changed=False)
delete_domain(module) delete_domain(module)

10
play.yml Normal file
View File

@ -0,0 +1,10 @@
---
- hosts: localhost
gather_facts: false
connection: local
tasks:
- name: Create foobar.autonomic.zone
gandi_dns:
domain: foobar.autonomic.zone
ipv4: 94.130.105.60
state: absent

View File

@ -9,5 +9,4 @@ build-backend = "setuptools.build_meta"
[tool.black] [tool.black]
line-length = 80 line-length = 80
target-version = ["py38"] target-version = ["py38"]
skip-string-normalization = true
include = '\.pyi?$' include = '\.pyi?$'

View File

@ -5,5 +5,5 @@ from ansible.module_utils._text import to_bytes
def set_module_args(args): def set_module_args(args):
args = json.dumps({'ANSIBLE_MODULE_ARGS': args}) args = json.dumps({"ANSIBLE_MODULE_ARGS": args})
basic._ANSIBLE_ARGS = to_bytes(args) basic._ANSIBLE_ARGS = to_bytes(args)

View File

@ -1,6 +1,6 @@
import pytest import pytest
gandi_cli = pytest.importorskip('lexicon') gandi_cli = pytest.importorskip("lexicon")
def test_TODO(): def test_TODO():