From 16dd8cf340a70059a4479af6ce14266280459c73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Fern=C3=A1ndez?= <7312236+fernandezcuesta@users.noreply.github.com> Date: Mon, 18 May 2026 22:15:44 +0200 Subject: [PATCH 1/2] fix: add missing ownerRef and ownerSelector fields MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jesús Fernández <7312236+fernandezcuesta@users.noreply.github.com> --- .../postgresql/v1alpha1/database_types.go | 12 +++ .../v1alpha1/zz_generated.deepcopy.go | 10 +++ .../v1alpha1/zz_generated.resolvers.go | 27 ++++++ .../postgresql/v1alpha1/database_types.go | 12 +++ .../v1alpha1/zz_generated.deepcopy.go | 10 +++ .../v1alpha1/zz_generated.resolvers.go | 27 ++++++ ...ostgresql.sql.crossplane.io_databases.yaml | 76 +++++++++++++++++ ...tgresql.sql.m.crossplane.io_databases.yaml | 82 +++++++++++++++++++ 8 files changed, 256 insertions(+) diff --git a/apis/cluster/postgresql/v1alpha1/database_types.go b/apis/cluster/postgresql/v1alpha1/database_types.go index 2b758a8e..b46769bd 100644 --- a/apis/cluster/postgresql/v1alpha1/database_types.go +++ b/apis/cluster/postgresql/v1alpha1/database_types.go @@ -39,8 +39,20 @@ type DatabaseParameters struct { // use the default (namely, the user executing the command). To create a // database owned by another role, you must be a direct or indirect member // of that role, or be a superuser. + // +optional + // +crossplane:generate:reference:type=Role Owner *string `json:"owner,omitempty"` + // OwnerRef references the role object that will own this database. + // +immutable + // +optional + OwnerRef *xpv1.Reference `json:"ownerRef,omitempty"` + + // OwnerSelector selects a reference to a Role that will own this database. + // +immutable + // +optional + OwnerSelector *xpv1.Selector `json:"ownerSelector,omitempty"` + // The name of the template from which to create the new database, or // DEFAULT to use the default template (template1). Template *string `json:"template,omitempty"` diff --git a/apis/cluster/postgresql/v1alpha1/zz_generated.deepcopy.go b/apis/cluster/postgresql/v1alpha1/zz_generated.deepcopy.go index dedd46c2..32147816 100644 --- a/apis/cluster/postgresql/v1alpha1/zz_generated.deepcopy.go +++ b/apis/cluster/postgresql/v1alpha1/zz_generated.deepcopy.go @@ -80,6 +80,16 @@ func (in *DatabaseParameters) DeepCopyInto(out *DatabaseParameters) { *out = new(string) **out = **in } + if in.OwnerRef != nil { + in, out := &in.OwnerRef, &out.OwnerRef + *out = new(v1.Reference) + (*in).DeepCopyInto(*out) + } + if in.OwnerSelector != nil { + in, out := &in.OwnerSelector, &out.OwnerSelector + *out = new(v1.Selector) + (*in).DeepCopyInto(*out) + } if in.Template != nil { in, out := &in.Template, &out.Template *out = new(string) diff --git a/apis/cluster/postgresql/v1alpha1/zz_generated.resolvers.go b/apis/cluster/postgresql/v1alpha1/zz_generated.resolvers.go index e9571428..36a49c91 100644 --- a/apis/cluster/postgresql/v1alpha1/zz_generated.resolvers.go +++ b/apis/cluster/postgresql/v1alpha1/zz_generated.resolvers.go @@ -13,6 +13,33 @@ import ( client "sigs.k8s.io/controller-runtime/pkg/client" ) +// ResolveReferences of this Database. +func (mg *Database) ResolveReferences(ctx context.Context, c client.Reader) error { + r := reference.NewAPIResolver(c, mg) + + var rsp reference.ResolutionResponse + var err error + + rsp, err = r.Resolve(ctx, reference.ResolutionRequest{ + CurrentValue: reference.FromPtrValue(mg.Spec.ForProvider.Owner), + Extract: reference.ExternalName(), + Namespace: mg.GetNamespace(), + Reference: mg.Spec.ForProvider.OwnerRef, + Selector: mg.Spec.ForProvider.OwnerSelector, + To: reference.To{ + List: &RoleList{}, + Managed: &Role{}, + }, + }) + if err != nil { + return errors.Wrap(err, "mg.Spec.ForProvider.Owner") + } + mg.Spec.ForProvider.Owner = reference.ToPtrValue(rsp.ResolvedValue) + mg.Spec.ForProvider.OwnerRef = rsp.ResolvedReference + + return nil +} + // ResolveReferences of this DefaultPrivileges. func (mg *DefaultPrivileges) ResolveReferences(ctx context.Context, c client.Reader) error { r := reference.NewAPIResolver(c, mg) diff --git a/apis/namespaced/postgresql/v1alpha1/database_types.go b/apis/namespaced/postgresql/v1alpha1/database_types.go index 1530bf9d..b1966d0e 100644 --- a/apis/namespaced/postgresql/v1alpha1/database_types.go +++ b/apis/namespaced/postgresql/v1alpha1/database_types.go @@ -40,8 +40,20 @@ type DatabaseParameters struct { // use the default (namely, the user executing the command). To create a // database owned by another role, you must be a direct or indirect member // of that role, or be a superuser. + // +optional + // +crossplane:generate:reference:type=Role Owner *string `json:"owner,omitempty"` + // OwnerRef references the role object that will own this database. + // +immutable + // +optional + OwnerRef *xpv1.NamespacedReference `json:"ownerRef,omitempty"` + + // OwnerSelector selects a reference to a Role that will own this database. + // +immutable + // +optional + OwnerSelector *xpv1.NamespacedSelector `json:"ownerSelector,omitempty"` + // The name of the template from which to create the new database, or // DEFAULT to use the default template (template1). Template *string `json:"template,omitempty"` diff --git a/apis/namespaced/postgresql/v1alpha1/zz_generated.deepcopy.go b/apis/namespaced/postgresql/v1alpha1/zz_generated.deepcopy.go index 7a62990c..ee6ad79b 100644 --- a/apis/namespaced/postgresql/v1alpha1/zz_generated.deepcopy.go +++ b/apis/namespaced/postgresql/v1alpha1/zz_generated.deepcopy.go @@ -197,6 +197,16 @@ func (in *DatabaseParameters) DeepCopyInto(out *DatabaseParameters) { *out = new(string) **out = **in } + if in.OwnerRef != nil { + in, out := &in.OwnerRef, &out.OwnerRef + *out = new(v1.NamespacedReference) + (*in).DeepCopyInto(*out) + } + if in.OwnerSelector != nil { + in, out := &in.OwnerSelector, &out.OwnerSelector + *out = new(v1.NamespacedSelector) + (*in).DeepCopyInto(*out) + } if in.Template != nil { in, out := &in.Template, &out.Template *out = new(string) diff --git a/apis/namespaced/postgresql/v1alpha1/zz_generated.resolvers.go b/apis/namespaced/postgresql/v1alpha1/zz_generated.resolvers.go index 6bd36271..1d737da9 100644 --- a/apis/namespaced/postgresql/v1alpha1/zz_generated.resolvers.go +++ b/apis/namespaced/postgresql/v1alpha1/zz_generated.resolvers.go @@ -13,6 +13,33 @@ import ( client "sigs.k8s.io/controller-runtime/pkg/client" ) +// ResolveReferences of this Database. +func (mg *Database) ResolveReferences(ctx context.Context, c client.Reader) error { + r := reference.NewAPINamespacedResolver(c, mg) + + var rsp reference.NamespacedResolutionResponse + var err error + + rsp, err = r.Resolve(ctx, reference.NamespacedResolutionRequest{ + CurrentValue: reference.FromPtrValue(mg.Spec.ForProvider.Owner), + Extract: reference.ExternalName(), + Namespace: mg.GetNamespace(), + Reference: mg.Spec.ForProvider.OwnerRef, + Selector: mg.Spec.ForProvider.OwnerSelector, + To: reference.To{ + List: &RoleList{}, + Managed: &Role{}, + }, + }) + if err != nil { + return errors.Wrap(err, "mg.Spec.ForProvider.Owner") + } + mg.Spec.ForProvider.Owner = reference.ToPtrValue(rsp.ResolvedValue) + mg.Spec.ForProvider.OwnerRef = rsp.ResolvedReference + + return nil +} + // ResolveReferences of this DefaultPrivileges. func (mg *DefaultPrivileges) ResolveReferences(ctx context.Context, c client.Reader) error { r := reference.NewAPINamespacedResolver(c, mg) diff --git a/package/crds/postgresql.sql.crossplane.io_databases.yaml b/package/crds/postgresql.sql.crossplane.io_databases.yaml index f83585ca..52f4a3ee 100644 --- a/package/crds/postgresql.sql.crossplane.io_databases.yaml +++ b/package/crds/postgresql.sql.crossplane.io_databases.yaml @@ -117,6 +117,82 @@ spec: database owned by another role, you must be a direct or indirect member of that role, or be a superuser. type: string + ownerRef: + description: OwnerRef references the role object that will own + this database. + properties: + name: + description: Name of the referenced object. + type: string + policy: + description: Policies for referencing. + properties: + resolution: + default: Required + description: |- + Resolution specifies whether resolution of this reference is required. + The default is 'Required', which means the reconcile will fail if the + reference cannot be resolved. 'Optional' means this reference will be + a no-op if it cannot be resolved. + enum: + - Required + - Optional + type: string + resolve: + description: |- + Resolve specifies when this reference should be resolved. The default + is 'IfNotPresent', which will attempt to resolve the reference only when + the corresponding field is not present. Use 'Always' to resolve the + reference on every reconcile. + enum: + - Always + - IfNotPresent + type: string + type: object + required: + - name + type: object + ownerSelector: + description: OwnerSelector selects a reference to a Role that + will own this database. + properties: + matchControllerRef: + description: |- + MatchControllerRef ensures an object with the same controller reference + as the selecting object is selected. + type: boolean + matchLabels: + additionalProperties: + type: string + description: MatchLabels ensures an object with matching labels + is selected. + type: object + policy: + description: Policies for selection. + properties: + resolution: + default: Required + description: |- + Resolution specifies whether resolution of this reference is required. + The default is 'Required', which means the reconcile will fail if the + reference cannot be resolved. 'Optional' means this reference will be + a no-op if it cannot be resolved. + enum: + - Required + - Optional + type: string + resolve: + description: |- + Resolve specifies when this reference should be resolved. The default + is 'IfNotPresent', which will attempt to resolve the reference only when + the corresponding field is not present. Use 'Always' to resolve the + reference on every reconcile. + enum: + - Always + - IfNotPresent + type: string + type: object + type: object strategy: description: |- Strategy sets the method used to create the database from the template. diff --git a/package/crds/postgresql.sql.m.crossplane.io_databases.yaml b/package/crds/postgresql.sql.m.crossplane.io_databases.yaml index f4ad6a78..660d3181 100644 --- a/package/crds/postgresql.sql.m.crossplane.io_databases.yaml +++ b/package/crds/postgresql.sql.m.crossplane.io_databases.yaml @@ -103,6 +103,88 @@ spec: database owned by another role, you must be a direct or indirect member of that role, or be a superuser. type: string + ownerRef: + description: OwnerRef references the role object that will own + this database. + properties: + name: + description: Name of the referenced object. + type: string + namespace: + description: Namespace of the referenced object + type: string + policy: + description: Policies for referencing. + properties: + resolution: + default: Required + description: |- + Resolution specifies whether resolution of this reference is required. + The default is 'Required', which means the reconcile will fail if the + reference cannot be resolved. 'Optional' means this reference will be + a no-op if it cannot be resolved. + enum: + - Required + - Optional + type: string + resolve: + description: |- + Resolve specifies when this reference should be resolved. The default + is 'IfNotPresent', which will attempt to resolve the reference only when + the corresponding field is not present. Use 'Always' to resolve the + reference on every reconcile. + enum: + - Always + - IfNotPresent + type: string + type: object + required: + - name + type: object + ownerSelector: + description: OwnerSelector selects a reference to a Role that + will own this database. + properties: + matchControllerRef: + description: |- + MatchControllerRef ensures an object with the same controller reference + as the selecting object is selected. + type: boolean + matchLabels: + additionalProperties: + type: string + description: MatchLabels ensures an object with matching labels + is selected. + type: object + namespace: + description: Namespace for the selector + type: string + policy: + description: Policies for selection. + properties: + resolution: + default: Required + description: |- + Resolution specifies whether resolution of this reference is required. + The default is 'Required', which means the reconcile will fail if the + reference cannot be resolved. 'Optional' means this reference will be + a no-op if it cannot be resolved. + enum: + - Required + - Optional + type: string + resolve: + description: |- + Resolve specifies when this reference should be resolved. The default + is 'IfNotPresent', which will attempt to resolve the reference only when + the corresponding field is not present. Use 'Always' to resolve the + reference on every reconcile. + enum: + - Always + - IfNotPresent + type: string + type: object + type: object strategy: description: |- Strategy sets the method used to create the database from the template. From 746612c9b5f7b1fc7dba3edac5d146e799d7bc04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Fern=C3=A1ndez?= <7312236+fernandezcuesta@users.noreply.github.com> Date: Mon, 18 May 2026 22:23:24 +0200 Subject: [PATCH 2/2] chore: add tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jesús Fernández <7312236+fernandezcuesta@users.noreply.github.com> --- cluster/local/postgresdb_functions.sh | 18 ++++++++++++++++++ examples/cluster/postgresql/database.yaml | 10 ++++++++++ examples/cluster/postgresql/role.yaml | 2 ++ examples/namespaced/postgresql/database.yaml | 14 ++++++++++++++ examples/namespaced/postgresql/role.yaml | 2 ++ 5 files changed, 46 insertions(+) diff --git a/cluster/local/postgresdb_functions.sh b/cluster/local/postgresdb_functions.sh index 263e59ac..ca75b1f5 100644 --- a/cluster/local/postgresdb_functions.sh +++ b/cluster/local/postgresdb_functions.sh @@ -110,6 +110,23 @@ setup_postgresdb_tests(){ echo_step_completed } +check_database_owner_ref() { + echo_step "check if database created with ownerRef has correct owner" + + local owner + owner=$(PGPASSWORD="${postgres_root_pw}" psql -h localhost -p 5432 -U postgres -d postgres -wtAc \ + "SELECT pg_catalog.pg_get_userbyid(d.datdba) FROM pg_catalog.pg_database d WHERE d.datname = 'db-owner-ref';") + owner=$(echo "${owner}" | xargs) + + if [ "${owner}" = "ownerrole" ]; then + echo_info "ownerRef resolved correctly: owner=${owner}" + else + echo_error "ERROR: expected owner 'ownerrole' but got '${owner}'" + fi + + echo_step_completed +} + check_all_roles_privileges() { # check if granting mechanism is working properly echo_step "check if grant mechanism is working" @@ -431,6 +448,7 @@ integration_tests_postgres() { setup_observe_only_database setup_postgresdb_tests check_observe_only_database + check_database_owner_ref check_all_roles_privileges check_all_schema_privileges check_custom_object_privileges diff --git a/examples/cluster/postgresql/database.yaml b/examples/cluster/postgresql/database.yaml index 58720418..e5639197 100644 --- a/examples/cluster/postgresql/database.yaml +++ b/examples/cluster/postgresql/database.yaml @@ -17,6 +17,16 @@ spec: --- apiVersion: postgresql.sql.crossplane.io/v1alpha1 kind: Database +metadata: + name: db-owner-ref +spec: + forProvider: + ownerSelector: + matchLabels: + role: owner +--- +apiVersion: postgresql.sql.crossplane.io/v1alpha1 +kind: Database metadata: name: db-observe spec: diff --git a/examples/cluster/postgresql/role.yaml b/examples/cluster/postgresql/role.yaml index 860de429..762f8971 100644 --- a/examples/cluster/postgresql/role.yaml +++ b/examples/cluster/postgresql/role.yaml @@ -32,6 +32,8 @@ apiVersion: postgresql.sql.crossplane.io/v1alpha1 kind: Role metadata: name: ownerrole + labels: + role: owner spec: deletionPolicy: Orphan writeConnectionSecretToRef: diff --git a/examples/namespaced/postgresql/database.yaml b/examples/namespaced/postgresql/database.yaml index 21abda3b..37ebb8ea 100644 --- a/examples/namespaced/postgresql/database.yaml +++ b/examples/namespaced/postgresql/database.yaml @@ -24,6 +24,20 @@ spec: --- apiVersion: postgresql.sql.m.crossplane.io/v1alpha1 kind: Database +metadata: + name: db-owner-ref + namespace: default +spec: + forProvider: + ownerSelector: + matchLabels: + role: owner + providerConfigRef: + kind: ProviderConfig + name: default +--- +apiVersion: postgresql.sql.m.crossplane.io/v1alpha1 +kind: Database metadata: name: db-observe namespace: default diff --git a/examples/namespaced/postgresql/role.yaml b/examples/namespaced/postgresql/role.yaml index 4b96650d..61078256 100644 --- a/examples/namespaced/postgresql/role.yaml +++ b/examples/namespaced/postgresql/role.yaml @@ -39,6 +39,8 @@ kind: Role metadata: name: ownerrole namespace: default + labels: + role: owner spec: writeConnectionSecretToRef: name: ownerrole-secret