Skip to content

feat: make database name, owner, and admin role configurable#54

Merged
mmols merged 5 commits into
mainfrom
feat/PLAT-446/config-users-db
Apr 17, 2026
Merged

feat: make database name, owner, and admin role configurable#54
mmols merged 5 commits into
mainfrom
feat/PLAT-446/config-users-db

Conversation

@mmols
Copy link
Copy Markdown
Member

@mmols mmols commented Apr 16, 2026

Summary

Makes the database name, owner, and admin role configurable via Helm values instead of being hardcoded to app/admin. All defaults are unchanged — existing deployments see no difference.

Changes

Templates

  • init-spock-job.yamlDB_NAME and ADMIN_USER env vars read from values instead of hardcoded
  • cert-manager-cert-ca.yamlcommonName for app-client-cert and admin-client-cert templated from initdb.owner and pgEdge.adminUser

Values

  • Added pgEdge.adminUser (default: admin)

Go code

  • config.go — reads ADMIN_USER env var with admin as default

Docs

  • Added "Users and authentication" section to configuration docs:
    • Role table explaining all managed users and their purpose
    • Customizing database name and owner
    • Customizing the admin role
    • Creating additional users via CloudNativePG managed roles
    • Password authentication: per-node passwords, aligning passwords across nodes, disabling passwords
  • Removed stale "App database name" limitation from limitations.md

Tests

  • Unit: custom DB_NAME, ADMIN_USER, cert commonName assertions
  • E2E: TestCustomDatabaseInstall — deploys with custom database (mydb), owner (myuser), and admin (dbadmin), verifies cert auth for both custom admin and owner, spock initialization on custom database
  • Added ExecSQLAs helper for specifying user and database in psql calls

Backwards compatibility

Verified via helm template diff — the only change with default values is the addition of ADMIN_USER: admin env var in the init-spock job. All other rendered output is identical.

Test plan

  • Unit tests pass (35/35)
  • E2E TestCustomDatabaseInstall — custom database, owner, and admin all connect and spock initializes
  • Existing E2E tests unaffected

Addresses #42.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 16, 2026

📝 Walkthrough

Walkthrough

Adds a configurable pgEdge.adminUser Helm value (default "admin"), surfaces it in templates and docs, makes config load ADMIN_USER from the environment, and adds unit/integration tests and fixtures for custom database/admin configurations; also updates user/auth documentation and removes an outdated database limitation.

Changes

Cohort / File(s) Summary
Values & Config loader
values.yaml, internal/config/config.go, internal/config/config_test.go
Added Helm value pgEdge.adminUser (default "admin"). Load reads ADMIN_USER env var with "admin" fallback. Added tests for default and overridden AdminUser.
Documentation
README.md, README.md.gotmpl, docs/configuration.md, docs/configuration.md.gotmpl, docs/limitations.md, changes/unreleased/...
Added "Users and authentication" section, documented managed roles/auth methods, password behavior, how to rename admin via pgEdge.adminUser, reworded Spock wording, removed the "App database name" limitation, and added changelog entry.
Helm templates
templates/cert-manager-cert-ca.yaml, templates/init-spock-job.yaml
Replaced hardcoded certificate commonNames with chart values (admin{{ .Values.pgEdge.adminUser }}, app{{ .Values.pgEdge.clusterSpec.bootstrap.initdb.owner }}). Init-spock job now reads DB_NAME from clusterSpec.bootstrap.initdb.database and injects ADMIN_USER from pgEdge.adminUser.
Tests, helpers & fixtures
test/pkg/kube/kube.go, test/integration/install_test.go, test/unit/job_test.go, test/integration/testdata/custom-database-values.yaml, test/unit/testdata/custom-database-values.yaml
Added ExecSQLWith helper and updated ExecSQL to default to admin/app. Added integration test TestCustomDatabaseInstall, unit tests for rendered manifests/env/Certificates, and matching test value files for a custom DB/admin (mydb/dbadmin).

Poem

🐇 I hop through charts and gentle code,
Admin name now finds its road,
Templates sing and tests agree,
Databases dance, certs set free,
A rabbit cheers—configured mode!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 7.14% 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 and concisely describes the main change: making database name, owner, and admin role configurable via Helm values instead of hardcoded.
Description check ✅ Passed The description is comprehensive and directly related to the changeset, detailing templates, values, code, documentation, and test changes that align with the PR objectives.

✏️ 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-446/config-users-db

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

