Skip to content

feat: PostgREST image registry and schema preflight (PLAT-499, PLAT-500)#300

Open
moizpgedge wants to merge 3 commits intomainfrom
feat/PLAT-499/PLAT-500/PostgREST-prerequisites/image-wiring
Open

feat: PostgREST image registry and schema preflight (PLAT-499, PLAT-500)#300
moizpgedge wants to merge 3 commits intomainfrom
feat/PLAT-499/PLAT-500/PostgREST-prerequisites/image-wiring

Conversation

@moizpgedge
Copy link
Copy Markdown
Contributor

@moizpgedge moizpgedge commented Mar 17, 2026

Register PostgREST container images and add schema/role preflight validation

Pins PostgREST to 14.5 from the pgEdge registry (ghcr.io/pgedge/postgrest:14.5). The bare ref lets serviceImageTag prepend the configured ImageRepositoryHost, consistent with how MCP images are registered.

Add schema/role preflight validation before provisioning PostgREST. Before the plan step, the workflow connects to the primary Postgres instance and verifies that all configured schemas and the anonymous role exist. Missing objects produce a clear error, e.g.:

schema "api" does not exist in database "storefront";
create it before deploying PostgREST

PLAT-499, PLAT-500

Summary

Registers the PostgREST service image (ghcr.io/pgedge/postgrest:14.5) and adds a preflight check that validates required schemas and the anonymous role exist in Postgres before PostgREST provisioning begins. Also extends the service version pattern to accept two-part versions (e.g., 14.5) in both the Goa design and server-side validation.

All PostgREST config fields are optional with sensible defaults — db_schemas defaults to "public" and db_anon_role defaults to "pgedge_application_read_only". A minimal PostgREST deployment requires only "config": {}.
Optional:-

  "config": {
          "db_schemas": "api",
          "db_anon_role": "web_anon"
        }

Changes

  • Register postgrest service image pinned to 14.5 via ghcr.io/pgedge/postgrest.
  • Extend serviceVersionPattern and semverPattern to accept two-part versions (\d+.\d+) in addition to three-part semver and latest.
  • Add ValidatePostgRESTPrereqs activity: connects to the primary instance (with Patroni-based primary discovery and reachable-instance fallback) and checks all configured schemas and the anonymous role exist.
  • Run preflight in UpdateDatabase workflow before the plan step; invalid config fails fast with a clear error.
  • All PostgREST config fields are optional: db_schemas → "public", db_anon_role → "pgedge_application_read_only", db_pool → 10, max_rows → 1000.

Testing

go test ./server/internal/orchestrator/swarm/... -v
go test ./server/internal/api/apiv1/... -v
go test ./server/internal/workflows/...

Checklist

  • Tests added or updated (unit and/or e2e, as needed)
  • Documentation updated (if needed)
  • Issue is linked (branch name or URL in PR description)
  • Changelog entry added for user-facing behavior changes
  • Breaking changes (if any) are clearly called out in the PR description

Notes for Reviewers

Preflight connects read-only; falls back to the first reachable instance if primary lookup via Patroni fails (stale data edge case)
"config": {} is valid and sufficient for a standard PostgREST deployment against the public schema

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 17, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This pull request adds support for the PostgREST service version 14.5, including service image configuration, updates service version validation patterns to accept two-part version numbers, and introduces a new PostgRESTPreflightResource for pre-deployment schema validation. A helper function refactor extracts shared database connection logic.

Changes

Cohort / File(s) Summary
PostgREST Service Image Support
server/internal/orchestrator/swarm/service_images.go, server/internal/orchestrator/swarm/service_images_test.go
Added PostgREST service image version 14.5 to service versions registry and corresponding test coverage including version validation and constraint checks.
Service Version Pattern Updates
api/apiv1/design/database.go, server/internal/api/apiv1/validate.go, server/internal/api/apiv1/validate_test.go
Broadened service version regex pattern from strict three-segment SemVer to allow two-segment versions (e.g., "14.5"), updated API documentation and examples, and added validation test case for two-part version format.
PostgREST Preflight Resource
server/internal/orchestrator/swarm/postgrest_preflight_resource.go, server/internal/orchestrator/swarm/resources.go
Introduced new PostgRESTPreflightResource type with schema and role existence validation logic; registered resource type in control-plane resource registry.
Connection Logic Refactoring
server/internal/orchestrator/swarm/service_user_role.go
Extracted primary database connection logic into reusable package-level connectToPrimaryDB helper function to reduce duplication across resource types.

