From 3f153add004e5265dd96ee920408a05499d352af Mon Sep 17 00:00:00 2001 From: Manuel Alejandro de Brito Fontes Date: Sun, 8 Nov 2020 11:30:43 -0300 Subject: [PATCH] Refactor handling of path Prefix and Exact --- go.mod | 1 + go.sum | 4 + internal/ingress/controller/controller.go | 14 ++ .../ingress/controller/controller_test.go | 54 +++---- internal/ingress/controller/location.go | 112 +++++++++++++ .../ingress/controller/template/template.go | 12 +- rootfs/etc/nginx/lua/lua_ingress.lua | 25 --- test/e2e/annotations/rewrite.go | 8 +- test/e2e/ingress/pathtype_exact.go | 8 +- test/e2e/ingress/pathtype_mixed.go | 149 ++++++++++++++++++ 10 files changed, 316 insertions(+), 71 deletions(-) create mode 100644 internal/ingress/controller/location.go create mode 100644 test/e2e/ingress/pathtype_mixed.go diff --git a/go.mod b/go.mod index a08f1c1a0..6f2e0a05b 100644 --- a/go.mod +++ b/go.mod @@ -11,6 +11,7 @@ require ( github.com/imdario/mergo v0.3.10 github.com/json-iterator/go v1.1.10 github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348 + github.com/mitchellh/copystructure v1.0.0 github.com/mitchellh/go-ps v1.0.0 github.com/mitchellh/hashstructure v1.0.0 github.com/mitchellh/mapstructure v1.3.2 diff --git a/go.sum b/go.sum index d588619d5..3f425491b 100644 --- a/go.sum +++ b/go.sum @@ -421,6 +421,8 @@ github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182aff github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= +github.com/mitchellh/copystructure v1.0.0 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMKeZ+mmkFQ= +github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-ps v1.0.0 h1:i6ampVEEF4wQFF+bkYfwYgY+F/uYJDktmvLPf7qIgjc= @@ -435,6 +437,8 @@ github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQz github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.3.2 h1:mRS76wmkOn3KkKAyXDu42V+6ebnXWIztFSYGN7GeoRg= github.com/mitchellh/mapstructure v1.3.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/reflectwalk v1.0.0 h1:9D+8oIskB4VJBN5SFlmc27fSlIBZaov1Wpk/IfikLNY= +github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/mmarkdown/mmark v2.0.40+incompatible h1:vMeUeDzBK3H+/mU0oMVfMuhSXJlIA+DE/DMPQNAj5C4= github.com/mmarkdown/mmark v2.0.40+incompatible/go.mod h1:Uvmoz7tvsWpr7bMVxIpqZPyN3FbOtzDmnsJDFp7ltJs= github.com/moby/sys/mountinfo v0.1.3/go.mod h1:w2t2Avltqx8vE7gX5l+QiBKxODu2TX0+Syr3h52Tw4o= diff --git a/internal/ingress/controller/controller.go b/internal/ingress/controller/controller.go index 7f10477a2..03ff9d04a 100644 --- a/internal/ingress/controller/controller.go +++ b/internal/ingress/controller/controller.go @@ -430,6 +430,20 @@ func (n *NGINXController) getConfiguration(ingresses []*ingress.Ingress) (sets.S hosts := sets.NewString() for _, server := range servers { + // If a location is defined by a prefix string that ends with the slash character, and requests are processed by one of + // proxy_pass, fastcgi_pass, uwsgi_pass, scgi_pass, memcached_pass, or grpc_pass, then the special processing is performed. + // In response to a request with URI equal to // this string, but without the trailing slash, a permanent redirect with the + // code 301 will be returned to the requested URI with the slash appended. If this is not desired, an exact match of the + // URIand location could be defined like this: + // + // location /user/ { + // proxy_pass http://user.example.com; + // } + // location = /user { + // proxy_pass http://login.example.com; + // } + server.Locations = updateServerLocations(server.Locations) + if !hosts.Has(server.Hostname) { hosts.Insert(server.Hostname) } diff --git a/internal/ingress/controller/controller_test.go b/internal/ingress/controller/controller_test.go index 847096ae6..d1f600345 100644 --- a/internal/ingress/controller/controller_test.go +++ b/internal/ingress/controller/controller_test.go @@ -270,8 +270,6 @@ func TestCheckIngress(t *testing.T) { }) } -var pathPrefix = networking.PathTypePrefix - func TestMergeAlternativeBackends(t *testing.T) { testCases := map[string]struct { ingress *ingress.Ingress @@ -295,7 +293,7 @@ func TestMergeAlternativeBackends(t *testing.T) { Paths: []networking.HTTPIngressPath{ { Path: "/", - PathType: &pathPrefix, + PathType: &pathTypePrefix, Backend: networking.IngressBackend{ ServiceName: "http-svc-canary", ServicePort: intstr.IntOrString{ @@ -331,7 +329,7 @@ func TestMergeAlternativeBackends(t *testing.T) { Locations: []*ingress.Location{ { Path: "/", - PathType: &pathPrefix, + PathType: &pathTypePrefix, Backend: "example-http-svc-80", }, }, @@ -357,7 +355,7 @@ func TestMergeAlternativeBackends(t *testing.T) { Locations: []*ingress.Location{ { Path: "/", - PathType: &pathPrefix, + PathType: &pathTypePrefix, Backend: "example-http-svc-80", }, }, @@ -379,7 +377,7 @@ func TestMergeAlternativeBackends(t *testing.T) { Paths: []networking.HTTPIngressPath{ { Path: "/", - PathType: &pathPrefix, + PathType: &pathTypePrefix, Backend: networking.IngressBackend{ ServiceName: "foo-http-svc-canary", ServicePort: intstr.IntOrString{ @@ -399,7 +397,7 @@ func TestMergeAlternativeBackends(t *testing.T) { Paths: []networking.HTTPIngressPath{ { Path: "/", - PathType: &pathPrefix, + PathType: &pathTypePrefix, Backend: networking.IngressBackend{ ServiceName: "http-svc-canary", ServicePort: intstr.IntOrString{ @@ -447,7 +445,7 @@ func TestMergeAlternativeBackends(t *testing.T) { Locations: []*ingress.Location{ { Path: "/", - PathType: &pathPrefix, + PathType: &pathTypePrefix, Backend: "example-foo-http-svc-80", }, }, @@ -457,7 +455,7 @@ func TestMergeAlternativeBackends(t *testing.T) { Locations: []*ingress.Location{ { Path: "/", - PathType: &pathPrefix, + PathType: &pathTypePrefix, Backend: "example-http-svc-80", }, }, @@ -495,7 +493,7 @@ func TestMergeAlternativeBackends(t *testing.T) { Locations: []*ingress.Location{ { Path: "/", - PathType: &pathPrefix, + PathType: &pathTypePrefix, Backend: "example-http-svc-80", }, }, @@ -517,7 +515,7 @@ func TestMergeAlternativeBackends(t *testing.T) { Paths: []networking.HTTPIngressPath{ { Path: "/", - PathType: &pathPrefix, + PathType: &pathTypePrefix, Backend: networking.IngressBackend{ ServiceName: "http-svc-canary", ServicePort: intstr.IntOrString{ @@ -582,7 +580,7 @@ func TestMergeAlternativeBackends(t *testing.T) { Locations: []*ingress.Location{ { Path: "/", - PathType: &pathPrefix, + PathType: &pathTypePrefix, Backend: "example-http-svc-80", }, }, @@ -608,7 +606,7 @@ func TestMergeAlternativeBackends(t *testing.T) { Locations: []*ingress.Location{ { Path: "/", - PathType: &pathPrefix, + PathType: &pathTypePrefix, Backend: "example-http-svc-80", }, }, @@ -650,7 +648,7 @@ func TestMergeAlternativeBackends(t *testing.T) { Locations: []*ingress.Location{ { Path: "/", - PathType: &pathPrefix, + PathType: &pathTypePrefix, Backend: "upstream-default-backend", }, }, @@ -663,7 +661,7 @@ func TestMergeAlternativeBackends(t *testing.T) { Locations: []*ingress.Location{ { Path: "/", - PathType: &pathPrefix, + PathType: &pathTypePrefix, Backend: "upstream-default-backend", }, }, @@ -999,7 +997,7 @@ func TestGetBackendServers(t *testing.T) { Paths: []networking.HTTPIngressPath{ { Path: "/", - PathType: &pathPrefix, + PathType: &pathTypePrefix, Backend: networking.IngressBackend{ ServiceName: "http-svc-canary", ServicePort: intstr.IntOrString{ @@ -1059,7 +1057,7 @@ func TestGetBackendServers(t *testing.T) { Paths: []networking.HTTPIngressPath{ { Path: "/", - PathType: &pathPrefix, + PathType: &pathTypePrefix, Backend: networking.IngressBackend{ ServiceName: "http-svc", ServicePort: intstr.IntOrString{ @@ -1096,7 +1094,7 @@ func TestGetBackendServers(t *testing.T) { Paths: []networking.HTTPIngressPath{ { Path: "/", - PathType: &pathPrefix, + PathType: &pathTypePrefix, Backend: networking.IngressBackend{ ServiceName: "http-svc-canary", ServicePort: intstr.IntOrString{ @@ -1165,7 +1163,7 @@ func TestGetBackendServers(t *testing.T) { Paths: []networking.HTTPIngressPath{ { Path: "/a", - PathType: &pathPrefix, + PathType: &pathTypePrefix, Backend: networking.IngressBackend{ ServiceName: "http-svc-1", ServicePort: intstr.IntOrString{ @@ -1202,7 +1200,7 @@ func TestGetBackendServers(t *testing.T) { Paths: []networking.HTTPIngressPath{ { Path: "/a", - PathType: &pathPrefix, + PathType: &pathTypePrefix, Backend: networking.IngressBackend{ ServiceName: "http-svc-2", ServicePort: intstr.IntOrString{ @@ -1239,7 +1237,7 @@ func TestGetBackendServers(t *testing.T) { Paths: []networking.HTTPIngressPath{ { Path: "/b", - PathType: &pathPrefix, + PathType: &pathTypePrefix, Backend: networking.IngressBackend{ ServiceName: "http-svc-2", ServicePort: intstr.IntOrString{ @@ -1276,7 +1274,7 @@ func TestGetBackendServers(t *testing.T) { Paths: []networking.HTTPIngressPath{ { Path: "/b", - PathType: &pathPrefix, + PathType: &pathTypePrefix, Backend: networking.IngressBackend{ ServiceName: "http-svc-1", ServicePort: intstr.IntOrString{ @@ -1313,7 +1311,7 @@ func TestGetBackendServers(t *testing.T) { Paths: []networking.HTTPIngressPath{ { Path: "/c", - PathType: &pathPrefix, + PathType: &pathTypePrefix, Backend: networking.IngressBackend{ ServiceName: "http-svc-1", ServicePort: intstr.IntOrString{ @@ -1350,7 +1348,7 @@ func TestGetBackendServers(t *testing.T) { Paths: []networking.HTTPIngressPath{ { Path: "/c", - PathType: &pathPrefix, + PathType: &pathTypePrefix, Backend: networking.IngressBackend{ ServiceName: "http-svc-2", ServicePort: intstr.IntOrString{ @@ -1435,7 +1433,7 @@ func TestGetBackendServers(t *testing.T) { Paths: []networking.HTTPIngressPath{ { Path: "/path1", - PathType: &pathPrefix, + PathType: &pathTypePrefix, Backend: networking.IngressBackend{ ServiceName: "path1-svc", ServicePort: intstr.IntOrString{ @@ -1475,7 +1473,7 @@ func TestGetBackendServers(t *testing.T) { Paths: []networking.HTTPIngressPath{ { Path: "/path2", - PathType: &pathPrefix, + PathType: &pathTypePrefix, Backend: networking.IngressBackend{ ServiceName: "path2-svc", ServicePort: intstr.IntOrString{ @@ -1540,7 +1538,7 @@ func TestGetBackendServers(t *testing.T) { Paths: []networking.HTTPIngressPath{ { Path: "/path1", - PathType: &pathPrefix, + PathType: &pathTypePrefix, Backend: networking.IngressBackend{ ServiceName: "path1-svc", ServicePort: intstr.IntOrString{ @@ -1580,7 +1578,7 @@ func TestGetBackendServers(t *testing.T) { Paths: []networking.HTTPIngressPath{ { Path: "/path2", - PathType: &pathPrefix, + PathType: &pathTypePrefix, Backend: networking.IngressBackend{ ServiceName: "path2-svc", ServicePort: intstr.IntOrString{ diff --git a/internal/ingress/controller/location.go b/internal/ingress/controller/location.go new file mode 100644 index 000000000..556fafc6b --- /dev/null +++ b/internal/ingress/controller/location.go @@ -0,0 +1,112 @@ +/* +Copyright 2020 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package controller + +import ( + "fmt" + "strings" + + "github.com/mitchellh/copystructure" + networking "k8s.io/api/networking/v1beta1" + "k8s.io/ingress-nginx/internal/ingress" + "k8s.io/klog/v2" +) + +var ( + pathTypeExact = networking.PathTypeExact + pathTypePrefix = networking.PathTypePrefix +) + +// updateServerLocations inspects the generated locations configuration for a server +// normalizing the path and adding an additional exact location when is possible +func updateServerLocations(locations []*ingress.Location) []*ingress.Location { + newLocations := []*ingress.Location{} + + // get Exact locations to check if one already exists + exactLocations := map[string]*ingress.Location{} + for _, location := range locations { + if *location.PathType == pathTypeExact { + exactLocations[location.Path] = location + } + } + + for _, location := range locations { + // location / does not require any update + if location.Path == rootLocation { + newLocations = append(newLocations, location) + continue + } + + // only Prefix locations could require an additional location block + if *location.PathType != pathTypePrefix { + newLocations = append(newLocations, location) + continue + } + + // locations with rewrite or using regular expressions are not modified + if needsRewrite(location) || location.Rewrite.UseRegex { + newLocations = append(newLocations, location) + continue + } + + // If exists an Exact location is not possible to create a new one. + if _, alreadyExists := exactLocations[location.Path]; alreadyExists { + // normalize path. Must end in / + location.Path = normalizePrefixPath(location.Path) + newLocations = append(newLocations, location) + continue + } + + // copy location before any change + el, err := copystructure.Copy(location) + if err != nil { + klog.ErrorS(err, "copying location") + } + + // normalize path. Must end in / + location.Path = normalizePrefixPath(location.Path) + newLocations = append(newLocations, location) + + // add exact location + exactLocation := el.(*ingress.Location) + exactLocation.PathType = &pathTypeExact + + newLocations = append(newLocations, exactLocation) + } + + return newLocations +} + +func normalizePrefixPath(path string) string { + if path == rootLocation { + return rootLocation + } + + if !strings.HasSuffix(path, "/") { + return fmt.Sprintf("%v/", path) + } + + return path +} + +func needsRewrite(location *ingress.Location) bool { + if len(location.Rewrite.Target) > 0 && location.Rewrite.Target != location.Path { + return true + } + + return false +} diff --git a/internal/ingress/controller/template/template.go b/internal/ingress/controller/template/template.go index ab032b7fe..f806130c9 100644 --- a/internal/ingress/controller/template/template.go +++ b/internal/ingress/controller/template/template.go @@ -317,26 +317,16 @@ func locationConfigForLua(l interface{}, a interface{}) string { return "{}" } - pathType := "" - if location.PathType != nil { - pathType = fmt.Sprintf("%v", *location.PathType) - } - if needsRewrite(location) || location.Rewrite.UseRegex { - pathType = "" - } - return fmt.Sprintf(`{ force_ssl_redirect = %t, ssl_redirect = %t, force_no_ssl_redirect = %t, use_port_in_redirects = %t, - path_type = "%v", }`, location.Rewrite.ForceSSLRedirect, location.Rewrite.SSLRedirect, isLocationInLocationList(l, all.Cfg.NoTLSRedirectLocations), location.UsePortInRedirects, - pathType, ) } @@ -417,7 +407,7 @@ func buildLocation(input interface{}, enforceRegex bool) string { } if location.PathType != nil && *location.PathType == networkingv1beta1.PathTypeExact { - return fmt.Sprintf(`~ ^%s$`, path) + return fmt.Sprintf(`= %s`, path) } return path diff --git a/rootfs/etc/nginx/lua/lua_ingress.lua b/rootfs/etc/nginx/lua/lua_ingress.lua index 0200b4682..3facb14c4 100644 --- a/rootfs/etc/nginx/lua/lua_ingress.lua +++ b/rootfs/etc/nginx/lua/lua_ingress.lua @@ -97,25 +97,6 @@ local function parse_x_forwarded_host() return hosts[1] end -local function k8s_matches_pathtype_prefix(current_uri, prefix) - if prefix == "/" then - return true - end - if current_uri == prefix then - return true - end - if #current_uri < #prefix then - return false - end - - local _, to = string.find(current_uri, prefix) - if to == nil then - return false - end - - return string.sub(current_uri, to + 1, to + 1) == "/" -end - function _M.init_worker() randomseed() end @@ -128,12 +109,6 @@ end -- This is where we do variable assignments to be used in subsequent -- phases or redirection function _M.rewrite(location_config) - if location_config.path_type == "Prefix" and - not k8s_matches_pathtype_prefix(ngx.var.uri, ngx.var.location_path) then - - return ngx.exit(ngx.HTTP_NOT_FOUND) - end - ngx.var.pass_access_scheme = ngx.var.scheme ngx.var.best_http_host = ngx.var.http_host or ngx.var.host diff --git a/test/e2e/annotations/rewrite.go b/test/e2e/annotations/rewrite.go index 37c5a75b9..667df920f 100644 --- a/test/e2e/annotations/rewrite.go +++ b/test/e2e/annotations/rewrite.go @@ -95,7 +95,8 @@ var _ = framework.DescribeAnnotation("rewrite-target use-regex enable-rewrite-lo f.WaitForNginxServer(host, func(server string) bool { - return strings.Contains(server, `location ~* "^/" {`) && strings.Contains(server, `location ~* "^/.well-known/acme/challenge" {`) + return strings.Contains(server, `location ~* "^/" {`) && + strings.Contains(server, `location ~* "^/.well-known/acme/challenge" {`) }) ginkgo.By("making a second request to the non-rewritten location") @@ -116,7 +117,7 @@ var _ = framework.DescribeAnnotation("rewrite-target use-regex enable-rewrite-lo f.WaitForNginxServer(host, func(server string) bool { - return strings.Contains(server, "location /foo {") + return strings.Contains(server, "location /foo/ {") }) ginkgo.By(`creating an ingress definition with the use-regex amd rewrite-target annotation`) @@ -129,7 +130,8 @@ var _ = framework.DescribeAnnotation("rewrite-target use-regex enable-rewrite-lo f.WaitForNginxServer(host, func(server string) bool { - return strings.Contains(server, `location ~* "^/foo" {`) && strings.Contains(server, `location ~* "^/foo.+" {`) + return strings.Contains(server, `location ~* "^/foo" {`) && + strings.Contains(server, `location ~* "^/foo.+" {`) }) ginkgo.By("ensuring '/foo' matches '~* ^/foo'") diff --git a/test/e2e/ingress/pathtype_exact.go b/test/e2e/ingress/pathtype_exact.go index 4edeff1b5..8aaa99215 100644 --- a/test/e2e/ingress/pathtype_exact.go +++ b/test/e2e/ingress/pathtype_exact.go @@ -60,8 +60,8 @@ var _ = framework.IngressNginxDescribe("[Ingress] [PathType] exact", func() { f.WaitForNginxServer(host, func(server string) bool { return strings.Contains(server, host) && - strings.Contains(server, "location ~ ^/exact$") && - strings.Contains(server, "location /exact") + strings.Contains(server, "location = /exact") && + strings.Contains(server, "location /exact/") }) body := f.HTTPTestClient(). @@ -98,8 +98,8 @@ var _ = framework.IngressNginxDescribe("[Ingress] [PathType] exact", func() { f.WaitForNginxServer(host, func(server string) bool { return strings.Contains(server, host) && - strings.Contains(server, "location ~ ^/exact$") && - strings.Contains(server, "location /exact") + strings.Contains(server, "location = /exact") && + strings.Contains(server, "location /exact/") }) body = f.HTTPTestClient(). diff --git a/test/e2e/ingress/pathtype_mixed.go b/test/e2e/ingress/pathtype_mixed.go new file mode 100644 index 000000000..aac7d9ffa --- /dev/null +++ b/test/e2e/ingress/pathtype_mixed.go @@ -0,0 +1,149 @@ +/* +Copyright 2019 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package ingress + +import ( + "net/http" + "strings" + + "github.com/onsi/ginkgo" + "github.com/stretchr/testify/assert" + + networkingv1beta1 "k8s.io/api/networking/v1beta1" + "k8s.io/ingress-nginx/test/e2e/framework" +) + +var _ = framework.IngressNginxDescribe("[Ingress] [PathType] mix Exact and Prefix paths", func() { + f := framework.NewDefaultFramework("mixed") + + ginkgo.BeforeEach(func() { + f.NewEchoDeployment() + }) + + var exactPathType = networkingv1beta1.PathTypeExact + + ginkgo.It("should choose the correct location", func() { + if !f.IsIngressV1Beta1Ready { + ginkgo.Skip("Test requires Kubernetes v1.18 or higher") + } + + host := "mixed.path" + + annotations := map[string]string{ + "nginx.ingress.kubernetes.io/configuration-snippet": `more_set_input_headers "pathType: exact";more_set_input_headers "pathlocation: /";`, + } + ing := framework.NewSingleIngress("exact-root", "/", host, f.Namespace, framework.EchoService, 80, annotations) + ing.Spec.Rules[0].IngressRuleValue.HTTP.Paths[0].PathType = &exactPathType + f.EnsureIngress(ing) + + annotations = map[string]string{ + "nginx.ingress.kubernetes.io/configuration-snippet": `more_set_input_headers "pathType: prefix";more_set_input_headers "pathlocation: /";`, + } + ing = framework.NewSingleIngress("prefix-root", "/", host, f.Namespace, framework.EchoService, 80, annotations) + f.EnsureIngress(ing) + + f.WaitForNginxServer(host, + func(server string) bool { + return strings.Contains(server, host) && + strings.Contains(server, "location = /") && + strings.Contains(server, "location /") + }) + + ginkgo.By("Checking exact request to /") + body := f.HTTPTestClient(). + GET("/"). + WithHeader("Host", host). + Expect(). + Status(http.StatusOK). + Body(). + Raw() + + assert.NotContains(ginkgo.GinkgoT(), body, "pathtype=prefix") + assert.Contains(ginkgo.GinkgoT(), body, "pathtype=exact") + assert.Contains(ginkgo.GinkgoT(), body, "pathlocation=/") + + ginkgo.By("Checking prefix request to /bar") + body = f.HTTPTestClient(). + GET("/bar"). + WithHeader("Host", host). + Expect(). + Status(http.StatusOK). + Body(). + Raw() + + assert.Contains(ginkgo.GinkgoT(), body, "pathtype=prefix") + assert.NotContains(ginkgo.GinkgoT(), body, "pathtype=exact") + assert.Contains(ginkgo.GinkgoT(), body, "pathlocation=/") + + annotations = map[string]string{ + "nginx.ingress.kubernetes.io/configuration-snippet": `more_set_input_headers "pathType: exact";more_set_input_headers "pathlocation: /foo";`, + } + ing = framework.NewSingleIngress("exact-foo", "/foo", host, f.Namespace, framework.EchoService, 80, annotations) + ing.Spec.Rules[0].IngressRuleValue.HTTP.Paths[0].PathType = &exactPathType + f.EnsureIngress(ing) + + annotations = map[string]string{ + "nginx.ingress.kubernetes.io/configuration-snippet": `more_set_input_headers "pathType: prefix";more_set_input_headers "pathlocation: /foo";`, + } + ing = framework.NewSingleIngress("prefix-foo", "/foo", host, f.Namespace, framework.EchoService, 80, annotations) + f.EnsureIngress(ing) + + f.WaitForNginxServer(host, + func(server string) bool { + return strings.Contains(server, host) && + strings.Contains(server, "location = /foo") && + strings.Contains(server, "location /foo/") + }) + + ginkgo.By("Checking exact request to /foo") + body = f.HTTPTestClient(). + GET("/foo"). + WithHeader("Host", host). + Expect(). + Status(http.StatusOK). + Body(). + Raw() + + assert.NotContains(ginkgo.GinkgoT(), body, "pathtype=prefix") + assert.Contains(ginkgo.GinkgoT(), body, "pathtype=exact") + assert.Contains(ginkgo.GinkgoT(), body, "pathlocation=/foo") + + ginkgo.By("Checking prefix request to /foo/bar") + body = f.HTTPTestClient(). + GET("/foo/bar"). + WithHeader("Host", host). + Expect(). + Status(http.StatusOK). + Body(). + Raw() + + assert.Contains(ginkgo.GinkgoT(), body, "pathtype=prefix") + assert.Contains(ginkgo.GinkgoT(), body, "pathlocation=/foo") + + ginkgo.By("Checking prefix request to /foobar") + body = f.HTTPTestClient(). + GET("/foobar"). + WithHeader("Host", host). + Expect(). + Status(http.StatusOK). + Body(). + Raw() + + assert.Contains(ginkgo.GinkgoT(), body, "pathtype=prefix") + assert.Contains(ginkgo.GinkgoT(), body, "pathlocation=/") + }) +})