@mmols mmols changed the title feat: make database name, owner, and admin role configurable (#42) feat: make database name, owner, and admin role configurable Apr 16, 2026
@codacy-production
Copy link
Copy Markdown

codacy-production Bot commented Apr 16, 2026

Up to standards ✅

🟢 Issues 1 medium

Results:
1 new issue

Category Results
Complexity 1 medium

View in Codacy

🟢 Metrics 19 duplication

Metric Results
Duplication 19

View in Codacy

TIP This summary will be updated as you push new changes. Give us feedback

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: 5

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

Inline comments:
In `@docs/configuration.md.gotmpl`:
- Around line 77-78: The docs currently hardcode per-node password secret names
as `pgedge-n#-app` and `pgedge-n#-admin` which conflicts with configurable role
names; update the table rows for the "app" (owner) and "admin" entries so the
secret name examples reflect the configurable role (e.g., `pgedge-n#-<role>` or
explicitly reference the configuration keys `initdb.owner` and
`pgEdge.adminUser`) and mention the actual role name will be substituted from
those settings rather than being fixed to "app" or "admin".

In `@templates/cert-manager-cert-ca.yaml`:
- Line 110: The templated commonName values in
templates/cert-manager-cert-ca.yaml (the commonName entries that use
.Values.pgEdge.adminUser and the other commonName at the second occurrence)
should be wrapped with the Helm quote filter to avoid YAML parsing edge cases;
locate the commonName template uses and apply the quote filter to those .Values
references so user-provided strings with special characters or YAML-ambiguous
content are rendered safely.

In `@templates/init-spock-job.yaml`:
- Around line 25-27: The DB_NAME and ADMIN_USER env values are not quoted;
update the template to use the Helm quote filter on both values so they match
the style of APP_NAME and INIT_SPOCK_TIMEOUT — locate the DB_NAME env entry
(value using .Values.pgEdge.clusterSpec.bootstrap.initdb.database) and the
ADMIN_USER env entry (value using .Values.pgEdge.adminUser) and change them to
use the | quote filter.

In `@test/integration/install_test.go`:
- Around line 272-297: The two tests custom_admin_can_connect and
custom_owner_can_connect race with cert-manager; before calling ConnectWithCert
you should wait for the corresponding certificate to be issued/ready. Add a
readiness wait for "admin-client-cert" before the ConnectWithCert call in
custom_admin_can_connect and for "app-client-cert" in custom_owner_can_connect
(e.g. call wait.ForCertificateReady(testKube, "admin-client-cert", timeout) and
wait.ForCertificateReady(testKube, "app-client-cert", timeout) or the equivalent
testKube.WaitForCertificateReady helper) and fail the test with a clear message
if the wait errors.

In `@test/unit/job_test.go`:
- Around line 164-172: The test currently slices hba with hba[:3] which will
panic if fewer than 3 entries are present; modify the assertion in the test (the
hba variable usage in the test function) to guard against short slices by
computing a safe iteration bound (e.g., n := min(3, len(hba))) and iterate up to
n, and if len(hba) < 3 call t.Fatalf or t.Errorf with a clear message so missing
entries produce a test failure instead of a panic.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f2b912f2-a076-4ca4-9908-89ddc66a75a2

📥 Commits

Reviewing files that changed from the base of the PR and between 7a11168 and ca780b4.

📒 Files selected for processing (15)
  • README.md
  • README.md.gotmpl
  • docs/configuration.md
  • docs/configuration.md.gotmpl
  • docs/limitations.md
  • internal/config/config.go
  • internal/config/config_test.go
  • templates/cert-manager-cert-ca.yaml
  • templates/init-spock-job.yaml
  • test/integration/install_test.go
  • test/integration/testdata/custom-database-values.yaml
  • test/pkg/kube/kube.go
  • test/unit/job_test.go
  • test/unit/testdata/custom-database-values.yaml
  • values.yaml
💤 Files with no reviewable changes (1)
  • docs/limitations.md

Comment thread docs/configuration.md.gotmpl Outdated
Comment thread templates/cert-manager-cert-ca.yaml Outdated
Comment thread templates/init-spock-job.yaml Outdated
Comment thread test/integration/install_test.go
Comment thread test/unit/job_test.go
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)
test/unit/job_test.go (1)

91-142: Consider extracting a shared helper for Job container env parsing.

NestedSlice(...containers...) and env-map extraction are repeated; a small test helper would reduce duplication and make failures easier to interpret.

