mirror of
https://github.com/BerriAI/litellm.git
synced 2026-08-28 05:25:59 +00:00
fix(redis): keep the credential provider off the Sentinel monitors
This commit is contained in:
parent
308c906cdd
commit
09b391d7b3
2 changed files with 43 additions and 2 deletions
|
|
@ -551,14 +551,22 @@ def _init_redis_sentinel(redis_kwargs) -> redis.Redis:
|
|||
return sentinel.master_for(service_name, **connection_kwargs)
|
||||
|
||||
|
||||
def _sentinel_auth_kwargs(connection_kwargs: dict, sentinel_password: str | None) -> dict:
|
||||
"""The Sentinel monitors are separate servers with their own password, and redis-py refuses a
|
||||
password passed alongside a credential provider, so the data node's provider stays behind once
|
||||
a Sentinel password is configured."""
|
||||
superseded: Final = frozenset({"credential_provider"}) if sentinel_password else frozenset()
|
||||
kept: Final = ((k, v) for k, v in connection_kwargs.items() if k not in superseded)
|
||||
return dict(kept, password=sentinel_password)
|
||||
|
||||
|
||||
def _init_async_redis_sentinel(redis_kwargs) -> async_redis.Redis:
|
||||
sentinel_nodes: Final = redis_kwargs.get("sentinel_nodes")
|
||||
sentinel_password: Final = redis_kwargs.get("sentinel_password")
|
||||
service_name: Final = redis_kwargs.get("service_name")
|
||||
connection_kwargs: Final = _get_redis_sentinel_connection_kwargs(redis_kwargs)
|
||||
connection_kwargs.setdefault("socket_timeout", REDIS_SOCKET_TIMEOUT)
|
||||
sentinel_kwargs: Final = dict(connection_kwargs)
|
||||
sentinel_kwargs["password"] = sentinel_password
|
||||
sentinel_kwargs: Final = _sentinel_auth_kwargs(connection_kwargs, sentinel_password)
|
||||
|
||||
if not sentinel_nodes or not service_name:
|
||||
raise ValueError("Both 'sentinel_nodes' and 'service_name' are required for Redis Sentinel.")
|
||||
|
|
|
|||
|
|
@ -1024,3 +1024,36 @@ def test_async_cluster_drops_a_connect_func_it_cannot_pass_on():
|
|||
client = get_redis_async_client()
|
||||
|
||||
assert isinstance(client, async_redis.RedisCluster)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"markers, provider_cls",
|
||||
[
|
||||
(AZURE_AD_CONNECT_FUNC, AzureADCredentialProvider),
|
||||
(GCP_IAM_CONNECT_FUNC, GCPIAMCredentialProvider),
|
||||
],
|
||||
ids=["azure_ad", "gcp_iam"],
|
||||
)
|
||||
def test_async_sentinel_keeps_the_credential_provider_off_the_monitors(markers, provider_cls):
|
||||
"""The Sentinel monitors authenticate with their own password, and redis-py refuses a password
|
||||
passed alongside a credential provider, so only the data node may carry the provider.
|
||||
"""
|
||||
redis_kwargs = {
|
||||
"sentinel_nodes": [("sentinel-1", 26379)],
|
||||
"sentinel_password": "sentinel-secret",
|
||||
"service_name": "mymaster",
|
||||
"redis_connect_func": SimpleNamespace(**markers),
|
||||
}
|
||||
|
||||
with patch("litellm._redis.async_redis.Sentinel") as mock_sentinel_cls:
|
||||
with patch("litellm._redis._get_redis_client_logic", return_value=redis_kwargs):
|
||||
get_redis_async_client()
|
||||
|
||||
sentinel_kwargs = mock_sentinel_cls.call_args[1]["sentinel_kwargs"]
|
||||
assert sentinel_kwargs["password"] == "sentinel-secret"
|
||||
assert "credential_provider" not in sentinel_kwargs
|
||||
async_redis.Connection(host="sentinel-1", port=26379, **sentinel_kwargs)
|
||||
|
||||
master_kwargs = mock_sentinel_cls.return_value.master_for.call_args[1]
|
||||
assert isinstance(master_kwargs["credential_provider"], provider_cls)
|
||||
assert "password" not in master_kwargs
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue