from typing import Optional from dataclasses import dataclass from cryptography.fernet import Fernet from django.conf import settings from labs.tidb_serverless.schemas import TiDBServerlessClusterConfig from labs.utils.database_fields import get_legacy_fernet_key class AWSAuroraServerlessClusterConfig: cluster_identifier: str instance_identifier: str port: int endpoint: Optional[str] reader_endpoint: Optional[str] database_name: str master_username: str master_password: str def __init__( self, *, cluster_identifier: str, instance_identifier: str, port: int, endpoint: Optional[str], reader_endpoint: Optional[str], database_name: str, master_username: str, master_password: str, ): self.fernet = Fernet(get_legacy_fernet_key(settings.SECRET_KEY)) self.cluster_identifier = cluster_identifier self.instance_identifier = instance_identifier self.port = port self.endpoint = endpoint self.reader_endpoint = reader_endpoint self.database_name = database_name self.master_username = master_username self.master_password = master_password def _encrypt(self, value: str) -> str: return self.fernet.encrypt(value.encode("utf-8")).decode("utf-8") def _decrypt(self, value: str) -> str: return self.fernet.decrypt(value.encode("utf-8")).decode("utf-8") @classmethod def from_encrypted_dict(cls, data: dict) -> "AWSAuroraServerlessClusterConfig": instance = cls( cluster_identifier=data["cluster_identifier"], instance_identifier=data["instance_identifier"], port=data["port"], endpoint=data.get("endpoint"), reader_endpoint=data.get("reader_endpoint"), database_name=data["database_name"], master_username="", master_password="", ) instance.master_username = instance._decrypt(data["master_username"]) instance.master_password = instance._decrypt(data["master_password"]) return instance def to_encrypted_dict(self) -> dict: return { "cluster_identifier": self.cluster_identifier, "instance_identifier": self.instance_identifier, "port": self.port, "endpoint": self.endpoint, "reader_endpoint": self.reader_endpoint, "database_name": self.database_name, "master_username": self._encrypt(self.master_username), "master_password": self._encrypt(self.master_password), } @dataclass class EC2Instance: role: str public_ip: str private_ip: str ssh_user: Optional[str] = None web_ssh: bool = False https_reverse_proxy: bool = False @classmethod def from_dict(cls, data: dict) -> "EC2Instance": return cls( role=data["Role"], public_ip=data["Public IP"], private_ip=data["Private IP"], ssh_user=data.get("SSH User"), web_ssh=data.get("Web SSH", data.get("Entrypoint", False)), https_reverse_proxy=data.get( "HTTPS Reverse Proxy", data.get("Secure Access", False) ), ) def to_dict(self) -> dict: return { "Type": "Virtual Machine", "Role": self.role, "Public IP": self.public_ip, "Private IP": self.private_ip, "SSH User": self.ssh_user, "Web SSH": self.web_ssh, "HTTPS Reverse Proxy": self.https_reverse_proxy, } @dataclass class LabResource: ec2_instances: list[EC2Instance] tidb_serverless: Optional[TiDBServerlessClusterConfig] = None aws_aurora_serverless: Optional[AWSAuroraServerlessClusterConfig] = None @classmethod def from_db_lab_instance_resource(cls, data: dict) -> "LabResource": obj = cls( ec2_instances=[], tidb_serverless=None, aws_aurora_serverless=None, ) for instance in data.get("ec2_instances", []): obj.ec2_instances.append(EC2Instance.from_dict(instance)) if data.get("tidb_serverless"): obj.tidb_serverless = TiDBServerlessClusterConfig.from_encrypted_dict( data["tidb_serverless"] ) if data.get("aws_aurora_serverless"): obj.aws_aurora_serverless = ( AWSAuroraServerlessClusterConfig.from_encrypted_dict( data["aws_aurora_serverless"] ) ) return obj def to_db_lab_instance_resource(self) -> dict: return { "ec2_instances": [instance.to_dict() for instance in self.ec2_instances], "tidb_serverless": self.tidb_serverless.to_encrypted_dict() if self.tidb_serverless else None, "aws_aurora_serverless": self.aws_aurora_serverless.to_encrypted_dict() if self.aws_aurora_serverless else None, } def to_frontend(self) -> list[dict]: data = [] for instance in self.ec2_instances: data.append(instance.to_dict()) if self.tidb_serverless: data.append( { "Role": "TiDB Cloud Serverless", "Type": "Database", "Endpoint": self.tidb_serverless.cluster_host, "Port": self.tidb_serverless.cluster_port, "Username": self.tidb_serverless.admin_name, "Password": self.tidb_serverless.admin_password, "Environment": self.tidb_serverless.environment, "Cluster Name": self.tidb_serverless.cluster_name, "Database Name": self.tidb_serverless.database_name, } ) if self.aws_aurora_serverless and self.aws_aurora_serverless.endpoint: data.append( { "Role": "AWS Aurora Serverless", "Type": "Database", "Endpoint": self.aws_aurora_serverless.endpoint, "Port": self.aws_aurora_serverless.port, "Username": self.aws_aurora_serverless.master_username, "Password": self.aws_aurora_serverless.master_password, "Database Name": self.aws_aurora_serverless.database_name, } ) return data