From fab521ea356daf096c35961cc0b53f5fdf291665 Mon Sep 17 00:00:00 2001
From: Jakub Jarosz <99677300+jjngx@users.noreply.github.com>
Date: Thu, 4 May 2023 18:28:01 +0100
Subject: [PATCH] Fix gunzip support for VS and add python tests (#3844)
* Add gunzip support for VirtualServer
---
.../crds/k8s.nginx.org_virtualservers.yaml | 2 +-
.../crds/k8s.nginx.org_virtualservers.yaml | 2 +-
...server-and-virtualserverroute-resources.md | 2 +-
examples/custom-resources/jwt/README.md | 18 +
internal/configs/version2/http.go | 2 +-
.../version2/nginx-plus.virtualserver.tmpl | 2 +-
.../configs/version2/nginx.virtualserver.tmpl | 2 +-
internal/configs/version2/templates_test.go | 32 +-
internal/configs/virtualserver.go | 1 +
internal/configs/virtualserver_test.go | 12525 +++++++++-------
pkg/apis/configuration/v1/types.go | 2 +-
.../configuration/validation/virtualserver.go | 10 -
.../validation/virtualserver_test.go | 229 -
.../virtual-server/virtual-server-gunzip.yaml | 21 +
tests/suite/test_virtual_server.py | 41 +
15 files changed, 7045 insertions(+), 5846 deletions(-)
create mode 100644 tests/data/virtual-server/virtual-server-gunzip.yaml
diff --git a/deployments/common/crds/k8s.nginx.org_virtualservers.yaml b/deployments/common/crds/k8s.nginx.org_virtualservers.yaml
index c5015904fd..b288111874 100644
--- a/deployments/common/crds/k8s.nginx.org_virtualservers.yaml
+++ b/deployments/common/crds/k8s.nginx.org_virtualservers.yaml
@@ -87,7 +87,7 @@ spec:
recordType:
type: string
gunzip:
- type: string
+ type: boolean
host:
type: string
http-snippets:
diff --git a/deployments/helm-chart/crds/k8s.nginx.org_virtualservers.yaml b/deployments/helm-chart/crds/k8s.nginx.org_virtualservers.yaml
index c5015904fd..b288111874 100644
--- a/deployments/helm-chart/crds/k8s.nginx.org_virtualservers.yaml
+++ b/deployments/helm-chart/crds/k8s.nginx.org_virtualservers.yaml
@@ -87,7 +87,7 @@ spec:
recordType:
type: string
gunzip:
- type: string
+ type: boolean
host:
type: string
http-snippets:
diff --git a/docs/content/configuration/virtualserver-and-virtualserverroute-resources.md b/docs/content/configuration/virtualserver-and-virtualserverroute-resources.md
index 412cf848e6..3240d64794 100644
--- a/docs/content/configuration/virtualserver-and-virtualserverroute-resources.md
+++ b/docs/content/configuration/virtualserver-and-virtualserverroute-resources.md
@@ -54,7 +54,7 @@ spec:
| ---| ---| ---| --- |
|``host`` | The host (domain name) of the server. Must be a valid subdomain as defined in RFC 1123, such as ``my-app`` or ``hello.example.com``. When using a wildcard domain like ``*.example.com`` the domain must be contained in double quotes. The ``host`` value needs to be unique among all Ingress and VirtualServer resources. See also [Handling Host and Listener Collisions](/nginx-ingress-controller/configuration/handling-host-and-listener-collisions). | ``string`` | Yes |
|``tls`` | The TLS termination configuration. | [tls](#virtualservertls) | No |
-|``gunzip`` | Enables or disables [decompression](https://docs.nginx.com/nginx/admin-guide/web-server/compression/) of gzipped responses for clients. Allowed values are: "on" or "off". If the ``gunzip`` value is not set, it defaults to ``off``. | ``string`` | No |
+|``gunzip`` | Enables or disables [decompression](https://docs.nginx.com/nginx/admin-guide/web-server/compression/) of gzipped responses for clients. Allowed values “on”/“off”, “true”/“false” or “yes”/“no”. If the ``gunzip`` value is not set, it defaults to ``off``. | ``boolean`` | No |
|``externalDNS`` | The externalDNS configuration for a VirtualServer. | [externalDNS](#virtualserverexternaldns) | No |
|``dos`` | A reference to a DosProtectedResource, setting this enables DOS protection of the VirtualServer. | ``string`` | No |
|``policies`` | A list of policies. | [[]policy](#virtualserverpolicy) | No |
diff --git a/examples/custom-resources/jwt/README.md b/examples/custom-resources/jwt/README.md
index 8466ceb334..e812b64cd6 100644
--- a/examples/custom-resources/jwt/README.md
+++ b/examples/custom-resources/jwt/README.md
@@ -67,3 +67,21 @@ Date: 10/Sep/2020:18:20:03 +0000
URI: /
Request ID: db2c07ce640755ccbe9f666d16f85620
```
+
+> **Note**:
+You can add a ``gunzip`` option to the VirtualServer spec. Adding the ``gunzip`` allows NIC to decompress responses where an item
+like a JWT token is compressed by the IdP.
+If an IdP compresses a JWT token and NIC is not configured to decompress responses (``gunzip`` not set to ``on``), the error "invalid JWK set while sending to client" is generated by NIC.
+When the ``gunzip`` value is set to ``on``, NIC automatically decompresses responses with “Content-Encoding: gzip” header.
+
+Example:
+```yaml
+apiVersion: k8s.nginx.org/v1
+kind: VirtualServer
+metadata:
+ name: webapp
+spec:
+ host: webapp.example.com
+ gunzip: on
+ ...
+```
diff --git a/internal/configs/version2/http.go b/internal/configs/version2/http.go
index 63b8a899d7..2a8e5c934d 100644
--- a/internal/configs/version2/http.go
+++ b/internal/configs/version2/http.go
@@ -81,7 +81,7 @@ type Server struct {
VSNamespace string
VSName string
DisableIPV6 bool
- Gunzip string
+ Gunzip bool
}
// SSL defines SSL configuration for a server.
diff --git a/internal/configs/version2/nginx-plus.virtualserver.tmpl b/internal/configs/version2/nginx-plus.virtualserver.tmpl
index ccf14b1f42..dc2ebf1b81 100644
--- a/internal/configs/version2/nginx-plus.virtualserver.tmpl
+++ b/internal/configs/version2/nginx-plus.virtualserver.tmpl
@@ -63,7 +63,7 @@ proxy_cache_path /var/cache/nginx/jwks_uri_{{$s.VSName}} levels=1 keys_zone=jwks
{{ end }}
server {
- {{ if (eq $s.Gunzip "on") }}gunzip {{ $s.Gunzip }};{{end}}
+ {{ if $s.Gunzip }}gunzip on;{{end}}
listen 80{{ if $s.ProxyProtocol }} proxy_protocol{{ end }};
{{ if not $s.DisableIPV6 }}listen [::]:80{{ if $s.ProxyProtocol }} proxy_protocol{{ end }};{{ end }}
diff --git a/internal/configs/version2/nginx.virtualserver.tmpl b/internal/configs/version2/nginx.virtualserver.tmpl
index 1b03b44df5..bd032e9c4e 100644
--- a/internal/configs/version2/nginx.virtualserver.tmpl
+++ b/internal/configs/version2/nginx.virtualserver.tmpl
@@ -40,7 +40,7 @@ limit_req_zone {{ $z.Key }} zone={{ $z.ZoneName }}:{{ $z.ZoneSize }} rate={{ $z.
{{ $s := .Server }}
server {
- {{ if (eq $s.Gunzip "on") }}gunzip {{ $s.Gunzip }};{{end}}
+ {{ if $s.Gunzip }}gunzip on;{{end}}
listen 80{{ if $s.ProxyProtocol }} proxy_protocol{{ end }};
{{ if not $s.DisableIPV6 }}listen [::]:80{{ if $s.ProxyProtocol }} proxy_protocol{{ end }};{{ end }}
diff --git a/internal/configs/version2/templates_test.go b/internal/configs/version2/templates_test.go
index e2af009e90..f56315880d 100644
--- a/internal/configs/version2/templates_test.go
+++ b/internal/configs/version2/templates_test.go
@@ -1,6 +1,7 @@
package version2
import (
+ "bytes"
"testing"
)
@@ -38,6 +39,9 @@ func TestExecuteVirtualServerTemplate_RendersTemplateWithServerGunzipOn(t *testi
if err != nil {
t.Error(err)
}
+ if !bytes.Contains(got, []byte("gunzip on;")) {
+ t.Error("want `gunzip on` directive, got no directive")
+ }
t.Log(string(got))
}
@@ -51,32 +55,25 @@ func TestExecuteVirtualServerTemplate_RendersTemplateWithServerGunzipOff(t *test
if err != nil {
t.Error(err)
}
- t.Log(string(got))
-}
-
-func TestExecuteVirtualServerTemplate_RendersTemplateWithServerGunzipEmpty(t *testing.T) {
- t.Parallel()
- executor, err := NewTemplateExecutor(nginxPlusVirtualServerTmpl, nginxPlusTransportServerTmpl)
- if err != nil {
- t.Fatal(err)
- }
- got, err := executor.ExecuteVirtualServerTemplate(&virtualServerCfgWithEmptyGunzip)
- if err != nil {
- t.Error(err)
+ if bytes.Contains(got, []byte("gunzip on;")) {
+ t.Error("want no directive, got `gunzip on`")
}
t.Log(string(got))
}
-func TestExecuteVirtualServerTemplate_RendersTemplateWithoutServerGunzip(t *testing.T) {
+func TestExecuteVirtualServerTemplate_RendersTemplateWithServerGunzipNotSet(t *testing.T) {
t.Parallel()
executor, err := NewTemplateExecutor(nginxPlusVirtualServerTmpl, nginxPlusTransportServerTmpl)
if err != nil {
t.Fatal(err)
}
- got, err := executor.ExecuteVirtualServerTemplate(&virtualServerCfg)
+ got, err := executor.ExecuteVirtualServerTemplate(&virtualServerCfgWithGunzipNotSet)
if err != nil {
t.Error(err)
}
+ if bytes.Contains(got, []byte("gunzip on;")) {
+ t.Error("want no directive, got `gunzip on` directive")
+ }
t.Log(string(got))
}
@@ -853,7 +850,7 @@ var (
},
},
},
- Gunzip: "on",
+ Gunzip: true,
},
}
@@ -1199,11 +1196,11 @@ var (
},
},
},
- Gunzip: "off",
+ Gunzip: false,
},
}
- virtualServerCfgWithEmptyGunzip = VirtualServerConfig{
+ virtualServerCfgWithGunzipNotSet = VirtualServerConfig{
LimitReqZones: []LimitReqZone{
{
ZoneName: "pol_rl_test_test_test", Rate: "10r/s", ZoneSize: "10m", Key: "$url",
@@ -1545,7 +1542,6 @@ var (
},
},
},
- Gunzip: "",
},
}
diff --git a/internal/configs/virtualserver.go b/internal/configs/virtualserver.go
index 2cc81d200b..c399b910d1 100644
--- a/internal/configs/virtualserver.go
+++ b/internal/configs/virtualserver.go
@@ -684,6 +684,7 @@ func (vsc *virtualServerConfigurator) GenerateVirtualServerConfig(
HTTPSnippets: httpSnippets,
Server: version2.Server{
ServerName: vsEx.VirtualServer.Spec.Host,
+ Gunzip: vsEx.VirtualServer.Spec.Gunzip,
StatusZone: vsEx.VirtualServer.Spec.Host,
ProxyProtocol: vsc.cfgParams.ProxyProtocol,
SSL: sslConfig,
diff --git a/internal/configs/virtualserver_test.go b/internal/configs/virtualserver_test.go
index 6dd37de689..2e40ed2385 100644
--- a/internal/configs/virtualserver_test.go
+++ b/internal/configs/virtualserver_test.go
@@ -197,210 +197,12 @@ func TestVariableNamer(t *testing.T) {
}
}
-func TestGenerateVirtualServerConfig(t *testing.T) {
+func TestGenerateVSConfig_GeneratesConfigWithGunzipOn(t *testing.T) {
t.Parallel()
- virtualServerEx := VirtualServerEx{
- VirtualServer: &conf_v1.VirtualServer{
- ObjectMeta: meta_v1.ObjectMeta{
- Name: "cafe",
- Namespace: "default",
- },
- Spec: conf_v1.VirtualServerSpec{
- Host: "cafe.example.com",
- Upstreams: []conf_v1.Upstream{
- {
- Name: "tea",
- Service: "tea-svc",
- Port: 80,
- },
- {
- Name: "tea-latest",
- Service: "tea-svc",
- Subselector: map[string]string{"version": "v1"},
- Port: 80,
- },
- {
- Name: "coffee",
- Service: "coffee-svc",
- Port: 80,
- },
- },
- Routes: []conf_v1.Route{
- {
- Path: "/tea",
- Action: &conf_v1.Action{
- Pass: "tea",
- },
- },
- {
- Path: "/tea-latest",
- Action: &conf_v1.Action{
- Pass: "tea-latest",
- },
- },
- {
- Path: "/coffee",
- Route: "default/coffee",
- },
- {
- Path: "/subtea",
- Route: "default/subtea",
- },
- {
- Path: "/coffee-errorpage",
- Action: &conf_v1.Action{
- Pass: "coffee",
- },
- ErrorPages: []conf_v1.ErrorPage{
- {
- Codes: []int{401, 403},
- Redirect: &conf_v1.ErrorPageRedirect{
- ActionRedirect: conf_v1.ActionRedirect{
- URL: "http://nginx.com",
- Code: 301,
- },
- },
- },
- },
- },
- {
- Path: "/coffee-errorpage-subroute",
- Route: "default/subcoffee",
- ErrorPages: []conf_v1.ErrorPage{
- {
- Codes: []int{401, 403},
- Redirect: &conf_v1.ErrorPageRedirect{
- ActionRedirect: conf_v1.ActionRedirect{
- URL: "http://nginx.com",
- Code: 301,
- },
- },
- },
- },
- },
- },
- },
- },
- Endpoints: map[string][]string{
- "default/tea-svc:80": {
- "10.0.0.20:80",
- },
- "default/tea-svc_version=v1:80": {
- "10.0.0.30:80",
- },
- "default/coffee-svc:80": {
- "10.0.0.40:80",
- },
- "default/sub-tea-svc_version=v1:80": {
- "10.0.0.50:80",
- },
- },
- VirtualServerRoutes: []*conf_v1.VirtualServerRoute{
- {
- ObjectMeta: meta_v1.ObjectMeta{
- Name: "coffee",
- Namespace: "default",
- },
- Spec: conf_v1.VirtualServerRouteSpec{
- Host: "cafe.example.com",
- Upstreams: []conf_v1.Upstream{
- {
- Name: "coffee",
- Service: "coffee-svc",
- Port: 80,
- },
- },
- Subroutes: []conf_v1.Route{
- {
- Path: "/coffee",
- Action: &conf_v1.Action{
- Pass: "coffee",
- },
- },
- },
- },
- },
- {
- ObjectMeta: meta_v1.ObjectMeta{
- Name: "subtea",
- Namespace: "default",
- },
- Spec: conf_v1.VirtualServerRouteSpec{
- Host: "cafe.example.com",
- Upstreams: []conf_v1.Upstream{
- {
- Name: "subtea",
- Service: "sub-tea-svc",
- Port: 80,
- Subselector: map[string]string{"version": "v1"},
- },
- },
- Subroutes: []conf_v1.Route{
- {
- Path: "/subtea",
- Action: &conf_v1.Action{
- Pass: "subtea",
- },
- },
- },
- },
- },
- {
- ObjectMeta: meta_v1.ObjectMeta{
- Name: "subcoffee",
- Namespace: "default",
- },
- Spec: conf_v1.VirtualServerRouteSpec{
- Host: "cafe.example.com",
- Upstreams: []conf_v1.Upstream{
- {
- Name: "coffee",
- Service: "coffee-svc",
- Port: 80,
- },
- },
- Subroutes: []conf_v1.Route{
- {
- Path: "/coffee-errorpage-subroute",
- Action: &conf_v1.Action{
- Pass: "coffee",
- },
- },
- {
- Path: "/coffee-errorpage-subroute-defined",
- Action: &conf_v1.Action{
- Pass: "coffee",
- },
- ErrorPages: []conf_v1.ErrorPage{
- {
- Codes: []int{502, 503},
- Return: &conf_v1.ErrorPageReturn{
- ActionReturn: conf_v1.ActionReturn{
- Code: 200,
- Type: "text/plain",
- Body: "All Good",
- },
- },
- },
- },
- },
- },
- },
- },
- },
- }
- baseCfgParams := ConfigParams{
- ServerTokens: "off",
- Keepalive: 16,
- ServerSnippets: []string{"# server snippet"},
- ProxyProtocol: true,
- SetRealIPFrom: []string{"0.0.0.0/0"},
- RealIPHeader: "X-Real-IP",
- RealIPRecursive: true,
- }
+ vsc := newVirtualServerConfigurator(&baseCfgParams, true, false, &StaticConfigParams{TLSPassthrough: true}, false)
- expected := version2.VirtualServerConfig{
+ want := version2.VirtualServerConfig{
Upstreams: []version2.Upstream{
{
UpstreamLabels: version2.UpstreamLabels{
@@ -497,6 +299,7 @@ func TestGenerateVirtualServerConfig(t *testing.T) {
LimitReqZones: []version2.LimitReqZone{},
Server: version2.Server{
ServerName: "cafe.example.com",
+ Gunzip: true,
StatusZone: "cafe.example.com",
VSNamespace: "default",
VSName: "cafe",
@@ -643,79 +446,37 @@ func TestGenerateVirtualServerConfig(t *testing.T) {
},
}
- isPlus := false
- isResolverConfigured := false
- isWildcardEnabled := false
- vsc := newVirtualServerConfigurator(
- &baseCfgParams,
- isPlus,
- isResolverConfigured,
- &StaticConfigParams{TLSPassthrough: true},
- isWildcardEnabled,
- )
-
- result, warnings := vsc.GenerateVirtualServerConfig(&virtualServerEx, nil, nil)
- if diff := cmp.Diff(expected, result); diff != "" {
- t.Errorf("GenerateVirtualServerConfig() mismatch (-want +got):\n%s", diff)
+ got, warnings := vsc.GenerateVirtualServerConfig(&virtualServerExWithGunzipOn, nil, nil)
+ if len(warnings) > 0 {
+ t.Fatalf("want no warnings, got: %v", vsc.warnings)
}
-
- if len(warnings) != 0 {
- t.Errorf("GenerateVirtualServerConfig returned warnings: %v", vsc.warnings)
+ if !cmp.Equal(want, got) {
+ t.Error(cmp.Diff(want, got))
}
}
-func TestGenerateVirtualServerConfigIPV6Disabled(t *testing.T) {
+func TestGenerateVSConfig_GeneratesConfigWithGunzipOff(t *testing.T) {
t.Parallel()
- virtualServerEx := VirtualServerEx{
- VirtualServer: &conf_v1.VirtualServer{
- ObjectMeta: meta_v1.ObjectMeta{
- Name: "cafe",
- Namespace: "default",
- },
- Spec: conf_v1.VirtualServerSpec{
- Host: "cafe.example.com",
- Upstreams: []conf_v1.Upstream{
- {
- Name: "tea",
- Service: "tea-svc",
- Port: 80,
- },
+
+ vsc := newVirtualServerConfigurator(&baseCfgParams, true, false, &StaticConfigParams{TLSPassthrough: true}, false)
+
+ want := version2.VirtualServerConfig{
+ Upstreams: []version2.Upstream{
+ {
+ UpstreamLabels: version2.UpstreamLabels{
+ Service: "tea-svc",
+ ResourceType: "virtualserver",
+ ResourceName: "cafe",
+ ResourceNamespace: "default",
+ },
+ Name: "vs_default_cafe_tea",
+ Servers: []version2.UpstreamServer{
{
- Name: "coffee",
- Service: "coffee-svc",
- Port: 80,
+ Address: "10.0.0.20:80",
},
},
- Routes: []conf_v1.Route{
- {
- Path: "/tea",
- Action: &conf_v1.Action{
- Pass: "tea",
- },
- },
- {
- Path: "/coffee",
- Action: &conf_v1.Action{
- Pass: "coffee",
- },
- },
- },
- },
- },
- Endpoints: map[string][]string{
- "default/tea-svc:80": {
- "10.0.0.20:80",
- },
- "default/coffee-svc:80": {
- "10.0.0.40:80",
+ Keepalive: 16,
},
- },
- }
-
- baseCfgParams := ConfigParams{}
-
- expected := version2.VirtualServerConfig{
- Upstreams: []version2.Upstream{
{
UpstreamLabels: version2.UpstreamLabels{
Service: "tea-svc",
@@ -723,12 +484,13 @@ func TestGenerateVirtualServerConfigIPV6Disabled(t *testing.T) {
ResourceName: "cafe",
ResourceNamespace: "default",
},
- Name: "vs_default_cafe_tea",
+ Name: "vs_default_cafe_tea-latest",
Servers: []version2.UpstreamServer{
{
- Address: "10.0.0.20:80",
+ Address: "10.0.0.30:80",
},
},
+ Keepalive: 16,
},
{
UpstreamLabels: version2.UpstreamLabels{
@@ -743,429 +505,473 @@ func TestGenerateVirtualServerConfigIPV6Disabled(t *testing.T) {
Address: "10.0.0.40:80",
},
},
+ Keepalive: 16,
+ },
+ {
+ UpstreamLabels: version2.UpstreamLabels{
+ Service: "coffee-svc",
+ ResourceType: "virtualserverroute",
+ ResourceName: "coffee",
+ ResourceNamespace: "default",
+ },
+ Name: "vs_default_cafe_vsr_default_coffee_coffee",
+ Servers: []version2.UpstreamServer{
+ {
+ Address: "10.0.0.40:80",
+ },
+ },
+ Keepalive: 16,
+ },
+ {
+ UpstreamLabels: version2.UpstreamLabels{
+ Service: "sub-tea-svc",
+ ResourceType: "virtualserverroute",
+ ResourceName: "subtea",
+ ResourceNamespace: "default",
+ },
+ Name: "vs_default_cafe_vsr_default_subtea_subtea",
+ Servers: []version2.UpstreamServer{
+ {
+ Address: "10.0.0.50:80",
+ },
+ },
+ Keepalive: 16,
+ },
+ {
+ UpstreamLabels: version2.UpstreamLabels{
+ Service: "coffee-svc",
+ ResourceType: "virtualserverroute",
+ ResourceName: "subcoffee",
+ ResourceNamespace: "default",
+ },
+ Name: "vs_default_cafe_vsr_default_subcoffee_coffee",
+ Servers: []version2.UpstreamServer{
+ {
+ Address: "10.0.0.40:80",
+ },
+ },
+ Keepalive: 16,
},
},
HTTPSnippets: []string{},
LimitReqZones: []version2.LimitReqZone{},
Server: version2.Server{
- ServerName: "cafe.example.com",
- StatusZone: "cafe.example.com",
- VSNamespace: "default",
- VSName: "cafe",
- DisableIPV6: true,
+ ServerName: "cafe.example.com",
+ Gunzip: false,
+ StatusZone: "cafe.example.com",
+ VSNamespace: "default",
+ VSName: "cafe",
+ ProxyProtocol: true,
+ ServerTokens: "off",
+ SetRealIPFrom: []string{"0.0.0.0/0"},
+ RealIPHeader: "X-Real-IP",
+ RealIPRecursive: true,
+ Snippets: []string{"# server snippet"},
+ TLSPassthrough: true,
Locations: []version2.Location{
{
Path: "/tea",
ProxyPass: "http://vs_default_cafe_tea",
ProxyNextUpstream: "error timeout",
ProxyNextUpstreamTimeout: "0s",
+ ProxyNextUpstreamTries: 0,
+ HasKeepalive: true,
ProxySSLName: "tea-svc.default.svc",
ProxyPassRequestHeaders: true,
ProxySetHeaders: []version2.Header{{Name: "Host", Value: "$host"}},
ServiceName: "tea-svc",
},
{
- Path: "/coffee",
+ Path: "/tea-latest",
+ ProxyPass: "http://vs_default_cafe_tea-latest",
+ ProxyNextUpstream: "error timeout",
+ ProxyNextUpstreamTimeout: "0s",
+ ProxyNextUpstreamTries: 0,
+ HasKeepalive: true,
+ ProxySSLName: "tea-svc.default.svc",
+ ProxyPassRequestHeaders: true,
+ ProxySetHeaders: []version2.Header{{Name: "Host", Value: "$host"}},
+ ServiceName: "tea-svc",
+ },
+ // Order changes here because we generate first all the VS Routes and then all the VSR Subroutes (separated for loops)
+ {
+ Path: "/coffee-errorpage",
ProxyPass: "http://vs_default_cafe_coffee",
ProxyNextUpstream: "error timeout",
ProxyNextUpstreamTimeout: "0s",
+ ProxyNextUpstreamTries: 0,
+ HasKeepalive: true,
+ ProxyInterceptErrors: true,
+ ErrorPages: []version2.ErrorPage{
+ {
+ Name: "http://nginx.com",
+ Codes: "401 403",
+ ResponseCode: 301,
+ },
+ },
+ ProxySSLName: "coffee-svc.default.svc",
+ ProxyPassRequestHeaders: true,
+ ProxySetHeaders: []version2.Header{{Name: "Host", Value: "$host"}},
+ ServiceName: "coffee-svc",
+ },
+ {
+ Path: "/coffee",
+ ProxyPass: "http://vs_default_cafe_vsr_default_coffee_coffee",
+ ProxyNextUpstream: "error timeout",
+ ProxyNextUpstreamTimeout: "0s",
+ ProxyNextUpstreamTries: 0,
+ HasKeepalive: true,
ProxySSLName: "coffee-svc.default.svc",
ProxyPassRequestHeaders: true,
ProxySetHeaders: []version2.Header{{Name: "Host", Value: "$host"}},
ServiceName: "coffee-svc",
+ IsVSR: true,
+ VSRName: "coffee",
+ VSRNamespace: "default",
+ },
+ {
+ Path: "/subtea",
+ ProxyPass: "http://vs_default_cafe_vsr_default_subtea_subtea",
+ ProxyNextUpstream: "error timeout",
+ ProxyNextUpstreamTimeout: "0s",
+ ProxyNextUpstreamTries: 0,
+ HasKeepalive: true,
+ ProxySSLName: "sub-tea-svc.default.svc",
+ ProxyPassRequestHeaders: true,
+ ProxySetHeaders: []version2.Header{{Name: "Host", Value: "$host"}},
+ ServiceName: "sub-tea-svc",
+ IsVSR: true,
+ VSRName: "subtea",
+ VSRNamespace: "default",
+ },
+
+ {
+ Path: "/coffee-errorpage-subroute",
+ ProxyPass: "http://vs_default_cafe_vsr_default_subcoffee_coffee",
+ ProxyNextUpstream: "error timeout",
+ ProxyNextUpstreamTimeout: "0s",
+ ProxyNextUpstreamTries: 0,
+ HasKeepalive: true,
+ ProxyInterceptErrors: true,
+ ErrorPages: []version2.ErrorPage{
+ {
+ Name: "http://nginx.com",
+ Codes: "401 403",
+ ResponseCode: 301,
+ },
+ },
+ ProxySSLName: "coffee-svc.default.svc",
+ ProxyPassRequestHeaders: true,
+ ProxySetHeaders: []version2.Header{{Name: "Host", Value: "$host"}},
+ ServiceName: "coffee-svc",
+ IsVSR: true,
+ VSRName: "subcoffee",
+ VSRNamespace: "default",
+ },
+ {
+ Path: "/coffee-errorpage-subroute-defined",
+ ProxyPass: "http://vs_default_cafe_vsr_default_subcoffee_coffee",
+ ProxyNextUpstream: "error timeout",
+ ProxyNextUpstreamTimeout: "0s",
+ ProxyNextUpstreamTries: 0,
+ HasKeepalive: true,
+ ProxyInterceptErrors: true,
+ ErrorPages: []version2.ErrorPage{
+ {
+ Name: "@error_page_0_0",
+ Codes: "502 503",
+ ResponseCode: 200,
+ },
+ },
+ ProxySSLName: "coffee-svc.default.svc",
+ ProxyPassRequestHeaders: true,
+ ProxySetHeaders: []version2.Header{{Name: "Host", Value: "$host"}},
+ ServiceName: "coffee-svc",
+ IsVSR: true,
+ VSRName: "subcoffee",
+ VSRNamespace: "default",
+ },
+ },
+ ErrorPageLocations: []version2.ErrorPageLocation{
+ {
+ Name: "@error_page_0_0",
+ DefaultType: "text/plain",
+ Return: &version2.Return{
+ Text: "All Good",
+ },
},
},
},
}
- isPlus := false
- isResolverConfigured := false
- isWildcardEnabled := false
- vsc := newVirtualServerConfigurator(
- &baseCfgParams,
- isPlus,
- isResolverConfigured,
- &StaticConfigParams{DisableIPV6: true},
- isWildcardEnabled,
- )
-
- result, warnings := vsc.GenerateVirtualServerConfig(&virtualServerEx, nil, nil)
- if diff := cmp.Diff(expected, result); diff != "" {
- t.Errorf("GenerateVirtualServerConfig() mismatch (-want +got):\n%s", diff)
+ got, warnings := vsc.GenerateVirtualServerConfig(&virtualServerExWithGunzipOff, nil, nil)
+ if len(warnings) > 0 {
+ t.Fatalf("want no warnings, got: %v", vsc.warnings)
}
-
- if len(warnings) != 0 {
- t.Errorf("GenerateVirtualServerConfig returned warnings: %v", vsc.warnings)
+ if !cmp.Equal(want, got) {
+ t.Error(cmp.Diff(want, got))
}
}
-func TestGenerateVirtualServerConfigGrpcErrorPageWarning(t *testing.T) {
+func TestGenerateVSConfig_GeneratesConfigWithNoGunzip(t *testing.T) {
t.Parallel()
- virtualServerEx := VirtualServerEx{
- VirtualServer: &conf_v1.VirtualServer{
- ObjectMeta: meta_v1.ObjectMeta{
- Name: "cafe",
- Namespace: "default",
- },
- Spec: conf_v1.VirtualServerSpec{
- Host: "cafe.example.com",
- TLS: &conf_v1.TLS{
- Secret: "",
+
+ vsc := newVirtualServerConfigurator(&baseCfgParams, true, false, &StaticConfigParams{TLSPassthrough: true}, false)
+
+ want := version2.VirtualServerConfig{
+ Upstreams: []version2.Upstream{
+ {
+ UpstreamLabels: version2.UpstreamLabels{
+ Service: "tea-svc",
+ ResourceType: "virtualserver",
+ ResourceName: "cafe",
+ ResourceNamespace: "default",
},
- Upstreams: []conf_v1.Upstream{
+ Name: "vs_default_cafe_tea",
+ Servers: []version2.UpstreamServer{
{
- Name: "grpc-app-1",
- Service: "grpc-svc",
- Port: 50051,
- Type: "grpc",
- TLS: conf_v1.UpstreamTLS{
- Enable: true,
- },
- },
- {
- Name: "grpc-app-2",
- Service: "grpc-svc2",
- Port: 50052,
- Type: "grpc",
- TLS: conf_v1.UpstreamTLS{
- Enable: true,
- },
- },
- {
- Name: "tea",
- Service: "tea-svc",
- Port: 80,
- },
- },
- Routes: []conf_v1.Route{
- {
- Path: "/grpc-errorpage",
- Action: &conf_v1.Action{
- Pass: "grpc-app-1",
- },
- ErrorPages: []conf_v1.ErrorPage{
- {
- Codes: []int{404, 405},
- Return: &conf_v1.ErrorPageReturn{
- ActionReturn: conf_v1.ActionReturn{
- Code: 200,
- Type: "text/plain",
- Body: "All Good",
- },
- },
- },
- },
- },
- {
- Path: "/grpc-matches",
- Matches: []conf_v1.Match{
- {
- Conditions: []conf_v1.Condition{
- {
- Variable: "$request_method",
- Value: "POST",
- },
- },
- Action: &conf_v1.Action{
- Pass: "grpc-app-2",
- },
- },
- },
- Action: &conf_v1.Action{
- Pass: "tea",
- },
- ErrorPages: []conf_v1.ErrorPage{
- {
- Codes: []int{404},
- Return: &conf_v1.ErrorPageReturn{
- ActionReturn: conf_v1.ActionReturn{
- Code: 200,
- Type: "text/plain",
- Body: "Original resource not found, but success!",
- },
- },
- },
- },
- },
- {
- Path: "/grpc-splits",
- Splits: []conf_v1.Split{
- {
- Weight: 90,
- Action: &conf_v1.Action{
- Pass: "grpc-app-1",
- },
- },
- {
- Weight: 10,
- Action: &conf_v1.Action{
- Pass: "grpc-app-2",
- },
- },
- },
- ErrorPages: []conf_v1.ErrorPage{
- {
- Codes: []int{404, 405},
- Return: &conf_v1.ErrorPageReturn{
- ActionReturn: conf_v1.ActionReturn{
- Code: 200,
- Type: "text/plain",
- Body: "All Good",
- },
- },
- },
- },
+ Address: "10.0.0.20:80",
},
},
+ Keepalive: 16,
},
- },
- Endpoints: map[string][]string{
- "default/grpc-svc:50051": {
- "10.0.0.20:80",
- },
- },
- }
-
- baseCfgParams := ConfigParams{
- HTTP2: true,
- }
-
- expected := version2.VirtualServerConfig{
- Upstreams: []version2.Upstream{
{
UpstreamLabels: version2.UpstreamLabels{
- Service: "grpc-svc",
+ Service: "tea-svc",
ResourceType: "virtualserver",
ResourceName: "cafe",
ResourceNamespace: "default",
},
- Name: "vs_default_cafe_grpc-app-1",
+ Name: "vs_default_cafe_tea-latest",
Servers: []version2.UpstreamServer{
{
- Address: "10.0.0.20:80",
+ Address: "10.0.0.30:80",
},
},
+ Keepalive: 16,
},
{
- Name: "vs_default_cafe_grpc-app-2",
UpstreamLabels: version2.UpstreamLabels{
- Service: "grpc-svc2",
+ Service: "coffee-svc",
ResourceType: "virtualserver",
ResourceName: "cafe",
ResourceNamespace: "default",
},
+ Name: "vs_default_cafe_coffee",
Servers: []version2.UpstreamServer{
{
- Address: "unix:/var/lib/nginx/nginx-502-server.sock",
+ Address: "10.0.0.40:80",
},
},
+ Keepalive: 16,
},
{
- Name: "vs_default_cafe_tea",
UpstreamLabels: version2.UpstreamLabels{
- Service: "tea-svc",
- ResourceType: "virtualserver",
- ResourceName: "cafe",
+ Service: "coffee-svc",
+ ResourceType: "virtualserverroute",
+ ResourceName: "coffee",
ResourceNamespace: "default",
},
+ Name: "vs_default_cafe_vsr_default_coffee_coffee",
Servers: []version2.UpstreamServer{
{
- Address: "unix:/var/lib/nginx/nginx-502-server.sock",
+ Address: "10.0.0.40:80",
},
},
+ Keepalive: 16,
},
- },
- HTTPSnippets: []string{},
- LimitReqZones: []version2.LimitReqZone{},
- Maps: []version2.Map{
{
- Source: "$request_method",
- Variable: "$vs_default_cafe_matches_0_match_0_cond_0",
- Parameters: []version2.Parameter{
- {
- Value: `"POST"`,
- Result: "1",
- },
+ UpstreamLabels: version2.UpstreamLabels{
+ Service: "sub-tea-svc",
+ ResourceType: "virtualserverroute",
+ ResourceName: "subtea",
+ ResourceNamespace: "default",
+ },
+ Name: "vs_default_cafe_vsr_default_subtea_subtea",
+ Servers: []version2.UpstreamServer{
{
- Value: "default",
- Result: "0",
+ Address: "10.0.0.50:80",
},
},
+ Keepalive: 16,
},
{
- Source: "$vs_default_cafe_matches_0_match_0_cond_0",
- Variable: "$vs_default_cafe_matches_0",
- Parameters: []version2.Parameter{
- {
- Value: "~^1",
- Result: "/internal_location_matches_0_match_0",
- },
+ UpstreamLabels: version2.UpstreamLabels{
+ Service: "coffee-svc",
+ ResourceType: "virtualserverroute",
+ ResourceName: "subcoffee",
+ ResourceNamespace: "default",
+ },
+ Name: "vs_default_cafe_vsr_default_subcoffee_coffee",
+ Servers: []version2.UpstreamServer{
{
- Value: "default",
- Result: "/internal_location_matches_0_default",
+ Address: "10.0.0.40:80",
},
},
+ Keepalive: 16,
},
},
+ HTTPSnippets: []string{},
+ LimitReqZones: []version2.LimitReqZone{},
Server: version2.Server{
- ServerName: "cafe.example.com",
- StatusZone: "cafe.example.com",
- VSNamespace: "default",
- VSName: "cafe",
- SSL: &version2.SSL{
- HTTP2: true,
- Certificate: "/etc/nginx/secrets/wildcard",
- CertificateKey: "/etc/nginx/secrets/wildcard",
- },
- InternalRedirectLocations: []version2.InternalRedirectLocation{
- {
- Path: "/grpc-matches",
- Destination: "$vs_default_cafe_matches_0",
- },
- {
- Path: "/grpc-splits",
- Destination: "$vs_default_cafe_splits_0",
- },
- },
+ ServerName: "cafe.example.com",
+ Gunzip: false,
+ StatusZone: "cafe.example.com",
+ VSNamespace: "default",
+ VSName: "cafe",
+ ProxyProtocol: true,
+ ServerTokens: "off",
+ SetRealIPFrom: []string{"0.0.0.0/0"},
+ RealIPHeader: "X-Real-IP",
+ RealIPRecursive: true,
+ Snippets: []string{"# server snippet"},
+ TLSPassthrough: true,
Locations: []version2.Location{
{
- Path: "/grpc-errorpage",
- ProxyPass: "https://vs_default_cafe_grpc-app-1",
- ProxyNextUpstream: "error timeout",
- ProxyNextUpstreamTimeout: "0s",
- ProxyNextUpstreamTries: 0,
- ErrorPages: []version2.ErrorPage{{Name: "@error_page_0_0", Codes: "404 405", ResponseCode: 200}},
- ProxyInterceptErrors: true,
- ProxySSLName: "grpc-svc.default.svc",
- ProxyPassRequestHeaders: true,
- ProxySetHeaders: []version2.Header{{Name: "Host", Value: "$host"}},
- ServiceName: "grpc-svc",
- GRPCPass: "grpcs://vs_default_cafe_grpc-app-1",
- },
- {
- Path: "/internal_location_matches_0_match_0",
- Internal: true,
- ProxyPass: "https://vs_default_cafe_grpc-app-2$request_uri",
+ Path: "/tea",
+ ProxyPass: "http://vs_default_cafe_tea",
ProxyNextUpstream: "error timeout",
ProxyNextUpstreamTimeout: "0s",
ProxyNextUpstreamTries: 0,
- Rewrites: []string{"^ $request_uri break"},
- ErrorPages: []version2.ErrorPage{{Name: "@error_page_1_0", Codes: "404", ResponseCode: 200}},
- ProxyInterceptErrors: true,
- ProxySSLName: "grpc-svc2.default.svc",
+ HasKeepalive: true,
+ ProxySSLName: "tea-svc.default.svc",
ProxyPassRequestHeaders: true,
ProxySetHeaders: []version2.Header{{Name: "Host", Value: "$host"}},
- ServiceName: "grpc-svc2",
- GRPCPass: "grpcs://vs_default_cafe_grpc-app-2",
+ ServiceName: "tea-svc",
},
{
- Path: "/internal_location_matches_0_default",
- Internal: true,
- ProxyPass: "http://vs_default_cafe_tea$request_uri",
+ Path: "/tea-latest",
+ ProxyPass: "http://vs_default_cafe_tea-latest",
ProxyNextUpstream: "error timeout",
ProxyNextUpstreamTimeout: "0s",
ProxyNextUpstreamTries: 0,
- ErrorPages: []version2.ErrorPage{{Name: "@error_page_1_0", Codes: "404", ResponseCode: 200}},
- ProxyInterceptErrors: true,
+ HasKeepalive: true,
ProxySSLName: "tea-svc.default.svc",
ProxyPassRequestHeaders: true,
ProxySetHeaders: []version2.Header{{Name: "Host", Value: "$host"}},
ServiceName: "tea-svc",
},
+ // Order changes here because we generate first all the VS Routes and then all the VSR Subroutes (separated for loops)
{
- Path: "/internal_location_splits_0_split_0",
- Internal: true,
- ProxyPass: "https://vs_default_cafe_grpc-app-1$request_uri",
+ Path: "/coffee-errorpage",
+ ProxyPass: "http://vs_default_cafe_coffee",
ProxyNextUpstream: "error timeout",
ProxyNextUpstreamTimeout: "0s",
ProxyNextUpstreamTries: 0,
- HasKeepalive: false,
- ErrorPages: []version2.ErrorPage{{Name: "@error_page_2_0", Codes: "404 405", ResponseCode: 200}},
+ HasKeepalive: true,
ProxyInterceptErrors: true,
- Rewrites: []string{"^ $request_uri break"},
- ProxySSLName: "grpc-svc.default.svc",
- ProxyPassRequestHeaders: true,
- ProxySetHeaders: []version2.Header{{Name: "Host", Value: "$host"}},
- ServiceName: "grpc-svc",
- GRPCPass: "grpcs://vs_default_cafe_grpc-app-1",
+ ErrorPages: []version2.ErrorPage{
+ {
+ Name: "http://nginx.com",
+ Codes: "401 403",
+ ResponseCode: 301,
+ },
+ },
+ ProxySSLName: "coffee-svc.default.svc",
+ ProxyPassRequestHeaders: true,
+ ProxySetHeaders: []version2.Header{{Name: "Host", Value: "$host"}},
+ ServiceName: "coffee-svc",
},
{
- Path: "/internal_location_splits_0_split_1",
- Internal: true,
- ProxyPass: "https://vs_default_cafe_grpc-app-2$request_uri",
+ Path: "/coffee",
+ ProxyPass: "http://vs_default_cafe_vsr_default_coffee_coffee",
ProxyNextUpstream: "error timeout",
ProxyNextUpstreamTimeout: "0s",
ProxyNextUpstreamTries: 0,
- HasKeepalive: false,
- ErrorPages: []version2.ErrorPage{{Name: "@error_page_2_0", Codes: "404 405", ResponseCode: 200}},
- ProxyInterceptErrors: true,
- Rewrites: []string{"^ $request_uri break"},
- ProxySSLName: "grpc-svc2.default.svc",
+ HasKeepalive: true,
+ ProxySSLName: "coffee-svc.default.svc",
ProxyPassRequestHeaders: true,
ProxySetHeaders: []version2.Header{{Name: "Host", Value: "$host"}},
- ServiceName: "grpc-svc2",
- GRPCPass: "grpcs://vs_default_cafe_grpc-app-2",
+ ServiceName: "coffee-svc",
+ IsVSR: true,
+ VSRName: "coffee",
+ VSRNamespace: "default",
},
- },
- ErrorPageLocations: []version2.ErrorPageLocation{
{
- Name: "@error_page_0_0",
- DefaultType: "text/plain",
- Return: &version2.Return{Text: "All Good"},
+ Path: "/subtea",
+ ProxyPass: "http://vs_default_cafe_vsr_default_subtea_subtea",
+ ProxyNextUpstream: "error timeout",
+ ProxyNextUpstreamTimeout: "0s",
+ ProxyNextUpstreamTries: 0,
+ HasKeepalive: true,
+ ProxySSLName: "sub-tea-svc.default.svc",
+ ProxyPassRequestHeaders: true,
+ ProxySetHeaders: []version2.Header{{Name: "Host", Value: "$host"}},
+ ServiceName: "sub-tea-svc",
+ IsVSR: true,
+ VSRName: "subtea",
+ VSRNamespace: "default",
},
+
{
- Name: "@error_page_1_0",
- DefaultType: "text/plain",
- Return: &version2.Return{Text: "Original resource not found, but success!"},
+ Path: "/coffee-errorpage-subroute",
+ ProxyPass: "http://vs_default_cafe_vsr_default_subcoffee_coffee",
+ ProxyNextUpstream: "error timeout",
+ ProxyNextUpstreamTimeout: "0s",
+ ProxyNextUpstreamTries: 0,
+ HasKeepalive: true,
+ ProxyInterceptErrors: true,
+ ErrorPages: []version2.ErrorPage{
+ {
+ Name: "http://nginx.com",
+ Codes: "401 403",
+ ResponseCode: 301,
+ },
+ },
+ ProxySSLName: "coffee-svc.default.svc",
+ ProxyPassRequestHeaders: true,
+ ProxySetHeaders: []version2.Header{{Name: "Host", Value: "$host"}},
+ ServiceName: "coffee-svc",
+ IsVSR: true,
+ VSRName: "subcoffee",
+ VSRNamespace: "default",
},
{
- Name: "@error_page_2_0",
- DefaultType: "text/plain",
- Return: &version2.Return{Text: "All Good"},
+ Path: "/coffee-errorpage-subroute-defined",
+ ProxyPass: "http://vs_default_cafe_vsr_default_subcoffee_coffee",
+ ProxyNextUpstream: "error timeout",
+ ProxyNextUpstreamTimeout: "0s",
+ ProxyNextUpstreamTries: 0,
+ HasKeepalive: true,
+ ProxyInterceptErrors: true,
+ ErrorPages: []version2.ErrorPage{
+ {
+ Name: "@error_page_0_0",
+ Codes: "502 503",
+ ResponseCode: 200,
+ },
+ },
+ ProxySSLName: "coffee-svc.default.svc",
+ ProxyPassRequestHeaders: true,
+ ProxySetHeaders: []version2.Header{{Name: "Host", Value: "$host"}},
+ ServiceName: "coffee-svc",
+ IsVSR: true,
+ VSRName: "subcoffee",
+ VSRNamespace: "default",
},
},
- },
- SplitClients: []version2.SplitClient{
- {
- Source: "$request_id",
- Variable: "$vs_default_cafe_splits_0",
- Distributions: []version2.Distribution{
- {
- Weight: "90%",
- Value: "/internal_location_splits_0_split_0",
- },
- {
- Weight: "10%",
- Value: "/internal_location_splits_0_split_1",
+ ErrorPageLocations: []version2.ErrorPageLocation{
+ {
+ Name: "@error_page_0_0",
+ DefaultType: "text/plain",
+ Return: &version2.Return{
+ Text: "All Good",
},
},
},
},
}
- expectedWarnings := Warnings{
- virtualServerEx.VirtualServer: {
- `The error page configuration for the upstream grpc-app-1 is ignored for status code(s) [404 405], which cannot be used for GRPC upstreams.`,
- `The error page configuration for the upstream grpc-app-2 is ignored for status code(s) [404], which cannot be used for GRPC upstreams.`,
- `The error page configuration for the upstream grpc-app-1 is ignored for status code(s) [404 405], which cannot be used for GRPC upstreams.`,
- `The error page configuration for the upstream grpc-app-2 is ignored for status code(s) [404 405], which cannot be used for GRPC upstreams.`,
- },
- }
- isPlus := false
- isResolverConfigured := false
- isWildcardEnabled := true
- vsc := newVirtualServerConfigurator(&baseCfgParams, isPlus, isResolverConfigured, &StaticConfigParams{}, isWildcardEnabled)
- result, warnings := vsc.GenerateVirtualServerConfig(&virtualServerEx, nil, nil)
- if diff := cmp.Diff(expected, result); diff != "" {
- t.Errorf("TestGenerateVirtualServerConfigGrpcErrorPageWarning() mismatch (-want +got):\n%s", diff)
+ got, warnings := vsc.GenerateVirtualServerConfig(&virtualServerExWithNoGunzip, nil, nil)
+ if len(warnings) > 0 {
+ t.Fatalf("want no warnings, got: %v", vsc.warnings)
}
-
- if !reflect.DeepEqual(vsc.warnings, expectedWarnings) {
- t.Errorf("GenerateVirtualServerConfig() returned warnings of \n%v but expected \n%v", warnings, expectedWarnings)
+ if !cmp.Equal(want, got) {
+ t.Error(cmp.Diff(want, got))
}
}
-func TestGenerateVirtualServerConfigWithSpiffeCerts(t *testing.T) {
+func TestGenerateVirtualServerConfig(t *testing.T) {
t.Parallel()
virtualServerEx := VirtualServerEx{
VirtualServer: &conf_v1.VirtualServer{
@@ -1181,6 +987,17 @@ func TestGenerateVirtualServerConfigWithSpiffeCerts(t *testing.T) {
Service: "tea-svc",
Port: 80,
},
+ {
+ Name: "tea-latest",
+ Service: "tea-svc",
+ Subselector: map[string]string{"version": "v1"},
+ Port: 80,
+ },
+ {
+ Name: "coffee",
+ Service: "coffee-svc",
+ Port: 80,
+ },
},
Routes: []conf_v1.Route{
{
@@ -1189,6 +1006,52 @@ func TestGenerateVirtualServerConfigWithSpiffeCerts(t *testing.T) {
Pass: "tea",
},
},
+ {
+ Path: "/tea-latest",
+ Action: &conf_v1.Action{
+ Pass: "tea-latest",
+ },
+ },
+ {
+ Path: "/coffee",
+ Route: "default/coffee",
+ },
+ {
+ Path: "/subtea",
+ Route: "default/subtea",
+ },
+ {
+ Path: "/coffee-errorpage",
+ Action: &conf_v1.Action{
+ Pass: "coffee",
+ },
+ ErrorPages: []conf_v1.ErrorPage{
+ {
+ Codes: []int{401, 403},
+ Redirect: &conf_v1.ErrorPageRedirect{
+ ActionRedirect: conf_v1.ActionRedirect{
+ URL: "http://nginx.com",
+ Code: 301,
+ },
+ },
+ },
+ },
+ },
+ {
+ Path: "/coffee-errorpage-subroute",
+ Route: "default/subcoffee",
+ ErrorPages: []conf_v1.ErrorPage{
+ {
+ Codes: []int{401, 403},
+ Redirect: &conf_v1.ErrorPageRedirect{
+ ActionRedirect: conf_v1.ActionRedirect{
+ URL: "http://nginx.com",
+ Code: 301,
+ },
+ },
+ },
+ },
+ },
},
},
},
@@ -1196,117 +1059,107 @@ func TestGenerateVirtualServerConfigWithSpiffeCerts(t *testing.T) {
"default/tea-svc:80": {
"10.0.0.20:80",
},
+ "default/tea-svc_version=v1:80": {
+ "10.0.0.30:80",
+ },
+ "default/coffee-svc:80": {
+ "10.0.0.40:80",
+ },
+ "default/sub-tea-svc_version=v1:80": {
+ "10.0.0.50:80",
+ },
},
- }
-
- baseCfgParams := ConfigParams{
- ServerTokens: "off",
- Keepalive: 16,
- ServerSnippets: []string{"# server snippet"},
- ProxyProtocol: true,
- SetRealIPFrom: []string{"0.0.0.0/0"},
- RealIPHeader: "X-Real-IP",
- RealIPRecursive: true,
- }
-
- expected := version2.VirtualServerConfig{
- Upstreams: []version2.Upstream{
+ VirtualServerRoutes: []*conf_v1.VirtualServerRoute{
{
- UpstreamLabels: version2.UpstreamLabels{
- Service: "tea-svc",
- ResourceType: "virtualserver",
- ResourceName: "cafe",
- ResourceNamespace: "default",
+ ObjectMeta: meta_v1.ObjectMeta{
+ Name: "coffee",
+ Namespace: "default",
},
- Name: "vs_default_cafe_tea",
- Servers: []version2.UpstreamServer{
- {
- Address: "10.0.0.20:80",
+ Spec: conf_v1.VirtualServerRouteSpec{
+ Host: "cafe.example.com",
+ Upstreams: []conf_v1.Upstream{
+ {
+ Name: "coffee",
+ Service: "coffee-svc",
+ Port: 80,
+ },
+ },
+ Subroutes: []conf_v1.Route{
+ {
+ Path: "/coffee",
+ Action: &conf_v1.Action{
+ Pass: "coffee",
+ },
+ },
},
},
- Keepalive: 16,
},
- },
- HTTPSnippets: []string{},
- LimitReqZones: []version2.LimitReqZone{},
- Server: version2.Server{
- ServerName: "cafe.example.com",
- StatusZone: "cafe.example.com",
- VSNamespace: "default",
- VSName: "cafe",
- ProxyProtocol: true,
- ServerTokens: "off",
- SetRealIPFrom: []string{"0.0.0.0/0"},
- RealIPHeader: "X-Real-IP",
- RealIPRecursive: true,
- Snippets: []string{"# server snippet"},
- TLSPassthrough: true,
- Locations: []version2.Location{
- {
- Path: "/tea",
- ProxyPass: "https://vs_default_cafe_tea",
- ProxyNextUpstream: "error timeout",
- ProxyNextUpstreamTimeout: "0s",
- ProxyNextUpstreamTries: 0,
- HasKeepalive: true,
- ProxySSLName: "tea-svc.default.svc",
- ProxyPassRequestHeaders: true,
- ProxySetHeaders: []version2.Header{{Name: "Host", Value: "$host"}},
- ServiceName: "tea-svc",
+ {
+ ObjectMeta: meta_v1.ObjectMeta{
+ Name: "subtea",
+ Namespace: "default",
},
- },
- },
- SpiffeClientCerts: true,
- }
-
- isPlus := false
- isResolverConfigured := false
- staticConfigParams := &StaticConfigParams{TLSPassthrough: true, NginxServiceMesh: true}
- isWildcardEnabled := false
- vsc := newVirtualServerConfigurator(&baseCfgParams, isPlus, isResolverConfigured, staticConfigParams, isWildcardEnabled)
-
- result, warnings := vsc.GenerateVirtualServerConfig(&virtualServerEx, nil, nil)
- if diff := cmp.Diff(expected, result); diff != "" {
- t.Errorf("GenerateVirtualServerConfig() mismatch (-want +got):\n%s", diff)
- }
-
- if len(warnings) != 0 {
- t.Errorf("GenerateVirtualServerConfig returned warnings: %v", vsc.warnings)
- }
-}
-
-func TestGenerateVirtualServerConfigWithInternalRoutes(t *testing.T) {
- t.Parallel()
- virtualServerEx := VirtualServerEx{
- VirtualServer: &conf_v1.VirtualServer{
- ObjectMeta: meta_v1.ObjectMeta{
- Name: "cafe",
- Namespace: "default",
- },
- Spec: conf_v1.VirtualServerSpec{
- Host: "cafe.example.com",
- Upstreams: []conf_v1.Upstream{
- {
- Name: "tea",
- Service: "tea-svc",
- Port: 80,
- TLS: conf_v1.UpstreamTLS{Enable: false},
+ Spec: conf_v1.VirtualServerRouteSpec{
+ Host: "cafe.example.com",
+ Upstreams: []conf_v1.Upstream{
+ {
+ Name: "subtea",
+ Service: "sub-tea-svc",
+ Port: 80,
+ Subselector: map[string]string{"version": "v1"},
+ },
},
- },
- Routes: []conf_v1.Route{
- {
- Path: "/",
- Action: &conf_v1.Action{
- Pass: "tea",
+ Subroutes: []conf_v1.Route{
+ {
+ Path: "/subtea",
+ Action: &conf_v1.Action{
+ Pass: "subtea",
+ },
},
},
},
- InternalRoute: true,
},
- },
- Endpoints: map[string][]string{
- "default/tea-svc:80": {
- "10.0.0.20:80",
+ {
+ ObjectMeta: meta_v1.ObjectMeta{
+ Name: "subcoffee",
+ Namespace: "default",
+ },
+ Spec: conf_v1.VirtualServerRouteSpec{
+ Host: "cafe.example.com",
+ Upstreams: []conf_v1.Upstream{
+ {
+ Name: "coffee",
+ Service: "coffee-svc",
+ Port: 80,
+ },
+ },
+ Subroutes: []conf_v1.Route{
+ {
+ Path: "/coffee-errorpage-subroute",
+ Action: &conf_v1.Action{
+ Pass: "coffee",
+ },
+ },
+ {
+ Path: "/coffee-errorpage-subroute-defined",
+ Action: &conf_v1.Action{
+ Pass: "coffee",
+ },
+ ErrorPages: []conf_v1.ErrorPage{
+ {
+ Codes: []int{502, 503},
+ Return: &conf_v1.ErrorPageReturn{
+ ActionReturn: conf_v1.ActionReturn{
+ Code: 200,
+ Type: "text/plain",
+ Body: "All Good",
+ },
+ },
+ },
+ },
+ },
+ },
+ },
},
},
}
@@ -1338,6 +1191,81 @@ func TestGenerateVirtualServerConfigWithInternalRoutes(t *testing.T) {
},
Keepalive: 16,
},
+ {
+ UpstreamLabels: version2.UpstreamLabels{
+ Service: "tea-svc",
+ ResourceType: "virtualserver",
+ ResourceName: "cafe",
+ ResourceNamespace: "default",
+ },
+ Name: "vs_default_cafe_tea-latest",
+ Servers: []version2.UpstreamServer{
+ {
+ Address: "10.0.0.30:80",
+ },
+ },
+ Keepalive: 16,
+ },
+ {
+ UpstreamLabels: version2.UpstreamLabels{
+ Service: "coffee-svc",
+ ResourceType: "virtualserver",
+ ResourceName: "cafe",
+ ResourceNamespace: "default",
+ },
+ Name: "vs_default_cafe_coffee",
+ Servers: []version2.UpstreamServer{
+ {
+ Address: "10.0.0.40:80",
+ },
+ },
+ Keepalive: 16,
+ },
+ {
+ UpstreamLabels: version2.UpstreamLabels{
+ Service: "coffee-svc",
+ ResourceType: "virtualserverroute",
+ ResourceName: "coffee",
+ ResourceNamespace: "default",
+ },
+ Name: "vs_default_cafe_vsr_default_coffee_coffee",
+ Servers: []version2.UpstreamServer{
+ {
+ Address: "10.0.0.40:80",
+ },
+ },
+ Keepalive: 16,
+ },
+ {
+ UpstreamLabels: version2.UpstreamLabels{
+ Service: "sub-tea-svc",
+ ResourceType: "virtualserverroute",
+ ResourceName: "subtea",
+ ResourceNamespace: "default",
+ },
+ Name: "vs_default_cafe_vsr_default_subtea_subtea",
+ Servers: []version2.UpstreamServer{
+ {
+ Address: "10.0.0.50:80",
+ },
+ },
+ Keepalive: 16,
+ },
+ {
+ UpstreamLabels: version2.UpstreamLabels{
+ Service: "coffee-svc",
+ ResourceType: "virtualserverroute",
+ ResourceName: "subcoffee",
+ ResourceNamespace: "default",
+ },
+ Name: "vs_default_cafe_vsr_default_subcoffee_coffee",
+ Servers: []version2.UpstreamServer{
+ {
+ Address: "10.0.0.40:80",
+ },
+ },
+ Keepalive: 16,
+ },
},
HTTPSnippets: []string{},
LimitReqZones: []version2.LimitReqZone{},
@@ -1355,7 +1283,7 @@ func TestGenerateVirtualServerConfigWithInternalRoutes(t *testing.T) {
TLSPassthrough: true,
Locations: []version2.Location{
{
- Path: "/",
+ Path: "/tea",
ProxyPass: "http://vs_default_cafe_tea",
ProxyNextUpstream: "error timeout",
ProxyNextUpstreamTimeout: "0s",
@@ -1366,73 +1294,199 @@ func TestGenerateVirtualServerConfigWithInternalRoutes(t *testing.T) {
ProxySetHeaders: []version2.Header{{Name: "Host", Value: "$host"}},
ServiceName: "tea-svc",
},
- },
- },
- SpiffeCerts: true,
- SpiffeClientCerts: false,
- }
-
- isPlus := false
- isResolverConfigured := false
- staticConfigParams := &StaticConfigParams{TLSPassthrough: true, NginxServiceMesh: true, EnableInternalRoutes: true}
- isWildcardEnabled := false
- vsc := newVirtualServerConfigurator(&baseCfgParams, isPlus, isResolverConfigured, staticConfigParams, isWildcardEnabled)
-
- result, warnings := vsc.GenerateVirtualServerConfig(&virtualServerEx, nil, nil)
- if diff := cmp.Diff(expected, result); diff != "" {
- t.Errorf("GenerateVirtualServerConfig() mismatch (-want +got):\n%s", diff)
- }
-
- if len(warnings) != 0 {
- t.Errorf("GenerateVirtualServerConfig returned warnings: %v", vsc.warnings)
- }
-}
-
-func TestGenerateVirtualServerConfigWithInternalRoutesWarning(t *testing.T) {
- t.Parallel()
- virtualServerEx := VirtualServerEx{
- VirtualServer: &conf_v1.VirtualServer{
- ObjectMeta: meta_v1.ObjectMeta{
- Name: "cafe",
- Namespace: "default",
- },
- Spec: conf_v1.VirtualServerSpec{
- Host: "cafe.example.com",
- Upstreams: []conf_v1.Upstream{
- {
- Name: "tea",
- Service: "tea-svc",
- Port: 80,
- TLS: conf_v1.UpstreamTLS{Enable: false},
- },
+ {
+ Path: "/tea-latest",
+ ProxyPass: "http://vs_default_cafe_tea-latest",
+ ProxyNextUpstream: "error timeout",
+ ProxyNextUpstreamTimeout: "0s",
+ ProxyNextUpstreamTries: 0,
+ HasKeepalive: true,
+ ProxySSLName: "tea-svc.default.svc",
+ ProxyPassRequestHeaders: true,
+ ProxySetHeaders: []version2.Header{{Name: "Host", Value: "$host"}},
+ ServiceName: "tea-svc",
},
- Routes: []conf_v1.Route{
+ // Order changes here because we generate first all the VS Routes and then all the VSR Subroutes (separated for loops)
+ {
+ Path: "/coffee-errorpage",
+ ProxyPass: "http://vs_default_cafe_coffee",
+ ProxyNextUpstream: "error timeout",
+ ProxyNextUpstreamTimeout: "0s",
+ ProxyNextUpstreamTries: 0,
+ HasKeepalive: true,
+ ProxyInterceptErrors: true,
+ ErrorPages: []version2.ErrorPage{
+ {
+ Name: "http://nginx.com",
+ Codes: "401 403",
+ ResponseCode: 301,
+ },
+ },
+ ProxySSLName: "coffee-svc.default.svc",
+ ProxyPassRequestHeaders: true,
+ ProxySetHeaders: []version2.Header{{Name: "Host", Value: "$host"}},
+ ServiceName: "coffee-svc",
+ },
+ {
+ Path: "/coffee",
+ ProxyPass: "http://vs_default_cafe_vsr_default_coffee_coffee",
+ ProxyNextUpstream: "error timeout",
+ ProxyNextUpstreamTimeout: "0s",
+ ProxyNextUpstreamTries: 0,
+ HasKeepalive: true,
+ ProxySSLName: "coffee-svc.default.svc",
+ ProxyPassRequestHeaders: true,
+ ProxySetHeaders: []version2.Header{{Name: "Host", Value: "$host"}},
+ ServiceName: "coffee-svc",
+ IsVSR: true,
+ VSRName: "coffee",
+ VSRNamespace: "default",
+ },
+ {
+ Path: "/subtea",
+ ProxyPass: "http://vs_default_cafe_vsr_default_subtea_subtea",
+ ProxyNextUpstream: "error timeout",
+ ProxyNextUpstreamTimeout: "0s",
+ ProxyNextUpstreamTries: 0,
+ HasKeepalive: true,
+ ProxySSLName: "sub-tea-svc.default.svc",
+ ProxyPassRequestHeaders: true,
+ ProxySetHeaders: []version2.Header{{Name: "Host", Value: "$host"}},
+ ServiceName: "sub-tea-svc",
+ IsVSR: true,
+ VSRName: "subtea",
+ VSRNamespace: "default",
+ },
+
+ {
+ Path: "/coffee-errorpage-subroute",
+ ProxyPass: "http://vs_default_cafe_vsr_default_subcoffee_coffee",
+ ProxyNextUpstream: "error timeout",
+ ProxyNextUpstreamTimeout: "0s",
+ ProxyNextUpstreamTries: 0,
+ HasKeepalive: true,
+ ProxyInterceptErrors: true,
+ ErrorPages: []version2.ErrorPage{
+ {
+ Name: "http://nginx.com",
+ Codes: "401 403",
+ ResponseCode: 301,
+ },
+ },
+ ProxySSLName: "coffee-svc.default.svc",
+ ProxyPassRequestHeaders: true,
+ ProxySetHeaders: []version2.Header{{Name: "Host", Value: "$host"}},
+ ServiceName: "coffee-svc",
+ IsVSR: true,
+ VSRName: "subcoffee",
+ VSRNamespace: "default",
+ },
+ {
+ Path: "/coffee-errorpage-subroute-defined",
+ ProxyPass: "http://vs_default_cafe_vsr_default_subcoffee_coffee",
+ ProxyNextUpstream: "error timeout",
+ ProxyNextUpstreamTimeout: "0s",
+ ProxyNextUpstreamTries: 0,
+ HasKeepalive: true,
+ ProxyInterceptErrors: true,
+ ErrorPages: []version2.ErrorPage{
+ {
+ Name: "@error_page_0_0",
+ Codes: "502 503",
+ ResponseCode: 200,
+ },
+ },
+ ProxySSLName: "coffee-svc.default.svc",
+ ProxyPassRequestHeaders: true,
+ ProxySetHeaders: []version2.Header{{Name: "Host", Value: "$host"}},
+ ServiceName: "coffee-svc",
+ IsVSR: true,
+ VSRName: "subcoffee",
+ VSRNamespace: "default",
+ },
+ },
+ ErrorPageLocations: []version2.ErrorPageLocation{
+ {
+ Name: "@error_page_0_0",
+ DefaultType: "text/plain",
+ Return: &version2.Return{
+ Text: "All Good",
+ },
+ },
+ },
+ },
+ }
+
+ isPlus := false
+ isResolverConfigured := false
+ isWildcardEnabled := false
+ vsc := newVirtualServerConfigurator(
+ &baseCfgParams,
+ isPlus,
+ isResolverConfigured,
+ &StaticConfigParams{TLSPassthrough: true},
+ isWildcardEnabled,
+ )
+
+ result, warnings := vsc.GenerateVirtualServerConfig(&virtualServerEx, nil, nil)
+ if diff := cmp.Diff(expected, result); diff != "" {
+ t.Errorf("GenerateVirtualServerConfig() mismatch (-want +got):\n%s", diff)
+ }
+
+ if len(warnings) != 0 {
+ t.Errorf("GenerateVirtualServerConfig returned warnings: %v", vsc.warnings)
+ }
+}
+
+func TestGenerateVirtualServerConfigIPV6Disabled(t *testing.T) {
+ t.Parallel()
+ virtualServerEx := VirtualServerEx{
+ VirtualServer: &conf_v1.VirtualServer{
+ ObjectMeta: meta_v1.ObjectMeta{
+ Name: "cafe",
+ Namespace: "default",
+ },
+ Spec: conf_v1.VirtualServerSpec{
+ Host: "cafe.example.com",
+ Upstreams: []conf_v1.Upstream{
{
- Path: "/",
+ Name: "tea",
+ Service: "tea-svc",
+ Port: 80,
+ },
+ {
+ Name: "coffee",
+ Service: "coffee-svc",
+ Port: 80,
+ },
+ },
+ Routes: []conf_v1.Route{
+ {
+ Path: "/tea",
Action: &conf_v1.Action{
Pass: "tea",
},
},
+ {
+ Path: "/coffee",
+ Action: &conf_v1.Action{
+ Pass: "coffee",
+ },
+ },
},
- InternalRoute: true,
},
},
Endpoints: map[string][]string{
"default/tea-svc:80": {
"10.0.0.20:80",
},
+ "default/coffee-svc:80": {
+ "10.0.0.40:80",
+ },
},
}
- baseCfgParams := ConfigParams{
- ServerTokens: "off",
- Keepalive: 16,
- ServerSnippets: []string{"# server snippet"},
- ProxyProtocol: true,
- SetRealIPFrom: []string{"0.0.0.0/0"},
- RealIPHeader: "X-Real-IP",
- RealIPRecursive: true,
- }
+ baseCfgParams := ConfigParams{}
expected := version2.VirtualServerConfig{
Upstreams: []version2.Upstream{
@@ -1449,59 +1503,77 @@ func TestGenerateVirtualServerConfigWithInternalRoutesWarning(t *testing.T) {
Address: "10.0.0.20:80",
},
},
- Keepalive: 16,
+ },
+ {
+ UpstreamLabels: version2.UpstreamLabels{
+ Service: "coffee-svc",
+ ResourceType: "virtualserver",
+ ResourceName: "cafe",
+ ResourceNamespace: "default",
+ },
+ Name: "vs_default_cafe_coffee",
+ Servers: []version2.UpstreamServer{
+ {
+ Address: "10.0.0.40:80",
+ },
+ },
},
},
HTTPSnippets: []string{},
LimitReqZones: []version2.LimitReqZone{},
Server: version2.Server{
- ServerName: "cafe.example.com",
- StatusZone: "cafe.example.com",
- VSNamespace: "default",
- VSName: "cafe",
- ProxyProtocol: true,
- ServerTokens: "off",
- SetRealIPFrom: []string{"0.0.0.0/0"},
- RealIPHeader: "X-Real-IP",
- RealIPRecursive: true,
- Snippets: []string{"# server snippet"},
- TLSPassthrough: true,
+ ServerName: "cafe.example.com",
+ StatusZone: "cafe.example.com",
+ VSNamespace: "default",
+ VSName: "cafe",
+ DisableIPV6: true,
Locations: []version2.Location{
{
- Path: "/",
+ Path: "/tea",
ProxyPass: "http://vs_default_cafe_tea",
ProxyNextUpstream: "error timeout",
ProxyNextUpstreamTimeout: "0s",
- ProxyNextUpstreamTries: 0,
- HasKeepalive: true,
ProxySSLName: "tea-svc.default.svc",
ProxyPassRequestHeaders: true,
ProxySetHeaders: []version2.Header{{Name: "Host", Value: "$host"}},
ServiceName: "tea-svc",
},
+ {
+ Path: "/coffee",
+ ProxyPass: "http://vs_default_cafe_coffee",
+ ProxyNextUpstream: "error timeout",
+ ProxyNextUpstreamTimeout: "0s",
+ ProxySSLName: "coffee-svc.default.svc",
+ ProxyPassRequestHeaders: true,
+ ProxySetHeaders: []version2.Header{{Name: "Host", Value: "$host"}},
+ ServiceName: "coffee-svc",
+ },
},
},
- SpiffeCerts: true,
- SpiffeClientCerts: true,
}
isPlus := false
isResolverConfigured := false
- staticConfigParams := &StaticConfigParams{TLSPassthrough: true, NginxServiceMesh: true, EnableInternalRoutes: false}
isWildcardEnabled := false
- vsc := newVirtualServerConfigurator(&baseCfgParams, isPlus, isResolverConfigured, staticConfigParams, isWildcardEnabled)
+ vsc := newVirtualServerConfigurator(
+ &baseCfgParams,
+ isPlus,
+ isResolverConfigured,
+ &StaticConfigParams{DisableIPV6: true},
+ isWildcardEnabled,
+ )
result, warnings := vsc.GenerateVirtualServerConfig(&virtualServerEx, nil, nil)
- if diff := cmp.Diff(expected, result); diff == "" {
- t.Errorf("GenerateVirtualServerConfig() should not configure internal route")
+ if diff := cmp.Diff(expected, result); diff != "" {
+ t.Errorf("GenerateVirtualServerConfig() mismatch (-want +got):\n%s", diff)
}
- if len(warnings) != 1 {
- t.Errorf("GenerateVirtualServerConfig should return warning to enable internal routing")
+ if len(warnings) != 0 {
+ t.Errorf("GenerateVirtualServerConfig returned warnings: %v", vsc.warnings)
}
}
-func TestGenerateVirtualServerConfigForVirtualServerWithSplits(t *testing.T) {
+func TestGenerateVirtualServerConfigGrpcErrorPageWarning(t *testing.T) {
t.Parallel()
virtualServerEx := VirtualServerEx{
VirtualServer: &conf_v1.VirtualServer{
@@ -1511,91 +1583,108 @@ func TestGenerateVirtualServerConfigForVirtualServerWithSplits(t *testing.T) {
},
Spec: conf_v1.VirtualServerSpec{
Host: "cafe.example.com",
+ TLS: &conf_v1.TLS{
+ Secret: "",
+ },
Upstreams: []conf_v1.Upstream{
{
- Name: "tea-v1",
- Service: "tea-svc-v1",
- Port: 80,
+ Name: "grpc-app-1",
+ Service: "grpc-svc",
+ Port: 50051,
+ Type: "grpc",
+ TLS: conf_v1.UpstreamTLS{
+ Enable: true,
+ },
},
{
- Name: "tea-v2",
- Service: "tea-svc-v2",
+ Name: "grpc-app-2",
+ Service: "grpc-svc2",
+ Port: 50052,
+ Type: "grpc",
+ TLS: conf_v1.UpstreamTLS{
+ Enable: true,
+ },
+ },
+ {
+ Name: "tea",
+ Service: "tea-svc",
Port: 80,
},
},
Routes: []conf_v1.Route{
{
- Path: "/tea",
- Splits: []conf_v1.Split{
+ Path: "/grpc-errorpage",
+ Action: &conf_v1.Action{
+ Pass: "grpc-app-1",
+ },
+ ErrorPages: []conf_v1.ErrorPage{
{
- Weight: 90,
- Action: &conf_v1.Action{
- Pass: "tea-v1",
+ Codes: []int{404, 405},
+ Return: &conf_v1.ErrorPageReturn{
+ ActionReturn: conf_v1.ActionReturn{
+ Code: 200,
+ Type: "text/plain",
+ Body: "All Good",
+ },
},
},
+ },
+ },
+ {
+ Path: "/grpc-matches",
+ Matches: []conf_v1.Match{
{
- Weight: 10,
+ Conditions: []conf_v1.Condition{
+ {
+ Variable: "$request_method",
+ Value: "POST",
+ },
+ },
Action: &conf_v1.Action{
- Pass: "tea-v2",
+ Pass: "grpc-app-2",
},
},
},
- },
- {
- Path: "/coffee",
- Route: "default/coffee",
- },
- },
- },
- },
- Endpoints: map[string][]string{
- "default/tea-svc-v1:80": {
- "10.0.0.20:80",
- },
- "default/tea-svc-v2:80": {
- "10.0.0.21:80",
- },
- "default/coffee-svc-v1:80": {
- "10.0.0.30:80",
- },
- "default/coffee-svc-v2:80": {
- "10.0.0.31:80",
- },
- },
- VirtualServerRoutes: []*conf_v1.VirtualServerRoute{
- {
- ObjectMeta: meta_v1.ObjectMeta{
- Name: "coffee",
- Namespace: "default",
- },
- Spec: conf_v1.VirtualServerRouteSpec{
- Host: "cafe.example.com",
- Upstreams: []conf_v1.Upstream{
- {
- Name: "coffee-v1",
- Service: "coffee-svc-v1",
- Port: 80,
+ Action: &conf_v1.Action{
+ Pass: "tea",
},
- {
- Name: "coffee-v2",
- Service: "coffee-svc-v2",
- Port: 80,
+ ErrorPages: []conf_v1.ErrorPage{
+ {
+ Codes: []int{404},
+ Return: &conf_v1.ErrorPageReturn{
+ ActionReturn: conf_v1.ActionReturn{
+ Code: 200,
+ Type: "text/plain",
+ Body: "Original resource not found, but success!",
+ },
+ },
+ },
},
},
- Subroutes: []conf_v1.Route{
- {
- Path: "/coffee",
- Splits: []conf_v1.Split{
- {
- Weight: 40,
- Action: &conf_v1.Action{
- Pass: "coffee-v1",
- },
+ {
+ Path: "/grpc-splits",
+ Splits: []conf_v1.Split{
+ {
+ Weight: 90,
+ Action: &conf_v1.Action{
+ Pass: "grpc-app-1",
},
- {
- Weight: 60,
- Action: &conf_v1.Action{
- Pass: "coffee-v2",
+ },
+ {
+ Weight: 10,
+ Action: &conf_v1.Action{
+ Pass: "grpc-app-2",
+ },
+ },
+ },
+ ErrorPages: []conf_v1.ErrorPage{
+ {
+ Codes: []int{404, 405},
+ Return: &conf_v1.ErrorPageReturn{
+ ActionReturn: conf_v1.ActionReturn{
+ Code: 200,
+ Type: "text/plain",
+ Body: "All Good",
},
},
},
@@ -1604,20 +1693,27 @@ func TestGenerateVirtualServerConfigForVirtualServerWithSplits(t *testing.T) {
},
},
},
+ Endpoints: map[string][]string{
+ "default/grpc-svc:50051": {
+ "10.0.0.20:80",
+ },
+ },
}
- baseCfgParams := ConfigParams{}
+ baseCfgParams := ConfigParams{
+ HTTP2: true,
+ }
expected := version2.VirtualServerConfig{
Upstreams: []version2.Upstream{
{
- Name: "vs_default_cafe_tea-v1",
UpstreamLabels: version2.UpstreamLabels{
- Service: "tea-svc-v1",
+ Service: "grpc-svc",
ResourceType: "virtualserver",
ResourceName: "cafe",
ResourceNamespace: "default",
},
+ Name: "vs_default_cafe_grpc-app-1",
Servers: []version2.UpstreamServer{
{
Address: "10.0.0.20:80",
@@ -1625,170 +1721,225 @@ func TestGenerateVirtualServerConfigForVirtualServerWithSplits(t *testing.T) {
},
},
{
- Name: "vs_default_cafe_tea-v2",
+ Name: "vs_default_cafe_grpc-app-2",
UpstreamLabels: version2.UpstreamLabels{
- Service: "tea-svc-v2",
+ Service: "grpc-svc2",
ResourceType: "virtualserver",
ResourceName: "cafe",
ResourceNamespace: "default",
},
Servers: []version2.UpstreamServer{
{
- Address: "10.0.0.21:80",
- },
- },
- },
- {
- Name: "vs_default_cafe_vsr_default_coffee_coffee-v1",
- UpstreamLabels: version2.UpstreamLabels{
- Service: "coffee-svc-v1",
- ResourceType: "virtualserverroute",
- ResourceName: "coffee",
- ResourceNamespace: "default",
- },
- Servers: []version2.UpstreamServer{
- {
- Address: "10.0.0.30:80",
+ Address: "unix:/var/lib/nginx/nginx-502-server.sock",
},
},
},
{
- Name: "vs_default_cafe_vsr_default_coffee_coffee-v2",
+ Name: "vs_default_cafe_tea",
UpstreamLabels: version2.UpstreamLabels{
- Service: "coffee-svc-v2",
- ResourceType: "virtualserverroute",
- ResourceName: "coffee",
+ Service: "tea-svc",
+ ResourceType: "virtualserver",
+ ResourceName: "cafe",
ResourceNamespace: "default",
},
Servers: []version2.UpstreamServer{
{
- Address: "10.0.0.31:80",
+ Address: "unix:/var/lib/nginx/nginx-502-server.sock",
},
},
},
},
- SplitClients: []version2.SplitClient{
+ HTTPSnippets: []string{},
+ LimitReqZones: []version2.LimitReqZone{},
+ Maps: []version2.Map{
{
- Source: "$request_id",
- Variable: "$vs_default_cafe_splits_0",
- Distributions: []version2.Distribution{
+ Source: "$request_method",
+ Variable: "$vs_default_cafe_matches_0_match_0_cond_0",
+ Parameters: []version2.Parameter{
{
- Weight: "90%",
- Value: "/internal_location_splits_0_split_0",
+ Value: `"POST"`,
+ Result: "1",
},
{
- Weight: "10%",
- Value: "/internal_location_splits_0_split_1",
+ Value: "default",
+ Result: "0",
},
},
},
{
- Source: "$request_id",
- Variable: "$vs_default_cafe_splits_1",
- Distributions: []version2.Distribution{
+ Source: "$vs_default_cafe_matches_0_match_0_cond_0",
+ Variable: "$vs_default_cafe_matches_0",
+ Parameters: []version2.Parameter{
{
- Weight: "40%",
- Value: "/internal_location_splits_1_split_0",
+ Value: "~^1",
+ Result: "/internal_location_matches_0_match_0",
},
{
- Weight: "60%",
- Value: "/internal_location_splits_1_split_1",
+ Value: "default",
+ Result: "/internal_location_matches_0_default",
},
},
},
},
- HTTPSnippets: []string{},
- LimitReqZones: []version2.LimitReqZone{},
Server: version2.Server{
ServerName: "cafe.example.com",
StatusZone: "cafe.example.com",
VSNamespace: "default",
VSName: "cafe",
+ SSL: &version2.SSL{
+ HTTP2: true,
+ Certificate: "/etc/nginx/secrets/wildcard",
+ CertificateKey: "/etc/nginx/secrets/wildcard",
+ },
InternalRedirectLocations: []version2.InternalRedirectLocation{
{
- Path: "/tea",
- Destination: "$vs_default_cafe_splits_0",
+ Path: "/grpc-matches",
+ Destination: "$vs_default_cafe_matches_0",
},
{
- Path: "/coffee",
- Destination: "$vs_default_cafe_splits_1",
+ Path: "/grpc-splits",
+ Destination: "$vs_default_cafe_splits_0",
},
},
Locations: []version2.Location{
{
- Path: "/internal_location_splits_0_split_0",
- ProxyPass: "http://vs_default_cafe_tea-v1$request_uri",
+ Path: "/grpc-errorpage",
+ ProxyPass: "https://vs_default_cafe_grpc-app-1",
ProxyNextUpstream: "error timeout",
ProxyNextUpstreamTimeout: "0s",
ProxyNextUpstreamTries: 0,
- Internal: true,
- ProxySSLName: "tea-svc-v1.default.svc",
+ ErrorPages: []version2.ErrorPage{{Name: "@error_page_0_0", Codes: "404 405", ResponseCode: 200}},
+ ProxyInterceptErrors: true,
+ ProxySSLName: "grpc-svc.default.svc",
ProxyPassRequestHeaders: true,
ProxySetHeaders: []version2.Header{{Name: "Host", Value: "$host"}},
- ServiceName: "tea-svc-v1",
+ ServiceName: "grpc-svc",
+ GRPCPass: "grpcs://vs_default_cafe_grpc-app-1",
},
{
- Path: "/internal_location_splits_0_split_1",
- ProxyPass: "http://vs_default_cafe_tea-v2$request_uri",
+ Path: "/internal_location_matches_0_match_0",
+ Internal: true,
+ ProxyPass: "https://vs_default_cafe_grpc-app-2$request_uri",
ProxyNextUpstream: "error timeout",
ProxyNextUpstreamTimeout: "0s",
ProxyNextUpstreamTries: 0,
- Internal: true,
- ProxySSLName: "tea-svc-v2.default.svc",
+ Rewrites: []string{"^ $request_uri break"},
+ ErrorPages: []version2.ErrorPage{{Name: "@error_page_1_0", Codes: "404", ResponseCode: 200}},
+ ProxyInterceptErrors: true,
+ ProxySSLName: "grpc-svc2.default.svc",
ProxyPassRequestHeaders: true,
ProxySetHeaders: []version2.Header{{Name: "Host", Value: "$host"}},
- ServiceName: "tea-svc-v2",
+ ServiceName: "grpc-svc2",
+ GRPCPass: "grpcs://vs_default_cafe_grpc-app-2",
},
{
- Path: "/internal_location_splits_1_split_0",
- ProxyPass: "http://vs_default_cafe_vsr_default_coffee_coffee-v1$request_uri",
+ Path: "/internal_location_matches_0_default",
+ Internal: true,
+ ProxyPass: "http://vs_default_cafe_tea$request_uri",
ProxyNextUpstream: "error timeout",
ProxyNextUpstreamTimeout: "0s",
ProxyNextUpstreamTries: 0,
- Internal: true,
- ProxySSLName: "coffee-svc-v1.default.svc",
- ProxyPassRequestHeaders: true,
+ ErrorPages: []version2.ErrorPage{{Name: "@error_page_1_0", Codes: "404", ResponseCode: 200}},
+ ProxyInterceptErrors: true,
+ ProxySSLName: "tea-svc.default.svc",
+ ProxyPassRequestHeaders: true,
ProxySetHeaders: []version2.Header{{Name: "Host", Value: "$host"}},
- ServiceName: "coffee-svc-v1",
- IsVSR: true,
- VSRName: "coffee",
- VSRNamespace: "default",
+ ServiceName: "tea-svc",
},
{
- Path: "/internal_location_splits_1_split_1",
- ProxyPass: "http://vs_default_cafe_vsr_default_coffee_coffee-v2$request_uri",
+ Path: "/internal_location_splits_0_split_0",
+ Internal: true,
+ ProxyPass: "https://vs_default_cafe_grpc-app-1$request_uri",
ProxyNextUpstream: "error timeout",
ProxyNextUpstreamTimeout: "0s",
ProxyNextUpstreamTries: 0,
+ HasKeepalive: false,
+ ErrorPages: []version2.ErrorPage{{Name: "@error_page_2_0", Codes: "404 405", ResponseCode: 200}},
+ ProxyInterceptErrors: true,
+ Rewrites: []string{"^ $request_uri break"},
+ ProxySSLName: "grpc-svc.default.svc",
+ ProxyPassRequestHeaders: true,
+ ProxySetHeaders: []version2.Header{{Name: "Host", Value: "$host"}},
+ ServiceName: "grpc-svc",
+ GRPCPass: "grpcs://vs_default_cafe_grpc-app-1",
+ },
+ {
+ Path: "/internal_location_splits_0_split_1",
Internal: true,
- ProxySSLName: "coffee-svc-v2.default.svc",
+ ProxyPass: "https://vs_default_cafe_grpc-app-2$request_uri",
+ ProxyNextUpstream: "error timeout",
+ ProxyNextUpstreamTimeout: "0s",
+ ProxyNextUpstreamTries: 0,
+ HasKeepalive: false,
+ ErrorPages: []version2.ErrorPage{{Name: "@error_page_2_0", Codes: "404 405", ResponseCode: 200}},
+ ProxyInterceptErrors: true,
+ Rewrites: []string{"^ $request_uri break"},
+ ProxySSLName: "grpc-svc2.default.svc",
ProxyPassRequestHeaders: true,
ProxySetHeaders: []version2.Header{{Name: "Host", Value: "$host"}},
- ServiceName: "coffee-svc-v2",
- IsVSR: true,
- VSRName: "coffee",
- VSRNamespace: "default",
+ ServiceName: "grpc-svc2",
+ GRPCPass: "grpcs://vs_default_cafe_grpc-app-2",
+ },
+ },
+ ErrorPageLocations: []version2.ErrorPageLocation{
+ {
+ Name: "@error_page_0_0",
+ DefaultType: "text/plain",
+ Return: &version2.Return{Text: "All Good"},
+ },
+ {
+ Name: "@error_page_1_0",
+ DefaultType: "text/plain",
+ Return: &version2.Return{Text: "Original resource not found, but success!"},
+ },
+ {
+ Name: "@error_page_2_0",
+ DefaultType: "text/plain",
+ Return: &version2.Return{Text: "All Good"},
+ },
+ },
+ },
+ SplitClients: []version2.SplitClient{
+ {
+ Source: "$request_id",
+ Variable: "$vs_default_cafe_splits_0",
+ Distributions: []version2.Distribution{
+ {
+ Weight: "90%",
+ Value: "/internal_location_splits_0_split_0",
+ },
+ {
+ Weight: "10%",
+ Value: "/internal_location_splits_0_split_1",
+ },
},
},
},
}
-
+ expectedWarnings := Warnings{
+ virtualServerEx.VirtualServer: {
+ `The error page configuration for the upstream grpc-app-1 is ignored for status code(s) [404 405], which cannot be used for GRPC upstreams.`,
+ `The error page configuration for the upstream grpc-app-2 is ignored for status code(s) [404], which cannot be used for GRPC upstreams.`,
+ `The error page configuration for the upstream grpc-app-1 is ignored for status code(s) [404 405], which cannot be used for GRPC upstreams.`,
+ `The error page configuration for the upstream grpc-app-2 is ignored for status code(s) [404 405], which cannot be used for GRPC upstreams.`,
+ },
+ }
isPlus := false
isResolverConfigured := false
- isWildcardEnabled := false
+ isWildcardEnabled := true
vsc := newVirtualServerConfigurator(&baseCfgParams, isPlus, isResolverConfigured, &StaticConfigParams{}, isWildcardEnabled)
result, warnings := vsc.GenerateVirtualServerConfig(&virtualServerEx, nil, nil)
if diff := cmp.Diff(expected, result); diff != "" {
- t.Errorf("GenerateVirtualServerConfig() mismatch (-want +got):\n%s", diff)
+ t.Errorf("TestGenerateVirtualServerConfigGrpcErrorPageWarning() mismatch (-want +got):\n%s", diff)
}
- if len(warnings) != 0 {
- t.Errorf("GenerateVirtualServerConfig returned warnings: %v", vsc.warnings)
+ if !reflect.DeepEqual(vsc.warnings, expectedWarnings) {
+ t.Errorf("GenerateVirtualServerConfig() returned warnings of \n%v but expected \n%v", warnings, expectedWarnings)
}
}
-func TestGenerateVirtualServerConfigForVirtualServerWithMatches(t *testing.T) {
+func TestGenerateVirtualServerConfigWithSpiffeCerts(t *testing.T) {
t.Parallel()
virtualServerEx := VirtualServerEx{
VirtualServer: &conf_v1.VirtualServer{
@@ -1800,302 +1951,206 @@ func TestGenerateVirtualServerConfigForVirtualServerWithMatches(t *testing.T) {
Host: "cafe.example.com",
Upstreams: []conf_v1.Upstream{
{
- Name: "tea-v1",
- Service: "tea-svc-v1",
- Port: 80,
- },
- {
- Name: "tea-v2",
- Service: "tea-svc-v2",
+ Name: "tea",
+ Service: "tea-svc",
Port: 80,
},
},
Routes: []conf_v1.Route{
{
Path: "/tea",
- Matches: []conf_v1.Match{
- {
- Conditions: []conf_v1.Condition{
- {
- Header: "x-version",
- Value: "v2",
- },
- },
- Action: &conf_v1.Action{
- Pass: "tea-v2",
- },
- },
- },
Action: &conf_v1.Action{
- Pass: "tea-v1",
+ Pass: "tea",
},
},
- {
- Path: "/coffee",
- Route: "default/coffee",
- },
},
},
},
Endpoints: map[string][]string{
- "default/tea-svc-v1:80": {
+ "default/tea-svc:80": {
"10.0.0.20:80",
},
- "default/tea-svc-v2:80": {
- "10.0.0.21:80",
- },
- "default/coffee-svc-v1:80": {
- "10.0.0.30:80",
- },
- "default/coffee-svc-v2:80": {
- "10.0.0.31:80",
- },
- },
- VirtualServerRoutes: []*conf_v1.VirtualServerRoute{
- {
- ObjectMeta: meta_v1.ObjectMeta{
- Name: "coffee",
- Namespace: "default",
- },
- Spec: conf_v1.VirtualServerRouteSpec{
- Host: "cafe.example.com",
- Upstreams: []conf_v1.Upstream{
- {
- Name: "coffee-v1",
- Service: "coffee-svc-v1",
- Port: 80,
- },
- {
- Name: "coffee-v2",
- Service: "coffee-svc-v2",
- Port: 80,
- },
- },
- Subroutes: []conf_v1.Route{
- {
- Path: "/coffee",
- Matches: []conf_v1.Match{
- {
- Conditions: []conf_v1.Condition{
- {
- Argument: "version",
- Value: "v2",
- },
- },
- Action: &conf_v1.Action{
- Pass: "coffee-v2",
- },
- },
- },
- Action: &conf_v1.Action{
- Pass: "coffee-v1",
- },
- },
- },
- },
- },
},
}
- baseCfgParams := ConfigParams{}
+ baseCfgParams := ConfigParams{
+ ServerTokens: "off",
+ Keepalive: 16,
+ ServerSnippets: []string{"# server snippet"},
+ ProxyProtocol: true,
+ SetRealIPFrom: []string{"0.0.0.0/0"},
+ RealIPHeader: "X-Real-IP",
+ RealIPRecursive: true,
+ }
expected := version2.VirtualServerConfig{
Upstreams: []version2.Upstream{
{
UpstreamLabels: version2.UpstreamLabels{
- Service: "tea-svc-v1",
+ Service: "tea-svc",
ResourceType: "virtualserver",
ResourceName: "cafe",
ResourceNamespace: "default",
},
- Name: "vs_default_cafe_tea-v1",
+ Name: "vs_default_cafe_tea",
Servers: []version2.UpstreamServer{
{
Address: "10.0.0.20:80",
},
},
+ Keepalive: 16,
},
- {
- Name: "vs_default_cafe_tea-v2",
- UpstreamLabels: version2.UpstreamLabels{
- Service: "tea-svc-v2",
- ResourceType: "virtualserver",
- ResourceName: "cafe",
- ResourceNamespace: "default",
- },
- Servers: []version2.UpstreamServer{
- {
- Address: "10.0.0.21:80",
- },
- },
- },
- {
- Name: "vs_default_cafe_vsr_default_coffee_coffee-v1",
- UpstreamLabels: version2.UpstreamLabels{
- Service: "coffee-svc-v1",
- ResourceType: "virtualserverroute",
- ResourceName: "coffee",
- ResourceNamespace: "default",
- },
- Servers: []version2.UpstreamServer{
- {
- Address: "10.0.0.30:80",
- },
- },
- },
- {
- Name: "vs_default_cafe_vsr_default_coffee_coffee-v2",
- UpstreamLabels: version2.UpstreamLabels{
- Service: "coffee-svc-v2",
- ResourceType: "virtualserverroute",
- ResourceName: "coffee",
- ResourceNamespace: "default",
- },
- Servers: []version2.UpstreamServer{
- {
- Address: "10.0.0.31:80",
- },
+ },
+ HTTPSnippets: []string{},
+ LimitReqZones: []version2.LimitReqZone{},
+ Server: version2.Server{
+ ServerName: "cafe.example.com",
+ StatusZone: "cafe.example.com",
+ VSNamespace: "default",
+ VSName: "cafe",
+ ProxyProtocol: true,
+ ServerTokens: "off",
+ SetRealIPFrom: []string{"0.0.0.0/0"},
+ RealIPHeader: "X-Real-IP",
+ RealIPRecursive: true,
+ Snippets: []string{"# server snippet"},
+ TLSPassthrough: true,
+ Locations: []version2.Location{
+ {
+ Path: "/tea",
+ ProxyPass: "https://vs_default_cafe_tea",
+ ProxyNextUpstream: "error timeout",
+ ProxyNextUpstreamTimeout: "0s",
+ ProxyNextUpstreamTries: 0,
+ HasKeepalive: true,
+ ProxySSLName: "tea-svc.default.svc",
+ ProxyPassRequestHeaders: true,
+ ProxySetHeaders: []version2.Header{{Name: "Host", Value: "$host"}},
+ ServiceName: "tea-svc",
},
},
},
- Maps: []version2.Map{
- {
- Source: "$http_x_version",
- Variable: "$vs_default_cafe_matches_0_match_0_cond_0",
- Parameters: []version2.Parameter{
- {
- Value: `"v2"`,
- Result: "1",
- },
- {
- Value: "default",
- Result: "0",
- },
- },
+ SpiffeClientCerts: true,
+ }
+
+ isPlus := false
+ isResolverConfigured := false
+ staticConfigParams := &StaticConfigParams{TLSPassthrough: true, NginxServiceMesh: true}
+ isWildcardEnabled := false
+ vsc := newVirtualServerConfigurator(&baseCfgParams, isPlus, isResolverConfigured, staticConfigParams, isWildcardEnabled)
+
+ result, warnings := vsc.GenerateVirtualServerConfig(&virtualServerEx, nil, nil)
+ if diff := cmp.Diff(expected, result); diff != "" {
+ t.Errorf("GenerateVirtualServerConfig() mismatch (-want +got):\n%s", diff)
+ }
+
+ if len(warnings) != 0 {
+ t.Errorf("GenerateVirtualServerConfig returned warnings: %v", vsc.warnings)
+ }
+}
+
+func TestGenerateVirtualServerConfigWithInternalRoutes(t *testing.T) {
+ t.Parallel()
+ virtualServerEx := VirtualServerEx{
+ VirtualServer: &conf_v1.VirtualServer{
+ ObjectMeta: meta_v1.ObjectMeta{
+ Name: "cafe",
+ Namespace: "default",
},
- {
- Source: "$vs_default_cafe_matches_0_match_0_cond_0",
- Variable: "$vs_default_cafe_matches_0",
- Parameters: []version2.Parameter{
- {
- Value: "~^1",
- Result: "/internal_location_matches_0_match_0",
- },
+ Spec: conf_v1.VirtualServerSpec{
+ Host: "cafe.example.com",
+ Upstreams: []conf_v1.Upstream{
{
- Value: "default",
- Result: "/internal_location_matches_0_default",
+ Name: "tea",
+ Service: "tea-svc",
+ Port: 80,
+ TLS: conf_v1.UpstreamTLS{Enable: false},
},
},
- },
- {
- Source: "$arg_version",
- Variable: "$vs_default_cafe_matches_1_match_0_cond_0",
- Parameters: []version2.Parameter{
- {
- Value: `"v2"`,
- Result: "1",
- },
+ Routes: []conf_v1.Route{
{
- Value: "default",
- Result: "0",
+ Path: "/",
+ Action: &conf_v1.Action{
+ Pass: "tea",
+ },
},
},
+ InternalRoute: true,
+ },
+ },
+ Endpoints: map[string][]string{
+ "default/tea-svc:80": {
+ "10.0.0.20:80",
},
+ },
+ }
+
+ baseCfgParams := ConfigParams{
+ ServerTokens: "off",
+ Keepalive: 16,
+ ServerSnippets: []string{"# server snippet"},
+ ProxyProtocol: true,
+ SetRealIPFrom: []string{"0.0.0.0/0"},
+ RealIPHeader: "X-Real-IP",
+ RealIPRecursive: true,
+ }
+
+ expected := version2.VirtualServerConfig{
+ Upstreams: []version2.Upstream{
{
- Source: "$vs_default_cafe_matches_1_match_0_cond_0",
- Variable: "$vs_default_cafe_matches_1",
- Parameters: []version2.Parameter{
- {
- Value: "~^1",
- Result: "/internal_location_matches_1_match_0",
- },
+ UpstreamLabels: version2.UpstreamLabels{
+ Service: "tea-svc",
+ ResourceType: "virtualserver",
+ ResourceName: "cafe",
+ ResourceNamespace: "default",
+ },
+ Name: "vs_default_cafe_tea",
+ Servers: []version2.UpstreamServer{
{
- Value: "default",
- Result: "/internal_location_matches_1_default",
+ Address: "10.0.0.20:80",
},
},
+ Keepalive: 16,
},
},
HTTPSnippets: []string{},
LimitReqZones: []version2.LimitReqZone{},
Server: version2.Server{
- ServerName: "cafe.example.com",
- StatusZone: "cafe.example.com",
- VSNamespace: "default",
- VSName: "cafe",
- InternalRedirectLocations: []version2.InternalRedirectLocation{
- {
- Path: "/tea",
- Destination: "$vs_default_cafe_matches_0",
- },
- {
- Path: "/coffee",
- Destination: "$vs_default_cafe_matches_1",
- },
- },
+ ServerName: "cafe.example.com",
+ StatusZone: "cafe.example.com",
+ VSNamespace: "default",
+ VSName: "cafe",
+ ProxyProtocol: true,
+ ServerTokens: "off",
+ SetRealIPFrom: []string{"0.0.0.0/0"},
+ RealIPHeader: "X-Real-IP",
+ RealIPRecursive: true,
+ Snippets: []string{"# server snippet"},
+ TLSPassthrough: true,
Locations: []version2.Location{
{
- Path: "/internal_location_matches_0_match_0",
- ProxyPass: "http://vs_default_cafe_tea-v2$request_uri",
- ProxyNextUpstream: "error timeout",
- ProxyNextUpstreamTimeout: "0s",
- ProxyNextUpstreamTries: 0,
- Internal: true,
- ProxySSLName: "tea-svc-v2.default.svc",
- ProxyPassRequestHeaders: true,
- ProxySetHeaders: []version2.Header{{Name: "Host", Value: "$host"}},
- ServiceName: "tea-svc-v2",
- },
- {
- Path: "/internal_location_matches_0_default",
- ProxyPass: "http://vs_default_cafe_tea-v1$request_uri",
- ProxyNextUpstream: "error timeout",
- ProxyNextUpstreamTimeout: "0s",
- ProxyNextUpstreamTries: 0,
- Internal: true,
- ProxySSLName: "tea-svc-v1.default.svc",
- ProxyPassRequestHeaders: true,
- ProxySetHeaders: []version2.Header{{Name: "Host", Value: "$host"}},
- ServiceName: "tea-svc-v1",
- },
- {
- Path: "/internal_location_matches_1_match_0",
- ProxyPass: "http://vs_default_cafe_vsr_default_coffee_coffee-v2$request_uri",
- ProxyNextUpstream: "error timeout",
- ProxyNextUpstreamTimeout: "0s",
- ProxyNextUpstreamTries: 0,
- Internal: true,
- ProxySSLName: "coffee-svc-v2.default.svc",
- ProxyPassRequestHeaders: true,
- ProxySetHeaders: []version2.Header{{Name: "Host", Value: "$host"}},
- ServiceName: "coffee-svc-v2",
- IsVSR: true,
- VSRName: "coffee",
- VSRNamespace: "default",
- },
- {
- Path: "/internal_location_matches_1_default",
- ProxyPass: "http://vs_default_cafe_vsr_default_coffee_coffee-v1$request_uri",
+ Path: "/",
+ ProxyPass: "http://vs_default_cafe_tea",
ProxyNextUpstream: "error timeout",
ProxyNextUpstreamTimeout: "0s",
ProxyNextUpstreamTries: 0,
- Internal: true,
- ProxySSLName: "coffee-svc-v1.default.svc",
+ HasKeepalive: true,
+ ProxySSLName: "tea-svc.default.svc",
ProxyPassRequestHeaders: true,
ProxySetHeaders: []version2.Header{{Name: "Host", Value: "$host"}},
- ServiceName: "coffee-svc-v1",
- IsVSR: true,
- VSRName: "coffee",
- VSRNamespace: "default",
+ ServiceName: "tea-svc",
},
},
},
+ SpiffeCerts: true,
+ SpiffeClientCerts: false,
}
isPlus := false
isResolverConfigured := false
+ staticConfigParams := &StaticConfigParams{TLSPassthrough: true, NginxServiceMesh: true, EnableInternalRoutes: true}
isWildcardEnabled := false
- vsc := newVirtualServerConfigurator(&baseCfgParams, isPlus, isResolverConfigured, &StaticConfigParams{}, isWildcardEnabled)
+ vsc := newVirtualServerConfigurator(&baseCfgParams, isPlus, isResolverConfigured, staticConfigParams, isWildcardEnabled)
result, warnings := vsc.GenerateVirtualServerConfig(&virtualServerEx, nil, nil)
if diff := cmp.Diff(expected, result); diff != "" {
@@ -2107,44 +2162,8 @@ func TestGenerateVirtualServerConfigForVirtualServerWithMatches(t *testing.T) {
}
}
-func TestGenerateVirtualServerConfigForVirtualServerRoutesWithDos(t *testing.T) {
+func TestGenerateVirtualServerConfigWithInternalRoutesWarning(t *testing.T) {
t.Parallel()
- dosResources := map[string]*appProtectDosResource{
- "/coffee": {
- AppProtectDosEnable: "on",
- AppProtectDosLogEnable: false,
- AppProtectDosMonitorURI: "test.example.com",
- AppProtectDosMonitorProtocol: "http",
- AppProtectDosMonitorTimeout: 0,
- AppProtectDosName: "my-dos-coffee",
- AppProtectDosAccessLogDst: "svc.dns.com:123",
- AppProtectDosPolicyFile: "",
- AppProtectDosLogConfFile: "",
- },
- "/tea": {
- AppProtectDosEnable: "on",
- AppProtectDosLogEnable: false,
- AppProtectDosMonitorURI: "test.example.com",
- AppProtectDosMonitorProtocol: "http",
- AppProtectDosMonitorTimeout: 0,
- AppProtectDosName: "my-dos-tea",
- AppProtectDosAccessLogDst: "svc.dns.com:123",
- AppProtectDosPolicyFile: "",
- AppProtectDosLogConfFile: "",
- },
- "/juice": {
- AppProtectDosEnable: "on",
- AppProtectDosLogEnable: false,
- AppProtectDosMonitorURI: "test.example.com",
- AppProtectDosMonitorProtocol: "http",
- AppProtectDosMonitorTimeout: 0,
- AppProtectDosName: "my-dos-juice",
- AppProtectDosAccessLogDst: "svc.dns.com:123",
- AppProtectDosPolicyFile: "",
- AppProtectDosLogConfFile: "",
- },
- }
-
virtualServerEx := VirtualServerEx{
VirtualServer: &conf_v1.VirtualServer{
ObjectMeta: meta_v1.ObjectMeta{
@@ -2153,18 +2172,152 @@ func TestGenerateVirtualServerConfigForVirtualServerRoutesWithDos(t *testing.T)
},
Spec: conf_v1.VirtualServerSpec{
Host: "cafe.example.com",
- Routes: []conf_v1.Route{
- {
- Path: "/coffee",
- Route: "default/coffee",
- },
+ Upstreams: []conf_v1.Upstream{
{
- Path: "/tea",
- Route: "default/tea",
+ Name: "tea",
+ Service: "tea-svc",
+ Port: 80,
+ TLS: conf_v1.UpstreamTLS{Enable: false},
},
+ },
+ Routes: []conf_v1.Route{
{
- Path: "/juice",
- Route: "default/juice",
+ Path: "/",
+ Action: &conf_v1.Action{
+ Pass: "tea",
+ },
+ },
+ },
+ InternalRoute: true,
+ },
+ },
+ Endpoints: map[string][]string{
+ "default/tea-svc:80": {
+ "10.0.0.20:80",
+ },
+ },
+ }
+
+ baseCfgParams := ConfigParams{
+ ServerTokens: "off",
+ Keepalive: 16,
+ ServerSnippets: []string{"# server snippet"},
+ ProxyProtocol: true,
+ SetRealIPFrom: []string{"0.0.0.0/0"},
+ RealIPHeader: "X-Real-IP",
+ RealIPRecursive: true,
+ }
+
+ expected := version2.VirtualServerConfig{
+ Upstreams: []version2.Upstream{
+ {
+ UpstreamLabels: version2.UpstreamLabels{
+ Service: "tea-svc",
+ ResourceType: "virtualserver",
+ ResourceName: "cafe",
+ ResourceNamespace: "default",
+ },
+ Name: "vs_default_cafe_tea",
+ Servers: []version2.UpstreamServer{
+ {
+ Address: "10.0.0.20:80",
+ },
+ },
+ Keepalive: 16,
+ },
+ },
+ HTTPSnippets: []string{},
+ LimitReqZones: []version2.LimitReqZone{},
+ Server: version2.Server{
+ ServerName: "cafe.example.com",
+ StatusZone: "cafe.example.com",
+ VSNamespace: "default",
+ VSName: "cafe",
+ ProxyProtocol: true,
+ ServerTokens: "off",
+ SetRealIPFrom: []string{"0.0.0.0/0"},
+ RealIPHeader: "X-Real-IP",
+ RealIPRecursive: true,
+ Snippets: []string{"# server snippet"},
+ TLSPassthrough: true,
+ Locations: []version2.Location{
+ {
+ Path: "/",
+ ProxyPass: "http://vs_default_cafe_tea",
+ ProxyNextUpstream: "error timeout",
+ ProxyNextUpstreamTimeout: "0s",
+ ProxyNextUpstreamTries: 0,
+ HasKeepalive: true,
+ ProxySSLName: "tea-svc.default.svc",
+ ProxyPassRequestHeaders: true,
+ ProxySetHeaders: []version2.Header{{Name: "Host", Value: "$host"}},
+ ServiceName: "tea-svc",
+ },
+ },
+ },
+ SpiffeCerts: true,
+ SpiffeClientCerts: true,
+ }
+
+ isPlus := false
+ isResolverConfigured := false
+ staticConfigParams := &StaticConfigParams{TLSPassthrough: true, NginxServiceMesh: true, EnableInternalRoutes: false}
+ isWildcardEnabled := false
+ vsc := newVirtualServerConfigurator(&baseCfgParams, isPlus, isResolverConfigured, staticConfigParams, isWildcardEnabled)
+
+ result, warnings := vsc.GenerateVirtualServerConfig(&virtualServerEx, nil, nil)
+ if diff := cmp.Diff(expected, result); diff == "" {
+ t.Errorf("GenerateVirtualServerConfig() should not configure internal route")
+ }
+
+ if len(warnings) != 1 {
+ t.Errorf("GenerateVirtualServerConfig should return warning to enable internal routing")
+ }
+}
+
+func TestGenerateVirtualServerConfigForVirtualServerWithSplits(t *testing.T) {
+ t.Parallel()
+ virtualServerEx := VirtualServerEx{
+ VirtualServer: &conf_v1.VirtualServer{
+ ObjectMeta: meta_v1.ObjectMeta{
+ Name: "cafe",
+ Namespace: "default",
+ },
+ Spec: conf_v1.VirtualServerSpec{
+ Host: "cafe.example.com",
+ Upstreams: []conf_v1.Upstream{
+ {
+ Name: "tea-v1",
+ Service: "tea-svc-v1",
+ Port: 80,
+ },
+ {
+ Name: "tea-v2",
+ Service: "tea-svc-v2",
+ Port: 80,
+ },
+ },
+ Routes: []conf_v1.Route{
+ {
+ Path: "/tea",
+ Splits: []conf_v1.Split{
+ {
+ Weight: 90,
+ Action: &conf_v1.Action{
+ Pass: "tea-v1",
+ },
+ },
+ {
+ Weight: 10,
+ Action: &conf_v1.Action{
+ Pass: "tea-v2",
+ },
+ },
+ },
+ },
+ {
+ Path: "/coffee",
+ Route: "default/coffee",
},
},
},
@@ -2182,12 +2335,6 @@ func TestGenerateVirtualServerConfigForVirtualServerRoutesWithDos(t *testing.T)
"default/coffee-svc-v2:80": {
"10.0.0.31:80",
},
- "default/juice-svc-v1:80": {
- "10.0.0.33:80",
- },
- "default/juice-svc-v2:80": {
- "10.0.0.34:80",
- },
},
VirtualServerRoutes: []*conf_v1.VirtualServerRoute{
{
@@ -2212,220 +2359,201 @@ func TestGenerateVirtualServerConfigForVirtualServerRoutesWithDos(t *testing.T)
Subroutes: []conf_v1.Route{
{
Path: "/coffee",
- Matches: []conf_v1.Match{
+ Splits: []conf_v1.Split{
{
- Conditions: []conf_v1.Condition{
- {
- Argument: "version",
- Value: "v2",
- },
+ Weight: 40,
+ Action: &conf_v1.Action{
+ Pass: "coffee-v1",
},
+ },
+ {
+ Weight: 60,
Action: &conf_v1.Action{
Pass: "coffee-v2",
},
},
},
- Dos: "test_ns/dos_protected",
- Action: &conf_v1.Action{
- Pass: "coffee-v1",
- },
},
},
},
},
+ },
+ }
+
+ baseCfgParams := ConfigParams{}
+
+ expected := version2.VirtualServerConfig{
+ Upstreams: []version2.Upstream{
{
- ObjectMeta: meta_v1.ObjectMeta{
- Name: "tea",
- Namespace: "default",
+ Name: "vs_default_cafe_tea-v1",
+ UpstreamLabels: version2.UpstreamLabels{
+ Service: "tea-svc-v1",
+ ResourceType: "virtualserver",
+ ResourceName: "cafe",
+ ResourceNamespace: "default",
},
- Spec: conf_v1.VirtualServerRouteSpec{
- Host: "cafe.example.com",
- Upstreams: []conf_v1.Upstream{
- {
- Name: "tea-v1",
- Service: "tea-svc-v1",
- Port: 80,
- },
- {
- Name: "tea-v2",
- Service: "tea-svc-v2",
- Port: 80,
- },
- },
- Subroutes: []conf_v1.Route{
- {
- Path: "/tea",
- Dos: "test_ns/dos_protected",
- Action: &conf_v1.Action{
- Pass: "tea-v1",
- },
- },
+ Servers: []version2.UpstreamServer{
+ {
+ Address: "10.0.0.20:80",
},
},
},
{
- ObjectMeta: meta_v1.ObjectMeta{
- Name: "juice",
- Namespace: "default",
+ Name: "vs_default_cafe_tea-v2",
+ UpstreamLabels: version2.UpstreamLabels{
+ Service: "tea-svc-v2",
+ ResourceType: "virtualserver",
+ ResourceName: "cafe",
+ ResourceNamespace: "default",
},
- Spec: conf_v1.VirtualServerRouteSpec{
- Host: "cafe.example.com",
- Upstreams: []conf_v1.Upstream{
- {
- Name: "juice-v1",
- Service: "juice-svc-v1",
- Port: 80,
- },
- {
- Name: "juice-v2",
- Service: "juice-svc-v2",
- Port: 80,
- },
+ Servers: []version2.UpstreamServer{
+ {
+ Address: "10.0.0.21:80",
},
- Subroutes: []conf_v1.Route{
- {
- Path: "/juice",
- Dos: "test_ns/dos_protected",
- Splits: []conf_v1.Split{
- {
- Weight: 80,
- Action: &conf_v1.Action{
- Pass: "juice-v1",
- },
- },
- {
- Weight: 20,
- Action: &conf_v1.Action{
- Pass: "juice-v2",
- },
- },
- },
- },
+ },
+ },
+ {
+ Name: "vs_default_cafe_vsr_default_coffee_coffee-v1",
+ UpstreamLabels: version2.UpstreamLabels{
+ Service: "coffee-svc-v1",
+ ResourceType: "virtualserverroute",
+ ResourceName: "coffee",
+ ResourceNamespace: "default",
+ },
+ Servers: []version2.UpstreamServer{
+ {
+ Address: "10.0.0.30:80",
},
},
},
- },
- }
-
- baseCfgParams := ConfigParams{}
-
- expected := []version2.Location{
- {
- Path: "/internal_location_matches_0_match_0",
- ProxyPass: "http://vs_default_cafe_vsr_default_coffee_coffee-v2$request_uri",
- ProxyNextUpstream: "error timeout",
- ProxyNextUpstreamTimeout: "0s",
- ProxyNextUpstreamTries: 0,
- Internal: true,
- ProxySSLName: "coffee-svc-v2.default.svc",
- ProxyPassRequestHeaders: true,
- ProxySetHeaders: []version2.Header{{Name: "Host", Value: "$host"}},
- ServiceName: "coffee-svc-v2",
- IsVSR: true,
- VSRName: "coffee",
- VSRNamespace: "default",
- Dos: &version2.Dos{
- Enable: "on",
- Name: "my-dos-coffee",
- ApDosMonitorURI: "test.example.com",
- ApDosMonitorProtocol: "http",
- ApDosAccessLogDest: "svc.dns.com:123",
+ {
+ Name: "vs_default_cafe_vsr_default_coffee_coffee-v2",
+ UpstreamLabels: version2.UpstreamLabels{
+ Service: "coffee-svc-v2",
+ ResourceType: "virtualserverroute",
+ ResourceName: "coffee",
+ ResourceNamespace: "default",
+ },
+ Servers: []version2.UpstreamServer{
+ {
+ Address: "10.0.0.31:80",
+ },
+ },
},
},
- {
- Path: "/internal_location_matches_0_default",
- ProxyPass: "http://vs_default_cafe_vsr_default_coffee_coffee-v1$request_uri",
- ProxyNextUpstream: "error timeout",
- ProxyNextUpstreamTimeout: "0s",
- ProxyNextUpstreamTries: 0,
- Internal: true,
- ProxySSLName: "coffee-svc-v1.default.svc",
- ProxyPassRequestHeaders: true,
- ProxySetHeaders: []version2.Header{{Name: "Host", Value: "$host"}},
- ServiceName: "coffee-svc-v1",
- IsVSR: true,
- VSRName: "coffee",
- VSRNamespace: "default",
- Dos: &version2.Dos{
- Enable: "on",
- Name: "my-dos-coffee",
- ApDosMonitorURI: "test.example.com",
- ApDosMonitorProtocol: "http",
- ApDosAccessLogDest: "svc.dns.com:123",
+ SplitClients: []version2.SplitClient{
+ {
+ Source: "$request_id",
+ Variable: "$vs_default_cafe_splits_0",
+ Distributions: []version2.Distribution{
+ {
+ Weight: "90%",
+ Value: "/internal_location_splits_0_split_0",
+ },
+ {
+ Weight: "10%",
+ Value: "/internal_location_splits_0_split_1",
+ },
+ },
},
- },
- {
- Path: "/tea",
- ProxyPass: "http://vs_default_cafe_vsr_default_tea_tea-v1",
- ProxyNextUpstream: "error timeout",
- ProxyNextUpstreamTimeout: "0s",
- ProxyNextUpstreamTries: 0,
- Internal: false,
- ProxySSLName: "tea-svc-v1.default.svc",
- ProxyPassRequestHeaders: true,
- ProxySetHeaders: []version2.Header{{Name: "Host", Value: "$host"}},
- ServiceName: "tea-svc-v1",
- IsVSR: true,
- VSRName: "tea",
- VSRNamespace: "default",
- Dos: &version2.Dos{
- Enable: "on",
- Name: "my-dos-tea",
- ApDosMonitorURI: "test.example.com",
- ApDosMonitorProtocol: "http",
- ApDosAccessLogDest: "svc.dns.com:123",
+ {
+ Source: "$request_id",
+ Variable: "$vs_default_cafe_splits_1",
+ Distributions: []version2.Distribution{
+ {
+ Weight: "40%",
+ Value: "/internal_location_splits_1_split_0",
+ },
+ {
+ Weight: "60%",
+ Value: "/internal_location_splits_1_split_1",
+ },
+ },
},
},
- {
- Path: "/internal_location_splits_0_split_0",
- Internal: true,
- ProxyPass: "http://vs_default_cafe_vsr_default_juice_juice-v1$request_uri",
- ProxyNextUpstream: "error timeout",
- ProxyNextUpstreamTimeout: "0s",
- ProxyPassRequestHeaders: true,
- ProxySetHeaders: []version2.Header{{Name: "Host", Value: "$host"}},
- ProxySSLName: "juice-svc-v1.default.svc",
- Dos: &version2.Dos{
- Enable: "on",
- Name: "my-dos-juice",
- ApDosMonitorURI: "test.example.com",
- ApDosMonitorProtocol: "http",
- ApDosAccessLogDest: "svc.dns.com:123",
+ HTTPSnippets: []string{},
+ LimitReqZones: []version2.LimitReqZone{},
+ Server: version2.Server{
+ ServerName: "cafe.example.com",
+ StatusZone: "cafe.example.com",
+ VSNamespace: "default",
+ VSName: "cafe",
+ InternalRedirectLocations: []version2.InternalRedirectLocation{
+ {
+ Path: "/tea",
+ Destination: "$vs_default_cafe_splits_0",
+ },
+ {
+ Path: "/coffee",
+ Destination: "$vs_default_cafe_splits_1",
+ },
},
- ServiceName: "juice-svc-v1",
- IsVSR: true,
- VSRName: "juice",
- VSRNamespace: "default",
- },
- {
- Path: "/internal_location_splits_0_split_1",
- Internal: true,
- ProxyPass: "http://vs_default_cafe_vsr_default_juice_juice-v2$request_uri",
- ProxyNextUpstream: "error timeout",
- ProxyNextUpstreamTimeout: "0s",
- ProxyPassRequestHeaders: true,
- ProxySetHeaders: []version2.Header{{Name: "Host", Value: "$host"}},
- ProxySSLName: "juice-svc-v2.default.svc",
- Dos: &version2.Dos{
- Enable: "on",
- Name: "my-dos-juice",
- ApDosMonitorURI: "test.example.com",
- ApDosMonitorProtocol: "http",
- ApDosAccessLogDest: "svc.dns.com:123",
+ Locations: []version2.Location{
+ {
+ Path: "/internal_location_splits_0_split_0",
+ ProxyPass: "http://vs_default_cafe_tea-v1$request_uri",
+ ProxyNextUpstream: "error timeout",
+ ProxyNextUpstreamTimeout: "0s",
+ ProxyNextUpstreamTries: 0,
+ Internal: true,
+ ProxySSLName: "tea-svc-v1.default.svc",
+ ProxyPassRequestHeaders: true,
+ ProxySetHeaders: []version2.Header{{Name: "Host", Value: "$host"}},
+ ServiceName: "tea-svc-v1",
+ },
+ {
+ Path: "/internal_location_splits_0_split_1",
+ ProxyPass: "http://vs_default_cafe_tea-v2$request_uri",
+ ProxyNextUpstream: "error timeout",
+ ProxyNextUpstreamTimeout: "0s",
+ ProxyNextUpstreamTries: 0,
+ Internal: true,
+ ProxySSLName: "tea-svc-v2.default.svc",
+ ProxyPassRequestHeaders: true,
+ ProxySetHeaders: []version2.Header{{Name: "Host", Value: "$host"}},
+ ServiceName: "tea-svc-v2",
+ },
+ {
+ Path: "/internal_location_splits_1_split_0",
+ ProxyPass: "http://vs_default_cafe_vsr_default_coffee_coffee-v1$request_uri",
+ ProxyNextUpstream: "error timeout",
+ ProxyNextUpstreamTimeout: "0s",
+ ProxyNextUpstreamTries: 0,
+ Internal: true,
+ ProxySSLName: "coffee-svc-v1.default.svc",
+ ProxyPassRequestHeaders: true,
+ ProxySetHeaders: []version2.Header{{Name: "Host", Value: "$host"}},
+ ServiceName: "coffee-svc-v1",
+ IsVSR: true,
+ VSRName: "coffee",
+ VSRNamespace: "default",
+ },
+ {
+ Path: "/internal_location_splits_1_split_1",
+ ProxyPass: "http://vs_default_cafe_vsr_default_coffee_coffee-v2$request_uri",
+ ProxyNextUpstream: "error timeout",
+ ProxyNextUpstreamTimeout: "0s",
+ ProxyNextUpstreamTries: 0,
+ Internal: true,
+ ProxySSLName: "coffee-svc-v2.default.svc",
+ ProxyPassRequestHeaders: true,
+ ProxySetHeaders: []version2.Header{{Name: "Host", Value: "$host"}},
+ ServiceName: "coffee-svc-v2",
+ IsVSR: true,
+ VSRName: "coffee",
+ VSRNamespace: "default",
+ },
},
- ServiceName: "juice-svc-v2",
- IsVSR: true,
- VSRName: "juice",
- VSRNamespace: "default",
},
}
isPlus := false
isResolverConfigured := false
- vsc := newVirtualServerConfigurator(&baseCfgParams, isPlus, isResolverConfigured, &StaticConfigParams{MainAppProtectDosLoadModule: true}, false)
+ isWildcardEnabled := false
+ vsc := newVirtualServerConfigurator(&baseCfgParams, isPlus, isResolverConfigured, &StaticConfigParams{}, isWildcardEnabled)
- result, warnings := vsc.GenerateVirtualServerConfig(&virtualServerEx, nil, dosResources)
- if diff := cmp.Diff(expected, result.Server.Locations); diff != "" {
+ result, warnings := vsc.GenerateVirtualServerConfig(&virtualServerEx, nil, nil)
+ if diff := cmp.Diff(expected, result); diff != "" {
t.Errorf("GenerateVirtualServerConfig() mismatch (-want +got):\n%s", diff)
}
@@ -2434,135 +2562,107 @@ func TestGenerateVirtualServerConfigForVirtualServerRoutesWithDos(t *testing.T)
}
}
-func TestGenerateVirtualServerConfigForVirtualServerWithReturns(t *testing.T) {
+func TestGenerateVirtualServerConfigForVirtualServerWithMatches(t *testing.T) {
t.Parallel()
virtualServerEx := VirtualServerEx{
VirtualServer: &conf_v1.VirtualServer{
ObjectMeta: meta_v1.ObjectMeta{
- Name: "returns",
+ Name: "cafe",
Namespace: "default",
},
Spec: conf_v1.VirtualServerSpec{
- Host: "example.com",
- Routes: []conf_v1.Route{
+ Host: "cafe.example.com",
+ Upstreams: []conf_v1.Upstream{
{
- Path: "/return",
- Action: &conf_v1.Action{
- Return: &conf_v1.ActionReturn{
- Body: "hello 0",
- },
- },
+ Name: "tea-v1",
+ Service: "tea-svc-v1",
+ Port: 80,
},
{
- Path: "/splits-with-return",
- Splits: []conf_v1.Split{
+ Name: "tea-v2",
+ Service: "tea-svc-v2",
+ Port: 80,
+ },
+ },
+ Routes: []conf_v1.Route{
+ {
+ Path: "/tea",
+ Matches: []conf_v1.Match{
{
- Weight: 90,
+ Conditions: []conf_v1.Condition{
+ {
+ Header: "x-version",
+ Value: "v2",
+ },
+ },
Action: &conf_v1.Action{
- Return: &conf_v1.ActionReturn{
- Body: "hello 1",
- },
- },
- },
- {
- Weight: 10,
- Action: &conf_v1.Action{
- Return: &conf_v1.ActionReturn{
- Body: "hello 2",
- },
- },
- },
- },
- },
- {
- Path: "/matches-with-return",
- Matches: []conf_v1.Match{
- {
- Conditions: []conf_v1.Condition{
- {
- Header: "x-version",
- Value: "v2",
- },
- },
- Action: &conf_v1.Action{
- Return: &conf_v1.ActionReturn{
- Body: "hello 3",
- },
+ Pass: "tea-v2",
},
},
},
Action: &conf_v1.Action{
- Return: &conf_v1.ActionReturn{
- Body: "hello 4",
- },
+ Pass: "tea-v1",
},
},
{
- Path: "/more",
- Route: "default/more-returns",
+ Path: "/coffee",
+ Route: "default/coffee",
},
},
},
},
+ Endpoints: map[string][]string{
+ "default/tea-svc-v1:80": {
+ "10.0.0.20:80",
+ },
+ "default/tea-svc-v2:80": {
+ "10.0.0.21:80",
+ },
+ "default/coffee-svc-v1:80": {
+ "10.0.0.30:80",
+ },
+ "default/coffee-svc-v2:80": {
+ "10.0.0.31:80",
+ },
+ },
VirtualServerRoutes: []*conf_v1.VirtualServerRoute{
{
ObjectMeta: meta_v1.ObjectMeta{
- Name: "more-returns",
+ Name: "coffee",
Namespace: "default",
},
Spec: conf_v1.VirtualServerRouteSpec{
- Host: "example.com",
- Subroutes: []conf_v1.Route{
+ Host: "cafe.example.com",
+ Upstreams: []conf_v1.Upstream{
{
- Path: "/more/return",
- Action: &conf_v1.Action{
- Return: &conf_v1.ActionReturn{
- Body: "hello 5",
- },
- },
+ Name: "coffee-v1",
+ Service: "coffee-svc-v1",
+ Port: 80,
},
{
- Path: "/more/splits-with-return",
- Splits: []conf_v1.Split{
- {
- Weight: 90,
- Action: &conf_v1.Action{
- Return: &conf_v1.ActionReturn{
- Body: "hello 6",
- },
- },
- },
- {
- Weight: 10,
- Action: &conf_v1.Action{
- Return: &conf_v1.ActionReturn{
- Body: "hello 7",
- },
- },
- },
- },
+ Name: "coffee-v2",
+ Service: "coffee-svc-v2",
+ Port: 80,
},
+ },
+ Subroutes: []conf_v1.Route{
{
- Path: "/more/matches-with-return",
+ Path: "/coffee",
Matches: []conf_v1.Match{
{
Conditions: []conf_v1.Condition{
{
- Header: "x-version",
- Value: "v2",
+ Argument: "version",
+ Value: "v2",
},
},
Action: &conf_v1.Action{
- Return: &conf_v1.ActionReturn{
- Body: "hello 8",
- },
+ Pass: "coffee-v2",
},
},
},
Action: &conf_v1.Action{
- Return: &conf_v1.ActionReturn{
- Body: "hello 9",
- },
+ Pass: "coffee-v1",
},
},
},
@@ -2574,38 +2674,68 @@ func TestGenerateVirtualServerConfigForVirtualServerWithReturns(t *testing.T) {
baseCfgParams := ConfigParams{}
expected := version2.VirtualServerConfig{
- Maps: []version2.Map{
+ Upstreams: []version2.Upstream{
{
- Source: "$http_x_version",
- Variable: "$vs_default_returns_matches_0_match_0_cond_0",
- Parameters: []version2.Parameter{
+ UpstreamLabels: version2.UpstreamLabels{
+ Service: "tea-svc-v1",
+ ResourceType: "virtualserver",
+ ResourceName: "cafe",
+ ResourceNamespace: "default",
+ },
+ Name: "vs_default_cafe_tea-v1",
+ Servers: []version2.UpstreamServer{
{
- Value: `"v2"`,
- Result: "1",
+ Address: "10.0.0.20:80",
},
+ },
+ },
+ {
+ Name: "vs_default_cafe_tea-v2",
+ UpstreamLabels: version2.UpstreamLabels{
+ Service: "tea-svc-v2",
+ ResourceType: "virtualserver",
+ ResourceName: "cafe",
+ ResourceNamespace: "default",
+ },
+ Servers: []version2.UpstreamServer{
{
- Value: "default",
- Result: "0",
+ Address: "10.0.0.21:80",
},
},
},
{
- Source: "$vs_default_returns_matches_0_match_0_cond_0",
- Variable: "$vs_default_returns_matches_0",
- Parameters: []version2.Parameter{
+ Name: "vs_default_cafe_vsr_default_coffee_coffee-v1",
+ UpstreamLabels: version2.UpstreamLabels{
+ Service: "coffee-svc-v1",
+ ResourceType: "virtualserverroute",
+ ResourceName: "coffee",
+ ResourceNamespace: "default",
+ },
+ Servers: []version2.UpstreamServer{
{
- Value: "~^1",
- Result: "/internal_location_matches_0_match_0",
+ Address: "10.0.0.30:80",
},
+ },
+ },
+ {
+ Name: "vs_default_cafe_vsr_default_coffee_coffee-v2",
+ UpstreamLabels: version2.UpstreamLabels{
+ Service: "coffee-svc-v2",
+ ResourceType: "virtualserverroute",
+ ResourceName: "coffee",
+ ResourceNamespace: "default",
+ },
+ Servers: []version2.UpstreamServer{
{
- Value: "default",
- Result: "/internal_location_matches_0_default",
+ Address: "10.0.0.31:80",
},
},
},
+ },
+ Maps: []version2.Map{
{
Source: "$http_x_version",
- Variable: "$vs_default_returns_matches_1_match_0_cond_0",
+ Variable: "$vs_default_cafe_matches_0_match_0_cond_0",
Parameters: []version2.Parameter{
{
Value: `"v2"`,
@@ -2618,46 +2748,44 @@ func TestGenerateVirtualServerConfigForVirtualServerWithReturns(t *testing.T) {
},
},
{
- Source: "$vs_default_returns_matches_1_match_0_cond_0",
- Variable: "$vs_default_returns_matches_1",
+ Source: "$vs_default_cafe_matches_0_match_0_cond_0",
+ Variable: "$vs_default_cafe_matches_0",
Parameters: []version2.Parameter{
{
Value: "~^1",
- Result: "/internal_location_matches_1_match_0",
+ Result: "/internal_location_matches_0_match_0",
},
{
Value: "default",
- Result: "/internal_location_matches_1_default",
+ Result: "/internal_location_matches_0_default",
},
},
},
- },
- SplitClients: []version2.SplitClient{
{
- Source: "$request_id",
- Variable: "$vs_default_returns_splits_0",
- Distributions: []version2.Distribution{
+ Source: "$arg_version",
+ Variable: "$vs_default_cafe_matches_1_match_0_cond_0",
+ Parameters: []version2.Parameter{
{
- Weight: "90%",
- Value: "/internal_location_splits_0_split_0",
+ Value: `"v2"`,
+ Result: "1",
},
{
- Weight: "10%",
- Value: "/internal_location_splits_0_split_1",
+ Value: "default",
+ Result: "0",
},
},
},
{
- Source: "$request_id",
- Variable: "$vs_default_returns_splits_1",
- Distributions: []version2.Distribution{
+ Source: "$vs_default_cafe_matches_1_match_0_cond_0",
+ Variable: "$vs_default_cafe_matches_1",
+ Parameters: []version2.Parameter{
{
- Weight: "90%",
- Value: "/internal_location_splits_1_split_0",
+ Value: "~^1",
+ Result: "/internal_location_matches_1_match_0",
},
{
- Weight: "10%",
- Value: "/internal_location_splits_1_split_1",
+ Value: "default",
+ Result: "/internal_location_matches_1_default",
},
},
},
@@ -2665,230 +2793,74 @@ func TestGenerateVirtualServerConfigForVirtualServerWithReturns(t *testing.T) {
HTTPSnippets: []string{},
LimitReqZones: []version2.LimitReqZone{},
Server: version2.Server{
- ServerName: "example.com",
- StatusZone: "example.com",
+ ServerName: "cafe.example.com",
+ StatusZone: "cafe.example.com",
VSNamespace: "default",
- VSName: "returns",
+ VSName: "cafe",
InternalRedirectLocations: []version2.InternalRedirectLocation{
{
- Path: "/splits-with-return",
- Destination: "$vs_default_returns_splits_0",
- },
- {
- Path: "/matches-with-return",
- Destination: "$vs_default_returns_matches_0",
+ Path: "/tea",
+ Destination: "$vs_default_cafe_matches_0",
},
{
- Path: "/more/splits-with-return",
- Destination: "$vs_default_returns_splits_1",
+ Path: "/coffee",
+ Destination: "$vs_default_cafe_matches_1",
},
+ },
+ Locations: []version2.Location{
{
- Path: "/more/matches-with-return",
- Destination: "$vs_default_returns_matches_1",
+ Path: "/internal_location_matches_0_match_0",
+ ProxyPass: "http://vs_default_cafe_tea-v2$request_uri",
+ ProxyNextUpstream: "error timeout",
+ ProxyNextUpstreamTimeout: "0s",
+ ProxyNextUpstreamTries: 0,
+ Internal: true,
+ ProxySSLName: "tea-svc-v2.default.svc",
+ ProxyPassRequestHeaders: true,
+ ProxySetHeaders: []version2.Header{{Name: "Host", Value: "$host"}},
+ ServiceName: "tea-svc-v2",
},
- },
- ReturnLocations: []version2.ReturnLocation{
{
- Name: "@return_0",
- DefaultType: "text/plain",
- Return: version2.Return{
- Code: 0,
- Text: "hello 0",
- },
+ Path: "/internal_location_matches_0_default",
+ ProxyPass: "http://vs_default_cafe_tea-v1$request_uri",
+ ProxyNextUpstream: "error timeout",
+ ProxyNextUpstreamTimeout: "0s",
+ ProxyNextUpstreamTries: 0,
+ Internal: true,
+ ProxySSLName: "tea-svc-v1.default.svc",
+ ProxyPassRequestHeaders: true,
+ ProxySetHeaders: []version2.Header{{Name: "Host", Value: "$host"}},
+ ServiceName: "tea-svc-v1",
},
{
- Name: "@return_1",
- DefaultType: "text/plain",
- Return: version2.Return{
- Code: 0,
- Text: "hello 1",
- },
- },
- {
- Name: "@return_2",
- DefaultType: "text/plain",
- Return: version2.Return{
- Code: 0,
- Text: "hello 2",
- },
- },
- {
- Name: "@return_3",
- DefaultType: "text/plain",
- Return: version2.Return{
- Code: 0,
- Text: "hello 3",
- },
- },
- {
- Name: "@return_4",
- DefaultType: "text/plain",
- Return: version2.Return{
- Code: 0,
- Text: "hello 4",
- },
- },
- {
- Name: "@return_5",
- DefaultType: "text/plain",
- Return: version2.Return{
- Code: 0,
- Text: "hello 5",
- },
- },
- {
- Name: "@return_6",
- DefaultType: "text/plain",
- Return: version2.Return{
- Code: 0,
- Text: "hello 6",
- },
- },
- {
- Name: "@return_7",
- DefaultType: "text/plain",
- Return: version2.Return{
- Code: 0,
- Text: "hello 7",
- },
- },
- {
- Name: "@return_8",
- DefaultType: "text/plain",
- Return: version2.Return{
- Code: 0,
- Text: "hello 8",
- },
- },
- {
- Name: "@return_9",
- DefaultType: "text/plain",
- Return: version2.Return{
- Code: 0,
- Text: "hello 9",
- },
- },
- },
- Locations: []version2.Location{
- {
- Path: "/return",
- ProxyInterceptErrors: true,
- ErrorPages: []version2.ErrorPage{
- {
- Name: "@return_0",
- Codes: "418",
- ResponseCode: 200,
- },
- },
- InternalProxyPass: "http://unix:/var/lib/nginx/nginx-418-server.sock",
- },
- {
- Path: "/internal_location_splits_0_split_0",
- ProxyInterceptErrors: true,
- ErrorPages: []version2.ErrorPage{
- {
- Name: "@return_1",
- Codes: "418",
- ResponseCode: 200,
- },
- },
- InternalProxyPass: "http://unix:/var/lib/nginx/nginx-418-server.sock",
- },
- {
- Path: "/internal_location_splits_0_split_1",
- ProxyInterceptErrors: true,
- ErrorPages: []version2.ErrorPage{
- {
- Name: "@return_2",
- Codes: "418",
- ResponseCode: 200,
- },
- },
- InternalProxyPass: "http://unix:/var/lib/nginx/nginx-418-server.sock",
- },
- {
- Path: "/internal_location_matches_0_match_0",
- ProxyInterceptErrors: true,
- ErrorPages: []version2.ErrorPage{
- {
- Name: "@return_3",
- Codes: "418",
- ResponseCode: 200,
- },
- },
- InternalProxyPass: "http://unix:/var/lib/nginx/nginx-418-server.sock",
- },
- {
- Path: "/internal_location_matches_0_default",
- ProxyInterceptErrors: true,
- ErrorPages: []version2.ErrorPage{
- {
- Name: "@return_4",
- Codes: "418",
- ResponseCode: 200,
- },
- },
- InternalProxyPass: "http://unix:/var/lib/nginx/nginx-418-server.sock",
- },
- {
- Path: "/more/return",
- ProxyInterceptErrors: true,
- ErrorPages: []version2.ErrorPage{
- {
- Name: "@return_5",
- Codes: "418",
- ResponseCode: 200,
- },
- },
- InternalProxyPass: "http://unix:/var/lib/nginx/nginx-418-server.sock",
- },
- {
- Path: "/internal_location_splits_1_split_0",
- ProxyInterceptErrors: true,
- ErrorPages: []version2.ErrorPage{
- {
- Name: "@return_6",
- Codes: "418",
- ResponseCode: 200,
- },
- },
- InternalProxyPass: "http://unix:/var/lib/nginx/nginx-418-server.sock",
- },
- {
- Path: "/internal_location_splits_1_split_1",
- ProxyInterceptErrors: true,
- ErrorPages: []version2.ErrorPage{
- {
- Name: "@return_7",
- Codes: "418",
- ResponseCode: 200,
- },
- },
- InternalProxyPass: "http://unix:/var/lib/nginx/nginx-418-server.sock",
- },
- {
- Path: "/internal_location_matches_1_match_0",
- ProxyInterceptErrors: true,
- ErrorPages: []version2.ErrorPage{
- {
- Name: "@return_8",
- Codes: "418",
- ResponseCode: 200,
- },
- },
- InternalProxyPass: "http://unix:/var/lib/nginx/nginx-418-server.sock",
+ Path: "/internal_location_matches_1_match_0",
+ ProxyPass: "http://vs_default_cafe_vsr_default_coffee_coffee-v2$request_uri",
+ ProxyNextUpstream: "error timeout",
+ ProxyNextUpstreamTimeout: "0s",
+ ProxyNextUpstreamTries: 0,
+ Internal: true,
+ ProxySSLName: "coffee-svc-v2.default.svc",
+ ProxyPassRequestHeaders: true,
+ ProxySetHeaders: []version2.Header{{Name: "Host", Value: "$host"}},
+ ServiceName: "coffee-svc-v2",
+ IsVSR: true,
+ VSRName: "coffee",
+ VSRNamespace: "default",
},
{
- Path: "/internal_location_matches_1_default",
- ProxyInterceptErrors: true,
- ErrorPages: []version2.ErrorPage{
- {
- Name: "@return_9",
- Codes: "418",
- ResponseCode: 200,
- },
- },
- InternalProxyPass: "http://unix:/var/lib/nginx/nginx-418-server.sock",
+ Path: "/internal_location_matches_1_default",
+ ProxyPass: "http://vs_default_cafe_vsr_default_coffee_coffee-v1$request_uri",
+ ProxyNextUpstream: "error timeout",
+ ProxyNextUpstreamTimeout: "0s",
+ ProxyNextUpstreamTries: 0,
+ Internal: true,
+ ProxySSLName: "coffee-svc-v1.default.svc",
+ ProxyPassRequestHeaders: true,
+ ProxySetHeaders: []version2.Header{{Name: "Host", Value: "$host"}},
+ ServiceName: "coffee-svc-v1",
+ IsVSR: true,
+ VSRName: "coffee",
+ VSRNamespace: "default",
},
},
},
@@ -2900,8 +2872,8 @@ func TestGenerateVirtualServerConfigForVirtualServerWithReturns(t *testing.T) {
vsc := newVirtualServerConfigurator(&baseCfgParams, isPlus, isResolverConfigured, &StaticConfigParams{}, isWildcardEnabled)
result, warnings := vsc.GenerateVirtualServerConfig(&virtualServerEx, nil, nil)
- if !reflect.DeepEqual(result, expected) {
- t.Errorf("GenerateVirtualServerConfig returned \n%+v but expected \n%+v", result, expected)
+ if diff := cmp.Diff(expected, result); diff != "" {
+ t.Errorf("GenerateVirtualServerConfig() mismatch (-want +got):\n%s", diff)
}
if len(warnings) != 0 {
@@ -2909,8 +2881,43 @@ func TestGenerateVirtualServerConfigForVirtualServerWithReturns(t *testing.T) {
}
}
-func TestGenerateVirtualServerConfigJWKSPolicy(t *testing.T) {
+func TestGenerateVirtualServerConfigForVirtualServerRoutesWithDos(t *testing.T) {
t.Parallel()
+ dosResources := map[string]*appProtectDosResource{
+ "/coffee": {
+ AppProtectDosEnable: "on",
+ AppProtectDosLogEnable: false,
+ AppProtectDosMonitorURI: "test.example.com",
+ AppProtectDosMonitorProtocol: "http",
+ AppProtectDosMonitorTimeout: 0,
+ AppProtectDosName: "my-dos-coffee",
+ AppProtectDosAccessLogDst: "svc.dns.com:123",
+ AppProtectDosPolicyFile: "",
+ AppProtectDosLogConfFile: "",
+ },
+ "/tea": {
+ AppProtectDosEnable: "on",
+ AppProtectDosLogEnable: false,
+ AppProtectDosMonitorURI: "test.example.com",
+ AppProtectDosMonitorProtocol: "http",
+ AppProtectDosMonitorTimeout: 0,
+ AppProtectDosName: "my-dos-tea",
+ AppProtectDosAccessLogDst: "svc.dns.com:123",
+ AppProtectDosPolicyFile: "",
+ AppProtectDosLogConfFile: "",
+ },
+ "/juice": {
+ AppProtectDosEnable: "on",
+ AppProtectDosLogEnable: false,
+ AppProtectDosMonitorURI: "test.example.com",
+ AppProtectDosMonitorProtocol: "http",
+ AppProtectDosMonitorTimeout: 0,
+ AppProtectDosName: "my-dos-juice",
+ AppProtectDosAccessLogDst: "svc.dns.com:123",
+ AppProtectDosPolicyFile: "",
+ AppProtectDosLogConfFile: "",
+ },
+ }
virtualServerEx := VirtualServerEx{
VirtualServer: &conf_v1.VirtualServer{
@@ -2920,242 +2927,279 @@ func TestGenerateVirtualServerConfigJWKSPolicy(t *testing.T) {
},
Spec: conf_v1.VirtualServerSpec{
Host: "cafe.example.com",
- Policies: []conf_v1.PolicyReference{
+ Routes: []conf_v1.Route{
{
- Name: "jwt-policy",
+ Path: "/coffee",
+ Route: "default/coffee",
},
- },
- Upstreams: []conf_v1.Upstream{
{
- Name: "tea",
- Service: "tea-svc",
- Port: 80,
+ Path: "/tea",
+ Route: "default/tea",
},
{
- Name: "coffee",
- Service: "coffee-svc",
- Port: 80,
+ Path: "/juice",
+ Route: "default/juice",
},
},
- Routes: []conf_v1.Route{
- {
- Path: "/tea",
- Action: &conf_v1.Action{
- Pass: "tea",
+ },
+ },
+ Endpoints: map[string][]string{
+ "default/tea-svc-v1:80": {
+ "10.0.0.20:80",
+ },
+ "default/tea-svc-v2:80": {
+ "10.0.0.21:80",
+ },
+ "default/coffee-svc-v1:80": {
+ "10.0.0.30:80",
+ },
+ "default/coffee-svc-v2:80": {
+ "10.0.0.31:80",
+ },
+ "default/juice-svc-v1:80": {
+ "10.0.0.33:80",
+ },
+ "default/juice-svc-v2:80": {
+ "10.0.0.34:80",
+ },
+ },
+ VirtualServerRoutes: []*conf_v1.VirtualServerRoute{
+ {
+ ObjectMeta: meta_v1.ObjectMeta{
+ Name: "coffee",
+ Namespace: "default",
+ },
+ Spec: conf_v1.VirtualServerRouteSpec{
+ Host: "cafe.example.com",
+ Upstreams: []conf_v1.Upstream{
+ {
+ Name: "coffee-v1",
+ Service: "coffee-svc-v1",
+ Port: 80,
},
- Policies: []conf_v1.PolicyReference{
- {
- Name: "jwt-policy-route",
- },
+ {
+ Name: "coffee-v2",
+ Service: "coffee-svc-v2",
+ Port: 80,
},
},
- {
- Path: "/coffee",
- Action: &conf_v1.Action{
- Pass: "coffee",
- },
- Policies: []conf_v1.PolicyReference{
- {
- Name: "jwt-policy-route",
+ Subroutes: []conf_v1.Route{
+ {
+ Path: "/coffee",
+ Matches: []conf_v1.Match{
+ {
+ Conditions: []conf_v1.Condition{
+ {
+ Argument: "version",
+ Value: "v2",
+ },
+ },
+ Action: &conf_v1.Action{
+ Pass: "coffee-v2",
+ },
+ },
+ },
+ Dos: "test_ns/dos_protected",
+ Action: &conf_v1.Action{
+ Pass: "coffee-v1",
},
},
},
},
},
- },
- Policies: map[string]*conf_v1.Policy{
- "default/jwt-policy": {
+ {
ObjectMeta: meta_v1.ObjectMeta{
- Name: "jwt-policy",
+ Name: "tea",
Namespace: "default",
},
- Spec: conf_v1.PolicySpec{
- JWTAuth: &conf_v1.JWTAuth{
- Realm: "Spec Realm API",
- JwksURI: "https://idp.spec.example.com:443/spec-keys",
- KeyCache: "1h",
+ Spec: conf_v1.VirtualServerRouteSpec{
+ Host: "cafe.example.com",
+ Upstreams: []conf_v1.Upstream{
+ {
+ Name: "tea-v1",
+ Service: "tea-svc-v1",
+ Port: 80,
+ },
+ {
+ Name: "tea-v2",
+ Service: "tea-svc-v2",
+ Port: 80,
+ },
+ },
+ Subroutes: []conf_v1.Route{
+ {
+ Path: "/tea",
+ Dos: "test_ns/dos_protected",
+ Action: &conf_v1.Action{
+ Pass: "tea-v1",
+ },
+ },
},
},
},
- "default/jwt-policy-route": {
+ {
ObjectMeta: meta_v1.ObjectMeta{
- Name: "jwt-policy-route",
+ Name: "juice",
Namespace: "default",
},
- Spec: conf_v1.PolicySpec{
- JWTAuth: &conf_v1.JWTAuth{
- Realm: "Route Realm API",
- JwksURI: "http://idp.route.example.com:80/route-keys",
- KeyCache: "1h",
+ Spec: conf_v1.VirtualServerRouteSpec{
+ Host: "cafe.example.com",
+ Upstreams: []conf_v1.Upstream{
+ {
+ Name: "juice-v1",
+ Service: "juice-svc-v1",
+ Port: 80,
+ },
+ {
+ Name: "juice-v2",
+ Service: "juice-svc-v2",
+ Port: 80,
+ },
+ },
+ Subroutes: []conf_v1.Route{
+ {
+ Path: "/juice",
+ Dos: "test_ns/dos_protected",
+ Splits: []conf_v1.Split{
+ {
+ Weight: 80,
+ Action: &conf_v1.Action{
+ Pass: "juice-v1",
+ },
+ },
+ {
+ Weight: 20,
+ Action: &conf_v1.Action{
+ Pass: "juice-v2",
+ },
+ },
+ },
+ },
},
},
},
},
- Endpoints: map[string][]string{
- "default/tea-svc:80": {
- "10.0.0.20:80",
- },
- "default/coffee-svc:80": {
- "10.0.0.30:80",
- },
- },
}
- expected := version2.VirtualServerConfig{
- Upstreams: []version2.Upstream{
- {
- UpstreamLabels: version2.UpstreamLabels{
- Service: "tea-svc",
- ResourceType: "virtualserver",
- ResourceName: "cafe",
- ResourceNamespace: "default",
- },
- Name: "vs_default_cafe_tea",
- Servers: []version2.UpstreamServer{
- {
- Address: "10.0.0.20:80",
- },
- },
- Keepalive: 16,
+ baseCfgParams := ConfigParams{}
+
+ expected := []version2.Location{
+ {
+ Path: "/internal_location_matches_0_match_0",
+ ProxyPass: "http://vs_default_cafe_vsr_default_coffee_coffee-v2$request_uri",
+ ProxyNextUpstream: "error timeout",
+ ProxyNextUpstreamTimeout: "0s",
+ ProxyNextUpstreamTries: 0,
+ Internal: true,
+ ProxySSLName: "coffee-svc-v2.default.svc",
+ ProxyPassRequestHeaders: true,
+ ProxySetHeaders: []version2.Header{{Name: "Host", Value: "$host"}},
+ ServiceName: "coffee-svc-v2",
+ IsVSR: true,
+ VSRName: "coffee",
+ VSRNamespace: "default",
+ Dos: &version2.Dos{
+ Enable: "on",
+ Name: "my-dos-coffee",
+ ApDosMonitorURI: "test.example.com",
+ ApDosMonitorProtocol: "http",
+ ApDosAccessLogDest: "svc.dns.com:123",
},
- {
- UpstreamLabels: version2.UpstreamLabels{
- Service: "coffee-svc",
- ResourceType: "virtualserver",
- ResourceName: "cafe",
- ResourceNamespace: "default",
- },
- Name: "vs_default_cafe_coffee",
- Servers: []version2.UpstreamServer{
- {
- Address: "10.0.0.30:80",
- },
- },
- Keepalive: 16,
+ },
+ {
+ Path: "/internal_location_matches_0_default",
+ ProxyPass: "http://vs_default_cafe_vsr_default_coffee_coffee-v1$request_uri",
+ ProxyNextUpstream: "error timeout",
+ ProxyNextUpstreamTimeout: "0s",
+ ProxyNextUpstreamTries: 0,
+ Internal: true,
+ ProxySSLName: "coffee-svc-v1.default.svc",
+ ProxyPassRequestHeaders: true,
+ ProxySetHeaders: []version2.Header{{Name: "Host", Value: "$host"}},
+ ServiceName: "coffee-svc-v1",
+ IsVSR: true,
+ VSRName: "coffee",
+ VSRNamespace: "default",
+ Dos: &version2.Dos{
+ Enable: "on",
+ Name: "my-dos-coffee",
+ ApDosMonitorURI: "test.example.com",
+ ApDosMonitorProtocol: "http",
+ ApDosAccessLogDest: "svc.dns.com:123",
},
},
- HTTPSnippets: []string{},
- LimitReqZones: []version2.LimitReqZone{},
- Server: version2.Server{
- JWTAuthList: map[string]*version2.JWTAuth{
- "default/jwt-policy": {
- Key: "default/jwt-policy",
- Realm: "Spec Realm API",
- KeyCache: "1h",
- JwksURI: version2.JwksURI{
- JwksScheme: "https",
- JwksHost: "idp.spec.example.com",
- JwksPort: "443",
- JwksPath: "/spec-keys",
- },
- },
- "default/jwt-policy-route": {
- Key: "default/jwt-policy-route",
- Realm: "Route Realm API",
- KeyCache: "1h",
- JwksURI: version2.JwksURI{
- JwksScheme: "http",
- JwksHost: "idp.route.example.com",
- JwksPort: "80",
- JwksPath: "/route-keys",
- },
- },
+ {
+ Path: "/tea",
+ ProxyPass: "http://vs_default_cafe_vsr_default_tea_tea-v1",
+ ProxyNextUpstream: "error timeout",
+ ProxyNextUpstreamTimeout: "0s",
+ ProxyNextUpstreamTries: 0,
+ Internal: false,
+ ProxySSLName: "tea-svc-v1.default.svc",
+ ProxyPassRequestHeaders: true,
+ ProxySetHeaders: []version2.Header{{Name: "Host", Value: "$host"}},
+ ServiceName: "tea-svc-v1",
+ IsVSR: true,
+ VSRName: "tea",
+ VSRNamespace: "default",
+ Dos: &version2.Dos{
+ Enable: "on",
+ Name: "my-dos-tea",
+ ApDosMonitorURI: "test.example.com",
+ ApDosMonitorProtocol: "http",
+ ApDosAccessLogDest: "svc.dns.com:123",
},
- JWTAuth: &version2.JWTAuth{
- Key: "default/jwt-policy",
- Realm: "Spec Realm API",
- KeyCache: "1h",
- JwksURI: version2.JwksURI{
- JwksScheme: "https",
- JwksHost: "idp.spec.example.com",
- JwksPort: "443",
- JwksPath: "/spec-keys",
- },
+ },
+ {
+ Path: "/internal_location_splits_0_split_0",
+ Internal: true,
+ ProxyPass: "http://vs_default_cafe_vsr_default_juice_juice-v1$request_uri",
+ ProxyNextUpstream: "error timeout",
+ ProxyNextUpstreamTimeout: "0s",
+ ProxyPassRequestHeaders: true,
+ ProxySetHeaders: []version2.Header{{Name: "Host", Value: "$host"}},
+ ProxySSLName: "juice-svc-v1.default.svc",
+ Dos: &version2.Dos{
+ Enable: "on",
+ Name: "my-dos-juice",
+ ApDosMonitorURI: "test.example.com",
+ ApDosMonitorProtocol: "http",
+ ApDosAccessLogDest: "svc.dns.com:123",
},
- JWKSAuthEnabled: true,
- ServerName: "cafe.example.com",
- StatusZone: "cafe.example.com",
- ProxyProtocol: true,
- ServerTokens: "off",
- RealIPHeader: "X-Real-IP",
- SetRealIPFrom: []string{"0.0.0.0/0"},
- RealIPRecursive: true,
- Snippets: []string{"# server snippet"},
- TLSPassthrough: true,
- VSNamespace: "default",
- VSName: "cafe",
- Locations: []version2.Location{
- {
- Path: "/tea",
- ProxyPass: "http://vs_default_cafe_tea",
- ProxyNextUpstream: "error timeout",
- ProxyNextUpstreamTimeout: "0s",
- ProxyNextUpstreamTries: 0,
- HasKeepalive: true,
- ProxySSLName: "tea-svc.default.svc",
- ProxyPassRequestHeaders: true,
- ProxySetHeaders: []version2.Header{{Name: "Host", Value: "$host"}},
- ServiceName: "tea-svc",
- JWTAuth: &version2.JWTAuth{
- Key: "default/jwt-policy-route",
- Realm: "Route Realm API",
- KeyCache: "1h",
- JwksURI: version2.JwksURI{
- JwksScheme: "http",
- JwksHost: "idp.route.example.com",
- JwksPort: "80",
- JwksPath: "/route-keys",
- },
- },
- },
- {
- Path: "/coffee",
- ProxyPass: "http://vs_default_cafe_coffee",
- ProxyNextUpstream: "error timeout",
- ProxyNextUpstreamTimeout: "0s",
- ProxyNextUpstreamTries: 0,
- HasKeepalive: true,
- ProxySSLName: "coffee-svc.default.svc",
- ProxyPassRequestHeaders: true,
- ProxySetHeaders: []version2.Header{{Name: "Host", Value: "$host"}},
- ServiceName: "coffee-svc",
- JWTAuth: &version2.JWTAuth{
- Key: "default/jwt-policy-route",
- Realm: "Route Realm API",
- KeyCache: "1h",
- JwksURI: version2.JwksURI{
- JwksScheme: "http",
- JwksHost: "idp.route.example.com",
- JwksPort: "80",
- JwksPath: "/route-keys",
- },
- },
- },
+ ServiceName: "juice-svc-v1",
+ IsVSR: true,
+ VSRName: "juice",
+ VSRNamespace: "default",
+ },
+ {
+ Path: "/internal_location_splits_0_split_1",
+ Internal: true,
+ ProxyPass: "http://vs_default_cafe_vsr_default_juice_juice-v2$request_uri",
+ ProxyNextUpstream: "error timeout",
+ ProxyNextUpstreamTimeout: "0s",
+ ProxyPassRequestHeaders: true,
+ ProxySetHeaders: []version2.Header{{Name: "Host", Value: "$host"}},
+ ProxySSLName: "juice-svc-v2.default.svc",
+ Dos: &version2.Dos{
+ Enable: "on",
+ Name: "my-dos-juice",
+ ApDosMonitorURI: "test.example.com",
+ ApDosMonitorProtocol: "http",
+ ApDosAccessLogDest: "svc.dns.com:123",
},
+ ServiceName: "juice-svc-v2",
+ IsVSR: true,
+ VSRName: "juice",
+ VSRNamespace: "default",
},
}
- baseCfgParams := ConfigParams{
- ServerTokens: "off",
- Keepalive: 16,
- ServerSnippets: []string{"# server snippet"},
- ProxyProtocol: true,
- SetRealIPFrom: []string{"0.0.0.0/0"},
- RealIPHeader: "X-Real-IP",
- RealIPRecursive: true,
- }
-
- vsc := newVirtualServerConfigurator(
- &baseCfgParams,
- false,
- false,
- &StaticConfigParams{TLSPassthrough: true},
- false,
- )
-
- result, warnings := vsc.GenerateVirtualServerConfig(&virtualServerEx, nil, nil)
+ isPlus := false
+ isResolverConfigured := false
+ vsc := newVirtualServerConfigurator(&baseCfgParams, isPlus, isResolverConfigured, &StaticConfigParams{MainAppProtectDosLoadModule: true}, false)
- if diff := cmp.Diff(expected, result); diff != "" {
+ result, warnings := vsc.GenerateVirtualServerConfig(&virtualServerEx, nil, dosResources)
+ if diff := cmp.Diff(expected, result.Server.Locations); diff != "" {
t.Errorf("GenerateVirtualServerConfig() mismatch (-want +got):\n%s", diff)
}
@@ -3164,1352 +3208,1127 @@ func TestGenerateVirtualServerConfigJWKSPolicy(t *testing.T) {
}
}
-func TestGeneratePolicies(t *testing.T) {
+func TestGenerateVirtualServerConfigForVirtualServerWithReturns(t *testing.T) {
t.Parallel()
- ownerDetails := policyOwnerDetails{
- owner: nil, // nil is OK for the unit test
- ownerNamespace: "default",
- vsNamespace: "default",
- vsName: "test",
- }
- mTLSCertPath := "/etc/nginx/secrets/default-ingress-mtls-secret-ca.crt"
- mTLSCrlPath := "/etc/nginx/secrets/default-ingress-mtls-secret-ca.crl"
- mTLSCertAndCrlPath := fmt.Sprintf("%s %s", mTLSCertPath, mTLSCrlPath)
- policyOpts := policyOptions{
- tls: true,
- secretRefs: map[string]*secrets.SecretReference{
- "default/ingress-mtls-secret": {
- Secret: &api_v1.Secret{
- Type: secrets.SecretTypeCA,
- },
- Path: mTLSCertPath,
- },
- "default/ingress-mtls-secret-crl": {
- Secret: &api_v1.Secret{
- Type: secrets.SecretTypeCA,
- Data: map[string][]byte{
- "ca.crl": []byte("base64crl"),
- },
- },
- Path: mTLSCertAndCrlPath,
- },
- "default/egress-mtls-secret": {
- Secret: &api_v1.Secret{
- Type: api_v1.SecretTypeTLS,
- },
- Path: "/etc/nginx/secrets/default-egress-mtls-secret",
- },
- "default/egress-trusted-ca-secret": {
- Secret: &api_v1.Secret{
- Type: secrets.SecretTypeCA,
- },
- Path: "/etc/nginx/secrets/default-egress-trusted-ca-secret",
- },
- "default/egress-trusted-ca-secret-crl": {
- Secret: &api_v1.Secret{
- Type: secrets.SecretTypeCA,
- },
- Path: mTLSCertAndCrlPath,
- },
- "default/jwt-secret": {
- Secret: &api_v1.Secret{
- Type: secrets.SecretTypeJWK,
- },
- Path: "/etc/nginx/secrets/default-jwt-secret",
- },
- "default/htpasswd-secret": {
- Secret: &api_v1.Secret{
- Type: secrets.SecretTypeHtpasswd,
- },
- Path: "/etc/nginx/secrets/default-htpasswd-secret",
+ virtualServerEx := VirtualServerEx{
+ VirtualServer: &conf_v1.VirtualServer{
+ ObjectMeta: meta_v1.ObjectMeta{
+ Name: "returns",
+ Namespace: "default",
},
- "default/oidc-secret": {
- Secret: &api_v1.Secret{
- Type: secrets.SecretTypeOIDC,
- Data: map[string][]byte{
- "client-secret": []byte("super_secret_123"),
+ Spec: conf_v1.VirtualServerSpec{
+ Host: "example.com",
+ Routes: []conf_v1.Route{
+ {
+ Path: "/return",
+ Action: &conf_v1.Action{
+ Return: &conf_v1.ActionReturn{
+ Body: "hello 0",
+ },
+ },
},
- },
- },
- },
- apResources: &appProtectResourcesForVS{
- Policies: map[string]string{
- "default/dataguard-alarm": "/etc/nginx/waf/nac-policies/default-dataguard-alarm",
- },
- LogConfs: map[string]string{
- "default/logconf": "/etc/nginx/waf/nac-logconfs/default-logconf",
- },
- },
- }
-
- tests := []struct {
- policyRefs []conf_v1.PolicyReference
- policies map[string]*conf_v1.Policy
- context string
- expected policiesCfg
- msg string
- }{
- {
- policyRefs: []conf_v1.PolicyReference{
- {
- Name: "allow-policy",
- Namespace: "default",
- },
- },
- policies: map[string]*conf_v1.Policy{
- "default/allow-policy": {
- Spec: conf_v1.PolicySpec{
- AccessControl: &conf_v1.AccessControl{
- Allow: []string{"127.0.0.1"},
+ {
+ Path: "/splits-with-return",
+ Splits: []conf_v1.Split{
+ {
+ Weight: 90,
+ Action: &conf_v1.Action{
+ Return: &conf_v1.ActionReturn{
+ Body: "hello 1",
+ },
+ },
+ },
+ {
+ Weight: 10,
+ Action: &conf_v1.Action{
+ Return: &conf_v1.ActionReturn{
+ Body: "hello 2",
+ },
+ },
+ },
},
},
- },
- },
- expected: policiesCfg{
- Allow: []string{"127.0.0.1"},
- },
- msg: "explicit reference",
- },
- {
- policyRefs: []conf_v1.PolicyReference{
- {
- Name: "allow-policy",
- },
- },
- policies: map[string]*conf_v1.Policy{
- "default/allow-policy": {
- Spec: conf_v1.PolicySpec{
- AccessControl: &conf_v1.AccessControl{
- Allow: []string{"127.0.0.1"},
+ {
+ Path: "/matches-with-return",
+ Matches: []conf_v1.Match{
+ {
+ Conditions: []conf_v1.Condition{
+ {
+ Header: "x-version",
+ Value: "v2",
+ },
+ },
+ Action: &conf_v1.Action{
+ Return: &conf_v1.ActionReturn{
+ Body: "hello 3",
+ },
+ },
+ },
},
+ Action: &conf_v1.Action{
+ Return: &conf_v1.ActionReturn{
+ Body: "hello 4",
+ },
+ },
+ },
+ {
+ Path: "/more",
+ Route: "default/more-returns",
},
},
},
- expected: policiesCfg{
- Allow: []string{"127.0.0.1"},
- },
- msg: "implicit reference",
},
- {
- policyRefs: []conf_v1.PolicyReference{
- {
- Name: "allow-policy-1",
- },
- {
- Name: "allow-policy-2",
+ VirtualServerRoutes: []*conf_v1.VirtualServerRoute{
+ {
+ ObjectMeta: meta_v1.ObjectMeta{
+ Name: "more-returns",
+ Namespace: "default",
},
- },
- policies: map[string]*conf_v1.Policy{
- "default/allow-policy-1": {
- Spec: conf_v1.PolicySpec{
- AccessControl: &conf_v1.AccessControl{
- Allow: []string{"127.0.0.1"},
+ Spec: conf_v1.VirtualServerRouteSpec{
+ Host: "example.com",
+ Subroutes: []conf_v1.Route{
+ {
+ Path: "/more/return",
+ Action: &conf_v1.Action{
+ Return: &conf_v1.ActionReturn{
+ Body: "hello 5",
+ },
+ },
},
- },
- },
- "default/allow-policy-2": {
- Spec: conf_v1.PolicySpec{
- AccessControl: &conf_v1.AccessControl{
- Allow: []string{"127.0.0.2"},
+ {
+ Path: "/more/splits-with-return",
+ Splits: []conf_v1.Split{
+ {
+ Weight: 90,
+ Action: &conf_v1.Action{
+ Return: &conf_v1.ActionReturn{
+ Body: "hello 6",
+ },
+ },
+ },
+ {
+ Weight: 10,
+ Action: &conf_v1.Action{
+ Return: &conf_v1.ActionReturn{
+ Body: "hello 7",
+ },
+ },
+ },
+ },
+ },
+ {
+ Path: "/more/matches-with-return",
+ Matches: []conf_v1.Match{
+ {
+ Conditions: []conf_v1.Condition{
+ {
+ Header: "x-version",
+ Value: "v2",
+ },
+ },
+ Action: &conf_v1.Action{
+ Return: &conf_v1.ActionReturn{
+ Body: "hello 8",
+ },
+ },
+ },
+ },
+ Action: &conf_v1.Action{
+ Return: &conf_v1.ActionReturn{
+ Body: "hello 9",
+ },
+ },
},
},
},
},
- expected: policiesCfg{
- Allow: []string{"127.0.0.1", "127.0.0.2"},
- },
- msg: "merging",
},
- {
- policyRefs: []conf_v1.PolicyReference{
- {
- Name: "rateLimit-policy",
- Namespace: "default",
+ }
+
+ baseCfgParams := ConfigParams{}
+
+ expected := version2.VirtualServerConfig{
+ Maps: []version2.Map{
+ {
+ Source: "$http_x_version",
+ Variable: "$vs_default_returns_matches_0_match_0_cond_0",
+ Parameters: []version2.Parameter{
+ {
+ Value: `"v2"`,
+ Result: "1",
+ },
+ {
+ Value: "default",
+ Result: "0",
+ },
},
},
- policies: map[string]*conf_v1.Policy{
- "default/rateLimit-policy": {
- Spec: conf_v1.PolicySpec{
- RateLimit: &conf_v1.RateLimit{
- Key: "test",
- ZoneSize: "10M",
- Rate: "10r/s",
- LogLevel: "notice",
- },
+ {
+ Source: "$vs_default_returns_matches_0_match_0_cond_0",
+ Variable: "$vs_default_returns_matches_0",
+ Parameters: []version2.Parameter{
+ {
+ Value: "~^1",
+ Result: "/internal_location_matches_0_match_0",
+ },
+ {
+ Value: "default",
+ Result: "/internal_location_matches_0_default",
},
},
},
- expected: policiesCfg{
- LimitReqZones: []version2.LimitReqZone{
+ {
+ Source: "$http_x_version",
+ Variable: "$vs_default_returns_matches_1_match_0_cond_0",
+ Parameters: []version2.Parameter{
{
- Key: "test",
- ZoneSize: "10M",
- Rate: "10r/s",
- ZoneName: "pol_rl_default_rateLimit-policy_default_test",
+ Value: `"v2"`,
+ Result: "1",
+ },
+ {
+ Value: "default",
+ Result: "0",
},
},
- LimitReqOptions: version2.LimitReqOptions{
- LogLevel: "notice",
- RejectCode: 503,
- },
- LimitReqs: []version2.LimitReq{
+ },
+ {
+ Source: "$vs_default_returns_matches_1_match_0_cond_0",
+ Variable: "$vs_default_returns_matches_1",
+ Parameters: []version2.Parameter{
{
- ZoneName: "pol_rl_default_rateLimit-policy_default_test",
+ Value: "~^1",
+ Result: "/internal_location_matches_1_match_0",
+ },
+ {
+ Value: "default",
+ Result: "/internal_location_matches_1_default",
},
},
},
- msg: "rate limit reference",
},
- {
- policyRefs: []conf_v1.PolicyReference{
- {
- Name: "rateLimit-policy",
- Namespace: "default",
- },
- {
- Name: "rateLimit-policy2",
- Namespace: "default",
- },
- },
- policies: map[string]*conf_v1.Policy{
- "default/rateLimit-policy": {
- Spec: conf_v1.PolicySpec{
- RateLimit: &conf_v1.RateLimit{
- Key: "test",
- ZoneSize: "10M",
- Rate: "10r/s",
- },
- },
- },
- "default/rateLimit-policy2": {
- Spec: conf_v1.PolicySpec{
- RateLimit: &conf_v1.RateLimit{
- Key: "test2",
- ZoneSize: "20M",
- Rate: "20r/s",
- },
- },
- },
- },
- expected: policiesCfg{
- LimitReqZones: []version2.LimitReqZone{
+ SplitClients: []version2.SplitClient{
+ {
+ Source: "$request_id",
+ Variable: "$vs_default_returns_splits_0",
+ Distributions: []version2.Distribution{
{
- Key: "test",
- ZoneSize: "10M",
- Rate: "10r/s",
- ZoneName: "pol_rl_default_rateLimit-policy_default_test",
+ Weight: "90%",
+ Value: "/internal_location_splits_0_split_0",
},
{
- Key: "test2",
- ZoneSize: "20M",
- Rate: "20r/s",
- ZoneName: "pol_rl_default_rateLimit-policy2_default_test",
+ Weight: "10%",
+ Value: "/internal_location_splits_0_split_1",
},
},
- LimitReqOptions: version2.LimitReqOptions{
- LogLevel: "error",
- RejectCode: 503,
- },
- LimitReqs: []version2.LimitReq{
+ },
+ {
+ Source: "$request_id",
+ Variable: "$vs_default_returns_splits_1",
+ Distributions: []version2.Distribution{
{
- ZoneName: "pol_rl_default_rateLimit-policy_default_test",
+ Weight: "90%",
+ Value: "/internal_location_splits_1_split_0",
},
{
- ZoneName: "pol_rl_default_rateLimit-policy2_default_test",
+ Weight: "10%",
+ Value: "/internal_location_splits_1_split_1",
},
},
},
- msg: "multi rate limit reference",
},
- {
- policyRefs: []conf_v1.PolicyReference{
+ HTTPSnippets: []string{},
+ LimitReqZones: []version2.LimitReqZone{},
+ Server: version2.Server{
+ ServerName: "example.com",
+ StatusZone: "example.com",
+ VSNamespace: "default",
+ VSName: "returns",
+ InternalRedirectLocations: []version2.InternalRedirectLocation{
{
- Name: "jwt-policy",
- Namespace: "default",
+ Path: "/splits-with-return",
+ Destination: "$vs_default_returns_splits_0",
},
- },
- policies: map[string]*conf_v1.Policy{
- "default/jwt-policy": {
- ObjectMeta: meta_v1.ObjectMeta{
- Name: "jwt-policy",
- Namespace: "default",
- },
- Spec: conf_v1.PolicySpec{
- JWTAuth: &conf_v1.JWTAuth{
- Realm: "My Test API",
- Secret: "jwt-secret",
- },
- },
+ {
+ Path: "/matches-with-return",
+ Destination: "$vs_default_returns_matches_0",
},
- },
- expected: policiesCfg{
- JWTAuth: &version2.JWTAuth{
- Secret: "/etc/nginx/secrets/default-jwt-secret",
- Realm: "My Test API",
+ {
+ Path: "/more/splits-with-return",
+ Destination: "$vs_default_returns_splits_1",
},
- JWKSAuthEnabled: false,
- },
- msg: "jwt reference",
- },
- {
- policyRefs: []conf_v1.PolicyReference{
{
- Name: "jwt-policy-2",
- Namespace: "default",
+ Path: "/more/matches-with-return",
+ Destination: "$vs_default_returns_matches_1",
},
},
- policies: map[string]*conf_v1.Policy{
- "default/jwt-policy-2": {
- ObjectMeta: meta_v1.ObjectMeta{
- Name: "jwt-policy-2",
- Namespace: "default",
- },
- Spec: conf_v1.PolicySpec{
- JWTAuth: &conf_v1.JWTAuth{
- Realm: "My Test API",
- JwksURI: "https://idp.example.com:443/keys",
- KeyCache: "1h",
- },
+ ReturnLocations: []version2.ReturnLocation{
+ {
+ Name: "@return_0",
+ DefaultType: "text/plain",
+ Return: version2.Return{
+ Code: 0,
+ Text: "hello 0",
},
},
- },
- expected: policiesCfg{
- JWTAuth: &version2.JWTAuth{
- Key: "default/jwt-policy-2",
- Realm: "My Test API",
- JwksURI: version2.JwksURI{
- JwksScheme: "https",
- JwksHost: "idp.example.com",
- JwksPort: "443",
- JwksPath: "/keys",
+ {
+ Name: "@return_1",
+ DefaultType: "text/plain",
+ Return: version2.Return{
+ Code: 0,
+ Text: "hello 1",
},
- KeyCache: "1h",
},
- JWKSAuthEnabled: true,
- },
- msg: "Basic jwks example",
- },
- {
- policyRefs: []conf_v1.PolicyReference{
{
- Name: "jwt-policy-2",
- Namespace: "default",
+ Name: "@return_2",
+ DefaultType: "text/plain",
+ Return: version2.Return{
+ Code: 0,
+ Text: "hello 2",
+ },
},
- },
- policies: map[string]*conf_v1.Policy{
- "default/jwt-policy-2": {
- ObjectMeta: meta_v1.ObjectMeta{
- Name: "jwt-policy-2",
- Namespace: "default",
+ {
+ Name: "@return_3",
+ DefaultType: "text/plain",
+ Return: version2.Return{
+ Code: 0,
+ Text: "hello 3",
},
- Spec: conf_v1.PolicySpec{
- JWTAuth: &conf_v1.JWTAuth{
- Realm: "My Test API",
- JwksURI: "https://idp.example.com/keys",
- KeyCache: "1h",
- },
+ },
+ {
+ Name: "@return_4",
+ DefaultType: "text/plain",
+ Return: version2.Return{
+ Code: 0,
+ Text: "hello 4",
},
},
- },
- expected: policiesCfg{
- JWTAuth: &version2.JWTAuth{
- Key: "default/jwt-policy-2",
- Realm: "My Test API",
- JwksURI: version2.JwksURI{
- JwksScheme: "https",
- JwksHost: "idp.example.com",
- JwksPort: "",
- JwksPath: "/keys",
+ {
+ Name: "@return_5",
+ DefaultType: "text/plain",
+ Return: version2.Return{
+ Code: 0,
+ Text: "hello 5",
},
- KeyCache: "1h",
},
- JWKSAuthEnabled: true,
- },
- msg: "Basic jwks example, no port in JwksURI",
- },
- {
- policyRefs: []conf_v1.PolicyReference{
{
- Name: "basic-auth-policy",
- Namespace: "default",
+ Name: "@return_6",
+ DefaultType: "text/plain",
+ Return: version2.Return{
+ Code: 0,
+ Text: "hello 6",
+ },
},
- },
- policies: map[string]*conf_v1.Policy{
- "default/basic-auth-policy": {
- ObjectMeta: meta_v1.ObjectMeta{
- Name: "basic-auth-policy",
- Namespace: "default",
+ {
+ Name: "@return_7",
+ DefaultType: "text/plain",
+ Return: version2.Return{
+ Code: 0,
+ Text: "hello 7",
},
- Spec: conf_v1.PolicySpec{
- BasicAuth: &conf_v1.BasicAuth{
- Realm: "My Test API",
- Secret: "htpasswd-secret",
- },
+ },
+ {
+ Name: "@return_8",
+ DefaultType: "text/plain",
+ Return: version2.Return{
+ Code: 0,
+ Text: "hello 8",
},
},
- },
- expected: policiesCfg{
- BasicAuth: &version2.BasicAuth{
- Secret: "/etc/nginx/secrets/default-htpasswd-secret",
- Realm: "My Test API",
+ {
+ Name: "@return_9",
+ DefaultType: "text/plain",
+ Return: version2.Return{
+ Code: 0,
+ Text: "hello 9",
+ },
},
},
- msg: "basic auth reference",
- },
- {
- policyRefs: []conf_v1.PolicyReference{
+ Locations: []version2.Location{
{
- Name: "ingress-mtls-policy",
- Namespace: "default",
- },
- },
- policies: map[string]*conf_v1.Policy{
- "default/ingress-mtls-policy": {
- ObjectMeta: meta_v1.ObjectMeta{
- Name: "ingress-mtls-policy",
- Namespace: "default",
- },
- Spec: conf_v1.PolicySpec{
- IngressMTLS: &conf_v1.IngressMTLS{
- ClientCertSecret: "ingress-mtls-secret",
- VerifyClient: "off",
+ Path: "/return",
+ ProxyInterceptErrors: true,
+ ErrorPages: []version2.ErrorPage{
+ {
+ Name: "@return_0",
+ Codes: "418",
+ ResponseCode: 200,
},
},
+ InternalProxyPass: "http://unix:/var/lib/nginx/nginx-418-server.sock",
},
- },
- context: "spec",
- expected: policiesCfg{
- IngressMTLS: &version2.IngressMTLS{
- ClientCert: mTLSCertPath,
- VerifyClient: "off",
- VerifyDepth: 1,
- },
- },
- msg: "ingressMTLS reference",
- },
- {
- policyRefs: []conf_v1.PolicyReference{
{
- Name: "ingress-mtls-policy-crl",
- Namespace: "default",
- },
- },
- policies: map[string]*conf_v1.Policy{
- "default/ingress-mtls-policy-crl": {
- ObjectMeta: meta_v1.ObjectMeta{
- Name: "ingress-mtls-policy-crl",
- Namespace: "default",
- },
- Spec: conf_v1.PolicySpec{
- IngressMTLS: &conf_v1.IngressMTLS{
- ClientCertSecret: "ingress-mtls-secret-crl",
- VerifyClient: "off",
+ Path: "/internal_location_splits_0_split_0",
+ ProxyInterceptErrors: true,
+ ErrorPages: []version2.ErrorPage{
+ {
+ Name: "@return_1",
+ Codes: "418",
+ ResponseCode: 200,
},
},
+ InternalProxyPass: "http://unix:/var/lib/nginx/nginx-418-server.sock",
},
- },
- context: "spec",
- expected: policiesCfg{
- IngressMTLS: &version2.IngressMTLS{
- ClientCert: mTLSCertPath,
- ClientCrl: mTLSCrlPath,
- VerifyClient: "off",
- VerifyDepth: 1,
- },
- },
- msg: "ingressMTLS reference with ca.crl field in secret",
- },
- {
- policyRefs: []conf_v1.PolicyReference{
{
- Name: "ingress-mtls-policy-crl",
- Namespace: "default",
- },
- },
- policies: map[string]*conf_v1.Policy{
- "default/ingress-mtls-policy-crl": {
- ObjectMeta: meta_v1.ObjectMeta{
- Name: "ingress-mtls-policy-crl",
- Namespace: "default",
- },
- Spec: conf_v1.PolicySpec{
- IngressMTLS: &conf_v1.IngressMTLS{
- ClientCertSecret: "ingress-mtls-secret",
- CrlFileName: "default-ingress-mtls-secret-ca.crl",
- VerifyClient: "off",
+ Path: "/internal_location_splits_0_split_1",
+ ProxyInterceptErrors: true,
+ ErrorPages: []version2.ErrorPage{
+ {
+ Name: "@return_2",
+ Codes: "418",
+ ResponseCode: 200,
},
},
+ InternalProxyPass: "http://unix:/var/lib/nginx/nginx-418-server.sock",
},
- },
- context: "spec",
- expected: policiesCfg{
- IngressMTLS: &version2.IngressMTLS{
- ClientCert: mTLSCertPath,
- ClientCrl: mTLSCrlPath,
- VerifyClient: "off",
- VerifyDepth: 1,
- },
- },
- msg: "ingressMTLS reference with crl field in policy",
- },
- {
- policyRefs: []conf_v1.PolicyReference{
{
- Name: "egress-mtls-policy",
- Namespace: "default",
- },
- },
- policies: map[string]*conf_v1.Policy{
- "default/egress-mtls-policy": {
- Spec: conf_v1.PolicySpec{
- EgressMTLS: &conf_v1.EgressMTLS{
- TLSSecret: "egress-mtls-secret",
- ServerName: true,
- SessionReuse: createPointerFromBool(false),
- TrustedCertSecret: "egress-trusted-ca-secret",
+ Path: "/internal_location_matches_0_match_0",
+ ProxyInterceptErrors: true,
+ ErrorPages: []version2.ErrorPage{
+ {
+ Name: "@return_3",
+ Codes: "418",
+ ResponseCode: 200,
},
},
+ InternalProxyPass: "http://unix:/var/lib/nginx/nginx-418-server.sock",
},
- },
- context: "route",
- expected: policiesCfg{
- EgressMTLS: &version2.EgressMTLS{
- Certificate: "/etc/nginx/secrets/default-egress-mtls-secret",
- CertificateKey: "/etc/nginx/secrets/default-egress-mtls-secret",
- Ciphers: "DEFAULT",
- Protocols: "TLSv1 TLSv1.1 TLSv1.2",
- ServerName: true,
- SessionReuse: false,
- VerifyDepth: 1,
- VerifyServer: false,
- TrustedCert: "/etc/nginx/secrets/default-egress-trusted-ca-secret",
- SSLName: "$proxy_host",
- },
- },
- msg: "egressMTLS reference",
- },
- {
- policyRefs: []conf_v1.PolicyReference{
{
- Name: "egress-mtls-policy",
- Namespace: "default",
- },
- },
- policies: map[string]*conf_v1.Policy{
- "default/egress-mtls-policy": {
- Spec: conf_v1.PolicySpec{
- EgressMTLS: &conf_v1.EgressMTLS{
- TLSSecret: "egress-mtls-secret",
- ServerName: true,
- SessionReuse: createPointerFromBool(false),
- TrustedCertSecret: "egress-trusted-ca-secret-crl",
+ Path: "/internal_location_matches_0_default",
+ ProxyInterceptErrors: true,
+ ErrorPages: []version2.ErrorPage{
+ {
+ Name: "@return_4",
+ Codes: "418",
+ ResponseCode: 200,
},
},
+ InternalProxyPass: "http://unix:/var/lib/nginx/nginx-418-server.sock",
},
- },
- context: "route",
- expected: policiesCfg{
- EgressMTLS: &version2.EgressMTLS{
- Certificate: "/etc/nginx/secrets/default-egress-mtls-secret",
- CertificateKey: "/etc/nginx/secrets/default-egress-mtls-secret",
- Ciphers: "DEFAULT",
- Protocols: "TLSv1 TLSv1.1 TLSv1.2",
- ServerName: true,
- SessionReuse: false,
- VerifyDepth: 1,
- VerifyServer: false,
- TrustedCert: mTLSCertPath,
- SSLName: "$proxy_host",
- },
- },
- msg: "egressMTLS with crt and crl",
- },
- {
- policyRefs: []conf_v1.PolicyReference{
{
- Name: "oidc-policy",
- Namespace: "default",
- },
- },
- policies: map[string]*conf_v1.Policy{
- "default/oidc-policy": {
- ObjectMeta: meta_v1.ObjectMeta{
- Name: "oidc-policy",
- Namespace: "default",
- },
- Spec: conf_v1.PolicySpec{
- OIDC: &conf_v1.OIDC{
- AuthEndpoint: "http://example.com/auth",
- TokenEndpoint: "http://example.com/token",
- JWKSURI: "http://example.com/jwks",
- ClientID: "client-id",
- ClientSecret: "oidc-secret",
- Scope: "scope",
- RedirectURI: "/redirect",
- ZoneSyncLeeway: createPointerFromInt(20),
- AccessTokenEnable: true,
+ Path: "/more/return",
+ ProxyInterceptErrors: true,
+ ErrorPages: []version2.ErrorPage{
+ {
+ Name: "@return_5",
+ Codes: "418",
+ ResponseCode: 200,
},
},
+ InternalProxyPass: "http://unix:/var/lib/nginx/nginx-418-server.sock",
},
- },
- expected: policiesCfg{
- OIDC: true,
- },
- msg: "oidc reference",
- },
- {
- policyRefs: []conf_v1.PolicyReference{
{
- Name: "waf-policy",
- Namespace: "default",
- },
- },
- policies: map[string]*conf_v1.Policy{
- "default/waf-policy": {
- Spec: conf_v1.PolicySpec{
- WAF: &conf_v1.WAF{
- Enable: true,
- ApPolicy: "default/dataguard-alarm",
- SecurityLog: &conf_v1.SecurityLog{
- Enable: true,
- ApLogConf: "default/logconf",
- LogDest: "syslog:server=127.0.0.1:514",
- },
+ Path: "/internal_location_splits_1_split_0",
+ ProxyInterceptErrors: true,
+ ErrorPages: []version2.ErrorPage{
+ {
+ Name: "@return_6",
+ Codes: "418",
+ ResponseCode: 200,
},
},
+ InternalProxyPass: "http://unix:/var/lib/nginx/nginx-418-server.sock",
},
- },
- context: "route",
- expected: policiesCfg{
- WAF: &version2.WAF{
- Enable: "on",
- ApPolicy: "/etc/nginx/waf/nac-policies/default-dataguard-alarm",
- ApSecurityLogEnable: true,
- ApLogConf: []string{"/etc/nginx/waf/nac-logconfs/default-logconf syslog:server=127.0.0.1:514"},
+ {
+ Path: "/internal_location_splits_1_split_1",
+ ProxyInterceptErrors: true,
+ ErrorPages: []version2.ErrorPage{
+ {
+ Name: "@return_7",
+ Codes: "418",
+ ResponseCode: 200,
+ },
+ },
+ InternalProxyPass: "http://unix:/var/lib/nginx/nginx-418-server.sock",
},
- },
- msg: "WAF reference",
- },
- }
-
- vsc := newVirtualServerConfigurator(&ConfigParams{}, false, false, &StaticConfigParams{}, false)
-
- for _, test := range tests {
- result := vsc.generatePolicies(ownerDetails, test.policyRefs, test.policies, test.context, policyOpts)
- if diff := cmp.Diff(test.expected, result); diff != "" {
- t.Errorf("generatePolicies() '%v' mismatch (-want +got):\n%s", test.msg, diff)
- }
- if len(vsc.warnings) > 0 {
- t.Errorf("generatePolicies() returned unexpected warnings %v for the case of %s", vsc.warnings, test.msg)
- }
- }
-}
-
-func TestGeneratePolicies_GeneratesWAFPolicyOnValidApBundle(t *testing.T) {
- t.Parallel()
-
- ownerDetails := policyOwnerDetails{
- owner: nil, // nil is OK for the unit test
- ownerNamespace: "default",
- vsNamespace: "default",
- vsName: "test",
- }
-
- test := struct {
- policyRefs []conf_v1.PolicyReference
- policies map[string]*conf_v1.Policy
- policyOpts policyOptions
- context string
- want policiesCfg
- }{
- policyRefs: []conf_v1.PolicyReference{
- {
- Name: "waf-bundle",
- Namespace: "default",
- },
- },
- policies: map[string]*conf_v1.Policy{
- "default/waf-bundle": {
- Spec: conf_v1.PolicySpec{
- WAF: &conf_v1.WAF{
- Enable: true,
- ApBundle: "testWAFPolicyBundle.tgz",
+ {
+ Path: "/internal_location_matches_1_match_0",
+ ProxyInterceptErrors: true,
+ ErrorPages: []version2.ErrorPage{
+ {
+ Name: "@return_8",
+ Codes: "418",
+ ResponseCode: 200,
+ },
+ },
+ InternalProxyPass: "http://unix:/var/lib/nginx/nginx-418-server.sock",
+ },
+ {
+ Path: "/internal_location_matches_1_default",
+ ProxyInterceptErrors: true,
+ ErrorPages: []version2.ErrorPage{
+ {
+ Name: "@return_9",
+ Codes: "418",
+ ResponseCode: 200,
+ },
},
+ InternalProxyPass: "http://unix:/var/lib/nginx/nginx-418-server.sock",
},
},
},
- context: "route",
}
- vsc := newVirtualServerConfigurator(&ConfigParams{}, false, false, &StaticConfigParams{}, false)
- want := policiesCfg{
- WAF: &version2.WAF{
- Enable: "on",
- ApBundle: "/etc/nginx/waf/bundles/testWAFPolicyBundle.tgz",
- },
+ isPlus := false
+ isResolverConfigured := false
+ isWildcardEnabled := false
+ vsc := newVirtualServerConfigurator(&baseCfgParams, isPlus, isResolverConfigured, &StaticConfigParams{}, isWildcardEnabled)
+
+ result, warnings := vsc.GenerateVirtualServerConfig(&virtualServerEx, nil, nil)
+ if !reflect.DeepEqual(result, expected) {
+ t.Errorf("GenerateVirtualServerConfig returned \n%+v but expected \n%+v", result, expected)
}
- got := vsc.generatePolicies(ownerDetails, test.policyRefs, test.policies, test.context, policyOptions{})
- if !cmp.Equal(want, got) {
- t.Error(cmp.Diff(want, got))
+
+ if len(warnings) != 0 {
+ t.Errorf("GenerateVirtualServerConfig returned warnings: %v", vsc.warnings)
}
}
-func TestGeneratePoliciesFails(t *testing.T) {
+func TestGenerateVirtualServerConfigJWKSPolicy(t *testing.T) {
t.Parallel()
- ownerDetails := policyOwnerDetails{
- owner: nil, // nil is OK for the unit test
- ownerNamespace: "default",
- vsNamespace: "default",
- vsName: "test",
- }
-
- dryRunOverride := true
- rejectCodeOverride := 505
-
- ingressMTLSCertPath := "/etc/nginx/secrets/default-ingress-mtls-secret-ca.crt"
- ingressMTLSCrlPath := "/etc/nginx/secrets/default-ingress-mtls-secret-ca.crl"
- tests := []struct {
- policyRefs []conf_v1.PolicyReference
- policies map[string]*conf_v1.Policy
- policyOpts policyOptions
- trustedCAFileName string
- context string
- oidcPolCfg *oidcPolicyCfg
- expected policiesCfg
- expectedWarnings Warnings
- expectedOidc *oidcPolicyCfg
- msg string
- }{
- {
- policyRefs: []conf_v1.PolicyReference{
- {
- Name: "allow-policy",
- Namespace: "default",
- },
- },
- policies: map[string]*conf_v1.Policy{},
- policyOpts: policyOptions{},
- expected: policiesCfg{
- ErrorReturn: &version2.Return{
- Code: 500,
- },
- },
- expectedWarnings: Warnings{
- nil: {
- "Policy default/allow-policy is missing or invalid",
- },
+ virtualServerEx := VirtualServerEx{
+ VirtualServer: &conf_v1.VirtualServer{
+ ObjectMeta: meta_v1.ObjectMeta{
+ Name: "cafe",
+ Namespace: "default",
},
- expectedOidc: &oidcPolicyCfg{},
- msg: "missing policy",
- },
- {
- policyRefs: []conf_v1.PolicyReference{
- {
- Name: "allow-policy",
+ Spec: conf_v1.VirtualServerSpec{
+ Host: "cafe.example.com",
+ Policies: []conf_v1.PolicyReference{
+ {
+ Name: "jwt-policy",
+ },
},
- {
- Name: "deny-policy",
+ Upstreams: []conf_v1.Upstream{
+ {
+ Name: "tea",
+ Service: "tea-svc",
+ Port: 80,
+ },
+ {
+ Name: "coffee",
+ Service: "coffee-svc",
+ Port: 80,
+ },
},
- },
- policies: map[string]*conf_v1.Policy{
- "default/allow-policy": {
- Spec: conf_v1.PolicySpec{
- AccessControl: &conf_v1.AccessControl{
- Allow: []string{"127.0.0.1"},
+ Routes: []conf_v1.Route{
+ {
+ Path: "/tea",
+ Action: &conf_v1.Action{
+ Pass: "tea",
+ },
+ Policies: []conf_v1.PolicyReference{
+ {
+ Name: "jwt-policy-route",
+ },
},
},
- },
- "default/deny-policy": {
- Spec: conf_v1.PolicySpec{
- AccessControl: &conf_v1.AccessControl{
- Deny: []string{"127.0.0.2"},
+ {
+ Path: "/coffee",
+ Action: &conf_v1.Action{
+ Pass: "coffee",
+ },
+ Policies: []conf_v1.PolicyReference{
+ {
+ Name: "jwt-policy-route",
+ },
},
},
},
},
- policyOpts: policyOptions{},
- expected: policiesCfg{
- Allow: []string{"127.0.0.1"},
- Deny: []string{"127.0.0.2"},
- },
- expectedWarnings: Warnings{
- nil: {
- "AccessControl policy (or policies) with deny rules is overridden by policy (or policies) with allow rules",
- },
- },
- expectedOidc: &oidcPolicyCfg{},
- msg: "conflicting policies",
},
- {
- policyRefs: []conf_v1.PolicyReference{
- {
- Name: "rateLimit-policy",
+ Policies: map[string]*conf_v1.Policy{
+ "default/jwt-policy": {
+ ObjectMeta: meta_v1.ObjectMeta{
+ Name: "jwt-policy",
Namespace: "default",
},
- {
- Name: "rateLimit-policy2",
- Namespace: "default",
+ Spec: conf_v1.PolicySpec{
+ JWTAuth: &conf_v1.JWTAuth{
+ Realm: "Spec Realm API",
+ JwksURI: "https://idp.spec.example.com:443/spec-keys",
+ KeyCache: "1h",
+ },
},
},
- policies: map[string]*conf_v1.Policy{
- "default/rateLimit-policy": {
- Spec: conf_v1.PolicySpec{
- RateLimit: &conf_v1.RateLimit{
- Key: "test",
- ZoneSize: "10M",
- Rate: "10r/s",
- },
- },
- },
- "default/rateLimit-policy2": {
- Spec: conf_v1.PolicySpec{
- RateLimit: &conf_v1.RateLimit{
- Key: "test2",
- ZoneSize: "20M",
- Rate: "20r/s",
- DryRun: &dryRunOverride,
- LogLevel: "info",
- RejectCode: &rejectCodeOverride,
- },
+ "default/jwt-policy-route": {
+ ObjectMeta: meta_v1.ObjectMeta{
+ Name: "jwt-policy-route",
+ Namespace: "default",
+ },
+ Spec: conf_v1.PolicySpec{
+ JWTAuth: &conf_v1.JWTAuth{
+ Realm: "Route Realm API",
+ JwksURI: "http://idp.route.example.com:80/route-keys",
+ KeyCache: "1h",
},
},
},
- policyOpts: policyOptions{},
- expected: policiesCfg{
- LimitReqZones: []version2.LimitReqZone{
- {
- Key: "test",
- ZoneSize: "10M",
- Rate: "10r/s",
- ZoneName: "pol_rl_default_rateLimit-policy_default_test",
- },
+ },
+ Endpoints: map[string][]string{
+ "default/tea-svc:80": {
+ "10.0.0.20:80",
+ },
+ "default/coffee-svc:80": {
+ "10.0.0.30:80",
+ },
+ },
+ }
+
+ expected := version2.VirtualServerConfig{
+ Upstreams: []version2.Upstream{
+ {
+ UpstreamLabels: version2.UpstreamLabels{
+ Service: "tea-svc",
+ ResourceType: "virtualserver",
+ ResourceName: "cafe",
+ ResourceNamespace: "default",
+ },
+ Name: "vs_default_cafe_tea",
+ Servers: []version2.UpstreamServer{
{
- Key: "test2",
- ZoneSize: "20M",
- Rate: "20r/s",
- ZoneName: "pol_rl_default_rateLimit-policy2_default_test",
+ Address: "10.0.0.20:80",
},
},
- LimitReqOptions: version2.LimitReqOptions{
- LogLevel: "error",
- RejectCode: 503,
+ Keepalive: 16,
+ },
+ {
+ UpstreamLabels: version2.UpstreamLabels{
+ Service: "coffee-svc",
+ ResourceType: "virtualserver",
+ ResourceName: "cafe",
+ ResourceNamespace: "default",
},
- LimitReqs: []version2.LimitReq{
- {
- ZoneName: "pol_rl_default_rateLimit-policy_default_test",
- },
+ Name: "vs_default_cafe_coffee",
+ Servers: []version2.UpstreamServer{
{
- ZoneName: "pol_rl_default_rateLimit-policy2_default_test",
+ Address: "10.0.0.30:80",
},
},
+ Keepalive: 16,
},
- expectedWarnings: Warnings{
- nil: {
- `RateLimit policy default/rateLimit-policy2 with limit request option dryRun='true' is overridden to dryRun='false' by the first policy reference in this context`,
- `RateLimit policy default/rateLimit-policy2 with limit request option logLevel='info' is overridden to logLevel='error' by the first policy reference in this context`,
- `RateLimit policy default/rateLimit-policy2 with limit request option rejectCode='505' is overridden to rejectCode='503' by the first policy reference in this context`,
- },
- },
- expectedOidc: &oidcPolicyCfg{},
- msg: "rate limit policy limit request option override",
},
- {
- policyRefs: []conf_v1.PolicyReference{
- {
- Name: "jwt-policy",
- Namespace: "default",
- },
- },
- policies: map[string]*conf_v1.Policy{
+ HTTPSnippets: []string{},
+ LimitReqZones: []version2.LimitReqZone{},
+ Server: version2.Server{
+ JWTAuthList: map[string]*version2.JWTAuth{
"default/jwt-policy": {
- ObjectMeta: meta_v1.ObjectMeta{
- Name: "jwt-policy",
- Namespace: "default",
- },
- Spec: conf_v1.PolicySpec{
- JWTAuth: &conf_v1.JWTAuth{
- Realm: "test",
- Secret: "jwt-secret",
- },
+ Key: "default/jwt-policy",
+ Realm: "Spec Realm API",
+ KeyCache: "1h",
+ JwksURI: version2.JwksURI{
+ JwksScheme: "https",
+ JwksHost: "idp.spec.example.com",
+ JwksPort: "443",
+ JwksPath: "/spec-keys",
},
},
- },
- policyOpts: policyOptions{
- secretRefs: map[string]*secrets.SecretReference{
- "default/jwt-secret": {
- Secret: &api_v1.Secret{
- Type: secrets.SecretTypeJWK,
- },
- Error: errors.New("secret is invalid"),
+ "default/jwt-policy-route": {
+ Key: "default/jwt-policy-route",
+ Realm: "Route Realm API",
+ KeyCache: "1h",
+ JwksURI: version2.JwksURI{
+ JwksScheme: "http",
+ JwksHost: "idp.route.example.com",
+ JwksPort: "80",
+ JwksPath: "/route-keys",
},
},
},
- expected: policiesCfg{
- ErrorReturn: &version2.Return{
- Code: 500,
- },
- },
- expectedWarnings: Warnings{
- nil: {
- `JWT policy default/jwt-policy references an invalid secret default/jwt-secret: secret is invalid`,
+ JWTAuth: &version2.JWTAuth{
+ Key: "default/jwt-policy",
+ Realm: "Spec Realm API",
+ KeyCache: "1h",
+ JwksURI: version2.JwksURI{
+ JwksScheme: "https",
+ JwksHost: "idp.spec.example.com",
+ JwksPort: "443",
+ JwksPath: "/spec-keys",
},
},
- expectedOidc: &oidcPolicyCfg{},
- msg: "jwt reference missing secret",
- },
- {
- policyRefs: []conf_v1.PolicyReference{
+ JWKSAuthEnabled: true,
+ ServerName: "cafe.example.com",
+ StatusZone: "cafe.example.com",
+ ProxyProtocol: true,
+ ServerTokens: "off",
+ RealIPHeader: "X-Real-IP",
+ SetRealIPFrom: []string{"0.0.0.0/0"},
+ RealIPRecursive: true,
+ Snippets: []string{"# server snippet"},
+ TLSPassthrough: true,
+ VSNamespace: "default",
+ VSName: "cafe",
+ Locations: []version2.Location{
{
- Name: "jwt-policy",
- Namespace: "default",
- },
- },
- policies: map[string]*conf_v1.Policy{
- "default/jwt-policy": {
- ObjectMeta: meta_v1.ObjectMeta{
- Name: "jwt-policy",
- Namespace: "default",
- },
- Spec: conf_v1.PolicySpec{
- JWTAuth: &conf_v1.JWTAuth{
- Realm: "test",
- Secret: "jwt-secret",
+ Path: "/tea",
+ ProxyPass: "http://vs_default_cafe_tea",
+ ProxyNextUpstream: "error timeout",
+ ProxyNextUpstreamTimeout: "0s",
+ ProxyNextUpstreamTries: 0,
+ HasKeepalive: true,
+ ProxySSLName: "tea-svc.default.svc",
+ ProxyPassRequestHeaders: true,
+ ProxySetHeaders: []version2.Header{{Name: "Host", Value: "$host"}},
+ ServiceName: "tea-svc",
+ JWTAuth: &version2.JWTAuth{
+ Key: "default/jwt-policy-route",
+ Realm: "Route Realm API",
+ KeyCache: "1h",
+ JwksURI: version2.JwksURI{
+ JwksScheme: "http",
+ JwksHost: "idp.route.example.com",
+ JwksPort: "80",
+ JwksPath: "/route-keys",
},
},
},
- },
- policyOpts: policyOptions{
- secretRefs: map[string]*secrets.SecretReference{
- "default/jwt-secret": {
- Secret: &api_v1.Secret{
- Type: secrets.SecretTypeCA,
+ {
+ Path: "/coffee",
+ ProxyPass: "http://vs_default_cafe_coffee",
+ ProxyNextUpstream: "error timeout",
+ ProxyNextUpstreamTimeout: "0s",
+ ProxyNextUpstreamTries: 0,
+ HasKeepalive: true,
+ ProxySSLName: "coffee-svc.default.svc",
+ ProxyPassRequestHeaders: true,
+ ProxySetHeaders: []version2.Header{{Name: "Host", Value: "$host"}},
+ ServiceName: "coffee-svc",
+ JWTAuth: &version2.JWTAuth{
+ Key: "default/jwt-policy-route",
+ Realm: "Route Realm API",
+ KeyCache: "1h",
+ JwksURI: version2.JwksURI{
+ JwksScheme: "http",
+ JwksHost: "idp.route.example.com",
+ JwksPort: "80",
+ JwksPath: "/route-keys",
},
},
},
},
- expected: policiesCfg{
- ErrorReturn: &version2.Return{
- Code: 500,
- },
- },
- expectedWarnings: Warnings{
- nil: {
- `JWT policy default/jwt-policy references a secret default/jwt-secret of a wrong type 'nginx.org/ca', must be 'nginx.org/jwk'`,
- },
- },
- expectedOidc: &oidcPolicyCfg{},
- msg: "jwt references wrong secret type",
},
- {
- policyRefs: []conf_v1.PolicyReference{
- {
- Name: "jwt-policy",
- Namespace: "default",
- },
- {
- Name: "jwt-policy2",
- Namespace: "default",
- },
- },
- policies: map[string]*conf_v1.Policy{
- "default/jwt-policy": {
- ObjectMeta: meta_v1.ObjectMeta{
- Name: "jwt-policy",
- Namespace: "default",
- },
- Spec: conf_v1.PolicySpec{
- JWTAuth: &conf_v1.JWTAuth{
- Realm: "test",
- Secret: "jwt-secret",
- },
- },
- },
- "default/jwt-policy2": {
- ObjectMeta: meta_v1.ObjectMeta{
- Name: "jwt-policy2",
- Namespace: "default",
- },
- Spec: conf_v1.PolicySpec{
- JWTAuth: &conf_v1.JWTAuth{
- Realm: "test",
- Secret: "jwt-secret2",
- },
- },
+ }
+
+ baseCfgParams := ConfigParams{
+ ServerTokens: "off",
+ Keepalive: 16,
+ ServerSnippets: []string{"# server snippet"},
+ ProxyProtocol: true,
+ SetRealIPFrom: []string{"0.0.0.0/0"},
+ RealIPHeader: "X-Real-IP",
+ RealIPRecursive: true,
+ }
+
+ vsc := newVirtualServerConfigurator(
+ &baseCfgParams,
+ false,
+ false,
+ &StaticConfigParams{TLSPassthrough: true},
+ false,
+ )
+
+ result, warnings := vsc.GenerateVirtualServerConfig(&virtualServerEx, nil, nil)
+
+ if diff := cmp.Diff(expected, result); diff != "" {
+ t.Errorf("GenerateVirtualServerConfig() mismatch (-want +got):\n%s", diff)
+ }
+
+ if len(warnings) != 0 {
+ t.Errorf("GenerateVirtualServerConfig returned warnings: %v", vsc.warnings)
+ }
+}
+
+func TestGeneratePolicies(t *testing.T) {
+ t.Parallel()
+ ownerDetails := policyOwnerDetails{
+ owner: nil, // nil is OK for the unit test
+ ownerNamespace: "default",
+ vsNamespace: "default",
+ vsName: "test",
+ }
+ mTLSCertPath := "/etc/nginx/secrets/default-ingress-mtls-secret-ca.crt"
+ mTLSCrlPath := "/etc/nginx/secrets/default-ingress-mtls-secret-ca.crl"
+ mTLSCertAndCrlPath := fmt.Sprintf("%s %s", mTLSCertPath, mTLSCrlPath)
+ policyOpts := policyOptions{
+ tls: true,
+ secretRefs: map[string]*secrets.SecretReference{
+ "default/ingress-mtls-secret": {
+ Secret: &api_v1.Secret{
+ Type: secrets.SecretTypeCA,
},
+ Path: mTLSCertPath,
},
- policyOpts: policyOptions{
- secretRefs: map[string]*secrets.SecretReference{
- "default/jwt-secret": {
- Secret: &api_v1.Secret{
- Type: secrets.SecretTypeJWK,
- },
- Path: "/etc/nginx/secrets/default-jwt-secret",
- },
- "default/jwt-secret2": {
- Secret: &api_v1.Secret{
- Type: secrets.SecretTypeJWK,
- },
- Path: "/etc/nginx/secrets/default-jwt-secret2",
+ "default/ingress-mtls-secret-crl": {
+ Secret: &api_v1.Secret{
+ Type: secrets.SecretTypeCA,
+ Data: map[string][]byte{
+ "ca.crl": []byte("base64crl"),
},
},
+ Path: mTLSCertAndCrlPath,
},
- expected: policiesCfg{
- JWTAuth: &version2.JWTAuth{
- Secret: "/etc/nginx/secrets/default-jwt-secret",
- Realm: "test",
+ "default/egress-mtls-secret": {
+ Secret: &api_v1.Secret{
+ Type: api_v1.SecretTypeTLS,
},
+ Path: "/etc/nginx/secrets/default-egress-mtls-secret",
},
- expectedWarnings: Warnings{
- nil: {
- `Multiple jwt policies in the same context is not valid. JWT policy default/jwt-policy2 will be ignored`,
+ "default/egress-trusted-ca-secret": {
+ Secret: &api_v1.Secret{
+ Type: secrets.SecretTypeCA,
},
+ Path: "/etc/nginx/secrets/default-egress-trusted-ca-secret",
+ },
+ "default/egress-trusted-ca-secret-crl": {
+ Secret: &api_v1.Secret{
+ Type: secrets.SecretTypeCA,
+ },
+ Path: mTLSCertAndCrlPath,
+ },
+ "default/jwt-secret": {
+ Secret: &api_v1.Secret{
+ Type: secrets.SecretTypeJWK,
+ },
+ Path: "/etc/nginx/secrets/default-jwt-secret",
+ },
+ "default/htpasswd-secret": {
+ Secret: &api_v1.Secret{
+ Type: secrets.SecretTypeHtpasswd,
+ },
+ Path: "/etc/nginx/secrets/default-htpasswd-secret",
+ },
+ "default/oidc-secret": {
+ Secret: &api_v1.Secret{
+ Type: secrets.SecretTypeOIDC,
+ Data: map[string][]byte{
+ "client-secret": []byte("super_secret_123"),
+ },
+ },
+ },
+ },
+ apResources: &appProtectResourcesForVS{
+ Policies: map[string]string{
+ "default/dataguard-alarm": "/etc/nginx/waf/nac-policies/default-dataguard-alarm",
+ },
+ LogConfs: map[string]string{
+ "default/logconf": "/etc/nginx/waf/nac-logconfs/default-logconf",
},
- expectedOidc: &oidcPolicyCfg{},
- msg: "multi jwt reference",
},
+ }
+
+ tests := []struct {
+ policyRefs []conf_v1.PolicyReference
+ policies map[string]*conf_v1.Policy
+ context string
+ expected policiesCfg
+ msg string
+ }{
{
policyRefs: []conf_v1.PolicyReference{
{
- Name: "basic-auth-policy",
+ Name: "allow-policy",
Namespace: "default",
},
},
policies: map[string]*conf_v1.Policy{
- "default/basic-auth-policy": {
- ObjectMeta: meta_v1.ObjectMeta{
- Name: "basic-auth-policy",
- Namespace: "default",
- },
+ "default/allow-policy": {
Spec: conf_v1.PolicySpec{
- BasicAuth: &conf_v1.BasicAuth{
- Realm: "test",
- Secret: "htpasswd-secret",
+ AccessControl: &conf_v1.AccessControl{
+ Allow: []string{"127.0.0.1"},
},
},
},
},
- policyOpts: policyOptions{
- secretRefs: map[string]*secrets.SecretReference{
- "default/htpasswd-secret": {
- Secret: &api_v1.Secret{
- Type: secrets.SecretTypeHtpasswd,
+ expected: policiesCfg{
+ Allow: []string{"127.0.0.1"},
+ },
+ msg: "explicit reference",
+ },
+ {
+ policyRefs: []conf_v1.PolicyReference{
+ {
+ Name: "allow-policy",
+ },
+ },
+ policies: map[string]*conf_v1.Policy{
+ "default/allow-policy": {
+ Spec: conf_v1.PolicySpec{
+ AccessControl: &conf_v1.AccessControl{
+ Allow: []string{"127.0.0.1"},
},
- Error: errors.New("secret is invalid"),
},
},
},
expected: policiesCfg{
- ErrorReturn: &version2.Return{
- Code: 500,
- },
- },
- expectedWarnings: Warnings{
- nil: {
- `Basic Auth policy default/basic-auth-policy references an invalid secret default/htpasswd-secret: secret is invalid`,
- },
+ Allow: []string{"127.0.0.1"},
},
- expectedOidc: &oidcPolicyCfg{},
- msg: "basic auth reference missing secret",
+ msg: "implicit reference",
},
{
policyRefs: []conf_v1.PolicyReference{
{
- Name: "basic-auth-policy",
- Namespace: "default",
+ Name: "allow-policy-1",
+ },
+ {
+ Name: "allow-policy-2",
},
},
policies: map[string]*conf_v1.Policy{
- "default/basic-auth-policy": {
- ObjectMeta: meta_v1.ObjectMeta{
- Name: "basic-auth-policy",
- Namespace: "default",
+ "default/allow-policy-1": {
+ Spec: conf_v1.PolicySpec{
+ AccessControl: &conf_v1.AccessControl{
+ Allow: []string{"127.0.0.1"},
+ },
},
+ },
+ "default/allow-policy-2": {
Spec: conf_v1.PolicySpec{
- BasicAuth: &conf_v1.BasicAuth{
- Realm: "test",
- Secret: "htpasswd-secret",
+ AccessControl: &conf_v1.AccessControl{
+ Allow: []string{"127.0.0.2"},
},
},
},
},
- policyOpts: policyOptions{
- secretRefs: map[string]*secrets.SecretReference{
- "default/htpasswd-secret": {
- Secret: &api_v1.Secret{
- Type: secrets.SecretTypeCA,
+ expected: policiesCfg{
+ Allow: []string{"127.0.0.1", "127.0.0.2"},
+ },
+ msg: "merging",
+ },
+ {
+ policyRefs: []conf_v1.PolicyReference{
+ {
+ Name: "rateLimit-policy",
+ Namespace: "default",
+ },
+ },
+ policies: map[string]*conf_v1.Policy{
+ "default/rateLimit-policy": {
+ Spec: conf_v1.PolicySpec{
+ RateLimit: &conf_v1.RateLimit{
+ Key: "test",
+ ZoneSize: "10M",
+ Rate: "10r/s",
+ LogLevel: "notice",
},
},
},
},
expected: policiesCfg{
- ErrorReturn: &version2.Return{
- Code: 500,
+ LimitReqZones: []version2.LimitReqZone{
+ {
+ Key: "test",
+ ZoneSize: "10M",
+ Rate: "10r/s",
+ ZoneName: "pol_rl_default_rateLimit-policy_default_test",
+ },
},
- },
- expectedWarnings: Warnings{
- nil: {
- `Basic Auth policy default/basic-auth-policy references a secret default/htpasswd-secret of a wrong type 'nginx.org/ca', must be 'nginx.org/htpasswd'`,
+ LimitReqOptions: version2.LimitReqOptions{
+ LogLevel: "notice",
+ RejectCode: 503,
+ },
+ LimitReqs: []version2.LimitReq{
+ {
+ ZoneName: "pol_rl_default_rateLimit-policy_default_test",
+ },
},
},
- expectedOidc: &oidcPolicyCfg{},
- msg: "basic auth references wrong secret type",
+ msg: "rate limit reference",
},
{
policyRefs: []conf_v1.PolicyReference{
{
- Name: "basic-auth-policy",
+ Name: "rateLimit-policy",
Namespace: "default",
},
{
- Name: "basic-auth-policy2",
+ Name: "rateLimit-policy2",
Namespace: "default",
},
},
policies: map[string]*conf_v1.Policy{
- "default/basic-auth-policy": {
- ObjectMeta: meta_v1.ObjectMeta{
- Name: "basic-auth-policy",
- Namespace: "default",
- },
+ "default/rateLimit-policy": {
Spec: conf_v1.PolicySpec{
- BasicAuth: &conf_v1.BasicAuth{
- Realm: "test",
- Secret: "htpasswd-secret",
+ RateLimit: &conf_v1.RateLimit{
+ Key: "test",
+ ZoneSize: "10M",
+ Rate: "10r/s",
},
},
},
- "default/basic-auth-policy2": {
- ObjectMeta: meta_v1.ObjectMeta{
- Name: "basic-auth-policy2",
- Namespace: "default",
- },
+ "default/rateLimit-policy2": {
Spec: conf_v1.PolicySpec{
- BasicAuth: &conf_v1.BasicAuth{
- Realm: "test",
- Secret: "htpasswd-secret2",
+ RateLimit: &conf_v1.RateLimit{
+ Key: "test2",
+ ZoneSize: "20M",
+ Rate: "20r/s",
},
},
},
},
- policyOpts: policyOptions{
- secretRefs: map[string]*secrets.SecretReference{
- "default/htpasswd-secret": {
- Secret: &api_v1.Secret{
- Type: secrets.SecretTypeHtpasswd,
- },
- Path: "/etc/nginx/secrets/default-htpasswd-secret",
+ expected: policiesCfg{
+ LimitReqZones: []version2.LimitReqZone{
+ {
+ Key: "test",
+ ZoneSize: "10M",
+ Rate: "10r/s",
+ ZoneName: "pol_rl_default_rateLimit-policy_default_test",
},
- "default/htpasswd-secret2": {
- Secret: &api_v1.Secret{
- Type: secrets.SecretTypeHtpasswd,
- },
- Path: "/etc/nginx/secrets/default-htpasswd-secret2",
+ {
+ Key: "test2",
+ ZoneSize: "20M",
+ Rate: "20r/s",
+ ZoneName: "pol_rl_default_rateLimit-policy2_default_test",
},
},
- },
- expected: policiesCfg{
- BasicAuth: &version2.BasicAuth{
- Secret: "/etc/nginx/secrets/default-htpasswd-secret",
- Realm: "test",
+ LimitReqOptions: version2.LimitReqOptions{
+ LogLevel: "error",
+ RejectCode: 503,
},
- },
- expectedWarnings: Warnings{
- nil: {
- `Multiple basic auth policies in the same context is not valid. Basic auth policy default/basic-auth-policy2 will be ignored`,
+ LimitReqs: []version2.LimitReq{
+ {
+ ZoneName: "pol_rl_default_rateLimit-policy_default_test",
+ },
+ {
+ ZoneName: "pol_rl_default_rateLimit-policy2_default_test",
+ },
},
},
- expectedOidc: &oidcPolicyCfg{},
- msg: "multi basic auth reference",
+ msg: "multi rate limit reference",
},
{
policyRefs: []conf_v1.PolicyReference{
{
- Name: "ingress-mtls-policy",
+ Name: "jwt-policy",
Namespace: "default",
},
},
policies: map[string]*conf_v1.Policy{
- "default/ingress-mtls-policy": {
+ "default/jwt-policy": {
ObjectMeta: meta_v1.ObjectMeta{
- Name: "ingress-mtls-policy",
+ Name: "jwt-policy",
Namespace: "default",
},
Spec: conf_v1.PolicySpec{
- IngressMTLS: &conf_v1.IngressMTLS{
- ClientCertSecret: "ingress-mtls-secret",
+ JWTAuth: &conf_v1.JWTAuth{
+ Realm: "My Test API",
+ Secret: "jwt-secret",
},
},
},
},
- policyOpts: policyOptions{
- tls: true,
- secretRefs: map[string]*secrets.SecretReference{
- "default/ingress-mtls-secret": {
- Error: errors.New("secret is invalid"),
- },
- },
- },
- context: "spec",
expected: policiesCfg{
- ErrorReturn: &version2.Return{
- Code: 500,
- },
- },
- expectedWarnings: Warnings{
- nil: {
- `IngressMTLS policy "default/ingress-mtls-policy" references an invalid secret default/ingress-mtls-secret: secret is invalid`,
+ JWTAuth: &version2.JWTAuth{
+ Secret: "/etc/nginx/secrets/default-jwt-secret",
+ Realm: "My Test API",
},
+ JWKSAuthEnabled: false,
},
- expectedOidc: &oidcPolicyCfg{},
- msg: "ingress mtls reference an invalid secret",
+ msg: "jwt reference",
},
{
policyRefs: []conf_v1.PolicyReference{
{
- Name: "ingress-mtls-policy",
+ Name: "jwt-policy-2",
Namespace: "default",
},
},
policies: map[string]*conf_v1.Policy{
- "default/ingress-mtls-policy": {
+ "default/jwt-policy-2": {
ObjectMeta: meta_v1.ObjectMeta{
- Name: "ingress-mtls-policy",
+ Name: "jwt-policy-2",
Namespace: "default",
},
Spec: conf_v1.PolicySpec{
- IngressMTLS: &conf_v1.IngressMTLS{
- ClientCertSecret: "ingress-mtls-secret",
- },
- },
- },
- },
- policyOpts: policyOptions{
- tls: true,
- secretRefs: map[string]*secrets.SecretReference{
- "default/ingress-mtls-secret": {
- Secret: &api_v1.Secret{
- Type: api_v1.SecretTypeTLS,
+ JWTAuth: &conf_v1.JWTAuth{
+ Realm: "My Test API",
+ JwksURI: "https://idp.example.com:443/keys",
+ KeyCache: "1h",
},
},
},
},
- context: "spec",
expected: policiesCfg{
- ErrorReturn: &version2.Return{
- Code: 500,
- },
- },
- expectedWarnings: Warnings{
- nil: {
- `IngressMTLS policy default/ingress-mtls-policy references a secret default/ingress-mtls-secret of a wrong type 'kubernetes.io/tls', must be 'nginx.org/ca'`,
+ JWTAuth: &version2.JWTAuth{
+ Key: "default/jwt-policy-2",
+ Realm: "My Test API",
+ JwksURI: version2.JwksURI{
+ JwksScheme: "https",
+ JwksHost: "idp.example.com",
+ JwksPort: "443",
+ JwksPath: "/keys",
+ },
+ KeyCache: "1h",
},
+ JWKSAuthEnabled: true,
},
- expectedOidc: &oidcPolicyCfg{},
- msg: "ingress mtls references wrong secret type",
+ msg: "Basic jwks example",
},
{
policyRefs: []conf_v1.PolicyReference{
{
- Name: "ingress-mtls-policy",
- Namespace: "default",
- },
- {
- Name: "ingress-mtls-policy2",
+ Name: "jwt-policy-2",
Namespace: "default",
},
},
policies: map[string]*conf_v1.Policy{
- "default/ingress-mtls-policy": {
+ "default/jwt-policy-2": {
ObjectMeta: meta_v1.ObjectMeta{
- Name: "ingress-mtls-policy",
+ Name: "jwt-policy-2",
Namespace: "default",
},
Spec: conf_v1.PolicySpec{
- IngressMTLS: &conf_v1.IngressMTLS{
- ClientCertSecret: "ingress-mtls-secret",
- },
- },
- },
- "default/ingress-mtls-policy2": {
- Spec: conf_v1.PolicySpec{
- IngressMTLS: &conf_v1.IngressMTLS{
- ClientCertSecret: "ingress-mtls-secret2",
- },
- },
- },
- },
- policyOpts: policyOptions{
- tls: true,
- secretRefs: map[string]*secrets.SecretReference{
- "default/ingress-mtls-secret": {
- Secret: &api_v1.Secret{
- Type: secrets.SecretTypeCA,
+ JWTAuth: &conf_v1.JWTAuth{
+ Realm: "My Test API",
+ JwksURI: "https://idp.example.com/keys",
+ KeyCache: "1h",
},
- Path: ingressMTLSCertPath,
},
},
},
- context: "spec",
expected: policiesCfg{
- IngressMTLS: &version2.IngressMTLS{
- ClientCert: ingressMTLSCertPath,
- VerifyClient: "on",
- VerifyDepth: 1,
- },
- },
- expectedWarnings: Warnings{
- nil: {
- `Multiple ingressMTLS policies are not allowed. IngressMTLS policy default/ingress-mtls-policy2 will be ignored`,
+ JWTAuth: &version2.JWTAuth{
+ Key: "default/jwt-policy-2",
+ Realm: "My Test API",
+ JwksURI: version2.JwksURI{
+ JwksScheme: "https",
+ JwksHost: "idp.example.com",
+ JwksPort: "",
+ JwksPath: "/keys",
+ },
+ KeyCache: "1h",
},
+ JWKSAuthEnabled: true,
},
- expectedOidc: &oidcPolicyCfg{},
- msg: "multi ingress mtls",
+ msg: "Basic jwks example, no port in JwksURI",
},
{
policyRefs: []conf_v1.PolicyReference{
{
- Name: "ingress-mtls-policy",
+ Name: "basic-auth-policy",
Namespace: "default",
},
},
policies: map[string]*conf_v1.Policy{
- "default/ingress-mtls-policy": {
+ "default/basic-auth-policy": {
ObjectMeta: meta_v1.ObjectMeta{
- Name: "ingress-mtls-policy",
+ Name: "basic-auth-policy",
Namespace: "default",
},
Spec: conf_v1.PolicySpec{
- IngressMTLS: &conf_v1.IngressMTLS{
- ClientCertSecret: "ingress-mtls-secret",
- },
- },
- },
- },
- policyOpts: policyOptions{
- tls: true,
- secretRefs: map[string]*secrets.SecretReference{
- "default/ingress-mtls-secret": {
- Secret: &api_v1.Secret{
- Type: secrets.SecretTypeCA,
+ BasicAuth: &conf_v1.BasicAuth{
+ Realm: "My Test API",
+ Secret: "htpasswd-secret",
},
- Path: ingressMTLSCertPath,
},
},
},
- context: "route",
expected: policiesCfg{
- ErrorReturn: &version2.Return{
- Code: 500,
- },
- },
- expectedWarnings: Warnings{
- nil: {
- `IngressMTLS policy default/ingress-mtls-policy is not allowed in the route context`,
+ BasicAuth: &version2.BasicAuth{
+ Secret: "/etc/nginx/secrets/default-htpasswd-secret",
+ Realm: "My Test API",
},
},
- expectedOidc: &oidcPolicyCfg{},
- msg: "ingress mtls in the wrong context",
+ msg: "basic auth reference",
},
{
policyRefs: []conf_v1.PolicyReference{
@@ -4527,130 +4346,102 @@ func TestGeneratePoliciesFails(t *testing.T) {
Spec: conf_v1.PolicySpec{
IngressMTLS: &conf_v1.IngressMTLS{
ClientCertSecret: "ingress-mtls-secret",
+ VerifyClient: "off",
},
},
},
},
- policyOpts: policyOptions{
- tls: false,
- secretRefs: map[string]*secrets.SecretReference{
- "default/ingress-mtls-secret": {
- Secret: &api_v1.Secret{
- Type: secrets.SecretTypeCA,
- },
- Path: ingressMTLSCertPath,
- },
- },
- },
- context: "route",
+ context: "spec",
expected: policiesCfg{
- ErrorReturn: &version2.Return{
- Code: 500,
- },
- },
- expectedWarnings: Warnings{
- nil: {
- `TLS must be enabled in VirtualServer for IngressMTLS policy default/ingress-mtls-policy`,
+ IngressMTLS: &version2.IngressMTLS{
+ ClientCert: mTLSCertPath,
+ VerifyClient: "off",
+ VerifyDepth: 1,
},
},
- expectedOidc: &oidcPolicyCfg{},
- msg: "ingress mtls missing TLS config",
+ msg: "ingressMTLS reference",
},
{
policyRefs: []conf_v1.PolicyReference{
{
- Name: "ingress-mtls-policy",
+ Name: "ingress-mtls-policy-crl",
Namespace: "default",
},
},
policies: map[string]*conf_v1.Policy{
- "default/ingress-mtls-policy": {
+ "default/ingress-mtls-policy-crl": {
ObjectMeta: meta_v1.ObjectMeta{
- Name: "ingress-mtls-policy",
+ Name: "ingress-mtls-policy-crl",
Namespace: "default",
},
Spec: conf_v1.PolicySpec{
IngressMTLS: &conf_v1.IngressMTLS{
- ClientCertSecret: "ingress-mtls-secret",
- CrlFileName: "default-ingress-mtls-secret-ca.crl",
- },
- },
- },
- },
- policyOpts: policyOptions{
- tls: true,
- secretRefs: map[string]*secrets.SecretReference{
- "default/ingress-mtls-secret": {
- Secret: &api_v1.Secret{
- Type: secrets.SecretTypeCA,
- Data: map[string][]byte{
- "ca.crl": []byte("base64crl"),
- },
+ ClientCertSecret: "ingress-mtls-secret-crl",
+ VerifyClient: "off",
},
- Path: ingressMTLSCertPath,
},
},
},
context: "spec",
expected: policiesCfg{
IngressMTLS: &version2.IngressMTLS{
- ClientCert: ingressMTLSCertPath,
- ClientCrl: ingressMTLSCrlPath,
- VerifyClient: "on",
+ ClientCert: mTLSCertPath,
+ ClientCrl: mTLSCrlPath,
+ VerifyClient: "off",
VerifyDepth: 1,
},
- ErrorReturn: nil,
- },
- expectedWarnings: Warnings{
- nil: {
- `Both ca.crl in the Secret and ingressMTLS.crlFileName fields cannot be used. ca.crl in default/ingress-mtls-secret will be ignored and default/ingress-mtls-policy will be applied`,
- },
},
- expectedOidc: &oidcPolicyCfg{},
- msg: "ingress mtls ca.crl and ingressMTLS.Crl set",
+ msg: "ingressMTLS reference with ca.crl field in secret",
},
{
policyRefs: []conf_v1.PolicyReference{
{
- Name: "egress-mtls-policy",
- Namespace: "default",
- },
- {
- Name: "egress-mtls-policy2",
+ Name: "ingress-mtls-policy-crl",
Namespace: "default",
},
},
policies: map[string]*conf_v1.Policy{
- "default/egress-mtls-policy": {
+ "default/ingress-mtls-policy-crl": {
ObjectMeta: meta_v1.ObjectMeta{
- Name: "egress-mtls-policy",
+ Name: "ingress-mtls-policy-crl",
Namespace: "default",
},
Spec: conf_v1.PolicySpec{
- EgressMTLS: &conf_v1.EgressMTLS{
- TLSSecret: "egress-mtls-secret",
+ IngressMTLS: &conf_v1.IngressMTLS{
+ ClientCertSecret: "ingress-mtls-secret",
+ CrlFileName: "default-ingress-mtls-secret-ca.crl",
+ VerifyClient: "off",
},
},
},
- "default/egress-mtls-policy2": {
- ObjectMeta: meta_v1.ObjectMeta{
- Name: "egress-mtls-policy2",
- Namespace: "default",
- },
- Spec: conf_v1.PolicySpec{
- EgressMTLS: &conf_v1.EgressMTLS{
- TLSSecret: "egress-mtls-secret2",
- },
- },
+ },
+ context: "spec",
+ expected: policiesCfg{
+ IngressMTLS: &version2.IngressMTLS{
+ ClientCert: mTLSCertPath,
+ ClientCrl: mTLSCrlPath,
+ VerifyClient: "off",
+ VerifyDepth: 1,
},
},
- policyOpts: policyOptions{
- secretRefs: map[string]*secrets.SecretReference{
- "default/egress-mtls-secret": {
- Secret: &api_v1.Secret{
- Type: api_v1.SecretTypeTLS,
+ msg: "ingressMTLS reference with crl field in policy",
+ },
+ {
+ policyRefs: []conf_v1.PolicyReference{
+ {
+ Name: "egress-mtls-policy",
+ Namespace: "default",
+ },
+ },
+ policies: map[string]*conf_v1.Policy{
+ "default/egress-mtls-policy": {
+ Spec: conf_v1.PolicySpec{
+ EgressMTLS: &conf_v1.EgressMTLS{
+ TLSSecret: "egress-mtls-secret",
+ ServerName: true,
+ SessionReuse: createPointerFromBool(false),
+ TrustedCertSecret: "egress-trusted-ca-secret",
},
- Path: "/etc/nginx/secrets/default-egress-mtls-secret",
},
},
},
@@ -4659,21 +4450,17 @@ func TestGeneratePoliciesFails(t *testing.T) {
EgressMTLS: &version2.EgressMTLS{
Certificate: "/etc/nginx/secrets/default-egress-mtls-secret",
CertificateKey: "/etc/nginx/secrets/default-egress-mtls-secret",
- VerifyServer: false,
- VerifyDepth: 1,
Ciphers: "DEFAULT",
Protocols: "TLSv1 TLSv1.1 TLSv1.2",
- SessionReuse: true,
+ ServerName: true,
+ SessionReuse: false,
+ VerifyDepth: 1,
+ VerifyServer: false,
+ TrustedCert: "/etc/nginx/secrets/default-egress-trusted-ca-secret",
SSLName: "$proxy_host",
},
},
- expectedWarnings: Warnings{
- nil: {
- `Multiple egressMTLS policies in the same context is not valid. EgressMTLS policy default/egress-mtls-policy2 will be ignored`,
- },
- },
- expectedOidc: &oidcPolicyCfg{},
- msg: "multi egress mtls",
+ msg: "egressMTLS reference",
},
{
policyRefs: []conf_v1.PolicyReference{
@@ -4684,253 +4471,354 @@ func TestGeneratePoliciesFails(t *testing.T) {
},
policies: map[string]*conf_v1.Policy{
"default/egress-mtls-policy": {
- ObjectMeta: meta_v1.ObjectMeta{
- Name: "egress-mtls-policy",
- Namespace: "default",
- },
Spec: conf_v1.PolicySpec{
EgressMTLS: &conf_v1.EgressMTLS{
- TrustedCertSecret: "egress-trusted-secret",
- SSLName: "foo.com",
- },
- },
- },
- },
- policyOpts: policyOptions{
- secretRefs: map[string]*secrets.SecretReference{
- "default/egress-trusted-secret": {
- Secret: &api_v1.Secret{
- Type: secrets.SecretTypeCA,
+ TLSSecret: "egress-mtls-secret",
+ ServerName: true,
+ SessionReuse: createPointerFromBool(false),
+ TrustedCertSecret: "egress-trusted-ca-secret-crl",
},
- Error: errors.New("secret is invalid"),
},
},
},
context: "route",
expected: policiesCfg{
- ErrorReturn: &version2.Return{
- Code: 500,
- },
- },
- expectedWarnings: Warnings{
- nil: {
- `EgressMTLS policy default/egress-mtls-policy references an invalid secret default/egress-trusted-secret: secret is invalid`,
+ EgressMTLS: &version2.EgressMTLS{
+ Certificate: "/etc/nginx/secrets/default-egress-mtls-secret",
+ CertificateKey: "/etc/nginx/secrets/default-egress-mtls-secret",
+ Ciphers: "DEFAULT",
+ Protocols: "TLSv1 TLSv1.1 TLSv1.2",
+ ServerName: true,
+ SessionReuse: false,
+ VerifyDepth: 1,
+ VerifyServer: false,
+ TrustedCert: mTLSCertPath,
+ SSLName: "$proxy_host",
},
},
- expectedOidc: &oidcPolicyCfg{},
- msg: "egress mtls referencing an invalid CA secret",
+ msg: "egressMTLS with crt and crl",
},
{
policyRefs: []conf_v1.PolicyReference{
{
- Name: "egress-mtls-policy",
+ Name: "oidc-policy",
Namespace: "default",
},
},
policies: map[string]*conf_v1.Policy{
- "default/egress-mtls-policy": {
+ "default/oidc-policy": {
ObjectMeta: meta_v1.ObjectMeta{
- Name: "egress-mtls-policy",
+ Name: "oidc-policy",
Namespace: "default",
},
Spec: conf_v1.PolicySpec{
- EgressMTLS: &conf_v1.EgressMTLS{
- TLSSecret: "egress-mtls-secret",
- SSLName: "foo.com",
- },
- },
- },
- },
- policyOpts: policyOptions{
- secretRefs: map[string]*secrets.SecretReference{
- "default/egress-mtls-secret": {
- Secret: &api_v1.Secret{
- Type: secrets.SecretTypeCA,
+ OIDC: &conf_v1.OIDC{
+ AuthEndpoint: "http://example.com/auth",
+ TokenEndpoint: "http://example.com/token",
+ JWKSURI: "http://example.com/jwks",
+ ClientID: "client-id",
+ ClientSecret: "oidc-secret",
+ Scope: "scope",
+ RedirectURI: "/redirect",
+ ZoneSyncLeeway: createPointerFromInt(20),
+ AccessTokenEnable: true,
},
},
},
},
- context: "route",
expected: policiesCfg{
- ErrorReturn: &version2.Return{
- Code: 500,
- },
- },
- expectedWarnings: Warnings{
- nil: {
- `EgressMTLS policy default/egress-mtls-policy references a secret default/egress-mtls-secret of a wrong type 'nginx.org/ca', must be 'kubernetes.io/tls'`,
- },
+ OIDC: true,
},
- expectedOidc: &oidcPolicyCfg{},
- msg: "egress mtls referencing wrong secret type",
+ msg: "oidc reference",
},
{
policyRefs: []conf_v1.PolicyReference{
{
- Name: "egress-mtls-policy",
+ Name: "waf-policy",
Namespace: "default",
},
},
policies: map[string]*conf_v1.Policy{
- "default/egress-mtls-policy": {
- ObjectMeta: meta_v1.ObjectMeta{
- Name: "egress-mtls-policy",
- Namespace: "default",
- },
+ "default/waf-policy": {
Spec: conf_v1.PolicySpec{
- EgressMTLS: &conf_v1.EgressMTLS{
- TrustedCertSecret: "egress-trusted-secret",
- SSLName: "foo.com",
- },
- },
- },
- },
- policyOpts: policyOptions{
- secretRefs: map[string]*secrets.SecretReference{
- "default/egress-trusted-secret": {
- Secret: &api_v1.Secret{
- Type: api_v1.SecretTypeTLS,
+ WAF: &conf_v1.WAF{
+ Enable: true,
+ ApPolicy: "default/dataguard-alarm",
+ SecurityLog: &conf_v1.SecurityLog{
+ Enable: true,
+ ApLogConf: "default/logconf",
+ LogDest: "syslog:server=127.0.0.1:514",
+ },
},
},
},
},
context: "route",
expected: policiesCfg{
- ErrorReturn: &version2.Return{
- Code: 500,
- },
- },
- expectedWarnings: Warnings{
- nil: {
- `EgressMTLS policy default/egress-mtls-policy references a secret default/egress-trusted-secret of a wrong type 'kubernetes.io/tls', must be 'nginx.org/ca'`,
+ WAF: &version2.WAF{
+ Enable: "on",
+ ApPolicy: "/etc/nginx/waf/nac-policies/default-dataguard-alarm",
+ ApSecurityLogEnable: true,
+ ApLogConf: []string{"/etc/nginx/waf/nac-logconfs/default-logconf syslog:server=127.0.0.1:514"},
},
},
- expectedOidc: &oidcPolicyCfg{},
- msg: "egress trusted secret referencing wrong secret type",
+ msg: "WAF reference",
},
- {
- policyRefs: []conf_v1.PolicyReference{
- {
- Name: "egress-mtls-policy",
+ }
+
+ vsc := newVirtualServerConfigurator(&ConfigParams{}, false, false, &StaticConfigParams{}, false)
+
+ for _, test := range tests {
+ result := vsc.generatePolicies(ownerDetails, test.policyRefs, test.policies, test.context, policyOpts)
+ if diff := cmp.Diff(test.expected, result); diff != "" {
+ t.Errorf("generatePolicies() '%v' mismatch (-want +got):\n%s", test.msg, diff)
+ }
+ if len(vsc.warnings) > 0 {
+ t.Errorf("generatePolicies() returned unexpected warnings %v for the case of %s", vsc.warnings, test.msg)
+ }
+ }
+}
+
+func TestGeneratePolicies_GeneratesWAFPolicyOnValidApBundle(t *testing.T) {
+ t.Parallel()
+
+ ownerDetails := policyOwnerDetails{
+ owner: nil, // nil is OK for the unit test
+ ownerNamespace: "default",
+ vsNamespace: "default",
+ vsName: "test",
+ }
+
+ test := struct {
+ policyRefs []conf_v1.PolicyReference
+ policies map[string]*conf_v1.Policy
+ policyOpts policyOptions
+ context string
+ want policiesCfg
+ }{
+ policyRefs: []conf_v1.PolicyReference{
+ {
+ Name: "waf-bundle",
+ Namespace: "default",
+ },
+ },
+ policies: map[string]*conf_v1.Policy{
+ "default/waf-bundle": {
+ Spec: conf_v1.PolicySpec{
+ WAF: &conf_v1.WAF{
+ Enable: true,
+ ApBundle: "testWAFPolicyBundle.tgz",
+ },
+ },
+ },
+ },
+ context: "route",
+ }
+
+ vsc := newVirtualServerConfigurator(&ConfigParams{}, false, false, &StaticConfigParams{}, false)
+ want := policiesCfg{
+ WAF: &version2.WAF{
+ Enable: "on",
+ ApBundle: "/etc/nginx/waf/bundles/testWAFPolicyBundle.tgz",
+ },
+ }
+ got := vsc.generatePolicies(ownerDetails, test.policyRefs, test.policies, test.context, policyOptions{})
+ if !cmp.Equal(want, got) {
+ t.Error(cmp.Diff(want, got))
+ }
+}
+
+func TestGeneratePoliciesFails(t *testing.T) {
+ t.Parallel()
+ ownerDetails := policyOwnerDetails{
+ owner: nil, // nil is OK for the unit test
+ ownerNamespace: "default",
+ vsNamespace: "default",
+ vsName: "test",
+ }
+
+ dryRunOverride := true
+ rejectCodeOverride := 505
+
+ ingressMTLSCertPath := "/etc/nginx/secrets/default-ingress-mtls-secret-ca.crt"
+ ingressMTLSCrlPath := "/etc/nginx/secrets/default-ingress-mtls-secret-ca.crl"
+
+ tests := []struct {
+ policyRefs []conf_v1.PolicyReference
+ policies map[string]*conf_v1.Policy
+ policyOpts policyOptions
+ trustedCAFileName string
+ context string
+ oidcPolCfg *oidcPolicyCfg
+ expected policiesCfg
+ expectedWarnings Warnings
+ expectedOidc *oidcPolicyCfg
+ msg string
+ }{
+ {
+ policyRefs: []conf_v1.PolicyReference{
+ {
+ Name: "allow-policy",
Namespace: "default",
},
},
+ policies: map[string]*conf_v1.Policy{},
+ policyOpts: policyOptions{},
+ expected: policiesCfg{
+ ErrorReturn: &version2.Return{
+ Code: 500,
+ },
+ },
+ expectedWarnings: Warnings{
+ nil: {
+ "Policy default/allow-policy is missing or invalid",
+ },
+ },
+ expectedOidc: &oidcPolicyCfg{},
+ msg: "missing policy",
+ },
+ {
+ policyRefs: []conf_v1.PolicyReference{
+ {
+ Name: "allow-policy",
+ },
+ {
+ Name: "deny-policy",
+ },
+ },
policies: map[string]*conf_v1.Policy{
- "default/egress-mtls-policy": {
- ObjectMeta: meta_v1.ObjectMeta{
- Name: "egress-mtls-policy",
- Namespace: "default",
- },
+ "default/allow-policy": {
Spec: conf_v1.PolicySpec{
- EgressMTLS: &conf_v1.EgressMTLS{
- TLSSecret: "egress-mtls-secret",
- SSLName: "foo.com",
+ AccessControl: &conf_v1.AccessControl{
+ Allow: []string{"127.0.0.1"},
},
},
},
- },
- policyOpts: policyOptions{
- secretRefs: map[string]*secrets.SecretReference{
- "default/egress-mtls-secret": {
- Secret: &api_v1.Secret{
- Type: api_v1.SecretTypeTLS,
+ "default/deny-policy": {
+ Spec: conf_v1.PolicySpec{
+ AccessControl: &conf_v1.AccessControl{
+ Deny: []string{"127.0.0.2"},
},
- Error: errors.New("secret is invalid"),
},
},
},
- context: "route",
+ policyOpts: policyOptions{},
expected: policiesCfg{
- ErrorReturn: &version2.Return{
- Code: 500,
- },
+ Allow: []string{"127.0.0.1"},
+ Deny: []string{"127.0.0.2"},
},
expectedWarnings: Warnings{
nil: {
- `EgressMTLS policy default/egress-mtls-policy references an invalid secret default/egress-mtls-secret: secret is invalid`,
+ "AccessControl policy (or policies) with deny rules is overridden by policy (or policies) with allow rules",
},
},
expectedOidc: &oidcPolicyCfg{},
- msg: "egress mtls referencing missing tls secret",
+ msg: "conflicting policies",
},
{
policyRefs: []conf_v1.PolicyReference{
{
- Name: "oidc-policy",
+ Name: "rateLimit-policy",
+ Namespace: "default",
+ },
+ {
+ Name: "rateLimit-policy2",
Namespace: "default",
},
},
policies: map[string]*conf_v1.Policy{
- "default/oidc-policy": {
- ObjectMeta: meta_v1.ObjectMeta{
- Name: "oidc-policy",
- Namespace: "default",
- },
+ "default/rateLimit-policy": {
Spec: conf_v1.PolicySpec{
- OIDC: &conf_v1.OIDC{
- ClientSecret: "oidc-secret",
+ RateLimit: &conf_v1.RateLimit{
+ Key: "test",
+ ZoneSize: "10M",
+ Rate: "10r/s",
},
},
},
- },
- policyOpts: policyOptions{
- secretRefs: map[string]*secrets.SecretReference{
- "default/oidc-secret": {
- Secret: &api_v1.Secret{
- Type: secrets.SecretTypeOIDC,
+ "default/rateLimit-policy2": {
+ Spec: conf_v1.PolicySpec{
+ RateLimit: &conf_v1.RateLimit{
+ Key: "test2",
+ ZoneSize: "20M",
+ Rate: "20r/s",
+ DryRun: &dryRunOverride,
+ LogLevel: "info",
+ RejectCode: &rejectCodeOverride,
},
- Error: errors.New("secret is invalid"),
},
},
},
- context: "route",
+ policyOpts: policyOptions{},
expected: policiesCfg{
- ErrorReturn: &version2.Return{
- Code: 500,
+ LimitReqZones: []version2.LimitReqZone{
+ {
+ Key: "test",
+ ZoneSize: "10M",
+ Rate: "10r/s",
+ ZoneName: "pol_rl_default_rateLimit-policy_default_test",
+ },
+ {
+ Key: "test2",
+ ZoneSize: "20M",
+ Rate: "20r/s",
+ ZoneName: "pol_rl_default_rateLimit-policy2_default_test",
+ },
+ },
+ LimitReqOptions: version2.LimitReqOptions{
+ LogLevel: "error",
+ RejectCode: 503,
+ },
+ LimitReqs: []version2.LimitReq{
+ {
+ ZoneName: "pol_rl_default_rateLimit-policy_default_test",
+ },
+ {
+ ZoneName: "pol_rl_default_rateLimit-policy2_default_test",
+ },
},
},
expectedWarnings: Warnings{
nil: {
- `OIDC policy default/oidc-policy references an invalid secret default/oidc-secret: secret is invalid`,
+ `RateLimit policy default/rateLimit-policy2 with limit request option dryRun='true' is overridden to dryRun='false' by the first policy reference in this context`,
+ `RateLimit policy default/rateLimit-policy2 with limit request option logLevel='info' is overridden to logLevel='error' by the first policy reference in this context`,
+ `RateLimit policy default/rateLimit-policy2 with limit request option rejectCode='505' is overridden to rejectCode='503' by the first policy reference in this context`,
},
},
expectedOidc: &oidcPolicyCfg{},
- msg: "oidc referencing missing oidc secret",
+ msg: "rate limit policy limit request option override",
},
{
policyRefs: []conf_v1.PolicyReference{
{
- Name: "oidc-policy",
+ Name: "jwt-policy",
Namespace: "default",
},
},
policies: map[string]*conf_v1.Policy{
- "default/oidc-policy": {
+ "default/jwt-policy": {
ObjectMeta: meta_v1.ObjectMeta{
- Name: "oidc-policy",
+ Name: "jwt-policy",
Namespace: "default",
},
Spec: conf_v1.PolicySpec{
- OIDC: &conf_v1.OIDC{
- ClientSecret: "oidc-secret",
- AuthEndpoint: "http://foo.com/bar",
- TokenEndpoint: "http://foo.com/bar",
- JWKSURI: "http://foo.com/bar",
- AccessTokenEnable: true,
+ JWTAuth: &conf_v1.JWTAuth{
+ Realm: "test",
+ Secret: "jwt-secret",
},
},
},
},
policyOpts: policyOptions{
secretRefs: map[string]*secrets.SecretReference{
- "default/oidc-secret": {
+ "default/jwt-secret": {
Secret: &api_v1.Secret{
- Type: api_v1.SecretTypeTLS,
+ Type: secrets.SecretTypeJWK,
},
+ Error: errors.New("secret is invalid"),
},
},
},
- context: "spec",
expected: policiesCfg{
ErrorReturn: &version2.Return{
Code: 500,
@@ -4938,80 +4826,42 @@ func TestGeneratePoliciesFails(t *testing.T) {
},
expectedWarnings: Warnings{
nil: {
- `OIDC policy default/oidc-policy references a secret default/oidc-secret of a wrong type 'kubernetes.io/tls', must be 'nginx.org/oidc'`,
+ `JWT policy default/jwt-policy references an invalid secret default/jwt-secret: secret is invalid`,
},
},
expectedOidc: &oidcPolicyCfg{},
- msg: "oidc secret referencing wrong secret type",
+ msg: "jwt reference missing secret",
},
{
policyRefs: []conf_v1.PolicyReference{
{
- Name: "oidc-policy-2",
+ Name: "jwt-policy",
Namespace: "default",
},
},
policies: map[string]*conf_v1.Policy{
- "default/oidc-policy-1": {
+ "default/jwt-policy": {
ObjectMeta: meta_v1.ObjectMeta{
- Name: "oidc-policy-1",
+ Name: "jwt-policy",
Namespace: "default",
},
Spec: conf_v1.PolicySpec{
- OIDC: &conf_v1.OIDC{
- ClientID: "foo",
- ClientSecret: "oidc-secret",
- AuthEndpoint: "https://foo.com/auth",
- TokenEndpoint: "https://foo.com/token",
- JWKSURI: "https://foo.com/certs",
- AccessTokenEnable: true,
- },
- },
- },
- "default/oidc-policy-2": {
- ObjectMeta: meta_v1.ObjectMeta{
- Name: "oidc-policy-2",
- Namespace: "default",
- },
- Spec: conf_v1.PolicySpec{
- OIDC: &conf_v1.OIDC{
- ClientID: "foo",
- ClientSecret: "oidc-secret",
- AuthEndpoint: "https://bar.com/auth",
- TokenEndpoint: "https://bar.com/token",
- JWKSURI: "https://bar.com/certs",
- AccessTokenEnable: true,
+ JWTAuth: &conf_v1.JWTAuth{
+ Realm: "test",
+ Secret: "jwt-secret",
},
},
},
},
policyOpts: policyOptions{
secretRefs: map[string]*secrets.SecretReference{
- "default/oidc-secret": {
+ "default/jwt-secret": {
Secret: &api_v1.Secret{
- Type: secrets.SecretTypeOIDC,
- Data: map[string][]byte{
- "client-secret": []byte("super_secret_123"),
- },
+ Type: secrets.SecretTypeCA,
},
},
},
},
- context: "route",
- oidcPolCfg: &oidcPolicyCfg{
- oidc: &version2.OIDC{
- AuthEndpoint: "https://foo.com/auth",
- TokenEndpoint: "https://foo.com/token",
- JwksURI: "https://foo.com/certs",
- ClientID: "foo",
- ClientSecret: "super_secret_123",
- RedirectURI: "/_codexch",
- Scope: "openid",
- ZoneSyncLeeway: 0,
- AccessTokenEnable: true,
- },
- key: "default/oidc-policy-1",
- },
expected: policiesCfg{
ErrorReturn: &version2.Return{
Code: 500,
@@ -5019,396 +4869,1320 @@ func TestGeneratePoliciesFails(t *testing.T) {
},
expectedWarnings: Warnings{
nil: {
- `Only one oidc policy is allowed in a VirtualServer and its VirtualServerRoutes. Can't use default/oidc-policy-2. Use default/oidc-policy-1`,
- },
- },
- expectedOidc: &oidcPolicyCfg{
- oidc: &version2.OIDC{
- AuthEndpoint: "https://foo.com/auth",
- TokenEndpoint: "https://foo.com/token",
- JwksURI: "https://foo.com/certs",
- ClientID: "foo",
- ClientSecret: "super_secret_123",
- RedirectURI: "/_codexch",
- Scope: "openid",
- AccessTokenEnable: true,
+ `JWT policy default/jwt-policy references a secret default/jwt-secret of a wrong type 'nginx.org/ca', must be 'nginx.org/jwk'`,
},
- key: "default/oidc-policy-1",
},
- msg: "multiple oidc policies",
+ expectedOidc: &oidcPolicyCfg{},
+ msg: "jwt references wrong secret type",
},
{
policyRefs: []conf_v1.PolicyReference{
{
- Name: "oidc-policy",
+ Name: "jwt-policy",
Namespace: "default",
},
{
- Name: "oidc-policy2",
+ Name: "jwt-policy2",
Namespace: "default",
},
},
policies: map[string]*conf_v1.Policy{
- "default/oidc-policy": {
+ "default/jwt-policy": {
ObjectMeta: meta_v1.ObjectMeta{
- Name: "oidc-policy",
+ Name: "jwt-policy",
Namespace: "default",
},
Spec: conf_v1.PolicySpec{
- OIDC: &conf_v1.OIDC{
- ClientSecret: "oidc-secret",
- AuthEndpoint: "https://foo.com/auth",
- TokenEndpoint: "https://foo.com/token",
- JWKSURI: "https://foo.com/certs",
- ClientID: "foo",
- AccessTokenEnable: true,
+ JWTAuth: &conf_v1.JWTAuth{
+ Realm: "test",
+ Secret: "jwt-secret",
},
},
},
- "default/oidc-policy2": {
+ "default/jwt-policy2": {
ObjectMeta: meta_v1.ObjectMeta{
- Name: "oidc-policy2",
+ Name: "jwt-policy2",
Namespace: "default",
},
Spec: conf_v1.PolicySpec{
- OIDC: &conf_v1.OIDC{
- ClientSecret: "oidc-secret",
- AuthEndpoint: "https://bar.com/auth",
- TokenEndpoint: "https://bar.com/token",
- JWKSURI: "https://bar.com/certs",
- ClientID: "bar",
- AccessTokenEnable: true,
+ JWTAuth: &conf_v1.JWTAuth{
+ Realm: "test",
+ Secret: "jwt-secret2",
},
},
},
},
policyOpts: policyOptions{
secretRefs: map[string]*secrets.SecretReference{
- "default/oidc-secret": {
+ "default/jwt-secret": {
Secret: &api_v1.Secret{
- Type: secrets.SecretTypeOIDC,
- Data: map[string][]byte{
- "client-secret": []byte("super_secret_123"),
- },
+ Type: secrets.SecretTypeJWK,
+ },
+ Path: "/etc/nginx/secrets/default-jwt-secret",
+ },
+ "default/jwt-secret2": {
+ Secret: &api_v1.Secret{
+ Type: secrets.SecretTypeJWK,
},
+ Path: "/etc/nginx/secrets/default-jwt-secret2",
},
},
},
- context: "route",
expected: policiesCfg{
- OIDC: true,
+ JWTAuth: &version2.JWTAuth{
+ Secret: "/etc/nginx/secrets/default-jwt-secret",
+ Realm: "test",
+ },
},
expectedWarnings: Warnings{
nil: {
- `Multiple oidc policies in the same context is not valid. OIDC policy default/oidc-policy2 will be ignored`,
- },
- },
- expectedOidc: &oidcPolicyCfg{
- &version2.OIDC{
- AuthEndpoint: "https://foo.com/auth",
- TokenEndpoint: "https://foo.com/token",
- JwksURI: "https://foo.com/certs",
- ClientID: "foo",
- ClientSecret: "super_secret_123",
- RedirectURI: "/_codexch",
- Scope: "openid",
- ZoneSyncLeeway: 200,
- AccessTokenEnable: true,
+ `Multiple jwt policies in the same context is not valid. JWT policy default/jwt-policy2 will be ignored`,
},
- "default/oidc-policy",
},
- msg: "multi oidc",
+ expectedOidc: &oidcPolicyCfg{},
+ msg: "multi jwt reference",
},
{
policyRefs: []conf_v1.PolicyReference{
{
- Name: "waf-policy",
- Namespace: "default",
- },
- {
- Name: "waf-policy2",
+ Name: "basic-auth-policy",
Namespace: "default",
},
},
policies: map[string]*conf_v1.Policy{
- "default/waf-policy": {
+ "default/basic-auth-policy": {
ObjectMeta: meta_v1.ObjectMeta{
- Name: "waf-policy",
+ Name: "basic-auth-policy",
Namespace: "default",
},
Spec: conf_v1.PolicySpec{
- WAF: &conf_v1.WAF{
- Enable: true,
- ApPolicy: "default/dataguard-alarm",
+ BasicAuth: &conf_v1.BasicAuth{
+ Realm: "test",
+ Secret: "htpasswd-secret",
},
},
},
- "default/waf-policy2": {
+ },
+ policyOpts: policyOptions{
+ secretRefs: map[string]*secrets.SecretReference{
+ "default/htpasswd-secret": {
+ Secret: &api_v1.Secret{
+ Type: secrets.SecretTypeHtpasswd,
+ },
+ Error: errors.New("secret is invalid"),
+ },
+ },
+ },
+ expected: policiesCfg{
+ ErrorReturn: &version2.Return{
+ Code: 500,
+ },
+ },
+ expectedWarnings: Warnings{
+ nil: {
+ `Basic Auth policy default/basic-auth-policy references an invalid secret default/htpasswd-secret: secret is invalid`,
+ },
+ },
+ expectedOidc: &oidcPolicyCfg{},
+ msg: "basic auth reference missing secret",
+ },
+ {
+ policyRefs: []conf_v1.PolicyReference{
+ {
+ Name: "basic-auth-policy",
+ Namespace: "default",
+ },
+ },
+ policies: map[string]*conf_v1.Policy{
+ "default/basic-auth-policy": {
ObjectMeta: meta_v1.ObjectMeta{
- Name: "waf-policy2",
+ Name: "basic-auth-policy",
Namespace: "default",
},
Spec: conf_v1.PolicySpec{
- WAF: &conf_v1.WAF{
- Enable: true,
- ApPolicy: "default/dataguard-alarm",
+ BasicAuth: &conf_v1.BasicAuth{
+ Realm: "test",
+ Secret: "htpasswd-secret",
},
},
},
},
policyOpts: policyOptions{
- apResources: &appProtectResourcesForVS{
- Policies: map[string]string{
- "default/dataguard-alarm": "/etc/nginx/waf/nac-policies/default-dataguard-alarm",
- },
- LogConfs: map[string]string{
- "default/logconf": "/etc/nginx/waf/nac-logconfs/default-logconf",
+ secretRefs: map[string]*secrets.SecretReference{
+ "default/htpasswd-secret": {
+ Secret: &api_v1.Secret{
+ Type: secrets.SecretTypeCA,
+ },
},
},
},
- context: "route",
expected: policiesCfg{
- WAF: &version2.WAF{
- Enable: "on",
- ApPolicy: "/etc/nginx/waf/nac-policies/default-dataguard-alarm",
+ ErrorReturn: &version2.Return{
+ Code: 500,
},
},
expectedWarnings: Warnings{
nil: {
- `Multiple WAF policies in the same context is not valid. WAF policy default/waf-policy2 will be ignored`,
+ `Basic Auth policy default/basic-auth-policy references a secret default/htpasswd-secret of a wrong type 'nginx.org/ca', must be 'nginx.org/htpasswd'`,
},
},
expectedOidc: &oidcPolicyCfg{},
- msg: "multi waf",
+ msg: "basic auth references wrong secret type",
},
- }
-
- for _, test := range tests {
- vsc := newVirtualServerConfigurator(&ConfigParams{}, false, false, &StaticConfigParams{}, false)
-
- if test.oidcPolCfg != nil {
- vsc.oidcPolCfg = test.oidcPolCfg
- }
-
- result := vsc.generatePolicies(ownerDetails, test.policyRefs, test.policies, test.context, test.policyOpts)
- if diff := cmp.Diff(test.expected, result); diff != "" {
- t.Errorf("generatePolicies() '%v' mismatch (-want +got):\n%s", test.msg, diff)
- }
- if !reflect.DeepEqual(vsc.warnings, test.expectedWarnings) {
- t.Errorf(
- "generatePolicies() returned warnings of \n%v but expected \n%v for the case of %s",
- vsc.warnings,
- test.expectedWarnings,
- test.msg,
- )
- }
- if diff := cmp.Diff(test.expectedOidc.oidc, vsc.oidcPolCfg.oidc); diff != "" {
- t.Errorf("generatePolicies() '%v' mismatch (-want +got):\n%s", test.msg, diff)
- }
- if diff := cmp.Diff(test.expectedOidc.key, vsc.oidcPolCfg.key); diff != "" {
- t.Errorf("generatePolicies() '%v' mismatch (-want +got):\n%s", test.msg, diff)
- }
- }
-}
-
-func TestRemoveDuplicates(t *testing.T) {
- t.Parallel()
- tests := []struct {
- rlz []version2.LimitReqZone
- expected []version2.LimitReqZone
- }{
{
- rlz: []version2.LimitReqZone{
- {ZoneName: "test"},
- {ZoneName: "test"},
- {ZoneName: "test2"},
- {ZoneName: "test3"},
- },
- expected: []version2.LimitReqZone{
- {ZoneName: "test"},
- {ZoneName: "test2"},
- {ZoneName: "test3"},
+ policyRefs: []conf_v1.PolicyReference{
+ {
+ Name: "basic-auth-policy",
+ Namespace: "default",
+ },
+ {
+ Name: "basic-auth-policy2",
+ Namespace: "default",
+ },
},
- },
- {
- rlz: []version2.LimitReqZone{
- {ZoneName: "test"},
- {ZoneName: "test"},
- {ZoneName: "test2"},
- {ZoneName: "test3"},
- {ZoneName: "test3"},
+ policies: map[string]*conf_v1.Policy{
+ "default/basic-auth-policy": {
+ ObjectMeta: meta_v1.ObjectMeta{
+ Name: "basic-auth-policy",
+ Namespace: "default",
+ },
+ Spec: conf_v1.PolicySpec{
+ BasicAuth: &conf_v1.BasicAuth{
+ Realm: "test",
+ Secret: "htpasswd-secret",
+ },
+ },
+ },
+ "default/basic-auth-policy2": {
+ ObjectMeta: meta_v1.ObjectMeta{
+ Name: "basic-auth-policy2",
+ Namespace: "default",
+ },
+ Spec: conf_v1.PolicySpec{
+ BasicAuth: &conf_v1.BasicAuth{
+ Realm: "test",
+ Secret: "htpasswd-secret2",
+ },
+ },
+ },
},
- expected: []version2.LimitReqZone{
- {ZoneName: "test"},
- {ZoneName: "test2"},
- {ZoneName: "test3"},
+ policyOpts: policyOptions{
+ secretRefs: map[string]*secrets.SecretReference{
+ "default/htpasswd-secret": {
+ Secret: &api_v1.Secret{
+ Type: secrets.SecretTypeHtpasswd,
+ },
+ Path: "/etc/nginx/secrets/default-htpasswd-secret",
+ },
+ "default/htpasswd-secret2": {
+ Secret: &api_v1.Secret{
+ Type: secrets.SecretTypeHtpasswd,
+ },
+ Path: "/etc/nginx/secrets/default-htpasswd-secret2",
+ },
+ },
},
- },
- }
- for _, test := range tests {
- result := removeDuplicateLimitReqZones(test.rlz)
- if !reflect.DeepEqual(result, test.expected) {
- t.Errorf("removeDuplicateLimitReqZones() returned \n%v, but expected \n%v", result, test.expected)
- }
- }
-}
-
-func TestAddPoliciesCfgToLocations(t *testing.T) {
- t.Parallel()
- cfg := policiesCfg{
- Allow: []string{"127.0.0.1"},
- Deny: []string{"127.0.0.2"},
- ErrorReturn: &version2.Return{
- Code: 400,
- },
- }
-
- locations := []version2.Location{
- {
- Path: "/",
- },
- }
-
- expectedLocations := []version2.Location{
- {
- Path: "/",
- Allow: []string{"127.0.0.1"},
- Deny: []string{"127.0.0.2"},
- PoliciesErrorReturn: &version2.Return{
- Code: 400,
+ expected: policiesCfg{
+ BasicAuth: &version2.BasicAuth{
+ Secret: "/etc/nginx/secrets/default-htpasswd-secret",
+ Realm: "test",
+ },
},
- },
- }
-
- addPoliciesCfgToLocations(cfg, locations)
- if !reflect.DeepEqual(locations, expectedLocations) {
- t.Errorf("addPoliciesCfgToLocations() returned \n%+v but expected \n%+v", locations, expectedLocations)
- }
-}
-
-func TestGenerateUpstream(t *testing.T) {
- t.Parallel()
- name := "test-upstream"
- upstream := conf_v1.Upstream{Service: name, Port: 80}
- endpoints := []string{
- "192.168.10.10:8080",
- }
- cfgParams := ConfigParams{
- LBMethod: "random",
- MaxFails: 1,
- MaxConns: 0,
- FailTimeout: "10s",
- Keepalive: 21,
- UpstreamZoneSize: "256k",
- }
-
- expected := version2.Upstream{
- Name: "test-upstream",
- UpstreamLabels: version2.UpstreamLabels{
- Service: "test-upstream",
- },
- Servers: []version2.UpstreamServer{
- {
- Address: "192.168.10.10:8080",
+ expectedWarnings: Warnings{
+ nil: {
+ `Multiple basic auth policies in the same context is not valid. Basic auth policy default/basic-auth-policy2 will be ignored`,
+ },
},
+ expectedOidc: &oidcPolicyCfg{},
+ msg: "multi basic auth reference",
},
- MaxFails: 1,
- MaxConns: 0,
- FailTimeout: "10s",
- LBMethod: "random",
- Keepalive: 21,
- UpstreamZoneSize: "256k",
- }
-
- vsc := newVirtualServerConfigurator(&cfgParams, false, false, &StaticConfigParams{}, false)
- result := vsc.generateUpstream(nil, name, upstream, false, endpoints)
- if !reflect.DeepEqual(result, expected) {
- t.Errorf("generateUpstream() returned %v but expected %v", result, expected)
- }
-
- if len(vsc.warnings) != 0 {
- t.Errorf("generateUpstream returned warnings for %v", upstream)
- }
-}
-
-func TestGenerateUpstreamWithKeepalive(t *testing.T) {
- t.Parallel()
- name := "test-upstream"
- noKeepalive := 0
- keepalive := 32
- endpoints := []string{
- "192.168.10.10:8080",
- }
-
- tests := []struct {
- upstream conf_v1.Upstream
- cfgParams *ConfigParams
- expected version2.Upstream
- msg string
- }{
{
- conf_v1.Upstream{Keepalive: &keepalive, Service: name, Port: 80},
- &ConfigParams{Keepalive: 21},
- version2.Upstream{
- Name: "test-upstream",
- UpstreamLabels: version2.UpstreamLabels{
- Service: "test-upstream",
+ policyRefs: []conf_v1.PolicyReference{
+ {
+ Name: "ingress-mtls-policy",
+ Namespace: "default",
},
- Servers: []version2.UpstreamServer{
- {
- Address: "192.168.10.10:8080",
+ },
+ policies: map[string]*conf_v1.Policy{
+ "default/ingress-mtls-policy": {
+ ObjectMeta: meta_v1.ObjectMeta{
+ Name: "ingress-mtls-policy",
+ Namespace: "default",
+ },
+ Spec: conf_v1.PolicySpec{
+ IngressMTLS: &conf_v1.IngressMTLS{
+ ClientCertSecret: "ingress-mtls-secret",
+ },
},
},
- Keepalive: 32,
},
- "upstream keepalive set, configparam set",
- },
- {
- conf_v1.Upstream{Service: name, Port: 80},
- &ConfigParams{Keepalive: 21},
- version2.Upstream{
- Name: "test-upstream",
- UpstreamLabels: version2.UpstreamLabels{
- Service: "test-upstream",
- },
- Servers: []version2.UpstreamServer{
- {
- Address: "192.168.10.10:8080",
+ policyOpts: policyOptions{
+ tls: true,
+ secretRefs: map[string]*secrets.SecretReference{
+ "default/ingress-mtls-secret": {
+ Error: errors.New("secret is invalid"),
},
},
- Keepalive: 21,
},
- "upstream keepalive not set, configparam set",
- },
- {
- conf_v1.Upstream{Keepalive: &noKeepalive, Service: name, Port: 80},
- &ConfigParams{Keepalive: 21},
- version2.Upstream{
- Name: "test-upstream",
- UpstreamLabels: version2.UpstreamLabels{
- Service: "test-upstream",
+ context: "spec",
+ expected: policiesCfg{
+ ErrorReturn: &version2.Return{
+ Code: 500,
},
- Servers: []version2.UpstreamServer{
- {
- Address: "192.168.10.10:8080",
- },
+ },
+ expectedWarnings: Warnings{
+ nil: {
+ `IngressMTLS policy "default/ingress-mtls-policy" references an invalid secret default/ingress-mtls-secret: secret is invalid`,
},
},
- "upstream keepalive set to 0, configparam set",
+ expectedOidc: &oidcPolicyCfg{},
+ msg: "ingress mtls reference an invalid secret",
},
- }
-
- for _, test := range tests {
- vsc := newVirtualServerConfigurator(test.cfgParams, false, false, &StaticConfigParams{}, false)
- result := vsc.generateUpstream(nil, name, test.upstream, false, endpoints)
- if !reflect.DeepEqual(result, test.expected) {
- t.Errorf("generateUpstream() returned %v but expected %v for the case of %v", result, test.expected, test.msg)
- }
-
- if len(vsc.warnings) != 0 {
- t.Errorf("generateUpstream() returned warnings for %v", test.upstream)
- }
- }
+ {
+ policyRefs: []conf_v1.PolicyReference{
+ {
+ Name: "ingress-mtls-policy",
+ Namespace: "default",
+ },
+ },
+ policies: map[string]*conf_v1.Policy{
+ "default/ingress-mtls-policy": {
+ ObjectMeta: meta_v1.ObjectMeta{
+ Name: "ingress-mtls-policy",
+ Namespace: "default",
+ },
+ Spec: conf_v1.PolicySpec{
+ IngressMTLS: &conf_v1.IngressMTLS{
+ ClientCertSecret: "ingress-mtls-secret",
+ },
+ },
+ },
+ },
+ policyOpts: policyOptions{
+ tls: true,
+ secretRefs: map[string]*secrets.SecretReference{
+ "default/ingress-mtls-secret": {
+ Secret: &api_v1.Secret{
+ Type: api_v1.SecretTypeTLS,
+ },
+ },
+ },
+ },
+ context: "spec",
+ expected: policiesCfg{
+ ErrorReturn: &version2.Return{
+ Code: 500,
+ },
+ },
+ expectedWarnings: Warnings{
+ nil: {
+ `IngressMTLS policy default/ingress-mtls-policy references a secret default/ingress-mtls-secret of a wrong type 'kubernetes.io/tls', must be 'nginx.org/ca'`,
+ },
+ },
+ expectedOidc: &oidcPolicyCfg{},
+ msg: "ingress mtls references wrong secret type",
+ },
+ {
+ policyRefs: []conf_v1.PolicyReference{
+ {
+ Name: "ingress-mtls-policy",
+ Namespace: "default",
+ },
+ {
+ Name: "ingress-mtls-policy2",
+ Namespace: "default",
+ },
+ },
+ policies: map[string]*conf_v1.Policy{
+ "default/ingress-mtls-policy": {
+ ObjectMeta: meta_v1.ObjectMeta{
+ Name: "ingress-mtls-policy",
+ Namespace: "default",
+ },
+ Spec: conf_v1.PolicySpec{
+ IngressMTLS: &conf_v1.IngressMTLS{
+ ClientCertSecret: "ingress-mtls-secret",
+ },
+ },
+ },
+ "default/ingress-mtls-policy2": {
+ Spec: conf_v1.PolicySpec{
+ IngressMTLS: &conf_v1.IngressMTLS{
+ ClientCertSecret: "ingress-mtls-secret2",
+ },
+ },
+ },
+ },
+ policyOpts: policyOptions{
+ tls: true,
+ secretRefs: map[string]*secrets.SecretReference{
+ "default/ingress-mtls-secret": {
+ Secret: &api_v1.Secret{
+ Type: secrets.SecretTypeCA,
+ },
+ Path: ingressMTLSCertPath,
+ },
+ },
+ },
+ context: "spec",
+ expected: policiesCfg{
+ IngressMTLS: &version2.IngressMTLS{
+ ClientCert: ingressMTLSCertPath,
+ VerifyClient: "on",
+ VerifyDepth: 1,
+ },
+ },
+ expectedWarnings: Warnings{
+ nil: {
+ `Multiple ingressMTLS policies are not allowed. IngressMTLS policy default/ingress-mtls-policy2 will be ignored`,
+ },
+ },
+ expectedOidc: &oidcPolicyCfg{},
+ msg: "multi ingress mtls",
+ },
+ {
+ policyRefs: []conf_v1.PolicyReference{
+ {
+ Name: "ingress-mtls-policy",
+ Namespace: "default",
+ },
+ },
+ policies: map[string]*conf_v1.Policy{
+ "default/ingress-mtls-policy": {
+ ObjectMeta: meta_v1.ObjectMeta{
+ Name: "ingress-mtls-policy",
+ Namespace: "default",
+ },
+ Spec: conf_v1.PolicySpec{
+ IngressMTLS: &conf_v1.IngressMTLS{
+ ClientCertSecret: "ingress-mtls-secret",
+ },
+ },
+ },
+ },
+ policyOpts: policyOptions{
+ tls: true,
+ secretRefs: map[string]*secrets.SecretReference{
+ "default/ingress-mtls-secret": {
+ Secret: &api_v1.Secret{
+ Type: secrets.SecretTypeCA,
+ },
+ Path: ingressMTLSCertPath,
+ },
+ },
+ },
+ context: "route",
+ expected: policiesCfg{
+ ErrorReturn: &version2.Return{
+ Code: 500,
+ },
+ },
+ expectedWarnings: Warnings{
+ nil: {
+ `IngressMTLS policy default/ingress-mtls-policy is not allowed in the route context`,
+ },
+ },
+ expectedOidc: &oidcPolicyCfg{},
+ msg: "ingress mtls in the wrong context",
+ },
+ {
+ policyRefs: []conf_v1.PolicyReference{
+ {
+ Name: "ingress-mtls-policy",
+ Namespace: "default",
+ },
+ },
+ policies: map[string]*conf_v1.Policy{
+ "default/ingress-mtls-policy": {
+ ObjectMeta: meta_v1.ObjectMeta{
+ Name: "ingress-mtls-policy",
+ Namespace: "default",
+ },
+ Spec: conf_v1.PolicySpec{
+ IngressMTLS: &conf_v1.IngressMTLS{
+ ClientCertSecret: "ingress-mtls-secret",
+ },
+ },
+ },
+ },
+ policyOpts: policyOptions{
+ tls: false,
+ secretRefs: map[string]*secrets.SecretReference{
+ "default/ingress-mtls-secret": {
+ Secret: &api_v1.Secret{
+ Type: secrets.SecretTypeCA,
+ },
+ Path: ingressMTLSCertPath,
+ },
+ },
+ },
+ context: "route",
+ expected: policiesCfg{
+ ErrorReturn: &version2.Return{
+ Code: 500,
+ },
+ },
+ expectedWarnings: Warnings{
+ nil: {
+ `TLS must be enabled in VirtualServer for IngressMTLS policy default/ingress-mtls-policy`,
+ },
+ },
+ expectedOidc: &oidcPolicyCfg{},
+ msg: "ingress mtls missing TLS config",
+ },
+ {
+ policyRefs: []conf_v1.PolicyReference{
+ {
+ Name: "ingress-mtls-policy",
+ Namespace: "default",
+ },
+ },
+ policies: map[string]*conf_v1.Policy{
+ "default/ingress-mtls-policy": {
+ ObjectMeta: meta_v1.ObjectMeta{
+ Name: "ingress-mtls-policy",
+ Namespace: "default",
+ },
+ Spec: conf_v1.PolicySpec{
+ IngressMTLS: &conf_v1.IngressMTLS{
+ ClientCertSecret: "ingress-mtls-secret",
+ CrlFileName: "default-ingress-mtls-secret-ca.crl",
+ },
+ },
+ },
+ },
+ policyOpts: policyOptions{
+ tls: true,
+ secretRefs: map[string]*secrets.SecretReference{
+ "default/ingress-mtls-secret": {
+ Secret: &api_v1.Secret{
+ Type: secrets.SecretTypeCA,
+ Data: map[string][]byte{
+ "ca.crl": []byte("base64crl"),
+ },
+ },
+ Path: ingressMTLSCertPath,
+ },
+ },
+ },
+ context: "spec",
+ expected: policiesCfg{
+ IngressMTLS: &version2.IngressMTLS{
+ ClientCert: ingressMTLSCertPath,
+ ClientCrl: ingressMTLSCrlPath,
+ VerifyClient: "on",
+ VerifyDepth: 1,
+ },
+ ErrorReturn: nil,
+ },
+ expectedWarnings: Warnings{
+ nil: {
+ `Both ca.crl in the Secret and ingressMTLS.crlFileName fields cannot be used. ca.crl in default/ingress-mtls-secret will be ignored and default/ingress-mtls-policy will be applied`,
+ },
+ },
+ expectedOidc: &oidcPolicyCfg{},
+ msg: "ingress mtls ca.crl and ingressMTLS.Crl set",
+ },
+ {
+ policyRefs: []conf_v1.PolicyReference{
+ {
+ Name: "egress-mtls-policy",
+ Namespace: "default",
+ },
+ {
+ Name: "egress-mtls-policy2",
+ Namespace: "default",
+ },
+ },
+ policies: map[string]*conf_v1.Policy{
+ "default/egress-mtls-policy": {
+ ObjectMeta: meta_v1.ObjectMeta{
+ Name: "egress-mtls-policy",
+ Namespace: "default",
+ },
+ Spec: conf_v1.PolicySpec{
+ EgressMTLS: &conf_v1.EgressMTLS{
+ TLSSecret: "egress-mtls-secret",
+ },
+ },
+ },
+ "default/egress-mtls-policy2": {
+ ObjectMeta: meta_v1.ObjectMeta{
+ Name: "egress-mtls-policy2",
+ Namespace: "default",
+ },
+ Spec: conf_v1.PolicySpec{
+ EgressMTLS: &conf_v1.EgressMTLS{
+ TLSSecret: "egress-mtls-secret2",
+ },
+ },
+ },
+ },
+ policyOpts: policyOptions{
+ secretRefs: map[string]*secrets.SecretReference{
+ "default/egress-mtls-secret": {
+ Secret: &api_v1.Secret{
+ Type: api_v1.SecretTypeTLS,
+ },
+ Path: "/etc/nginx/secrets/default-egress-mtls-secret",
+ },
+ },
+ },
+ context: "route",
+ expected: policiesCfg{
+ EgressMTLS: &version2.EgressMTLS{
+ Certificate: "/etc/nginx/secrets/default-egress-mtls-secret",
+ CertificateKey: "/etc/nginx/secrets/default-egress-mtls-secret",
+ VerifyServer: false,
+ VerifyDepth: 1,
+ Ciphers: "DEFAULT",
+ Protocols: "TLSv1 TLSv1.1 TLSv1.2",
+ SessionReuse: true,
+ SSLName: "$proxy_host",
+ },
+ },
+ expectedWarnings: Warnings{
+ nil: {
+ `Multiple egressMTLS policies in the same context is not valid. EgressMTLS policy default/egress-mtls-policy2 will be ignored`,
+ },
+ },
+ expectedOidc: &oidcPolicyCfg{},
+ msg: "multi egress mtls",
+ },
+ {
+ policyRefs: []conf_v1.PolicyReference{
+ {
+ Name: "egress-mtls-policy",
+ Namespace: "default",
+ },
+ },
+ policies: map[string]*conf_v1.Policy{
+ "default/egress-mtls-policy": {
+ ObjectMeta: meta_v1.ObjectMeta{
+ Name: "egress-mtls-policy",
+ Namespace: "default",
+ },
+ Spec: conf_v1.PolicySpec{
+ EgressMTLS: &conf_v1.EgressMTLS{
+ TrustedCertSecret: "egress-trusted-secret",
+ SSLName: "foo.com",
+ },
+ },
+ },
+ },
+ policyOpts: policyOptions{
+ secretRefs: map[string]*secrets.SecretReference{
+ "default/egress-trusted-secret": {
+ Secret: &api_v1.Secret{
+ Type: secrets.SecretTypeCA,
+ },
+ Error: errors.New("secret is invalid"),
+ },
+ },
+ },
+ context: "route",
+ expected: policiesCfg{
+ ErrorReturn: &version2.Return{
+ Code: 500,
+ },
+ },
+ expectedWarnings: Warnings{
+ nil: {
+ `EgressMTLS policy default/egress-mtls-policy references an invalid secret default/egress-trusted-secret: secret is invalid`,
+ },
+ },
+ expectedOidc: &oidcPolicyCfg{},
+ msg: "egress mtls referencing an invalid CA secret",
+ },
+ {
+ policyRefs: []conf_v1.PolicyReference{
+ {
+ Name: "egress-mtls-policy",
+ Namespace: "default",
+ },
+ },
+ policies: map[string]*conf_v1.Policy{
+ "default/egress-mtls-policy": {
+ ObjectMeta: meta_v1.ObjectMeta{
+ Name: "egress-mtls-policy",
+ Namespace: "default",
+ },
+ Spec: conf_v1.PolicySpec{
+ EgressMTLS: &conf_v1.EgressMTLS{
+ TLSSecret: "egress-mtls-secret",
+ SSLName: "foo.com",
+ },
+ },
+ },
+ },
+ policyOpts: policyOptions{
+ secretRefs: map[string]*secrets.SecretReference{
+ "default/egress-mtls-secret": {
+ Secret: &api_v1.Secret{
+ Type: secrets.SecretTypeCA,
+ },
+ },
+ },
+ },
+ context: "route",
+ expected: policiesCfg{
+ ErrorReturn: &version2.Return{
+ Code: 500,
+ },
+ },
+ expectedWarnings: Warnings{
+ nil: {
+ `EgressMTLS policy default/egress-mtls-policy references a secret default/egress-mtls-secret of a wrong type 'nginx.org/ca', must be 'kubernetes.io/tls'`,
+ },
+ },
+ expectedOidc: &oidcPolicyCfg{},
+ msg: "egress mtls referencing wrong secret type",
+ },
+ {
+ policyRefs: []conf_v1.PolicyReference{
+ {
+ Name: "egress-mtls-policy",
+ Namespace: "default",
+ },
+ },
+ policies: map[string]*conf_v1.Policy{
+ "default/egress-mtls-policy": {
+ ObjectMeta: meta_v1.ObjectMeta{
+ Name: "egress-mtls-policy",
+ Namespace: "default",
+ },
+ Spec: conf_v1.PolicySpec{
+ EgressMTLS: &conf_v1.EgressMTLS{
+ TrustedCertSecret: "egress-trusted-secret",
+ SSLName: "foo.com",
+ },
+ },
+ },
+ },
+ policyOpts: policyOptions{
+ secretRefs: map[string]*secrets.SecretReference{
+ "default/egress-trusted-secret": {
+ Secret: &api_v1.Secret{
+ Type: api_v1.SecretTypeTLS,
+ },
+ },
+ },
+ },
+ context: "route",
+ expected: policiesCfg{
+ ErrorReturn: &version2.Return{
+ Code: 500,
+ },
+ },
+ expectedWarnings: Warnings{
+ nil: {
+ `EgressMTLS policy default/egress-mtls-policy references a secret default/egress-trusted-secret of a wrong type 'kubernetes.io/tls', must be 'nginx.org/ca'`,
+ },
+ },
+ expectedOidc: &oidcPolicyCfg{},
+ msg: "egress trusted secret referencing wrong secret type",
+ },
+ {
+ policyRefs: []conf_v1.PolicyReference{
+ {
+ Name: "egress-mtls-policy",
+ Namespace: "default",
+ },
+ },
+ policies: map[string]*conf_v1.Policy{
+ "default/egress-mtls-policy": {
+ ObjectMeta: meta_v1.ObjectMeta{
+ Name: "egress-mtls-policy",
+ Namespace: "default",
+ },
+ Spec: conf_v1.PolicySpec{
+ EgressMTLS: &conf_v1.EgressMTLS{
+ TLSSecret: "egress-mtls-secret",
+ SSLName: "foo.com",
+ },
+ },
+ },
+ },
+ policyOpts: policyOptions{
+ secretRefs: map[string]*secrets.SecretReference{
+ "default/egress-mtls-secret": {
+ Secret: &api_v1.Secret{
+ Type: api_v1.SecretTypeTLS,
+ },
+ Error: errors.New("secret is invalid"),
+ },
+ },
+ },
+ context: "route",
+ expected: policiesCfg{
+ ErrorReturn: &version2.Return{
+ Code: 500,
+ },
+ },
+ expectedWarnings: Warnings{
+ nil: {
+ `EgressMTLS policy default/egress-mtls-policy references an invalid secret default/egress-mtls-secret: secret is invalid`,
+ },
+ },
+ expectedOidc: &oidcPolicyCfg{},
+ msg: "egress mtls referencing missing tls secret",
+ },
+ {
+ policyRefs: []conf_v1.PolicyReference{
+ {
+ Name: "oidc-policy",
+ Namespace: "default",
+ },
+ },
+ policies: map[string]*conf_v1.Policy{
+ "default/oidc-policy": {
+ ObjectMeta: meta_v1.ObjectMeta{
+ Name: "oidc-policy",
+ Namespace: "default",
+ },
+ Spec: conf_v1.PolicySpec{
+ OIDC: &conf_v1.OIDC{
+ ClientSecret: "oidc-secret",
+ },
+ },
+ },
+ },
+ policyOpts: policyOptions{
+ secretRefs: map[string]*secrets.SecretReference{
+ "default/oidc-secret": {
+ Secret: &api_v1.Secret{
+ Type: secrets.SecretTypeOIDC,
+ },
+ Error: errors.New("secret is invalid"),
+ },
+ },
+ },
+ context: "route",
+ expected: policiesCfg{
+ ErrorReturn: &version2.Return{
+ Code: 500,
+ },
+ },
+ expectedWarnings: Warnings{
+ nil: {
+ `OIDC policy default/oidc-policy references an invalid secret default/oidc-secret: secret is invalid`,
+ },
+ },
+ expectedOidc: &oidcPolicyCfg{},
+ msg: "oidc referencing missing oidc secret",
+ },
+ {
+ policyRefs: []conf_v1.PolicyReference{
+ {
+ Name: "oidc-policy",
+ Namespace: "default",
+ },
+ },
+ policies: map[string]*conf_v1.Policy{
+ "default/oidc-policy": {
+ ObjectMeta: meta_v1.ObjectMeta{
+ Name: "oidc-policy",
+ Namespace: "default",
+ },
+ Spec: conf_v1.PolicySpec{
+ OIDC: &conf_v1.OIDC{
+ ClientSecret: "oidc-secret",
+ AuthEndpoint: "http://foo.com/bar",
+ TokenEndpoint: "http://foo.com/bar",
+ JWKSURI: "http://foo.com/bar",
+ AccessTokenEnable: true,
+ },
+ },
+ },
+ },
+ policyOpts: policyOptions{
+ secretRefs: map[string]*secrets.SecretReference{
+ "default/oidc-secret": {
+ Secret: &api_v1.Secret{
+ Type: api_v1.SecretTypeTLS,
+ },
+ },
+ },
+ },
+ context: "spec",
+ expected: policiesCfg{
+ ErrorReturn: &version2.Return{
+ Code: 500,
+ },
+ },
+ expectedWarnings: Warnings{
+ nil: {
+ `OIDC policy default/oidc-policy references a secret default/oidc-secret of a wrong type 'kubernetes.io/tls', must be 'nginx.org/oidc'`,
+ },
+ },
+ expectedOidc: &oidcPolicyCfg{},
+ msg: "oidc secret referencing wrong secret type",
+ },
+ {
+ policyRefs: []conf_v1.PolicyReference{
+ {
+ Name: "oidc-policy-2",
+ Namespace: "default",
+ },
+ },
+ policies: map[string]*conf_v1.Policy{
+ "default/oidc-policy-1": {
+ ObjectMeta: meta_v1.ObjectMeta{
+ Name: "oidc-policy-1",
+ Namespace: "default",
+ },
+ Spec: conf_v1.PolicySpec{
+ OIDC: &conf_v1.OIDC{
+ ClientID: "foo",
+ ClientSecret: "oidc-secret",
+ AuthEndpoint: "https://foo.com/auth",
+ TokenEndpoint: "https://foo.com/token",
+ JWKSURI: "https://foo.com/certs",
+ AccessTokenEnable: true,
+ },
+ },
+ },
+ "default/oidc-policy-2": {
+ ObjectMeta: meta_v1.ObjectMeta{
+ Name: "oidc-policy-2",
+ Namespace: "default",
+ },
+ Spec: conf_v1.PolicySpec{
+ OIDC: &conf_v1.OIDC{
+ ClientID: "foo",
+ ClientSecret: "oidc-secret",
+ AuthEndpoint: "https://bar.com/auth",
+ TokenEndpoint: "https://bar.com/token",
+ JWKSURI: "https://bar.com/certs",
+ AccessTokenEnable: true,
+ },
+ },
+ },
+ },
+ policyOpts: policyOptions{
+ secretRefs: map[string]*secrets.SecretReference{
+ "default/oidc-secret": {
+ Secret: &api_v1.Secret{
+ Type: secrets.SecretTypeOIDC,
+ Data: map[string][]byte{
+ "client-secret": []byte("super_secret_123"),
+ },
+ },
+ },
+ },
+ },
+ context: "route",
+ oidcPolCfg: &oidcPolicyCfg{
+ oidc: &version2.OIDC{
+ AuthEndpoint: "https://foo.com/auth",
+ TokenEndpoint: "https://foo.com/token",
+ JwksURI: "https://foo.com/certs",
+ ClientID: "foo",
+ ClientSecret: "super_secret_123",
+ RedirectURI: "/_codexch",
+ Scope: "openid",
+ ZoneSyncLeeway: 0,
+ AccessTokenEnable: true,
+ },
+ key: "default/oidc-policy-1",
+ },
+ expected: policiesCfg{
+ ErrorReturn: &version2.Return{
+ Code: 500,
+ },
+ },
+ expectedWarnings: Warnings{
+ nil: {
+ `Only one oidc policy is allowed in a VirtualServer and its VirtualServerRoutes. Can't use default/oidc-policy-2. Use default/oidc-policy-1`,
+ },
+ },
+ expectedOidc: &oidcPolicyCfg{
+ oidc: &version2.OIDC{
+ AuthEndpoint: "https://foo.com/auth",
+ TokenEndpoint: "https://foo.com/token",
+ JwksURI: "https://foo.com/certs",
+ ClientID: "foo",
+ ClientSecret: "super_secret_123",
+ RedirectURI: "/_codexch",
+ Scope: "openid",
+ AccessTokenEnable: true,
+ },
+ key: "default/oidc-policy-1",
+ },
+ msg: "multiple oidc policies",
+ },
+ {
+ policyRefs: []conf_v1.PolicyReference{
+ {
+ Name: "oidc-policy",
+ Namespace: "default",
+ },
+ {
+ Name: "oidc-policy2",
+ Namespace: "default",
+ },
+ },
+ policies: map[string]*conf_v1.Policy{
+ "default/oidc-policy": {
+ ObjectMeta: meta_v1.ObjectMeta{
+ Name: "oidc-policy",
+ Namespace: "default",
+ },
+ Spec: conf_v1.PolicySpec{
+ OIDC: &conf_v1.OIDC{
+ ClientSecret: "oidc-secret",
+ AuthEndpoint: "https://foo.com/auth",
+ TokenEndpoint: "https://foo.com/token",
+ JWKSURI: "https://foo.com/certs",
+ ClientID: "foo",
+ AccessTokenEnable: true,
+ },
+ },
+ },
+ "default/oidc-policy2": {
+ ObjectMeta: meta_v1.ObjectMeta{
+ Name: "oidc-policy2",
+ Namespace: "default",
+ },
+ Spec: conf_v1.PolicySpec{
+ OIDC: &conf_v1.OIDC{
+ ClientSecret: "oidc-secret",
+ AuthEndpoint: "https://bar.com/auth",
+ TokenEndpoint: "https://bar.com/token",
+ JWKSURI: "https://bar.com/certs",
+ ClientID: "bar",
+ AccessTokenEnable: true,
+ },
+ },
+ },
+ },
+ policyOpts: policyOptions{
+ secretRefs: map[string]*secrets.SecretReference{
+ "default/oidc-secret": {
+ Secret: &api_v1.Secret{
+ Type: secrets.SecretTypeOIDC,
+ Data: map[string][]byte{
+ "client-secret": []byte("super_secret_123"),
+ },
+ },
+ },
+ },
+ },
+ context: "route",
+ expected: policiesCfg{
+ OIDC: true,
+ },
+ expectedWarnings: Warnings{
+ nil: {
+ `Multiple oidc policies in the same context is not valid. OIDC policy default/oidc-policy2 will be ignored`,
+ },
+ },
+ expectedOidc: &oidcPolicyCfg{
+ &version2.OIDC{
+ AuthEndpoint: "https://foo.com/auth",
+ TokenEndpoint: "https://foo.com/token",
+ JwksURI: "https://foo.com/certs",
+ ClientID: "foo",
+ ClientSecret: "super_secret_123",
+ RedirectURI: "/_codexch",
+ Scope: "openid",
+ ZoneSyncLeeway: 200,
+ AccessTokenEnable: true,
+ },
+ "default/oidc-policy",
+ },
+ msg: "multi oidc",
+ },
+ {
+ policyRefs: []conf_v1.PolicyReference{
+ {
+ Name: "waf-policy",
+ Namespace: "default",
+ },
+ {
+ Name: "waf-policy2",
+ Namespace: "default",
+ },
+ },
+ policies: map[string]*conf_v1.Policy{
+ "default/waf-policy": {
+ ObjectMeta: meta_v1.ObjectMeta{
+ Name: "waf-policy",
+ Namespace: "default",
+ },
+ Spec: conf_v1.PolicySpec{
+ WAF: &conf_v1.WAF{
+ Enable: true,
+ ApPolicy: "default/dataguard-alarm",
+ },
+ },
+ },
+ "default/waf-policy2": {
+ ObjectMeta: meta_v1.ObjectMeta{
+ Name: "waf-policy2",
+ Namespace: "default",
+ },
+ Spec: conf_v1.PolicySpec{
+ WAF: &conf_v1.WAF{
+ Enable: true,
+ ApPolicy: "default/dataguard-alarm",
+ },
+ },
+ },
+ },
+ policyOpts: policyOptions{
+ apResources: &appProtectResourcesForVS{
+ Policies: map[string]string{
+ "default/dataguard-alarm": "/etc/nginx/waf/nac-policies/default-dataguard-alarm",
+ },
+ LogConfs: map[string]string{
+ "default/logconf": "/etc/nginx/waf/nac-logconfs/default-logconf",
+ },
+ },
+ },
+ context: "route",
+ expected: policiesCfg{
+ WAF: &version2.WAF{
+ Enable: "on",
+ ApPolicy: "/etc/nginx/waf/nac-policies/default-dataguard-alarm",
+ },
+ },
+ expectedWarnings: Warnings{
+ nil: {
+ `Multiple WAF policies in the same context is not valid. WAF policy default/waf-policy2 will be ignored`,
+ },
+ },
+ expectedOidc: &oidcPolicyCfg{},
+ msg: "multi waf",
+ },
+ }
+
+ for _, test := range tests {
+ vsc := newVirtualServerConfigurator(&ConfigParams{}, false, false, &StaticConfigParams{}, false)
+
+ if test.oidcPolCfg != nil {
+ vsc.oidcPolCfg = test.oidcPolCfg
+ }
+
+ result := vsc.generatePolicies(ownerDetails, test.policyRefs, test.policies, test.context, test.policyOpts)
+ if diff := cmp.Diff(test.expected, result); diff != "" {
+ t.Errorf("generatePolicies() '%v' mismatch (-want +got):\n%s", test.msg, diff)
+ }
+ if !reflect.DeepEqual(vsc.warnings, test.expectedWarnings) {
+ t.Errorf(
+ "generatePolicies() returned warnings of \n%v but expected \n%v for the case of %s",
+ vsc.warnings,
+ test.expectedWarnings,
+ test.msg,
+ )
+ }
+ if diff := cmp.Diff(test.expectedOidc.oidc, vsc.oidcPolCfg.oidc); diff != "" {
+ t.Errorf("generatePolicies() '%v' mismatch (-want +got):\n%s", test.msg, diff)
+ }
+ if diff := cmp.Diff(test.expectedOidc.key, vsc.oidcPolCfg.key); diff != "" {
+ t.Errorf("generatePolicies() '%v' mismatch (-want +got):\n%s", test.msg, diff)
+ }
+ }
+}
+
+func TestRemoveDuplicates(t *testing.T) {
+ t.Parallel()
+ tests := []struct {
+ rlz []version2.LimitReqZone
+ expected []version2.LimitReqZone
+ }{
+ {
+ rlz: []version2.LimitReqZone{
+ {ZoneName: "test"},
+ {ZoneName: "test"},
+ {ZoneName: "test2"},
+ {ZoneName: "test3"},
+ },
+ expected: []version2.LimitReqZone{
+ {ZoneName: "test"},
+ {ZoneName: "test2"},
+ {ZoneName: "test3"},
+ },
+ },
+ {
+ rlz: []version2.LimitReqZone{
+ {ZoneName: "test"},
+ {ZoneName: "test"},
+ {ZoneName: "test2"},
+ {ZoneName: "test3"},
+ {ZoneName: "test3"},
+ },
+ expected: []version2.LimitReqZone{
+ {ZoneName: "test"},
+ {ZoneName: "test2"},
+ {ZoneName: "test3"},
+ },
+ },
+ }
+ for _, test := range tests {
+ result := removeDuplicateLimitReqZones(test.rlz)
+ if !reflect.DeepEqual(result, test.expected) {
+ t.Errorf("removeDuplicateLimitReqZones() returned \n%v, but expected \n%v", result, test.expected)
+ }
+ }
+}
+
+func TestAddPoliciesCfgToLocations(t *testing.T) {
+ t.Parallel()
+ cfg := policiesCfg{
+ Allow: []string{"127.0.0.1"},
+ Deny: []string{"127.0.0.2"},
+ ErrorReturn: &version2.Return{
+ Code: 400,
+ },
+ }
+
+ locations := []version2.Location{
+ {
+ Path: "/",
+ },
+ }
+
+ expectedLocations := []version2.Location{
+ {
+ Path: "/",
+ Allow: []string{"127.0.0.1"},
+ Deny: []string{"127.0.0.2"},
+ PoliciesErrorReturn: &version2.Return{
+ Code: 400,
+ },
+ },
+ }
+
+ addPoliciesCfgToLocations(cfg, locations)
+ if !reflect.DeepEqual(locations, expectedLocations) {
+ t.Errorf("addPoliciesCfgToLocations() returned \n%+v but expected \n%+v", locations, expectedLocations)
+ }
+}
+
+func TestGenerateUpstream(t *testing.T) {
+ t.Parallel()
+ name := "test-upstream"
+ upstream := conf_v1.Upstream{Service: name, Port: 80}
+ endpoints := []string{
+ "192.168.10.10:8080",
+ }
+ cfgParams := ConfigParams{
+ LBMethod: "random",
+ MaxFails: 1,
+ MaxConns: 0,
+ FailTimeout: "10s",
+ Keepalive: 21,
+ UpstreamZoneSize: "256k",
+ }
+
+ expected := version2.Upstream{
+ Name: "test-upstream",
+ UpstreamLabels: version2.UpstreamLabels{
+ Service: "test-upstream",
+ },
+ Servers: []version2.UpstreamServer{
+ {
+ Address: "192.168.10.10:8080",
+ },
+ },
+ MaxFails: 1,
+ MaxConns: 0,
+ FailTimeout: "10s",
+ LBMethod: "random",
+ Keepalive: 21,
+ UpstreamZoneSize: "256k",
+ }
+
+ vsc := newVirtualServerConfigurator(&cfgParams, false, false, &StaticConfigParams{}, false)
+ result := vsc.generateUpstream(nil, name, upstream, false, endpoints)
+ if !reflect.DeepEqual(result, expected) {
+ t.Errorf("generateUpstream() returned %v but expected %v", result, expected)
+ }
+
+ if len(vsc.warnings) != 0 {
+ t.Errorf("generateUpstream returned warnings for %v", upstream)
+ }
+}
+
+func TestGenerateUpstreamWithKeepalive(t *testing.T) {
+ t.Parallel()
+ name := "test-upstream"
+ noKeepalive := 0
+ keepalive := 32
+ endpoints := []string{
+ "192.168.10.10:8080",
+ }
+
+ tests := []struct {
+ upstream conf_v1.Upstream
+ cfgParams *ConfigParams
+ expected version2.Upstream
+ msg string
+ }{
+ {
+ conf_v1.Upstream{Keepalive: &keepalive, Service: name, Port: 80},
+ &ConfigParams{Keepalive: 21},
+ version2.Upstream{
+ Name: "test-upstream",
+ UpstreamLabels: version2.UpstreamLabels{
+ Service: "test-upstream",
+ },
+ Servers: []version2.UpstreamServer{
+ {
+ Address: "192.168.10.10:8080",
+ },
+ },
+ Keepalive: 32,
+ },
+ "upstream keepalive set, configparam set",
+ },
+ {
+ conf_v1.Upstream{Service: name, Port: 80},
+ &ConfigParams{Keepalive: 21},
+ version2.Upstream{
+ Name: "test-upstream",
+ UpstreamLabels: version2.UpstreamLabels{
+ Service: "test-upstream",
+ },
+ Servers: []version2.UpstreamServer{
+ {
+ Address: "192.168.10.10:8080",
+ },
+ },
+ Keepalive: 21,
+ },
+ "upstream keepalive not set, configparam set",
+ },
+ {
+ conf_v1.Upstream{Keepalive: &noKeepalive, Service: name, Port: 80},
+ &ConfigParams{Keepalive: 21},
+ version2.Upstream{
+ Name: "test-upstream",
+ UpstreamLabels: version2.UpstreamLabels{
+ Service: "test-upstream",
+ },
+ Servers: []version2.UpstreamServer{
+ {
+ Address: "192.168.10.10:8080",
+ },
+ },
+ },
+ "upstream keepalive set to 0, configparam set",
+ },
+ }
+
+ for _, test := range tests {
+ vsc := newVirtualServerConfigurator(test.cfgParams, false, false, &StaticConfigParams{}, false)
+ result := vsc.generateUpstream(nil, name, test.upstream, false, endpoints)
+ if !reflect.DeepEqual(result, test.expected) {
+ t.Errorf("generateUpstream() returned %v but expected %v for the case of %v", result, test.expected, test.msg)
+ }
+
+ if len(vsc.warnings) != 0 {
+ t.Errorf("generateUpstream() returned warnings for %v", test.upstream)
+ }
+ }
}
func TestGenerateUpstreamForExternalNameService(t *testing.T) {
@@ -6669,97 +7443,510 @@ func TestGenerateDefaultSplitsConfig(t *testing.T) {
},
},
}
- virtualServer := conf_v1.VirtualServer{
- ObjectMeta: meta_v1.ObjectMeta{
- Name: "cafe",
- Namespace: "default",
- },
- }
+ virtualServer := conf_v1.VirtualServer{
+ ObjectMeta: meta_v1.ObjectMeta{
+ Name: "cafe",
+ Namespace: "default",
+ },
+ }
+ upstreamNamer := NewUpstreamNamerForVirtualServer(&virtualServer)
+ variableNamer := newVariableNamer(&virtualServer)
+ index := 1
+
+ expected := routingCfg{
+ SplitClients: []version2.SplitClient{
+ {
+ Source: "$request_id",
+ Variable: "$vs_default_cafe_splits_1",
+ Distributions: []version2.Distribution{
+ {
+ Weight: "90%",
+ Value: "/internal_location_splits_1_split_0",
+ },
+ {
+ Weight: "10%",
+ Value: "/internal_location_splits_1_split_1",
+ },
+ },
+ },
+ },
+ Locations: []version2.Location{
+ {
+ Path: "/internal_location_splits_1_split_0",
+ ProxyPass: "http://vs_default_cafe_coffee-v1$request_uri",
+ ProxyNextUpstream: "error timeout",
+ ProxyNextUpstreamTimeout: "0s",
+ ProxyNextUpstreamTries: 0,
+ Internal: true,
+ ProxySSLName: "coffee-v1.default.svc",
+ ProxyPassRequestHeaders: true,
+ ProxySetHeaders: []version2.Header{{Name: "Host", Value: "$host"}},
+ ServiceName: "coffee-v1",
+ IsVSR: true,
+ VSRName: "coffee",
+ VSRNamespace: "default",
+ },
+ {
+ Path: "/internal_location_splits_1_split_1",
+ ProxyPass: "http://vs_default_cafe_coffee-v2$request_uri",
+ ProxyNextUpstream: "error timeout",
+ ProxyNextUpstreamTimeout: "0s",
+ ProxyNextUpstreamTries: 0,
+ Internal: true,
+ ProxySSLName: "coffee-v2.default.svc",
+ ProxyPassRequestHeaders: true,
+ ProxySetHeaders: []version2.Header{{Name: "Host", Value: "$host"}},
+ ServiceName: "coffee-v2",
+ IsVSR: true,
+ VSRName: "coffee",
+ VSRNamespace: "default",
+ },
+ },
+ InternalRedirectLocation: version2.InternalRedirectLocation{
+ Path: "/",
+ Destination: "$vs_default_cafe_splits_1",
+ },
+ }
+
+ cfgParams := ConfigParams{}
+ locSnippet := ""
+ enableSnippets := false
+ crUpstreams := map[string]conf_v1.Upstream{
+ "vs_default_cafe_coffee-v1": {
+ Service: "coffee-v1",
+ },
+ "vs_default_cafe_coffee-v2": {
+ Service: "coffee-v2",
+ },
+ }
+
+ errorPageDetails := errorPageDetails{
+ pages: route.ErrorPages,
+ index: 0,
+ owner: nil,
+ }
+
+ result := generateDefaultSplitsConfig(route, upstreamNamer, crUpstreams, variableNamer, index, &cfgParams,
+ errorPageDetails, "", locSnippet, enableSnippets, 0, true, "coffee", "default", Warnings{})
+ if !reflect.DeepEqual(result, expected) {
+ t.Errorf("generateDefaultSplitsConfig() returned \n%+v but expected \n%+v", result, expected)
+ }
+}
+
+func TestGenerateMatchesConfig(t *testing.T) {
+ t.Parallel()
+ route := conf_v1.Route{
+ Path: "/",
+ Matches: []conf_v1.Match{
+ {
+ Conditions: []conf_v1.Condition{
+ {
+ Header: "x-version",
+ Value: "v1",
+ },
+ {
+ Cookie: "user",
+ Value: "john",
+ },
+ {
+ Argument: "answer",
+ Value: "yes",
+ },
+ {
+ Variable: "$request_method",
+ Value: "GET",
+ },
+ },
+ Action: &conf_v1.Action{
+ Pass: "coffee-v1",
+ },
+ },
+ {
+ Conditions: []conf_v1.Condition{
+ {
+ Header: "x-version",
+ Value: "v2",
+ },
+ {
+ Cookie: "user",
+ Value: "paul",
+ },
+ {
+ Argument: "answer",
+ Value: "no",
+ },
+ {
+ Variable: "$request_method",
+ Value: "POST",
+ },
+ },
+ Splits: []conf_v1.Split{
+ {
+ Weight: 90,
+ Action: &conf_v1.Action{
+ Pass: "coffee-v1",
+ },
+ },
+ {
+ Weight: 10,
+ Action: &conf_v1.Action{
+ Pass: "coffee-v2",
+ },
+ },
+ },
+ },
+ },
+ Action: &conf_v1.Action{
+ Pass: "tea",
+ },
+ }
+ virtualServer := conf_v1.VirtualServer{
+ ObjectMeta: meta_v1.ObjectMeta{
+ Name: "cafe",
+ Namespace: "default",
+ },
+ }
+ errorPages := []conf_v1.ErrorPage{
+ {
+ Codes: []int{400, 500},
+ Return: &conf_v1.ErrorPageReturn{
+ ActionReturn: conf_v1.ActionReturn{
+ Code: 200,
+ Type: "application/json",
+ Body: `{\"message\": \"ok\"}`,
+ },
+ Headers: []conf_v1.Header{
+ {
+ Name: "Set-Cookie",
+ Value: "cookie1=value",
+ },
+ },
+ },
+ Redirect: nil,
+ },
+ {
+ Codes: []int{500, 502},
+ Return: nil,
+ Redirect: &conf_v1.ErrorPageRedirect{
+ ActionRedirect: conf_v1.ActionRedirect{
+ URL: "http://nginx.com",
+ Code: 301,
+ },
+ },
+ },
+ }
upstreamNamer := NewUpstreamNamerForVirtualServer(&virtualServer)
variableNamer := newVariableNamer(&virtualServer)
index := 1
+ scIndex := 2
expected := routingCfg{
- SplitClients: []version2.SplitClient{
+ Maps: []version2.Map{
{
- Source: "$request_id",
- Variable: "$vs_default_cafe_splits_1",
- Distributions: []version2.Distribution{
+ Source: "$http_x_version",
+ Variable: "$vs_default_cafe_matches_1_match_0_cond_0",
+ Parameters: []version2.Parameter{
{
- Weight: "90%",
- Value: "/internal_location_splits_1_split_0",
+ Value: `"v1"`,
+ Result: "$vs_default_cafe_matches_1_match_0_cond_1",
},
{
- Weight: "10%",
- Value: "/internal_location_splits_1_split_1",
+ Value: "default",
+ Result: "0",
+ },
+ },
+ },
+ {
+ Source: "$cookie_user",
+ Variable: "$vs_default_cafe_matches_1_match_0_cond_1",
+ Parameters: []version2.Parameter{
+ {
+ Value: `"john"`,
+ Result: "$vs_default_cafe_matches_1_match_0_cond_2",
+ },
+ {
+ Value: "default",
+ Result: "0",
+ },
+ },
+ },
+ {
+ Source: "$arg_answer",
+ Variable: "$vs_default_cafe_matches_1_match_0_cond_2",
+ Parameters: []version2.Parameter{
+ {
+ Value: `"yes"`,
+ Result: "$vs_default_cafe_matches_1_match_0_cond_3",
+ },
+ {
+ Value: "default",
+ Result: "0",
+ },
+ },
+ },
+ {
+ Source: "$request_method",
+ Variable: "$vs_default_cafe_matches_1_match_0_cond_3",
+ Parameters: []version2.Parameter{
+ {
+ Value: `"GET"`,
+ Result: "1",
+ },
+ {
+ Value: "default",
+ Result: "0",
+ },
+ },
+ },
+ {
+ Source: "$http_x_version",
+ Variable: "$vs_default_cafe_matches_1_match_1_cond_0",
+ Parameters: []version2.Parameter{
+ {
+ Value: `"v2"`,
+ Result: "$vs_default_cafe_matches_1_match_1_cond_1",
+ },
+ {
+ Value: "default",
+ Result: "0",
+ },
+ },
+ },
+ {
+ Source: "$cookie_user",
+ Variable: "$vs_default_cafe_matches_1_match_1_cond_1",
+ Parameters: []version2.Parameter{
+ {
+ Value: `"paul"`,
+ Result: "$vs_default_cafe_matches_1_match_1_cond_2",
+ },
+ {
+ Value: "default",
+ Result: "0",
+ },
+ },
+ },
+ {
+ Source: "$arg_answer",
+ Variable: "$vs_default_cafe_matches_1_match_1_cond_2",
+ Parameters: []version2.Parameter{
+ {
+ Value: `"no"`,
+ Result: "$vs_default_cafe_matches_1_match_1_cond_3",
+ },
+ {
+ Value: "default",
+ Result: "0",
+ },
+ },
+ },
+ {
+ Source: "$request_method",
+ Variable: "$vs_default_cafe_matches_1_match_1_cond_3",
+ Parameters: []version2.Parameter{
+ {
+ Value: `"POST"`,
+ Result: "1",
+ },
+ {
+ Value: "default",
+ Result: "0",
+ },
+ },
+ },
+ {
+ Source: "$vs_default_cafe_matches_1_match_0_cond_0$vs_default_cafe_matches_1_match_1_cond_0",
+ Variable: "$vs_default_cafe_matches_1",
+ Parameters: []version2.Parameter{
+ {
+ Value: "~^1",
+ Result: "/internal_location_matches_1_match_0",
+ },
+ {
+ Value: "~^01",
+ Result: "$vs_default_cafe_splits_2",
+ },
+ {
+ Value: "default",
+ Result: "/internal_location_matches_1_default",
},
},
},
},
Locations: []version2.Location{
{
- Path: "/internal_location_splits_1_split_0",
+ Path: "/internal_location_matches_1_match_0",
ProxyPass: "http://vs_default_cafe_coffee-v1$request_uri",
ProxyNextUpstream: "error timeout",
ProxyNextUpstreamTimeout: "0s",
ProxyNextUpstreamTries: 0,
+ ProxyInterceptErrors: true,
Internal: true,
- ProxySSLName: "coffee-v1.default.svc",
- ProxyPassRequestHeaders: true,
- ProxySetHeaders: []version2.Header{{Name: "Host", Value: "$host"}},
- ServiceName: "coffee-v1",
- IsVSR: true,
- VSRName: "coffee",
- VSRNamespace: "default",
+ ErrorPages: []version2.ErrorPage{
+ {
+ Name: "@error_page_2_0",
+ Codes: "400 500",
+ ResponseCode: 200,
+ },
+ {
+ Name: "http://nginx.com",
+ Codes: "500 502",
+ ResponseCode: 301,
+ },
+ },
+ ProxySSLName: "coffee-v1.default.svc",
+ ProxyPassRequestHeaders: true,
+ ProxySetHeaders: []version2.Header{{Name: "Host", Value: "$host"}},
+ ServiceName: "coffee-v1",
+ IsVSR: false,
+ VSRName: "",
+ VSRNamespace: "",
},
{
- Path: "/internal_location_splits_1_split_1",
+ Path: "/internal_location_splits_2_split_0",
+ ProxyPass: "http://vs_default_cafe_coffee-v1$request_uri",
+ ProxyNextUpstream: "error timeout",
+ ProxyNextUpstreamTimeout: "0s",
+ ProxyNextUpstreamTries: 0,
+ ProxyInterceptErrors: true,
+ Internal: true,
+ ErrorPages: []version2.ErrorPage{
+ {
+ Name: "@error_page_2_0",
+ Codes: "400 500",
+ ResponseCode: 200,
+ },
+ {
+ Name: "http://nginx.com",
+ Codes: "500 502",
+ ResponseCode: 301,
+ },
+ },
+ ProxySSLName: "coffee-v1.default.svc",
+ ProxyPassRequestHeaders: true,
+ ProxySetHeaders: []version2.Header{{Name: "Host", Value: "$host"}},
+ ServiceName: "coffee-v1",
+ IsVSR: false,
+ VSRName: "",
+ VSRNamespace: "",
+ },
+ {
+ Path: "/internal_location_splits_2_split_1",
ProxyPass: "http://vs_default_cafe_coffee-v2$request_uri",
ProxyNextUpstream: "error timeout",
ProxyNextUpstreamTimeout: "0s",
ProxyNextUpstreamTries: 0,
+ ProxyInterceptErrors: true,
Internal: true,
- ProxySSLName: "coffee-v2.default.svc",
- ProxyPassRequestHeaders: true,
- ProxySetHeaders: []version2.Header{{Name: "Host", Value: "$host"}},
- ServiceName: "coffee-v2",
- IsVSR: true,
- VSRName: "coffee",
- VSRNamespace: "default",
+ ErrorPages: []version2.ErrorPage{
+ {
+ Name: "@error_page_2_0",
+ Codes: "400 500",
+ ResponseCode: 200,
+ },
+ {
+ Name: "http://nginx.com",
+ Codes: "500 502",
+ ResponseCode: 301,
+ },
+ },
+ ProxySSLName: "coffee-v2.default.svc",
+ ProxyPassRequestHeaders: true,
+ ProxySetHeaders: []version2.Header{{Name: "Host", Value: "$host"}},
+ ServiceName: "coffee-v2",
+ IsVSR: false,
+ VSRName: "",
+ VSRNamespace: "",
+ },
+ {
+ Path: "/internal_location_matches_1_default",
+ ProxyPass: "http://vs_default_cafe_tea$request_uri",
+ ProxyNextUpstream: "error timeout",
+ ProxyNextUpstreamTimeout: "0s",
+ ProxyNextUpstreamTries: 0,
+ ProxyInterceptErrors: true,
+ Internal: true,
+ ErrorPages: []version2.ErrorPage{
+ {
+ Name: "@error_page_2_0",
+ Codes: "400 500",
+ ResponseCode: 200,
+ },
+ {
+ Name: "http://nginx.com",
+ Codes: "500 502",
+ ResponseCode: 301,
+ },
+ },
+ ProxySSLName: "tea.default.svc",
+ ProxyPassRequestHeaders: true,
+ ProxySetHeaders: []version2.Header{{Name: "Host", Value: "$host"}},
+ ServiceName: "tea",
+ IsVSR: false,
+ VSRName: "",
+ VSRNamespace: "",
},
},
InternalRedirectLocation: version2.InternalRedirectLocation{
Path: "/",
- Destination: "$vs_default_cafe_splits_1",
+ Destination: "$vs_default_cafe_matches_1",
+ },
+ SplitClients: []version2.SplitClient{
+ {
+ Source: "$request_id",
+ Variable: "$vs_default_cafe_splits_2",
+ Distributions: []version2.Distribution{
+ {
+ Weight: "90%",
+ Value: "/internal_location_splits_2_split_0",
+ },
+ {
+ Weight: "10%",
+ Value: "/internal_location_splits_2_split_1",
+ },
+ },
+ },
},
}
cfgParams := ConfigParams{}
- locSnippet := ""
enableSnippets := false
+ locSnippets := ""
crUpstreams := map[string]conf_v1.Upstream{
- "vs_default_cafe_coffee-v1": {
- Service: "coffee-v1",
- },
- "vs_default_cafe_coffee-v2": {
- Service: "coffee-v2",
- },
+ "vs_default_cafe_coffee-v1": {Service: "coffee-v1"},
+ "vs_default_cafe_coffee-v2": {Service: "coffee-v2"},
+ "vs_default_cafe_tea": {Service: "tea"},
}
errorPageDetails := errorPageDetails{
- pages: route.ErrorPages,
- index: 0,
+ pages: errorPages,
+ index: 2,
owner: nil,
}
- result := generateDefaultSplitsConfig(route, upstreamNamer, crUpstreams, variableNamer, index, &cfgParams,
- errorPageDetails, "", locSnippet, enableSnippets, 0, true, "coffee", "default", Warnings{})
+ result := generateMatchesConfig(
+ route,
+ upstreamNamer,
+ crUpstreams,
+ variableNamer,
+ index,
+ scIndex,
+ &cfgParams,
+ errorPageDetails,
+ locSnippets,
+ enableSnippets,
+ 0,
+ false,
+ "",
+ "",
+ Warnings{},
+ )
if !reflect.DeepEqual(result, expected) {
- t.Errorf("generateDefaultSplitsConfig() returned \n%+v but expected \n%+v", result, expected)
+ t.Errorf("generateMatchesConfig() returned \n%+v but expected \n%+v", result, expected)
}
}
-func TestGenerateMatchesConfig(t *testing.T) {
+func TestGenerateMatchesConfigWithMultipleSplits(t *testing.T) {
t.Parallel()
route := conf_v1.Route{
Path: "/",
@@ -6770,22 +7957,21 @@ func TestGenerateMatchesConfig(t *testing.T) {
Header: "x-version",
Value: "v1",
},
+ },
+ Splits: []conf_v1.Split{
{
- Cookie: "user",
- Value: "john",
- },
- {
- Argument: "answer",
- Value: "yes",
+ Weight: 30,
+ Action: &conf_v1.Action{
+ Pass: "coffee-v1",
+ },
},
{
- Variable: "$request_method",
- Value: "GET",
+ Weight: 70,
+ Action: &conf_v1.Action{
+ Pass: "coffee-v2",
+ },
},
},
- Action: &conf_v1.Action{
- Pass: "coffee-v1",
- },
},
{
Conditions: []conf_v1.Condition{
@@ -6793,37 +7979,36 @@ func TestGenerateMatchesConfig(t *testing.T) {
Header: "x-version",
Value: "v2",
},
- {
- Cookie: "user",
- Value: "paul",
- },
- {
- Argument: "answer",
- Value: "no",
- },
- {
- Variable: "$request_method",
- Value: "POST",
- },
},
Splits: []conf_v1.Split{
{
Weight: 90,
Action: &conf_v1.Action{
- Pass: "coffee-v1",
+ Pass: "coffee-v2",
},
},
{
Weight: 10,
Action: &conf_v1.Action{
- Pass: "coffee-v2",
+ Pass: "coffee-v1",
},
},
},
},
},
- Action: &conf_v1.Action{
- Pass: "tea",
+ Splits: []conf_v1.Split{
+ {
+ Weight: 99,
+ Action: &conf_v1.Action{
+ Pass: "coffee-v1",
+ },
+ },
+ {
+ Weight: 1,
+ Action: &conf_v1.Action{
+ Pass: "coffee-v2",
+ },
+ },
},
}
virtualServer := conf_v1.VirtualServer{
@@ -6832,6 +8017,10 @@ func TestGenerateMatchesConfig(t *testing.T) {
Namespace: "default",
},
}
+ upstreamNamer := NewUpstreamNamerForVirtualServer(&virtualServer)
+ variableNamer := newVariableNamer(&virtualServer)
+ index := 1
+ scIndex := 2
errorPages := []conf_v1.ErrorPage{
{
Codes: []int{400, 500},
@@ -6861,10 +8050,6 @@ func TestGenerateMatchesConfig(t *testing.T) {
},
},
}
- upstreamNamer := NewUpstreamNamerForVirtualServer(&virtualServer)
- variableNamer := newVariableNamer(&virtualServer)
- index := 1
- scIndex := 2
expected := routingCfg{
Maps: []version2.Map{
@@ -6874,48 +8059,6 @@ func TestGenerateMatchesConfig(t *testing.T) {
Parameters: []version2.Parameter{
{
Value: `"v1"`,
- Result: "$vs_default_cafe_matches_1_match_0_cond_1",
- },
- {
- Value: "default",
- Result: "0",
- },
- },
- },
- {
- Source: "$cookie_user",
- Variable: "$vs_default_cafe_matches_1_match_0_cond_1",
- Parameters: []version2.Parameter{
- {
- Value: `"john"`,
- Result: "$vs_default_cafe_matches_1_match_0_cond_2",
- },
- {
- Value: "default",
- Result: "0",
- },
- },
- },
- {
- Source: "$arg_answer",
- Variable: "$vs_default_cafe_matches_1_match_0_cond_2",
- Parameters: []version2.Parameter{
- {
- Value: `"yes"`,
- Result: "$vs_default_cafe_matches_1_match_0_cond_3",
- },
- {
- Value: "default",
- Result: "0",
- },
- },
- },
- {
- Source: "$request_method",
- Variable: "$vs_default_cafe_matches_1_match_0_cond_3",
- Parameters: []version2.Parameter{
- {
- Value: `"GET"`,
Result: "1",
},
{
@@ -6930,7 +8073,7 @@ func TestGenerateMatchesConfig(t *testing.T) {
Parameters: []version2.Parameter{
{
Value: `"v2"`,
- Result: "$vs_default_cafe_matches_1_match_1_cond_1",
+ Result: "1",
},
{
Value: "default",
@@ -6939,78 +8082,91 @@ func TestGenerateMatchesConfig(t *testing.T) {
},
},
{
- Source: "$cookie_user",
- Variable: "$vs_default_cafe_matches_1_match_1_cond_1",
+ Source: "$vs_default_cafe_matches_1_match_0_cond_0$vs_default_cafe_matches_1_match_1_cond_0",
+ Variable: "$vs_default_cafe_matches_1",
Parameters: []version2.Parameter{
{
- Value: `"paul"`,
- Result: "$vs_default_cafe_matches_1_match_1_cond_2",
- },
- {
- Value: "default",
- Result: "0",
+ Value: "~^1",
+ Result: "$vs_default_cafe_splits_2",
},
- },
- },
- {
- Source: "$arg_answer",
- Variable: "$vs_default_cafe_matches_1_match_1_cond_2",
- Parameters: []version2.Parameter{
{
- Value: `"no"`,
- Result: "$vs_default_cafe_matches_1_match_1_cond_3",
+ Value: "~^01",
+ Result: "$vs_default_cafe_splits_3",
},
{
Value: "default",
- Result: "0",
+ Result: "$vs_default_cafe_splits_4",
},
},
},
+ },
+ Locations: []version2.Location{
{
- Source: "$request_method",
- Variable: "$vs_default_cafe_matches_1_match_1_cond_3",
- Parameters: []version2.Parameter{
+ Path: "/internal_location_splits_2_split_0",
+ ProxyPass: "http://vs_default_cafe_coffee-v1$request_uri",
+ ProxyNextUpstream: "error timeout",
+ ProxyNextUpstreamTimeout: "0s",
+ ProxyNextUpstreamTries: 0,
+ Internal: true,
+ ErrorPages: []version2.ErrorPage{
{
- Value: `"POST"`,
- Result: "1",
+ Name: "@error_page_0_0",
+ Codes: "400 500",
+ ResponseCode: 200,
},
{
- Value: "default",
- Result: "0",
+ Name: "http://nginx.com",
+ Codes: "500 502",
+ ResponseCode: 301,
},
},
+ ProxyInterceptErrors: true,
+ ProxySSLName: "coffee-v1.default.svc",
+ ProxyPassRequestHeaders: true,
+ ProxySetHeaders: []version2.Header{{Name: "Host", Value: "$host"}},
+ ServiceName: "coffee-v1",
+ IsVSR: true,
+ VSRName: "coffee",
+ VSRNamespace: "default",
},
{
- Source: "$vs_default_cafe_matches_1_match_0_cond_0$vs_default_cafe_matches_1_match_1_cond_0",
- Variable: "$vs_default_cafe_matches_1",
- Parameters: []version2.Parameter{
- {
- Value: "~^1",
- Result: "/internal_location_matches_1_match_0",
- },
+ Path: "/internal_location_splits_2_split_1",
+ ProxyPass: "http://vs_default_cafe_coffee-v2$request_uri",
+ ProxyNextUpstream: "error timeout",
+ ProxyNextUpstreamTimeout: "0s",
+ ProxyNextUpstreamTries: 0,
+ Internal: true,
+ ErrorPages: []version2.ErrorPage{
{
- Value: "~^01",
- Result: "$vs_default_cafe_splits_2",
+ Name: "@error_page_0_0",
+ Codes: "400 500",
+ ResponseCode: 200,
},
{
- Value: "default",
- Result: "/internal_location_matches_1_default",
+ Name: "http://nginx.com",
+ Codes: "500 502",
+ ResponseCode: 301,
},
},
+ ProxyInterceptErrors: true,
+ ProxySSLName: "coffee-v2.default.svc",
+ ProxyPassRequestHeaders: true,
+ ProxySetHeaders: []version2.Header{{Name: "Host", Value: "$host"}},
+ ServiceName: "coffee-v2",
+ IsVSR: true,
+ VSRName: "coffee",
+ VSRNamespace: "default",
},
- },
- Locations: []version2.Location{
{
- Path: "/internal_location_matches_1_match_0",
- ProxyPass: "http://vs_default_cafe_coffee-v1$request_uri",
+ Path: "/internal_location_splits_3_split_0",
+ ProxyPass: "http://vs_default_cafe_coffee-v2$request_uri",
ProxyNextUpstream: "error timeout",
ProxyNextUpstreamTimeout: "0s",
ProxyNextUpstreamTries: 0,
- ProxyInterceptErrors: true,
Internal: true,
ErrorPages: []version2.ErrorPage{
{
- Name: "@error_page_2_0",
+ Name: "@error_page_0_0",
Codes: "400 500",
ResponseCode: 200,
},
@@ -7020,25 +8176,25 @@ func TestGenerateMatchesConfig(t *testing.T) {
ResponseCode: 301,
},
},
- ProxySSLName: "coffee-v1.default.svc",
+ ProxyInterceptErrors: true,
+ ProxySSLName: "coffee-v2.default.svc",
ProxyPassRequestHeaders: true,
ProxySetHeaders: []version2.Header{{Name: "Host", Value: "$host"}},
- ServiceName: "coffee-v1",
- IsVSR: false,
- VSRName: "",
- VSRNamespace: "",
+ ServiceName: "coffee-v2",
+ IsVSR: true,
+ VSRName: "coffee",
+ VSRNamespace: "default",
},
{
- Path: "/internal_location_splits_2_split_0",
+ Path: "/internal_location_splits_3_split_1",
ProxyPass: "http://vs_default_cafe_coffee-v1$request_uri",
ProxyNextUpstream: "error timeout",
ProxyNextUpstreamTimeout: "0s",
ProxyNextUpstreamTries: 0,
- ProxyInterceptErrors: true,
Internal: true,
ErrorPages: []version2.ErrorPage{
{
- Name: "@error_page_2_0",
+ Name: "@error_page_0_0",
Codes: "400 500",
ResponseCode: 200,
},
@@ -7048,25 +8204,25 @@ func TestGenerateMatchesConfig(t *testing.T) {
ResponseCode: 301,
},
},
+ ProxyInterceptErrors: true,
ProxySSLName: "coffee-v1.default.svc",
ProxyPassRequestHeaders: true,
ProxySetHeaders: []version2.Header{{Name: "Host", Value: "$host"}},
ServiceName: "coffee-v1",
- IsVSR: false,
- VSRName: "",
- VSRNamespace: "",
+ IsVSR: true,
+ VSRName: "coffee",
+ VSRNamespace: "default",
},
{
- Path: "/internal_location_splits_2_split_1",
- ProxyPass: "http://vs_default_cafe_coffee-v2$request_uri",
+ Path: "/internal_location_splits_4_split_0",
+ ProxyPass: "http://vs_default_cafe_coffee-v1$request_uri",
ProxyNextUpstream: "error timeout",
ProxyNextUpstreamTimeout: "0s",
ProxyNextUpstreamTries: 0,
- ProxyInterceptErrors: true,
Internal: true,
ErrorPages: []version2.ErrorPage{
{
- Name: "@error_page_2_0",
+ Name: "@error_page_0_0",
Codes: "400 500",
ResponseCode: 200,
},
@@ -7076,25 +8232,25 @@ func TestGenerateMatchesConfig(t *testing.T) {
ResponseCode: 301,
},
},
- ProxySSLName: "coffee-v2.default.svc",
+ ProxyInterceptErrors: true,
+ ProxySSLName: "coffee-v1.default.svc",
ProxyPassRequestHeaders: true,
ProxySetHeaders: []version2.Header{{Name: "Host", Value: "$host"}},
- ServiceName: "coffee-v2",
- IsVSR: false,
- VSRName: "",
- VSRNamespace: "",
+ ServiceName: "coffee-v1",
+ IsVSR: true,
+ VSRName: "coffee",
+ VSRNamespace: "default",
},
{
- Path: "/internal_location_matches_1_default",
- ProxyPass: "http://vs_default_cafe_tea$request_uri",
+ Path: "/internal_location_splits_4_split_1",
+ ProxyPass: "http://vs_default_cafe_coffee-v2$request_uri",
ProxyNextUpstream: "error timeout",
ProxyNextUpstreamTimeout: "0s",
ProxyNextUpstreamTries: 0,
- ProxyInterceptErrors: true,
Internal: true,
ErrorPages: []version2.ErrorPage{
{
- Name: "@error_page_2_0",
+ Name: "@error_page_0_0",
Codes: "400 500",
ResponseCode: 200,
},
@@ -7104,13 +8260,14 @@ func TestGenerateMatchesConfig(t *testing.T) {
ResponseCode: 301,
},
},
- ProxySSLName: "tea.default.svc",
+ ProxyInterceptErrors: true,
+ ProxySSLName: "coffee-v2.default.svc",
ProxyPassRequestHeaders: true,
ProxySetHeaders: []version2.Header{{Name: "Host", Value: "$host"}},
- ServiceName: "tea",
- IsVSR: false,
- VSRName: "",
- VSRNamespace: "",
+ ServiceName: "coffee-v2",
+ IsVSR: true,
+ VSRName: "coffee",
+ VSRNamespace: "default",
},
},
InternalRedirectLocation: version2.InternalRedirectLocation{
@@ -7123,15 +8280,43 @@ func TestGenerateMatchesConfig(t *testing.T) {
Variable: "$vs_default_cafe_splits_2",
Distributions: []version2.Distribution{
{
- Weight: "90%",
+ Weight: "30%",
Value: "/internal_location_splits_2_split_0",
},
{
- Weight: "10%",
+ Weight: "70%",
Value: "/internal_location_splits_2_split_1",
},
},
},
+ {
+ Source: "$request_id",
+ Variable: "$vs_default_cafe_splits_3",
+ Distributions: []version2.Distribution{
+ {
+ Weight: "90%",
+ Value: "/internal_location_splits_3_split_0",
+ },
+ {
+ Weight: "10%",
+ Value: "/internal_location_splits_3_split_1",
+ },
+ },
+ },
+ {
+ Source: "$request_id",
+ Variable: "$vs_default_cafe_splits_4",
+ Distributions: []version2.Distribution{
+ {
+ Weight: "99%",
+ Value: "/internal_location_splits_4_split_0",
+ },
+ {
+ Weight: "1%",
+ Value: "/internal_location_splits_4_split_1",
+ },
+ },
+ },
},
}
@@ -7141,12 +8326,11 @@ func TestGenerateMatchesConfig(t *testing.T) {
crUpstreams := map[string]conf_v1.Upstream{
"vs_default_cafe_coffee-v1": {Service: "coffee-v1"},
"vs_default_cafe_coffee-v2": {Service: "coffee-v2"},
- "vs_default_cafe_tea": {Service: "tea"},
}
errorPageDetails := errorPageDetails{
pages: errorPages,
- index: 2,
+ index: 0,
owner: nil,
}
@@ -7162,9 +8346,9 @@ func TestGenerateMatchesConfig(t *testing.T) {
locSnippets,
enableSnippets,
0,
- false,
- "",
- "",
+ true,
+ "coffee",
+ "default",
Warnings{},
)
if !reflect.DeepEqual(result, expected) {
@@ -7172,2470 +8356,2647 @@ func TestGenerateMatchesConfig(t *testing.T) {
}
}
-func TestGenerateMatchesConfigWithMultipleSplits(t *testing.T) {
+func TestGenerateValueForMatchesRouteMap(t *testing.T) {
t.Parallel()
- route := conf_v1.Route{
- Path: "/",
- Matches: []conf_v1.Match{
- {
- Conditions: []conf_v1.Condition{
- {
- Header: "x-version",
- Value: "v1",
- },
+ tests := []struct {
+ input string
+ expectedValue string
+ expectedIsNegative bool
+ }{
+ {
+ input: "default",
+ expectedValue: `\default`,
+ expectedIsNegative: false,
+ },
+ {
+ input: "!default",
+ expectedValue: `\default`,
+ expectedIsNegative: true,
+ },
+ {
+ input: "hostnames",
+ expectedValue: `\hostnames`,
+ expectedIsNegative: false,
+ },
+ {
+ input: "include",
+ expectedValue: `\include`,
+ expectedIsNegative: false,
+ },
+ {
+ input: "volatile",
+ expectedValue: `\volatile`,
+ expectedIsNegative: false,
+ },
+ {
+ input: "abc",
+ expectedValue: `"abc"`,
+ expectedIsNegative: false,
+ },
+ {
+ input: "!abc",
+ expectedValue: `"abc"`,
+ expectedIsNegative: true,
+ },
+ {
+ input: "",
+ expectedValue: `""`,
+ expectedIsNegative: false,
+ },
+ {
+ input: "!",
+ expectedValue: `""`,
+ expectedIsNegative: true,
+ },
+ }
+
+ for _, test := range tests {
+ resultValue, resultIsNegative := generateValueForMatchesRouteMap(test.input)
+ if resultValue != test.expectedValue {
+ t.Errorf("generateValueForMatchesRouteMap(%q) returned %q but expected %q as the value", test.input, resultValue, test.expectedValue)
+ }
+ if resultIsNegative != test.expectedIsNegative {
+ t.Errorf("generateValueForMatchesRouteMap(%q) returned %v but expected %v as the isNegative", test.input, resultIsNegative, test.expectedIsNegative)
+ }
+ }
+}
+
+func TestGenerateParametersForMatchesRouteMap(t *testing.T) {
+ t.Parallel()
+ tests := []struct {
+ inputMatchedValue string
+ inputSuccessfulResult string
+ expected []version2.Parameter
+ }{
+ {
+ inputMatchedValue: "abc",
+ inputSuccessfulResult: "1",
+ expected: []version2.Parameter{
+ {
+ Value: `"abc"`,
+ Result: "1",
},
- Splits: []conf_v1.Split{
- {
- Weight: 30,
- Action: &conf_v1.Action{
- Pass: "coffee-v1",
- },
- },
- {
- Weight: 70,
- Action: &conf_v1.Action{
- Pass: "coffee-v2",
- },
- },
+ {
+ Value: "default",
+ Result: "0",
},
},
- {
- Conditions: []conf_v1.Condition{
- {
- Header: "x-version",
- Value: "v2",
- },
+ },
+ {
+ inputMatchedValue: "!abc",
+ inputSuccessfulResult: "1",
+ expected: []version2.Parameter{
+ {
+ Value: `"abc"`,
+ Result: "0",
},
- Splits: []conf_v1.Split{
- {
- Weight: 90,
- Action: &conf_v1.Action{
- Pass: "coffee-v2",
- },
- },
- {
- Weight: 10,
- Action: &conf_v1.Action{
- Pass: "coffee-v1",
- },
- },
+ {
+ Value: "default",
+ Result: "1",
},
},
},
- Splits: []conf_v1.Split{
- {
- Weight: 99,
- Action: &conf_v1.Action{
- Pass: "coffee-v1",
- },
+ }
+
+ for _, test := range tests {
+ result := generateParametersForMatchesRouteMap(test.inputMatchedValue, test.inputSuccessfulResult)
+ if !reflect.DeepEqual(result, test.expected) {
+ t.Errorf("generateParametersForMatchesRouteMap(%q, %q) returned %v but expected %v", test.inputMatchedValue, test.inputSuccessfulResult, result, test.expected)
+ }
+ }
+}
+
+func TestGetNameForSourceForMatchesRouteMapFromCondition(t *testing.T) {
+ t.Parallel()
+ tests := []struct {
+ input conf_v1.Condition
+ expected string
+ }{
+ {
+ input: conf_v1.Condition{
+ Header: "x-version",
+ },
+ expected: "$http_x_version",
+ },
+ {
+ input: conf_v1.Condition{
+ Cookie: "mycookie",
},
- {
- Weight: 1,
- Action: &conf_v1.Action{
- Pass: "coffee-v2",
- },
+ expected: "$cookie_mycookie",
+ },
+ {
+ input: conf_v1.Condition{
+ Argument: "arg",
},
+ expected: "$arg_arg",
},
- }
- virtualServer := conf_v1.VirtualServer{
- ObjectMeta: meta_v1.ObjectMeta{
- Name: "cafe",
- Namespace: "default",
+ {
+ input: conf_v1.Condition{
+ Variable: "$request_method",
+ },
+ expected: "$request_method",
},
}
- upstreamNamer := NewUpstreamNamerForVirtualServer(&virtualServer)
- variableNamer := newVariableNamer(&virtualServer)
- index := 1
- scIndex := 2
- errorPages := []conf_v1.ErrorPage{
+
+ for _, test := range tests {
+ result := getNameForSourceForMatchesRouteMapFromCondition(test.input)
+ if result != test.expected {
+ t.Errorf("getNameForSourceForMatchesRouteMapFromCondition() returned %q but expected %q for input %v", result, test.expected, test.input)
+ }
+ }
+}
+
+func TestGenerateLBMethod(t *testing.T) {
+ t.Parallel()
+ defaultMethod := "random two least_conn"
+
+ tests := []struct {
+ input string
+ expected string
+ }{
{
- Codes: []int{400, 500},
- Return: &conf_v1.ErrorPageReturn{
- ActionReturn: conf_v1.ActionReturn{
- Code: 200,
- Type: "application/json",
- Body: `{\"message\": \"ok\"}`,
- },
- Headers: []conf_v1.Header{
- {
- Name: "Set-Cookie",
- Value: "cookie1=value",
- },
- },
- },
- Redirect: nil,
+ input: "",
+ expected: defaultMethod,
},
{
- Codes: []int{500, 502},
- Return: nil,
- Redirect: &conf_v1.ErrorPageRedirect{
- ActionRedirect: conf_v1.ActionRedirect{
- URL: "http://nginx.com",
- Code: 301,
- },
- },
+ input: "round_robin",
+ expected: "",
+ },
+ {
+ input: "random",
+ expected: "random",
},
}
+ for _, test := range tests {
+ result := generateLBMethod(test.input, defaultMethod)
+ if result != test.expected {
+ t.Errorf("generateLBMethod() returned %q but expected %q for input '%v'", result, test.expected, test.input)
+ }
+ }
+}
- expected := routingCfg{
- Maps: []version2.Map{
- {
- Source: "$http_x_version",
- Variable: "$vs_default_cafe_matches_1_match_0_cond_0",
- Parameters: []version2.Parameter{
- {
- Value: `"v1"`,
- Result: "1",
- },
- {
- Value: "default",
- Result: "0",
- },
- },
- },
- {
- Source: "$http_x_version",
- Variable: "$vs_default_cafe_matches_1_match_1_cond_0",
- Parameters: []version2.Parameter{
- {
- Value: `"v2"`,
- Result: "1",
- },
- {
- Value: "default",
- Result: "0",
- },
- },
- },
- {
- Source: "$vs_default_cafe_matches_1_match_0_cond_0$vs_default_cafe_matches_1_match_1_cond_0",
- Variable: "$vs_default_cafe_matches_1",
- Parameters: []version2.Parameter{
- {
- Value: "~^1",
- Result: "$vs_default_cafe_splits_2",
- },
- {
- Value: "~^01",
- Result: "$vs_default_cafe_splits_3",
- },
- {
- Value: "default",
- Result: "$vs_default_cafe_splits_4",
- },
- },
- },
+func TestUpstreamHasKeepalive(t *testing.T) {
+ t.Parallel()
+ noKeepalive := 0
+ keepalive := 32
+
+ tests := []struct {
+ upstream conf_v1.Upstream
+ cfgParams *ConfigParams
+ expected bool
+ msg string
+ }{
+ {
+ conf_v1.Upstream{},
+ &ConfigParams{Keepalive: keepalive},
+ true,
+ "upstream keepalive not set, configparam keepalive set",
},
- Locations: []version2.Location{
- {
- Path: "/internal_location_splits_2_split_0",
- ProxyPass: "http://vs_default_cafe_coffee-v1$request_uri",
- ProxyNextUpstream: "error timeout",
- ProxyNextUpstreamTimeout: "0s",
- ProxyNextUpstreamTries: 0,
- Internal: true,
- ErrorPages: []version2.ErrorPage{
- {
- Name: "@error_page_0_0",
- Codes: "400 500",
- ResponseCode: 200,
- },
- {
- Name: "http://nginx.com",
- Codes: "500 502",
- ResponseCode: 301,
+ {
+ conf_v1.Upstream{Keepalive: &noKeepalive},
+ &ConfigParams{Keepalive: keepalive},
+ false,
+ "upstream keepalive set to 0, configparam keepalive set",
+ },
+ {
+ conf_v1.Upstream{Keepalive: &keepalive},
+ &ConfigParams{Keepalive: noKeepalive},
+ true,
+ "upstream keepalive set, configparam keepalive set to 0",
+ },
+ }
+
+ for _, test := range tests {
+ result := upstreamHasKeepalive(test.upstream, test.cfgParams)
+ if result != test.expected {
+ t.Errorf("upstreamHasKeepalive() returned %v, but expected %v for the case of %v", result, test.expected, test.msg)
+ }
+ }
+}
+
+func TestNewHealthCheckWithDefaults(t *testing.T) {
+ t.Parallel()
+ upstreamName := "test-upstream"
+ baseCfgParams := &ConfigParams{
+ ProxySendTimeout: "5s",
+ ProxyReadTimeout: "5s",
+ ProxyConnectTimeout: "5s",
+ }
+ expected := &version2.HealthCheck{
+ Name: upstreamName,
+ ProxySendTimeout: "5s",
+ ProxyReadTimeout: "5s",
+ ProxyConnectTimeout: "5s",
+ ProxyPass: fmt.Sprintf("http://%v", upstreamName),
+ URI: "/",
+ Interval: "5s",
+ Jitter: "0s",
+ KeepaliveTime: "60s",
+ Fails: 1,
+ Passes: 1,
+ Headers: make(map[string]string),
+ }
+
+ result := newHealthCheckWithDefaults(conf_v1.Upstream{}, upstreamName, baseCfgParams)
+
+ if !reflect.DeepEqual(result, expected) {
+ t.Errorf("newHealthCheckWithDefaults returned \n%v but expected \n%v", result, expected)
+ }
+}
+
+func TestGenerateHealthCheck(t *testing.T) {
+ t.Parallel()
+ upstreamName := "test-upstream"
+ tests := []struct {
+ upstream conf_v1.Upstream
+ upstreamName string
+ expected *version2.HealthCheck
+ msg string
+ }{
+ {
+ upstream: conf_v1.Upstream{
+ HealthCheck: &conf_v1.HealthCheck{
+ Enable: true,
+ Path: "/healthz",
+ Interval: "5s",
+ Jitter: "2s",
+ KeepaliveTime: "120s",
+ Fails: 3,
+ Passes: 2,
+ Port: 8080,
+ ConnectTimeout: "20s",
+ SendTimeout: "20s",
+ ReadTimeout: "20s",
+ Headers: []conf_v1.Header{
+ {
+ Name: "Host",
+ Value: "my.service",
+ },
+ {
+ Name: "User-Agent",
+ Value: "nginx",
+ },
},
+ StatusMatch: "! 500",
},
- ProxyInterceptErrors: true,
- ProxySSLName: "coffee-v1.default.svc",
- ProxyPassRequestHeaders: true,
- ProxySetHeaders: []version2.Header{{Name: "Host", Value: "$host"}},
- ServiceName: "coffee-v1",
- IsVSR: true,
- VSRName: "coffee",
- VSRNamespace: "default",
},
- {
- Path: "/internal_location_splits_2_split_1",
- ProxyPass: "http://vs_default_cafe_coffee-v2$request_uri",
- ProxyNextUpstream: "error timeout",
- ProxyNextUpstreamTimeout: "0s",
- ProxyNextUpstreamTries: 0,
- Internal: true,
- ErrorPages: []version2.ErrorPage{
- {
- Name: "@error_page_0_0",
- Codes: "400 500",
- ResponseCode: 200,
- },
- {
- Name: "http://nginx.com",
- Codes: "500 502",
- ResponseCode: 301,
- },
+ upstreamName: upstreamName,
+ expected: &version2.HealthCheck{
+ Name: upstreamName,
+ ProxyConnectTimeout: "20s",
+ ProxySendTimeout: "20s",
+ ProxyReadTimeout: "20s",
+ ProxyPass: fmt.Sprintf("http://%v", upstreamName),
+ URI: "/healthz",
+ Interval: "5s",
+ Jitter: "2s",
+ KeepaliveTime: "120s",
+ Fails: 3,
+ Passes: 2,
+ Port: 8080,
+ Headers: map[string]string{
+ "Host": "my.service",
+ "User-Agent": "nginx",
},
- ProxyInterceptErrors: true,
- ProxySSLName: "coffee-v2.default.svc",
- ProxyPassRequestHeaders: true,
- ProxySetHeaders: []version2.Header{{Name: "Host", Value: "$host"}},
- ServiceName: "coffee-v2",
- IsVSR: true,
- VSRName: "coffee",
- VSRNamespace: "default",
+ Match: fmt.Sprintf("%v_match", upstreamName),
},
- {
- Path: "/internal_location_splits_3_split_0",
- ProxyPass: "http://vs_default_cafe_coffee-v2$request_uri",
- ProxyNextUpstream: "error timeout",
- ProxyNextUpstreamTimeout: "0s",
- ProxyNextUpstreamTries: 0,
- Internal: true,
- ErrorPages: []version2.ErrorPage{
- {
- Name: "@error_page_0_0",
- Codes: "400 500",
- ResponseCode: 200,
- },
- {
- Name: "http://nginx.com",
- Codes: "500 502",
- ResponseCode: 301,
- },
+ msg: "HealthCheck with changed parameters",
+ },
+ {
+ upstream: conf_v1.Upstream{
+ HealthCheck: &conf_v1.HealthCheck{
+ Enable: true,
},
- ProxyInterceptErrors: true,
- ProxySSLName: "coffee-v2.default.svc",
- ProxyPassRequestHeaders: true,
- ProxySetHeaders: []version2.Header{{Name: "Host", Value: "$host"}},
- ServiceName: "coffee-v2",
- IsVSR: true,
- VSRName: "coffee",
- VSRNamespace: "default",
+ ProxyConnectTimeout: "30s",
+ ProxyReadTimeout: "30s",
+ ProxySendTimeout: "30s",
},
- {
- Path: "/internal_location_splits_3_split_1",
- ProxyPass: "http://vs_default_cafe_coffee-v1$request_uri",
- ProxyNextUpstream: "error timeout",
- ProxyNextUpstreamTimeout: "0s",
- ProxyNextUpstreamTries: 0,
- Internal: true,
- ErrorPages: []version2.ErrorPage{
- {
- Name: "@error_page_0_0",
- Codes: "400 500",
- ResponseCode: 200,
- },
- {
- Name: "http://nginx.com",
- Codes: "500 502",
- ResponseCode: 301,
- },
- },
- ProxyInterceptErrors: true,
- ProxySSLName: "coffee-v1.default.svc",
- ProxyPassRequestHeaders: true,
- ProxySetHeaders: []version2.Header{{Name: "Host", Value: "$host"}},
- ServiceName: "coffee-v1",
- IsVSR: true,
- VSRName: "coffee",
- VSRNamespace: "default",
+ upstreamName: upstreamName,
+ expected: &version2.HealthCheck{
+ Name: upstreamName,
+ ProxyConnectTimeout: "30s",
+ ProxyReadTimeout: "30s",
+ ProxySendTimeout: "30s",
+ ProxyPass: fmt.Sprintf("http://%v", upstreamName),
+ URI: "/",
+ Interval: "5s",
+ Jitter: "0s",
+ KeepaliveTime: "60s",
+ Fails: 1,
+ Passes: 1,
+ Headers: make(map[string]string),
},
- {
- Path: "/internal_location_splits_4_split_0",
- ProxyPass: "http://vs_default_cafe_coffee-v1$request_uri",
- ProxyNextUpstream: "error timeout",
- ProxyNextUpstreamTimeout: "0s",
- ProxyNextUpstreamTries: 0,
- Internal: true,
- ErrorPages: []version2.ErrorPage{
- {
- Name: "@error_page_0_0",
- Codes: "400 500",
- ResponseCode: 200,
- },
- {
- Name: "http://nginx.com",
- Codes: "500 502",
- ResponseCode: 301,
- },
+ msg: "HealthCheck with default parameters from Upstream",
+ },
+ {
+ upstream: conf_v1.Upstream{
+ HealthCheck: &conf_v1.HealthCheck{
+ Enable: true,
},
- ProxyInterceptErrors: true,
- ProxySSLName: "coffee-v1.default.svc",
- ProxyPassRequestHeaders: true,
- ProxySetHeaders: []version2.Header{{Name: "Host", Value: "$host"}},
- ServiceName: "coffee-v1",
- IsVSR: true,
- VSRName: "coffee",
- VSRNamespace: "default",
},
- {
- Path: "/internal_location_splits_4_split_1",
- ProxyPass: "http://vs_default_cafe_coffee-v2$request_uri",
- ProxyNextUpstream: "error timeout",
- ProxyNextUpstreamTimeout: "0s",
- ProxyNextUpstreamTries: 0,
- Internal: true,
- ErrorPages: []version2.ErrorPage{
- {
- Name: "@error_page_0_0",
- Codes: "400 500",
- ResponseCode: 200,
- },
- {
- Name: "http://nginx.com",
- Codes: "500 502",
- ResponseCode: 301,
- },
- },
- ProxyInterceptErrors: true,
- ProxySSLName: "coffee-v2.default.svc",
- ProxyPassRequestHeaders: true,
- ProxySetHeaders: []version2.Header{{Name: "Host", Value: "$host"}},
- ServiceName: "coffee-v2",
- IsVSR: true,
- VSRName: "coffee",
- VSRNamespace: "default",
+ upstreamName: upstreamName,
+ expected: &version2.HealthCheck{
+ Name: upstreamName,
+ ProxyConnectTimeout: "5s",
+ ProxyReadTimeout: "5s",
+ ProxySendTimeout: "5s",
+ ProxyPass: fmt.Sprintf("http://%v", upstreamName),
+ URI: "/",
+ Interval: "5s",
+ Jitter: "0s",
+ KeepaliveTime: "60s",
+ Fails: 1,
+ Passes: 1,
+ Headers: make(map[string]string),
},
+ msg: "HealthCheck with default parameters from ConfigMap (not defined in Upstream)",
},
- InternalRedirectLocation: version2.InternalRedirectLocation{
- Path: "/",
- Destination: "$vs_default_cafe_matches_1",
+ {
+ upstream: conf_v1.Upstream{},
+ upstreamName: upstreamName,
+ expected: nil,
+ msg: "HealthCheck not enabled",
},
- SplitClients: []version2.SplitClient{
- {
- Source: "$request_id",
- Variable: "$vs_default_cafe_splits_2",
- Distributions: []version2.Distribution{
- {
- Weight: "30%",
- Value: "/internal_location_splits_2_split_0",
- },
- {
- Weight: "70%",
- Value: "/internal_location_splits_2_split_1",
- },
+ {
+ upstream: conf_v1.Upstream{
+ HealthCheck: &conf_v1.HealthCheck{
+ Enable: true,
+ Interval: "1m 5s",
+ Jitter: "2m 3s",
+ KeepaliveTime: "1m 6s",
+ ConnectTimeout: "1m 10s",
+ SendTimeout: "1m 20s",
+ ReadTimeout: "1m 30s",
},
},
- {
- Source: "$request_id",
- Variable: "$vs_default_cafe_splits_3",
- Distributions: []version2.Distribution{
- {
- Weight: "90%",
- Value: "/internal_location_splits_3_split_0",
- },
- {
- Weight: "10%",
- Value: "/internal_location_splits_3_split_1",
- },
- },
+ upstreamName: upstreamName,
+ expected: &version2.HealthCheck{
+ Name: upstreamName,
+ ProxyConnectTimeout: "1m10s",
+ ProxySendTimeout: "1m20s",
+ ProxyReadTimeout: "1m30s",
+ ProxyPass: fmt.Sprintf("http://%v", upstreamName),
+ URI: "/",
+ Interval: "1m5s",
+ Jitter: "2m3s",
+ KeepaliveTime: "1m6s",
+ Fails: 1,
+ Passes: 1,
+ Headers: make(map[string]string),
},
- {
- Source: "$request_id",
- Variable: "$vs_default_cafe_splits_4",
- Distributions: []version2.Distribution{
- {
- Weight: "99%",
- Value: "/internal_location_splits_4_split_0",
- },
- {
- Weight: "1%",
- Value: "/internal_location_splits_4_split_1",
- },
+ msg: "HealthCheck with time parameters have correct format",
+ },
+ {
+ upstream: conf_v1.Upstream{
+ HealthCheck: &conf_v1.HealthCheck{
+ Enable: true,
+ Mandatory: true,
+ Persistent: true,
},
+ ProxyConnectTimeout: "30s",
+ ProxyReadTimeout: "30s",
+ ProxySendTimeout: "30s",
+ },
+ upstreamName: upstreamName,
+ expected: &version2.HealthCheck{
+ Name: upstreamName,
+ ProxyConnectTimeout: "30s",
+ ProxyReadTimeout: "30s",
+ ProxySendTimeout: "30s",
+ ProxyPass: fmt.Sprintf("http://%v", upstreamName),
+ URI: "/",
+ Interval: "5s",
+ Jitter: "0s",
+ KeepaliveTime: "60s",
+ Fails: 1,
+ Passes: 1,
+ Headers: make(map[string]string),
+ Mandatory: true,
+ Persistent: true,
},
+ msg: "HealthCheck with mandatory and persistent set",
},
}
- cfgParams := ConfigParams{}
- enableSnippets := false
- locSnippets := ""
- crUpstreams := map[string]conf_v1.Upstream{
- "vs_default_cafe_coffee-v1": {Service: "coffee-v1"},
- "vs_default_cafe_coffee-v2": {Service: "coffee-v2"},
- }
-
- errorPageDetails := errorPageDetails{
- pages: errorPages,
- index: 0,
- owner: nil,
+ baseCfgParams := &ConfigParams{
+ ProxySendTimeout: "5s",
+ ProxyReadTimeout: "5s",
+ ProxyConnectTimeout: "5s",
}
- result := generateMatchesConfig(
- route,
- upstreamNamer,
- crUpstreams,
- variableNamer,
- index,
- scIndex,
- &cfgParams,
- errorPageDetails,
- locSnippets,
- enableSnippets,
- 0,
- true,
- "coffee",
- "default",
- Warnings{},
- )
- if !reflect.DeepEqual(result, expected) {
- t.Errorf("generateMatchesConfig() returned \n%+v but expected \n%+v", result, expected)
+ for _, test := range tests {
+ result := generateHealthCheck(test.upstream, test.upstreamName, baseCfgParams)
+ if !reflect.DeepEqual(result, test.expected) {
+ t.Errorf("generateHealthCheck returned \n%v but expected \n%v \n for case: %v", result, test.expected, test.msg)
+ }
}
}
-func TestGenerateValueForMatchesRouteMap(t *testing.T) {
+func TestGenerateGrpcHealthCheck(t *testing.T) {
t.Parallel()
+ upstreamName := "test-upstream"
tests := []struct {
- input string
- expectedValue string
- expectedIsNegative bool
+ upstream conf_v1.Upstream
+ upstreamName string
+ expected *version2.HealthCheck
+ msg string
}{
{
- input: "default",
- expectedValue: `\default`,
- expectedIsNegative: false,
- },
- {
- input: "!default",
- expectedValue: `\default`,
- expectedIsNegative: true,
- },
- {
- input: "hostnames",
- expectedValue: `\hostnames`,
- expectedIsNegative: false,
- },
- {
- input: "include",
- expectedValue: `\include`,
- expectedIsNegative: false,
- },
- {
- input: "volatile",
- expectedValue: `\volatile`,
- expectedIsNegative: false,
- },
- {
- input: "abc",
- expectedValue: `"abc"`,
- expectedIsNegative: false,
- },
- {
- input: "!abc",
- expectedValue: `"abc"`,
- expectedIsNegative: true,
- },
- {
- input: "",
- expectedValue: `""`,
- expectedIsNegative: false,
+ upstream: conf_v1.Upstream{
+ HealthCheck: &conf_v1.HealthCheck{
+ Enable: true,
+ Interval: "5s",
+ Jitter: "2s",
+ KeepaliveTime: "120s",
+ Fails: 3,
+ Passes: 2,
+ Port: 50051,
+ ConnectTimeout: "20s",
+ SendTimeout: "20s",
+ ReadTimeout: "20s",
+ GRPCStatus: createPointerFromInt(12),
+ GRPCService: "grpc-service",
+ Headers: []conf_v1.Header{
+ {
+ Name: "Host",
+ Value: "my.service",
+ },
+ {
+ Name: "User-Agent",
+ Value: "nginx",
+ },
+ },
+ },
+ Type: "grpc",
+ },
+ upstreamName: upstreamName,
+ expected: &version2.HealthCheck{
+ Name: upstreamName,
+ ProxyConnectTimeout: "20s",
+ ProxySendTimeout: "20s",
+ ProxyReadTimeout: "20s",
+ ProxyPass: fmt.Sprintf("http://%v", upstreamName),
+ GRPCPass: fmt.Sprintf("grpc://%v", upstreamName),
+ Interval: "5s",
+ Jitter: "2s",
+ KeepaliveTime: "120s",
+ Fails: 3,
+ Passes: 2,
+ Port: 50051,
+ GRPCStatus: createPointerFromInt(12),
+ GRPCService: "grpc-service",
+ Headers: map[string]string{
+ "Host": "my.service",
+ "User-Agent": "nginx",
+ },
+ },
+ msg: "HealthCheck with changed parameters",
},
{
- input: "!",
- expectedValue: `""`,
- expectedIsNegative: true,
+ upstream: conf_v1.Upstream{
+ HealthCheck: &conf_v1.HealthCheck{
+ Enable: true,
+ },
+ ProxyConnectTimeout: "30s",
+ ProxyReadTimeout: "30s",
+ ProxySendTimeout: "30s",
+ Type: "grpc",
+ },
+ upstreamName: upstreamName,
+ expected: &version2.HealthCheck{
+ Name: upstreamName,
+ ProxyConnectTimeout: "30s",
+ ProxyReadTimeout: "30s",
+ ProxySendTimeout: "30s",
+ ProxyPass: fmt.Sprintf("http://%v", upstreamName),
+ GRPCPass: fmt.Sprintf("grpc://%v", upstreamName),
+ Interval: "5s",
+ Jitter: "0s",
+ KeepaliveTime: "60s",
+ Fails: 1,
+ Passes: 1,
+ Headers: make(map[string]string),
+ },
+ msg: "HealthCheck with default parameters from Upstream",
},
}
+ baseCfgParams := &ConfigParams{
+ ProxySendTimeout: "5s",
+ ProxyReadTimeout: "5s",
+ ProxyConnectTimeout: "5s",
+ }
+
for _, test := range tests {
- resultValue, resultIsNegative := generateValueForMatchesRouteMap(test.input)
- if resultValue != test.expectedValue {
- t.Errorf("generateValueForMatchesRouteMap(%q) returned %q but expected %q as the value", test.input, resultValue, test.expectedValue)
- }
- if resultIsNegative != test.expectedIsNegative {
- t.Errorf("generateValueForMatchesRouteMap(%q) returned %v but expected %v as the isNegative", test.input, resultIsNegative, test.expectedIsNegative)
+ result := generateHealthCheck(test.upstream, test.upstreamName, baseCfgParams)
+ if !reflect.DeepEqual(result, test.expected) {
+ t.Errorf("generateHealthCheck returned \n%v but expected \n%v \n for case: %v", result, test.expected, test.msg)
}
}
}
-func TestGenerateParametersForMatchesRouteMap(t *testing.T) {
+func TestGenerateEndpointsForUpstream(t *testing.T) {
t.Parallel()
+ name := "test"
+ namespace := "test-namespace"
+
tests := []struct {
- inputMatchedValue string
- inputSuccessfulResult string
- expected []version2.Parameter
+ upstream conf_v1.Upstream
+ vsEx *VirtualServerEx
+ isPlus bool
+ isResolverConfigured bool
+ expected []string
+ warningsExpected bool
+ msg string
}{
{
- inputMatchedValue: "abc",
- inputSuccessfulResult: "1",
- expected: []version2.Parameter{
- {
- Value: `"abc"`,
- Result: "1",
+ upstream: conf_v1.Upstream{
+ Service: name,
+ Port: 80,
+ },
+ vsEx: &VirtualServerEx{
+ VirtualServer: &conf_v1.VirtualServer{
+ ObjectMeta: meta_v1.ObjectMeta{
+ Name: name,
+ Namespace: namespace,
+ },
+ },
+ Endpoints: map[string][]string{
+ "test-namespace/test:80": {"example.com:80"},
+ },
+ ExternalNameSvcs: map[string]bool{
+ "test-namespace/test": true,
+ },
+ },
+ isPlus: true,
+ isResolverConfigured: true,
+ expected: []string{"example.com:80"},
+ msg: "ExternalName service",
+ },
+ {
+ upstream: conf_v1.Upstream{
+ Service: name,
+ Port: 80,
+ },
+ vsEx: &VirtualServerEx{
+ VirtualServer: &conf_v1.VirtualServer{
+ ObjectMeta: meta_v1.ObjectMeta{
+ Name: name,
+ Namespace: namespace,
+ },
},
- {
- Value: "default",
- Result: "0",
+ Endpoints: map[string][]string{
+ "test-namespace/test:80": {"example.com:80"},
+ },
+ ExternalNameSvcs: map[string]bool{
+ "test-namespace/test": true,
},
},
+ isPlus: true,
+ isResolverConfigured: false,
+ warningsExpected: true,
+ expected: []string{},
+ msg: "ExternalName service without resolver configured",
},
{
- inputMatchedValue: "!abc",
- inputSuccessfulResult: "1",
- expected: []version2.Parameter{
- {
- Value: `"abc"`,
- Result: "0",
+ upstream: conf_v1.Upstream{
+ Service: name,
+ Port: 8080,
+ },
+ vsEx: &VirtualServerEx{
+ VirtualServer: &conf_v1.VirtualServer{
+ ObjectMeta: meta_v1.ObjectMeta{
+ Name: name,
+ Namespace: namespace,
+ },
},
- {
- Value: "default",
- Result: "1",
+ Endpoints: map[string][]string{
+ "test-namespace/test:8080": {"192.168.10.10:8080"},
},
},
+ isPlus: false,
+ isResolverConfigured: false,
+ expected: []string{"192.168.10.10:8080"},
+ msg: "Service with endpoints",
},
- }
-
- for _, test := range tests {
- result := generateParametersForMatchesRouteMap(test.inputMatchedValue, test.inputSuccessfulResult)
- if !reflect.DeepEqual(result, test.expected) {
- t.Errorf("generateParametersForMatchesRouteMap(%q, %q) returned %v but expected %v", test.inputMatchedValue, test.inputSuccessfulResult, result, test.expected)
- }
- }
-}
-
-func TestGetNameForSourceForMatchesRouteMapFromCondition(t *testing.T) {
- t.Parallel()
- tests := []struct {
- input conf_v1.Condition
- expected string
- }{
{
- input: conf_v1.Condition{
- Header: "x-version",
+ upstream: conf_v1.Upstream{
+ Service: name,
+ Port: 8080,
},
- expected: "$http_x_version",
+ vsEx: &VirtualServerEx{
+ VirtualServer: &conf_v1.VirtualServer{
+ ObjectMeta: meta_v1.ObjectMeta{
+ Name: name,
+ Namespace: namespace,
+ },
+ },
+ Endpoints: map[string][]string{},
+ },
+ isPlus: false,
+ isResolverConfigured: false,
+ expected: []string{nginx502Server},
+ msg: "Service with no endpoints",
},
{
- input: conf_v1.Condition{
- Cookie: "mycookie",
+ upstream: conf_v1.Upstream{
+ Service: name,
+ Port: 8080,
},
- expected: "$cookie_mycookie",
+ vsEx: &VirtualServerEx{
+ VirtualServer: &conf_v1.VirtualServer{
+ ObjectMeta: meta_v1.ObjectMeta{
+ Name: name,
+ Namespace: namespace,
+ },
+ },
+ Endpoints: map[string][]string{},
+ },
+ isPlus: true,
+ isResolverConfigured: false,
+ expected: nil,
+ msg: "Service with no endpoints",
},
{
- input: conf_v1.Condition{
- Argument: "arg",
+ upstream: conf_v1.Upstream{
+ Service: name,
+ Port: 8080,
+ Subselector: map[string]string{"version": "test"},
},
- expected: "$arg_arg",
+ vsEx: &VirtualServerEx{
+ VirtualServer: &conf_v1.VirtualServer{
+ ObjectMeta: meta_v1.ObjectMeta{
+ Name: name,
+ Namespace: namespace,
+ },
+ },
+ Endpoints: map[string][]string{
+ "test-namespace/test_version=test:8080": {"192.168.10.10:8080"},
+ },
+ },
+ isPlus: false,
+ isResolverConfigured: false,
+ expected: []string{"192.168.10.10:8080"},
+ msg: "Upstream with subselector, with a matching endpoint",
},
{
- input: conf_v1.Condition{
- Variable: "$request_method",
+ upstream: conf_v1.Upstream{
+ Service: name,
+ Port: 8080,
+ Subselector: map[string]string{"version": "test"},
},
- expected: "$request_method",
+ vsEx: &VirtualServerEx{
+ VirtualServer: &conf_v1.VirtualServer{
+ ObjectMeta: meta_v1.ObjectMeta{
+ Name: name,
+ Namespace: namespace,
+ },
+ },
+ Endpoints: map[string][]string{
+ "test-namespace/test:8080": {"192.168.10.10:8080"},
+ },
+ },
+ isPlus: false,
+ isResolverConfigured: false,
+ expected: []string{nginx502Server},
+ msg: "Upstream with subselector, without a matching endpoint",
},
}
for _, test := range tests {
- result := getNameForSourceForMatchesRouteMapFromCondition(test.input)
- if result != test.expected {
- t.Errorf("getNameForSourceForMatchesRouteMapFromCondition() returned %q but expected %q for input %v", result, test.expected, test.input)
+ isWildcardEnabled := false
+ vsc := newVirtualServerConfigurator(
+ &ConfigParams{},
+ test.isPlus,
+ test.isResolverConfigured,
+ &StaticConfigParams{},
+ isWildcardEnabled,
+ )
+ result := vsc.generateEndpointsForUpstream(test.vsEx.VirtualServer, namespace, test.upstream, test.vsEx)
+ if !reflect.DeepEqual(result, test.expected) {
+ t.Errorf("generateEndpointsForUpstream(isPlus=%v, isResolverConfigured=%v) returned %v, but expected %v for case: %v",
+ test.isPlus, test.isResolverConfigured, result, test.expected, test.msg)
+ }
+
+ if len(vsc.warnings) == 0 && test.warningsExpected {
+ t.Errorf(
+ "generateEndpointsForUpstream(isPlus=%v, isResolverConfigured=%v) didn't return any warnings for %v but warnings expected",
+ test.isPlus,
+ test.isResolverConfigured,
+ test.upstream,
+ )
+ }
+
+ if len(vsc.warnings) != 0 && !test.warningsExpected {
+ t.Errorf("generateEndpointsForUpstream(isPlus=%v, isResolverConfigured=%v) returned warnings for %v",
+ test.isPlus, test.isResolverConfigured, test.upstream)
}
}
}
-func TestGenerateLBMethod(t *testing.T) {
+func TestGenerateSlowStartForPlusWithInCompatibleLBMethods(t *testing.T) {
t.Parallel()
- defaultMethod := "random two least_conn"
+ serviceName := "test-slowstart-with-incompatible-LBMethods"
+ upstream := conf_v1.Upstream{Service: serviceName, Port: 80, SlowStart: "10s"}
+ expected := ""
- tests := []struct {
- input string
- expected string
- }{
- {
- input: "",
- expected: defaultMethod,
- },
- {
- input: "round_robin",
- expected: "",
- },
- {
- input: "random",
- expected: "random",
- },
+ tests := []string{
+ "random",
+ "ip_hash",
+ "hash 123",
+ "random two",
+ "random two least_conn",
+ "random two least_time=header",
+ "random two least_time=last_byte",
}
- for _, test := range tests {
- result := generateLBMethod(test.input, defaultMethod)
- if result != test.expected {
- t.Errorf("generateLBMethod() returned %q but expected %q for input '%v'", result, test.expected, test.input)
+
+ for _, lbMethod := range tests {
+ vsc := newVirtualServerConfigurator(&ConfigParams{}, true, false, &StaticConfigParams{}, false)
+ result := vsc.generateSlowStartForPlus(&conf_v1.VirtualServer{}, upstream, lbMethod)
+
+ if !reflect.DeepEqual(result, expected) {
+ t.Errorf("generateSlowStartForPlus returned %v, but expected %v for lbMethod %v", result, expected, lbMethod)
+ }
+
+ if len(vsc.warnings) == 0 {
+ t.Errorf("generateSlowStartForPlus returned no warnings for %v but warnings expected", upstream)
}
}
}
-func TestUpstreamHasKeepalive(t *testing.T) {
- t.Parallel()
- noKeepalive := 0
- keepalive := 32
+func TestGenerateSlowStartForPlus(t *testing.T) {
+ serviceName := "test-slowstart"
tests := []struct {
- upstream conf_v1.Upstream
- cfgParams *ConfigParams
- expected bool
- msg string
- }{
- {
- conf_v1.Upstream{},
- &ConfigParams{Keepalive: keepalive},
- true,
- "upstream keepalive not set, configparam keepalive set",
- },
+ upstream conf_v1.Upstream
+ lbMethod string
+ expected string
+ }{
{
- conf_v1.Upstream{Keepalive: &noKeepalive},
- &ConfigParams{Keepalive: keepalive},
- false,
- "upstream keepalive set to 0, configparam keepalive set",
+ upstream: conf_v1.Upstream{Service: serviceName, Port: 80, SlowStart: "", LBMethod: "least_conn"},
+ lbMethod: "least_conn",
+ expected: "",
},
{
- conf_v1.Upstream{Keepalive: &keepalive},
- &ConfigParams{Keepalive: noKeepalive},
- true,
- "upstream keepalive set, configparam keepalive set to 0",
+ upstream: conf_v1.Upstream{Service: serviceName, Port: 80, SlowStart: "10s", LBMethod: "least_conn"},
+ lbMethod: "least_conn",
+ expected: "10s",
},
}
for _, test := range tests {
- result := upstreamHasKeepalive(test.upstream, test.cfgParams)
- if result != test.expected {
- t.Errorf("upstreamHasKeepalive() returned %v, but expected %v for the case of %v", result, test.expected, test.msg)
+ vsc := newVirtualServerConfigurator(&ConfigParams{}, true, false, &StaticConfigParams{}, false)
+ result := vsc.generateSlowStartForPlus(&conf_v1.VirtualServer{}, test.upstream, test.lbMethod)
+ if !reflect.DeepEqual(result, test.expected) {
+ t.Errorf("generateSlowStartForPlus returned %v, but expected %v", result, test.expected)
+ }
+
+ if len(vsc.warnings) != 0 {
+ t.Errorf("generateSlowStartForPlus returned warnings for %v", test.upstream)
}
}
}
-func TestNewHealthCheckWithDefaults(t *testing.T) {
+func TestCreateEndpointsFromUpstream(t *testing.T) {
t.Parallel()
- upstreamName := "test-upstream"
- baseCfgParams := &ConfigParams{
- ProxySendTimeout: "5s",
- ProxyReadTimeout: "5s",
- ProxyConnectTimeout: "5s",
- }
- expected := &version2.HealthCheck{
- Name: upstreamName,
- ProxySendTimeout: "5s",
- ProxyReadTimeout: "5s",
- ProxyConnectTimeout: "5s",
- ProxyPass: fmt.Sprintf("http://%v", upstreamName),
- URI: "/",
- Interval: "5s",
- Jitter: "0s",
- KeepaliveTime: "60s",
- Fails: 1,
- Passes: 1,
- Headers: make(map[string]string),
+ ups := version2.Upstream{
+ Servers: []version2.UpstreamServer{
+ {
+ Address: "10.0.0.20:80",
+ },
+ {
+ Address: "10.0.0.30:80",
+ },
+ },
}
- result := newHealthCheckWithDefaults(conf_v1.Upstream{}, upstreamName, baseCfgParams)
+ expected := []string{
+ "10.0.0.20:80",
+ "10.0.0.30:80",
+ }
- if !reflect.DeepEqual(result, expected) {
- t.Errorf("newHealthCheckWithDefaults returned \n%v but expected \n%v", result, expected)
+ endpoints := createEndpointsFromUpstream(ups)
+ if !reflect.DeepEqual(endpoints, expected) {
+ t.Errorf("createEndpointsFromUpstream returned %v, but expected %v", endpoints, expected)
}
}
-func TestGenerateHealthCheck(t *testing.T) {
+func TestGenerateUpstreamWithQueue(t *testing.T) {
t.Parallel()
- upstreamName := "test-upstream"
+ serviceName := "test-queue"
+
tests := []struct {
- upstream conf_v1.Upstream
- upstreamName string
- expected *version2.HealthCheck
- msg string
+ name string
+ upstream conf_v1.Upstream
+ isPlus bool
+ expected version2.Upstream
+ msg string
}{
{
- upstream: conf_v1.Upstream{
- HealthCheck: &conf_v1.HealthCheck{
- Enable: true,
- Path: "/healthz",
- Interval: "5s",
- Jitter: "2s",
- KeepaliveTime: "120s",
- Fails: 3,
- Passes: 2,
- Port: 8080,
- ConnectTimeout: "20s",
- SendTimeout: "20s",
- ReadTimeout: "20s",
- Headers: []conf_v1.Header{
- {
- Name: "Host",
- Value: "my.service",
- },
- {
- Name: "User-Agent",
- Value: "nginx",
- },
- },
- StatusMatch: "! 500",
+ name: "test-upstream-queue",
+ upstream: conf_v1.Upstream{Service: serviceName, Port: 80, Queue: &conf_v1.UpstreamQueue{
+ Size: 10,
+ Timeout: "10s",
+ }},
+ isPlus: true,
+ expected: version2.Upstream{
+ UpstreamLabels: version2.UpstreamLabels{
+ Service: "test-queue",
},
- },
- upstreamName: upstreamName,
- expected: &version2.HealthCheck{
- Name: upstreamName,
- ProxyConnectTimeout: "20s",
- ProxySendTimeout: "20s",
- ProxyReadTimeout: "20s",
- ProxyPass: fmt.Sprintf("http://%v", upstreamName),
- URI: "/healthz",
- Interval: "5s",
- Jitter: "2s",
- KeepaliveTime: "120s",
- Fails: 3,
- Passes: 2,
- Port: 8080,
- Headers: map[string]string{
- "Host": "my.service",
- "User-Agent": "nginx",
+ Name: "test-upstream-queue",
+ Queue: &version2.Queue{
+ Size: 10,
+ Timeout: "10s",
},
- Match: fmt.Sprintf("%v_match", upstreamName),
},
- msg: "HealthCheck with changed parameters",
+ msg: "upstream queue with size and timeout",
},
{
+ name: "test-upstream-queue-with-default-timeout",
upstream: conf_v1.Upstream{
- HealthCheck: &conf_v1.HealthCheck{
- Enable: true,
- },
- ProxyConnectTimeout: "30s",
- ProxyReadTimeout: "30s",
- ProxySendTimeout: "30s",
- },
- upstreamName: upstreamName,
- expected: &version2.HealthCheck{
- Name: upstreamName,
- ProxyConnectTimeout: "30s",
- ProxyReadTimeout: "30s",
- ProxySendTimeout: "30s",
- ProxyPass: fmt.Sprintf("http://%v", upstreamName),
- URI: "/",
- Interval: "5s",
- Jitter: "0s",
- KeepaliveTime: "60s",
- Fails: 1,
- Passes: 1,
- Headers: make(map[string]string),
+ Service: serviceName,
+ Port: 80,
+ Queue: &conf_v1.UpstreamQueue{Size: 10, Timeout: ""},
},
- msg: "HealthCheck with default parameters from Upstream",
- },
- {
- upstream: conf_v1.Upstream{
- HealthCheck: &conf_v1.HealthCheck{
- Enable: true,
+ isPlus: true,
+ expected: version2.Upstream{
+ UpstreamLabels: version2.UpstreamLabels{
+ Service: "test-queue",
},
- },
- upstreamName: upstreamName,
- expected: &version2.HealthCheck{
- Name: upstreamName,
- ProxyConnectTimeout: "5s",
- ProxyReadTimeout: "5s",
- ProxySendTimeout: "5s",
- ProxyPass: fmt.Sprintf("http://%v", upstreamName),
- URI: "/",
- Interval: "5s",
- Jitter: "0s",
- KeepaliveTime: "60s",
- Fails: 1,
- Passes: 1,
- Headers: make(map[string]string),
- },
- msg: "HealthCheck with default parameters from ConfigMap (not defined in Upstream)",
- },
- {
- upstream: conf_v1.Upstream{},
- upstreamName: upstreamName,
- expected: nil,
- msg: "HealthCheck not enabled",
- },
- {
- upstream: conf_v1.Upstream{
- HealthCheck: &conf_v1.HealthCheck{
- Enable: true,
- Interval: "1m 5s",
- Jitter: "2m 3s",
- KeepaliveTime: "1m 6s",
- ConnectTimeout: "1m 10s",
- SendTimeout: "1m 20s",
- ReadTimeout: "1m 30s",
+ Name: "test-upstream-queue-with-default-timeout",
+ Queue: &version2.Queue{
+ Size: 10,
+ Timeout: "60s",
},
},
- upstreamName: upstreamName,
- expected: &version2.HealthCheck{
- Name: upstreamName,
- ProxyConnectTimeout: "1m10s",
- ProxySendTimeout: "1m20s",
- ProxyReadTimeout: "1m30s",
- ProxyPass: fmt.Sprintf("http://%v", upstreamName),
- URI: "/",
- Interval: "1m5s",
- Jitter: "2m3s",
- KeepaliveTime: "1m6s",
- Fails: 1,
- Passes: 1,
- Headers: make(map[string]string),
- },
- msg: "HealthCheck with time parameters have correct format",
+ msg: "upstream queue with only size",
},
{
- upstream: conf_v1.Upstream{
- HealthCheck: &conf_v1.HealthCheck{
- Enable: true,
- Mandatory: true,
- Persistent: true,
- },
- ProxyConnectTimeout: "30s",
- ProxyReadTimeout: "30s",
- ProxySendTimeout: "30s",
- },
- upstreamName: upstreamName,
- expected: &version2.HealthCheck{
- Name: upstreamName,
- ProxyConnectTimeout: "30s",
- ProxyReadTimeout: "30s",
- ProxySendTimeout: "30s",
- ProxyPass: fmt.Sprintf("http://%v", upstreamName),
- URI: "/",
- Interval: "5s",
- Jitter: "0s",
- KeepaliveTime: "60s",
- Fails: 1,
- Passes: 1,
- Headers: make(map[string]string),
- Mandatory: true,
- Persistent: true,
+ name: "test-upstream-queue-nil",
+ upstream: conf_v1.Upstream{Service: serviceName, Port: 80, Queue: nil},
+ isPlus: false,
+ expected: version2.Upstream{
+ UpstreamLabels: version2.UpstreamLabels{
+ Service: "test-queue",
+ },
+ Name: "test-upstream-queue-nil",
},
- msg: "HealthCheck with mandatory and persistent set",
+ msg: "upstream queue with nil for OSS",
},
}
- baseCfgParams := &ConfigParams{
- ProxySendTimeout: "5s",
- ProxyReadTimeout: "5s",
- ProxyConnectTimeout: "5s",
- }
-
for _, test := range tests {
- result := generateHealthCheck(test.upstream, test.upstreamName, baseCfgParams)
+ vsc := newVirtualServerConfigurator(&ConfigParams{}, test.isPlus, false, &StaticConfigParams{}, false)
+ result := vsc.generateUpstream(nil, test.name, test.upstream, false, []string{})
if !reflect.DeepEqual(result, test.expected) {
- t.Errorf("generateHealthCheck returned \n%v but expected \n%v \n for case: %v", result, test.expected, test.msg)
+ t.Errorf("generateUpstream() returned %v but expected %v for the case of %v", result, test.expected, test.msg)
}
}
}
-func TestGenerateGrpcHealthCheck(t *testing.T) {
+func TestGenerateQueueForPlus(t *testing.T) {
t.Parallel()
- upstreamName := "test-upstream"
tests := []struct {
- upstream conf_v1.Upstream
- upstreamName string
- expected *version2.HealthCheck
- msg string
+ upstreamQueue *conf_v1.UpstreamQueue
+ expected *version2.Queue
+ msg string
}{
{
- upstream: conf_v1.Upstream{
- HealthCheck: &conf_v1.HealthCheck{
- Enable: true,
- Interval: "5s",
- Jitter: "2s",
- KeepaliveTime: "120s",
- Fails: 3,
- Passes: 2,
- Port: 50051,
- ConnectTimeout: "20s",
- SendTimeout: "20s",
- ReadTimeout: "20s",
- GRPCStatus: createPointerFromInt(12),
- GRPCService: "grpc-service",
- Headers: []conf_v1.Header{
- {
- Name: "Host",
- Value: "my.service",
- },
- {
- Name: "User-Agent",
- Value: "nginx",
- },
- },
- },
- Type: "grpc",
- },
- upstreamName: upstreamName,
- expected: &version2.HealthCheck{
- Name: upstreamName,
- ProxyConnectTimeout: "20s",
- ProxySendTimeout: "20s",
- ProxyReadTimeout: "20s",
- ProxyPass: fmt.Sprintf("http://%v", upstreamName),
- GRPCPass: fmt.Sprintf("grpc://%v", upstreamName),
- Interval: "5s",
- Jitter: "2s",
- KeepaliveTime: "120s",
- Fails: 3,
- Passes: 2,
- Port: 50051,
- GRPCStatus: createPointerFromInt(12),
- GRPCService: "grpc-service",
- Headers: map[string]string{
- "Host": "my.service",
- "User-Agent": "nginx",
- },
- },
- msg: "HealthCheck with changed parameters",
+ upstreamQueue: &conf_v1.UpstreamQueue{Size: 10, Timeout: "10s"},
+ expected: &version2.Queue{Size: 10, Timeout: "10s"},
+ msg: "upstream queue with size and timeout",
},
{
- upstream: conf_v1.Upstream{
- HealthCheck: &conf_v1.HealthCheck{
- Enable: true,
- },
- ProxyConnectTimeout: "30s",
- ProxyReadTimeout: "30s",
- ProxySendTimeout: "30s",
- Type: "grpc",
- },
- upstreamName: upstreamName,
- expected: &version2.HealthCheck{
- Name: upstreamName,
- ProxyConnectTimeout: "30s",
- ProxyReadTimeout: "30s",
- ProxySendTimeout: "30s",
- ProxyPass: fmt.Sprintf("http://%v", upstreamName),
- GRPCPass: fmt.Sprintf("grpc://%v", upstreamName),
- Interval: "5s",
- Jitter: "0s",
- KeepaliveTime: "60s",
- Fails: 1,
- Passes: 1,
- Headers: make(map[string]string),
- },
- msg: "HealthCheck with default parameters from Upstream",
+ upstreamQueue: nil,
+ expected: nil,
+ msg: "upstream queue with nil",
+ },
+ {
+ upstreamQueue: &conf_v1.UpstreamQueue{Size: 10},
+ expected: &version2.Queue{Size: 10, Timeout: "60s"},
+ msg: "upstream queue with only size",
},
}
- baseCfgParams := &ConfigParams{
- ProxySendTimeout: "5s",
- ProxyReadTimeout: "5s",
- ProxyConnectTimeout: "5s",
+ for _, test := range tests {
+ result := generateQueueForPlus(test.upstreamQueue, "60s")
+ if !reflect.DeepEqual(result, test.expected) {
+ t.Errorf("generateQueueForPlus() returned %v but expected %v for the case of %v", result, test.expected, test.msg)
+ }
}
+}
+func TestGenerateSessionCookie(t *testing.T) {
+ t.Parallel()
+ tests := []struct {
+ sc *conf_v1.SessionCookie
+ expected *version2.SessionCookie
+ msg string
+ }{
+ {
+ sc: &conf_v1.SessionCookie{Enable: true, Name: "test"},
+ expected: &version2.SessionCookie{Enable: true, Name: "test"},
+ msg: "session cookie with name",
+ },
+ {
+ sc: nil,
+ expected: nil,
+ msg: "session cookie with nil",
+ },
+ {
+ sc: &conf_v1.SessionCookie{Name: "test"},
+ expected: nil,
+ msg: "session cookie not enabled",
+ },
+ }
for _, test := range tests {
- result := generateHealthCheck(test.upstream, test.upstreamName, baseCfgParams)
+ result := generateSessionCookie(test.sc)
if !reflect.DeepEqual(result, test.expected) {
- t.Errorf("generateHealthCheck returned \n%v but expected \n%v \n for case: %v", result, test.expected, test.msg)
+ t.Errorf("generateSessionCookie() returned %v, but expected %v for the case of: %v", result, test.expected, test.msg)
}
}
}
-func TestGenerateEndpointsForUpstream(t *testing.T) {
+func TestGeneratePath(t *testing.T) {
t.Parallel()
- name := "test"
- namespace := "test-namespace"
-
tests := []struct {
- upstream conf_v1.Upstream
- vsEx *VirtualServerEx
- isPlus bool
- isResolverConfigured bool
- expected []string
- warningsExpected bool
- msg string
+ path string
+ expected string
}{
{
- upstream: conf_v1.Upstream{
- Service: name,
- Port: 80,
- },
- vsEx: &VirtualServerEx{
- VirtualServer: &conf_v1.VirtualServer{
- ObjectMeta: meta_v1.ObjectMeta{
- Name: name,
- Namespace: namespace,
- },
- },
- Endpoints: map[string][]string{
- "test-namespace/test:80": {"example.com:80"},
- },
- ExternalNameSvcs: map[string]bool{
- "test-namespace/test": true,
- },
- },
- isPlus: true,
- isResolverConfigured: true,
- expected: []string{"example.com:80"},
- msg: "ExternalName service",
+ path: "/",
+ expected: "/",
},
{
- upstream: conf_v1.Upstream{
- Service: name,
- Port: 80,
- },
- vsEx: &VirtualServerEx{
- VirtualServer: &conf_v1.VirtualServer{
- ObjectMeta: meta_v1.ObjectMeta{
- Name: name,
- Namespace: namespace,
- },
- },
- Endpoints: map[string][]string{
- "test-namespace/test:80": {"example.com:80"},
- },
- ExternalNameSvcs: map[string]bool{
- "test-namespace/test": true,
- },
- },
- isPlus: true,
- isResolverConfigured: false,
- warningsExpected: true,
- expected: []string{},
- msg: "ExternalName service without resolver configured",
+ path: "=/exact/match",
+ expected: "=/exact/match",
},
{
- upstream: conf_v1.Upstream{
- Service: name,
- Port: 8080,
- },
- vsEx: &VirtualServerEx{
- VirtualServer: &conf_v1.VirtualServer{
- ObjectMeta: meta_v1.ObjectMeta{
- Name: name,
- Namespace: namespace,
- },
- },
- Endpoints: map[string][]string{
- "test-namespace/test:8080": {"192.168.10.10:8080"},
- },
- },
- isPlus: false,
- isResolverConfigured: false,
- expected: []string{"192.168.10.10:8080"},
- msg: "Service with endpoints",
+ path: `~ *\\.jpg`,
+ expected: `~ "*\\.jpg"`,
+ },
+ {
+ path: `~* *\\.PNG`,
+ expected: `~* "*\\.PNG"`,
+ },
+ }
+
+ for _, test := range tests {
+ result := generatePath(test.path)
+ if result != test.expected {
+ t.Errorf("generatePath() returned %v, but expected %v.", result, test.expected)
+ }
+ }
+}
+
+func TestGenerateErrorPageName(t *testing.T) {
+ t.Parallel()
+ tests := []struct {
+ routeIndex int
+ index int
+ expected string
+ }{
+ {
+ 0,
+ 0,
+ "@error_page_0_0",
},
{
- upstream: conf_v1.Upstream{
- Service: name,
- Port: 8080,
- },
- vsEx: &VirtualServerEx{
- VirtualServer: &conf_v1.VirtualServer{
- ObjectMeta: meta_v1.ObjectMeta{
- Name: name,
- Namespace: namespace,
- },
- },
- Endpoints: map[string][]string{},
- },
- isPlus: false,
- isResolverConfigured: false,
- expected: []string{nginx502Server},
- msg: "Service with no endpoints",
+ 0,
+ 1,
+ "@error_page_0_1",
},
{
- upstream: conf_v1.Upstream{
- Service: name,
- Port: 8080,
- },
- vsEx: &VirtualServerEx{
- VirtualServer: &conf_v1.VirtualServer{
- ObjectMeta: meta_v1.ObjectMeta{
- Name: name,
- Namespace: namespace,
- },
- },
- Endpoints: map[string][]string{},
- },
- isPlus: true,
- isResolverConfigured: false,
- expected: nil,
- msg: "Service with no endpoints",
+ 1,
+ 0,
+ "@error_page_1_0",
},
+ }
+
+ for _, test := range tests {
+ result := generateErrorPageName(test.routeIndex, test.index)
+ if result != test.expected {
+ t.Errorf("generateErrorPageName(%v, %v) returned %v but expected %v", test.routeIndex, test.index, result, test.expected)
+ }
+ }
+}
+
+func TestGenerateErrorPageCodes(t *testing.T) {
+ t.Parallel()
+ tests := []struct {
+ codes []int
+ expected string
+ }{
{
- upstream: conf_v1.Upstream{
- Service: name,
- Port: 8080,
- Subselector: map[string]string{"version": "test"},
- },
- vsEx: &VirtualServerEx{
- VirtualServer: &conf_v1.VirtualServer{
- ObjectMeta: meta_v1.ObjectMeta{
- Name: name,
- Namespace: namespace,
+ codes: []int{400},
+ expected: "400",
+ },
+ {
+ codes: []int{404, 405, 502},
+ expected: "404 405 502",
+ },
+ }
+
+ for _, test := range tests {
+ result := generateErrorPageCodes(test.codes)
+ if result != test.expected {
+ t.Errorf("generateErrorPageCodes(%v) returned %v but expected %v", test.codes, result, test.expected)
+ }
+ }
+}
+
+func TestGenerateErrorPages(t *testing.T) {
+ t.Parallel()
+ tests := []struct {
+ upstreamName string
+ errorPages []conf_v1.ErrorPage
+ expected []version2.ErrorPage
+ }{
+ {}, // empty errorPages
+ {
+ "vs_test_test",
+ []conf_v1.ErrorPage{
+ {
+ Codes: []int{404, 405, 500, 502},
+ Return: &conf_v1.ErrorPageReturn{
+ ActionReturn: conf_v1.ActionReturn{
+ Code: 200,
+ },
+ Headers: nil,
},
+ Redirect: nil,
},
- Endpoints: map[string][]string{
- "test-namespace/test_version=test:8080": {"192.168.10.10:8080"},
+ },
+ []version2.ErrorPage{
+ {
+ Name: "@error_page_1_0",
+ Codes: "404 405 500 502",
+ ResponseCode: 200,
},
},
- isPlus: false,
- isResolverConfigured: false,
- expected: []string{"192.168.10.10:8080"},
- msg: "Upstream with subselector, with a matching endpoint",
},
{
- upstream: conf_v1.Upstream{
- Service: name,
- Port: 8080,
- Subselector: map[string]string{"version": "test"},
- },
- vsEx: &VirtualServerEx{
- VirtualServer: &conf_v1.VirtualServer{
- ObjectMeta: meta_v1.ObjectMeta{
- Name: name,
- Namespace: namespace,
+ "vs_test_test",
+ []conf_v1.ErrorPage{
+ {
+ Codes: []int{404, 405, 500, 502},
+ Return: nil,
+ Redirect: &conf_v1.ErrorPageRedirect{
+ ActionRedirect: conf_v1.ActionRedirect{
+ URL: "http://nginx.org",
+ Code: 302,
+ },
},
},
- Endpoints: map[string][]string{
- "test-namespace/test:8080": {"192.168.10.10:8080"},
+ },
+ []version2.ErrorPage{
+ {
+ Name: "http://nginx.org",
+ Codes: "404 405 500 502",
+ ResponseCode: 302,
},
},
- isPlus: false,
- isResolverConfigured: false,
- expected: []string{nginx502Server},
- msg: "Upstream with subselector, without a matching endpoint",
},
}
- for _, test := range tests {
- isWildcardEnabled := false
- vsc := newVirtualServerConfigurator(
- &ConfigParams{},
- test.isPlus,
- test.isResolverConfigured,
- &StaticConfigParams{},
- isWildcardEnabled,
- )
- result := vsc.generateEndpointsForUpstream(test.vsEx.VirtualServer, namespace, test.upstream, test.vsEx)
+ for i, test := range tests {
+ result := generateErrorPages(i, test.errorPages)
if !reflect.DeepEqual(result, test.expected) {
- t.Errorf("generateEndpointsForUpstream(isPlus=%v, isResolverConfigured=%v) returned %v, but expected %v for case: %v",
- test.isPlus, test.isResolverConfigured, result, test.expected, test.msg)
- }
-
- if len(vsc.warnings) == 0 && test.warningsExpected {
- t.Errorf(
- "generateEndpointsForUpstream(isPlus=%v, isResolverConfigured=%v) didn't return any warnings for %v but warnings expected",
- test.isPlus,
- test.isResolverConfigured,
- test.upstream,
- )
- }
-
- if len(vsc.warnings) != 0 && !test.warningsExpected {
- t.Errorf("generateEndpointsForUpstream(isPlus=%v, isResolverConfigured=%v) returned warnings for %v",
- test.isPlus, test.isResolverConfigured, test.upstream)
+ t.Errorf("generateErrorPages(%v, %v) returned %v but expected %v", test.upstreamName, test.errorPages, result, test.expected)
}
}
}
-func TestGenerateSlowStartForPlusWithInCompatibleLBMethods(t *testing.T) {
+func TestGenerateErrorPageLocations(t *testing.T) {
t.Parallel()
- serviceName := "test-slowstart-with-incompatible-LBMethods"
- upstream := conf_v1.Upstream{Service: serviceName, Port: 80, SlowStart: "10s"}
- expected := ""
-
- tests := []string{
- "random",
- "ip_hash",
- "hash 123",
- "random two",
- "random two least_conn",
- "random two least_time=header",
- "random two least_time=last_byte",
- }
-
- for _, lbMethod := range tests {
- vsc := newVirtualServerConfigurator(&ConfigParams{}, true, false, &StaticConfigParams{}, false)
- result := vsc.generateSlowStartForPlus(&conf_v1.VirtualServer{}, upstream, lbMethod)
-
- if !reflect.DeepEqual(result, expected) {
- t.Errorf("generateSlowStartForPlus returned %v, but expected %v for lbMethod %v", result, expected, lbMethod)
- }
-
- if len(vsc.warnings) == 0 {
- t.Errorf("generateSlowStartForPlus returned no warnings for %v but warnings expected", upstream)
- }
- }
-}
-
-func TestGenerateSlowStartForPlus(t *testing.T) {
- serviceName := "test-slowstart"
-
tests := []struct {
- upstream conf_v1.Upstream
- lbMethod string
- expected string
+ upstreamName string
+ errorPages []conf_v1.ErrorPage
+ expected []version2.ErrorPageLocation
}{
+ {},
{
- upstream: conf_v1.Upstream{Service: serviceName, Port: 80, SlowStart: "", LBMethod: "least_conn"},
- lbMethod: "least_conn",
- expected: "",
+ "vs_test_test",
+ []conf_v1.ErrorPage{
+ {
+ Codes: []int{404, 405, 500, 502},
+ Return: nil,
+ Redirect: &conf_v1.ErrorPageRedirect{
+ ActionRedirect: conf_v1.ActionRedirect{
+ URL: "http://nginx.org",
+ Code: 302,
+ },
+ },
+ },
+ },
+ nil,
},
{
- upstream: conf_v1.Upstream{Service: serviceName, Port: 80, SlowStart: "10s", LBMethod: "least_conn"},
- lbMethod: "least_conn",
- expected: "10s",
+ "vs_test_test",
+ []conf_v1.ErrorPage{
+ {
+ Codes: []int{404, 405, 500, 502},
+ Return: &conf_v1.ErrorPageReturn{
+ ActionReturn: conf_v1.ActionReturn{
+ Code: 200,
+ Type: "application/json",
+ Body: "Hello World",
+ },
+ Headers: []conf_v1.Header{
+ {
+ Name: "HeaderName",
+ Value: "HeaderValue",
+ },
+ },
+ },
+ Redirect: nil,
+ },
+ },
+ []version2.ErrorPageLocation{
+ {
+ Name: "@error_page_2_0",
+ DefaultType: "application/json",
+ Return: &version2.Return{
+ Code: 0,
+ Text: "Hello World",
+ },
+ Headers: []version2.Header{
+ {
+ Name: "HeaderName",
+ Value: "HeaderValue",
+ },
+ },
+ },
+ },
},
}
- for _, test := range tests {
- vsc := newVirtualServerConfigurator(&ConfigParams{}, true, false, &StaticConfigParams{}, false)
- result := vsc.generateSlowStartForPlus(&conf_v1.VirtualServer{}, test.upstream, test.lbMethod)
+ for i, test := range tests {
+ result := generateErrorPageLocations(i, test.errorPages)
if !reflect.DeepEqual(result, test.expected) {
- t.Errorf("generateSlowStartForPlus returned %v, but expected %v", result, test.expected)
- }
-
- if len(vsc.warnings) != 0 {
- t.Errorf("generateSlowStartForPlus returned warnings for %v", test.upstream)
+ t.Errorf("generateErrorPageLocations(%v, %v) returned %v but expected %v", test.upstreamName, test.errorPages, result, test.expected)
}
}
}
-func TestCreateEndpointsFromUpstream(t *testing.T) {
+func TestGenerateProxySSLName(t *testing.T) {
t.Parallel()
- ups := version2.Upstream{
- Servers: []version2.UpstreamServer{
- {
- Address: "10.0.0.20:80",
- },
- {
- Address: "10.0.0.30:80",
- },
- },
- }
-
- expected := []string{
- "10.0.0.20:80",
- "10.0.0.30:80",
- }
-
- endpoints := createEndpointsFromUpstream(ups)
- if !reflect.DeepEqual(endpoints, expected) {
- t.Errorf("createEndpointsFromUpstream returned %v, but expected %v", endpoints, expected)
+ result := generateProxySSLName("coffee-v1", "default")
+ if result != "coffee-v1.default.svc" {
+ t.Errorf("generateProxySSLName(coffee-v1, default) returned %v but expected coffee-v1.default.svc", result)
}
}
-func TestGenerateUpstreamWithQueue(t *testing.T) {
+func TestIsTLSEnabled(t *testing.T) {
t.Parallel()
- serviceName := "test-queue"
-
tests := []struct {
- name string
- upstream conf_v1.Upstream
- isPlus bool
- expected version2.Upstream
- msg string
+ upstream conf_v1.Upstream
+ spiffeCert bool
+ nsmEgress bool
+ expected bool
}{
{
- name: "test-upstream-queue",
- upstream: conf_v1.Upstream{Service: serviceName, Port: 80, Queue: &conf_v1.UpstreamQueue{
- Size: 10,
- Timeout: "10s",
- }},
- isPlus: true,
- expected: version2.Upstream{
- UpstreamLabels: version2.UpstreamLabels{
- Service: "test-queue",
- },
- Name: "test-upstream-queue",
- Queue: &version2.Queue{
- Size: 10,
- Timeout: "10s",
+ upstream: conf_v1.Upstream{
+ TLS: conf_v1.UpstreamTLS{
+ Enable: false,
},
},
- msg: "upstream queue with size and timeout",
+ spiffeCert: false,
+ expected: false,
},
{
- name: "test-upstream-queue-with-default-timeout",
upstream: conf_v1.Upstream{
- Service: serviceName,
- Port: 80,
- Queue: &conf_v1.UpstreamQueue{Size: 10, Timeout: ""},
- },
- isPlus: true,
- expected: version2.Upstream{
- UpstreamLabels: version2.UpstreamLabels{
- Service: "test-queue",
- },
- Name: "test-upstream-queue-with-default-timeout",
- Queue: &version2.Queue{
- Size: 10,
- Timeout: "60s",
+ TLS: conf_v1.UpstreamTLS{
+ Enable: false,
},
},
- msg: "upstream queue with only size",
+ spiffeCert: true,
+ expected: true,
},
{
- name: "test-upstream-queue-nil",
- upstream: conf_v1.Upstream{Service: serviceName, Port: 80, Queue: nil},
- isPlus: false,
- expected: version2.Upstream{
- UpstreamLabels: version2.UpstreamLabels{
- Service: "test-queue",
+ upstream: conf_v1.Upstream{
+ TLS: conf_v1.UpstreamTLS{
+ Enable: true,
},
- Name: "test-upstream-queue-nil",
},
- msg: "upstream queue with nil for OSS",
- },
- }
-
- for _, test := range tests {
- vsc := newVirtualServerConfigurator(&ConfigParams{}, test.isPlus, false, &StaticConfigParams{}, false)
- result := vsc.generateUpstream(nil, test.name, test.upstream, false, []string{})
- if !reflect.DeepEqual(result, test.expected) {
- t.Errorf("generateUpstream() returned %v but expected %v for the case of %v", result, test.expected, test.msg)
- }
- }
-}
-
-func TestGenerateQueueForPlus(t *testing.T) {
- t.Parallel()
- tests := []struct {
- upstreamQueue *conf_v1.UpstreamQueue
- expected *version2.Queue
- msg string
- }{
- {
- upstreamQueue: &conf_v1.UpstreamQueue{Size: 10, Timeout: "10s"},
- expected: &version2.Queue{Size: 10, Timeout: "10s"},
- msg: "upstream queue with size and timeout",
+ spiffeCert: true,
+ expected: true,
},
{
- upstreamQueue: nil,
- expected: nil,
- msg: "upstream queue with nil",
+ upstream: conf_v1.Upstream{
+ TLS: conf_v1.UpstreamTLS{
+ Enable: true,
+ },
+ },
+ spiffeCert: false,
+ expected: true,
},
{
- upstreamQueue: &conf_v1.UpstreamQueue{Size: 10},
- expected: &version2.Queue{Size: 10, Timeout: "60s"},
- msg: "upstream queue with only size",
+ upstream: conf_v1.Upstream{
+ TLS: conf_v1.UpstreamTLS{
+ Enable: true,
+ },
+ },
+ nsmEgress: true,
+ spiffeCert: false,
+ expected: false,
},
}
for _, test := range tests {
- result := generateQueueForPlus(test.upstreamQueue, "60s")
- if !reflect.DeepEqual(result, test.expected) {
- t.Errorf("generateQueueForPlus() returned %v but expected %v for the case of %v", result, test.expected, test.msg)
+ result := isTLSEnabled(test.upstream, test.spiffeCert, test.nsmEgress)
+ if result != test.expected {
+ t.Errorf("isTLSEnabled(%v, %v) returned %v but expected %v", test.upstream, test.spiffeCert, result, test.expected)
}
}
}
-func TestGenerateSessionCookie(t *testing.T) {
+func TestGenerateRewrites(t *testing.T) {
t.Parallel()
tests := []struct {
- sc *conf_v1.SessionCookie
- expected *version2.SessionCookie
- msg string
+ path string
+ proxy *conf_v1.ActionProxy
+ internal bool
+ originalPath string
+ grpcEnabled bool
+ expected []string
+ msg string
}{
{
- sc: &conf_v1.SessionCookie{Enable: true, Name: "test"},
- expected: &version2.SessionCookie{Enable: true, Name: "test"},
- msg: "session cookie with name",
+ proxy: nil,
+ expected: nil,
+ msg: "action isn't proxy",
},
{
- sc: nil,
+ proxy: &conf_v1.ActionProxy{
+ RewritePath: "",
+ },
expected: nil,
- msg: "session cookie with nil",
+ msg: "no rewrite is configured",
},
{
- sc: &conf_v1.SessionCookie{Name: "test"},
+ path: "/path",
+ proxy: &conf_v1.ActionProxy{
+ RewritePath: "/rewrite",
+ },
expected: nil,
- msg: "session cookie not enabled",
+ msg: "non-regex rewrite for non-internal location is not needed",
},
- }
- for _, test := range tests {
- result := generateSessionCookie(test.sc)
- if !reflect.DeepEqual(result, test.expected) {
- t.Errorf("generateSessionCookie() returned %v, but expected %v for the case of: %v", result, test.expected, test.msg)
- }
- }
-}
-
-func TestGeneratePath(t *testing.T) {
- t.Parallel()
- tests := []struct {
- path string
- expected string
- }{
{
- path: "/",
- expected: "/",
+ path: "/_internal_path",
+ internal: true,
+ proxy: &conf_v1.ActionProxy{
+ RewritePath: "/rewrite",
+ },
+ originalPath: "/path",
+ expected: []string{`^ $request_uri_no_args`, `"^/path(.*)$" "/rewrite$1" break`},
+ msg: "non-regex rewrite for internal location",
},
{
- path: "=/exact/match",
- expected: "=/exact/match",
+ path: "~/regex",
+ internal: true,
+ proxy: &conf_v1.ActionProxy{
+ RewritePath: "/rewrite",
+ },
+ originalPath: "/path",
+ expected: []string{`^ $request_uri_no_args`, `"^/path(.*)$" "/rewrite$1" break`},
+ msg: "regex rewrite for internal location",
},
{
- path: `~ *\\.jpg`,
- expected: `~ "*\\.jpg"`,
+ path: "~/regex",
+ internal: false,
+ proxy: &conf_v1.ActionProxy{
+ RewritePath: "/rewrite",
+ },
+ expected: []string{`"^/regex" "/rewrite" break`},
+ msg: "regex rewrite for non-internal location",
+ },
+ {
+ path: "/_internal_path",
+ internal: true,
+ proxy: &conf_v1.ActionProxy{
+ RewritePath: "/rewrite",
+ },
+ originalPath: "/path",
+ grpcEnabled: true,
+ expected: []string{`^ $request_uri_no_args`, `"^/path(.*)$" "/rewrite$1" break`},
+ msg: "non-regex rewrite for internal location with grpc enabled",
},
{
- path: `~* *\\.PNG`,
- expected: `~* "*\\.PNG"`,
+ path: "/_internal_path",
+ internal: true,
+ originalPath: "/path",
+ grpcEnabled: true,
+ expected: []string{`^ $request_uri break`},
+ msg: "empty rewrite for internal location with grpc enabled",
},
}
for _, test := range tests {
- result := generatePath(test.path)
- if result != test.expected {
- t.Errorf("generatePath() returned %v, but expected %v.", result, test.expected)
+ result := generateRewrites(test.path, test.proxy, test.internal, test.originalPath, test.grpcEnabled)
+ if diff := cmp.Diff(test.expected, result); diff != "" {
+ t.Errorf("generateRewrites() '%v' mismatch (-want +got):\n%s", test.msg, diff)
}
}
}
-func TestGenerateErrorPageName(t *testing.T) {
+func TestGenerateProxyPassRewrite(t *testing.T) {
t.Parallel()
tests := []struct {
- routeIndex int
- index int
- expected string
+ path string
+ proxy *conf_v1.ActionProxy
+ internal bool
+ expected string
}{
{
- 0,
- 0,
- "@error_page_0_0",
+ expected: "",
},
{
- 0,
- 1,
- "@error_page_0_1",
+ internal: true,
+ proxy: &conf_v1.ActionProxy{
+ RewritePath: "/rewrite",
+ },
+ expected: "",
},
{
- 1,
- 0,
- "@error_page_1_0",
+ path: "/path",
+ proxy: &conf_v1.ActionProxy{
+ RewritePath: "/rewrite",
+ },
+ expected: "/rewrite",
},
- }
-
- for _, test := range tests {
- result := generateErrorPageName(test.routeIndex, test.index)
- if result != test.expected {
- t.Errorf("generateErrorPageName(%v, %v) returned %v but expected %v", test.routeIndex, test.index, result, test.expected)
- }
- }
-}
-
-func TestGenerateErrorPageCodes(t *testing.T) {
- t.Parallel()
- tests := []struct {
- codes []int
- expected string
- }{
{
- codes: []int{400},
- expected: "400",
+ path: "=/path",
+ proxy: &conf_v1.ActionProxy{
+ RewritePath: "/rewrite",
+ },
+ expected: "/rewrite",
},
{
- codes: []int{404, 405, 502},
- expected: "404 405 502",
+ path: "~/path",
+ proxy: &conf_v1.ActionProxy{
+ RewritePath: "/rewrite",
+ },
+ expected: "",
},
}
for _, test := range tests {
- result := generateErrorPageCodes(test.codes)
+ result := generateProxyPassRewrite(test.path, test.proxy, test.internal)
if result != test.expected {
- t.Errorf("generateErrorPageCodes(%v) returned %v but expected %v", test.codes, result, test.expected)
+ t.Errorf("generateProxyPassRewrite(%v, %v, %v) returned %v but expected %v",
+ test.path, test.proxy, test.internal, result, test.expected)
}
}
}
-func TestGenerateErrorPages(t *testing.T) {
+func TestGenerateProxySetHeaders(t *testing.T) {
t.Parallel()
tests := []struct {
- upstreamName string
- errorPages []conf_v1.ErrorPage
- expected []version2.ErrorPage
+ proxy *conf_v1.ActionProxy
+ expected []version2.Header
+ msg string
}{
- {}, // empty errorPages
{
- "vs_test_test",
- []conf_v1.ErrorPage{
- {
- Codes: []int{404, 405, 500, 502},
- Return: &conf_v1.ErrorPageReturn{
- ActionReturn: conf_v1.ActionReturn{
- Code: 200,
+ proxy: nil,
+ expected: []version2.Header{{Name: "Host", Value: "$host"}},
+ msg: "no action proxy",
+ },
+ {
+ proxy: &conf_v1.ActionProxy{},
+ expected: []version2.Header{{Name: "Host", Value: "$host"}},
+ msg: "empty action proxy",
+ },
+ {
+ proxy: &conf_v1.ActionProxy{
+ RequestHeaders: &conf_v1.ProxyRequestHeaders{
+ Set: []conf_v1.Header{
+ {
+ Name: "Header-Name",
+ Value: "HeaderValue",
},
- Headers: nil,
},
- Redirect: nil,
},
},
- []version2.ErrorPage{
+ expected: []version2.Header{
{
- Name: "@error_page_1_0",
- Codes: "404 405 500 502",
- ResponseCode: 200,
+ Name: "Header-Name",
+ Value: "HeaderValue",
+ },
+ {
+ Name: "Host",
+ Value: "$host",
},
},
+ msg: "set headers without host",
},
{
- "vs_test_test",
- []conf_v1.ErrorPage{
- {
- Codes: []int{404, 405, 500, 502},
- Return: nil,
- Redirect: &conf_v1.ErrorPageRedirect{
- ActionRedirect: conf_v1.ActionRedirect{
- URL: "http://nginx.org",
- Code: 302,
+ proxy: &conf_v1.ActionProxy{
+ RequestHeaders: &conf_v1.ProxyRequestHeaders{
+ Set: []conf_v1.Header{
+ {
+ Name: "Header-Name",
+ Value: "HeaderValue",
+ },
+ {
+ Name: "Host",
+ Value: "example.com",
},
},
},
},
- []version2.ErrorPage{
+ expected: []version2.Header{
{
- Name: "http://nginx.org",
- Codes: "404 405 500 502",
- ResponseCode: 302,
+ Name: "Header-Name",
+ Value: "HeaderValue",
},
- },
- },
- }
-
- for i, test := range tests {
- result := generateErrorPages(i, test.errorPages)
- if !reflect.DeepEqual(result, test.expected) {
- t.Errorf("generateErrorPages(%v, %v) returned %v but expected %v", test.upstreamName, test.errorPages, result, test.expected)
- }
- }
-}
-
-func TestGenerateErrorPageLocations(t *testing.T) {
- t.Parallel()
- tests := []struct {
- upstreamName string
- errorPages []conf_v1.ErrorPage
- expected []version2.ErrorPageLocation
- }{
- {},
- {
- "vs_test_test",
- []conf_v1.ErrorPage{
{
- Codes: []int{404, 405, 500, 502},
- Return: nil,
- Redirect: &conf_v1.ErrorPageRedirect{
- ActionRedirect: conf_v1.ActionRedirect{
- URL: "http://nginx.org",
- Code: 302,
- },
- },
+ Name: "Host",
+ Value: "example.com",
},
},
- nil,
+ msg: "set headers with host capitalized",
},
{
- "vs_test_test",
- []conf_v1.ErrorPage{
- {
- Codes: []int{404, 405, 500, 502},
- Return: &conf_v1.ErrorPageReturn{
- ActionReturn: conf_v1.ActionReturn{
- Code: 200,
- Type: "application/json",
- Body: "Hello World",
+ proxy: &conf_v1.ActionProxy{
+ RequestHeaders: &conf_v1.ProxyRequestHeaders{
+ Set: []conf_v1.Header{
+ {
+ Name: "Header-Name",
+ Value: "HeaderValue",
},
- Headers: []conf_v1.Header{
- {
- Name: "HeaderName",
- Value: "HeaderValue",
- },
+ {
+ Name: "hoST",
+ Value: "example.com",
},
},
- Redirect: nil,
},
},
- []version2.ErrorPageLocation{
+ expected: []version2.Header{
{
- Name: "@error_page_2_0",
- DefaultType: "application/json",
- Return: &version2.Return{
- Code: 0,
- Text: "Hello World",
- },
- Headers: []version2.Header{
+ Name: "Header-Name",
+ Value: "HeaderValue",
+ },
+ {
+ Name: "hoST",
+ Value: "example.com",
+ },
+ },
+ msg: "set headers with host in mixed case",
+ },
+ {
+ proxy: &conf_v1.ActionProxy{
+ RequestHeaders: &conf_v1.ProxyRequestHeaders{
+ Set: []conf_v1.Header{
{
- Name: "HeaderName",
+ Name: "Header-Name",
Value: "HeaderValue",
},
+ {
+ Name: "Host",
+ Value: "one.example.com",
+ },
+ {
+ Name: "Host",
+ Value: "two.example.com",
+ },
},
},
},
+ expected: []version2.Header{
+ {
+ Name: "Header-Name",
+ Value: "HeaderValue",
+ },
+ {
+ Name: "Host",
+ Value: "one.example.com",
+ },
+ {
+ Name: "Host",
+ Value: "two.example.com",
+ },
+ },
+ msg: "set headers with multiple hosts",
},
}
- for i, test := range tests {
- result := generateErrorPageLocations(i, test.errorPages)
- if !reflect.DeepEqual(result, test.expected) {
- t.Errorf("generateErrorPageLocations(%v, %v) returned %v but expected %v", test.upstreamName, test.errorPages, result, test.expected)
+ for _, test := range tests {
+ result := generateProxySetHeaders(test.proxy)
+ if diff := cmp.Diff(test.expected, result); diff != "" {
+ t.Errorf("generateProxySetHeaders() '%v' mismatch (-want +got):\n%s", test.msg, diff)
}
}
}
-func TestGenerateProxySSLName(t *testing.T) {
- t.Parallel()
- result := generateProxySSLName("coffee-v1", "default")
- if result != "coffee-v1.default.svc" {
- t.Errorf("generateProxySSLName(coffee-v1, default) returned %v but expected coffee-v1.default.svc", result)
- }
-}
-
-func TestIsTLSEnabled(t *testing.T) {
+func TestGenerateProxyPassRequestHeaders(t *testing.T) {
t.Parallel()
+ passTrue := true
+ passFalse := false
tests := []struct {
- upstream conf_v1.Upstream
- spiffeCert bool
- nsmEgress bool
- expected bool
+ proxy *conf_v1.ActionProxy
+ expected bool
}{
{
- upstream: conf_v1.Upstream{
- TLS: conf_v1.UpstreamTLS{
- Enable: false,
- },
- },
- spiffeCert: false,
- expected: false,
+ proxy: nil,
+ expected: true,
},
{
- upstream: conf_v1.Upstream{
- TLS: conf_v1.UpstreamTLS{
- Enable: false,
- },
- },
- spiffeCert: true,
- expected: true,
+ proxy: &conf_v1.ActionProxy{},
+ expected: true,
},
{
- upstream: conf_v1.Upstream{
- TLS: conf_v1.UpstreamTLS{
- Enable: true,
+ proxy: &conf_v1.ActionProxy{
+ RequestHeaders: &conf_v1.ProxyRequestHeaders{
+ Pass: nil,
},
},
- spiffeCert: true,
- expected: true,
+ expected: true,
},
{
- upstream: conf_v1.Upstream{
- TLS: conf_v1.UpstreamTLS{
- Enable: true,
+ proxy: &conf_v1.ActionProxy{
+ RequestHeaders: &conf_v1.ProxyRequestHeaders{
+ Pass: &passTrue,
},
},
- spiffeCert: false,
- expected: true,
+ expected: true,
},
{
- upstream: conf_v1.Upstream{
- TLS: conf_v1.UpstreamTLS{
- Enable: true,
+ proxy: &conf_v1.ActionProxy{
+ RequestHeaders: &conf_v1.ProxyRequestHeaders{
+ Pass: &passFalse,
},
},
- nsmEgress: true,
- spiffeCert: false,
- expected: false,
+ expected: false,
},
}
for _, test := range tests {
- result := isTLSEnabled(test.upstream, test.spiffeCert, test.nsmEgress)
+ result := generateProxyPassRequestHeaders(test.proxy)
if result != test.expected {
- t.Errorf("isTLSEnabled(%v, %v) returned %v but expected %v", test.upstream, test.spiffeCert, result, test.expected)
+ t.Errorf("generateProxyPassRequestHeaders(%v) returned %v but expected %v", test.proxy, result, test.expected)
}
}
}
-func TestGenerateRewrites(t *testing.T) {
+func TestGenerateProxyHideHeaders(t *testing.T) {
t.Parallel()
tests := []struct {
- path string
- proxy *conf_v1.ActionProxy
- internal bool
- originalPath string
- grpcEnabled bool
- expected []string
- msg string
+ proxy *conf_v1.ActionProxy
+ expected []string
}{
{
proxy: nil,
expected: nil,
- msg: "action isn't proxy",
},
{
proxy: &conf_v1.ActionProxy{
- RewritePath: "",
+ ResponseHeaders: nil,
},
- expected: nil,
- msg: "no rewrite is configured",
},
{
- path: "/path",
proxy: &conf_v1.ActionProxy{
- RewritePath: "/rewrite",
+ ResponseHeaders: &conf_v1.ProxyResponseHeaders{
+ Hide: []string{"Header", "Header-2"},
+ },
},
+ expected: []string{"Header", "Header-2"},
+ },
+ }
+
+ for _, test := range tests {
+ result := generateProxyHideHeaders(test.proxy)
+ if !reflect.DeepEqual(result, test.expected) {
+ t.Errorf("generateProxyHideHeaders(%v) returned %v but expected %v", test.proxy, result, test.expected)
+ }
+ }
+}
+
+func TestGenerateProxyPassHeaders(t *testing.T) {
+ t.Parallel()
+ tests := []struct {
+ proxy *conf_v1.ActionProxy
+ expected []string
+ }{
+ {
+ proxy: nil,
expected: nil,
- msg: "non-regex rewrite for non-internal location is not needed",
},
{
- path: "/_internal_path",
- internal: true,
proxy: &conf_v1.ActionProxy{
- RewritePath: "/rewrite",
+ ResponseHeaders: nil,
},
- originalPath: "/path",
- expected: []string{`^ $request_uri_no_args`, `"^/path(.*)$" "/rewrite$1" break`},
- msg: "non-regex rewrite for internal location",
},
{
- path: "~/regex",
- internal: true,
proxy: &conf_v1.ActionProxy{
- RewritePath: "/rewrite",
+ ResponseHeaders: &conf_v1.ProxyResponseHeaders{
+ Pass: []string{"Header", "Header-2"},
+ },
},
- originalPath: "/path",
- expected: []string{`^ $request_uri_no_args`, `"^/path(.*)$" "/rewrite$1" break`},
- msg: "regex rewrite for internal location",
+ expected: []string{"Header", "Header-2"},
},
+ }
+
+ for _, test := range tests {
+ result := generateProxyPassHeaders(test.proxy)
+ if !reflect.DeepEqual(result, test.expected) {
+ t.Errorf("generateProxyPassHeaders(%v) returned %v but expected %v", test.proxy, result, test.expected)
+ }
+ }
+}
+
+func TestGenerateProxyIgnoreHeaders(t *testing.T) {
+ t.Parallel()
+ tests := []struct {
+ proxy *conf_v1.ActionProxy
+ expected string
+ }{
{
- path: "~/regex",
- internal: false,
- proxy: &conf_v1.ActionProxy{
- RewritePath: "/rewrite",
- },
- expected: []string{`"^/regex" "/rewrite" break`},
- msg: "regex rewrite for non-internal location",
+ proxy: nil,
+ expected: "",
},
{
- path: "/_internal_path",
- internal: true,
proxy: &conf_v1.ActionProxy{
- RewritePath: "/rewrite",
+ ResponseHeaders: nil,
},
- originalPath: "/path",
- grpcEnabled: true,
- expected: []string{`^ $request_uri_no_args`, `"^/path(.*)$" "/rewrite$1" break`},
- msg: "non-regex rewrite for internal location with grpc enabled",
+ expected: "",
},
{
- path: "/_internal_path",
- internal: true,
- originalPath: "/path",
- grpcEnabled: true,
- expected: []string{`^ $request_uri break`},
- msg: "empty rewrite for internal location with grpc enabled",
+ proxy: &conf_v1.ActionProxy{
+ ResponseHeaders: &conf_v1.ProxyResponseHeaders{
+ Ignore: []string{"Header", "Header-2"},
+ },
+ },
+ expected: "Header Header-2",
},
}
for _, test := range tests {
- result := generateRewrites(test.path, test.proxy, test.internal, test.originalPath, test.grpcEnabled)
- if diff := cmp.Diff(test.expected, result); diff != "" {
- t.Errorf("generateRewrites() '%v' mismatch (-want +got):\n%s", test.msg, diff)
+ result := generateProxyIgnoreHeaders(test.proxy)
+ if result != test.expected {
+ t.Errorf("generateProxyIgnoreHeaders(%v) returned %v but expected %v", test.proxy, result, test.expected)
}
}
}
-func TestGenerateProxyPassRewrite(t *testing.T) {
+func TestGenerateProxyAddHeaders(t *testing.T) {
t.Parallel()
tests := []struct {
- path string
proxy *conf_v1.ActionProxy
- internal bool
- expected string
+ expected []version2.AddHeader
}{
{
- expected: "",
+ proxy: nil,
+ expected: nil,
+ },
+ {
+ proxy: &conf_v1.ActionProxy{},
+ expected: nil,
+ },
+ {
+ proxy: &conf_v1.ActionProxy{
+ ResponseHeaders: &conf_v1.ProxyResponseHeaders{
+ Add: []conf_v1.AddHeader{
+ {
+ Header: conf_v1.Header{
+ Name: "Header-Name",
+ Value: "HeaderValue",
+ },
+ Always: true,
+ },
+ {
+ Header: conf_v1.Header{
+ Name: "Server",
+ Value: "myServer",
+ },
+ Always: false,
+ },
+ },
+ },
+ },
+ expected: []version2.AddHeader{
+ {
+ Header: version2.Header{
+ Name: "Header-Name",
+ Value: "HeaderValue",
+ },
+ Always: true,
+ },
+ {
+ Header: version2.Header{
+ Name: "Server",
+ Value: "myServer",
+ },
+ Always: false,
+ },
+ },
},
+ }
+
+ for _, test := range tests {
+ result := generateProxyAddHeaders(test.proxy)
+ if !reflect.DeepEqual(result, test.expected) {
+ t.Errorf("generateProxyAddHeaders(%v) returned %v but expected %v", test.proxy, result, test.expected)
+ }
+ }
+}
+
+func TestGetUpstreamResourceLabels(t *testing.T) {
+ t.Parallel()
+ tests := []struct {
+ owner runtime.Object
+ expected version2.UpstreamLabels
+ }{
{
- internal: true,
- proxy: &conf_v1.ActionProxy{
- RewritePath: "/rewrite",
- },
- expected: "",
+ owner: nil,
+ expected: version2.UpstreamLabels{},
},
{
- path: "/path",
- proxy: &conf_v1.ActionProxy{
- RewritePath: "/rewrite",
+ owner: &conf_v1.VirtualServer{
+ ObjectMeta: meta_v1.ObjectMeta{
+ Namespace: "namespace",
+ Name: "name",
+ },
},
- expected: "/rewrite",
- },
- {
- path: "=/path",
- proxy: &conf_v1.ActionProxy{
- RewritePath: "/rewrite",
+ expected: version2.UpstreamLabels{
+ ResourceNamespace: "namespace",
+ ResourceName: "name",
+ ResourceType: "virtualserver",
},
- expected: "/rewrite",
},
{
- path: "~/path",
- proxy: &conf_v1.ActionProxy{
- RewritePath: "/rewrite",
+ owner: &conf_v1.VirtualServerRoute{
+ ObjectMeta: meta_v1.ObjectMeta{
+ Namespace: "namespace",
+ Name: "name",
+ },
+ },
+ expected: version2.UpstreamLabels{
+ ResourceNamespace: "namespace",
+ ResourceName: "name",
+ ResourceType: "virtualserverroute",
},
- expected: "",
},
}
-
for _, test := range tests {
- result := generateProxyPassRewrite(test.path, test.proxy, test.internal)
- if result != test.expected {
- t.Errorf("generateProxyPassRewrite(%v, %v, %v) returned %v but expected %v",
- test.path, test.proxy, test.internal, result, test.expected)
+ result := getUpstreamResourceLabels(test.owner)
+ if !reflect.DeepEqual(result, test.expected) {
+ t.Errorf("getUpstreamResourceLabels(%+v) returned %+v but expected %+v", test.owner, result, test.expected)
}
}
}
-func TestGenerateProxySetHeaders(t *testing.T) {
+func TestAddWafConfig(t *testing.T) {
t.Parallel()
tests := []struct {
- proxy *conf_v1.ActionProxy
- expected []version2.Header
- msg string
+ wafInput *conf_v1.WAF
+ polKey string
+ polNamespace string
+ apResources *appProtectResourcesForVS
+ wafConfig *version2.WAF
+ expected *validationResults
+ msg string
}{
{
- proxy: nil,
- expected: []version2.Header{{Name: "Host", Value: "$host"}},
- msg: "no action proxy",
- },
- {
- proxy: &conf_v1.ActionProxy{},
- expected: []version2.Header{{Name: "Host", Value: "$host"}},
- msg: "empty action proxy",
- },
- {
- proxy: &conf_v1.ActionProxy{
- RequestHeaders: &conf_v1.ProxyRequestHeaders{
- Set: []conf_v1.Header{
- {
- Name: "Header-Name",
- Value: "HeaderValue",
- },
- },
- },
+ wafInput: &conf_v1.WAF{
+ Enable: true,
},
- expected: []version2.Header{
- {
- Name: "Header-Name",
- Value: "HeaderValue",
- },
- {
- Name: "Host",
- Value: "$host",
- },
+ polKey: "default/waf-policy",
+ polNamespace: "default",
+ apResources: &appProtectResourcesForVS{
+ Policies: map[string]string{},
+ LogConfs: map[string]string{},
},
- msg: "set headers without host",
+ wafConfig: &version2.WAF{
+ Enable: "on",
+ },
+ expected: &validationResults{isError: false},
+ msg: "valid waf config, default App Protect config",
},
{
- proxy: &conf_v1.ActionProxy{
- RequestHeaders: &conf_v1.ProxyRequestHeaders{
- Set: []conf_v1.Header{
- {
- Name: "Header-Name",
- Value: "HeaderValue",
- },
- {
- Name: "Host",
- Value: "example.com",
- },
- },
+ wafInput: &conf_v1.WAF{
+ Enable: true,
+ ApPolicy: "dataguard-alarm",
+ SecurityLog: &conf_v1.SecurityLog{
+ Enable: true,
+ ApLogConf: "logconf",
+ LogDest: "syslog:server=127.0.0.1:514",
},
},
- expected: []version2.Header{
- {
- Name: "Header-Name",
- Value: "HeaderValue",
+ polKey: "default/waf-policy",
+ polNamespace: "default",
+ apResources: &appProtectResourcesForVS{
+ Policies: map[string]string{
+ "default/dataguard-alarm": "/etc/nginx/waf/nac-policies/default-dataguard-alarm",
},
- {
- Name: "Host",
- Value: "example.com",
+ LogConfs: map[string]string{
+ "default/logconf": "/etc/nginx/waf/nac-logconfs/default-logconf",
},
},
- msg: "set headers with host capitalized",
+ wafConfig: &version2.WAF{
+ ApPolicy: "/etc/nginx/waf/nac-policies/default-dataguard-alarm",
+ ApSecurityLogEnable: true,
+ ApLogConf: []string{"/etc/nginx/waf/nac-logconfs/default-logconf"},
+ },
+ expected: &validationResults{isError: false},
+ msg: "valid waf config",
},
{
- proxy: &conf_v1.ActionProxy{
- RequestHeaders: &conf_v1.ProxyRequestHeaders{
- Set: []conf_v1.Header{
- {
- Name: "Header-Name",
- Value: "HeaderValue",
- },
- {
- Name: "hoST",
- Value: "example.com",
- },
+ wafInput: &conf_v1.WAF{
+ Enable: true,
+ ApPolicy: "dataguard-alarm",
+ SecurityLogs: []*conf_v1.SecurityLog{
+ {
+ Enable: true,
+ ApLogConf: "logconf",
+ LogDest: "syslog:server=127.0.0.1:514",
},
},
},
- expected: []version2.Header{
- {
- Name: "Header-Name",
- Value: "HeaderValue",
+ polKey: "default/waf-policy",
+ polNamespace: "default",
+ apResources: &appProtectResourcesForVS{
+ Policies: map[string]string{
+ "default/dataguard-alarm": "/etc/nginx/waf/nac-policies/default-dataguard-alarm",
},
- {
- Name: "hoST",
- Value: "example.com",
+ LogConfs: map[string]string{
+ "default/logconf": "/etc/nginx/waf/nac-logconfs/default-logconf",
},
},
- msg: "set headers with host in mixed case",
+ wafConfig: &version2.WAF{
+ ApPolicy: "/etc/nginx/waf/nac-policies/default-dataguard-alarm",
+ ApSecurityLogEnable: true,
+ ApLogConf: []string{"/etc/nginx/waf/nac-logconfs/default-logconf"},
+ },
+ expected: &validationResults{isError: false},
+ msg: "valid waf config",
},
{
- proxy: &conf_v1.ActionProxy{
- RequestHeaders: &conf_v1.ProxyRequestHeaders{
- Set: []conf_v1.Header{
- {
- Name: "Header-Name",
- Value: "HeaderValue",
- },
- {
- Name: "Host",
- Value: "one.example.com",
- },
- {
- Name: "Host",
- Value: "two.example.com",
- },
- },
+ wafInput: &conf_v1.WAF{
+ Enable: true,
+ ApPolicy: "default/dataguard-alarm",
+ SecurityLog: &conf_v1.SecurityLog{
+ Enable: true,
+ ApLogConf: "default/logconf",
+ LogDest: "syslog:server=127.0.0.1:514",
},
},
- expected: []version2.Header{
- {
- Name: "Header-Name",
- Value: "HeaderValue",
- },
- {
- Name: "Host",
- Value: "one.example.com",
- },
- {
- Name: "Host",
- Value: "two.example.com",
+ polKey: "default/waf-policy",
+ polNamespace: "",
+ apResources: &appProtectResourcesForVS{
+ Policies: map[string]string{
+ "default/dataguard-alarm": "/etc/nginx/waf/nac-policies/default-dataguard-alarm",
},
+ LogConfs: map[string]string{},
},
- msg: "set headers with multiple hosts",
- },
- }
-
- for _, test := range tests {
- result := generateProxySetHeaders(test.proxy)
- if diff := cmp.Diff(test.expected, result); diff != "" {
- t.Errorf("generateProxySetHeaders() '%v' mismatch (-want +got):\n%s", test.msg, diff)
- }
- }
-}
-
-func TestGenerateProxyPassRequestHeaders(t *testing.T) {
- t.Parallel()
- passTrue := true
- passFalse := false
- tests := []struct {
- proxy *conf_v1.ActionProxy
- expected bool
- }{
- {
- proxy: nil,
- expected: true,
- },
- {
- proxy: &conf_v1.ActionProxy{},
- expected: true,
+ wafConfig: &version2.WAF{
+ ApPolicy: "/etc/nginx/waf/nac-policies/default-dataguard-alarm",
+ ApSecurityLogEnable: true,
+ ApLogConf: []string{"/etc/nginx/waf/nac-logconfs/default-logconf"},
+ },
+ expected: &validationResults{
+ isError: true,
+ warnings: []string{
+ `WAF policy default/waf-policy references an invalid or non-existing log config default/logconf`,
+ },
+ },
+ msg: "invalid waf config, apLogConf references non-existing log conf",
},
{
- proxy: &conf_v1.ActionProxy{
- RequestHeaders: &conf_v1.ProxyRequestHeaders{
- Pass: nil,
+ wafInput: &conf_v1.WAF{
+ Enable: true,
+ ApPolicy: "default/dataguard-alarm",
+ SecurityLog: &conf_v1.SecurityLog{
+ Enable: true,
+ LogDest: "syslog:server=127.0.0.1:514",
},
},
- expected: true,
+ polKey: "default/waf-policy",
+ polNamespace: "",
+ apResources: &appProtectResourcesForVS{
+ Policies: map[string]string{},
+ LogConfs: map[string]string{
+ "default/logconf": "/etc/nginx/waf/nac-logconfs/default-logconf",
+ },
+ },
+ wafConfig: &version2.WAF{
+ ApPolicy: "/etc/nginx/waf/nac-policies/default-dataguard-alarm",
+ ApSecurityLogEnable: true,
+ ApLogConf: []string{"/etc/nginx/waf/nac-logconfs/default-logconf"},
+ },
+ expected: &validationResults{
+ isError: true,
+ warnings: []string{
+ `WAF policy default/waf-policy references an invalid or non-existing App Protect policy default/dataguard-alarm`,
+ },
+ },
+ msg: "invalid waf config, apLogConf references non-existing ap conf",
},
{
- proxy: &conf_v1.ActionProxy{
- RequestHeaders: &conf_v1.ProxyRequestHeaders{
- Pass: &passTrue,
+ wafInput: &conf_v1.WAF{
+ Enable: true,
+ ApPolicy: "ns1/dataguard-alarm",
+ SecurityLog: &conf_v1.SecurityLog{
+ Enable: true,
+ ApLogConf: "ns2/logconf",
+ LogDest: "syslog:server=127.0.0.1:514",
},
},
- expected: true,
+ polKey: "default/waf-policy",
+ polNamespace: "",
+ apResources: &appProtectResourcesForVS{
+ Policies: map[string]string{
+ "ns1/dataguard-alarm": "/etc/nginx/waf/nac-policies/ns1-dataguard-alarm",
+ },
+ LogConfs: map[string]string{
+ "ns2/logconf": "/etc/nginx/waf/nac-logconfs/ns2-logconf",
+ },
+ },
+ wafConfig: &version2.WAF{
+ ApPolicy: "/etc/nginx/waf/nac-policies/ns1-dataguard-alarm",
+ ApSecurityLogEnable: true,
+ ApLogConf: []string{"/etc/nginx/waf/nac-logconfs/ns2-logconf"},
+ },
+ expected: &validationResults{},
+ msg: "valid waf config, cross ns reference",
},
{
- proxy: &conf_v1.ActionProxy{
- RequestHeaders: &conf_v1.ProxyRequestHeaders{
- Pass: &passFalse,
+ wafInput: &conf_v1.WAF{
+ Enable: false,
+ ApPolicy: "dataguard-alarm",
+ },
+ polKey: "default/waf-policy",
+ polNamespace: "default",
+ apResources: &appProtectResourcesForVS{
+ Policies: map[string]string{
+ "default/dataguard-alarm": "/etc/nginx/waf/nac-policies/ns1-dataguard-alarm",
+ },
+ LogConfs: map[string]string{
+ "default/logconf": "/etc/nginx/waf/nac-logconfs/ns2-logconf",
},
},
- expected: false,
+ wafConfig: &version2.WAF{
+ Enable: "off",
+ ApPolicy: "/etc/nginx/waf/nac-policies/ns1-dataguard-alarm",
+ },
+ expected: &validationResults{},
+ msg: "valid waf config, disable waf",
},
}
for _, test := range tests {
- result := generateProxyPassRequestHeaders(test.proxy)
- if result != test.expected {
- t.Errorf("generateProxyPassRequestHeaders(%v) returned %v but expected %v", test.proxy, result, test.expected)
+ polCfg := newPoliciesConfig()
+ result := polCfg.addWAFConfig(test.wafInput, test.polKey, test.polNamespace, test.apResources)
+ if diff := cmp.Diff(test.expected.warnings, result.warnings); diff != "" {
+ t.Errorf("policiesCfg.addWAFConfig() '%v' mismatch (-want +got):\n%s", test.msg, diff)
}
}
}
-func TestGenerateProxyHideHeaders(t *testing.T) {
+func TestGenerateTime(t *testing.T) {
t.Parallel()
tests := []struct {
- proxy *conf_v1.ActionProxy
- expected []string
+ value, expected string
}{
{
- proxy: nil,
- expected: nil,
+ value: "0s",
+ expected: "0s",
},
{
- proxy: &conf_v1.ActionProxy{
- ResponseHeaders: nil,
- },
+ value: "0",
+ expected: "0s",
},
{
- proxy: &conf_v1.ActionProxy{
- ResponseHeaders: &conf_v1.ProxyResponseHeaders{
- Hide: []string{"Header", "Header-2"},
- },
- },
- expected: []string{"Header", "Header-2"},
+ value: "1h",
+ expected: "1h",
+ },
+ {
+ value: "1h 30m",
+ expected: "1h30m",
},
}
for _, test := range tests {
- result := generateProxyHideHeaders(test.proxy)
- if !reflect.DeepEqual(result, test.expected) {
- t.Errorf("generateProxyHideHeaders(%v) returned %v but expected %v", test.proxy, result, test.expected)
+ result := generateTime(test.value)
+ if result != test.expected {
+ t.Errorf("generateTime(%q) returned %q but expected %q", test.value, result, test.expected)
}
}
}
-func TestGenerateProxyPassHeaders(t *testing.T) {
+func TestGenerateTimeWithDefault(t *testing.T) {
t.Parallel()
tests := []struct {
- proxy *conf_v1.ActionProxy
- expected []string
+ value, defaultValue, expected string
}{
{
- proxy: nil,
- expected: nil,
+ value: "1h 30m",
+ defaultValue: "",
+ expected: "1h30m",
},
{
- proxy: &conf_v1.ActionProxy{
- ResponseHeaders: nil,
- },
+ value: "",
+ defaultValue: "60s",
+ expected: "60s",
},
{
- proxy: &conf_v1.ActionProxy{
- ResponseHeaders: &conf_v1.ProxyResponseHeaders{
- Pass: []string{"Header", "Header-2"},
- },
- },
- expected: []string{"Header", "Header-2"},
+ value: "",
+ defaultValue: "test",
+ expected: "test",
},
}
for _, test := range tests {
- result := generateProxyPassHeaders(test.proxy)
- if !reflect.DeepEqual(result, test.expected) {
- t.Errorf("generateProxyPassHeaders(%v) returned %v but expected %v", test.proxy, result, test.expected)
+ result := generateTimeWithDefault(test.value, test.defaultValue)
+ if result != test.expected {
+ t.Errorf("generateTimeWithDefault(%q, %q) returned %q but expected %q", test.value, test.defaultValue, result, test.expected)
}
}
}
-func TestGenerateProxyIgnoreHeaders(t *testing.T) {
- t.Parallel()
- tests := []struct {
- proxy *conf_v1.ActionProxy
- expected string
- }{
- {
- proxy: nil,
- expected: "",
- },
- {
- proxy: &conf_v1.ActionProxy{
- ResponseHeaders: nil,
+var (
+ baseCfgParams = ConfigParams{
+ ServerTokens: "off",
+ Keepalive: 16,
+ ServerSnippets: []string{"# server snippet"},
+ ProxyProtocol: true,
+ SetRealIPFrom: []string{"0.0.0.0/0"},
+ RealIPHeader: "X-Real-IP",
+ RealIPRecursive: true,
+ }
+
+ virtualServerExWithGunzipOn = VirtualServerEx{
+ VirtualServer: &conf_v1.VirtualServer{
+ ObjectMeta: meta_v1.ObjectMeta{
+ Name: "cafe",
+ Namespace: "default",
},
- expected: "",
- },
- {
- proxy: &conf_v1.ActionProxy{
- ResponseHeaders: &conf_v1.ProxyResponseHeaders{
- Ignore: []string{"Header", "Header-2"},
+ Spec: conf_v1.VirtualServerSpec{
+ Host: "cafe.example.com",
+ Gunzip: true,
+ Upstreams: []conf_v1.Upstream{
+ {
+ Name: "tea",
+ Service: "tea-svc",
+ Port: 80,
+ },
+ {
+ Name: "tea-latest",
+ Service: "tea-svc",
+ Subselector: map[string]string{"version": "v1"},
+ Port: 80,
+ },
+ {
+ Name: "coffee",
+ Service: "coffee-svc",
+ Port: 80,
+ },
+ },
+ Routes: []conf_v1.Route{
+ {
+ Path: "/tea",
+ Action: &conf_v1.Action{
+ Pass: "tea",
+ },
+ },
+ {
+ Path: "/tea-latest",
+ Action: &conf_v1.Action{
+ Pass: "tea-latest",
+ },
+ },
+ {
+ Path: "/coffee",
+ Route: "default/coffee",
+ },
+ {
+ Path: "/subtea",
+ Route: "default/subtea",
+ },
+ {
+ Path: "/coffee-errorpage",
+ Action: &conf_v1.Action{
+ Pass: "coffee",
+ },
+ ErrorPages: []conf_v1.ErrorPage{
+ {
+ Codes: []int{401, 403},
+ Redirect: &conf_v1.ErrorPageRedirect{
+ ActionRedirect: conf_v1.ActionRedirect{
+ URL: "http://nginx.com",
+ Code: 301,
+ },
+ },
+ },
+ },
+ },
+ {
+ Path: "/coffee-errorpage-subroute",
+ Route: "default/subcoffee",
+ ErrorPages: []conf_v1.ErrorPage{
+ {
+ Codes: []int{401, 403},
+ Redirect: &conf_v1.ErrorPageRedirect{
+ ActionRedirect: conf_v1.ActionRedirect{
+ URL: "http://nginx.com",
+ Code: 301,
+ },
+ },
+ },
+ },
+ },
},
},
- expected: "Header Header-2",
- },
- }
-
- for _, test := range tests {
- result := generateProxyIgnoreHeaders(test.proxy)
- if result != test.expected {
- t.Errorf("generateProxyIgnoreHeaders(%v) returned %v but expected %v", test.proxy, result, test.expected)
- }
- }
-}
-
-func TestGenerateProxyAddHeaders(t *testing.T) {
- t.Parallel()
- tests := []struct {
- proxy *conf_v1.ActionProxy
- expected []version2.AddHeader
- }{
- {
- proxy: nil,
- expected: nil,
},
- {
- proxy: &conf_v1.ActionProxy{},
- expected: nil,
+ Endpoints: map[string][]string{
+ "default/tea-svc:80": {
+ "10.0.0.20:80",
+ },
+ "default/tea-svc_version=v1:80": {
+ "10.0.0.30:80",
+ },
+ "default/coffee-svc:80": {
+ "10.0.0.40:80",
+ },
+ "default/sub-tea-svc_version=v1:80": {
+ "10.0.0.50:80",
+ },
},
- {
- proxy: &conf_v1.ActionProxy{
- ResponseHeaders: &conf_v1.ProxyResponseHeaders{
- Add: []conf_v1.AddHeader{
+ VirtualServerRoutes: []*conf_v1.VirtualServerRoute{
+ {
+ ObjectMeta: meta_v1.ObjectMeta{
+ Name: "coffee",
+ Namespace: "default",
+ },
+ Spec: conf_v1.VirtualServerRouteSpec{
+ Host: "cafe.example.com",
+ Upstreams: []conf_v1.Upstream{
{
- Header: conf_v1.Header{
- Name: "Header-Name",
- Value: "HeaderValue",
- },
- Always: true,
+ Name: "coffee",
+ Service: "coffee-svc",
+ Port: 80,
},
+ },
+ Subroutes: []conf_v1.Route{
{
- Header: conf_v1.Header{
- Name: "Server",
- Value: "myServer",
+ Path: "/coffee",
+ Action: &conf_v1.Action{
+ Pass: "coffee",
},
- Always: false,
},
},
},
},
- expected: []version2.AddHeader{
- {
- Header: version2.Header{
- Name: "Header-Name",
- Value: "HeaderValue",
+ {
+ ObjectMeta: meta_v1.ObjectMeta{
+ Name: "subtea",
+ Namespace: "default",
+ },
+ Spec: conf_v1.VirtualServerRouteSpec{
+ Host: "cafe.example.com",
+ Upstreams: []conf_v1.Upstream{
+ {
+ Name: "subtea",
+ Service: "sub-tea-svc",
+ Port: 80,
+ Subselector: map[string]string{"version": "v1"},
+ },
+ },
+ Subroutes: []conf_v1.Route{
+ {
+ Path: "/subtea",
+ Action: &conf_v1.Action{
+ Pass: "subtea",
+ },
+ },
},
- Always: true,
},
- {
- Header: version2.Header{
- Name: "Server",
- Value: "myServer",
+ },
+ {
+ ObjectMeta: meta_v1.ObjectMeta{
+ Name: "subcoffee",
+ Namespace: "default",
+ },
+ Spec: conf_v1.VirtualServerRouteSpec{
+ Host: "cafe.example.com",
+ Upstreams: []conf_v1.Upstream{
+ {
+ Name: "coffee",
+ Service: "coffee-svc",
+ Port: 80,
+ },
+ },
+ Subroutes: []conf_v1.Route{
+ {
+ Path: "/coffee-errorpage-subroute",
+ Action: &conf_v1.Action{
+ Pass: "coffee",
+ },
+ },
+ {
+ Path: "/coffee-errorpage-subroute-defined",
+ Action: &conf_v1.Action{
+ Pass: "coffee",
+ },
+ ErrorPages: []conf_v1.ErrorPage{
+ {
+ Codes: []int{502, 503},
+ Return: &conf_v1.ErrorPageReturn{
+ ActionReturn: conf_v1.ActionReturn{
+ Code: 200,
+ Type: "text/plain",
+ Body: "All Good",
+ },
+ },
+ },
+ },
+ },
},
- Always: false,
},
},
},
}
- for _, test := range tests {
- result := generateProxyAddHeaders(test.proxy)
- if !reflect.DeepEqual(result, test.expected) {
- t.Errorf("generateProxyAddHeaders(%v) returned %v but expected %v", test.proxy, result, test.expected)
- }
- }
-}
-
-func TestGetUpstreamResourceLabels(t *testing.T) {
- t.Parallel()
- tests := []struct {
- owner runtime.Object
- expected version2.UpstreamLabels
- }{
- {
- owner: nil,
- expected: version2.UpstreamLabels{},
- },
- {
- owner: &conf_v1.VirtualServer{
- ObjectMeta: meta_v1.ObjectMeta{
- Namespace: "namespace",
- Name: "name",
- },
- },
- expected: version2.UpstreamLabels{
- ResourceNamespace: "namespace",
- ResourceName: "name",
- ResourceType: "virtualserver",
+ virtualServerExWithGunzipOff = VirtualServerEx{
+ VirtualServer: &conf_v1.VirtualServer{
+ ObjectMeta: meta_v1.ObjectMeta{
+ Name: "cafe",
+ Namespace: "default",
},
- },
- {
- owner: &conf_v1.VirtualServerRoute{
- ObjectMeta: meta_v1.ObjectMeta{
- Namespace: "namespace",
- Name: "name",
+ Spec: conf_v1.VirtualServerSpec{
+ Host: "cafe.example.com",
+ Gunzip: false,
+ Upstreams: []conf_v1.Upstream{
+ {
+ Name: "tea",
+ Service: "tea-svc",
+ Port: 80,
+ },
+ {
+ Name: "tea-latest",
+ Service: "tea-svc",
+ Subselector: map[string]string{"version": "v1"},
+ Port: 80,
+ },
+ {
+ Name: "coffee",
+ Service: "coffee-svc",
+ Port: 80,
+ },
+ },
+ Routes: []conf_v1.Route{
+ {
+ Path: "/tea",
+ Action: &conf_v1.Action{
+ Pass: "tea",
+ },
+ },
+ {
+ Path: "/tea-latest",
+ Action: &conf_v1.Action{
+ Pass: "tea-latest",
+ },
+ },
+ {
+ Path: "/coffee",
+ Route: "default/coffee",
+ },
+ {
+ Path: "/subtea",
+ Route: "default/subtea",
+ },
+ {
+ Path: "/coffee-errorpage",
+ Action: &conf_v1.Action{
+ Pass: "coffee",
+ },
+ ErrorPages: []conf_v1.ErrorPage{
+ {
+ Codes: []int{401, 403},
+ Redirect: &conf_v1.ErrorPageRedirect{
+ ActionRedirect: conf_v1.ActionRedirect{
+ URL: "http://nginx.com",
+ Code: 301,
+ },
+ },
+ },
+ },
+ },
+ {
+ Path: "/coffee-errorpage-subroute",
+ Route: "default/subcoffee",
+ ErrorPages: []conf_v1.ErrorPage{
+ {
+ Codes: []int{401, 403},
+ Redirect: &conf_v1.ErrorPageRedirect{
+ ActionRedirect: conf_v1.ActionRedirect{
+ URL: "http://nginx.com",
+ Code: 301,
+ },
+ },
+ },
+ },
+ },
},
},
- expected: version2.UpstreamLabels{
- ResourceNamespace: "namespace",
- ResourceName: "name",
- ResourceType: "virtualserverroute",
- },
},
- }
- for _, test := range tests {
- result := getUpstreamResourceLabels(test.owner)
- if !reflect.DeepEqual(result, test.expected) {
- t.Errorf("getUpstreamResourceLabels(%+v) returned %+v but expected %+v", test.owner, result, test.expected)
- }
- }
-}
-
-func TestAddWafConfig(t *testing.T) {
- t.Parallel()
- tests := []struct {
- wafInput *conf_v1.WAF
- polKey string
- polNamespace string
- apResources *appProtectResourcesForVS
- wafConfig *version2.WAF
- expected *validationResults
- msg string
- }{
- {
- wafInput: &conf_v1.WAF{
- Enable: true,
+ Endpoints: map[string][]string{
+ "default/tea-svc:80": {
+ "10.0.0.20:80",
},
- polKey: "default/waf-policy",
- polNamespace: "default",
- apResources: &appProtectResourcesForVS{
- Policies: map[string]string{},
- LogConfs: map[string]string{},
+ "default/tea-svc_version=v1:80": {
+ "10.0.0.30:80",
},
- wafConfig: &version2.WAF{
- Enable: "on",
+ "default/coffee-svc:80": {
+ "10.0.0.40:80",
},
- expected: &validationResults{isError: false},
- msg: "valid waf config, default App Protect config",
- },
- {
- wafInput: &conf_v1.WAF{
- Enable: true,
- ApPolicy: "dataguard-alarm",
- SecurityLog: &conf_v1.SecurityLog{
- Enable: true,
- ApLogConf: "logconf",
- LogDest: "syslog:server=127.0.0.1:514",
- },
+ "default/sub-tea-svc_version=v1:80": {
+ "10.0.0.50:80",
},
- polKey: "default/waf-policy",
- polNamespace: "default",
- apResources: &appProtectResourcesForVS{
- Policies: map[string]string{
- "default/dataguard-alarm": "/etc/nginx/waf/nac-policies/default-dataguard-alarm",
+ },
+ VirtualServerRoutes: []*conf_v1.VirtualServerRoute{
+ {
+ ObjectMeta: meta_v1.ObjectMeta{
+ Name: "coffee",
+ Namespace: "default",
},
- LogConfs: map[string]string{
- "default/logconf": "/etc/nginx/waf/nac-logconfs/default-logconf",
+ Spec: conf_v1.VirtualServerRouteSpec{
+ Host: "cafe.example.com",
+ Upstreams: []conf_v1.Upstream{
+ {
+ Name: "coffee",
+ Service: "coffee-svc",
+ Port: 80,
+ },
+ },
+ Subroutes: []conf_v1.Route{
+ {
+ Path: "/coffee",
+ Action: &conf_v1.Action{
+ Pass: "coffee",
+ },
+ },
+ },
},
},
- wafConfig: &version2.WAF{
- ApPolicy: "/etc/nginx/waf/nac-policies/default-dataguard-alarm",
- ApSecurityLogEnable: true,
- ApLogConf: []string{"/etc/nginx/waf/nac-logconfs/default-logconf"},
- },
- expected: &validationResults{isError: false},
- msg: "valid waf config",
- },
- {
- wafInput: &conf_v1.WAF{
- Enable: true,
- ApPolicy: "dataguard-alarm",
- SecurityLogs: []*conf_v1.SecurityLog{
- {
- Enable: true,
- ApLogConf: "logconf",
- LogDest: "syslog:server=127.0.0.1:514",
+ {
+ ObjectMeta: meta_v1.ObjectMeta{
+ Name: "subtea",
+ Namespace: "default",
+ },
+ Spec: conf_v1.VirtualServerRouteSpec{
+ Host: "cafe.example.com",
+ Upstreams: []conf_v1.Upstream{
+ {
+ Name: "subtea",
+ Service: "sub-tea-svc",
+ Port: 80,
+ Subselector: map[string]string{"version": "v1"},
+ },
+ },
+ Subroutes: []conf_v1.Route{
+ {
+ Path: "/subtea",
+ Action: &conf_v1.Action{
+ Pass: "subtea",
+ },
+ },
},
},
},
- polKey: "default/waf-policy",
- polNamespace: "default",
- apResources: &appProtectResourcesForVS{
- Policies: map[string]string{
- "default/dataguard-alarm": "/etc/nginx/waf/nac-policies/default-dataguard-alarm",
+ {
+ ObjectMeta: meta_v1.ObjectMeta{
+ Name: "subcoffee",
+ Namespace: "default",
},
- LogConfs: map[string]string{
- "default/logconf": "/etc/nginx/waf/nac-logconfs/default-logconf",
+ Spec: conf_v1.VirtualServerRouteSpec{
+ Host: "cafe.example.com",
+ Upstreams: []conf_v1.Upstream{
+ {
+ Name: "coffee",
+ Service: "coffee-svc",
+ Port: 80,
+ },
+ },
+ Subroutes: []conf_v1.Route{
+ {
+ Path: "/coffee-errorpage-subroute",
+ Action: &conf_v1.Action{
+ Pass: "coffee",
+ },
+ },
+ {
+ Path: "/coffee-errorpage-subroute-defined",
+ Action: &conf_v1.Action{
+ Pass: "coffee",
+ },
+ ErrorPages: []conf_v1.ErrorPage{
+ {
+ Codes: []int{502, 503},
+ Return: &conf_v1.ErrorPageReturn{
+ ActionReturn: conf_v1.ActionReturn{
+ Code: 200,
+ Type: "text/plain",
+ Body: "All Good",
+ },
+ },
+ },
+ },
+ },
+ },
},
},
- wafConfig: &version2.WAF{
- ApPolicy: "/etc/nginx/waf/nac-policies/default-dataguard-alarm",
- ApSecurityLogEnable: true,
- ApLogConf: []string{"/etc/nginx/waf/nac-logconfs/default-logconf"},
- },
- expected: &validationResults{isError: false},
- msg: "valid waf config",
},
- {
- wafInput: &conf_v1.WAF{
- Enable: true,
- ApPolicy: "default/dataguard-alarm",
- SecurityLog: &conf_v1.SecurityLog{
- Enable: true,
- ApLogConf: "default/logconf",
- LogDest: "syslog:server=127.0.0.1:514",
- },
+ }
+
+ virtualServerExWithNoGunzip = VirtualServerEx{
+ VirtualServer: &conf_v1.VirtualServer{
+ ObjectMeta: meta_v1.ObjectMeta{
+ Name: "cafe",
+ Namespace: "default",
},
- polKey: "default/waf-policy",
- polNamespace: "",
- apResources: &appProtectResourcesForVS{
- Policies: map[string]string{
- "default/dataguard-alarm": "/etc/nginx/waf/nac-policies/default-dataguard-alarm",
+ Spec: conf_v1.VirtualServerSpec{
+ Host: "cafe.example.com",
+ Upstreams: []conf_v1.Upstream{
+ {
+ Name: "tea",
+ Service: "tea-svc",
+ Port: 80,
+ },
+ {
+ Name: "tea-latest",
+ Service: "tea-svc",
+ Subselector: map[string]string{"version": "v1"},
+ Port: 80,
+ },
+ {
+ Name: "coffee",
+ Service: "coffee-svc",
+ Port: 80,
+ },
},
- LogConfs: map[string]string{},
- },
- wafConfig: &version2.WAF{
- ApPolicy: "/etc/nginx/waf/nac-policies/default-dataguard-alarm",
- ApSecurityLogEnable: true,
- ApLogConf: []string{"/etc/nginx/waf/nac-logconfs/default-logconf"},
- },
- expected: &validationResults{
- isError: true,
- warnings: []string{
- `WAF policy default/waf-policy references an invalid or non-existing log config default/logconf`,
+ Routes: []conf_v1.Route{
+ {
+ Path: "/tea",
+ Action: &conf_v1.Action{
+ Pass: "tea",
+ },
+ },
+ {
+ Path: "/tea-latest",
+ Action: &conf_v1.Action{
+ Pass: "tea-latest",
+ },
+ },
+ {
+ Path: "/coffee",
+ Route: "default/coffee",
+ },
+ {
+ Path: "/subtea",
+ Route: "default/subtea",
+ },
+ {
+ Path: "/coffee-errorpage",
+ Action: &conf_v1.Action{
+ Pass: "coffee",
+ },
+ ErrorPages: []conf_v1.ErrorPage{
+ {
+ Codes: []int{401, 403},
+ Redirect: &conf_v1.ErrorPageRedirect{
+ ActionRedirect: conf_v1.ActionRedirect{
+ URL: "http://nginx.com",
+ Code: 301,
+ },
+ },
+ },
+ },
+ },
+ {
+ Path: "/coffee-errorpage-subroute",
+ Route: "default/subcoffee",
+ ErrorPages: []conf_v1.ErrorPage{
+ {
+ Codes: []int{401, 403},
+ Redirect: &conf_v1.ErrorPageRedirect{
+ ActionRedirect: conf_v1.ActionRedirect{
+ URL: "http://nginx.com",
+ Code: 301,
+ },
+ },
+ },
+ },
+ },
},
},
- msg: "invalid waf config, apLogConf references non-existing log conf",
},
- {
- wafInput: &conf_v1.WAF{
- Enable: true,
- ApPolicy: "default/dataguard-alarm",
- SecurityLog: &conf_v1.SecurityLog{
- Enable: true,
- LogDest: "syslog:server=127.0.0.1:514",
- },
+ Endpoints: map[string][]string{
+ "default/tea-svc:80": {
+ "10.0.0.20:80",
},
- polKey: "default/waf-policy",
- polNamespace: "",
- apResources: &appProtectResourcesForVS{
- Policies: map[string]string{},
- LogConfs: map[string]string{
- "default/logconf": "/etc/nginx/waf/nac-logconfs/default-logconf",
- },
+ "default/tea-svc_version=v1:80": {
+ "10.0.0.30:80",
},
- wafConfig: &version2.WAF{
- ApPolicy: "/etc/nginx/waf/nac-policies/default-dataguard-alarm",
- ApSecurityLogEnable: true,
- ApLogConf: []string{"/etc/nginx/waf/nac-logconfs/default-logconf"},
+ "default/coffee-svc:80": {
+ "10.0.0.40:80",
},
- expected: &validationResults{
- isError: true,
- warnings: []string{
- `WAF policy default/waf-policy references an invalid or non-existing App Protect policy default/dataguard-alarm`,
- },
+ "default/sub-tea-svc_version=v1:80": {
+ "10.0.0.50:80",
},
- msg: "invalid waf config, apLogConf references non-existing ap conf",
},
- {
- wafInput: &conf_v1.WAF{
- Enable: true,
- ApPolicy: "ns1/dataguard-alarm",
- SecurityLog: &conf_v1.SecurityLog{
- Enable: true,
- ApLogConf: "ns2/logconf",
- LogDest: "syslog:server=127.0.0.1:514",
+ VirtualServerRoutes: []*conf_v1.VirtualServerRoute{
+ {
+ ObjectMeta: meta_v1.ObjectMeta{
+ Name: "coffee",
+ Namespace: "default",
+ },
+ Spec: conf_v1.VirtualServerRouteSpec{
+ Host: "cafe.example.com",
+ Upstreams: []conf_v1.Upstream{
+ {
+ Name: "coffee",
+ Service: "coffee-svc",
+ Port: 80,
+ },
+ },
+ Subroutes: []conf_v1.Route{
+ {
+ Path: "/coffee",
+ Action: &conf_v1.Action{
+ Pass: "coffee",
+ },
+ },
+ },
},
},
- polKey: "default/waf-policy",
- polNamespace: "",
- apResources: &appProtectResourcesForVS{
- Policies: map[string]string{
- "ns1/dataguard-alarm": "/etc/nginx/waf/nac-policies/ns1-dataguard-alarm",
+ {
+ ObjectMeta: meta_v1.ObjectMeta{
+ Name: "subtea",
+ Namespace: "default",
},
- LogConfs: map[string]string{
- "ns2/logconf": "/etc/nginx/waf/nac-logconfs/ns2-logconf",
+ Spec: conf_v1.VirtualServerRouteSpec{
+ Host: "cafe.example.com",
+ Upstreams: []conf_v1.Upstream{
+ {
+ Name: "subtea",
+ Service: "sub-tea-svc",
+ Port: 80,
+ Subselector: map[string]string{"version": "v1"},
+ },
+ },
+ Subroutes: []conf_v1.Route{
+ {
+ Path: "/subtea",
+ Action: &conf_v1.Action{
+ Pass: "subtea",
+ },
+ },
+ },
},
},
- wafConfig: &version2.WAF{
- ApPolicy: "/etc/nginx/waf/nac-policies/ns1-dataguard-alarm",
- ApSecurityLogEnable: true,
- ApLogConf: []string{"/etc/nginx/waf/nac-logconfs/ns2-logconf"},
- },
- expected: &validationResults{},
- msg: "valid waf config, cross ns reference",
- },
- {
- wafInput: &conf_v1.WAF{
- Enable: false,
- ApPolicy: "dataguard-alarm",
- },
- polKey: "default/waf-policy",
- polNamespace: "default",
- apResources: &appProtectResourcesForVS{
- Policies: map[string]string{
- "default/dataguard-alarm": "/etc/nginx/waf/nac-policies/ns1-dataguard-alarm",
+ {
+ ObjectMeta: meta_v1.ObjectMeta{
+ Name: "subcoffee",
+ Namespace: "default",
},
- LogConfs: map[string]string{
- "default/logconf": "/etc/nginx/waf/nac-logconfs/ns2-logconf",
+ Spec: conf_v1.VirtualServerRouteSpec{
+ Host: "cafe.example.com",
+ Upstreams: []conf_v1.Upstream{
+ {
+ Name: "coffee",
+ Service: "coffee-svc",
+ Port: 80,
+ },
+ },
+ Subroutes: []conf_v1.Route{
+ {
+ Path: "/coffee-errorpage-subroute",
+ Action: &conf_v1.Action{
+ Pass: "coffee",
+ },
+ },
+ {
+ Path: "/coffee-errorpage-subroute-defined",
+ Action: &conf_v1.Action{
+ Pass: "coffee",
+ },
+ ErrorPages: []conf_v1.ErrorPage{
+ {
+ Codes: []int{502, 503},
+ Return: &conf_v1.ErrorPageReturn{
+ ActionReturn: conf_v1.ActionReturn{
+ Code: 200,
+ Type: "text/plain",
+ Body: "All Good",
+ },
+ },
+ },
+ },
+ },
+ },
},
},
- wafConfig: &version2.WAF{
- Enable: "off",
- ApPolicy: "/etc/nginx/waf/nac-policies/ns1-dataguard-alarm",
- },
- expected: &validationResults{},
- msg: "valid waf config, disable waf",
- },
- }
-
- for _, test := range tests {
- polCfg := newPoliciesConfig()
- result := polCfg.addWAFConfig(test.wafInput, test.polKey, test.polNamespace, test.apResources)
- if diff := cmp.Diff(test.expected.warnings, result.warnings); diff != "" {
- t.Errorf("policiesCfg.addWAFConfig() '%v' mismatch (-want +got):\n%s", test.msg, diff)
- }
- }
-}
-
-func TestGenerateTime(t *testing.T) {
- t.Parallel()
- tests := []struct {
- value, expected string
- }{
- {
- value: "0s",
- expected: "0s",
- },
- {
- value: "0",
- expected: "0s",
- },
- {
- value: "1h",
- expected: "1h",
- },
- {
- value: "1h 30m",
- expected: "1h30m",
- },
- }
-
- for _, test := range tests {
- result := generateTime(test.value)
- if result != test.expected {
- t.Errorf("generateTime(%q) returned %q but expected %q", test.value, result, test.expected)
- }
- }
-}
-
-func TestGenerateTimeWithDefault(t *testing.T) {
- t.Parallel()
- tests := []struct {
- value, defaultValue, expected string
- }{
- {
- value: "1h 30m",
- defaultValue: "",
- expected: "1h30m",
- },
- {
- value: "",
- defaultValue: "60s",
- expected: "60s",
- },
- {
- value: "",
- defaultValue: "test",
- expected: "test",
},
}
-
- for _, test := range tests {
- result := generateTimeWithDefault(test.value, test.defaultValue)
- if result != test.expected {
- t.Errorf("generateTimeWithDefault(%q, %q) returned %q but expected %q", test.value, test.defaultValue, result, test.expected)
- }
- }
-}
+)
diff --git a/pkg/apis/configuration/v1/types.go b/pkg/apis/configuration/v1/types.go
index d6d55e5e77..adc519df13 100644
--- a/pkg/apis/configuration/v1/types.go
+++ b/pkg/apis/configuration/v1/types.go
@@ -39,7 +39,7 @@ type VirtualServerSpec struct {
IngressClass string `json:"ingressClassName"`
Host string `json:"host"`
TLS *TLS `json:"tls"`
- Gunzip string `json:"gunzip"`
+ Gunzip bool `json:"gunzip"`
Policies []PolicyReference `json:"policies"`
Upstreams []Upstream `json:"upstreams"`
Routes []Route `json:"routes"`
diff --git a/pkg/apis/configuration/validation/virtualserver.go b/pkg/apis/configuration/validation/virtualserver.go
index ee036d9cf3..1ac7be51e8 100644
--- a/pkg/apis/configuration/validation/virtualserver.go
+++ b/pkg/apis/configuration/validation/virtualserver.go
@@ -77,7 +77,6 @@ func (vsv *VirtualServerValidator) validateVirtualServerSpec(spec *v1.VirtualSer
allErrs := field.ErrorList{}
allErrs = append(allErrs, validateHost(spec.Host, fieldPath.Child("host"))...)
- allErrs = append(allErrs, validateGunzip(spec.Gunzip, fieldPath.Child("gunzip"))...)
allErrs = append(allErrs, vsv.validateTLS(spec.TLS, fieldPath.Child("tls"))...)
allErrs = append(allErrs, validatePolicies(spec.Policies, fieldPath.Child("policies"), namespace)...)
@@ -115,15 +114,6 @@ func validateHost(host string, fieldPath *field.Path) field.ErrorList {
return allErrs
}
-func validateGunzip(fieldValue string, fl *field.Path) field.ErrorList {
- switch fieldValue {
- case "on", "off", "":
- return nil
- default:
- return field.ErrorList{field.NotSupported(fl, fieldValue, []string{"on", "off"})}
- }
-}
-
func validatePolicies(policies []v1.PolicyReference, fieldPath *field.Path, namespace string) field.ErrorList {
allErrs := field.ErrorList{}
policyKeys := sets.Set[string]{}
diff --git a/pkg/apis/configuration/validation/virtualserver_test.go b/pkg/apis/configuration/validation/virtualserver_test.go
index 6eae75a179..a694fa76d2 100644
--- a/pkg/apis/configuration/validation/virtualserver_test.go
+++ b/pkg/apis/configuration/validation/virtualserver_test.go
@@ -69,215 +69,6 @@ func TestValidateVirtualServer(t *testing.T) {
}
}
-func TestValidateVirtualServer_PassesOnValidGunzipOn(t *testing.T) {
- t.Parallel()
-
- vsv := &VirtualServerValidator{isPlus: false, isDosEnabled: false}
- err := vsv.ValidateVirtualServer(&virtualServerWithValidGunzipOn)
- if err != nil {
- t.Errorf("ValidateVirtualServer() returned error %v for valid input %+v", err, virtualServerWithValidGunzipOn)
- }
-}
-
-func TestValidateVirtualServer_PassesOnValidGunzipOff(t *testing.T) {
- t.Parallel()
-
- vsv := &VirtualServerValidator{isPlus: false, isDosEnabled: false}
- err := vsv.ValidateVirtualServer(&virtualServerWithValidGunzipOff)
- if err != nil {
- t.Errorf("ValidateVirtualServer() returned error %v for valid input %+v", err, virtualServerWithValidGunzipOff)
- }
-}
-
-func TestValidateVirtualServer_PassesOnNoGunzip(t *testing.T) {
- t.Parallel()
-
- vsv := &VirtualServerValidator{isPlus: false, isDosEnabled: false}
- err := vsv.ValidateVirtualServer(&virtualServerWithNoGunzip)
- if err != nil {
- t.Errorf("ValidateVirtualServer() returned error %v for valid input %+v", err, virtualServerWithNoGunzip)
- }
-}
-
-func TestValidateVirtualServer_FailsOnBogusGunzipValue(t *testing.T) {
- t.Parallel()
-
- vsv := &VirtualServerValidator{isPlus: false, isDosEnabled: false}
- err := vsv.ValidateVirtualServer(&virtualServerWithBogusGunzipValue)
- if err == nil {
- t.Error("ValidateVirtualServer() returned no error on bogus gunzip value")
- }
-}
-
-var (
- virtualServerWithValidGunzipOn = v1.VirtualServer{
- ObjectMeta: meta_v1.ObjectMeta{
- Name: "cafe",
- Namespace: "default",
- },
- Spec: v1.VirtualServerSpec{
- Host: "example.com",
- Gunzip: "on",
- Upstreams: []v1.Upstream{
- {
- Name: "first",
- Service: "service-1",
- LBMethod: "random",
- Port: 80,
- MaxFails: createPointerFromInt(8),
- MaxConns: createPointerFromInt(16),
- Keepalive: createPointerFromInt(32),
- Type: "grpc",
- },
- {
- Name: "second",
- Service: "service-2",
- Port: 80,
- },
- },
- Routes: []v1.Route{
- {
- Path: "/first",
- Action: &v1.Action{
- Pass: "first",
- },
- },
- {
- Path: "/second",
- Action: &v1.Action{
- Pass: "second",
- },
- },
- },
- },
- }
-
- virtualServerWithValidGunzipOff = v1.VirtualServer{
- ObjectMeta: meta_v1.ObjectMeta{
- Name: "cafe",
- Namespace: "default",
- },
- Spec: v1.VirtualServerSpec{
- Host: "example.com",
- Gunzip: "off",
- Upstreams: []v1.Upstream{
- {
- Name: "first",
- Service: "service-1",
- LBMethod: "random",
- Port: 80,
- MaxFails: createPointerFromInt(8),
- MaxConns: createPointerFromInt(16),
- Keepalive: createPointerFromInt(32),
- Type: "grpc",
- },
- {
- Name: "second",
- Service: "service-2",
- Port: 80,
- },
- },
- Routes: []v1.Route{
- {
- Path: "/first",
- Action: &v1.Action{
- Pass: "first",
- },
- },
- {
- Path: "/second",
- Action: &v1.Action{
- Pass: "second",
- },
- },
- },
- },
- }
-
- virtualServerWithNoGunzip = v1.VirtualServer{
- ObjectMeta: meta_v1.ObjectMeta{
- Name: "cafe",
- Namespace: "default",
- },
- Spec: v1.VirtualServerSpec{
- Host: "example.com",
- Upstreams: []v1.Upstream{
- {
- Name: "first",
- Service: "service-1",
- LBMethod: "random",
- Port: 80,
- MaxFails: createPointerFromInt(8),
- MaxConns: createPointerFromInt(16),
- Keepalive: createPointerFromInt(32),
- Type: "grpc",
- },
- {
- Name: "second",
- Service: "service-2",
- Port: 80,
- },
- },
- Routes: []v1.Route{
- {
- Path: "/first",
- Action: &v1.Action{
- Pass: "first",
- },
- },
- {
- Path: "/second",
- Action: &v1.Action{
- Pass: "second",
- },
- },
- },
- },
- }
-
- virtualServerWithBogusGunzipValue = v1.VirtualServer{
- ObjectMeta: meta_v1.ObjectMeta{
- Name: "cafe",
- Namespace: "default",
- },
- Spec: v1.VirtualServerSpec{
- Host: "example.com",
- Gunzip: "bogus",
- Upstreams: []v1.Upstream{
- {
- Name: "first",
- Service: "service-1",
- LBMethod: "random",
- Port: 80,
- MaxFails: createPointerFromInt(8),
- MaxConns: createPointerFromInt(16),
- Keepalive: createPointerFromInt(32),
- Type: "grpc",
- },
- {
- Name: "second",
- Service: "service-2",
- Port: 80,
- },
- },
- Routes: []v1.Route{
- {
- Path: "/first",
- Action: &v1.Action{
- Pass: "first",
- },
- },
- {
- Path: "/second",
- Action: &v1.Action{
- Pass: "second",
- },
- },
- },
- },
- }
-)
-
func TestValidateHost(t *testing.T) {
t.Parallel()
validHosts := []string{
@@ -311,26 +102,6 @@ func TestValidateHost(t *testing.T) {
}
}
-func TestValidateGunzip_FailsOnBogusGunzipValue(t *testing.T) {
- t.Parallel()
- bogusGunzipValue := "bogus"
- allErr := validateGunzip(bogusGunzipValue, field.NewPath("gunzip"))
- if len(allErr) == 0 {
- t.Errorf("validateGunzip(%q) did not return error on invalid input.", bogusGunzipValue)
- }
-}
-
-func TestValidateGunzip_PassesOnValidGunzipValues(t *testing.T) {
- t.Parallel()
- tt := []string{"on", "off", ""}
- for _, v := range tt {
- allErr := validateGunzip(v, field.NewPath("gunzip"))
- if len(allErr) > 0 {
- t.Errorf("validateGunzip(%q) returned errors %v for valid input", v, allErr)
- }
- }
-}
-
func TestValidateDos(t *testing.T) {
t.Parallel()
validDosResources := []string{
diff --git a/tests/data/virtual-server/virtual-server-gunzip.yaml b/tests/data/virtual-server/virtual-server-gunzip.yaml
new file mode 100644
index 0000000000..41a0f0154c
--- /dev/null
+++ b/tests/data/virtual-server/virtual-server-gunzip.yaml
@@ -0,0 +1,21 @@
+apiVersion: k8s.nginx.org/v1
+kind: VirtualServer
+metadata:
+ name: virtual-server
+spec:
+ host: virtual-server.example.com
+ gunzip: on
+ upstreams:
+ - name: backend2
+ service: backend2-svc
+ port: 80
+ - name: backend1
+ service: backend1-svc
+ port: 80
+ routes:
+ - path: "/backend1"
+ action:
+ pass: backend1
+ - path: "/backend2"
+ action:
+ pass: backend2
diff --git a/tests/suite/test_virtual_server.py b/tests/suite/test_virtual_server.py
index b13c46838a..8e254d804c 100644
--- a/tests/suite/test_virtual_server.py
+++ b/tests/suite/test_virtual_server.py
@@ -5,6 +5,7 @@
from suite.utils.resources_utils import (
create_service_from_yaml,
delete_service,
+ get_first_pod_name,
patch_rbac,
read_service,
replace_service,
@@ -13,6 +14,7 @@
from suite.utils.vs_vsr_resources_utils import (
create_virtual_server_from_yaml,
delete_virtual_server,
+ get_vs_nginx_template_conf,
patch_virtual_server_from_yaml,
)
from suite.utils.yaml_utils import get_first_host_from_yaml, get_name_from_yaml, get_paths_from_vs_yaml
@@ -166,6 +168,45 @@ def test_responses_after_crd_removal_on_the_fly(self, kube_apis, crd_ingress_con
wait_and_assert_status_code(200, virtual_server_setup.backend_1_url, virtual_server_setup.vs_host)
wait_and_assert_status_code(200, virtual_server_setup.backend_2_url, virtual_server_setup.vs_host)
+ def test_responses_after_virtual_server_update_with_gunzip(
+ self, kube_apis, ingress_controller_prerequisites, crd_ingress_controller, virtual_server_setup
+ ):
+ print("Step 1: update gunzip in the VS and check")
+ patch_virtual_server_from_yaml(
+ kube_apis.custom_objects,
+ virtual_server_setup.vs_name,
+ f"{TEST_DATA}/virtual-server/virtual-server-gunzip.yaml",
+ virtual_server_setup.namespace,
+ )
+ wait_before_test(1)
+ wait_and_assert_status_code(200, virtual_server_setup.backend_1_url, virtual_server_setup.vs_host)
+ wait_and_assert_status_code(200, virtual_server_setup.backend_2_url, virtual_server_setup.vs_host)
+
+ print("Step 2: verify gunzip directive is present")
+
+ pod_name = get_first_pod_name(kube_apis.v1, ingress_controller_prerequisites.namespace)
+
+ confFile = get_vs_nginx_template_conf(
+ kube_apis.v1,
+ virtual_server_setup.namespace,
+ virtual_server_setup.vs_name,
+ pod_name,
+ ingress_controller_prerequisites.namespace,
+ )
+
+ assert "gunzip on;" in confFile
+
+ print("Step 3: restore VS and check")
+ patch_virtual_server_from_yaml(
+ kube_apis.custom_objects,
+ virtual_server_setup.vs_name,
+ f"{TEST_DATA}/virtual-server/standard/virtual-server.yaml",
+ virtual_server_setup.namespace,
+ )
+ wait_before_test(1)
+ wait_and_assert_status_code(200, virtual_server_setup.backend_1_url, virtual_server_setup.vs_host)
+ wait_and_assert_status_code(200, virtual_server_setup.backend_2_url, virtual_server_setup.vs_host)
+
@pytest.mark.vs
@pytest.mark.parametrize(