istio.io/istio@v0.0.0-20240520182934-d79c90f27776/tests/integration/security/sds_ingress/util/util.go (about) 1 //go:build integ 2 // +build integ 3 4 // Copyright Istio Authors 5 // 6 // Licensed under the Apache License, Version 2.0 (the "License"); 7 // you may not use this file except in compliance with the License. 8 // You may obtain a copy of the License at 9 // 10 // http://www.apache.org/licenses/LICENSE-2.0 11 // 12 // Unless required by applicable law or agreed to in writing, software 13 // distributed under the License is distributed on an "AS IS" BASIS, 14 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 // See the License for the specific language governing permissions and 16 // limitations under the License. 17 18 package util 19 20 import ( 21 "context" 22 "fmt" 23 "net/http" 24 "strings" 25 "time" 26 27 "github.com/hashicorp/go-multierror" 28 v1 "k8s.io/api/core/v1" 29 "k8s.io/apimachinery/pkg/api/errors" 30 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 31 "sigs.k8s.io/yaml" 32 33 "istio.io/istio/pkg/config/protocol" 34 "istio.io/istio/pkg/http/headers" 35 "istio.io/istio/pkg/test/framework" 36 "istio.io/istio/pkg/test/framework/components/cluster" 37 "istio.io/istio/pkg/test/framework/components/echo" 38 "istio.io/istio/pkg/test/framework/components/echo/check" 39 "istio.io/istio/pkg/test/framework/components/echo/common/deployment" 40 "istio.io/istio/pkg/test/framework/components/echo/echotest" 41 "istio.io/istio/pkg/test/framework/components/istio" 42 "istio.io/istio/pkg/test/framework/components/istio/ingress" 43 "istio.io/istio/pkg/test/framework/components/namespace" 44 "istio.io/istio/pkg/test/framework/resource" 45 "istio.io/istio/pkg/test/util/retry" 46 ) 47 48 const ( 49 // The ID/name for the certificate chain in kubernetes tls secret. 50 tlsScrtCert = "tls.crt" 51 // The ID/name for the k8sKey in kubernetes tls secret. 52 tlsScrtKey = "tls.key" 53 // The ID/name for the CA certificate in kubernetes tls secret 54 tlsScrtCaCert = "ca.crt" 55 // The ID/name for the CRL in kubernetes tls secret 56 tlsScrtCaCrl = "ca.crl" 57 // The ID/name for the certificate chain in kubernetes generic secret. 58 genericScrtCert = "cert" 59 // The ID/name for the private key in kubernetes generic secret. 60 genericScrtKey = "key" 61 // The ID/name for the CA certificate in kubernetes generic secret. 62 genericScrtCaCert = "cacert" 63 // The ID/name for the CRL in kubernetes generic secret. 64 genericScrtCaCrl = "crl" 65 ASvc = "a" 66 VMSvc = "vm" 67 ) 68 69 type EchoDeployments struct { 70 ServerNs namespace.Instance 71 All echo.Instances 72 } 73 74 type IngressCredential struct { 75 PrivateKey string 76 Certificate string 77 CaCert string 78 Crl string 79 } 80 81 var IngressCredentialA = IngressCredential{ 82 PrivateKey: TLSServerKeyA, 83 Certificate: TLSServerCertA, 84 CaCert: CaCertA, 85 } 86 87 var IngressCredentialServerKeyCertA = IngressCredential{ 88 PrivateKey: TLSServerKeyA, 89 Certificate: TLSServerCertA, 90 } 91 92 var IngressCredentialCaCertA = IngressCredential{ 93 CaCert: CaCertA, 94 } 95 96 var IngressCredentialB = IngressCredential{ 97 PrivateKey: TLSServerKeyB, 98 Certificate: TLSServerCertB, 99 CaCert: CaCertB, 100 } 101 102 var IngressCredentialServerKeyCertB = IngressCredential{ 103 PrivateKey: TLSServerKeyB, 104 Certificate: TLSServerCertB, 105 } 106 107 var ( 108 A echo.Instances 109 VM echo.Instances 110 ) 111 112 // IngressKubeSecretYAML will generate a credential for a gateway 113 func IngressKubeSecretYAML(name, namespace string, ingressType CallType, ingressCred IngressCredential) string { 114 // Create Kubernetes secret for ingress gateway 115 secret := createSecret(ingressType, name, namespace, ingressCred, true) 116 by, err := yaml.Marshal(secret) 117 if err != nil { 118 panic(err) 119 } 120 return string(by) + "\n---\n" 121 } 122 123 // CreateIngressKubeSecret reads credential names from credNames and key/cert from ingressCred, 124 // and creates K8s secrets for ingress gateway. 125 // nolint: interfacer 126 func CreateIngressKubeSecret(t framework.TestContext, credName string, 127 ingressType CallType, ingressCred IngressCredential, isCompoundAndNotGeneric bool, clusters ...cluster.Cluster, 128 ) { 129 t.Helper() 130 131 // Get namespace for ingress gateway pod. 132 istioCfg := istio.DefaultConfigOrFail(t, t) 133 systemNS := namespace.ClaimOrFail(t, t, istioCfg.SystemNamespace) 134 CreateIngressKubeSecretInNamespace(t, credName, ingressType, ingressCred, isCompoundAndNotGeneric, systemNS.Name(), clusters...) 135 } 136 137 // CreateIngressKubeSecretInNamespace reads credential names from credNames and key/cert from ingressCred, 138 // and creates K8s secrets for ingress gateway in the given namespace. 139 func CreateIngressKubeSecretInNamespace(t framework.TestContext, credName string, 140 ingressType CallType, ingressCred IngressCredential, isCompoundAndNotGeneric bool, ns string, clusters ...cluster.Cluster, 141 ) { 142 t.Helper() 143 144 t.CleanupConditionally(func() { 145 deleteKubeSecret(t, credName) 146 }) 147 148 // Create Kubernetes secret for ingress gateway 149 wg := multierror.Group{} 150 if len(clusters) == 0 { 151 clusters = t.Clusters() 152 } 153 for _, c := range clusters { 154 c := c 155 wg.Go(func() error { 156 secret := createSecret(ingressType, credName, ns, ingressCred, isCompoundAndNotGeneric) 157 _, err := c.Kube().CoreV1().Secrets(ns).Create(context.TODO(), secret, metav1.CreateOptions{}) 158 if err != nil { 159 if errors.IsAlreadyExists(err) { 160 if _, err := c.Kube().CoreV1().Secrets(ns).Update(context.TODO(), secret, metav1.UpdateOptions{}); err != nil { 161 return fmt.Errorf("failed to update secret (error: %s)", err) 162 } 163 } else { 164 return fmt.Errorf("failed to update secret (error: %s)", err) 165 } 166 } 167 // Check if Kubernetes secret is ready 168 return retry.UntilSuccess(func() error { 169 _, err := c.Kube().CoreV1().Secrets(ns).Get(context.TODO(), credName, metav1.GetOptions{}) 170 if err != nil { 171 return fmt.Errorf("secret %v not found: %v", credName, err) 172 } 173 return nil 174 }, retry.Timeout(time.Second*5)) 175 }) 176 } 177 if err := wg.Wait().ErrorOrNil(); err != nil { 178 t.Fatal(err) 179 } 180 } 181 182 // deleteKubeSecret deletes a secret 183 // nolint: interfacer 184 func deleteKubeSecret(t framework.TestContext, credName string) { 185 // Get namespace for ingress gateway pod. 186 istioCfg := istio.DefaultConfigOrFail(t, t) 187 systemNS := namespace.ClaimOrFail(t, t, istioCfg.SystemNamespace) 188 189 // Delete Kubernetes secret for ingress gateway 190 c := t.Clusters().Default() 191 var immediate int64 192 err := c.Kube().CoreV1().Secrets(systemNS.Name()).Delete(context.TODO(), credName, 193 metav1.DeleteOptions{GracePeriodSeconds: &immediate}) 194 if err != nil && !errors.IsNotFound(err) { 195 t.Fatalf("Failed to delete secret (error: %s)", err) 196 } 197 } 198 199 // createSecret creates a kubernetes secret which stores private key, server certificate for TLS ingress gateway. 200 // For mTLS ingress gateway, createSecret adds ca certificate into the secret object. 201 202 func createSecret(ingressType CallType, cn, ns string, ic IngressCredential, isCompoundAndNotGeneric bool) *v1.Secret { 203 if ingressType == Mtls { 204 if isCompoundAndNotGeneric { 205 return &v1.Secret{ 206 TypeMeta: metav1.TypeMeta{ 207 Kind: "Secret", 208 APIVersion: "v1", 209 }, 210 ObjectMeta: metav1.ObjectMeta{ 211 Name: cn, 212 Namespace: ns, 213 }, 214 Data: map[string][]byte{ 215 tlsScrtCert: []byte(ic.Certificate), 216 tlsScrtKey: []byte(ic.PrivateKey), 217 tlsScrtCaCert: []byte(ic.CaCert), 218 tlsScrtCaCrl: []byte(ic.Crl), 219 }, 220 } 221 } 222 return &v1.Secret{ 223 TypeMeta: metav1.TypeMeta{ 224 Kind: "Secret", 225 APIVersion: "v1", 226 }, 227 ObjectMeta: metav1.ObjectMeta{ 228 Name: cn, 229 Namespace: ns, 230 }, 231 Data: map[string][]byte{ 232 genericScrtCert: []byte(ic.Certificate), 233 genericScrtKey: []byte(ic.PrivateKey), 234 genericScrtCaCert: []byte(ic.CaCert), 235 genericScrtCaCrl: []byte(ic.Crl), 236 }, 237 } 238 } 239 data := map[string][]byte{} 240 if ic.Certificate != "" { 241 data[tlsScrtCert] = []byte(ic.Certificate) 242 } 243 if ic.PrivateKey != "" { 244 data[tlsScrtKey] = []byte(ic.PrivateKey) 245 } 246 if ic.CaCert != "" { 247 data[tlsScrtCaCert] = []byte(ic.CaCert) 248 } 249 return &v1.Secret{ 250 TypeMeta: metav1.TypeMeta{ 251 Kind: "Secret", 252 APIVersion: "v1", 253 }, 254 ObjectMeta: metav1.ObjectMeta{ 255 Name: cn, 256 Namespace: ns, 257 }, 258 Data: data, 259 } 260 } 261 262 // CallType defines ingress gateway type 263 type CallType int 264 265 const ( 266 TLS CallType = iota 267 Mtls 268 ) 269 270 type ExpectedResponse struct { 271 StatusCode int 272 SkipErrorMessageVerification bool 273 ErrorMessage string 274 } 275 276 type TLSContext struct { 277 // CaCert is inline base64 encoded root certificate that authenticates server certificate provided 278 // by ingress gateway. 279 CaCert string 280 // PrivateKey is inline base64 encoded private key for test client. 281 PrivateKey string 282 // Cert is inline base64 encoded certificate for test client. 283 Cert string 284 } 285 286 // SendRequestOrFail makes HTTPS request to ingress gateway to visit product page 287 func SendRequestOrFail(ctx framework.TestContext, ing ingress.Instance, host string, path string, 288 callType CallType, tlsCtx TLSContext, exRsp ExpectedResponse, 289 ) { 290 doSendRequestsOrFail(ctx, ing, host, path, callType, tlsCtx, exRsp, false /* useHTTP3 */) 291 } 292 293 func SendQUICRequestsOrFail(ctx framework.TestContext, ing ingress.Instance, host string, path string, 294 callType CallType, tlsCtx TLSContext, exRsp ExpectedResponse, 295 ) { 296 doSendRequestsOrFail(ctx, ing, host, path, callType, tlsCtx, exRsp, true /* useHTTP3 */) 297 } 298 299 func doSendRequestsOrFail(ctx framework.TestContext, ing ingress.Instance, host string, path string, 300 callType CallType, tlsCtx TLSContext, exRsp ExpectedResponse, useHTTP3 bool, 301 ) { 302 ctx.Helper() 303 opts := echo.CallOptions{ 304 Timeout: time.Second, 305 Retry: echo.Retry{ 306 Options: []retry.Option{retry.Timeout(time.Minute * 2)}, 307 }, 308 Port: echo.Port{ 309 Protocol: protocol.HTTPS, 310 }, 311 HTTP: echo.HTTP{ 312 HTTP3: useHTTP3, 313 Path: fmt.Sprintf("/%s", path), 314 Headers: headers.New().WithHost(host).Build(), 315 }, 316 TLS: echo.TLS{ 317 CaCert: tlsCtx.CaCert, 318 }, 319 Check: func(result echo.CallResult, err error) error { 320 // Check that the error message is expected. 321 if err != nil { 322 // If expected error message is empty, but we got some error 323 // message then it should be treated as error when error message 324 // verification is not skipped. Error message verification is skipped 325 // when the error message is non-deterministic. 326 if !exRsp.SkipErrorMessageVerification && len(exRsp.ErrorMessage) == 0 { 327 return fmt.Errorf("unexpected error: %w", err) 328 } 329 if !exRsp.SkipErrorMessageVerification && !strings.Contains(err.Error(), exRsp.ErrorMessage) { 330 return fmt.Errorf("expected response error message %s but got %w", 331 exRsp.ErrorMessage, err) 332 } 333 return nil 334 } 335 if callType == Mtls { 336 return check.And(check.Status(exRsp.StatusCode), check.MTLSForHTTP()).Check(result, nil) 337 } 338 339 return check.Status(exRsp.StatusCode).Check(result, nil) 340 }, 341 } 342 343 if callType == Mtls { 344 opts.TLS.Key = tlsCtx.PrivateKey 345 opts.TLS.Cert = tlsCtx.Cert 346 } 347 348 // Certs occasionally take quite a while to become active in Envoy, so retry for a long time (2min) 349 ing.CallOrFail(ctx, opts) 350 } 351 352 // RotateSecrets deletes kubernetes secrets by name in credNames and creates same secrets using key/cert 353 // from ingressCred. 354 func RotateSecrets(ctx framework.TestContext, credName string, // nolint:interfacer 355 ingressType CallType, ingressCred IngressCredential, isCompoundAndNotGeneric bool, 356 ) { 357 ctx.Helper() 358 c := ctx.Clusters().Default() 359 ist := istio.GetOrFail(ctx, ctx) 360 systemNS := namespace.ClaimOrFail(ctx, ctx, ist.Settings().SystemNamespace) 361 scrt, err := c.Kube().CoreV1().Secrets(systemNS.Name()).Get(context.TODO(), credName, metav1.GetOptions{}) 362 if err != nil { 363 ctx.Errorf("Failed to get secret %s:%s (error: %s)", systemNS.Name(), credName, err) 364 } 365 scrt = updateSecret(ingressType, scrt, ingressCred, isCompoundAndNotGeneric) 366 if _, err = c.Kube().CoreV1().Secrets(systemNS.Name()).Update(context.TODO(), scrt, metav1.UpdateOptions{}); err != nil { 367 ctx.Errorf("Failed to update secret %s:%s (error: %s)", scrt.Namespace, scrt.Name, err) 368 } 369 // Check if Kubernetes secret is ready 370 retry.UntilSuccessOrFail(ctx, func() error { 371 _, err := c.Kube().CoreV1().Secrets(systemNS.Name()).Get(context.TODO(), credName, metav1.GetOptions{}) 372 if err != nil { 373 return fmt.Errorf("secret %v not found: %v", credName, err) 374 } 375 return nil 376 }, retry.Timeout(time.Second*5)) 377 } 378 379 // createSecret creates a kubernetes secret which stores private key, server certificate for TLS ingress gateway. 380 // For mTLS ingress gateway, createSecret adds ca certificate into the secret object. 381 func updateSecret(ingressType CallType, scrt *v1.Secret, ic IngressCredential, isCompoundAndNotGeneric bool) *v1.Secret { 382 if ingressType == Mtls { 383 if isCompoundAndNotGeneric { 384 scrt.Data[tlsScrtCert] = []byte(ic.Certificate) 385 scrt.Data[tlsScrtKey] = []byte(ic.PrivateKey) 386 scrt.Data[tlsScrtCaCert] = []byte(ic.CaCert) 387 scrt.Data[tlsScrtCaCrl] = []byte(ic.Crl) 388 } else { 389 scrt.Data[genericScrtCert] = []byte(ic.Certificate) 390 scrt.Data[genericScrtKey] = []byte(ic.PrivateKey) 391 scrt.Data[genericScrtCaCert] = []byte(ic.CaCert) 392 scrt.Data[genericScrtCaCrl] = []byte(ic.Crl) 393 } 394 } else { 395 scrt.Data[tlsScrtCert] = []byte(ic.Certificate) 396 scrt.Data[tlsScrtKey] = []byte(ic.PrivateKey) 397 } 398 return scrt 399 } 400 401 func EchoConfig(service string, ns namespace.Instance, buildVM bool) echo.Config { 402 return echo.Config{ 403 Service: service, 404 Namespace: ns, 405 Ports: []echo.Port{ 406 { 407 Name: "http", 408 Protocol: protocol.HTTP, 409 // We use a port > 1024 to not require root 410 WorkloadPort: 8090, 411 }, 412 }, 413 DeployAsVM: buildVM, 414 } 415 } 416 417 func SetupTest(ctx resource.Context, customCfg *[]echo.Config, customNs namespace.Getter) error { 418 var customConfig []echo.Config 419 buildVM := !ctx.Settings().Skip(echo.VM) 420 a := EchoConfig(ASvc, customNs.Get(), false) 421 vm := EchoConfig(VMSvc, customNs.Get(), buildVM) 422 customConfig = append(customConfig, a, vm) 423 *customCfg = customConfig 424 return nil 425 } 426 427 type TestConfig struct { 428 Mode string 429 CredentialName string 430 Host string 431 ServiceName string 432 GatewayLabel string 433 } 434 435 const vsTemplate = ` 436 apiVersion: networking.istio.io/v1alpha3 437 kind: VirtualService 438 metadata: 439 name: {{.CredentialName}} 440 spec: 441 hosts: 442 - "{{.Host}}" 443 gateways: 444 - {{.CredentialName}} 445 http: 446 - match: 447 - uri: 448 exact: /{{.CredentialName}} 449 route: 450 - destination: 451 host: {{.ServiceName}} 452 port: 453 number: 80 454 ` 455 456 const gwTemplate = ` 457 apiVersion: networking.istio.io/v1alpha3 458 kind: Gateway 459 metadata: 460 name: {{.CredentialName}} 461 spec: 462 selector: 463 istio: {{.GatewayLabel | default "ingressgateway"}} 464 servers: 465 - port: 466 number: 443 467 name: https 468 protocol: HTTPS 469 tls: 470 mode: {{.Mode}} 471 credentialName: "{{.CredentialName}}" 472 hosts: 473 - "{{.Host}}" 474 ` 475 476 func SetupConfig(ctx framework.TestContext, ns namespace.Instance, config ...TestConfig) { 477 b := ctx.ConfigIstio().New() 478 for _, c := range config { 479 b.Eval(ns.Name(), c, vsTemplate) 480 b.Eval(ns.Name(), c, gwTemplate) 481 } 482 b.ApplyOrFail(ctx) 483 } 484 485 // RunTestMultiMtlsGateways deploys multiple mTLS gateways with SDS enabled, and creates kubernetes secret that stores 486 // private key, server certificate and CA certificate for each mTLS gateway. Verifies that all gateways are able to terminate 487 // mTLS connections successfully. 488 func RunTestMultiMtlsGateways(ctx framework.TestContext, inst istio.Instance, ns namespace.Getter) { // nolint:interfacer 489 var credNames []string 490 var tests []TestConfig 491 echotest.New(ctx, A). 492 SetupForDestination(func(ctx framework.TestContext, to echo.Target) error { 493 for i := 1; i < 6; i++ { 494 cred := fmt.Sprintf("runtestmultimtlsgateways-%d", i) 495 tests = append(tests, TestConfig{ 496 Mode: "MUTUAL", 497 CredentialName: cred, 498 Host: fmt.Sprintf("runtestmultimtlsgateways%d.example.com", i), 499 ServiceName: to.Config().Service, 500 GatewayLabel: inst.Settings().IngressGatewayIstioLabel, 501 }) 502 credNames = append(credNames, cred) 503 } 504 SetupConfig(ctx, ns.Get(), tests...) 505 return nil 506 }). 507 To(echotest.SimplePodServiceAndAllSpecial(1)). 508 RunFromClusters(func(ctx framework.TestContext, fromCluster cluster.Cluster, to echo.Target) { 509 for _, cn := range credNames { 510 CreateIngressKubeSecret(ctx, cn, Mtls, IngressCredentialA, false) 511 } 512 513 ing := inst.IngressFor(fromCluster) 514 if ing == nil { 515 ctx.Skip() 516 } 517 tlsContext := TLSContext{ 518 CaCert: CaCertA, 519 PrivateKey: TLSClientKeyA, 520 Cert: TLSClientCertA, 521 } 522 callType := Mtls 523 524 for _, h := range tests { 525 ctx.NewSubTest(h.Host).Run(func(t framework.TestContext) { 526 SendRequestOrFail(t, ing, h.Host, h.CredentialName, callType, tlsContext, 527 ExpectedResponse{StatusCode: http.StatusOK}) 528 }) 529 } 530 }) 531 } 532 533 // RunTestMultiTLSGateways deploys multiple TLS gateways with SDS enabled, and creates kubernetes secret that stores 534 // private key and server certificate for each TLS gateway. Verifies that all gateways are able to terminate 535 // SSL connections successfully. 536 func RunTestMultiTLSGateways(t framework.TestContext, inst istio.Instance, ns namespace.Getter) { // nolint:interfacer 537 var credNames []string 538 var tests []TestConfig 539 echotest.New(t, A). 540 SetupForDestination(func(t framework.TestContext, to echo.Target) error { 541 for i := 1; i < 6; i++ { 542 cred := fmt.Sprintf("runtestmultitlsgateways-%d", i) 543 tests = append(tests, TestConfig{ 544 Mode: "SIMPLE", 545 CredentialName: cred, 546 Host: fmt.Sprintf("runtestmultitlsgateways%d.example.com", i), 547 ServiceName: to.Config().Service, 548 GatewayLabel: inst.Settings().IngressGatewayIstioLabel, 549 }) 550 credNames = append(credNames, cred) 551 } 552 SetupConfig(t, ns.Get(), tests...) 553 return nil 554 }). 555 To(echotest.SimplePodServiceAndAllSpecial(1)). 556 RunFromClusters(func(t framework.TestContext, fromCluster cluster.Cluster, to echo.Target) { 557 for _, cn := range credNames { 558 CreateIngressKubeSecret(t, cn, TLS, IngressCredentialA, false) 559 } 560 561 ing := inst.IngressFor(fromCluster) 562 if ing == nil { 563 t.Skip() 564 } 565 tlsContext := TLSContext{ 566 CaCert: CaCertA, 567 } 568 callType := TLS 569 570 for _, h := range tests { 571 t.NewSubTest(h.Host).Run(func(t framework.TestContext) { 572 SendRequestOrFail(t, ing, h.Host, h.CredentialName, callType, tlsContext, 573 ExpectedResponse{StatusCode: http.StatusOK}) 574 }) 575 } 576 }) 577 } 578 579 // RunTestMultiQUICGateways deploys multiple TLS/mTLS gateways with SDS enabled, and creates kubernetes secret that stores 580 // private key and server certificate for each TLS/mTLS gateway. Verifies that all gateways are able to terminate 581 // QUIC connections successfully. 582 func RunTestMultiQUICGateways(t framework.TestContext, inst istio.Instance, callType CallType, ns namespace.Getter) { 583 var credNames []string 584 var tests []TestConfig 585 allInstances := []echo.Instances{A, VM} 586 for _, instances := range allInstances { 587 echotest.New(t, instances). 588 SetupForDestination(func(t framework.TestContext, to echo.Target) error { 589 for i := 1; i < 6; i++ { 590 cred := fmt.Sprintf("runtestmultitlsgateways-%d", i) 591 mode := "SIMPLE" 592 if callType == Mtls { 593 mode = "MUTUAL" 594 } 595 tests = append(tests, TestConfig{ 596 Mode: mode, 597 CredentialName: cred, 598 Host: fmt.Sprintf("runtestmultitlsgateways%d.example.com", i), 599 ServiceName: to.Config().Service, 600 GatewayLabel: inst.Settings().IngressGatewayIstioLabel, 601 }) 602 credNames = append(credNames, cred) 603 } 604 SetupConfig(t, ns.Get(), tests...) 605 return nil 606 }). 607 To(echotest.SimplePodServiceAndAllSpecial(1)). 608 RunFromClusters(func(t framework.TestContext, fromCluster cluster.Cluster, to echo.Target) { 609 for _, cn := range credNames { 610 CreateIngressKubeSecret(t, cn, TLS, IngressCredentialA, false) 611 } 612 613 ing := inst.IngressFor(fromCluster) 614 if ing == nil { 615 t.Skip() 616 } 617 tlsContext := TLSContext{ 618 CaCert: CaCertA, 619 } 620 if callType == Mtls { 621 tlsContext = TLSContext{ 622 CaCert: CaCertA, 623 PrivateKey: TLSClientKeyA, 624 Cert: TLSClientCertA, 625 } 626 } 627 628 for _, h := range tests { 629 t.NewSubTest(h.Host).Run(func(t framework.TestContext) { 630 SendQUICRequestsOrFail(t, ing, h.Host, h.CredentialName, callType, tlsContext, 631 ExpectedResponse{StatusCode: http.StatusOK}) 632 }) 633 } 634 }) 635 } 636 } 637 638 func CreateCustomInstances(apps *deployment.SingleNamespaceView) error { 639 for index, namespacedName := range apps.EchoNamespace.All.NamespacedNames() { 640 switch { 641 case namespacedName.Name == "a": 642 A = apps.EchoNamespace.All[index] 643 case namespacedName.Name == "vm": 644 VM = apps.EchoNamespace.All[index] 645 } 646 } 647 return nil 648 }