import logging import typing import boto3 import botocore.client def paginate_by_marker( func: callable, list_field: str, ) -> typing.Iterable: """ Simplifies paginating over AWS results with a simpler interface than Paginators. :param func: A function that accepts a parameter named `Marker` :param list_field: Result field to iterate on """ marker = "" while True: result = func(Marker=marker) items = result.get(list_field, []) yield from items if "Marker" not in result: break marker = result["Marker"] logger = logging.getLogger(__name__) class Rediser: @classmethod def new(cls): return cls(boto3.client("elasticache", region_name='eu-central-1')) def __init__(self, elasticache: botocore.client.BaseClient): self.elasticache = elasticache def _get_redis_acl( self, cluster_id: str = None, status: str = "available", ) -> typing.Optional[dict]: """ Finds a Redis replication group by its ID :param cluster_id: Replication group ID :param status: Replication status. One of available/starting/modifying/deleting. :return: RedisInstance or throws LookupError error """ try: redis = self.elasticache.describe_replication_groups(ReplicationGroupId=cluster_id)["ReplicationGroups"][0] if redis["Status"] != status: raise Exception(f"Found redis with {cluster_id=} but its status={redis['Status']}") except self.elasticache.exceptions.ReplicationGroupNotFoundFault: return None return dict( cluster_id=redis["ReplicationGroupId"], host=redis["NodeGroups"][0]["PrimaryEndpoint"]["Address"], port=redis["NodeGroups"][0]["PrimaryEndpoint"]["Port"], arn=redis["ARN"], user_group_ids=redis["UserGroupIds"], ) def create_redis_acl( self, *, cluster_id: str, cluster_description: typing.Optional[str] = None, node_type: str, subnet_group_name: str, security_group_id: str, ): redis = self._get_redis_acl(cluster_id=cluster_id) if not redis: res = self.elasticache.create_replication_group( ReplicationGroupId=cluster_id, ReplicationGroupDescription=cluster_description or cluster_id, ReplicasPerNodeGroup=0, Engine="redis", EngineVersion="6.x", CacheNodeType=node_type, CacheSubnetGroupName=subnet_group_name, TransitEncryptionEnabled=True, # must be True to use Redis ACL UserGroupIds=[ user_group_id, ], SecurityGroupIds=[ security_group_id, ], Tags=[{"Key": k, "Value": v} for k, v in tags.items()], MultiAZEnabled=False, AutomaticFailoverEnabled=False, ) def main(): r = Rediser.new() res = r.create_redis_acl(cluster_id='hey', node_type='cache.t2.micro') if __name__ == '__main__': main()