github.com/verrazzano/verrazzano@v1.7.1/tests/e2e/update/nginxistio/nginxistio_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 nginxistio 5 6 import ( 7 "bytes" 8 "context" 9 "fmt" 10 "github.com/verrazzano/verrazzano/pkg/log/vzlog" 11 "github.com/verrazzano/verrazzano/pkg/nginxutil" 12 "reflect" 13 "strings" 14 "text/template" 15 "time" 16 17 "github.com/verrazzano/verrazzano/pkg/constants" 18 "github.com/verrazzano/verrazzano/pkg/k8s/resource" 19 "github.com/verrazzano/verrazzano/pkg/k8sutil" 20 vzapi "github.com/verrazzano/verrazzano/platform-operator/apis/verrazzano/v1alpha1" 21 "github.com/verrazzano/verrazzano/tests/e2e/pkg" 22 "github.com/verrazzano/verrazzano/tests/e2e/pkg/test/framework" 23 "github.com/verrazzano/verrazzano/tests/e2e/pkg/update" 24 25 . "github.com/onsi/ginkgo/v2" 26 "github.com/onsi/gomega" 27 corev1 "k8s.io/api/core/v1" 28 apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" 29 v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 30 "k8s.io/apimachinery/pkg/util/intstr" 31 "sigs.k8s.io/yaml" 32 ) 33 34 const ( 35 nginxLabelKey = "app.kubernetes.io/component" 36 nginxLabelValue = "controller" 37 istioAppLabelKey = "app" 38 istioIngressLabelValue = "istio-ingressgateway" 39 istioEgressLabelValue = "istio-egressgateway" 40 nginxIngressServiceName = "ingress-controller-ingress-nginx-controller" 41 istioIngressServiceName = "istio-ingressgateway" 42 waitTimeout = 10 * time.Minute 43 pollingInterval = 10 * time.Second 44 ociLBShapeAnnotation = "service.beta.kubernetes.io/oci-load-balancer-shape" 45 nginxTestAnnotationName = "name-n" 46 nginxTestAnnotationValue = "value-n" 47 istioTestAnnotationName = "name-i" 48 istioTestAnnotationValue = "value-i" 49 newReplicas = 3 50 nginxLBShapeValue = "flexible" 51 istioLBShapeValue = "flexible" 52 ) 53 54 var ingressNGINXNamespace string 55 56 var testNginxIngressPorts = []corev1.ServicePort{ 57 { 58 Name: "https", 59 Protocol: "TCP", 60 Port: 443, 61 NodePort: 31443, 62 TargetPort: intstr.IntOrString{ 63 Type: intstr.String, 64 StrVal: "https", 65 }, 66 }, 67 } 68 69 var testIstioIngressPorts = []corev1.ServicePort{ 70 { 71 Name: "https", 72 Protocol: "TCP", 73 Port: 443, 74 NodePort: 32443, 75 TargetPort: intstr.FromInt(8443), 76 }, 77 } 78 79 type externalLBsTemplateData struct { 80 ServerList string 81 } 82 83 type NginxAutoscalingIstioRelicasAffintyModifier struct { 84 nginxReplicas uint32 85 istioIngressReplicas uint32 86 istioEgressReplicas uint32 87 NginxIstioIngressServiceAnnotationModifier 88 } 89 90 type NginxIstioNodePortModifier struct { 91 systemExternalLBIP string 92 applicationExternalLBIP string 93 NginxAutoscalingIstioRelicasAffintyModifier 94 } 95 96 type NginxIstioLoadBalancerModifier struct { 97 NginxIstioNodePortModifier 98 } 99 100 type NginxIstioIngressServiceAnnotationModifier struct { 101 nginxLBShape string 102 istioLBShape string 103 } 104 105 func (m NginxAutoscalingIstioRelicasAffintyModifier) ModifyCR(cr *vzapi.Verrazzano) { 106 if cr.Spec.Components.Ingress == nil { 107 cr.Spec.Components.Ingress = &vzapi.IngressNginxComponent{} 108 } 109 if cr.Spec.Components.Istio == nil { 110 cr.Spec.Components.Istio = &vzapi.IstioComponent{} 111 } 112 // update nginx 113 nginxYaml := fmt.Sprintf(`controller: 114 autoscaling: 115 enabled: true 116 minReplicas: %v 117 service: 118 annotations: 119 service.beta.kubernetes.io/oci-load-balancer-shape: %s 120 name-n: value-n`, m.nginxReplicas, m.nginxLBShape) 121 cr.Spec.Components.Ingress.ValueOverrides = createOverridesOrDie(nginxYaml) 122 123 // update Istio 124 istioYaml := fmt.Sprintf(`apiVersion: install.istio.io/v1alpha1 125 kind: IstioOperator 126 spec: 127 components: 128 egressGateways: 129 - enabled: true 130 k8s: 131 replicaCount: %v 132 name: istio-egressgateway 133 ingressGateways: 134 - enabled: true 135 k8s: 136 replicaCount: %v 137 service: 138 type: LoadBalancer 139 name: istio-ingressgateway 140 values: 141 gateways: 142 istio-ingressgateway: 143 serviceAnnotations: 144 name-i: value-i 145 service.beta.kubernetes.io/oci-load-balancer-shape: %s`, m.istioEgressReplicas, m.istioIngressReplicas, m.istioLBShape) 146 cr.Spec.Components.Istio.ValueOverrides = createOverridesOrDie(istioYaml) 147 // istio 1.11.4 has a bug handling this particular Affinity 148 // it works fine if istio is installed with it 149 // but it fails updating istio with it even though running pods has met replicaCount, istio is trying to schedule more 150 // which results in pending pods 151 // 152 //if cr.Spec.Components.Istio.Ingress.Kubernetes.Affinity == nil { 153 // cr.Spec.Components.Istio.Ingress.Kubernetes.Affinity = &corev1.Affinity{} 154 //} 155 //if cr.Spec.Components.Istio.Ingress.Kubernetes.Affinity.PodAntiAffinity == nil { 156 // cr.Spec.Components.Istio.Ingress.Kubernetes.Affinity.PodAntiAffinity = &corev1.PodAntiAffinity{} 157 //} 158 //requiredIngressAntiAffinity := cr.Spec.Components.Istio.Ingress.Kubernetes.Affinity.PodAntiAffinity.RequiredDuringSchedulingIgnoredDuringExecution 159 //requiredIngressAntiAffinity = append(requiredIngressAntiAffinity, corev1.PodAffinityTerm{ 160 // LabelSelector: &metav1.LabelSelector{ 161 // MatchLabels: nil, 162 // MatchExpressions: []metav1.LabelSelectorRequirement{ 163 // { 164 // Key: istioAppLabelKey, 165 // Operator: "In", 166 // Values: []string{ 167 // istioIngressLabelValue, 168 // }, 169 // }, 170 // }, 171 // }, 172 // TopologyKey: "kubernetes.io/hostname", 173 //}) 174 //cr.Spec.Components.Istio.Ingress.Kubernetes.Affinity.PodAntiAffinity.RequiredDuringSchedulingIgnoredDuringExecution = requiredIngressAntiAffinity 175 // istio 1.11.4 has a bug handling this particular Affinity 176 // it works fine if istio is installed with it 177 // but it fails updating istio with it even though running pods has met replicaCount, istio is trying to schedule more 178 // which results in pending pods 179 //if cr.Spec.Components.Istio.Egress.Kubernetes.Affinity == nil { 180 // cr.Spec.Components.Istio.Egress.Kubernetes.Affinity = &corev1.Affinity{} 181 //} 182 //if cr.Spec.Components.Istio.Egress.Kubernetes.Affinity.PodAntiAffinity == nil { 183 // cr.Spec.Components.Istio.Egress.Kubernetes.Affinity.PodAntiAffinity = &corev1.PodAntiAffinity{} 184 //} 185 //requiredEgressAntiAffinity := cr.Spec.Components.Istio.Egress.Kubernetes.Affinity.PodAntiAffinity.RequiredDuringSchedulingIgnoredDuringExecution 186 //requiredEgressAntiAffinity = append(requiredEgressAntiAffinity, corev1.PodAffinityTerm{ 187 // LabelSelector: &metav1.LabelSelector{ 188 // MatchLabels: nil, 189 // MatchExpressions: []metav1.LabelSelectorRequirement{ 190 // { 191 // Key: istioAppLabelKey, 192 // Operator: "In", 193 // Values: []string{ 194 // istioEgressLabelValue, 195 // }, 196 // }, 197 // }, 198 // }, 199 // TopologyKey: "kubernetes.io/hostname", 200 //}) 201 //cr.Spec.Components.Istio.Egress.Kubernetes.Affinity.PodAntiAffinity.RequiredDuringSchedulingIgnoredDuringExecution = requiredEgressAntiAffinity 202 } 203 204 func (u NginxIstioNodePortModifier) ModifyCR(cr *vzapi.Verrazzano) { 205 if cr.Spec.Components.Ingress == nil { 206 cr.Spec.Components.Ingress = &vzapi.IngressNginxComponent{} 207 } 208 if cr.Spec.Components.Istio == nil { 209 cr.Spec.Components.Istio = &vzapi.IstioComponent{} 210 } 211 cr.Spec.Components.Ingress.Ports = testNginxIngressPorts 212 cr.Spec.Components.Ingress.Type = vzapi.NodePort 213 cr.Spec.Components.Ingress.NGINXInstallArgs = append(cr.Spec.Components.Ingress.NGINXInstallArgs, vzapi.InstallArgs{ 214 Name: "controller.service.externalIPs", 215 ValueList: []string{u.systemExternalLBIP}, 216 }) 217 // update nginx 218 nginxYaml := fmt.Sprintf(`controller: 219 autoscaling: 220 enabled: true 221 minReplicas: %v 222 annotations: 223 service.beta.kubernetes.io/oci-load-balancer-shape: %s 224 name-n: value-n`, u.nginxReplicas, u.nginxLBShape) 225 cr.Spec.Components.Ingress.ValueOverrides = createOverridesOrDie(nginxYaml) 226 227 istio := cr.Spec.Components.Istio 228 istio.IstioInstallArgs = []vzapi.InstallArgs{ 229 { 230 Name: "gateways.istio-ingressgateway.externalIPs", 231 ValueList: []string{u.applicationExternalLBIP}, 232 }, 233 } 234 istio.Ingress = &vzapi.IstioIngressSection{ 235 Type: vzapi.NodePort, 236 Ports: testIstioIngressPorts, 237 Kubernetes: &vzapi.IstioKubernetesSection{ 238 CommonKubernetesSpec: vzapi.CommonKubernetesSpec{ 239 Replicas: newReplicas, 240 }, 241 }, 242 } 243 istio.Egress = &vzapi.IstioEgressSection{ 244 Kubernetes: &vzapi.IstioKubernetesSection{ 245 CommonKubernetesSpec: vzapi.CommonKubernetesSpec{ 246 Replicas: newReplicas, 247 }, 248 }, 249 } 250 // update Istio 251 istioYaml := fmt.Sprintf(`apiVersion: install.istio.io/v1alpha1 252 kind: IstioOperator 253 spec: 254 values: 255 gateways: 256 istio-ingressgateway: 257 serviceAnnotations: 258 name-i: value-i 259 service.beta.kubernetes.io/oci-load-balancer-shape: %s`, u.istioLBShape) 260 cr.Spec.Components.Istio.ValueOverrides = createOverridesOrDie(istioYaml) 261 } 262 263 func (u NginxIstioLoadBalancerModifier) ModifyCR(cr *vzapi.Verrazzano) { 264 if cr.Spec.Components.Ingress == nil { 265 cr.Spec.Components.Ingress = &vzapi.IngressNginxComponent{} 266 } 267 cr.Spec.Components.Ingress.Type = vzapi.LoadBalancer 268 if cr.Spec.Components.Istio == nil { 269 cr.Spec.Components.Istio = &vzapi.IstioComponent{} 270 } 271 272 // update nginx 273 nginxYaml := fmt.Sprintf(`controller: 274 autoscaling: 275 enabled: true 276 minReplicas: %v 277 service: 278 annotations: 279 service.beta.kubernetes.io/oci-load-balancer-shape: %s 280 name-n: value-n`, u.nginxReplicas, u.nginxLBShape) 281 cr.Spec.Components.Ingress.ValueOverrides = createOverridesOrDie(nginxYaml) 282 283 // update Istio 284 istioYaml := fmt.Sprintf(`apiVersion: install.istio.io/v1alpha1 285 kind: IstioOperator 286 spec: 287 components: 288 egressGateways: 289 - enabled: true 290 k8s: 291 replicaCount: %v 292 name: istio-egressgateway 293 ingressGateways: 294 - enabled: true 295 k8s: 296 replicaCount: %v 297 service: 298 type: LoadBalancer 299 name: istio-ingressgateway 300 values: 301 gateways: 302 istio-ingressgateway: 303 serviceAnnotations: 304 name-i: value-i 305 service.beta.kubernetes.io/oci-load-balancer-shape: %s`, u.istioEgressReplicas, u.istioIngressReplicas, u.istioLBShape) 306 cr.Spec.Components.Istio.ValueOverrides = createOverridesOrDie(istioYaml) 307 } 308 309 func (u NginxIstioIngressServiceAnnotationModifier) ModifyCR(cr *vzapi.Verrazzano) { 310 if cr.Spec.Components.Ingress == nil { 311 cr.Spec.Components.Ingress = &vzapi.IngressNginxComponent{} 312 } 313 ingress := cr.Spec.Components.Ingress 314 ingress.Type = vzapi.LoadBalancer 315 nginxYaml := fmt.Sprintf(`controller: 316 service: 317 annotations: 318 service.beta.kubernetes.io/oci-load-balancer-shape: %s 319 name-n: value-n`, u.nginxLBShape) 320 ingress.ValueOverrides = createOverridesOrDie(nginxYaml) 321 if cr.Spec.Components.Istio == nil { 322 cr.Spec.Components.Istio = &vzapi.IstioComponent{} 323 } 324 istio := cr.Spec.Components.Istio 325 istioYaml := fmt.Sprintf(`apiVersion: install.istio.io/v1alpha1 326 kind: IstioOperator 327 spec: 328 components: 329 egressGateways: 330 - enabled: true 331 name: istio-egressgateway 332 ingressGateways: 333 - enabled: true 334 k8s: 335 service: 336 type: LoadBalancer 337 name: istio-ingressgateway 338 values: 339 gateways: 340 istio-ingressgateway: 341 serviceAnnotations: 342 name-i: value-i 343 service.beta.kubernetes.io/oci-load-balancer-shape: %s`, u.istioLBShape) 344 istio.ValueOverrides = createOverridesOrDie(istioYaml) 345 } 346 347 func createOverridesOrDie(yamlString string) []vzapi.Overrides { 348 data, err := yaml.YAMLToJSON([]byte(yamlString)) 349 if err != nil { 350 t.Logs.Errorf("Failed to convert yaml to JSON: %s", yamlString) 351 panic(err) 352 } 353 return []vzapi.Overrides{ 354 { 355 ConfigMapRef: nil, 356 SecretRef: nil, 357 Values: &apiextensionsv1.JSON{ 358 Raw: data, 359 }, 360 }, 361 } 362 } 363 364 var t = framework.NewTestFramework("update nginx-istio") 365 366 var systemExternalIP, applicationExternalIP string 367 368 var beforeSuite = t.BeforeSuiteFunc(func() { 369 var err error 370 systemExternalIP, applicationExternalIP, err = deployExternalLBs() 371 if err != nil { 372 Fail(err.Error()) 373 } 374 ingressNGINXNamespace, err = nginxutil.DetermineNamespaceForIngressNGINX(vzlog.DefaultLogger()) 375 if err != nil { 376 Fail(err.Error()) 377 } 378 }) 379 380 var _ = BeforeSuite(beforeSuite) 381 382 var _ = t.Describe("Update nginx-istio", Serial, Ordered, Label("f:platform-lcm.update"), func() { 383 t.Describe("verrazzano-nginx-istio verify", Label("f:platform-lcm.nginx-istio-verify"), func() { 384 t.It("nginx-istio default replicas", func() { 385 cr := update.GetCR() 386 387 expectedIstioRunning := uint32(1) 388 expectedNGINXRunning := uint32(1) 389 if cr.Spec.Profile == "prod" || cr.Spec.Profile == "" { 390 expectedIstioRunning = 2 391 expectedNGINXRunning = 2 392 } 393 update.ValidatePods(nginxLabelValue, nginxLabelKey, ingressNGINXNamespace, expectedNGINXRunning, false) 394 update.ValidatePods(istioIngressLabelValue, istioAppLabelKey, constants.IstioSystemNamespace, expectedIstioRunning, false) 395 update.ValidatePods(istioEgressLabelValue, istioAppLabelKey, constants.IstioSystemNamespace, expectedIstioRunning, false) 396 }) 397 }) 398 399 t.Describe("verrazzano-nginx-istio update ingress service annotations", Label("f:platform-lcm.nginx-istio-update-annotations"), func() { 400 t.It("nginx-istio update ingress service annotations", func() { 401 m := NginxIstioIngressServiceAnnotationModifier{nginxLBShape: nginxLBShapeValue, istioLBShape: istioLBShapeValue} 402 update.UpdateCRWithRetries(m, pollingInterval, waitTimeout) 403 404 validateServiceAnnotations(m) 405 }) 406 }) 407 408 t.Describe("verrazzano-nginx-istio update replicas", Label("f:platform-lcm.nginx-istio-update-replicas"), func() { 409 t.It("nginx-istio update replicas", func() { 410 m := NginxAutoscalingIstioRelicasAffintyModifier{ 411 nginxReplicas: newReplicas, 412 istioIngressReplicas: newReplicas, 413 istioEgressReplicas: newReplicas, 414 NginxIstioIngressServiceAnnotationModifier: NginxIstioIngressServiceAnnotationModifier{ 415 nginxLBShape: nginxLBShapeValue, 416 istioLBShape: istioLBShapeValue, 417 }, 418 } 419 update.UpdateCRWithRetries(m, pollingInterval, waitTimeout) 420 421 update.ValidatePods(nginxLabelValue, nginxLabelKey, ingressNGINXNamespace, newReplicas, false) 422 update.ValidatePods(istioIngressLabelValue, istioAppLabelKey, constants.IstioSystemNamespace, newReplicas, false) 423 update.ValidatePods(istioEgressLabelValue, istioAppLabelKey, constants.IstioSystemNamespace, newReplicas, false) 424 }) 425 }) 426 427 t.Describe("verrazzano-nginx-istio update nodeport", Label("f:platform-lcm.nginx-istio-update-nodeport"), func() { 428 t.It("nginx-istio update ingress type to nodeport", func() { 429 t.Logs.Infof("Update nginx/istio ingresses to use NodePort type with external load balancers: %s and %s", systemExternalIP, applicationExternalIP) 430 m := NginxIstioNodePortModifier{ 431 systemExternalLBIP: systemExternalIP, 432 applicationExternalLBIP: applicationExternalIP, 433 NginxAutoscalingIstioRelicasAffintyModifier: NginxAutoscalingIstioRelicasAffintyModifier{ 434 nginxReplicas: newReplicas, 435 istioIngressReplicas: newReplicas, 436 istioEgressReplicas: newReplicas, 437 NginxIstioIngressServiceAnnotationModifier: NginxIstioIngressServiceAnnotationModifier{ 438 nginxLBShape: nginxLBShapeValue, 439 istioLBShape: istioLBShapeValue, 440 }, 441 }, 442 } 443 update.UpdateCRWithRetries(m, pollingInterval, waitTimeout) 444 445 t.Logs.Info("Validate nginx/istio ingresses for NodePort type and externalIPs") 446 validateServiceNodePortAndExternalIP(systemExternalIP, applicationExternalIP) 447 }) 448 }) 449 450 t.Describe("verrazzano-nginx-istio update loadbalancer", Label("f:platform-lcm.nginx-istio-update-loadbalancer"), func() { 451 t.It("nginx-istio update ingress type to loadbalancer", func() { 452 t.Logs.Infof("Update nginx/istio ingresses to use LoadBalancer type") 453 m := NginxIstioLoadBalancerModifier{ 454 NginxIstioNodePortModifier{ 455 systemExternalLBIP: systemExternalIP, 456 applicationExternalLBIP: applicationExternalIP, 457 NginxAutoscalingIstioRelicasAffintyModifier: NginxAutoscalingIstioRelicasAffintyModifier{ 458 nginxReplicas: newReplicas, 459 istioIngressReplicas: newReplicas, 460 istioEgressReplicas: newReplicas, 461 NginxIstioIngressServiceAnnotationModifier: NginxIstioIngressServiceAnnotationModifier{ 462 nginxLBShape: nginxLBShapeValue, 463 istioLBShape: istioLBShapeValue, 464 }, 465 }, 466 }, 467 } 468 update.UpdateCRWithRetries(m, pollingInterval, waitTimeout) 469 470 t.Logs.Info("Validate nginx/istio ingresses for LoadBalancer type and loadBalancer IP") 471 validateServiceLoadBalancer() 472 }) 473 }) 474 475 t.Describe("verrazzano-nginx-istio update nodeport 2", Label("f:platform-lcm.nginx-istio-update-nodeport-2"), func() { 476 t.It("nginx-istio update ingress type to nodeport 2", func() { 477 t.Logs.Infof("Update nginx/istio ingresses to use NodePort type with external load balancers: %s and %s", systemExternalIP, applicationExternalIP) 478 m := NginxIstioNodePortModifier{ 479 systemExternalLBIP: systemExternalIP, 480 applicationExternalLBIP: applicationExternalIP, 481 NginxAutoscalingIstioRelicasAffintyModifier: NginxAutoscalingIstioRelicasAffintyModifier{ 482 nginxReplicas: newReplicas, 483 istioIngressReplicas: newReplicas, 484 istioEgressReplicas: newReplicas, 485 NginxIstioIngressServiceAnnotationModifier: NginxIstioIngressServiceAnnotationModifier{ 486 nginxLBShape: nginxLBShapeValue, 487 istioLBShape: istioLBShapeValue, 488 }, 489 }, 490 } 491 update.UpdateCRWithRetries(m, pollingInterval, waitTimeout) 492 493 t.Logs.Info("Validate nginx/istio ingresses for NodePort type and externalIPs") 494 validateServiceNodePortAndExternalIP(systemExternalIP, applicationExternalIP) 495 }) 496 }) 497 }) 498 499 func deployExternalLBs() (string, string, error) { 500 _, err := pkg.CreateNamespaceWithAnnotations("external-lb", map[string]string{}, map[string]string{}) 501 if err != nil { 502 return "", "", err 503 } 504 505 systemServerList, applicationServerList, err := buildServerLists() 506 if err != nil { 507 return "", "", err 508 } 509 510 applyResource("testdata/external-lb/system-external-lb-cm.yaml", &externalLBsTemplateData{ServerList: systemServerList}) 511 applyResource("testdata/external-lb/system-external-lb.yaml", &externalLBsTemplateData{}) 512 applyResource("testdata/external-lb/system-external-lb-svc.yaml", &externalLBsTemplateData{}) 513 applyResource("testdata/external-lb/application-external-lb-cm.yaml", &externalLBsTemplateData{ServerList: applicationServerList}) 514 applyResource("testdata/external-lb/application-external-lb.yaml", &externalLBsTemplateData{}) 515 applyResource("testdata/external-lb/application-external-lb-svc.yaml", &externalLBsTemplateData{}) 516 517 sysIP, err := getServiceLoadBalancerIP("external-lb", "system-external-lb-svc") 518 if err != nil { 519 return "", "", err 520 } 521 522 appIP, err := getServiceLoadBalancerIP("external-lb", "application-external-lb-svc") 523 if err != nil { 524 return "", "", err 525 } 526 527 return sysIP, appIP, nil 528 } 529 530 func buildServerLists() (string, string, error) { 531 nodes, err := pkg.ListNodes() 532 if err != nil { 533 return "", "", err 534 } 535 if len(nodes.Items) < 1 { 536 return "", "", fmt.Errorf("can not find node in the cluster") 537 } 538 var serverListNginx, serverListIstio string 539 for _, node := range nodes.Items { 540 if len(node.Status.Addresses) < 1 { 541 return "", "", fmt.Errorf("can not find address in the node") 542 } 543 serverListNginx = serverListNginx + fmt.Sprintf(" server %s:31443;\n", node.Status.Addresses[0].Address) 544 serverListIstio = serverListIstio + fmt.Sprintf(" server %s:32443;\n", node.Status.Addresses[0].Address) 545 } 546 return serverListNginx, serverListIstio, nil 547 } 548 549 func applyResource(resourceFile string, templateData *externalLBsTemplateData) { 550 file, err := pkg.FindTestDataFile(resourceFile) 551 if err != nil { 552 Fail(err.Error()) 553 } 554 fileTemplate, err := template.ParseFiles(file) 555 if err != nil { 556 Fail(err.Error()) 557 } 558 var buff bytes.Buffer 559 err = fileTemplate.Execute(&buff, templateData) 560 if err != nil { 561 Fail(err.Error()) 562 } 563 564 err = resource.CreateOrUpdateResourceFromBytes(buff.Bytes(), t.Logs) 565 if err != nil { 566 Fail(err.Error()) 567 } 568 } 569 570 func getServiceLoadBalancerIP(ns, svcName string) (string, error) { 571 gomega.Eventually(func() error { 572 svc, err := pkg.GetService(ns, svcName) 573 if err != nil { 574 return err 575 } 576 if len(svc.Status.LoadBalancer.Ingress) == 0 { 577 return fmt.Errorf("loadBalancer for service %s/%s is not ready yet", ns, svcName) 578 } 579 return nil 580 }, waitTimeout, pollingInterval).Should(gomega.BeNil(), fmt.Sprintf("Expected to get a loadBalancer for service %s/%s", ns, svcName)) 581 582 // Get the CR 583 svc, err := pkg.GetService(ns, svcName) 584 if err != nil { 585 return "", fmt.Errorf("can not get IP for service %s/%s due to error: %v", ns, svcName, err.Error()) 586 } 587 if len(svc.Status.LoadBalancer.Ingress) > 0 { 588 return svc.Status.LoadBalancer.Ingress[0].IP, nil 589 } 590 591 return "", fmt.Errorf("no IP is found for service %s/%s", ns, svcName) 592 } 593 594 func validateServiceAnnotations(m NginxIstioIngressServiceAnnotationModifier) { 595 gomega.Eventually(func() error { 596 var err error 597 nginxIngress, err := pkg.GetService(ingressNGINXNamespace, nginxIngressServiceName) 598 if err != nil { 599 return err 600 } 601 if nginxIngress.Annotations[nginxTestAnnotationName] != nginxTestAnnotationValue { 602 return fmt.Errorf("expect nginx ingress annotation %v with %v, but got %v", nginxTestAnnotationName, nginxTestAnnotationValue, nginxIngress.Annotations[nginxTestAnnotationName]) 603 } 604 if nginxIngress.Annotations[ociLBShapeAnnotation] != m.nginxLBShape { 605 return fmt.Errorf("expect nginx ingress annotation %v with value %v, but got %v", ociLBShapeAnnotation, m.nginxLBShape, nginxIngress.Annotations[ociLBShapeAnnotation]) 606 } 607 istioIngress, err := pkg.GetService(constants.IstioSystemNamespace, istioIngressServiceName) 608 if err != nil { 609 return err 610 } 611 if istioIngress.Annotations[istioTestAnnotationName] != istioTestAnnotationValue { 612 return fmt.Errorf("expect istio ingress annotation %v with %v, but got %v", istioTestAnnotationName, istioTestAnnotationValue, istioIngress.Annotations[istioTestAnnotationName]) 613 } 614 if istioIngress.Annotations[ociLBShapeAnnotation] != m.istioLBShape { 615 return fmt.Errorf("expect istio ingress annotation %v with value %v, but got %v", ociLBShapeAnnotation, m.istioLBShape, istioIngress.Annotations[ociLBShapeAnnotation]) 616 } 617 return nil 618 }, waitTimeout, pollingInterval).Should(gomega.BeNil(), "expect to get correct ports setting from nginx and istio services") 619 } 620 621 func validateServiceNodePortAndExternalIP(expectedSystemExternalIP, expectedApplicationExternalIP string) { 622 gomega.Eventually(func() error { 623 // validate Nginx Ingress service 624 var err error 625 nginxIngress, err := pkg.GetService(ingressNGINXNamespace, nginxIngressServiceName) 626 if err != nil { 627 return err 628 } 629 if nginxIngress.Spec.Type != corev1.ServiceTypeNodePort { 630 return fmt.Errorf("expect nginx ingress with type NodePort, but got %v", nginxIngress.Spec.Type) 631 } 632 if !reflect.DeepEqual(testNginxIngressPorts, nginxIngress.Spec.Ports) { 633 return fmt.Errorf("expect nginx ingress with ports %v, but got %v", testNginxIngressPorts, nginxIngress.Spec.Ports) 634 } 635 expectedSysIPs := []string{expectedSystemExternalIP} 636 if !reflect.DeepEqual(expectedSysIPs, nginxIngress.Spec.ExternalIPs) { 637 return fmt.Errorf("expect nginx ingress with externalIPs %v, but got %v", expectedSysIPs, nginxIngress.Spec.ExternalIPs) 638 } 639 640 // validate Istio Ingress Service 641 istioIngress, err := pkg.GetService(constants.IstioSystemNamespace, istioIngressServiceName) 642 if err != nil { 643 return err 644 } 645 if istioIngress.Spec.Type != corev1.ServiceTypeNodePort { 646 return fmt.Errorf("expect istio ingress with type NodePort, but got %v", istioIngress.Spec.Type) 647 } 648 if !reflect.DeepEqual(testIstioIngressPorts, istioIngress.Spec.Ports) { 649 return fmt.Errorf("expect istio ingress with ports %v, but got %v", testNginxIngressPorts, istioIngress.Spec.Ports) 650 } 651 expectedAppIPs := []string{expectedApplicationExternalIP} 652 if !reflect.DeepEqual(expectedAppIPs, istioIngress.Spec.ExternalIPs) { 653 return fmt.Errorf("expect istio ingress with externalIPs %v, but got %v", expectedAppIPs, istioIngress.Spec.ExternalIPs) 654 } 655 656 // validate Ingress Host 657 err = validateIngressHost(expectedSystemExternalIP, "keycloak", "keycloak") 658 if err != nil { 659 return err 660 } 661 err = validateIngressHost(expectedSystemExternalIP, "verrazzano-ingress", "verrazzano-system") 662 if err != nil { 663 return err 664 } 665 666 // validate application Host 667 err = validateApplicationHost(expectedApplicationExternalIP) 668 if err != nil { 669 return err 670 } 671 672 return nil 673 }, waitTimeout, pollingInterval).Should(gomega.BeNil(), "expect to get NodePort type and externalIPs from nginx and istio services") 674 } 675 676 func validateServiceLoadBalancer() { 677 gomega.Eventually(func() error { 678 // validate Nginx Ingress service 679 var err error 680 nginxIngress, err := pkg.GetService(ingressNGINXNamespace, nginxIngressServiceName) 681 if err != nil { 682 return err 683 } 684 if nginxIngress.Spec.Type != corev1.ServiceTypeLoadBalancer { 685 return fmt.Errorf("expect nginx ingress with type LoadBalancer, but got %v", nginxIngress.Spec.Type) 686 } 687 nginxLBIP, err := getServiceLoadBalancerIP(ingressNGINXNamespace, nginxIngressServiceName) 688 if err != nil { 689 return err 690 } 691 if len(nginxLBIP) == 0 { 692 return fmt.Errorf("invalid loadBalancer IP %s for nginx", nginxLBIP) 693 } 694 695 // validate Istio Ingress Service 696 istioIngress, err := pkg.GetService(constants.IstioSystemNamespace, istioIngressServiceName) 697 if err != nil { 698 return err 699 } 700 if istioIngress.Spec.Type != corev1.ServiceTypeLoadBalancer { 701 return fmt.Errorf("expect istio ingress with type LoadBalancer, but got %v", istioIngress.Spec.Type) 702 } 703 istioLBIP, err := getServiceLoadBalancerIP(constants.IstioSystemNamespace, istioIngressServiceName) 704 if err != nil { 705 return err 706 } 707 if len(istioLBIP) == 0 { 708 return fmt.Errorf("invalid loadBalancer IP %s for istio", istioLBIP) 709 } 710 711 // validate Ingress Host 712 err = validateIngressHost(nginxLBIP, "keycloak", "keycloak") 713 if err != nil { 714 return err 715 } 716 err = validateIngressHost(nginxLBIP, "verrazzano-ingress", "verrazzano-system") 717 if err != nil { 718 return err 719 } 720 721 // validate application Host 722 err = validateApplicationHost(istioLBIP) 723 if err != nil { 724 return err 725 } 726 727 return nil 728 }, waitTimeout, pollingInterval).Should(gomega.BeNil(), "expect to get LoadBalancer type and loadBalancer IP from nginx and istio services") 729 } 730 731 func validateIngressHost(expectedIP, ingressName, ns string) error { 732 kubeConfigPath, err := k8sutil.GetKubeConfigLocation() 733 if err != nil { 734 return err 735 } 736 clientset, err := pkg.GetKubernetesClientsetForCluster(kubeConfigPath) 737 if err != nil { 738 return err 739 } 740 ingress, err := clientset.NetworkingV1().Ingresses(ns).Get(context.TODO(), ingressName, v1.GetOptions{}) 741 if err != nil { 742 return err 743 } 744 if len(ingress.Spec.Rules) == 0 { 745 return fmt.Errorf("expect Ingress %s/%s to have at least one host", ns, ingressName) 746 } 747 host := ingress.Spec.Rules[0].Host 748 if !strings.Contains(host, expectedIP) { 749 return fmt.Errorf("expect Ingress %s/%s Host %s to contain IP %s", ns, ingressName, host, expectedIP) 750 } 751 return nil 752 } 753 754 func validateApplicationHost(expectedIP string) error { 755 host, err := k8sutil.GetHostnameFromGateway("hello-helidon", "") 756 if err != nil { 757 return err 758 } 759 if !strings.Contains(host, expectedIP) { 760 return fmt.Errorf("expect hello-helidon HOST %s to contain IP %s", host, expectedIP) 761 } 762 return nil 763 }