♻️ Optional refactor sketch
+func getJobEnvMap(t *testing.T, job unstructured.Unstructured) map[string]string {
+	t.Helper()
+	containers, found, _ := unstructured.NestedSlice(job.Object, "spec", "template", "spec", "containers")
+	if !found || len(containers) == 0 {
+		t.Fatal("no containers found in job")
+	}
+	container := containers[0].(map[string]interface{})
+	envVars, _, _ := unstructured.NestedSlice(container, "env")
+	out := map[string]string{}
+	for _, e := range envVars {
+		env := e.(map[string]interface{})
+		name, nOK := env["name"].(string)
+		val, vOK := env["value"].(string)
+		if nOK && vOK {
+			out[name] = val
+		}
+	}
+	return out
+}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/unit/job_test.go` around lines 91 - 142, Extract the duplicated
container/env parsing into a small test helper (e.g., getJobContainerEnvMap or
extractContainerEnvMap) that takes the rendered objects or a Job unstructured
object and returns a map[string]string of env names->values; replace the
duplicated blocks in TestInitSpockJob and TestInitSpockJobCustomDBName to call
this helper instead of repeating
unstructured.NestedSlice(...,"spec","template","spec","containers") and the
envMap population loop, preserving the existing error checks (no containers
found) inside the helper so callers can simply assert on the returned map
values.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@test/unit/job_test.go`:
- Around line 91-142: Extract the duplicated container/env parsing into a small
test helper (e.g., getJobContainerEnvMap or extractContainerEnvMap) that takes
the rendered objects or a Job unstructured object and returns a
map[string]string of env names->values; replace the duplicated blocks in
TestInitSpockJob and TestInitSpockJobCustomDBName to call this helper instead of
repeating unstructured.NestedSlice(...,"spec","template","spec","containers")
and the envMap population loop, preserving the existing error checks (no
containers found) inside the helper so callers can simply assert on the returned
map values.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 476ed851-2631-4554-9b70-d549992cad67

📥 Commits

Reviewing files that changed from the base of the PR and between ca780b4 and d1b487f.

📒 Files selected for processing (7)
  • changes/unreleased/Added-20260416-155036.yaml
  • docs/configuration.md
  • docs/configuration.md.gotmpl
  • templates/cert-manager-cert-ca.yaml
  • templates/init-spock-job.yaml
  • test/integration/install_test.go
  • test/unit/job_test.go
✅ Files skipped from review due to trivial changes (2)
  • changes/unreleased/Added-20260416-155036.yaml
  • docs/configuration.md.gotmpl
🚧 Files skipped from review as they are similar to previous changes (2)
  • templates/init-spock-job.yaml
  • docs/configuration.md

@mmols mmols requested a review from rshoemaker April 17, 2026 13:50
Copy link
Copy Markdown
Contributor

@rshoemaker rshoemaker left a comment

Choose a reason for hiding this comment

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

Looks good - couple minor questions/suggestions.

Comment thread test/pkg/kube/kube.go
Comment thread docs/configuration.md.gotmpl
mmols added 5 commits April 17, 2026 15:15
The init-spock job hardcoded DB_NAME=app and the admin user was
hardcoded throughout templates and Go code. This made it impossible
to customize the database name, owner, or admin role without forking.

- Template DB_NAME and ADMIN_USER in the init-spock job from values
- Template cert commonName for app and admin certs from values
- Add pgEdge.adminUser value (default: admin)
- Read ADMIN_USER env var in Go config with admin as default
- Add users and authentication docs addressing #42
- Add unit and E2E tests for custom database/owner/admin overrides
- Quote DB_NAME, ADMIN_USER, and cert commonName template values
- Use dynamic secret names in docs for configurable roles
- Add certificate readiness gate before cert-auth E2E assertions
- Guard hba[:3] slice against panic on short arrays
@mmols mmols force-pushed the feat/PLAT-446/config-users-db branch from d1b487f to a7fb246 Compare April 17, 2026 20:15
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 (2)
test/unit/job_test.go (1)

110-142: Consider extracting env parsing into a shared test helper.

The env-to-map conversion pattern is likely reusable and will reduce repetition across Job env assertions.

