2015-11-13 10:50:32 +00:00
|
|
|
package provider
|
|
|
|
|
|
|
|
import (
|
|
|
|
"io/ioutil"
|
|
|
|
"os"
|
|
|
|
"strings"
|
|
|
|
"testing"
|
|
|
|
"text/template"
|
2016-05-20 15:17:38 +00:00
|
|
|
|
|
|
|
"github.com/containous/traefik/types"
|
2017-11-17 09:12:03 +00:00
|
|
|
"github.com/stretchr/testify/assert"
|
|
|
|
"github.com/stretchr/testify/require"
|
2015-11-13 10:50:32 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
type myProvider struct {
|
2016-01-13 21:46:44 +00:00
|
|
|
BaseProvider
|
2017-08-25 16:22:03 +00:00
|
|
|
TLS *types.ClientTLS
|
2015-11-13 10:50:32 +00: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-13 21:46:44 +00:00
|
|
|
BaseProvider{
|
2015-11-13 10:50:32 +00:00
|
|
|
Filename: "/non/existent/template.tmpl",
|
|
|
|
},
|
2016-08-14 02:05:43 +00:00
|
|
|
nil,
|
2015-11-13 10:50:32 +00: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-13 21:46:44 +00:00
|
|
|
BaseProvider{
|
2015-11-13 10:50:32 +00:00
|
|
|
Filename: templateErrorFile.Name(),
|
|
|
|
},
|
2016-08-14 02:05:43 +00:00
|
|
|
nil,
|
2015-11-13 10:50:32 +00:00
|
|
|
},
|
|
|
|
expectedError: `function "Bar" not defined`,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
provider: &myProvider{
|
2016-01-13 21:46:44 +00:00
|
|
|
BaseProvider{
|
2015-11-13 10:50:32 +00:00
|
|
|
Filename: templateInvalidTOMLFile.Name(),
|
|
|
|
},
|
2016-08-14 02:05:43 +00:00
|
|
|
nil,
|
2015-11-13 10:50:32 +00:00
|
|
|
},
|
2017-04-11 15:10:46 +00:00
|
|
|
expectedError: "Near line 1 (last key parsed 'Hello'): expected key separator '=', but got '<' instead",
|
2015-11-13 10:50:32 +00:00
|
|
|
funcMap: template.FuncMap{
|
|
|
|
"Foo": func() string {
|
|
|
|
return "bar"
|
|
|
|
},
|
|
|
|
},
|
|
|
|
templateObjects: struct{ Name string }{Name: "bar"},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, invalid := range invalids {
|
2017-04-15 13:49:53 +00:00
|
|
|
configuration, err := invalid.provider.GetConfiguration(invalid.defaultTemplate, invalid.funcMap, nil)
|
2015-11-13 10:50:32 +00: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-13 21:46:44 +00:00
|
|
|
BaseProvider{
|
2015-11-13 10:50:32 +00:00
|
|
|
Filename: templateFile.Name(),
|
|
|
|
},
|
2016-08-14 02:05:43 +00:00
|
|
|
nil,
|
2015-11-13 10:50:32 +00:00
|
|
|
}
|
2017-04-15 13:49:53 +00:00
|
|
|
configuration, err := provider.GetConfiguration(templateFile.Name(), nil, nil)
|
2015-11-13 10:50:32 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("Shouldn't have error out, got %v", err)
|
|
|
|
}
|
|
|
|
if configuration == nil {
|
2017-05-26 15:03:14 +00:00
|
|
|
t.Fatal("Configuration should not be nil, but was")
|
2015-11-13 10:50:32 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-04-13 08:11:36 +00: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 02:05:43 +00:00
|
|
|
nil,
|
2016-04-13 08:11:36 +00:00
|
|
|
}
|
2017-04-15 13:49:53 +00:00
|
|
|
configuration, err := provider.GetConfiguration(templateFile.Name(), nil, nil)
|
2016-04-13 08:11:36 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("Shouldn't have error out, got %v", err)
|
|
|
|
}
|
|
|
|
if configuration == nil {
|
2017-05-26 15:03:14 +00:00
|
|
|
t.Fatal("Configuration should not be nil, but was")
|
2016-04-13 08:11:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if configuration.Backends["backend1"].MaxConn.Amount != 10 {
|
2017-05-26 15:03:14 +00:00
|
|
|
t.Fatal("Configuration did not parse MaxConn.Amount properly")
|
2016-04-13 08:11:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if configuration.Backends["backend1"].MaxConn.ExtractorFunc != "request.host" {
|
2017-05-26 15:03:14 +00:00
|
|
|
t.Fatal("Configuration did not parse MaxConn.ExtractorFunc properly")
|
2016-04-13 08:11:36 +00:00
|
|
|
}
|
|
|
|
}
|
2016-05-20 15:17:38 +00:00
|
|
|
|
2016-08-14 02:05:43 +00:00
|
|
|
func TestNilClientTLS(t *testing.T) {
|
|
|
|
provider := &myProvider{
|
|
|
|
BaseProvider{
|
|
|
|
Filename: "",
|
|
|
|
},
|
|
|
|
nil,
|
|
|
|
}
|
|
|
|
_, err := provider.TLS.CreateTLSConfig()
|
|
|
|
if err != nil {
|
2017-05-26 15:03:14 +00:00
|
|
|
t.Fatal("CreateTLSConfig should assume that consumer does not want a TLS configuration if input is nil")
|
2016-08-14 02:05:43 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-08-25 08:26:02 +00:00
|
|
|
func TestInsecureSkipVerifyClientTLS(t *testing.T) {
|
|
|
|
provider := &myProvider{
|
|
|
|
BaseProvider{
|
|
|
|
Filename: "",
|
|
|
|
},
|
2017-08-25 16:22:03 +00:00
|
|
|
&types.ClientTLS{
|
2017-08-25 08:26:02 +00: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 16:22:03 +00:00
|
|
|
&types.ClientTLS{
|
2017-08-25 08:26:02 +00: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 15:17:38 +00:00
|
|
|
func TestMatchingConstraints(t *testing.T) {
|
|
|
|
cases := []struct {
|
2016-11-09 18:27:04 +00:00
|
|
|
constraints types.Constraints
|
2016-05-20 15:17:38 +00:00
|
|
|
tags []string
|
|
|
|
expected bool
|
|
|
|
}{
|
|
|
|
// simple test: must match
|
|
|
|
{
|
2016-11-09 18:27:04 +00:00
|
|
|
constraints: types.Constraints{
|
2016-05-20 15:17:38 +00: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 18:27:04 +00:00
|
|
|
constraints: types.Constraints{
|
2016-05-20 15:17:38 +00:00
|
|
|
{
|
|
|
|
Key: "tag",
|
|
|
|
MustMatch: true,
|
|
|
|
Regex: "us-east-1",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
tags: []string{
|
|
|
|
"us-east-2",
|
|
|
|
},
|
|
|
|
expected: false,
|
|
|
|
},
|
|
|
|
// simple test: must not match
|
|
|
|
{
|
2016-11-09 18:27:04 +00:00
|
|
|
constraints: types.Constraints{
|
2016-05-20 15:17:38 +00:00
|
|
|
{
|
|
|
|
Key: "tag",
|
|
|
|
MustMatch: false,
|
|
|
|
Regex: "us-east-1",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
tags: []string{
|
|
|
|
"us-east-1",
|
|
|
|
},
|
|
|
|
expected: false,
|
|
|
|
},
|
|
|
|
// complex test: globbing
|
|
|
|
{
|
2016-11-09 18:27:04 +00:00
|
|
|
constraints: types.Constraints{
|
2016-05-20 15:17:38 +00:00
|
|
|
{
|
|
|
|
Key: "tag",
|
|
|
|
MustMatch: true,
|
|
|
|
Regex: "us-east-*",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
tags: []string{
|
|
|
|
"us-east-1",
|
|
|
|
},
|
|
|
|
expected: true,
|
|
|
|
},
|
|
|
|
// complex test: multiple constraints
|
|
|
|
{
|
2016-11-09 18:27:04 +00:00
|
|
|
constraints: types.Constraints{
|
2016-05-20 15:17:38 +00: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 02:05:43 +00:00
|
|
|
nil,
|
2016-05-20 15:17:38 +00:00
|
|
|
}
|
|
|
|
actual, _ := provider.MatchConstraints(c.tags)
|
|
|
|
if actual != c.expected {
|
2016-08-14 02:05:43 +00:00
|
|
|
t.Fatalf("test #%v: expected %t, got %t, for %#v", i, c.expected, actual, c.constraints)
|
2016-05-20 15:17:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-12-03 09:12:22 +00: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 14:46:44 +00:00
|
|
|
[frontends.{{normalize "frontend/1"}}]
|
2016-12-03 09:12:22 +00: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 13:49:53 +00:00
|
|
|
configuration, err := provider.GetConfiguration(templateFile.Name(), nil, nil)
|
2016-12-03 09:12:22 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("Shouldn't have error out, got %v", err)
|
|
|
|
}
|
|
|
|
if configuration == nil {
|
2017-05-26 15:03:14 +00:00
|
|
|
t.Fatal("Configuration should not be nil, but was")
|
2016-12-03 09:12:22 +00:00
|
|
|
}
|
|
|
|
if _, ok := configuration.Backends["backend1"]; !ok {
|
2017-05-26 15:03:14 +00:00
|
|
|
t.Fatal("backend1 should exists, but it not")
|
2016-12-03 09:12:22 +00:00
|
|
|
}
|
|
|
|
if _, ok := configuration.Frontends["frontend-1"]; !ok {
|
2017-05-26 15:03:14 +00:00
|
|
|
t.Fatal("Frontend frontend-1 should exists, but it not")
|
2016-12-03 09:12:22 +00:00
|
|
|
}
|
|
|
|
}
|
2017-08-10 18:42:39 +00: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 09:12:03 +00: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)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func readTemplateFile(t *testing.T, path string) string {
|
|
|
|
t.Helper()
|
|
|
|
expectedContent, err := ioutil.ReadFile(path)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
return string(expectedContent)
|
|
|
|
}
|