1616// - Bundle deletion → target cleanup
1717// - Negative: Secret target without SecretTargets enabled
1818// - Negative: useDefaultCAs without DefaultCAPackage enabled
19- // - Negative: ConfigMap source outside trust namespace not synced
19+ // - Negative: ConfigMap + Secret sources outside trust namespace not synced
2020//
2121// Group 2 — SecretTargets enabled:
2222// - Inline source → Secret target
2323// - Inline source → ConfigMap + Secret dual targets
2424// - ConfigMap source → Secret target
25+ // - Secret target data drift reconciliation (tamper → restore)
2526// - Negative: Bundle name not in authorizedSecrets list
27+ // - Transition: Enabled → Disabled → existing synced Bundle reports SecretTargetsDisabled
2628//
2729// Group 3 — DefaultCAPackage enabled:
2830// - useDefaultCAs → ConfigMap target
3335//
3436// Group 5 — Custom TrustNamespace:
3537// - ConfigMap source in custom trust namespace → ConfigMap target
36- // - Negative: ConfigMap source in default namespace not synced when custom trust namespace is configured
38+ // - Secret source in custom trust namespace → ConfigMap target
39+ // - Negative: ConfigMap + Secret sources in default namespace not synced when custom trust namespace is configured
40+ //
41+ // Group 6 — FilterExpiredCertificates enabled:
42+ // - ConfigMap source with valid + expired certs → only valid cert in ConfigMap target
43+ // - Transition to Disabled → same Bundle re-syncs with both certs in target
3744package e2e
3845
3946import (
4047 "context"
4148 "crypto/x509"
4249 "fmt"
4350 "strings"
51+ "time"
4452
4553 . "github.com/onsi/ginkgo/v2"
4654 . "github.com/onsi/gomega"
@@ -65,8 +73,8 @@ var _ = Describe("Bundle", Ordered, Label("Feature:TrustManager"), func() {
6573 ctx := context .TODO ()
6674
6775 var (
68- testNS * corev1.Namespace
69- testCertPEM1 , testCertPEM2 string
76+ testNS * corev1.Namespace
77+ testCertPEM1 , testCertPEM2 , expiredCertPEM string
7078 )
7179
7280 BeforeAll (func () {
@@ -89,6 +97,14 @@ var _ = Describe("Bundle", Ordered, Label("Feature:TrustManager"), func() {
8997 testCertPEM1 = testutils .GenerateCertificate ("e2e-test-ca-1" , []string {"cert-manager-operator-e2e" }, caTweak )
9098 testCertPEM2 = testutils .GenerateCertificate ("e2e-test-ca-2" , []string {"cert-manager-operator-e2e" }, caTweak )
9199
100+ expiredCATweak := func (cert * x509.Certificate ) {
101+ cert .IsCA = true
102+ cert .KeyUsage |= x509 .KeyUsageCertSign
103+ cert .NotBefore = time .Now ().Add (- 48 * time .Hour )
104+ cert .NotAfter = time .Now ().Add (- 24 * time .Hour )
105+ }
106+ expiredCertPEM = testutils .GenerateCertificate ("e2e-expired-ca" , []string {"cert-manager-operator-e2e" }, expiredCATweak )
107+
92108 By ("creating test namespace for target verification" )
93109 testNS = createNamespaceWithCleanup (ctx , "bundle-e2e-" , map [string ]string {bundleTestNamespaceLabel : "true" })
94110 })
@@ -330,21 +346,24 @@ var _ = Describe("Bundle", Ordered, Label("Feature:TrustManager"), func() {
330346 verifyBundleNeverSynced (ctx , bundleName )
331347 })
332348
333- It ("should not sync ConfigMap source that exists outside the trust namespace" , func () {
349+ It ("should not sync sources that exist outside the trust namespace" , func () {
334350 bundleName := "bundle-wrong-ns-" + randomStr (5 )
335351 sourceCMName := "src-wrong-ns-" + randomStr (5 )
352+ sourceSecretName := "src-secret-wrong-ns-" + randomStr (5 )
336353
337- By (fmt .Sprintf ("creating source ConfigMap in test namespace %q instead of trust namespace %q" , testNS .Name , trustManagerNamespace ))
354+ By (fmt .Sprintf ("creating source ConfigMap and Secret in test namespace %q instead of trust namespace %q" , testNS .Name , trustManagerNamespace ))
338355 createSourceConfigMap (ctx , testNS .Name , sourceCMName , bundleSourceKey , testCertPEM1 )
356+ createSourceSecret (ctx , testNS .Name , sourceSecretName , bundleSourceKey , testCertPEM2 )
339357
340358 bundle := newBundle (bundleName ).
341359 WithConfigMapSource (sourceCMName , bundleSourceKey ).
360+ WithSecretSource (sourceSecretName , bundleSourceKey ).
342361 WithConfigMapTarget (bundleTargetKey ).
343362 Build ()
344363
345364 createBundleWithCleanup (ctx , bundle )
346365
347- By ("verifying Bundle does not reach Synced=True because source is not in trust namespace" )
366+ By ("verifying Bundle does not reach Synced=True because sources are not in trust namespace" )
348367 verifyBundleNeverSynced (ctx , bundleName )
349368
350369 By ("verifying no target ConfigMap is created in test namespace" )
@@ -359,12 +378,14 @@ var _ = Describe("Bundle", Ordered, Label("Feature:TrustManager"), func() {
359378 bundleSecretTarget = "bundle-secret-tgt"
360379 bundleDualTarget = "bundle-dual-tgt"
361380 bundleCMToSecretTarget = "bundle-cm-to-secret"
381+ bundleSecretDrift = "bundle-secret-drift"
382+ bundleSecretDisable = "bundle-secret-disable"
362383 )
363384
364385 BeforeAll (func () {
365386 createTrustManager (ctx , newTrustManagerCR ().WithSecretTargets (
366387 v1alpha1 .SecretTargetsPolicyCustom ,
367- []string {bundleSecretTarget , bundleDualTarget , bundleCMToSecretTarget },
388+ []string {bundleSecretTarget , bundleDualTarget , bundleCMToSecretTarget , bundleSecretDrift , bundleSecretDisable },
368389 ))
369390 })
370391 AfterAll (func () { deleteTrustManager (ctx ) })
@@ -422,6 +443,30 @@ var _ = Describe("Bundle", Ordered, Label("Feature:TrustManager"), func() {
422443 verifyBundleSynced (ctx , bundleCMToSecretTarget )
423444 })
424445
446+ It ("should restore Secret target when tampered" , func () {
447+ bundle := newBundle (bundleSecretDrift ).
448+ WithInLineSource (testCertPEM1 ).
449+ WithSecretTarget (bundleTargetKey ).
450+ Build ()
451+
452+ createBundleWithCleanup (ctx , bundle )
453+
454+ By ("verifying Secret target is synced" )
455+ err := waitForSecretTarget (ctx , bundleClient , bundleSecretDrift , testNS .Name , bundleTargetKey , testCertPEM1 , highTimeout )
456+ Expect (err ).ShouldNot (HaveOccurred ())
457+
458+ By ("tampering with the target Secret data" )
459+ targetSecret , err := k8sClientSet .CoreV1 ().Secrets (testNS .Name ).Get (ctx , bundleSecretDrift , metav1.GetOptions {})
460+ Expect (err ).ShouldNot (HaveOccurred ())
461+ targetSecret .Data [bundleTargetKey ] = []byte ("tampered-data" )
462+ _ , err = k8sClientSet .CoreV1 ().Secrets (testNS .Name ).Update (ctx , targetSecret , metav1.UpdateOptions {})
463+ Expect (err ).ShouldNot (HaveOccurred ())
464+
465+ By ("verifying trust-manager restores the target Secret" )
466+ err = waitForSecretTarget (ctx , bundleClient , bundleSecretDrift , testNS .Name , bundleTargetKey , testCertPEM1 , highTimeout )
467+ Expect (err ).ShouldNot (HaveOccurred ())
468+ })
469+
425470 It ("should not sync Secret when Bundle name is not in authorizedSecrets list" , func () {
426471 bundleName := "bundle-not-authorized"
427472 bundle := newBundle (bundleName ).
@@ -438,6 +483,39 @@ var _ = Describe("Bundle", Ordered, Label("Feature:TrustManager"), func() {
438483 _ , err := k8sClientSet .CoreV1 ().Secrets (testNS .Name ).Get (ctx , bundleName , metav1.GetOptions {})
439484 Expect (apierrors .IsNotFound (err )).Should (BeTrue ())
440485 })
486+
487+ It ("should report SecretTargetsDisabled on existing synced Bundle after disabling secretTargets" , func () {
488+ bundle := newBundle (bundleSecretDisable ).
489+ WithInLineSource (testCertPEM1 ).
490+ WithSecretTarget (bundleTargetKey ).
491+ Build ()
492+
493+ createBundleWithCleanup (ctx , bundle )
494+
495+ By ("verifying Secret target syncs while feature is enabled" )
496+ err := waitForSecretTarget (ctx , bundleClient , bundleSecretDisable , testNS .Name , bundleTargetKey , testCertPEM1 , highTimeout )
497+ Expect (err ).ShouldNot (HaveOccurred ())
498+ verifyBundleSynced (ctx , bundleSecretDisable )
499+
500+ By ("disabling secretTargets on TrustManager CR" )
501+ Eventually (func () error {
502+ tm , err := trustManagerClient ().Get (ctx , "cluster" , metav1.GetOptions {})
503+ if err != nil {
504+ return err
505+ }
506+ tm .Spec .TrustManagerConfig .SecretTargets = v1alpha1.SecretTargetsConfig {
507+ Policy : v1alpha1 .SecretTargetsPolicyDisabled ,
508+ }
509+ _ , err = trustManagerClient ().Update (ctx , tm , metav1.UpdateOptions {})
510+ return err
511+ }, lowTimeout , fastPollInterval ).Should (Succeed ())
512+
513+ waitForTrustManagerReady (ctx )
514+
515+ By ("verifying Bundle status transitions to Synced=False" )
516+ err = waitForBundleCondition (ctx , bundleClient , bundleSecretDisable , trustapi .BundleConditionSynced , metav1 .ConditionFalse , highTimeout )
517+ Expect (err ).ShouldNot (HaveOccurred ())
518+ })
441519 })
442520
443521 // ===== Group 3: DefaultCAPackage enabled =====
@@ -590,26 +668,122 @@ var _ = Describe("Bundle", Ordered, Label("Feature:TrustManager"), func() {
590668 verifyBundleSynced (ctx , bundleName )
591669 })
592670
593- It ("should not sync ConfigMap source from default namespace when custom trust namespace is configured" , func () {
671+ It ("should sync Secret source from custom trust namespace to target" , func () {
672+ bundleName := "bundle-secret-custom-ns-" + randomStr (5 )
673+ sourceSecretName := "src-secret-custom-ns-" + randomStr (5 )
674+
675+ By (fmt .Sprintf ("creating source Secret in custom trust namespace %q" , customTrustNS .Name ))
676+ createSourceSecret (ctx , customTrustNS .Name , sourceSecretName , bundleSourceKey , testCertPEM1 )
677+
678+ bundle := newBundle (bundleName ).
679+ WithSecretSource (sourceSecretName , bundleSourceKey ).
680+ WithConfigMapTarget (bundleTargetKey ).
681+ Build ()
682+
683+ createBundleWithCleanup (ctx , bundle )
684+
685+ By ("verifying ConfigMap target is synced in test namespace" )
686+ err := waitForConfigMapTarget (ctx , bundleClient , bundleName , testNS .Name , bundleTargetKey , testCertPEM1 , highTimeout )
687+ Expect (err ).ShouldNot (HaveOccurred ())
688+
689+ verifyBundleSynced (ctx , bundleName )
690+ })
691+
692+ It ("should not sync sources from default namespace when custom trust namespace is configured" , func () {
594693 bundleName := "bundle-default-ns-miss-" + randomStr (5 )
595694 sourceCMName := "src-default-ns-miss-" + randomStr (5 )
695+ sourceSecretName := "src-secret-default-miss-" + randomStr (5 )
596696
597- By (fmt .Sprintf ("creating source ConfigMap in default namespace %q (not the custom trust namespace %q)" , trustManagerNamespace , customTrustNS .Name ))
697+ By (fmt .Sprintf ("creating source ConfigMap and Secret in default namespace %q (not the custom trust namespace %q)" , trustManagerNamespace , customTrustNS .Name ))
598698 createSourceConfigMap (ctx , trustManagerNamespace , sourceCMName , bundleSourceKey , testCertPEM1 )
699+ createSourceSecret (ctx , trustManagerNamespace , sourceSecretName , bundleSourceKey , testCertPEM2 )
599700
600701 bundle := newBundle (bundleName ).
601702 WithConfigMapSource (sourceCMName , bundleSourceKey ).
703+ WithSecretSource (sourceSecretName , bundleSourceKey ).
602704 WithConfigMapTarget (bundleTargetKey ).
603705 Build ()
604706
605707 createBundleWithCleanup (ctx , bundle )
606708
607- By ("verifying Bundle does not reach Synced=True because source is not in custom trust namespace" )
709+ By ("verifying Bundle does not reach Synced=True because sources are not in custom trust namespace" )
608710 verifyBundleNeverSynced (ctx , bundleName )
609711
610712 By ("verifying no target ConfigMap is created in test namespace" )
611713 _ , err := k8sClientSet .CoreV1 ().ConfigMaps (testNS .Name ).Get (ctx , bundleName , metav1.GetOptions {})
612714 Expect (apierrors .IsNotFound (err )).Should (BeTrue ())
613715 })
614716 })
717+
718+ // ===== Group 6: FilterExpiredCertificates =====
719+ Context ("with FilterExpiredCertificates enabled" , Ordered , func () {
720+ var (
721+ filterBundleName string
722+ sourceCMName string
723+ )
724+
725+ BeforeAll (func () {
726+ createTrustManager (ctx , newTrustManagerCR ().
727+ WithFilterExpiredCertificates (v1alpha1 .FilterExpiredCertificatesPolicyEnabled ))
728+
729+ sourceCMName = "filter-src-cm-" + randomStr (5 )
730+ combinedPEM := testCertPEM1 + expiredCertPEM
731+
732+ By ("creating source ConfigMap with valid + expired certs in trust namespace" )
733+ createSourceConfigMap (ctx , trustManagerNamespace , sourceCMName , bundleSourceKey , combinedPEM )
734+ })
735+ AfterAll (func () { deleteTrustManager (ctx ) })
736+
737+ It ("should exclude expired certificates from ConfigMap target when using ConfigMap source" , func () {
738+ filterBundleName = "bundle-filter-expired-" + randomStr (5 )
739+
740+ bundle := newBundle (filterBundleName ).
741+ WithConfigMapSource (sourceCMName , bundleSourceKey ).
742+ WithConfigMapTarget (bundleTargetKey ).
743+ Build ()
744+
745+ createBundleWithCleanup (ctx , bundle )
746+
747+ By ("verifying target contains the valid certificate" )
748+ err := waitForConfigMapTarget (ctx , bundleClient , filterBundleName , testNS .Name , bundleTargetKey , testCertPEM1 , highTimeout )
749+ Expect (err ).ShouldNot (HaveOccurred ())
750+
751+ By ("verifying target does NOT contain the expired certificate" )
752+ Eventually (func (g Gomega ) {
753+ cm , err := k8sClientSet .CoreV1 ().ConfigMaps (testNS .Name ).Get (ctx , filterBundleName , metav1.GetOptions {})
754+ g .Expect (err ).ShouldNot (HaveOccurred ())
755+ data := cm .Data [bundleTargetKey ]
756+ g .Expect (strings .Contains (data , strings .TrimSpace (testCertPEM1 ))).Should (BeTrue (), "should contain valid cert" )
757+ g .Expect (strings .Contains (data , strings .TrimSpace (expiredCertPEM ))).Should (BeFalse (), "should not contain expired cert" )
758+ }, highTimeout , fastPollInterval ).Should (Succeed ())
759+
760+ verifyBundleSynced (ctx , filterBundleName )
761+ })
762+
763+ It ("should re-sync same Bundle with expired certs included after disabling filter" , func () {
764+ By ("disabling filterExpiredCertificates on TrustManager CR" )
765+ Eventually (func () error {
766+ tm , err := trustManagerClient ().Get (ctx , "cluster" , metav1.GetOptions {})
767+ if err != nil {
768+ return err
769+ }
770+ tm .Spec .TrustManagerConfig .FilterExpiredCertificates = v1alpha1 .FilterExpiredCertificatesPolicyDisabled
771+ _ , err = trustManagerClient ().Update (ctx , tm , metav1.UpdateOptions {})
772+ return err
773+ }, lowTimeout , fastPollInterval ).Should (Succeed ())
774+
775+ waitForTrustManagerReady (ctx )
776+
777+ By ("verifying the same Bundle's target now includes the expired certificate" )
778+ Eventually (func (g Gomega ) {
779+ cm , err := k8sClientSet .CoreV1 ().ConfigMaps (testNS .Name ).Get (ctx , filterBundleName , metav1.GetOptions {})
780+ g .Expect (err ).ShouldNot (HaveOccurred ())
781+ data := cm .Data [bundleTargetKey ]
782+ g .Expect (strings .Contains (data , strings .TrimSpace (testCertPEM1 ))).Should (BeTrue (), "should contain valid cert" )
783+ g .Expect (strings .Contains (data , strings .TrimSpace (expiredCertPEM ))).Should (BeTrue (), "should contain expired cert after disabling filter" )
784+ }, highTimeout , fastPollInterval ).Should (Succeed ())
785+
786+ verifyBundleSynced (ctx , filterBundleName )
787+ })
788+ })
615789})
0 commit comments