mirror of
https://github.com/containous/traefik.git
synced 2025-09-26 01:44:23 +03:00
Compare commits
24 Commits
v1.0.0-bet
...
v1.0.0-bet
Author | SHA1 | Date | |
---|---|---|---|
|
d050e60da2 | ||
|
866b9835a6 | ||
|
f6564909aa | ||
|
315e8b64b8 | ||
|
f99f634816 | ||
|
5292a5b9d4 | ||
|
cf22d62a74 | ||
|
9363e2ab83 | ||
|
e5ddd92677 | ||
|
04628056af | ||
|
dada86c0b0 | ||
|
92c269c972 | ||
|
6991e3c99b | ||
|
3ee3daee00 | ||
|
85fcff4cf7 | ||
|
30db47d9b6 | ||
|
4d2c85ffdc | ||
|
e36433c23a | ||
|
8486766a60 | ||
|
ef72d355d6 | ||
|
7d013ad5e8 | ||
|
5fcce6567e | ||
|
00af537b0d | ||
|
78449fa62f |
@@ -1,4 +1,5 @@
|
||||
dist/
|
||||
vendor/
|
||||
!dist/traefik
|
||||
site/
|
||||
site/
|
||||
**/*.test
|
||||
|
21
.travis.yml
21
.travis.yml
@@ -1,22 +1,14 @@
|
||||
branches:
|
||||
except:
|
||||
- /^v\d\.\d\.\d.*$/
|
||||
|
||||
env:
|
||||
REPO: $TRAVIS_REPO_SLUG
|
||||
VERSION: v1.0.0-beta.$TRAVIS_BUILD_NUMBER
|
||||
|
||||
global:
|
||||
- secure: btt4r13t09gQlHb6gYrvGC2yGCMMHfnp1Mz1RQedc4Mpf/FfT8aE6xmK2a2i9CCvskjrP0t/BFaS4yxIURjnFRn+ugQIEa0pLspB9UJArW/vgOSpIWM9/OQ/fg8z5XuMxN6Md4DL1/iLypMNSageA1x0TRdt89+D1N1dALpg5XRCXLFbC84TLi0gjlFuib9ibPKzEhLT+anCRJ6iZMzeupDSoaCVbAtJMoDvXw4+4AcRZ1+k4MybBLyCib5boaEOt4pTT88mz4Kk0YaMwPVJyg9Qv36VqyUcPS09Yd95LuyVQ4+tZt8Y1ccbIzULsK+sLM3hLCzxlmlpN3dQBlZJiiRtQde0mgGAKyC0P0A1XjuDTywcsa5edB+fTk1Dsewz9xZ9V0NmMz8t+UNZnaSsAPga9i86jULbXUUwMVSzVRc+Xgx02liB/8qI1xYC9FM6ilStt7rn7mF0k3KbiWhcptgeXjO6Lah9FjEKd5w4MXsdUSTi/86rQaLo+kj+XdaTrXCTulKHyRyQEUj+8V1w0oVz7pcGjePHd7y5oU9ByifVQy6sytuFBfRZvugM5bKHo+i0pcWvixrZS42DrzwxZJsspANOvqSe5ifVbvOkfUppQdCBIwptxV5N1b49XPKU3W/w34QJ8xGmKp3TFA7WwVCztriFHjPgiRpB3EG99Bg=
|
||||
- REPO: $TRAVIS_REPO_SLUG
|
||||
- VERSION: v1.0.0-beta.$TRAVIS_BUILD_NUMBER
|
||||
sudo: required
|
||||
|
||||
services:
|
||||
- docker
|
||||
|
||||
before_install:
|
||||
- openssl aes-256-cbc -K $encrypted_27087ae1f4db_key -iv $encrypted_27087ae1f4db_iv -in .travis/traefik.id_rsa.enc -out ~/.ssh/traefik.id_rsa -d
|
||||
- eval "$(ssh-agent -s)"
|
||||
- chmod 600 ~/.ssh/traefik.id_rsa
|
||||
- ssh-add ~/.ssh/traefik.id_rsa
|
||||
|
||||
install:
|
||||
- sudo service docker stop
|
||||
- sudo curl https://get.docker.com/builds/Linux/x86_64/docker-1.10.1 -o /usr/bin/docker
|
||||
@@ -24,16 +16,13 @@ install:
|
||||
- sudo service docker start
|
||||
- pip install --user mkdocs
|
||||
- pip install --user pymdown-extensions
|
||||
|
||||
before_script:
|
||||
- make validate
|
||||
- make binary
|
||||
|
||||
script:
|
||||
- make test-unit
|
||||
- make test-integration
|
||||
- make crossbinary
|
||||
- make image
|
||||
|
||||
after_success:
|
||||
- make deploy
|
||||
- make deploy
|
||||
|
Binary file not shown.
@@ -6,6 +6,7 @@
|
||||
[](https://travis-ci.org/containous/traefik)
|
||||
[](https://docs.traefik.io)
|
||||
[](http://goreportcard.com/report/containous/traefik)
|
||||
[](https://imagelayers.io/?images=traefik)
|
||||
[](https://github.com/containous/traefik/blob/master/LICENSE.md)
|
||||
[](https://traefik.herokuapp.com)
|
||||
[](https://twitter.com/intent/follow?screen_name=traefikproxy)
|
||||
@@ -41,7 +42,7 @@ Run it and forget it!
|
||||
|
||||
## Features
|
||||
|
||||
- [It's fast](docs/index.md#benchmarks)
|
||||
- [It's fast](http://docs.traefik.io/benchmarks)
|
||||
- No dependency hell, single binary made with go
|
||||
- Rest API
|
||||
- Multiple backends supported: Docker, Mesos/Marathon, Consul, Etcd, and more to come
|
||||
@@ -51,7 +52,7 @@ Run it and forget it!
|
||||
- Circuit breakers on backends
|
||||
- Round Robin, rebalancer load-balancers
|
||||
- Rest Metrics
|
||||
- Tiny docker image included [](https://imagelayers.io/?images=containous/traefik:latest)
|
||||
- [Tiny](https://imagelayers.io/?images=traefik) [official](https://hub.docker.com/r/_/traefik/) docker image included
|
||||
- SSL backends support
|
||||
- SSL frontend support (with SNI)
|
||||
- Clean AngularJS Web UI
|
||||
@@ -92,7 +93,7 @@ You can access to a simple HTML frontend of Træfik.
|
||||
- Use the tiny Docker image:
|
||||
|
||||
```shell
|
||||
docker run -d -p 8080:8080 -p 80:80 -v $PWD/traefik.toml:/etc/traefik/traefik.toml containous/traefik
|
||||
docker run -d -p 8080:8080 -p 80:80 -v $PWD/traefik.toml:/etc/traefik/traefik.toml traefik
|
||||
```
|
||||
|
||||
- From sources:
|
||||
|
@@ -59,13 +59,14 @@ Here is an example of entrypoints definition:
|
||||
A frontend is a set of rules that forwards the incoming traffic from an entrypoint to a backend.
|
||||
Frontends can be defined using the following rules:
|
||||
|
||||
- `Headers`: Headers adds a matcher for request header values. It accepts a sequence of key/value pairs to be matched. For example: `Content-Type, application/json`
|
||||
- `HeadersRegexp`: Regular expressions can be used with headers as well. It accepts a sequence of key/value pairs, where the value has regex support. For example: `Content-Type, application/(text|json)`
|
||||
- `Host`: Host adds a matcher for the URL host. It accepts a template with zero or more URL variables enclosed by `{}`. Variables can define an optional regexp pattern to be matched: `www.traefik.io`, `{subdomain:[a-z]+}.traefik.io`
|
||||
- `Methods`: Methods adds a matcher for HTTP methods. It accepts a sequence of one or more methods to be matched, e.g.: `GET`, `POST`, `PUT`
|
||||
- `Path`: Path adds a matcher for the URL path. It accepts a template with zero or more URL variables enclosed by `{}`. The template must start with a `/`. For exemple `/products/` `/articles/{category}/{id:[0-9]+}`
|
||||
- `Headers: Content-Type, application/json`: Headers adds a matcher for request header values. It accepts a sequence of key/value pairs to be matched.
|
||||
- `HeadersRegexp: Content-Type, application/(text|json)`: Regular expressions can be used with headers as well. It accepts a sequence of key/value pairs, where the value has regex support.
|
||||
- `Host: traefik.io, www.traefik.io`: Match request host with given host list.
|
||||
- `HostRegexp: traefik.io, {subdomain:[a-z]+}.traefik.io`: Adds a matcher for the URL hosts. It accepts templates with zero or more URL variables enclosed by `{}`. Variables can define an optional regexp pattern to be matched.
|
||||
- `Method: GET, POST, PUT`: Method adds a matcher for HTTP methods. It accepts a sequence of one or more methods to be matched.
|
||||
- `Path: /products/, /articles/{category}/{id:[0-9]+}`: Path adds a matcher for the URL paths. It accepts templates with zero or more URL variables enclosed by `{}`.
|
||||
- `PathStrip`: Same as `Path` but strip the given prefix from the request URL's Path.
|
||||
- `PathPrefix`: PathPrefix adds a matcher for the URL path prefix. 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.
|
||||
|
||||
You can optionally enable `passHostHeader` to forward client `Host` header to the backend.
|
||||
@@ -77,21 +78,21 @@ Here is an example of frontends definition:
|
||||
[frontends.frontend1]
|
||||
backend = "backend2"
|
||||
[frontends.frontend1.routes.test_1]
|
||||
rule = "Host:test.localhost"
|
||||
rule = "Host: test.localhost, test2.localhost"
|
||||
[frontends.frontend2]
|
||||
backend = "backend1"
|
||||
passHostHeader = true
|
||||
entrypoints = ["https"] # overrides defaultEntryPoints
|
||||
[frontends.frontend2.routes.test_1]
|
||||
rule = "Host:{subdomain:[a-z]+}.localhost"
|
||||
rule = "Host: localhost, {subdomain:[a-z]+}.localhost"
|
||||
[frontends.frontend3]
|
||||
backend = "backend2"
|
||||
rule = "Path:/test"
|
||||
```
|
||||
|
||||
- Three frontends are defined: `frontend1`, `frontend2` and `frontend3`
|
||||
- `frontend1` will forward the traffic to the `backend2` if the rule `Host:test.localhost` is matched
|
||||
- `frontend2` will forward the traffic to the `backend1` if the rule `Host:{subdomain:[a-z]+}.localhost` is matched (forwarding client `Host` header to the backend)
|
||||
- `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)
|
||||
- `frontend3` will forward the traffic to the `backend2` if the rule `Path:/test` is matched
|
||||
|
||||
## Backends
|
||||
|
@@ -52,7 +52,7 @@ Transfer/sec: 0.99MB
|
||||
```sh
|
||||
$ docker run -d -l traefik.backend=test1 -l traefik.frontend.rule=Host -l traefik.frontend.value=test.traefik.localhost emilevauge/whoami
|
||||
$ docker run -d -l traefik.backend=test1 -l traefik.frontend.rule=Host -l traefik.frontend.value=test.traefik.localhost emilevauge/whoami
|
||||
$ docker run -d -p 8080:8080 -p 80:80 -v $PWD/traefik.toml:/traefik.toml -v /var/run/docker.sock:/var/run/docker.sock containous/traefik
|
||||
$ docker run -d -p 8080:8080 -p 80:80 -v $PWD/traefik.toml:/traefik.toml -v /var/run/docker.sock:/var/run/docker.sock traefik
|
||||
$ wrk -t12 -c400 -d60s -H "Host: test.traefik.localhost" --latency http://127.0.0.1:80
|
||||
Running 1m test @ http://127.0.0.1:80
|
||||
12 threads and 400 connections
|
||||
|
@@ -57,7 +57,7 @@ You can grab the latest binary from the [releases](https://github.com/containous
|
||||
Using the tiny Docker image:
|
||||
|
||||
```shell
|
||||
docker run -d -p 8080:8080 -p 80:80 -v $PWD/traefik.toml:/etc/traefik/traefik.toml containous/traefik
|
||||
docker run -d -p 8080:8080 -p 80:80 -v $PWD/traefik.toml:/etc/traefik/traefik.toml traefik
|
||||
```
|
||||
|
||||
## Test it
|
||||
@@ -66,7 +66,7 @@ You can test Træfɪk easily using [Docker compose](https://docs.docker.com/comp
|
||||
|
||||
```yaml
|
||||
traefik:
|
||||
image: containous/traefik
|
||||
image: traefik
|
||||
command: --web --docker --docker.domain=docker.localhost --logLevel=DEBUG
|
||||
ports:
|
||||
- "80:80"
|
||||
|
@@ -1,5 +1,5 @@
|
||||
traefik:
|
||||
image: containous/traefik
|
||||
image: traefik
|
||||
command: --web --docker --docker.domain=docker.localhost --logLevel=DEBUG
|
||||
ports:
|
||||
- "80:80"
|
||||
|
37
glide.lock
generated
37
glide.lock
generated
@@ -1,5 +1,5 @@
|
||||
hash: 2e15595ec349ec462fa2b0a52e26e3f3dcbd17fed66dad9a1e1c2e2c0385fe49
|
||||
updated: 2016-04-02T15:25:37.354420171+02:00
|
||||
hash: 79b6eb2a613b5e2ce5c57150eec41ac04def3f232a3613fd8b5a88b5e1041b38
|
||||
updated: 2016-04-02T15:42:37.505896092+02:00
|
||||
imports:
|
||||
- name: github.com/alecthomas/template
|
||||
version: b867cc6ab45cece8143cfcc6fc9c77cf3f2c23c0
|
||||
@@ -89,10 +89,18 @@ imports:
|
||||
- types/container
|
||||
- types/filters
|
||||
- types/strslice
|
||||
- client/transport
|
||||
- client/transport/cancellable
|
||||
- types/network
|
||||
- types/registry
|
||||
- types/time
|
||||
- types/blkiodev
|
||||
- name: github.com/docker/go-connections
|
||||
version: f549a9393d05688dff0992ef3efd8bbe6c628aeb
|
||||
subpackages:
|
||||
- nat
|
||||
- sockets
|
||||
- tlsconfig
|
||||
- name: github.com/docker/go-units
|
||||
version: 5d2041e26a699eaca682e2ea41c8f891e1060444
|
||||
- name: github.com/docker/libcompose
|
||||
@@ -113,26 +121,6 @@ imports:
|
||||
version: d5cac425555ca5cf00694df246e04f05e6a55150
|
||||
- name: github.com/flynn/go-shlex
|
||||
version: 3f9db97f856818214da2e1057f8ad84803971cff
|
||||
- name: github.com/fsouza/go-dockerclient
|
||||
version: a49c8269a6899cae30da1f8a4b82e0ce945f9967
|
||||
subpackages:
|
||||
- external/github.com/docker/docker/opts
|
||||
- external/github.com/docker/docker/pkg/archive
|
||||
- external/github.com/docker/docker/pkg/fileutils
|
||||
- external/github.com/docker/docker/pkg/homedir
|
||||
- external/github.com/docker/docker/pkg/stdcopy
|
||||
- external/github.com/hashicorp/go-cleanhttp
|
||||
- external/github.com/Sirupsen/logrus
|
||||
- external/github.com/docker/docker/pkg/idtools
|
||||
- external/github.com/docker/docker/pkg/ioutils
|
||||
- external/github.com/docker/docker/pkg/longpath
|
||||
- external/github.com/docker/docker/pkg/pools
|
||||
- external/github.com/docker/docker/pkg/promise
|
||||
- external/github.com/docker/docker/pkg/system
|
||||
- external/github.com/opencontainers/runc/libcontainer/user
|
||||
- external/golang.org/x/sys/unix
|
||||
- external/golang.org/x/net/context
|
||||
- external/github.com/docker/go-units
|
||||
- name: github.com/gambol99/go-marathon
|
||||
version: ade11d1dc2884ee1f387078fc28509559b6235d1
|
||||
- name: github.com/go-check/check
|
||||
@@ -182,6 +170,8 @@ imports:
|
||||
version: 565402cd71fbd9c12aa7e295324ea357e970a61e
|
||||
- name: github.com/mailgun/timetools
|
||||
version: fd192d755b00c968d312d23f521eb0cdc6f66bd0
|
||||
- name: github.com/Microsoft/go-winio
|
||||
version: 9e2895e5f6c3f16473b91d37fae6e89990a4520c
|
||||
- name: github.com/miekg/dns
|
||||
version: 7e024ce8ce18b21b475ac6baf8fa3c42536bf2fa
|
||||
- name: github.com/mitchellh/mapstructure
|
||||
@@ -223,6 +213,8 @@ imports:
|
||||
version: 54ed61c2b47e263ae2f01b86837b0c4bd1da28e8
|
||||
- name: github.com/unrolled/render
|
||||
version: 26b4e3aac686940fe29521545afad9966ddfc80c
|
||||
- name: github.com/vdemeester/docker-events
|
||||
version: bd72e1848b08db4b5ed1a2e9277621b9f5e5d1f3
|
||||
- name: github.com/vdemeester/libkermit
|
||||
version: 7e4e689a6fa9281e0fb9b7b9c297e22d5342a5ec
|
||||
- name: github.com/vdemeester/shakers
|
||||
@@ -257,6 +249,7 @@ imports:
|
||||
subpackages:
|
||||
- context
|
||||
- publicsuffix
|
||||
- proxy
|
||||
- name: golang.org/x/sys
|
||||
version: eb2c74142fd19a79b3f237334c7384d5167b1b46
|
||||
subpackages:
|
||||
|
@@ -52,8 +52,6 @@ import:
|
||||
ref: 26b4e3aac686940fe29521545afad9966ddfc80c
|
||||
- package: github.com/flynn/go-shlex
|
||||
ref: 3f9db97f856818214da2e1057f8ad84803971cff
|
||||
- package: github.com/fsouza/go-dockerclient
|
||||
ref: a49c8269a6899cae30da1f8a4b82e0ce945f9967
|
||||
- package: github.com/boltdb/bolt
|
||||
ref: 51f99c862475898df9773747d3accd05a7ca33c1
|
||||
- package: gopkg.in/mgo.v2
|
||||
@@ -168,8 +166,11 @@ import:
|
||||
- types/container
|
||||
- types/filters
|
||||
- types/strslice
|
||||
- package: github.com/vdemeester/docker-events
|
||||
- package: github.com/docker/go-connections
|
||||
subpackages:
|
||||
- nat
|
||||
- sockets
|
||||
- tlsconfig
|
||||
- package: github.com/docker/go-units
|
||||
- package: github.com/mailgun/multibuf
|
||||
|
@@ -7,18 +7,20 @@ import (
|
||||
|
||||
// StripPrefix is a middleware used to strip prefix from an URL request
|
||||
type StripPrefix struct {
|
||||
Handler http.Handler
|
||||
Prefix string
|
||||
Handler http.Handler
|
||||
Prefixes []string
|
||||
}
|
||||
|
||||
func (s *StripPrefix) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
if p := strings.TrimPrefix(r.URL.Path, s.Prefix); len(p) < len(r.URL.Path) {
|
||||
r.URL.Path = p
|
||||
r.RequestURI = r.URL.RequestURI()
|
||||
s.Handler.ServeHTTP(w, r)
|
||||
} else {
|
||||
http.NotFound(w, r)
|
||||
for _, prefix := range s.Prefixes {
|
||||
if p := strings.TrimPrefix(r.URL.Path, strings.TrimSpace(prefix)); len(p) < len(r.URL.Path) {
|
||||
r.URL.Path = p
|
||||
r.RequestURI = r.URL.RequestURI()
|
||||
s.Handler.ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
}
|
||||
http.NotFound(w, r)
|
||||
}
|
||||
|
||||
// SetHandler sets handler
|
||||
|
@@ -2,19 +2,31 @@ package provider
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"text/template"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/BurntSushi/ty/fun"
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/cenkalti/backoff"
|
||||
"github.com/containous/traefik/safe"
|
||||
"github.com/containous/traefik/types"
|
||||
"github.com/fsouza/go-dockerclient"
|
||||
"github.com/docker/engine-api/client"
|
||||
dockertypes "github.com/docker/engine-api/types"
|
||||
eventtypes "github.com/docker/engine-api/types/events"
|
||||
"github.com/docker/engine-api/types/filters"
|
||||
"github.com/docker/go-connections/sockets"
|
||||
"github.com/docker/go-connections/tlsconfig"
|
||||
"github.com/vdemeester/docker-events"
|
||||
)
|
||||
|
||||
// DockerAPIVersion is a constant holding the version of the Docker API traefik will use
|
||||
const DockerAPIVersion string = "1.21"
|
||||
|
||||
// Docker holds configurations of the Docker provider.
|
||||
type Docker struct {
|
||||
BaseProvider `mapstructure:",squash"`
|
||||
@@ -31,59 +43,94 @@ type DockerTLS struct {
|
||||
InsecureSkipVerify bool
|
||||
}
|
||||
|
||||
func (provider *Docker) createClient() (client.APIClient, error) {
|
||||
var httpClient *http.Client
|
||||
httpHeaders := map[string]string{
|
||||
// FIXME(vdemeester) use version here O:)
|
||||
"User-Agent": "Traefik",
|
||||
}
|
||||
if provider.TLS != nil {
|
||||
tlsOptions := tlsconfig.Options{
|
||||
CAFile: provider.TLS.CA,
|
||||
CertFile: provider.TLS.Cert,
|
||||
KeyFile: provider.TLS.Key,
|
||||
InsecureSkipVerify: provider.TLS.InsecureSkipVerify,
|
||||
}
|
||||
config, err := tlsconfig.Client(tlsOptions)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tr := &http.Transport{
|
||||
TLSClientConfig: config,
|
||||
}
|
||||
proto, addr, _, err := client.ParseHost(provider.Endpoint)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sockets.ConfigureTransport(tr, proto, addr)
|
||||
|
||||
httpClient = &http.Client{
|
||||
Transport: tr,
|
||||
}
|
||||
}
|
||||
return client.NewClient(provider.Endpoint, DockerAPIVersion, httpClient, httpHeaders)
|
||||
}
|
||||
|
||||
// Provide allows the provider to provide configurations to traefik
|
||||
// using the given configuration channel.
|
||||
func (provider *Docker) Provide(configurationChan chan<- types.ConfigMessage) error {
|
||||
safe.Go(func() {
|
||||
operation := func() error {
|
||||
var dockerClient *docker.Client
|
||||
var err error
|
||||
|
||||
if provider.TLS != nil {
|
||||
dockerClient, err = docker.NewTLSClient(provider.Endpoint,
|
||||
provider.TLS.Cert, provider.TLS.Key, provider.TLS.CA)
|
||||
if err == nil {
|
||||
dockerClient.TLSConfig.InsecureSkipVerify = provider.TLS.InsecureSkipVerify
|
||||
}
|
||||
} else {
|
||||
dockerClient, err = docker.NewClient(provider.Endpoint)
|
||||
}
|
||||
dockerClient, err := provider.createClient()
|
||||
if err != nil {
|
||||
log.Errorf("Failed to create a client for docker, error: %s", err)
|
||||
return err
|
||||
}
|
||||
err = dockerClient.Ping()
|
||||
version, err := dockerClient.ServerVersion(context.Background())
|
||||
log.Debugf("Docker connection established with docker %s (API %s)", version.Version, version.APIVersion)
|
||||
containers, err := listContainers(dockerClient)
|
||||
if err != nil {
|
||||
log.Errorf("Docker connection error %+v", err)
|
||||
log.Errorf("Failed to list containers for docker, error %s", err)
|
||||
return err
|
||||
}
|
||||
log.Debug("Docker connection established")
|
||||
configuration := provider.loadDockerConfig(listContainers(dockerClient))
|
||||
configuration := provider.loadDockerConfig(containers)
|
||||
configurationChan <- types.ConfigMessage{
|
||||
ProviderName: "docker",
|
||||
Configuration: configuration,
|
||||
}
|
||||
if provider.Watch {
|
||||
dockerEvents := make(chan *docker.APIEvents)
|
||||
dockerClient.AddEventListener(dockerEvents)
|
||||
log.Debug("Docker listening")
|
||||
for {
|
||||
event := <-dockerEvents
|
||||
if event == nil {
|
||||
return errors.New("Docker event nil")
|
||||
// log.Fatalf("Docker connection error")
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
f := filters.NewArgs()
|
||||
f.Add("type", "container")
|
||||
options := dockertypes.EventsOptions{
|
||||
Filters: f,
|
||||
}
|
||||
eventHandler := events.NewHandler(events.ByAction)
|
||||
startStopHandle := func(m eventtypes.Message) {
|
||||
log.Debugf("Docker event received %+v", m)
|
||||
containers, err := listContainers(dockerClient)
|
||||
if err != nil {
|
||||
log.Errorf("Failed to list containers for docker, error %s", err)
|
||||
// Call cancel to get out of the monitor
|
||||
cancel()
|
||||
}
|
||||
if event.Status == "start" || event.Status == "die" {
|
||||
log.Debugf("Docker event receveived %+v", event)
|
||||
configuration := provider.loadDockerConfig(listContainers(dockerClient))
|
||||
if configuration != nil {
|
||||
configurationChan <- types.ConfigMessage{
|
||||
ProviderName: "docker",
|
||||
Configuration: configuration,
|
||||
}
|
||||
configuration := provider.loadDockerConfig(containers)
|
||||
if configuration != nil {
|
||||
configurationChan <- types.ConfigMessage{
|
||||
ProviderName: "docker",
|
||||
Configuration: configuration,
|
||||
}
|
||||
}
|
||||
}
|
||||
eventHandler.Handle("start", startStopHandle)
|
||||
eventHandler.Handle("die", startStopHandle)
|
||||
errChan := events.MonitorWithHandler(ctx, dockerClient, options, eventHandler)
|
||||
if err := <-errChan; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -99,7 +146,7 @@ func (provider *Docker) Provide(configurationChan chan<- types.ConfigMessage) er
|
||||
return nil
|
||||
}
|
||||
|
||||
func (provider *Docker) loadDockerConfig(containersInspected []docker.Container) *types.Configuration {
|
||||
func (provider *Docker) loadDockerConfig(containersInspected []dockertypes.ContainerJSON) *types.Configuration {
|
||||
var DockerFuncMap = template.FuncMap{
|
||||
"getBackend": provider.getBackend,
|
||||
"getPort": provider.getPort,
|
||||
@@ -113,16 +160,16 @@ func (provider *Docker) loadDockerConfig(containersInspected []docker.Container)
|
||||
}
|
||||
|
||||
// filter containers
|
||||
filteredContainers := fun.Filter(containerFilter, containersInspected).([]docker.Container)
|
||||
filteredContainers := fun.Filter(containerFilter, containersInspected).([]dockertypes.ContainerJSON)
|
||||
|
||||
frontends := map[string][]docker.Container{}
|
||||
frontends := map[string][]dockertypes.ContainerJSON{}
|
||||
for _, container := range filteredContainers {
|
||||
frontends[provider.getFrontendName(container)] = append(frontends[provider.getFrontendName(container)], container)
|
||||
}
|
||||
|
||||
templateObjects := struct {
|
||||
Containers []docker.Container
|
||||
Frontends map[string][]docker.Container
|
||||
Containers []dockertypes.ContainerJSON
|
||||
Frontends map[string][]dockertypes.ContainerJSON
|
||||
Domain string
|
||||
}{
|
||||
filteredContainers,
|
||||
@@ -137,7 +184,7 @@ func (provider *Docker) loadDockerConfig(containersInspected []docker.Container)
|
||||
return configuration
|
||||
}
|
||||
|
||||
func containerFilter(container docker.Container) bool {
|
||||
func containerFilter(container dockertypes.ContainerJSON) bool {
|
||||
if len(container.NetworkSettings.Ports) == 0 {
|
||||
log.Debugf("Filtering container without port %s", container.Name)
|
||||
return false
|
||||
@@ -156,14 +203,14 @@ func containerFilter(container docker.Container) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (provider *Docker) getFrontendName(container docker.Container) string {
|
||||
func (provider *Docker) getFrontendName(container dockertypes.ContainerJSON) string {
|
||||
// Replace '.' with '-' in quoted keys because of this issue https://github.com/BurntSushi/toml/issues/78
|
||||
return normalize(provider.getFrontendRule(container))
|
||||
}
|
||||
|
||||
// GetFrontendRule returns the frontend rule for the specified container, using
|
||||
// it's label. It returns a default one (Host) if the label is not present.
|
||||
func (provider *Docker) getFrontendRule(container docker.Container) string {
|
||||
func (provider *Docker) getFrontendRule(container dockertypes.ContainerJSON) string {
|
||||
// ⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠
|
||||
// TODO: backwards compatibility with DEPRECATED rule.Value
|
||||
if value, ok := container.Config.Labels["traefik.frontend.value"]; ok {
|
||||
@@ -179,14 +226,14 @@ func (provider *Docker) getFrontendRule(container docker.Container) string {
|
||||
return "Host:" + getEscapedName(container.Name) + "." + provider.Domain
|
||||
}
|
||||
|
||||
func (provider *Docker) getBackend(container docker.Container) string {
|
||||
func (provider *Docker) getBackend(container dockertypes.ContainerJSON) string {
|
||||
if label, err := getLabel(container, "traefik.backend"); err == nil {
|
||||
return label
|
||||
}
|
||||
return normalize(container.Name)
|
||||
}
|
||||
|
||||
func (provider *Docker) getPort(container docker.Container) string {
|
||||
func (provider *Docker) getPort(container dockertypes.ContainerJSON) string {
|
||||
if label, err := getLabel(container, "traefik.port"); err == nil {
|
||||
return label
|
||||
}
|
||||
@@ -196,42 +243,42 @@ func (provider *Docker) getPort(container docker.Container) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (provider *Docker) getWeight(container docker.Container) string {
|
||||
func (provider *Docker) getWeight(container dockertypes.ContainerJSON) string {
|
||||
if label, err := getLabel(container, "traefik.weight"); err == nil {
|
||||
return label
|
||||
}
|
||||
return "1"
|
||||
}
|
||||
|
||||
func (provider *Docker) getDomain(container docker.Container) string {
|
||||
func (provider *Docker) getDomain(container dockertypes.ContainerJSON) string {
|
||||
if label, err := getLabel(container, "traefik.domain"); err == nil {
|
||||
return label
|
||||
}
|
||||
return provider.Domain
|
||||
}
|
||||
|
||||
func (provider *Docker) getProtocol(container docker.Container) string {
|
||||
func (provider *Docker) getProtocol(container dockertypes.ContainerJSON) string {
|
||||
if label, err := getLabel(container, "traefik.protocol"); err == nil {
|
||||
return label
|
||||
}
|
||||
return "http"
|
||||
}
|
||||
|
||||
func (provider *Docker) getPassHostHeader(container docker.Container) string {
|
||||
func (provider *Docker) getPassHostHeader(container dockertypes.ContainerJSON) string {
|
||||
if passHostHeader, err := getLabel(container, "traefik.frontend.passHostHeader"); err == nil {
|
||||
return passHostHeader
|
||||
}
|
||||
return "false"
|
||||
}
|
||||
|
||||
func (provider *Docker) getEntryPoints(container docker.Container) []string {
|
||||
func (provider *Docker) getEntryPoints(container dockertypes.ContainerJSON) []string {
|
||||
if entryPoints, err := getLabel(container, "traefik.frontend.entryPoints"); err == nil {
|
||||
return strings.Split(entryPoints, ",")
|
||||
}
|
||||
return []string{}
|
||||
}
|
||||
|
||||
func getLabel(container docker.Container, label string) (string, error) {
|
||||
func getLabel(container dockertypes.ContainerJSON, label string) (string, error) {
|
||||
for key, value := range container.Config.Labels {
|
||||
if key == label {
|
||||
return value, nil
|
||||
@@ -240,7 +287,7 @@ func getLabel(container docker.Container, label string) (string, error) {
|
||||
return "", errors.New("Label not found:" + label)
|
||||
}
|
||||
|
||||
func getLabels(container docker.Container, labels []string) (map[string]string, error) {
|
||||
func getLabels(container dockertypes.ContainerJSON, labels []string) (map[string]string, error) {
|
||||
var globalErr error
|
||||
foundLabels := map[string]string{}
|
||||
for _, label := range labels {
|
||||
@@ -256,14 +303,20 @@ func getLabels(container docker.Container, labels []string) (map[string]string,
|
||||
return foundLabels, globalErr
|
||||
}
|
||||
|
||||
func listContainers(dockerClient *docker.Client) []docker.Container {
|
||||
containerList, _ := dockerClient.ListContainers(docker.ListContainersOptions{})
|
||||
containersInspected := []docker.Container{}
|
||||
func listContainers(dockerClient client.APIClient) ([]dockertypes.ContainerJSON, error) {
|
||||
containerList, err := dockerClient.ContainerList(context.Background(), dockertypes.ContainerListOptions{})
|
||||
if err != nil {
|
||||
return []dockertypes.ContainerJSON{}, err
|
||||
}
|
||||
containersInspected := []dockertypes.ContainerJSON{}
|
||||
|
||||
// get inspect containers
|
||||
for _, container := range containerList {
|
||||
containerInspected, _ := dockerClient.InspectContainer(container.ID)
|
||||
containersInspected = append(containersInspected, *containerInspected)
|
||||
containerInspected, err := dockerClient.ContainerInspect(context.Background(), container.ID)
|
||||
if err != nil {
|
||||
log.Warnf("Failed to inpsect container %s, error: %s", container.ID, err)
|
||||
}
|
||||
containersInspected = append(containersInspected, containerInspected)
|
||||
}
|
||||
return containersInspected
|
||||
return containersInspected, nil
|
||||
}
|
||||
|
@@ -6,7 +6,10 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/containous/traefik/types"
|
||||
"github.com/fsouza/go-dockerclient"
|
||||
docker "github.com/docker/engine-api/types"
|
||||
"github.com/docker/engine-api/types/container"
|
||||
"github.com/docker/engine-api/types/network"
|
||||
"github.com/docker/go-connections/nat"
|
||||
)
|
||||
|
||||
func TestDockerGetFrontendName(t *testing.T) {
|
||||
@@ -15,20 +18,24 @@ func TestDockerGetFrontendName(t *testing.T) {
|
||||
}
|
||||
|
||||
containers := []struct {
|
||||
container docker.Container
|
||||
container docker.ContainerJSON
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
container: docker.Container{
|
||||
Name: "foo",
|
||||
Config: &docker.Config{},
|
||||
container: docker.ContainerJSON{
|
||||
ContainerJSONBase: &docker.ContainerJSONBase{
|
||||
Name: "foo",
|
||||
},
|
||||
Config: &container.Config{},
|
||||
},
|
||||
expected: "Host-foo-docker-localhost",
|
||||
},
|
||||
{
|
||||
container: docker.Container{
|
||||
Name: "bar",
|
||||
Config: &docker.Config{
|
||||
container: docker.ContainerJSON{
|
||||
ContainerJSONBase: &docker.ContainerJSONBase{
|
||||
Name: "bar",
|
||||
},
|
||||
Config: &container.Config{
|
||||
Labels: map[string]string{
|
||||
"traefik.frontend.rule": "Headers:User-Agent,bat/0.1.0",
|
||||
},
|
||||
@@ -37,9 +44,11 @@ func TestDockerGetFrontendName(t *testing.T) {
|
||||
expected: "Headers-User-Agent-bat-0-1-0",
|
||||
},
|
||||
{
|
||||
container: docker.Container{
|
||||
Name: "test",
|
||||
Config: &docker.Config{
|
||||
container: docker.ContainerJSON{
|
||||
ContainerJSONBase: &docker.ContainerJSONBase{
|
||||
Name: "test",
|
||||
},
|
||||
Config: &container.Config{
|
||||
Labels: map[string]string{
|
||||
"traefik.frontend.rule": "Host:foo.bar",
|
||||
},
|
||||
@@ -48,9 +57,11 @@ func TestDockerGetFrontendName(t *testing.T) {
|
||||
expected: "Host-foo-bar",
|
||||
},
|
||||
{
|
||||
container: docker.Container{
|
||||
Name: "test",
|
||||
Config: &docker.Config{
|
||||
container: docker.ContainerJSON{
|
||||
ContainerJSONBase: &docker.ContainerJSONBase{
|
||||
Name: "test",
|
||||
},
|
||||
Config: &container.Config{
|
||||
Labels: map[string]string{
|
||||
"traefik.frontend.rule": "Path:/test",
|
||||
},
|
||||
@@ -59,9 +70,11 @@ func TestDockerGetFrontendName(t *testing.T) {
|
||||
expected: "Path-test",
|
||||
},
|
||||
{
|
||||
container: docker.Container{
|
||||
Name: "test",
|
||||
Config: &docker.Config{
|
||||
container: docker.ContainerJSON{
|
||||
ContainerJSONBase: &docker.ContainerJSONBase{
|
||||
Name: "test",
|
||||
},
|
||||
Config: &container.Config{
|
||||
Labels: map[string]string{
|
||||
"traefik.frontend.rule": "PathPrefix:/test2",
|
||||
},
|
||||
@@ -85,27 +98,33 @@ func TestDockerGetFrontendRule(t *testing.T) {
|
||||
}
|
||||
|
||||
containers := []struct {
|
||||
container docker.Container
|
||||
container docker.ContainerJSON
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
container: docker.Container{
|
||||
Name: "foo",
|
||||
Config: &docker.Config{},
|
||||
container: docker.ContainerJSON{
|
||||
ContainerJSONBase: &docker.ContainerJSONBase{
|
||||
Name: "foo",
|
||||
},
|
||||
Config: &container.Config{},
|
||||
},
|
||||
expected: "Host:foo.docker.localhost",
|
||||
},
|
||||
{
|
||||
container: docker.Container{
|
||||
Name: "bar",
|
||||
Config: &docker.Config{},
|
||||
container: docker.ContainerJSON{
|
||||
ContainerJSONBase: &docker.ContainerJSONBase{
|
||||
Name: "bar",
|
||||
},
|
||||
Config: &container.Config{},
|
||||
},
|
||||
expected: "Host:bar.docker.localhost",
|
||||
},
|
||||
{
|
||||
container: docker.Container{
|
||||
Name: "test",
|
||||
Config: &docker.Config{
|
||||
container: docker.ContainerJSON{
|
||||
ContainerJSONBase: &docker.ContainerJSONBase{
|
||||
Name: "test",
|
||||
},
|
||||
Config: &container.Config{
|
||||
Labels: map[string]string{
|
||||
"traefik.frontend.rule": "Host:foo.bar",
|
||||
},
|
||||
@@ -114,9 +133,11 @@ func TestDockerGetFrontendRule(t *testing.T) {
|
||||
expected: "Host:foo.bar",
|
||||
},
|
||||
{
|
||||
container: docker.Container{
|
||||
Name: "test",
|
||||
Config: &docker.Config{
|
||||
container: docker.ContainerJSON{
|
||||
ContainerJSONBase: &docker.ContainerJSONBase{
|
||||
Name: "test",
|
||||
},
|
||||
Config: &container.Config{
|
||||
Labels: map[string]string{
|
||||
"traefik.frontend.rule": "Path:/test",
|
||||
},
|
||||
@@ -138,27 +159,33 @@ func TestDockerGetBackend(t *testing.T) {
|
||||
provider := &Docker{}
|
||||
|
||||
containers := []struct {
|
||||
container docker.Container
|
||||
container docker.ContainerJSON
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
container: docker.Container{
|
||||
Name: "foo",
|
||||
Config: &docker.Config{},
|
||||
container: docker.ContainerJSON{
|
||||
ContainerJSONBase: &docker.ContainerJSONBase{
|
||||
Name: "foo",
|
||||
},
|
||||
Config: &container.Config{},
|
||||
},
|
||||
expected: "foo",
|
||||
},
|
||||
{
|
||||
container: docker.Container{
|
||||
Name: "bar",
|
||||
Config: &docker.Config{},
|
||||
container: docker.ContainerJSON{
|
||||
ContainerJSONBase: &docker.ContainerJSONBase{
|
||||
Name: "bar",
|
||||
},
|
||||
Config: &container.Config{},
|
||||
},
|
||||
expected: "bar",
|
||||
},
|
||||
{
|
||||
container: docker.Container{
|
||||
Name: "test",
|
||||
Config: &docker.Config{
|
||||
container: docker.ContainerJSON{
|
||||
ContainerJSONBase: &docker.ContainerJSONBase{
|
||||
Name: "test",
|
||||
},
|
||||
Config: &container.Config{
|
||||
Labels: map[string]string{
|
||||
"traefik.backend": "foobar",
|
||||
},
|
||||
@@ -180,24 +207,30 @@ func TestDockerGetPort(t *testing.T) {
|
||||
provider := &Docker{}
|
||||
|
||||
containers := []struct {
|
||||
container docker.Container
|
||||
container docker.ContainerJSON
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
container: docker.Container{
|
||||
Name: "foo",
|
||||
Config: &docker.Config{},
|
||||
container: docker.ContainerJSON{
|
||||
ContainerJSONBase: &docker.ContainerJSONBase{
|
||||
Name: "foo",
|
||||
},
|
||||
Config: &container.Config{},
|
||||
NetworkSettings: &docker.NetworkSettings{},
|
||||
},
|
||||
expected: "",
|
||||
},
|
||||
{
|
||||
container: docker.Container{
|
||||
Name: "bar",
|
||||
Config: &docker.Config{},
|
||||
container: docker.ContainerJSON{
|
||||
ContainerJSONBase: &docker.ContainerJSONBase{
|
||||
Name: "bar",
|
||||
},
|
||||
Config: &container.Config{},
|
||||
NetworkSettings: &docker.NetworkSettings{
|
||||
Ports: map[docker.Port][]docker.PortBinding{
|
||||
"80/tcp": {},
|
||||
NetworkSettingsBase: docker.NetworkSettingsBase{
|
||||
Ports: nat.PortMap{
|
||||
"80/tcp": {},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -205,9 +238,9 @@ func TestDockerGetPort(t *testing.T) {
|
||||
},
|
||||
// FIXME handle this better..
|
||||
// {
|
||||
// container: docker.Container{
|
||||
// container: docker.ContainerJSON{
|
||||
// Name: "bar",
|
||||
// Config: &docker.Config{},
|
||||
// Config: &container.Config{},
|
||||
// NetworkSettings: &docker.NetworkSettings{
|
||||
// Ports: map[docker.Port][]docker.PortBinding{
|
||||
// "80/tcp": []docker.PortBinding{},
|
||||
@@ -218,16 +251,20 @@ func TestDockerGetPort(t *testing.T) {
|
||||
// expected: "80",
|
||||
// },
|
||||
{
|
||||
container: docker.Container{
|
||||
Name: "test",
|
||||
Config: &docker.Config{
|
||||
container: docker.ContainerJSON{
|
||||
ContainerJSONBase: &docker.ContainerJSONBase{
|
||||
Name: "test",
|
||||
},
|
||||
Config: &container.Config{
|
||||
Labels: map[string]string{
|
||||
"traefik.port": "8080",
|
||||
},
|
||||
},
|
||||
NetworkSettings: &docker.NetworkSettings{
|
||||
Ports: map[docker.Port][]docker.PortBinding{
|
||||
"80/tcp": {},
|
||||
NetworkSettingsBase: docker.NetworkSettingsBase{
|
||||
Ports: nat.PortMap{
|
||||
"80/tcp": {},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -247,20 +284,24 @@ func TestDockerGetWeight(t *testing.T) {
|
||||
provider := &Docker{}
|
||||
|
||||
containers := []struct {
|
||||
container docker.Container
|
||||
container docker.ContainerJSON
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
container: docker.Container{
|
||||
Name: "foo",
|
||||
Config: &docker.Config{},
|
||||
container: docker.ContainerJSON{
|
||||
ContainerJSONBase: &docker.ContainerJSONBase{
|
||||
Name: "foo",
|
||||
},
|
||||
Config: &container.Config{},
|
||||
},
|
||||
expected: "1",
|
||||
},
|
||||
{
|
||||
container: docker.Container{
|
||||
Name: "test",
|
||||
Config: &docker.Config{
|
||||
container: docker.ContainerJSON{
|
||||
ContainerJSONBase: &docker.ContainerJSONBase{
|
||||
Name: "test",
|
||||
},
|
||||
Config: &container.Config{
|
||||
Labels: map[string]string{
|
||||
"traefik.weight": "10",
|
||||
},
|
||||
@@ -284,20 +325,24 @@ func TestDockerGetDomain(t *testing.T) {
|
||||
}
|
||||
|
||||
containers := []struct {
|
||||
container docker.Container
|
||||
container docker.ContainerJSON
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
container: docker.Container{
|
||||
Name: "foo",
|
||||
Config: &docker.Config{},
|
||||
container: docker.ContainerJSON{
|
||||
ContainerJSONBase: &docker.ContainerJSONBase{
|
||||
Name: "foo",
|
||||
},
|
||||
Config: &container.Config{},
|
||||
},
|
||||
expected: "docker.localhost",
|
||||
},
|
||||
{
|
||||
container: docker.Container{
|
||||
Name: "test",
|
||||
Config: &docker.Config{
|
||||
container: docker.ContainerJSON{
|
||||
ContainerJSONBase: &docker.ContainerJSONBase{
|
||||
Name: "test",
|
||||
},
|
||||
Config: &container.Config{
|
||||
Labels: map[string]string{
|
||||
"traefik.domain": "foo.bar",
|
||||
},
|
||||
@@ -319,20 +364,24 @@ func TestDockerGetProtocol(t *testing.T) {
|
||||
provider := &Docker{}
|
||||
|
||||
containers := []struct {
|
||||
container docker.Container
|
||||
container docker.ContainerJSON
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
container: docker.Container{
|
||||
Name: "foo",
|
||||
Config: &docker.Config{},
|
||||
container: docker.ContainerJSON{
|
||||
ContainerJSONBase: &docker.ContainerJSONBase{
|
||||
Name: "foo",
|
||||
},
|
||||
Config: &container.Config{},
|
||||
},
|
||||
expected: "http",
|
||||
},
|
||||
{
|
||||
container: docker.Container{
|
||||
Name: "test",
|
||||
Config: &docker.Config{
|
||||
container: docker.ContainerJSON{
|
||||
ContainerJSONBase: &docker.ContainerJSONBase{
|
||||
Name: "test",
|
||||
},
|
||||
Config: &container.Config{
|
||||
Labels: map[string]string{
|
||||
"traefik.protocol": "https",
|
||||
},
|
||||
@@ -354,20 +403,24 @@ func TestDockerGetPassHostHeader(t *testing.T) {
|
||||
provider := &Docker{}
|
||||
|
||||
containers := []struct {
|
||||
container docker.Container
|
||||
container docker.ContainerJSON
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
container: docker.Container{
|
||||
Name: "foo",
|
||||
Config: &docker.Config{},
|
||||
container: docker.ContainerJSON{
|
||||
ContainerJSONBase: &docker.ContainerJSONBase{
|
||||
Name: "foo",
|
||||
},
|
||||
Config: &container.Config{},
|
||||
},
|
||||
expected: "false",
|
||||
},
|
||||
{
|
||||
container: docker.Container{
|
||||
Name: "test",
|
||||
Config: &docker.Config{
|
||||
container: docker.ContainerJSON{
|
||||
ContainerJSONBase: &docker.ContainerJSONBase{
|
||||
Name: "test",
|
||||
},
|
||||
Config: &container.Config{
|
||||
Labels: map[string]string{
|
||||
"traefik.frontend.passHostHeader": "true",
|
||||
},
|
||||
@@ -387,18 +440,18 @@ func TestDockerGetPassHostHeader(t *testing.T) {
|
||||
|
||||
func TestDockerGetLabel(t *testing.T) {
|
||||
containers := []struct {
|
||||
container docker.Container
|
||||
container docker.ContainerJSON
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
container: docker.Container{
|
||||
Config: &docker.Config{},
|
||||
container: docker.ContainerJSON{
|
||||
Config: &container.Config{},
|
||||
},
|
||||
expected: "Label not found:",
|
||||
},
|
||||
{
|
||||
container: docker.Container{
|
||||
Config: &docker.Config{
|
||||
container: docker.ContainerJSON{
|
||||
Config: &container.Config{
|
||||
Labels: map[string]string{
|
||||
"foo": "bar",
|
||||
},
|
||||
@@ -424,20 +477,20 @@ func TestDockerGetLabel(t *testing.T) {
|
||||
|
||||
func TestDockerGetLabels(t *testing.T) {
|
||||
containers := []struct {
|
||||
container docker.Container
|
||||
container docker.ContainerJSON
|
||||
expectedLabels map[string]string
|
||||
expectedError string
|
||||
}{
|
||||
{
|
||||
container: docker.Container{
|
||||
Config: &docker.Config{},
|
||||
container: docker.ContainerJSON{
|
||||
Config: &container.Config{},
|
||||
},
|
||||
expectedLabels: map[string]string{},
|
||||
expectedError: "Label not found:",
|
||||
},
|
||||
{
|
||||
container: docker.Container{
|
||||
Config: &docker.Config{
|
||||
container: docker.ContainerJSON{
|
||||
Config: &container.Config{
|
||||
Labels: map[string]string{
|
||||
"foo": "fooz",
|
||||
},
|
||||
@@ -449,8 +502,8 @@ func TestDockerGetLabels(t *testing.T) {
|
||||
expectedError: "Label not found: bar",
|
||||
},
|
||||
{
|
||||
container: docker.Container{
|
||||
Config: &docker.Config{
|
||||
container: docker.ContainerJSON{
|
||||
Config: &container.Config{
|
||||
Labels: map[string]string{
|
||||
"foo": "fooz",
|
||||
"bar": "barz",
|
||||
@@ -480,125 +533,168 @@ func TestDockerGetLabels(t *testing.T) {
|
||||
|
||||
func TestDockerTraefikFilter(t *testing.T) {
|
||||
containers := []struct {
|
||||
container docker.Container
|
||||
container docker.ContainerJSON
|
||||
expected bool
|
||||
}{
|
||||
{
|
||||
container: docker.Container{
|
||||
Config: &docker.Config{},
|
||||
container: docker.ContainerJSON{
|
||||
ContainerJSONBase: &docker.ContainerJSONBase{
|
||||
Name: "container",
|
||||
},
|
||||
Config: &container.Config{},
|
||||
NetworkSettings: &docker.NetworkSettings{},
|
||||
},
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
container: docker.Container{
|
||||
Config: &docker.Config{
|
||||
container: docker.ContainerJSON{
|
||||
ContainerJSONBase: &docker.ContainerJSONBase{
|
||||
Name: "container",
|
||||
},
|
||||
Config: &container.Config{
|
||||
Labels: map[string]string{
|
||||
"traefik.enable": "false",
|
||||
},
|
||||
},
|
||||
NetworkSettings: &docker.NetworkSettings{
|
||||
Ports: map[docker.Port][]docker.PortBinding{
|
||||
"80/tcp": {},
|
||||
NetworkSettingsBase: docker.NetworkSettingsBase{
|
||||
Ports: nat.PortMap{
|
||||
"80/tcp": {},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
container: docker.Container{
|
||||
Config: &docker.Config{
|
||||
container: docker.ContainerJSON{
|
||||
ContainerJSONBase: &docker.ContainerJSONBase{
|
||||
Name: "container",
|
||||
},
|
||||
Config: &container.Config{
|
||||
Labels: map[string]string{
|
||||
"traefik.frontend.rule": "Host:foo.bar",
|
||||
},
|
||||
},
|
||||
NetworkSettings: &docker.NetworkSettings{
|
||||
Ports: map[docker.Port][]docker.PortBinding{
|
||||
"80/tcp": {},
|
||||
NetworkSettingsBase: docker.NetworkSettingsBase{
|
||||
Ports: nat.PortMap{
|
||||
"80/tcp": {},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
container: docker.Container{
|
||||
Config: &docker.Config{},
|
||||
container: docker.ContainerJSON{
|
||||
ContainerJSONBase: &docker.ContainerJSONBase{
|
||||
Name: "container",
|
||||
},
|
||||
Config: &container.Config{},
|
||||
NetworkSettings: &docker.NetworkSettings{
|
||||
Ports: map[docker.Port][]docker.PortBinding{
|
||||
"80/tcp": {},
|
||||
"443/tcp": {},
|
||||
NetworkSettingsBase: docker.NetworkSettingsBase{
|
||||
Ports: nat.PortMap{
|
||||
"80/tcp": {},
|
||||
"443/tcp": {},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
container: docker.Container{
|
||||
Config: &docker.Config{},
|
||||
container: docker.ContainerJSON{
|
||||
ContainerJSONBase: &docker.ContainerJSONBase{
|
||||
Name: "container",
|
||||
},
|
||||
Config: &container.Config{},
|
||||
NetworkSettings: &docker.NetworkSettings{
|
||||
Ports: map[docker.Port][]docker.PortBinding{
|
||||
"80/tcp": {},
|
||||
NetworkSettingsBase: docker.NetworkSettingsBase{
|
||||
Ports: nat.PortMap{
|
||||
"80/tcp": {},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
container: docker.Container{
|
||||
Config: &docker.Config{
|
||||
container: docker.ContainerJSON{
|
||||
ContainerJSONBase: &docker.ContainerJSONBase{
|
||||
Name: "container",
|
||||
},
|
||||
Config: &container.Config{
|
||||
Labels: map[string]string{
|
||||
"traefik.port": "80",
|
||||
},
|
||||
},
|
||||
NetworkSettings: &docker.NetworkSettings{
|
||||
Ports: map[docker.Port][]docker.PortBinding{
|
||||
"80/tcp": {},
|
||||
"443/tcp": {},
|
||||
NetworkSettingsBase: docker.NetworkSettingsBase{
|
||||
Ports: nat.PortMap{
|
||||
"80/tcp": {},
|
||||
"443/tcp": {},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
container: docker.Container{
|
||||
Config: &docker.Config{
|
||||
container: docker.ContainerJSON{
|
||||
ContainerJSONBase: &docker.ContainerJSONBase{
|
||||
Name: "container",
|
||||
},
|
||||
Config: &container.Config{
|
||||
Labels: map[string]string{
|
||||
"traefik.enable": "true",
|
||||
},
|
||||
},
|
||||
NetworkSettings: &docker.NetworkSettings{
|
||||
Ports: map[docker.Port][]docker.PortBinding{
|
||||
"80/tcp": {},
|
||||
NetworkSettingsBase: docker.NetworkSettingsBase{
|
||||
Ports: nat.PortMap{
|
||||
"80/tcp": {},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
container: docker.Container{
|
||||
Config: &docker.Config{
|
||||
container: docker.ContainerJSON{
|
||||
ContainerJSONBase: &docker.ContainerJSONBase{
|
||||
Name: "container",
|
||||
},
|
||||
Config: &container.Config{
|
||||
Labels: map[string]string{
|
||||
"traefik.enable": "anything",
|
||||
},
|
||||
},
|
||||
NetworkSettings: &docker.NetworkSettings{
|
||||
Ports: map[docker.Port][]docker.PortBinding{
|
||||
"80/tcp": {},
|
||||
NetworkSettingsBase: docker.NetworkSettingsBase{
|
||||
Ports: nat.PortMap{
|
||||
"80/tcp": {},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
container: docker.Container{
|
||||
Config: &docker.Config{
|
||||
container: docker.ContainerJSON{
|
||||
ContainerJSONBase: &docker.ContainerJSONBase{
|
||||
Name: "container",
|
||||
},
|
||||
Config: &container.Config{
|
||||
Labels: map[string]string{
|
||||
"traefik.frontend.rule": "Host:foo.bar",
|
||||
},
|
||||
},
|
||||
NetworkSettings: &docker.NetworkSettings{
|
||||
Ports: map[docker.Port][]docker.PortBinding{
|
||||
"80/tcp": {},
|
||||
NetworkSettingsBase: docker.NetworkSettingsBase{
|
||||
Ports: nat.PortMap{
|
||||
"80/tcp": {},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -616,26 +712,30 @@ func TestDockerTraefikFilter(t *testing.T) {
|
||||
|
||||
func TestDockerLoadDockerConfig(t *testing.T) {
|
||||
cases := []struct {
|
||||
containers []docker.Container
|
||||
containers []docker.ContainerJSON
|
||||
expectedFrontends map[string]*types.Frontend
|
||||
expectedBackends map[string]*types.Backend
|
||||
}{
|
||||
{
|
||||
containers: []docker.Container{},
|
||||
containers: []docker.ContainerJSON{},
|
||||
expectedFrontends: map[string]*types.Frontend{},
|
||||
expectedBackends: map[string]*types.Backend{},
|
||||
},
|
||||
{
|
||||
containers: []docker.Container{
|
||||
containers: []docker.ContainerJSON{
|
||||
{
|
||||
Name: "test",
|
||||
Config: &docker.Config{},
|
||||
ContainerJSONBase: &docker.ContainerJSONBase{
|
||||
Name: "test",
|
||||
},
|
||||
Config: &container.Config{},
|
||||
NetworkSettings: &docker.NetworkSettings{
|
||||
Ports: map[docker.Port][]docker.PortBinding{
|
||||
"80/tcp": {},
|
||||
NetworkSettingsBase: docker.NetworkSettingsBase{
|
||||
Ports: nat.PortMap{
|
||||
"80/tcp": {},
|
||||
},
|
||||
},
|
||||
Networks: map[string]docker.ContainerNetwork{
|
||||
"bridgde": {
|
||||
Networks: map[string]*network.EndpointSettings{
|
||||
"bridge": {
|
||||
IPAddress: "127.0.0.1",
|
||||
},
|
||||
},
|
||||
@@ -667,38 +767,46 @@ func TestDockerLoadDockerConfig(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
containers: []docker.Container{
|
||||
containers: []docker.ContainerJSON{
|
||||
{
|
||||
Name: "test1",
|
||||
Config: &docker.Config{
|
||||
ContainerJSONBase: &docker.ContainerJSONBase{
|
||||
Name: "test1",
|
||||
},
|
||||
Config: &container.Config{
|
||||
Labels: map[string]string{
|
||||
"traefik.backend": "foobar",
|
||||
"traefik.frontend.entryPoints": "http,https",
|
||||
},
|
||||
},
|
||||
NetworkSettings: &docker.NetworkSettings{
|
||||
Ports: map[docker.Port][]docker.PortBinding{
|
||||
"80/tcp": {},
|
||||
NetworkSettingsBase: docker.NetworkSettingsBase{
|
||||
Ports: nat.PortMap{
|
||||
"80/tcp": {},
|
||||
},
|
||||
},
|
||||
Networks: map[string]docker.ContainerNetwork{
|
||||
"bridgde": {
|
||||
Networks: map[string]*network.EndpointSettings{
|
||||
"bridge": {
|
||||
IPAddress: "127.0.0.1",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "test2",
|
||||
Config: &docker.Config{
|
||||
ContainerJSONBase: &docker.ContainerJSONBase{
|
||||
Name: "test2",
|
||||
},
|
||||
Config: &container.Config{
|
||||
Labels: map[string]string{
|
||||
"traefik.backend": "foobar",
|
||||
},
|
||||
},
|
||||
NetworkSettings: &docker.NetworkSettings{
|
||||
Ports: map[docker.Port][]docker.PortBinding{
|
||||
"80/tcp": {},
|
||||
NetworkSettingsBase: docker.NetworkSettingsBase{
|
||||
Ports: nat.PortMap{
|
||||
"80/tcp": {},
|
||||
},
|
||||
},
|
||||
Networks: map[string]docker.ContainerNetwork{
|
||||
Networks: map[string]*network.EndpointSettings{
|
||||
"bridge": {
|
||||
IPAddress: "127.0.0.1",
|
||||
},
|
||||
|
91
rules.go
91
rules.go
@@ -3,35 +3,82 @@ package main
|
||||
import (
|
||||
"errors"
|
||||
"github.com/gorilla/mux"
|
||||
"net"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Rules holds rule parsing and configuration
|
||||
type Rules struct {
|
||||
route *serverRoute
|
||||
err error
|
||||
}
|
||||
|
||||
func (r *Rules) host(host string) *mux.Route {
|
||||
return r.route.route.Host(host)
|
||||
func (r *Rules) host(hosts ...string) *mux.Route {
|
||||
return r.route.route.MatcherFunc(func(req *http.Request, route *mux.RouteMatch) bool {
|
||||
reqHost, _, err := net.SplitHostPort(req.Host)
|
||||
if err != nil {
|
||||
reqHost = req.Host
|
||||
}
|
||||
for _, host := range hosts {
|
||||
if reqHost == strings.TrimSpace(host) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
})
|
||||
}
|
||||
|
||||
func (r *Rules) path(path string) *mux.Route {
|
||||
return r.route.route.Path(path)
|
||||
func (r *Rules) hostRegexp(hosts ...string) *mux.Route {
|
||||
router := r.route.route.Subrouter()
|
||||
for _, host := range hosts {
|
||||
router.Host(strings.TrimSpace(host))
|
||||
}
|
||||
return r.route.route
|
||||
}
|
||||
|
||||
func (r *Rules) pathPrefix(path string) *mux.Route {
|
||||
return r.route.route.PathPrefix(path)
|
||||
func (r *Rules) path(paths ...string) *mux.Route {
|
||||
router := r.route.route.Subrouter()
|
||||
for _, path := range paths {
|
||||
router.Path(strings.TrimSpace(path))
|
||||
}
|
||||
return r.route.route
|
||||
}
|
||||
|
||||
func (r *Rules) pathStrip(path string) *mux.Route {
|
||||
r.route.stripPrefix = path
|
||||
return r.route.route.Path(path)
|
||||
func (r *Rules) pathPrefix(paths ...string) *mux.Route {
|
||||
router := r.route.route.Subrouter()
|
||||
for _, path := range paths {
|
||||
router.PathPrefix(strings.TrimSpace(path))
|
||||
}
|
||||
return r.route.route
|
||||
}
|
||||
|
||||
func (r *Rules) pathPrefixStrip(path string) *mux.Route {
|
||||
r.route.stripPrefix = path
|
||||
return r.route.route.PathPrefix(path)
|
||||
type bySize []string
|
||||
|
||||
func (a bySize) Len() int { return len(a) }
|
||||
func (a bySize) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
||||
func (a bySize) Less(i, j int) bool { return len(a[i]) > len(a[j]) }
|
||||
|
||||
func (r *Rules) pathStrip(paths ...string) *mux.Route {
|
||||
sort.Sort(bySize(paths))
|
||||
r.route.stripPrefixes = paths
|
||||
router := r.route.route.Subrouter()
|
||||
for _, path := range paths {
|
||||
router.Path(strings.TrimSpace(path))
|
||||
}
|
||||
return r.route.route
|
||||
}
|
||||
|
||||
func (r *Rules) pathPrefixStrip(paths ...string) *mux.Route {
|
||||
sort.Sort(bySize(paths))
|
||||
r.route.stripPrefixes = paths
|
||||
router := r.route.route.Subrouter()
|
||||
for _, path := range paths {
|
||||
router.PathPrefix(strings.TrimSpace(path))
|
||||
}
|
||||
return r.route.route
|
||||
}
|
||||
|
||||
func (r *Rules) methods(methods ...string) *mux.Route {
|
||||
@@ -50,32 +97,33 @@ func (r *Rules) headersRegexp(headers ...string) *mux.Route {
|
||||
func (r *Rules) Parse(expression string) (*mux.Route, error) {
|
||||
functions := map[string]interface{}{
|
||||
"Host": r.host,
|
||||
"HostRegexp": r.hostRegexp,
|
||||
"Path": r.path,
|
||||
"PathStrip": r.pathStrip,
|
||||
"PathPrefix": r.pathPrefix,
|
||||
"PathPrefixStrip": r.pathPrefixStrip,
|
||||
"Methods": r.methods,
|
||||
"Method": r.methods,
|
||||
"Headers": r.headers,
|
||||
"HeadersRegexp": r.headersRegexp,
|
||||
}
|
||||
f := func(c rune) bool {
|
||||
return c == ':' || c == '='
|
||||
return c == ':'
|
||||
}
|
||||
// get function
|
||||
parsedFunctions := strings.FieldsFunc(expression, f)
|
||||
if len(parsedFunctions) != 2 {
|
||||
if len(parsedFunctions) == 0 {
|
||||
return nil, errors.New("Error parsing rule: " + expression)
|
||||
}
|
||||
parsedFunction, ok := functions[parsedFunctions[0]]
|
||||
if !ok {
|
||||
return nil, errors.New("Error parsing rule: " + expression + ". Unknow function: " + parsedFunctions[0])
|
||||
}
|
||||
|
||||
parsedFunctions = append(parsedFunctions[:0], parsedFunctions[1:]...)
|
||||
fargs := func(c rune) bool {
|
||||
return c == ',' || c == ';'
|
||||
}
|
||||
// get function
|
||||
parsedArgs := strings.FieldsFunc(parsedFunctions[1], fargs)
|
||||
parsedArgs := strings.FieldsFunc(strings.Join(parsedFunctions, ":"), fargs)
|
||||
if len(parsedArgs) == 0 {
|
||||
return nil, errors.New("Error parsing args from rule: " + expression)
|
||||
}
|
||||
@@ -86,7 +134,14 @@ func (r *Rules) Parse(expression string) (*mux.Route, error) {
|
||||
}
|
||||
method := reflect.ValueOf(parsedFunction)
|
||||
if method.IsValid() {
|
||||
return method.Call(inputs)[0].Interface().(*mux.Route), nil
|
||||
resultRoute := method.Call(inputs)[0].Interface().(*mux.Route)
|
||||
if r.err != nil {
|
||||
return nil, r.err
|
||||
}
|
||||
if resultRoute.GetError() != nil {
|
||||
return nil, resultRoute.GetError()
|
||||
}
|
||||
return resultRoute, nil
|
||||
}
|
||||
return nil, errors.New("Method not found: " + parsedFunctions[0])
|
||||
}
|
||||
|
@@ -2,35 +2,64 @@
|
||||
set -e
|
||||
|
||||
if ([ "$TRAVIS_BRANCH" = "master" ] || [ ! -z "$TRAVIS_TAG" ]) && [ "$TRAVIS_PULL_REQUEST" = "false" ]; then
|
||||
echo "Deploying"
|
||||
echo "Deploying..."
|
||||
else
|
||||
echo "Skipping deploy"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
curl -LO https://github.com/tcnksm/ghr/releases/download/pre-release/linux_amd64.zip
|
||||
git config --global user.email "emile@vauge.com"
|
||||
git config --global user.name "Emile Vauge"
|
||||
|
||||
# load ssh key
|
||||
echo "Loading key..."
|
||||
openssl aes-256-cbc -d -k "$pass" -in .travis/traefik.id_rsa.enc -out ~/.ssh/traefik.id_rsa
|
||||
eval "$(ssh-agent -s)"
|
||||
chmod 600 ~/.ssh/traefik.id_rsa
|
||||
ssh-add ~/.ssh/traefik.id_rsa
|
||||
|
||||
# download github release
|
||||
echo "Downloading ghr..."
|
||||
curl -LOs https://github.com/tcnksm/ghr/releases/download/pre-release/linux_amd64.zip
|
||||
unzip -q linux_amd64.zip
|
||||
sudo mv ghr /usr/bin/ghr
|
||||
sudo chmod +x /usr/bin/ghr
|
||||
|
||||
# github release and tag
|
||||
echo "Github release..."
|
||||
ghr -t $GITHUB_TOKEN -u containous -r traefik --prerelease ${VERSION} dist/
|
||||
|
||||
# update docs.traefik.io
|
||||
mkdocs gh-deploy --clean
|
||||
echo "Generating and updating documentation..."
|
||||
# DOESN'T WORK :'(
|
||||
# git remote add ssh git@github.com:containous/traefik.git
|
||||
# mkdocs gh-deploy -m $VERSION -c -r ssh
|
||||
|
||||
mkdir site
|
||||
cd site
|
||||
git init
|
||||
git remote add origin git@github.com:containous/traefik.git
|
||||
git fetch origin
|
||||
git checkout gh-pages
|
||||
cd ..
|
||||
mkdocs build --clean
|
||||
cd site
|
||||
git add .
|
||||
echo $VERSION | git commit --file -
|
||||
git push -q -f origin gh-pages > /dev/null 2>&1
|
||||
|
||||
# update traefik-library-image repo (official Docker image)
|
||||
git config --global user.email "emile@vauge.com"
|
||||
git config --global user.name "Emile Vauge"
|
||||
echo "Updating traefik-library-imag repo..."
|
||||
git clone git@github.com:containous/traefik-library-image.git
|
||||
cd traefik-library-image
|
||||
./update.sh $VERSION
|
||||
git add -A
|
||||
echo $VERSION | git commit --file -
|
||||
echo $VERSION | git tag -a $VERSION --file -
|
||||
git push -q --follow-tags -u origin master
|
||||
git push -q --follow-tags -u origin master > /dev/null 2>&1
|
||||
|
||||
# create docker image emilevauge/traefik (compatibility)
|
||||
echo "Updating docker emilevauge/traefik image..."
|
||||
docker login -e $DOCKER_EMAIL -u $DOCKER_USER -p $DOCKER_PASS
|
||||
docker tag containous/traefik emilevauge/traefik:latest
|
||||
docker push emilevauge/traefik:latest
|
||||
|
12
server.go
12
server.go
@@ -57,8 +57,8 @@ type serverEntryPoint struct {
|
||||
}
|
||||
|
||||
type serverRoute struct {
|
||||
route *mux.Route
|
||||
stripPrefix string
|
||||
route *mux.Route
|
||||
stripPrefixes []string
|
||||
}
|
||||
|
||||
// NewServer returns an initialized Server.
|
||||
@@ -425,7 +425,7 @@ func (server *Server) loadConfig(configurations configs, globalConfiguration Glo
|
||||
}
|
||||
// retry ?
|
||||
if globalConfiguration.Retry != nil {
|
||||
retries := len(configuration.Backends[frontend.Backend].Servers) - 1
|
||||
retries := len(configuration.Backends[frontend.Backend].Servers)
|
||||
if globalConfiguration.Retry.Attempts > 0 {
|
||||
retries = globalConfiguration.Retry.Attempts
|
||||
}
|
||||
@@ -471,10 +471,10 @@ func (server *Server) loadConfig(configurations configs, globalConfiguration Glo
|
||||
|
||||
func (server *Server) wireFrontendBackend(serverRoute *serverRoute, handler http.Handler) {
|
||||
// strip prefix
|
||||
if len(serverRoute.stripPrefix) > 0 {
|
||||
if len(serverRoute.stripPrefixes) > 0 {
|
||||
serverRoute.route.Handler(&middlewares.StripPrefix{
|
||||
Prefix: serverRoute.stripPrefix,
|
||||
Handler: handler,
|
||||
Prefixes: serverRoute.stripPrefixes,
|
||||
Handler: handler,
|
||||
})
|
||||
} else {
|
||||
serverRoute.route.Handler(handler)
|
||||
|
@@ -513,7 +513,7 @@
|
||||
# [frontends.frontend1]
|
||||
# backend = "backend2"
|
||||
# [frontends.frontend1.routes.test_1]
|
||||
# rule = "Host:test.localhost"
|
||||
# rule = "Host: test.localhost, other.localhost"
|
||||
# [frontends.frontend2]
|
||||
# backend = "backend1"
|
||||
# passHostHeader = true
|
||||
@@ -523,4 +523,4 @@
|
||||
# [frontends.frontend3]
|
||||
# entrypoints = ["http", "https"] # overrides defaultEntryPoints
|
||||
# backend = "backend2"
|
||||
# rule = "Path:/test"
|
||||
# rule = "Path: /test, /other"
|
@@ -24,3 +24,11 @@
|
||||
.tabset-row__providers {
|
||||
margin-top: 3rem;
|
||||
}
|
||||
|
||||
table {
|
||||
table-layout: fixed;
|
||||
}
|
||||
|
||||
td, th {
|
||||
word-wrap: break-word;
|
||||
}
|
Reference in New Issue
Block a user