Merge branch 'master' into master

This commit is contained in:
Almog Baku 2016-06-07 19:45:53 +03:00
commit 1274d26b4c
25 changed files with 294 additions and 79 deletions

View file

@ -69,6 +69,8 @@ Frontends can be defined using the following rules:
- `PathPrefix`: PathPrefix adds a matcher for the URL path prefixes. This matches if the given template is a prefix of the full URL path. - `PathPrefix`: PathPrefix adds a matcher for the URL path prefixes. This matches if the given template is a prefix of the full URL path.
- `PathPrefixStrip`: Same as `PathPrefix` but strip the given prefix from the request URL's Path. - `PathPrefixStrip`: Same as `PathPrefix` but strip the given prefix from the request URL's Path.
You can use multiple rules by separating them by `;`
You can optionally enable `passHostHeader` to forward client `Host` header to the backend. You can optionally enable `passHostHeader` to forward client `Host` header to the backend.
Here is an example of frontends definition: Here is an example of frontends definition:
@ -82,18 +84,40 @@ Here is an example of frontends definition:
[frontends.frontend2] [frontends.frontend2]
backend = "backend1" backend = "backend1"
passHostHeader = true passHostHeader = true
priority = 10
entrypoints = ["https"] # overrides defaultEntryPoints entrypoints = ["https"] # overrides defaultEntryPoints
[frontends.frontend2.routes.test_1] [frontends.frontend2.routes.test_1]
rule = "Host: localhost, {subdomain:[a-z]+}.localhost" rule = "Host: localhost, {subdomain:[a-z]+}.localhost"
[frontends.frontend3] [frontends.frontend3]
backend = "backend2" backend = "backend2"
rule = "Path:/test" rule = "Host: test3.localhost;Path:/test"
``` ```
- Three frontends are defined: `frontend1`, `frontend2` and `frontend3` - Three frontends are defined: `frontend1`, `frontend2` and `frontend3`
- `frontend1` will forward the traffic to the `backend2` if the rule `Host: test.localhost, test2.localhost` is matched - `frontend1` will forward the traffic to the `backend2` if the rule `Host: test.localhost, test2.localhost` is matched
- `frontend2` will forward the traffic to the `backend1` if the rule `Host: localhost, {subdomain:[a-z]+}.localhost` is matched (forwarding client `Host` header to the backend) - `frontend2` will forward the traffic to the `backend1` if the rule `Host: localhost, {subdomain:[a-z]+}.localhost` is matched (forwarding client `Host` header to the backend)
- `frontend3` will forward the traffic to the `backend2` if the rule `Path:/test` is matched - `frontend3` will forward the traffic to the `backend2` if the rules `Host: test3.localhost` and `Path:/test` are matched
By default, routes will be sorted using rules length (to avoid path overlap):
`PathPrefix:/12345` will be matched before `PathPrefix:/1234` that will be matched before `PathPrefix:/1`.
You can customize priority by frontend:
```
[frontends]
[frontends.frontend1]
backend = "backend1"
priority = 10
passHostHeader = true
[frontends.frontend1.routes.test_1]
rule = "PathPrefix:/to"
[frontends.frontend2]
priority = 5
backend = "backend2"
passHostHeader = true
[frontends.frontend2.routes.test_1]
rule = "PathPrefix:/toto"
```
## Backends ## Backends

View file

