1
0
Fork 0
mirror of https://github.com/ansible-collections/community.general.git synced 2024-09-14 20:13:21 +02:00

cache/redis: feature redis sentinel (#1055) (#1130)

* FEATURE: cache/redis: add redis sentinel support

* FEATURE: add changelog fragment

* STYLE: fix sanity

* DOCS: update fragment, specify redis cache plugin

* STYLE: spelling

Co-authored-by: Andrew Klychkov <aaklychkov@mail.ru>

* STYLE: spelling

* FIX: import to_native

* FIX: remove kw args to prevent secrets leak

Co-authored-by: Benjamin Pereto <benjamin@sandchaschte.ch>
Co-authored-by: Andrew Klychkov <aaklychkov@mail.ru>
(cherry picked from commit a6c950a44b)

Co-authored-by: Benjamin Pereto <dev@sandchaschte.ch>
This commit is contained in:
patchback[bot] 2020-10-20 10:00:16 +02:00 committed by GitHub
parent bb2ad10eef
commit 5571a0cdf8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 58 additions and 2 deletions

View file

@ -0,0 +1,3 @@
minor_changes:
- redis cache plugin - add redis sentinel functionality to cache plugin
(https://github.com/ansible-collections/community.general/pull/1055).

View file

@ -18,6 +18,7 @@ DOCUMENTATION = '''
- A colon separated string of connection information for Redis.
- The format is C(host:port:db:password), for example C(localhost:6379:0:changeme).
- To use encryption in transit, prefix the connection with C(tls://), as in C(tls://localhost:6379:0:changeme).
- To use redis sentinel, use separator C(;), for example C(localhost:26379;localhost:26379;0:changeme). Requires redis>=2.9.0.
required: True
env:
- name: ANSIBLE_CACHE_PLUGIN_CONNECTION
@ -41,6 +42,14 @@ DOCUMENTATION = '''
- key: fact_caching_redis_keyset_name
section: defaults
version_added: 1.3.0
_sentinel_service_name:
description: The redis sentinel service name (or referenced as cluster name).
env:
- name: ANSIBLE_CACHE_REDIS_SENTINEL
ini:
- key: fact_caching_redis_sentinel
section: defaults
version_added: 1.3.0
_timeout:
default: 86400
description: Expiration timeout in seconds for the cache plugin data. Set to 0 to never expire
@ -57,6 +66,7 @@ import json
from ansible import constants as C
from ansible.errors import AnsibleError
from ansible.module_utils._text import to_native
from ansible.parsing.ajson import AnsibleJSONEncoder, AnsibleJSONDecoder
from ansible.plugins.cache import BaseCacheModule
from ansible.utils.display import Display
@ -78,6 +88,8 @@ class CacheModule(BaseCacheModule):
to expire keys. This mechanism is used or a pattern matched 'scan' for
performance.
"""
_sentinel_service_name = None
def __init__(self, *args, **kwargs):
uri = ''
@ -88,6 +100,7 @@ class CacheModule(BaseCacheModule):
self._timeout = float(self.get_option('_timeout'))
self._prefix = self.get_option('_prefix')
self._keys_set = self.get_option('_keyset_name')
self._sentinel_service_name = self.get_option('_sentinel_service_name')
except KeyError:
display.deprecated('Rather than importing CacheModules directly, '
'use ansible.plugins.loader.cache_loader',
@ -100,13 +113,53 @@ class CacheModule(BaseCacheModule):
self._cache = {}
kw = {}
# tls connection
tlsprefix = 'tls://'
if uri.startswith(tlsprefix):
kw['ssl'] = True
uri = uri[len(tlsprefix):]
connection = uri.split(':')
self._db = StrictRedis(*connection, **kw)
# redis sentinel connection
if self._sentinel_service_name:
self._db = self._get_sentinel_connection(uri, kw)
# normal connection
else:
connection = uri.split(':')
self._db = StrictRedis(*connection, **kw)
display.vv('Redis connection: %s' % self._db)
def _get_sentinel_connection(self, uri, kw):
"""
get sentinel connection details from _uri
"""
try:
from redis.sentinel import Sentinel
except ImportError:
raise AnsibleError("The 'redis' python module (version 2.9.0 or newer) is required to use redis sentinel.")
if ';' not in uri:
raise AnsibleError('_uri does not have sentinel syntax.')
# format: "localhost:26379;localhost2:26379;0:changeme"
connections = uri.split(';')
connection_args = connections.pop(-1)
if len(connection_args) > 0: # hanle if no db nr is given
connection_args = connection_args.split(':')
kw['db'] = connection_args.pop(0)
try:
kw['password'] = connection_args.pop(0)
except IndexError:
pass # password is optional
sentinels = [tuple(shost.split(':')) for shost in connections]
display.vv('\nUsing redis sentinels: %s' % sentinels)
scon = Sentinel(sentinels, **kw)
try:
return scon.master_for(self._sentinel_service_name, socket_timeout=0.2)
except Exception as exc:
raise AnsibleError('Could not connect to redis sentinel: %s' % to_native(exc))
def _make_key(self, key):
return self._prefix + key