Skip to content

Commit

Permalink
MVP for the clickhouse module (#56)
Browse files Browse the repository at this point in the history
* mvp integration tests

* init clickhouse mvp role

* Update plugins/modules/clickhouse_role.py

Co-authored-by: Andrew Klychkov <[email protected]>

* Update plugins/modules/clickhouse_role.py

Co-authored-by: Andrew Klychkov <[email protected]>

* clickhouse mvp role

* basic integration tests

Co-authored-by: Andrew Klychkov <[email protected]>

* correct module name

* consistent spacing between tests

Co-authored-by: Andrew Klychkov <[email protected]>

* update role integration tests

---------

Co-authored-by: Andrew Klychkov <[email protected]>
  • Loading branch information
oraNod and Andersson007 authored Apr 9, 2024
1 parent a9a9d25 commit abc9593
Show file tree
Hide file tree
Showing 5 changed files with 310 additions and 0 deletions.
175 changes: 175 additions & 0 deletions plugins/modules/clickhouse_role.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-

# Copyright: (c) 2024, Don Naro (@oranod) <[email protected]>
# 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 = r"""
---
module: clickhouse_role
short_description: Creates or removes a ClickHouse role.
description:
- Creates or removes a ClickHouse role.
attributes:
check_mode:
description: Supports check_mode.
support: full
version_added: '0.5.0'
author:
- Don Naro (@oranod)
extends_documentation_fragment:
- community.clickhouse.client_inst_opts
options:
state:
description:
- Role state.
- C(present) creates the role if it does not exist.
- C(absent) deletes the role if it exists.
type: str
choices: ['present', 'absent']
default: 'present'
name:
description:
- Role name to add or remove.
type: str
required: true
"""

EXAMPLES = r"""
- name: Create role
community.clickhouse.clickhouse_role:
name: test_role
state: present
- name: Remove role
community.clickhouse.clickhouse_role:
name: test_role
state: absent
"""

RETURN = r"""
executed_statements:
description:
- Data-modifying executed statements.
returned: on success
type: list
sample: ['CREATE ROLE test_role']
"""

from ansible.module_utils.basic import AnsibleModule

from ansible_collections.community.clickhouse.plugins.module_utils.clickhouse import (
check_clickhouse_driver,
client_common_argument_spec,
connect_to_db_via_client,
execute_query,
get_main_conn_kwargs,
)

executed_statements = []


class ClickHouseRole:
def __init__(self, module, client, name):
self.module = module
self.client = client
self.name = name
self.exists = self.check_exists()

def check_exists(self):
query = "SELECT 1 FROM system.roles WHERE name = '%s' LIMIT 1" % self.name
result = execute_query(self.module, self.client, query)
return bool(result)

def create(self):
if not self.exists:
query = "CREATE ROLE %s" % self.name
executed_statements.append(query)

if not self.module.check_mode:
execute_query(self.module, self.client, query)

self.exists = True
return True
else:
return False

def drop(self):
if self.exists:
query = "DROP ROLE %s" % self.name
executed_statements.append(query)

if not self.module.check_mode:
execute_query(self.module, self.client, query)

self.exists = False
return True
else:
return False


def main():
# Set up arguments.
# If there are common arguments shared across several modules,
# create the common_argument_spec() function under plugins/module_utils/*
# and invoke here to return a dict with those arguments
argument_spec = client_common_argument_spec()
argument_spec.update(
state=dict(type="str", choices=["present", "absent"], default="present"),
name=dict(type="str", required=True),
)

# Instantiate an object of module class
module = AnsibleModule(
argument_spec=argument_spec,
supports_check_mode=True,
)

# Assign passed options to variables
client_kwargs = module.params["client_kwargs"]
# The reason why these arguments are separate from client_kwargs
# is that we need to protect some sensitive data like passwords passed
# to the module from logging (see the arguments above with no_log=True);
# Such data must be passed as module arguments (not nested deep in values).
main_conn_kwargs = get_main_conn_kwargs(module)
state = module.params["state"]
name = module.params["name"]

# Will fail if no driver informing the user
check_clickhouse_driver(module)

# Connect to DB
client = connect_to_db_via_client(module, main_conn_kwargs, client_kwargs)

# Do the job
changed = False
role = ClickHouseRole(module, client, name)

if state == "present":
if not role.exists:
changed = role.create()
else:
# If state is absent
if role.exists:
changed = role.drop()

# Close connection
client.disconnect_connection()

# Users will get this in JSON output after execution
module.exit_json(changed=changed, executed_statements=executed_statements)


if __name__ == "__main__":
main()
2 changes: 2 additions & 0 deletions tests/integration/inventory
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[testgroup]
testhost ansible_connection="local" ansible_pipelining="yes" ansible_python_interpreter="/usr/bin/python"
2 changes: 2 additions & 0 deletions tests/integration/targets/clickhouse_role/meta/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
dependencies:
- setup_clickhouse
124 changes: 124 additions & 0 deletions tests/integration/targets/clickhouse_role/tasks/initial.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
####################################################################
# WARNING: These are designed specifically for Ansible tests #
# and should not be used as examples of how to write Ansible roles #
####################################################################

# Test 1
- name: Test 1 - Create a test role in check mode
register: result
check_mode: true
community.clickhouse.clickhouse_role:
state: present
name: test_role

- name: Test 1 - Check the return values
ansible.builtin.assert:
that:
- result is changed
- result.executed_statements == ['CREATE ROLE test_role']

- name: Test 1 - Check the role was created
register: result
community.clickhouse.clickhouse_client:
execute: "SELECT name FROM system.roles WHERE name = 'test_role'"

- name: Test 1 - Verify that the role is not present
ansible.builtin.assert:
that:
- result.result == []

# Test 2
- name: Test 2 - Create a test role in real mode
register: result
community.clickhouse.clickhouse_role:
state: present
name: test_role

- name: Test 2 - Check the return values
ansible.builtin.assert:
that:
- result is changed
- result.executed_statements == ['CREATE ROLE test_role']

- name: Test 2 - Check the role was created
register: result
community.clickhouse.clickhouse_client:
execute: "SELECT name FROM system.roles WHERE name = 'test_role'"

- name: Test 2 - Verify that the role is present
ansible.builtin.assert:
that:
- result.result == [["test_role"]]

# Test 3
- name: Test 3 - Check idempotency by creating the test role again
register: result
community.clickhouse.clickhouse_role:
state: present
name: test_role

- name: Test 3 - Check the return values
ansible.builtin.assert:
that:
- result is not changed
- result.executed_statements == []

# Test 4
- name: Test 4 - Remove the test role in check mode
register: result
check_mode: true
community.clickhouse.clickhouse_role:
state: absent
name: test_role

- name: Test 4 - Check the return values
ansible.builtin.assert:
that:
- result is changed
- result.executed_statements == ['DROP ROLE test_role']

- name: Test 4 - Check the role was removed in check mode
register: result
community.clickhouse.clickhouse_client:
execute: "SELECT name FROM system.roles WHERE name = 'test_role'"

- name: Test 4 - Verify that the role is still present
ansible.builtin.assert:
that:
- result.result == [["test_role"]]

# Test 5
- name: Test 5 - Remove the test role in real mode
register: result
community.clickhouse.clickhouse_role:
state: absent
name: test_role

- name: Test 5 - Check the return values
ansible.builtin.assert:
that:
- result is changed
- result.executed_statements == ['DROP ROLE test_role']

- name: Test 5 - Check the role was removed in real mode
register: result
community.clickhouse.clickhouse_client:
execute: "SELECT name FROM system.roles WHERE name = 'test_role'"

- name: Test 5 - Verify that the role is absent
ansible.builtin.assert:
that:
- result.result == []

# Test 6
- name: Test 6 - Try to remove role in real mode again to check idempotency
register: result
community.clickhouse.clickhouse_role:
state: absent
name: test_role

- name: Test 6 - Check return values
ansible.builtin.assert:
that:
- result is not changed
- result.executed_statements == []
7 changes: 7 additions & 0 deletions tests/integration/targets/clickhouse_role/tasks/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
####################################################################
# WARNING: These are designed specifically for Ansible tests #
# and should not be used as examples of how to write Ansible roles #
####################################################################

# Initial CI tests of clickhouse_db module
- import_tasks: initial.yml

0 comments on commit abc9593

Please sign in to comment.