Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
101 changes: 85 additions & 16 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
@@ -1,30 +1,99 @@
name: Run tests
name: Run parallel tests

on: ["push", "pull_request"]
on: [push, pull_request]

jobs:
docker-tests:
name: Docker Tests / Python ${{ matrix.python-version }}
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13"]

steps:
- uses: actions/checkout@v3

test:
- name: Set up Python
uses: actions/setup-python@v3
with:
python-version: ${{ matrix.python-version }}

- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install hatch
- name: Install Docker CE
run: |
sudo apt-get update
sudo apt-get install -y \
ca-certificates \
curl \
gnupg \
lsb-release
sudo mkdir -p /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Depending on curl here is very bad, it means the tests won't pass if that site is down or something similar is broken.

echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
$(lsb_release -cs) stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
sudo apt-get install -y docker-ce docker-ce-cli containerd.io
sudo systemctl start docker
docker --version
- name: Run Docker scenario tests
run: |
hatch run ci -- dbtesttools.tests.test_pgfixture_docker dbtesttools.tests.test_isolation
podman-tests:
name: Podman Tests / Python ${{ matrix.python-version }}
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"]
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13"]

steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v3

- name: Set up Python
uses: actions/setup-python@v3
with:
python-version: ${{ matrix.python-version }}

- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v3
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install hatch
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install hatch
- name: Install Podman
run: |
sudo apt-get update
sudo apt-get install -y podman
podman --version
- name: Run tests
run: |
hatch run ci
- name: Start Podman API (rootless)
run: |
export XDG_RUNTIME_DIR="/run/user/$(id -u)"
sudo mkdir -p "$XDG_RUNTIME_DIR"
sudo chown $(id -u):$(id -g) "$XDG_RUNTIME_DIR"
mkdir -p "$XDG_RUNTIME_DIR"
nohup podman system service --time=0 unix://$XDG_RUNTIME_DIR/podman/podman.sock > podman-api.log 2>&1 &
echo "Waiting for podman.sock..."
for i in {1..10}; do
if podman info > /dev/null 2>&1; then
echo "Podman is up"
break
fi
sleep 2
done
cat podman-api.log
if ! podman info > /dev/null 2>&1; then
echo "::error ::Podman API service failed to start"
cat podman-api.log
exit 1
fi
- name: Run Podman scenario tests
run: |
hatch run ci -- dbtesttools.tests.test_pgfixture_podman dbtesttools.tests.test_isolation
9 changes: 9 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,15 @@ PG containers. You will need to do up to two extra things:
can set the DBTESTTOOLS_PG_IP_ADDR environment variable.


If you want to use Podman instead of Docker engine you will need to follow
these steps:
1. Enable and start podman on your host
```bash
systemctl --user enable podman
systemctl --user start podman
```
2. `export DBTESTTOOLS_USE_PODMAN=1` variable.

This code has been in use daily on a large project at Cisco for a few years
now, and is very stable.

Expand Down
22 changes: 21 additions & 1 deletion dbtesttools/engines/postgres.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,26 @@ def has_savepoint(self):
def setUp(self):
"""Do all the work to bring up a working Postgres fixture."""
super().setUp()
# Podman integration: optionally override Docker socket
if os.getenv("DBTESTTOOLS_USE_PODMAN") == "1":
if sys.platform == "darwin":
# MacOS Podman socket
podman_socket = (
f"unix://{os.getenv('HOME')}"
f"/.local/share/containers/podman/machine/podman.sock"
)
else:
# Linux Podman socket
podman_socket = (
f"unix:///run/user/{os.getuid()}/podman/podman.sock"
)
if os.path.exists(podman_socket.replace("unix://", "")):
os.environ["DOCKER_HOST"] = podman_socket
else:
raise FileNotFoundError(
f"Podman socket not found at {podman_socket}"
)

self.client = docker.from_env()
self.pull_image()
self.find_free_port()
Expand Down Expand Up @@ -156,7 +176,7 @@ def set_up_test_database(self):
cur.close()
c.close()

@retry(psycopg2.OperationalError, tries=30, delay=1)
@retry(psycopg2.OperationalError, tries=60, delay=1)
def wait_for_pg_start(self):
c = psycopg2.connect(
"user='postgres' host='{ip}' port='{port}'"
Expand Down
66 changes: 66 additions & 0 deletions dbtesttools/tests/test_pgfixture.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import os
import unittest
import warnings

import testscenarios
import testtools
from sqlalchemy import text

from dbtesttools.engines.postgres import PostgresContainerFixture

warnings.filterwarnings("ignore", category=UserWarning, module="urllib3")


class TestPostgresContainer(
testscenarios.TestWithScenarios, testtools.TestCase
):
"""Test that we can bring up a Postgres container."""

scenarios = [
("docker", {"podman": False}),
("podman", {"podman": True}),
]

def setUp(self):
self.pg_fixture = None
super().setUp()
if self.podman:
os.environ["DBTESTTOOLS_USE_PODMAN"] = "1"
else:
os.environ.pop("DBTESTTOOLS_USE_PODMAN", None)
try:
fixture = PostgresContainerFixture(future=True)
fixture.setUp()
self.pg_fixture = fixture
except Exception as e:
print(f'[ERROR] Failed to start Postgres fixture: {e}')

def tearDown(self):
if self.pg_fixture is not None:
if hasattr(self.pg_fixture, "engine"):
try:
self.pg_fixture.engine.dispose()
except Exception as e:
print(f"Warning: failed to dispose engine: {e}")
if hasattr(self.pg_fixture, "container"):
try:
self.pg_fixture.container.kill()
except Exception as e:
print(f"Warning: failed to kill container: {e}")
super().tearDown()

def test_connection(self):
if self.pg_fixture is None:
self.fail("Postgres fixture was not initialized")
# Actually test that the database is reachable
conn = self.pg_fixture.connect()
try:
result = conn.execute(text("SELECT 1;"))
value = result.scalar()
self.assertEqual(value, 1)
finally:
conn.close()


if __name__ == "__main__":
unittest.main()
9 changes: 9 additions & 0 deletions dbtesttools/tests/test_pgfixture_docker.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from .test_pgfixture import TestPostgresContainer

# Override the scenarios to only run Docker
TestPostgresContainer.scenarios = [
("docker", {"podman": False}),
]

# Optional: re-export the test class to make unittest discovery happy
__all__ = ["TestPostgresContainer"]
9 changes: 9 additions & 0 deletions dbtesttools/tests/test_pgfixture_podman.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from .test_pgfixture import TestPostgresContainer

# Override the scenarios to only run Podman
TestPostgresContainer.scenarios = [
("podman", {"podman": True}),
]
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand why you're doing this, and the previous change. The scenarios declaration will run both of these automatically. This change means that the last file to to get imported overrides the scenarios as well, so for example on my machine the only scenario that gets run now is podman.

When running hatch run ci locally it should do exactly the same tests as Workflows would run, which is why the initial workflow file was as simple as it was, with the addition of the Python test matrix (which should arguably be declared in the Hatch matrix, but that's on my list of things to fix later).

In other words, we should not depend on GH Actions to run all of our tests to completion.

Let's go back to basics with this change and simply run up a worker with podman installed. See here https://github.com/marketplace/actions/install-podman for one in the marketplace.

I don't think you need all that docker-ce stuff as well, was there a reason to do it?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah apparently that podman action is deprecated, crap. Back to manual installation.


# Optional: re-export the test class to make unittest discovery happy
__all__ = ["TestPostgresContainer"]