Skip to content

Commit 40d560a

Browse files
authored
Initial poc of initdb job (#491)
* Initial poc of initdb job * Creating the actual resource ... * Use correct namespace * Change job/cm name * Remove yaml syntax from go code * Central initdb ConfigMap * Add monitoring user * Add pg_monitor role to monitoring user * Revert "Do not reference the postgres secret for sidecars (but use the existing environment variable instead)" This reverts commit 35666e4. * * Add constants for new technical Users * Refactor existing constants * Move role mapping to initdb job * At least *some* permissions are required * Proper naming * Add ttlSecondsAfterFinished (requires K8s >= 1.23)
1 parent 53c8e10 commit 40d560a

File tree

4 files changed

+168
-9
lines changed

4 files changed

+168
-9
lines changed

api/v1/postgres_types.go

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,11 @@ const (
9191
PostgresAutoAssignedIPLabelKey = "postgres.database.fits.cloud/auto-assigned-ip"
9292
// PostgresAutoAssignedIPLabel tag to identify ips auto-assigned for a postgres
9393
PostgresAutoAssignedIPLabel = PostgresAutoAssignedIPLabelKey + "=true"
94+
95+
PostresConfigSuperUsername = "postgres"
96+
PostgresConfigReplicationUsername = "standby"
97+
PostgresConfigAuditorUsername = "auditor"
98+
PostgresConfigMonitoringUsername = "monitoring"
9499
)
95100

96101
var (
@@ -661,7 +666,9 @@ func (p *Postgres) ToUnstructuredZalandoPostgresql(z *zalando.Postgresql, c *cor
661666
z.Spec.Users[ownerName] = zalando.UserFlags{"createdb", "createrole", "superuser"}
662667
}
663668
// Add auditor user
664-
z.Spec.Users["auditor"] = zalando.UserFlags{"nologin"}
669+
z.Spec.Users[PostgresConfigAuditorUsername] = zalando.UserFlags{"nologin"}
670+
// Add monitoring user
671+
z.Spec.Users[PostgresConfigMonitoringUsername] = zalando.UserFlags{"login"}
665672

666673
// Create default database
667674
z.Spec.Databases = make(map[string]string)
@@ -836,6 +843,16 @@ func (p *Postgres) buildSidecars(c *corev1.ConfigMap) []zalando.Sidecar {
836843
return nil
837844
}
838845

846+
// Deal with dynamically assigned name
847+
for i := range sidecars {
848+
for j := range sidecars[i].Env {
849+
if sidecars[i].Env[j].ValueFrom != nil && sidecars[i].Env[j].ValueFrom.SecretKeyRef != nil {
850+
sidecars[i].Env[j].ValueFrom.SecretKeyRef.Name = PostgresConfigMonitoringUsername + "." + p.ToPeripheralResourceName() + ".credentials"
851+
break
852+
}
853+
}
854+
}
855+
839856
return sidecars
840857
}
841858

controllers/postgres_controller.go

Lines changed: 140 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222

2323
"github.com/go-logr/logr"
2424
zalando "github.com/zalando/postgres-operator/pkg/apis/acid.zalan.do/v1"
25+
batchv1 "k8s.io/api/batch/v1"
2526
corev1 "k8s.io/api/core/v1"
2627
"k8s.io/client-go/tools/record"
2728
"k8s.io/utils/pointer"
@@ -56,6 +57,8 @@ const (
5657
storageEncryptionKeyFinalizerName string = "postgres.database.fits.cloud/secret-finalizer"
5758
walGEncryptionSecretNamePostfix string = "-walg-encryption"
5859
walGEncryptionSecretKeyName string = "key"
60+
initDBName string = "postgres-initdb"
61+
initDBSQLDummy string = `SELECT 'NOOP';`
5962
)
6063

6164
// requeue defines in how many seconds a requeue should happen
@@ -85,6 +88,8 @@ type PostgresReconciler struct {
8588
EnableRandomStorageEncryptionSecret bool
8689
EnableWalGEncryption bool
8790
PostgresletFullname string
91+
PostgresImage string
92+
InitDBJobConfigMapName string
8893
EnableBootstrapStandbyFromS3 bool
8994
EnableSuperUserForDBO bool
9095
}
@@ -286,6 +291,11 @@ func (r *PostgresReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c
286291
return ctrl.Result{}, fmt.Errorf("failed to create or update zalando postgresql: %w", err)
287292
}
288293

294+
if err := r.ensureInitDBJob(ctx, instance); err != nil {
295+
r.recorder.Eventf(instance, "Warning", "Error", "failed to create initDB job resource: %v", err)
296+
return ctrl.Result{}, fmt.Errorf("failed to create or update initdb job: %w", err)
297+
}
298+
289299
if err := r.LBManager.ReconcileSvcLBs(ctx, instance); err != nil {
290300
r.recorder.Eventf(instance, "Warning", "Error", "failed to create Service: %v", err)
291301
return ctrl.Result{}, err
@@ -861,7 +871,7 @@ func (r *PostgresReconciler) ensureStandbySecrets(ctx context.Context, instance
861871
}
862872

863873
// Check if secrets exist local in SERVICE Cluster
864-
localStandbySecretName := operatormanager.PostgresConfigReplicationUsername + "." + instance.ToPeripheralResourceName() + ".credentials"
874+
localStandbySecretName := pg.PostgresConfigReplicationUsername + "." + instance.ToPeripheralResourceName() + ".credentials"
865875
localSecretNamespace := instance.ToPeripheralResourceNamespace()
866876
localStandbySecret := &corev1.Secret{}
867877
r.Log.Info("checking for local standby secret", "namespace", localSecretNamespace, "name", localStandbySecretName)
@@ -899,7 +909,7 @@ func (r *PostgresReconciler) ensureCloneSecrets(ctx context.Context, instance *p
899909
}
900910

901911
// Check if secrets exist local in SERVICE Cluster
902-
localStandbySecretName := operatormanager.PostresConfigSuperUsername + "." + instance.ToPeripheralResourceName() + ".credentials"
912+
localStandbySecretName := pg.PostresConfigSuperUsername + "." + instance.ToPeripheralResourceName() + ".credentials"
903913
localSecretNamespace := instance.ToPeripheralResourceNamespace()
904914
localStandbySecret := &corev1.Secret{}
905915
r.Log.Info("checking for local postgres secret", "namespace", localSecretNamespace, "name", localStandbySecretName)
@@ -938,7 +948,7 @@ func (r *PostgresReconciler) copySecrets(ctx context.Context, sourceSecret types
938948
// copy all but the standby secrets...
939949
for username := range remoteSecret.Data {
940950
// check if we skip the standby user (e.g. to prevent old standby intances from connecting once a clone took over its sources ip/port)
941-
if ignoreStandbyUser && username == operatormanager.PostgresConfigReplicationUsername {
951+
if ignoreStandbyUser && username == pg.PostgresConfigReplicationUsername {
942952
continue
943953
}
944954

@@ -1556,3 +1566,130 @@ func removeElem(ss []string, s string) (out []string) {
15561566
}
15571567
return
15581568
}
1569+
1570+
func (r *PostgresReconciler) ensureInitDBJob(ctx context.Context, instance *pg.Postgres) error {
1571+
ns := types.NamespacedName{
1572+
Namespace: instance.ToPeripheralResourceNamespace(),
1573+
Name: initDBName,
1574+
}
1575+
cm := &corev1.ConfigMap{}
1576+
if err := r.SvcClient.Get(ctx, ns, cm); err == nil {
1577+
// configmap already exists, nothing to do here
1578+
r.Log.Info("initdb ConfigMap already exists")
1579+
// return nil // TODO return or update?
1580+
} else {
1581+
cm.Name = ns.Name
1582+
cm.Namespace = ns.Namespace
1583+
cm.Data = map[string]string{}
1584+
1585+
// only execute SQL when encountering a **new** database, not for standbies or clones
1586+
if instance.Spec.PostgresConnection == nil && instance.Spec.PostgresRestore == nil {
1587+
// TODO fetch central init job and copy its contents
1588+
1589+
// try to fetch the global initjjob configmap
1590+
cns := types.NamespacedName{
1591+
Namespace: r.PostgresletNamespace,
1592+
Name: r.InitDBJobConfigMapName,
1593+
}
1594+
globalInitjobCM := &corev1.ConfigMap{}
1595+
if err := r.SvcClient.Get(ctx, cns, globalInitjobCM); err == nil {
1596+
cm.Data = globalInitjobCM.Data
1597+
} else {
1598+
r.Log.Error(err, "global initdb ConfigMap could not be loaded, using dummy data")
1599+
// fall back to dummy data
1600+
cm.Data["initdb.sql"] = initDBSQLDummy
1601+
}
1602+
1603+
} else {
1604+
// use dummy job for standbies and clones
1605+
cm.Data["initdb.sql"] = initDBSQLDummy
1606+
}
1607+
1608+
if err := r.SvcClient.Create(ctx, cm); err != nil {
1609+
return fmt.Errorf("error while creating the new initdb ConfigMap: %w", err)
1610+
}
1611+
1612+
r.Log.Info("new initdb ConfigMap created")
1613+
}
1614+
1615+
j := &batchv1.Job{}
1616+
1617+
if err := r.SvcClient.Get(ctx, ns, j); err == nil {
1618+
// job already exists, nothing to do here
1619+
r.Log.Info("initdb Job already exists")
1620+
return nil // TODO return or update?
1621+
}
1622+
1623+
j.Name = ns.Name
1624+
j.Namespace = ns.Namespace
1625+
1626+
var backOffLimit int32 = 99
1627+
j.Spec = batchv1.JobSpec{
1628+
Template: corev1.PodTemplateSpec{
1629+
Spec: corev1.PodSpec{
1630+
Containers: []corev1.Container{
1631+
{
1632+
Name: "psql",
1633+
Image: r.PostgresImage,
1634+
Command: []string{"sh", "-c", "echo ${PGPASSWORD_SUPERUSER} | psql --host=${SCOPE} --port=5432 --username=${PGUSER_SUPERUSER} --file=/initdb.d/initdb.sql"},
1635+
Env: []corev1.EnvVar{
1636+
{
1637+
Name: "PGUSER_SUPERUSER",
1638+
Value: "postgres",
1639+
},
1640+
{
1641+
Name: "PGPASSWORD_SUPERUSER",
1642+
ValueFrom: &corev1.EnvVarSource{
1643+
SecretKeyRef: &corev1.SecretKeySelector{
1644+
Key: "password",
1645+
LocalObjectReference: corev1.LocalObjectReference{
1646+
Name: "postgres." + instance.ToPeripheralResourceName() + ".credentials",
1647+
},
1648+
},
1649+
},
1650+
},
1651+
{
1652+
Name: "SCOPE",
1653+
Value: instance.ToPeripheralResourceName(),
1654+
},
1655+
},
1656+
SecurityContext: &corev1.SecurityContext{
1657+
AllowPrivilegeEscalation: pointer.Bool(false),
1658+
Privileged: pointer.Bool(false),
1659+
ReadOnlyRootFilesystem: pointer.Bool(true),
1660+
RunAsUser: pointer.Int64(101),
1661+
RunAsGroup: pointer.Int64(101),
1662+
},
1663+
VolumeMounts: []corev1.VolumeMount{
1664+
{
1665+
Name: initDBName + "-volume",
1666+
MountPath: "/initdb.d",
1667+
},
1668+
},
1669+
},
1670+
},
1671+
RestartPolicy: corev1.RestartPolicyNever,
1672+
Volumes: []corev1.Volume{
1673+
{
1674+
Name: initDBName + "-volume",
1675+
VolumeSource: corev1.VolumeSource{
1676+
ConfigMap: &corev1.ConfigMapVolumeSource{
1677+
LocalObjectReference: corev1.LocalObjectReference{
1678+
Name: initDBName,
1679+
},
1680+
},
1681+
},
1682+
},
1683+
},
1684+
},
1685+
},
1686+
BackoffLimit: &backOffLimit,
1687+
TTLSecondsAfterFinished: pointer.Int32(180),
1688+
}
1689+
1690+
if err := r.SvcClient.Create(ctx, j); err != nil {
1691+
return fmt.Errorf("error while creating the new initdb Job: %w", err)
1692+
}
1693+
1694+
return nil
1695+
}

main.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ const (
7272
enableRandomStorageEncrytionSecretFlg = "enable-random-storage-encryption-secret"
7373
enableWalGEncryptionFlg = "enable-walg-encryption"
7474
enableForceSharedIPFlg = "enable-force-shared-ip"
75+
initDBJobCMNameFlg = "initdb-job-configmap-name"
7576
enableBootstrapStandbyFromS3Flg = "enable-bootsrtap-standby-from-s3"
7677
enableSuperUserForDBOFlg = "enable-superuser-for-dbo"
7778
)
@@ -114,6 +115,7 @@ func main() {
114115
etcdBackupSecretName string
115116
etcdPSPName string
116117
postgresletFullname string
118+
initDBJobCMName string
117119

118120
enableLeaderElection bool
119121
enableCRDValidation bool
@@ -264,6 +266,9 @@ func main() {
264266
viper.SetDefault(enableForceSharedIPFlg, true) // TODO switch to false?
265267
enableForceSharedIP = viper.GetBool(enableForceSharedIPFlg)
266268

269+
viper.SetDefault(initDBJobCMNameFlg, "postgreslet-postgres-initdbjob")
270+
initDBJobCMName = viper.GetString(initDBJobCMNameFlg)
271+
267272
viper.SetDefault(enableBootstrapStandbyFromS3Flg, true)
268273
enableBootstrapStandbyFromS3 = viper.GetBool(enableBootstrapStandbyFromS3Flg)
269274

@@ -309,6 +314,7 @@ func main() {
309314
postgresletFullnameFlg, postgresletFullname,
310315
enableWalGEncryptionFlg, enableWalGEncryption,
311316
enableForceSharedIPFlg, enableForceSharedIP,
317+
initDBJobCMNameFlg, initDBJobCMName,
312318
enableBootstrapStandbyFromS3Flg, enableBootstrapStandbyFromS3,
313319
enableSuperUserForDBOFlg, enableSuperUserForDBO,
314320
)
@@ -418,6 +424,8 @@ func main() {
418424
EnableRandomStorageEncryptionSecret: enableRandomStorageEncrytionSecret,
419425
EnableWalGEncryption: enableWalGEncryption,
420426
PostgresletFullname: postgresletFullname,
427+
PostgresImage: postgresImage,
428+
InitDBJobConfigMapName: initDBJobCMName,
421429
EnableBootstrapStandbyFromS3: enableBootstrapStandbyFromS3,
422430
EnableSuperUserForDBO: enableSuperUserForDBO,
423431
}).SetupWithManager(ctrlPlaneClusterMgr); err != nil {

pkg/operatormanager/operatormanager.go

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,6 @@ const (
5151
SidecarsCMExporterQueriesKey string = "queries.yaml"
5252

5353
localSidecarsCMName = "postgres-sidecars-configmap"
54-
55-
PostresConfigSuperUsername = "postgres"
56-
PostgresConfigReplicationUsername = "standby"
5754
)
5855

5956
// operatorPodMatchingLabels is for listing operator pods
@@ -426,8 +423,8 @@ func (m *OperatorManager) editConfigMap(cm *corev1.ConfigMap, namespace string,
426423
cm.Data["major_version_upgrade_mode"] = options.MajorVersionUpgradeMode
427424

428425
// we specifically refer to those two users in the cloud-api, so we hardcode them here as well to be on the safe side.
429-
cm.Data["super_username"] = PostresConfigSuperUsername
430-
cm.Data["replication_username"] = PostgresConfigReplicationUsername
426+
cm.Data["super_username"] = pg.PostresConfigSuperUsername
427+
cm.Data["replication_username"] = pg.PostgresConfigReplicationUsername
431428

432429
cm.Data["enable_pod_antiaffinity"] = strconv.FormatBool(options.PodAntiaffinity)
433430
}

0 commit comments

Comments
 (0)