♻️ Optional refactor
+func envMapFromContainer(t *testing.T, container map[string]interface{}) map[string]string {
+	t.Helper()
+	envVars, found, _ := unstructured.NestedSlice(container, "env")
+	if !found {
+		t.Fatal("expected env list in job container")
+	}
+	out := map[string]string{}
+	for _, e := range envVars {
+		env, ok := e.(map[string]interface{})
+		if !ok {
+			continue
+		}
+		name, _ := env["name"].(string)
+		val, _ := env["value"].(string)
+		if name != "" {
+			out[name] = val
+		}
+	}
+	return out
+}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/unit/job_test.go` around lines 110 - 142, Extract the env-to-map
conversion in TestInitSpockJobCustomDBName into a shared test helper (e.g.,
envSliceToMap or parseEnvVarsToMap) that accepts the envVars slice (the variable
currently built from unstructured.NestedSlice) and returns a map[string]string;
then replace the inline loop that builds envMap in TestInitSpockJobCustomDBName
with a call to that helper, and reuse this helper in other tests that perform
similar env assertions (helpers should be placed in the same package test file
or a _test_helpers.go file so TestInitSpockJobCustomDBName, renderTemplate, and
filterByKind can call it).
test/integration/install_test.go (1)

318-327: Use an exact boolean/row-count assertion for app DB absence.

strings.Contains(out, "1") is workable but loose. An exact EXISTS/COUNT check makes failures less ambiguous.

✅ Suggested tightening
-		out, err := testKube.ExecSQLWith(pod,
-			"SELECT 1 FROM pg_database WHERE datname = 'app';", "dbadmin", "mydb")
+		out, err := testKube.ExecSQLWith(pod,
+			"SELECT EXISTS (SELECT 1 FROM pg_database WHERE datname = 'app');", "dbadmin", "mydb")
 		if err != nil {
 			t.Fatalf("failed to query pg_database: %v", err)
 		}
-		if strings.Contains(out, "1") {
-			t.Error("expected 'app' database to not exist, but it does")
+		if strings.TrimSpace(out) != "f" {
+			t.Errorf("expected app database to not exist, got: %q", out)
 		}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/integration/install_test.go` around lines 318 - 327, The test uses a
loose substring check on ExecSQLWith output to assert the absence of the "app"
DB; change the SQL to a deterministic count or exists check (e.g., "SELECT
COUNT(*) FROM pg_database WHERE datname = 'app';" or "SELECT EXISTS(SELECT 1
FROM pg_database WHERE datname = 'app');"), call testKube.ExecSQLWith (same call
site in the t.Run "default_app_database_does_not_exist" using
getPodName("pgedge-n1")), parse the returned output into an integer/boolean, and
assert exactly that the count == 0 or exists == false instead of using
strings.Contains(out, "1").
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@test/integration/install_test.go`:
- Around line 318-327: The test uses a loose substring check on ExecSQLWith
output to assert the absence of the "app" DB; change the SQL to a deterministic
count or exists check (e.g., "SELECT COUNT(*) FROM pg_database WHERE datname =
'app';" or "SELECT EXISTS(SELECT 1 FROM pg_database WHERE datname = 'app');"),
call testKube.ExecSQLWith (same call site in the t.Run
"default_app_database_does_not_exist" using getPodName("pgedge-n1")), parse the
returned output into an integer/boolean, and assert exactly that the count == 0
or exists == false instead of using strings.Contains(out, "1").

In `@test/unit/job_test.go`:
- Around line 110-142: Extract the env-to-map conversion in
TestInitSpockJobCustomDBName into a shared test helper (e.g., envSliceToMap or
parseEnvVarsToMap) that accepts the envVars slice (the variable currently built
from unstructured.NestedSlice) and returns a map[string]string; then replace the
inline loop that builds envMap in TestInitSpockJobCustomDBName with a call to
that helper, and reuse this helper in other tests that perform similar env
assertions (helpers should be placed in the same package test file or a
_test_helpers.go file so TestInitSpockJobCustomDBName, renderTemplate, and
filterByKind can call it).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 53a15342-046f-467f-b22c-731d67825dd9

📥 Commits

Reviewing files that changed from the base of the PR and between d1b487f and a7fb246.

📒 Files selected for processing (16)
  • README.md
  • README.md.gotmpl
  • changes/unreleased/Added-20260416-155036.yaml
  • docs/configuration.md
  • docs/configuration.md.gotmpl
  • docs/limitations.md
  • internal/config/config.go
  • internal/config/config_test.go
  • templates/cert-manager-cert-ca.yaml
  • templates/init-spock-job.yaml
  • test/integration/install_test.go
  • test/integration/testdata/custom-database-values.yaml
  • test/pkg/kube/kube.go
  • test/unit/job_test.go
  • test/unit/testdata/custom-database-values.yaml
  • values.yaml
💤 Files with no reviewable changes (1)
  • docs/limitations.md
✅ Files skipped from review due to trivial changes (8)
  • README.md
  • changes/unreleased/Added-20260416-155036.yaml
  • README.md.gotmpl
  • internal/config/config_test.go
  • templates/cert-manager-cert-ca.yaml
  • test/integration/testdata/custom-database-values.yaml
  • docs/configuration.md.gotmpl
  • test/unit/testdata/custom-database-values.yaml
🚧 Files skipped from review as they are similar to previous changes (5)
  • values.yaml
  • templates/init-spock-job.yaml
  • internal/config/config.go
  • docs/configuration.md
  • test/pkg/kube/kube.go

@mmols mmols merged commit 1b331bb into main Apr 17, 2026
5 checks passed
@mmols mmols deleted the feat/PLAT-446/config-users-db branch April 17, 2026 20:32
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