@@ -213,6 +213,10 @@ func (r *PGClusterReconciler) Reconcile(ctx context.Context, request reconcile.R
213213 return reconcile.Result {}, nil
214214 }
215215
216+ if err := r .reconcileTLS (ctx , cr ); err != nil {
217+ return reconcile.Result {}, errors .Wrap (err , "reconcile TLS" )
218+ }
219+
216220 if err := r .reconcileExternalWatchers (ctx , cr ); err != nil {
217221 return reconcile.Result {}, errors .Wrap (err , "start external watchers" )
218222 }
@@ -282,6 +286,131 @@ func (r *PGClusterReconciler) Reconcile(ctx context.Context, request reconcile.R
282286 return ctrl.Result {}, nil
283287}
284288
289+ func (r * PGClusterReconciler ) reconcileTLS (ctx context.Context , cr * v2.PerconaPGCluster ) error {
290+ checkSecretProjection := func (p * corev1.SecretProjection , requiredPaths ... string ) error {
291+ if p == nil {
292+ return nil
293+ }
294+
295+ if p .Name == "" {
296+ return errors .New ("secret name is not specified" )
297+ }
298+
299+ secret := new (corev1.Secret )
300+ nn := types.NamespacedName {Name : p .Name , Namespace : cr .Namespace }
301+ if err := r .Client .Get (ctx , nn , secret ); err != nil {
302+ return errors .Wrapf (err , "failed to get secret %s" , nn .Name )
303+ }
304+
305+ pathMap := make (map [string ]struct {})
306+ for _ , item := range p .Items {
307+ if _ , ok := secret .Data [item .Key ]; ! ok {
308+ return errors .Errorf ("key %s doesn't exist in secret %s" , item .Key , secret .Name )
309+ }
310+ pathMap [item .Path ] = struct {}{}
311+ }
312+
313+ for _ , path := range requiredPaths {
314+ if _ , ok := pathMap [path ]; ! ok {
315+ if _ , ok := secret .Data [path ]; ! ok {
316+ return errors .Errorf ("required path %s was not found both in secret %s and in the .items section" , path , secret .Name )
317+ }
318+ }
319+ }
320+
321+ return nil
322+ }
323+
324+ if err := checkSecretProjection (cr .Spec .Secrets .CustomRootCATLSSecret , "root.crt" , "root.key" ); err != nil {
325+ return errors .Wrap (err , "failed to validate .spec.customRootCATLSSecret" )
326+ }
327+
328+ certPaths := []string {"tls.key" , "tls.crt" }
329+ if cr .Spec .Secrets .CustomRootCATLSSecret == nil {
330+ certPaths = append (certPaths , "ca.crt" )
331+ }
332+ if err := checkSecretProjection (cr .Spec .Secrets .CustomTLSSecret , certPaths ... ); err != nil {
333+ return errors .Wrap (err , "failed to validate .spec.customTLSSecret" )
334+ }
335+ if err := checkSecretProjection (cr .Spec .Secrets .CustomReplicationClientTLSSecret , certPaths ... ); err != nil {
336+ return errors .Wrap (err , "failed to validate .spec.customReplicationTLSSecret" )
337+ }
338+
339+ if cr .Spec .Secrets .CustomRootCATLSSecret != nil {
340+ return nil
341+ }
342+
343+ if err := r .reconcileOldCACert (ctx , cr ); err != nil {
344+ return errors .Wrap (err , "reconcile old CA" )
345+ }
346+
347+ return nil
348+ }
349+
350+ func (r * PGClusterReconciler ) reconcileOldCACert (ctx context.Context , cr * v2.PerconaPGCluster ) error {
351+ oldCASecret := & corev1.Secret {
352+ ObjectMeta : metav1.ObjectMeta {
353+ Name : naming .RootCertSecret ,
354+ Namespace : cr .Namespace ,
355+ },
356+ }
357+ err := r .Client .Get (ctx , client .ObjectKeyFromObject (oldCASecret ), oldCASecret )
358+ if client .IgnoreNotFound (err ) != nil {
359+ return errors .Wrap (err , "failed to get old ca secret" )
360+ }
361+
362+ if cr .CompareVersion ("2.5.0" ) < 0 {
363+ if k8serrors .IsNotFound (err ) {
364+ // K8SPG-555: We should create an empty secret with old name, so that crunchy part can populate it
365+ // instead of creating secrets unique to the cluster
366+ // TODO: remove when 2.4.0 will become unsupported
367+ if err := r .Client .Create (ctx , oldCASecret ); err != nil {
368+ return errors .Wrap (err , "failed to create ca secret" )
369+ }
370+ }
371+ return nil
372+ }
373+ if k8serrors .IsNotFound (err ) {
374+ return nil
375+ }
376+
377+ // K8SPG-555: Previously we used a single CA secret for all clusters in a namespace.
378+ // We should copy the contents of the old CA secret, if it exists, to the new one, which is unique for each cluster.
379+ // TODO: remove when 2.4.0 will become unsupported
380+ newCASecret := & corev1.Secret {
381+ ObjectMeta : naming .PostgresRootCASecret (
382+ & v1beta1.PostgresCluster {
383+ ObjectMeta : metav1.ObjectMeta {
384+ Name : cr .Name ,
385+ Namespace : cr .Namespace ,
386+ },
387+ }),
388+ }
389+ err = r .Client .Get (ctx , client .ObjectKeyFromObject (newCASecret ), new (corev1.Secret ))
390+ if client .IgnoreNotFound (err ) != nil {
391+ return errors .Wrap (err , "failed to get new ca secret" )
392+ }
393+
394+ if k8serrors .IsNotFound (err ) {
395+ err := r .Client .Get (ctx , types.NamespacedName {
396+ Name : cr .Name ,
397+ Namespace : cr .Namespace ,
398+ }, new (v1beta1.PostgresCluster ))
399+ if client .IgnoreNotFound (err ) != nil {
400+ return errors .Wrap (err , "failed to get crunchy cluster" )
401+ }
402+ // If the cluster is new, we should not copy the old CA secret.
403+ // We should create an empty secret instead, so that crunchy part can populate it.
404+ if ! k8serrors .IsNotFound (err ) {
405+ newCASecret .Data = oldCASecret .Data
406+ }
407+ if err := r .Client .Create (ctx , newCASecret ); err != nil {
408+ return errors .Wrap (err , "failed to create updated CA secret" )
409+ }
410+ }
411+ return nil
412+ }
413+
285414func (r * PGClusterReconciler ) reconcilePMM (ctx context.Context , cr * v2.PerconaPGCluster ) error {
286415 if ! cr .PMMEnabled () {
287416 return nil
0 commit comments