Support YAML for the dynamic configuration.

This commit is contained in:
Ludovic Fernandez 2019-06-26 18:18:04 +02:00 committed by Traefiker Bot
parent 96962dd21f
commit e69d4cba88
36 changed files with 1529 additions and 289 deletions

View file

@ -49,7 +49,12 @@ Once positioned, this option sets (and resets) all the default values of the sub
### Configuration File ### Configuration File
At startup, Traefik searches for a file named `traefik.toml` in `/etc/traefik/`, `$XDG_CONFIG_HOME/`, `$HOME/.config/`, and `.` (_the working directory_). At startup, Traefik searches for a file named `traefik.toml` (or `traefik.yml` or `traefik.yaml`) in:
- `/etc/traefik/`
- `$XDG_CONFIG_HOME/`
- `$HOME/.config/`
- `.` (_the working directory_).
You can override this using the `configFile` argument. You can override this using the `configFile` argument.

View file

@ -3,10 +3,10 @@
Good Old Configuration File Good Old Configuration File
{: .subtitle } {: .subtitle }
The file provider lets you define the [dynamic configuration](./overview.md) in a `toml` file. The file provider lets you define the [dynamic configuration](./overview.md) in a TOML or YAML file.
You can write these configuration elements: You can write these configuration elements:
* At the end of the main Traefik configuration file (by default: `traefik.toml`). * At the end of the main Traefik configuration file (by default: `traefik.toml`/`traefik.yml`/`traefik.yaml`).
* In [a dedicated file](#filename) * In [a dedicated file](#filename)
* In [several dedicated files](#directory) * In [several dedicated files](#directory)
@ -20,10 +20,20 @@ You can write these configuration elements:
??? example "Declaring Routers, Middlewares & Services" ??? example "Declaring Routers, Middlewares & Services"
``` toml Enabling the file provider:
# Enabling the file provider
[providers.file]
```toml tab="TOML"
[providers.file]
```
```yaml tab="YAML"
providers:
file: {}
```
Declaring Routers, Middlewares & Services:
```toml tab="TOML"
[http] [http]
# Add the router # Add the router
[http.routers] [http.routers]
@ -49,6 +59,32 @@ You can write these configuration elements:
[[http.services.service-foo.LoadBalancer.Servers]] [[http.services.service-foo.LoadBalancer.Servers]]
url = "http://bar/" url = "http://bar/"
``` ```
```yaml tab="YAML"
http:
routers:
router0:
entrypoints:
- web
middlewares:
- my-basic-auth
service: service-foo
rule: Path(`foo`)
middlewares:
my-basic-auth:
basicAuth:
users:
- test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/
- test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0
usersfile: etc/traefik/.htpasswd
headerfield: ""
services:
service-foo:
loadbalancer:
servers:
- url: http://foo/
- url: http://bar/
```
## Provider Configuration Options ## Provider Configuration Options
@ -61,24 +97,36 @@ _Optional_
Defines the path of the configuration file. Defines the path of the configuration file.
```toml ```toml tab="TOML"
[providers] [providers]
[providers.file] [providers.file]
filename = "rules.toml" filename = "rules.toml"
``` ```
```yaml tab="YAML"
providers:
file:
filename: rules.yaml
```
### `directory` ### `directory`
_Optional_ _Optional_
Defines the directory that contains the configuration files. Defines the directory that contains the configuration files.
```toml ```toml tab="TOML"
[providers] [providers]
[providers.file] [providers.file]
directory = "/path/to/config" directory = "/path/to/config"
``` ```
```yaml tab="YAML"
providers:
file:
directory: /path/to/config
```
### `watch` ### `watch`
_Optional_ _Optional_
@ -86,24 +134,32 @@ _Optional_
Set the `watch` option to `true` to allow Traefik to automatically watch for file changes. Set the `watch` option to `true` to allow Traefik to automatically watch for file changes.
It works with both the `filename` and the `directory` options. It works with both the `filename` and the `directory` options.
```toml ```toml tab="TOML"
[providers] [providers]
[providers.file] [providers.file]
filename = "rules.toml" filename = "rules.toml"
watch = true watch = true
``` ```
### TOML Templating ```yaml tab="YAML"
providers:
file:
filename: rules.yml
watch: true
```
### Go Templating
!!! warning !!! warning
TOML templating only works along with dedicated configuration files. Templating does not work in the Traefik main configuration file. Go Templating only works along with dedicated configuration files.
Templating does not work in the Traefik main configuration file.
Traefik allows using TOML templating. Traefik allows using Go templating.
Thus, it's possible to define easily lot of routers, services and TLS certificates as described in the file `template-rules.toml` : Thus, it's possible to define easily lot of routers, services and TLS certificates as described in the file `template-rules.toml` :
??? example "Configuring Using Templating" ??? example "Configuring Using Templating"
```toml ```toml tab="TOML"
# template-rules.toml # template-rules.toml
[http] [http]
@ -149,3 +205,43 @@ Thus, it's possible to define easily lot of routers, services and TLS certificat
# ... # ...
{{ end }} {{ end }}
``` ```
```yaml tab="YAML"
http:
{{range $i, $e := until 100 }}
routers:
router{{ $e }:
# ...
{{end}}
{{range $i, $e := until 100 }}
services:
application{{ $e }}:
# ...
{{end}}
tcp:
{{range $i, $e := until 100 }}
routers:
router{{ $e }:
# ...
{{end}}
{{range $i, $e := until 100 }}
services:
service{{ $e }}:
# ...
{{end}}
{{ range $i, $e := until 10 }}
tls:
store:
- "my-store-foo-{{ $e }}"
- "my-store-bar-{{ $e }}"
certificate:
certfile: "/etc/traefik/cert-{{ $e }}.pem"
keyfile: "/etc/traefik/cert-{{ $e }}.key"
{{end}}
```

View file

@ -1,8 +1,12 @@
# File Configuration Reference # File Configuration Reference
Dynamic configuration with toml files Dynamic configuration with files
{: .subtitle } {: .subtitle }
```toml ```toml tab="TOML"
--8<-- "content/reference/dynamic-configuration/file.toml" --8<-- "content/reference/dynamic-configuration/file.toml"
``` ```
```yml tab="YAML"
--8<-- "content/reference/dynamic-configuration/file.yaml"
```

View file

@ -0,0 +1,294 @@
http:
routers:
Router0:
entrypoints:
- foobar
- foobar
middlewares:
- foobar
- foobar
service: foobar
rule: foobar
priority: 42
tls:
options: TLS0
middlewares:
Middleware0:
addPrefix:
prefix: foobar
Middleware1:
stripPrefix:
prefixes:
- foobar
- foobar
Middleware2:
stripPrefixRegex:
regex:
- foobar
- foobar
Middleware3:
replacePath:
path: foobar
Middleware4:
replacePathRegex:
regex: foobar
replacement: foobar
Middleware5:
chain:
middlewares:
- foobar
- foobar
Middleware6:
ipWhiteList:
sourcerange:
- foobar
- foobar
ipstrategy: null
Middleware7:
ipWhiteList:
sourcerange: []
ipstrategy:
depth: 42
excludedips:
- foobar
- foobar
Middleware8:
headers:
customrequestheaders:
name0: foobar
name1: foobar
customresponseheaders:
name0: foobar
name1: foobar
accesscontrolallowcredentials: true
accesscontrolallowheaders:
- foobar
- foobar
accesscontrolallowmethods:
- foobar
- foobar
accesscontrolalloworigin: foobar
accesscontrolexposeheaders:
- foobar
- foobar
accesscontrolmaxage: 42
addvaryheader: true
allowedhosts:
- foobar
- foobar
hostsproxyheaders:
- foobar
- foobar
sslredirect: true
ssltemporaryredirect: true
sslhost: foobar
sslproxyheaders:
name0: foobar
name1: foobar
sslforcehost: true
stsseconds: 42
stsincludesubdomains: true
stspreload: true
forcestsheader: true
framedeny: true
customframeoptionsvalue: foobar
contenttypenosniff: true
browserxssfilter: true
custombrowserxssvalue: foobar
contentsecuritypolicy: foobar
publickey: foobar
referrerpolicy: foobar
isdevelopment: true
Middleware9:
errors:
status:
- foobar
- foobar
service: foobar
query: foobar
Middleware10:
rateLimit:
rateset:
Rate0:
period: 42000000000
average: 42
burst: 42
Rate1:
period: 42000000000
average: 42
burst: 42
extractorfunc: foobar
Middleware11:
redirectRegex:
regex: foobar
replacement: foobar
permanent: true
Middleware12:
redirectScheme:
scheme: foobar
port: foobar
permanent: true
Middleware13:
basicAuth:
users:
- foobar
- foobar
usersfile: foobar
realm: foobar
removeheader: true
headerfield: foobar
Middleware14:
digestAuth:
users:
- foobar
- foobar
usersfile: foobar
removeheader: true
realm: foobar
headerfield: foobar
Middleware15:
forwardAuth:
address: foobar
tls:
ca: foobar
caoptional: true
cert: foobar
key: foobar
insecureskipverify: true
trustforwardheader: true
authresponseheaders:
- foobar
- foobar
Middleware16:
maxConn:
amount: 42
extractorfunc: foobar
Middleware17:
buffering:
maxrequestbodybytes: 42
memrequestbodybytes: 42
maxresponsebodybytes: 42
memresponsebodybytes: 42
retryexpression: foobar
Middleware18:
circuitBreaker:
expression: foobar
Middleware19:
compress: {}
Middleware20:
passTLSClientCert:
pem: true
info:
notafter: true
notbefore: true
sans: true
subject:
country: true
province: true
locality: true
organization: true
commonname: true
serialnumber: true
domaincomponent: true
issuer:
country: true
province: true
locality: true
organization: true
commonname: true
serialnumber: true
domaincomponent: true
Middleware21:
retry:
attempts: 42
services:
Service0:
loadbalancer:
stickiness:
cookiename: foobar
securecookie: false
httponlycookie: false
servers:
- url: foobar
scheme: ""
port: ""
- url: foobar
scheme: ""
port: ""
healthcheck:
scheme: foobar
path: foobar
port: 42
interval: foobar
timeout: foobar
hostname: foobar
headers:
name0: foobar
name1: foobar
passhostheader: true
responseforwarding:
flushinterval: foobar
tcp:
routers:
TCPRouter0:
entrypoints:
- foobar
- foobar
service: foobar
rule: foobar
tls:
passthrough: true
options: TLS1
services:
TCPService0:
loadbalancer:
servers:
- address: foobar
port: ""
- address: foobar
port: ""
tls:
- stores:
- foobar
- foobar
certificate:
certfile: foobar
keyfile: foobar
- stores:
- foobar
- foobar
certificate:
certfile: foobar
keyfile: foobar
tlsoptions:
TLS0:
minversion: foobar
ciphersuites:
- foobar
- foobar
clientca:
files:
- foobar
- foobar
optional: true
snistrict: true
TLS1:
minversion: foobar
ciphersuites:
- foobar
- foobar
clientca:
files:
- foobar
- foobar
optional: true
snistrict: true
tlsstores:
Store0:
defaultcertificate:
certfile: foobar
keyfile: foobar
Store1:
defaultcertificate:
certfile: foobar
keyfile: foobar

View file

@ -1,7 +1,9 @@
# Static Configuration: File # Static Configuration: File
## TOML ```toml tab="TOML"
```toml
--8<-- "content/reference/static-configuration/file.toml" --8<-- "content/reference/static-configuration/file.toml"
``` ```
```yml tab="YAML"
--8<-- "content/reference/static-configuration/file.yaml"
```

View file

@ -133,16 +133,16 @@
[Metrics.Datadog] [Metrics.Datadog]
Address = "foobar" Address = "foobar"
PushInterval = "foobar" PushInterval = "10s"
[Metrics.StatsD] [Metrics.StatsD]
Address = "foobar" Address = "foobar"
PushInterval = "foobar" PushInterval = "10s"
[Metrics.InfluxDB] [Metrics.InfluxDB]
Address = "foobar" Address = "foobar"
Protocol = "foobar" Protocol = "foobar"
PushInterval = "foobar" PushInterval = "10s"
Database = "foobar" Database = "foobar"
RetentionPolicy = "foobar" RetentionPolicy = "foobar"
Username = "foobar" Username = "foobar"

View file

@ -0,0 +1,240 @@
global:
checknewversion: true
sendanonymoususage: true
serverstransport:
insecureskipverify: true
rootcas:
- foobar
- foobar
maxidleconnsperhost: 42
forwardingtimeouts:
dialtimeout: 42000000000
responseheadertimeout: 42000000000
entrypoints:
EntryPoint0:
address: foobar
transport:
lifecycle:
requestacceptgracetimeout: 42000000000
gracetimeout: 42000000000
respondingtimeouts:
readtimeout: 42000000000
writetimeout: 42000000000
idletimeout: 42000000000
proxyprotocol:
insecure: true
trustedips:
- foobar
- foobar
forwardedheaders:
insecure: true
trustedips:
- foobar
- foobar
providers:
providersthrottleduration: 42000000000
docker:
constraints: foobar
watch: true
endpoint: foobar
defaultrule: foobar
tls:
ca: foobar
caoptional: true
cert: foobar
key: foobar
insecureskipverify: true
exposedbydefault: true
usebindportip: true
swarmmode: true
network: foobar
swarmmoderefreshseconds: 42000000000
file:
directory: foobar
watch: true
filename: foobar
debugloggeneratedtemplate: true
traefikfile: foobar
marathon:
constraints: foobar
trace: true
watch: true
endpoint: foobar
defaultrule: foobar
exposedbydefault: true
dcostoken: foobar
tls:
ca: foobar
caoptional: true
cert: foobar
key: foobar
insecureskipverify: true
dialertimeout: 42000000000
responseheadertimeout: 42000000000
tlshandshaketimeout: 42000000000
keepalive: 42000000000
forcetaskhostname: true
basic:
httpbasicauthuser: foobar
httpbasicpassword: foobar
respectreadinesschecks: true
kubernetes:
endpoint: foobar
token: foobar
certauthfilepath: foobar
disablepasshostheaders: true
namespaces:
- foobar
- foobar
labelselector: foobar
ingressclass: foobar
ingressendpoint:
ip: foobar
hostname: foobar
publishedservice: foobar
kubernetescrd:
endpoint: foobar
token: foobar
certauthfilepath: foobar
disablepasshostheaders: true
namespaces:
- foobar
- foobar
labelselector: foobar
ingressclass: foobar
rest:
entrypoint: foobar
rancher:
constraints: foobar
watch: true
defaultrule: foobar
exposedbydefault: true
enableservicehealthfilter: true
refreshseconds: 42
intervalpoll: true
prefix: foobar
api:
entrypoint: foobar
dashboard: true
debug: false
statistics:
recenterrors: 42
middlewares:
- foobar
- foobar
dashboardassets: null
metrics:
prometheus:
buckets:
- 42
- 42
entrypoint: foobar
middlewares:
- foobar
- foobar
datadog:
address: foobar
pushinterval: 10000000000
statsd:
address: foobar
pushinterval: 10000000000
influxdb:
address: foobar
protocol: foobar
pushinterval: 10000000000
database: foobar
retentionpolicy: foobar
username: foobar
password: foobar
ping:
entrypoint: foobar
middlewares:
- foobar
- foobar
log:
level: foobar
filepath: foobar
format: foobar
accesslog:
filepath: foobar
format: foobar
filters:
statuscodes:
- foobar
- foobar
retryattempts: true
minduration: 42000000000
fields:
defaultmode: foobar
names:
name0: foobar
name1: foobar
headers:
defaultmode: foobar
names:
name0: foobar
name1: foobar
bufferingsize: 42
tracing:
backend: foobar
servicename: foobar
spannamelimit: 42
jaeger:
samplingserverurl: foobar
samplingtype: foobar
samplingparam: 42
localagenthostport: foobar
gen128bit: true
propagation: foobar
tracecontextheadername: foobar
zipkin:
httpendpoint: foobar
samespan: true
id128bit: true
debug: true
samplerate: 42
datadog:
localagenthostport: foobar
globaltag: foobar
debug: true
prioritysampling: true
traceidheadername: foobar
parentidheadername: foobar
samplingpriorityheadername: foobar
bagageprefixheadername: foobar
instana:
localagenthost: foobar
localagentport: 42
loglevel: foobar
haystack: null
hostresolver:
cnameflattening: true
resolvconfig: foobar
resolvdepth: 42
acme:
email: foobar
acmelogging: true
caserver: foobar
storage: foobar
entrypoint: foobar
keytype: foobar
onhostrule: true
dnschallenge:
provider: foobar
delaybeforecheck: 42000000000
resolvers:
- foobar
- foobar
disablepropagationcheck: true
httpchallenge:
entrypoint: foobar
tlschallenge: {}
domains:
- main: foobar
sans:
- foobar
- foobar
- main: foobar
sans:
- foobar
- foobar

View file

@ -24,29 +24,79 @@ If they do, the router might transform the request using pieces of [middleware](
Below is an example of a full configuration file for the [file provider](../providers/file.md) that forwards `http://domain/whoami/` requests to a service reachable on `http://private/whoami-service/`. Below is an example of a full configuration file for the [file provider](../providers/file.md) that forwards `http://domain/whoami/` requests to a service reachable on `http://private/whoami-service/`.
In the process, Traefik will make sure that the user is authenticated (using the [BasicAuth middleware](../middlewares/basicauth.md)). In the process, Traefik will make sure that the user is authenticated (using the [BasicAuth middleware](../middlewares/basicauth.md)).
```toml Static configuration:
```toml tab="TOML"
[entryPoints] [entryPoints]
[entryPoints.web] [entryPoints.web]
address = ":8081" # Listen on port 8081 for incoming requests # Listen on port 8081 for incoming requests
address = ":8081"
[providers] [providers]
[providers.file] # Enable the file provider to define routers / middlewares / services in a file # Enable the file provider to define routers / middlewares / services in a file
[providers.file]
```
[http] # http routing section ```yaml tab="YAML"
[http.routers] entrypoints:
[http.routers.to-whoami] # Define a connection between requests and services web:
rule = "Host(domain) && PathPrefix(/whoami/)" # Listen on port 8081 for incoming requests
middlewares = ["test-user"] # If the rule matches, applies the middleware address: :8081
service = "whoami" # If the rule matches, forward to the whoami service (declared below) providers:
# Enable the file provider to define routers / middlewares / services in a file
file: {}
```
[http.middlewares] Dynamic configuration:
[http.middlewares.test-user.basicauth] # Define an authentication mechanism
users = ["test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/"]
[http.services] ```toml tab="TOML"
[http.services.whoami.loadbalancer] # Define how to reach an existing service on our infrastructure # http routing section
[[http.services.whoami.loadbalancer.servers]] [http]
url = "http://private/whoami-service" [http.routers]
# Define a connection between requests and services
[http.routers.to-whoami]
rule = "Host(`domain`) && PathPrefix(`/whoami/`)"
# If the rule matches, applies the middleware
middlewares = ["test-user"]
# If the rule matches, forward to the whoami service (declared below)
service = "whoami"
[http.middlewares]
# Define an authentication mechanism
[http.middlewares.test-user.basicauth]
users = ["test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/"]
[http.services]
# Define how to reach an existing service on our infrastructure
[http.services.whoami.loadbalancer]
[[http.services.whoami.loadbalancer.servers]]
url = "http://private/whoami-service"
```
```yaml tab="YAML"
# http routing section
http:
routers:
# Define a connection between requests and services
to-whoami:
rule: "Host(`domain`) && PathPrefix(`/whoami/`)"
# If the rule matches, applies the middleware
middlewares:
- test-user
# If the rule matches, forward to the whoami service (declared below)
service: whoami
middlewares:
# Define an authentication mechanism
test-user:
basicAuth:
users:
- test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/
services:
# Define how to reach an existing service on our infrastructure
whoami:
loadbalancer:
servers:
- url: http://private/whoami-service
``` ```
!!! note "The File Provider" !!! note "The File Provider"
@ -61,27 +111,51 @@ In the process, Traefik will make sure that the user is authenticated (using the
??? example "Adding a TCP route for TLS requests on whoami.traefik.io" ??? example "Adding a TCP route for TLS requests on whoami.traefik.io"
```toml Static configuration:
```toml tab="TOML"
[entryPoints] [entryPoints]
[entryPoints.web] [entryPoints.web]
address = ":8081" # Listen on port 8081 for incoming requests # Listen on port 8081 for incoming requests
address = ":8081"
[providers] [providers]
[providers.file] # Enable the file provider to define routers / middlewares / services in a file # Enable the file provider to define routers / middlewares / services in a file
[providers.file]
```
```yaml tab="YAML"
entrypoints:
web:
# Listen on port 8081 for incoming requests
address: :8081
providers:
# Enable the file provider to define routers / middlewares / services in a file
file: {}
```
Dynamic configuration:
[http] # http routing section ```toml tab="TOML"
# http routing section
[http]
[http.routers] [http.routers]
[http.routers.to-whoami] # Define a connection between requests and services # Define a connection between requests and services
rule = "Host(`domain`) && PathPrefix(/whoami/)" [http.routers.to-whoami]
middlewares = ["test-user"] # If the rule matches, applies the middleware rule = "Host(`domain`) && PathPrefix(`/whoami/`)"
service = "whoami" # If the rule matches, forward to the whoami service (declared below) # If the rule matches, applies the middleware
middlewares = ["test-user"]
# If the rule matches, forward to the whoami service (declared below)
service = "whoami"
[http.middlewares] [http.middlewares]
[http.middlewares.test-user.basicauth] # Define an authentication mechanism # Define an authentication mechanism
[http.middlewares.test-user.basicauth]
users = ["test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/"] users = ["test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/"]
[http.services] [http.services]
[http.services.whoami.loadbalancer] # Define how to reach an existing service on our infrastructure # Define how to reach an existing service on our infrastructure
[http.services.whoami.loadbalancer]
[[http.services.whoami.loadbalancer.servers]] [[http.services.whoami.loadbalancer.servers]]
url = "http://private/whoami-service" url = "http://private/whoami-service"
@ -97,3 +171,39 @@ In the process, Traefik will make sure that the user is authenticated (using the
[[tcp.services.whoami-tcp.loadbalancer.servers]] [[tcp.services.whoami-tcp.loadbalancer.servers]]
address = "xx.xx.xx.xx:xx" address = "xx.xx.xx.xx:xx"
``` ```
```yaml tab="YAML"
# http routing section
http:
routers:
# Define a connection between requests and services
to-whoami:
rule: Host(`domain`) && PathPrefix(`/whoami/`)
# If the rule matches, applies the middleware
middlewares:
- test-user
# If the rule matches, forward to the whoami service (declared below)
service: whoami
middlewares:
# Define an authentication mechanism
test-user:
basicAuth:
users:
- test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/
services:
# Define how to reach an existing service on our infrastructure
whoami:
loadbalancer:
servers:
- url: http://private/whoami-service
tcp:
routers:
to-whoami-tcp:
service: whoami-tcp
rule: HostSNI(`whoami-tcp.traefik.io`)
services:
whoami-tcp:
loadbalancer:
servers:
- address: xx.xx.xx.xx:xx
```

View file

@ -215,7 +215,7 @@ type Message struct {
type Configuration struct { type Configuration struct {
HTTP *HTTPConfiguration HTTP *HTTPConfiguration
TCP *TCPConfiguration TCP *TCPConfiguration
TLS []*traefiktls.Configuration `json:"-" label:"-"` TLS []*traefiktls.Configuration `json:"-" label:"-" yaml:"tls"`
TLSOptions map[string]traefiktls.TLS TLSOptions map[string]traefiktls.TLS
TLSStores map[string]traefiktls.Store TLSStores map[string]traefiktls.Store
} }

View file

@ -5,6 +5,7 @@ import (
"io/ioutil" "io/ioutil"
"path/filepath" "path/filepath"
"reflect" "reflect"
"strings"
"github.com/BurntSushi/toml" "github.com/BurntSushi/toml"
"github.com/containous/traefik/pkg/config/parser" "github.com/containous/traefik/pkg/config/parser"
@ -22,7 +23,7 @@ func decodeFileToNode(filePath string, filters ...string) (*parser.Node, error)
data := make(map[string]interface{}) data := make(map[string]interface{})
switch filepath.Ext(filePath) { switch strings.ToLower(filepath.Ext(filePath)) {
case ".toml": case ".toml":
err = toml.Unmarshal(content, &data) err = toml.Unmarshal(content, &data)
if err != nil { if err != nil {
@ -30,14 +31,11 @@ func decodeFileToNode(filePath string, filters ...string) (*parser.Node, error)
} }
case ".yml", ".yaml": case ".yml", ".yaml":
var err error
err = yaml.Unmarshal(content, data) err = yaml.Unmarshal(content, data)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return decodeRawToNode(data, parser.DefaultRootName, filters...)
default: default:
return nil, fmt.Errorf("unsupported file extension: %s", filePath) return nil, fmt.Errorf("unsupported file extension: %s", filePath)
} }

View file

@ -9,27 +9,27 @@ import (
// Middleware holds the Middleware configuration. // Middleware holds the Middleware configuration.
type Middleware struct { type Middleware struct {
AddPrefix *AddPrefix `json:"addPrefix,omitempty"` AddPrefix *AddPrefix `json:"addPrefix,omitempty" yaml:"addPrefix,omitempty"`
StripPrefix *StripPrefix `json:"stripPrefix,omitempty"` StripPrefix *StripPrefix `json:"stripPrefix,omitempty" yaml:"stripPrefix,omitempty"`
StripPrefixRegex *StripPrefixRegex `json:"stripPrefixRegex,omitempty"` StripPrefixRegex *StripPrefixRegex `json:"stripPrefixRegex,omitempty" yaml:"stripPrefixRegex,omitempty"`
ReplacePath *ReplacePath `json:"replacePath,omitempty"` ReplacePath *ReplacePath `json:"replacePath,omitempty" yaml:"replacePath,omitempty"`
ReplacePathRegex *ReplacePathRegex `json:"replacePathRegex,omitempty"` ReplacePathRegex *ReplacePathRegex `json:"replacePathRegex,omitempty" yaml:"replacePathRegex,omitempty"`
Chain *Chain `json:"chain,omitempty"` Chain *Chain `json:"chain,omitempty" yaml:"chain,omitempty"`
IPWhiteList *IPWhiteList `json:"ipWhiteList,omitempty"` IPWhiteList *IPWhiteList `json:"ipWhiteList,omitempty" yaml:"ipWhiteList,omitempty"`
Headers *Headers `json:"headers,omitempty"` Headers *Headers `json:"headers,omitempty" yaml:"headers,omitempty"`
Errors *ErrorPage `json:"errors,omitempty"` Errors *ErrorPage `json:"errors,omitempty" yaml:"errors,omitempty"`
RateLimit *RateLimit `json:"rateLimit,omitempty"` RateLimit *RateLimit `json:"rateLimit,omitempty" yaml:"rateLimit,omitempty"`
RedirectRegex *RedirectRegex `json:"redirectRegex,omitempty"` RedirectRegex *RedirectRegex `json:"redirectRegex,omitempty" yaml:"redirectRegex,omitempty"`
RedirectScheme *RedirectScheme `json:"redirectScheme,omitempty"` RedirectScheme *RedirectScheme `json:"redirectScheme,omitempty" yaml:"redirectScheme,omitempty"`
BasicAuth *BasicAuth `json:"basicAuth,omitempty"` BasicAuth *BasicAuth `json:"basicAuth,omitempty" yaml:"basicAuth,omitempty"`
DigestAuth *DigestAuth `json:"digestAuth,omitempty"` DigestAuth *DigestAuth `json:"digestAuth,omitempty" yaml:"digestAuth,omitempty"`
ForwardAuth *ForwardAuth `json:"forwardAuth,omitempty"` ForwardAuth *ForwardAuth `json:"forwardAuth,omitempty" yaml:"forwardAuth,omitempty"`
MaxConn *MaxConn `json:"maxConn,omitempty"` MaxConn *MaxConn `json:"maxConn,omitempty" yaml:"maxConn,omitempty"`
Buffering *Buffering `json:"buffering,omitempty"` Buffering *Buffering `json:"buffering,omitempty" yaml:"buffering,omitempty"`
CircuitBreaker *CircuitBreaker `json:"circuitBreaker,omitempty"` CircuitBreaker *CircuitBreaker `json:"circuitBreaker,omitempty" yaml:"circuitBreaker,omitempty"`
Compress *Compress `json:"compress,omitempty" label:"allowEmpty"` Compress *Compress `json:"compress,omitempty" label:"allowEmpty" yaml:"compress,omitempty" label:"allowEmpty"`
PassTLSClientCert *PassTLSClientCert `json:"passTLSClientCert,omitempty"` PassTLSClientCert *PassTLSClientCert `json:"passTLSClientCert,omitempty" yaml:"passTLSClientCert,omitempty"`
Retry *Retry `json:"retry,omitempty"` Retry *Retry `json:"retry,omitempty" yaml:"retry,omitempty"`
} }
// +k8s:deepcopy-gen=true // +k8s:deepcopy-gen=true

View file

@ -7,7 +7,6 @@ import (
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"os" "os"
"path"
"path/filepath" "path/filepath"
"strings" "strings"
"text/template" "text/template"
@ -20,6 +19,7 @@ import (
"github.com/containous/traefik/pkg/safe" "github.com/containous/traefik/pkg/safe"
"github.com/containous/traefik/pkg/tls" "github.com/containous/traefik/pkg/tls"
"gopkg.in/fsnotify.v1" "gopkg.in/fsnotify.v1"
"gopkg.in/yaml.v2"
) )
const providerName = "file" const providerName = "file"
@ -170,28 +170,13 @@ func sendConfigToChannel(configurationChan chan<- config.Message, configuration
} }
} }
func readFile(filename string) (string, error) {
if len(filename) > 0 {
buf, err := ioutil.ReadFile(filename)
if err != nil {
return "", err
}
return string(buf), nil
}
return "", fmt.Errorf("invalid filename: %s", filename)
}
func (p *Provider) loadFileConfig(filename string, parseTemplate bool) (*config.Configuration, error) { func (p *Provider) loadFileConfig(filename string, parseTemplate bool) (*config.Configuration, error) {
fileContent, err := readFile(filename) var err error
if err != nil {
return nil, fmt.Errorf("error reading configuration file: %s - %s", filename, err)
}
var configuration *config.Configuration var configuration *config.Configuration
if parseTemplate { if parseTemplate {
configuration, err = p.CreateConfiguration(fileContent, template.FuncMap{}, false) configuration, err = p.CreateConfiguration(filename, template.FuncMap{}, false)
} else { } else {
configuration, err = p.DecodeConfiguration(fileContent) configuration, err = p.DecodeConfiguration(filename)
} }
if err != nil { if err != nil {
return nil, err return nil, err
@ -223,7 +208,6 @@ func (p *Provider) loadFileConfigFromDirectory(ctx context.Context, directory st
logger := log.FromContext(ctx) logger := log.FromContext(ctx)
fileList, err := ioutil.ReadDir(directory) fileList, err := ioutil.ReadDir(directory)
if err != nil { if err != nil {
return configuration, fmt.Errorf("unable to read directory %s: %v", directory, err) return configuration, fmt.Errorf("unable to read directory %s: %v", directory, err)
} }
@ -243,6 +227,7 @@ func (p *Provider) loadFileConfigFromDirectory(ctx context.Context, directory st
} }
configTLSMaps := make(map[*tls.Configuration]struct{}) configTLSMaps := make(map[*tls.Configuration]struct{})
for _, item := range fileList { for _, item := range fileList {
if item.IsDir() { if item.IsDir() {
@ -251,13 +236,17 @@ func (p *Provider) loadFileConfigFromDirectory(ctx context.Context, directory st
return configuration, fmt.Errorf("unable to load content configuration from subdirectory %s: %v", item, err) return configuration, fmt.Errorf("unable to load content configuration from subdirectory %s: %v", item, err)
} }
continue continue
} else if !strings.HasSuffix(item.Name(), ".toml") && !strings.HasSuffix(item.Name(), ".tmpl") { }
switch strings.ToLower(filepath.Ext(item.Name())) {
case ".toml", ".yaml", ".yml":
// noop
default:
continue continue
} }
var c *config.Configuration var c *config.Configuration
c, err = p.loadFileConfig(path.Join(directory, item.Name()), true) c, err = p.loadFileConfig(filepath.Join(directory, item.Name()), true)
if err != nil { if err != nil {
return configuration, err return configuration, err
} }
@ -318,7 +307,12 @@ func (p *Provider) loadFileConfigFromDirectory(ctx context.Context, directory st
} }
// CreateConfiguration creates a provider configuration from content using templating. // CreateConfiguration creates a provider configuration from content using templating.
func (p *Provider) CreateConfiguration(tmplContent string, funcMap template.FuncMap, templateObjects interface{}) (*config.Configuration, error) { func (p *Provider) CreateConfiguration(filename string, funcMap template.FuncMap, templateObjects interface{}) (*config.Configuration, error) {
tmplContent, err := readFile(filename)
if err != nil {
return nil, fmt.Errorf("error reading configuration file: %s - %s", filename, err)
}
var defaultFuncMap = sprig.TxtFuncMap() var defaultFuncMap = sprig.TxtFuncMap()
defaultFuncMap["normalize"] = provider.Normalize defaultFuncMap["normalize"] = provider.Normalize
defaultFuncMap["split"] = strings.Split defaultFuncMap["split"] = strings.Split
@ -328,7 +322,7 @@ func (p *Provider) CreateConfiguration(tmplContent string, funcMap template.Func
tmpl := template.New(p.Filename).Funcs(defaultFuncMap) tmpl := template.New(p.Filename).Funcs(defaultFuncMap)
_, err := tmpl.Parse(tmplContent) _, err = tmpl.Parse(tmplContent)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -341,14 +335,25 @@ func (p *Provider) CreateConfiguration(tmplContent string, funcMap template.Func
var renderedTemplate = buffer.String() var renderedTemplate = buffer.String()
if p.DebugLogGeneratedTemplate { if p.DebugLogGeneratedTemplate {
log.Debugf("Template content: %s", tmplContent) logger := log.WithoutContext().WithField(log.ProviderName, providerName)
log.Debugf("Rendering results: %s", renderedTemplate) logger.Debugf("Template content: %s", tmplContent)
logger.Debugf("Rendering results: %s", renderedTemplate)
} }
return p.DecodeConfiguration(renderedTemplate)
return p.decodeConfiguration(filename, renderedTemplate)
} }
// DecodeConfiguration Decodes a *types.Configuration from a content. // DecodeConfiguration Decodes a *types.Configuration from a content.
func (p *Provider) DecodeConfiguration(content string) (*config.Configuration, error) { func (p *Provider) DecodeConfiguration(filename string) (*config.Configuration, error) {
content, err := readFile(filename)
if err != nil {
return nil, fmt.Errorf("error reading configuration file: %s - %s", filename, err)
}
return p.decodeConfiguration(filename, content)
}
func (p *Provider) decodeConfiguration(filePath string, content string) (*config.Configuration, error) {
configuration := &config.Configuration{ configuration := &config.Configuration{
HTTP: &config.HTTPConfiguration{ HTTP: &config.HTTPConfiguration{
Routers: make(map[string]*config.Router), Routers: make(map[string]*config.Router),
@ -363,8 +368,35 @@ func (p *Provider) DecodeConfiguration(content string) (*config.Configuration, e
TLSStores: make(map[string]tls.Store), TLSStores: make(map[string]tls.Store),
TLSOptions: make(map[string]tls.TLS), TLSOptions: make(map[string]tls.TLS),
} }
if _, err := toml.Decode(content, configuration); err != nil {
return nil, err switch strings.ToLower(filepath.Ext(filePath)) {
case ".toml":
_, err := toml.Decode(content, configuration)
if err != nil {
return nil, err
}
case ".yml", ".yaml":
var err error
err = yaml.Unmarshal([]byte(content), configuration)
if err != nil {
return nil, err
}
default:
return nil, fmt.Errorf("unsupported file extension: %s", filePath)
} }
return configuration, nil return configuration, nil
} }
func readFile(filename string) (string, error) {
if len(filename) > 0 {
buf, err := ioutil.ReadFile(filename)
if err != nil {
return "", err
}
return string(buf), nil
}
return "", fmt.Errorf("invalid filename: %s", filename)
}

View file

@ -2,10 +2,11 @@ package file
import ( import (
"context" "context"
"fmt" "io"
"io/ioutil" "io/ioutil"
"os" "os"
"path" "path/filepath"
"strconv"
"testing" "testing"
"time" "time"
@ -17,14 +18,62 @@ import (
type ProvideTestCase struct { type ProvideTestCase struct {
desc string desc string
directoryContent []string directoryPaths []string
fileContent string filePath string
traefikFileContent string traefikFilePath string
expectedNumRouter int expectedNumRouter int
expectedNumService int expectedNumService int
expectedNumTLSConf int expectedNumTLSConf int
} }
func TestTLSContent(t *testing.T) {
tempDir := createTempDir(t, "testdir")
defer os.RemoveAll(tempDir)
fileTLS, err := createTempFile("./fixtures/toml/tls_file.cert", tempDir)
require.NoError(t, err)
fileConfig, err := ioutil.TempFile(tempDir, "temp*.toml")
require.NoError(t, err)
content := `
[[tls]]
[tls.certificate]
certFile = "` + fileTLS.Name() + `"
keyFile = "` + fileTLS.Name() + `"
`
_, err = fileConfig.Write([]byte(content))
require.NoError(t, err)
provider := &Provider{}
configuration, err := provider.loadFileConfig(fileConfig.Name(), true)
require.NoError(t, err)
require.Equal(t, "CONTENT", configuration.TLS[0].Certificate.CertFile.String())
require.Equal(t, "CONTENT", configuration.TLS[0].Certificate.KeyFile.String())
}
func TestErrorWhenEmptyConfig(t *testing.T) {
provider := &Provider{}
configChan := make(chan config.Message)
errorChan := make(chan struct{})
go func() {
err := provider.Provide(configChan, safe.NewPool(context.Background()))
assert.Error(t, err)
close(errorChan)
}()
timeout := time.After(time.Second)
select {
case <-configChan:
t.Fatal("We should not receive config message")
case <-timeout:
t.Fatal("timeout while waiting for config")
case <-errorChan:
}
}
func TestProvideWithoutWatch(t *testing.T) { func TestProvideWithoutWatch(t *testing.T) {
for _, test := range getTestCases() { for _, test := range getTestCases() {
t.Run(test.desc+" without watch", func(t *testing.T) { t.Run(test.desc+" without watch", func(t *testing.T) {
@ -74,21 +123,20 @@ func TestProvideWithWatch(t *testing.T) {
t.Errorf("timeout while waiting for config") t.Errorf("timeout while waiting for config")
} }
if len(test.fileContent) > 0 { if len(test.filePath) > 0 {
if err := ioutil.WriteFile(provider.Filename, []byte(test.fileContent), 0755); err != nil { err := copyFile(test.filePath, provider.Filename)
t.Error(err) require.NoError(t, err)
}
} }
if len(test.traefikFileContent) > 0 { if len(test.traefikFilePath) > 0 {
if err := ioutil.WriteFile(provider.TraefikFile, []byte(test.traefikFileContent), 0755); err != nil { err := copyFile(test.traefikFilePath, provider.TraefikFile)
t.Error(err) require.NoError(t, err)
}
} }
if len(test.directoryContent) > 0 { if len(test.directoryPaths) > 0 {
for _, fileContent := range test.directoryContent { for i, filePath := range test.directoryPaths {
createRandomFile(t, provider.Directory, fileContent) err := copyFile(filePath, filepath.Join(provider.Directory, strconv.Itoa(i)+filepath.Ext(filePath)))
require.NoError(t, err)
} }
} }
@ -114,62 +162,86 @@ func TestProvideWithWatch(t *testing.T) {
} }
} }
func TestErrorWhenEmptyConfig(t *testing.T) {
provider := &Provider{}
configChan := make(chan config.Message)
errorChan := make(chan struct{})
go func() {
err := provider.Provide(configChan, safe.NewPool(context.Background()))
assert.Error(t, err)
close(errorChan)
}()
timeout := time.After(time.Second)
select {
case <-configChan:
t.Fatal("We should not receive config message")
case <-timeout:
t.Fatal("timeout while waiting for config")
case <-errorChan:
}
}
func getTestCases() []ProvideTestCase { func getTestCases() []ProvideTestCase {
return []ProvideTestCase{ return []ProvideTestCase{
{ {
desc: "simple file", desc: "simple file",
fileContent: createRoutersConfiguration(3) + createServicesConfiguration(6) + createTLS(5), filePath: "./fixtures/toml/simple_file_01.toml",
expectedNumRouter: 3, expectedNumRouter: 3,
expectedNumService: 6, expectedNumService: 6,
expectedNumTLSConf: 5, expectedNumTLSConf: 5,
}, },
{ {
desc: "simple file and a traefik file", desc: "simple file yaml",
fileContent: createRoutersConfiguration(4) + createServicesConfiguration(8) + createTLS(4), filePath: "./fixtures/yaml/simple_file_01.yml",
traefikFileContent: ` expectedNumRouter: 3,
debug=true expectedNumService: 6,
`, expectedNumTLSConf: 5,
},
{
desc: "simple file and a traefik file",
filePath: "./fixtures/toml/simple_file_02.toml",
traefikFilePath: "./fixtures/toml/simple_traefik_file_01.toml",
expectedNumRouter: 4, expectedNumRouter: 4,
expectedNumService: 8, expectedNumService: 8,
expectedNumTLSConf: 4, expectedNumTLSConf: 4,
}, },
{ {
desc: "template file", desc: "simple file and a traefik file yaml",
fileContent: ` filePath: "./fixtures/yaml/simple_file_02.yml",
[http.routers] traefikFilePath: "./fixtures/yaml/simple_traefik_file_01.yml",
{{ range $i, $e := until 20 }} expectedNumRouter: 4,
[http.routers.router{{ $e }}] expectedNumService: 8,
service = "application" expectedNumTLSConf: 4,
{{ end }} },
`, {
desc: "simple traefik file",
traefikFilePath: "./fixtures/toml/simple_traefik_file_02.toml",
expectedNumRouter: 2,
expectedNumService: 3,
expectedNumTLSConf: 4,
},
{
desc: "simple traefik file yaml",
traefikFilePath: "./fixtures/yaml/simple_traefik_file_02.yml",
expectedNumRouter: 2,
expectedNumService: 3,
expectedNumTLSConf: 4,
},
{
desc: "template file",
filePath: "./fixtures/toml/template_file.toml",
expectedNumRouter: 20, expectedNumRouter: 20,
}, },
{
desc: "template file yaml",
filePath: "./fixtures/yaml/template_file.yml",
expectedNumRouter: 20,
},
{
desc: "simple traefik file with templating",
traefikFilePath: "./fixtures/toml/simple_traefik_file_with_templating.toml",
expectedNumRouter: 2,
expectedNumService: 3,
expectedNumTLSConf: 4,
},
{ {
desc: "simple directory", desc: "simple directory",
directoryContent: []string{ directoryPaths: []string{
createRoutersConfiguration(2), "./fixtures/toml/dir01_file01.toml",
createServicesConfiguration(3), "./fixtures/toml/dir01_file02.toml",
createTLS(4), "./fixtures/toml/dir01_file03.toml",
},
expectedNumRouter: 2,
expectedNumService: 3,
expectedNumTLSConf: 4,
},
{
desc: "simple directory yaml",
directoryPaths: []string{
"./fixtures/yaml/dir01_file01.yml",
"./fixtures/yaml/dir01_file02.yml",
"./fixtures/yaml/dir01_file03.yml",
}, },
expectedNumRouter: 2, expectedNumRouter: 2,
expectedNumService: 3, expectedNumService: 3,
@ -177,45 +249,21 @@ func getTestCases() []ProvideTestCase {
}, },
{ {
desc: "template in directory", desc: "template in directory",
directoryContent: []string{ directoryPaths: []string{
` "./fixtures/toml/template_in_directory_file01.toml",
[http.routers] "./fixtures/toml/template_in_directory_file02.toml",
{{ range $i, $e := until 20 }}
[http.routers.router{{ $e }}]
service = "application"
{{ end }}
`,
`
[http.services]
{{ range $i, $e := until 20 }}
[http.services.application-{{ $e }}]
[[http.services.application-{{ $e }}.servers]]
url="http://127.0.0.1"
{{ end }}
`,
}, },
expectedNumRouter: 20, expectedNumRouter: 20,
expectedNumService: 20, expectedNumService: 20,
}, },
{ {
desc: "simple traefik file", desc: "template in directory yaml",
traefikFileContent: ` directoryPaths: []string{
debug=true "./fixtures/yaml/template_in_directory_file01.yml",
[providers.file] "./fixtures/yaml/template_in_directory_file02.yml",
` + createRoutersConfiguration(2) + createServicesConfiguration(3) + createTLS(4), },
expectedNumRouter: 2, expectedNumRouter: 20,
expectedNumService: 3, expectedNumService: 20,
expectedNumTLSConf: 4,
},
{
desc: "simple traefik file with templating",
traefikFileContent: `
temp="{{ getTag \"test\" }}"
[providers.file]
` + createRoutersConfiguration(2) + createServicesConfiguration(3) + createTLS(4),
expectedNumRouter: 2,
expectedNumService: 3,
expectedNumTLSConf: 4,
}, },
} }
} }
@ -226,30 +274,46 @@ func createProvider(t *testing.T, test ProvideTestCase, watch bool) (*Provider,
provider := &Provider{} provider := &Provider{}
provider.Watch = true provider.Watch = true
if len(test.directoryContent) > 0 { if len(test.directoryPaths) > 0 {
if !watch { if !watch {
for _, fileContent := range test.directoryContent { for _, filePath := range test.directoryPaths {
createRandomFile(t, tempDir, fileContent) var err error
_, err = createTempFile(filePath, tempDir)
require.NoError(t, err)
} }
} }
provider.Directory = tempDir provider.Directory = tempDir
} }
if len(test.fileContent) > 0 { if len(test.filePath) > 0 {
if watch {
test.fileContent = ""
}
filename := createRandomFile(t, tempDir, test.fileContent)
provider.Filename = filename.Name()
var file *os.File
if watch {
var err error
file, err = ioutil.TempFile(tempDir, "temp*"+filepath.Ext(test.filePath))
require.NoError(t, err)
} else {
var err error
file, err = createTempFile(test.filePath, tempDir)
require.NoError(t, err)
}
provider.Filename = file.Name()
} }
if len(test.traefikFileContent) > 0 { if len(test.traefikFilePath) > 0 {
var file *os.File
if watch { if watch {
test.traefikFileContent = "" var err error
file, err = ioutil.TempFile(tempDir, "temp*"+filepath.Ext(test.traefikFilePath))
require.NoError(t, err)
} else {
var err error
file, err = createTempFile(test.traefikFilePath, tempDir)
require.NoError(t, err)
} }
filename := createRandomFile(t, tempDir, test.traefikFileContent)
provider.TraefikFile = filename.Name() provider.TraefikFile = file.Name()
} }
return provider, func() { return provider, func() {
@ -257,39 +321,10 @@ func createProvider(t *testing.T, test ProvideTestCase, watch bool) (*Provider,
} }
} }
// createRandomFile Helper
func createRandomFile(t *testing.T, tempDir string, contents ...string) *os.File {
return createFile(t, tempDir, fmt.Sprintf("temp%d.toml", time.Now().UnixNano()), contents...)
}
// createFile Helper
func createFile(t *testing.T, tempDir string, name string, contents ...string) *os.File {
t.Helper()
fileName := path.Join(tempDir, name)
tempFile, err := os.Create(fileName)
if err != nil {
t.Fatal(err)
}
for _, content := range contents {
_, err = tempFile.WriteString(content)
if err != nil {
t.Fatal(err)
}
}
err = tempFile.Close()
if err != nil {
t.Fatal(err)
}
return tempFile
}
// createTempDir Helper // createTempDir Helper
func createTempDir(t *testing.T, dir string) string { func createTempDir(t *testing.T, dir string) string {
t.Helper() t.Helper()
d, err := ioutil.TempDir("", dir) d, err := ioutil.TempDir("", dir)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
@ -297,62 +332,36 @@ func createTempDir(t *testing.T, dir string) string {
return d return d
} }
// createRoutersConfiguration Helper func copyFile(srcPath, dstPath string) error {
func createRoutersConfiguration(n int) string { dst, err := os.OpenFile(dstPath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666)
conf := "[http.routers]\n" if err != nil {
for i := 1; i <= n; i++ { return err
conf += fmt.Sprintf(`
[http.routers."router%[1]d"]
service = "application-%[1]d"
`, i)
} }
return conf defer dst.Close()
}
// createServicesConfiguration Helper src, err := os.Open(srcPath)
func createServicesConfiguration(n int) string { if err != nil {
conf := "[http.services]\n" return err
for i := 1; i <= n; i++ {
conf += fmt.Sprintf(`
[http.services.application-%[1]d.loadbalancer]
[[http.services.application-%[1]d.loadbalancer.servers]]
url = "http://172.17.0.%[1]d:80"
`, i)
} }
return conf defer src.Close()
_, err = io.Copy(dst, src)
return err
} }
// createTLS Helper func createTempFile(srcPath, tempDir string) (*os.File, error) {
func createTLS(n int) string { file, err := ioutil.TempFile(tempDir, "temp*"+filepath.Ext(srcPath))
var conf string if err != nil {
for i := 1; i <= n; i++ { return nil, err
conf += fmt.Sprintf(`[[TLS]]
EntryPoints = ["https"]
[TLS.Certificate]
CertFile = "integration/fixtures/https/snitest%[1]d.com.cert"
KeyFile = "integration/fixtures/https/snitest%[1]d.com.key"
`, i)
} }
return conf defer file.Close()
}
src, err := os.Open(srcPath)
func TestTLSContent(t *testing.T) { if err != nil {
tempDir := createTempDir(t, "testdir") return nil, err
defer os.RemoveAll(tempDir) }
defer src.Close()
fileTLS := createRandomFile(t, tempDir, "CONTENT")
fileConfig := createRandomFile(t, tempDir, ` _, err = io.Copy(file, src)
[[tls]] return file, err
entryPoints = ["https"]
[tls.certificate]
certFile = "`+fileTLS.Name()+`"
keyFile = "`+fileTLS.Name()+`"
`)
provider := &Provider{}
configuration, err := provider.loadFileConfig(fileConfig.Name(), true)
require.NoError(t, err)
require.Equal(t, "CONTENT", configuration.TLS[0].Certificate.CertFile.String())
require.Equal(t, "CONTENT", configuration.TLS[0].Certificate.KeyFile.String())
} }

View file

@ -0,0 +1,7 @@
[http.routers]
[http.routers."router1"]
service = "application-1"
[http.routers."router2"]
service = "application-2"

View file

@ -0,0 +1,13 @@
[http.services]
[http.services.application-1.loadbalancer]
[[http.services.application-1.loadbalancer.servers]]
url = "http://172.17.0.1:80"
[http.services.application-2.loadbalancer]
[[http.services.application-2.loadbalancer.servers]]
url = "http://172.17.0.2:80"
[http.services.application-3.loadbalancer]
[[http.services.application-3.loadbalancer.servers]]
url = "http://172.17.0.3:80"

View file

@ -0,0 +1,16 @@
[[TLS]]
[TLS.Certificate]
CertFile = "integration/fixtures/https/snitest1.com.cert"
KeyFile = "integration/fixtures/https/snitest1.com.key"
[[TLS]]
[TLS.Certificate]
CertFile = "integration/fixtures/https/snitest2.com.cert"
KeyFile = "integration/fixtures/https/snitest2.com.key"
[[TLS]]
[TLS.Certificate]
CertFile = "integration/fixtures/https/snitest3.com.cert"
KeyFile = "integration/fixtures/https/snitest3.com.key"
[[TLS]]
[TLS.Certificate]
CertFile = "integration/fixtures/https/snitest4.com.cert"
KeyFile = "integration/fixtures/https/snitest4.com.key"

View file

@ -0,0 +1,55 @@
[http.routers]
[http.routers."router1"]
service = "application-1"
[http.routers."router2"]
service = "application-2"
[http.routers."router3"]
service = "application-3"
[http.services]
[http.services.application-1.loadbalancer]
[[http.services.application-1.loadbalancer.servers]]
url = "http://172.17.0.1:80"
[http.services.application-2.loadbalancer]
[[http.services.application-2.loadbalancer.servers]]
url = "http://172.17.0.2:80"
[http.services.application-3.loadbalancer]
[[http.services.application-3.loadbalancer.servers]]
url = "http://172.17.0.3:80"
[http.services.application-4.loadbalancer]
[[http.services.application-4.loadbalancer.servers]]
url = "http://172.17.0.4:80"
[http.services.application-5.loadbalancer]
[[http.services.application-5.loadbalancer.servers]]
url = "http://172.17.0.5:80"
[http.services.application-6.loadbalancer]
[[http.services.application-6.loadbalancer.servers]]
url = "http://172.17.0.6:80"
[[TLS]]
[TLS.Certificate]
CertFile = "integration/fixtures/https/snitest1.com.cert"
KeyFile = "integration/fixtures/https/snitest1.com.key"
[[TLS]]
[TLS.Certificate]
CertFile = "integration/fixtures/https/snitest2.com.cert"
KeyFile = "integration/fixtures/https/snitest2.com.key"
[[TLS]]
[TLS.Certificate]
CertFile = "integration/fixtures/https/snitest3.com.cert"
KeyFile = "integration/fixtures/https/snitest3.com.key"
[[TLS]]
[TLS.Certificate]
CertFile = "integration/fixtures/https/snitest4.com.cert"
KeyFile = "integration/fixtures/https/snitest4.com.key"
[[TLS]]
[TLS.Certificate]
CertFile = "integration/fixtures/https/snitest5.com.cert"
KeyFile = "integration/fixtures/https/snitest5.com.key"

View file

@ -0,0 +1,62 @@
[http.routers]
[http.routers."router1"]
service = "application-1"
[http.routers."router2"]
service = "application-2"
[http.routers."router3"]
service = "application-3"
[http.routers."router4"]
service = "application-4"
[http.services]
[http.services.application-1.loadbalancer]
[[http.services.application-1.loadbalancer.servers]]
url = "http://172.17.0.1:80"
[http.services.application-2.loadbalancer]
[[http.services.application-2.loadbalancer.servers]]
url = "http://172.17.0.2:80"
[http.services.application-3.loadbalancer]
[[http.services.application-3.loadbalancer.servers]]
url = "http://172.17.0.3:80"
[http.services.application-4.loadbalancer]
[[http.services.application-4.loadbalancer.servers]]
url = "http://172.17.0.4:80"
[http.services.application-5.loadbalancer]
[[http.services.application-5.loadbalancer.servers]]
url = "http://172.17.0.5:80"
[http.services.application-6.loadbalancer]
[[http.services.application-6.loadbalancer.servers]]
url = "http://172.17.0.6:80"
[http.services.application-7.loadbalancer]
[[http.services.application-7.loadbalancer.servers]]
url = "http://172.17.0.7:80"
[http.services.application-8.loadbalancer]
[[http.services.application-8.loadbalancer.servers]]
url = "http://172.17.0.8:80"
[[TLS]]
[TLS.Certificate]
CertFile = "integration/fixtures/https/snitest1.com.cert"
KeyFile = "integration/fixtures/https/snitest1.com.key"
[[TLS]]
[TLS.Certificate]
CertFile = "integration/fixtures/https/snitest2.com.cert"
KeyFile = "integration/fixtures/https/snitest2.com.key"
[[TLS]]
[TLS.Certificate]
CertFile = "integration/fixtures/https/snitest3.com.cert"
KeyFile = "integration/fixtures/https/snitest3.com.key"
[[TLS]]
[TLS.Certificate]
CertFile = "integration/fixtures/https/snitest4.com.cert"
KeyFile = "integration/fixtures/https/snitest4.com.key"

View file

@ -0,0 +1,3 @@
[log]
level = "DEBUG"

View file

@ -0,0 +1,38 @@
[providers.file]
[http.routers]
[http.routers."router1"]
service = "application-1"
[http.routers."router2"]
service = "application-2"
[http.services]
[http.services.application-1.loadbalancer]
[[http.services.application-1.loadbalancer.servers]]
url = "http://172.17.0.1:80"
[http.services.application-2.loadbalancer]
[[http.services.application-2.loadbalancer.servers]]
url = "http://172.17.0.2:80"
[http.services.application-3.loadbalancer]
[[http.services.application-3.loadbalancer.servers]]
url = "http://172.17.0.3:80"
[[TLS]]
[TLS.Certificate]
CertFile = "integration/fixtures/https/snitest1.com.cert"
KeyFile = "integration/fixtures/https/snitest1.com.key"
[[TLS]]
[TLS.Certificate]
CertFile = "integration/fixtures/https/snitest2.com.cert"
KeyFile = "integration/fixtures/https/snitest2.com.key"
[[TLS]]
[TLS.Certificate]
CertFile = "integration/fixtures/https/snitest3.com.cert"
KeyFile = "integration/fixtures/https/snitest3.com.key"
[[TLS]]
[TLS.Certificate]
CertFile = "integration/fixtures/https/snitest4.com.cert"
KeyFile = "integration/fixtures/https/snitest4.com.key"

View file

@ -0,0 +1,39 @@
temp="{{ getTag \"test\" }}"
[providers.file]
[http.routers]
[http.routers."router1"]
service = "application-1"
[http.routers."router2"]
service = "application-2"
[http.services]
[http.services.application-1.loadbalancer]
[[http.services.application-1.loadbalancer.servers]]
url = "http://172.17.0.1:80"
[http.services.application-2.loadbalancer]
[[http.services.application-2.loadbalancer.servers]]
url = "http://172.17.0.2:80"
[http.services.application-3.loadbalancer]
[[http.services.application-3.loadbalancer.servers]]
url = "http://172.17.0.3:80"
[[TLS]]
[TLS.Certificate]
CertFile = "integration/fixtures/https/snitest1.com.cert"
KeyFile = "integration/fixtures/https/snitest1.com.key"
[[TLS]]
[TLS.Certificate]
CertFile = "integration/fixtures/https/snitest2.com.cert"
KeyFile = "integration/fixtures/https/snitest2.com.key"
[[TLS]]
[TLS.Certificate]
CertFile = "integration/fixtures/https/snitest3.com.cert"
KeyFile = "integration/fixtures/https/snitest3.com.key"
[[TLS]]
[TLS.Certificate]
CertFile = "integration/fixtures/https/snitest4.com.cert"
KeyFile = "integration/fixtures/https/snitest4.com.key"

View file

@ -0,0 +1,6 @@
[http.routers]
{{ range $i, $e := until 20 }}
[http.routers.router{{ $e }}]
service = "application"
{{ end }}

View file

@ -0,0 +1,5 @@
[http.routers]
{{ range $i, $e := until 20 }}
[http.routers.router{{ $e }}]
service = "application"
{{ end }}

View file

@ -0,0 +1,6 @@
[http.services]
{{ range $i, $e := until 20 }}
[http.services.application-{{ $e }}]
[[http.services.application-{{ $e }}.servers]]
url="http://127.0.0.1"
{{ end }}

View file

@ -0,0 +1 @@
CONTENT

View file

@ -0,0 +1,6 @@
http:
routers:
router1:
service: application-1
router2:
service: application-2

View file

@ -0,0 +1,14 @@
http:
services:
application-1:
loadbalancer:
servers:
- url: 'http://172.17.0.1:80'
application-2:
loadbalancer:
servers:
- url: 'http://172.17.0.2:80'
application-3:
loadbalancer:
servers:
- url: 'http://172.17.0.3:80'

View file

@ -0,0 +1,13 @@
tls:
- certificate:
certfile: integration/fixtures/https/snitest1.com.cert
keyfile: integration/fixtures/https/snitest1.com.key
- certificate:
certfile: integration/fixtures/https/snitest2.com.cert
keyfile: integration/fixtures/https/snitest2.com.key
- certificate:
certfile: integration/fixtures/https/snitest3.com.cert
keyfile: integration/fixtures/https/snitest3.com.key
- certificate:
certfile: integration/fixtures/https/snitest4.com.cert
keyfile: integration/fixtures/https/snitest4.com.key

View file

@ -0,0 +1,50 @@
http:
routers:
router1:
service: application-1
router2:
service: application-2
router3:
service: application-3
services:
application-1:
loadbalancer:
servers:
- url: 'http://172.17.0.1:80'
application-2:
loadbalancer:
servers:
- url: 'http://172.17.0.2:80'
application-3:
loadbalancer:
servers:
- url: 'http://172.17.0.3:80'
application-4:
loadbalancer:
servers:
- url: 'http://172.17.0.4:80'
application-5:
loadbalancer:
servers:
- url: 'http://172.17.0.5:80'
application-6:
loadbalancer:
servers:
- url: 'http://172.17.0.6:80'
tls:
- certificate:
certfile: integration/fixtures/https/snitest1.com.cert
keyfile: integration/fixtures/https/snitest1.com.key
- certificate:
certfile: integration/fixtures/https/snitest2.com.cert
keyfile: integration/fixtures/https/snitest2.com.key
- certificate:
certfile: integration/fixtures/https/snitest3.com.cert
keyfile: integration/fixtures/https/snitest3.com.key
- certificate:
certfile: integration/fixtures/https/snitest4.com.cert
keyfile: integration/fixtures/https/snitest4.com.key
- certificate:
certfile: integration/fixtures/https/snitest5.com.cert
keyfile: integration/fixtures/https/snitest5.com.key

View file

@ -0,0 +1,58 @@
http:
routers:
router1:
service: application-1
router2:
service: application-2
router3:
service: application-3
router4:
service: application-4
services:
application-1:
loadbalancer:
servers:
- url: 'http://172.17.0.1:80'
application-2:
loadbalancer:
servers:
- url: 'http://172.17.0.2:80'
application-3:
loadbalancer:
servers:
- url: 'http://172.17.0.3:80'
application-4:
loadbalancer:
servers:
- url: 'http://172.17.0.4:80'
application-5:
loadbalancer:
servers:
- url: 'http://172.17.0.5:80'
application-6:
loadbalancer:
servers:
- url: 'http://172.17.0.6:80'
application-7:
loadbalancer:
servers:
- url: 'http://172.17.0.7:80'
application-8:
loadbalancer:
servers:
- url: 'http://172.17.0.8:80'
tls:
- certificate:
certfile: integration/fixtures/https/snitest1.com.cert
keyfile: integration/fixtures/https/snitest1.com.key
- certificate:
certfile: integration/fixtures/https/snitest2.com.cert
keyfile: integration/fixtures/https/snitest2.com.key
- certificate:
certfile: integration/fixtures/https/snitest3.com.cert
keyfile: integration/fixtures/https/snitest3.com.key
- certificate:
certfile: integration/fixtures/https/snitest4.com.cert
keyfile: integration/fixtures/https/snitest4.com.key

View file

@ -0,0 +1,2 @@
log:
level: DEBUG

View file

@ -0,0 +1,35 @@
providers:
file: {}
http:
routers:
router1:
service: application-1
router2:
service: application-2
services:
application-1:
loadbalancer:
servers:
- url: 'http://172.17.0.1:80'
application-2:
loadbalancer:
servers:
- url: 'http://172.17.0.2:80'
application-3:
loadbalancer:
servers:
- url: 'http://172.17.0.3:80'
tls:
- certificate:
certfile: integration/fixtures/https/snitest1.com.cert
keyfile: integration/fixtures/https/snitest1.com.key
- certificate:
certfile: integration/fixtures/https/snitest2.com.cert
keyfile: integration/fixtures/https/snitest2.com.key
- certificate:
certfile: integration/fixtures/https/snitest3.com.cert
keyfile: integration/fixtures/https/snitest3.com.key
- certificate:
certfile: integration/fixtures/https/snitest4.com.cert
keyfile: integration/fixtures/https/snitest4.com.key

View file

@ -0,0 +1,6 @@
http:
{{ range $i, $e := until 20 }}
routers:
router{{ $e }}:
service: application-1
{{ end }}

View file

@ -0,0 +1,6 @@
http:
{{ range $i, $e := until 20 }}
routers:
router{{ $e }}:
service: application-1
{{ end }}

View file

@ -0,0 +1,8 @@
http:
services:
{{ range $i, $e := until 20 }}
application-{{ $e }}:
loadbalancer:
servers:
- url: 'http://127.0.0.1'
{{ end }}

View file

@ -0,0 +1 @@
CONTENT