Poem

🐰 PostgREST hops in, version 14.5 so fine,
Two-part versions dance in patterns all mine,
Schemas validated before deployment's call,
Connection helpers shared by one and all! 🌿

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 14.81% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main changes: adding PostgREST image registry support and schema preflight validation for the specified Jira issues.
Description check ✅ Passed The description follows the template with all major sections completed: summary, detailed changes, testing commands, and checklist. Issues are clearly linked and notes for reviewers are provided.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/PLAT-499/PLAT-500/PostgREST-prerequisites/image-wiring

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@server/internal/orchestrator/swarm/service_images_test.go`:
- Around line 109-114: The test case named "postgrest service has versions"
currently only checks the length; change it to assert the actual returned
versions from SupportedServiceVersions("postgrest") rather than just count:
collect the map keys returned by SupportedServiceVersions, sort them (or compare
as an unordered set) and assert they equal the expected set {"latest","v14.5"}
so the test fails if any other two-element combination is returned; update the
assertion in the test function in service_images_test.go accordingly (use the
test helper or testify/reflect-based equality as appropriate).

In `@server/internal/workflows/activities/validate_postgrest_prereqs.go`:
- Around line 134-150: The code currently falls back to db.Instances[0] when
Patroni discovery fails; instead, iterate db.Instances and pick the first
instance whose connection info successfully resolves via
a.DatabaseService.GetInstanceConnectionInfo (and optionally verify
reachability), then assign its InstanceID to primaryInstanceID. Update the
fallback block so that if primaryInstanceID == "" you loop through db.Instances,
call a.DatabaseService.GetInstanceConnectionInfo(ctx, databaseID,
inst.InstanceID), use the first call that returns no error (and/or a lightweight
reachability check) to set primaryInstanceID, rather than unconditionally using
db.Instances[0]; keep references to patroni.NewClient and
database.GetPrimaryInstanceID unchanged.

In `@server/internal/workflows/update_database.go`:
- Around line 106-117: The code currently ignores parseErrs from
ParsePostgRESTServiceConfig and only runs preflight when parseErrs is empty;
change this to fail fast when parseErrs is non-empty: if
ParsePostgRESTServiceConfig returns any parseErrs, return an error (wrap via
handleError) describing the invalid PostgREST config and include parseErrs
details so the workflow stops before planning; keep the existing preflight call
(ExecuteValidatePostgRESTPrereqs with ValidatePostgRESTPrereqsInput using
cfg.DBSchemas and cfg.DBAnonRole) for the valid-path case.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: fc966f26-8fa1-4b2b-acdb-4b3e618ba438

📥 Commits

Reviewing files that changed from the base of the PR and between e35708d and b8b714d.

📒 Files selected for processing (5)
  • server/internal/orchestrator/swarm/service_images.go
  • server/internal/orchestrator/swarm/service_images_test.go
  • server/internal/workflows/activities/activities.go
  • server/internal/workflows/activities/validate_postgrest_prereqs.go
  • server/internal/workflows/update_database.go

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Registers PostgREST service images in the Swarm service image registry and adds a preflight workflow activity that validates required Postgres schemas and the configured anonymous role exist before provisioning PostgREST.

Changes:

  • Add ValidatePostgRESTPrereqs workflow activity to connect to the database and verify configured schemas + anon role exist.
  • Run the PostgREST preflight checks in UpdateDatabase before planning/provisioning.
  • Register PostgREST service image versions and extend Swarm service image tests accordingly.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
server/internal/workflows/update_database.go Runs PostgREST prerequisite validation before the plan/apply phases.
server/internal/workflows/activities/validate_postgrest_prereqs.go New activity that connects to a DB instance and checks schemas + anon role existence.
server/internal/workflows/activities/activities.go Registers the new ValidatePostgRESTPrereqs activity with the worker.
server/internal/orchestrator/swarm/service_images.go Adds PostgREST image entries (latest, v14.5) to the service image registry.
server/internal/orchestrator/swarm/service_images_test.go Adds/updates tests to cover PostgREST image resolution and version listing expectations.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@server/internal/workflows/activities/validate_postgrest_prereqs.go`:
- Around line 160-163: The current call to
DatabaseService.GetInstanceConnectionInfo(databaseID, primaryInstanceID) returns
an error and bails out immediately; instead, when that call fails, fall back to
the previously-captured reachable connection info (e.g., reachableConnInfo /
reachableInstanceID) for the read-only checks: attempt to assign connInfo =
reachableConnInfo and proceed (optionally log or warn that primary lookup failed
and reachable instance is being used); only return the wrapped error if no
reachableConnInfo is available. Update the logic around connInfo,
primaryInstanceID, and reachableConnInfo in the ValidatePostgrestPrereqs flow to
prefer the primary result but accept the reachable fallback on error.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: b76c1ad0-1ff4-4562-aa87-401f010c42b8

