@@ -19,16 +19,21 @@ package postgresql
1919import (
2020 "context"
2121 "reflect"
22+ "strconv"
23+ "strings"
2224 "time"
2325
2426 "github.com/go-logr/logr"
2527 "github.com/prometheus/client_golang/prometheus"
2628 "k8s.io/apimachinery/pkg/api/errors"
2729 "k8s.io/apimachinery/pkg/runtime"
30+ "k8s.io/apimachinery/pkg/types"
2831 "k8s.io/client-go/tools/record"
2932 "sigs.k8s.io/controller-runtime/pkg/client"
3033 "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
3134
35+ corev1 "k8s.io/api/core/v1"
36+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
3237 ctrl "sigs.k8s.io/controller-runtime"
3338
3439 postgresqlv1alpha1 "github.com/easymile/postgresql-operator/api/postgresql/v1alpha1"
@@ -47,6 +52,15 @@ type PostgresqlBackupReconciler struct {
4752 ReconcileTimeout time.Duration
4853}
4954
55+ const (
56+ pgDumpEnvHost = "PGHOST"
57+ pgDumpEnvPort = "PGPORT"
58+ pgDumpEnvUser = "PGUSER"
59+ pgDumpEnvPassword = "PGPASSWORD"
60+ pgDumpEnvDatabase = "PGDATABASE"
61+ pgDumpEnvSSLMode = "PGSSLMODE"
62+ )
63+
5064// +kubebuilder:rbac:groups=postgresql.easymile.com,resources=postgresqlbackups,verbs=get;list;watch;create;update;patch;delete
5165// +kubebuilder:rbac:groups=postgresql.easymile.com,resources=postgresqlbackups/status,verbs=get;update;patch
5266// +kubebuilder:rbac:groups=postgresql.easymile.com,resources=postgresqlbackups/finalizers,verbs=update
@@ -158,13 +172,27 @@ func (r *PostgresqlBackupReconciler) mainReconcile(
158172 if err != nil {
159173 return r .manageError (ctx , reqLogger , instance , originalPatch , err )
160174 }
175+ // Check that postgres database is ready before continue but only if it is the first time
176+ // If not, requeue event
177+ if ! database .Status .Ready {
178+ reqLogger .Info ("PostgresqlDatabase not ready, waiting for it" )
179+ r .Recorder .Event (instance , "Warning" , "Processing" , "Processing stopped because PostgresqlDatabase isn't ready. Waiting for it." )
180+
181+ return ctrl.Result {}, nil
182+ }
161183
162184 // Find pgec
163185 pgec , err := utils .FindPgEngineCfg (ctx , r .Client , database )
164186 // Check error
165187 if err != nil {
166188 return r .manageError (ctx , reqLogger , instance , originalPatch , err )
167189 }
190+ if ! pgec .Status .Ready {
191+ reqLogger .Info ("PostgresqlEngineConfiguration not ready, waiting for it" )
192+ r .Recorder .Event (instance , "Warning" , "Processing" , "Processing stopped because PostgresqlEngineConfiguration isn't ready. Waiting for it." )
193+
194+ return ctrl.Result {}, nil
195+ }
168196
169197 // Find pgec secret
170198 pgecSecret , err := utils .FindSecretPgEngineCfg (ctx , r .Client , pgec )
@@ -180,12 +208,125 @@ func (r *PostgresqlBackupReconciler) mainReconcile(
180208 return r .manageError (ctx , reqLogger , instance , originalPatch , err )
181209 }
182210
183- // Compute spec hash
211+ _ , err = r .manageSecret (ctx , instance , database , pgec , pgecSecret , backupProvider )
212+ if err != nil {
213+ return r .manageError (ctx , reqLogger , instance , originalPatch , err )
214+ }
215+
216+ // Manage cronjob part
184217
185218 // Success
186219 return r .manageSuccess (ctx , reqLogger , instance , originalPatch )
187220}
188221
222+ func (r * PostgresqlBackupReconciler ) manageSecret (
223+ ctx context.Context ,
224+ instance * postgresqlv1alpha1.PostgresqlBackup ,
225+ database * postgresqlv1alpha1.PostgresqlDatabase ,
226+ pgec * postgresqlv1alpha1.PostgresqlEngineConfiguration ,
227+ pgecSecret * corev1.Secret ,
228+ backupProvider * postgresqlv1alpha1.PostgresqlBackupProvider ,
229+ ) (string , error ) {
230+ // TODO Add a random string to secret
231+ secretName := backupProvider .Spec .GeneratedSecretNamePrefix
232+ if secretName == "" {
233+ return "" , errors .NewBadRequest ("backup provider generated secret name is empty" )
234+ }
235+
236+ user := string (pgecSecret .Data [pgecSecretUserKey ])
237+ password := string (pgecSecret .Data [pgecSecretPassKey ])
238+ if user == "" || password == "" {
239+ return "" , errors .NewBadRequest ("engine configuration secret must contain \" user\" and \" password\" values" )
240+ }
241+
242+ databaseName := database .Status .Database
243+ if databaseName == "" {
244+ return "" , errors .NewBadRequest ("database name is empty" )
245+ }
246+
247+ host := pgec .Spec .Host
248+ port := pgec .Spec .Port
249+ uriArgs := pgec .Spec .URIArgs
250+
251+ data := map [string ][]byte {
252+ pgDumpEnvHost : []byte (host ),
253+ pgDumpEnvPort : []byte (strconv .Itoa (port )),
254+ pgDumpEnvUser : []byte (user ),
255+ pgDumpEnvPassword : []byte (password ),
256+ pgDumpEnvDatabase : []byte (databaseName ),
257+ }
258+
259+ if uriArgs != "" {
260+ for _ , part := range strings .Split (uriArgs , "&" ) {
261+ if part == "" {
262+ continue
263+ }
264+ keyValue := strings .SplitN (part , "=" , 2 )
265+ if len (keyValue ) == 2 && keyValue [0 ] == "sslmode" && keyValue [1 ] != "" {
266+ data [pgDumpEnvSSLMode ] = []byte (keyValue [1 ])
267+
268+ break
269+ }
270+ }
271+ }
272+
273+ // Create secret structure
274+ secret := & corev1.Secret {
275+ ObjectMeta : metav1.ObjectMeta {
276+ Name : secretName ,
277+ Namespace : instance .Namespace ,
278+ Labels : backupProvider .Spec .Labels ,
279+ Annotations : backupProvider .Spec .Annotations ,
280+ },
281+ Type : corev1 .SecretTypeOpaque ,
282+ Data : data ,
283+ }
284+
285+ // Add controller reference
286+ err := controllerutil .SetControllerReference (instance , secret , r .Scheme )
287+ if err != nil {
288+ return "" , err
289+ }
290+
291+ // Try to find it in kubernetes
292+ found := & corev1.Secret {}
293+ err = r .Get (
294+ ctx ,
295+ types.NamespacedName {
296+ Name : secret .Name ,
297+ Namespace : secret .Namespace ,
298+ },
299+ found ,
300+ )
301+ // Check if error is present and it isn't a not found error
302+ if err != nil && ! errors .IsNotFound (err ) {
303+ return "" , err
304+ }
305+
306+ // Check if error is present and if it is a not found error
307+ if err != nil && errors .IsNotFound (err ) {
308+ // Create secret
309+ return secretName , r .Create (ctx , secret )
310+ }
311+
312+ // Update case
313+
314+ // Check if update is needed
315+ if ! reflect .DeepEqual (found .Data , secret .Data ) ||
316+ ! reflect .DeepEqual (found .Labels , secret .Labels ) ||
317+ ! reflect .DeepEqual (found .Annotations , secret .Annotations ) {
318+ found .Data = secret .Data
319+ found .Labels = secret .Labels
320+ found .Annotations = secret .Annotations
321+
322+ // Update
323+ return secretName , r .Update (ctx , found )
324+ }
325+
326+ // Nothing to update or patch
327+ return secretName , nil
328+ }
329+
189330func (r * PostgresqlBackupReconciler ) updateInstance (
190331 ctx context.Context ,
191332 instance * postgresqlv1alpha1.PostgresqlBackup ,
0 commit comments