2023-05-15 08:51:32 +08:00
|
|
|
import redis
|
2024-01-12 12:34:01 +08:00
|
|
|
from redis.connection import Connection, SSLConnection
|
2023-05-15 08:51:32 +08:00
|
|
|
|
|
|
|
redis_client = redis.Redis()
|
|
|
|
|
|
|
|
|
|
|
|
def init_app(app):
|
2023-05-17 15:40:21 +08:00
|
|
|
connection_class = Connection
|
|
|
|
if app.config.get('REDIS_USE_SSL', False):
|
|
|
|
connection_class = SSLConnection
|
|
|
|
|
2024-06-14 03:07:17 +08:00
|
|
|
cluster_name = app.config.get('REDIS_CLUSTER_NAME', None)
|
|
|
|
if cluster_name:
|
|
|
|
username = app.config.get('REDIS_USERNAME', None)
|
|
|
|
creds_provider = ElastiCacheIAMProvider(user=username, cluster_name=cluster_name)
|
|
|
|
redis_client.connection_pool = redis.ConnectionPool(**{
|
|
|
|
'host': app.config.get('REDIS_HOST', 'localhost'),
|
|
|
|
'port': app.config.get('REDIS_PORT', 6379),
|
|
|
|
'credential_provider': creds_provider,
|
|
|
|
'db': app.config.get('REDIS_DB', 0),
|
|
|
|
'encoding': 'utf-8',
|
|
|
|
'encoding_errors': 'strict',
|
|
|
|
'decode_responses': False
|
|
|
|
}, connection_class=connection_class)
|
|
|
|
|
2023-05-15 08:51:32 +08:00
|
|
|
redis_client.connection_pool = redis.ConnectionPool(**{
|
|
|
|
'host': app.config.get('REDIS_HOST', 'localhost'),
|
|
|
|
'port': app.config.get('REDIS_PORT', 6379),
|
2023-05-17 15:40:21 +08:00
|
|
|
'username': app.config.get('REDIS_USERNAME', None),
|
2023-05-15 08:51:32 +08:00
|
|
|
'password': app.config.get('REDIS_PASSWORD', None),
|
|
|
|
'db': app.config.get('REDIS_DB', 0),
|
|
|
|
'encoding': 'utf-8',
|
|
|
|
'encoding_errors': 'strict',
|
|
|
|
'decode_responses': False
|
2023-05-17 15:40:21 +08:00
|
|
|
}, connection_class=connection_class)
|
2023-05-15 08:51:32 +08:00
|
|
|
|
|
|
|
app.extensions['redis'] = redis_client
|
2024-06-14 03:07:17 +08:00
|
|
|
|
|
|
|
|
|
|
|
class ElastiCacheIAMProvider(redis.CredentialProvider):
|
|
|
|
def __init__(self, user, cluster_name, region="us-east-1"):
|
|
|
|
self.user = user
|
|
|
|
self.cluster_name = cluster_name
|
|
|
|
self.region = region
|
|
|
|
|
|
|
|
session = botocore.session.get_session()
|
|
|
|
self.request_signer = RequestSigner(
|
|
|
|
ServiceId("elasticache"),
|
|
|
|
self.region,
|
|
|
|
"elasticache",
|
|
|
|
"v4",
|
|
|
|
session.get_credentials(),
|
|
|
|
session.get_component("event_emitter"),
|
|
|
|
)
|
|
|
|
|
|
|
|
# Generated IAM tokens are valid for 15 minutes
|
|
|
|
@cached(cache=TTLCache(maxsize=128, ttl=900))
|
|
|
|
def get_credentials(self) -> Union[Tuple[str], Tuple[str, str]]:
|
|
|
|
query_params = {"Action": "connect", "User": self.user}
|
|
|
|
url = urlunparse(
|
|
|
|
ParseResult(
|
|
|
|
scheme="https",
|
|
|
|
netloc=self.cluster_name,
|
|
|
|
path="/",
|
|
|
|
query=urlencode(query_params),
|
|
|
|
params="",
|
|
|
|
fragment="",
|
|
|
|
)
|
|
|
|
)
|
|
|
|
signed_url = self.request_signer.generate_presigned_url(
|
|
|
|
{"method": "GET", "url": url, "body": {}, "headers": {}, "context": {}},
|
|
|
|
operation_name="connect",
|
|
|
|
expires_in=900,
|
|
|
|
region_name=self.region,
|
|
|
|
)
|
|
|
|
# RequestSigner only seems to work if the URL has a protocol, but
|
|
|
|
# Elasticache only accepts the URL without a protocol
|
|
|
|
# So strip it off the signed URL before returning
|
|
|
|
return (self.user, signed_url.removeprefix("https://"))
|