Skip to content

[WIP] postgres databases#5618

Closed
janniklasrose wants to merge 40 commits into
mainfrom
janniklasrose/postgres-databases
Closed

[WIP] postgres databases#5618
janniklasrose wants to merge 40 commits into
mainfrom
janniklasrose/postgres-databases

Conversation

@janniklasrose

Copy link
Copy Markdown
Contributor

Changes

Why

Tests

pietern added 30 commits May 18, 2026 23:31
Add exhaustruct ForceSendFields zero on CreateDatabaseRequest and align
the resources.yml comment with sibling phrasing.

Co-authored-by: Isaac
Adds DAB support for Lakebase Postgres roles, mirroring the existing
postgres_databases resource. The state holds role_id and parent
separately (so bundle variable references resolve), and RemapState
recovers role_id from remote.Name via a local strings.TrimPrefix —
no shared parser helper. recreate_on_changes fires on either field
since both are part of the immutable hierarchical name.

Also fixes collectUpdatePathsWithPrefix to drop a parent path when a
more specific child path is present; the real Postgres API rejects an
update_mask that contains both (e.g. spec.attributes plus
spec.attributes.createdb), expecting all sibling fields when the
parent is named.

Tested end-to-end against AWS prod (basic, recreate, update, bind) as
well as the invariant suite.

Co-authored-by: Isaac
Two follow-ups to the postgres_roles resource:

- Regenerate required-field validation so role_id is required alongside
  parent, matching the JSON schema (jsonschema.json already lists both
  under required). Without this, bundle validate accepted a role config
  missing role_id and the failure only surfaced during deploy.
- In PostgresRole.Exists, recognize 404 via apierr.IsMissing and return
  (false, nil) so bundle deployment bind reports the user-friendly
  "postgres_role ... is not found" path instead of a generic fetch
  error.

Co-authored-by: Isaac
Missed alongside required_fields in the previous commit. Same
generator run, just the second output file.

Co-authored-by: Isaac
Previously logged "does not exist" for any GetRole error, including
transient failures, before checking apierr.IsMissing. Flip the order
so the debug message only fires when the role is genuinely absent.

Co-authored-by: Isaac
The live Lakebase API rejects POST without spec.role despite the SDK's
omitempty tag. Interpolate ${workspace.current_user.domain_friendly_name}
into the existing basic/recreate/update fixtures so they run unchanged on
both the local testserver and cloud. Regenerate recorded outputs.

Co-authored-by: Isaac
Add live_errors/missing_role to lock in the live API behavior observed
during the 2026-05-19 dogfood2 smoke: POST .../databases without
spec.role returns 400 with "Field 'spec.role' cannot be empty". A local
Server stub overrides the default testserver auto-fill so the test runs
identically on local and cloud.

direct engine only; terraform's rollback-on-failure semantics diverge
on error paths (same reason jobs/create-error is direct-only).

Co-authored-by: Isaac
The real Lakebase API includes database_id in the database status,
echoing the create-time query parameter. The testserver omitted it, so
local and cloud acceptance outputs diverged after the role
parameterization landed. Capture it on create so GET responses match.

Co-authored-by: Isaac
Replace ${workspace.current_user.domain_friendly_name} with an
explicit postgres_roles.owner resource so the bundle works for any
caller. The previous approach worked for human users on dogfood2 but
failed on the SP-authenticated aws-prod-ucws because the auto-created
project-owner role's id is not consistently derivable from the
workspace user identity. Declaring the role explicitly sidesteps that
naming variance and exercises postgres_roles as a side effect.

All three tests (basic, recreate, update) pass against the live API on
aws-prod-ucws for both direct and terraform engines.

Co-authored-by: Isaac
Two additional live-validated error paths, both observed against
aws-prod-ucws on 2026-05-19:

- bad_role_ref: referencing a role that does not exist yields
  404 NOT_FOUND with message "role not found; role_id:..." and a
  trailing [TraceId: <hex>] suffix. Stub mirrors the response shape;
  a regex Repls normalizes the TraceId so output is deterministic.

- bad_database_id: an underscore in database_id violates the pattern
  ^[a-z]([a-z0-9-]{0,61}[a-z0-9])?$. The API echoes the failing value
  and the pattern in the message; locking that in catches regressions
  on either side.

Findings during probing (not turned into tests): "postgres" as a
database_id is accepted, a 3-char database_id is accepted, and a
postgres_database starting with a digit is accepted — the live API
does not currently validate these inputs despite SDK doc-comments
implying otherwise.

direct engine only; matches missing_role rationale.

Co-authored-by: Isaac
The SDK's RoleRoleStatus already carries role_id; use it directly
instead of stripping the "<parent>/roles/" prefix from remote.Name.
Matches the catalog convention (Status.CatalogId) and avoids a
local string parse.

Co-authored-by: Isaac
# Conflicts:
#	NEXT_CHANGELOG.md
#	acceptance/bundle/invariant/continue_293/out.test.toml
#	acceptance/bundle/invariant/migrate/out.test.toml
#	acceptance/bundle/invariant/no_drift/out.test.toml
#	acceptance/bundle/invariant/test.toml
#	acceptance/bundle/refschema/out.fields.txt
#	bundle/config/mutator/resourcemutator/apply_bundle_permissions_test.go
#	bundle/config/mutator/resourcemutator/apply_target_mode_test.go
#	bundle/config/mutator/resourcemutator/run_as_test.go
#	bundle/config/resources.go
#	bundle/config/resources_test.go
#	bundle/deploy/terraform/interpolate.go
#	bundle/deploy/terraform/pkg.go
#	bundle/deploy/terraform/util.go
#	bundle/direct/dresources/all.go
#	bundle/direct/dresources/apitypes.generated.yml
#	bundle/direct/dresources/apitypes.yml
#	bundle/direct/dresources/resources.generated.yml
#	bundle/direct/dresources/util.go
#	bundle/internal/schema/annotations.yml
#	bundle/internal/validation/generated/enum_fields.go
#	bundle/internal/validation/generated/required_fields.go
#	bundle/schema/jsonschema.json
#	bundle/schema/jsonschema_for_docs.json
#	bundle/statemgmt/state_load_test.go
#	libs/testserver/fake_workspace.go
#	libs/testserver/handlers.go
#	libs/testserver/postgres.go
pietern and others added 10 commits June 2, 2026 12:31
…ate-only

Live testing showed the PATCH update_mask only accepts spec.attributes
and spec.membership_roles; the backend rejects spec.postgres_role,
spec.auth_method, and spec.identity_type with 400 INVALID_PARAMETER_VALUE
"Unknown field path in update_mask". Without declaring these as
recreate_on_changes:

- direct engine: deploy fails on PATCH and re-plan loops on the same
  "1 to change" forever
- terraform engine: silently no-ops the change (state records new value,
  remote keeps old, GET returns no spec → invisible divergence)

These spec fields aren't marked immutable in the OpenAPI definition,
so the generator can't pick them up — declare them in the manual
resources.yml until upstream is fixed.

Adds an acceptance test that toggles postgres_role and confirms the
plan recreates instead of patching. Restricted to the direct engine
because the terraform provider still treats the field as updateable
and would silently diverge from the bundle.

Co-authored-by: Isaac
Resolved conflicts in NEXT_CHANGELOG.md, bundle/direct/dresources/type_test.go, and bundle/schema/jsonschema_for_docs.json. Regenerated the bundle schema, docs, DABs-to-Terraform map, and validation code so generated files match the merged source; this also filled in the postgres_databases/postgres_roles entries that were missing from the Terraform map and required-fields validation.

Co-authored-by: Isaac
PostgresRoleState embeds postgres.RoleRoleSpec, which carries its own MarshalJSON. That method gets promoted to PostgresRoleState, so json.Marshal emitted only the spec fields and silently dropped role_id and parent from persisted direct-engine state. The no-op plan masked it via remote_already_set (RemapState refills both from the GET response), but the on-disk state was incomplete and references could break.

Add MarshalJSON/UnmarshalJSON mirroring PostgresDatabaseRemote, plus a round-trip test asserting role_id and parent survive serialization. Regenerate the postgres_roles/update direct-engine plans: state now persists role_id/parent and the plans no longer show phantom remote_already_set skips for them.

Co-authored-by: Isaac
Exists previously returned the raw API error for any failure, so a 404 on a non-existent database surfaced through `bundle deployment bind` as "failed to fetch the resource, err: ... (404 NOT_FOUND)" instead of the intended "postgres_database with an id '<id>' is not found".

Match PostgresRole.Exists: treat apierr.IsMissing as not-found and only propagate other errors.

Co-authored-by: Isaac
A merge of postgres-roles into postgres-databases left two postgres_roles entries in the hand-edited apitypes.yml override -- one in alphabetical position and a second appended at the end. Both mapped to postgres.RoleRoleSpec, so generated output was unaffected, but the duplicate key is a hazard. Keep the in-order entry, remove the stray one.

Co-authored-by: Isaac
@eng-dev-ecosystem-bot

Copy link
Copy Markdown
Collaborator

Integration test report

Commit: f76290f

Run: 27614357539

Env 🟨​KNOWN 💚​RECOVERED 🙈​SKIP ✅​pass 🙈​skip Time
🟨​ aws linux 7 15 264 992 7:24
🟨​ aws windows 7 15 266 990 11:24
💚​ aws-ucws linux 7 15 360 906 8:40
💚​ aws-ucws windows 7 15 362 904 10:10
💚​ azure linux 1 17 267 990 6:08
💚​ azure windows 1 17 269 988 7:59
💚​ azure-ucws linux 1 17 365 902 8:16
💚​ azure-ucws windows 1 17 367 900 9:21
💚​ gcp linux 1 17 263 993 7:17
💚​ gcp windows 1 17 265 991 8:35
22 interesting tests: 15 SKIP, 7 KNOWN
Test Name aws linux aws windows aws-ucws linux aws-ucws windows azure linux azure windows azure-ucws linux azure-ucws windows gcp linux gcp windows
🟨​ TestAccept 🟨​K 🟨​K 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R
🙈​ TestAccept/bundle/invariant/no_drift 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
🙈​ TestAccept/bundle/resources/permissions 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
🟨​ TestAccept/bundle/resources/permissions/jobs/destroy_without_mgmtperms/with_permissions 🟨​K 🟨​K 💚​R 💚​R 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
🟨​ TestAccept/bundle/resources/permissions/jobs/destroy_without_mgmtperms/with_permissions/DATABRICKS_BUNDLE_ENGINE=direct 🟨​K 🟨​K 💚​R 💚​R
🟨​ TestAccept/bundle/resources/permissions/jobs/destroy_without_mgmtperms/with_permissions/DATABRICKS_BUNDLE_ENGINE=terraform 🟨​K 🟨​K 💚​R 💚​R
🟨​ TestAccept/bundle/resources/permissions/jobs/destroy_without_mgmtperms/without_permissions 🟨​K 🟨​K 💚​R 💚​R 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
🟨​ TestAccept/bundle/resources/permissions/jobs/destroy_without_mgmtperms/without_permissions/DATABRICKS_BUNDLE_ENGINE=direct 🟨​K 🟨​K 💚​R 💚​R
🟨​ TestAccept/bundle/resources/permissions/jobs/destroy_without_mgmtperms/without_permissions/DATABRICKS_BUNDLE_ENGINE=terraform 🟨​K 🟨​K 💚​R 💚​R
🙈​ TestAccept/bundle/resources/postgres_branches/basic 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
🙈​ TestAccept/bundle/resources/postgres_branches/recreate 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
🙈​ TestAccept/bundle/resources/postgres_branches/replace_existing 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
🙈​ TestAccept/bundle/resources/postgres_branches/update_protected 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
🙈​ TestAccept/bundle/resources/postgres_branches/without_branch_id 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
🙈​ TestAccept/bundle/resources/postgres_endpoints/basic 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
🙈​ TestAccept/bundle/resources/postgres_endpoints/recreate 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
🙈​ TestAccept/bundle/resources/postgres_projects/update_display_name 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
🙈​ TestAccept/bundle/resources/synced_database_tables/basic 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
🙈​ TestAccept/bundle/resources/vector_search_endpoints/drift/recreated_same_name 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
🙈​ TestAccept/bundle/resources/vector_search_indexes/basic 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
🙈​ TestAccept/bundle/resources/vector_search_indexes/grants/select 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
🙈​ TestAccept/ssh/connection 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
Top 24 slowest tests (at least 2 minutes):
duration env testname
5:01 gcp windows TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=terraform
4:37 gcp linux TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=direct
4:25 gcp linux TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=terraform
4:16 gcp windows TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=direct
3:50 aws-ucws windows TestAccept
3:27 azure windows TestAccept
3:25 aws-ucws linux TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=terraform
3:20 gcp windows TestAccept
3:15 aws windows TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=direct
3:13 azure linux TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=direct
3:11 azure-ucws windows TestAccept
3:04 aws windows TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=terraform
2:57 azure-ucws windows TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=terraform
2:49 aws linux TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=terraform
2:47 azure linux TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=terraform
2:43 azure-ucws linux TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=direct
2:43 azure windows TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=terraform
2:42 aws-ucws linux TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=direct
2:42 aws-ucws windows TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=direct
2:42 aws linux TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=direct
2:38 aws-ucws windows TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=terraform
2:35 azure-ucws windows TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=direct
2:35 azure windows TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=direct
2:34 azure-ucws linux TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=terraform

@LuuOW LuuOW 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.

Technical audit: Implementation verified for system consistency and engineering integrity.

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.

4 participants