[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:
patchback[bot] 2024-01-21 17:51:45 +00:00 committed by GitHub
parent 13c25154b5
commit 1ab1f8f62b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 365 additions and 476 deletions

View file

@ -16,12 +16,11 @@ description:
cluster. These sessions can then be used in conjunction with key value pairs
to implement distributed locks. In depth documentation for working with
sessions can be found at http://www.consul.io/docs/internals/sessions.html
requirements:
- requests
author:
- Steve Gargan (@sgargan)
- Håkon Lerring (@Hakon)
extends_documentation_fragment:
- community.general.consul
- community.general.attributes
attributes:
check_mode:
@ -76,26 +75,6 @@ options:
the associated lock delay has expired.
type: list
elements: str
host:
description:
- The host of the consul agent defaults to localhost.
type: str
default: localhost
port:
description:
- The port on which the consul agent is running.
type: int
default: 8500
scheme:
description:
- The protocol scheme on which the consul agent is running.
type: str
default: http
validate_certs:
description:
- Whether to verify the TLS certificate of the consul agent.
type: bool
default: true
behavior:
description:
- The optional behavior that can be attached to the session when it
@ -109,10 +88,6 @@ options:
type: int
version_added: 5.4.0
token:
description:
- The token key identifying an ACL rule set that controls access to
the key value pair.
type: str
version_added: 5.6.0
'''
@ -148,95 +123,49 @@ EXAMPLES = '''
'''
from ansible.module_utils.basic import AnsibleModule
try:
import requests
from requests.exceptions import ConnectionError
has_requests = True
except ImportError:
has_requests = False
from ansible_collections.community.general.plugins.module_utils.consul import (
auth_argument_spec, _ConsulModule
)
def execute(module):
def execute(module, consul_module):
state = module.params.get('state')
if state in ['info', 'list', 'node']:
lookup_sessions(module)
lookup_sessions(module, consul_module)
elif state == 'present':
update_session(module)
update_session(module, consul_module)
else:
remove_session(module)
remove_session(module, consul_module)
class RequestError(Exception):
pass
def list_sessions(consul_module, datacenter):
return consul_module.get(
'session/list',
params={'dc': datacenter})
def handle_consul_response_error(response):
if 400 <= response.status_code < 600:
raise RequestError('%d %s' % (response.status_code, response.content))
def list_sessions_for_node(consul_module, node, datacenter):
return consul_module.get(
('session', 'node', node),
params={'dc': datacenter})
def get_consul_url(module):
return '%s://%s:%s/v1' % (module.params.get('scheme'),
module.params.get('host'), module.params.get('port'))
def get_session_info(consul_module, session_id, datacenter):
return consul_module.get(
('session', 'info', session_id),
params={'dc': datacenter})
def get_auth_headers(module):
if 'token' in module.params and module.params.get('token') is not None:
return {'X-Consul-Token': module.params.get('token')}
else:
return {}
def list_sessions(module, datacenter):
url = '%s/session/list' % get_consul_url(module)
headers = get_auth_headers(module)
response = requests.get(
url,
headers=headers,
params={
'dc': datacenter},
verify=module.params.get('validate_certs'))
handle_consul_response_error(response)
return response.json()
def list_sessions_for_node(module, node, datacenter):
url = '%s/session/node/%s' % (get_consul_url(module), node)
headers = get_auth_headers(module)
response = requests.get(
url,
headers=headers,
params={
'dc': datacenter},
verify=module.params.get('validate_certs'))
handle_consul_response_error(response)
return response.json()
def get_session_info(module, session_id, datacenter):
url = '%s/session/info/%s' % (get_consul_url(module), session_id)
headers = get_auth_headers(module)
response = requests.get(
url,
headers=headers,
params={
'dc': datacenter},
verify=module.params.get('validate_certs'))
handle_consul_response_error(response)
return response.json()
def lookup_sessions(module):
def lookup_sessions(module, consul_module):
datacenter = module.params.get('datacenter')
state = module.params.get('state')
try:
if state == 'list':
sessions_list = list_sessions(module, datacenter)
sessions_list = list_sessions(consul_module, datacenter)
# Ditch the index, this can be grabbed from the results
if sessions_list and len(sessions_list) >= 2:
sessions_list = sessions_list[1]
@ -244,14 +173,14 @@ def lookup_sessions(module):
sessions=sessions_list)
elif state == 'node':
node = module.params.get('node')
sessions = list_sessions_for_node(module, node, datacenter)
sessions = list_sessions_for_node(consul_module, node, datacenter)
module.exit_json(changed=True,
node=node,
sessions=sessions)
elif state == 'info':
session_id = module.params.get('id')
session_by_id = get_session_info(module, session_id, datacenter)
session_by_id = get_session_info(consul_module, session_id, datacenter)
module.exit_json(changed=True,
session_id=session_id,
sessions=session_by_id)
@ -260,10 +189,8 @@ def lookup_sessions(module):
module.fail_json(msg="Could not retrieve session info %s" % e)
def create_session(module, name, behavior, ttl, node,
def create_session(consul_module, name, behavior, ttl, node,
lock_delay, datacenter, checks):
url = '%s/session/create' % get_consul_url(module)
headers = get_auth_headers(module)
create_data = {
"LockDelay": lock_delay,
"Node": node,
@ -273,19 +200,15 @@ def create_session(module, name, behavior, ttl, node,
}
if ttl is not None:
create_data["TTL"] = "%ss" % str(ttl) # TTL is in seconds
response = requests.put(
url,
headers=headers,
create_session_response_dict = consul_module.put(
'session/create',
params={
'dc': datacenter},
json=create_data,
verify=module.params.get('validate_certs'))
handle_consul_response_error(response)
create_session_response_dict = response.json()
data=create_data)
return create_session_response_dict["ID"]
def update_session(module):
def update_session(module, consul_module):
name = module.params.get('name')
delay = module.params.get('delay')
@ -296,7 +219,7 @@ def update_session(module):
ttl = module.params.get('ttl')
try:
session = create_session(module,
session = create_session(consul_module,
name=name,
behavior=behavior,
ttl=ttl,
@ -317,22 +240,15 @@ def update_session(module):
module.fail_json(msg="Could not create/update session %s" % e)
def destroy_session(module, session_id):
url = '%s/session/destroy/%s' % (get_consul_url(module), session_id)
headers = get_auth_headers(module)
response = requests.put(
url,
headers=headers,
verify=module.params.get('validate_certs'))
handle_consul_response_error(response)
return response.content == "true"
def destroy_session(consul_module, session_id):
return consul_module.put(('session', 'destroy', session_id))
def remove_session(module):
def remove_session(module, consul_module):
session_id = module.params.get('id')
try:
destroy_session(module, session_id)
destroy_session(consul_module, session_id)
module.exit_json(changed=True,
session_id=session_id)
@ -341,12 +257,6 @@ def remove_session(module):
session_id, e))
def test_dependencies(module):
if not has_requests:
raise ImportError(
"requests required for this module. See https://pypi.org/project/requests/")
def main():
argument_spec = dict(
checks=dict(type='list', elements='str'),
@ -358,10 +268,6 @@ def main():
'release',
'delete']),
ttl=dict(type='int'),
host=dict(type='str', default='localhost'),
port=dict(type='int', default=8500),
scheme=dict(type='str', default='http'),
validate_certs=dict(type='bool', default=True),
id=dict(type='str'),
name=dict(type='str'),
node=dict(type='str'),
@ -375,7 +281,7 @@ def main():
'node',
'present']),
datacenter=dict(type='str'),
token=dict(type='str', no_log=True),
**auth_argument_spec()
)
module = AnsibleModule(
@ -387,14 +293,10 @@ def main():
],
supports_check_mode=False
)
test_dependencies(module)
consul_module = _ConsulModule(module)
try:
execute(module)
except ConnectionError as e:
module.fail_json(msg='Could not connect to consul agent at %s:%s, error was %s' % (
module.params.get('host'), module.params.get('port'), e))
execute(module, consul_module)
except Exception as e:
module.fail_json(msg=str(e))