Skip to content

Commit

Permalink
Refactoring the code and adding units tests (#30)
Browse files Browse the repository at this point in the history
* Refactoring the code and adding units tests

* Fixing the python version for units tests
  • Loading branch information
aleksvagachev authored Feb 13, 2024
1 parent 864ae83 commit d9c0343
Show file tree
Hide file tree
Showing 9 changed files with 221 additions and 232 deletions.
7 changes: 5 additions & 2 deletions .github/workflows/ansible-test-plugins.yml
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,12 @@ jobs:
- name: Install ansible-base (${{ matrix.ansible }})
run: pip install https://github.com/ansible/ansible/archive/${{ matrix.ansible }}.tar.gz --disable-pip-version-check

- name: Instal clickhouse-driver
run: pip install clickhouse-driver

# Run the unit tests
- name: Run unit test
run: ansible-test units -v --color --docker --coverage
run: ansible-test units -v --color --python 3.10 --docker --coverage
working-directory: ./ansible_collections/community/clickhouse

# ansible-test support producing code coverage date
Expand All @@ -91,7 +94,7 @@ jobs:
fail_ci_if_error: false

integration:
name: "Integration (Python: ${{ matrix.python }}, Ansible: ${{ matrix.ansible }}"
name: "Integration (Python: ${{ matrix.python }}, Ansible: ${{ matrix.ansible }}, ClickHouse: ${{ matrix.clickhouse }}"
runs-on: ubuntu-latest
strategy:
fail-fast: false
Expand Down
110 changes: 110 additions & 0 deletions plugins/module_utils/clickhouse.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
# This code is part of Ansible, but is an independent component.
# This particular file snippet, and this file snippet only, is BSD licensed.
# Modules you write using this snippet, which is embedded dynamically by Ansible
# still belong to the author of the module, and may assign their own license
# to the complete work.
#
# Simplified BSD License (see simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)

from __future__ import absolute_import, division, print_function

__metaclass__ = type

from ansible.module_utils.basic import missing_required_lib
from ansible.module_utils._text import to_native

Client = None
try:
from clickhouse_driver import Client
from clickhouse_driver import __version__ as driver_version
HAS_DB_DRIVER = True
except ImportError:
HAS_DB_DRIVER = False

PRIV_ERR_CODE = 497


def client_common_argument_spec():
"""
Return a dictionary with connection options.
The options are commonly used by many modules.
"""
return dict(
login_host=dict(type='str', default='localhost'),
login_port=dict(type='int', default=None),
login_db=dict(type='str', default=None),
login_user=dict(type='str', default=None),
login_password=dict(type='str', default=None, no_log=True),
client_kwargs=dict(type='dict', default={}),
)


def get_main_conn_kwargs(module):
"""Retrieves main connection arguments values and translates
them into corresponding clickhouse_driver.Client() arguments.
Returns a dictionary of arguments with values to pass to Client().
"""
main_conn_kwargs = {}
main_conn_kwargs['host'] = module.params['login_host'] # Has a default value
if module.params['login_port']:
main_conn_kwargs['port'] = module.params['login_port']
if module.params['login_db']:
main_conn_kwargs['database'] = module.params['login_db']
if module.params['login_user']:
main_conn_kwargs['user'] = module.params['login_user']
if module.params['login_password']:
main_conn_kwargs['password'] = module.params['login_password']
return main_conn_kwargs


def check_clickhouse_driver(module):
"""Checks if the driver is present.
Informs user if no driver and fails.
"""
if not HAS_DB_DRIVER:
module.fail_json(msg=missing_required_lib('clickhouse_driver'))


def version_clickhouse_driver():
"""
Returns the current version of clickhouse_driver.
"""
return driver_version


def connect_to_db_via_client(module, main_conn_kwargs, client_kwargs):
"""Connects to DB using the Client() class.
Returns Client() object.
"""
try:
# Merge the kwargs as Python 2 would through an error
# when unpaking them separately to Client()
client_kwargs.update(main_conn_kwargs)
client = Client(**client_kwargs)
except Exception as e:
module.fail_json(msg="Failed to connect to database: %s" % to_native(e))

return client


def execute_query(module, client, query, execute_kwargs=None):
"""Execute query.
Returns rows returned in response.
"""
# Some modules do not pass this argument
if execute_kwargs is None:
execute_kwargs = {}

try:
result = client.execute(query, **execute_kwargs)
except Exception as e:
if "Not enough privileges" in to_native(e):
return PRIV_ERR_CODE
module.fail_json(msg="Failed to execute query: %s" % to_native(e))

return result
38 changes: 0 additions & 38 deletions plugins/module_utils/connect.py

This file was deleted.

64 changes: 6 additions & 58 deletions plugins/modules/clickhouse_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,18 +121,14 @@
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils._text import to_native

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

Client = None
try:
from clickhouse_driver import Client
HAS_DB_DRIVER = True
except ImportError:
HAS_DB_DRIVER = False


def is_uuid(value):
"""Checks if the value is valid UUID.
Expand Down Expand Up @@ -179,25 +175,6 @@ def replace_val_in_tuple(tup, idx, val):
return tuple(tmp)


def get_main_conn_kwargs(module):
"""Retrieves main connection arguments values and translates
them into corresponding clickhouse_driver.Client() arguments.
Returns a dictionary of arguments with values to pass to Client().
"""
main_conn_kwargs = {}
main_conn_kwargs['host'] = module.params['login_host'] # Has a default value
if module.params['login_port']:
main_conn_kwargs['port'] = module.params['login_port']
if module.params['login_db']:
main_conn_kwargs['database'] = module.params['login_db']
if module.params['login_user']:
main_conn_kwargs['user'] = module.params['login_user']
if module.params['login_password']:
main_conn_kwargs['password'] = module.params['login_password']
return main_conn_kwargs


def get_query_statistics(module, client):
"""Retrieve query statistics from the Client() object.
Expand Down Expand Up @@ -229,19 +206,6 @@ def get_query_statistics(module, client):
return statistics


def execute_query(module, client, query, execute_kwargs):
"""Execute query.
Returns rows returned in response.
"""
try:
result = client.execute(query, **execute_kwargs)
except Exception as e:
module.fail_json(msg="Failed to execute query: %s" % to_native(e))

return result


def get_substituted_query(module, client, query, execute_kwargs):
"""Substitute params in a query. If no params, just return the query as is.
Expand All @@ -262,22 +226,6 @@ def get_substituted_query(module, client, query, execute_kwargs):
return substituted_query


def connect_to_db_via_client(module, main_conn_kwargs, client_kwargs):
"""Connects to DB using the Client() class.
Returns Client() object.
"""
try:
# Merge the kwargs as Python 2 would through an error
# when unpaking them separately to Client()
client_kwargs.update(main_conn_kwargs)
client = Client(**client_kwargs)
except Exception as e:
module.fail_json(msg="Failed to connect to database: %s" % to_native(e))

return client


def main():
# Set up arguments.
# If there are common arguments shared across several modules,
Expand Down Expand Up @@ -306,7 +254,7 @@ def main():
main_conn_kwargs = get_main_conn_kwargs(module)

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

# Connect to DB
client = connect_to_db_via_client(module, main_conn_kwargs, client_kwargs)
Expand Down
68 changes: 6 additions & 62 deletions plugins/modules/clickhouse_db.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,75 +81,19 @@
'''

from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils._text import to_native

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

Client = None
try:
from clickhouse_driver import Client
HAS_DB_DRIVER = True
except ImportError:
HAS_DB_DRIVER = False

executed_statements = []


def get_main_conn_kwargs(module):
"""Retrieves main connection arguments values and translates
them into corresponding clickhouse_driver.Client() arguments.
Returns a dictionary of arguments with values to pass to Client().
"""
main_conn_kwargs = {}
main_conn_kwargs['host'] = module.params['login_host'] # Has a default value
if module.params['login_port']:
main_conn_kwargs['port'] = module.params['login_port']
if module.params['login_db']:
main_conn_kwargs['database'] = module.params['login_db']
if module.params['login_user']:
main_conn_kwargs['user'] = module.params['login_user']
if module.params['login_password']:
main_conn_kwargs['password'] = module.params['login_password']
return main_conn_kwargs


def execute_query(module, client, query, execute_kwargs=None):
"""Execute query.
Returns rows returned in response.
"""
# Some modules do not pass this argument
if execute_kwargs is None:
execute_kwargs = {}

try:
result = client.execute(query, **execute_kwargs)
except Exception as e:
module.fail_json(msg="Failed to execute query: %s" % to_native(e))

return result


def connect_to_db_via_client(module, main_conn_kwargs, client_kwargs):
"""Connects to DB using the Client() class.
Returns Client() object.
"""
try:
# Merge the kwargs as Python 2 would through an error
# when unpaking them separately to Client()
client_kwargs.update(main_conn_kwargs)
client = Client(**client_kwargs)
except Exception as e:
module.fail_json(msg="Failed to connect to database: %s" % to_native(e))

return client


class ClickHouseDB():
def __init__(self, module, client, name):
self.module = module
Expand Down Expand Up @@ -238,7 +182,7 @@ def main():
engine = module.params['engine']

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

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

0 comments on commit d9c0343

Please sign in to comment.