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())