@@ -22,6 +22,7 @@ import (
22
22
23
23
"github.com/go-logr/logr"
24
24
zalando "github.com/zalando/postgres-operator/pkg/apis/acid.zalan.do/v1"
25
+ batchv1 "k8s.io/api/batch/v1"
25
26
corev1 "k8s.io/api/core/v1"
26
27
"k8s.io/client-go/tools/record"
27
28
"k8s.io/utils/pointer"
@@ -56,6 +57,8 @@ const (
56
57
storageEncryptionKeyFinalizerName string = "postgres.database.fits.cloud/secret-finalizer"
57
58
walGEncryptionSecretNamePostfix string = "-walg-encryption"
58
59
walGEncryptionSecretKeyName string = "key"
60
+ initDBName string = "postgres-initdb"
61
+ initDBSQLDummy string = `SELECT 'NOOP';`
59
62
)
60
63
61
64
// requeue defines in how many seconds a requeue should happen
@@ -85,6 +88,8 @@ type PostgresReconciler struct {
85
88
EnableRandomStorageEncryptionSecret bool
86
89
EnableWalGEncryption bool
87
90
PostgresletFullname string
91
+ PostgresImage string
92
+ InitDBJobConfigMapName string
88
93
EnableBootstrapStandbyFromS3 bool
89
94
EnableSuperUserForDBO bool
90
95
}
@@ -286,6 +291,11 @@ func (r *PostgresReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c
286
291
return ctrl.Result {}, fmt .Errorf ("failed to create or update zalando postgresql: %w" , err )
287
292
}
288
293
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
+
289
299
if err := r .LBManager .ReconcileSvcLBs (ctx , instance ); err != nil {
290
300
r .recorder .Eventf (instance , "Warning" , "Error" , "failed to create Service: %v" , err )
291
301
return ctrl.Result {}, err
@@ -861,7 +871,7 @@ func (r *PostgresReconciler) ensureStandbySecrets(ctx context.Context, instance
861
871
}
862
872
863
873
// Check if secrets exist local in SERVICE Cluster
864
- localStandbySecretName := operatormanager .PostgresConfigReplicationUsername + "." + instance .ToPeripheralResourceName () + ".credentials"
874
+ localStandbySecretName := pg .PostgresConfigReplicationUsername + "." + instance .ToPeripheralResourceName () + ".credentials"
865
875
localSecretNamespace := instance .ToPeripheralResourceNamespace ()
866
876
localStandbySecret := & corev1.Secret {}
867
877
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
899
909
}
900
910
901
911
// Check if secrets exist local in SERVICE Cluster
902
- localStandbySecretName := operatormanager .PostresConfigSuperUsername + "." + instance .ToPeripheralResourceName () + ".credentials"
912
+ localStandbySecretName := pg .PostresConfigSuperUsername + "." + instance .ToPeripheralResourceName () + ".credentials"
903
913
localSecretNamespace := instance .ToPeripheralResourceNamespace ()
904
914
localStandbySecret := & corev1.Secret {}
905
915
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
938
948
// copy all but the standby secrets...
939
949
for username := range remoteSecret .Data {
940
950
// 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 {
942
952
continue
943
953
}
944
954
@@ -1556,3 +1566,130 @@ func removeElem(ss []string, s string) (out []string) {
1556
1566
}
1557
1567
return
1558
1568
}
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
+ }
0 commit comments