diff --git a/changelogs/fragments/4207-add-redis-tls-support.yml b/changelogs/fragments/4207-add-redis-tls-support.yml new file mode 100644 index 0000000000..1f8cd145f2 --- /dev/null +++ b/changelogs/fragments/4207-add-redis-tls-support.yml @@ -0,0 +1,2 @@ +minor_changes: + - redis - add authentication parameters ``login_user``, ``tls``, ``validate_certs``, and ``ca_certs`` (https://github.com/ansible-collections/community.general/pull/4207). diff --git a/plugins/module_utils/redis.py b/plugins/module_utils/redis.py index 9d55aecad0..de5c8c7fc3 100644 --- a/plugins/module_utils/redis.py +++ b/plugins/module_utils/redis.py @@ -27,20 +27,20 @@ except ImportError: HAS_CERTIFI_PACKAGE = False -def fail_imports(module): +def fail_imports(module, needs_certifi=True): errors = [] traceback = [] if not HAS_REDIS_PACKAGE: errors.append(missing_required_lib('redis')) traceback.append(REDIS_IMP_ERR) - if not HAS_CERTIFI_PACKAGE: + if not HAS_CERTIFI_PACKAGE and needs_certifi: errors.append(missing_required_lib('certifi')) traceback.append(CERTIFI_IMPORT_ERROR) if errors: module.fail_json(errors=errors, traceback='\n'.join(traceback)) -def redis_auth_argument_spec(): +def redis_auth_argument_spec(tls_default=True): return dict( login_host=dict(type='str', default='localhost',), @@ -50,7 +50,7 @@ def redis_auth_argument_spec(): ), login_port=dict(type='int', default=6379), tls=dict(type='bool', - default=True), + default=tls_default), validate_certs=dict(type='bool', default=True ), @@ -58,6 +58,30 @@ def redis_auth_argument_spec(): ) +def redis_auth_params(module): + login_host = module.params['login_host'] + login_user = module.params['login_user'] + login_password = module.params['login_password'] + login_port = module.params['login_port'] + tls = module.params['tls'] + validate_certs = 'required' if module.params['validate_certs'] else None + ca_certs = module.params['ca_certs'] + if tls and ca_certs is None: + ca_certs = str(certifi.where()) + if tuple(map(int, redis_version.split('.'))) < (3, 4, 0) and login_user is not None: + module.fail_json( + msg='The option `username` in only supported with redis >= 3.4.0.') + params = {'host': login_host, + 'port': login_port, + 'password': login_password, + 'ssl_ca_certs': ca_certs, + 'ssl_cert_reqs': validate_certs, + 'ssl': tls} + if login_user is not None: + params['username'] = login_user + return params + + class RedisAnsible(object): '''Base class for Redis module''' @@ -66,28 +90,8 @@ class RedisAnsible(object): self.connection = self._connect() def _connect(self): - login_host = self.module.params['login_host'] - login_user = self.module.params['login_user'] - login_password = self.module.params['login_password'] - login_port = self.module.params['login_port'] - tls = self.module.params['tls'] - validate_certs = 'required' if self.module.params['validate_certs'] else None - ca_certs = self.module.params['ca_certs'] - if tls and ca_certs is None: - ca_certs = str(certifi.where()) - if tuple(map(int, redis_version.split('.'))) < (3, 4, 0) and login_user is not None: - self.module.fail_json( - msg='The option `username` in only supported with redis >= 3.4.0.') - params = {'host': login_host, - 'port': login_port, - 'password': login_password, - 'ssl_ca_certs': ca_certs, - 'ssl_cert_reqs': validate_certs, - 'ssl': tls} - if login_user is not None: - params['username'] = login_user try: - return Redis(**params) + return Redis(**redis_auth_params(self.module)) except Exception as e: self.module.fail_json(msg='{0}'.format(str(e))) return None diff --git a/plugins/modules/database/misc/redis.py b/plugins/modules/database/misc/redis.py index 960b072fea..13a1f5060b 100644 --- a/plugins/modules/database/misc/redis.py +++ b/plugins/modules/database/misc/redis.py @@ -13,6 +13,8 @@ module: redis short_description: Various redis commands, replica and flush description: - Unified utility to interact with redis instances. +extends_documentation_fragment: + - community.general.redis options: command: description: @@ -22,20 +24,15 @@ options: - C(replica) sets a redis instance in replica or master mode. (C(slave) is an alias for C(replica).) choices: [ config, flush, replica, slave ] type: str - login_password: - description: - - The password used to authenticate with (usually not used) - type: str - login_host: - description: - - The host running the database - default: localhost - type: str - login_port: - description: - - The port to connect to - default: 6379 - type: int + tls: + default: false + version_added: 4.6.0 + login_user: + version_added: 4.6.0 + validate_certs: + version_added: 4.6.0 + ca_certs: + version_added: 4.6.0 master_host: description: - The host of the master instance [replica command] @@ -144,6 +141,8 @@ else: from ansible.module_utils.basic import AnsibleModule, missing_required_lib from ansible.module_utils.common.text.formatters import human_to_bytes from ansible.module_utils.common.text.converters import to_native +from ansible_collections.community.general.plugins.module_utils.redis import ( + fail_imports, redis_auth_argument_spec, redis_auth_params) import re @@ -175,29 +174,28 @@ def flush(client, db=None): # Module execution. def main(): + redis_auth_args = redis_auth_argument_spec(tls_default=False) + module_args = dict( + command=dict(type='str', choices=['config', 'flush', 'replica', 'slave']), + master_host=dict(type='str'), + master_port=dict(type='int'), + replica_mode=dict(type='str', default='replica', choices=['master', 'replica', 'slave'], + aliases=["slave_mode"]), + db=dict(type='int'), + flush_mode=dict(type='str', default='all', choices=['all', 'db']), + name=dict(type='str'), + value=dict(type='str'), + ) + module_args.update(redis_auth_args) module = AnsibleModule( - argument_spec=dict( - command=dict(type='str', choices=['config', 'flush', 'replica', 'slave']), - login_password=dict(type='str', no_log=True), - login_host=dict(type='str', default='localhost'), - login_port=dict(type='int', default=6379), - master_host=dict(type='str'), - master_port=dict(type='int'), - replica_mode=dict(type='str', default='replica', choices=['master', 'replica', 'slave'], aliases=["slave_mode"]), - db=dict(type='int'), - flush_mode=dict(type='str', default='all', choices=['all', 'db']), - name=dict(type='str'), - value=dict(type='str') - ), + argument_spec=module_args, supports_check_mode=True, ) - if not redis_found: - module.fail_json(msg=missing_required_lib('redis'), exception=REDIS_IMP_ERR) + fail_imports(module, module.params['tls']) + + redis_params = redis_auth_params(module) - login_password = module.params['login_password'] - login_host = module.params['login_host'] - login_port = module.params['login_port'] command = module.params['command'] if command == "slave": command = "replica" @@ -219,7 +217,7 @@ def main(): module.fail_json(msg='In replica mode master port must be provided') # Connect and check - r = redis.StrictRedis(host=login_host, port=login_port, password=login_password) + r = redis.StrictRedis(**redis_params) try: r.ping() except Exception as e: @@ -270,7 +268,7 @@ def main(): module.fail_json(msg="In db mode the db number must be provided") # Connect and check - r = redis.StrictRedis(host=login_host, port=login_port, password=login_password, db=db) + r = redis.StrictRedis(db=db, **redis_params) try: r.ping() except Exception as e: @@ -301,7 +299,7 @@ def main(): except ValueError: value = module.params['value'] - r = redis.StrictRedis(host=login_host, port=login_port, password=login_password) + r = redis.StrictRedis(**redis_params) try: r.ping()