Skip to content

Commit 426e040

Browse files
committed
rewrite ParadeDbExtension to use ProxiedDockerContainerExtension as base
1 parent 6d57e38 commit 426e040

File tree

4 files changed

+94
-168
lines changed

4 files changed

+94
-168
lines changed
Lines changed: 89 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
11
import os
2+
import socket
23
import logging
34

4-
from localstack_paradedb.utils.docker import DatabaseDockerContainerExtension
5+
from localstack_extensions.utils.docker import ProxiedDockerContainerExtension
6+
from localstack.extensions.api import http
7+
from localstack.utils.docker_utils import DOCKER_CLIENT
8+
from localstack.utils.container_utils.container_client import PortMappings
9+
from localstack.utils.sync import retry
10+
from werkzeug.datastructures import Headers
511

612
LOG = logging.getLogger(__name__)
713

@@ -18,7 +24,7 @@
1824
DEFAULT_POSTGRES_PORT = 5432
1925

2026

21-
class ParadeDbExtension(DatabaseDockerContainerExtension):
27+
class ParadeDbExtension(ProxiedDockerContainerExtension):
2228
name = "paradedb"
2329

2430
# Name of the Docker image to spin up
@@ -34,37 +40,97 @@ def __init__(self):
3440
postgres_port = int(os.environ.get(ENV_POSTGRES_PORT, DEFAULT_POSTGRES_PORT))
3541

3642
# Environment variables to pass to the container
37-
env_vars = {
43+
self.env_vars = {
3844
"POSTGRES_USER": postgres_user,
3945
"POSTGRES_PASSWORD": postgres_password,
4046
"POSTGRES_DB": postgres_db,
4147
}
4248

43-
super().__init__(
44-
image_name=self.DOCKER_IMAGE,
45-
container_ports=[postgres_port],
46-
env_vars=env_vars,
47-
)
48-
4949
# Store configuration for connection info
5050
self.postgres_user = postgres_user
5151
self.postgres_password = postgres_password
5252
self.postgres_db = postgres_db
5353
self.postgres_port = postgres_port
5454

55+
super().__init__(
56+
image_name=self.DOCKER_IMAGE,
57+
container_ports=[postgres_port],
58+
)
59+
60+
def should_proxy_request(self, headers: Headers) -> bool:
61+
"""
62+
Define whether a request should be proxied based on request headers.
63+
For database extensions, this is not used as connections are direct TCP.
64+
"""
65+
return False
66+
67+
def update_gateway_routes(self, router: http.Router[http.RouteHandler]):
68+
"""
69+
Override to start container without setting up HTTP gateway routes.
70+
Database extensions don't need HTTP routing - clients connect directly via TCP.
71+
"""
72+
self.start_container()
73+
74+
def start_container(self) -> None:
75+
"""Override to add env_vars support and database-specific health checking."""
76+
LOG.debug("Starting extension container %s", self.container_name)
77+
78+
port_mapping = PortMappings()
79+
for port in self.container_ports:
80+
port_mapping.add(port)
81+
82+
try:
83+
DOCKER_CLIENT.run_container(
84+
self.image_name,
85+
detach=True,
86+
remove=True,
87+
name=self.container_name,
88+
ports=port_mapping,
89+
env_vars=self.env_vars,
90+
)
91+
except Exception as e:
92+
LOG.debug("Failed to start container %s: %s", self.container_name, e)
93+
raise
94+
95+
def _check_health():
96+
"""Check if PostgreSQL port is accepting connections."""
97+
self._check_tcp_port(self.container_host, self.postgres_port)
98+
99+
try:
100+
retry(_check_health, retries=60, sleep=1)
101+
except Exception as e:
102+
LOG.info("Failed to connect to container %s: %s", self.container_name, e)
103+
self._remove_container()
104+
raise
105+
106+
LOG.info(
107+
"Successfully started extension container %s on %s:%s",
108+
self.container_name,
109+
self.container_host,
110+
self.postgres_port,
111+
)
112+
113+
def _check_tcp_port(self, host: str, port: int, timeout: float = 2.0) -> None:
114+
"""Check if a TCP port is accepting connections."""
115+
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
116+
sock.settimeout(timeout)
117+
try:
118+
sock.connect((host, port))
119+
sock.close()
120+
except (socket.timeout, socket.error) as e:
121+
raise AssertionError(f"Port {port} not ready: {e}")
122+
55123
def get_connection_info(self) -> dict:
56124
"""Return connection information for ParadeDB."""
57-
info = super().get_connection_info()
58-
info.update(
59-
{
60-
"database": self.postgres_db,
61-
"user": self.postgres_user,
62-
"password": self.postgres_password,
63-
"port": self.postgres_port,
64-
"connection_string": (
65-
f"postgresql://{self.postgres_user}:{self.postgres_password}"
66-
f"@{self.container_host}:{self.postgres_port}/{self.postgres_db}"
67-
),
68-
}
69-
)
70-
return info
125+
return {
126+
"host": self.container_host,
127+
"database": self.postgres_db,
128+
"user": self.postgres_user,
129+
"password": self.postgres_password,
130+
"port": self.postgres_port,
131+
"ports": {self.postgres_port: self.postgres_port},
132+
"connection_string": (
133+
f"postgresql://{self.postgres_user}:{self.postgres_password}"
134+
f"@{self.container_host}:{self.postgres_port}/{self.postgres_db}"
135+
),
136+
}

paradedb/localstack_paradedb/utils/__init__.py

Whitespace-only changes.

paradedb/localstack_paradedb/utils/docker.py

Lines changed: 0 additions & 144 deletions
This file was deleted.

paradedb/pyproject.toml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,11 @@ authors = [
1313
]
1414
keywords = ["LocalStack", "ParadeDB", "PostgreSQL", "Search", "Analytics"]
1515
classifiers = []
16-
dependencies = []
16+
dependencies = [
17+
# TODO remove / replace prior to merge!
18+
# "localstack-extensions-utils",
19+
"localstack-extensions-utils @ git+https://github.com/localstack/localstack-extensions.git@extract-utils-package#subdirectory=utils"
20+
]
1721

1822
[project.urls]
1923
Homepage = "https://github.com/localstack/localstack-extensions"

0 commit comments

Comments
 (0)