@ -301,6 +301,7 @@ defaultEntryPoints = ["http", "https"]
[frontends.frontend2] [frontends.frontend2]
backend = "backend1" backend = "backend1"
passHostHeader = true passHostHeader = true
priority = 10
entrypoints = ["https"] # overrides defaultEntryPoints entrypoints = ["https"] # overrides defaultEntryPoints
[frontends.frontend2.routes.test_1] [frontends.frontend2.routes.test_1]
rule = "Host:{subdomain:[a-z]+}.localhost" rule = "Host:{subdomain:[a-z]+}.localhost"
@ -367,6 +368,7 @@ filename = "rules.toml"
[frontends.frontend2] [frontends.frontend2]
backend = "backend1" backend = "backend1"
passHostHeader = true passHostHeader = true
priority = 10
entrypoints = ["https"] # overrides defaultEntryPoints entrypoints = ["https"] # overrides defaultEntryPoints
[frontends.frontend2.routes.test_1] [frontends.frontend2.routes.test_1]
rule = "Host:{subdomain:[a-z]+}.localhost" rule = "Host:{subdomain:[a-z]+}.localhost"
@ -583,6 +585,7 @@ Labels can be used on containers to override default behaviour:
- `traefik.enable=false`: disable this container in Træfɪk - `traefik.enable=false`: disable this container in Træfɪk
- `traefik.frontend.rule=Host:test.traefik.io`: override the default frontend rule (Default: `Host:{containerName}.{domain}`). - `traefik.frontend.rule=Host:test.traefik.io`: override the default frontend rule (Default: `Host:{containerName}.{domain}`).
- `traefik.frontend.passHostHeader=true`: forward client `Host` header to the backend. - `traefik.frontend.passHostHeader=true`: forward client `Host` header to the backend.
- `traefik.frontend.priority=10`: override default frontend priority
- `traefik.frontend.entryPoints=http,https`: assign this frontend to entry points `http` and `https`. Overrides `defaultEntryPoints`. - `traefik.frontend.entryPoints=http,https`: assign this frontend to entry points `http` and `https`. Overrides `defaultEntryPoints`.
- `traefik.domain=traefik.localhost`: override the default domain - `traefik.domain=traefik.localhost`: override the default domain
- `traefik.docker.network`: Set the docker network to use for connections to this container - `traefik.docker.network`: Set the docker network to use for connections to this container
@ -673,6 +676,7 @@ Labels can be used on containers to override default behaviour:
- `traefik.enable=false`: disable this application in Træfɪk - `traefik.enable=false`: disable this application in Træfɪk
- `traefik.frontend.rule=Host:test.traefik.io`: override the default frontend rule (Default: `Host:{containerName}.{domain}`). - `traefik.frontend.rule=Host:test.traefik.io`: override the default frontend rule (Default: `Host:{containerName}.{domain}`).
- `traefik.frontend.passHostHeader=true`: forward client `Host` header to the backend. - `traefik.frontend.passHostHeader=true`: forward client `Host` header to the backend.
- `traefik.frontend.priority=10`: override default frontend priority
- `traefik.frontend.entryPoints=http,https`: assign this frontend to entry points `http` and `https`. Overrides `defaultEntryPoints`. - `traefik.frontend.entryPoints=http,https`: assign this frontend to entry points `http` and `https`. Overrides `defaultEntryPoints`.
- `traefik.domain=traefik.localhost`: override the default domain - `traefik.domain=traefik.localhost`: override the default domain
@ -810,14 +814,15 @@ used in consul.
Additional settings can be defined using Consul Catalog tags: Additional settings can be defined using Consul Catalog tags:
- ```traefik.enable=false```: disable this container in Træfɪk - `traefik.enable=false`: disable this container in Træfɪk
- ```traefik.protocol=https```: override the default `http` protocol - `traefik.protocol=https`: override the default `http` protocol
- ```traefik.backend.weight=10```: assign this weight to the container - `traefik.backend.weight=10`: assign this weight to the container
- ```traefik.backend.circuitbreaker=NetworkErrorRatio() > 0.5``` - `traefik.backend.circuitbreaker=NetworkErrorRatio() > 0.5`
- ```traefik.backend.loadbalancer=drr```: override the default load balancing mode - `traefik.backend.loadbalancer=drr`: override the default load balancing mode
- ```traefik.frontend.rule=Host:test.traefik.io```: override the default frontend rule (Default: `Host:{containerName}.{domain}`). - `traefik.frontend.rule=Host:test.traefik.io`: override the default frontend rule (Default: `Host:{containerName}.{domain}`).
- ```traefik.frontend.passHostHeader=true```: forward client `Host` header to the backend. - `traefik.frontend.passHostHeader=true`: forward client `Host` header to the backend.
- ```traefik.frontend.entryPoints=http,https```: assign this frontend to entry points `http` and `https`. Overrides `defaultEntryPoints`. - `traefik.frontend.priority=10`: override default frontend priority
- `traefik.frontend.entryPoints=http,https`: assign this frontend to entry points `http` and `https`. Overrides `defaultEntryPoints`.
## Etcd backend ## Etcd backend
@ -992,9 +997,10 @@ The Keys-Values structure should look (using `prefix = "/traefik"`):
- frontend 2 - frontend 2
| Key | Value | | Key | Value |
|----------------------------------------------------|--------------| |----------------------------------------------------|--------------------|
| `/traefik/frontends/frontend2/backend` | `backend1` | | `/traefik/frontends/frontend2/backend` | `backend1` |
| `/traefik/frontends/frontend2/passHostHeader` | `true` | | `/traefik/frontends/frontend2/passHostHeader` | `true` |
| `/traefik/frontends/frontend2/priority` | `10` |
| `/traefik/frontends/frontend2/entrypoints` | `http,https` | | `/traefik/frontends/frontend2/entrypoints` | `http,https` |
| `/traefik/frontends/frontend2/routes/test_2/rule` | `PathPrefix:/test` | | `/traefik/frontends/frontend2/routes/test_2/rule` | `PathPrefix:/test` |

View file

@ -41,12 +41,3 @@ marathon:
MARATHON_ZK: zk://127.0.0.1:2181/marathon MARATHON_ZK: zk://127.0.0.1:2181/marathon
MARATHON_HOSTNAME: 127.0.0.1 MARATHON_HOSTNAME: 127.0.0.1
command: --event_subscriber http_callback command: --event_subscriber http_callback
traefik:
image: containous/traefik
command: -c /dev/null --web --logLevel=DEBUG --marathon --marathon.domain marathon.localhost --marathon.endpoint http://172.17.0.1:8080 --marathon.watch
ports:
- "8000:80"
- "8081:8080"
volumes:
- /var/run/docker.sock:/var/run/docker.sock

