2016-05-25 21:04:34 +00:00
/ *
2016-09-08 11:02:39 +00:00
Copyright 2015 The Kubernetes Authors .
2016-05-25 21:04:34 +00:00
Licensed under the Apache License , Version 2.0 ( the "License" ) ;
you may not use this file except in compliance with the License .
You may obtain a copy of the License at
http : //www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing , software
distributed under the License is distributed on an "AS IS" BASIS ,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND , either express or implied .
See the License for the specific language governing permissions and
limitations under the License .
* /
2016-08-07 22:53:08 +00:00
package template
2016-05-25 21:04:34 +00:00
import (
2021-07-06 17:50:19 +00:00
"bytes"
2019-08-13 21:14:55 +00:00
"encoding/base64"
"fmt"
2017-09-17 18:42:31 +00:00
"net"
2016-11-16 18:24:26 +00:00
"os"
"path"
2019-08-13 21:14:55 +00:00
"path/filepath"
2017-02-04 00:43:15 +00:00
"reflect"
2017-04-02 14:07:07 +00:00
"strings"
2016-05-25 21:04:34 +00:00
"testing"
2018-09-22 17:25:01 +00:00
jsoniter "github.com/json-iterator/go"
2021-07-06 17:50:19 +00:00
"github.com/pmezard/go-difflib/difflib"
2019-08-31 15:24:01 +00:00
apiv1 "k8s.io/api/core/v1"
2021-08-21 20:42:00 +00:00
networking "k8s.io/api/networking/v1"
2019-08-31 15:24:01 +00:00
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2019-08-13 21:14:55 +00:00
2017-11-07 22:02:12 +00:00
"k8s.io/ingress-nginx/internal/ingress/annotations/authreq"
2019-05-25 21:32:13 +00:00
"k8s.io/ingress-nginx/internal/ingress/annotations/modsecurity"
2023-03-22 18:58:22 +00:00
"k8s.io/ingress-nginx/internal/ingress/annotations/opentelemetry"
2020-01-26 00:39:20 +00:00
"k8s.io/ingress-nginx/internal/ingress/annotations/opentracing"
2019-01-29 06:21:08 +00:00
"k8s.io/ingress-nginx/internal/ingress/annotations/ratelimit"
2017-11-07 22:02:12 +00:00
"k8s.io/ingress-nginx/internal/ingress/annotations/rewrite"
"k8s.io/ingress-nginx/internal/ingress/controller/config"
2019-08-13 21:14:55 +00:00
"k8s.io/ingress-nginx/internal/nginx"
2022-07-22 00:32:48 +00:00
"k8s.io/ingress-nginx/pkg/apis/ingress"
2016-05-25 21:04:34 +00:00
)
2019-08-13 21:14:55 +00:00
func init ( ) {
// the default value of nginx.TemplatePath assumes the template exists in
// the root filesystem and not in the rootfs directory
path , err := filepath . Abs ( filepath . Join ( "../../../../rootfs/" , nginx . TemplatePath ) )
if err == nil {
nginx . TemplatePath = path
}
}
2016-05-25 21:04:34 +00:00
var (
2021-08-21 20:42:00 +00:00
pathPrefix networking . PathType = networking . PathTypePrefix
2018-03-18 13:13:41 +00:00
// TODO: add tests for SSLPassthrough
2016-05-25 21:04:34 +00:00
tmplFuncTestcases = map [ string ] struct {
2021-07-29 19:49:19 +00:00
Path string
Target string
Location string
ProxyPass string
AutoHttpProxyPass string
Sticky bool
XForwardedPrefix string
SecureBackend bool
enforceRegex bool
2016-05-25 21:04:34 +00:00
} {
2018-03-18 13:13:41 +00:00
"when secure backend enabled" : {
"/" ,
"/" ,
"/" ,
2018-10-09 22:36:10 +00:00
"proxy_pass https://upstream_balancer;" ,
2021-07-29 19:49:19 +00:00
"proxy_pass https://upstream_balancer;" ,
2018-03-18 13:13:41 +00:00
false ,
2019-03-11 16:23:14 +00:00
"" ,
2018-09-14 19:07:57 +00:00
true ,
2018-10-08 15:26:06 +00:00
false ,
} ,
2018-03-18 13:13:41 +00:00
"when secure backend and dynamic config enabled" : {
"/" ,
"/" ,
"/" ,
"proxy_pass https://upstream_balancer;" ,
2021-07-29 19:49:19 +00:00
"proxy_pass https://upstream_balancer;" ,
2018-03-18 13:13:41 +00:00
false ,
2019-03-11 16:23:14 +00:00
"" ,
2018-03-18 13:13:41 +00:00
true ,
2018-10-09 22:36:10 +00:00
false ,
} ,
2020-11-27 17:26:53 +00:00
"when secure backend, stickiness and dynamic config enabled" : {
2018-03-18 13:13:41 +00:00
"/" ,
"/" ,
"/" ,
"proxy_pass https://upstream_balancer;" ,
2021-07-29 19:49:19 +00:00
"proxy_pass https://upstream_balancer;" ,
2018-03-18 13:13:41 +00:00
true ,
2019-03-11 16:23:14 +00:00
"" ,
2018-03-18 13:13:41 +00:00
true ,
2018-10-08 15:26:06 +00:00
false ,
} ,
2018-03-18 13:13:41 +00:00
"invalid redirect / to / with dynamic config enabled" : {
"/" ,
"/" ,
"/" ,
"proxy_pass http://upstream_balancer;" ,
2021-07-29 19:49:19 +00:00
"proxy_pass $scheme://upstream_balancer;" ,
2018-03-18 13:13:41 +00:00
false ,
2019-03-11 16:23:14 +00:00
"" ,
2018-09-14 19:07:57 +00:00
false ,
2018-10-08 15:26:06 +00:00
false ,
} ,
2018-03-18 13:13:41 +00:00
"invalid redirect / to /" : {
"/" ,
"/" ,
"/" ,
2018-10-09 22:36:10 +00:00
"proxy_pass http://upstream_balancer;" ,
2021-07-29 19:49:19 +00:00
"proxy_pass $scheme://upstream_balancer;" ,
2018-03-18 13:13:41 +00:00
false ,
2019-03-11 16:23:14 +00:00
"" ,
2018-03-18 13:13:41 +00:00
false ,
2018-09-14 19:07:57 +00:00
false ,
2018-10-08 15:26:06 +00:00
} ,
2018-03-18 13:13:41 +00:00
"redirect / to /jenkins" : {
"/" ,
"/jenkins" ,
2018-12-13 18:02:05 +00:00
` ~* "^/" ` ,
2018-03-18 13:13:41 +00:00
`
2018-12-13 18:02:05 +00:00
rewrite "(?i)/" / jenkins break ;
proxy_pass http : //upstream_balancer;`,
2021-07-29 19:49:19 +00:00
`
rewrite "(?i)/" / jenkins break ;
proxy_pass $ scheme : //upstream_balancer;`,
2018-03-18 13:13:41 +00:00
false ,
2019-03-11 16:23:14 +00:00
"" ,
2018-03-18 13:13:41 +00:00
false ,
2018-10-08 15:26:06 +00:00
true ,
} ,
2018-03-18 13:13:41 +00:00
"redirect / to /something with sticky enabled" : {
"/" ,
"/something" ,
2018-12-13 18:02:05 +00:00
` ~* "^/" ` ,
2018-03-18 13:13:41 +00:00
`
2018-12-13 18:02:05 +00:00
rewrite "(?i)/" / something break ;
proxy_pass http : //upstream_balancer;`,
2021-07-29 19:49:19 +00:00
`
rewrite "(?i)/" / something break ;
proxy_pass $ scheme : //upstream_balancer;`,
2018-03-18 13:13:41 +00:00
true ,
2019-03-11 16:23:14 +00:00
"" ,
2018-03-18 13:13:41 +00:00
false ,
2018-10-08 15:26:06 +00:00
true ,
} ,
2018-03-18 13:13:41 +00:00
"redirect / to /something with sticky and dynamic config enabled" : {
"/" ,
"/something" ,
2018-12-13 18:02:05 +00:00
` ~* "^/" ` ,
2018-03-18 13:13:41 +00:00
`
2018-12-13 18:02:05 +00:00
rewrite "(?i)/" / something break ;
proxy_pass http : //upstream_balancer;`,
2021-07-29 19:49:19 +00:00
`
rewrite "(?i)/" / something break ;
proxy_pass $ scheme : //upstream_balancer;`,
2018-03-18 13:13:41 +00:00
true ,
2019-03-11 16:23:14 +00:00
"" ,
2018-09-14 19:07:57 +00:00
false ,
2018-10-08 15:26:06 +00:00
true ,
} ,
2018-03-18 13:13:41 +00:00
"add the X-Forwarded-Prefix header" : {
"/there" ,
"/something" ,
2018-12-13 18:02:05 +00:00
` ~* "^/there" ` ,
2018-03-18 13:13:41 +00:00
`
2018-12-13 18:02:05 +00:00
rewrite "(?i)/there" / something break ;
proxy_set_header X - Forwarded - Prefix "/there" ;
proxy_pass http : //upstream_balancer;`,
2021-07-29 19:49:19 +00:00
`
rewrite "(?i)/there" / something break ;
proxy_set_header X - Forwarded - Prefix "/there" ;
proxy_pass $ scheme : //upstream_balancer;`,
2018-03-18 13:13:41 +00:00
true ,
2019-03-11 16:23:14 +00:00
"/there" ,
2018-03-18 13:13:41 +00:00
false ,
2018-10-08 15:26:06 +00:00
true ,
} ,
2018-10-01 17:54:11 +00:00
"use ~* location modifier when ingress does not use rewrite/regex target but at least one other ingress does" : {
2018-09-14 19:07:57 +00:00
"/something" ,
"/something" ,
2018-10-04 14:58:38 +00:00
` ~* "^/something" ` ,
2018-10-09 22:36:10 +00:00
"proxy_pass http://upstream_balancer;" ,
2021-07-29 19:49:19 +00:00
"proxy_pass $scheme://upstream_balancer;" ,
2018-09-14 19:07:57 +00:00
false ,
2019-03-11 16:23:14 +00:00
"" ,
2018-09-14 19:07:57 +00:00
false ,
2018-10-08 15:26:06 +00:00
true ,
} ,
2016-05-25 21:04:34 +00:00
}
)
2021-07-06 17:50:19 +00:00
func getTestDataDir ( ) ( string , error ) {
pwd , err := os . Getwd ( )
if err != nil {
return "" , err
}
return path . Join ( pwd , "../../../../test/data" ) , nil
}
2018-04-08 20:37:13 +00:00
func TestBuildLuaSharedDictionaries ( t * testing . T ) {
2019-01-29 06:21:08 +00:00
invalidType := & ingress . Ingress { }
expected := ""
2019-08-06 11:21:59 +00:00
// config lua dict
cfg := config . Configuration {
LuaSharedDicts : map [ string ] int {
2021-08-12 18:13:50 +00:00
"configuration_data" : 10240 , "certificate_data" : 20480 ,
2019-08-06 11:21:59 +00:00
} ,
}
2019-11-04 18:41:26 +00:00
actual := buildLuaSharedDictionaries ( cfg , invalidType )
2019-01-29 06:21:08 +00:00
if ! reflect . DeepEqual ( expected , actual ) {
t . Errorf ( "Expected '%v' but returned '%v'" , expected , actual )
}
2018-04-08 20:37:13 +00:00
servers := [ ] * ingress . Server {
{
Hostname : "foo.bar" ,
2021-08-21 20:42:00 +00:00
Locations : [ ] * ingress . Location { { Path : "/" , PathType : & pathPrefix } } ,
2018-04-08 20:37:13 +00:00
} ,
{
Hostname : "another.host" ,
2021-08-21 20:42:00 +00:00
Locations : [ ] * ingress . Location { { Path : "/" , PathType : & pathPrefix } } ,
2018-04-08 20:37:13 +00:00
} ,
}
2019-08-06 11:21:59 +00:00
// returns value from config
2019-11-04 18:41:26 +00:00
configuration := buildLuaSharedDictionaries ( cfg , servers )
2019-08-14 23:23:20 +00:00
if ! strings . Contains ( configuration , "lua_shared_dict configuration_data 10M;\n" ) {
2019-05-25 21:32:13 +00:00
t . Errorf ( "expected to include 'configuration_data' but got %s" , configuration )
2018-04-08 20:37:13 +00:00
}
2019-08-14 23:23:20 +00:00
if ! strings . Contains ( configuration , "lua_shared_dict certificate_data 20M;\n" ) {
t . Errorf ( "expected to include 'certificate_data' but got %s" , configuration )
}
2019-08-06 11:21:59 +00:00
// test invalid config
2019-11-04 18:41:26 +00:00
configuration = buildLuaSharedDictionaries ( invalidType , servers )
2019-08-23 16:08:40 +00:00
if configuration != "" {
t . Errorf ( "expected an empty string, but got %s" , configuration )
}
if actual != expected {
2019-08-06 11:21:59 +00:00
t . Errorf ( "Expected '%v' but returned '%v' " , expected , actual )
}
2018-04-08 20:37:13 +00:00
}
2019-08-15 02:03:17 +00:00
func TestLuaConfigurationRequestBodySize ( t * testing . T ) {
cfg := config . Configuration {
LuaSharedDicts : map [ string ] int {
2021-08-12 18:13:50 +00:00
"configuration_data" : 10240 , "certificate_data" : 20480 ,
2019-08-15 02:03:17 +00:00
} ,
}
size := luaConfigurationRequestBodySize ( cfg )
2021-08-12 18:13:50 +00:00
if size != "21M" {
t . Errorf ( "expected the size to be 21M but got: %v" , size )
2019-08-15 02:03:17 +00:00
}
}
2017-06-09 03:11:00 +00:00
func TestFormatIP ( t * testing . T ) {
cases := map [ string ] struct {
Input , Output string
} {
"ipv4-localhost" : { "127.0.0.1" , "127.0.0.1" } ,
"ipv4-internet" : { "8.8.8.8" , "8.8.8.8" } ,
"ipv6-localhost" : { "::1" , "[::1]" } ,
"ipv6-internet" : { "2001:4860:4860::8888" , "[2001:4860:4860::8888]" } ,
"invalid-ip" : { "nonsense" , "nonsense" } ,
"empty-ip" : { "" , "" } ,
}
for k , tc := range cases {
res := formatIP ( tc . Input )
if res != tc . Output {
t . Errorf ( "%s: called formatIp('%s'); expected '%v' but returned '%v'" , k , tc . Input , tc . Output , res )
}
}
}
2019-08-09 19:47:29 +00:00
func TestQuote ( t * testing . T ) {
2020-03-05 09:59:30 +00:00
foo := "foo"
2019-08-09 19:47:29 +00:00
cases := map [ interface { } ] string {
"foo" : ` "foo" ` ,
"\"foo\"" : ` "\"foo\"" ` ,
"foo\nbar" : ` "foo\nbar" ` ,
2020-03-05 09:59:30 +00:00
& foo : ` "foo" ` ,
2019-08-09 19:47:29 +00:00
10 : ` "10" ` ,
}
for input , output := range cases {
actual := quote ( input )
if actual != output {
t . Errorf ( "quote('%s'): expected '%v' but returned '%v'" , input , output , actual )
}
}
}
2016-05-25 21:04:34 +00:00
func TestBuildLocation ( t * testing . T ) {
2019-01-29 06:21:08 +00:00
invalidType := & ingress . Ingress { }
expected := "/"
2020-07-15 15:20:47 +00:00
actual := buildLocation ( invalidType , true )
2019-01-29 06:21:08 +00:00
if ! reflect . DeepEqual ( expected , actual ) {
t . Errorf ( "Expected '%v' but returned '%v'" , expected , actual )
}
2016-05-25 21:04:34 +00:00
for k , tc := range tmplFuncTestcases {
2016-08-07 22:53:08 +00:00
loc := & ingress . Location {
2021-08-21 20:42:00 +00:00
Path : tc . Path ,
PathType : & pathPrefix ,
Rewrite : rewrite . Config { Target : tc . Target } ,
2016-05-25 21:04:34 +00:00
}
2020-07-15 15:20:47 +00:00
newLoc := buildLocation ( loc , tc . enforceRegex )
2016-05-25 21:04:34 +00:00
if tc . Location != newLoc {
2016-05-27 14:58:13 +00:00
t . Errorf ( "%s: expected '%v' but returned %v" , k , tc . Location , newLoc )
2016-05-25 21:04:34 +00:00
}
}
}
func TestBuildProxyPass ( t * testing . T ) {
2017-08-23 16:11:33 +00:00
defaultBackend := "upstream-name"
defaultHost := "example.com"
2016-05-25 21:04:34 +00:00
for k , tc := range tmplFuncTestcases {
2016-08-07 22:53:08 +00:00
loc := & ingress . Location {
2017-12-06 20:11:18 +00:00
Path : tc . Path ,
2021-08-21 20:42:00 +00:00
PathType : & pathPrefix ,
2018-12-13 18:02:05 +00:00
Rewrite : rewrite . Config { Target : tc . Target } ,
2017-12-06 20:11:18 +00:00
Backend : defaultBackend ,
XForwardedPrefix : tc . XForwardedPrefix ,
2017-08-23 16:11:33 +00:00
}
2018-10-08 15:26:06 +00:00
if tc . SecureBackend {
loc . BackendProtocol = "HTTPS"
}
2018-03-18 13:13:41 +00:00
backend := & ingress . Backend {
2018-10-08 15:26:06 +00:00
Name : defaultBackend ,
2018-03-18 13:13:41 +00:00
}
2017-08-23 16:11:33 +00:00
if tc . Sticky {
2018-03-18 13:13:41 +00:00
backend . SessionAffinity = ingress . SessionAffinityConfig {
AffinityType : "cookie" ,
CookieSessionAffinity : ingress . CookieSessionAffinity {
Locations : map [ string ] [ ] string {
defaultHost : { tc . Path } ,
2017-08-23 16:11:33 +00:00
} ,
} ,
}
2016-05-25 21:04:34 +00:00
}
2018-03-18 13:13:41 +00:00
backends := [ ] * ingress . Backend { backend }
2018-10-09 22:36:10 +00:00
pp := buildProxyPass ( defaultHost , backends , loc )
2016-05-27 14:58:13 +00:00
if ! strings . EqualFold ( tc . ProxyPass , pp ) {
t . Errorf ( "%s: expected \n'%v'\nbut returned \n'%v'" , k , tc . ProxyPass , pp )
2016-05-25 21:04:34 +00:00
}
}
}
2016-11-16 18:24:26 +00:00
2021-07-29 19:49:19 +00:00
func TestBuildProxyPassAutoHttp ( t * testing . T ) {
defaultBackend := "upstream-name"
defaultHost := "example.com"
for k , tc := range tmplFuncTestcases {
loc := & ingress . Location {
Path : tc . Path ,
Rewrite : rewrite . Config { Target : tc . Target } ,
Backend : defaultBackend ,
XForwardedPrefix : tc . XForwardedPrefix ,
}
if tc . SecureBackend {
loc . BackendProtocol = "HTTPS"
} else {
loc . BackendProtocol = "AUTO_HTTP"
}
backend := & ingress . Backend {
Name : defaultBackend ,
}
if tc . Sticky {
backend . SessionAffinity = ingress . SessionAffinityConfig {
AffinityType : "cookie" ,
CookieSessionAffinity : ingress . CookieSessionAffinity {
Locations : map [ string ] [ ] string {
defaultHost : { tc . Path } ,
} ,
} ,
}
}
backends := [ ] * ingress . Backend { backend }
pp := buildProxyPass ( defaultHost , backends , loc )
if ! strings . EqualFold ( tc . AutoHttpProxyPass , pp ) {
t . Errorf ( "%s: expected \n'%v'\nbut returned \n'%v'" , k , tc . ProxyPass , pp )
}
}
}
2018-01-28 00:32:08 +00:00
func TestBuildAuthLocation ( t * testing . T ) {
2019-01-29 06:21:08 +00:00
invalidType := & ingress . Ingress { }
expected := ""
2018-11-27 16:12:17 +00:00
actual := buildAuthLocation ( invalidType , "" )
2019-01-29 06:21:08 +00:00
if ! reflect . DeepEqual ( expected , actual ) {
t . Errorf ( "Expected '%v' but returned '%v'" , expected , actual )
}
2018-01-28 00:32:08 +00:00
authURL := "foo.com/auth"
2018-11-27 16:12:17 +00:00
globalAuthURL := "foo.com/global-auth"
2018-01-28 00:32:08 +00:00
loc := & ingress . Location {
ExternalAuth : authreq . Config {
URL : authURL ,
} ,
2018-11-27 16:12:17 +00:00
Path : "/cat" ,
EnableGlobalAuth : true ,
2018-01-28 00:32:08 +00:00
}
encodedAuthURL := strings . Replace ( base64 . URLEncoding . EncodeToString ( [ ] byte ( loc . Path ) ) , "=" , "" , - 1 )
2020-11-12 17:00:21 +00:00
externalAuthPath := fmt . Sprintf ( "/_external-auth-%v-default" , encodedAuthURL )
2018-11-27 16:12:17 +00:00
testCases := [ ] struct {
title string
authURL string
globalAuthURL string
enableglobalExternalAuth bool
expected string
} {
{ "authURL, globalAuthURL and enabled" , authURL , globalAuthURL , true , externalAuthPath } ,
{ "authURL, globalAuthURL and disabled" , authURL , globalAuthURL , false , externalAuthPath } ,
{ "authURL, empty globalAuthURL and enabled" , authURL , "" , true , externalAuthPath } ,
{ "authURL, empty globalAuthURL and disabled" , authURL , "" , false , externalAuthPath } ,
{ "globalAuthURL and enabled" , "" , globalAuthURL , true , externalAuthPath } ,
{ "globalAuthURL and disabled" , "" , globalAuthURL , false , "" } ,
{ "all empty and enabled" , "" , "" , true , "" } ,
{ "all empty and disabled" , "" , "" , false , "" } ,
}
for _ , testCase := range testCases {
loc . ExternalAuth . URL = testCase . authURL
loc . EnableGlobalAuth = testCase . enableglobalExternalAuth
2018-01-28 00:32:08 +00:00
2018-11-27 16:12:17 +00:00
str := buildAuthLocation ( loc , testCase . globalAuthURL )
if str != testCase . expected {
t . Errorf ( "%v: expected '%v' but returned '%v'" , testCase . title , testCase . expected , str )
}
2018-01-28 00:32:08 +00:00
}
}
2018-11-27 16:12:17 +00:00
func TestShouldApplyGlobalAuth ( t * testing . T ) {
2019-01-29 06:21:08 +00:00
2018-11-27 16:12:17 +00:00
authURL := "foo.com/auth"
globalAuthURL := "foo.com/global-auth"
2019-01-29 06:21:08 +00:00
2017-02-04 00:43:15 +00:00
loc := & ingress . Location {
2018-11-27 16:12:17 +00:00
ExternalAuth : authreq . Config {
URL : authURL ,
} ,
Path : "/cat" ,
EnableGlobalAuth : true ,
}
testCases := [ ] struct {
title string
authURL string
globalAuthURL string
enableglobalExternalAuth bool
expected bool
} {
{ "authURL, globalAuthURL and enabled" , authURL , globalAuthURL , true , false } ,
{ "authURL, globalAuthURL and disabled" , authURL , globalAuthURL , false , false } ,
{ "authURL, empty globalAuthURL and enabled" , authURL , "" , true , false } ,
{ "authURL, empty globalAuthURL and disabled" , authURL , "" , false , false } ,
{ "globalAuthURL and enabled" , "" , globalAuthURL , true , true } ,
{ "globalAuthURL and disabled" , "" , globalAuthURL , false , false } ,
{ "all empty and enabled" , "" , "" , true , false } ,
{ "all empty and disabled" , "" , "" , false , false } ,
}
for _ , testCase := range testCases {
loc . ExternalAuth . URL = testCase . authURL
loc . EnableGlobalAuth = testCase . enableglobalExternalAuth
result := shouldApplyGlobalAuth ( loc , testCase . globalAuthURL )
if result != testCase . expected {
t . Errorf ( "%v: expected '%v' but returned '%v'" , testCase . title , testCase . expected , result )
}
2017-02-04 00:43:15 +00:00
}
2018-11-27 16:12:17 +00:00
}
func TestBuildAuthResponseHeaders ( t * testing . T ) {
2022-04-09 03:22:04 +00:00
externalAuthResponseHeaders := [ ] string { "h1" , "H-With-Caps-And-Dashes" }
tests := [ ] struct {
headers [ ] string
expected [ ] string
lua bool
} {
{
headers : externalAuthResponseHeaders ,
lua : false ,
expected : [ ] string {
"auth_request_set $authHeader0 $upstream_http_h1;" ,
"proxy_set_header 'h1' $authHeader0;" ,
"auth_request_set $authHeader1 $upstream_http_h_with_caps_and_dashes;" ,
"proxy_set_header 'H-With-Caps-And-Dashes' $authHeader1;" ,
} ,
} ,
{
headers : externalAuthResponseHeaders ,
lua : true ,
expected : [ ] string {
"set $authHeader0 '';" ,
"proxy_set_header 'h1' $authHeader0;" ,
"set $authHeader1 '';" ,
"proxy_set_header 'H-With-Caps-And-Dashes' $authHeader1;" ,
} ,
} ,
}
for _ , test := range tests {
got := buildAuthResponseHeaders ( proxySetHeader ( nil ) , test . headers , test . lua )
if ! reflect . DeepEqual ( test . expected , got ) {
t . Errorf ( "Expected \n'%v'\nbut returned \n'%v'" , test . expected , got )
}
}
}
func TestBuildAuthResponseLua ( t * testing . T ) {
2018-11-27 16:12:17 +00:00
externalAuthResponseHeaders := [ ] string { "h1" , "H-With-Caps-And-Dashes" }
expected := [ ] string {
2022-04-09 03:22:04 +00:00
"ngx.var.authHeader0 = res.header['h1']" ,
"ngx.var.authHeader1 = res.header['H-With-Caps-And-Dashes']" ,
2017-02-04 00:43:15 +00:00
}
2022-04-09 03:22:04 +00:00
headers := buildAuthUpstreamLuaHeaders ( externalAuthResponseHeaders )
2018-11-27 16:12:17 +00:00
2017-02-04 00:43:15 +00:00
if ! reflect . DeepEqual ( expected , headers ) {
t . Errorf ( "Expected \n'%v'\nbut returned \n'%v'" , expected , headers )
}
}
2019-09-24 14:53:23 +00:00
func TestBuildAuthProxySetHeaders ( t * testing . T ) {
proxySetHeaders := map [ string ] string {
"header1" : "value1" ,
"header2" : "value2" ,
}
expected := [ ] string {
"proxy_set_header 'header1' 'value1';" ,
"proxy_set_header 'header2' 'value2';" ,
}
headers := buildAuthProxySetHeaders ( proxySetHeaders )
if ! reflect . DeepEqual ( expected , headers ) {
t . Errorf ( "Expected \n'%v'\nbut returned \n'%v'" , expected , headers )
}
}
2022-04-09 03:22:04 +00:00
func TestBuildAuthUpstreamName ( t * testing . T ) {
invalidType := & ingress . Ingress { }
expected := ""
actual := buildAuthUpstreamName ( invalidType , "" )
if ! reflect . DeepEqual ( expected , actual ) {
t . Errorf ( "Expected '%v' but returned '%v'" , expected , actual )
}
loc := & ingress . Location {
ExternalAuth : authreq . Config {
URL : "foo.com/auth" ,
} ,
Path : "/cat" ,
}
encodedAuthURL := strings . Replace ( base64 . URLEncoding . EncodeToString ( [ ] byte ( loc . Path ) ) , "=" , "" , - 1 )
externalAuthPath := fmt . Sprintf ( "external-auth-%v-default" , encodedAuthURL )
testCases := [ ] struct {
title string
host string
expected string
} {
{ "valid host" , "auth.my.site" , fmt . Sprintf ( "%s-%s" , "auth.my.site" , externalAuthPath ) } ,
{ "valid host" , "your.auth.site" , fmt . Sprintf ( "%s-%s" , "your.auth.site" , externalAuthPath ) } ,
{ "empty host" , "" , "" } ,
}
for _ , testCase := range testCases {
str := buildAuthUpstreamName ( loc , testCase . host )
if str != testCase . expected {
t . Errorf ( "%v: expected '%v' but returned '%v'" , testCase . title , testCase . expected , str )
}
}
}
func TestShouldApplyAuthUpstream ( t * testing . T ) {
authURL := "foo.com/auth"
loc := & ingress . Location {
ExternalAuth : authreq . Config {
URL : authURL ,
KeepaliveConnections : 0 ,
} ,
Path : "/cat" ,
}
cfg := config . Configuration {
UseHTTP2 : false ,
}
testCases := [ ] struct {
title string
authURL string
keepaliveConnections int
expected bool
} {
{ "authURL, no keepalive" , authURL , 0 , false } ,
{ "authURL, keepalive" , authURL , 10 , true } ,
{ "empty, no keepalive" , "" , 0 , false } ,
{ "empty, keepalive" , "" , 10 , false } ,
}
for _ , testCase := range testCases {
loc . ExternalAuth . URL = testCase . authURL
loc . ExternalAuth . KeepaliveConnections = testCase . keepaliveConnections
result := shouldApplyAuthUpstream ( loc , cfg )
if result != testCase . expected {
t . Errorf ( "%v: expected '%v' but returned '%v'" , testCase . title , testCase . expected , result )
}
}
// keepalive is not supported with UseHTTP2
cfg . UseHTTP2 = true
for _ , testCase := range testCases {
loc . ExternalAuth . URL = testCase . authURL
loc . ExternalAuth . KeepaliveConnections = testCase . keepaliveConnections
result := shouldApplyAuthUpstream ( loc , cfg )
if result != false {
t . Errorf ( "%v: expected '%v' but returned '%v'" , testCase . title , false , result )
}
}
}
func TestExtractHostPort ( t * testing . T ) {
testCases := [ ] struct {
title string
url string
expected string
} {
{ "full URL" , "https://my.auth.site:5000/path" , "my.auth.site:5000" } ,
{ "URL with no port" , "http://my.auth.site/path" , "my.auth.site" } ,
{ "URL with no path" , "https://my.auth.site:5000" , "my.auth.site:5000" } ,
{ "URL no port and path" , "http://my.auth.site" , "my.auth.site" } ,
{ "missing method" , "my.auth.site/path" , "" } ,
{ "all empty" , "" , "" } ,
}
for _ , testCase := range testCases {
result := extractHostPort ( testCase . url )
if result != testCase . expected {
t . Errorf ( "%v: expected '%v' but returned '%v'" , testCase . title , testCase . expected , result )
}
}
}
func TestChangeHostPort ( t * testing . T ) {
testCases := [ ] struct {
title string
url string
host string
expected string
} {
{ "full URL" , "https://my.auth.site:5000/path" , "your.domain" , "https://your.domain/path" } ,
{ "URL with no port" , "http://my.auth.site/path" , "your.domain" , "http://your.domain/path" } ,
{ "URL with no path" , "http://my.auth.site:5000" , "your.domain:8888" , "http://your.domain:8888" } ,
{ "URL with no port and path" , "https://my.auth.site" , "your.domain:8888" , "https://your.domain:8888" } ,
{ "invalid host" , "my.auth.site/path" , "" , "" } ,
{ "missing method" , "my.auth.site/path" , "your.domain" , "" } ,
{ "all empty" , "" , "" , "" } ,
}
for _ , testCase := range testCases {
result := changeHostPort ( testCase . url , testCase . host )
if result != testCase . expected {
t . Errorf ( "%v: expected '%v' but returned '%v'" , testCase . title , testCase . expected , result )
}
}
}
2016-11-16 18:24:26 +00:00
func TestTemplateWithData ( t * testing . T ) {
pwd , _ := os . Getwd ( )
2017-11-05 01:18:28 +00:00
f , err := os . Open ( path . Join ( pwd , "../../../../test/data/config.json" ) )
2016-11-16 18:24:26 +00:00
if err != nil {
t . Errorf ( "unexpected error reading json file: %v" , err )
}
defer f . Close ( )
2021-08-06 14:18:17 +00:00
data , err := os . ReadFile ( f . Name ( ) )
2016-11-16 18:24:26 +00:00
if err != nil {
t . Error ( "unexpected error reading json file: " , err )
}
var dat config . TemplateConfig
2018-09-22 17:25:01 +00:00
if err := jsoniter . ConfigCompatibleWithStandardLibrary . Unmarshal ( data , & dat ) ; err != nil {
2016-11-16 18:24:26 +00:00
t . Errorf ( "unexpected error unmarshalling json: %v" , err )
}
2017-08-24 13:33:26 +00:00
if dat . ListenPorts == nil {
dat . ListenPorts = & config . ListenPorts { }
}
2017-11-22 13:40:54 +00:00
2019-08-13 21:14:55 +00:00
ngxTpl , err := NewTemplate ( nginx . TemplatePath )
2016-11-16 18:24:26 +00:00
if err != nil {
t . Errorf ( "invalid NGINX template: %v" , err )
}
2019-08-13 21:14:55 +00:00
dat . Cfg . DefaultSSLCertificate = & ingress . SSLCert { }
2018-05-16 18:08:43 +00:00
rt , err := ngxTpl . Write ( dat )
2016-11-16 18:24:26 +00:00
if err != nil {
t . Errorf ( "invalid NGINX template: %v" , err )
}
2018-05-16 18:08:43 +00:00
if ! strings . Contains ( string ( rt ) , "listen [2001:db8:a0b:12f0::1]" ) {
t . Errorf ( "invalid NGINX template, expected IPV6 listen address not present" )
}
if ! strings . Contains ( string ( rt ) , "listen [3731:54:65fe:2::a7]" ) {
t . Errorf ( "invalid NGINX template, expected IPV6 listen address not present" )
}
if ! strings . Contains ( string ( rt ) , "listen 2.2.2.2" ) {
t . Errorf ( "invalid NGINX template, expected IPV4 listen address not present" )
}
2016-11-16 18:24:26 +00:00
}
func BenchmarkTemplateWithData ( b * testing . B ) {
pwd , _ := os . Getwd ( )
2017-11-05 01:18:28 +00:00
f , err := os . Open ( path . Join ( pwd , "../../../../test/data/config.json" ) )
2016-11-16 18:24:26 +00:00
if err != nil {
b . Errorf ( "unexpected error reading json file: %v" , err )
}
defer f . Close ( )
2021-08-06 14:18:17 +00:00
data , err := os . ReadFile ( f . Name ( ) )
2016-11-16 18:24:26 +00:00
if err != nil {
b . Error ( "unexpected error reading json file: " , err )
}
var dat config . TemplateConfig
2018-09-22 17:25:01 +00:00
if err := jsoniter . ConfigCompatibleWithStandardLibrary . Unmarshal ( data , & dat ) ; err != nil {
2016-11-16 18:24:26 +00:00
b . Errorf ( "unexpected error unmarshalling json: %v" , err )
}
2019-08-13 21:14:55 +00:00
ngxTpl , err := NewTemplate ( nginx . TemplatePath )
2016-11-16 18:24:26 +00:00
if err != nil {
b . Errorf ( "invalid NGINX template: %v" , err )
}
for i := 0 ; i < b . N ; i ++ {
2017-02-20 02:34:05 +00:00
ngxTpl . Write ( dat )
2016-11-16 18:24:26 +00:00
}
}
2017-06-02 03:30:22 +00:00
func TestBuildDenyVariable ( t * testing . T ) {
2019-01-29 06:21:08 +00:00
invalidType := & ingress . Ingress { }
expected := ""
actual := buildDenyVariable ( invalidType )
if expected != actual {
t . Errorf ( "Expected '%v' but returned '%v'" , expected , actual )
}
2017-06-02 03:30:22 +00:00
a := buildDenyVariable ( "host1.example.com_/.well-known/acme-challenge" )
b := buildDenyVariable ( "host1.example.com_/.well-known/acme-challenge" )
if ! reflect . DeepEqual ( a , b ) {
t . Errorf ( "Expected '%v' but returned '%v'" , a , b )
}
}
2017-08-23 04:57:35 +00:00
2018-11-09 14:45:11 +00:00
func TestBuildByteSize ( t * testing . T ) {
2018-11-30 01:45:32 +00:00
cases := [ ] struct {
2018-12-02 18:10:36 +00:00
value interface { }
isOffset bool
2018-11-30 01:45:32 +00:00
expected bool
} {
2018-12-02 18:10:36 +00:00
{ "1000" , false , true } ,
{ "1000k" , false , true } ,
{ "1m" , false , true } ,
{ "10g" , false , false } ,
{ " 1m " , false , true } ,
{ "1000kk" , false , false } ,
{ "1000km" , false , false } ,
{ "1mm" , false , false } ,
{ nil , false , false } ,
{ "" , false , false } ,
{ " " , false , false } ,
{ "1G" , true , true } ,
{ "1000kk" , true , false } ,
{ "" , true , false } ,
2018-11-30 01:45:32 +00:00
}
for _ , tc := range cases {
2018-12-02 18:10:36 +00:00
val := isValidByteSize ( tc . value , tc . isOffset )
2018-11-30 01:45:32 +00:00
if tc . expected != val {
t . Errorf ( "Expected '%v' but returned '%v'" , tc . expected , val )
}
2017-08-23 04:57:35 +00:00
}
}
2017-09-09 05:10:38 +00:00
func TestIsLocationAllowed ( t * testing . T ) {
2019-01-29 06:21:08 +00:00
invalidType := & ingress . Ingress { }
expected := false
actual := isLocationAllowed ( invalidType )
if expected != actual {
t . Errorf ( "Expected '%v' but returned '%v'" , expected , actual )
}
2017-09-09 05:10:38 +00:00
loc := ingress . Location {
Denied : nil ,
}
isAllowed := isLocationAllowed ( & loc )
if ! isAllowed {
t . Errorf ( "Expected '%v' but returned '%v'" , true , isAllowed )
}
}
func TestBuildForwardedFor ( t * testing . T ) {
2019-01-29 06:21:08 +00:00
invalidType := & ingress . Ingress { }
expected := ""
actual := buildForwardedFor ( invalidType )
if expected != actual {
t . Errorf ( "Expected '%v' but returned '%v'" , expected , actual )
}
2017-09-09 05:10:38 +00:00
2019-01-29 06:21:08 +00:00
inputStr := "X-Forwarded-For"
expected = "$http_x_forwarded_for"
actual = buildForwardedFor ( inputStr )
2017-09-09 05:10:38 +00:00
2018-07-18 02:27:10 +00:00
if expected != actual {
t . Errorf ( "Expected '%v' but returned '%v'" , expected , actual )
}
}
2017-09-09 05:10:38 +00:00
func TestBuildResolvers ( t * testing . T ) {
ipOne := net . ParseIP ( "192.0.0.1" )
ipTwo := net . ParseIP ( "2001:db8:1234:0000:0000:0000:0000:0000" )
ipList := [ ] net . IP { ipOne , ipTwo }
2019-01-29 06:21:08 +00:00
invalidType := & ingress . Ingress { }
expected := ""
actual := buildResolvers ( invalidType , false )
// Invalid Type for []net.IP
if expected != actual {
t . Errorf ( "Expected '%v' but returned '%v'" , expected , actual )
}
actual = buildResolvers ( ipList , invalidType )
// Invalid Type for bool
if expected != actual {
t . Errorf ( "Expected '%v' but returned '%v'" , expected , actual )
}
2017-09-09 05:10:38 +00:00
validResolver := "resolver 192.0.0.1 [2001:db8:1234::] valid=30s;"
2018-02-02 19:53:28 +00:00
resolver := buildResolvers ( ipList , false )
if resolver != validResolver {
t . Errorf ( "Expected '%v' but returned '%v'" , validResolver , resolver )
}
validResolver = "resolver 192.0.0.1 valid=30s ipv6=off;"
resolver = buildResolvers ( ipList , true )
2017-09-09 05:10:38 +00:00
if resolver != validResolver {
t . Errorf ( "Expected '%v' but returned '%v'" , validResolver , resolver )
}
}
func TestBuildNextUpstream ( t * testing . T ) {
2019-01-29 06:21:08 +00:00
invalidType := & ingress . Ingress { }
expected := ""
actual := buildNextUpstream ( invalidType , "" )
if expected != actual {
t . Errorf ( "Expected '%v' but returned '%v'" , expected , actual )
}
2017-10-10 10:18:45 +00:00
cases := map [ string ] struct {
NextUpstream string
NonIdempotent bool
Output string
} {
"default" : {
"timeout http_500 http_502" ,
false ,
"timeout http_500 http_502" ,
} ,
"global" : {
"timeout http_500 http_502" ,
true ,
"timeout http_500 http_502 non_idempotent" ,
} ,
"local" : {
"timeout http_500 http_502 non_idempotent" ,
false ,
"timeout http_500 http_502 non_idempotent" ,
} ,
}
2017-09-09 05:10:38 +00:00
2017-10-10 10:18:45 +00:00
for k , tc := range cases {
nextUpstream := buildNextUpstream ( tc . NextUpstream , tc . NonIdempotent )
if nextUpstream != tc . Output {
t . Errorf (
"%s: called buildNextUpstream('%s', %v); expected '%v' but returned '%v'" ,
k ,
tc . NextUpstream ,
tc . NonIdempotent ,
tc . Output ,
nextUpstream ,
)
}
2017-09-09 05:10:38 +00:00
}
}
func TestBuildRateLimit ( t * testing . T ) {
2019-01-29 06:21:08 +00:00
invalidType := & ingress . Ingress { }
expected := [ ] string { }
actual := buildRateLimit ( invalidType )
if ! reflect . DeepEqual ( expected , actual ) {
t . Errorf ( "Expected '%v' but returned '%v'" , expected , actual )
}
2017-10-01 00:48:14 +00:00
loc := & ingress . Location { }
2017-09-09 05:10:38 +00:00
loc . RateLimit . Connections . Name = "con"
loc . RateLimit . Connections . Limit = 1
loc . RateLimit . RPS . Name = "rps"
loc . RateLimit . RPS . Limit = 1
loc . RateLimit . RPS . Burst = 1
loc . RateLimit . RPM . Name = "rpm"
loc . RateLimit . RPM . Limit = 2
loc . RateLimit . RPM . Burst = 2
loc . RateLimit . LimitRateAfter = 1
loc . RateLimit . LimitRate = 1
validLimits := [ ] string {
"limit_conn con 1;" ,
"limit_req zone=rps burst=1 nodelay;" ,
"limit_req zone=rpm burst=2 nodelay;" ,
"limit_rate_after 1k;" ,
"limit_rate 1k;" ,
}
limits := buildRateLimit ( loc )
for i , limit := range limits {
if limit != validLimits [ i ] {
t . Errorf ( "Expected '%v' but returned '%v'" , validLimits , limits )
}
}
2019-01-29 06:21:08 +00:00
// Invalid limit
limits = buildRateLimit ( & ingress . Ingress { } )
if ! reflect . DeepEqual ( expected , limits ) {
t . Errorf ( "Expected '%v' but returned '%v'" , expected , limits )
}
}
// TODO: Needs more tests
func TestBuildRateLimitZones ( t * testing . T ) {
invalidType := & ingress . Ingress { }
expected := [ ] string { }
actual := buildRateLimitZones ( invalidType )
if ! reflect . DeepEqual ( expected , actual ) {
t . Errorf ( "Expected '%v' but returned '%v'" , expected , actual )
}
}
// TODO: Needs more tests
func TestFilterRateLimits ( t * testing . T ) {
invalidType := & ingress . Ingress { }
expected := [ ] ratelimit . Config { }
actual := filterRateLimits ( invalidType )
if ! reflect . DeepEqual ( expected , actual ) {
t . Errorf ( "Expected '%v' but returned '%v'" , expected , actual )
}
2017-09-09 05:10:38 +00:00
}
2017-10-05 04:55:42 +00:00
func TestBuildAuthSignURL ( t * testing . T ) {
cases := map [ string ] struct {
2020-08-18 09:03:38 +00:00
Input , RedirectParam , Output string
2017-10-05 04:55:42 +00:00
} {
2020-08-18 09:03:38 +00:00
"default url and redirect" : { "http://google.com" , "rd" , "http://google.com?rd=$pass_access_scheme://$http_host$escaped_request_uri" } ,
"default url and custom redirect" : { "http://google.com" , "orig" , "http://google.com?orig=$pass_access_scheme://$http_host$escaped_request_uri" } ,
"with random field" : { "http://google.com?cat=0" , "rd" , "http://google.com?cat=0&rd=$pass_access_scheme://$http_host$escaped_request_uri" } ,
"with random field and custom redirect" : { "http://google.com?cat=0" , "orig" , "http://google.com?cat=0&orig=$pass_access_scheme://$http_host$escaped_request_uri" } ,
"with rd field" : { "http://google.com?cat&rd=$request" , "rd" , "http://google.com?cat&rd=$request" } ,
"with orig field" : { "http://google.com?cat&orig=$request" , "orig" , "http://google.com?cat&orig=$request" } ,
2017-10-05 04:55:42 +00:00
}
for k , tc := range cases {
2020-08-18 09:03:38 +00:00
res := buildAuthSignURL ( tc . Input , tc . RedirectParam )
2017-10-05 04:55:42 +00:00
if res != tc . Output {
2020-08-18 09:03:38 +00:00
t . Errorf ( "%s: called buildAuthSignURL('%s','%s'); expected '%v' but returned '%v'" , k , tc . Input , tc . RedirectParam , tc . Output , res )
2017-10-05 04:55:42 +00:00
}
}
}
2018-03-18 20:44:59 +00:00
func TestIsLocationInLocationList ( t * testing . T ) {
2019-01-29 06:21:08 +00:00
invalidType := & ingress . Ingress { }
expected := false
actual := isLocationInLocationList ( invalidType , "" )
if expected != actual {
t . Errorf ( "Expected '%v' but returned '%v'" , expected , actual )
}
2018-03-18 20:44:59 +00:00
testCases := [ ] struct {
location * ingress . Location
rawLocationList string
expected bool
} {
{ & ingress . Location { Path : "/match" } , "/match" , true } ,
{ & ingress . Location { Path : "/match" } , ",/match" , true } ,
{ & ingress . Location { Path : "/match" } , "/dontmatch" , false } ,
{ & ingress . Location { Path : "/match" } , ",/dontmatch" , false } ,
{ & ingress . Location { Path : "/match" } , "/dontmatch,/match" , true } ,
{ & ingress . Location { Path : "/match" } , "/dontmatch,/dontmatcheither" , false } ,
}
for _ , testCase := range testCases {
result := isLocationInLocationList ( testCase . location , testCase . rawLocationList )
if result != testCase . expected {
t . Errorf ( " expected %v but return %v, path: '%s', rawLocation: '%s'" , testCase . expected , result , testCase . location . Path , testCase . rawLocationList )
}
}
}
2018-04-12 18:01:46 +00:00
func TestBuildUpstreamName ( t * testing . T ) {
2019-01-29 06:21:08 +00:00
invalidType := & ingress . Ingress { }
expected := ""
actual := buildUpstreamName ( invalidType )
if expected != actual {
t . Errorf ( "Expected '%v' but returned '%v'" , expected , actual )
}
2018-04-12 18:01:46 +00:00
defaultBackend := "upstream-name"
defaultHost := "example.com"
for k , tc := range tmplFuncTestcases {
loc := & ingress . Location {
Path : tc . Path ,
2021-08-21 20:42:00 +00:00
PathType : & pathPrefix ,
2018-12-13 18:02:05 +00:00
Rewrite : rewrite . Config { Target : tc . Target } ,
2018-04-12 18:01:46 +00:00
Backend : defaultBackend ,
XForwardedPrefix : tc . XForwardedPrefix ,
}
2018-10-08 15:26:06 +00:00
if tc . SecureBackend {
loc . BackendProtocol = "HTTPS"
}
2018-04-12 18:01:46 +00:00
backend := & ingress . Backend {
2018-10-08 15:26:06 +00:00
Name : defaultBackend ,
2018-04-12 18:01:46 +00:00
}
expected := defaultBackend
if tc . Sticky {
backend . SessionAffinity = ingress . SessionAffinityConfig {
AffinityType : "cookie" ,
CookieSessionAffinity : ingress . CookieSessionAffinity {
Locations : map [ string ] [ ] string {
defaultHost : { tc . Path } ,
} ,
} ,
}
}
2018-10-09 22:36:10 +00:00
pp := buildUpstreamName ( loc )
2018-04-12 18:01:46 +00:00
if ! strings . EqualFold ( expected , pp ) {
t . Errorf ( "%s: expected \n'%v'\nbut returned \n'%v'" , k , expected , pp )
}
}
2018-04-12 18:14:43 +00:00
}
2018-10-03 22:05:12 +00:00
2018-10-09 19:58:50 +00:00
func TestEscapeLiteralDollar ( t * testing . T ) {
escapedPath := escapeLiteralDollar ( "/$" )
2018-10-03 22:05:12 +00:00
expected := "/${literal_dollar}"
if escapedPath != expected {
2018-10-09 19:58:50 +00:00
t . Errorf ( "Expected %v but returned %v" , expected , escapedPath )
2018-10-03 22:05:12 +00:00
}
2018-10-09 19:58:50 +00:00
escapedPath = escapeLiteralDollar ( "/hello-$/world-$/" )
expected = "/hello-${literal_dollar}/world-${literal_dollar}/"
if escapedPath != expected {
t . Errorf ( "Expected %v but returned %v" , expected , escapedPath )
}
2020-11-27 17:26:53 +00:00
leaveUnchanged := "/leave-me/unchanged"
escapedPath = escapeLiteralDollar ( leaveUnchanged )
if escapedPath != leaveUnchanged {
t . Errorf ( "Expected %v but returned %v" , leaveUnchanged , escapedPath )
2018-10-09 19:58:50 +00:00
}
escapedPath = escapeLiteralDollar ( false )
2018-10-03 22:05:12 +00:00
expected = ""
if escapedPath != expected {
2018-10-09 19:58:50 +00:00
t . Errorf ( "Expected %v but returned %v" , expected , escapedPath )
2018-10-03 22:05:12 +00:00
}
}
2018-11-27 13:35:29 +00:00
2018-12-04 19:59:54 +00:00
func TestOpentracingPropagateContext ( t * testing . T ) {
2020-01-26 00:39:20 +00:00
tests := map [ * ingress . Location ] string {
2021-07-29 19:49:19 +00:00
{ BackendProtocol : "HTTP" } : "opentracing_propagate_context;" ,
{ BackendProtocol : "HTTPS" } : "opentracing_propagate_context;" ,
{ BackendProtocol : "AUTO_HTTP" } : "opentracing_propagate_context;" ,
{ BackendProtocol : "GRPC" } : "opentracing_grpc_propagate_context;" ,
{ BackendProtocol : "GRPCS" } : "opentracing_grpc_propagate_context;" ,
2023-06-13 19:55:59 +00:00
{ BackendProtocol : "FCGI" } : "opentracing_propagate_context;" ,
2021-07-29 19:49:19 +00:00
nil : "" ,
2018-11-27 13:35:29 +00:00
}
for loc , expectedDirective := range tests {
actualDirective := opentracingPropagateContext ( loc )
if actualDirective != expectedDirective {
t . Errorf ( "Expected %v but returned %v" , expectedDirective , actualDirective )
}
}
}
2018-12-18 03:23:28 +00:00
2023-03-22 18:58:22 +00:00
func TestOpentelemetryPropagateContext ( t * testing . T ) {
tests := map [ * ingress . Location ] string {
{ BackendProtocol : "HTTP" } : "opentelemetry_propagate;" ,
{ BackendProtocol : "HTTPS" } : "opentelemetry_propagate;" ,
{ BackendProtocol : "AUTO_HTTP" } : "opentelemetry_propagate;" ,
{ BackendProtocol : "GRPC" } : "opentelemetry_propagate;" ,
{ BackendProtocol : "GRPCS" } : "opentelemetry_propagate;" ,
2023-06-13 19:55:59 +00:00
{ BackendProtocol : "FCGI" } : "opentelemetry_propagate;" ,
2023-03-22 18:58:22 +00:00
nil : "" ,
}
for loc , expectedDirective := range tests {
actualDirective := opentelemetryPropagateContext ( loc )
if actualDirective != expectedDirective {
t . Errorf ( "Expected %v but returned %v" , expectedDirective , actualDirective )
}
}
}
2018-12-18 03:23:28 +00:00
func TestGetIngressInformation ( t * testing . T ) {
2019-08-31 15:24:01 +00:00
testcases := map [ string ] struct {
Ingress interface { }
Host string
Path interface { }
Expected * ingressInformation
} {
"wrong ingress type" : {
"wrongtype" ,
"host1" ,
"/ok" ,
& ingressInformation { } ,
2018-12-18 03:23:28 +00:00
} ,
2019-08-31 15:24:01 +00:00
"wrong path type" : {
& ingress . Ingress { } ,
"host1" ,
10 ,
& ingressInformation { } ,
} ,
2021-08-21 20:42:00 +00:00
"valid ingress definition with name validIng in namespace default using a service with name a-svc port number 8080" : {
& ingress . Ingress {
Ingress : networking . Ingress {
ObjectMeta : metav1 . ObjectMeta {
Name : "validIng" ,
Namespace : apiv1 . NamespaceDefault ,
Annotations : map [ string ] string {
"ingress.annotation" : "ok" ,
} ,
} ,
Spec : networking . IngressSpec {
DefaultBackend : & networking . IngressBackend {
Service : & networking . IngressServiceBackend {
Name : "a-svc" ,
Port : networking . ServiceBackendPort {
Number : 8080 ,
} ,
} ,
} ,
} ,
} ,
} ,
"host1" ,
"" ,
& ingressInformation {
Namespace : "default" ,
Rule : "validIng" ,
Path : "/" ,
Annotations : map [ string ] string {
"ingress.annotation" : "ok" ,
} ,
Service : "a-svc" ,
ServicePort : "8080" ,
} ,
} ,
"valid ingress definition with name validIng in namespace default using a service with name a-svc port name b-svc" : {
& ingress . Ingress {
Ingress : networking . Ingress {
ObjectMeta : metav1 . ObjectMeta {
Name : "validIng" ,
Namespace : apiv1 . NamespaceDefault ,
Annotations : map [ string ] string {
"ingress.annotation" : "ok" ,
} ,
} ,
Spec : networking . IngressSpec {
DefaultBackend : & networking . IngressBackend {
Service : & networking . IngressServiceBackend {
Name : "a-svc" ,
Port : networking . ServiceBackendPort {
Name : "b-svc" ,
} ,
} ,
} ,
} ,
} ,
} ,
"host1" ,
"" ,
& ingressInformation {
Namespace : "default" ,
Rule : "validIng" ,
Path : "/" ,
Annotations : map [ string ] string {
"ingress.annotation" : "ok" ,
} ,
Service : "a-svc" ,
ServicePort : "b-svc" ,
} ,
} ,
2019-08-31 15:24:01 +00:00
"valid ingress definition with name validIng in namespace default" : {
& ingress . Ingress {
2020-01-07 13:53:12 +00:00
Ingress : networking . Ingress {
2019-08-31 15:24:01 +00:00
ObjectMeta : metav1 . ObjectMeta {
Name : "validIng" ,
Namespace : apiv1 . NamespaceDefault ,
Annotations : map [ string ] string {
"ingress.annotation" : "ok" ,
} ,
} ,
Spec : networking . IngressSpec {
2021-08-21 20:42:00 +00:00
DefaultBackend : & networking . IngressBackend {
Service : & networking . IngressServiceBackend {
Name : "a-svc" ,
} ,
2018-12-18 03:23:28 +00:00
} ,
} ,
} ,
2019-08-31 15:24:01 +00:00
} ,
"host1" ,
"" ,
& ingressInformation {
Namespace : "default" ,
Rule : "validIng" ,
2021-08-21 20:42:00 +00:00
Path : "/" ,
2019-08-31 15:24:01 +00:00
Annotations : map [ string ] string {
"ingress.annotation" : "ok" ,
} ,
Service : "a-svc" ,
2018-12-18 03:23:28 +00:00
} ,
} ,
2019-08-31 15:24:01 +00:00
"valid ingress definition with name demo in namespace something and path /ok using a service with name b-svc port 80" : {
& ingress . Ingress {
2020-01-07 13:53:12 +00:00
Ingress : networking . Ingress {
2019-08-31 15:24:01 +00:00
ObjectMeta : metav1 . ObjectMeta {
Name : "demo" ,
Namespace : "something" ,
Annotations : map [ string ] string {
"ingress.annotation" : "ok" ,
2019-03-07 11:18:06 +00:00
} ,
} ,
2019-08-31 15:24:01 +00:00
Spec : networking . IngressSpec {
Rules : [ ] networking . IngressRule {
{
Host : "foo.bar" ,
IngressRuleValue : networking . IngressRuleValue {
HTTP : & networking . HTTPIngressRuleValue {
Paths : [ ] networking . HTTPIngressPath {
{
2021-08-21 20:42:00 +00:00
Path : "/ok" ,
PathType : & pathPrefix ,
2019-08-31 15:24:01 +00:00
Backend : networking . IngressBackend {
2021-08-21 20:42:00 +00:00
Service : & networking . IngressServiceBackend {
Name : "b-svc" ,
Port : networking . ServiceBackendPort {
Number : 80 ,
} ,
} ,
2019-08-31 15:24:01 +00:00
} ,
} ,
} ,
} ,
} ,
} ,
{ } ,
} ,
} ,
} ,
} ,
"foo.bar" ,
"/ok" ,
& ingressInformation {
Namespace : "something" ,
Rule : "demo" ,
Annotations : map [ string ] string {
"ingress.annotation" : "ok" ,
2019-03-07 11:18:06 +00:00
} ,
2019-08-31 15:24:01 +00:00
Service : "b-svc" ,
ServicePort : "80" ,
2019-03-07 11:18:06 +00:00
} ,
} ,
2021-08-21 20:42:00 +00:00
"valid ingress definition with name demo in namespace something and path /ok using a service with name b-svc port name b-svc-80" : {
& ingress . Ingress {
Ingress : networking . Ingress {
ObjectMeta : metav1 . ObjectMeta {
Name : "demo" ,
Namespace : "something" ,
Annotations : map [ string ] string {
"ingress.annotation" : "ok" ,
} ,
} ,
Spec : networking . IngressSpec {
Rules : [ ] networking . IngressRule {
{
Host : "foo.bar" ,
IngressRuleValue : networking . IngressRuleValue {
HTTP : & networking . HTTPIngressRuleValue {
Paths : [ ] networking . HTTPIngressPath {
{
Path : "/ok" ,
PathType : & pathPrefix ,
Backend : networking . IngressBackend {
Service : & networking . IngressServiceBackend {
Name : "b-svc" ,
Port : networking . ServiceBackendPort {
Name : "b-svc-80" ,
} ,
} ,
} ,
} ,
} ,
} ,
} ,
} ,
{ } ,
} ,
} ,
} ,
} ,
"foo.bar" ,
"/ok" ,
& ingressInformation {
Namespace : "something" ,
Rule : "demo" ,
Annotations : map [ string ] string {
"ingress.annotation" : "ok" ,
} ,
Service : "b-svc" ,
ServicePort : "b-svc-80" ,
} ,
} ,
2021-08-26 00:45:23 +00:00
"valid ingress definition with name demo in namespace something and path /ok with a nil backend service" : {
& ingress . Ingress {
Ingress : networking . Ingress {
ObjectMeta : metav1 . ObjectMeta {
Name : "demo" ,
Namespace : "something" ,
Annotations : map [ string ] string {
"ingress.annotation" : "ok" ,
} ,
} ,
Spec : networking . IngressSpec {
Rules : [ ] networking . IngressRule {
{
Host : "foo.bar" ,
IngressRuleValue : networking . IngressRuleValue {
HTTP : & networking . HTTPIngressRuleValue {
Paths : [ ] networking . HTTPIngressPath {
{
Path : "/ok" ,
PathType : & pathPrefix ,
Backend : networking . IngressBackend {
Service : nil ,
} ,
} ,
} ,
} ,
} ,
} ,
{ } ,
} ,
} ,
} ,
} ,
"foo.bar" ,
"/ok" ,
& ingressInformation {
Namespace : "something" ,
Rule : "demo" ,
Annotations : map [ string ] string {
"ingress.annotation" : "ok" ,
} ,
} ,
} ,
"valid ingress definition with name demo in namespace something and path /ok with both a nil service and a valid one" : {
& ingress . Ingress {
Ingress : networking . Ingress {
ObjectMeta : metav1 . ObjectMeta {
Name : "demo" ,
Namespace : "something" ,
Annotations : map [ string ] string {
"ingress.annotation" : "ok" ,
} ,
} ,
Spec : networking . IngressSpec {
Rules : [ ] networking . IngressRule {
{
Host : "foo.bar" ,
IngressRuleValue : networking . IngressRuleValue {
HTTP : & networking . HTTPIngressRuleValue {
Paths : [ ] networking . HTTPIngressPath {
{
Path : "/ok" ,
PathType : & pathPrefix ,
Backend : networking . IngressBackend {
Service : nil ,
} ,
} ,
{
Path : "/oksvc" ,
PathType : & pathPrefix ,
Backend : networking . IngressBackend {
Service : & networking . IngressServiceBackend {
Name : "b-svc" ,
Port : networking . ServiceBackendPort {
Name : "b-svc-80" ,
} ,
} ,
} ,
} ,
} ,
} ,
} ,
} ,
{ } ,
} ,
} ,
} ,
} ,
"foo.bar" ,
"/oksvc" ,
& ingressInformation {
Namespace : "something" ,
Rule : "demo" ,
Annotations : map [ string ] string {
"ingress.annotation" : "ok" ,
} ,
Service : "b-svc" ,
ServicePort : "b-svc-80" ,
} ,
} ,
2019-03-07 11:18:06 +00:00
}
2019-08-31 15:24:01 +00:00
for title , testCase := range testcases {
info := getIngressInformation ( testCase . Ingress , testCase . Host , testCase . Path )
if ! info . Equal ( testCase . Expected ) {
t . Fatalf ( "%s: expected '%v' but returned %v" , title , testCase . Expected , info )
}
2019-03-07 11:18:06 +00:00
}
2018-12-18 03:23:28 +00:00
}
2019-01-29 06:21:08 +00:00
2019-02-05 14:28:37 +00:00
func TestBuildCustomErrorLocationsPerServer ( t * testing . T ) {
testCases := [ ] struct {
server interface { }
expectedResults [ ] errorLocation
} {
{ // Single ingress
& ingress . Server { Locations : [ ] * ingress . Location {
{
DefaultBackendUpstreamName : "custom-default-backend-test-backend" ,
CustomHTTPErrors : [ ] int { 401 , 402 } ,
} ,
} } ,
[ ] errorLocation {
{
UpstreamName : "custom-default-backend-test-backend" ,
Codes : [ ] int { 401 , 402 } ,
} ,
} ,
} ,
{ // Two ingresses, overlapping error codes, same backend
& ingress . Server { Locations : [ ] * ingress . Location {
{
DefaultBackendUpstreamName : "custom-default-backend-test-backend" ,
CustomHTTPErrors : [ ] int { 401 , 402 } ,
} ,
{
DefaultBackendUpstreamName : "custom-default-backend-test-backend" ,
CustomHTTPErrors : [ ] int { 402 , 403 } ,
} ,
} } ,
[ ] errorLocation {
{
UpstreamName : "custom-default-backend-test-backend" ,
Codes : [ ] int { 401 , 402 , 403 } ,
} ,
} ,
} ,
{ // Two ingresses, overlapping error codes, different backends
& ingress . Server { Locations : [ ] * ingress . Location {
{
DefaultBackendUpstreamName : "custom-default-backend-test-one" ,
CustomHTTPErrors : [ ] int { 401 , 402 } ,
} ,
{
DefaultBackendUpstreamName : "custom-default-backend-test-two" ,
CustomHTTPErrors : [ ] int { 402 , 403 } ,
} ,
} } ,
[ ] errorLocation {
{
UpstreamName : "custom-default-backend-test-one" ,
Codes : [ ] int { 401 , 402 } ,
} ,
{
UpstreamName : "custom-default-backend-test-two" ,
Codes : [ ] int { 402 , 403 } ,
} ,
} ,
} ,
{ // Many ingresses, overlapping error codes, different backends
& ingress . Server { Locations : [ ] * ingress . Location {
{
DefaultBackendUpstreamName : "custom-default-backend-test-one" ,
CustomHTTPErrors : [ ] int { 401 , 402 } ,
} ,
{
DefaultBackendUpstreamName : "custom-default-backend-test-one" ,
CustomHTTPErrors : [ ] int { 501 , 502 } ,
} ,
{
DefaultBackendUpstreamName : "custom-default-backend-test-two" ,
CustomHTTPErrors : [ ] int { 409 , 410 } ,
} ,
{
DefaultBackendUpstreamName : "custom-default-backend-test-two" ,
CustomHTTPErrors : [ ] int { 504 , 505 } ,
} ,
} } ,
[ ] errorLocation {
{
UpstreamName : "custom-default-backend-test-one" ,
Codes : [ ] int { 401 , 402 , 501 , 502 } ,
} ,
{
UpstreamName : "custom-default-backend-test-two" ,
Codes : [ ] int { 409 , 410 , 504 , 505 } ,
} ,
} ,
2019-01-29 06:21:08 +00:00
} ,
}
2019-02-05 14:28:37 +00:00
for _ , c := range testCases {
response := buildCustomErrorLocationsPerServer ( c . server )
2019-12-17 15:06:17 +00:00
if ! reflect . DeepEqual ( c . expectedResults , response ) {
t . Errorf ( "Expected %+v but got %+v" , c . expectedResults , response )
2019-02-05 14:28:37 +00:00
}
2019-01-29 06:21:08 +00:00
}
}
func TestProxySetHeader ( t * testing . T ) {
2021-07-13 06:08:29 +00:00
tests := [ ] struct {
name string
loc interface { }
expected string
} {
{
name : "nil" ,
loc : nil ,
expected : "proxy_set_header" ,
} ,
{
name : "invalid type" ,
loc : & ingress . Ingress { } ,
expected : "proxy_set_header" ,
} ,
{
name : "http backend" ,
loc : & ingress . Location { } ,
expected : "proxy_set_header" ,
} ,
{
name : "gRPC backend" ,
loc : & ingress . Location {
BackendProtocol : "GRPC" ,
} ,
expected : "grpc_set_header" ,
} ,
2019-01-29 06:21:08 +00:00
}
2021-07-13 06:08:29 +00:00
for _ , tt := range tests {
t . Run ( tt . name , func ( t * testing . T ) {
if got := proxySetHeader ( tt . loc ) ; got != tt . expected {
t . Errorf ( "proxySetHeader() = %v, expected %v" , got , tt . expected )
}
} )
2019-01-29 06:21:08 +00:00
}
}
func TestBuildOpenTracing ( t * testing . T ) {
invalidType := & ingress . Ingress { }
expected := ""
2020-01-29 15:20:05 +00:00
actual := buildOpentracing ( invalidType , [ ] * ingress . Server { } )
2019-01-29 06:21:08 +00:00
if expected != actual {
t . Errorf ( "Expected '%v' but returned '%v'" , expected , actual )
}
2021-02-18 19:40:04 +00:00
cfgNoHost := config . Configuration {
EnableOpentracing : true ,
}
expected = "\r\n"
actual = buildOpentracing ( cfgNoHost , [ ] * ingress . Server { } )
if expected != actual {
t . Errorf ( "Expected '%v' but returned '%v'" , expected , actual )
}
2019-01-29 06:21:08 +00:00
cfgJaeger := config . Configuration {
EnableOpentracing : true ,
JaegerCollectorHost : "jaeger-host.com" ,
}
expected = "opentracing_load_tracer /usr/local/lib/libjaegertracing_plugin.so /etc/nginx/opentracing.json;\r\n"
2020-01-29 15:20:05 +00:00
actual = buildOpentracing ( cfgJaeger , [ ] * ingress . Server { } )
2019-01-29 06:21:08 +00:00
if expected != actual {
t . Errorf ( "Expected '%v' but returned '%v'" , expected , actual )
}
cfgZipkin := config . Configuration {
EnableOpentracing : true ,
ZipkinCollectorHost : "zipkin-host.com" ,
}
2020-09-08 18:29:34 +00:00
expected = "opentracing_load_tracer /usr/local/lib/libzipkin_opentracing_plugin.so /etc/nginx/opentracing.json;\r\n"
2020-01-29 15:20:05 +00:00
actual = buildOpentracing ( cfgZipkin , [ ] * ingress . Server { } )
2019-01-29 06:21:08 +00:00
if expected != actual {
t . Errorf ( "Expected '%v' but returned '%v'" , expected , actual )
}
2019-02-15 20:20:10 +00:00
cfgDatadog := config . Configuration {
EnableOpentracing : true ,
DatadogCollectorHost : "datadog-host.com" ,
}
2021-09-05 11:50:28 +00:00
expected = "opentracing_load_tracer /usr/local/lib/libdd_opentracing.so /etc/nginx/opentracing.json;\r\n"
2020-01-29 15:20:05 +00:00
actual = buildOpentracing ( cfgDatadog , [ ] * ingress . Server { } )
2019-02-15 20:20:10 +00:00
if expected != actual {
t . Errorf ( "Expected '%v' but returned '%v'" , expected , actual )
}
2021-02-18 19:40:04 +00:00
cfgJaegerEndpoint := config . Configuration {
EnableOpentracing : true ,
JaegerEndpoint : "http://jaeger-collector.com:14268/api/traces" ,
}
expected = "opentracing_load_tracer /usr/local/lib/libjaegertracing_plugin.so /etc/nginx/opentracing.json;\r\n"
actual = buildOpentracing ( cfgJaegerEndpoint , [ ] * ingress . Server { } )
if expected != actual {
t . Errorf ( "Expected '%v' but returned '%v'" , expected , actual )
}
2020-07-23 19:55:18 +00:00
cfgOpenTracing := config . Configuration {
EnableOpentracing : true ,
DatadogCollectorHost : "datadog-host.com" ,
OpentracingOperationName : "my-operation-name" ,
OpentracingLocationOperationName : "my-location-operation-name" ,
}
2021-09-05 11:50:28 +00:00
expected = "opentracing_load_tracer /usr/local/lib/libdd_opentracing.so /etc/nginx/opentracing.json;\r\n"
2020-07-23 19:55:18 +00:00
expected += "opentracing_operation_name \"my-operation-name\";\n"
expected += "opentracing_location_operation_name \"my-location-operation-name\";\n"
actual = buildOpentracing ( cfgOpenTracing , [ ] * ingress . Server { } )
if expected != actual {
t . Errorf ( "Expected '%v' but returned '%v'" , expected , actual )
}
2019-01-29 06:21:08 +00:00
}
2023-03-22 18:58:22 +00:00
func TestBuildOpenTelemetry ( t * testing . T ) {
invalidType := & ingress . Ingress { }
expected := ""
actual := buildOpentelemetry ( invalidType , [ ] * ingress . Server { } )
if expected != actual {
t . Errorf ( "Expected '%v' but returned '%v'" , expected , actual )
}
cfgNoHost := config . Configuration {
EnableOpentelemetry : true ,
}
expected = "\r\n"
actual = buildOpentelemetry ( cfgNoHost , [ ] * ingress . Server { } )
if expected != actual {
t . Errorf ( "Expected '%v' but returned '%v'" , expected , actual )
}
cfgOpenTelemetry := config . Configuration {
EnableOpentelemetry : true ,
OpentelemetryOperationName : "my-operation-name" ,
}
expected = "\r\n"
expected += "opentelemetry_operation_name \"my-operation-name\";\n"
actual = buildOpentelemetry ( cfgOpenTelemetry , [ ] * ingress . Server { } )
if expected != actual {
t . Errorf ( "Expected '%v' but returned '%v'" , expected , actual )
}
}
2020-07-15 15:20:47 +00:00
func TestEnforceRegexModifier ( t * testing . T ) {
invalidType := & ingress . Ingress { }
expected := false
actual := enforceRegexModifier ( invalidType )
if expected != actual {
t . Errorf ( "Expected '%v' but returned '%v'" , expected , actual )
}
locs := [ ] * ingress . Location {
{
Rewrite : rewrite . Config {
Target : "/alright" ,
UseRegex : true ,
} ,
2021-08-21 20:42:00 +00:00
Path : "/ok" ,
PathType : & pathPrefix ,
2020-07-15 15:20:47 +00:00
} ,
}
expected = true
actual = enforceRegexModifier ( locs )
if expected != actual {
t . Errorf ( "Expected '%v' but returned '%v'" , expected , actual )
}
}
2019-05-25 21:32:13 +00:00
func TestShouldLoadModSecurityModule ( t * testing . T ) {
// ### Invalid argument type tests ###
// The first tests should return false.
expected := false
invalidType := & ingress . Ingress { }
actual := shouldLoadModSecurityModule ( config . Configuration { } , invalidType )
if expected != actual {
t . Errorf ( "Expected '%v' but returned '%v'" , expected , actual )
}
actual = shouldLoadModSecurityModule ( invalidType , [ ] * ingress . Server { } )
if expected != actual {
t . Errorf ( "Expected '%v' but returned '%v'" , expected , actual )
}
// ### Functional tests ###
actual = shouldLoadModSecurityModule ( config . Configuration { } , [ ] * ingress . Server { } )
if expected != actual {
t . Errorf ( "Expected '%v' but returned '%v'" , expected , actual )
}
// All further tests should return true.
expected = true
configuration := config . Configuration { EnableModsecurity : true }
actual = shouldLoadModSecurityModule ( configuration , [ ] * ingress . Server { } )
if expected != actual {
t . Errorf ( "Expected '%v' but returned '%v'" , expected , actual )
}
servers := [ ] * ingress . Server {
{
Locations : [ ] * ingress . Location {
{
ModSecurity : modsecurity . Config {
Enable : true ,
} ,
} ,
} ,
} ,
}
actual = shouldLoadModSecurityModule ( config . Configuration { } , servers )
if expected != actual {
t . Errorf ( "Expected '%v' but returned '%v'" , expected , actual )
}
actual = shouldLoadModSecurityModule ( configuration , servers )
if expected != actual {
t . Errorf ( "Expected '%v' but returned '%v'" , expected , actual )
}
}
2020-01-26 00:39:20 +00:00
func TestOpentracingForLocation ( t * testing . T ) {
2020-01-29 15:20:05 +00:00
trueVal := true
2021-10-24 21:36:21 +00:00
falseVal := false
2020-01-29 15:20:05 +00:00
loadOT := ` opentracing on ;
opentracing_propagate_context ; `
2021-10-24 21:36:21 +00:00
loadOTUntrustedSpan := ` opentracing on ;
opentracing_propagate_context ;
opentracing_trust_incoming_span off ; `
2020-01-26 00:39:20 +00:00
testCases := [ ] struct {
2021-10-24 21:36:21 +00:00
description string
globalOT bool
isSetInLoc bool
isOTInLoc * bool
globalTrust bool
isTrustSetInLoc bool
isTrustInLoc * bool
expected string
2020-01-26 00:39:20 +00:00
} {
2021-10-24 21:36:21 +00:00
{ "globally enabled, without annotation" , true , false , nil , true , false , nil , loadOT } ,
{ "globally enabled and enabled in location" , true , true , & trueVal , true , false , nil , loadOT } ,
{ "globally disabled and not enabled in location" , false , false , nil , true , false , nil , "" } ,
{ "globally disabled but enabled in location" , false , true , & trueVal , true , false , nil , loadOT } ,
{ "globally trusted, not trusted in location" , true , false , nil , true , true , & falseVal , loadOTUntrustedSpan } ,
{ "not globally trusted, trust set in location" , true , false , nil , false , true , & trueVal , loadOT } ,
{ "not globally trusted, trust not set in location" , true , false , nil , false , false , nil , loadOTUntrustedSpan } ,
2020-01-26 00:39:20 +00:00
}
for _ , testCase := range testCases {
2020-01-29 15:20:05 +00:00
il := & ingress . Location {
2021-10-24 21:36:21 +00:00
Opentracing : opentracing . Config { Set : testCase . isSetInLoc , TrustSet : testCase . isTrustSetInLoc } ,
2020-01-29 15:20:05 +00:00
}
if il . Opentracing . Set {
il . Opentracing . Enabled = * testCase . isOTInLoc
}
2021-10-24 21:36:21 +00:00
if il . Opentracing . TrustSet {
il . Opentracing . TrustEnabled = * testCase . isTrustInLoc
}
2020-01-29 15:20:05 +00:00
2021-10-24 21:36:21 +00:00
actual := buildOpentracingForLocation ( testCase . globalOT , testCase . globalTrust , il )
2020-01-26 00:39:20 +00:00
if testCase . expected != actual {
t . Errorf ( "%v: expected '%v' but returned '%v'" , testCase . description , testCase . expected , actual )
}
}
}
2020-01-29 15:20:05 +00:00
func TestShouldLoadOpentracingModule ( t * testing . T ) {
// ### Invalid argument type tests ###
// The first tests should return false.
expected := false
invalidType := & ingress . Ingress { }
actual := shouldLoadOpentracingModule ( config . Configuration { } , invalidType )
if expected != actual {
t . Errorf ( "Expected '%v' but returned '%v'" , expected , actual )
}
actual = shouldLoadOpentracingModule ( invalidType , [ ] * ingress . Server { } )
if expected != actual {
t . Errorf ( "Expected '%v' but returned '%v'" , expected , actual )
}
// ### Functional tests ###
actual = shouldLoadOpentracingModule ( config . Configuration { } , [ ] * ingress . Server { } )
if expected != actual {
t . Errorf ( "Expected '%v' but returned '%v'" , expected , actual )
}
// All further tests should return true.
expected = true
configuration := config . Configuration { EnableOpentracing : true }
actual = shouldLoadOpentracingModule ( configuration , [ ] * ingress . Server { } )
if expected != actual {
t . Errorf ( "Expected '%v' but returned '%v'" , expected , actual )
}
servers := [ ] * ingress . Server {
{
Locations : [ ] * ingress . Location {
{
Opentracing : opentracing . Config {
Enabled : true ,
} ,
} ,
} ,
} ,
}
actual = shouldLoadOpentracingModule ( config . Configuration { } , servers )
if expected != actual {
t . Errorf ( "Expected '%v' but returned '%v'" , expected , actual )
}
actual = shouldLoadOpentracingModule ( configuration , servers )
if expected != actual {
t . Errorf ( "Expected '%v' but returned '%v'" , expected , actual )
}
}
2020-02-04 17:04:11 +00:00
2023-03-22 18:58:22 +00:00
func TestOpentelemetryForLocation ( t * testing . T ) {
trueVal := true
falseVal := false
loadOT := ` opentelemetry on ;
opentelemetry_propagate ;
opentelemetry_trust_incoming_spans on ; `
loadOTUntrustedSpan := ` opentelemetry on ;
opentelemetry_propagate ;
opentelemetry_trust_incoming_spans off ; `
testCases := [ ] struct {
description string
globalOT bool
isSetInLoc bool
isOTInLoc * bool
globalTrust bool
isTrustSetInLoc bool
isTrustInLoc * bool
expected string
} {
{ "globally enabled, without annotation" , true , false , nil , true , false , nil , loadOT } ,
{ "globally enabled and enabled in location" , true , true , & trueVal , true , false , nil , loadOT } ,
{ "globally disabled and not enabled in location" , false , false , nil , true , false , nil , "" } ,
{ "globally disabled but enabled in location" , false , true , & trueVal , true , false , nil , loadOT } ,
{ "globally trusted, not trusted in location" , true , false , nil , true , true , & falseVal , loadOTUntrustedSpan } ,
{ "not globally trusted, trust set in location" , true , false , nil , false , true , & trueVal , loadOT } ,
{ "not globally trusted, trust not set in location" , true , false , nil , false , false , nil , loadOTUntrustedSpan } ,
}
for _ , testCase := range testCases {
il := & ingress . Location {
Opentelemetry : opentelemetry . Config { Set : testCase . isSetInLoc , TrustSet : testCase . isTrustSetInLoc } ,
}
if il . Opentelemetry . Set {
il . Opentelemetry . Enabled = * testCase . isOTInLoc
}
if il . Opentelemetry . TrustSet {
il . Opentelemetry . TrustEnabled = * testCase . isTrustInLoc
}
actual := buildOpentelemetryForLocation ( testCase . globalOT , testCase . globalTrust , il )
if testCase . expected != actual {
t . Errorf ( "%v: expected '%v' but returned '%v'" , testCase . description , testCase . expected , actual )
}
}
}
func TestShouldLoadOpentelemetryModule ( t * testing . T ) {
// ### Invalid argument type tests ###
// The first tests should return false.
expected := false
invalidType := & ingress . Ingress { }
actual := shouldLoadOpentelemetryModule ( config . Configuration { } , invalidType )
if expected != actual {
t . Errorf ( "Expected '%v' but returned '%v'" , expected , actual )
}
actual = shouldLoadOpentelemetryModule ( invalidType , [ ] * ingress . Server { } )
if expected != actual {
t . Errorf ( "Expected '%v' but returned '%v'" , expected , actual )
}
// ### Functional tests ###
actual = shouldLoadOpentelemetryModule ( config . Configuration { } , [ ] * ingress . Server { } )
if expected != actual {
t . Errorf ( "Expected '%v' but returned '%v'" , expected , actual )
}
// All further tests should return true.
expected = true
configuration := config . Configuration { EnableOpentelemetry : true }
actual = shouldLoadOpentelemetryModule ( configuration , [ ] * ingress . Server { } )
if expected != actual {
t . Errorf ( "Expected '%v' but returned '%v'" , expected , actual )
}
servers := [ ] * ingress . Server {
{
Locations : [ ] * ingress . Location {
{
Opentelemetry : opentelemetry . Config {
Enabled : true ,
} ,
} ,
} ,
} ,
}
actual = shouldLoadOpentelemetryModule ( config . Configuration { } , servers )
if expected != actual {
t . Errorf ( "Expected '%v' but returned '%v'" , expected , actual )
}
actual = shouldLoadOpentelemetryModule ( configuration , servers )
if expected != actual {
t . Errorf ( "Expected '%v' but returned '%v'" , expected , actual )
}
}
2020-02-04 17:04:11 +00:00
func TestModSecurityForLocation ( t * testing . T ) {
loadModule := ` modsecurity on ;
2020-04-02 01:04:36 +00:00
`
modSecCfg := ` modsecurity_rules_file / etc / nginx / modsecurity / modsecurity . conf ;
2020-02-04 17:04:11 +00:00
`
2020-03-21 19:35:07 +00:00
modsecOff := "modsecurity off;"
2020-02-04 17:04:11 +00:00
modsecRule := ` modsecurity_rules '
# RULE #
' ;
`
owaspRules := ` modsecurity_rules_file / etc / nginx / owasp - modsecurity - crs / nginx - modsecurity . conf ;
`
transactionID := "0000000"
transactionCfg := fmt . Sprintf ( ` modsecurity_transaction_id "%v" ;
` , transactionID )
testRule := "#RULE#"
testCases := [ ] struct {
description string
isEnabledInCM bool
isOwaspEnabledInCM bool
isEnabledInLoc bool
2020-03-21 19:35:07 +00:00
isEnableSetInLoc bool
2020-02-04 17:04:11 +00:00
isOwaspEnabledInLoc bool
snippet string
transactionID string
expected string
} {
2020-03-21 19:35:07 +00:00
{ "configmap enabled, configmap OWASP disabled, without annotation, snippet or transaction ID" , true , false , false , false , false , "" , "" , "" } ,
{ "configmap enabled, configmap OWASP disabled, without annotation, snippet and with transaction ID" , true , false , false , false , false , "" , transactionID , transactionCfg } ,
{ "configmap enabled, configmap OWASP enabled, without annotation, OWASP enabled" , true , true , false , false , false , "" , "" , "" } ,
{ "configmap enabled, configmap OWASP enabled, without annotation, OWASP disabled, with snippet and no transaction ID" , true , true , false , false , false , testRule , "" , modsecRule } ,
{ "configmap enabled, configmap OWASP enabled, without annotation, OWASP disabled, with snippet and transaction ID" , true , true , false , false , false , testRule , transactionID , fmt . Sprintf ( "%v%v" , modsecRule , transactionCfg ) } ,
{ "configmap enabled, configmap OWASP disabled, annotation enabled, OWASP disabled" , true , false , true , true , false , "" , "" , "" } ,
{ "configmap enabled, configmap OWASP disabled, annotation enabled, OWASP enabled, no snippet and no transaction ID" , true , false , true , true , true , "" , "" , owaspRules } ,
{ "configmap enabled, configmap OWASP disabled, annotation disabled, OWASP disabled, no snippet and no transaction ID" , true , false , false , true , false , "" , "" , modsecOff } ,
{ "configmap enabled, configmap OWASP disabled, annotation enabled, OWASP enabled, with snippet and no transaction ID" , true , false , true , true , true , "" , "" , owaspRules } ,
2020-04-02 01:04:36 +00:00
{ "configmap enabled, configmap OWASP disabled, annotation enabled, OWASP enabled, with snippet and transaction ID" , true , false , true , true , true , "" , transactionID , fmt . Sprintf ( "%v%v" , transactionCfg , owaspRules ) } ,
2020-03-21 19:35:07 +00:00
{ "configmap enabled, configmap OWASP enabled, annotation enabled, OWASP disabled" , true , true , true , true , false , "" , "" , "" } ,
2020-04-02 01:04:36 +00:00
{ "configmap disabled, annotation enabled, OWASP disabled" , false , false , true , true , false , "" , "" , fmt . Sprintf ( "%v%v" , loadModule , modSecCfg ) } ,
2020-03-21 19:35:07 +00:00
{ "configmap disabled, annotation disabled, OWASP disabled" , false , false , false , true , false , "" , "" , "" } ,
2021-12-23 11:34:38 +00:00
{ "configmap disabled, annotation enabled, OWASP disabled" , false , false , true , true , false , testRule , "" , fmt . Sprintf ( "%v%v" , loadModule , modsecRule ) } ,
{ "configmap disabled, annotation enabled, OWASP enabled" , false , false , true , true , false , testRule , "" , fmt . Sprintf ( "%v%v" , loadModule , modsecRule ) } ,
2020-02-04 17:04:11 +00:00
}
for _ , testCase := range testCases {
il := & ingress . Location {
ModSecurity : modsecurity . Config {
Enable : testCase . isEnabledInLoc ,
2020-03-21 19:35:07 +00:00
EnableSet : testCase . isEnableSetInLoc ,
2020-02-04 17:04:11 +00:00
OWASPRules : testCase . isOwaspEnabledInLoc ,
Snippet : testCase . snippet ,
TransactionID : testCase . transactionID ,
} ,
}
actual := buildModSecurityForLocation ( config . Configuration {
EnableModsecurity : testCase . isEnabledInCM ,
EnableOWASPCoreRules : testCase . isOwaspEnabledInCM ,
} , il )
if testCase . expected != actual {
t . Errorf ( "%v: expected '%v' but returned '%v'" , testCase . description , testCase . expected , actual )
}
}
}
2020-10-19 22:40:06 +00:00
func TestBuildServerName ( t * testing . T ) {
testCases := [ ] struct {
title string
hostname string
expected string
} {
{ "simple domain" , "foo.bar" , "foo.bar" } ,
{ "simple www domain" , "www.foo.bar" , "www.foo.bar" } ,
{ "wildcard domain" , "*.foo.bar" , "~^(?<subdomain>[\\w-]+)\\.foo\\.bar$" } ,
{ "wildcard two levels domain" , "*.sub.foo.bar" , "~^(?<subdomain>[\\w-]+)\\.sub\\.foo\\.bar$" } ,
}
for _ , testCase := range testCases {
result := buildServerName ( testCase . hostname )
if result != testCase . expected {
t . Errorf ( "%v: expected '%v' but returned '%v'" , testCase . title , testCase . expected , result )
}
}
}
2020-12-24 16:39:12 +00:00
func TestParseComplexNginxVarIntoLuaTable ( t * testing . T ) {
testCases := [ ] struct {
ngxVar string
expectedLuaTable string
} {
{ "foo" , ` { { nil, nil, nil, "foo", }, } ` } ,
{ "$foo" , ` { { nil, nil, "foo", nil, }, } ` } ,
{ "${foo}" , ` { { nil, "foo", nil, nil, }, } ` } ,
{ "\\$foo" , ` { { "\$foo", nil, nil, nil, }, } ` } ,
{
"foo\\$bar$baz${daz}xiyar$pomidor" ,
` { { nil, nil, nil, "foo", }, { "\$bar", nil, nil, nil, }, { nil, nil, "baz", nil, }, ` +
` { nil, "daz", nil, nil, }, { nil, nil, nil, "xiyar", }, { nil, nil, "pomidor", nil, }, } ` ,
} ,
}
for _ , testCase := range testCases {
actualLuaTable := parseComplexNginxVarIntoLuaTable ( testCase . ngxVar )
if actualLuaTable != testCase . expectedLuaTable {
t . Errorf ( "expected %v but returned %v" , testCase . expectedLuaTable , actualLuaTable )
}
}
}
func TestConvertGoSliceIntoLuaTablet ( t * testing . T ) {
testCases := [ ] struct {
title string
goSlice interface { }
emptyStringAsNil bool
expectedLuaTable string
expectedErr error
} {
{
"flat string slice" ,
[ ] string { "one" , "two" , "three" } ,
false ,
` { "one", "two", "three", } ` ,
nil ,
} ,
{
"nested string slice" ,
[ ] [ ] string { { "one" , "" , "three" } , { "foo" , "bar" } } ,
false ,
` { { "one", "", "three", }, { "foo", "bar", }, } ` ,
nil ,
} ,
{
"converts empty string to nil when enabled" ,
[ ] [ ] string { { "one" , "" , "three" } , { "foo" , "bar" } } ,
true ,
` { { "one", nil, "three", }, { "foo", "bar", }, } ` ,
nil ,
} ,
{
"boolean slice" ,
[ ] bool { true , true , false } ,
false ,
` { true, true, false, } ` ,
nil ,
} ,
{
"integer slice" ,
[ ] int { 4 , 3 , 6 } ,
false ,
` { 4, 3, 6, } ` ,
nil ,
} ,
}
for _ , testCase := range testCases {
actualLuaTable , err := convertGoSliceIntoLuaTable ( testCase . goSlice , testCase . emptyStringAsNil )
if testCase . expectedErr != nil && err != nil && testCase . expectedErr . Error ( ) != err . Error ( ) {
t . Errorf ( "expected error '%v' but returned '%v'" , testCase . expectedErr , err )
}
if testCase . expectedErr == nil && err != nil {
t . Errorf ( "expected error to be nil but returned '%v'" , err )
}
if testCase . expectedLuaTable != actualLuaTable {
t . Errorf ( "%v: expected '%v' but returned '%v'" , testCase . title , testCase . expectedLuaTable , actualLuaTable )
}
}
}
2021-07-06 17:50:19 +00:00
func TestCleanConf ( t * testing . T ) {
testDataDir , err := getTestDataDir ( )
if err != nil {
t . Error ( "unexpected error reading conf file: " , err )
}
actual := & bytes . Buffer { }
{
2021-08-06 14:18:17 +00:00
data , err := os . ReadFile ( testDataDir + "/cleanConf.src.conf" )
2021-07-06 17:50:19 +00:00
if err != nil {
t . Error ( "unexpected error reading conf file: " , err )
}
in := bytes . NewBuffer ( data )
err = cleanConf ( in , actual )
if err != nil {
t . Error ( "cleanConf failed: " , err )
}
}
2021-08-06 14:18:17 +00:00
expected , err := os . ReadFile ( testDataDir + "/cleanConf.expected.conf" )
2021-07-06 17:50:19 +00:00
if err != nil {
t . Error ( "unexpected error reading conf file: " , err )
}
if ! bytes . Equal ( expected , actual . Bytes ( ) ) {
diff , err := difflib . GetUnifiedDiffString ( difflib . UnifiedDiff { A : strings . SplitAfter ( string ( expected ) , "\n" ) , B : strings . SplitAfter ( actual . String ( ) , "\n" ) , Context : 3 } )
if err != nil {
t . Error ( "failed to get diff for cleanConf" , err )
}
t . Errorf ( "cleanConf result don't match with expected: %s" , diff )
}
}