From 9a593af0e3fc689c01bd8b907fe7707867ba0e0d Mon Sep 17 00:00:00 2001 From: Abhijeet Kasurde Date: Thu, 15 Aug 2024 12:10:59 -0700 Subject: [PATCH] Add project module (#238) * new module for managing project * new module for listing project info * tests Signed-off-by: Abhijeet Kasurde --- .config/dictionary.txt | 1 + plugins/modules/project.py | 180 ++++++++++++++++++ plugins/modules/project_info.py | 121 ++++++++++++ .../targets/project/tasks/create.yml | 81 ++++++++ .../targets/project/tasks/delete.yml | 45 +++++ .../targets/project/tasks/main.yml | 35 ++++ .../targets/project/tasks/update.yml | 67 +++++++ .../targets/project_info/tasks/create.yml | 19 ++ .../targets/project_info/tasks/main.yml | 70 +++++++ 9 files changed, 619 insertions(+) create mode 100644 plugins/modules/project.py create mode 100644 plugins/modules/project_info.py create mode 100644 tests/integration/targets/project/tasks/create.yml create mode 100644 tests/integration/targets/project/tasks/delete.yml create mode 100644 tests/integration/targets/project/tasks/main.yml create mode 100644 tests/integration/targets/project/tasks/update.yml create mode 100644 tests/integration/targets/project_info/tasks/create.yml create mode 100644 tests/integration/targets/project_info/tasks/main.yml diff --git a/.config/dictionary.txt b/.config/dictionary.txt index e469ccf1..334fad53 100644 --- a/.config/dictionary.txt +++ b/.config/dictionary.txt @@ -55,3 +55,4 @@ auths Passw AUTHS EDAHTTP +refspec diff --git a/plugins/modules/project.py b/plugins/modules/project.py new file mode 100644 index 00000000..970fb4cd --- /dev/null +++ b/plugins/modules/project.py @@ -0,0 +1,180 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright: Contributors to the 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 = """ +--- +module: project +author: + - Nikhil Jain (@jainnikhil30) + - Abhijeet Kasurde (@akasurde) +short_description: Create, update or delete project in EDA Controller +description: + - This module allows user to create, update or delete project in a EDA controller. +version_added: '2.0.0' +options: + name: + description: + - The name of the project. + type: str + required: true + new_name: + description: + - Setting this option will change the existing name. + type: str + description: + description: + - The description of the project. + type: str + url: + description: + - The git URL of the project. + type: str + credential: + description: + - The name of the credential to associate with the project. + type: str + state: + description: + - Desired state of the resource. + default: "present" + choices: ["present", "absent"] + type: str +extends_documentation_fragment: + - ansible.eda.eda_controller.auths +""" + +EXAMPLES = """ +- name: Create EDA Projects + ansible.eda.project: + controller_host: https://my_eda_host/ + controller_username: admin + controller_password: MySuperSecretPassw0rd + name: "Example Project" + description: "Example project description" + url: "https://example.com/project1" + state: present + +- name: Update the name of the project + ansible.eda.project: + controller_host: https://my_eda_host/ + controller_username: admin + controller_password: MySuperSecretPassw0rd + name: "Example Project" + new_name: "Latest Example Project" + description: "Example project description" + url: "https://example.com/project1" + state: present + +- name: Delete the project + ansible.eda.project: + controller_host: https://my_eda_host/ + controller_username: admin + controller_password: MySuperSecretPassw0rd + name: "Example Project" + state: absent +""" + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.ansible.eda.plugins.module_utils.arguments import AUTH_ARGSPEC +from ansible_collections.ansible.eda.plugins.module_utils.client import Client +from ansible_collections.ansible.eda.plugins.module_utils.controller import Controller +from ansible_collections.ansible.eda.plugins.module_utils.errors import EDAError + + +def main(): + argument_spec = dict( + name=dict(required=True), + new_name=dict(), + description=dict(), + url=dict(), + credential=dict(), + state=dict(choices=["present", "absent"], default="present"), + ) + + argument_spec.update(AUTH_ARGSPEC) + + module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=True) + + client = Client( + host=module.params.get("controller_host"), + username=module.params.get("controller_username"), + password=module.params.get("controller_password"), + timeout=module.params.get("request_timeout"), + validate_certs=module.params.get("validate_certs"), + ) + + project_endpoint = "projects" + controller = Controller(client, module) + + project_name = module.params.get("name") + new_name = module.params.get("new_name") + description = module.params.get("description") + url = module.params.get("url") + state = module.params.get("state") + credential = module.params.get("credential") + ret = {} + + try: + project_type = controller.get_one_or_many(project_endpoint, name=project_name) + except EDAError as eda_err: + module.fail_json(msg=str(eda_err)) + + if state == "absent": + # If the state was absent we can let the module delete it if needed, the module will handle exiting from this + try: + ret = controller.delete_if_needed(project_type, endpoint=project_endpoint) + except EDAError as eda_err: + module.fail_json(msg=str(eda_err)) + module.exit_json(**ret) + + project_type_params = {} + if description: + project_type_params["description"] = description + if url: + project_type_params["url"] = url + + credential_id = None + if credential: + try: + credential_id = controller.get_one_or_many( + "eda-credentials", name=credential + ) + except EDAError as eda_err: + module.fail_json(msg=str(eda_err)) + + if credential_id is not None: + # this is resolved earlier, so save an API call and don't do it again + # in the loop above + project_type_params["eda_credential_id"] = credential_id["id"] + + if new_name: + project_type_params["name"] = new_name + elif project_type: + project_type_params["name"] = controller.get_item_name(project_type) + else: + project_type_params["name"] = project_name + + # If the state was present and we can let the module build or update the existing project type, + # this will return on its own + try: + ret = controller.create_or_update_if_needed( + project_type, + project_type_params, + endpoint=project_endpoint, + item_type="project type", + ) + except EDAError as eda_err: + module.fail_json(msg=str(eda_err)) + + module.exit_json(**ret) + + +if __name__ == "__main__": + main() diff --git a/plugins/modules/project_info.py b/plugins/modules/project_info.py new file mode 100644 index 00000000..61baa988 --- /dev/null +++ b/plugins/modules/project_info.py @@ -0,0 +1,121 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright: Contributors to the 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 = """ +--- +module: project_info +author: + - Abhijeet Kasurde (@akasurde) +short_description: List projects in EDA Controller +description: + - This module allows user to list project in a EDA controller. +version_added: '2.0.0' +options: + name: + description: + - The name of the project. + - Return information about particular project available on EDA Controller. + type: str +extends_documentation_fragment: + - ansible.eda.eda_controller.auths +""" + +EXAMPLES = """ +- name: List a particular project + ansible.eda.project_info: + controller_host: https://my_eda_host/ + controller_username: admin + controller_password: MySuperSecretPassw0rd + name: "Example" + register: r + +- name: List all projects + ansible.eda.project_info: + controller_host: https://my_eda_host/ + controller_username: admin + controller_password: MySuperSecretPassw0rd + register: r +""" + +RETURN = """ +projects: + description: List of dicts containing information about projects + returned: success + type: list + sample: [ + { + "created_at": "2024-08-12T20:35:28.367702Z", + "description": "", + "eda_credential_id": null, + "git_hash": "417b4dbe9b3472fd64212ef8233b865585e5ade3", + "id": 17, + "import_error": null, + "import_state": "completed", + "modified_at": "2024-08-12T20:35:28.367724Z", + "name": "Sample Example Project", + "organization_id": 1, + "proxy": "", + "scm_branch": "", + "scm_refspec": "", + "scm_type": "git", + "signature_validation_credential_id": null, + "url": "https://github.com/ansible/ansible-ui", + "verify_ssl": true + }, + ] +""" # NOQA + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.ansible.eda.plugins.module_utils.arguments import AUTH_ARGSPEC +from ansible_collections.ansible.eda.plugins.module_utils.client import Client +from ansible_collections.ansible.eda.plugins.module_utils.controller import Controller +from ansible_collections.ansible.eda.plugins.module_utils.errors import EDAError + + +def main(): + argument_spec = dict( + name=dict(), + ) + + argument_spec.update(AUTH_ARGSPEC) + + module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=True) + + client = Client( + host=module.params.get("controller_host"), + username=module.params.get("controller_username"), + password=module.params.get("controller_password"), + timeout=module.params.get("request_timeout"), + validate_certs=module.params.get("validate_certs"), + ) + + project_endpoint = "projects" + controller = Controller(client, module) + + project_name = module.params.get("name") + + ret = {} + + try: + ret = controller.get_one_or_many( + project_endpoint, name=project_name, want_one=False + ) + except EDAError as eda_err: + module.fail_json(msg=str(eda_err)) + + if ret is None: + ret = [] + if not isinstance(ret, list): + ret = [ret] + module.exit_json(projects=ret) + + +if __name__ == "__main__": + main() diff --git a/tests/integration/targets/project/tasks/create.yml b/tests/integration/targets/project/tasks/create.yml new file mode 100644 index 00000000..32d74ba5 --- /dev/null +++ b/tests/integration/targets/project/tasks/create.yml @@ -0,0 +1,81 @@ +--- +# Copyright: Contributors to the Ansible project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +- name: Create project in check mode + ansible.eda.project: + controller_host: "{{ controller_host }}" + controller_username: "{{ controller_user }}" + validate_certs: "{{ validate_certs }}" + name: "{{ project_name }}" + url: "{{ url }}" + description: "Example project description" + state: present + check_mode: true + register: r + +- name: Check project creation in check mode + assert: + that: + - r.changed + +- name: Create project + ansible.eda.project: + controller_host: "{{ controller_host }}" + controller_username: "{{ controller_user }}" + validate_certs: "{{ validate_certs }}" + name: "{{ project_name }}" + url: "{{ url }}" + description: "Example project description" + state: present + register: r + +- name: Check project creation + assert: + that: + - r.changed + +- name: Create project again + ansible.eda.project: + controller_host: "{{ controller_host }}" + controller_username: "{{ controller_user }}" + validate_certs: "{{ validate_certs }}" + name: "{{ project_name }}" + url: "{{ url }}" + description: "Example project description" + state: present + register: r + +- name: Check project is not created again + assert: + that: + - not r.changed + +- name: Delete project in check mode + ansible.eda.project: + controller_host: "{{ controller_host }}" + controller_username: "{{ controller_user }}" + validate_certs: "{{ validate_certs }}" + name: "{{ project_name }}" + state: absent + check_mode: true + register: r + +- name: Check if delete project in check mode + assert: + that: + - r.changed + +- name: Delete project + ansible.eda.project: + controller_host: "{{ controller_host }}" + controller_username: "{{ controller_user }}" + validate_certs: "{{ validate_certs }}" + name: "{{ project_name }}" + state: absent + register: r + +- name: Check if delete project + assert: + that: + - r.changed diff --git a/tests/integration/targets/project/tasks/delete.yml b/tests/integration/targets/project/tasks/delete.yml new file mode 100644 index 00000000..9656269c --- /dev/null +++ b/tests/integration/targets/project/tasks/delete.yml @@ -0,0 +1,45 @@ +--- +# Copyright: Contributors to the Ansible project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +- name: Delete operation without required name parameter + ansible.eda.project: + controller_host: "{{ controller_host }}" + state: absent + ignore_errors: true + register: r + +- name: Check if project name is required + assert: + that: + - r.failed + - "'missing required arguments: name' in r.msg" + +- name: Delete non-existing project in check mode + ansible.eda.project: + controller_host: "{{ controller_host }}" + controller_username: "{{ controller_user }}" + validate_certs: "{{ validate_certs }}" + name: "{{ project_name }}" + state: absent + check_mode: true + register: r + +- name: Check if delete non-existing project in check mode + assert: + that: + - not r.changed + +- name: Delete non-existing project + ansible.eda.project: + controller_host: "{{ controller_host }}" + controller_username: "{{ controller_user }}" + validate_certs: "{{ validate_certs }}" + name: "{{ project_name }}_ee" + state: absent + register: r + +- name: Check if delete non-existing project + assert: + that: + - not r.changed diff --git a/tests/integration/targets/project/tasks/main.yml b/tests/integration/targets/project/tasks/main.yml new file mode 100644 index 00000000..dfbf38bb --- /dev/null +++ b/tests/integration/targets/project/tasks/main.yml @@ -0,0 +1,35 @@ +--- +# Copyright: Contributors to the Ansible project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +- block: + - name: Generate a random_string for the test + set_fact: + random_string: "{{ lookup('password', '/dev/null chars=ascii_letters length=16') }}" + when: random_string is not defined + + - name: Generate a ID for the test + set_fact: + test_id: "{{ random_string | to_uuid }}" + when: test_id is not defined + + - name: Define variables for credential and decision environment + set_fact: + project_name: "test_project_{{ test_id }}" + url: "https://example.com/ansible/eda-server" + + - include_tasks: create.yml + - include_tasks: delete.yml + - include_tasks: update.yml + always: + - name: Clean up - project + ansible.eda.project: + controller_host: "{{ controller_host }}" + controller_username: "{{ controller_user }}" + validate_certs: "{{ validate_certs }}" + name: "{{ item }}" + state: absent + loop: + - "{{ project_name }}" + - "{{ project_name }}_new" + ignore_errors: true diff --git a/tests/integration/targets/project/tasks/update.yml b/tests/integration/targets/project/tasks/update.yml new file mode 100644 index 00000000..b6d8117c --- /dev/null +++ b/tests/integration/targets/project/tasks/update.yml @@ -0,0 +1,67 @@ +--- +# Copyright: Contributors to the Ansible project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +- name: Create project + ansible.eda.project: + controller_host: "{{ controller_host }}" + controller_username: "{{ controller_user }}" + validate_certs: "{{ validate_certs }}" + name: "{{ project_name }}" + url: "{{ url }}" + description: "Example project description" + state: present + register: r + +- name: Check project creation + assert: + that: + - r.changed + +- name: Update project name + ansible.eda.project: + controller_host: "{{ controller_host }}" + controller_username: "{{ controller_user }}" + validate_certs: "{{ validate_certs }}" + name: "{{ project_name }}" + url: "{{ url }}" + new_name: "{{ project_name }}_new" + description: "Example project description" + state: present + register: r + +- name: Check project update + assert: + that: + - r.changed + +- name: Update project again + ansible.eda.project: + controller_host: "{{ controller_host }}" + controller_username: "{{ controller_user }}" + validate_certs: "{{ validate_certs }}" + name: "{{ project_name }}_new" + new_name: "{{ project_name }}_new" + description: "Example project description" + url: "https://example.com/project1" + state: present + register: r + +- name: Check project is not updated again + assert: + that: + - not r.changed + +- name: Delete updated project + ansible.eda.project: + controller_host: "{{ controller_host }}" + controller_username: "{{ controller_user }}" + validate_certs: "{{ validate_certs }}" + name: "{{ project_name }}_new" + state: absent + register: r + +- name: Check if delete project + assert: + that: + - r.changed diff --git a/tests/integration/targets/project_info/tasks/create.yml b/tests/integration/targets/project_info/tasks/create.yml new file mode 100644 index 00000000..eb92b83e --- /dev/null +++ b/tests/integration/targets/project_info/tasks/create.yml @@ -0,0 +1,19 @@ +--- +# Copyright: Contributors to the Ansible project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +- name: Create a project + ansible.eda.project: + controller_host: "{{ controller_host }}" + controller_username: "{{ controller_user }}" + validate_certs: "{{ validate_certs }}" + name: Example + description: "Example project description" + url: "https://example.com/project1" + state: present + register: r + +- name: Check project creation + assert: + that: + - r.changed diff --git a/tests/integration/targets/project_info/tasks/main.yml b/tests/integration/targets/project_info/tasks/main.yml new file mode 100644 index 00000000..53efce56 --- /dev/null +++ b/tests/integration/targets/project_info/tasks/main.yml @@ -0,0 +1,70 @@ +--- +# Copyright: Contributors to the Ansible project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +- block: + - include_tasks: create.yml + - name: List all projects in the given EDA Controller in check mode + ansible.eda.project_info: + controller_host: "{{ controller_host }}" + controller_username: "{{ controller_user }}" + validate_certs: "{{ validate_certs }}" + check_mode: true + register: r + + - name: Check if all the projects are returned in check mode + assert: + that: + - "'projects' in r" + + - name: List all projects in the given EDA Controller + ansible.eda.project_info: + controller_host: "{{ controller_host }}" + controller_username: "{{ controller_user }}" + validate_certs: "{{ validate_certs }}" + register: r + + - name: Check if all the projects are returned + assert: + that: + - "'projects' in r" + + - name: List a particular project in the given EDA Controller + ansible.eda.project_info: + controller_host: "{{ controller_host }}" + controller_username: "{{ controller_user }}" + validate_certs: "{{ validate_certs }}" + name: "Example" + register: r + + - name: Check if the project is returned + assert: + that: + - "'projects' in r" + - "'Example' in r['projects'][0]['name']" + + - name: List a non-existing particular project in the given EDA Controller + ansible.eda.project_info: + controller_host: "{{ controller_host }}" + controller_username: "{{ controller_user }}" + validate_certs: "{{ validate_certs }}" + name: "Example2" + register: r + + - name: Check if all the projects are returned + assert: + that: + - "'projects' in r" + - "r['projects'] == []" + + always: + - name: Clean up - project + ansible.eda.project: + controller_host: "{{ controller_host }}" + controller_username: "{{ controller_user }}" + validate_certs: "{{ validate_certs }}" + name: "{{ item }}" + state: absent + loop: + - Example + ignore_errors: true