mirror of
https://github.com/ansible-collections/community.general.git
synced 2025-05-31 13:29:08 -07:00
[PR #7826/44679e71 backport][stable-8] Refactor of consul modules (#7877)
Refactor of consul modules (#7826)
* Extract common functionality.
* Refactor duplicated code into module_utils.
* Fixed ansible-test issues.
* Address review comments.
* Revert changes to consul_acl.
It uses deprecated APIs disabled since Consul 1.11 (which is EOL), don't
bother updating the module anymore.
* Remove unused code.
* Merge token into default doc fragment.
* JSON all the way down.
* extract validation tests into custom file and prep for requests removal.
* Removed dependency on requests.
* Initial test for consul_kv.
* fixup license headers.
* Revert changes to consul.py since it utilizes python-consul.
* Disable the lookup test for now.
* Fix python 2.7 support.
* Address review comments.
* Address review comments.
* Addec changelog fragment.
* Mark ConsulModule as private.
(cherry picked from commit 44679e71a2
)
Co-authored-by: Florian Apolloner <florian@apolloner.eu>
This commit is contained in:
parent
13c25154b5
commit
1ab1f8f62b
10 changed files with 365 additions and 476 deletions
|
@ -19,6 +19,7 @@ description:
|
|||
author:
|
||||
- Håkon Lerring (@Hakon)
|
||||
extends_documentation_fragment:
|
||||
- community.general.consul
|
||||
- community.general.attributes
|
||||
attributes:
|
||||
check_mode:
|
||||
|
@ -34,7 +35,6 @@ options:
|
|||
state:
|
||||
description:
|
||||
- whether the role should be present or absent.
|
||||
required: false
|
||||
choices: ['present', 'absent']
|
||||
default: present
|
||||
type: str
|
||||
|
@ -42,7 +42,6 @@ options:
|
|||
description:
|
||||
- Description of the role.
|
||||
- If not specified, the assigned description will not be changed.
|
||||
required: false
|
||||
type: str
|
||||
policies:
|
||||
type: list
|
||||
|
@ -51,7 +50,6 @@ options:
|
|||
- List of policies to attach to the role. Each policy is a dict.
|
||||
- If the parameter is left blank, any policies currently assigned will not be changed.
|
||||
- Any empty array (V([])) will clear any policies previously set.
|
||||
required: false
|
||||
suboptions:
|
||||
name:
|
||||
description:
|
||||
|
@ -70,7 +68,6 @@ options:
|
|||
- List of service identities to attach to the role.
|
||||
- If not specified, any service identities currently assigned will not be changed.
|
||||
- If the parameter is an empty array (V([])), any node identities assigned will be unassigned.
|
||||
required: false
|
||||
suboptions:
|
||||
name:
|
||||
description:
|
||||
|
@ -95,7 +92,6 @@ options:
|
|||
- List of node identities to attach to the role.
|
||||
- If not specified, any node identities currently assigned will not be changed.
|
||||
- If the parameter is an empty array (V([])), any node identities assigned will be unassigned.
|
||||
required: false
|
||||
suboptions:
|
||||
name:
|
||||
description:
|
||||
|
@ -110,36 +106,6 @@ options:
|
|||
- This will result in effective policy only being valid in this datacenter.
|
||||
type: str
|
||||
required: true
|
||||
host:
|
||||
description:
|
||||
- Host of the consul agent, defaults to V(localhost).
|
||||
required: false
|
||||
default: localhost
|
||||
type: str
|
||||
port:
|
||||
type: int
|
||||
description:
|
||||
- The port on which the consul agent is running.
|
||||
required: false
|
||||
default: 8500
|
||||
scheme:
|
||||
description:
|
||||
- The protocol scheme on which the consul agent is running.
|
||||
required: false
|
||||
default: http
|
||||
type: str
|
||||
token:
|
||||
description:
|
||||
- A management token is required to manipulate the roles.
|
||||
type: str
|
||||
validate_certs:
|
||||
type: bool
|
||||
description:
|
||||
- Whether to verify the TLS certificate of the consul agent.
|
||||
required: false
|
||||
default: true
|
||||
requirements:
|
||||
- requests
|
||||
'''
|
||||
|
||||
EXAMPLES = """
|
||||
|
@ -204,28 +170,11 @@ operation:
|
|||
"""
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.basic import missing_required_lib
|
||||
from ansible_collections.community.general.plugins.module_utils.consul import (
|
||||
get_consul_url, get_auth_headers, handle_consul_response_error)
|
||||
import traceback
|
||||
_ConsulModule, auth_argument_spec)
|
||||
|
||||
REQUESTS_IMP_ERR = None
|
||||
|
||||
try:
|
||||
from requests.exceptions import ConnectionError
|
||||
import requests
|
||||
HAS_REQUESTS = True
|
||||
except ImportError:
|
||||
HAS_REQUESTS = False
|
||||
REQUESTS_IMP_ERR = traceback.format_exc()
|
||||
|
||||
TOKEN_PARAMETER_NAME = "token"
|
||||
HOST_PARAMETER_NAME = "host"
|
||||
SCHEME_PARAMETER_NAME = "scheme"
|
||||
VALIDATE_CERTS_PARAMETER_NAME = "validate_certs"
|
||||
NAME_PARAMETER_NAME = "name"
|
||||
DESCRIPTION_PARAMETER_NAME = "description"
|
||||
PORT_PARAMETER_NAME = "port"
|
||||
POLICIES_PARAMETER_NAME = "policies"
|
||||
SERVICE_IDENTITIES_PARAMETER_NAME = "service_identities"
|
||||
NODE_IDENTITIES_PARAMETER_NAME = "node_identities"
|
||||
|
@ -254,19 +203,15 @@ SERVICE_ID_RULE_SPEC = dict(
|
|||
)
|
||||
|
||||
_ARGUMENT_SPEC = {
|
||||
TOKEN_PARAMETER_NAME: dict(no_log=True),
|
||||
PORT_PARAMETER_NAME: dict(default=8500, type='int'),
|
||||
HOST_PARAMETER_NAME: dict(default='localhost'),
|
||||
SCHEME_PARAMETER_NAME: dict(default='http'),
|
||||
VALIDATE_CERTS_PARAMETER_NAME: dict(type='bool', default=True),
|
||||
NAME_PARAMETER_NAME: dict(required=True),
|
||||
DESCRIPTION_PARAMETER_NAME: dict(required=False, type='str', default=None),
|
||||
POLICIES_PARAMETER_NAME: dict(type='list', elements='dict', options=POLICY_RULE_SPEC,
|
||||
mutually_exclusive=[('name', 'id')], required_one_of=[('name', 'id')], default=None),
|
||||
SERVICE_IDENTITIES_PARAMETER_NAME: dict(type='list', elements='dict', options=SERVICE_ID_RULE_SPEC, default=None),
|
||||
NODE_IDENTITIES_PARAMETER_NAME: dict(type='list', elements='dict', options=NODE_ID_RULE_SPEC, default=None),
|
||||
STATE_PARAMETER_NAME: dict(default=PRESENT_STATE_VALUE, choices=[PRESENT_STATE_VALUE, ABSENT_STATE_VALUE]),
|
||||
STATE_PARAMETER_NAME: dict(default=PRESENT_STATE_VALUE, choices=[PRESENT_STATE_VALUE, ABSENT_STATE_VALUE])
|
||||
}
|
||||
_ARGUMENT_SPEC.update(auth_argument_spec())
|
||||
|
||||
|
||||
def compare_consul_api_role_policy_objects(first, second):
|
||||
|
@ -280,11 +225,7 @@ def compare_consul_api_role_policy_objects(first, second):
|
|||
return first == second
|
||||
|
||||
|
||||
def update_role(role, configuration):
|
||||
url = '%s/acl/role/%s' % (get_consul_url(configuration),
|
||||
role['ID'])
|
||||
headers = get_auth_headers(configuration)
|
||||
|
||||
def update_role(role, configuration, consul_module):
|
||||
update_role_data = {
|
||||
'Name': configuration.name,
|
||||
'Description': configuration.description,
|
||||
|
@ -370,10 +311,7 @@ def update_role(role, configuration):
|
|||
if not node_id_specified and role.get('NodeIdentities') is not None:
|
||||
update_role_data["NodeIdentities"] = role.get('NodeIdentities')
|
||||
|
||||
response = requests.put(url, headers=headers, json=update_role_data, verify=configuration.validate_certs)
|
||||
handle_consul_response_error(response)
|
||||
|
||||
resulting_role = response.json()
|
||||
resulting_role = consul_module.put(('acl', 'role', role['ID']), data=update_role_data)
|
||||
changed = (
|
||||
role['Description'] != resulting_role['Description'] or
|
||||
role.get('Policies', None) != resulting_role.get('Policies', None) or
|
||||
|
@ -384,10 +322,7 @@ def update_role(role, configuration):
|
|||
return Output(changed=changed, operation=UPDATE_OPERATION, role=resulting_role)
|
||||
|
||||
|
||||
def create_role(configuration):
|
||||
url = '%s/acl/role' % get_consul_url(configuration)
|
||||
headers = get_auth_headers(configuration)
|
||||
|
||||
def create_role(configuration, consul_module):
|
||||
# check if the user omitted policies, service identities, or node identities
|
||||
policy_specified = True
|
||||
if len(configuration.policies) == 1 and configuration.policies[0] is None:
|
||||
|
@ -423,28 +358,21 @@ def create_role(configuration):
|
|||
create_role_data["NodeIdentities"] = [x.to_dict() for x in configuration.node_identities]
|
||||
|
||||
if not configuration.check_mode:
|
||||
response = requests.put(url, headers=headers, json=create_role_data, verify=configuration.validate_certs)
|
||||
handle_consul_response_error(response)
|
||||
|
||||
resulting_role = response.json()
|
||||
|
||||
resulting_role = consul_module.put('acl/role', data=create_role_data)
|
||||
return Output(changed=True, operation=CREATE_OPERATION, role=resulting_role)
|
||||
else:
|
||||
return Output(changed=True, operation=CREATE_OPERATION)
|
||||
|
||||
|
||||
def remove_role(configuration):
|
||||
roles = get_roles(configuration)
|
||||
def remove_role(configuration, consul_module):
|
||||
roles = get_roles(consul_module)
|
||||
|
||||
if configuration.name in roles:
|
||||
|
||||
role_id = roles[configuration.name]['ID']
|
||||
|
||||
if not configuration.check_mode:
|
||||
url = '%s/acl/role/%s' % (get_consul_url(configuration), role_id)
|
||||
headers = get_auth_headers(configuration)
|
||||
response = requests.delete(url, headers=headers, verify=configuration.validate_certs)
|
||||
handle_consul_response_error(response)
|
||||
consul_module.delete(('acl', 'role', role_id))
|
||||
|
||||
changed = True
|
||||
else:
|
||||
|
@ -452,33 +380,25 @@ def remove_role(configuration):
|
|||
return Output(changed=changed, operation=REMOVE_OPERATION)
|
||||
|
||||
|
||||
def get_roles(configuration):
|
||||
url = '%s/acl/roles' % get_consul_url(configuration)
|
||||
headers = get_auth_headers(configuration)
|
||||
response = requests.get(url, headers=headers, verify=configuration.validate_certs)
|
||||
handle_consul_response_error(response)
|
||||
roles = response.json()
|
||||
def get_roles(consul_module):
|
||||
roles = consul_module.get('acl/roles')
|
||||
existing_roles_mapped_by_id = dict((role['Name'], role) for role in roles if role['Name'] is not None)
|
||||
return existing_roles_mapped_by_id
|
||||
|
||||
|
||||
def get_consul_version(configuration):
|
||||
url = '%s/agent/self' % get_consul_url(configuration)
|
||||
headers = get_auth_headers(configuration)
|
||||
response = requests.get(url, headers=headers, verify=configuration.validate_certs)
|
||||
handle_consul_response_error(response)
|
||||
config = response.json()["Config"]
|
||||
def get_consul_version(consul_module):
|
||||
config = consul_module.get('agent/self')["Config"]
|
||||
return ConsulVersion(config["Version"])
|
||||
|
||||
|
||||
def set_role(configuration):
|
||||
roles = get_roles(configuration)
|
||||
def set_role(configuration, consul_module):
|
||||
roles = get_roles(consul_module)
|
||||
|
||||
if configuration.name in roles:
|
||||
role = roles[configuration.name]
|
||||
return update_role(role, configuration)
|
||||
return update_role(role, configuration, consul_module)
|
||||
else:
|
||||
return create_role(configuration)
|
||||
return create_role(configuration, consul_module)
|
||||
|
||||
|
||||
class ConsulVersion:
|
||||
|
@ -556,13 +476,8 @@ class Configuration:
|
|||
Configuration for this module.
|
||||
"""
|
||||
|
||||
def __init__(self, token=None, host=None, scheme=None, validate_certs=None, name=None, description=None, port=None,
|
||||
policies=None, service_identities=None, node_identities=None, state=None, check_mode=None):
|
||||
self.token = token # type: str
|
||||
self.host = host # type: str
|
||||
self.port = port # type: int
|
||||
self.scheme = scheme # type: str
|
||||
self.validate_certs = validate_certs # type: bool
|
||||
def __init__(self, name=None, description=None, policies=None, service_identities=None,
|
||||
node_identities=None, state=None, check_mode=None):
|
||||
self.name = name # type: str
|
||||
self.description = description # type: str
|
||||
if policies is not None:
|
||||
|
@ -597,44 +512,29 @@ def main():
|
|||
Main method.
|
||||
"""
|
||||
module = AnsibleModule(_ARGUMENT_SPEC, supports_check_mode=True)
|
||||
|
||||
if not HAS_REQUESTS:
|
||||
module.fail_json(msg=missing_required_lib("requests"),
|
||||
exception=REQUESTS_IMP_ERR)
|
||||
consul_module = _ConsulModule(module)
|
||||
|
||||
try:
|
||||
configuration = Configuration(
|
||||
token=module.params.get(TOKEN_PARAMETER_NAME),
|
||||
host=module.params.get(HOST_PARAMETER_NAME),
|
||||
port=module.params.get(PORT_PARAMETER_NAME),
|
||||
scheme=module.params.get(SCHEME_PARAMETER_NAME),
|
||||
validate_certs=module.params.get(VALIDATE_CERTS_PARAMETER_NAME),
|
||||
name=module.params.get(NAME_PARAMETER_NAME),
|
||||
description=module.params.get(DESCRIPTION_PARAMETER_NAME),
|
||||
policies=module.params.get(POLICIES_PARAMETER_NAME),
|
||||
service_identities=module.params.get(SERVICE_IDENTITIES_PARAMETER_NAME),
|
||||
node_identities=module.params.get(NODE_IDENTITIES_PARAMETER_NAME),
|
||||
state=module.params.get(STATE_PARAMETER_NAME),
|
||||
check_mode=module.check_mode
|
||||
|
||||
check_mode=module.check_mode,
|
||||
)
|
||||
except ValueError as err:
|
||||
module.fail_json(msg='Configuration error: %s' % str(err))
|
||||
return
|
||||
|
||||
try:
|
||||
version = get_consul_version(consul_module)
|
||||
configuration.version = version
|
||||
|
||||
version = get_consul_version(configuration)
|
||||
configuration.version = version
|
||||
|
||||
if configuration.state == PRESENT_STATE_VALUE:
|
||||
output = set_role(configuration)
|
||||
else:
|
||||
output = remove_role(configuration)
|
||||
except ConnectionError as e:
|
||||
module.fail_json(msg='Could not connect to consul agent at %s:%s, error was %s' % (
|
||||
configuration.host, configuration.port, str(e)))
|
||||
raise
|
||||
if configuration.state == PRESENT_STATE_VALUE:
|
||||
output = set_role(configuration, consul_module)
|
||||
else:
|
||||
output = remove_role(configuration, consul_module)
|
||||
|
||||
return_values = dict(changed=output.changed, operation=output.operation, role=output.role)
|
||||
module.exit_json(**return_values)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue