From be7a252f8e89be9a8cd91a81841cf8a887bb9edd Mon Sep 17 00:00:00 2001 From: Thomas Sapelza Date: Tue, 3 Feb 2026 12:14:51 +0100 Subject: [PATCH 1/3] creating a Schema CR instance should require the Database name in which the schema should be created --- docs/schema.md | 2 + .../crd/schema/SchemaReconciler.java | 6 ++- .../postgresql/crd/schema/SchemaSpec.java | 11 +++++ .../creator/ClusterConnectionCreate.java | 1 + .../persisted/creator/DatabaseCreate.java | 11 ++--- .../persisted/creator/RoleCreate.java | 10 ++--- .../persisted/creator/SchemaCreate.java | 44 +++++++++++++++++-- .../crd/schema/SchemaReconcilerTest.java | 19 ++++++-- 8 files changed, 85 insertions(+), 19 deletions(-) diff --git a/docs/schema.md b/docs/schema.md index 3fd7509..0d08235 100644 --- a/docs/schema.md +++ b/docs/schema.md @@ -7,6 +7,7 @@ The `Schema` Custom Resource Definition (CRD) is responsible for managing Postgr | Field | Type | Description | Required | Immutable | |-----------------|--------------------|----------------------------------------------------------------------------------------------------|----------|-----------| | `clusterRef` | `ClusterReference` | Reference to the `ClusterConnection` to use. | Yes | No | +| `database` | `string` | The name of the database in which the schema is created. | Yes | Yes | | `name` | `string` | The name of the schema to create. | Yes | Yes | | `owner` | `string` | The owner of the schema. | No | No | | `reclaimPolicy` | `string` | The policy for reclaiming the schema when the CR is deleted. Values: `Retain` (Default), `Delete`. | No | No | @@ -35,6 +36,7 @@ metadata: spec: clusterRef: name: my-postgres-connection + database: my_database name: my_schema owner: my_role reclaimPolicy: Retain diff --git a/operator/src/main/java/it/aboutbits/postgresql/crd/schema/SchemaReconciler.java b/operator/src/main/java/it/aboutbits/postgresql/crd/schema/SchemaReconciler.java index 54acc4d..9b56749 100644 --- a/operator/src/main/java/it/aboutbits/postgresql/crd/schema/SchemaReconciler.java +++ b/operator/src/main/java/it/aboutbits/postgresql/crd/schema/SchemaReconciler.java @@ -67,11 +67,12 @@ public UpdateControl reconcile( .rescheduleAfter(60, TimeUnit.SECONDS); } + var database = spec.getDatabase(); var clusterConnection = clusterConnectionOptional.get(); UpdateControl updateControl; - try (var dsl = contextFactory.getDSLContext(clusterConnection)) { + try (var dsl = contextFactory.getDSLContext(clusterConnection, database)) { // Run everything in a single transaction updateControl = dsl.transactionResult( cfg -> reconcileInTransaction( @@ -149,9 +150,10 @@ public DeleteControl cleanup( .rescheduleAfter(60, TimeUnit.SECONDS); } + var database = spec.getDatabase(); var clusterConnection = clusterConnectionOptional.get(); - try (var dsl = contextFactory.getDSLContext(clusterConnection)) { + try (var dsl = contextFactory.getDSLContext(clusterConnection, database)) { schemaService.dropSchema(dsl, spec); return DeleteControl.defaultDelete(); diff --git a/operator/src/main/java/it/aboutbits/postgresql/crd/schema/SchemaSpec.java b/operator/src/main/java/it/aboutbits/postgresql/crd/schema/SchemaSpec.java index 2038ca3..b9568a8 100644 --- a/operator/src/main/java/it/aboutbits/postgresql/crd/schema/SchemaSpec.java +++ b/operator/src/main/java/it/aboutbits/postgresql/crd/schema/SchemaSpec.java @@ -16,6 +16,17 @@ public class SchemaSpec { @Required private ClusterReference clusterRef = new ClusterReference(); + @Required + @ValidationRule( + value = "self == oldSelf", + message = "The Schema database is immutable. Moving a schema to another database requires dumping and restoring the schema to the new database." + ) + @ValidationRule( + value = "self.trim().size() > 0", + message = "The Schema database must not be empty." + ) + private String database = ""; + @Required @ValidationRule( value = "self == oldSelf", diff --git a/operator/src/test/java/it/aboutbits/postgresql/_support/testdata/persisted/creator/ClusterConnectionCreate.java b/operator/src/test/java/it/aboutbits/postgresql/_support/testdata/persisted/creator/ClusterConnectionCreate.java index 0c27ae5..169fd19 100644 --- a/operator/src/test/java/it/aboutbits/postgresql/_support/testdata/persisted/creator/ClusterConnectionCreate.java +++ b/operator/src/test/java/it/aboutbits/postgresql/_support/testdata/persisted/creator/ClusterConnectionCreate.java @@ -82,6 +82,7 @@ protected ClusterConnection create(int index) { ); var spec = new ClusterConnectionSpec(); + spec.setHost(getHost()); spec.setPort(getPort()); spec.setDatabase(getDatabase()); diff --git a/operator/src/test/java/it/aboutbits/postgresql/_support/testdata/persisted/creator/DatabaseCreate.java b/operator/src/test/java/it/aboutbits/postgresql/_support/testdata/persisted/creator/DatabaseCreate.java index 1b0071c..f4988ba 100644 --- a/operator/src/test/java/it/aboutbits/postgresql/_support/testdata/persisted/creator/DatabaseCreate.java +++ b/operator/src/test/java/it/aboutbits/postgresql/_support/testdata/persisted/creator/DatabaseCreate.java @@ -72,15 +72,16 @@ protected Database create(int index) { .build() ); - var spec = new DatabaseSpec(); - spec.setName(name); - spec.setReclaimPolicy(withReclaimPolicy); - spec.setOwner(withOwner); - var clusterRef = new ClusterReference(); clusterRef.setName(getClusterConnectionName()); clusterRef.setNamespace(withClusterConnectionNamespace); + + var spec = new DatabaseSpec(); + spec.setClusterRef(clusterRef); + spec.setName(name); + spec.setReclaimPolicy(withReclaimPolicy); + spec.setOwner(withOwner); item.setSpec(spec); diff --git a/operator/src/test/java/it/aboutbits/postgresql/_support/testdata/persisted/creator/RoleCreate.java b/operator/src/test/java/it/aboutbits/postgresql/_support/testdata/persisted/creator/RoleCreate.java index 8547140..b0d48da 100644 --- a/operator/src/test/java/it/aboutbits/postgresql/_support/testdata/persisted/creator/RoleCreate.java +++ b/operator/src/test/java/it/aboutbits/postgresql/_support/testdata/persisted/creator/RoleCreate.java @@ -93,15 +93,15 @@ protected Role create(int index) { .build() ); - var spec = new RoleSpec(); - spec.setName(name); - spec.setComment(withComment); - var clusterRef = new ClusterReference(); clusterRef.setName(getClusterConnectionName()); clusterRef.setNamespace(withClusterConnectionNamespace); - spec.setClusterRef(clusterRef); + var spec = new RoleSpec(); + + spec.setClusterRef(clusterRef); + spec.setName(name); + spec.setComment(withComment); spec.setPasswordSecretRef(withPasswordSecretRef); if (withFlags != null) { diff --git a/operator/src/test/java/it/aboutbits/postgresql/_support/testdata/persisted/creator/SchemaCreate.java b/operator/src/test/java/it/aboutbits/postgresql/_support/testdata/persisted/creator/SchemaCreate.java index 91ef69c..6792452 100644 --- a/operator/src/test/java/it/aboutbits/postgresql/_support/testdata/persisted/creator/SchemaCreate.java +++ b/operator/src/test/java/it/aboutbits/postgresql/_support/testdata/persisted/creator/SchemaCreate.java @@ -16,6 +16,8 @@ import java.util.concurrent.TimeUnit; +import static it.aboutbits.postgresql.core.ReclaimPolicy.DELETE; + @NullMarked @Setter @Accessors(fluent = true, chain = true) @@ -38,6 +40,9 @@ public class SchemaCreate extends TestDataCreator { @Nullable private String withClusterConnectionNamespace; + @Nullable + private String withDatabase; + private ReclaimPolicy withReclaimPolicy = ReclaimPolicy.RETAIN; @Nullable @@ -72,15 +77,20 @@ protected Schema create(int index) { .build() ); - var spec = new SchemaSpec(); - spec.setName(name); - spec.setReclaimPolicy(withReclaimPolicy); - spec.setOwner(withOwner); + // We have to create the database first which also modifies the specified withClusterConnectionName so the connection points to the newly created DB + var database = getDatabase(); var clusterRef = new ClusterReference(); clusterRef.setName(getClusterConnectionName()); clusterRef.setNamespace(withClusterConnectionNamespace); + + var spec = new SchemaSpec(); + spec.setClusterRef(clusterRef); + spec.setDatabase(database); + spec.setName(name); + spec.setReclaimPolicy(withReclaimPolicy); + spec.setOwner(withOwner); item.setSpec(spec); @@ -139,4 +149,30 @@ private String getClusterConnectionName() { return clusterConnection.getMetadata().getName(); } + + private String getDatabase() { + if (withDatabase != null) { + return withDatabase; + } + + var item = given.one() + .database() + .withClusterConnectionName(getClusterConnectionName()) + .withClusterConnectionNamespace(withClusterConnectionNamespace) + .withReclaimPolicy(DELETE) + .returnFirst(); + + withDatabase = item.getSpec().getName(); + + var clusterConnectionDb = given.one() + .clusterConnection() + .withName(getClusterConnectionName() + "-db") + .withNamespace(withClusterConnectionNamespace) + .withDatabase(withDatabase) + .returnFirst(); + + withClusterConnectionName = clusterConnectionDb.getMetadata().getName(); + + return withDatabase; + } } diff --git a/operator/src/test/java/it/aboutbits/postgresql/crd/schema/SchemaReconcilerTest.java b/operator/src/test/java/it/aboutbits/postgresql/crd/schema/SchemaReconcilerTest.java index f0a87e1..7e1e6b2 100644 --- a/operator/src/test/java/it/aboutbits/postgresql/crd/schema/SchemaReconcilerTest.java +++ b/operator/src/test/java/it/aboutbits/postgresql/crd/schema/SchemaReconcilerTest.java @@ -39,19 +39,32 @@ void resetEnvironment() { @DisplayName("When a Schema is created, it should be reconciled to READY") void createSchema_andStatusReady() { // given - var clusterConnection = given.one() + var clusterConnectionMain = given.one() .clusterConnection() .withName("test-connection-schema") .returnFirst(); + var database = given.one() + .database() + .withClusterConnectionName(clusterConnectionMain.getMetadata().getName()) + .returnFirst(); + + // Creating a second ClusterConnection CR is only required for the tests + var clusterConnectionDb = given.one() + .clusterConnection() + .withName("test-connection-schema-db") + .withDatabase(database.getSpec().getName()) + .returnFirst(); + var now = OffsetDateTime.now(ZoneOffset.UTC); var schemaName = "test-schema"; // when var schema = given.one() .schema() + .withDatabase(database.getName()) .withName(schemaName) - .withClusterConnectionName(clusterConnection.getMetadata().getName()) + .withClusterConnectionName(clusterConnectionDb.getMetadata().getName()) .withReclaimPolicy(DELETE) .returnFirst(); @@ -67,7 +80,7 @@ void createSchema_andStatusReady() { now ); - var dsl = postgreSQLContextFactory.getDSLContext(clusterConnection); + var dsl = postgreSQLContextFactory.getDSLContext(clusterConnectionDb); assertThat(schemaService.schemaExists(dsl, schema.getSpec())).isTrue(); } From 573d179331a47ec4dbf319a03f5276b7cfed9013 Mon Sep 17 00:00:00 2001 From: Thomas Sapelza Date: Tue, 3 Feb 2026 14:18:25 +0100 Subject: [PATCH 2/3] fix failing tests which depend on SchemaCreate --- .../creator/DefaultPrivilegeCreate.java | 24 +++---------------- .../persisted/creator/GrantCreate.java | 18 +++++--------- .../DefaultPrivilegeReconcilerTest.java | 2 ++ .../crd/grant/GrantReconcilerTest.java | 5 ++++ 4 files changed, 16 insertions(+), 33 deletions(-) diff --git a/operator/src/test/java/it/aboutbits/postgresql/_support/testdata/persisted/creator/DefaultPrivilegeCreate.java b/operator/src/test/java/it/aboutbits/postgresql/_support/testdata/persisted/creator/DefaultPrivilegeCreate.java index 8982c99..50b1cf0 100644 --- a/operator/src/test/java/it/aboutbits/postgresql/_support/testdata/persisted/creator/DefaultPrivilegeCreate.java +++ b/operator/src/test/java/it/aboutbits/postgresql/_support/testdata/persisted/creator/DefaultPrivilegeCreate.java @@ -234,29 +234,11 @@ private String getSchema() { return withSchema; } - var databaseName = getDatabase(); - if (databaseName.isBlank()) { - databaseName = given.one() - .database() - .withClusterConnectionName(getClusterConnectionName()) - .withClusterConnectionNamespace(withClusterConnectionNamespace) - .withReclaimPolicy(DELETE) - .returnFirst() - .getSpec() - .getName(); - } - - var clusterConnectionDb = given.one() - .clusterConnection() - .withName(getClusterConnectionName() + "-db") - .withNamespace(withClusterConnectionNamespace) - .withDatabase(databaseName) - .returnFirst(); - var item = given.one() .schema() - .withClusterConnectionName(clusterConnectionDb.getMetadata().getName()) - .withClusterConnectionNamespace(clusterConnectionDb.getMetadata().getNamespace()) + .withClusterConnectionName(getClusterConnectionName()) + .withClusterConnectionNamespace(withClusterConnectionNamespace) + .withDatabase(getDatabase()) .withReclaimPolicy(DELETE) .returnFirst(); diff --git a/operator/src/test/java/it/aboutbits/postgresql/_support/testdata/persisted/creator/GrantCreate.java b/operator/src/test/java/it/aboutbits/postgresql/_support/testdata/persisted/creator/GrantCreate.java index 2cbc55d..4314969 100644 --- a/operator/src/test/java/it/aboutbits/postgresql/_support/testdata/persisted/creator/GrantCreate.java +++ b/operator/src/test/java/it/aboutbits/postgresql/_support/testdata/persisted/creator/GrantCreate.java @@ -236,9 +236,9 @@ private String getSchema() { return withSchema; } - var databaseName = getDatabase(); - if (databaseName.isBlank()) { - databaseName = given.one() + var database = getDatabase(); + if (database.isBlank()) { + database = given.one() .database() .withClusterConnectionName(getClusterConnectionName()) .withClusterConnectionNamespace(withClusterConnectionNamespace) @@ -248,17 +248,11 @@ private String getSchema() { .getName(); } - var clusterConnectionDb = given.one() - .clusterConnection() - .withName(getClusterConnectionName() + "-db") - .withNamespace(withClusterConnectionNamespace) - .withDatabase(databaseName) - .returnFirst(); - var item = given.one() .schema() - .withClusterConnectionName(clusterConnectionDb.getMetadata().getName()) - .withClusterConnectionNamespace(clusterConnectionDb.getMetadata().getNamespace()) + .withClusterConnectionName(getClusterConnectionName()) + .withClusterConnectionNamespace(withClusterConnectionNamespace) + .withDatabase(database) .withReclaimPolicy(DELETE) .returnFirst(); diff --git a/operator/src/test/java/it/aboutbits/postgresql/crd/defaultprivilege/DefaultPrivilegeReconcilerTest.java b/operator/src/test/java/it/aboutbits/postgresql/crd/defaultprivilege/DefaultPrivilegeReconcilerTest.java index 039233e..a93f866 100644 --- a/operator/src/test/java/it/aboutbits/postgresql/crd/defaultprivilege/DefaultPrivilegeReconcilerTest.java +++ b/operator/src/test/java/it/aboutbits/postgresql/crd/defaultprivilege/DefaultPrivilegeReconcilerTest.java @@ -497,6 +497,7 @@ void defaultPrivilegeOnTable( var schema = given.one() .schema() .withClusterConnectionName(clusterConnectionDb.getMetadata().getName()) + .withDatabase(database.getSpec().getName()) .withReclaimPolicy(DELETE) .returnFirst(); @@ -623,6 +624,7 @@ void defaultPrivilegeOnSequence() { var schema = given.one() .schema() .withClusterConnectionName(clusterConnectionDb.getMetadata().getName()) + .withDatabase(database.getSpec().getName()) .withReclaimPolicy(DELETE) .returnFirst(); diff --git a/operator/src/test/java/it/aboutbits/postgresql/crd/grant/GrantReconcilerTest.java b/operator/src/test/java/it/aboutbits/postgresql/crd/grant/GrantReconcilerTest.java index 4fc98f0..ba6f611 100644 --- a/operator/src/test/java/it/aboutbits/postgresql/crd/grant/GrantReconcilerTest.java +++ b/operator/src/test/java/it/aboutbits/postgresql/crd/grant/GrantReconcilerTest.java @@ -505,6 +505,7 @@ void grantOnSchema() { var schema = given.one() .schema() .withClusterConnectionName(clusterConnectionDb.getMetadata().getName()) + .withDatabase(database.getSpec().getName()) .withReclaimPolicy(DELETE) .returnFirst(); @@ -627,6 +628,7 @@ void grantOnTable( var schema = given.one() .schema() .withClusterConnectionName(clusterConnectionDb.getMetadata().getName()) + .withDatabase(database.getSpec().getName()) .withReclaimPolicy(DELETE) .returnFirst(); @@ -762,6 +764,7 @@ void grantOnAllTables( var schema = given.one() .schema() .withClusterConnectionName(clusterConnectionDb.getMetadata().getName()) + .withDatabase(database.getSpec().getName()) .withReclaimPolicy(DELETE) .returnFirst(); @@ -945,6 +948,7 @@ void grantOnSequence() { var schema = given.one() .schema() .withClusterConnectionName(clusterConnectionDb.getMetadata().getName()) + .withDatabase(database.getSpec().getName()) .withReclaimPolicy(DELETE) .returnFirst(); @@ -1078,6 +1082,7 @@ void grantOnAllSequences() { var schema = given.one() .schema() .withClusterConnectionName(clusterConnectionDb.getMetadata().getName()) + .withDatabase(database.getSpec().getName()) .withReclaimPolicy(DELETE) .returnFirst(); From 8af1acb0cd82e1d1ea0bde898482574e3edfd3f1 Mon Sep 17 00:00:00 2001 From: Thomas Sapelza Date: Tue, 3 Feb 2026 15:10:57 +0100 Subject: [PATCH 3/3] fix failing tests for good --- .../testdata/persisted/creator/SchemaCreate.java | 9 --------- .../defaultprivilege/DefaultPrivilegeReconcilerTest.java | 1 + .../postgresql/crd/grant/GrantReconcilerTest.java | 1 + .../postgresql/crd/schema/SchemaReconcilerTest.java | 4 ++-- 4 files changed, 4 insertions(+), 11 deletions(-) diff --git a/operator/src/test/java/it/aboutbits/postgresql/_support/testdata/persisted/creator/SchemaCreate.java b/operator/src/test/java/it/aboutbits/postgresql/_support/testdata/persisted/creator/SchemaCreate.java index 6792452..9136b9c 100644 --- a/operator/src/test/java/it/aboutbits/postgresql/_support/testdata/persisted/creator/SchemaCreate.java +++ b/operator/src/test/java/it/aboutbits/postgresql/_support/testdata/persisted/creator/SchemaCreate.java @@ -164,15 +164,6 @@ private String getDatabase() { withDatabase = item.getSpec().getName(); - var clusterConnectionDb = given.one() - .clusterConnection() - .withName(getClusterConnectionName() + "-db") - .withNamespace(withClusterConnectionNamespace) - .withDatabase(withDatabase) - .returnFirst(); - - withClusterConnectionName = clusterConnectionDb.getMetadata().getName(); - return withDatabase; } } diff --git a/operator/src/test/java/it/aboutbits/postgresql/crd/defaultprivilege/DefaultPrivilegeReconcilerTest.java b/operator/src/test/java/it/aboutbits/postgresql/crd/defaultprivilege/DefaultPrivilegeReconcilerTest.java index a93f866..f727fb4 100644 --- a/operator/src/test/java/it/aboutbits/postgresql/crd/defaultprivilege/DefaultPrivilegeReconcilerTest.java +++ b/operator/src/test/java/it/aboutbits/postgresql/crd/defaultprivilege/DefaultPrivilegeReconcilerTest.java @@ -328,6 +328,7 @@ void errorWhenUnsupportedMaintainTablePrivilege() { var schema = given.one() .schema() .withClusterConnectionName(clusterConnectionDb.getMetadata().getName()) + .withDatabase(database.getSpec().getName()) .withReclaimPolicy(DELETE) .returnFirst(); diff --git a/operator/src/test/java/it/aboutbits/postgresql/crd/grant/GrantReconcilerTest.java b/operator/src/test/java/it/aboutbits/postgresql/crd/grant/GrantReconcilerTest.java index ba6f611..8db9801 100644 --- a/operator/src/test/java/it/aboutbits/postgresql/crd/grant/GrantReconcilerTest.java +++ b/operator/src/test/java/it/aboutbits/postgresql/crd/grant/GrantReconcilerTest.java @@ -323,6 +323,7 @@ void errorWhenUnsupportedMaintainTablePrivilege() { var schema = given.one() .schema() .withClusterConnectionName(clusterConnectionDb.getMetadata().getName()) + .withDatabase(database.getSpec().getName()) .withReclaimPolicy(DELETE) .returnFirst(); diff --git a/operator/src/test/java/it/aboutbits/postgresql/crd/schema/SchemaReconcilerTest.java b/operator/src/test/java/it/aboutbits/postgresql/crd/schema/SchemaReconcilerTest.java index 7e1e6b2..633927c 100644 --- a/operator/src/test/java/it/aboutbits/postgresql/crd/schema/SchemaReconcilerTest.java +++ b/operator/src/test/java/it/aboutbits/postgresql/crd/schema/SchemaReconcilerTest.java @@ -62,9 +62,9 @@ void createSchema_andStatusReady() { // when var schema = given.one() .schema() - .withDatabase(database.getName()) - .withName(schemaName) .withClusterConnectionName(clusterConnectionDb.getMetadata().getName()) + .withDatabase(database.getSpec().getName()) + .withName(schemaName) .withReclaimPolicy(DELETE) .returnFirst();