2015-11-13 13:50:32 +03:00
package provider
import (
"io/ioutil"
"os"
"strings"
"testing"
"text/template"
2016-05-20 18:17:38 +03:00
"github.com/containous/traefik/types"
2017-11-17 12:12:03 +03:00
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
2015-11-13 13:50:32 +03:00
)
type myProvider struct {
2016-01-14 00:46:44 +03:00
BaseProvider
2017-08-25 19:22:03 +03:00
TLS * types . ClientTLS
2015-11-13 13:50:32 +03:00
}
func ( p * myProvider ) Foo ( ) string {
return "bar"
}
func TestConfigurationErrors ( t * testing . T ) {
templateErrorFile , err := ioutil . TempFile ( "" , "provider-configuration-error" )
2018-04-11 14:54:03 +03:00
require . NoError ( t , err )
2015-11-13 13:50:32 +03:00
defer os . RemoveAll ( templateErrorFile . Name ( ) )
2018-04-11 14:54:03 +03:00
2015-11-13 13:50:32 +03:00
data := [ ] byte ( "Not a valid template {{ Bar }}" )
2018-04-11 14:54:03 +03:00
2015-11-13 13:50:32 +03:00
err = ioutil . WriteFile ( templateErrorFile . Name ( ) , data , 0700 )
2018-04-11 14:54:03 +03:00
require . NoError ( t , err )
2015-11-13 13:50:32 +03:00
templateInvalidTOMLFile , err := ioutil . TempFile ( "" , "provider-configuration-error" )
2018-04-11 14:54:03 +03:00
require . NoError ( t , err )
2015-11-13 13:50:32 +03:00
defer os . RemoveAll ( templateInvalidTOMLFile . Name ( ) )
2018-04-11 14:54:03 +03:00
2015-11-13 13:50:32 +03:00
data = [ ] byte ( ` Hello { { . Name } }
{ { Foo } } ` )
2018-04-11 14:54:03 +03:00
2015-11-13 13:50:32 +03:00
err = ioutil . WriteFile ( templateInvalidTOMLFile . Name ( ) , data , 0700 )
2018-04-11 14:54:03 +03:00
require . NoError ( t , err )
2015-11-13 13:50:32 +03:00
invalids := [ ] struct {
provider * myProvider
defaultTemplate string
expectedError string
funcMap template . FuncMap
templateObjects interface { }
} {
{
provider : & myProvider {
2018-04-11 14:54:03 +03:00
BaseProvider : BaseProvider {
2015-11-13 13:50:32 +03:00
Filename : "/non/existent/template.tmpl" ,
} ,
} ,
expectedError : "open /non/existent/template.tmpl: no such file or directory" ,
} ,
{
provider : & myProvider { } ,
defaultTemplate : "non/existent/template.tmpl" ,
expectedError : "Asset non/existent/template.tmpl not found" ,
} ,
{
provider : & myProvider {
2018-04-11 14:54:03 +03:00
BaseProvider : BaseProvider {
2015-11-13 13:50:32 +03:00
Filename : templateErrorFile . Name ( ) ,
} ,
} ,
expectedError : ` function "Bar" not defined ` ,
} ,
{
provider : & myProvider {
2018-04-11 14:54:03 +03:00
BaseProvider : BaseProvider {
2015-11-13 13:50:32 +03:00
Filename : templateInvalidTOMLFile . Name ( ) ,
} ,
} ,
2017-04-11 18:10:46 +03:00
expectedError : "Near line 1 (last key parsed 'Hello'): expected key separator '=', but got '<' instead" ,
2015-11-13 13:50:32 +03:00
funcMap : template . FuncMap {
"Foo" : func ( ) string {
return "bar"
} ,
} ,
templateObjects : struct { Name string } { Name : "bar" } ,
} ,
}
for _ , invalid := range invalids {
2017-04-15 16:49:53 +03:00
configuration , err := invalid . provider . GetConfiguration ( invalid . defaultTemplate , invalid . funcMap , nil )
2015-11-13 13:50:32 +03:00
if err == nil || ! strings . Contains ( err . Error ( ) , invalid . expectedError ) {
t . Fatalf ( "should have generate an error with %q, got %v" , invalid . expectedError , err )
}
2018-04-11 14:54:03 +03:00
assert . Nil ( t , configuration )
2015-11-13 13:50:32 +03:00
}
}
func TestGetConfiguration ( t * testing . T ) {
templateFile , err := ioutil . TempFile ( "" , "provider-configuration" )
2018-04-11 14:54:03 +03:00
require . NoError ( t , err )
2015-11-13 13:50:32 +03:00
defer os . RemoveAll ( templateFile . Name ( ) )
2018-04-11 14:54:03 +03:00
2015-11-13 13:50:32 +03:00
data := [ ] byte ( ` [ backends ]
[ backends . backend1 ]
[ backends . backend1 . circuitbreaker ]
expression = "NetworkErrorRatio() > 0.5"
[ backends . backend1 . servers . server1 ]
url = "http://172.17.0.2:80"
weight = 10
[ backends . backend1 . servers . server2 ]
url = "http://172.17.0.3:80"
weight = 1
[ frontends ]
[ frontends . frontend1 ]
backend = "backend1"
passHostHeader = true
[ frontends . frontend11 . routes . test_2 ]
rule = "Path"
value = "/test" ` )
2018-04-11 14:54:03 +03:00
2015-11-13 13:50:32 +03:00
err = ioutil . WriteFile ( templateFile . Name ( ) , data , 0700 )
2018-04-11 14:54:03 +03:00
require . NoError ( t , err )
2015-11-13 13:50:32 +03:00
provider := & myProvider {
2018-04-11 14:54:03 +03:00
BaseProvider : BaseProvider {
2015-11-13 13:50:32 +03:00
Filename : templateFile . Name ( ) ,
} ,
}
2018-04-11 14:54:03 +03:00
2017-04-15 16:49:53 +03:00
configuration , err := provider . GetConfiguration ( templateFile . Name ( ) , nil , nil )
2018-04-11 14:54:03 +03:00
require . NoError ( t , err )
assert . NotNil ( t , configuration )
2015-11-13 13:50:32 +03:00
}
2016-04-13 11:11:36 +03:00
func TestGetConfigurationReturnsCorrectMaxConnConfiguration ( t * testing . T ) {
templateFile , err := ioutil . TempFile ( "" , "provider-configuration" )
2018-04-11 14:54:03 +03:00
require . NoError ( t , err )
2016-04-13 11:11:36 +03:00
defer os . RemoveAll ( templateFile . Name ( ) )
2018-04-11 14:54:03 +03:00
2016-04-13 11:11:36 +03:00
data := [ ] byte ( ` [ backends ]
[ backends . backend1 ]
[ backends . backend1 . maxconn ]
amount = 10
extractorFunc = "request.host" ` )
2018-04-11 14:54:03 +03:00
2016-04-13 11:11:36 +03:00
err = ioutil . WriteFile ( templateFile . Name ( ) , data , 0700 )
2018-04-11 14:54:03 +03:00
require . NoError ( t , err )
2016-04-13 11:11:36 +03:00
provider := & myProvider {
2018-04-11 14:54:03 +03:00
BaseProvider : BaseProvider {
2016-04-13 11:11:36 +03:00
Filename : templateFile . Name ( ) ,
} ,
}
2018-04-11 14:54:03 +03:00
configuration , err := provider . GetConfiguration ( templateFile . Name ( ) , nil , nil )
require . NoError ( t , err )
2016-04-13 11:11:36 +03:00
2018-04-11 14:54:03 +03:00
require . NotNil ( t , configuration )
require . Contains ( t , configuration . Backends , "backend1" )
assert . EqualValues ( t , 10 , configuration . Backends [ "backend1" ] . MaxConn . Amount )
assert . Equal ( t , "request.host" , configuration . Backends [ "backend1" ] . MaxConn . ExtractorFunc )
2016-04-13 11:11:36 +03:00
}
2016-05-20 18:17:38 +03:00
2016-08-14 05:05:43 +03:00
func TestNilClientTLS ( t * testing . T ) {
2018-04-11 14:54:03 +03:00
p := & myProvider {
BaseProvider : BaseProvider {
2016-08-14 05:05:43 +03:00
Filename : "" ,
} ,
}
2018-04-11 14:54:03 +03:00
_ , err := p . TLS . CreateTLSConfig ( )
require . NoError ( t , err , "CreateTLSConfig should assume that consumer does not want a TLS configuration if input is nil" )
2016-08-14 05:05:43 +03:00
}
2017-08-25 11:26:02 +03:00
func TestInsecureSkipVerifyClientTLS ( t * testing . T ) {
2018-04-11 14:54:03 +03:00
p := & myProvider {
BaseProvider : BaseProvider {
2017-08-25 11:26:02 +03:00
Filename : "" ,
} ,
2018-04-11 14:54:03 +03:00
TLS : & types . ClientTLS {
2017-08-25 11:26:02 +03:00
InsecureSkipVerify : true ,
} ,
}
2018-04-11 14:54:03 +03:00
config , err := p . TLS . CreateTLSConfig ( )
require . NoError ( t , err , "CreateTLSConfig should assume that consumer does not want a TLS configuration if input is nil" )
assert . True ( t , config . InsecureSkipVerify , "CreateTLSConfig should support setting only InsecureSkipVerify property" )
2017-08-25 11:26:02 +03:00
}
func TestInsecureSkipVerifyFalseClientTLS ( t * testing . T ) {
2018-04-11 14:54:03 +03:00
p := & myProvider {
BaseProvider : BaseProvider {
2017-08-25 11:26:02 +03:00
Filename : "" ,
} ,
2018-04-11 14:54:03 +03:00
TLS : & types . ClientTLS {
2017-08-25 11:26:02 +03:00
InsecureSkipVerify : false ,
} ,
}
2018-04-11 14:54:03 +03:00
_ , err := p . TLS . CreateTLSConfig ( )
assert . Errorf ( t , err , "CreateTLSConfig should error if consumer does not set a TLS cert or key configuration and not chooses InsecureSkipVerify to be true" )
2017-08-25 11:26:02 +03:00
}
2016-05-20 18:17:38 +03:00
func TestMatchingConstraints ( t * testing . T ) {
2018-04-11 14:54:03 +03:00
testCases := [ ] struct {
desc string
2016-11-09 21:27:04 +03:00
constraints types . Constraints
2016-05-20 18:17:38 +03:00
tags [ ] string
expected bool
} {
// simple test: must match
{
2018-04-11 14:54:03 +03:00
desc : "tag==us-east-1 with us-east-1" ,
2016-11-09 21:27:04 +03:00
constraints : types . Constraints {
2016-05-20 18:17:38 +03:00
{
Key : "tag" ,
MustMatch : true ,
Regex : "us-east-1" ,
} ,
} ,
tags : [ ] string {
"us-east-1" ,
} ,
expected : true ,
} ,
// simple test: must match but does not match
{
2018-04-11 14:54:03 +03:00
desc : "tag==us-east-1 with us-east-2" ,
2016-11-09 21:27:04 +03:00
constraints : types . Constraints {
2016-05-20 18:17:38 +03:00
{
Key : "tag" ,
MustMatch : true ,
Regex : "us-east-1" ,
} ,
} ,
tags : [ ] string {
"us-east-2" ,
} ,
expected : false ,
} ,
// simple test: must not match
{
2018-04-11 14:54:03 +03:00
desc : "tag!=us-east-1 with us-east-1" ,
2016-11-09 21:27:04 +03:00
constraints : types . Constraints {
2016-05-20 18:17:38 +03:00
{
Key : "tag" ,
MustMatch : false ,
Regex : "us-east-1" ,
} ,
} ,
tags : [ ] string {
"us-east-1" ,
} ,
expected : false ,
} ,
// complex test: globbing
{
2018-04-11 14:54:03 +03:00
desc : "tag!=us-east-* with us-east-1" ,
2016-11-09 21:27:04 +03:00
constraints : types . Constraints {
2016-05-20 18:17:38 +03:00
{
Key : "tag" ,
MustMatch : true ,
Regex : "us-east-*" ,
} ,
} ,
tags : [ ] string {
"us-east-1" ,
} ,
expected : true ,
} ,
// complex test: multiple constraints
{
2018-04-11 14:54:03 +03:00
desc : "tag==us-east-* & tag!=api with us-east-1 & api" ,
2016-11-09 21:27:04 +03:00
constraints : types . Constraints {
2016-05-20 18:17:38 +03:00
{
Key : "tag" ,
MustMatch : true ,
Regex : "us-east-*" ,
} ,
{
Key : "tag" ,
MustMatch : false ,
Regex : "api" ,
} ,
} ,
tags : [ ] string {
"api" ,
"us-east-1" ,
} ,
expected : false ,
} ,
}
2018-04-11 14:54:03 +03:00
for _ , test := range testCases {
p := myProvider {
BaseProvider : BaseProvider {
Constraints : test . constraints ,
2016-05-20 18:17:38 +03:00
} ,
}
2018-04-11 14:54:03 +03:00
actual , _ := p . MatchConstraints ( test . tags )
assert . Equal ( t , test . expected , actual )
2016-05-20 18:17:38 +03:00
}
}
2016-12-03 12:12:22 +03:00
func TestDefaultFuncMap ( t * testing . T ) {
templateFile , err := ioutil . TempFile ( "" , "provider-configuration" )
2018-04-11 14:54:03 +03:00
require . NoError ( t , err )
2016-12-03 12:12:22 +03:00
defer os . RemoveAll ( templateFile . Name ( ) )
2018-04-11 14:54:03 +03:00
2016-12-03 12:12:22 +03:00
data := [ ] byte ( `
[ backends ]
[ backends . { { "backend-1" | replace "-" "" } } ]
[ backends . { { "BACKEND1" | tolower } } . circuitbreaker ]
expression = "NetworkErrorRatio() > 0.5"
[ backends . servers . server1 ]
url = "http://172.17.0.2:80"
weight = 10
[ backends . backend1 . servers . server2 ]
url = "http://172.17.0.3:80"
weight = 1
[ frontends ]
2017-04-15 17:46:44 +03:00
[ frontends . { { normalize "frontend/1" } } ]
2016-12-03 12:12:22 +03:00
{ { $ backend := "backend1/test/value" | split "/" } }
{ { $ backendid := index $ backend 1 } }
{ { if "backend1" | contains "backend" } }
backend = "backend1"
{ { end } }
passHostHeader = true
[ frontends . frontend - 1. routes . test_2 ]
rule = "Path"
value = "/test" ` )
2018-04-11 14:54:03 +03:00
2016-12-03 12:12:22 +03:00
err = ioutil . WriteFile ( templateFile . Name ( ) , data , 0700 )
2018-04-11 14:54:03 +03:00
require . NoError ( t , err )
2016-12-03 12:12:22 +03:00
provider := & myProvider {
2018-04-11 14:54:03 +03:00
BaseProvider : BaseProvider {
2016-12-03 12:12:22 +03:00
Filename : templateFile . Name ( ) ,
} ,
}
2018-04-11 14:54:03 +03:00
2017-04-15 16:49:53 +03:00
configuration , err := provider . GetConfiguration ( templateFile . Name ( ) , nil , nil )
2018-04-11 14:54:03 +03:00
require . NoError ( t , err )
require . NotNil ( t , configuration )
assert . Contains ( t , configuration . Backends , "backend1" )
assert . Contains ( t , configuration . Frontends , "frontend-1" )
2016-12-03 12:12:22 +03:00
}
2017-08-10 21:42:39 +03:00
func TestSprigFunctions ( t * testing . T ) {
templateFile , err := ioutil . TempFile ( "" , "provider-configuration" )
2018-04-11 14:54:03 +03:00
require . NoError ( t , err )
2017-08-10 21:42:39 +03:00
defer os . RemoveAll ( templateFile . Name ( ) )
2018-04-11 14:54:03 +03:00
2017-08-10 21:42:39 +03:00
data := [ ] byte ( `
{ { $ backend_name := trimAll "-" uuidv4 } }
[ backends ]
[ backends . { { $ backend_name } } ]
[ backends . { { $ backend_name } } . circuitbreaker ]
[ backends . { { $ backend_name } } . servers . server2 ]
url = "http://172.17.0.3:80"
weight = 1
[ frontends ]
[ frontends . { { normalize "frontend/1" } } ]
backend = "{{$backend_name}}"
passHostHeader = true
[ frontends . frontend - 1. routes . test_2 ]
rule = "Path"
value = "/test" ` )
2018-04-11 14:54:03 +03:00
2017-08-10 21:42:39 +03:00
err = ioutil . WriteFile ( templateFile . Name ( ) , data , 0700 )
2018-04-11 14:54:03 +03:00
require . NoError ( t , err )
2017-08-10 21:42:39 +03:00
provider := & myProvider {
2018-04-11 14:54:03 +03:00
BaseProvider : BaseProvider {
2017-08-10 21:42:39 +03:00
Filename : templateFile . Name ( ) ,
} ,
}
2018-04-11 14:54:03 +03:00
2017-08-10 21:42:39 +03:00
configuration , err := provider . GetConfiguration ( templateFile . Name ( ) , nil , nil )
2018-04-11 14:54:03 +03:00
require . NoError ( t , err )
require . NotNil ( t , configuration )
assert . Len ( t , configuration . Backends , 1 )
assert . Contains ( t , configuration . Frontends , "frontend-1" )
2017-08-10 21:42:39 +03:00
}
2017-11-17 12:12:03 +03:00
func TestBaseProvider_GetConfiguration ( t * testing . T ) {
baseProvider := BaseProvider { }
testCases := [ ] struct {
name string
defaultTemplateFile string
expectedContent string
} {
{
defaultTemplateFile : "templates/docker.tmpl" ,
expectedContent : readTemplateFile ( t , "./../templates/docker.tmpl" ) ,
} ,
{
defaultTemplateFile : ` template content ` ,
expectedContent : ` template content ` ,
} ,
}
for _ , test := range testCases {
test := test
t . Run ( test . name , func ( t * testing . T ) {
content , err := baseProvider . getTemplateContent ( test . defaultTemplateFile )
require . NoError ( t , err )
assert . Equal ( t , test . expectedContent , content )
} )
}
}
2017-12-04 22:04:08 +03:00
func TestNormalize ( t * testing . T ) {
testCases := [ ] struct {
desc string
name string
expected string
} {
{
desc : "without special chars" ,
name : "foobar" ,
expected : "foobar" ,
} ,
{
desc : "with special chars" ,
name : "foo.foo.foo;foo:foo!foo/foo\\foo)foo_123-ç_àéè" ,
expected : "foo-foo-foo-foo-foo-foo-foo-foo-foo-123-ç-àéè" ,
} ,
{
desc : "starts with special chars" ,
name : ".foo.foo" ,
expected : "foo-foo" ,
} ,
{
desc : "ends with special chars" ,
name : "foo.foo." ,
expected : "foo-foo" ,
} ,
}
for _ , test := range testCases {
test := test
t . Run ( test . desc , func ( t * testing . T ) {
t . Parallel ( )
actual := Normalize ( test . name )
assert . Equal ( t , test . expected , actual )
} )
}
}
2017-11-17 12:12:03 +03:00
func readTemplateFile ( t * testing . T , path string ) string {
t . Helper ( )
expectedContent , err := ioutil . ReadFile ( path )
if err != nil {
t . Fatal ( err )
}
return string ( expectedContent )
}