View file

@ -26,6 +26,7 @@
"labels": { "labels": {
"traefik.weight": "1", "traefik.weight": "1",
"traefik.protocol": "http", "traefik.protocol": "http",
"traefik.frontend.rule" : "Host:test.marathon.localhost" "traefik.frontend.rule" : "Host:test.marathon.localhost",
"traefik.frontend.priority" : "10"
} }
} }

16
glide.lock generated
View file

@ -1,5 +1,5 @@
hash: dc59755b72e71945a21135c5a37e4a5c11ae511ac7404d1440166ea0aed736c4 hash: 5a6dbc30a69abd002736bd5113e0f783c448faee20a0791c724ec2c3c1cfb8bb
updated: 2016-06-02T15:11:52.77657652+02:00 updated: 2016-06-03T18:11:43.839017153+02:00
imports: imports:
- name: github.com/boltdb/bolt - name: github.com/boltdb/bolt
version: dfb21201d9270c1082d5fb0f07f500311ff72f18 version: dfb21201d9270c1082d5fb0f07f500311ff72f18
@ -16,9 +16,11 @@ imports:
- name: github.com/codegangsta/cli - name: github.com/codegangsta/cli
version: bf4a526f48af7badd25d2cb02d587e1b01be3b50 version: bf4a526f48af7badd25d2cb02d587e1b01be3b50
- name: github.com/codegangsta/negroni - name: github.com/codegangsta/negroni
version: fb7b7c045dfb05dc81a5c3688c568550b5bd6e36 version: feacfc52d357c844f524c794947493483ed881b3
- name: github.com/containous/flaeg - name: github.com/containous/flaeg
version: b98687da5c323650f4513fda6b6203fcbdec9313 version: b98687da5c323650f4513fda6b6203fcbdec9313
- name: github.com/containous/mux
version: a819b77bba13f0c0cbe36e437bc2e948411b3996
- name: github.com/containous/oxy - name: github.com/containous/oxy
version: 183212964e13e7b8afe01a08b193d04300554a68 version: 183212964e13e7b8afe01a08b193d04300554a68
subpackages: subpackages:
@ -43,7 +45,7 @@ imports:
subpackages: subpackages:
- spew - spew
- name: github.com/docker/distribution - name: github.com/docker/distribution
version: bb330cd684eb4afab9cc4f2453d7c8918099d7ee version: feddf6cd4e439577ab270d8e3ba63a5d7c5c0d55
subpackages: subpackages:
- reference - reference
- digest - digest
@ -100,10 +102,8 @@ imports:
- query - query
- name: github.com/gorilla/context - name: github.com/gorilla/context
version: aed02d124ae4a0e94fea4541c8effd05bf0c8296 version: aed02d124ae4a0e94fea4541c8effd05bf0c8296
- name: github.com/gorilla/mux
version: bd09be08ed4377796d312df0a45314e11b8f5dc1
- name: github.com/hashicorp/consul - name: github.com/hashicorp/consul
version: ebf7ea1d759184c02a5bb5263a7c52d29838ffc3 version: 802b29ab948dedb7f7b1b903f535bdf250388c50
subpackages: subpackages:
- api - api
- name: github.com/hashicorp/go-cleanhttp - name: github.com/hashicorp/go-cleanhttp
@ -136,7 +136,7 @@ imports:
- name: github.com/ogier/pflag - name: github.com/ogier/pflag
version: 45c278ab3607870051a2ea9040bb85fcb8557481 version: 45c278ab3607870051a2ea9040bb85fcb8557481
- name: github.com/opencontainers/runc - name: github.com/opencontainers/runc
version: 6c485e6902bb9dd77b8234042b8f00e20ef87a18 version: 3211c9f721237f55a16da9c111e3d7e8777e53b5
subpackages: subpackages:
- libcontainer/user - libcontainer/user
- name: github.com/parnurzeal/gorequest - name: github.com/parnurzeal/gorequest

View file

@ -40,7 +40,7 @@ import:
- package: github.com/elazarl/go-bindata-assetfs - package: github.com/elazarl/go-bindata-assetfs
- package: github.com/gambol99/go-marathon - package: github.com/gambol99/go-marathon
version: ade11d1dc2884ee1f387078fc28509559b6235d1 version: ade11d1dc2884ee1f387078fc28509559b6235d1
- package: github.com/gorilla/mux - package: github.com/containous/mux
- package: github.com/hashicorp/consul - package: github.com/hashicorp/consul
subpackages: subpackages:
- api - api

View file

