Repository: python-arq/arq (language: python)

Issue:
Dropped SSL Support for redis connection
I think that the changes introduced here
https://github.com/samuelcolvin/arq/pull/259
somehow broke the support for a complex ssl configuartion, now the `__init__` method from redis requires a boolean as opposed to a boolean or an  `SSLContext` object like it used to. For example that setup does not work anymore:
```python
    ctx = ssl.SSLContext()
    ctx.check_hostname = False
    ctx.verify_mode = ssl.CERT_NONE
    return RedisSettings(
        host=settings.REDIS_HOST,
        port=settings.REDIS_PORT,
        password=settings.REDIS_PASSWORD,
        ssl=ctx,
        conn_timeout=10,
    )
```

Here is an extract of what the `Redis` client init method expects:
```python
    def __init__(
        self,
        *,
        ....
        ssl: bool = False,
        ssl_keyfile: Optional[str] = None,
        ssl_certfile: Optional[str] = None,
        ssl_cert_reqs: str = "required",
        ssl_ca_certs: Optional[str] = None,
        ssl_ca_data: Optional[str] = None,
        ssl_check_hostname: bool = False,
        max_connections: Optional[int] = None,
      ...
    ):
```


Discussion / hints:

Fix patch:
diff --git a/arq/connections.py b/arq/connections.py
index 067b9670..bf8d1bff 100644
--- a/arq/connections.py
+++ b/arq/connections.py
@@ -1,15 +1,13 @@
 import asyncio
 import functools
 import logging
-import ssl
 from dataclasses import dataclass
 from datetime import datetime, timedelta
 from operator import attrgetter
-from typing import Any, Callable, Generator, List, Optional, Tuple, Union
+from typing import Any, Callable, List, Optional, Tuple, Union
 from urllib.parse import urlparse
 from uuid import uuid4
 
-from pydantic.validators import make_arbitrary_type_validator
 from redis.asyncio import ConnectionPool, Redis
 from redis.asyncio.sentinel import Sentinel
 from redis.exceptions import RedisError, WatchError
@@ -21,16 +19,6 @@
 logger = logging.getLogger('arq.connections')
 
 
-class SSLContext(ssl.SSLContext):
-    """
-    Required to avoid problems with
-    """
-
-    @classmethod
-    def __get_validators__(cls) -> Generator[Callable[..., Any], None, None]:
-        yield make_arbitrary_type_validator(ssl.SSLContext)
-
-
 @dataclass
 class RedisSettings:
     """
@@ -44,7 +32,13 @@ class RedisSettings:
     database: int = 0
     username: Optional[str] = None
     password: Optional[str] = None
-    ssl: Union[bool, None, SSLContext] = None
+    ssl: bool = False
+    ssl_keyfile: Optional[str] = None
+    ssl_certfile: Optional[str] = None
+    ssl_cert_reqs: str = 'required'
+    ssl_ca_certs: Optional[str] = None
+    ssl_ca_data: Optional[str] = None
+    ssl_check_hostname: bool = False
     conn_timeout: int = 1
     conn_retries: int = 5
     conn_retry_delay: int = 1
@@ -215,7 +209,12 @@ async def create_pool(
     if settings.sentinel:
 
         def pool_factory(*args: Any, **kwargs: Any) -> ArqRedis:
-            client = Sentinel(*args, sentinels=settings.host, ssl=settings.ssl, **kwargs)
+            client = Sentinel(
+                *args,
+                sentinels=settings.host,
+                ssl=settings.ssl,
+                **kwargs,
+            )
             return client.master_for(settings.sentinel_master, redis_class=ArqRedis)
 
     else:
@@ -225,6 +224,12 @@ def pool_factory(*args: Any, **kwargs: Any) -> ArqRedis:
             port=settings.port,
             socket_connect_timeout=settings.conn_timeout,
             ssl=settings.ssl,
+            ssl_keyfile=settings.ssl_keyfile,
+            ssl_certfile=settings.ssl_certfile,
+            ssl_cert_reqs=settings.ssl_cert_reqs,
+            ssl_ca_certs=settings.ssl_ca_certs,
+            ssl_ca_data=settings.ssl_ca_data,
+            ssl_check_hostname=settings.ssl_check_hostname,
         )
 
     try:


Test patch:
diff --git a/tests/test_utils.py b/tests/test_utils.py
index d067b08f..a5e71e78 100644
--- a/tests/test_utils.py
+++ b/tests/test_utils.py
@@ -17,8 +17,10 @@ def test_settings_changed():
     settings = RedisSettings(port=123)
     assert settings.port == 123
     assert (
-        "RedisSettings(host='localhost', port=123, database=0, username=None, password=None, ssl=None, conn_timeout=1, "
-        "conn_retries=5, conn_retry_delay=1, sentinel=False, sentinel_master='mymaster')"
+        "RedisSettings(host='localhost', port=123, database=0, username=None, password=None, ssl=False, "
+        "ssl_keyfile=None, ssl_certfile=None, ssl_cert_reqs='required', ssl_ca_certs=None, "
+        'ssl_ca_data=None, ssl_check_hostname=False, conn_timeout=1, conn_retries=5, conn_retry_delay=1, '
+        "sentinel=False, sentinel_master='mymaster')"
     ) == str(settings)
 
 
@@ -114,7 +116,7 @@ def parse_redis_settings(cls, v):
     assert s2.redis_settings.host == 'testing.com'
     assert s2.redis_settings.port == 6379
 
-    with pytest.raises(ValueError, match='instance of SSLContext expected'):
+    with pytest.raises(ValueError, match='1 validation error for Settings\nredis_settings -> ssl'):
         Settings(redis_settings={'ssl': 123})
 
     s3 = Settings(redis_settings={'ssl': True})