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 }