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" )
if err != nil {
t . Fatal ( err )
}
defer os . RemoveAll ( templateErrorFile . Name ( ) )
data := [ ] byte ( "Not a valid template {{ Bar }}" )
err = ioutil . WriteFile ( templateErrorFile . Name ( ) , data , 0700 )
if err != nil {
t . Fatal ( err )
}
templateInvalidTOMLFile , err := ioutil . TempFile ( "" , "provider-configuration-error" )
if err != nil {
t . Fatal ( err )
}
defer os . RemoveAll ( templateInvalidTOMLFile . Name ( ) )
data = [ ] byte ( ` Hello { { . Name } }
{ { Foo } } ` )
err = ioutil . WriteFile ( templateInvalidTOMLFile . Name ( ) , data , 0700 )
if err != nil {
t . Fatal ( err )
}
invalids := [ ] struct {
provider * myProvider
defaultTemplate string
expectedError string
funcMap template . FuncMap
templateObjects interface { }
} {
{
provider : & myProvider {
2016-01-14 00:46:44 +03:00
BaseProvider {
2015-11-13 13:50:32 +03:00
Filename : "/non/existent/template.tmpl" ,
} ,
2016-08-14 05:05:43 +03:00
nil ,
2015-11-13 13:50:32 +03:00
} ,
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 {
2016-01-14 00:46:44 +03:00
BaseProvider {
2015-11-13 13:50:32 +03:00
Filename : templateErrorFile . Name ( ) ,
} ,
2016-08-14 05:05:43 +03:00
nil ,
2015-11-13 13:50:32 +03:00
} ,
expectedError : ` function "Bar" not defined ` ,
} ,
{
provider : & myProvider {
2016-01-14 00:46:44 +03:00
BaseProvider {
2015-11-13 13:50:32 +03:00
Filename : templateInvalidTOMLFile . Name ( ) ,
} ,
2016-08-14 05:05:43 +03:00
nil ,
2015-11-13 13:50:32 +03:00
} ,
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 )
}
if configuration != nil {
t . Fatalf ( "shouldn't have return a configuration object : %v" , configuration )
}
}
}
func TestGetConfiguration ( t * testing . T ) {
templateFile , err := ioutil . TempFile ( "" , "provider-configuration" )
if err != nil {
t . Fatal ( err )
}
defer os . RemoveAll ( templateFile . Name ( ) )
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" ` )
err = ioutil . WriteFile ( templateFile . Name ( ) , data , 0700 )
if err != nil {
t . Fatal ( err )
}
provider := & myProvider {
2016-01-14 00:46:44 +03:00
BaseProvider {
2015-11-13 13:50:32 +03:00
Filename : templateFile . Name ( ) ,
} ,
2016-08-14 05:05:43 +03:00
nil ,
2015-11-13 13:50:32 +03:00
}
2017-04-15 16:49:53 +03:00
configuration , err := provider . GetConfiguration ( templateFile . Name ( ) , nil , nil )
2015-11-13 13:50:32 +03:00
if err != nil {
t . Fatalf ( "Shouldn't have error out, got %v" , err )
}
if configuration == nil {
2017-05-26 18:03:14 +03:00
t . Fatal ( "Configuration should not be nil, but was" )
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" )
if err != nil {
t . Fatal ( err )
}
defer os . RemoveAll ( templateFile . Name ( ) )
data := [ ] byte ( ` [ backends ]
[ backends . backend1 ]
[ backends . backend1 . maxconn ]
amount = 10
extractorFunc = "request.host" ` )
err = ioutil . WriteFile ( templateFile . Name ( ) , data , 0700 )
if err != nil {
t . Fatal ( err )
}
provider := & myProvider {
BaseProvider {
Filename : templateFile . Name ( ) ,
} ,
2016-08-14 05:05:43 +03:00
nil ,
2016-04-13 11:11:36 +03:00
}
2017-04-15 16:49:53 +03:00
configuration , err := provider . GetConfiguration ( templateFile . Name ( ) , nil , nil )
2016-04-13 11:11:36 +03:00
if err != nil {
t . Fatalf ( "Shouldn't have error out, got %v" , err )
}
if configuration == nil {
2017-05-26 18:03:14 +03:00
t . Fatal ( "Configuration should not be nil, but was" )
2016-04-13 11:11:36 +03:00
}
if configuration . Backends [ "backend1" ] . MaxConn . Amount != 10 {
2017-05-26 18:03:14 +03:00
t . Fatal ( "Configuration did not parse MaxConn.Amount properly" )
2016-04-13 11:11:36 +03:00
}
if configuration . Backends [ "backend1" ] . MaxConn . ExtractorFunc != "request.host" {
2017-05-26 18:03:14 +03:00
t . Fatal ( "Configuration did not parse MaxConn.ExtractorFunc properly" )
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 ) {
provider := & myProvider {
BaseProvider {
Filename : "" ,
} ,
nil ,
}
_ , err := provider . TLS . CreateTLSConfig ( )
if err != nil {
2017-05-26 18:03:14 +03:00
t . Fatal ( "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 ) {
provider := & myProvider {
BaseProvider {
Filename : "" ,
} ,
2017-08-25 19:22:03 +03:00
& types . ClientTLS {
2017-08-25 11:26:02 +03:00
InsecureSkipVerify : true ,
} ,
}
config , err := provider . TLS . CreateTLSConfig ( )
if err != nil {
t . Fatal ( "CreateTLSConfig should assume that consumer does not want a TLS configuration if input is nil" )
}
if ! config . InsecureSkipVerify {
t . Fatal ( "CreateTLSConfig should support setting only InsecureSkipVerify property" )
}
}
func TestInsecureSkipVerifyFalseClientTLS ( t * testing . T ) {
provider := & myProvider {
BaseProvider {
Filename : "" ,
} ,
2017-08-25 19:22:03 +03:00
& types . ClientTLS {
2017-08-25 11:26:02 +03:00
InsecureSkipVerify : false ,
} ,
}
_ , err := provider . TLS . CreateTLSConfig ( )
if err == nil {
t . Fatal ( "CreateTLSConfig should error if consumer does not set a TLS cert or key configuration and not chooses InsecureSkipVerify to be true" )
}
t . Log ( err )
}
2016-05-20 18:17:38 +03:00
func TestMatchingConstraints ( t * testing . T ) {
cases := [ ] struct {
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
{
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
{
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
{
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
{
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
{
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 ,
} ,
}
for i , c := range cases {
provider := myProvider {
BaseProvider {
Constraints : c . constraints ,
} ,
2016-08-14 05:05:43 +03:00
nil ,
2016-05-20 18:17:38 +03:00
}
actual , _ := provider . MatchConstraints ( c . tags )
if actual != c . expected {
2016-08-14 05:05:43 +03:00
t . Fatalf ( "test #%v: expected %t, got %t, for %#v" , i , c . expected , actual , c . constraints )
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" )
if err != nil {
t . Fatal ( err )
}
defer os . RemoveAll ( templateFile . Name ( ) )
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" ` )
err = ioutil . WriteFile ( templateFile . Name ( ) , data , 0700 )
if err != nil {
t . Fatal ( err )
}
provider := & myProvider {
BaseProvider {
Filename : templateFile . Name ( ) ,
} ,
nil ,
}
2017-04-15 16:49:53 +03:00
configuration , err := provider . GetConfiguration ( templateFile . Name ( ) , nil , nil )
2016-12-03 12:12:22 +03:00
if err != nil {
t . Fatalf ( "Shouldn't have error out, got %v" , err )
}
if configuration == nil {
2017-05-26 18:03:14 +03:00
t . Fatal ( "Configuration should not be nil, but was" )
2016-12-03 12:12:22 +03:00
}
if _ , ok := configuration . Backends [ "backend1" ] ; ! ok {
2017-05-26 18:03:14 +03:00
t . Fatal ( "backend1 should exists, but it not" )
2016-12-03 12:12:22 +03:00
}
if _ , ok := configuration . Frontends [ "frontend-1" ] ; ! ok {
2017-05-26 18:03:14 +03:00
t . Fatal ( "Frontend frontend-1 should exists, but it not" )
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" )
if err != nil {
t . Fatal ( err )
}
defer os . RemoveAll ( templateFile . Name ( ) )
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" ` )
err = ioutil . WriteFile ( templateFile . Name ( ) , data , 0700 )
if err != nil {
t . Fatal ( err )
}
provider := & myProvider {
BaseProvider {
Filename : templateFile . Name ( ) ,
} ,
nil ,
}
configuration , err := provider . GetConfiguration ( templateFile . Name ( ) , nil , nil )
if err != nil {
t . Fatalf ( "Shouldn't have error out, got %v" , err )
}
if configuration == nil {
t . Fatal ( "Configuration should not be nil, but was" )
}
if len ( configuration . Backends ) != 1 {
t . Fatal ( "one backend should be defined, but it's not" )
}
if _ , ok := configuration . Frontends [ "frontend-1" ] ; ! ok {
t . Fatal ( "Frontend frontend-1 should exists, but it not" )
}
}
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 )
}