@ -100,11 +100,13 @@ func (s *ConsulSuite) TestNominalConfiguration(c *check.C) {
frontend1 := map[string]string{ frontend1 := map[string]string{
"traefik/frontends/frontend1/backend": "backend2", "traefik/frontends/frontend1/backend": "backend2",
"traefik/frontends/frontend1/entrypoints": "http", "traefik/frontends/frontend1/entrypoints": "http",
"traefik/frontends/frontend1/priority": "1",
"traefik/frontends/frontend1/routes/test_1/rule": "Host:test.localhost", "traefik/frontends/frontend1/routes/test_1/rule": "Host:test.localhost",
} }
frontend2 := map[string]string{ frontend2 := map[string]string{
"traefik/frontends/frontend2/backend": "backend1", "traefik/frontends/frontend2/backend": "backend1",
"traefik/frontends/frontend2/entrypoints": "http", "traefik/frontends/frontend2/entrypoints": "http",
"traefik/frontends/frontend2/priority": "10",
"traefik/frontends/frontend2/routes/test_2/rule": "Path:/test", "traefik/frontends/frontend2/routes/test_2/rule": "Path:/test",
} }
for key, value := range backend1 { for key, value := range backend1 {

View file

@ -1,12 +1,11 @@
package main package main
import ( import (
"github.com/go-check/check"
"net/http" "net/http"
"os/exec" "os/exec"
"time" "time"
"github.com/go-check/check"
checker "github.com/vdemeester/shakers" checker "github.com/vdemeester/shakers"
"errors" "errors"
@ -104,11 +103,13 @@ func (s *EtcdSuite) TestNominalConfiguration(c *check.C) {
frontend1 := map[string]string{ frontend1 := map[string]string{
"/traefik/frontends/frontend1/backend": "backend2", "/traefik/frontends/frontend1/backend": "backend2",
"/traefik/frontends/frontend1/entrypoints": "http", "/traefik/frontends/frontend1/entrypoints": "http",
"/traefik/frontends/frontend1/priority": "1",
"/traefik/frontends/frontend1/routes/test_1/rule": "Host:test.localhost", "/traefik/frontends/frontend1/routes/test_1/rule": "Host:test.localhost",
} }
frontend2 := map[string]string{ frontend2 := map[string]string{
"/traefik/frontends/frontend2/backend": "backend1", "/traefik/frontends/frontend2/backend": "backend1",
"/traefik/frontends/frontend2/entrypoints": "http", "/traefik/frontends/frontend2/entrypoints": "http",
"/traefik/frontends/frontend2/priority": "10",
"/traefik/frontends/frontend2/routes/test_2/rule": "Path:/test", "/traefik/frontends/frontend2/routes/test_2/rule": "Path:/test",
} }
for key, value := range backend1 { for key, value := range backend1 {

View file

@ -15,6 +15,19 @@ type MarathonSuite struct{ BaseSuite }
func (s *MarathonSuite) SetUpSuite(c *check.C) { func (s *MarathonSuite) SetUpSuite(c *check.C) {
s.createComposeProject(c, "marathon") s.createComposeProject(c, "marathon")
s.composeProject.Start(c)
// wait for marathon
// err := utils.TryRequest("http://127.0.0.1:8080/ping", 60*time.Second, func(res *http.Response) error {
// body, err := ioutil.ReadAll(res.Body)
// if err != nil {
// return err
// }
// if !strings.Contains(string(body), "ping") {
// return errors.New("Incorrect marathon config")
// }
// return nil
// })
// c.Assert(err, checker.IsNil)
} }
func (s *MarathonSuite) TestSimpleConfiguration(c *check.C) { func (s *MarathonSuite) TestSimpleConfiguration(c *check.C) {

View file

@ -6,7 +6,7 @@ zk:
ZK_ID: " 1" ZK_ID: " 1"
master: master:
image: mesosphere/mesos-master:0.23.0-1.0.ubuntu1404 image: mesosphere/mesos-master:0.28.1-2.0.20.ubuntu1404
net: host net: host
environment: environment:
MESOS_ZK: zk://127.0.0.1:2181/mesos MESOS_ZK: zk://127.0.0.1:2181/mesos
@ -17,7 +17,7 @@ master:
MESOS_WORK_DIR: /var/lib/mesos MESOS_WORK_DIR: /var/lib/mesos
slave: slave:
image: mesosphere/mesos-slave:0.23.0-1.0.ubuntu1404 image: mesosphere/mesos-slave:0.28.1-2.0.20.ubuntu1404
net: host net: host
pid: host pid: host
privileged: true privileged: true
@ -31,9 +31,10 @@ slave:
- /usr/bin/docker:/usr/bin/docker:ro - /usr/bin/docker:/usr/bin/docker:ro
- /usr/lib/x86_64-linux-gnu/libapparmor.so.1:/usr/lib/x86_64-linux-gnu/libapparmor.so.1:ro - /usr/lib/x86_64-linux-gnu/libapparmor.so.1:/usr/lib/x86_64-linux-gnu/libapparmor.so.1:ro
- /var/run/docker.sock:/var/run/docker.sock - /var/run/docker.sock:/var/run/docker.sock
- /lib/x86_64-linux-gnu/libsystemd-journal.so.0:/lib/x86_64-linux-gnu/libsystemd-journal.so.0
marathon: marathon:
image: mesosphere/marathon:v0.9.2 image: mesosphere/marathon:v1.1.1
net: host net: host
environment: environment:
MARATHON_MASTER: zk://127.0.0.1:2181/mesos MARATHON_MASTER: zk://127.0.0.1:2181/mesos

View file

@ -1,8 +1,8 @@
package middlewares package middlewares
import ( import (
"github.com/containous/mux"
"github.com/containous/traefik/safe" "github.com/containous/traefik/safe"
"github.com/gorilla/mux"
"net/http" "net/http"
) )

View file

@ -5,7 +5,7 @@ import (
"log" "log"
"net/http" "net/http"
"github.com/gorilla/mux" "github.com/containous/mux"
) )
// Routes holds the gorilla mux routes (for the API & co). // Routes holds the gorilla mux routes (for the API & co).

View file

@ -167,6 +167,7 @@ func (provider *Docker) loadDockerConfig(containersInspected []dockertypes.Conta
"getDomain": provider.getDomain, "getDomain": provider.getDomain,
"getProtocol": provider.getProtocol, "getProtocol": provider.getProtocol,
"getPassHostHeader": provider.getPassHostHeader, "getPassHostHeader": provider.getPassHostHeader,
"getPriority": provider.getPriority,
"getEntryPoints": provider.getEntryPoints, "getEntryPoints": provider.getEntryPoints,
"getFrontendRule": provider.getFrontendRule, "getFrontendRule": provider.getFrontendRule,
"replace": replace, "replace": replace,
@ -300,6 +301,13 @@ func (provider *Docker) getPassHostHeader(container dockertypes.ContainerJSON) s
return "true" return "true"
} }
func (provider *Docker) getPriority(container dockertypes.ContainerJSON) string {
if priority, err := getLabel(container, "traefik.frontend.priority"); err == nil {
return priority
}
return "0"
}
func (provider *Docker) getEntryPoints(container dockertypes.ContainerJSON) []string { func (provider *Docker) getEntryPoints(container dockertypes.ContainerJSON) []string {
if entryPoints, err := getLabel(container, "traefik.frontend.entryPoints"); err == nil { if entryPoints, err := getLabel(container, "traefik.frontend.entryPoints"); err == nil {
return strings.Split(entryPoints, ",") return strings.Split(entryPoints, ",")

View file

@ -116,6 +116,7 @@ func (provider *Marathon) loadMarathonConfig() *types.Configuration {
"getDomain": provider.getDomain, "getDomain": provider.getDomain,
"getProtocol": provider.getProtocol, "getProtocol": provider.getProtocol,
"getPassHostHeader": provider.getPassHostHeader, "getPassHostHeader": provider.getPassHostHeader,
"getPriority": provider.getPriority,
"getEntryPoints": provider.getEntryPoints, "getEntryPoints": provider.getEntryPoints,
"getFrontendRule": provider.getFrontendRule, "getFrontendRule": provider.getFrontendRule,
"getFrontendBackend": provider.getFrontendBackend, "getFrontendBackend": provider.getFrontendBackend,
@ -322,6 +323,13 @@ func (provider *Marathon) getPassHostHeader(application marathon.Application) st
return "true" return "true"
} }
func (provider *Marathon) getPriority(application marathon.Application) string {
if priority, err := provider.getLabel(application, "traefik.frontend.priority"); err == nil {
return priority
}
return "0"
}
func (provider *Marathon) getEntryPoints(application marathon.Application) []string { func (provider *Marathon) getEntryPoints(application marathon.Application) []string {
if entryPoints, err := provider.getLabel(application, "traefik.frontend.entryPoints"); err == nil { if entryPoints, err := provider.getLabel(application, "traefik.frontend.entryPoints"); err == nil {
return strings.Split(entryPoints, ",") return strings.Split(entryPoints, ",")

View file

@ -2,7 +2,7 @@ package main
import ( import (
"errors" "errors"
"github.com/gorilla/mux" "github.com/containous/mux"
"net" "net"
"net/http" "net/http"
"reflect" "reflect"
@ -109,23 +109,34 @@ func (r *Rules) Parse(expression string) (*mux.Route, error) {
f := func(c rune) bool { f := func(c rune) bool {
return c == ':' return c == ':'
} }
// Allow multiple rules separated by ;
splitRule := func(c rune) bool {
return c == ';'
}
parsedRules := strings.FieldsFunc(expression, splitRule)
var resultRoute *mux.Route
for _, rule := range parsedRules {
// get function // get function
parsedFunctions := strings.FieldsFunc(expression, f) parsedFunctions := strings.FieldsFunc(rule, f)
if len(parsedFunctions) == 0 { if len(parsedFunctions) == 0 {
return nil, errors.New("Error parsing rule: " + expression) return nil, errors.New("Error parsing rule: " + rule)
} }
parsedFunction, ok := functions[parsedFunctions[0]] parsedFunction, ok := functions[parsedFunctions[0]]
if !ok { if !ok {
return nil, errors.New("Error parsing rule: " + expression + ". Unknown function: " + parsedFunctions[0]) return nil, errors.New("Error parsing rule: " + rule + ". Unknown function: " + parsedFunctions[0])
} }
parsedFunctions = append(parsedFunctions[:0], parsedFunctions[1:]...) parsedFunctions = append(parsedFunctions[:0], parsedFunctions[1:]...)
fargs := func(c rune) bool { fargs := func(c rune) bool {
return c == ',' || c == ';' return c == ','
} }
// get function // get function
parsedArgs := strings.FieldsFunc(strings.Join(parsedFunctions, ":"), fargs) parsedArgs := strings.FieldsFunc(strings.Join(parsedFunctions, ":"), fargs)
if len(parsedArgs) == 0 { if len(parsedArgs) == 0 {
return nil, errors.New("Error parsing args from rule: " + expression) return nil, errors.New("Error parsing args from rule: " + rule)
} }
inputs := make([]reflect.Value, len(parsedArgs)) inputs := make([]reflect.Value, len(parsedArgs))
@ -134,14 +145,17 @@ func (r *Rules) Parse(expression string) (*mux.Route, error) {
} }
method := reflect.ValueOf(parsedFunction) method := reflect.ValueOf(parsedFunction)
if method.IsValid() { if method.IsValid() {
resultRoute := method.Call(inputs)[0].Interface().(*mux.Route) resultRoute = method.Call(inputs)[0].Interface().(*mux.Route)
if r.err != nil { if r.err != nil {
return nil, r.err return nil, r.err
} }
if resultRoute.GetError() != nil { if resultRoute.GetError() != nil {
return nil, resultRoute.GetError() return nil, resultRoute.GetError()
} }
return resultRoute, nil
} } else {
return nil, errors.New("Method not found: " + parsedFunctions[0]) return nil, errors.New("Method not found: " + parsedFunctions[0])
} }
}
return resultRoute, nil
}

132
rules_test.go Normal file
View file

@ -0,0 +1,132 @@
package main
import (
"github.com/containous/mux"
"net/http"
"net/url"
"testing"
)
func TestParseOneRule(t *testing.T) {
router := mux.NewRouter()
route := router.NewRoute()
serverRoute := &serverRoute{route: route}
rules := &Rules{route: serverRoute}
expression := "Host:foo.bar"
routeResult, err := rules.Parse(expression)
if err != nil {
t.Fatal("Error while building route for Host:foo.bar")
}
request, err := http.NewRequest("GET", "http://foo.bar", nil)
routeMatch := routeResult.Match(request, &mux.RouteMatch{Route: routeResult})
if routeMatch == false {
t.Log(err)
t.Fatal("Rule Host:foo.bar don't match")
}
}
func TestParseTwoRules(t *testing.T) {
router := mux.NewRouter()
route := router.NewRoute()
serverRoute := &serverRoute{route: route}
rules := &Rules{route: serverRoute}
expression := "Host:foo.bar;Path:/foobar"
routeResult, err := rules.Parse(expression)
if err != nil {
t.Fatal("Error while building route for Host:foo.bar;Path:/foobar")
}
request, err := http.NewRequest("GET", "http://foo.bar/foobar", nil)
routeMatch := routeResult.Match(request, &mux.RouteMatch{Route: routeResult})
if routeMatch == false {
t.Log(err)
t.Fatal("Rule Host:foo.bar;Path:/foobar don't match")
}
}
func TestPriorites(t *testing.T) {
router := mux.NewRouter()
router.StrictSlash(true)
rules := &Rules{route: &serverRoute{route: router.NewRoute()}}
routeFoo, err := rules.Parse("PathPrefix:/foo")
if err != nil {
t.Fatal("Error while building route for PathPrefix:/foo")
}
fooHandler := &fakeHandler{name: "fooHandler"}
routeFoo.Handler(fooHandler)
if !router.Match(&http.Request{URL: &url.URL{
Path: "/foo",
}}, &mux.RouteMatch{}) {
t.Fatalf("Error matching route")
}
if router.Match(&http.Request{URL: &url.URL{
Path: "/fo",
}}, &mux.RouteMatch{}) {
t.Fatalf("Error matching route")
}
multipleRules := &Rules{route: &serverRoute{route: router.NewRoute()}}
routeFoobar, err := multipleRules.Parse("PathPrefix:/foobar")
if err != nil {
t.Fatal("Error while building route for PathPrefix:/foobar")
}
foobarHandler := &fakeHandler{name: "foobarHandler"}
routeFoobar.Handler(foobarHandler)
if !router.Match(&http.Request{URL: &url.URL{
Path: "/foo",
}}, &mux.RouteMatch{}) {
t.Fatalf("Error matching route")
}
fooMatcher := &mux.RouteMatch{}
if !router.Match(&http.Request{URL: &url.URL{
Path: "/foobar",
}}, fooMatcher) {
t.Fatalf("Error matching route")
}
if fooMatcher.Handler == foobarHandler {
t.Fatalf("Error matching priority")
}
if fooMatcher.Handler != fooHandler {
t.Fatalf("Error matching priority")
}
routeFoo.Priority(1)
routeFoobar.Priority(10)
router.SortRoutes()
foobarMatcher := &mux.RouteMatch{}
if !router.Match(&http.Request{URL: &url.URL{
Path: "/foobar",
}}, foobarMatcher) {
t.Fatalf("Error matching route")
}
if foobarMatcher.Handler != foobarHandler {
t.Fatalf("Error matching priority")
}
if foobarMatcher.Handler == fooHandler {
t.Fatalf("Error matching priority")
}
}
type fakeHandler struct {
name string
}
func (h *fakeHandler) ServeHTTP(http.ResponseWriter, *http.Request) {
}

View file

@ -20,6 +20,7 @@ import (
log "github.com/Sirupsen/logrus" log "github.com/Sirupsen/logrus"
"github.com/codegangsta/negroni" "github.com/codegangsta/negroni"
"github.com/containous/mux"
"github.com/containous/oxy/cbreaker" "github.com/containous/oxy/cbreaker"
"github.com/containous/oxy/connlimit" "github.com/containous/oxy/connlimit"
"github.com/containous/oxy/forward" "github.com/containous/oxy/forward"
@ -30,7 +31,6 @@ import (
"github.com/containous/traefik/provider" "github.com/containous/traefik/provider"
"github.com/containous/traefik/safe" "github.com/containous/traefik/safe"
"github.com/containous/traefik/types" "github.com/containous/traefik/types"
"github.com/gorilla/mux"
"github.com/mailgun/manners" "github.com/mailgun/manners"
"github.com/streamrail/concurrent-map" "github.com/streamrail/concurrent-map"
) )
@ -501,6 +501,9 @@ func (server *Server) loadConfig(configurations configs, globalConfiguration Glo
} else { } else {
log.Debugf("Reusing backend %s", frontend.Backend) log.Debugf("Reusing backend %s", frontend.Backend)
} }
if frontend.Priority > 0 {
newServerRoute.route.Priority(frontend.Priority)
}
server.wireFrontendBackend(newServerRoute, backends[frontend.Backend]) server.wireFrontendBackend(newServerRoute, backends[frontend.Backend])
} }
err := newServerRoute.route.GetError() err := newServerRoute.route.GetError()
@ -511,6 +514,10 @@ func (server *Server) loadConfig(configurations configs, globalConfiguration Glo
} }
} }
middlewares.SetBackend2FrontendMap(&backend2FrontendMap) middlewares.SetBackend2FrontendMap(&backend2FrontendMap)
//sort routes
for _, serverEntryPoint := range serverEntryPoints {
serverEntryPoint.httpRouter.GetHandler().SortRoutes()
}
return serverEntryPoints, nil return serverEntryPoints, nil
} }
@ -576,6 +583,7 @@ func getRoute(serverRoute *serverRoute, route *types.Route) error {
if err != nil { if err != nil {
return err return err
} }
newRoute.Priority(serverRoute.route.GetPriority() + len(route.Rule))
serverRoute.route = newRoute serverRoute.route = newRoute
return nil return nil
} }

View file

@ -30,6 +30,7 @@
[frontends.frontend-{{.ServiceName}}] [frontends.frontend-{{.ServiceName}}]
backend = "backend-{{.ServiceName}}" backend = "backend-{{.ServiceName}}"
passHostHeader = {{getAttribute "frontend.passHostHeader" .Attributes "true"}} passHostHeader = {{getAttribute "frontend.passHostHeader" .Attributes "true"}}
priority = {{getAttribute "frontend.priority" .Attributes "0"}}
{{$entryPoints := getAttribute "frontend.entrypoints" .Attributes ""}} {{$entryPoints := getAttribute "frontend.entrypoints" .Attributes ""}}
{{with $entryPoints}} {{with $entryPoints}}
entrypoints = [{{range getEntryPoints $entryPoints}} entrypoints = [{{range getEntryPoints $entryPoints}}

View file

@ -8,6 +8,7 @@
[frontends."frontend-{{$frontend}}"]{{$container := index $containers 0}} [frontends."frontend-{{$frontend}}"]{{$container := index $containers 0}}
backend = "backend-{{getBackend $container}}" backend = "backend-{{getBackend $container}}"
passHostHeader = {{getPassHostHeader $container}} passHostHeader = {{getPassHostHeader $container}}
priority = {{getPriority $container}}
entryPoints = [{{range getEntryPoints $container}} entryPoints = [{{range getEntryPoints $container}}
"{{.}}", "{{.}}",
{{end}}] {{end}}]

View file

@ -40,6 +40,7 @@
[frontends."{{$frontend}}"] [frontends."{{$frontend}}"]
backend = "{{Get "" . "/backend"}}" backend = "{{Get "" . "/backend"}}"
passHostHeader = {{Get "true" . "/passHostHeader"}} passHostHeader = {{Get "true" . "/passHostHeader"}}
priority = {{Get "0" . "/priority"}}
entryPoints = [{{range $entryPoints}} entryPoints = [{{range $entryPoints}}
"{{.}}", "{{.}}",
{{end}}] {{end}}]

View file

@ -9,6 +9,7 @@
[frontends.frontend{{.ID | replace "/" "-"}}] [frontends.frontend{{.ID | replace "/" "-"}}]
backend = "backend{{getFrontendBackend .}}" backend = "backend{{getFrontendBackend .}}"
passHostHeader = {{getPassHostHeader .}} passHostHeader = {{getPassHostHeader .}}
priority = {{getPriority .}}
entryPoints = [{{range getEntryPoints .}} entryPoints = [{{range getEntryPoints .}}
"{{.}}", "{{.}}",
{{end}}] {{end}}]

View file

@ -103,7 +103,7 @@ Complete documentation is available at https://traefik.io`,
s.AddSource(toml) s.AddSource(toml)
s.AddSource(f) s.AddSource(f)
if _, err := s.LoadConfig(); err != nil { if _, err := s.LoadConfig(); err != nil {
fmtlog.Println(err) fmtlog.Println(fmt.Errorf("Error reading TOML config file %s : %s", toml.ConfigFileUsed(), err))
} }
traefikConfiguration.ConfigFile = toml.ConfigFileUsed() traefikConfiguration.ConfigFile = toml.ConfigFileUsed()

View file

@ -34,7 +34,7 @@ type CircuitBreaker struct {
// Server holds server configuration. // Server holds server configuration.
type Server struct { type Server struct {
URL string `json:"url,omitempty"` URL string `json:"url,omitempty"`
Weight int `json:"weight,omitempty"` Weight int `json:"weight"`
} }
// Route holds route configuration. // Route holds route configuration.
@ -52,6 +52,7 @@ type Frontend struct {
Backend string `json:"backend,omitempty"` Backend string `json:"backend,omitempty"`
Routes map[string]Route `json:"routes,omitempty"` Routes map[string]Route `json:"routes,omitempty"`
PassHostHeader bool `json:"passHostHeader,omitempty"` PassHostHeader bool `json:"passHostHeader,omitempty"`
Priority int `json:"priority"`
} }
// LoadBalancerMethod holds the method of load balancing to use. // LoadBalancerMethod holds the method of load balancing to use.

2
web.go
View file

@ -9,11 +9,11 @@ import (
"runtime" "runtime"
log "github.com/Sirupsen/logrus" log "github.com/Sirupsen/logrus"
"github.com/containous/mux"
"github.com/containous/traefik/autogen" "github.com/containous/traefik/autogen"
"github.com/containous/traefik/safe" "github.com/containous/traefik/safe"
"github.com/containous/traefik/types" "github.com/containous/traefik/types"
"github.com/elazarl/go-bindata-assetfs" "github.com/elazarl/go-bindata-assetfs"
"github.com/gorilla/mux"
"github.com/thoas/stats" "github.com/thoas/stats"
"github.com/unrolled/render" "github.com/unrolled/render"
) )

View file

@ -16,7 +16,8 @@
</div> </div>
<div data-bg-show="frontendCtrl.frontend.backend" class="panel-footer"> <div data-bg-show="frontendCtrl.frontend.backend" class="panel-footer">
<span data-ng-repeat="entryPoint in frontendCtrl.frontend.entryPoints"><span class="label label-primary">{{entryPoint}}</span><span data-ng-hide="$last">&nbsp;</span></span> <span data-ng-repeat="entryPoint in frontendCtrl.frontend.entryPoints"><span class="label label-primary">{{entryPoint}}</span><span data-ng-hide="$last">&nbsp;</span></span>
<span class="label label-warning" role="button" data-toggle="collapse" href="#{{frontendCtrl.frontend.backend}}" aria-expanded="false">{{frontendCtrl.frontend.backend}}</span> <span class="label label-warning" role="button" data-toggle="collapse" href="#{{frontendCtrl.frontend.backend}}" aria-expanded="false">Backend:{{frontendCtrl.frontend.backend}}</span>
<span data-ng-show="frontendCtrl.frontend.passHostHeader" class="label label-warning">PassHostHeader</span> <span data-ng-show="frontendCtrl.frontend.passHostHeader" class="label label-warning">PassHostHeader</span>
<span data-ng-show="frontendCtrl.frontend.priority" class="label label-warning">Priority:{{frontendCtrl.frontend.priority}}</span>
</div> </div>
</div> </div>