📥 Commits

Reviewing files that changed from the base of the PR and between b8b714d and 389adf9.

📒 Files selected for processing (4)
  • server/internal/orchestrator/swarm/service_images.go
  • server/internal/orchestrator/swarm/service_images_test.go
  • server/internal/workflows/activities/validate_postgrest_prereqs.go
  • server/internal/workflows/update_database.go

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

@moizpgedge moizpgedge requested a review from rshoemaker March 17, 2026 15:20
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
server/internal/api/apiv1/validate.go (1)

260-264: Consider updating the error message to reflect two-part version support.

The error message references "semver format (e.g., '1.0.0')" but the validation now also accepts two-part versions like "14.5". Consider updating the message to be more accurate:

-		err := errors.New("version must be in semver format (e.g., '1.0.0') or 'latest'")
+		err := errors.New("version must be in format 'X.Y' or 'X.Y.Z' (e.g., '14.5', '1.0.0') or 'latest'")
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@server/internal/api/apiv1/validate.go` around lines 260 - 264, The validation
error message for svc.Version is out of date: update the string created where
svc.Version is checked (the block using semverPattern, newValidationError,
appendPath, and errs) to accurately state that versions may be semver (e.g.,
"1.0.0"), two-part versions (e.g., "14.5"), or "latest" so the message matches
the accepted formats.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@server/internal/api/apiv1/validate.go`:
- Around line 260-264: The validation error message for svc.Version is out of
date: update the string created where svc.Version is checked (the block using
semverPattern, newValidationError, appendPath, and errs) to accurately state
that versions may be semver (e.g., "1.0.0"), two-part versions (e.g., "14.5"),
or "latest" so the message matches the accepted formats.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 90ed306b-89ae-47e2-b987-2070738d8d56

📥 Commits

Reviewing files that changed from the base of the PR and between 389adf9 and e80ba4c.

