diff --git a/apis/sql/postgresql/v1alpha1/extension_types.go b/apis/sql/postgresql/v1alpha1/extension_types.go index c6aab18940..7502eed7b3 100644 --- a/apis/sql/postgresql/v1alpha1/extension_types.go +++ b/apis/sql/postgresql/v1alpha1/extension_types.go @@ -20,10 +20,10 @@ import ( "context" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "sigs.k8s.io/controller-runtime/pkg/client" + client "sigs.k8s.io/controller-runtime/pkg/client" xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1" - "github.com/crossplane/crossplane-runtime/pkg/reference" + reference "github.com/crossplane/crossplane-runtime/pkg/reference" "github.com/pkg/errors" ) diff --git a/apis/sql/postgresql/v1alpha1/grant_types.go b/apis/sql/postgresql/v1alpha1/grant_types.go index 188392062d..be54193f93 100644 --- a/apis/sql/postgresql/v1alpha1/grant_types.go +++ b/apis/sql/postgresql/v1alpha1/grant_types.go @@ -20,13 +20,19 @@ import ( "context" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "sigs.k8s.io/controller-runtime/pkg/client" + client "sigs.k8s.io/controller-runtime/pkg/client" xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1" - "github.com/crossplane/crossplane-runtime/pkg/reference" + reference "github.com/crossplane/crossplane-runtime/pkg/reference" "github.com/pkg/errors" ) +const ( + errNoPrivileges = "privileges not passed" + errUnknownGrant = "cannot identify grant type based on passed params" + errMemberOfWithPrivileges = "cannot set privileges in the same grant as memberOf" +) + // A GrantSpec defines the desired state of a Grant. type GrantSpec struct { xpv1.ResourceSpec `json:",inline"` @@ -43,24 +49,169 @@ type GrantPrivilege string // +kubebuilder:validation:MinItems:=1 type GrantPrivileges []GrantPrivilege +type GrantType string + +// GrantType is the list of the possible grant types represented by a GrantParameters +const ( + RoleMember GrantType = "ROLE_MEMBER" + RoleDatabase GrantType = "ROLE_DATABASE" + RoleSchema GrantType = "ROLE_SCHEMA" + RoleTable GrantType = "ROLE_TABLE" + RoleSequence GrantType = "ROLE_SEQUENCE" + RoleRoutine GrantType = "ROLE_ROUTE" + RoleColumn GrantType = "ROLE_COLUMN" + RoleForeignDataWrapper GrantType = "ROLE_FOREIGN_DATA_WRAPPER" + RoleForeignServer GrantType = "ROLE_FOREIGN_SERVER" +) + +type marker struct{} +type stringSet struct { + elements map[string]marker +} + +func newStringSet() *stringSet { + return &stringSet{ + elements: make(map[string]marker), + } +} + +func (s *stringSet) add(element string) { + s.elements[element] = marker{} +} + +func (s *stringSet) contains(element string) bool { + _, exists := s.elements[element] + return exists +} + +func (s *stringSet) containsExactly(elements ...string) bool { + if len(s.elements) != len(elements) { + return false + } + for _, elem := range elements { + if !s.contains(elem) { + return false + } + } + return true +} + +func (gp *GrantParameters) filledInFields() *stringSet { + fields := map[string]bool{ + "MemberOf": gp.MemberOf != nil, + "Database": gp.Database != nil, + "Schema": gp.Schema != nil, + "Tables": len(gp.Tables) > 0, + "Columns": len(gp.Columns) > 0, + "Sequences": len(gp.Sequences) > 0, + "Routines": len(gp.Routines) > 0, + "ForeignServers": len(gp.ForeignServers) > 0, + "ForeignDataWrappers": len(gp.ForeignDataWrappers) > 0, + } + set := newStringSet() + + for key, hasField := range fields { + if hasField { + set.add(key) + } + } + return set +} + +var grantTypeFields = map[GrantType][]string{ + RoleMember: {"MemberOf"}, + RoleDatabase: {"Database"}, + RoleSchema: {"Database", "Schema"}, + RoleTable: {"Database", "Schema", "Tables"}, + RoleColumn: {"Database", "Schema", "Tables", "Columns"}, + RoleSequence: {"Database", "Schema", "Sequences"}, + RoleRoutine: {"Database", "Schema", "Routines"}, + RoleForeignServer: {"Database", "ForeignServers"}, + RoleForeignDataWrapper: {"Database", "ForeignDataWrappers"}, +} + +// IdentifyGrantType return the deduced GrantType from the filled in fields. +func (gp *GrantParameters) IdentifyGrantType() (GrantType, error) { + ff := gp.filledInFields() + pc := len(gp.Privileges) + + var gt *GrantType + + for k, v := range grantTypeFields { + if ff.containsExactly(v...) { + gt = &k + break + } + } + if gt == nil { + return "", errors.New(errUnknownGrant) + } + if *gt == RoleMember && pc > 0 { + return "", errors.New(errMemberOfWithPrivileges) + } + if *gt != RoleMember && pc < 1 { + return "", errors.New(errNoPrivileges) + } + return *gt, nil +} + // Some privileges are shorthands for multiple privileges. These translations // happen internally inside postgresql when making grants. When we query the // privileges back, we need to look for the expanded set. // https://www.postgresql.org/docs/15/ddl-priv.html -var grantReplacements = map[GrantPrivilege]GrantPrivileges{ - "ALL": {"CREATE", "TEMPORARY", "CONNECT"}, - "ALL PRIVILEGES": {"CREATE", "TEMPORARY", "CONNECT"}, - "TEMP": {"TEMPORARY"}, +var grantReplacements = map[GrantType]map[GrantPrivilege]GrantPrivileges{ + RoleDatabase: { + "ALL": {"CREATE", "TEMPORARY", "CONNECT"}, + "ALL PRIVILEGES": {"CREATE", "TEMPORARY", "CONNECT"}, + "TEMP": {"TEMPORARY"}, + }, + RoleSchema: { + "ALL": {"CREATE", "USAGE"}, + "ALL PRIVILEGES": {"CREATE", "USAGE"}, + }, + RoleTable: { + "ALL": {"SELECT", "INSERT", "UPDATE", "DELETE", "TRUNCATE", "REFERENCES", "TRIGGER", "MAINTAIN"}, + "ALL PRIVILEGES": {"SELECT", "INSERT", "UPDATE", "DELETE", "TRUNCATE", "REFERENCES", "TRIGGER", "MAINTAIN"}, + }, + RoleColumn: { + "ALL": {"SELECT", "INSERT", "UPDATE", "REFERENCES"}, + "ALL PRIVILEGES": {"SELECT", "INSERT", "UPDATE", "REFERENCES"}, + }, + RoleSequence: { + "ALL": {"USAGE", "SELECT", "UPDATE"}, + "ALL PRIVILEGES": {"USAGE", "SELECT", "UPDATE"}, + }, + RoleRoutine: { + "ALL": {"EXECUTE"}, + "ALL PRIVILEGES": {"EXECUTE"}, + }, + RoleForeignDataWrapper: { + "ALL": {"USAGE"}, + "ALL PRIVILEGES": {"USAGE"}, + }, + RoleForeignServer: { + "ALL": {"USAGE"}, + "ALL PRIVILEGES": {"USAGE"}, + }, } // ExpandPrivileges expands any shorthand privileges to their full equivalents. -func (gp *GrantPrivileges) ExpandPrivileges() GrantPrivileges { +func (gp *GrantParameters) ExpandPrivileges() GrantPrivileges { + gt, err := gp.IdentifyGrantType() + if err != nil { + return gp.Privileges + } + gr, ex := grantReplacements[gt] + if !ex { + return gp.Privileges + } + privilegeSet := make(map[GrantPrivilege]struct{}) // Replace any shorthand privileges with their full equivalents - for _, p := range *gp { - if _, ok := grantReplacements[p]; ok { - for _, rp := range grantReplacements[p] { + for _, p := range gp.Privileges { + if _, ok := gr[p]; ok { + for _, rp := range gr[p] { privilegeSet[rp] = struct{}{} } } else { @@ -99,6 +250,15 @@ const ( GrantOptionGrant GrantOption = "GRANT" ) +type Routine struct { + // The name of the routine. + Name string `json:"name,omitempty"` + + // The arguments of the routine. + // +optional + Arguments []string `json:"args,omitempty"` +} + // GrantParameters define the desired state of a PostgreSQL grant instance. type GrantParameters struct { // Privileges to be granted. @@ -141,6 +301,20 @@ type GrantParameters struct { // +optional DatabaseSelector *xpv1.Selector `json:"databaseSelector,omitempty"` + // Schema this grant is for. + // +optional + Schema *string `json:"schema,omitempty"` + + // SchemaRef references the schema object this grant it for. + // +immutable + // +optional + SchemaRef *xpv1.Reference `json:"schemaRef,omitempty"` + + // SchemaSelector selects a reference to a Schema this grant is for. + // +immutable + // +optional + SchemaSelector *xpv1.Selector `json:"schemaSelector,omitempty"` + // MemberOf is the Role that this grant makes Role a member of. // +optional MemberOf *string `json:"memberOf,omitempty"` @@ -154,6 +328,34 @@ type GrantParameters struct { // +immutable // +optional MemberOfSelector *xpv1.Selector `json:"memberOfSelector,omitempty"` + + // RevokePublicOnDb apply the statement "REVOKE ALL ON DATABASE %s FROM PUBLIC" to make database unreachable from public + // +optional + RevokePublicOnDb *bool `json:"revokePublicOnDb,omitempty" default:"false"` + + // The columns upon which to grant the privileges. + // +optional + Columns []string `json:"columns,omitempty"` + + // The tables upon which to grant the privileges. + // +optional + Tables []string `json:"tables,omitempty"` + + // The sequences upon which to grant the privileges. + // +optional + Sequences []string `json:"sequences,omitempty"` + + // The routines upon which to grant the privileges. + // +optional + Routines []Routine `json:"routines,omitempty"` + + // The foreign data wrappers upon which to grant the privileges. + // +optional + ForeignDataWrappers []string `json:"foreignDataWrappers,omitempty"` + + // The foreign servers upon which to grant the privileges. + // +optional + ForeignServers []string `json:"foreignServers,omitempty"` } // A GrantStatus represents the observed state of a Grant. @@ -208,6 +410,20 @@ func (mg *Grant) ResolveReferences(ctx context.Context, c client.Reader) error { mg.Spec.ForProvider.Database = reference.ToPtrValue(rsp.ResolvedValue) mg.Spec.ForProvider.DatabaseRef = rsp.ResolvedReference + // Resolve spec.forProvider.schema + rsp, err = r.Resolve(ctx, reference.ResolutionRequest{ + CurrentValue: reference.FromPtrValue(mg.Spec.ForProvider.Schema), + Reference: mg.Spec.ForProvider.SchemaRef, + Selector: mg.Spec.ForProvider.SchemaSelector, + To: reference.To{Managed: &Schema{}, List: &SchemaList{}}, + Extract: reference.ExternalName(), + }) + if err != nil { + return errors.Wrap(err, "spec.forProvider.schema") + } + mg.Spec.ForProvider.Schema = reference.ToPtrValue(rsp.ResolvedValue) + mg.Spec.ForProvider.SchemaRef = rsp.ResolvedReference + // Resolve spec.forProvider.role rsp, err = r.Resolve(ctx, reference.ResolutionRequest{ CurrentValue: reference.FromPtrValue(mg.Spec.ForProvider.Role), diff --git a/apis/sql/postgresql/v1alpha1/schema_types.go b/apis/sql/postgresql/v1alpha1/schema_types.go new file mode 100644 index 0000000000..8f9a9d7fae --- /dev/null +++ b/apis/sql/postgresql/v1alpha1/schema_types.go @@ -0,0 +1,98 @@ +/* +Copyright 2024 The Crossplane Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1alpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1" +) + +// A SchemaSpec defines the desired state of a Schema. +type SchemaSpec struct { + xpv1.ResourceSpec `json:",inline"` + ForProvider SchemaParameters `json:"forProvider"` +} + +// SchemaParameters define the desired state of a PostgreSQL schema. +type SchemaParameters struct { + // Role for ownership of this schema. + // +optional + // +crossplane:generate:reference:type=Role + Role *string `json:"role,omitempty"` + + // RoleRef references the role object this schema is for. + // +immutable + // +optional + RoleRef *xpv1.Reference `json:"roleRef,omitempty"` + + // RoleSelector selects a reference to a Role this schema is for. + // +immutable + // +optional + RoleSelector *xpv1.Selector `json:"roleSelector,omitempty"` + + // Database this schema is for. + // +optional + // +crossplane:generate:reference:type=Database + Database *string `json:"database,omitempty"` + + // DatabaseRef references the database object this schema is for. + // +immutable + // +optional + DatabaseRef *xpv1.Reference `json:"databaseRef,omitempty"` + + // DatabaseSelector selects a reference to a Database this schema is for. + // +immutable + // +optional + DatabaseSelector *xpv1.Selector `json:"databaseSelector,omitempty"` + + // RevokePublicOnSchema apply a "REVOKE ALL ON SCHEMA public FROM public" statement + // +optional + RevokePublicOnSchema *bool `json:"revokePublicOnSchema,omitempty" default:"false"` +} + +// A SchemaStatus represents the observed state of a Schema. +type SchemaStatus struct { + xpv1.ResourceStatus `json:",inline"` +} + +// +kubebuilder:object:root=true + +// A Schema represents the declarative state of a PostgreSQL schema. +// +kubebuilder:subresource:status +// +kubebuilder:printcolumn:name="READY",type="string",JSONPath=".status.conditions[?(@.type=='Ready')].status" +// +kubebuilder:printcolumn:name="SYNCED",type="string",JSONPath=".status.conditions[?(@.type=='Synced')].status" +// +kubebuilder:printcolumn:name="AGE",type="date",JSONPath=".metadata.creationTimestamp" +// +kubebuilder:printcolumn:name="ROLE",type="string",JSONPath=".spec.forProvider.role" +// +kubebuilder:printcolumn:name="DATABASE",type="string",JSONPath=".spec.forProvider.database" +// +kubebuilder:resource:scope=Cluster,categories={crossplane,managed,sql} +type Schema struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec SchemaSpec `json:"spec"` + Status SchemaStatus `json:"status,omitempty"` +} + +// +kubebuilder:object:root=true + +// SchemaList contains a list of Schema +type SchemaList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []Schema `json:"items"` +} diff --git a/apis/sql/postgresql/v1alpha1/zz_generated.deepcopy.go b/apis/sql/postgresql/v1alpha1/zz_generated.deepcopy.go index 9fa7f7f69f..87a458e4d9 100644 --- a/apis/sql/postgresql/v1alpha1/zz_generated.deepcopy.go +++ b/apis/sql/postgresql/v1alpha1/zz_generated.deepcopy.go @@ -395,6 +395,21 @@ func (in *GrantParameters) DeepCopyInto(out *GrantParameters) { *out = new(v1.Selector) (*in).DeepCopyInto(*out) } + if in.Schema != nil { + in, out := &in.Schema, &out.Schema + *out = new(string) + **out = **in + } + if in.SchemaRef != nil { + in, out := &in.SchemaRef, &out.SchemaRef + *out = new(v1.Reference) + (*in).DeepCopyInto(*out) + } + if in.SchemaSelector != nil { + in, out := &in.SchemaSelector, &out.SchemaSelector + *out = new(v1.Selector) + (*in).DeepCopyInto(*out) + } if in.MemberOf != nil { in, out := &in.MemberOf, &out.MemberOf *out = new(string) @@ -410,6 +425,43 @@ func (in *GrantParameters) DeepCopyInto(out *GrantParameters) { *out = new(v1.Selector) (*in).DeepCopyInto(*out) } + if in.RevokePublicOnDb != nil { + in, out := &in.RevokePublicOnDb, &out.RevokePublicOnDb + *out = new(bool) + **out = **in + } + if in.Columns != nil { + in, out := &in.Columns, &out.Columns + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.Tables != nil { + in, out := &in.Tables, &out.Tables + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.Sequences != nil { + in, out := &in.Sequences, &out.Sequences + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.Routines != nil { + in, out := &in.Routines, &out.Routines + *out = make([]Routine, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.ForeignDataWrappers != nil { + in, out := &in.ForeignDataWrappers, &out.ForeignDataWrappers + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.ForeignServers != nil { + in, out := &in.ForeignServers, &out.ForeignServers + *out = make([]string, len(*in)) + copy(*out, *in) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GrantParameters. @@ -869,3 +921,165 @@ func (in *RoleStatus) DeepCopy() *RoleStatus { in.DeepCopyInto(out) return out } + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Routine) DeepCopyInto(out *Routine) { + *out = *in + if in.Arguments != nil { + in, out := &in.Arguments, &out.Arguments + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Routine. +func (in *Routine) DeepCopy() *Routine { + if in == nil { + return nil + } + out := new(Routine) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Schema) DeepCopyInto(out *Schema) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Schema. +func (in *Schema) DeepCopy() *Schema { + if in == nil { + return nil + } + out := new(Schema) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *Schema) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SchemaList) DeepCopyInto(out *SchemaList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]Schema, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SchemaList. +func (in *SchemaList) DeepCopy() *SchemaList { + if in == nil { + return nil + } + out := new(SchemaList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *SchemaList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SchemaParameters) DeepCopyInto(out *SchemaParameters) { + *out = *in + if in.Role != nil { + in, out := &in.Role, &out.Role + *out = new(string) + **out = **in + } + if in.RoleRef != nil { + in, out := &in.RoleRef, &out.RoleRef + *out = new(v1.Reference) + (*in).DeepCopyInto(*out) + } + if in.RoleSelector != nil { + in, out := &in.RoleSelector, &out.RoleSelector + *out = new(v1.Selector) + (*in).DeepCopyInto(*out) + } + if in.Database != nil { + in, out := &in.Database, &out.Database + *out = new(string) + **out = **in + } + if in.DatabaseRef != nil { + in, out := &in.DatabaseRef, &out.DatabaseRef + *out = new(v1.Reference) + (*in).DeepCopyInto(*out) + } + if in.DatabaseSelector != nil { + in, out := &in.DatabaseSelector, &out.DatabaseSelector + *out = new(v1.Selector) + (*in).DeepCopyInto(*out) + } + if in.RevokePublicOnSchema != nil { + in, out := &in.RevokePublicOnSchema, &out.RevokePublicOnSchema + *out = new(bool) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SchemaParameters. +func (in *SchemaParameters) DeepCopy() *SchemaParameters { + if in == nil { + return nil + } + out := new(SchemaParameters) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SchemaSpec) DeepCopyInto(out *SchemaSpec) { + *out = *in + in.ResourceSpec.DeepCopyInto(&out.ResourceSpec) + in.ForProvider.DeepCopyInto(&out.ForProvider) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SchemaSpec. +func (in *SchemaSpec) DeepCopy() *SchemaSpec { + if in == nil { + return nil + } + out := new(SchemaSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SchemaStatus) DeepCopyInto(out *SchemaStatus) { + *out = *in + in.ResourceStatus.DeepCopyInto(&out.ResourceStatus) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SchemaStatus. +func (in *SchemaStatus) DeepCopy() *SchemaStatus { + if in == nil { + return nil + } + out := new(SchemaStatus) + in.DeepCopyInto(out) + return out +} diff --git a/apis/sql/postgresql/v1alpha1/zz_generated.managed.go b/apis/sql/postgresql/v1alpha1/zz_generated.managed.go index ed90f98546..68ae6cdb63 100644 --- a/apis/sql/postgresql/v1alpha1/zz_generated.managed.go +++ b/apis/sql/postgresql/v1alpha1/zz_generated.managed.go @@ -243,3 +243,63 @@ func (mg *Role) SetPublishConnectionDetailsTo(r *xpv1.PublishConnectionDetailsTo func (mg *Role) SetWriteConnectionSecretToReference(r *xpv1.SecretReference) { mg.Spec.WriteConnectionSecretToReference = r } + +// GetCondition of this Schema. +func (mg *Schema) GetCondition(ct xpv1.ConditionType) xpv1.Condition { + return mg.Status.GetCondition(ct) +} + +// GetDeletionPolicy of this Schema. +func (mg *Schema) GetDeletionPolicy() xpv1.DeletionPolicy { + return mg.Spec.DeletionPolicy +} + +// GetManagementPolicies of this Schema. +func (mg *Schema) GetManagementPolicies() xpv1.ManagementPolicies { + return mg.Spec.ManagementPolicies +} + +// GetProviderConfigReference of this Schema. +func (mg *Schema) GetProviderConfigReference() *xpv1.Reference { + return mg.Spec.ProviderConfigReference +} + +// GetPublishConnectionDetailsTo of this Schema. +func (mg *Schema) GetPublishConnectionDetailsTo() *xpv1.PublishConnectionDetailsTo { + return mg.Spec.PublishConnectionDetailsTo +} + +// GetWriteConnectionSecretToReference of this Schema. +func (mg *Schema) GetWriteConnectionSecretToReference() *xpv1.SecretReference { + return mg.Spec.WriteConnectionSecretToReference +} + +// SetConditions of this Schema. +func (mg *Schema) SetConditions(c ...xpv1.Condition) { + mg.Status.SetConditions(c...) +} + +// SetDeletionPolicy of this Schema. +func (mg *Schema) SetDeletionPolicy(r xpv1.DeletionPolicy) { + mg.Spec.DeletionPolicy = r +} + +// SetManagementPolicies of this Schema. +func (mg *Schema) SetManagementPolicies(r xpv1.ManagementPolicies) { + mg.Spec.ManagementPolicies = r +} + +// SetProviderConfigReference of this Schema. +func (mg *Schema) SetProviderConfigReference(r *xpv1.Reference) { + mg.Spec.ProviderConfigReference = r +} + +// SetPublishConnectionDetailsTo of this Schema. +func (mg *Schema) SetPublishConnectionDetailsTo(r *xpv1.PublishConnectionDetailsTo) { + mg.Spec.PublishConnectionDetailsTo = r +} + +// SetWriteConnectionSecretToReference of this Schema. +func (mg *Schema) SetWriteConnectionSecretToReference(r *xpv1.SecretReference) { + mg.Spec.WriteConnectionSecretToReference = r +} diff --git a/apis/sql/postgresql/v1alpha1/zz_generated.managedlist.go b/apis/sql/postgresql/v1alpha1/zz_generated.managedlist.go index 11925afc65..0b9a7c28a4 100644 --- a/apis/sql/postgresql/v1alpha1/zz_generated.managedlist.go +++ b/apis/sql/postgresql/v1alpha1/zz_generated.managedlist.go @@ -39,3 +39,12 @@ func (l *RoleList) GetItems() []resource.Managed { } return items } + +// GetItems of this SchemaList. +func (l *SchemaList) GetItems() []resource.Managed { + items := make([]resource.Managed, len(l.Items)) + for i := range l.Items { + items[i] = &l.Items[i] + } + return items +} diff --git a/apis/sql/postgresql/v1alpha1/zz_generated.resolvers.go b/apis/sql/postgresql/v1alpha1/zz_generated.resolvers.go new file mode 100644 index 0000000000..78b585ec80 --- /dev/null +++ b/apis/sql/postgresql/v1alpha1/zz_generated.resolvers.go @@ -0,0 +1,52 @@ +// Code generated by angryjet. DO NOT EDIT. + +package v1alpha1 + +import ( + "context" + reference "github.com/crossplane/crossplane-runtime/pkg/reference" + errors "github.com/pkg/errors" + client "sigs.k8s.io/controller-runtime/pkg/client" +) + +// ResolveReferences of this Schema. +func (mg *Schema) 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.Role), + Extract: reference.ExternalName(), + Reference: mg.Spec.ForProvider.RoleRef, + Selector: mg.Spec.ForProvider.RoleSelector, + To: reference.To{ + List: &RoleList{}, + Managed: &Role{}, + }, + }) + if err != nil { + return errors.Wrap(err, "mg.Spec.ForProvider.Role") + } + mg.Spec.ForProvider.Role = reference.ToPtrValue(rsp.ResolvedValue) + mg.Spec.ForProvider.RoleRef = rsp.ResolvedReference + + rsp, err = r.Resolve(ctx, reference.ResolutionRequest{ + CurrentValue: reference.FromPtrValue(mg.Spec.ForProvider.Database), + Extract: reference.ExternalName(), + Reference: mg.Spec.ForProvider.DatabaseRef, + Selector: mg.Spec.ForProvider.DatabaseSelector, + To: reference.To{ + List: &DatabaseList{}, + Managed: &Database{}, + }, + }) + if err != nil { + return errors.Wrap(err, "mg.Spec.ForProvider.Database") + } + mg.Spec.ForProvider.Database = reference.ToPtrValue(rsp.ResolvedValue) + mg.Spec.ForProvider.DatabaseRef = rsp.ResolvedReference + + return nil +} diff --git a/pkg/comp-functions/functions/common/backup/backup.go b/pkg/comp-functions/functions/common/backup/backup.go index 0e866686a2..0e38ed1f92 100644 --- a/pkg/comp-functions/functions/common/backup/backup.go +++ b/pkg/comp-functions/functions/common/backup/backup.go @@ -70,7 +70,7 @@ func createObjectBucket(ctx context.Context, comp common.InfoGetter, svc *runtim ResourceSpec: xpv1.ResourceSpec{ WriteConnectionSecretToReference: &xpv1.SecretReference{ Namespace: svc.GetCrossplaneNamespace(), - Name: credentialSecretName, + Name: credentialSecretName + "-" + comp.GetName(), }, }, }, diff --git a/pkg/comp-functions/functions/common/postgresql.go b/pkg/comp-functions/functions/common/postgresql.go index 05bdd17e4b..3cc9123e18 100644 --- a/pkg/comp-functions/functions/common/postgresql.go +++ b/pkg/comp-functions/functions/common/postgresql.go @@ -2,6 +2,7 @@ package common import ( "encoding/json" + "fmt" "dario.cat/mergo" xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1" @@ -159,7 +160,7 @@ func (a *PostgreSQLDependencyBuilder) CreateDependency() (string, error) { Parameters: *params, ResourceSpec: xpv1.ResourceSpec{ WriteConnectionSecretToReference: &xpv1.SecretReference{ - Name: PgSecretName, + Name: a.getPGSecretName(), Namespace: a.comp.GetInstanceNamespace(), }, }, @@ -181,5 +182,9 @@ func (a *PostgreSQLDependencyBuilder) CreateDependency() (string, error) { return "", err } - return PgSecretName, a.svc.SetDesiredComposedResource(pg) + return a.getPGSecretName(), a.svc.SetDesiredComposedResource(pg) +} + +func (a *PostgreSQLDependencyBuilder) getPGSecretName() string { + return fmt.Sprintf("%s-%s", PgSecretName, a.comp.GetName()) } diff --git a/pkg/comp-functions/functions/vshnpostgres/billing.go b/pkg/comp-functions/functions/vshnpostgres/billing.go index 57bf6bed2a..b51be4a8c0 100644 --- a/pkg/comp-functions/functions/vshnpostgres/billing.go +++ b/pkg/comp-functions/functions/vshnpostgres/billing.go @@ -3,6 +3,7 @@ package vshnpostgres import ( "context" "fmt" + xfnproto "github.com/crossplane/function-sdk-go/proto/v1" v1 "github.com/vshn/appcat/v4/apis/vshn/v1" "github.com/vshn/appcat/v4/pkg/comp-functions/functions/common" diff --git a/pkg/comp-functions/functions/vshnpostgres/user_management.go b/pkg/comp-functions/functions/vshnpostgres/user_management.go index cffd95bd12..df0cf82ac1 100644 --- a/pkg/comp-functions/functions/vshnpostgres/user_management.go +++ b/pkg/comp-functions/functions/vshnpostgres/user_management.go @@ -128,6 +128,12 @@ func addConnectionDetail(comp *vshnv1.VSHNPostgreSQL, svc *runtime.ServiceRuntim svc.AddResult(runtime.NewWarningResult(fmt.Sprintf("cannot get userpassword from secret: %s", err))) } + // Userpass is not yet ready, so we'll check again in 30 seconds + if len(userpassCD) == 0 { + svc.SetDesiredResourceReadiness(secretName, runtime.ResourceUnReady) + return nil + } + compositeCD := svc.GetConnectionDetails() url := getPostgresURLCustomUser(compositeCD, string(compositeCD["POSTGRESQL_HOST"]), username, string(userpassCD["userpass"])) @@ -265,7 +271,9 @@ func addDatabase(comp common.Composite, svc *runtime.ServiceRuntime, name string }, }, Spec: pgv1alpha1.DatabaseSpec{ - ForProvider: pgv1alpha1.DatabaseParameters{}, + ForProvider: pgv1alpha1.DatabaseParameters{ + Owner: &name, + }, ResourceSpec: xpv1.ResourceSpec{ ProviderConfigReference: &xpv1.Reference{ Name: comp.GetName(), @@ -305,6 +313,7 @@ func addGrants(comp common.Composite, svc *runtime.ServiceRuntime, username, dbn Privileges: privs, Role: &username, Database: &dbname, + Schema: ptr.To("public"), }, ResourceSpec: xpv1.ResourceSpec{ ProviderConfigReference: &xpv1.Reference{ diff --git a/pkg/comp-functions/runtime/function_mgr.go b/pkg/comp-functions/runtime/function_mgr.go index c0c240074e..f1d6ba7739 100644 --- a/pkg/comp-functions/runtime/function_mgr.go +++ b/pkg/comp-functions/runtime/function_mgr.go @@ -1447,9 +1447,6 @@ func (s *ServiceRuntime) deployConnectionDetailsToInstanceNS() error { compName := s.desiredResources[i].Resource.GetName() - // prefix the name in the connetiondetailsref - // and set to crossplane namespace - cdRef.Name = compName + "-" + cdRef.Name cdRef.Namespace = s.GetCrossplaneNamespace() s.desiredResources[i].Resource.SetWriteConnectionSecretToReference(cdRef)