@@ -17,7 +17,9 @@ import (
1717 appsv1 "k8s.io/api/apps/v1"
1818 corev1 "k8s.io/api/core/v1"
1919 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
20+ "k8s.io/apimachinery/pkg/runtime"
2021 "k8s.io/apimachinery/pkg/types"
22+ "k8s.io/client-go/rest"
2123 "sigs.k8s.io/controller-runtime/pkg/client"
2224
2325 "github.com/percona/percona-postgresql-operator/v2/internal/naming"
@@ -380,6 +382,150 @@ func TestReconcileCerts(t *testing.T) {
380382 })
381383}
382384
385+ // mockCertManagerController implements certmanager.Controller for testing.
386+ // Check returns nil (cert-manager is installed). All other methods panic
387+ // because they should not be called in the upgrade scenario.
388+ type mockCertManagerController struct {}
389+
390+ func (m * mockCertManagerController ) Check (context.Context , * rest.Config , string ) error { return nil }
391+ func (m * mockCertManagerController ) ApplyIssuer (context.Context , * v1beta1.PostgresCluster ) error {
392+ panic ("unexpected call" )
393+ }
394+ func (m * mockCertManagerController ) ApplyCAIssuer (context.Context , * v1beta1.PostgresCluster ) error {
395+ panic ("unexpected call" )
396+ }
397+ func (m * mockCertManagerController ) ApplyCACertificate (context.Context , * v1beta1.PostgresCluster ) error {
398+ panic ("unexpected call" )
399+ }
400+ func (m * mockCertManagerController ) ApplyClusterCertificate (context.Context , * v1beta1.PostgresCluster , []string ) error {
401+ panic ("unexpected call" )
402+ }
403+ func (m * mockCertManagerController ) ApplyInstanceCertificate (context.Context , * v1beta1.PostgresCluster , string , []string ) error {
404+ panic ("unexpected call" )
405+ }
406+ func (m * mockCertManagerController ) ApplyPGBouncerCertificate (context.Context , * v1beta1.PostgresCluster , []string ) error {
407+ panic ("unexpected call" )
408+ }
409+ func (m * mockCertManagerController ) ApplyPGBackRestClientCertificate (context.Context , * v1beta1.PostgresCluster ) error {
410+ panic ("unexpected call" )
411+ }
412+ func (m * mockCertManagerController ) ApplyPGBackRestRepoCertificate (context.Context , * v1beta1.PostgresCluster , []string ) error {
413+ panic ("unexpected call" )
414+ }
415+
416+ func mockCertManagerCtrlFunc (_ client.Client , _ * runtime.Scheme , _ bool ) certmanager.Controller {
417+ return & mockCertManagerController {}
418+ }
419+
420+ func TestUpgradeCertManagerDoesNotTakeOverInternalPKI (t * testing.T ) {
421+ if strings .EqualFold (os .Getenv ("USE_EXISTING_CLUSTER" ), "true" ) {
422+ t .Skip ("USE_EXISTING_CLUSTER: Test fails due to garbage collection" )
423+ }
424+
425+ _ , tClient := setupKubernetes (t )
426+ require .ParallelCapacity (t , 1 )
427+ ctx := t .Context ()
428+ namespace := require .Namespace (t , tClient ).Name
429+
430+ reconcilerWithoutCertManager := & Reconciler {
431+ Client : tClient ,
432+ Owner : ControllerName ,
433+ CertManagerCtrlFunc : certmanager .NewController ,
434+ }
435+
436+ cluster := testCluster ()
437+ cluster .Name = "upgrade-test"
438+ cluster .Namespace = namespace
439+ assert .NilError (t , tClient .Create (ctx , cluster ))
440+
441+ root , err := reconcilerWithoutCertManager .reconcileRootCertificate (ctx , cluster )
442+ assert .NilError (t , err )
443+
444+ primaryService := & corev1.Service {ObjectMeta : metav1.ObjectMeta {
445+ Namespace : namespace , Name : "upgrade-test-primary" ,
446+ }}
447+ replicaService := & corev1.Service {ObjectMeta : metav1.ObjectMeta {
448+ Namespace : namespace , Name : "upgrade-test-replicas" ,
449+ }}
450+
451+ _ , err = reconcilerWithoutCertManager .reconcileClusterCertificate (ctx , root , cluster , primaryService , replicaService )
452+ assert .NilError (t , err )
453+
454+ rootSecret := & corev1.Secret {}
455+ assert .NilError (t , tClient .Get (ctx , types.NamespacedName {
456+ Name : naming .PostgresRootCASecret (cluster ).Name ,
457+ Namespace : namespace ,
458+ }, rootSecret ))
459+ assert .Equal (t , rootSecret .Annotations ["cert-manager.io/certificate-name" ], "" )
460+
461+ clusterCertSecret := & corev1.Secret {}
462+ assert .NilError (t , tClient .Get (ctx , types.NamespacedName {
463+ Name : fmt .Sprintf (naming .ClusterCertSecret , cluster .Name ),
464+ Namespace : namespace ,
465+ }, clusterCertSecret ))
466+ originalCertData := clusterCertSecret .Data ["tls.crt" ]
467+
468+ reconcilerWithCertManager := & Reconciler {
469+ Client : tClient ,
470+ Owner : ControllerName ,
471+ CertManagerCtrlFunc : mockCertManagerCtrlFunc ,
472+ RestConfig : & rest.Config {},
473+ }
474+
475+ t .Run ("isRootCACertManagerManaged returns false for internal PKI root" , func (t * testing.T ) {
476+ managed , err := reconcilerWithCertManager .isRootCACertManagerManaged (ctx , cluster )
477+ assert .NilError (t , err )
478+ assert .Assert (t , ! managed )
479+ })
480+
481+ t .Run ("reconcileClusterCertificate uses internal PKI after upgrade" , func (t * testing.T ) {
482+ _ , err := reconcilerWithCertManager .reconcileClusterCertificate (ctx , root , cluster , primaryService , replicaService )
483+ assert .NilError (t , err )
484+
485+ updatedCertSecret := & corev1.Secret {}
486+ assert .NilError (t , tClient .Get (ctx , types.NamespacedName {
487+ Name : fmt .Sprintf (naming .ClusterCertSecret , cluster .Name ),
488+ Namespace : namespace ,
489+ }, updatedCertSecret ))
490+
491+ assert .DeepEqual (t , originalCertData , updatedCertSecret .Data ["tls.crt" ])
492+ })
493+
494+ t .Run ("isRootCACertManagerManaged returns true for cert-manager root" , func (t * testing.T ) {
495+ rootSecret .Annotations = map [string ]string {
496+ "cert-manager.io/certificate-name" : "test-ca-cert" ,
497+ }
498+ assert .NilError (t , tClient .Update (ctx , rootSecret ))
499+
500+ managed , err := reconcilerWithCertManager .isRootCACertManagerManaged (ctx , cluster )
501+ assert .NilError (t , err )
502+ assert .Assert (t , managed )
503+ })
504+
505+ t .Run ("isRootCACertManagerManaged returns true when no root CA exists" , func (t * testing.T ) {
506+ freshCluster := testCluster ()
507+ freshCluster .Name = "fresh-cluster"
508+ freshCluster .Namespace = namespace
509+ assert .NilError (t , tClient .Create (ctx , freshCluster ))
510+
511+ managed , err := reconcilerWithCertManager .isRootCACertManagerManaged (ctx , freshCluster )
512+ assert .NilError (t , err )
513+ assert .Assert (t , managed )
514+ })
515+
516+ t .Run ("isRootCACertManagerManaged returns false when cert-manager not installed" , func (t * testing.T ) {
517+ rNoCertManager := & Reconciler {
518+ Client : tClient ,
519+ Owner : ControllerName ,
520+ CertManagerCtrlFunc : certmanager .NewController ,
521+ }
522+
523+ managed , err := rNoCertManager .isRootCACertManagerManaged (ctx , cluster )
524+ assert .NilError (t , err )
525+ assert .Assert (t , ! managed )
526+ })
527+ }
528+
383529// getCertFromSecret returns a parsed certificate from the named secret
384530func getCertFromSecret (
385531 ctx context.Context , tClient client.Client , name , namespace , dataKey string ,
0 commit comments