diff --git a/api/v2/apisixroute_types.go b/api/v2/apisixroute_types.go index 14776b6c..04050e39 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 96301566..f0760d3b 100644 --- a/config/crd/bases/apisix.apache.org_apisixroutes.yaml +++ b/config/crd/bases/apisix.apache.org_apisixroutes.yaml @@ -347,6 +347,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. @@ -460,6 +463,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 505ba35f..1cdcb520 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 backendIndex, 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, ar.Name, fmt.Sprintf("%d", ruleIndex), fmt.Sprintf("%d", backendIndex)) @@ -352,6 +334,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, @@ -435,19 +457,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 62392344..a86c4a8d 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" @@ -185,4 +186,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 65207e93..9277a222 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())