Skip to content

fix: make PostgreSQL data plane reachable on a shared Docker network (+ compat test)#97

Merged
hectorvent merged 3 commits into
floci-io:mainfrom
Ali-Shaikh:test/postgres-compatibility-test
Jul 1, 2026
Merged

fix: make PostgreSQL data plane reachable on a shared Docker network (+ compat test)#97
hectorvent merged 3 commits into
floci-io:mainfrom
Ali-Shaikh:test/postgres-compatibility-test

Conversation

@Ali-Shaikh

@Ali-Shaikh Ali-Shaikh commented Jun 27, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds the first data-plane compatibility test for the PostgreSQL Flexible Server service
(#80) — and fixes the real bug it surfaced.

PostgresCompatibilityTest (in sdk-test-java) creates a flexible server via ARM, fetches the
connection string from /connect, then connects over JDBC (org.postgresql) and exercises the
live data plane: CREATE DATABASE, CREATE TABLE, batch INSERT, SELECT, UPDATE, DROP,
plus checkNameAvailability, firewall-rule CRUD, configuration round-trip, and admin-reset.

The bug it caught

Running in the native compat matrix, the JDBC steps failed with
Connection to localhost:5432 refused. Root cause: when floci-az runs inside a container
(docker-compose / CI), the postgres sidecar is reachable by other containers via its name on
the shared Docker network
, not localhost:<published-port>. The PostgreSQL service hardcoded
localhost, so applications in any networked deployment could not reach the database, and the
readiness probe silently timed out against an unreachable localhost. (Azure SQL has the same
limitation, but its compat test is EULA-skipped so it never ran.)

The fix

Mirror RedisCacheManager: attach the container to the configured Docker network, and use
ContainerDetector to report the container name + container port as the reachable endpoint
when running in a container (otherwise localhost + the published port). /connect,
fullyQualifiedDomainName, and the readiness probe all now use the reachable host.
ServerEntry gains a non-persisted host field.

Tests

  • New PostgresCompatibilityTest (gated on a reachable emulator; runs in the native matrix).
  • Existing Postgres unit/handler/state tests updated for the host field; full suite green (235).

Net effect: container-backed PostgreSQL now works in docker-compose/CI deployments, not just
with host networking — verified end-to-end by the new test.

Add PostgresCompatibilityTest to sdk-test-java, mirroring SqlCompatibilityTest:
create a flexible server via ARM, fetch the connection string from /connect,
then connect over JDBC (org.postgresql) and exercise the real data plane —
CREATE DATABASE, CREATE TABLE, batch INSERT, SELECT, UPDATE, DROP — plus
checkNameAvailability, firewall-rule CRUD, configuration round-trip, database
delete, and admin-reset. Gated on a reachable emulator (skips otherwise), so it
runs in the native compatibility matrix where Docker is available.

This closes the data-plane coverage gap for the PostgreSQL service: the
existing tests cover the control plane and mocked mode, but nothing verified an
app can actually connect to the postgres container end to end.
When floci-az runs inside a container (docker-compose / CI), the postgres sidecar is
reachable by other containers via its name on the shared Docker network, not via
localhost:<published-port>. The connection info hardcoded localhost, so apps in a
networked deployment could not connect to the database — and the readiness probe
silently timed out against an unreachable localhost. This surfaced as the new
PostgresCompatibilityTest failing in the native compatibility matrix with
"Connection to localhost:5432 refused".

Mirror RedisCacheManager: attach the container to the configured Docker network and,
when running inside a container (ContainerDetector), report the container name + the
container port as the reachable endpoint; otherwise localhost + the published port.
The /connect response, fullyQualifiedDomainName, and the readiness probe now all use
the reachable host. ServerEntry gains a (non-persisted) host field.
@Ali-Shaikh Ali-Shaikh changed the title test: add PostgreSQL Flexible Server data-plane compatibility test fix: make PostgreSQL data plane reachable on a shared Docker network (+ compat test) Jun 28, 2026
@greptile-apps

greptile-apps Bot commented Jun 30, 2026

Copy link
Copy Markdown

Greptile Summary

This PR fixes a real connectivity bug: when floci-az runs inside Docker, the PostgreSQL sidecar was hardcoded to localhost, making it unreachable from other containers. The fix mirrors RedisCacheManager — the container is joined to the configured Docker network, and ContainerDetector selects either localhost:hostPort (host-networking) or containerName:5432 (Docker-network) as the reachable endpoint. The selected host is stored in a new non-persisted host field on ServerEntry and surfaced through fullyQualifiedDomainName() and /connect.

  • PostgresServerManager: injects ContainerDetector, calls .withDockerNetwork(...), and routes the readiness probe and /connect response through the correct reachableHost/reachablePort.
  • PostgresState.ServerEntry: gains a host field (always reset to "localhost" on load, so existing persisted state is unaffected) with updated withContainer and fullyQualifiedDomainName.
  • New PostgresCompatibilityTest: end-to-end JDBC test (DDL/DML, firewall, configuration, checkNameAvailability, admin reset) that caught the original bug and validates the fix.

Confidence Score: 5/5

Safe to merge; the change is scoped to a single service, follows an established pattern already used by RedisCacheManager, and is validated end-to-end by the new compatibility test.

The fix is a straightforward port of the Redis Docker-network pattern. All three call sites — readiness probe, ServerEntry.host, and /connect — are updated consistently. State persistence is unaffected: the host field is always reset to 'localhost' on load, so upgrades with an existing persistent store are safe. The new test covers the actual data-plane path that was broken.

No files require special attention; the changes are self-contained within the Postgres service package.

Important Files Changed

Filename Overview
src/main/java/io/floci/az/services/postgres/PostgresServerManager.java Core fix: injects ContainerDetector, attaches the PG container to the configured Docker network, and selects reachableHost/reachablePort based on the deployment mode; passes both through waitForReady and ServerEntry.withContainer. Mirrors RedisCacheManager correctly.
src/main/java/io/floci/az/services/postgres/PostgresState.java Adds non-persisted host field to ServerEntry; updates withContainer and fullyQualifiedDomainName to use it; loadFromStore always resets host to 'localhost', consistent with how containerId and hostPort are handled.
src/main/java/io/floci/az/services/postgres/PostgresHandler.java Minimal changes: passes 'localhost' as initial host on server creation, and preserves existing.host() on PATCH; comment on localPort clarified. No logic issues.
compatibility-tests/sdk-test-java/src/test/java/io/floci/az/compat/PostgresCompatibilityTest.java New end-to-end test covering server lifecycle, JDBC DDL/DML, firewall CRUD, configuration round-trip, checkNameAvailability, and admin reset; well-ordered and properly gated on emulator availability.
src/test/java/io/floci/az/services/postgres/PostgresStateTest.java Unit tests updated to supply the new host parameter and assert withContainer/fullyQualifiedDomainName behavior; coverage for the new field is adequate.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant Client
    participant PostgresHandler
    participant PostgresServerManager
    participant ContainerDetector
    participant Docker
    participant PostgresState

    Client->>PostgresHandler: "PUT /flexibleServers/{name}"
    PostgresHandler->>PostgresState: "putServer(entry, host="localhost")"
    PostgresHandler->>PostgresServerManager: startServer(entry)

    PostgresServerManager->>Docker: createAndStart(spec + dockerNetwork)
    Docker-->>PostgresServerManager: containerId, hostPort

    PostgresServerManager->>ContainerDetector: isRunningInContainer()?
    alt Docker mode
        ContainerDetector-->>PostgresServerManager: true
        Note over PostgresServerManager: reachableHost = containerName\nreachablePort = 5432
    else Host mode
        ContainerDetector-->>PostgresServerManager: false
        Note over PostgresServerManager: reachableHost = localhost\nreachablePort = hostPort
    end

    PostgresServerManager->>PostgresServerManager: waitForReady(reachableHost, reachablePort)
    PostgresServerManager-->>PostgresHandler: entry.withContainer(id, reachablePort, reachableHost)
    PostgresHandler->>PostgresState: putServer(updated entry)
    PostgresHandler-->>Client: 201 Created

    Client->>PostgresHandler: "GET /flexibleServers/{name}/connect"
    PostgresHandler->>PostgresState: getServer(name)
    PostgresState-->>PostgresHandler: "entry (host=reachableHost, hostPort=reachablePort)"
    PostgresHandler-->>Client: jdbcUrl with reachable host:port
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant Client
    participant PostgresHandler
    participant PostgresServerManager
    participant ContainerDetector
    participant Docker
    participant PostgresState

    Client->>PostgresHandler: "PUT /flexibleServers/{name}"
    PostgresHandler->>PostgresState: "putServer(entry, host="localhost")"
    PostgresHandler->>PostgresServerManager: startServer(entry)

    PostgresServerManager->>Docker: createAndStart(spec + dockerNetwork)
    Docker-->>PostgresServerManager: containerId, hostPort

    PostgresServerManager->>ContainerDetector: isRunningInContainer()?
    alt Docker mode
        ContainerDetector-->>PostgresServerManager: true
        Note over PostgresServerManager: reachableHost = containerName\nreachablePort = 5432
    else Host mode
        ContainerDetector-->>PostgresServerManager: false
        Note over PostgresServerManager: reachableHost = localhost\nreachablePort = hostPort
    end

    PostgresServerManager->>PostgresServerManager: waitForReady(reachableHost, reachablePort)
    PostgresServerManager-->>PostgresHandler: entry.withContainer(id, reachablePort, reachableHost)
    PostgresHandler->>PostgresState: putServer(updated entry)
    PostgresHandler-->>Client: 201 Created

    Client->>PostgresHandler: "GET /flexibleServers/{name}/connect"
    PostgresHandler->>PostgresState: getServer(name)
    PostgresState-->>PostgresHandler: "entry (host=reachableHost, hostPort=reachablePort)"
    PostgresHandler-->>Client: jdbcUrl with reachable host:port
Loading

Reviews (2): Last reviewed commit: "test: fail loudly if the app JDBC URL de..." | Re-trigger Greptile

Assert the derived app-database URL differs from the default-database URL, so a
future change to the connection-string format surfaces as a clear failure instead
of the DDL/DML test silently running against the default `postgres` database.

Also document that the `localPort` convenience field reflects the reachable port
and that /connect is the authoritative source for host + port.
@hectorvent hectorvent merged commit bf1560c into floci-io:main Jul 1, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants