Bootstrapped databases with best practice role setup (#843)

* PreparedDatabases with default role setup

* merge changes from master

* include preparedDatabases spec check when syncing databases

* create a default preparedDB if not specified

* add more default privileges for schemas

* use empty brackets block for undefined objects

* cover more default privilege scenarios and always define admin role

* add DefaultUsers flag

* support extensions and defaultUsers for preparedDatabases

* remove exact version in deployment manifest

* enable CRD validation for new field

* update generated code

* reflect code review

* fix typo in SQL command

* add documentation for preparedDatabases feature + minor changes

* some datname should stay

* add unit tests

* reflect some feedback

* init users for preparedDatabases also on update

* only change DB default privileges on creation

* add one more section in user docs

* one more sentence
This commit is contained in:
Felix Kunde
2020-04-29 10:56:06 +02:00
committed by GitHub
parent cc635a02e3
commit d76203b3f9
17 changed files with 927 additions and 65 deletions
+37
View File
@@ -421,6 +421,43 @@ var PostgresCRDResourceValidation = apiextv1beta1.CustomResourceValidation{
},
},
},
"preparedDatabases": {
Type: "object",
AdditionalProperties: &apiextv1beta1.JSONSchemaPropsOrBool{
Schema: &apiextv1beta1.JSONSchemaProps{
Type: "object",
Properties: map[string]apiextv1beta1.JSONSchemaProps{
"defaultUsers": {
Type: "boolean",
},
"extensions": {
Type: "object",
AdditionalProperties: &apiextv1beta1.JSONSchemaPropsOrBool{
Schema: &apiextv1beta1.JSONSchemaProps{
Type: "string",
},
},
},
"schemas": {
Type: "object",
AdditionalProperties: &apiextv1beta1.JSONSchemaPropsOrBool{
Schema: &apiextv1beta1.JSONSchemaProps{
Type: "object",
Properties: map[string]apiextv1beta1.JSONSchemaProps{
"defaultUsers": {
Type: "boolean",
},
"defaultRoles": {
Type: "boolean",
},
},
},
},
},
},
},
},
},
"replicaLoadBalancer": {
Type: "boolean",
Description: "Deprecated",
+32 -18
View File
@@ -50,24 +50,25 @@ type PostgresSpec struct {
// load balancers' source ranges are the same for master and replica services
AllowedSourceRanges []string `json:"allowedSourceRanges"`
NumberOfInstances int32 `json:"numberOfInstances"`
Users map[string]UserFlags `json:"users"`
MaintenanceWindows []MaintenanceWindow `json:"maintenanceWindows,omitempty"`
Clone CloneDescription `json:"clone"`
ClusterName string `json:"-"`
Databases map[string]string `json:"databases,omitempty"`
Tolerations []v1.Toleration `json:"tolerations,omitempty"`
Sidecars []Sidecar `json:"sidecars,omitempty"`
InitContainers []v1.Container `json:"initContainers,omitempty"`
PodPriorityClassName string `json:"podPriorityClassName,omitempty"`
ShmVolume *bool `json:"enableShmVolume,omitempty"`
EnableLogicalBackup bool `json:"enableLogicalBackup,omitempty"`
LogicalBackupSchedule string `json:"logicalBackupSchedule,omitempty"`
StandbyCluster *StandbyDescription `json:"standby"`
PodAnnotations map[string]string `json:"podAnnotations"`
ServiceAnnotations map[string]string `json:"serviceAnnotations"`
TLS *TLSDescription `json:"tls"`
AdditionalVolumes []AdditionalVolume `json:"additionalVolumes,omitempty"`
NumberOfInstances int32 `json:"numberOfInstances"`
Users map[string]UserFlags `json:"users"`
MaintenanceWindows []MaintenanceWindow `json:"maintenanceWindows,omitempty"`
Clone CloneDescription `json:"clone"`
ClusterName string `json:"-"`
Databases map[string]string `json:"databases,omitempty"`
PreparedDatabases map[string]PreparedDatabase `json:"preparedDatabases,omitempty"`
Tolerations []v1.Toleration `json:"tolerations,omitempty"`
Sidecars []Sidecar `json:"sidecars,omitempty"`
InitContainers []v1.Container `json:"initContainers,omitempty"`
PodPriorityClassName string `json:"podPriorityClassName,omitempty"`
ShmVolume *bool `json:"enableShmVolume,omitempty"`
EnableLogicalBackup bool `json:"enableLogicalBackup,omitempty"`
LogicalBackupSchedule string `json:"logicalBackupSchedule,omitempty"`
StandbyCluster *StandbyDescription `json:"standby"`
PodAnnotations map[string]string `json:"podAnnotations"`
ServiceAnnotations map[string]string `json:"serviceAnnotations"`
TLS *TLSDescription `json:"tls"`
AdditionalVolumes []AdditionalVolume `json:"additionalVolumes,omitempty"`
// deprecated json tags
InitContainersOld []v1.Container `json:"init_containers,omitempty"`
@@ -84,6 +85,19 @@ type PostgresqlList struct {
Items []Postgresql `json:"items"`
}
// PreparedDatabase describes elements to be bootstrapped
type PreparedDatabase struct {
PreparedSchemas map[string]PreparedSchema `json:"schemas,omitempty"`
DefaultUsers bool `json:"defaultUsers,omitempty" defaults:"false"`
Extensions map[string]string `json:"extensions,omitempty"`
}
// PreparedSchema describes elements to be bootstrapped per schema
type PreparedSchema struct {
DefaultRoles *bool `json:"defaultRoles,omitempty" defaults:"true"`
DefaultUsers bool `json:"defaultUsers,omitempty" defaults:"false"`
}
// MaintenanceWindow describes the time window when the operator is allowed to do maintenance on a cluster.
type MaintenanceWindow struct {
Everyday bool
@@ -570,6 +570,13 @@ func (in *PostgresSpec) DeepCopyInto(out *PostgresSpec) {
(*out)[key] = val
}
}
if in.PreparedDatabases != nil {
in, out := &in.PreparedDatabases, &out.PreparedDatabases
*out = make(map[string]PreparedDatabase, len(*in))
for key, val := range *in {
(*out)[key] = *val.DeepCopy()
}
}
if in.Tolerations != nil {
in, out := &in.Tolerations, &out.Tolerations
*out = make([]corev1.Toleration, len(*in))
@@ -763,6 +770,57 @@ func (in *PostgresqlParam) DeepCopy() *PostgresqlParam {
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *PreparedDatabase) DeepCopyInto(out *PreparedDatabase) {
*out = *in
if in.PreparedSchemas != nil {
in, out := &in.PreparedSchemas, &out.PreparedSchemas
*out = make(map[string]PreparedSchema, len(*in))
for key, val := range *in {
(*out)[key] = *val.DeepCopy()
}
}
if in.Extensions != nil {
in, out := &in.Extensions, &out.Extensions
*out = make(map[string]string, len(*in))
for key, val := range *in {
(*out)[key] = val
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PreparedDatabase.
func (in *PreparedDatabase) DeepCopy() *PreparedDatabase {
if in == nil {
return nil
}
out := new(PreparedDatabase)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *PreparedSchema) DeepCopyInto(out *PreparedSchema) {
*out = *in
if in.DefaultRoles != nil {
in, out := &in.DefaultRoles, &out.DefaultRoles
*out = new(bool)
**out = **in
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PreparedSchema.
func (in *PreparedSchema) DeepCopy() *PreparedSchema {
if in == nil {
return nil
}
out := new(PreparedSchema)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ResourceDescription) DeepCopyInto(out *ResourceDescription) {
*out = *in