⛔ Files ignored due to path filters (7)
  • api/apiv1/gen/control_plane/service.go is excluded by !**/gen/**
  • api/apiv1/gen/http/control_plane/client/types.go is excluded by !**/gen/**
  • api/apiv1/gen/http/control_plane/server/types.go is excluded by !**/gen/**
  • api/apiv1/gen/http/openapi.json is excluded by !**/gen/**
  • api/apiv1/gen/http/openapi.yaml is excluded by !**/gen/**
  • api/apiv1/gen/http/openapi3.json is excluded by !**/gen/**
  • api/apiv1/gen/http/openapi3.yaml is excluded by !**/gen/**
📒 Files selected for processing (6)
  • api/apiv1/design/database.go
  • server/internal/api/apiv1/validate.go
  • server/internal/api/apiv1/validate_test.go
  • server/internal/orchestrator/swarm/service_images.go
  • server/internal/orchestrator/swarm/service_images_test.go
  • server/internal/workflows/activities/validate_postgrest_prereqs.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • server/internal/orchestrator/swarm/service_images_test.go
  • server/internal/workflows/activities/validate_postgrest_prereqs.go

@moizpgedge moizpgedge force-pushed the feat/PLAT-499/PLAT-500/PostgREST-prerequisites/image-wiring branch from 99689f0 to e80ba4c Compare March 18, 2026 14:49
@moizpgedge
Copy link
Copy Markdown
Contributor Author

moizpgedge commented Mar 19, 2026

Post Pr for PLAT-501/502/503

PR:-#303

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
server/internal/orchestrator/swarm/postgrest_preflight_resource.go (1)

74-81: Logger obtained but never used; error silently discarded.

The logger is retrieved but not used anywhere in the validate method. Additionally, the error from do.Invoke is discarded with _, which could mask DI configuration issues.

Consider either:

  1. Using the logger to log validation progress/results (e.g., schemas being checked, successful validation)
  2. Removing the logger invocation if it's not needed
♻️ Option A: Remove unused logger
 func (r *PostgRESTPreflightResource) validate(ctx context.Context, rc *resource.Context) error {
-	logger, _ := do.Invoke[zerolog.Logger](rc.Injector)
-
-	conn, err := connectToPrimaryDB(ctx, rc, r.DatabaseID, r.DatabaseName, logger)
+	conn, err := connectToPrimaryDB(ctx, rc, r.DatabaseID, r.DatabaseName, zerolog.Nop())
 	if err != nil {
 		return fmt.Errorf("preflight: failed to connect to database: %w", err)
 	}
♻️ Option B: Use logger and handle error
 func (r *PostgRESTPreflightResource) validate(ctx context.Context, rc *resource.Context) error {
-	logger, _ := do.Invoke[zerolog.Logger](rc.Injector)
+	logger, err := do.Invoke[zerolog.Logger](rc.Injector)
+	if err != nil {
+		return fmt.Errorf("failed to get logger: %w", err)
+	}
+	logger = logger.With().
+		Str("service_id", r.ServiceID).
+		Str("database_id", r.DatabaseID).
+		Logger()
+	logger.Debug().Msg("running PostgREST preflight validation")

 	conn, err := connectToPrimaryDB(ctx, rc, r.DatabaseID, r.DatabaseName, logger)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@server/internal/orchestrator/swarm/postgrest_preflight_resource.go` around
lines 74 - 81, The call to do.Invoke[zerolog.Logger](rc.Injector) in
PostgRESTPreflightResource.validate either needs its error handled and the
logger used, or the unused invocation removed; update the validate function to
call do.Invoke and check the returned error (not discard it) and then use the
logger to emit at least a start/ok/failure message around connectToPrimaryDB and
validation steps (include the database id/name from r.DatabaseID/r.DatabaseName
in logs), or simply delete the do.Invoke line if no logger is required—ensure
any retained logger variable is referenced so the compiler won’t flag it and
that DI errors from do.Invoke are returned instead of ignored.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@server/internal/orchestrator/swarm/postgrest_preflight_resource.go`:
- Around line 74-81: The call to do.Invoke[zerolog.Logger](rc.Injector) in
PostgRESTPreflightResource.validate either needs its error handled and the
logger used, or the unused invocation removed; update the validate function to
call do.Invoke and check the returned error (not discard it) and then use the
logger to emit at least a start/ok/failure message around connectToPrimaryDB and
validation steps (include the database id/name from r.DatabaseID/r.DatabaseName
in logs), or simply delete the do.Invoke line if no logger is required—ensure
any retained logger variable is referenced so the compiler won’t flag it and
that DI errors from do.Invoke are returned instead of ignored.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 11b93e64-2be7-4519-ad38-e99af521a779

📥 Commits

Reviewing files that changed from the base of the PR and between 99689f0 and c1b42cd.

📒 Files selected for processing (3)
  • server/internal/orchestrator/swarm/postgrest_preflight_resource.go
  • server/internal/orchestrator/swarm/resources.go
  • server/internal/orchestrator/swarm/service_user_role.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • server/internal/orchestrator/swarm/resources.go

- Pin postgrest image to ghcr.io/pgedge/postgrest:14.5
- Extend version pattern to accept two-part versions (e.g. 14.5)
- Add PostgRESTPreflightResource with PrimaryExecutor to validate
  that configured schemas and anon role exist before provisioning
- Refresh() returns ErrNotFound so the check re-runs on every apply
- Extract connectToPrimaryDB as a package-level helper shared between
  ServiceUserRole and PostgRESTPreflightResource
- All config fields optional: db_schemas defaults to "public",
  db_anon_role to "pgedge_application_read_only"

PLAT-499, PLAT-500
@moizpgedge moizpgedge force-pushed the feat/PLAT-499/PLAT-500/PostgREST-prerequisites/image-wiring branch from c1b42cd to f8de470 Compare March 24, 2026 13:26
Copy link
Copy Markdown
Member

@jason-lynch jason-lynch left a comment

Choose a reason for hiding this comment

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

Nice! This is looking pretty close.

- Remove connectToPrimaryDB helper; use database.GetPrimaryInstance +
  primary.Connection as the standard connection pattern
- Fix permadrift in PostgRESTPreflightResource.Refresh: run validation
  and only return ErrNotFound when prerequisites fail, avoiding
  unnecessary updates to dependent resources on every apply cycle
- Clean up unused imports (pgx, errors, time, certificates, patroni)

PLAT-499, PLAT-500
Copy link
Copy Markdown
Member

@jason-lynch jason-lynch left a comment

Choose a reason for hiding this comment

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

Awesome!

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.

3 participants