-
Notifications
You must be signed in to change notification settings - Fork 15
Expand file tree
/
Copy pathextension.py
More file actions
131 lines (108 loc) · 4.68 KB
/
extension.py
File metadata and controls
131 lines (108 loc) · 4.68 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
import os
import socket
from localstack_extensions.utils.docker import ProxiedDockerContainerExtension
from localstack import config
from localstack.extensions.api import http
# Environment variables for configuration
ENV_POSTGRES_USER = "PARADEDB_POSTGRES_USER"
ENV_POSTGRES_PASSWORD = "PARADEDB_POSTGRES_PASSWORD"
ENV_POSTGRES_DB = "PARADEDB_POSTGRES_DB"
# Default values
DEFAULT_POSTGRES_USER = "myuser"
DEFAULT_POSTGRES_PASSWORD = "mypassword"
DEFAULT_POSTGRES_DB = "mydatabase"
DEFAULT_POSTGRES_PORT = 5432
class ParadeDbExtension(ProxiedDockerContainerExtension):
name = "paradedb"
# Name of the Docker image to spin up
DOCKER_IMAGE = "paradedb/paradedb"
def __init__(self):
# Get configuration from environment variables
postgres_user = os.environ.get(ENV_POSTGRES_USER, DEFAULT_POSTGRES_USER)
postgres_password = os.environ.get(
ENV_POSTGRES_PASSWORD, DEFAULT_POSTGRES_PASSWORD
)
postgres_db = os.environ.get(ENV_POSTGRES_DB, DEFAULT_POSTGRES_DB)
postgres_port = DEFAULT_POSTGRES_PORT
# Store configuration for connection info
self.postgres_user = postgres_user
self.postgres_password = postgres_password
self.postgres_db = postgres_db
self.postgres_port = postgres_port
# Environment variables to pass to the container
env_vars = {
"POSTGRES_USER": postgres_user,
"POSTGRES_PASSWORD": postgres_password,
"POSTGRES_DB": postgres_db,
}
def _tcp_health_check():
"""Check if ParadeDB port is accepting connections."""
self._check_tcp_port(self.container_host, self.postgres_port)
super().__init__(
image_name=self.DOCKER_IMAGE,
container_ports=[postgres_port],
env_vars=env_vars,
health_check_fn=_tcp_health_check,
tcp_ports=[postgres_port], # Enable TCP proxying through gateway
)
# TODO: this should be migrated into the base class directly ..!
def update_gateway_routes(self, router: http.Router[http.RouteHandler]):
"""
Override to set up only TCP routing without HTTP proxy.
ParadeDB uses the native PostgreSQL wire protocol (not HTTP), so we
only need TCP protocol routing - not HTTP proxying. Adding an HTTP
proxy without a host restriction would cause all HTTP requests to be
forwarded to the PostgreSQL container, breaking other services.
"""
# Start the container
self.start_container()
# Set up only TCP protocol routing (skip HTTP proxy from base class)
if self.tcp_ports:
self._setup_tcp_protocol_routing()
def tcp_connection_matcher(self, data: bytes) -> bool:
"""
Identify PostgreSQL/ParadeDB connections by protocol handshake.
PostgreSQL can start with either:
1. SSL request: protocol code 80877103 (0x04D2162F)
2. Startup message: protocol version 3.0 (0x00030000)
Both use the same format:
- 4 bytes: message length
- 4 bytes: protocol version/code
"""
if len(data) < 8:
return False
# Check for SSL request (80877103 = 0x04D2162F)
if data[4:8] == b"\x04\xd2\x16\x2f":
return True
# Check for protocol version 3.0 (0x00030000)
if data[4:8] == b"\x00\x03\x00\x00":
return True
return False
def _check_tcp_port(self, host: str, port: int, timeout: float = 2.0) -> None:
"""Check if a TCP port is accepting connections."""
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(timeout)
try:
sock.connect((host, port))
sock.close()
except (socket.timeout, socket.error) as e:
raise AssertionError(f"Port {port} not ready: {e}")
def get_connection_info(self) -> dict:
"""Return connection information for ParadeDB."""
# Clients should connect through the LocalStack gateway
gateway_host = "paradedb.localhost.localstack.cloud"
gateway_port = config.LOCALSTACK_HOST.port
return {
"host": gateway_host,
"database": self.postgres_db,
"user": self.postgres_user,
"password": self.postgres_password,
"port": gateway_port,
"connection_string": (
f"postgresql://{self.postgres_user}:{self.postgres_password}"
f"@{gateway_host}:{gateway_port}/{self.postgres_db}"
),
# Also include container connection details for debugging
"container_host": self.container_host,
"container_port": self.postgres_port,
}