From 808884e01f5257de6a5305e6b22c219e6ad96d11 Mon Sep 17 00:00:00 2001 From: Gabriele Quaresima Date: Wed, 16 Oct 2024 12:55:01 +0200 Subject: [PATCH] chore: simplify field type inside spec and add docs Signed-off-by: Gabriele Quaresima --- api/v1/publication_types.go | 5 +- api/v1/zz_generated.deepcopy.go | 20 --- .../postgresql.cnpg.io_publications.yaml | 2 +- docs/src/cloudnative-pg.v1.md | 15 +- ...ive_publication_subscription_management.md | 135 ++++++++++++++++++ .../controller/publication_controller_sql.go | 2 +- tests/e2e/asserts_test.go | 24 ++++ tests/e2e/declarative_pub_sub_test.go | 6 +- .../declarative_pub_sub/pub.yaml.template | 2 +- 9 files changed, 169 insertions(+), 42 deletions(-) create mode 100644 docs/src/declarative_publication_subscription_management.md diff --git a/api/v1/publication_types.go b/api/v1/publication_types.go index 1e0d020416..50e1dec3b7 100644 --- a/api/v1/publication_types.go +++ b/api/v1/publication_types.go @@ -68,15 +68,12 @@ type PublicationSpec struct { // +kubebuilder:validation:XValidation:rule="(has(self.allTables) && !has(self.objects)) || (!has(self.allTables) && has(self.objects))",message="allTables and objects are not compatible" type PublicationTarget struct { // All tables should be publicated - AllTables *PublicationTargetAllTables `json:"allTables,omitempty"` + AllTables bool `json:"allTables,omitempty"` // Just the following schema objects Objects []PublicationTargetObject `json:"objects,omitempty"` } -// PublicationTargetAllTables means all tables should be publicated -type PublicationTargetAllTables struct{} - // PublicationTargetObject is an object to publicate type PublicationTargetObject struct { // The schema to publicate diff --git a/api/v1/zz_generated.deepcopy.go b/api/v1/zz_generated.deepcopy.go index 834ed0d9dd..f045980586 100644 --- a/api/v1/zz_generated.deepcopy.go +++ b/api/v1/zz_generated.deepcopy.go @@ -2271,11 +2271,6 @@ func (in *PublicationStatus) DeepCopy() *PublicationStatus { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *PublicationTarget) DeepCopyInto(out *PublicationTarget) { *out = *in - if in.AllTables != nil { - in, out := &in.AllTables, &out.AllTables - *out = new(PublicationTargetAllTables) - **out = **in - } if in.Objects != nil { in, out := &in.Objects, &out.Objects *out = make([]PublicationTargetObject, len(*in)) @@ -2295,21 +2290,6 @@ func (in *PublicationTarget) DeepCopy() *PublicationTarget { return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *PublicationTargetAllTables) DeepCopyInto(out *PublicationTargetAllTables) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PublicationTargetAllTables. -func (in *PublicationTargetAllTables) DeepCopy() *PublicationTargetAllTables { - if in == nil { - return nil - } - out := new(PublicationTargetAllTables) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *PublicationTargetObject) DeepCopyInto(out *PublicationTargetObject) { *out = *in diff --git a/config/crd/bases/postgresql.cnpg.io_publications.yaml b/config/crd/bases/postgresql.cnpg.io_publications.yaml index 7030872556..6c02b9e00a 100644 --- a/config/crd/bases/postgresql.cnpg.io_publications.yaml +++ b/config/crd/bases/postgresql.cnpg.io_publications.yaml @@ -102,7 +102,7 @@ spec: properties: allTables: description: All tables should be publicated - type: object + type: boolean objects: description: Just the following schema objects items: diff --git a/docs/src/cloudnative-pg.v1.md b/docs/src/cloudnative-pg.v1.md index 0fbfd41bc9..2b6d6c28ed 100644 --- a/docs/src/cloudnative-pg.v1.md +++ b/docs/src/cloudnative-pg.v1.md @@ -4116,7 +4116,7 @@ desired state that was synchronized

FieldDescription allTables [Required]
-PublicationTargetAllTables +bool

All tables should be publicated

@@ -4132,19 +4132,6 @@ desired state that was synchronized

-## PublicationTargetAllTables {#postgresql-cnpg-io-v1-PublicationTargetAllTables} - - -**Appears in:** - -- [PublicationTarget](#postgresql-cnpg-io-v1-PublicationTarget) - - -

PublicationTargetAllTables means all tables should be publicated

- - - - ## PublicationTargetObject {#postgresql-cnpg-io-v1-PublicationTargetObject} diff --git a/docs/src/declarative_publication_subscription_management.md b/docs/src/declarative_publication_subscription_management.md new file mode 100644 index 0000000000..adeb00eb41 --- /dev/null +++ b/docs/src/declarative_publication_subscription_management.md @@ -0,0 +1,135 @@ +# Declarative Publication/Subscription Management + +Declarative publication/subscription management enables users to set up +logical replication via new Custom Resource Definitions (CRD) +- `Database` , +- `Publication`, +- `Subscription`, + +Database CRD is widely discussed in +["Declarative database management"](declarative_database_management.md) section. + +Logical replication is set up between one source cluster with publication +and one destination cluster that is subscribed to that publication. + +### Example: Simple Publication Declaration + +A `Publication` object is managed by the instance manager of the source cluster's +primary instance. +Below is an example of a basic `Publication` configuration: + +```yaml +apiVersion: postgresql.cnpg.io/v1 +kind: Publication +metadata: + name: pub-one +spec: + name: pub + dbname: cat + cluster: + name: source-cluster + target: + allTables: true +``` + +The `dbname` field specifies the database the publication is applied to. +Once the reconciliation cycle is completed successfully, the `Publication` +status will show a `ready` field set to `true` and an empty `error` field. + +### Publication Deletion and Reclaim Policies + +A finalizer named `cnpg.io/deletePublication` is automatically added +to each `Publication` object to control its deletion process. + +By default, the `publicationReclaimPolicy` is set to `retain`, which means +that if the `Publication` object is deleted, the actual PostgreSQL publication +is retained for manual management by an administrator. + +Alternatively, if the `publicationReclaimPolicy` is set to `delete`, +the PostgreSQL publication will be automatically deleted when the `Publication` +object is removed. + +### Example: Publication with Delete Reclaim Policy + +The following example illustrates a `Publication` object with a `delete` +reclaim policy: + +```yaml +apiVersion: postgresql.cnpg.io/v1 +kind: Publication +metadata: + name: pub-one +spec: + name: pub + dbname: cat + publicationReclaimPolicy: delete + cluster: + name: source-cluster + target: + allTables: true +``` + +In this case, when the `Publication` object is deleted, the corresponding PostgreSQL publication will also be removed automatically. + + +### Example: Simple Subscription Declaration + +A `Subscription` object is managed by the instance manager of the destination cluster's +primary instance. +Below is an example of a basic `Subscription` configuration: + +```yaml +apiVersion: postgresql.cnpg.io/v1 +kind: Subscription +metadata: + name: sub-one +spec: + name: sub + dbname: cat + publicationName: pub + cluster: + name: destination-cluster + externalClusterName: source-cluster +``` + +The `dbname` field specifies the database the publication is applied to. +The `publicationName` field specifies the name of the publication the subscription refers to. +The `externalClusterName` field specifies the external cluster the publication belongs to. + +Once the reconciliation cycle is completed successfully, the `Subscription` +status will show a `ready` field set to `true` and an empty `error` field. + +### Subscription Deletion and Reclaim Policies + +A finalizer named `cnpg.io/deleteSubscription` is automatically added +to each `Subscription` object to control its deletion process. + +By default, the `subscriptionReclaimPolicy` is set to `retain`, which means +that if the `Subscription` object is deleted, the actual PostgreSQL publication +is retained for manual management by an administrator. + +Alternatively, if the `subscriptionReclaimPolicy` is set to `delete`, +the PostgreSQL publication will be automatically deleted when the `Publication` +object is removed. + +### Example: Subscription with Delete Reclaim Policy + +The following example illustrates a `Subscription` object with a `delete` +reclaim policy: + +```yaml +apiVersion: postgresql.cnpg.io/v1 +kind: Subscription +metadata: + name: sub-one +spec: + name: sub + dbname: cat + publicationName: pub + subscriptionReclaimPolicy: delete + cluster: + name: destination-cluster + externalClusterName: source-cluster +``` + +In this case, when the `Subscription` object is deleted, the corresponding PostgreSQL publication will also be removed automatically. diff --git a/internal/management/controller/publication_controller_sql.go b/internal/management/controller/publication_controller_sql.go index 4006c81463..8afe8c5dd5 100644 --- a/internal/management/controller/publication_controller_sql.go +++ b/internal/management/controller/publication_controller_sql.go @@ -176,7 +176,7 @@ func (r *PublicationReconciler) dropPublication(ctx context.Context, obj *apiv1. } func toPublicationTargetSQL(obj *apiv1.PublicationTarget) string { - if obj.AllTables != nil { + if obj.AllTables { return "FOR ALL TABLES" } diff --git a/tests/e2e/asserts_test.go b/tests/e2e/asserts_test.go index badcfe420c..55caaeaf0e 100644 --- a/tests/e2e/asserts_test.go +++ b/tests/e2e/asserts_test.go @@ -476,6 +476,30 @@ func AssertCreateTestDataWithDatabaseName( }) } +// AssertCreateTestDataWithDatabaseName create test data in a given database. +func AssertCreateTableWithDatabaseName( + env *testsUtils.TestingEnvironment, + namespace, + clusterName, + databaseName, + tableName string, +) { + By(fmt.Sprintf("creating table in cluster %v", clusterName), func() { + forward, conn, err := testsUtils.ForwardPSQLConnection( + env, + namespace, + clusterName, + databaseName, + apiv1.ApplicationUserSecretSuffix, + ) + Expect(err).ToNot(HaveOccurred()) + query := fmt.Sprintf("CREATE TABLE IF NOT EXISTS %v ();", tableName) + _, err = conn.Exec(query) + Expect(err).ToNot(HaveOccurred()) + forward.Close() + }) +} + type TableLocator struct { Namespace string ClusterName string diff --git a/tests/e2e/declarative_pub_sub_test.go b/tests/e2e/declarative_pub_sub_test.go index 84ed803031..fb7b9dbd08 100644 --- a/tests/e2e/declarative_pub_sub_test.go +++ b/tests/e2e/declarative_pub_sub_test.go @@ -169,6 +169,10 @@ var _ = Describe("Declarative publication and subscription test", Label(tests.La AssertDatabaseExists(namespace, primaryPodInfo.Name, dbname, true) }) + By("creating empty table inside destination database", func() { + AssertCreateTableWithDatabaseName(env, namespace, destinationClusterName, dbname, tableName) + }) + By("applying Publication CRD manifest", func() { CreateResourceFromFile(namespace, pubManifest) pubObjectName, err = env.GetResourceNameFromYAML(pubManifest) @@ -219,7 +223,7 @@ var _ = Describe("Declarative publication and subscription test", Label(tests.La assertSubscriptionExists(namespace, primaryPodInfo.Name, sub) }) - By("creating a new data in the source cluster database", func() { + By("creating new data in the source cluster database", func() { AssertCreateTestDataWithDatabaseName(env, namespace, sourceClusterName, dbname, tableName) }) diff --git a/tests/e2e/fixtures/declarative_pub_sub/pub.yaml.template b/tests/e2e/fixtures/declarative_pub_sub/pub.yaml.template index 1fa6bada73..bd09d64014 100644 --- a/tests/e2e/fixtures/declarative_pub_sub/pub.yaml.template +++ b/tests/e2e/fixtures/declarative_pub_sub/pub.yaml.template @@ -8,4 +8,4 @@ spec: cluster: name: source-cluster target: - allTables: {} + allTables: true