mirror of
https://github.com/ansible-collections/community.general.git
synced 2025-04-22 02:11:26 -07:00
[PR #7989/03966624 backport][stable-9] Consul implement agent service and check (#8513)
Consul implement agent service and check (#7989)
* Implement agent service and check (#7987)
* implement update of service and check
* update tests
update documentation
* update documentation
* add consul_agent_check/service to action_groups
check if unique_identifier of name is in params to get object
add suggested improvements
* update sanity
* fix sanity issues
update documentation
* fix naming
* fix naming
check if response_data has data
* fix sanity extra-docs
* add as ignore maintainer in BOTMETA.yml
update version_added to 8.4
* fix sanity
* add to maintainers
* Update plugins/modules/consul_agent_check.py
Co-authored-by: Felix Fontein <felix@fontein.de>
* Update plugins/modules/consul_agent_check.py
Co-authored-by: Felix Fontein <felix@fontein.de>
* Update plugins/modules/consul_agent_check.py
Co-authored-by: Felix Fontein <felix@fontein.de>
* update version_added
* if create and update return no object as result we read the object again
* get_first_appearing_identifier check the params for the given identifier and return it to simplify id vs name
* add unique_identifiers as a new property and a method to decide which identifier should be used
* fix sanity
* add self to team consul
remove params with no values
add operational_attributes that inherited classes can set them
get identifier value from object
* fix sanity
fix test
* remove the possibility to add checks with consul_agent_check.
check if service has changed
* remove tests for idempotency check because for checks it is not possible
* remove unique_identifier from consul.py
change unique_identifier to unique_identifiers
* get id from params
* Revert "remove unique_identifier from consul.py"
This reverts commit a4f0d0220dd23e95871914b152c25ff352097a2c.
* update version to 8.5
* Revert "Revert "remove unique_identifier from consul.py""
This reverts commit d2c35cf04c8aaf5f0175d772f862a796e22e35d4.
* update description
update test
* fix sanity tests
* fix sanity tests
* update documentation for agent_check
* fix line length
* add documentation
* fix sanity
* simplified check for Tcp
Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com>
* check duration with regex
* fix
* update documentation
---------
Co-authored-by: Felix Fontein <felix@fontein.de>
Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com>
(cherry picked from commit 03966624ba
)
Co-authored-by: Ilgmi <michael.ilg@mailbox.org>
This commit is contained in:
parent
d6168a196b
commit
2d26fba0b9
13 changed files with 810 additions and 21 deletions
|
@ -10,6 +10,7 @@ __metaclass__ = type
|
|||
|
||||
import copy
|
||||
import json
|
||||
import re
|
||||
|
||||
from ansible.module_utils.six.moves.urllib import error as urllib_error
|
||||
from ansible.module_utils.six.moves.urllib.parse import urlencode
|
||||
|
@ -68,6 +69,25 @@ def camel_case_key(key):
|
|||
return "".join(parts)
|
||||
|
||||
|
||||
def validate_check(check):
|
||||
validate_duration_keys = ['Interval', 'Ttl', 'Timeout']
|
||||
validate_tcp_regex = r"(?P<host>.*):(?P<port>(?:[0-9]+))$"
|
||||
if check.get('Tcp') is not None:
|
||||
match = re.match(validate_tcp_regex, check['Tcp'])
|
||||
if not match:
|
||||
raise Exception('tcp check must be in host:port format')
|
||||
for duration in validate_duration_keys:
|
||||
if duration in check and check[duration] is not None:
|
||||
check[duration] = validate_duration(check[duration])
|
||||
|
||||
|
||||
def validate_duration(duration):
|
||||
if duration:
|
||||
if not re.search(r"\d+(?:ns|us|ms|s|m|h)", duration):
|
||||
duration = "{0}s".format(duration)
|
||||
return duration
|
||||
|
||||
|
||||
STATE_PARAMETER = "state"
|
||||
STATE_PRESENT = "present"
|
||||
STATE_ABSENT = "absent"
|
||||
|
@ -81,7 +101,7 @@ OPERATION_DELETE = "remove"
|
|||
def _normalize_params(params, arg_spec):
|
||||
final_params = {}
|
||||
for k, v in params.items():
|
||||
if k not in arg_spec: # Alias
|
||||
if k not in arg_spec or v is None: # Alias
|
||||
continue
|
||||
spec = arg_spec[k]
|
||||
if (
|
||||
|
@ -105,9 +125,10 @@ class _ConsulModule:
|
|||
"""
|
||||
|
||||
api_endpoint = None # type: str
|
||||
unique_identifier = None # type: str
|
||||
unique_identifiers = None # type: list
|
||||
result_key = None # type: str
|
||||
create_only_fields = set()
|
||||
operational_attributes = set()
|
||||
params = {}
|
||||
|
||||
def __init__(self, module):
|
||||
|
@ -119,6 +140,8 @@ class _ConsulModule:
|
|||
if k not in STATE_PARAMETER and k not in AUTH_ARGUMENTS_SPEC
|
||||
}
|
||||
|
||||
self.operational_attributes.update({"CreateIndex", "CreateTime", "Hash", "ModifyIndex"})
|
||||
|
||||
def execute(self):
|
||||
obj = self.read_object()
|
||||
|
||||
|
@ -203,14 +226,24 @@ class _ConsulModule:
|
|||
return False
|
||||
|
||||
def prepare_object(self, existing, obj):
|
||||
operational_attributes = {"CreateIndex", "CreateTime", "Hash", "ModifyIndex"}
|
||||
existing = {
|
||||
k: v for k, v in existing.items() if k not in operational_attributes
|
||||
k: v for k, v in existing.items() if k not in self.operational_attributes
|
||||
}
|
||||
for k, v in obj.items():
|
||||
existing[k] = v
|
||||
return existing
|
||||
|
||||
def id_from_obj(self, obj, camel_case=False):
|
||||
def key_func(key):
|
||||
return camel_case_key(key) if camel_case else key
|
||||
|
||||
if self.unique_identifiers:
|
||||
for identifier in self.unique_identifiers:
|
||||
identifier = key_func(identifier)
|
||||
if identifier in obj:
|
||||
return obj[identifier]
|
||||
return None
|
||||
|
||||
def endpoint_url(self, operation, identifier=None):
|
||||
if operation == OPERATION_CREATE:
|
||||
return self.api_endpoint
|
||||
|
@ -219,7 +252,8 @@ class _ConsulModule:
|
|||
raise RuntimeError("invalid arguments passed")
|
||||
|
||||
def read_object(self):
|
||||
url = self.endpoint_url(OPERATION_READ, self.params.get(self.unique_identifier))
|
||||
identifier = self.id_from_obj(self.params)
|
||||
url = self.endpoint_url(OPERATION_READ, identifier)
|
||||
try:
|
||||
return self.get(url)
|
||||
except RequestError as e:
|
||||
|
@ -233,25 +267,28 @@ class _ConsulModule:
|
|||
if self._module.check_mode:
|
||||
return obj
|
||||
else:
|
||||
return self.put(self.api_endpoint, data=self.prepare_object({}, obj))
|
||||
url = self.endpoint_url(OPERATION_CREATE)
|
||||
created_obj = self.put(url, data=self.prepare_object({}, obj))
|
||||
if created_obj is None:
|
||||
created_obj = self.read_object()
|
||||
return created_obj
|
||||
|
||||
def update_object(self, existing, obj):
|
||||
url = self.endpoint_url(
|
||||
OPERATION_UPDATE, existing.get(camel_case_key(self.unique_identifier))
|
||||
)
|
||||
merged_object = self.prepare_object(existing, obj)
|
||||
if self._module.check_mode:
|
||||
return merged_object
|
||||
else:
|
||||
return self.put(url, data=merged_object)
|
||||
url = self.endpoint_url(OPERATION_UPDATE, self.id_from_obj(existing, camel_case=True))
|
||||
updated_obj = self.put(url, data=merged_object)
|
||||
if updated_obj is None:
|
||||
updated_obj = self.read_object()
|
||||
return updated_obj
|
||||
|
||||
def delete_object(self, obj):
|
||||
if self._module.check_mode:
|
||||
return {}
|
||||
else:
|
||||
url = self.endpoint_url(
|
||||
OPERATION_DELETE, obj.get(camel_case_key(self.unique_identifier))
|
||||
)
|
||||
url = self.endpoint_url(OPERATION_DELETE, self.id_from_obj(obj, camel_case=True))
|
||||
return self.delete(url)
|
||||
|
||||
def _request(self, method, url_parts, data=None, params=None):
|
||||
|
@ -309,7 +346,9 @@ class _ConsulModule:
|
|||
if 400 <= status < 600:
|
||||
raise RequestError(status, response_data)
|
||||
|
||||
return json.loads(response_data)
|
||||
if response_data:
|
||||
return json.loads(response_data)
|
||||
return None
|
||||
|
||||
def get(self, url_parts, **kwargs):
|
||||
return self._request("GET", url_parts, **kwargs)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue