Skip to content

Commit 6125cff

Browse files
Address PR comments & change python version support
1 parent 17640a3 commit 6125cff

10 files changed

Lines changed: 108 additions & 117 deletions

File tree

Makefile

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ VENV_DIR ?= .venv
33
VENV_RUN = . $(VENV_DIR)/bin/activate
44
PIP_CMD ?= pip
55
PYTHON_CMD ?= python
6-
TEST_DEPS ?= pytest pytest-timeout
7-
LINT_DEPS ?= ruff
6+
TEST_REQS ?= requirements-test.txt
7+
LINT_REQS ?= requirements-lint.txt
88

99
PG_TEST_CONTAINER ?= pg-proxy-local-tests
1010
PG_TEST_IMAGE ?= postgres:16
@@ -26,10 +26,10 @@ publish: ## Publish the library to the central PyPi repository
2626
($(VENV_RUN); pip install twine; python ./setup.py sdist && twine upload dist/*)
2727

2828
install-test: install ## Install test dependencies in local virtualenv
29-
($(VENV_RUN); $(PIP_CMD) install $(TEST_DEPS))
29+
($(VENV_RUN); $(PIP_CMD) install -r $(TEST_REQS))
3030

3131
install-lint: install ## Install lint dependencies in local virtualenv
32-
($(VENV_RUN); $(PIP_CMD) install $(LINT_DEPS))
32+
($(VENV_RUN); $(PIP_CMD) install -r $(LINT_REQS))
3333

3434
lint: install-lint ## Format code with ruff
3535
$(VENV_DIR)/bin/ruff format postgresql_proxy tests plugins

README.md

Lines changed: 86 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -5,26 +5,30 @@ Serves as a proper server that Postgresql clients can connect to. Can modify pac
55
Currently used for rewriting queries to force proper use of postgres-hll module by external proprietary software that doesn't know about that functionality
66

77
## Installing
8-
### Linux
9-
1. Make sure you have [python3 and pip3 installed on your system](https://stackoverflow.com/questions/6587507/how-to-install-pip-with-python-3#6587528). It has been tested with Python3.6 but should also run on Python3.5.
10-
2. Clone it locally and cd to that directory
11-
```
12-
git clone git@github.com:kfzteile24/postgresql-proxy.git
13-
cd postgresql-proxy
14-
```
15-
3. Run [setup.sh](setup.sh)
16-
```
17-
./setup.sh
18-
```
19-
4. Make a copy of [config.yml.example](config.yml.example) called `config.yml` and configure your proxy instances. Create the log directories if they're not there.
8+
9+
Requires Python `3.13+`.
10+
11+
1. Clone the repository:
12+
```bash
13+
git clone git@github.com:localstack/postgresql-proxy.git
14+
cd postgresql-proxy
15+
```
16+
2. Install dependencies into a local virtualenv:
17+
```bash
18+
make install
19+
```
20+
3. Copy the example config and edit it for your environment:
21+
```bash
22+
cp config.yml.example config.yml
23+
```
2024

2125
## Configuring
2226
In the `config.yml` file you can define the following things
2327
### Plugins
2428
A list of dynamically loaded modules that reside in the [plugins](plugins) directory. These plugins can be used in later configuration, to intercept queries, commands, or responses. View plugin documentation for example plugins for more details on how to do that.
2529
### Settings
2630
General application settings. Currently the following settings are used
27-
* `log-level` - the log level for the general log. See [python logging](https://docs.python.org/3.6/library/logging.html) for more details about the logging functionality
31+
* `log-level` - the log level for the general log. See [python logging](https://docs.python.org/3/library/logging.html) for more details about the logging functionality
2832
* `general-log` - the location for the general log. All general messages go in there.
2933
* `intercept-log` - the location for the intercept log. Intercepted messages and return values from various enabled plugins will be written there. This log can be quite verbose as it contains the full binary messages being circulated.
3034

@@ -42,19 +46,22 @@ Make sure to manage the logs yourself, as they accumulate and take up disk space
4246

4347
Each interceptor definition must have a `plugin`, which should also be present in the [plugins](#Plugins) configuration, and a `function`, that is found directly in that module, that will be called each time with the intercepted message as a byte string, and a context variable that is an instance of the `Proxy` class, that contains connection information and other useful stuff.
4448

45-
## Running in testing mode
46-
If you want to test it, do this. Otherwise scroll down for instructions on how to install it as a service
47-
### Linux
48-
1. Activate the virtual environment
49-
```
50-
source .venv/bin/activate
51-
```
52-
2. Run it
53-
```
54-
python proxy.py
55-
```
56-
57-
### Changelog
49+
## Running
50+
51+
Activate the virtualenv and run the proxy directly:
52+
53+
```bash
54+
source .venv/bin/activate
55+
python -m postgresql_proxy
56+
```
57+
58+
Or run it without activating the venv:
59+
60+
```bash
61+
.venv/bin/python -m postgresql_proxy
62+
```
63+
64+
## Changelog
5865
- v0.3.1
5966
- Fix SSL COPY stalls by draining pending SSL buffer after recv [#11](https://github.com/localstack/postgresql-proxy/pull/11)
6067
- Fix intermittent `BlockingIOError` on macOS during SSL negotiation
@@ -84,8 +91,60 @@ If you want to test it, do this. Otherwise scroll down for instructions on how t
8491

8592
## Testing
8693

87-
Run the full local test suite (starts a disposable PostgreSQL container automatically):
94+
All tests require a real PostgreSQL server and are organized at the top level:
95+
96+
- `test_proxy.py`: proxy behavior tests (connection, SSL, hang regressions)
97+
- `test_plugins.py`: plugin integration tests (HLL rewrite behavior)
98+
99+
### Prerequisites
100+
101+
- Python `3.13` (same version as CI)
102+
- Docker (for local disposable PostgreSQL)
103+
- `psql` (`postgresql-client`)
104+
- `openssl` (SSL tests generate a temporary self-signed cert/key at runtime)
105+
106+
Install Python deps in the project virtualenv:
107+
108+
```bash
109+
make install-test
110+
```
111+
112+
### Which command should I use?
113+
114+
- Fastest full local run with disposable Postgres: `make test`
115+
- Run only proxy tests (using your own Postgres): `python -m pytest tests/test_proxy.py -vv`
116+
- Run only plugin tests: `python -m pytest tests/test_plugins.py -vv`
117+
118+
#### 1) Full local suite (recommended)
119+
120+
`make test` starts a temporary PostgreSQL container, waits for readiness, sets DB env vars, then runs:
121+
122+
```bash
123+
python -m pytest -vv
124+
```
125+
126+
Use it when you want one command that matches normal contributor workflow.
88127

89128
```bash
90129
make test
91130
```
131+
132+
#### 2) Run against an existing PostgreSQL
133+
134+
If you already have PostgreSQL running, set connection env vars and run the tests you need:
135+
136+
```bash
137+
export E2E_PG_HOST=127.0.0.1
138+
export E2E_PG_PORT=5432
139+
export E2E_PG_USER=postgres
140+
export E2E_PG_PASSWORD=postgres
141+
export E2E_PG_DB=postgres
142+
143+
# Proxy tests only
144+
python -m pytest tests/test_proxy.py -vv
145+
146+
# Plugin tests only
147+
python -m pytest tests/test_plugins.py -vv
148+
```
149+
150+
If PostgreSQL is not reachable, tests fail fast at startup.

postgresql_proxy/__main__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
from postgresql_proxy.proxy import main
2+
3+
main()

postgresql_proxy/proxy.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -329,7 +329,7 @@ def stop(self):
329329
self.running = False
330330

331331

332-
if __name__ == '__main__':
332+
def main():
333333
import importlib
334334
import yaml
335335
import os
@@ -370,3 +370,7 @@ def stop(self):
370370
logging.info("Starting proxy instance")
371371
proxy = Proxy(instance, plugins)
372372
proxy.listen()
373+
374+
375+
if __name__ == "__main__":
376+
main()

requirements-lint.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
ruff==0.15.12

requirements-test.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
pytest==9.0.3
2+
pytest-timeout==2.4.0

setup.py

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
1-
import os
2-
import re
3-
import sys
41
from setuptools import find_packages, setup
52

63
install_requires = []
@@ -16,14 +13,8 @@
1613
install_requires=install_requires,
1714
zip_safe=False,
1815
classifiers=[
19-
'Programming Language :: Python :: 2',
20-
'Programming Language :: Python :: 2.6',
21-
'Programming Language :: Python :: 2.7',
2216
'Programming Language :: Python :: 3',
23-
'Programming Language :: Python :: 3.3',
24-
'Programming Language :: Python :: 3.6',
25-
'Programming Language :: Python :: 3.7',
26-
'Programming Language :: Python :: 3.8',
17+
'Programming Language :: Python :: 3.13',
2718
'License :: OSI Approved :: Apache Software License',
2819
'Topic :: Software Development :: Testing',
2920
]

tests/README.md

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

tests/conftest.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,4 +30,3 @@ def ensure_postgres_available(postgres_settings):
3030
pytest.fail(
3131
f"PostgreSQL backend is required for tests but is not reachable: {err}"
3232
)
33-

tests/test_proxy.py

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,6 @@
1515
from postgresql_proxy.proxy import Proxy
1616

1717

18-
19-
2018
def _get_free_tcp_port() -> int:
2119
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
2220
sock.bind(("127.0.0.1", 0))
@@ -233,12 +231,11 @@ def test_psql_ssl_file_batch_stress_no_hang(postgres_settings, ssl_proxy_port):
233231
if shutil.which("psql") is None:
234232
pytest.fail("psql is required for this test but was not found in PATH")
235233

236-
sql_file_path = None
237-
try:
234+
with tempfile.NamedTemporaryFile("w", suffix=".sql", delete=True) as tmp_file:
238235
sql_content = _build_dump_like_sql(table_count=24, rows_per_table=300)
239-
with tempfile.NamedTemporaryFile("w", suffix=".sql", delete=False) as tmp_file:
240-
tmp_file.write(sql_content)
241-
sql_file_path = tmp_file.name
236+
tmp_file.write(sql_content)
237+
tmp_file.flush()
238+
sql_file_path = tmp_file.name
242239

243240
for run_idx in range(3):
244241
started = time.time()
@@ -261,16 +258,13 @@ def test_psql_ssl_file_batch_stress_no_hang(postgres_settings, ssl_proxy_port):
261258
err_tail = "\n".join((result.stderr or "").splitlines()[-20:])
262259
pytest.fail(
263260
"psql -f batch failed over SSL via proxy "
264-
f"(run={run_idx + 1}, rc={result.returncode}, elapsed={elapsed:.2f}s) "
261+
f"(run={run_idx + 1}, rc={result.returncode}, {elapsed=:.2f}s) "
265262
f"stdout_tail={out_tail} stderr_tail={err_tail}"
266263
)
267264

268265
if "BATCH_OK" not in (result.stdout or ""):
269266
out_tail = "\n".join((result.stdout or "").splitlines()[-20:])
270267
pytest.fail(
271268
"psql -f batch succeeded but expected marker missing "
272-
f"(run={run_idx + 1}, elapsed={elapsed:.2f}s) stdout_tail={out_tail}"
269+
f"(run={run_idx + 1}, {elapsed=:.2f}s) stdout_tail={out_tail}"
273270
)
274-
finally:
275-
if sql_file_path and os.path.exists(sql_file_path):
276-
os.unlink(sql_file_path)

0 commit comments

Comments
 (0)