Skip to content

feat: add pg_hba_conf/pg_ident_conf to db spec#399

Merged
moizpgedge merged 2 commits into
mainfrom
Feat/PLAT-615/Allow-users-to-manage-pg_hba.conf-and-pg_ident.conf-via-the-Database-spec
Jun 4, 2026
Merged

feat: add pg_hba_conf/pg_ident_conf to db spec#399
moizpgedge merged 2 commits into
mainfrom
Feat/PLAT-615/Allow-users-to-manage-pg_hba.conf-and-pg_ident.conf-via-the-Database-spec

Conversation

@moizpgedge

@moizpgedge moizpgedge commented Jun 4, 2026

Copy link
Copy Markdown
Contributor

PR: feat: add pg_hba_conf/pg_ident_conf to db spec

Add user-managed pg_hba_conf and pg_ident_conf fields to the database spec, at both the database and per-node level, as arrays of strings (matching Patroni's pg_hba/pg_ident contract).

  • Per-node entries are prepended to database-level entries, giving node rules first-match priority while keeping the database-level list as the baseline. Empty/unset is identical to today's behavior.
  • Add a pg_hba/pg_ident line parser (postgres/hba) and minimal spec-time validation: reject hba_file/ident_file in postgresql_conf (they would make the entries ineffective) and lines that fail to parse, surfacing the offending line. No enum-style restriction on auth methods.

Scope: spec contract, parser, and validation only. The orchestrator generators do not yet emit these entries into the generated pg_hba.conf/pg_ident.conf; that lands in a follow-up.

See internal-design-docs: pg_hba / pg_ident User Configuration.

PLAT-615


Summary

Adds pg_hba_conf and pg_ident_conf to the database spec (database-level and per-node) so operators can supply their own authentication rules. This PR covers the spec contract, a pg_hba/pg_ident parser, and validation only — the entries are accepted, validated, stored, and round-tripped, but are not yet emitted into the generated config (follow-up ticket).

Changes

  • API design (api/apiv1/design/database.go): add pg_hba_conf and pg_ident_conf (ArrayOf(String)) to DatabaseSpec and DatabaseNodeSpec; regenerated Goa code and OpenAPI.
  • Domain model (server/internal/database/spec.go): add both fields to Spec, Node, and InstanceSpec (and their Clone() helpers); prepend-merge node-level entries ahead of database-level entries in NodeInstances().
  • API conversion (server/internal/api/apiv1/convert.go): map the new fields in both directions, at the database and node level.
  • Parser (server/internal/postgres/hba/parse.go): new ParseEntry (pg_hba) and ParseIdent (pg_ident) with a quote-aware tokenizer; structural validation only (connection type + field count), no auth-method enum restriction.
  • Validation (server/internal/api/apiv1/validate.go): reject hba_file/ident_file GUCs and unparseable lines (surfacing the offending line); wired into both validateDatabaseSpec and validateNode, so it runs on create and update.
  • Tests: parser unit tests, prepend-merge test, and validation cases (valid entries, unparseable lines, forbidden GUCs).

Testing

# build / static checks
go build ./...
go vet ./server/internal/...
make lint                       # golangci-lint: 0 issues

# unit tests (incl. golden tests — no regression)
go test ./server/internal/postgres/hba/... \
        ./server/internal/database/... \
        ./server/internal/api/apiv1/...

# API codegen is in sync
make -C api generate            # only the new fields change (Go); OpenAPI example churn is goa faker noise

Manual verification against a local dev cluster (restish):

  • Create with pg_hba_conf/pg_ident_conf (db-level + node-level) → accepted; get-database round-trips the exact entries.
  • Update an existing database's entries → accepted (modifyingavailable); changes round-trip.
  • Validation on both create and update: invalid pg_hba line, invalid pg_ident line, and hba_file/ident_file GUC each return 400 invalid_input with the offending line/key; a non-standard auth method (ldap2) is accepted.
  • Confirmed the running container's pg_hba.conf is unchanged (entries not yet emitted — expected for this scope).

Checklist

  • Tests added or updated (unit and/or e2e, as needed)
  • Documentation updated (if needed) — deferred to the follow-up ticket, when the entries take effect
  • Issue is linked (branch name or URL in PR description)
  • Changelog entry added for user-facing behavior changes — deferred until the feature is user-effective (entries emitted into the file)
  • Breaking changes (if any) are clearly called out in the PR description — none; new fields are optional and additive

Notes for Reviewers

  • Scope boundary: this is ticket 1 of the feature. The fields are validated and stored but the generators (swarm/common) do not yet write them into pg_hba.conf/pg_ident.conf, and there is no reload yet. That is the follow-up ticket.
  • Generated diff: api/apiv1/gen/http/openapi3.* shows a large diff because goa's example faker reseeds and reshuffles every placeholder when the design changes. It is deterministic and expected (a clean design regenerates to zero diff). Happy to split the regenerated gen/** into its own commit if that helps review.
  • Validation is intentionally minimal per the design doc (admins own these files): only hba_file/ident_file and unparseable lines are rejected — no auth-method enum, no trust-on-broad-CIDR block, no duplicate or map= cross-ref checks. PostgreSQL surfaces deeper errors at reload time.
  • Backward compatible: empty/unset fields produce byte-identical output to today (the new struct fields use omitempty; golden tests are unchanged).

Result:-

moiz@PGEDGE-ML-Moiz work %  restish control-plane-local-1 get-database pghba
HTTP/1.1 200 OK
Content-Length: 943
Content-Type: application/json
Date: Thu, 04 Jun 2026 14:16:41 GMT

{
  created_at: "2026-06-04T14:15:37Z"
  id: "pghba"
  instances: [
    {
      created_at: "2026-06-04T14:15:40Z"
      host_id: "host-1"
      id: "pghba-n1-689qacsi"
      node_name: "n1"
      postgres: {
        patroni_state: "running"
        role: "primary"
        version: "18.4"
      }
      spock: {
        read_only: "off"
        version: "5.0.8"
      }
      state: "available"
      status_updated_at: "2026-06-04T14:16:39Z"
      updated_at: "2026-06-04T14:16:04Z"
    }
  ]
  spec: {
    database_name: "testdb"
    database_users: [
      {
        attributes: ["LOGIN", "SUPERUSER"]
        db_owner: true
        username: "admin"
      }
      {
        attributes: ["LOGIN"]
        db_owner: false
        username: "myapp_user"
      }
    ]
    nodes: [
      {
        host_ids: ["host-1"]
        name: "n1"
        pg_hba_conf: ["host testdb myapp_user 10.0.0.0/8 scram-sha-256"]
      }
    ]
    pg_hba_conf: [
      "hostssl all myapp_user 203.0.113.0/24 scram-sha-256"
      "host all all 0.0.0.0/0 ldap2"
    ]
    pg_ident_conf: ["ssl_users CN=alice,O=example alice"]
    postgres_version: "18.4"
    spock_version: "5"
  }
  state: "available"
  updated_at: "2026-06-04T14:15:37Z"
}
moiz@PGEDGE-ML-Moiz work % 

Add user-managed `pg_hba_conf` and `pg_ident_conf` fields to the
database spec, at both the database and per-node level, as arrays of
strings (matching Patroni's `pg_hba`/`pg_ident` contract).

- Per-node entries are **prepended** to database-level entries, giving
  node rules first-match priority while keeping the database-level list
  as the baseline. Empty/unset is identical to today's behavior.
- Add a pg_hba/pg_ident line parser (`postgres/hba`) and minimal
  spec-time validation: reject `hba_file`/`ident_file` in
  `postgresql_conf` (they would make the entries ineffective) and lines
  that fail to parse, surfacing the offending line. No enum-style
  restriction on auth methods.

Scope: spec contract, parser, and validation only. The orchestrator
generators do not yet emit these entries into the generated
`pg_hba.conf`/`pg_ident.conf`; that lands in a follow-up.

See internal-design-docs: pg_hba / pg_ident User Configuration.

PLAT-615
@coderabbitai

coderabbitai Bot commented Jun 4, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 58c48e65-ee3b-4786-b195-9c670f0c5cb0

📥 Commits

Reviewing files that changed from the base of the PR and between 95d3497 and 498491a.

📒 Files selected for processing (3)
  • server/internal/database/spec.go
  • server/internal/postgres/hba/parse.go
  • server/internal/postgres/hba/parse_test.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • server/internal/postgres/hba/parse_test.go
  • server/internal/postgres/hba/parse.go

📝 Walkthrough

Walkthrough

Adds pg_hba.conf and pg_ident.conf support: new parsers, model fields on Node/Spec/Instance, API schema and conversion, instance-level merging (node-first), and validation with tests for parsing and configuration errors.

Changes

HBA/Ident Configuration Pipeline

Layer / File(s) Summary
PostgreSQL HBA/Ident parsers and tests
server/internal/postgres/hba/parse.go, server/internal/postgres/hba/parse_test.go
Tokenize and parse pg_hba.conf and pg_ident.conf lines (handles quoted fields and comments), expose ParseEntry/ParseIdent/IsComment, and test valid/invalid inputs.
Database model: Node/Spec/Instance fields & merging
server/internal/database/spec.go, server/internal/database/spec_test.go
Add PgHbaConf and PgIdentConf slices to Node, Spec, and InstanceSpec; update Clone() deep-copy behavior; Spec.NodeInstances() concatenates node-level slices before spec-level slices for instance output; tests verify merge behavior.
API schema additions
api/apiv1/design/database.go
Add pg_hba_conf and pg_ident_conf attributes to DatabaseNodeSpec and DatabaseSpec with examples and insertion/precedence documentation.
API ↔ internal model conversion
server/internal/api/apiv1/convert.go
Map PgHbaConf and PgIdentConf between API models and internal database models in both directions.
Validation wiring and tests
server/internal/api/apiv1/validate.go, server/internal/api/apiv1/validate_test.go
Import hba parsers; add validators that reject hba_file/ident_file GUCs and parse non-comment pg_hba_conf/pg_ident_conf lines at database and node scope, returning path-wrapped errors; tests cover acceptance and precise rejection messages.

Poem

🐇 I nibbled quotes and stripped the hash,
Merged node-first rules in a tidy cache,
Parsers hum, validators keep the gate,
HBA and ident now find their fate,
A rabbit's hop made configs straight.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 37.50% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main change: adding pg_hba_conf and pg_ident_conf fields to the database specification.
Description check ✅ Passed The PR description comprehensively covers all required template sections with detailed summary, changes, testing steps, and checklist items properly addressed.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch Feat/PLAT-615/Allow-users-to-manage-pg_hba.conf-and-pg_ident.conf-via-the-Database-spec

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.

@codacy-production

codacy-production Bot commented Jun 4, 2026

Copy link
Copy Markdown

Up to standards ✅

🟢 Issues 2 medium

Results:
2 new issues

Category Results
Complexity 2 medium

View in Codacy

🟢 Metrics 35 complexity · 4 duplication

Metric Results
Complexity 35
Duplication 4

View in Codacy

NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

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/database/spec_test.go (1)

597-609: ⚡ Quick win

Add prepend-order assertion for PgIdentConf in the same override scenario.

This subtest verifies prepend priority for PgHbaConf but not for PgIdentConf. Since both are merged independently, this should be asserted too.

Suggested test update
 t.Run("node entries are prepended ahead of database-level entries", func(t *testing.T) {
 	nodeHba := "host example myapp_user 10.0.0.0/8 scram-sha-256"
+	nodeIdent := "ssl_users CN=bob bob"
 	s := spec(
-		&database.Node{Name: "n1", HostIDs: []string{"host-1"}, PgHbaConf: []string{nodeHba}},
+		&database.Node{
+			Name:        "n1",
+			HostIDs:     []string{"host-1"},
+			PgHbaConf:   []string{nodeHba},
+			PgIdentConf: []string{nodeIdent},
+		},
 		&database.Node{Name: "n2", HostIDs: []string{"host-2"}},
 	)
 	nodes, err := s.NodeInstances()
 	assert.NoError(t, err)
 	// n1: node entry first, then database-level entry (first-match priority).
 	assert.Equal(t, append([]string{nodeHba}, dbHba...), nodes[0].Instances[0].PgHbaConf)
+	assert.Equal(t, append([]string{nodeIdent}, dbIdent...), nodes[0].Instances[0].PgIdentConf)
 	// n2: unchanged, inherits database-level list only.
 	assert.Equal(t, dbHba, nodes[1].Instances[0].PgHbaConf)
+	assert.Equal(t, dbIdent, nodes[1].Instances[0].PgIdentConf)
 })
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@server/internal/database/spec_test.go` around lines 597 - 609, The test adds
an assertion ensuring node-level PgHbaConf entries are prepended before
database-level entries but misses the equivalent check for PgIdentConf; update
the subtest in spec_test.go (the anonymous t.Run that calls spec(...),
s.NodeInstances(), and inspects nodes) to also assert that for node "n1" the
Instances[0].PgIdentConf equals append([]string{<nodeIdentEntry>}, dbIdent...)
(and that node "n2" still equals dbIdent), i.e. mirror the PgHbaConf checks for
PgIdentConf using the same setup of database.Node with a node-level PgIdentConf
entry so both merge behaviors are validated. Ensure you reference the same
variables used in the test (spec, database.Node, NodeInstances, nodes,
dbHba/dbIdent) when adding the assertions.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@server/internal/database/spec_test.go`:
- Around line 597-609: The test adds an assertion ensuring node-level PgHbaConf
entries are prepended before database-level entries but misses the equivalent
check for PgIdentConf; update the subtest in spec_test.go (the anonymous t.Run
that calls spec(...), s.NodeInstances(), and inspects nodes) to also assert that
for node "n1" the Instances[0].PgIdentConf equals
append([]string{<nodeIdentEntry>}, dbIdent...) (and that node "n2" still equals
dbIdent), i.e. mirror the PgHbaConf checks for PgIdentConf using the same setup
of database.Node with a node-level PgIdentConf entry so both merge behaviors are
validated. Ensure you reference the same variables used in the test (spec,
database.Node, NodeInstances, nodes, dbHba/dbIdent) when adding the assertions.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: ab00641f-10a7-4c97-9723-da29e377d6bd

📥 Commits

Reviewing files that changed from the base of the PR and between 3fb1e15 and 95d3497.

⛔ Files ignored due to path filters (9)
  • api/apiv1/gen/control_plane/service.go is excluded by !**/gen/**
  • api/apiv1/gen/http/control_plane/client/encode_decode.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/encode_decode.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 (8)
  • api/apiv1/design/database.go
  • server/internal/api/apiv1/convert.go
  • server/internal/api/apiv1/validate.go
  • server/internal/api/apiv1/validate_test.go
  • server/internal/database/spec.go
  • server/internal/database/spec_test.go
  • server/internal/postgres/hba/parse.go
  • server/internal/postgres/hba/parse_test.go

@jason-lynch jason-lynch left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Nice work! All of my comments are really minor, and the parser implementation is neat!

Since you're adding the API fields here we'll want to hold off on the release until this feature is complete. I'm on board with that since we have a few other in-flight changes we want to include in the release as well.

Comment thread server/internal/database/spec.go Outdated
Comment thread server/internal/postgres/hba/parse.go Outdated
Comment thread server/internal/postgres/hba/parse.go Outdated
Comment thread server/internal/postgres/hba/parse.go Outdated
Comment thread server/internal/postgres/hba/parse_test.go Outdated
Comment thread server/internal/postgres/hba/parse_test.go Outdated
Comment thread server/internal/postgres/hba/parse_test.go
Code-review cleanups for the pg_hba_conf/pg_ident_conf spec work. No
behavior change.

- Merge node-level and database-level entries with `slices.Concat`
  instead of a custom helper. Node entries come first to keep
  first-match priority.
- Use `ds.Set` for the pg_hba connection-type set instead of a plain
  `map[EntryType]struct{}`.
- Drop the redundant `strings.Contains` guard in `isBareIP`;
  `net.ParseIP` already returns nil for CIDR strings.
- Simplify the tokenizer to flush on `buf.Len() > 0` instead of a
  separate `hasToken` flag.
- Convert the parser tests to testify, asserting specific error
  messages via `require.ErrorContains`.

PLAT-615

@jason-lynch jason-lynch left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Awesome! Thank you for making those changes.

@moizpgedge moizpgedge merged commit 5d283c0 into main Jun 4, 2026
3 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