From e0b99f1168fc4fdcaa905fa1e327982b6cdd81b5 Mon Sep 17 00:00:00 2001 From: Abhijeet Kasurde Date: Fri, 16 Aug 2024 07:15:50 -0700 Subject: [PATCH] Add controller_token module (#256) * Added module to add controller token * tests Signed-off-by: Abhijeet Kasurde Co-authored-by: Sorin Sbarnea --- plugins/modules/controller_token.py | 154 ++++++++++++++++++ .../targets/controller_token/tasks/create.yml | 57 +++++++ .../targets/controller_token/tasks/main.yml | 31 ++++ 3 files changed, 242 insertions(+) create mode 100644 plugins/modules/controller_token.py create mode 100644 tests/integration/targets/controller_token/tasks/create.yml create mode 100644 tests/integration/targets/controller_token/tasks/main.yml diff --git a/plugins/modules/controller_token.py b/plugins/modules/controller_token.py new file mode 100644 index 00000000..2b185347 --- /dev/null +++ b/plugins/modules/controller_token.py @@ -0,0 +1,154 @@ +#!/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: controller_token +author: + - Abhijeet Kasurde (@akasurde) +short_description: Manage AWX tokens in EDA controller +description: + - This module allows the user to manage AWX tokens in a EDA controller. +version_added: '2.0.0' +options: + name: + description: + - The name of the AWX token. + type: str + required: true + description: + description: + - The description of the project. + - Required when O(state=present). + type: str + token: + description: + - The AWX token value. + - Required when O(state=present). + type: str + state: + description: + - Desired state of the resource. + default: "present" + choices: ["present", "absent"] + type: str +notes: + - Controller Token API does not support PATCH method, due to this reason the module deletes + and re-creates the token when existing controller token is found. + This will cause module to report changed, every time update is called. +extends_documentation_fragment: + - ansible.eda.eda_controller.auths +""" + +EXAMPLES = """ +- name: Create AWX token + ansible.eda.controller_token: + controller_host: https://my_eda_host/ + controller_username: admin + controller_password: MySuperSecretPassw0rd + name: "Example AWX token" + description: "Example AWX token description" + token: "" + state: present + no_log: true + +- name: Delete AWX token + ansible.eda.controller_token: + controller_host: https://my_eda_host/ + controller_username: admin + controller_password: MySuperSecretPassw0rd + name: "Example AWX token" + state: absent +""" + + +RETURN = """ +id: + description: ID of the managed AWX token. + returned: when state is 'present' and successful + type: str + sample: "123" +""" + +from ansible.module_utils.basic import AnsibleModule + +from ..module_utils.arguments import AUTH_ARGSPEC +from ..module_utils.client import Client +from ..module_utils.controller import Controller +from ..module_utils.errors import EDAError + + +def main(): + argument_spec = dict( + name=dict(required=True), + description=dict(), + token=dict(no_log=True), + state=dict(choices=["present", "absent"], default="present"), + ) + + required_if = [("state", "present", ("name", "token"))] + + argument_spec.update(AUTH_ARGSPEC) + + module = AnsibleModule( + argument_spec=argument_spec, required_if=required_if, 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"), + ) + + token_endpoint = "/users/me/awx-tokens/" + controller = Controller(client, module) + + token_name = module.params.get("name") + description = module.params.get("description") + state = module.params.get("state") + token = module.params.get("token") + ret = {} + + try: + token_type = controller.get_one_or_many(token_endpoint, name=token_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(token_type, endpoint=token_endpoint) + except EDAError as eda_err: + module.fail_json(msg=str(eda_err)) + module.exit_json(**ret) + + # Method 'PATCH' is not allowed, so delete and re-create the token + if token_type: + controller.delete_if_needed(token_type, endpoint=token_endpoint) + + token_param = { + "name": token_name, + "description": description, + "token": token, + } + # Attempt to create the new AWX token + ret = controller.create_if_needed( + existing_item=None, + new_item=token_param, + endpoint=token_endpoint, + item_type="controller token type", + ) + module.exit_json(**ret) + + +if __name__ == "__main__": + main() diff --git a/tests/integration/targets/controller_token/tasks/create.yml b/tests/integration/targets/controller_token/tasks/create.yml new file mode 100644 index 00000000..ac607806 --- /dev/null +++ b/tests/integration/targets/controller_token/tasks/create.yml @@ -0,0 +1,57 @@ +--- +# 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 controller token in check mode + ansible.eda.controller_token: + controller_host: "{{ controller_host }}" + controller_username: "{{ controller_user }}" + validate_certs: "{{ validate_certs }}" + name: "{{ controller_token_name }}" + description: "Example controller token description" + token: "TOKEN_VALUE" + state: present + check_mode: true + no_log: true + register: r + +- name: Check controller token creation in check mode + assert: + that: + - r.changed + +- name: Create controller token + ansible.eda.controller_token: + controller_host: "{{ controller_host }}" + controller_username: "{{ controller_user }}" + validate_certs: "{{ validate_certs }}" + name: "{{ controller_token_name }}" + description: "Example controller token description" + token: "TOKEN_VALUE" + state: present + no_log: true + register: r + +- name: Check controller token creation + assert: + that: + - r.changed + - "'id' in r" + +- name: Create controller token again + ansible.eda.controller_token: + controller_host: "{{ controller_host }}" + controller_username: "{{ controller_user }}" + validate_certs: "{{ validate_certs }}" + name: "{{ controller_token_name }}" + description: "Example controller token description" + token: "TOKEN_VALUE" + state: present + no_log: true + register: r + +- name: Check controller token creation again + assert: + that: + - r.changed + - "'id' in r" diff --git a/tests/integration/targets/controller_token/tasks/main.yml b/tests/integration/targets/controller_token/tasks/main.yml new file mode 100644 index 00000000..512dd5dc --- /dev/null +++ b/tests/integration/targets/controller_token/tasks/main.yml @@ -0,0 +1,31 @@ +--- +# 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 controller token + set_fact: + controller_token_name: "controller_token_{{ test_id }}" + + - include_tasks: create.yml + always: + - name: Clean up - controller token + ansible.eda.controller_token: + controller_host: "{{ controller_host }}" + controller_username: "{{ controller_user }}" + validate_certs: "{{ validate_certs }}" + name: "{{ item }}" + state: absent + loop: + - "{{ controller_token_name }}" + ignore_errors: true