This is an automated email from the ASF dual-hosted git repository.

alinsran pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/apisix-ingress-controller.git


The following commit(s) were added to refs/heads/master by this push:
     new fe5c1357 feat(apisixupstream): support portLevelSettings (#2582)
fe5c1357 is described below

commit fe5c1357ba76b1f73ecd49a0cc68583277271411
Author: AlinsRan <[email protected]>
AuthorDate: Mon Sep 29 15:13:33 2025 +0800

    feat(apisixupstream): support portLevelSettings (#2582)
---
 api/v2/apisixroute_types.go                        |   6 +
 .../crd/bases/apisix.apache.org_apisixroutes.yaml  |   6 +
 internal/adc/translator/apisixroute.go             | 100 ++++++++++++-----
 internal/adc/translator/apisixupstream.go          | 104 ++++++++++-------
 test/e2e/crds/v2/upstream.go                       | 124 +++++++++++++++++++++
 test/e2e/framework/apisix_consts.go                | 105 +++++++++++++++++
 test/e2e/framework/manifests/nginx.yaml            |  37 ++++++
 test/e2e/framework/nginx.go                        |   2 +
 8 files changed, 416 insertions(+), 68 deletions(-)

diff --git a/api/v2/apisixroute_types.go b/api/v2/apisixroute_types.go
index f7e79d20..376a3907 100644
--- a/api/v2/apisixroute_types.go
+++ b/api/v2/apisixroute_types.go
@@ -36,9 +36,15 @@ type ApisixRouteSpec struct {
        IngressClassName string `json:"ingressClassName,omitempty" 
yaml:"ingressClassName,omitempty"`
        // HTTP defines a list of HTTP route rules.
        // Each rule specifies conditions to match HTTP requests and how to 
forward them.
+       //
+       // +listType=map
+       // +listMapKey=name
        HTTP []ApisixRouteHTTP `json:"http,omitempty" yaml:"http,omitempty"`
        // Stream defines a list of stream route rules.
        // Each rule specifies conditions to match TCP/UDP traffic and how to 
forward them.
+       //
+       // +listType=map
+       // +listMapKey=name
        Stream []ApisixRouteStream `json:"stream,omitempty" 
yaml:"stream,omitempty"`
 }
 
diff --git a/config/crd/bases/apisix.apache.org_apisixroutes.yaml 
b/config/crd/bases/apisix.apache.org_apisixroutes.yaml
index 1d47fec4..51ec52c2 100644
--- a/config/crd/bases/apisix.apache.org_apisixroutes.yaml
+++ b/config/crd/bases/apisix.apache.org_apisixroutes.yaml
@@ -346,6 +346,9 @@ spec:
                   - name
                   type: object
                 type: array
+                x-kubernetes-list-map-keys:
+                - name
+                x-kubernetes-list-type: map
               ingressClassName:
                 description: |-
                   IngressClassName is the name of the IngressClass this route 
belongs to.
@@ -459,6 +462,9 @@ spec:
                   - protocol
                   type: object
                 type: array
+                x-kubernetes-list-map-keys:
+                - name
+                x-kubernetes-list-type: map
             type: object
           status:
             description: ApisixStatus is the status report for Apisix ingress 
Resources
diff --git a/internal/adc/translator/apisixroute.go 
b/internal/adc/translator/apisixroute.go
index 0fcf4424..5e2e1233 100644
--- a/internal/adc/translator/apisixroute.go
+++ b/internal/adc/translator/apisixroute.go
@@ -211,30 +211,12 @@ func (t *Translator) buildUpstream(tctx 
*provider.TranslateContext, service *adc
        )
 
        for _, backend := range rule.Backends {
-               var backendErr error
-               upstream := adc.NewDefaultUpstream()
                // try to get the apisixupstream with the same name as the 
backend service to be upstream config.
                // err is ignored because it does not care about the 
externalNodes of the apisixupstream.
-               auNN := types.NamespacedName{Namespace: ar.GetNamespace(), 
Name: backend.ServiceName}
-               if au, ok := tctx.Upstreams[auNN]; ok {
-                       upstream, _ = t.translateApisixUpstream(tctx, au)
-               }
-
-               if backend.ResolveGranularity == 
apiv2.ResolveGranularityService {
-                       upstream.Nodes, backendErr = 
t.translateApisixRouteBackendResolveGranularityService(tctx, 
utils.NamespacedName(ar), backend)
-                       if backendErr != nil {
-                               t.Log.Error(backendErr, "failed to translate 
ApisixRoute backend with ResolveGranularity Service")
-                               continue
-                       }
-               } else {
-                       upstream.Nodes, backendErr = 
t.translateApisixRouteBackendResolveGranularityEndpoint(tctx, 
utils.NamespacedName(ar), backend)
-                       if backendErr != nil {
-                               t.Log.Error(backendErr, "failed to translate 
ApisixRoute backend with ResolveGranularity Endpoint")
-                               continue
-                       }
-               }
-               if backend.Weight != nil {
-                       upstream.Labels["meta_weight"] = 
strconv.FormatInt(int64(*backend.Weight), 10)
+               upstream, err := t.translateApisixRouteHTTPBackend(tctx, ar, 
backend)
+               if err != nil {
+                       t.Log.Error(err, "failed to translate ApisixRoute 
backend", "backend", backend)
+                       continue
                }
 
                upstreamName := adc.ComposeUpstreamName(ar.Namespace, 
backend.ServiceName, backend.Subset, backend.ServicePort, 
backend.ResolveGranularity)
@@ -350,6 +332,46 @@ func getPortFromService(svc *v1.Service, backendSvcPort 
intstr.IntOrString) (int
        return port, nil
 }
 
+func (t *Translator) translateApisixRouteHTTPBackend(tctx 
*provider.TranslateContext, ar *apiv2.ApisixRoute, backend 
apiv2.ApisixRouteHTTPBackend) (*adc.Upstream, error) {
+       auNN := types.NamespacedName{
+               Namespace: ar.Namespace,
+               Name:      backend.ServiceName,
+       }
+       upstream := adc.NewDefaultUpstream()
+       if au, ok := tctx.Upstreams[auNN]; ok {
+               svc := tctx.Services[auNN]
+               if svc == nil {
+                       return nil, errors.Errorf("service not found, 
ApisixRoute: %s, Service: %s", utils.NamespacedName(ar).String(), auNN)
+               }
+               port, err := getPortFromService(svc, backend.ServicePort)
+               if err != nil {
+                       return nil, err
+               }
+               u, err := t.translateApisixUpstreamForPort(tctx, au, 
ptr.To(port))
+               if err != nil {
+                       return nil, err
+               }
+               upstream = u
+       }
+       var (
+               err   error
+               nodes adc.UpstreamNodes
+       )
+       if backend.ResolveGranularity == apiv2.ResolveGranularityService {
+               nodes, err = 
t.translateApisixRouteBackendResolveGranularityService(tctx, auNN, backend)
+       } else {
+               nodes, err = 
t.translateApisixRouteBackendResolveGranularityEndpoint(tctx, auNN, backend)
+       }
+       if err != nil {
+               return nil, err
+       }
+       upstream.Nodes = nodes
+       if backend.Weight != nil {
+               upstream.Labels["meta_weight"] = 
strconv.FormatInt(int64(*backend.Weight), 10)
+       }
+       return upstream, nil
+}
+
 func (t *Translator) translateApisixRouteBackendResolveGranularityService(tctx 
*provider.TranslateContext, arNN types.NamespacedName, backend 
apiv2.ApisixRouteHTTPBackend) (adc.UpstreamNodes, error) {
        serviceNN := types.NamespacedName{
                Namespace: arNN.Namespace,
@@ -433,19 +455,39 @@ func (t *Translator) translateStreamRule(tctx 
*provider.TranslateContext, ar *ap
        svc.ID = id.GenID(svc.Name)
        svc.StreamRoutes = append(svc.StreamRoutes, sr)
 
-       auNN := types.NamespacedName{Namespace: ar.GetNamespace(), Name: 
part.Backend.ServiceName}
-       upstream := adc.NewDefaultUpstream()
-       if au, ok := tctx.Upstreams[auNN]; ok {
-               upstream, _ = t.translateApisixUpstream(tctx, au)
-       }
-       nodes, err := 
t.translateApisixRouteStreamBackendResolveGranularity(tctx, 
utils.NamespacedName(ar), part.Backend)
+       upstream, err := t.translateApisixRouteStreamBackend(tctx, ar, 
part.Backend)
        if err != nil {
                return nil, err
        }
-       upstream.Nodes = nodes
        upstream.ID = ""
        upstream.Name = ""
 
        svc.Upstream = upstream
        return svc, nil
 }
+
+func (t *Translator) translateApisixRouteStreamBackend(tctx 
*provider.TranslateContext, ar *apiv2.ApisixRoute, backend 
apiv2.ApisixRouteStreamBackend) (*adc.Upstream, error) {
+       auNN := types.NamespacedName{Namespace: ar.GetNamespace(), Name: 
backend.ServiceName}
+       upstream := adc.NewDefaultUpstream()
+       if au, ok := tctx.Upstreams[auNN]; ok {
+               service := tctx.Services[auNN]
+               if service == nil {
+                       return nil, errors.Errorf("service not found, 
ApisixRoute: %s, Service: %s", utils.NamespacedName(ar), auNN)
+               }
+               port, err := getPortFromService(service, backend.ServicePort)
+               if err != nil {
+                       return nil, err
+               }
+               u, err := t.translateApisixUpstreamForPort(tctx, au, 
ptr.To(port))
+               if err != nil {
+                       return nil, err
+               }
+               upstream = u
+       }
+       nodes, err := 
t.translateApisixRouteStreamBackendResolveGranularity(tctx, 
utils.NamespacedName(ar), backend)
+       if err != nil {
+               return nil, err
+       }
+       upstream.Nodes = nodes
+       return upstream, nil
+}
diff --git a/internal/adc/translator/apisixupstream.go 
b/internal/adc/translator/apisixupstream.go
index b56791dc..7aec7bad 100644
--- a/internal/adc/translator/apisixupstream.go
+++ b/internal/adc/translator/apisixupstream.go
@@ -20,6 +20,7 @@ package translator
 import (
        "cmp"
        "fmt"
+       "maps"
 
        "github.com/api7/gopkg/pkg/log"
        "github.com/pkg/errors"
@@ -33,10 +34,49 @@ import (
        "github.com/apache/apisix-ingress-controller/internal/utils"
 )
 
-func (t *Translator) translateApisixUpstream(tctx *provider.TranslateContext, 
au *apiv2.ApisixUpstream) (ups *adc.Upstream, err error) {
-       ups = adc.NewDefaultUpstream()
-       for _, f := range []func(*apiv2.ApisixUpstream, *adc.Upstream) error{
-               patchApisixUpstreamBasics,
+func (t *Translator) translateApisixUpstream(tctx *provider.TranslateContext, 
au *apiv2.ApisixUpstream) (*adc.Upstream, error) {
+       return t.translateApisixUpstreamForPort(tctx, au, nil)
+}
+
+func (t *Translator) translateApisixUpstreamForPort(tctx 
*provider.TranslateContext, au *apiv2.ApisixUpstream, port *int32) 
(*adc.Upstream, error) {
+       log.Debugw("translating ApisixUpstream", zap.Any("apisixupstream", au), 
zap.Int32p("port", port))
+
+       ups := adc.NewDefaultUpstream()
+       ups.Name = composeExternalUpstreamName(au)
+       maps.Copy(ups.Labels, au.Labels)
+
+       // translateApisixUpstreamConfig translates the core upstream 
configuration fields
+       // from au.Spec.ApisixUpstreamConfig into the ADC upstream.
+       //
+       // Note: ExternalNodes is not part of ApisixUpstreamConfig but a 
separate field
+       // on ApisixUpstreamSpec, so it is handled separately in 
translateApisixUpstreamExternalNodes.
+       if err := translateApisixUpstreamConfig(tctx, 
&au.Spec.ApisixUpstreamConfig, ups); err != nil {
+               return nil, err
+       }
+       if err := translateApisixUpstreamExternalNodes(tctx, au, ups); err != 
nil {
+               return nil, err
+       }
+
+       // If PortLevelSettings is configured and a specific port is provided,
+       // apply the ApisixUpstreamConfig for the matching port to the upstream.
+       if len(au.Spec.PortLevelSettings) > 0 && port != nil {
+               for _, pls := range au.Spec.PortLevelSettings {
+                       if pls.Port != *port {
+                               continue
+                       }
+                       if err := translateApisixUpstreamConfig(tctx, 
&pls.ApisixUpstreamConfig, ups); err != nil {
+                               return nil, err
+                       }
+               }
+       }
+
+       log.Debugw("translated ApisixUpstream", zap.Any("upstream", ups))
+
+       return ups, nil
+}
+
+func translateApisixUpstreamConfig(tctx *provider.TranslateContext, config 
*apiv2.ApisixUpstreamConfig, ups *adc.Upstream) (err error) {
+       for _, f := range []func(*apiv2.ApisixUpstreamConfig, *adc.Upstream) 
error{
                translateApisixUpstreamScheme,
                translateApisixUpstreamLoadBalancer,
                translateApisixUpstreamRetriesAndTimeout,
@@ -44,39 +84,28 @@ func (t *Translator) translateApisixUpstream(tctx 
*provider.TranslateContext, au
                translateUpstreamHealthCheck,
                translateUpstreamDiscovery,
        } {
-               if err = f(au, ups); err != nil {
+               if err = f(config, ups); err != nil {
                        return
                }
        }
-       for _, f := range []func(*provider.TranslateContext, 
*apiv2.ApisixUpstream, *adc.Upstream) error{
+       for _, f := range []func(*provider.TranslateContext, 
*apiv2.ApisixUpstreamConfig, *adc.Upstream) error{
                translateApisixUpstreamClientTLS,
-               translateApisixUpstreamExternalNodes,
        } {
-               if err = f(tctx, au, ups); err != nil {
+               if err = f(tctx, config, ups); err != nil {
                        return
                }
        }
 
-       log.Debugw("translated ApisixUpstream", zap.Any("upstream", ups),
-               zap.String("namespace", au.Namespace), zap.String("name", 
au.Name))
        return
 }
 
-func patchApisixUpstreamBasics(au *apiv2.ApisixUpstream, ups *adc.Upstream) 
error {
-       ups.Name = composeExternalUpstreamName(au)
-       for k, v := range au.Labels {
-               ups.Labels[k] = v
-       }
+func translateApisixUpstreamScheme(config *apiv2.ApisixUpstreamConfig, ups 
*adc.Upstream) error {
+       ups.Scheme = cmp.Or(config.Scheme, apiv2.SchemeHTTP)
        return nil
 }
 
-func translateApisixUpstreamScheme(au *apiv2.ApisixUpstream, ups 
*adc.Upstream) error {
-       ups.Scheme = cmp.Or(au.Spec.Scheme, apiv2.SchemeHTTP)
-       return nil
-}
-
-func translateApisixUpstreamLoadBalancer(au *apiv2.ApisixUpstream, ups 
*adc.Upstream) error {
-       lb := au.Spec.LoadBalancer
+func translateApisixUpstreamLoadBalancer(config *apiv2.ApisixUpstreamConfig, 
ups *adc.Upstream) error {
+       lb := config.LoadBalancer
        if lb == nil || lb.Type == "" {
                ups.Type = apiv2.LbRoundRobin
                return nil
@@ -107,9 +136,9 @@ func translateApisixUpstreamLoadBalancer(au 
*apiv2.ApisixUpstream, ups *adc.Upst
        return nil
 }
 
-func translateApisixUpstreamRetriesAndTimeout(au *apiv2.ApisixUpstream, ups 
*adc.Upstream) error {
-       retries := au.Spec.Retries
-       timeout := au.Spec.Timeout
+func translateApisixUpstreamRetriesAndTimeout(config 
*apiv2.ApisixUpstreamConfig, ups *adc.Upstream) error {
+       retries := config.Retries
+       timeout := config.Timeout
 
        if retries != nil && *retries < 0 {
                return errors.New("invalid value retries")
@@ -144,15 +173,15 @@ func translateApisixUpstreamRetriesAndTimeout(au 
*apiv2.ApisixUpstream, ups *adc
        return nil
 }
 
-func translateApisixUpstreamClientTLS(tctx *provider.TranslateContext, au 
*apiv2.ApisixUpstream, ups *adc.Upstream) error {
-       if au.Spec.TLSSecret == nil {
+func translateApisixUpstreamClientTLS(tctx *provider.TranslateContext, config 
*apiv2.ApisixUpstreamConfig, ups *adc.Upstream) error {
+       if config.TLSSecret == nil {
                return nil
        }
 
        var (
                secretNN = types.NamespacedName{
-                       Namespace: au.Spec.TLSSecret.Namespace,
-                       Name:      au.Spec.TLSSecret.Name,
+                       Namespace: config.TLSSecret.Namespace,
+                       Name:      config.TLSSecret.Name,
                }
        )
        secret, ok := tctx.Secrets[secretNN]
@@ -173,9 +202,9 @@ func translateApisixUpstreamClientTLS(tctx 
*provider.TranslateContext, au *apiv2
        return nil
 }
 
-func translateApisixUpstreamPassHost(au *apiv2.ApisixUpstream, ups 
*adc.Upstream) error {
-       ups.PassHost = au.Spec.PassHost
-       ups.UpstreamHost = au.Spec.UpstreamHost
+func translateApisixUpstreamPassHost(config *apiv2.ApisixUpstreamConfig, ups 
*adc.Upstream) error {
+       ups.PassHost = config.PassHost
+       ups.UpstreamHost = config.UpstreamHost
 
        return nil
 }
@@ -259,11 +288,8 @@ func translateApisixUpstreamExternalNodesService(tctx 
*provider.TranslateContext
        return nil
 }
 
-func translateUpstreamHealthCheck(au *apiv2.ApisixUpstream, ups *adc.Upstream) 
error {
-       if au == nil {
-               return nil
-       }
-       healcheck := au.Spec.HealthCheck
+func translateUpstreamHealthCheck(config *apiv2.ApisixUpstreamConfig, ups 
*adc.Upstream) error {
+       healcheck := config.HealthCheck
        if healcheck == nil || (healcheck.Passive == nil && healcheck.Active == 
nil) {
                return nil
        }
@@ -346,8 +372,8 @@ func translateUpstreamPassiveHealthCheck(config 
*apiv2.PassiveHealthCheck) *adc.
        return &passive
 }
 
-func translateUpstreamDiscovery(au *apiv2.ApisixUpstream, ups *adc.Upstream) 
error {
-       discovery := au.Spec.Discovery
+func translateUpstreamDiscovery(config *apiv2.ApisixUpstreamConfig, ups 
*adc.Upstream) error {
+       discovery := config.Discovery
        if discovery == nil {
                return nil
        }
diff --git a/test/e2e/crds/v2/upstream.go b/test/e2e/crds/v2/upstream.go
index 674abf51..ffa6d550 100644
--- a/test/e2e/crds/v2/upstream.go
+++ b/test/e2e/crds/v2/upstream.go
@@ -25,6 +25,7 @@ import (
        . "github.com/onsi/ginkgo/v2"
        . "github.com/onsi/gomega"
        "k8s.io/apimachinery/pkg/types"
+       "k8s.io/utils/ptr"
 
        apiv2 "github.com/apache/apisix-ingress-controller/api/v2"
        "github.com/apache/apisix-ingress-controller/test/e2e/framework"
@@ -182,4 +183,127 @@ spec:
                        })
                })
        })
+
+       Context("portLevelSettings", func() {
+               var (
+                       auNginx = `
+apiVersion: apisix.apache.org/v2
+kind: ApisixUpstream
+metadata:
+  name: nginx
+spec:
+  ingressClassName: %s
+  portLevelSettings:
+    - port: 80
+      passHost: rewrite
+      upstreamHost: upstream.nginx.com
+`
+                       auNginx2 = `
+apiVersion: apisix.apache.org/v2
+kind: ApisixUpstream
+metadata:
+  name: nginx2
+spec:
+  ingressClassName: %s
+  portLevelSettings:
+    - port: 443
+      scheme: https
+    - port: 80
+      passHost: rewrite
+      upstreamHost: upstream.nginx.com
+`
+                       ar = `
+apiVersion: apisix.apache.org/v2
+kind: ApisixRoute
+metadata:
+  name: nginx-route
+spec:
+  ingressClassName: %s
+  http:
+  - name: rule0
+    match:
+      hosts:
+      - www.server.example.com
+      paths:
+      - /http-and-https
+    backends:
+    - serviceName: nginx
+      servicePort: 80
+      weight: 50
+    - serviceName: nginx2
+      servicePort: 443
+      weight: 50
+  - name: rule1
+    match:
+      hosts:
+      - www.server.example.com
+      paths:
+      - /http
+    backends:
+    - serviceName: nginx
+      servicePort: 80
+      weight: 50
+    - serviceName: nginx2
+      servicePort: 80
+      weight: 50
+`
+               )
+
+               BeforeEach(func() {
+                       s.DeployNginx(framework.NginxOptions{
+                               Namespace: s.Namespace(),
+                               Replicas:  ptr.To(int32(1)),
+                       })
+               })
+               It("should handle port level settings correctly", func() {
+                       applier.MustApplyAPIv2(types.NamespacedName{Namespace: 
s.Namespace(), Name: "nginx"},
+                               &apiv2.ApisixUpstream{}, fmt.Sprintf(auNginx, 
s.Namespace()))
+                       applier.MustApplyAPIv2(types.NamespacedName{Namespace: 
s.Namespace(), Name: "nginx2"},
+                               &apiv2.ApisixUpstream{}, fmt.Sprintf(auNginx2, 
s.Namespace()))
+                       applier.MustApplyAPIv2(types.NamespacedName{Namespace: 
s.Namespace(), Name: "nginx-route"},
+                               &apiv2.ApisixRoute{}, fmt.Sprintf(ar, 
s.Namespace()))
+
+                       s.RequestAssert(&scaffold.RequestAssert{
+                               Method: "GET",
+                               Path:   "/http",
+                               Host:   "www.server.example.com",
+                               Check:  scaffold.WithExpectedStatus(200),
+                       })
+
+                       s.RequestAssert(&scaffold.RequestAssert{
+                               Method: "GET",
+                               Path:   "/http-and-https",
+                               Host:   "www.server.example.com",
+                               Check:  scaffold.WithExpectedStatus(200),
+                       })
+
+                       By("testing http only")
+                       for range 10 {
+                               exp := 
s.NewAPISIXClient().GET("/http").WithHost("www.server.example.com").Expect()
+                               exp.Status(200)
+                               port := exp.Header("X-Port").Raw()
+                               host := exp.Header("X-Host").Raw()
+                               
Expect(host).Should(Equal("upstream.nginx.com"), "the host should be 
upstream.nginx.com")
+                               Expect(port).To(Equal("80"), "the port should 
be 80")
+                       }
+
+                       By("testing both http and https")
+                       port80 := false
+                       port443 := false
+                       for range 10 {
+                               exp := 
s.NewAPISIXClient().GET("/http-and-https").WithHost("www.server.example.com").Expect()
+                               exp.Status(200)
+                               port := exp.Header("X-Port").Raw()
+                               Expect(port == "80" || port == 
"443").To(BeTrue(), "the port should be 80 or 443")
+                               switch port {
+                               case "80":
+                                       port80 = true
+                               case "443":
+                                       port443 = true
+                               }
+                       }
+                       Expect(port80).To(BeTrue(), "should hit port 80")
+                       Expect(port443).To(BeTrue(), "should hit port 443")
+               })
+       })
 })
diff --git a/test/e2e/framework/apisix_consts.go 
b/test/e2e/framework/apisix_consts.go
index 1fe82d4e..11451a00 100644
--- a/test/e2e/framework/apisix_consts.go
+++ b/test/e2e/framework/apisix_consts.go
@@ -104,6 +104,111 @@ hhdq+X/vrgK+uicSx8Q1zL2iCLdfsZ0fPryMdTZrN3ytEBEWMPeX
 -----END RSA PRIVATE KEY-----`
 )
 
+const (
+       TestCACert = `-----BEGIN CERTIFICATE-----
+MIIDdzCCAl+gAwIBAgIUBB5PHXyymeboPDVdYeYihYnm5XIwDQYJKoZIhvcNAQEL
+BQAwXTELMAkGA1UEBhMCQ04xDjAMBgNVBAgMBVN0YXRlMQ0wCwYDVQQHDARDaXR5
+MRUwEwYDVQQKDAxPcmdhbml6YXRpb24xGDAWBgNVBAMMD3d3dy5leGFtcGxlLmNv
+bTAeFw0yNDA2MjYxNTQ5NTBaFw0zNDA2MjQxNTQ5NTBaMF0xCzAJBgNVBAYTAkNO
+MQ4wDAYDVQQIDAVTdGF0ZTENMAsGA1UEBwwEQ2l0eTEVMBMGA1UECgwMT3JnYW5p
+emF0aW9uMRgwFgYDVQQDDA93d3cuZXhhbXBsZS5jb20wggEiMA0GCSqGSIb3DQEB
+AQUAA4IBDwAwggEKAoIBAQCutPQZr71VAdVvLd65agMsj9xD46jdZxWzP1unfv34
+6VFhCFPJp39TuBkwcXmSEmSzCcQXyCvWhRV+PErr+N4dadUpoci2E/CcKAlOisxz
+OaPz52yS+h5kf8wpVS3Tk9JHONZp6P3HLY2yeUtRl/Bw3Fyo7nIJCtwx5jJ2cFgE
+dvqfruiZYTSU5vxvS2QDXvX0gcOuR4uP+RRBcwlY+oD+G0pP4vsUcLpCQpD58p1c
+RSLwoX6dPb3eohCXufCnskIwiIxC3jQcVrI1gOggJkZuGXNRh0aS5O7L0iu37l57
+UStFB4kjbnCWterNz/NRRhD2Ad8RTALnJMeGKb7uH9OzAgMBAAGjLzAtMAwGA1Ud
+EwQFMAMBAf8wHQYDVR0OBBYEFH78Ns0zkjTuK1EhiLMNYnXUZatxMA0GCSqGSIb3
+DQEBCwUAA4IBAQCVhAzUb32Qyjn5oZHsDYKaQIHfXe+/W2oM41dDTSxjFlbvBjaq
+JWxgAYBA5l28b+e9zUK2BTcSNzVbrfm5/qoykAQNaR4Vvhy3LxFyOd6G87as3+hv
+jlerjSa/gh8XCPFzs2t6wyhZqEgcNZBK6oagnaxKstoS2jXjAL+7dx4PRBdw7MTq
+joQ+TzLgsB9kFMnihmR+LpDFfQCqAfp5X0z9RLgnH0zVcBrRXKKb8AaOWBdkdK6g
+BLIW7+4ZxW5BzYmi6ZuDDjP96wLpWT7boJPi3BqnCEQIzywNMBbqZO9LiWdGf0TH
+EpkzMRsCTGGar43HkQgDZdjicRKiuWYFO47O
+-----END CERTIFICATE-----`
+
+       TestCAKey = `-----BEGIN PRIVATE KEY-----
+MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCutPQZr71VAdVv
+Ld65agMsj9xD46jdZxWzP1unfv346VFhCFPJp39TuBkwcXmSEmSzCcQXyCvWhRV+
+PErr+N4dadUpoci2E/CcKAlOisxzOaPz52yS+h5kf8wpVS3Tk9JHONZp6P3HLY2y
+eUtRl/Bw3Fyo7nIJCtwx5jJ2cFgEdvqfruiZYTSU5vxvS2QDXvX0gcOuR4uP+RRB
+cwlY+oD+G0pP4vsUcLpCQpD58p1cRSLwoX6dPb3eohCXufCnskIwiIxC3jQcVrI1
+gOggJkZuGXNRh0aS5O7L0iu37l57UStFB4kjbnCWterNz/NRRhD2Ad8RTALnJMeG
+Kb7uH9OzAgMBAAECggEAQb6h73qlZrSCc8zQuHivChl3G+sz1GGjFmm83YraG03+
+DGRcV5IbRc+NVyAUzkXytDd0Hjj7WkaJwJAC58snFu2JRJn31KErVjBw1ChCaQgj
+bTlFMAhE4LABDfrafHjv1FKMyZ1exxIa9TNVBzcEygv7KK1Wp5V5KKQGkHCVhtP5
+PDqKwyiUqFpsM7Codr8TmavHykSfVRhxPifDNXMDMXsSUT/2dFj0QXljA3tjzk6d
+UHzx4z44cODbjWE74ZeQw2SFslKHgK0ZVYivE6+f3L/p7fSqq7hJ6T0BXpKolbHZ
+yU7Xh6BBy0WKqACkUWALZ6tglcb+KoTqhZ/fTZW6mQKBgQDr6ivPmQI4deIbWGyi
+EGfzrLfiVEuqCsz5gX88nvBRoGevKi7kCbIf/IoNGIm5SJ8gJDs6eSPKFwAEd29H
+N2muFXmune8g7lVZjQ8GPGUu4IvJMS7OAcbLmpI4pUaVGtJsh2fmXnaTSR5D5y64
+XtPGdkluLr/B5vz/X0D0NG80GQKBgQC9lL04p1D6kJj+JwyNm7bZ1OhgpH5Pup82
+Ia+5GH8m7VLS4/PrpllOmhgccaCJK7M36EXLELHRHpmOoLzUiemgtnoQ0JMmeWfN
+pVigWdIsCMSS1wJLNklr17eDDgvdcs0W3cujja4/2LlksNY8/zh7LJ/k/YUTnlkG
+VsSeLfEfqwKBgQCNinmunAaRCWkXLv4+XcmAkWfiCuE6rDA+oktMe6+DydFrbsuj
+VY3hUwsgwFAhMkkGZ7aBZpzqatI/28iP2dc18vyGn4sRHu1mRRN2klXCwkYb9741
+KyuyjJKeGcs3Olh1dOgJdzN9OqlF5DZLt9kngWCdEr9J/uRb8zJtUehGQQKBgEuD
+yM+dThNQr7Bk64oooXApb5q3Sx0FEFAmoPFQwa1G0Tvx0wJl06MMnFgQJssc3hmB
+6vMVJk9PKgl3G2BpwubiaMLz4flsWJ3ApAnTXXVu1KZNALvm1t4fIhkQ6kb+aJUY
+KfpvAB6sfESQb/YCD4R45QP4vB5xb7Kns0/yqt5bAoGAQ0kPRvAbdmBURLscsOpN
+f4sdXsJLqxAppIqrYolUVqIQPAFYT7oWLpSMmIqzjFzRFBj3ZUf3fZCNCi2kkRvs
+60VyICYZobrnbcbkCx7qydIuhr2+8301lcginh8DIN58c0IZmoIRv/Z44SPfl1ku
+zDGD28KCK1oiIZOe9sgcgf4=
+-----END PRIVATE KEY-----`
+
+       TESTCert1 = `-----BEGIN CERTIFICATE-----
+MIIDxDCCAqygAwIBAgIUO1cgq5OMm6h5eb5U+r3DftaM7e0wDQYJKoZIhvcNAQEL
+BQAwXTELMAkGA1UEBhMCQ04xDjAMBgNVBAgMBVN0YXRlMQ0wCwYDVQQHDARDaXR5
+MRUwEwYDVQQKDAxPcmdhbml6YXRpb24xGDAWBgNVBAMMD3d3dy5leGFtcGxlLmNv
+bTAeFw0yNDA2MjYxNzA4MzFaFw0yODExMTIxNzA4MzFaMGAxCzAJBgNVBAYTAkNO
+MQ4wDAYDVQQIDAVTdGF0ZTENMAsGA1UEBwwEQ2l0eTEVMBMGA1UECgwMT3JnYW5p
+emF0aW9uMRswGQYDVQQDDBJzZXJ2ZXIuZXhhbXBsZS5jb20wggEiMA0GCSqGSIb3
+DQEBAQUAA4IBDwAwggEKAoIBAQC+nRQiu87exjwtnTb1+dBFdMffucW84qPZ3TZY
+e3beLJkIjM2eeptzEVf5eFAxo9lXpi9DKJQopI9aRc3SvyWRg9bj8wm88UdA/2LO
+8q1oRaDzKHlMfSVqjdZ8qTalbd4FzhUWw9TWKGQxhA5yypisuQjOrVDC6bvk3WMk
+BoD+2zil+q05nRjUSCbGkKO0HecXt2M2jq1nyN68J5ymtmooUbSM7TAC+ymBggJc
+JsgJ3DrquMNqQykjsSotVaz+y857DedzJoMoMgIvJy1cNexpY6e/PK2oO9OB+OTg
+Rq/XtC/wQ5ZJ0/rY1q1yU8t5JmlVkrV6RPUT5SuoVeA8Fdu7AgMBAAGjeTB3MDUG
+A1UdEQQuMCyCEnNlcnZlci5leGFtcGxlLmNvbYIWd3d3LnNlcnZlci5leGFtcGxl
+LmNvbTAdBgNVHQ4EFgQUZ3hBoewum8wvVnSPLiaAI1S22cwwHwYDVR0jBBgwFoAU
+fvw2zTOSNO4rUSGIsw1iddRlq3EwDQYJKoZIhvcNAQELBQADggEBAC2LQ/nLc1PP
+ioPeqxKwF094yrifdhZGCmCSFpsnPbxhgxTRKSSMe7+XPadS4xd4VeRkbmuyDuUg
+kYCAr3eTpSKfc3cTHP4S/+DDPefUn8u5lbPEE1Aq2JMNubXwCUMy+hNgX7dHWzBW
+sqR+GErLzGGsfkTWhIxwH8Vx/hhKS/Kv5EEvZ42HrvL3570/04zq1tUYPlqPoQBc
+t+6M2fJQx6lYdVjtYssm/6MnjNIM59NmmmwwrLZZyB96kDAW8xFndzcJQv4uojdb
+UjWkMt/J7i6TWZY9DrSmAwCo2ZDCUZT5vQUkmILc9st/ie8v3755lJxoAOIyxEmi
+Z+TO4JGixxQ=
+-----END CERTIFICATE-----`
+       TestKey1 = `-----BEGIN PRIVATE KEY-----
+MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQC+nRQiu87exjwt
+nTb1+dBFdMffucW84qPZ3TZYe3beLJkIjM2eeptzEVf5eFAxo9lXpi9DKJQopI9a
+Rc3SvyWRg9bj8wm88UdA/2LO8q1oRaDzKHlMfSVqjdZ8qTalbd4FzhUWw9TWKGQx
+hA5yypisuQjOrVDC6bvk3WMkBoD+2zil+q05nRjUSCbGkKO0HecXt2M2jq1nyN68
+J5ymtmooUbSM7TAC+ymBggJcJsgJ3DrquMNqQykjsSotVaz+y857DedzJoMoMgIv
+Jy1cNexpY6e/PK2oO9OB+OTgRq/XtC/wQ5ZJ0/rY1q1yU8t5JmlVkrV6RPUT5Suo
+VeA8Fdu7AgMBAAECggEASdlzxq06zebBw+5oL86UmYRQN+ayrKamUq848fkLqbJf
+rAdZVrAr793lVrr9Xu4bM7EoGH3tQP3YqGHpB2CVPpZ0uCYePLzCHXWUo5c0BfUM
+EYk5zZ+i0nCXi/7HNDqnzVn1o7dFi59kiiaermy90BV0Sxas9oc2C8qWMYvviE1d
+GS0/Prmma+H0gZDTiQTrQsR++HmSQ3jvu9LYvVx4AkNveYNbPgF1o52PgEryTH2Y
+4tvHuyp7KpjSSNKoRuXw25OpVLsPXaxDFnSoe8YK6ltIA4kG6f7G7JbufKdaiCbY
+c4d6co5xTJa77NMGzY3j55gTFU3mfhMC9QvrvcVycQKBgQDv7LvV2LNf6ixlBWXO
+t3TAEOMM2x6awmDDPDU8i1uIhHHA7ZqsHREIC33Sily+1NwOm/iuLWTlHv0+4cww
+y75Xvl8e2mDPtkoT8wNeHdKXTp4CLzrIvEHzG0qf063ob0hP24ymqy+PgO/WW8Pl
+K/iWTpTzc9UKUuhiNPzp2XIMYwKBgQDLYotPuKkQHwGrhI8mZKKTcefzN9Ton2Kw
+7qJ3qDPrbDS0yjbU5+TKaNjKlZ+fsPQkHb5aWkGK+hYU6x7X8V/MZrc5MSwtNjPI
+QfhTPKsxNSAPqRoTwP+QVXECQhCdjRZCpE6+4aaB/31VbIjTWdhxUyc6zrPLDimX
+p0hhEcH2yQKBgAnBna3HfxvSYPXGr2oliajZxvHZ4ze12ct2ok+Q9yro/9sxjk2b
+bPrfxMEQAU99RmmNrCIhFG5AwVmSQwRk9JuK0UFm7fLkXcTL6AImwk6G0uQR2Zka
+FrB1FqbDK9o81DrzGZgZc/io7JfR6XhjPluWXHY96pbd4jdEIli8D+gzAoGAAn0o
+O0eFOh9HA/RRVCTzIF7Ked17C4W3zXZ+Iny6de0TEAtRdHWKBTgXPxNpqqidtDtw
+8uYb2zmIP6VI8VeQ1o2DPH3vjnYVWCQGh+48IhQGWmq1WPyJpBiHk4F/do4dcZ9V
+H1zfjsOzovH7EqsMzQY5eqzA4oE/3Q09A4MWHpECgYAU3uxxs5g7QwdK01BPXznk
+H1y7bdn2LYS5otllOgAfZRX6BUQNFE8RtwA4HNe764SSEkk7EtTckx0d7ar8V1V2
+pfxfd8A0pNI54NtnFk8OS0BVkW5SGoBCWRh2nlV1r0B/7np0X61GgE95JDMslRn0
+0AOPIR/qFJ1YYT0a7yKyjQ==
+-----END PRIVATE KEY-----`
+)
+
 func init() {
        tpl, err := 
template.New("apisix-standalone").Funcs(sprig.TxtFuncMap()).Parse(apisixStandaloneTemplate)
        if err != nil {
diff --git a/test/e2e/framework/manifests/nginx.yaml 
b/test/e2e/framework/manifests/nginx.yaml
index 7fb93f08..053e383f 100644
--- a/test/e2e/framework/manifests/nginx.yaml
+++ b/test/e2e/framework/manifests/nginx.yaml
@@ -31,7 +31,14 @@ data:
     http {
       server {
         listen 80 default_server;
+        listen 443 ssl default_server;
 
+        ssl_certificate /etc/nginx/ssl/tls.crt;
+        ssl_certificate_key /etc/nginx/ssl/tls.key;
+        ssl_client_certificate /etc/nginx/ssl/ca.crt;
+
+        add_header X-Port $server_port;
+        add_header X-Host $host;
         location / {
           return 200 'Hello, World!';
         }
@@ -57,6 +64,9 @@ spec:
         - name: nginx-config
           configMap:
             name: nginx-config
+        - name: nginx-ssl
+          secret:
+            secretName: nginx-ssl
       containers:
         - livenessProbe:
             failureThreshold: 3
@@ -74,10 +84,15 @@ spec:
             - containerPort: 80
               name: "http"
               protocol: "TCP"
+            - containerPort: 443
+              name: "https"
+              protocol: "TCP"
           volumeMounts:
             - mountPath: /etc/nginx/nginx.conf
               name: nginx-config
               subPath: nginx.conf
+            - mountPath: /etc/nginx/ssl
+              name: nginx-ssl
 ---
 apiVersion: v1
 kind: Service
@@ -91,4 +106,26 @@ spec:
       port: 80
       protocol: TCP
       targetPort: 80
+    - name: https
+      port: 443
+      protocol: TCP
+      targetPort: 443
+  type: ClusterIP
+---
+apiVersion: v1
+kind: Service
+metadata:
+  name: nginx2
+spec:
+  selector:
+    app: nginx
+  ports:
+    - name: http
+      port: 80
+      protocol: TCP
+      targetPort: 80
+    - name: https
+      port: 443
+      protocol: TCP
+      targetPort: 443
   type: ClusterIP
diff --git a/test/e2e/framework/nginx.go b/test/e2e/framework/nginx.go
index 9612c4e3..0cfecebe 100644
--- a/test/e2e/framework/nginx.go
+++ b/test/e2e/framework/nginx.go
@@ -54,6 +54,8 @@ func (f *Framework) DeployNginx(opts NginxOptions) 
*corev1.Service {
        err := ngxSpecTpl.Execute(buf, opts)
        f.GomegaT.Expect(err).ToNot(HaveOccurred(), "rendering nginx spec")
 
+       f.applySSLSecret(opts.Namespace, "nginx-ssl", []byte(TESTCert1), 
[]byte(TestKey1), []byte(TestCACert))
+
        kubectlOpts := k8s.NewKubectlOptions("", "", opts.Namespace)
 
        k8s.KubectlApplyFromString(f.GinkgoT, kubectlOpts, buf.String())

Reply via email to