github.com/verrazzano/verrazzano@v1.7.1/tests/e2e/update/env-dns-cm/env_dns_cert_update_test.go (about)

     1  // Copyright (c) 2022, 2023, Oracle and/or its affiliates.
     2  // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl.
     3  
     4  package envdnscm
     5  
     6  import (
     7  	"fmt"
     8  	"log"
     9  	"os"
    10  	"os/exec"
    11  	"strings"
    12  	"time"
    13  
    14  	certmanagerv1 "github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1"
    15  	. "github.com/onsi/ginkgo/v2"
    16  	. "github.com/onsi/gomega"
    17  	"github.com/verrazzano/verrazzano/pkg/constants"
    18  	vzapi "github.com/verrazzano/verrazzano/platform-operator/apis/verrazzano/v1alpha1"
    19  	constants2 "github.com/verrazzano/verrazzano/platform-operator/constants"
    20  	"github.com/verrazzano/verrazzano/tests/e2e/pkg"
    21  	"github.com/verrazzano/verrazzano/tests/e2e/pkg/test/framework"
    22  	"github.com/verrazzano/verrazzano/tests/e2e/pkg/update"
    23  	"go.uber.org/zap"
    24  	netv1 "k8s.io/api/networking/v1"
    25  )
    26  
    27  const (
    28  	waitTimeout     = 5 * time.Minute
    29  	pollingInterval = 10 * time.Second
    30  )
    31  
    32  type EnvironmentNameModifier struct {
    33  	EnvironmentName string
    34  }
    35  
    36  type WildcardDNSModifier struct {
    37  	Domain string
    38  }
    39  
    40  type CustomCACertificateModifier struct {
    41  	ClusterResourceNamespace string
    42  	SecretName               string
    43  }
    44  
    45  func (u EnvironmentNameModifier) ModifyCR(cr *vzapi.Verrazzano) {
    46  	cr.Spec.EnvironmentName = u.EnvironmentName
    47  }
    48  
    49  func (u WildcardDNSModifier) ModifyCR(cr *vzapi.Verrazzano) {
    50  	if cr.Spec.Components.DNS == nil {
    51  		cr.Spec.Components.DNS = &vzapi.DNSComponent{}
    52  	}
    53  	if cr.Spec.Components.DNS.Wildcard == nil {
    54  		cr.Spec.Components.DNS.Wildcard = &vzapi.Wildcard{}
    55  	}
    56  	cr.Spec.Components.DNS.Wildcard.Domain = u.Domain
    57  }
    58  
    59  func (u CustomCACertificateModifier) ModifyCR(cr *vzapi.Verrazzano) {
    60  	if cr.Spec.Components.ClusterIssuer == nil {
    61  		cr.Spec.Components.ClusterIssuer = &vzapi.ClusterIssuerComponent{}
    62  	}
    63  	if cr.Spec.Components.ClusterIssuer.CA == nil {
    64  		cr.Spec.Components.ClusterIssuer.CA = &vzapi.CAIssuer{}
    65  	}
    66  	cr.Spec.Components.ClusterIssuer.ClusterResourceNamespace = u.ClusterResourceNamespace
    67  	cr.Spec.Components.ClusterIssuer.CA.SecretName = u.SecretName
    68  }
    69  
    70  var (
    71  	t                       = framework.NewTestFramework("update env-dns-cm")
    72  	testEnvironmentName     = "test-env"
    73  	testDNSDomain           = "sslip.io"
    74  	testCertName            = "test-ca"
    75  	testCertSecretName      = "test-secret-ca"
    76  	testCertSecretNamespace = "test-namespace"
    77  	testCertIssuerName      = "verrazzano-cluster-issuer"
    78  
    79  	currentEnvironmentName     string
    80  	currentDNSDomain           string
    81  	currentCertNamespace       = "cert-manager"
    82  	currentCertName            = "verrazzano-ca-certificate"
    83  	currentCertIssuerNamespace = "cert-manager"
    84  	currentCertIssuerName      = "verrazzano-selfsigned-issuer"
    85  	currentCertSecretNamespace = "cert-manager"
    86  	/* #nosec G101 -- This is a false positive */
    87  	currentCertSecretName = "verrazzano-ca-certificate-secret"
    88  )
    89  
    90  var afterSuite = t.AfterSuiteFunc(func() {
    91  	files := []string{testCertName + ".crt", testCertName + ".key"}
    92  	cleanupTemporaryFiles(files)
    93  })
    94  
    95  var _ = AfterSuite(afterSuite)
    96  
    97  var _ = t.Describe("Test updates to environment name, dns domain and cert-manager CA certificates", func() {
    98  
    99  	t.Context("Verify the current environment name", func() {
   100  		cr := update.GetCR()
   101  		currentEnvironmentName = pkg.GetEnvironmentName(cr)
   102  		currentDNSDomain = pkg.GetDNS(cr)
   103  		ItvalidateIngressList(currentEnvironmentName, currentDNSDomain)
   104  		ItvalidateVirtualServiceList(currentDNSDomain)
   105  	})
   106  
   107  	t.Context("Update and verify environment name", func() {
   108  		m := EnvironmentNameModifier{testEnvironmentName}
   109  		ItupdateCR(m)
   110  		ItvalidateIngressList(testEnvironmentName, currentDNSDomain)
   111  		ItvalidateVirtualServiceList(currentDNSDomain)
   112  		ItverifyIngressAccess(t.Logs)
   113  	})
   114  
   115  	t.Context("Update and verify dns domain", func() {
   116  		m := WildcardDNSModifier{testDNSDomain}
   117  		ItupdateCR(m)
   118  		ItvalidateIngressList(testEnvironmentName, testDNSDomain)
   119  		ItvalidateVirtualServiceList(testDNSDomain)
   120  		ItverifyIngressAccess(t.Logs)
   121  	})
   122  
   123  	t.Context("Update and verify CA certificate", func() {
   124  		createCustomCACertificate(testCertName, testCertSecretNamespace, testCertSecretName)
   125  		m := CustomCACertificateModifier{testCertSecretNamespace, testCertSecretName}
   126  		ItupdateCR(m)
   127  		ItvalidateCertManagerResourcesCleanup()
   128  		ItvalidateCACertificateIssuer()
   129  	})
   130  })
   131  
   132  func ItupdateCR(m update.CRModifier) {
   133  	t.It("Update the Verrazzano CR", func() {
   134  		update.UpdateCRWithRetries(m, pollingInterval, waitTimeout)
   135  	})
   136  }
   137  
   138  func ItvalidateIngressList(environmentName string, domain string) {
   139  	t.It("Expect Ingresses to contain the correct hostname and domain", func() {
   140  		Eventually(func() error {
   141  			ingressList, err := pkg.GetIngressList("")
   142  			if err != nil {
   143  				return err
   144  			}
   145  
   146  			// Verify that the ingresses contain the expected environment name and domain name
   147  			for _, ingress := range ingressList.Items {
   148  				err := validateIngress(environmentName, domain, ingress)
   149  				if err != nil {
   150  					return err
   151  				}
   152  			}
   153  			return nil
   154  		}).WithTimeout(waitTimeout).WithPolling(pollingInterval).ShouldNot(HaveOccurred())
   155  	})
   156  }
   157  
   158  func validateIngress(environmentName string, domain string, ingress netv1.Ingress) error {
   159  	// Verify that the ingress contains the expected environment name and domain name
   160  	if ingress.Namespace == constants.RancherSystemNamespace && ingress.Name == "vz-"+constants2.RancherIngress {
   161  		// If this is the copy of the Rancher ingress that VZ makes in order to retain access for the managed clusters
   162  		// until DNS updates have been pushed out to them, this ingress should have the old DNS. Skip this ingress when
   163  		// verifying that DNS was updated.
   164  		return nil
   165  	}
   166  	hostname := ingress.Spec.Rules[0].Host
   167  	if !strings.Contains(hostname, environmentName) {
   168  		return fmt.Errorf("ingress %s in namespace %s with hostname %s must contain %s", ingress.Name, ingress.Namespace, hostname, environmentName)
   169  	}
   170  	if !strings.Contains(hostname, domain) {
   171  		return fmt.Errorf("ingress %s in namespace %s with hostname %s must contain %s", ingress.Name, ingress.Namespace, hostname, domain)
   172  	}
   173  	return nil
   174  }
   175  
   176  func ItvalidateVirtualServiceList(domain string) {
   177  	// Fetch the virtual services for the deployed applications
   178  	t.It("Expect VirtualServices to contain the expected environment and domain name", func() {
   179  		Eventually(func() error {
   180  			virtualServiceList, err := pkg.GetVirtualServiceList("")
   181  			if err != nil {
   182  				return err
   183  			}
   184  
   185  			// Verify that the virtual services contain the expected environment name and domain name
   186  			for _, virtualService := range virtualServiceList.Items {
   187  				hostname := virtualService.Spec.Hosts[0]
   188  				if !strings.Contains(hostname, domain) {
   189  					return fmt.Errorf("virtual service %s in namespace %s with hostname %s must contain %s", virtualService.Name, virtualService.Namespace, hostname, domain)
   190  				}
   191  			}
   192  			return nil
   193  		}).WithTimeout(waitTimeout).WithPolling(pollingInterval).ShouldNot(HaveOccurred())
   194  	})
   195  }
   196  
   197  func ItverifyIngressAccess(log *zap.SugaredLogger) {
   198  	if log == nil {
   199  		log = zap.S()
   200  	}
   201  
   202  	t.DescribeTable("Access Ingresses",
   203  		func(access func() error) {
   204  			Eventually(access).WithTimeout(waitTimeout).WithPolling(pollingInterval).ShouldNot(HaveOccurred())
   205  		},
   206  		Entry("Access Keycloak", func() error { return pkg.VerifyKeycloakAccess(log) }),
   207  		Entry("Access Rancher", func() error { return pkg.VerifyRancherAccess(log) }),
   208  		Entry("Access Rancher with Keycloak", func() error { return pkg.VerifyRancherKeycloakAuthConfig(log) }),
   209  	)
   210  }
   211  
   212  func createCustomCACertificate(certName string, secretNamespace string, secretName string) {
   213  	log.Printf("Creating custom CA certificate")
   214  	output, err := exec.Command("/bin/sh", "create-custom-ca.sh", "-k", "-c", certName, "-s", secretName, "-n", secretNamespace).Output()
   215  	Expect(err).ToNot(HaveOccurred())
   216  	log.Println(string(output))
   217  }
   218  
   219  func fetchCACertificatesFromIssuer(certIssuer string) ([]certmanagerv1.Certificate, error) {
   220  	// Reintialize the certificate list
   221  	var certificates []certmanagerv1.Certificate
   222  	// Fetch the certificates for the deployed applications
   223  	certificateList, err := pkg.GetCertificateList("")
   224  	if err != nil {
   225  		return certificates, err
   226  	}
   227  
   228  	// Filter out the certificates that are issued by the given issuer
   229  	for _, certificate := range certificateList.Items {
   230  		if certificate.Spec.IssuerRef.Name == certIssuer {
   231  			certificates = append(certificates, certificate)
   232  		}
   233  	}
   234  	return certificates, nil
   235  }
   236  
   237  func ItvalidateCACertificateIssuer() {
   238  	t.It("Validate CA Certificate Issuer is listed Certificates", func() {
   239  		Eventually(func() error {
   240  			// Fetch the certificates
   241  			certificates, err := fetchCACertificatesFromIssuer(testCertIssuerName)
   242  			if err != nil {
   243  				return err
   244  			}
   245  			// Verify that the certificate is issued by the right cluster issuer
   246  			for _, certificate := range certificates {
   247  				if certificate.Spec.IssuerRef.Name != testCertIssuerName {
   248  					return fmt.Errorf("issuer for the certificate %s in namespace %s is %s; expected is %s", certificate.Name, certificate.Namespace, certificate.Spec.IssuerRef.Name, testCertIssuerName)
   249  				}
   250  			}
   251  			return nil
   252  		}, waitTimeout, pollingInterval).ShouldNot(HaveOccurred())
   253  	})
   254  }
   255  
   256  func ItvalidateCertManagerResourcesCleanup() {
   257  	// Verify that the certificates have been removed
   258  	t.It("Validate Certificate cleanup", func() {
   259  		Eventually(func() error {
   260  			// Fetch the certificates
   261  			certificates, err := fetchCACertificatesFromIssuer(testCertIssuerName)
   262  			if err != nil {
   263  				return err
   264  			}
   265  			// Verify that the certificate is issued by the right cluster issuer
   266  			for _, certificate := range certificates {
   267  				if certificate.Name == currentCertName {
   268  					return fmt.Errorf("certificate %s should NOT exist in the namespace %s", currentCertName, currentCertNamespace)
   269  				}
   270  			}
   271  			return nil
   272  		}, waitTimeout, pollingInterval).ShouldNot(HaveOccurred())
   273  	})
   274  
   275  	// Verify that the certificate issuer has been removed
   276  	t.It("Validate Issuer cleanup", func() {
   277  		Eventually(func() error {
   278  			// Fetch the certificates
   279  			issuerList, err := pkg.GetIssuerList(currentCertIssuerNamespace)
   280  			if err != nil {
   281  				return err
   282  			}
   283  			// Verify that the certificate is issued by the right cluster issuer
   284  			for _, issuer := range issuerList.Items {
   285  				if issuer.Name == currentCertIssuerName {
   286  					return fmt.Errorf("issuer %s should NOT exist in the namespace %s", currentCertIssuerName, currentCertIssuerNamespace)
   287  				}
   288  			}
   289  			return nil
   290  		}, waitTimeout, pollingInterval).ShouldNot(HaveOccurred())
   291  	})
   292  
   293  	// Verify that the secret used for the default certificate has been removed
   294  	t.It("Validating secret does not exist", func() {
   295  		Eventually(func() error {
   296  			_, err := pkg.GetSecret(currentCertSecretNamespace, currentCertSecretName)
   297  			return err
   298  		}).WithTimeout(waitTimeout).WithPolling(pollingInterval).Should(HaveOccurred())
   299  	})
   300  }
   301  
   302  func cleanupTemporaryFiles(files []string) {
   303  	log.Printf("Cleaning up temporary files")
   304  	for _, file := range files {
   305  		_, err := os.Stat(file)
   306  		if os.IsNotExist(err) {
   307  			log.Printf("File %s does not exist", file)
   308  			continue
   309  		}
   310  		err = os.Remove(file)
   311  		Expect(err).ToNot(HaveOccurred())
   312  	}
   313  }