Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions redis_sentinel/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# CHANGELOG - Redis Sentinel

## 1.2.0 / 2026-07-31

***Added***:
Comment thread
ian28223 marked this conversation as resolved.

* Add SSL/TLS support (`ssl`, `ssl_certfile`, `ssl_keyfile`, `ssl_ca_certs`, `ssl_cert_reqs`, `ssl_check_hostname`) for connecting to TLS-enabled Sentinel instances ([#3071](https://github.com/DataDog/integrations-extras/pull/3071))
* Add `sentinel_username` for Redis 6+ ACL authentication ([#3071](https://github.com/DataDog/integrations-extras/pull/3071))
* Add `socket_timeout` config option to prevent the check from hanging on unreachable sentinels ([#3071](https://github.com/DataDog/integrations-extras/pull/3071))

## 1.1.1 / 2023-07-26

* [Fixed] Removed logged instance line from check ([#1183](https://github.com/DataDog/integrations-extras/pull/2059))
Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = '1.1.1'
__version__ = '1.2.0'
Comment thread
ian28223 marked this conversation as resolved.
Outdated
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,69 @@ instances:
#
- sentinel_host: localhost

## @param sentinel_port - string - required
## @param sentinel_port - integer - required
## Port to connect to when reaching `sentinel_host`.
#
sentinel_port: 26379

## @param sentinel_password - string - optional
## Password for authenticating to the Sentinel host
## Password for authenticating to the Sentinel host.
#
# sentinel_password: <password>
# sentinel_password: <PASSWORD>

## @param sentinel_username - string - optional
## Username for authenticating to the Sentinel host. Redis 6+ only (ACL support).
#
# sentinel_username: <USERNAME>

## @param masters - list of strings - required
## List of masters name to collect data from
## List of masters name to collect data from.
#
masters:
- <MASTER_NAME_1>
- <MASTER_NAME_2>

## @param socket_timeout - integer - optional - default: 5
## Timeout in seconds for connecting to and reading from the Sentinel host.
#
# socket_timeout: 5

## @param ssl - boolean - optional - default: false
## Enable SSL/TLS encryption for the Sentinel connection.
#
# ssl: false

## @param ssl_keyfile - string - optional
## The path to the client-side private keyfile.
#
# ssl_keyfile: <CERT_KEY_PATH>

## @param ssl_certfile - string - optional
## The path to the client-side certificate file.
#
# ssl_certfile: <CERT_PEM_PATH>

## @param ssl_ca_certs - string - optional
## The path to the CA certs file.
#
# ssl_ca_certs: <CERT_PATH>

## @param ssl_cert_reqs - integer - optional - default: 2
## Specifies whether a certificate is required from the
## other side of the connection, and whether it's validated if provided.
## * 0 for ssl.CERT_NONE (certificates ignored)
## * 1 for ssl.CERT_OPTIONAL (not required, but validated if provided)
## * 2 for ssl.CERT_REQUIRED (required and validated)
#
# ssl_cert_reqs: 2

## @param ssl_check_hostname - boolean - optional - default: true
## Verify the server's SSL certificate hostname matches the connection target.
## Set to false if your certificate does not match the connection hostname
## (for example, if connecting by IP).
#
# ssl_check_hostname: true

## @param tags - list of key:value elements - optional
## List of tags to attach to every metric, event and service check emitted by this integration.
##
Expand Down
40 changes: 35 additions & 5 deletions redis_sentinel/datadog_checks/redis_sentinel/redis_sentinel.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,43 @@ def _load_config(self, instance):
raise ConfigurationError('Configuration error. "sentinel_port" must be an int.')

passwd = instance.get('sentinel_password', None)

return host, port, passwd
username = instance.get('sentinel_username', None)
socket_timeout = instance.get('socket_timeout', 5)

ssl_kwargs = {}
if instance.get('ssl', False):
Comment thread
ian28223 marked this conversation as resolved.
ssl_kwargs['ssl'] = True
ssl_certfile = instance.get('ssl_certfile')
if ssl_certfile:
ssl_kwargs['ssl_certfile'] = ssl_certfile
ssl_keyfile = instance.get('ssl_keyfile')
if ssl_keyfile:
ssl_kwargs['ssl_keyfile'] = ssl_keyfile
ssl_ca_certs = instance.get('ssl_ca_certs')
if ssl_ca_certs:
ssl_kwargs['ssl_ca_certs'] = ssl_ca_certs
ssl_cert_reqs = instance.get('ssl_cert_reqs')
if ssl_cert_reqs is not None:
ssl_kwargs['ssl_cert_reqs'] = ssl_cert_reqs
ssl_check_hostname = instance.get('ssl_check_hostname')
if ssl_check_hostname is not None:
ssl_kwargs['ssl_check_hostname'] = ssl_check_hostname

return host, port, passwd, username, socket_timeout, ssl_kwargs

def check(self, instance):
host, port, password = self._load_config(instance)

redis_conn = redis.StrictRedis(host=host, port=port, password=password, db=0)
host, port, password, username, socket_timeout, ssl_kwargs = self._load_config(instance)

redis_conn = redis.StrictRedis(
host=host,
port=port,
password=password,
username=username,
db=0,
socket_timeout=socket_timeout,
socket_connect_timeout=socket_timeout,
**ssl_kwargs,
)

for master_name in instance['masters']:
base_tags = ['redis_name:%s' % master_name] + instance.get('tags', [])
Expand Down
4 changes: 2 additions & 2 deletions redis_sentinel/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,15 @@ classifiers = [
"Private :: Do Not Upload",
]
dependencies = [
"datadog-checks-base>=4.2.0",
"datadog-checks-base>=37.10.0",
]
dynamic = [
"version",
]

[project.optional-dependencies]
deps = [
"redis==2.10.5",
"redis==7.3.0",
Comment thread
ian28223 marked this conversation as resolved.
]

[project.urls]
Expand Down
78 changes: 76 additions & 2 deletions redis_sentinel/tests/test_redis_sentinel.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,18 +46,92 @@ def test_load_config():
c._load_config({'sentinel_host': 'localhost', 'sentinel_port': 'port'})

# Expect to pass when port is an integer, with no password defined.
host, port, password = c._load_config({'sentinel_host': 'localhost', 'sentinel_port': 123, 'masters': 'mymaster'})
host, port, password, username, socket_timeout, ssl_kwargs = c._load_config(
{'sentinel_host': 'localhost', 'sentinel_port': 123, 'masters': 'mymaster'}
)
assert host == 'localhost'
assert port == 123
assert password is None
assert username is None
assert socket_timeout == 5
assert ssl_kwargs == {}

# Expect to pass when port is an integer, with password defined.
host, port, password = c._load_config(
host, port, password, username, socket_timeout, ssl_kwargs = c._load_config(
{'sentinel_host': 'localhost', 'sentinel_port': 123, 'masters': 'mymaster', 'sentinel_password': 'password1'}
)
assert host == 'localhost'
assert port == 123
assert password == 'password1'
assert username is None
assert ssl_kwargs == {}


@pytest.mark.unit
def test_load_config_username():
c = RedisSentinelCheck('redis_sentinel', {}, {})

host, port, password, username, socket_timeout, ssl_kwargs = c._load_config(
{
'sentinel_host': 'localhost',
'sentinel_port': 123,
'masters': 'mymaster',
'sentinel_password': 'password1',
'sentinel_username': 'myuser',
}
)
assert username == 'myuser'
assert password == 'password1'


@pytest.mark.unit
def test_load_config_ssl():
c = RedisSentinelCheck('redis_sentinel', {}, {})

# SSL disabled by default
_, _, _, _, _, ssl_kwargs = c._load_config(
{'sentinel_host': 'localhost', 'sentinel_port': 123, 'masters': 'mymaster'}
)
assert ssl_kwargs == {}

# SSL enabled with all options
_, _, _, _, _, ssl_kwargs = c._load_config(
{
'sentinel_host': 'localhost',
'sentinel_port': 123,
'masters': 'mymaster',
'ssl': True,
'ssl_certfile': '/path/to/cert.pem',
'ssl_keyfile': '/path/to/key.pem',
'ssl_ca_certs': '/path/to/ca.pem',
'ssl_cert_reqs': 2,
'ssl_check_hostname': False,
}
)
assert ssl_kwargs == {
'ssl': True,
'ssl_certfile': '/path/to/cert.pem',
'ssl_keyfile': '/path/to/key.pem',
'ssl_ca_certs': '/path/to/ca.pem',
'ssl_cert_reqs': 2,
'ssl_check_hostname': False,
}

# SSL enabled with minimal options
_, _, _, _, _, ssl_kwargs = c._load_config(
{'sentinel_host': 'localhost', 'sentinel_port': 123, 'masters': 'mymaster', 'ssl': True}
)
assert ssl_kwargs == {'ssl': True}


@pytest.mark.unit
def test_load_config_socket_timeout():
Comment thread
ian28223 marked this conversation as resolved.
c = RedisSentinelCheck('redis_sentinel', {}, {})

_, _, _, _, socket_timeout, _ = c._load_config(
{'sentinel_host': 'localhost', 'sentinel_port': 123, 'masters': 'mymaster', 'socket_timeout': 10}
)
assert socket_timeout == 10


@pytest.mark.integration
Expand Down
Loading