diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index aa5062b..ef70cab 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -11,15 +11,15 @@ assignees: '' -# Summary +## Summary -# Issue Type +## Issue Type - Bug Report -# Ansible, Collection, Event Driven Ansible Controller details +## Ansible, Collection, Event Driven Ansible Controller details @@ -34,15 +34,15 @@ Event Driven Ansible Controller version - ansible installation method: one of source, pip, OS package, EE -# OS / ENVIRONMENT +## OS / ENVIRONMENT -# Desired Behavior +## Desired Behavior -# Actual Behavior +## Actual Behavior @@ -59,7 +59,7 @@ Include a [minimum complete verifiable example] with: ``` -# STEPS TO REPRODUCE +## STEPS TO REPRODUCE diff --git a/README.md b/README.md index 4de2291..8c3208f 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,7 @@ collections: ## Using this collection -You can make use of this collection by directly invoking the roles or modules using the FQCN (fully qualified collection name). +You can make use of this collection by directly invoking the roles using the FQCN (fully qualified collection name). In a playbook this might look like: @@ -67,18 +67,6 @@ In a playbook this might look like: - infra.eda_configuration.projects ``` -or - -```yaml -- name: Call Project role - hosts: localhost - tasks: - - name: Add a project - infra.eda_configuration.project: - name: my_project - url: https://github.com/my/project.git -``` - ### See Also - [Ansible Using collections](https://docs.ansible.com/ansible/latest/user_guide/collections_using.html) for more details. diff --git a/changelogs/.plugin-cache.yaml b/changelogs/.plugin-cache.yaml index ac6084d..e50c9a1 100644 --- a/changelogs/.plugin-cache.yaml +++ b/changelogs/.plugin-cache.yaml @@ -29,59 +29,9 @@ objects: description: An Ansible Role to create users in EDA controller. name: user version_added: null - user_token: + controller_token: description: An Ansible Role to create user tokens in EDA controller. - name: user_token - version_added: null -plugins: - become: {} - cache: {} - callback: {} - cliconf: {} - connection: {} - filter: {} - httpapi: {} - inventory: {} - lookup: - eda_api: - description: Search the API for objects - name: eda_api - version_added: null - module: - credential: - description: Manage a Credential in EDA Controller - name: credential - namespace: '' - version_added: null - decision_environment: - description: Manage a Decision Environment in EDA Controller - name: decision_environment - namespace: '' - version_added: null - project: - description: Manage a project in EDA Controller - name: project - namespace: '' - version_added: null - project_sync: - description: Sync a project in EDA Controller - name: project_sync - namespace: '' - version_added: null - rulebook_activation: - description: Manage a rulebook_activation in EDA Controller - name: rulebook_activation - namespace: '' - version_added: null - user: - description: Manage a user in EDA Controller - name: user - namespace: '' - version_added: null - user_token: - description: Manage the user tokens of the current user in EDA Controller - name: user_token - namespace: '' + name: controller_token version_added: null netconf: {} shell: {} diff --git a/galaxy.yml b/galaxy.yml index 2479783..0a19a3d 100644 --- a/galaxy.yml +++ b/galaxy.yml @@ -4,7 +4,8 @@ authors: - Tom Page @Tompage1994 - Sean Sullivan @sean-m-sullivan - David Danielsson @djdanielsson -dependencies: {} +dependencies: + ansible.eda: ">=2.1.0" description: Ansible content that interacts with the Ansible EDA Controller. documentation: https://github.com/redhat-cop/eda_configuration/blob/devel/README.md license: diff --git a/plugins/doc_fragments/auth.py b/plugins/doc_fragments/auth.py deleted file mode 100644 index 6b354c3..0000000 --- a/plugins/doc_fragments/auth.py +++ /dev/null @@ -1,54 +0,0 @@ -# -*- coding: utf-8 -*- - -# Copyright: (c) 2023, Chris Renwick <@crenwick93> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function - -__metaclass__ = type - - -class ModuleDocFragment(object): - - # Ansible Galaxy documentation fragment - DOCUMENTATION = r""" -options: - eda_host: - description: - - URL to Ansible Galaxy or EDA Controller instance. - - If value not set, will try environment variable C(EDA_HOST) - - If value not specified by any means, the value of C(127.0.0.1) will be used - type: str - aliases: [ eda_hostname ] - eda_username: - description: - - Username for your Ansible Galaxy or EDA Controller instance. - - If value not set, will try environment variable C(EDA_USERNAME) - type: str - eda_password: - description: - - Password for your Ansible Galaxy or EDA Controller instance. - - If value not set, will try environment variable C(EDA_PASSWORD) - type: str - eda_token: - description: - - The Ansible Galaxy or EDA Controller API token to use. - - This value can be in one of two formats. - - A string which is the token itself. (i.e. bqV5txm97wqJqtkxlMkhQz0pKhRMMX) - - A dictionary structure as returned by the eda_token module. - - If value not set, will try environment variable C(EDA_API_TOKEN) - type: raw - validate_certs: - description: - - Whether to allow insecure connections to Galaxy or EDA Controller Server. - - If C(no), SSL certificates will not be validated. - - This should only be used on personally controlled sites using self-signed certificates. - - If value not set, will try environment variable C(EDA_VERIFY_SSL) - type: bool - aliases: [ eda_verify_ssl ] - request_timeout: - description: - - Specify the timeout Ansible should use in requests to the Galaxy or EDA Controller host. - - Defaults to 10s, but this is handled by the shared module_utils code - type: float -""" diff --git a/plugins/doc_fragments/auth_plugin.py b/plugins/doc_fragments/auth_plugin.py deleted file mode 100644 index bf09ef3..0000000 --- a/plugins/doc_fragments/auth_plugin.py +++ /dev/null @@ -1,43 +0,0 @@ -# -*- coding: utf-8 -*- - -# Copyright: (c) 2020, Ansible by Red Hat, Inc -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function - -__metaclass__ = type - - -class ModuleDocFragment(object): - - # Automation Platform EDA Controller documentation fragment - DOCUMENTATION = r""" -options: - host: - description: The network address of your EDA Controller host. - env: - - name: EDA_HOST - username: - description: The user that you plan to use to access EDA Controller. - env: - - name: EDA_USERNAME - password: - description: The password for your EDA Controller user. - env: - - name: EDA_PASSWORD - request_timeout: - description: - - Specify the timeout Ansible should use in requests to the EDA Controller host. - - Defaults to 10 seconds - type: float - env: - - name: EDA_REQUEST_TIMEOUT - verify_ssl: - description: - - Specify whether Ansible should verify the SSL certificate of the EDA Controller host. - - Defaults to True, but this is handled by the shared module_utils code - type: bool - env: - - name: EDA_VERIFY_SSL - aliases: [ validate_certs ] -""" diff --git a/plugins/lookup/eda_api.py b/plugins/lookup/eda_api.py deleted file mode 100644 index 3710a3b..0000000 --- a/plugins/lookup/eda_api.py +++ /dev/null @@ -1,160 +0,0 @@ -# (c) 2020 Ansible Project -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -from __future__ import absolute_import, division, print_function - -__metaclass__ = type - -DOCUMENTATION = """ -name: eda_api -author: Tom Page (@Tompage1994) -short_description: Search the API for objects -requirements: - - None -description: - - Returns GET requests from the EDA Controller API. -options: - _terms: - description: - - The endpoint to query, i.e. credentials, decision_environments, projects, etc. - required: True - query_params: - description: - - The query parameters to search for in the form of key/value pairs. - type: dict - required: False - aliases: [query, data, filter, params] - expect_objects: - description: - - Error if the response does not contain either a detail view or a list view. - type: boolean - default: False - aliases: [expect_object] - expect_one: - description: - - Error if the response contains more than one object. - type: boolean - default: False - return_objects: - description: - - If a list view is returned, promote the list of data to the top-level of list returned. - - Allows using this lookup plugin to loop over objects without additional work. - type: boolean - default: True - return_all: - description: - - If the response is paginated, return all pages. - type: boolean - default: False - return_ids: - description: - - If response contains objects, promote the id key to the top-level entries in the list. - - Allows looking up a related object and passing it as a parameter to another module. - - This will convert the return to a string or list of strings depending on the number of selected items. - type: boolean - aliases: [return_id] - default: False - max_objects: - description: - - if C(return_all) is true, this is the maximum of number of objects to return from the list. - - If a list view returns more an max_objects an exception will be raised - type: integer - default: 1000 -extends_documentation_fragment: infra.eda_configuration.auth_plugin -notes: - - If the query is not filtered properly this can cause a performance impact. -""" - -EXAMPLES = """ -- name: Report the usernames of all users - debug: - msg: "Users: {{ query('infra.eda_configuration.eda_api', 'users', return_all=true) | map(attribute='username') | list }}" - -- name: List all projects which use the ansible/eda github repo - debug: - msg: "{{ lookup('infra.eda_configuration.eda_api', 'project', host='https://eda.example.com', username='ansible', - password='Passw0rd123', verify_ssl=false, query_params={'url': 'https://github.com/ansible/event-driven-ansible.git'}) }}" -""" - -RETURN = """ -_raw: - description: - - Response from the API - type: dict - returned: on successful request -""" - -from ansible.plugins.lookup import LookupBase -from ansible.errors import AnsibleError -from ansible.module_utils._text import to_native -from ansible.utils.display import Display -from ..module_utils.eda_module import EDAModule - - -display = Display() - - -class LookupModule(LookupBase): - def handle_error(self, **kwargs): - raise AnsibleError(to_native(kwargs.get('msg'))) - - def warn_callback(self, warning): - self.display.warning(warning) - - def run(self, terms, variables=None, **kwargs): - if len(terms) != 1: - raise AnsibleError('You must pass exactly one endpoint to query') - - self.set_options(direct=kwargs) - - # Defer processing of params to logic shared with the modules - module_params = {} - for plugin_param, module_param in EDAModule.short_params.items(): - opt_val = self.get_option(plugin_param) - if opt_val is not None: - module_params[module_param] = opt_val - - # Create our module - module = EDAModule(argument_spec={}, direct_params=module_params, error_callback=self.handle_error, warn_callback=self.warn_callback) - - response = module.get_endpoint(terms[0], data=self.get_option('query_params', {})) - - if 'status_code' not in response: - raise AnsibleError("Unclear response from API: {0}".format(response)) - - if response['status_code'] != 200: - raise AnsibleError("Failed to query the API: {0}".format(response['json'].get('detail', response['json']))) - - return_data = response['json'] - - if self.get_option('expect_objects') or self.get_option('expect_one'): - if ('id' not in return_data) and ('results' not in return_data): - raise AnsibleError('Did not obtain a list or detail view at {0}, and ' 'expect_objects or expect_one is set to True'.format(terms[0])) - - if self.get_option('expect_one'): - if 'results' in return_data and len(return_data['results']) != 1: - raise AnsibleError('Expected one object from endpoint {0}, ' 'but obtained {1} from API'.format(terms[0], len(return_data['results']))) - - if self.get_option('return_all') and 'results' in return_data: - if return_data['count'] > self.get_option('max_objects'): - raise AnsibleError( - 'List view at {0} returned {1} objects, which is more than the maximum allowed ' - 'by max_objects, {2}'.format(terms[0], return_data['count'], self.get_option('max_objects')) - ) - - next_page = return_data['next'] - while next_page is not None: - next_response = module.get_endpoint(next_page) - return_data['results'] += next_response['json']['results'] - next_page = next_response['json']['next'] - return_data['next'] = None - - if self.get_option('return_ids'): - if 'results' in return_data: - return_data['results'] = [str(item['id']) for item in return_data['results']] - elif 'id' in return_data: - return_data = str(return_data['id']) - - if self.get_option('return_objects') and 'results' in return_data: - return return_data['results'] - else: - return [return_data] diff --git a/plugins/module_utils/eda_module.py b/plugins/module_utils/eda_module.py deleted file mode 100644 index 138000e..0000000 --- a/plugins/module_utils/eda_module.py +++ /dev/null @@ -1,904 +0,0 @@ -from __future__ import absolute_import, division, print_function - -__metaclass__ = type - -from ansible.module_utils.basic import AnsibleModule, env_fallback -from ansible.module_utils.urls import ( - Request, - SSLValidationError, - ConnectionError, -) -from ansible.module_utils.six import PY2 -from ansible.module_utils.six.moves.urllib.parse import urlparse, urlencode -from ansible.module_utils.six.moves.urllib.error import HTTPError -from ansible.module_utils.six.moves.http_cookiejar import CookieJar -import os.path -from socket import gethostbyname -import re -from json import loads, dumps -import base64 -import os -import time - - -class ItemNotDefined(Exception): - pass - - -class EDAModule(AnsibleModule): - url = None - session = None - AUTH_ARGSPEC = dict( - eda_host=dict( - required=False, - aliases=["eda_hostname"], - fallback=(env_fallback, ["EDA_HOST"]), - ), - eda_username=dict(required=False, fallback=(env_fallback, ["EDA_USERNAME"])), - eda_password=dict(no_log=True, required=False, fallback=(env_fallback, ["EDA_PASSWORD"])), - validate_certs=dict( - type="bool", - aliases=["eda_verify_ssl"], - required=False, - fallback=(env_fallback, ["EDA_VERIFY_SSL"]), - ), - eda_token=dict( - type="raw", - no_log=True, - required=False, - fallback=(env_fallback, ["EDA_API_TOKEN"]), - ), - request_timeout=dict( - type="float", - required=False, - fallback=(env_fallback, ["EDA_REQUEST_TIMEOUT"]) - ), - ) - ENCRYPTED_STRING = "$encrypted$" - short_params = { - "host": "eda_host", - "username": "eda_username", - "password": "eda_password", - "verify_ssl": "validate_certs", - "request_timeout": "request_timeout", - } - IDENTITY_FIELDS = {"users": "username"} - ENCRYPTED_STRING = "$encrypted$" - host = "127.0.0.1" - username = None - password = None - verify_ssl = True - request_timeout = 10 - basic_auth = True - authenticated = False - error_callback = None - warn_callback = None - - def __init__(self, argument_spec=None, direct_params=None, error_callback=None, warn_callback=None, require_auth=True, **kwargs): - full_argspec = {} - if require_auth: - full_argspec.update(EDAModule.AUTH_ARGSPEC) - full_argspec.update(argument_spec) - kwargs["supports_check_mode"] = True - - self.error_callback = error_callback - self.warn_callback = warn_callback - - self.json_output = {"changed": False} - - if direct_params is not None: - self.params = direct_params - else: - super(EDAModule, self).__init__(argument_spec=full_argspec, **kwargs) - - self.session = Request(cookies=CookieJar(), validate_certs=self.verify_ssl, timeout=self.request_timeout) - - # Parameters specified on command line will override settings in any config - for short_param, long_param in self.short_params.items(): - direct_value = self.params.get(long_param) - if direct_value is not None: - setattr(self, short_param, direct_value) - - # Perform some basic validation - if not re.match("^https{0,1}://", self.host): - self.host = "https://{0}".format(self.host) - - # Try to parse the hostname as a url - try: - self.url = urlparse(self.host) - except Exception as e: - self.fail_json(msg="Unable to parse eda host as a URL ({1}): {0}".format(self.host, e)) - - # Try to resolve the hostname - hostname = self.url.netloc.split(":")[0] - try: - gethostbyname(hostname) - except Exception as e: - self.fail_json(msg="Unable to resolve eda host ({1}): {0}".format(hostname, e)) - - if "update_secrets" in self.params: - self.update_secrets = self.params.pop("update_secrets") - else: - self.update_secrets = True - - def build_url(self, endpoint, query_params=None): - # Make sure we start with /api/vX - if not endpoint.startswith("/"): - endpoint = "/{0}".format(endpoint) - if not endpoint.startswith("/api/"): - endpoint = "api/eda/v1{0}".format(endpoint) - if not endpoint.endswith("/") and "?" not in endpoint: - endpoint = "{0}/".format(endpoint) - - # Update the URL path with the endpoint - url = self.url._replace(path=endpoint) - - if query_params: - url = url._replace(query=urlencode(query_params)) - - return url - - def fail_json(self, **kwargs): - # Try to log out if we are authenticated - if self.error_callback: - self.error_callback(**kwargs) - else: - super(EDAModule, self).fail_json(**kwargs) - - def exit_json(self, **kwargs): - # Try to log out if we are authenticated - super(EDAModule, self).exit_json(**kwargs) - - def warn(self, warning): - if self.warn_callback is not None: - self.warn_callback(warning) - else: - super(EDAModule, self).warn(warning) - - @staticmethod - def get_name_field_from_endpoint(endpoint): - return EDAModule.IDENTITY_FIELDS.get(endpoint, "name") - - def get_endpoint(self, endpoint, *args, **kwargs): - return self.make_request("GET", endpoint, **kwargs) - - def make_request(self, method, endpoint, *args, **kwargs): - # In case someone is calling us directly; make sure we were given a method, let's not just assume a GET - if not method: - raise Exception("The HTTP method must be defined") - - # Extract the headers, this will be used in a couple of places - headers = kwargs.get("headers", {}) - - # Authenticate to EDA controller (if we don't have a token and if not already done so) - if not self.authenticated: - self.authenticate(**kwargs) - if self.basic_auth: - basic_str = base64.b64encode("{0}:{1}".format(self.username, self.password).encode("ascii")) - headers["Authorization"] = "Basic {0}".format(basic_str.decode("ascii")) - if method in ["POST", "PUT", "PATCH"]: - headers.setdefault("Content-Type", "application/json") - kwargs["headers"] = headers - url = self.build_url(endpoint) - else: - url = self.build_url(endpoint, query_params=kwargs.get("data")) - - data = None # Important, if content type is not JSON, this should not be dict type - if headers.get("Content-Type", "") == "application/json": - data = dumps(kwargs.get("data", {})) - elif kwargs.get("binary", False): - data = kwargs.get("data", None) - - try: - response = self.session.open( - method, - url.geturl(), - headers=headers, - validate_certs=self.verify_ssl, - timeout=self.request_timeout, - follow_redirects=True, - data=data, - ) - except (SSLValidationError) as ssl_err: - self.fail_json(msg="Could not establish a secure connection to your host ({1}): {0}.".format(url.netloc, ssl_err)) - except (ConnectionError) as con_err: - self.fail_json(msg="There was a network error of some kind trying to connect to your host ({1}): {0}.".format(url.netloc, con_err)) - except (HTTPError) as he: - # Sanity check: Did the server send back some kind of internal error? - if he.code >= 500: - self.fail_json(msg="The host sent back a server error ({1}): {0}. Please check the logs and try again later".format(url.path, he)) - # Sanity check: Did we fail to authenticate properly? If so, fail out now; this is always a failure. - elif he.code == 401: - self.fail_json(msg="Invalid EDA Controller authentication credentials for {0} (HTTP 401).".format(url.path)) - # Sanity check: Did we get a forbidden response, which means that the user isn't allowed to do this? Report that. - elif he.code == 403: - self.fail_json(msg="You don't have permission to {1} to {0} (HTTP 403).".format(url.path, method)) - # Sanity check: Did we get a 404 response? - # Requests with primary keys will return a 404 if there is no response, and we want to consistently trap these. - elif he.code == 404: - if kwargs.get("return_none_on_404", False): - return None - if kwargs.get("return_errors_on_404", False): - page_data = he.read() - try: - return {"status_code": he.code, "json": loads(page_data)} - # JSONDecodeError only available on Python 3.5+ - except ValueError: - return {"status_code": he.code, "text": page_data} - self.fail_json( - msg="The requested object could not be found at {0}.".format(url.path), - response=he, - ) - # Sanity check: Did we get a 405 response? - # A 405 means we used a method that isn't allowed. Usually this is a bad request, but it requires special treatment because the - # API sends it as a logic error in a few situations (e.g. trying to cancel a job that isn't running). - elif he.code == 405: - self.fail_json(msg="The EDA Controller server says you can't make a request with the {0} method to this endpoint {1}".format(method, url.path)) - # Sanity check: Did we get some other kind of error? If so, write an appropriate error message. - elif he.code >= 400: - # We are going to return a 400 so the module can decide what to do with it - page_data = he.read() - try: - return {"status_code": he.code, "json": loads(page_data)} - # JSONDecodeError only available on Python 3.5+ - except ValueError: - return {"status_code": he.code, "text": page_data} - elif he.code == 204 and method == "DELETE": - # A 204 is a normal response for a delete function - pass - else: - self.fail_json(msg="Unexpected return code when calling {0}: {1}".format(url.geturl(), he)) - except (Exception) as e: - self.fail_json(msg="There was an unknown error when trying to connect to {2}: {0} {1}".format(type(e).__name__, e, url.geturl())) - - response_body = "" - try: - response_body = response.read() - except (Exception) as e: - self.fail_json(msg="Failed to read response body: {0}".format(e)) - - response_json = {} - if response_body and response_body != "": - try: - response_json = loads(response_body) - except (Exception) as e: - self.fail_json(msg="Failed to parse the response json: {0}".format(e)) - - if PY2: - status_code = response.getcode() - else: - status_code = response.status - return {"status_code": status_code, "json": response_json} - - def get_one(self, endpoint, name_or_id=None, allow_none=True, key="url", **kwargs): - new_kwargs = kwargs.copy() - if name_or_id: - name_field = self.get_name_field_from_endpoint(endpoint) - new_data = kwargs.get("data", {}).copy() - if name_field in new_data: - self.fail_json(msg="You can't specify the field {0} in your search data if using the name_or_id field".format(name_field)) - - try: - new_data["or__id"] = int(name_or_id) - new_data["or__{0}".format(name_field)] = name_or_id - except ValueError: - # If we get a value error, then we didn't have an integer so we can just pass and fall down to the fail - new_data[name_field] = name_or_id - new_kwargs["data"] = new_data - - response = self.get_endpoint(endpoint, **new_kwargs) - if response["status_code"] != 200: - fail_msg = "Got a {0} response when trying to get one from {1}".format(response["status_code"], endpoint) - if "detail" in response.get("json", {}): - fail_msg += ", detail: {0}".format(response["json"]["detail"]) - self.fail_json(msg=fail_msg) - - if "count" not in response["json"] or "results" not in response["json"]: - self.fail_json(msg="The endpoint did not provide count and results.") - - if response["json"]["count"] == 0: - if allow_none: - return None - else: - self.fail_wanted_one(response, endpoint, new_kwargs.get("data")) - elif response["json"]["count"] > 1: - if name_or_id: - # Since we did a name or ID search and got > 1 return something if the id matches or the name matches exactly - exact_matches = [] - for asset in response["json"]["results"]: - if str(asset["id"]) == name_or_id: - return self.existing_item_add_url(asset, endpoint, key=key) - if str(asset[name_field]) == name_or_id: - exact_matches.append(self.existing_item_add_url(asset, endpoint, key=key)) - # If there is one exact name match then return that - if len(exact_matches) == 1: - return exact_matches[0] - - # We got > 1 and either didn't find something by ID or exact name (which means multiple names) - # Or we weren't running with a or search and just got back too many to begin with. - if allow_none: - return None - else: - self.fail_wanted_one(response, endpoint, new_kwargs.get("data")) - else: - # Check if name actually matches and isn't just that we match part of the name - asset = response["json"]["results"][0] - if (str(asset["id"]) == name_or_id) or (str(asset[name_field]) == name_or_id): - return self.existing_item_add_url(response["json"]["results"][0], endpoint, key=key) - elif allow_none: - return None - else: - self.fail_wanted_one(response, endpoint, new_kwargs.get("data")) - - def get_by_id(self, endpoint, id, **kwargs): - new_kwargs = kwargs.copy() - - response = self.get_endpoint("{endpoint}/{id}".format(endpoint=endpoint, id=id), **new_kwargs) - if response["status_code"] != 200: - fail_msg = "Got a {0} response when trying to get id:{1} from {2}".format(response["status_code"], id, endpoint) - if "detail" in response.get("json", {}): - fail_msg += ", detail: {0}".format(response["json"]["detail"]) - self.fail_json(msg=fail_msg) - - return response["json"] - - def get_only(self, endpoint, name_or_id=None, allow_none=True, key="url", **kwargs): - new_kwargs = kwargs.copy() - if name_or_id: - name_field = self.get_name_field_from_endpoint(endpoint) - new_data = kwargs.get("data", {}).copy() - if name_field in new_data: - self.fail_json(msg="You can't specify the field {0} in your search data if using the name_or_id field".format(name_field)) - - try: - new_data["or__id"] = int(name_or_id) - new_data["or__{0}".format(name_field)] = name_or_id - except ValueError: - # If we get a value error, then we didn't have an integer so we can just pass and fall down to the fail - new_data[name_field] = name_or_id - new_kwargs["data"] = new_data - - response = self.get_endpoint(endpoint, **new_kwargs) - if response["status_code"] != 200: - fail_msg = "Got a {0} response when trying to get from {1}".format(response["status_code"], endpoint) - if "detail" in response.get("json", {}): - fail_msg += ", detail: {0}".format(response["json"]["detail"]) - self.fail_json(msg=fail_msg) - - return self.existing_item_add_url(response["json"], endpoint, key=key) - - def authenticate(self, **kwargs): - # Attempt to get a token from /auth/session/login by giving it our username/password combo - # If we have a username and password, we need to get a session cookie - - # Currently not implemented - # api_token_url = self.build_url("auth/session/login").geturl()[:-1] - - # If we have not managed to authenticate of these, then we can try un-authenticated access or use basic auth - self.authenticated = True - - def existing_item_add_url(self, existing_item, endpoint, key="url"): - # Add url and type to response as its missing in current iteration of EDA Controller. - existing_item[key] = "{0}{1}/".format(self.build_url(endpoint).geturl()[len(self.host):], existing_item["id"]) - existing_item["type"] = endpoint - return existing_item - - def delete_if_needed(self, existing_item, on_delete=None, auto_exit=True, key="url"): - # This will exit from the module on its own. - # If the method successfully deletes an item and on_delete param is defined, - # the on_delete parameter will be called as a method pasing in this object and the json from the response - # This will return one of two things: - # 1. None if the existing_item is not defined (so no delete needs to happen) - # 2. The response from EDA Controller from calling the delete on the endpont. It's up to you to process the response and exit from the module - # Note: common error codes from the EDA Controller API can cause the module to fail - if existing_item: - if existing_item["type"] == "token": - response = self.delete_endpoint(existing_item["endpoint"]) - else: - # If we have an item, we can try to delete it - try: - item_url = existing_item[key] - item_type = existing_item["type"] - item_id = existing_item["id"] - item_name = self.get_item_name(existing_item, allow_unknown=True) - except KeyError as ke: - self.fail_json(msg="Unable to process delete of item due to missing data {0}".format(ke)) - response = self.delete_endpoint(item_url) - else: - if auto_exit: - self.exit_json(**self.json_output) - else: - return self.json_output - - if response["status_code"] in [202, 204]: - if on_delete: - on_delete(self, response["json"]) - self.json_output["changed"] = True - if existing_item["type"] == "token": - self.json_output["msg"] = "Token Revoked" - self.exit_json(**self.json_output) - else: - self.json_output["id"] = item_id - self.exit_json(**self.json_output) - if auto_exit: - self.exit_json(**self.json_output) - else: - return self.json_output - else: - if "json" in response and "__all__" in response["json"]: - self.fail_json(msg="Unable to delete {0} {1}: {2}".format(item_type, item_name, response["json"]["__all__"][0])) - elif "json" in response: - # This is from a project delete (if there is an active job against it) - if "error" in response["json"]: - self.fail_json(msg="Unable to delete {0} {1}: {2}".format(item_type, item_name, response["json"]["error"])) - else: - self.fail_json(msg="Unable to delete {0} {1}: {2}".format(item_type, item_name, response["json"])) - else: - self.fail_json(msg="Unable to delete {0} {1}: {2}".format(item_type, item_name, response["status_code"])) - - def get_item_name(self, item, allow_unknown=False): - if item: - if "name" in item: - return item["name"] - - if allow_unknown: - return "unknown" - - if item: - self.exit_json(msg="Cannot determine identity field for {0} object.".format(item.get("type", "unknown"))) - else: - self.exit_json(msg="Cannot determine identity field for Undefined object.") - - def delete_endpoint(self, endpoint, *args, **kwargs): - # Handle check mode - if self.check_mode: - self.json_output["changed"] = True - self.exit_json(**self.json_output) - - return self.make_request("DELETE", endpoint, **kwargs) - - def create_or_update_if_needed( - self, - existing_item, - new_item, - endpoint=None, - item_type="unknown", - on_create=None, - on_update=None, - auto_exit=True, - associations=None, - require_id=True, - fixed_url=None, - key="url", - ): - if existing_item: - return self.update_if_needed( - existing_item, - new_item, - endpoint=endpoint, - on_update=on_update, - auto_exit=auto_exit, - associations=associations, - require_id=require_id, - fixed_url=fixed_url, - key=key, - ) - else: - return self.create_if_needed( - existing_item, - new_item, - endpoint, - on_create=on_create, - item_type=item_type, - auto_exit=auto_exit, - associations=associations, - ) - - def create_if_needed( - self, - existing_item, - new_item, - endpoint, - on_create=None, - auto_exit=True, - item_type="unknown", - associations=None, - treat_conflict_as_unchanged=False, - ): - - # This will exit from the module on its own - # If the method successfully creates an item and on_create param is defined, - # the on_create parameter will be called as a method pasing in this object and the json from the response - # This will return one of two things: - # 1. None if the existing_item is already defined (so no create needs to happen) - # 2. The response from EDA Controller from calling the patch on the endpont. It's up to you to process the response and exit from the module - # Note: common error codes from the EDA Controller API can cause the module to fail - - if not endpoint: - self.fail_json(msg="Unable to create new {0} due to missing endpoint".format(item_type)) - - item_url = None - if existing_item: - try: - item_url = existing_item["url"] - except KeyError as ke: - self.fail_json(msg="Unable to process create of item due to missing data {0}".format(ke)) - else: - # If we don't have an exisitng_item, we can try to create it - - # We have to rely on item_type being passed in since we don't have an existing item that declares its type - # We will pull the item_name out from the new_item, if it exists - item_name = self.get_item_name(new_item, allow_unknown=True) - - response = self.post_endpoint(endpoint, **{"data": new_item}) - - if response["status_code"] in [200, 201]: - self.json_output["name"] = "unknown" - for key in ("name", "username", "identifier", "hostname"): - if key in response["json"]: - self.json_output["name"] = response["json"][key] - if item_type != "token": - self.json_output["id"] = response["json"]["id"] - item_url = "{0}{1}/".format( - self.build_url(endpoint).geturl()[len(self.host):], - response["json"]["id"], - ) - self.json_output["changed"] = True - elif response["status_code"] in [409] and treat_conflict_as_unchanged: - self.json_output["changed"] = False - else: - if "json" in response and "__all__" in response["json"]: - self.fail_json(msg="Unable to create {0} {1}: {2}".format(item_type, item_name, response["json"]["__all__"][0])) - elif "json" in response: - self.fail_json(msg="Unable to create {0} {1}: {2}".format(item_type, item_name, response["json"])) - else: - self.fail_json(msg="Unable to create {0} {1}: {2}".format(item_type, item_name, response["status_code"])) - - # Process any associations with this item - if associations is not None: - for association_type in associations: - sub_endpoint = "{0}{1}/".format(item_url, association_type) - self.modify_associations(sub_endpoint, associations[association_type]) - - # If we have an on_create method and we actually changed something we can call on_create - if on_create is not None and self.json_output["changed"]: - on_create(self, response["json"]) - elif auto_exit: - self.exit_json(**self.json_output) - else: - last_data = response["json"] - return last_data - - def create_no_name( - self, - new_item, - endpoint, - on_create=None, - auto_exit=False, - item_type="unknown", - ): - - # This will exit from the module on its own - # If the method successfully creates an item and on_create param is defined, - # the on_create parameter will be called as a method pasing in this object and the json from the response - # This will return one of two things: - # 1. None if the existing_item is already defined (so no create needs to happen) - # 2. The response from EDA Controller from calling the patch on the endpont. It's up to you to process the response and exit from the module - # Note: common error codes from the EDA Controller API can cause the module to fail - - if not endpoint: - self.fail_json(msg="Unable to create new {0} due to missing endpoint".format(item_type)) - - # We have to rely on item_type being passed in since we don't have an existing item that declares its type - # We will pull the item_name out from the new_item, if it exists - - response = self.post_endpoint(endpoint, **{"data": new_item}) - - if response["status_code"] in [200, 201]: - self.json_output["changed"] = True - else: - if "json" in response and "__all__" in response["json"]: - self.fail_json(msg="Unable to create {0}: {1}".format(item_type, response["json"]["__all__"][0])) - elif "json" in response: - self.fail_json(msg="Unable to create {0}: {1}".format(item_type, response["json"])) - else: - self.fail_json(msg="Unable to create {0}: {1}".format(item_type, response["status_code"])) - - # If we have an on_create method and we actually changed something we can call on_create - if on_create is not None and self.json_output["changed"]: - on_create(self, response["json"]) - elif auto_exit: - self.exit_json(**self.json_output) - else: - last_data = response["json"] - return last_data - - def update_if_needed( - self, - existing_item, - new_item, - endpoint, - on_update=None, - auto_exit=True, - associations=None, - require_id=True, - fixed_url=None, - key="url", - ): - # This will exit from the module on its own - # If the method successfully updates an item and on_update param is defined, - # the on_update parameter will be called as a method pasing in this object and the json from the response - # This will return one of two things: - # 1. None if the existing_item does not need to be updated - # 2. The response from EDA Controller from patching to the endpoint. It's up to you to process the response and exit from the module. - # Note: common error codes from the EDA Controller API can cause the module to fail - response = None - if existing_item: - # If we have an item, we can see if it needs an update - try: - item_url = fixed_url or existing_item[key] - item_type = existing_item["type"] - name_field = self.get_name_field_from_endpoint(endpoint) - item_name = existing_item[name_field] - item_id = require_id and existing_item["id"] - except KeyError as ke: - self.fail_json(msg="Unable to process update of item due to missing data {0}".format(ke)) - - # Check to see if anything within the item requires the item to be updated - needs_patch = self.objects_could_be_different(existing_item, new_item) - - # If we decided the item needs to be updated, update it - self.json_output["id"] = item_id - self.json_output["name"] = item_name - self.json_output["type"] = item_type - if needs_patch: - response = self.patch_endpoint(item_url, **{"data": new_item}) - if response["status_code"] == 200: - # compare apples-to-apples, old API data to new API data - # but do so considering the fields given in parameters - self.json_output["changed"] = self.objects_could_be_different( - existing_item, - response["json"], - field_set=new_item.keys(), - warning=True, - ) - elif "json" in response and "__all__" in response["json"]: - self.fail_json(msg=response["json"]["__all__"]) - else: - self.fail_json( - **{ - "msg": "Unable to update {0} {1}, see response".format(item_type, item_name), - "response": response, - "input": new_item, - } - ) - - else: - raise RuntimeError("update_if_needed called incorrectly without existing_item") - - # Process any associations with this item - if associations is not None: - for association_type, id_list in associations.items(): - endpoint = "{0}{1}/".format(item_url, association_type) - self.modify_associations(endpoint, id_list) - - # If we change something and have an on_change call it - if on_update is not None and self.json_output["changed"]: - if response is None: - last_data = existing_item - else: - last_data = response["json"] - on_update(self, last_data) - elif auto_exit: - self.exit_json(**self.json_output) - else: - if response is None: - last_data = existing_item - else: - last_data = response["json"] - return last_data - - def modify_associations(self, association_endpoint, new_association_list): - # if we got None instead of [] we are not modifying the association_list - if new_association_list is None: - return - - # First get the existing associations - response = self.get_all_endpoint(association_endpoint) - existing_associated_ids = [association["id"] for association in response["json"]["results"]] - - # Disassociate anything that is in existing_associated_ids but not in new_association_list - ids_to_remove = list(set(existing_associated_ids) - set(new_association_list)) - for an_id in ids_to_remove: - response = self.post_endpoint(association_endpoint, **{"data": {"id": int(an_id), "disassociate": True}}) - if response["status_code"] == 204: - self.json_output["changed"] = True - else: - self.fail_json(msg="Failed to disassociate item {0}".format(response["json"].get("detail", response["json"]))) - - # Associate anything that is in new_association_list but not in `association` - for an_id in list(set(new_association_list) - set(existing_associated_ids)): - response = self.post_endpoint(association_endpoint, **{"data": {"id": int(an_id)}}) - if response["status_code"] == 204: - self.json_output["changed"] = True - else: - self.fail_json(msg="Failed to associate item {0}".format(response["json"].get("detail", response["json"]))) - - def post_endpoint(self, endpoint, *args, **kwargs): - # Handle check mode - if self.check_mode: - self.json_output["changed"] = True - self.exit_json(**self.json_output) - - return self.make_request("POST", endpoint, **kwargs) - - def patch_endpoint(self, endpoint, *args, **kwargs): - # Handle check mode - if self.check_mode: - self.json_output["changed"] = True - self.exit_json(**self.json_output) - - return self.make_request("PATCH", endpoint, **kwargs) - - def put_endpoint(self, endpoint, *args, **kwargs): - # Handle check mode - if self.check_mode: - self.json_output["changed"] = True - self.exit_json(**self.json_output) - - return self.make_request("PUT", endpoint, **kwargs) - - def get_all_endpoint(self, endpoint, *args, **kwargs): - response = self.get_endpoint(endpoint, *args, **kwargs) - if "next" not in response["json"]: - raise RuntimeError("Expected list from API at {0}, got: {1}".format(endpoint, response)) - next_page = response["json"]["next"] - - if response["json"]["count"] > 10000: - self.fail_json(msg="The number of items being queried for is higher than 10,000.") - - while next_page is not None: - next_response = self.get_endpoint(next_page) - response["json"]["results"] = response["json"]["results"] + next_response["json"]["results"] - next_page = next_response["json"]["next"] - response["json"]["next"] = next_page - return response - - def fail_wanted_one(self, response, endpoint, query_params): - sample = response.copy() - if len(sample["json"]["results"]) > 1: - sample["json"]["results"] = sample["json"]["results"][:2] + ["...more results snipped..."] - url = self.build_url(endpoint, query_params) - display_endpoint = url.geturl()[len(self.host):] # truncate to not include the base URL - self.fail_json( - msg="Request to {0} returned {1} items, expected 1".format(display_endpoint, response["json"]["count"]), - query=query_params, - response=sample, - total_results=response["json"]["count"], - ) - - def get_exactly_one(self, endpoint, name_or_id=None, **kwargs): - return self.get_one(endpoint, name_or_id=name_or_id, allow_none=False, **kwargs) - - def resolve_name_to_id(self, endpoint, name_or_id, data=None): - return self.get_exactly_one(endpoint, name_or_id, **{"data": data if data else {}})["id"] - - def objects_could_be_different(self, old, new, field_set=None, warning=False): - if field_set is None: - field_set = set(fd for fd in new.keys() if fd not in ("modified", "related", "summary_fields")) - for field in field_set: - new_field = new.get(field, None) - old_field = old.get(field, None) - if old_field != new_field: - if self.update_secrets: - return True # Something doesn't match, or something might not match - elif self.has_encrypted_values(new_field) or field not in new: - if self.update_secrets: - # case of 'field not in new' - user password write-only field that API will not display - self._encrypted_changed_warning(field, old, warning=warning) - return True - return False - - def sync_project(self, id, wait=True, interval=1, timeout=None): - self.json_output["id"] = id - - # If the state was present and we can let the module build or update the existing item, this will return on its own - response = self.post_endpoint('projects/{id}/sync'.format(id=id)) - - if response["status_code"] == 202: - if wait: - status = None - start = time.time() - elapsed = 0 - while status != "completed" and status != "failed": - project = self.get_endpoint('projects/{id}'.format(id=id))["json"] - self.json_output["project"] = project - status = project["import_state"] - time.sleep(interval) - elapsed = time.time() - start - if timeout and elapsed > timeout: - self.fail_json(msg="Timed out awaiting task completion.", project=project) - if status == "failed": - self.fail_json(msg="The project sync failed", task=project["import_error"]) - else: - if "json" in response and "__all__" in response["json"]: - if "detail" in response["json"]["__all__"][0] and response["json"]["__all__"][0]["detail"] == "Project import or sync is already running.": - self.json_output["changed"] = False - self.json_output["detail"] = response["json"]["__all__"][0]["detail"] - self.exit_json(**self.json_output) - else: - self.fail_json(msg="Unable to sync project: {0}".format(response["json"]["__all__"][0])) - elif "json" in response: - # This is from a project delete (if there is an active job against it) - if "error" in response["json"]: - if "detail" in response["json"]["error"] and response["json"]["error"]["detail"] == "Project import or sync is already running.": - self.json_output["changed"] = False - self.json_output["detail"] = response["json"]["error"]["detail"] - self.exit_json(**self.json_output) - else: - self.fail_json(msg="Unable to sync project: {0}".format(response["json"]["error"])) - else: - self.fail_json(msg="Unable to sync project: {0}".format(response["json"])) - else: - self.fail_json(msg="Unable to sync project: {0}".format(response["status_code"])) - - self.json_output["changed"] = True - self.exit_json(**self.json_output) - - def trigger_post_action( - self, - endpoint, - auto_exit=False, - data=None, - ): - - if not endpoint: - self.fail_json(msg="Unable to trigger action due to missing endpoint") - - response = self.post_endpoint(endpoint, **{"data": data}) - - if response["status_code"] in [200, 201, 204]: - self.json_output["changed"] = True - else: - if "json" in response and "__all__" in response["json"]: - self.fail_json(msg="Unable to trigger {0}: {1}".format(endpoint, response["json"]["__all__"][0])) - elif "json" in response: - self.fail_json(msg="Unable to trigger {0}: {1}".format(endpoint, response["json"])) - else: - self.fail_json(msg="Unable to trigger {0}: {1}".format(endpoint, response["status_code"])) - - if auto_exit: - self.exit_json(**self.json_output) - else: - last_data = response["json"] - return last_data - - @staticmethod - def _resolve_path(path): - return os.path.abspath(os.path.expanduser(os.path.expandvars(path))) - - @staticmethod - def has_encrypted_values(obj): - """Returns True if JSON-like python content in obj has $encrypted$ - anywhere in the data as a value - """ - if isinstance(obj, dict): - for val in obj.values(): - if EDAModule.has_encrypted_values(val): - return True - elif isinstance(obj, list): - for val in obj: - if EDAModule.has_encrypted_values(val): - return True - elif obj == EDAModule.ENCRYPTED_STRING: - return True - return False - - def _encrypted_changed_warning(self, field, old, warning=False): - if not warning: - return - self.warn( - "The field {0} of {1} {2} has encrypted data and may inaccurately report task is changed.".format( - field, old.get("type", "unknown"), old.get("id", "unknown") - ) - ) diff --git a/plugins/modules/__init__.py b/plugins/modules/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/plugins/modules/credential.py b/plugins/modules/credential.py deleted file mode 100644 index cbcb3fe..0000000 --- a/plugins/modules/credential.py +++ /dev/null @@ -1,143 +0,0 @@ -#!/usr/bin/python -# coding: utf-8 -*- - -# (c) 2024, Derek Waters -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function - -__metaclass__ = type - - -ANSIBLE_METADATA = { - "metadata_version": "1.1", - "status": ["preview"], - "supported_by": "community", -} - - -DOCUMENTATION = """ ---- -module: credential -author: "Derek Waters (@derekwaters)" -short_description: Manage a Credential in EDA Controller -description: - - Create, update and delete credentials in EDA Controller -options: - name: - description: - - The name of the credential. - required: True - type: str - new_name: - description: - - Setting this option will change the existing name (looked up via the name field). - type: str - description: - description: - - The description of the credential. - required: False - type: str - username: - description: - - The username used to access the project repository. - required: True - type: str - secret: - description: - - The secret associated with the credential (either a password or access token). - required: True - type: str - credential_type: - description: - - The type of the credential. - choices: ["GitHub Personal Access Token", "GitLab Personal Access Token", "Container Registry"] - default: "GitHub Personal Access Token" - type: str - state: - description: - - Desired state of the resource. - choices: ["present", "absent"] - default: "present" - type: str - -extends_documentation_fragment: infra.eda_configuration.auth -""" - - -EXAMPLES = """ -- name: Create eda credential - infra.eda_configuration.credential: - name: my_credential - description: my github access credential - username: derekwaters - secret: this_is_not_a_real_token - credential_type: "GitHub Personal Access Token" - state: present - eda_host: eda.example.com - eda_username: admin - eda_password: Sup3r53cr3t - -""" - -from ..module_utils.eda_module import EDAModule - - -def main(): - # Any additional arguments that are not fields of the item can be added here - argument_spec = dict( - name=dict(required=True), - new_name=dict(), - description=dict(), - username=dict(required=True), - secret=dict(required=True, no_log=True), - credential_type=dict(choices=["GitHub Personal Access Token", - "GitLab Personal Access Token", - "Container Registry"], - default="GitHub Personal Access Token"), - state=dict(choices=["present", "absent"], default="present"), - ) - - # Create a module for ourselves - module = EDAModule(argument_spec=argument_spec) - - # Extract our parameters - name = module.params.get("name") - new_name = module.params.get("new_name") - state = module.params.get("state") - - new_fields = {} - - # Attempt to look up an existing item based on the provided data - existing_item = module.get_one("credentials", name_or_id=name, key="req_url") - - if state == "absent": - # If the state was absent we can let the module delete it if needed, the module will handle exiting from this - module.delete_if_needed(existing_item, key="req_url") - - # Create the data that gets sent for create and update - # Remove these two comments for final - # Check that Links and groups works with this. - new_fields["name"] = new_name if new_name else (module.get_item_name(existing_item) if existing_item else name) - for field_name in ( - "description", - "credential_type", - "username", - "secret", - ): - field_val = module.params.get(field_name) - if field_val is not None: - new_fields[field_name] = field_val - - # If the state was present and we can let the module build or update the existing item, this will return on its own - module.create_or_update_if_needed( - existing_item, - new_fields, - endpoint="credentials", - item_type="credentials", - key="req_url", - ) - - -if __name__ == "__main__": - main() diff --git a/plugins/modules/decision_environment.py b/plugins/modules/decision_environment.py deleted file mode 100644 index bcb038f..0000000 --- a/plugins/modules/decision_environment.py +++ /dev/null @@ -1,133 +0,0 @@ -#!/usr/bin/python -# coding: utf-8 -*- - -# (c) 2024, Derek Waters -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function - -__metaclass__ = type - - -ANSIBLE_METADATA = { - "metadata_version": "1.1", - "status": ["preview"], - "supported_by": "community", -} - - -DOCUMENTATION = """ ---- -module: decision_environment -author: "Derek Waters (@derekwaters)" -short_description: Manage a Decision Environment in EDA Controller -description: - - Create, update and delete decision environments in EDA Controller -options: - name: - description: - - The name of the decision environment. - required: True - type: str - new_name: - description: - - Setting this option will change the existing name (looked up via the name field). - type: str - description: - description: - - The description of the decision environment. - required: False - type: str - image_url: - description: - - The full image location to use for the decision environment, including the container registry, image name, and version tag. - required: True - type: str - credential: - description: - - The token needed to access the container registry, if required. - required: False - type: str - state: - description: - - Desired state of the resource. - choices: ["present", "absent"] - default: "present" - type: str - -extends_documentation_fragment: infra.eda_configuration.auth -""" - - -EXAMPLES = """ -- name: Create eda decision environment - infra.eda_configuration.decision_environment: - name: my_de - description: my awesome decision environment - image_url: my-container_registry/ansible/de-minimal-8:latest - credential: registry_access_token - state: present - eda_host: eda.example.com - eda_username: admin - eda_password: Sup3r53cr3t - -""" - -from ..module_utils.eda_module import EDAModule - - -def main(): - # Any additional arguments that are not fields of the item can be added here - argument_spec = dict( - name=dict(required=True), - new_name=dict(), - description=dict(), - image_url=dict(required=True), - credential=dict(), - state=dict(choices=["present", "absent"], default="present"), - ) - - # Create a module for ourselves - module = EDAModule(argument_spec=argument_spec) - - # Extract our parameters - name = module.params.get("name") - new_name = module.params.get("new_name") - state = module.params.get("state") - - new_fields = {} - - # Attempt to look up an existing item based on the provided data - existing_item = module.get_one("decision-environments", name_or_id=name, key="req_url") - - if state == "absent": - # If the state was absent we can let the module delete it if needed, the module will handle exiting from this - module.delete_if_needed(existing_item, key="req_url") - - # Create the data that gets sent for create and update - # Remove these two comments for final - # Check that Links and groups works with this. - new_fields["name"] = new_name if new_name else (module.get_item_name(existing_item) if existing_item else name) - for field_name in ( - "description", - "image_url", - ): - field_val = module.params.get(field_name) - if field_val is not None: - new_fields[field_name] = field_val - - if module.params.get("credential") is not None: - new_fields["credential_id"] = module.resolve_name_to_id("credentials", module.params.get("credential")) - - # If the state was present and we can let the module build or update the existing item, this will return on its own - module.create_or_update_if_needed( - existing_item, - new_fields, - endpoint="decision-environments", - item_type="decision-environments", - key="req_url", - ) - - -if __name__ == "__main__": - main() diff --git a/plugins/modules/project.py b/plugins/modules/project.py deleted file mode 100644 index 8b061e3..0000000 --- a/plugins/modules/project.py +++ /dev/null @@ -1,141 +0,0 @@ -#!/usr/bin/python -# coding: utf-8 -*- - -# (c) 2023, Chris Renwick <@crenwick93> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function - -__metaclass__ = type - - -ANSIBLE_METADATA = { - "metadata_version": "1.1", - "status": ["preview"], - "supported_by": "community", -} - - -DOCUMENTATION = """ ---- -module: project -author: "Chris Renwick (@crenwick93)" -short_description: Manage a project in EDA Controller -description: - - Create, update and delete projects in EDA Controller -options: - name: - description: - - The name of the project. - required: True - type: str - new_name: - description: - - Setting this option will change the existing name (looked up via the name field). - type: str - description: - description: - - The description of the project. - required: False - type: str - url: - description: - - A URL to a remote archive, such as a Github Release or a build artifact stored in Artifactory and unpacks it into the project path for use. - required: True - type: str - aliases: ['scm_url'] - tls_validation: - description: - - Whether to use TLS validation against the url. - type: bool - default: True - credential: - description: - - The token needed to utilize the SCM URL. - required: False - type: str - state: - description: - - Desired state of the resource. - choices: ["present", "absent"] - default: "present" - type: str - -extends_documentation_fragment: infra.eda_configuration.auth -""" - - -EXAMPLES = """ -- name: Create eda project - infra.eda_configuration.project: - name: my_project - description: my awesome project - url: https://github.com/ansible/ansible-rulebook.git - credential: test_token - state: present - eda_host: eda.example.com - eda_username: admin - eda_password: Sup3r53cr3t - -""" - -from ..module_utils.eda_module import EDAModule - - -def main(): - # Any additional arguments that are not fields of the item can be added here - argument_spec = dict( - name=dict(required=True), - new_name=dict(), - description=dict(), - url=dict(required=True, aliases=["scm_url"]), - tls_validation=dict(type="bool", default=True), - credential=dict(), - state=dict(choices=["present", "absent"], default="present"), - ) - - # Create a module for ourselves - module = EDAModule(argument_spec=argument_spec) - - # Extract our parameters - name = module.params.get("name") - new_name = module.params.get("new_name") - state = module.params.get("state") - - new_fields = {} - - # Attempt to look up an existing item based on the provided data - existing_item = module.get_one("projects", name_or_id=name, key="req_url") - - if state == "absent": - # If the state was absent we can let the module delete it if needed, the module will handle exiting from this - module.delete_if_needed(existing_item, key="req_url") - - # Create the data that gets sent for create and update - # Remove these two comments for final - # Check that Links and groups works with this. - new_fields["name"] = new_name if new_name else (module.get_item_name(existing_item) if existing_item else name) - for field_name in ( - "description", - "url", - "tls_validation", - ): - field_val = module.params.get(field_name) - if field_val is not None: - new_fields[field_name] = field_val - - if module.params.get("credential") is not None: - new_fields["credential_id"] = module.resolve_name_to_id("credentials", module.params.get("credential")) - - # If the state was present and we can let the module build or update the existing item, this will return on its own - module.create_or_update_if_needed( - existing_item, - new_fields, - endpoint="projects", - item_type="projects", - key="req_url", - ) - - -if __name__ == "__main__": - main() diff --git a/plugins/modules/project_sync.py b/plugins/modules/project_sync.py deleted file mode 100644 index 1c30a9e..0000000 --- a/plugins/modules/project_sync.py +++ /dev/null @@ -1,95 +0,0 @@ -#!/usr/bin/python -# coding: utf-8 -*- - -# (c) 2023, Chris Renwick <@crenwick93> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function - -__metaclass__ = type - - -ANSIBLE_METADATA = { - "metadata_version": "1.1", - "status": ["preview"], - "supported_by": "community", -} - - -DOCUMENTATION = """ ---- -module: project_sync -author: "Tom Page (@Tompage1994)" -short_description: Sync a project in EDA Controller -description: - - Sync projects in EDA Controller -options: - name: - description: - - The name of the project. - required: True - type: str - wait: - description: - - Wait for the project to finish syncing before returning. - required: false - default: True - type: bool - interval: - description: - - The interval to request an update from EDA Controller. - required: False - default: 1 - type: float - timeout: - description: - - If waiting for the project to update this will abort after this - amount of seconds - type: int - -extends_documentation_fragment: infra.eda_configuration.auth -""" - - -EXAMPLES = """ -- name: Create eda project - infra.eda_configuration.project_sync: - name: my_project - wait: true - interval: 5 - timeout: 60 - eda_host: eda.example.com - eda_username: admin - eda_password: Sup3r53cr3t - -""" - -from ..module_utils.eda_module import EDAModule - - -def main(): - # Any additional arguments that are not fields of the item can be added here - argument_spec = dict( - name=dict(required=True), - wait=dict(default=True, type="bool"), - interval=dict(default=1.0, type="float"), - timeout=dict(default=None, type="int"), - ) - - # Create a module for ourselves - module = EDAModule(argument_spec=argument_spec) - - # Extract our parameters - name = module.params.get("name") - wait = module.params.get("wait") - interval = module.params.get("interval") - timeout = module.params.get("timeout") - - # Attempt to look up an existing item based on the provided data - project = module.get_one("projects", name_or_id=name, key="req_url", allow_none=False) - - module.sync_project(project["id"], wait, interval, timeout) - - -if __name__ == "__main__": - main() diff --git a/plugins/modules/rulebook_activation.py b/plugins/modules/rulebook_activation.py deleted file mode 100644 index c198fec..0000000 --- a/plugins/modules/rulebook_activation.py +++ /dev/null @@ -1,233 +0,0 @@ -#!/usr/bin/python -# coding: utf-8 -*- - -# (c) 2023, Tom Page <@Tompage1994> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function - -__metaclass__ = type - - -ANSIBLE_METADATA = { - "metadata_version": "1.1", - "status": ["preview"], - "supported_by": "community", -} - - -DOCUMENTATION = """ ---- -module: rulebook_activation -author: "Tom Page (@Tompage1994)" -short_description: Manage a rulebook_activation in EDA Controller -description: - - Create, enable, disable and delete rulebook activations in EDA Controller -options: - name: - description: - - The name of the rulebook activation. - required: True - type: str - description: - description: - - The description of the rulebook_activation. - required: False - type: str - project: - description: - - The project from which the rulebook is found. - required: False - type: str - rulebook: - description: - - The name of the rulebook to activate. - required: True - type: str - decision_environment: - description: - - The decision environment to be used. - required: True - type: str - restart_policy: - description: - - The policy used to determine whether to restart a rulebook. - required: False - choices: ["always", "never", "on-failure"] - default: "always" - type: str - extra_vars: - description: - - Specify C(extra_vars) for the template. - required: False - type: dict - awx_token: - description: - - The token used to authenticate to controller. - required: False - type: str - enabled: - description: - - Whether the rulebook activation is automatically enabled to run. - default: true - type: bool - state: - description: - - Desired state of the resource. - choices: ["present", "absent", "restarted"] - default: "present" - type: str - -extends_documentation_fragment: infra.eda_configuration.auth -""" - - -EXAMPLES = """ -- name: Create eda rulebook activation - infra.eda_configuration.rulebook_activation: - name: Github Hook - description: Hook to listen for changes in GitHub - project: eda_examples - rulebook: git-hook-deploy-rules.yml - decision_environment: my_de - extra_vars: - provider: github - repo_url: https://github.com/ansible/ansible-rulebook.git - enabled: true - awx_token: my_token - state: present - -- name: Restart eda rulebook activation - infra.eda_configuration.rulebook_activation: - name: Github Hook - state: restarted - -- name: Delete eda rulebook activation - infra.eda_configuration.rulebook_activation: - name: Github Hook - state: absent -""" - -from ..module_utils.eda_module import EDAModule -import json - - -def main(): - # Any additional arguments that are not fields of the item can be added here - argument_spec = dict( - name=dict(required=True), - description=dict(), - project=dict(), - rulebook=dict(required=True), - decision_environment=dict(required=True), - restart_policy=dict(choices=["always", "never", "on-failure"], default="always"), - extra_vars=dict(type="dict"), - enabled=dict(type="bool", default="true"), - state=dict(choices=["present", "absent", "restarted"], default="present"), - awx_token=dict(no_log=False), - ) - - # Create a module for ourselves - module = EDAModule(argument_spec=argument_spec, required_if=[("state", "present", ("rulebook", "decision_environment"))]) - - # Extract our parameters - name = module.params.get("name") - state = module.params.get("state") - - new_fields = {} - - # Attempt to look up an existing item based on the provided data - existing_item = module.get_one("activations", name_or_id=name, key="req_url") - - if state == "absent": - # If the state was absent we can let the module delete it if needed, the module will handle exiting from this - module.delete_if_needed(existing_item, key="req_url") - - if state == "restarted": - if module.params.get("enabled") is not None and not module.params.get("enabled"): - module.fail_json(msg="It is not possible to restart a disabled rulebook activation. Ensure it is set to enabled.") - # If the options want the activation enabled but it currently isn't then just run through as though enabling as that performs the restart - if existing_item["is_enabled"]: - # If the state was restarted we will hit the restart endpoint, the module will handle exiting from this - # If the item doesn't exist we will just create it anyway - module.trigger_post_action("activations/{id}/restart".format(id=existing_item["id"]), auto_exit=True) - - # Create the data that gets sent for create and update - # Remove these two comments for final - # Check that Links and groups works with this. - for field_name in ( - "name", - "description", - "restart_policy", - ): - field_val = module.params.get(field_name) - if field_val is not None: - new_fields[field_name] = field_val - - if module.params.get("enabled") is not None: - new_fields["is_enabled"] = module.params.get("enabled") - - if (module.params.get("project") is not None) and (module.params.get("rulebook") is not None): - new_fields["project_id"] = module.resolve_name_to_id("projects", module.params.get("project")) - new_fields["rulebook_id"] = module.resolve_name_to_id("rulebooks", - module.params.get("rulebook"), - data={"project_id": int(new_fields["project_id"])}, - ) - else: - new_fields["rulebook_id"] = module.resolve_name_to_id("rulebooks", module.params.get("rulebook")) - - if module.params.get("decision_environment") is not None: - new_fields["decision_environment_id"] = module.resolve_name_to_id("decision-environments", module.params.get("decision_environment")) - - if module.params.get("awx_token") is not None: - new_fields["awx_token_id"] = module.resolve_name_to_id("users/me/awx-tokens", module.params.get("awx_token")) - - # Create the extra_vars - if module.params.get("extra_vars") is not None: - if existing_item is not None: - new_fields["extra_var_id"] = -1 # Default it as something that isn't acceptable. Prove otherwise - if existing_item["extra_var_id"]: - # Check if matching existing extra_vars - existing_vars = module.get_by_id("extra-vars", id=existing_item["extra_var_id"]) - # Test if the same - if json.dumps(module.params.get("extra_vars")) == existing_vars["extra_var"]: - new_fields["extra_var_id"] = existing_item["extra_var_id"] - else: - new_fields["extra_var_id"] = module.create_no_name( - {"extra_var": json.dumps(module.params.get("extra_vars"))}, - endpoint="extra-vars", - item_type="extra_vars" - )["id"] - - if existing_item is not None: - # If the activation already exists, all we can do is change whether it is enabled or disabled. - # The module will exit from this section - - # First, fail; if trying to change anything other than being enabled - if ("description" in new_fields and existing_item["description"] != new_fields["description"] - or "restart_policy" in new_fields and existing_item["restart_policy"] != new_fields["restart_policy"] - or "project_id" in new_fields and existing_item["project_id"] != new_fields["project_id"] - or "rulebook_id" in new_fields and existing_item["rulebook_id"] != new_fields["rulebook_id"] - or "decision_environment_id" in new_fields and existing_item["decision_environment_id"] != new_fields["decision_environment_id"] - or "awx_token_id" in new_fields and existing_item["awx_token_id"] != new_fields["awx_token_id"] - or "extra_var_id" in new_fields and existing_item["extra_var_id"] != new_fields["extra_var_id"]): - module.fail_json(msg="Once an activation has been created it can only be enabled, disabled or deleted. Other changes cannot be made.") - - if module.params.get("enabled") is not None: - if module.params.get("enabled") and not existing_item["is_enabled"]: - module.trigger_post_action("activations/{id}/enable".format(id=existing_item["id"]), auto_exit=True) - elif (not module.params.get("enabled")) and existing_item["is_enabled"]: - module.trigger_post_action("activations/{id}/disable".format(id=existing_item["id"]), auto_exit=True) - module.exit_json(**module.json_output) - - # If the state was present and we can let the module build or update the existing item, this will return on its own - module.create_if_needed( - existing_item, - new_fields, - endpoint="activations", - item_type="rulebook_activations", - ) - - -if __name__ == "__main__": - main() diff --git a/plugins/modules/user.py b/plugins/modules/user.py deleted file mode 100644 index 1e056b4..0000000 --- a/plugins/modules/user.py +++ /dev/null @@ -1,157 +0,0 @@ -#!/usr/bin/python -# coding: utf-8 -*- - -# (c) 2023, Tom Page <@Tompage1994> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function - -__metaclass__ = type - - -ANSIBLE_METADATA = { - "metadata_version": "1.1", - "status": ["preview"], - "supported_by": "community", -} - - -DOCUMENTATION = """ ---- -module: user -author: "Tom Page (@Tompage1994)" -short_description: Manage a user in EDA Controller -description: - - Create, update and delete users in EDA Controller -options: - username: - description: - - Name of the user to create, remove, or modify. - required: true - type: str - new_username: - description: - - Setting this option will change the existing username (looked up via the name field). - type: str - first_name: - description: - - User's first name. - type: str - last_name: - description: - - User's last name. - type: str - email: - description: - - User's email address. That address must be correctly formed. - type: str - password: - description: - - User's password as a clear string. - type: str - update_secrets: - description: - - C(true) will always change password if user specifies password. - - C(false) will only set the password if other values change too. - type: bool - default: true - roles: - description: - - The roles the user is provided with. - - Current values are C(Viewer), C(Auditor), C(Editor), C(Contributor), C(Operator), C(Admin) - type: list - elements: str - state: - description: - - Desired state of the resource. - choices: ["present", "absent"] - default: "present" - type: str - -extends_documentation_fragment: infra.eda_configuration.auth -""" - - -EXAMPLES = """ -- name: Create eda user - infra.eda_configuration.user: - username: john_smith - first_name: john - last_name: smith - email: jsmith@example.com - password: my_p455word - roles: - - Viewer - - Auditor - - Contributor - state: present - eda_host: eda.example.com - eda_username: admin - eda_password: Sup3r53cr3t - -""" - -from ..module_utils.eda_module import EDAModule - - -def main(): - # Any additional arguments that are not fields of the item can be added here - argument_spec = dict( - username=dict(required=True), - new_username=dict(), - first_name=dict(), - last_name=dict(), - email=dict(), - password=dict(no_log=True), - update_secrets=dict(type='bool', default=True, no_log=False), - roles=dict(type="list", elements="str"), - state=dict(choices=["present", "absent"], default="present"), - ) - - # Create a module for ourselves - module = EDAModule(argument_spec=argument_spec) - - # Extract our parameters - username = module.params.get("username") - new_username = module.params.get("new_username") - state = module.params.get("state") - - new_fields = {} - - # Attempt to look up an existing item based on the provided data - existing_item = module.get_one("users", name_or_id=username, key="req_url") - - if state == "absent": - # If the state was absent we can let the module delete it if needed, the module will handle exiting from this - module.delete_if_needed(existing_item, key="req_url") - - # Create the data that gets sent for create and update - # Remove these two comments for final - # Check that Links and groups works with this. - new_fields["username"] = new_username if new_username else (existing_item["username"] if existing_item else username) - for field_name in ( - "first_name", - "last_name", - "email", - "password", - ): - field_val = module.params.get(field_name) - if field_val is not None: - new_fields[field_name] = field_val - - if module.params.get("roles") is not None: - roles = module.params.get("roles") - new_fields["roles"] = list(map(lambda role: module.resolve_name_to_id("roles", role), roles)) - - # If the state was present and we can let the module build or update the existing item, this will return on its own - module.create_or_update_if_needed( - existing_item, - new_fields, - endpoint="users", - item_type="users", - key="req_url", - ) - - -if __name__ == "__main__": - main() diff --git a/plugins/modules/user_token.py b/plugins/modules/user_token.py deleted file mode 100644 index da4f01d..0000000 --- a/plugins/modules/user_token.py +++ /dev/null @@ -1,111 +0,0 @@ -#!/usr/bin/python -# coding: utf-8 -*- - -# (c) 2024, Derek Waters <@derekwaters> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function - -__metaclass__ = type - - -ANSIBLE_METADATA = { - "metadata_version": "1.1", - "status": ["preview"], - "supported_by": "community", -} - - -DOCUMENTATION = """ ---- -module: user_token -author: "Derek Waters (@derekwaters)" -short_description: Manage the user tokens of the current user in EDA Controller -description: - - Create, update and delete user tokens in EDA Controller -options: - name: - description: - - The name of the token. - required: True - type: str - new_name: - description: - - Setting this option will change the existing name (looked up via the name field). - type: str - description: - description: - - The description of the token. - required: False - type: str - token: - description: - - The token data to set for the user. - required: True - type: str - -extends_documentation_fragment: infra.eda_configuration.auth -""" - - -EXAMPLES = """ -- name: Create eda user token - infra.eda_configuration.user_token: - name: my_user_token - description: my user token for accessing AAP - token: SOMETOKENDATA - eda_host: eda.example.com - eda_username: admin - eda_password: Sup3r53cr3t - -""" - -from ..module_utils.eda_module import EDAModule - - -def main(): - # Any additional arguments that are not fields of the item can be added here - argument_spec = dict( - name=dict(required=True), - new_name=dict(), - description=dict(), - token=dict(required=True, no_log=True), - ) - - # Create a module for ourselves - module = EDAModule(argument_spec=argument_spec) - - # Extract our parameters - name = module.params.get("name") - new_name = module.params.get("new_name") - - new_fields = {} - - # There is no way (that I can find) to search for an existing token - # based on name. This module can only attempt to create new tokens - # and fail safe if the token already exists (there is no way to patch - # an existing token) - - # Create the data that gets sent for create and update - # Remove these two comments for final - # Check that Links and groups works with this. - new_fields["name"] = new_name if new_name else name - for field_name in ( - "description", - "token", - ): - field_val = module.params.get(field_name) - if field_val is not None: - new_fields[field_name] = field_val - - module.create_if_needed( - None, - new_fields, - endpoint="users/me/awx-tokens", - item_type="awx-tokens", - treat_conflict_as_unchanged=True - ) - - -if __name__ == "__main__": - main() diff --git a/roles/user_token/README.md b/roles/controller_token/README.md similarity index 92% rename from roles/user_token/README.md rename to roles/controller_token/README.md index 3b0b8ad..fb73a20 100644 --- a/roles/user_token/README.md +++ b/roles/controller_token/README.md @@ -1,4 +1,4 @@ -# infra.eda_configuration.user_token +# infra.eda_configuration.controller_token ## Description @@ -15,7 +15,7 @@ Note that tokens cannot be updated, only created. |`eda_validate_certs`|`False`|no|Whether or not to validate the Ansible EDA Controller Server's SSL certificate.|| |`eda_request_timeout`|`10`|no|Specify the timeout Ansible should use in requests to the EDA Controller host.|| |`eda_configuration_async_dir`|`null`|no|Sets the directory to write the results file for async tasks. The default value is set to `null` which uses the Ansible Default of `/root/.ansible_async/`.|| -|`eda_user_tokens`|`see below`|yes|Data structure describing your user tokens, described below.|| +|`eda_controller_tokens`|`see below`|yes|Data structure describing your user tokens, described below.|| ### Secure Logging Variables @@ -50,7 +50,6 @@ This also speeds up the overall role. |Variable Name|Default Value|Required|Type|Description| |:---:|:---:|:---:|:---:|:---:| |`name`|""|yes|str|User Token name. Must be lower case containing only alphanumeric characters and underscores.| -|`new_name`|""|no|str|Setting this option will change the existing name (looked up via the name field.)| |`description`|""|no|str|Description to use for the Project.| |`token`|""|yes|str|The value of the token to associate with the user.| @@ -60,7 +59,7 @@ This also speeds up the overall role. ```yaml --- -eda_user_tokens: +eda_controller_tokens: - name: my_default_token description: my default user token token: TOKEN_VALUE @@ -79,7 +78,7 @@ eda_user_tokens: vars: eda_validate_certs: false # Define following vars here, or in eda_configs/eda_auth.yml - # eda_host: ansible-eda-web-svc-test-project.example.com + # controller_host: ansible-eda-web-svc-test-project.example.com # eda_token: changeme pre_tasks: - name: Include vars from eda_configs directory @@ -89,7 +88,7 @@ eda_user_tokens: tags: - always roles: - - ../../user_token + - infra.eda_configuration.controller_token ``` ## License diff --git a/roles/user_token/defaults/main.yml b/roles/controller_token/defaults/main.yml similarity index 92% rename from roles/user_token/defaults/main.yml rename to roles/controller_token/defaults/main.yml index 225c236..bddbfe7 100644 --- a/roles/user_token/defaults/main.yml +++ b/roles/controller_token/defaults/main.yml @@ -1,5 +1,5 @@ --- -eda_user_tokens: [] +eda_controller_tokens: [] eda_configuration_user_token_secure_logging: "{{ eda_configuration_secure_logging | default(false) }}" eda_configuration_user_token_async_retries: "{{ eda_configuration_async_retries | default(50) }}" diff --git a/roles/user_token/meta/argument_specs.yml b/roles/controller_token/meta/argument_specs.yml similarity index 96% rename from roles/user_token/meta/argument_specs.yml rename to roles/controller_token/meta/argument_specs.yml index 3bd855d..1bef966 100644 --- a/roles/user_token/meta/argument_specs.yml +++ b/roles/controller_token/meta/argument_specs.yml @@ -3,7 +3,7 @@ argument_specs: main: short_description: An Ansible Role to create user tokens in EDA controller. options: - eda_user_tokens: + eda_controller_tokens: default: [] required: false description: Data structure describing your user tokens to manage. @@ -45,7 +45,7 @@ argument_specs: description: This variable enables secure logging across all roles as a default. # Generic across all roles - eda_host: + controller_host: required: false description: URL to the EDA Controller Server. type: str @@ -59,11 +59,11 @@ argument_specs: required: false description: Specify the timeout Ansible should use in requests to the EDA Controller host. type: float - eda_username: + controller_username: required: false description: User for authentication on EDA Controller type: str - eda_password: + controller_password: required: false description: User's password For EDA Controller type: str diff --git a/roles/user_token/meta/main.yml b/roles/controller_token/meta/main.yml similarity index 95% rename from roles/user_token/meta/main.yml rename to roles/controller_token/meta/main.yml index 5e9171b..2c9060f 100644 --- a/roles/user_token/meta/main.yml +++ b/roles/controller_token/meta/main.yml @@ -1,6 +1,6 @@ --- galaxy_info: - role_name: "user_token" + role_name: controller_token author: "Derek Waters" description: "An Ansible Role to create a user token in EDA Controller." company: "Red Hat" @@ -33,8 +33,6 @@ galaxy_info: - "edacontroller" - "eda" - "configuration" - - "user" - - "users" dependencies: [] # List your role dependencies here, one per line. Be sure to remove the '[]' above, diff --git a/roles/controller_token/tasks/main.yml b/roles/controller_token/tasks/main.yml new file mode 100644 index 0000000..e45866f --- /dev/null +++ b/roles/controller_token/tasks/main.yml @@ -0,0 +1,39 @@ +--- + +# Create EDA Controller Tokens +- name: Add EDA Controller token + ansible.eda.controller_token: + name: "{{ __token_item.name }}" + description: "{{ __token_item.description | default(omit) }}" + token: "{{ __token_item.token | default(omit) }}" + controller_host: "{{ eda_host | default(eda_hostname) }}" + controller_username: "{{ eda_username | default(omit) }}" + controller_password: "{{ eda_password | default(omit) }}" + validate_certs: "{{ eda_validate_certs | default(omit) }}" + request_timeout: "{{ eda_request_timeout | default(omit) }}" + loop: "{{ eda_controller_tokens }}" + loop_control: + loop_var: "__token_item" + no_log: "{{ eda_configuration_user_token_secure_logging }}" + async: 1000 + poll: 0 + register: __controller_tokens_job_async + changed_when: not __controller_tokens_job_async.changed + vars: + ansible_async_dir: '{{ eda_configuration_async_dir }}' + +- name: "Create controller_token | Wait for finish the controller_token creation" + ansible.builtin.async_status: + jid: "{{ __controller_tokens_job_async_result_item.ansible_job_id }}" + register: __controller_tokens_job_async_result + until: __controller_tokens_job_async_result.finished + retries: "{{ eda_configuration_user_token_async_retries }}" + delay: "{{ eda_configuration_user_token_async_delay }}" + loop: "{{ __controller_tokens_job_async.results }}" + loop_control: + loop_var: __controller_tokens_job_async_result_item + when: __controller_tokens_job_async_result_item.ansible_job_id is defined + no_log: "{{ eda_configuration_user_token_secure_logging }}" + vars: + ansible_async_dir: '{{ eda_configuration_async_dir }}' +... diff --git a/roles/user_token/tests/test.yml b/roles/controller_token/tests/test.yml similarity index 81% rename from roles/user_token/tests/test.yml rename to roles/controller_token/tests/test.yml index c4adc44..3092026 100644 --- a/roles/user_token/tests/test.yml +++ b/roles/controller_token/tests/test.yml @@ -6,7 +6,7 @@ vars: eda_validate_certs: false # Define following vars here, or in eda_configs/eda_auth.yml - # eda_host: ansible-eda-web-svc-test-project.example.com + # controller_host: ansible-eda-web-svc-test-project.example.com # eda_token: changeme pre_tasks: - name: Include vars from eda_configs directory @@ -16,5 +16,5 @@ tags: - always roles: - - ../../user_token + - ../../controller_token ... diff --git a/roles/user_token/tests/vars/user_tokens.yml b/roles/controller_token/tests/vars/user_tokens.yml similarity index 78% rename from roles/user_token/tests/vars/user_tokens.yml rename to roles/controller_token/tests/vars/user_tokens.yml index dd23460..0ad5fc1 100644 --- a/roles/user_token/tests/vars/user_tokens.yml +++ b/roles/controller_token/tests/vars/user_tokens.yml @@ -1,5 +1,5 @@ --- -eda_user_tokens: +eda_controller_tokens: - name: my_user_token description: my awesome token token: ABCDEF diff --git a/roles/credential/README.md b/roles/credential/README.md index 729a716..3e46627 100644 --- a/roles/credential/README.md +++ b/roles/credential/README.md @@ -51,8 +51,8 @@ This also speeds up the overall role. |`name`|""|yes|str|Credential name. Must be lower case containing only alphanumeric characters and underscores.| |`new_name`|""|no|str|Setting this option will change the existing name (looked up via the name field.)| |`description`|""|no|str|Description to use for the credential.| -|`username`|""|yes|str|The username of the credential.| -|`secret`|""|yes|str|The token or password for the given username (depending upon the credential type).| +|`organization`|""|no|str|Organization this Credential belongs to.| +|`inputs`|""|no|dict|Credential inputs where the keys are var names used in templating. Refer to the EDA controller documentation for example syntax.| |`credential_type`|"GitHub Personal Access Token"|yes|str|The type of the credential.| |`state`|`present`|no|str|Desired state of the credential.| @@ -83,7 +83,7 @@ eda_credentials: vars: eda_validate_certs: false # Define following vars here, or in eda_configs/eda_auth.yml - # eda_host: ansible-eda-web-svc-test-credential.example.com + # controller_host: ansible-eda-web-svc-test-credential.example.com # eda_token: changeme pre_tasks: - name: Include vars from eda_configs directory @@ -93,7 +93,7 @@ eda_credentials: tags: - always roles: - - ../../credential + - infra.eda_configuration.credential ``` ## License diff --git a/roles/credential/meta/argument_specs.yml b/roles/credential/meta/argument_specs.yml index 0c8bf3f..521a8b0 100644 --- a/roles/credential/meta/argument_specs.yml +++ b/roles/credential/meta/argument_specs.yml @@ -45,7 +45,7 @@ argument_specs: description: This variable enables secure logging across all roles as a default. # Generic across all roles - eda_host: + controller_host: required: false description: URL to the EDA Controller Server. type: str @@ -59,11 +59,11 @@ argument_specs: required: false description: Specify the timeout Ansible should use in requests to the EDA Controller host. type: float - eda_username: + controller_username: required: false description: User for authentication on EDA Controller type: str - eda_password: + controller_password: required: false description: User's password For EDA Controller type: str diff --git a/roles/credential/tasks/main.yml b/roles/credential/tasks/main.yml index 6f73552..f885526 100644 --- a/roles/credential/tasks/main.yml +++ b/roles/credential/tasks/main.yml @@ -2,19 +2,19 @@ # Create EDA Controller Credential - name: Add EDA Controller credential - infra.eda_configuration.credential: - name: "{{ __credential_item.name }}" - new_name: "{{ __credential_item.new_name | default(omit) }}" - description: "{{ __credential_item.description | default(omit) }}" - credential_type: "{{ __credential_item.credential_type | default('GitHub Personal Access Token') }}" - username: "{{ __credential_item.username | default(omit) }}" - secret: "{{ __credential_item.secret | default(omit) }}" - state: "{{ __credential_item.state | default(eda_state | default('present')) }}" - eda_host: "{{ eda_host | default(eda_hostname) }}" - eda_username: "{{ eda_username | default(omit) }}" - eda_password: "{{ eda_password | default(omit) }}" - validate_certs: "{{ eda_validate_certs | default(omit) }}" - request_timeout: "{{ eda_request_timeout | default(omit) }}" + ansible.eda.credential: + name: "{{ __credential_item.name }}" + new_name: "{{ __credential_item.new_name | default(omit) }}" + description: "{{ __credential_item.description | default(omit) }}" + organization_name: "{{ __credential_item.organization | default(omit) }}" + credential_type_name: "{{ __credential_item.credential_type | default('GitHub Personal Access Token') }}" + inputs: "{{ __credential_item.inputs | default(omit) }}" + state: "{{ __credential_item.state | default(eda_state | default('present')) }}" + controller_host: "{{ eda_host | default(eda_hostname) }}" + controller_username: "{{ eda_username | default(omit) }}" + controller_password: "{{ eda_password | default(omit) }}" + validate_certs: "{{ eda_validate_certs | default(omit) }}" + request_timeout: "{{ eda_request_timeout | default(omit) }}" loop: "{{ eda_credentials }}" loop_control: loop_var: "__credential_item" diff --git a/roles/credential/tests/test.yml b/roles/credential/tests/test.yml index 460307a..2fd4e4d 100644 --- a/roles/credential/tests/test.yml +++ b/roles/credential/tests/test.yml @@ -6,7 +6,7 @@ vars: eda_validate_certs: false # Define following vars here, or in eda_configs/eda_auth.yml - # eda_host: ansible-eda-web-svc-test-project.example.com + # controller_host: ansible-eda-web-svc-test-project.example.com # eda_token: changeme pre_tasks: - name: Include vars from eda_configs directory diff --git a/roles/decision_environment/README.md b/roles/decision_environment/README.md index b4b678f..ef665b1 100644 --- a/roles/decision_environment/README.md +++ b/roles/decision_environment/README.md @@ -53,6 +53,7 @@ This also speeds up the overall role. |`description`|""|no|str|Description to use for the Project.| |`image_url`|""|yes|str|A URL to a a container image to use for the decision environment.| |`credential`|""|no|str|The credential used to access the container registry holding the image.| +|`organization`|""|no|str|Organization this decision environment belongs to.| |`state`|`present`|no|str|Desired state of the decision environment.| ### Standard Decision Environment Data Structure @@ -81,7 +82,7 @@ eda_decision_environments: vars: eda_validate_certs: false # Define following vars here, or in eda_configs/eda_auth.yml - # eda_host: ansible-eda-web-svc-test-project.example.com + # controller_host: ansible-eda-web-svc-test-project.example.com # eda_token: changeme pre_tasks: - name: Include vars from eda_configs directory @@ -91,7 +92,7 @@ eda_decision_environments: tags: - always roles: - - ../../decision_environment + - infra.eda_configuration.decision_environment ``` ## License diff --git a/roles/decision_environment/meta/argument_specs.yml b/roles/decision_environment/meta/argument_specs.yml index 204812f..bd3c910 100644 --- a/roles/decision_environment/meta/argument_specs.yml +++ b/roles/decision_environment/meta/argument_specs.yml @@ -45,7 +45,7 @@ argument_specs: description: This variable enables secure logging across all roles as a default. # Generic across all roles - eda_host: + controller_host: required: false description: URL to the EDA Controller Server. type: str @@ -59,11 +59,11 @@ argument_specs: required: false description: Specify the timeout Ansible should use in requests to the EDA Controller host. type: float - eda_username: + controller_username: required: false description: User for authentication on EDA Controller type: str - eda_password: + controller_password: required: false description: User's password For EDA Controller type: str diff --git a/roles/decision_environment/tasks/main.yml b/roles/decision_environment/tasks/main.yml index 55c62b0..7483933 100644 --- a/roles/decision_environment/tasks/main.yml +++ b/roles/decision_environment/tasks/main.yml @@ -2,18 +2,19 @@ # Create EDA Controller Decision Environment - name: Add EDA Controller decision environment - infra.eda_configuration.decision_environment: - name: "{{ __de_item.name }}" - new_name: "{{ __de_item.new_name | default(omit) }}" - description: "{{ __de_item.description | default(omit) }}" - image_url: "{{ __de_item.image_url | default(omit) }}" - credential: "{{ __de_item.credential | default(omit) }}" - state: "{{ __de_item.state | default(eda_state | default('present')) }}" - eda_host: "{{ eda_host | default(eda_hostname) }}" - eda_username: "{{ eda_username | default(omit) }}" - eda_password: "{{ eda_password | default(omit) }}" - validate_certs: "{{ eda_validate_certs | default(omit) }}" - request_timeout: "{{ eda_request_timeout | default(omit) }}" + ansible.eda.decision_environment: + name: "{{ __de_item.name }}" + new_name: "{{ __de_item.new_name | default(omit) }}" + description: "{{ __de_item.description | default(omit) }}" + image_url: "{{ __de_item.image_url | default(omit) }}" + credential: "{{ __de_item.credential | default(omit) }}" + organization_name: "{{ __de_item.organization | default(omit) }}" + state: "{{ __de_item.state | default(eda_state | default('present')) }}" + controller_host: "{{ eda_host | default(eda_hostname) }}" + controller_username: "{{ eda_username | default(omit) }}" + controller_password: "{{ eda_password | default(omit) }}" + validate_certs: "{{ eda_validate_certs | default(omit) }}" + request_timeout: "{{ eda_request_timeout | default(omit) }}" loop: "{{ eda_decision_environments }}" loop_control: loop_var: "__de_item" diff --git a/roles/decision_environment/tests/test.yml b/roles/decision_environment/tests/test.yml index 270e94a..549a7bd 100644 --- a/roles/decision_environment/tests/test.yml +++ b/roles/decision_environment/tests/test.yml @@ -6,7 +6,7 @@ vars: eda_validate_certs: false # Define following vars here, or in eda_configs/eda_auth.yml - # eda_host: ansible-eda-web-svc-test-project.example.com + # controller_host: ansible-eda-web-svc-test-project.example.com # eda_token: changeme pre_tasks: - name: Include vars from eda_configs directory diff --git a/roles/dispatch/README.md b/roles/dispatch/README.md index b93ca74..94ad6e4 100644 --- a/roles/dispatch/README.md +++ b/roles/dispatch/README.md @@ -16,7 +16,7 @@ Each role has its own variables, for information on those please see each role w eda_configuration_dispatcher_roles: - {role: user, var: eda_users, tags: user} - {role: credential, var: eda_credentials, tags: credential} - - {role: user_token, var: eda_user_tokens, tags: user_token} + - {role: controller_token, var: eda_controller_tokens, tags: controller_token} - {role: project, var: eda_projects, tags: project} - {role: project_sync, var: eda_projects, tags: project_sync} - {role: decision_environment, var: eda_decision_environments, tags: decision_environment} diff --git a/roles/dispatch/defaults/main.yml b/roles/dispatch/defaults/main.yml index 6c638b6..abab902 100644 --- a/roles/dispatch/defaults/main.yml +++ b/roles/dispatch/defaults/main.yml @@ -6,9 +6,9 @@ eda_configuration_dispatcher_roles: - role: credential var: eda_credentials tags: credential - - role: user_token - var: eda_user_tokens - tags: user_token + - role: controller_token + var: eda_controller_tokens + tags: controller_token - role: project var: eda_projects tags: project diff --git a/roles/dispatch/meta/argument_specs.yml b/roles/dispatch/meta/argument_specs.yml index 8dd5c4d..ba0a0df 100644 --- a/roles/dispatch/meta/argument_specs.yml +++ b/roles/dispatch/meta/argument_specs.yml @@ -7,7 +7,7 @@ argument_specs: default: - {role: user, var: eda_users, tags: user} - {role: credential, var: eda_credentials, tags: credential} - - {role: user_token, var: eda_user_tokens, tags: user_token} + - {role: controller_token, var: eda_controller_tokens, tags: controller_token} - {role: project, var: eda_projects, tags: project} - {role: project_sync, var: eda_projects, tags: project_sync} - {role: decision_environment, var: eda_decision_environments, tags: decision_environment} @@ -64,12 +64,12 @@ argument_specs: required: false description: Whether or not to validate the Ansible EDA Server's SSL certificate. type: str - eda_username: + controller_username: default: None required: false description: Admin User on the Ansible EDA Server. Either username / password or oauthtoken need to be specified. type: str - eda_password: + controller_password: default: None required: false description: eda Admin User's password on the Ansible EDA Server. This should be stored in an Ansible Vault at vars/eda-secrets.yml or elsewhere and called from a parent playbook. diff --git a/roles/dispatch/tests/test.yml b/roles/dispatch/tests/test.yml index 5f85267..f7aab8b 100644 --- a/roles/dispatch/tests/test.yml +++ b/roles/dispatch/tests/test.yml @@ -6,8 +6,8 @@ vars: eda_validate_certs: false eda_hostname: eda.example.com - eda_username: admin - eda_password: changeme + controller_username: admin + controller_password: changeme collections: - awx.awx diff --git a/roles/project/README.md b/roles/project/README.md index fdc3b46..57edec8 100644 --- a/roles/project/README.md +++ b/roles/project/README.md @@ -52,7 +52,7 @@ This also speeds up the overall role. |`new_name`|""|no|str|Setting this option will change the existing name (looked up via the name field.)| |`description`|""|no|str|Description to use for the Project.| |`url`|""|yes|str|A URL to a remote archive, such as a Github Release or a build artifact stored in Artifactory and unpacks it into the project path for use. (Alias: scm_url)| -|`tls_validation`|true|no|bool|Whether the URL should validate using TLS.| +|`organization`|""|no|str|Organization this project belongs to.| |`credential`|""|no|str|The token needed to utilize the SCM URL.| |`state`|`present`|no|str|Desired state of the project.| @@ -83,7 +83,7 @@ eda_projects: vars: eda_validate_certs: false # Define following vars here, or in eda_configs/eda_auth.yml - # eda_host: ansible-eda-web-svc-test-project.example.com + # controller_host: ansible-eda-web-svc-test-project.example.com # eda_token: changeme pre_tasks: - name: Include vars from eda_configs directory @@ -93,7 +93,7 @@ eda_projects: tags: - always roles: - - ../../project + - infra.eda_configuration.project ``` ## License diff --git a/roles/project/meta/argument_specs.yml b/roles/project/meta/argument_specs.yml index 42dc125..c487281 100644 --- a/roles/project/meta/argument_specs.yml +++ b/roles/project/meta/argument_specs.yml @@ -45,7 +45,7 @@ argument_specs: description: This variable enables secure logging across all roles as a default. # Generic across all roles - eda_host: + controller_host: required: false description: URL to the EDA Controller Server. type: str @@ -59,11 +59,11 @@ argument_specs: required: false description: Specify the timeout Ansible should use in requests to the EDA Controller host. type: float - eda_username: + controller_username: required: false description: User for authentication on EDA Controller type: str - eda_password: + controller_password: required: false description: User's password For EDA Controller type: str diff --git a/roles/project/tasks/main.yml b/roles/project/tasks/main.yml index 4d12a99..ca03b0c 100644 --- a/roles/project/tasks/main.yml +++ b/roles/project/tasks/main.yml @@ -2,19 +2,20 @@ # Create EDA Controller Project - name: Add EDA Controller project - infra.eda_configuration.project: - name: "{{ __project_item.name }}" - new_name: "{{ __project_item.new_name | default(omit) }}" - description: "{{ __project_item.description | default(omit) }}" - url: "{{ __project_item.url | default(__project_item.scm_url | default(omit)) }}" - tls_validation: "{{ __project_item.tls_validation | default(omit) }}" - credential: "{{ __project_item.credential | default(omit) }}" - state: "{{ __project_item.state | default(eda_state | default('present')) }}" - eda_host: "{{ eda_host | default(eda_hostname) }}" - eda_username: "{{ eda_username | default(omit) }}" - eda_password: "{{ eda_password | default(omit) }}" - validate_certs: "{{ eda_validate_certs | default(omit) }}" - request_timeout: "{{ eda_request_timeout | default(omit) }}" + ansible.eda.project: + name: "{{ __project_item.name }}" + new_name: "{{ __project_item.new_name | default(omit) }}" + description: "{{ __project_item.description | default(omit) }}" + url: "{{ __project_item.url | default(__project_item.scm_url | default(omit)) }}" + # tls_validation: "{{ __project_item.tls_validation | default(omit) }}" + credential: "{{ __project_item.credential | default(omit) }}" + organization_name: "{{ __project_item.organization | default(omit) }}" + state: "{{ __project_item.state | default(eda_state | default('present')) }}" + controller_host: "{{ eda_host | default(eda_hostname) }}" + controller_username: "{{ eda_username | default(omit) }}" + controller_password: "{{ eda_password | default(omit) }}" + validate_certs: "{{ eda_validate_certs | default(omit) }}" + request_timeout: "{{ eda_request_timeout | default(omit) }}" loop: "{{ eda_projects }}" loop_control: loop_var: "__project_item" diff --git a/roles/project/tests/test.yml b/roles/project/tests/test.yml index 4cc5b5e..c5b814e 100644 --- a/roles/project/tests/test.yml +++ b/roles/project/tests/test.yml @@ -6,7 +6,7 @@ vars: eda_validate_certs: false # Define following vars here, or in eda_configs/eda_auth.yml - # eda_host: ansible-eda-web-svc-test-project.example.com + # controller_host: ansible-eda-web-svc-test-project.example.com # eda_token: changeme pre_tasks: - name: Include vars from eda_configs directory diff --git a/roles/project_sync/README.md b/roles/project_sync/README.md deleted file mode 100644 index d9e96ef..0000000 --- a/roles/project_sync/README.md +++ /dev/null @@ -1,105 +0,0 @@ -# infra.eda_configuration.project - -## Description - -An Ansible Role to create Projects in EDA Controller. - -## Variables - -|Variable Name|Default Value|Required|Description|Example| -|:---:|:---:|:---:|:---:|:---:| -|`eda_host`|""|yes|URL to the EDA Controller (alias: `eda_hostname`)|127.0.0.1| -|`eda_username`|""|yes|Admin User on the EDA Controller || -|`eda_password`|""|yes|EDA Controller Admin User's password on the EDA Controller Server. This should be stored in an Ansible Vault at vars/tower-secrets.yml or elsewhere and called from a parent playbook.|| -|`eda_validate_certs`|`False`|no|Whether or not to validate the Ansible EDA Controller Server's SSL certificate.|| -|`eda_request_timeout`|`10`|no|Specify the timeout Ansible should use in requests to the EDA Controller host.|| -|`eda_configuration_async_dir`|`null`|no|Sets the directory to write the results file for async tasks. The default value is set to `null` which uses the Ansible Default of `/root/.ansible_async/`.|| -|`eda_projects`|`see below`|yes|Data structure describing your projects, described below. Note that this role will only do anything if the `sync` suboption of this variable is set to true.|| - -### Secure Logging Variables - -The following Variables compliment each other. -If Both variables are not set, secure logging defaults to false. -The role defaults to False as normally the add project task does not include sensitive information. -eda_configuration_project_secure_logging defaults to the value of eda_configuration_secure_logging if it is not explicitly called. This allows for secure logging to be toggled for the entire suite of EDA Controller configuration roles with a single variable, or for the user to selectively use it. - -|Variable Name|Default Value|Required|Description| -|:---:|:---:|:---:|:---:| -|`eda_configuration_project_secure_logging`|`False`|no|Whether or not to include the sensitive Project role tasks in the log. Set this value to `True` if you will be providing your sensitive values from elsewhere.| -|`eda_configuration_secure_logging`|`False`|no|This variable enables secure logging as well, but is shared across multiple roles, see above.| - -### Asynchronous Retry Variables - -The following Variables set asynchronous retries for the role. -If neither of the retries or delay or retries are set, they will default to their respective defaults. -This allows for all items to be created, then checked that the task finishes successfully. -This also speeds up the overall role. - -|Variable Name|Default Value|Required|Description| -|:---:|:---:|:---:|:---:| -|`eda_configuration_async_retries`|50|no|This variable sets the number of retries to attempt for the role globally.| -|`eda_configuration_project_sync_async_retries`|`eda_configuration_async_retries`|no|This variable sets the number of retries to attempt for the role.| -|`eda_configuration_async_delay`|1|no|This sets the delay between retries for the role globally.| -|`eda_configuration_project_sync_async_delay`|`eda_configuration_async_delay`|no|This sets the delay between retries for the role.| - -## Data Structure - -### Project Variables - -|Variable Name|Default Value|Required|Type|Description| -|:---:|:---:|:---:|:---:|:---:| -|`name`|""|yes|str|Project name. Must be lower case containing only alphanumeric characters and underscores.| -|`sync`|false|no|bool|Whether to sync the project. By default it will not sync unless this is set to true.| -|`wait`|true|no|str|Whether to wait for the sync to complete| -|`interval`|`eda_configuration_project_sync_async_delay`|no|str|The interval which the sync task will be checked for completion| -|`timeout`|""|no|str|How long to wait for the sync task to complete| - -### Standard Project Data Structure - -#### Yaml Example - -```yaml ---- -eda_projects: - - name: my_project - description: my awesome project - url: https://github.com/ansible/ansible-rulebook.git - credential: test_token - wait: true - interval: 10 - sync: true -``` - -## Playbook Examples - -### Standard Role Usage - -```yaml ---- -- name: Sync project to EDA Controller - hosts: localhost - connection: local - gather_facts: false - vars: - eda_validate_certs: false - # Define following vars here, or in eda_configs/eda_auth.yml - # eda_host: ansible-eda-web-svc-test-project.example.com - # eda_token: changeme - pre_tasks: - - name: Include vars from eda_configs directory - ansible.builtin.include_vars: - dir: ./vars - extensions: ["yml"] - tags: - - always - roles: - - ../../project_sync -``` - -## License - -[GPLv3+](https://github.com/redhat-cop/eda_configuration#licensing) - -## Author - -[Tom Page](https://github.com/Tompage1994/) diff --git a/roles/project_sync/defaults/main.yml b/roles/project_sync/defaults/main.yml deleted file mode 100644 index c71af40..0000000 --- a/roles/project_sync/defaults/main.yml +++ /dev/null @@ -1,8 +0,0 @@ ---- -eda_projects: [] - -eda_configuration_project_secure_logging: "{{ eda_configuration_secure_logging | default(false) }}" -eda_configuration_project_sync_async_retries: "{{ eda_configuration_async_retries | default(50) }}" -eda_configuration_project_sync_async_delay: "{{ eda_configuration_async_delay | default(1) }}" -eda_configuration_async_dir: null -... diff --git a/roles/project_sync/meta/argument_specs.yml b/roles/project_sync/meta/argument_specs.yml deleted file mode 100644 index f769329..0000000 --- a/roles/project_sync/meta/argument_specs.yml +++ /dev/null @@ -1,70 +0,0 @@ ---- -argument_specs: - main: - short_description: An Ansible Role to sync projects in EDA controller. - options: - eda_projects: - default: [] - required: false - description: Data structure describing your projects to manage. If the sync option is set then the project will be synced - type: list - elements: dict - - # Async variables - eda_configuration_project_sync_async_retries: - default: "{{ eda_configuration_async_retries | default(50) }}" - required: false - description: This variable sets the number of retries to attempt for the role. - eda_configuration_async_retries: - default: 50 - required: false - description: This variable sets number of retries across all roles as a default. - eda_configuration_project_sync_async_delay: - default: "{{ eda_configuration_async_delay | default(1) }}" - required: false - description: This variable sets delay between retries for the role. - eda_configuration_async_delay: - default: 1 - required: false - description: This variable sets delay between retries across all roles as a default. - eda_configuration_async_dir: - default: null - required: false - description: Sets the directory to write the results file for async tasks. The default value is set to `null` which uses the Ansible Default of `/root/.ansible_async/`. - - # No_log variables - eda_configuration_project_secure_logging: - default: "{{ eda_configuration_secure_logging | default(false) }}" - required: false - type: bool - description: Whether or not to include the sensitive role tasks in the log. Set this value to `true` if you will be providing your sensitive values from elsewhere. - eda_configuration_secure_logging: - default: false - required: false - type: bool - description: This variable enables secure logging across all roles as a default. - - # Generic across all roles - eda_host: - required: false - description: URL to the EDA Controller Server. - type: str - eda_validate_certs: - default: true - required: false - description: Whether or not to validate the EDA Controller Server's SSL certificate. - type: str - eda_request_timeout: - default: 10 - required: false - description: Specify the timeout Ansible should use in requests to the EDA Controller host. - type: float - eda_username: - required: false - description: User for authentication on EDA Controller - type: str - eda_password: - required: false - description: User's password For EDA Controller - type: str -... diff --git a/roles/project_sync/meta/main.yml b/roles/project_sync/meta/main.yml deleted file mode 100644 index 697a7df..0000000 --- a/roles/project_sync/meta/main.yml +++ /dev/null @@ -1,43 +0,0 @@ ---- -galaxy_info: - role_name: "project_sync" - author: "Tom Page" - description: "An Ansible Role to create a project in EDA Controller." - company: "Red Hat" - - # If the issue tracker for your role is not on github, uncomment the - # next line and provide a value - # issue_tracker_url: http://example.com/issue/tracker - license: "GPLv3+" - - min_ansible_version: 2.15.0 - - # Optionally specify the branch Galaxy will use when accessing the GitHub - # repo for this role. During role install, if no tags are available, - # Galaxy will use this branch. During import Galaxy will access files on - # this branch. If Travis integration is configured, only notifications for this - # branch will be accepted. Otherwise, in all cases, the repo's default branch - # (usually master) will be used. - - # github_branch: - - # - # platforms is a list of platforms, and each platform has a name and a list of versions. - # - platforms: - - name: "EL" - versions: - - "all" - - galaxy_tags: - - "edacontroller" - - "eda" - - "configuration" - - "project" - - "projects" - - "sync" - -dependencies: [] -# List your role dependencies here, one per line. Be sure to remove the '[]' above, -# if you add dependencies to this list. -... diff --git a/roles/project_sync/tasks/main.yml b/roles/project_sync/tasks/main.yml deleted file mode 100644 index 0f89887..0000000 --- a/roles/project_sync/tasks/main.yml +++ /dev/null @@ -1,41 +0,0 @@ ---- - -# Sync EDA Controller Project -- name: Sync EDA Controller project - infra.eda_configuration.project_sync: - name: "{{ __project_sync_item.name }}" - wait: "{{ __project_sync_item.wait | default(omit) }}" - interval: "{{ __project_sync_item.interval | default(eda_configuration_project_sync_async_delay) }}" - timeout: "{{ __project_sync_item.timeout | default(omit) }}" - eda_host: "{{ eda_host | default(eda_hostname) }}" - eda_username: "{{ eda_username | default(omit) }}" - eda_password: "{{ eda_password | default(omit) }}" - validate_certs: "{{ eda_validate_certs | default(omit) }}" - request_timeout: "{{ eda_request_timeout | default(omit) }}" - loop: "{{ eda_projects }}" - loop_control: - loop_var: "__project_sync_item" - when: __project_sync_item.sync | default(false) - no_log: "{{ eda_configuration_project_secure_logging }}" - async: 1000 - poll: 0 - register: __projects_sync_job_async - changed_when: not __projects_sync_job_async.changed - vars: - ansible_async_dir: '{{ eda_configuration_async_dir }}' - -- name: "Sync project | Wait for finish syncing the project" - ansible.builtin.async_status: - jid: "{{ __projects_sync_job_async_result_item.ansible_job_id }}" - register: __projects_sync_job_async_result - until: __projects_sync_job_async_result.finished - retries: "{{ eda_configuration_project_sync_async_retries }}" - delay: "{{ eda_configuration_project_sync_async_delay }}" - loop: "{{ __projects_sync_job_async.results }}" - loop_control: - loop_var: __projects_sync_job_async_result_item - when: __projects_sync_job_async_result_item.ansible_job_id is defined - no_log: "{{ eda_configuration_project_secure_logging }}" - vars: - ansible_async_dir: '{{ eda_configuration_async_dir }}' -... diff --git a/roles/project_sync/tests/test.yml b/roles/project_sync/tests/test.yml deleted file mode 100644 index 53f01c2..0000000 --- a/roles/project_sync/tests/test.yml +++ /dev/null @@ -1,20 +0,0 @@ ---- -- name: Sync project on EDA Controller - hosts: localhost - connection: local - gather_facts: false - vars: - eda_validate_certs: false - # Define following vars here, or in eda_configs/eda_auth.yml - # eda_host: ansible-eda-web-svc-test-project.example.com - # eda_token: changeme - pre_tasks: - - name: Include vars from eda_configs directory - ansible.builtin.include_vars: - dir: ./vars - extensions: ["yml"] - tags: - - always - roles: - - ../../project_sync -... diff --git a/roles/project_sync/tests/vars/projects.yml b/roles/project_sync/tests/vars/projects.yml deleted file mode 100644 index d5b5c7e..0000000 --- a/roles/project_sync/tests/vars/projects.yml +++ /dev/null @@ -1,11 +0,0 @@ ---- -eda_projects: - - name: my_project - description: my awesome project - url: https://github.com/ansible/ansible-rulebook.git - credential: test_token - sync: true - interval: 5 - timeout: 30 - wait: true -... diff --git a/roles/rulebook_activation/README.md b/roles/rulebook_activation/README.md index dbed80c..aa8792a 100644 --- a/roles/rulebook_activation/README.md +++ b/roles/rulebook_activation/README.md @@ -58,6 +58,12 @@ This also speeds up the overall role. |`awx_token`|""|no|str|The token used to authenticate to controller.| |`enabled`|"true"|no|str|Whether the rulebook activation is automatically enabled to run.| |`state`|`present`|no|str|Desired state of the rulebook activation.| +|`organization_name`|""|no|str|The name of the organization.| +|`eda_credentials`|""|no|list|A list of IDs for EDA credentials used by the rulebook activation.| +|`k8s_service_name`|""|no|str|The name of the Kubernetes service associated with this rulebook activation.| +|`swap_single_source`|"true"|no|bool|Allow swapping of single sources in a rulebook without name match.| +|`event_streams`|""|no|list|A list of event stream names that this rulebook activation listens to.| +|`log_level`|""|no|str|Allow setting the desired log level.| ### Standard rulebook activation Data Structure @@ -91,7 +97,7 @@ eda_rulebook_activations: vars: eda_validate_certs: false # Define following vars here, or in eda_configs/eda_auth.yml - # eda_host: ansible-eda-web-svc-test-project.example.com + # controller_host: ansible-eda-web-svc-test-project.example.com # eda_token: changeme pre_tasks: - name: Include vars from eda_configs directory diff --git a/roles/rulebook_activation/meta/argument_specs.yml b/roles/rulebook_activation/meta/argument_specs.yml index 23dbcf1..19ed5d3 100644 --- a/roles/rulebook_activation/meta/argument_specs.yml +++ b/roles/rulebook_activation/meta/argument_specs.yml @@ -45,7 +45,7 @@ argument_specs: description: This variable enables secure logging across all roles as a default. # Generic across all roles - eda_host: + controller_host: required: false description: URL to the EDA Controller Server. type: str @@ -59,11 +59,11 @@ argument_specs: required: false description: Specify the timeout Ansible should use in requests to the EDA Controller host. type: float - eda_username: + controller_username: required: false description: User for authentication on EDA Controller type: str - eda_password: + controller_password: required: false description: User's password For EDA Controller type: str diff --git a/roles/rulebook_activation/tasks/main.yml b/roles/rulebook_activation/tasks/main.yml index 1009d3d..f92ea53 100644 --- a/roles/rulebook_activation/tasks/main.yml +++ b/roles/rulebook_activation/tasks/main.yml @@ -2,7 +2,7 @@ # Create EDA Controller Rulebook Activation - name: Add EDA Controller rulebook activation - infra.eda_configuration.rulebook_activation: + ansible.eda.rulebook_activation: name: "{{ __ra_item.name }}" description: "{{ __ra_item.description | default(omit) }}" project: "{{ __ra_item.project | default(omit) }}" @@ -12,10 +12,16 @@ restart_policy: "{{ __ra_item.restart_policy | default(omit) }}" extra_vars: "{{ __ra_item.extra_vars | default(omit) }}" enabled: "{{ __ra_item.enabled | default(omit) }}" + organization_name: "{{ __ra_item.organization | default(omit) }}" + eda_credentials: "{{ __ra_item.eda_credentials | default(omit) }}" + k8s_service_name: "{{ __ra_item.k8s_service_name | default(omit) }}" + swap_single_source: "{{ __ra_item.swap_single_source | default(omit) }}" + event_streams: "{{ __ra_item.event_streams | default(omit) }}" + log_level: "{{ __ra_item.log_level | default(omit) }}" state: "{{ __ra_item.state | default(eda_state | default('present')) }}" - eda_host: "{{ eda_host | default(eda_hostname) }}" - eda_username: "{{ eda_username | default(omit) }}" - eda_password: "{{ eda_password | default(omit) }}" + controller_host: "{{ eda_host | default(eda_hostname) }}" + controller_username: "{{ eda_username | default(omit) }}" + controller_password: "{{ eda_password | default(omit) }}" validate_certs: "{{ eda_validate_certs | default(omit) }}" request_timeout: "{{ eda_request_timeout | default(omit) }}" loop: "{{ eda_rulebook_activations }}" diff --git a/roles/rulebook_activation/tests/test.yml b/roles/rulebook_activation/tests/test.yml index 51e91a7..4ad1576 100644 --- a/roles/rulebook_activation/tests/test.yml +++ b/roles/rulebook_activation/tests/test.yml @@ -6,7 +6,7 @@ vars: eda_validate_certs: false # Define following vars here, or in eda_configs/eda_auth.yml - # eda_host: ansible-eda-web-svc-test-project.example.com + # controller_host: ansible-eda-web-svc-test-project.example.com # eda_token: changeme pre_tasks: - name: Include vars from eda_configs directory diff --git a/roles/user/README.md b/roles/user/README.md index 92bd793..a0c07f2 100644 --- a/roles/user/README.md +++ b/roles/user/README.md @@ -55,6 +55,7 @@ This also speeds up the overall role. |`email`|""|no|str|User's email address.| |`password`|""|yes|str|Password to use for the user.| |`update_secrets`|true|no|bool|Setting true will always change password if user specifies password. Password will only change if false if other fields change.| +|`is_superuser`|""|no|bool|Make user as superuser.| |`roles`|""|yes|list|Roles the user will have. Current acceptable values are: Viewer, Auditor, Editor, Contributor, Operator, Admin.| |`state`|`present`|no|str|Desired state of the user.| @@ -89,7 +90,7 @@ eda_users: vars: eda_validate_certs: false # Define following vars here, or in eda_configs/eda_auth.yml - # eda_host: ansible-eda-web-svc-test-user.example.com + # controller_host: ansible-eda-web-svc-test-user.example.com # eda_token: changeme pre_tasks: - name: Include vars from eda_configs directory diff --git a/roles/user/meta/argument_specs.yml b/roles/user/meta/argument_specs.yml index e2800f1..761e50f 100644 --- a/roles/user/meta/argument_specs.yml +++ b/roles/user/meta/argument_specs.yml @@ -45,7 +45,7 @@ argument_specs: description: This variable enables secure logging across all roles as a default. # Generic across all roles - eda_host: + controller_host: required: false description: URL to the EDA Controller Server. type: str @@ -59,11 +59,11 @@ argument_specs: required: false description: Specify the timeout Ansible should use in requests to the EDA Controller host. type: float - eda_username: + controller_username: required: false description: User for authentication on EDA Controller type: str - eda_password: + controller_password: required: false description: User's password For EDA Controller type: str diff --git a/roles/user/tasks/main.yml b/roles/user/tasks/main.yml index 4c7dcf7..23e540b 100644 --- a/roles/user/tasks/main.yml +++ b/roles/user/tasks/main.yml @@ -2,21 +2,22 @@ # Create EDA Controller user - name: Add EDA Controller user - infra.eda_configuration.user: - username: "{{ __user_item.username }}" - new_username: "{{ __user_item.new_username | default(omit) }}" - first_name: "{{ __user_item.first_name | default(omit) }}" - last_name: "{{ __user_item.last_name | default(omit) }}" - email: "{{ __user_item.email | default(omit) }}" - password: "{{ __user_item.password | default(omit) }}" - update_secrets: "{{ __user_item.update_secrets | default(omit) }}" - roles: "{{ __user_item.roles | default(omit) }}" - state: "{{ __user_item.state | default(eda_state | default('present')) }}" - eda_host: "{{ eda_host | default(eda_hostname) }}" - eda_username: "{{ eda_username | default(omit) }}" - eda_password: "{{ eda_password | default(omit) }}" - validate_certs: "{{ eda_validate_certs | default(omit) }}" - request_timeout: "{{ eda_request_timeout | default(omit) }}" + ansible.eda.user: + username: "{{ __user_item.username }}" + new_username: "{{ __user_item.new_username | default(omit) }}" + first_name: "{{ __user_item.first_name | default(omit) }}" + last_name: "{{ __user_item.last_name | default(omit) }}" + email: "{{ __user_item.email | default(omit) }}" + password: "{{ __user_item.password | default(omit) }}" + update_secrets: "{{ __user_item.update_secrets | default(omit) }}" + is_superuser: "{{ __user_item.is_superuser | default(omit) }}" + roles: "{{ __user_item.roles | default(omit) }}" + state: "{{ __user_item.state | default(eda_state | default('present')) }}" + controller_host: "{{ eda_host | default(eda_hostname) }}" + controller_username: "{{ eda_username | default(omit) }}" + controller_password: "{{ eda_password | default(omit) }}" + validate_certs: "{{ eda_validate_certs | default(omit) }}" + request_timeout: "{{ eda_request_timeout | default(omit) }}" loop: "{{ eda_users }}" loop_control: loop_var: "__user_item" diff --git a/roles/user/tests/test.yml b/roles/user/tests/test.yml index 243cffb..8a3cbdd 100644 --- a/roles/user/tests/test.yml +++ b/roles/user/tests/test.yml @@ -6,7 +6,7 @@ vars: eda_validate_certs: false # Define following vars here, or in eda_configs/eda_auth.yml - # eda_host: ansible-eda-web-svc-test-user.example.com + # controller_host: ansible-eda-web-svc-test-user.example.com # eda_token: changeme pre_tasks: - name: Include vars from eda_configs directory diff --git a/roles/user_token/tasks/main.yml b/roles/user_token/tasks/main.yml deleted file mode 100644 index 0927e37..0000000 --- a/roles/user_token/tasks/main.yml +++ /dev/null @@ -1,40 +0,0 @@ ---- - -# Create EDA Controller User Tokens -- name: Add EDA Controller user token - infra.eda_configuration.user_token: - name: "{{ __token_item.name }}" - new_name: "{{ __token_item.new_name | default(omit) }}" - description: "{{ __token_item.description | default(omit) }}" - token: "{{ __token_item.token | default(omit) }}" - eda_host: "{{ eda_host | default(eda_hostname) }}" - eda_username: "{{ eda_username | default(omit) }}" - eda_password: "{{ eda_password | default(omit) }}" - validate_certs: "{{ eda_validate_certs | default(omit) }}" - request_timeout: "{{ eda_request_timeout | default(omit) }}" - loop: "{{ eda_user_tokens }}" - loop_control: - loop_var: "__token_item" - no_log: "{{ eda_configuration_user_token_secure_logging }}" - async: 1000 - poll: 0 - register: __user_tokens_job_async - changed_when: not __user_tokens_job_async.changed - vars: - ansible_async_dir: '{{ eda_configuration_async_dir }}' - -- name: "Create user_token | Wait for finish the user_token creation" - ansible.builtin.async_status: - jid: "{{ __user_tokens_job_async_result_item.ansible_job_id }}" - register: __user_tokens_job_async_result - until: __user_tokens_job_async_result.finished - retries: "{{ eda_configuration_user_token_async_retries }}" - delay: "{{ eda_configuration_user_token_async_delay }}" - loop: "{{ __user_tokens_job_async.results }}" - loop_control: - loop_var: __user_tokens_job_async_result_item - when: __user_tokens_job_async_result_item.ansible_job_id is defined - no_log: "{{ eda_configuration_user_token_secure_logging }}" - vars: - ansible_async_dir: '{{ eda_configuration_async_dir }}' -... diff --git a/tests/playbooks/eda_configs/eda_auth.yml b/tests/playbooks/eda_configs/eda_auth.yml index 34724ea..62d6f39 100644 --- a/tests/playbooks/eda_configs/eda_auth.yml +++ b/tests/playbooks/eda_configs/eda_auth.yml @@ -1,7 +1,7 @@ # User may add tower auth creds to this file and encrypt it using `ansible-vault` --- eda_hostname: http://localhost:8000 -eda_username: admin -eda_password: testpass +controller_username: admin +controller_password: testpass eda_validate_certs: false ... diff --git a/tests/playbooks/eda_configs/eda_user_tokens.yml b/tests/playbooks/eda_configs/eda_user_tokens.yml index dd23460..0ad5fc1 100644 --- a/tests/playbooks/eda_configs/eda_user_tokens.yml +++ b/tests/playbooks/eda_configs/eda_user_tokens.yml @@ -1,5 +1,5 @@ --- -eda_user_tokens: +eda_controller_tokens: - name: my_user_token description: my awesome token token: ABCDEF diff --git a/tests/playbooks/testing_collections_playbook.yml b/tests/playbooks/testing_collections_playbook.yml index f0a544e..9b7f6eb 100644 --- a/tests/playbooks/testing_collections_playbook.yml +++ b/tests/playbooks/testing_collections_playbook.yml @@ -38,7 +38,7 @@ tags: - assertions vars: - project_lookup: "{{ lookup('infra.eda_configuration.eda_api', 'projects', host=eda_hostname, username=eda_username, + project_lookup: "{{ lookup('ansible.eda.eda_api', 'projects', host=eda_hostname, username=eda_username, password=eda_password, verify_ssl=false, query_params=qp) }}" qp: url: 'https://github.com/ansible/event-driven-ansible.git'