forked from Ivasoft/traefik
Compare commits
12 Commits
v1.0.alpha
...
v1.0.alpha
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6c3c5578c6 | ||
|
|
122783e36b | ||
|
|
b84b95fe97 | ||
|
|
a99010b8c2 | ||
|
|
8954aa7118 | ||
|
|
3cf848958f | ||
|
|
1a5668377c | ||
|
|
dc10c56b35 | ||
|
|
331cd173ce | ||
|
|
1881d5eeed | ||
|
|
e0872b6157 | ||
|
|
63fb9c7135 |
6
.github/CONTRIBUTING.md
vendored
6
.github/CONTRIBUTING.md
vendored
@@ -23,7 +23,7 @@ Step 0 : FROM golang:1.5
|
||||
---> 8c6473912976
|
||||
Step 1 : RUN go get github.com/Masterminds/glide
|
||||
[...]
|
||||
docker run --rm -v "/var/run/docker.sock:/var/run/docker.sock" -it -e OS_ARCH_ARG -e OS_PLATFORM_ARG -e TESTFLAGS -v "/home/emile/dev/go/src/github.com/emilevauge/traefik/"dist":/go/src/github.com/emilevauge/traefik/"dist"" "traefik-dev:no-more-godep-ever" ./script/make.sh generate binary
|
||||
docker run --rm -v "/var/run/docker.sock:/var/run/docker.sock" -it -e OS_ARCH_ARG -e OS_PLATFORM_ARG -e TESTFLAGS -v "/home/emile/dev/go/src/github.com/containous/traefik/"dist":/go/src/github.com/containous/traefik/"dist"" "traefik-dev:no-more-godep-ever" ./script/make.sh generate binary
|
||||
---> Making bundle: generate (in .)
|
||||
removed 'gen.go'
|
||||
|
||||
@@ -66,13 +66,13 @@ integration test using the `test-integration` target.
|
||||
$ make test-unit
|
||||
docker build -t "traefik-dev:your-feature-branch" -f build.Dockerfile .
|
||||
# […]
|
||||
docker run --rm -it -e OS_ARCH_ARG -e OS_PLATFORM_ARG -e TESTFLAGS -v "/home/vincent/src/github/vdemeester/traefik/dist:/go/src/github.com/emilevauge/traefik/dist" "traefik-dev:your-feature-branch" ./script/make.sh generate test-unit
|
||||
docker run --rm -it -e OS_ARCH_ARG -e OS_PLATFORM_ARG -e TESTFLAGS -v "/home/vincent/src/github/vdemeester/traefik/dist:/go/src/github.com/containous/traefik/dist" "traefik-dev:your-feature-branch" ./script/make.sh generate test-unit
|
||||
---> Making bundle: generate (in .)
|
||||
removed 'gen.go'
|
||||
|
||||
---> Making bundle: test-unit (in .)
|
||||
+ go test -cover -coverprofile=cover.out .
|
||||
ok github.com/emilevauge/traefik 0.005s coverage: 4.1% of statements
|
||||
ok github.com/containous/traefik 0.005s coverage: 4.1% of statements
|
||||
|
||||
Test success
|
||||
```
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 Emile Vauge, emile@vauge.com
|
||||
Copyright (c) 2016 Containous SAS, Emile Vauge, emile@vauge.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
THE SOFTWARE.
|
||||
|
||||
4
Makefile
4
Makefile
@@ -9,12 +9,12 @@ TRAEFIK_ENVS := \
|
||||
SRCS = $(shell git ls-files '*.go' | grep -v '^external/')
|
||||
|
||||
BIND_DIR := "dist"
|
||||
TRAEFIK_MOUNT := -v "$(CURDIR)/$(BIND_DIR):/go/src/github.com/emilevauge/traefik/$(BIND_DIR)"
|
||||
TRAEFIK_MOUNT := -v "$(CURDIR)/$(BIND_DIR):/go/src/github.com/containous/traefik/$(BIND_DIR)"
|
||||
|
||||
GIT_BRANCH := $(shell git rev-parse --abbrev-ref HEAD 2>/dev/null)
|
||||
TRAEFIK_DEV_IMAGE := traefik-dev$(if $(GIT_BRANCH),:$(GIT_BRANCH))
|
||||
REPONAME := $(shell echo $(REPO) | tr '[:upper:]' '[:lower:]')
|
||||
TRAEFIK_IMAGE := $(if $(REPONAME),$(REPONAME),"emilevauge/traefik")
|
||||
TRAEFIK_IMAGE := $(if $(REPONAME),$(REPONAME),"containous/traefik")
|
||||
INTEGRATION_OPTS := $(if $(MAKE_DOCKER_HOST),-e "DOCKER_HOST=$(MAKE_DOCKER_HOST)", -v "/var/run/docker.sock:/var/run/docker.sock")
|
||||
|
||||
DOCKER_RUN_TRAEFIK := docker run $(INTEGRATION_OPTS) -it $(TRAEFIK_ENVS) $(TRAEFIK_MOUNT) "$(TRAEFIK_DEV_IMAGE)"
|
||||
|
||||
13
README.md
13
README.md
@@ -1,8 +1,8 @@
|
||||

|
||||
___
|
||||
|
||||
[](https://travis-ci.org/emilevauge/traefik)
|
||||
[](https://github.com/EmileVauge/traefik/blob/master/LICENSE.md)
|
||||
[](https://travis-ci.org/containous/traefik)
|
||||
[](https://github.com/containous/traefik/blob/master/LICENSE.md)
|
||||
[](https://traefik.herokuapp.com)
|
||||
[](https://twitter.com/intent/follow?screen_name=traefikproxy)
|
||||
|
||||
@@ -24,7 +24,7 @@ It supports several backends ([Docker :whale:](https://www.docker.com/), [Mesos/
|
||||
- Circuit breakers on backends
|
||||
- Round Robin, rebalancer load-balancers
|
||||
- Rest Metrics
|
||||
- Tiny docker image included [](https://imagelayers.io/?images=emilevauge/traefik:latest 'Image Layers')
|
||||
- Tiny docker image included [](https://imagelayers.io/?images=containous/traefik:latest)
|
||||
- SSL backends support
|
||||
- SSL frontend support
|
||||
- Clean AngularJS Web UI
|
||||
@@ -53,7 +53,7 @@ You can access to a simple HTML frontend of Træfik.
|
||||
|
||||
## Quick start
|
||||
|
||||
- The simple way: grab the latest binary from the [releases](https://github.com/emilevauge/traefik/releases) page and just run it with the [sample configuration file](https://raw.githubusercontent.com/EmileVauge/traefik/master/traefik.sample.toml):
|
||||
- The simple way: grab the latest binary from the [releases](https://github.com/containous/traefik/releases) page and just run it with the [sample configuration file](https://raw.githubusercontent.com/containous/traefik/master/traefik.sample.toml):
|
||||
|
||||
```shell
|
||||
./traefik -c traefik.toml
|
||||
@@ -62,13 +62,13 @@ 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 emilevauge/traefik
|
||||
docker run -d -p 8080:8080 -p 80:80 -v $PWD/traefik.toml:/etc/traefik/traefik.toml containous/traefik
|
||||
```
|
||||
|
||||
- From sources:
|
||||
|
||||
```shell
|
||||
git clone https://github.com/emilevauge/traefik
|
||||
git clone https://github.com/containous/traefik
|
||||
```
|
||||
|
||||
## Documentation
|
||||
@@ -106,3 +106,4 @@ software products.
|
||||
[](https://aster.is)
|
||||
|
||||
Founded in 2014, Asteris creates next-generation infrastructure software for the modern datacenter. Asteris writes software that makes it easy for companies to implement continuous delivery and realtime data pipelines. We support the HashiCorp stack, along with Kubernetes, Apache Mesos, Spark and Kafka. We're core committers on mantl.io, consul-cli and mesos-consul.
|
||||
.
|
||||
|
||||
@@ -21,9 +21,9 @@ RUN set -ex; \
|
||||
# Set the default Docker to be run
|
||||
RUN ln -s /usr/local/bin/docker-${DOCKER_VERSION} /usr/local/bin/docker
|
||||
|
||||
WORKDIR /go/src/github.com/emilevauge/traefik
|
||||
WORKDIR /go/src/github.com/containous/traefik
|
||||
|
||||
COPY glide.yaml glide.yaml
|
||||
RUN glide up --quick
|
||||
|
||||
COPY . /go/src/github.com/emilevauge/traefik
|
||||
COPY . /go/src/github.com/containous/traefik
|
||||
|
||||
44
cmd.go
44
cmd.go
@@ -11,8 +11,8 @@ import (
|
||||
"time"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/emilevauge/traefik/middlewares"
|
||||
"github.com/emilevauge/traefik/provider"
|
||||
"github.com/containous/traefik/middlewares"
|
||||
"github.com/containous/traefik/provider"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
"net/http"
|
||||
@@ -45,9 +45,11 @@ var arguments = struct {
|
||||
dockerTLS bool
|
||||
marathon bool
|
||||
consul bool
|
||||
consulTLS bool
|
||||
consulCatalog bool
|
||||
zookeeper bool
|
||||
etcd bool
|
||||
etcdTLS bool
|
||||
boltdb bool
|
||||
}{
|
||||
GlobalConfiguration{
|
||||
@@ -55,14 +57,22 @@ var arguments = struct {
|
||||
Docker: &provider.Docker{
|
||||
TLS: &provider.DockerTLS{},
|
||||
},
|
||||
File: &provider.File{},
|
||||
Web: &WebProvider{},
|
||||
Marathon: &provider.Marathon{},
|
||||
Consul: &provider.Consul{},
|
||||
File: &provider.File{},
|
||||
Web: &WebProvider{},
|
||||
Marathon: &provider.Marathon{},
|
||||
Consul: &provider.Consul{
|
||||
Kv: provider.Kv{
|
||||
TLS: &provider.KvTLS{},
|
||||
},
|
||||
},
|
||||
ConsulCatalog: &provider.ConsulCatalog{},
|
||||
Zookeeper: &provider.Zookepper{},
|
||||
Etcd: &provider.Etcd{},
|
||||
Boltdb: &provider.BoltDb{},
|
||||
Etcd: &provider.Etcd{
|
||||
Kv: provider.Kv{
|
||||
TLS: &provider.KvTLS{},
|
||||
},
|
||||
},
|
||||
Boltdb: &provider.BoltDb{},
|
||||
},
|
||||
false,
|
||||
false,
|
||||
@@ -74,6 +84,8 @@ var arguments = struct {
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
}
|
||||
|
||||
func init() {
|
||||
@@ -119,8 +131,13 @@ func init() {
|
||||
traefikCmd.PersistentFlags().BoolVar(&arguments.consul, "consul", false, "Enable Consul backend")
|
||||
traefikCmd.PersistentFlags().BoolVar(&arguments.Consul.Watch, "consul.watch", true, "Watch provider")
|
||||
traefikCmd.PersistentFlags().StringVar(&arguments.Consul.Filename, "consul.filename", "", "Override default configuration template. For advanced users :)")
|
||||
traefikCmd.PersistentFlags().StringVar(&arguments.Consul.Endpoint, "consul.endpoint", "127.0.0.1:8500", "Consul server endpoint")
|
||||
traefikCmd.PersistentFlags().StringVar(&arguments.Consul.Endpoint, "consul.endpoint", "127.0.0.1:8500", "Comma sepparated Consul server endpoints")
|
||||
traefikCmd.PersistentFlags().StringVar(&arguments.Consul.Prefix, "consul.prefix", "/traefik", "Prefix used for KV store")
|
||||
traefikCmd.PersistentFlags().BoolVar(&arguments.consulTLS, "consul.tls", false, "Enable Consul TLS support")
|
||||
traefikCmd.PersistentFlags().StringVar(&arguments.Consul.TLS.CA, "consul.tls.ca", "", "TLS CA")
|
||||
traefikCmd.PersistentFlags().StringVar(&arguments.Consul.TLS.Cert, "consul.tls.cert", "", "TLS cert")
|
||||
traefikCmd.PersistentFlags().StringVar(&arguments.Consul.TLS.Key, "consul.tls.key", "", "TLS key")
|
||||
traefikCmd.PersistentFlags().BoolVar(&arguments.Consul.TLS.InsecureSkipVerify, "consul.tls.insecureSkipVerify", false, "TLS insecure skip verify")
|
||||
|
||||
traefikCmd.PersistentFlags().BoolVar(&arguments.consulCatalog, "consulCatalog", false, "Enable Consul catalog backend")
|
||||
traefikCmd.PersistentFlags().StringVar(&arguments.ConsulCatalog.Domain, "consulCatalog.domain", "", "Default domain used")
|
||||
@@ -129,14 +146,19 @@ func init() {
|
||||
traefikCmd.PersistentFlags().BoolVar(&arguments.zookeeper, "zookeeper", false, "Enable Zookeeper backend")
|
||||
traefikCmd.PersistentFlags().BoolVar(&arguments.Zookeeper.Watch, "zookeeper.watch", true, "Watch provider")
|
||||
traefikCmd.PersistentFlags().StringVar(&arguments.Zookeeper.Filename, "zookeeper.filename", "", "Override default configuration template. For advanced users :)")
|
||||
traefikCmd.PersistentFlags().StringVar(&arguments.Zookeeper.Endpoint, "zookeeper.endpoint", "127.0.0.1:2181", "Zookeeper server endpoint")
|
||||
traefikCmd.PersistentFlags().StringVar(&arguments.Zookeeper.Endpoint, "zookeeper.endpoint", "127.0.0.1:2181", "Comma sepparated Zookeeper server endpoints")
|
||||
traefikCmd.PersistentFlags().StringVar(&arguments.Zookeeper.Prefix, "zookeeper.prefix", "/traefik", "Prefix used for KV store")
|
||||
|
||||
traefikCmd.PersistentFlags().BoolVar(&arguments.etcd, "etcd", false, "Enable Etcd backend")
|
||||
traefikCmd.PersistentFlags().BoolVar(&arguments.Etcd.Watch, "etcd.watch", true, "Watch provider")
|
||||
traefikCmd.PersistentFlags().StringVar(&arguments.Etcd.Filename, "etcd.filename", "", "Override default configuration template. For advanced users :)")
|
||||
traefikCmd.PersistentFlags().StringVar(&arguments.Etcd.Endpoint, "etcd.endpoint", "127.0.0.1:4001", "Etcd server endpoint")
|
||||
traefikCmd.PersistentFlags().StringVar(&arguments.Etcd.Endpoint, "etcd.endpoint", "127.0.0.1:4001", "Comma sepparated Etcd server endpoints")
|
||||
traefikCmd.PersistentFlags().StringVar(&arguments.Etcd.Prefix, "etcd.prefix", "/traefik", "Prefix used for KV store")
|
||||
traefikCmd.PersistentFlags().BoolVar(&arguments.etcdTLS, "etcd.tls", false, "Enable Etcd TLS support")
|
||||
traefikCmd.PersistentFlags().StringVar(&arguments.Etcd.TLS.CA, "etcd.tls.ca", "", "TLS CA")
|
||||
traefikCmd.PersistentFlags().StringVar(&arguments.Etcd.TLS.Cert, "etcd.tls.cert", "", "TLS cert")
|
||||
traefikCmd.PersistentFlags().StringVar(&arguments.Etcd.TLS.Key, "etcd.tls.key", "", "TLS key")
|
||||
traefikCmd.PersistentFlags().BoolVar(&arguments.Etcd.TLS.InsecureSkipVerify, "etcd.tls.insecureSkipVerify", false, "TLS insecure skip verify")
|
||||
|
||||
traefikCmd.PersistentFlags().BoolVar(&arguments.boltdb, "boltdb", false, "Enable Boltdb backend")
|
||||
traefikCmd.PersistentFlags().BoolVar(&arguments.Boltdb.Watch, "boltdb.watch", true, "Watch provider")
|
||||
|
||||
@@ -8,8 +8,8 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/emilevauge/traefik/provider"
|
||||
"github.com/emilevauge/traefik/types"
|
||||
"github.com/containous/traefik/provider"
|
||||
"github.com/containous/traefik/types"
|
||||
"github.com/mitchellh/mapstructure"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
@@ -222,6 +222,9 @@ func LoadConfiguration() *GlobalConfiguration {
|
||||
if arguments.marathon {
|
||||
viper.Set("marathon", arguments.Marathon)
|
||||
}
|
||||
if !arguments.consulTLS {
|
||||
arguments.Consul.TLS = nil
|
||||
}
|
||||
if arguments.consul {
|
||||
viper.Set("consul", arguments.Consul)
|
||||
}
|
||||
@@ -231,6 +234,9 @@ func LoadConfiguration() *GlobalConfiguration {
|
||||
if arguments.zookeeper {
|
||||
viper.Set("zookeeper", arguments.Zookeeper)
|
||||
}
|
||||
if !arguments.etcdTLS {
|
||||
arguments.Etcd.TLS = nil
|
||||
}
|
||||
if arguments.etcd {
|
||||
viper.Set("etcd", arguments.Etcd)
|
||||
}
|
||||
|
||||
@@ -37,7 +37,9 @@ Frontends can be defined using the following rules:
|
||||
- `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]+}`
|
||||
- `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.
|
||||
- `PathPrefixStrip`: Same as `PathPrefix` but strip the given prefix from the request URL's Path.
|
||||
|
||||
|
||||
A frontend is a set of rules that forwards the incoming http traffic to a backend.
|
||||
@@ -106,9 +108,14 @@ Flags:
|
||||
--boltdb.watch Watch provider (default true)
|
||||
-c, --configFile string Configuration file to use (TOML, JSON, YAML, HCL).
|
||||
--consul Enable Consul backend
|
||||
--consul.endpoint string Consul server endpoint (default "127.0.0.1:8500")
|
||||
--consul.endpoint string Comma sepparated Consul server endpoints (default "127.0.0.1:8500")
|
||||
--consul.filename string Override default configuration template. For advanced users :)
|
||||
--consul.prefix string Prefix used for KV store (default "/traefik")
|
||||
--consul.tls Enable Consul TLS support
|
||||
--consul.tls.ca string TLS CA
|
||||
--consul.tls.cert string TLS cert
|
||||
--consul.tls.insecureSkipVerify TLS insecure skip verify
|
||||
--consul.tls.key string TLS key
|
||||
--consul.watch Watch provider (default true)
|
||||
--consulCatalog Enable Consul catalog backend
|
||||
--consulCatalog.domain string Default domain used
|
||||
@@ -126,9 +133,14 @@ Flags:
|
||||
--docker.watch Watch provider (default true)
|
||||
--entryPoints value Entrypoints definition using format: --entryPoints='Name:http Address::8000 Redirect.EntryPoint:https' --entryPoints='Name:https Address::4442 TLS:tests/traefik.crt,tests/traefik.key'
|
||||
--etcd Enable Etcd backend
|
||||
--etcd.endpoint string Etcd server endpoint (default "127.0.0.1:4001")
|
||||
--etcd.endpoint string Comma sepparated Etcd server endpoints (default "127.0.0.1:4001")
|
||||
--etcd.filename string Override default configuration template. For advanced users :)
|
||||
--etcd.prefix string Prefix used for KV store (default "/traefik")
|
||||
--etcd.tls Enable Etcd TLS support
|
||||
--etcd.tls.ca string TLS CA
|
||||
--etcd.tls.cert string TLS cert
|
||||
--etcd.tls.insecureSkipVerify TLS insecure skip verify
|
||||
--etcd.tls.key string TLS key
|
||||
--etcd.watch Watch provider (default true)
|
||||
--file Enable File backend
|
||||
--file.filename string Override default configuration template. For advanced users :)
|
||||
@@ -142,7 +154,6 @@ Flags:
|
||||
--marathon.networkInterface string Network interface used to call Marathon web services. Needed in case of multiple network interfaces (default "eth0")
|
||||
--marathon.watch Watch provider (default true)
|
||||
--maxIdleConnsPerHost int If non-zero, controls the maximum idle (keep-alive) to keep per-host. If zero, DefaultMaxIdleConnsPerHost is used
|
||||
-p, --port string Reverse proxy port (default ":80")
|
||||
--providersThrottleDuration duration Backends throttle duration: minimum duration between 2 events from providers before applying a new configuration. It avoids unnecessary reloads if multiples events are sent in a short amount of time. (default 2s)
|
||||
--traefikLogsFile string Traefik logs file (default "log/traefik.log")
|
||||
--web Enable Web backend
|
||||
@@ -151,7 +162,7 @@ Flags:
|
||||
--web.keyFile string SSL certificate
|
||||
--web.readOnly Enable read only API
|
||||
--zookeeper Enable Zookeeper backend
|
||||
--zookeeper.endpoint string Zookeeper server endpoint (default "127.0.0.1:2181")
|
||||
--zookeeper.endpoint string Comma sepparated Zookeeper server endpoints (default "127.0.0.1:2181")
|
||||
--zookeeper.filename string Override default configuration template. For advanced users :)
|
||||
--zookeeper.prefix string Prefix used for KV store (default "/traefik")
|
||||
--zookeeper.watch Watch provider (default true)
|
||||
@@ -722,6 +733,16 @@ prefix = "traefik"
|
||||
# Optional
|
||||
#
|
||||
# filename = "consul.tmpl"
|
||||
|
||||
# Enable consul TLS connection
|
||||
#
|
||||
# Optional
|
||||
#
|
||||
# [consul.tls]
|
||||
# ca = "/etc/ssl/ca.crt"
|
||||
# cert = "/etc/ssl/consul.crt"
|
||||
# key = "/etc/ssl/consul.key"
|
||||
# insecureskipverify = true
|
||||
```
|
||||
|
||||
The Keys-Values structure should look (using `prefix = "/traefik"`):
|
||||
@@ -803,6 +824,16 @@ Træfɪk can be configured to use Etcd as a backend configuration:
|
||||
# Optional
|
||||
#
|
||||
# filename = "etcd.tmpl"
|
||||
|
||||
# Enable etcd TLS connection
|
||||
#
|
||||
# Optional
|
||||
#
|
||||
# [etcd.tls]
|
||||
# ca = "/etc/ssl/ca.crt"
|
||||
# cert = "/etc/ssl/etcd.crt"
|
||||
# key = "/etc/ssl/etcd.key"
|
||||
# insecureskipverify = true
|
||||
```
|
||||
|
||||
The Keys-Values structure should look (using `prefix = "/traefik"`):
|
||||
@@ -1107,7 +1138,7 @@ Percentage of the requests served within a certain time (ms)
|
||||
```sh
|
||||
docker run -d -l traefik.backend=test1 -l traefik.frontend.rule=Host -l traefik.frontend.value=test1.docker.localhost emilevauge/whoami
|
||||
docker run -d -l traefik.backend=test1 -l traefik.frontend.rule=Host -l traefik.frontend.value=test1.docker.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 emilevauge/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 containous/traefik
|
||||
$ ab -n 20000 -c 20 -r http://test1.docker.localhost/
|
||||
This is ApacheBench, Version 2.3 <$Revision: 1528965 $>
|
||||
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
|
||||
|
||||
27
integration/etcd_test.go
Normal file
27
integration/etcd_test.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"os/exec"
|
||||
"time"
|
||||
|
||||
"fmt"
|
||||
checker "github.com/vdemeester/shakers"
|
||||
check "gopkg.in/check.v1"
|
||||
)
|
||||
|
||||
func (s *EtcdSuite) TestSimpleConfiguration(c *check.C) {
|
||||
cmd := exec.Command(traefikBinary, "--configFile=fixtures/etcd/simple.toml")
|
||||
err := cmd.Start()
|
||||
c.Assert(err, checker.IsNil)
|
||||
defer cmd.Process.Kill()
|
||||
|
||||
time.Sleep(1000 * time.Millisecond)
|
||||
// TODO validate : run on 80
|
||||
resp, err := http.Get("http://127.0.0.1:8000/")
|
||||
|
||||
// Expected no response as we did not configure anything
|
||||
c.Assert(resp, checker.IsNil)
|
||||
c.Assert(err, checker.NotNil)
|
||||
c.Assert(err.Error(), checker.Contains, fmt.Sprintf("getsockopt: connection refused"))
|
||||
}
|
||||
10
integration/fixtures/etcd/simple.toml
Normal file
10
integration/fixtures/etcd/simple.toml
Normal file
@@ -0,0 +1,10 @@
|
||||
defaultEntryPoints = ["http"]
|
||||
|
||||
[entryPoints]
|
||||
[entryPoints.http]
|
||||
address = ":8000"
|
||||
|
||||
logLevel = "DEBUG"
|
||||
|
||||
[etcd]
|
||||
endpoint = "127.0.0.1:4003,127.0.0.1:4002,127.0.0.1:4001"
|
||||
@@ -10,9 +10,9 @@ import (
|
||||
"testing"
|
||||
"text/template"
|
||||
|
||||
"github.com/containous/traefik/integration/utils"
|
||||
"github.com/docker/libcompose/docker"
|
||||
"github.com/docker/libcompose/project"
|
||||
"github.com/emilevauge/traefik/integration/utils"
|
||||
|
||||
checker "github.com/vdemeester/shakers"
|
||||
check "gopkg.in/check.v1"
|
||||
@@ -29,6 +29,7 @@ func init() {
|
||||
check.Suite(&DockerSuite{})
|
||||
check.Suite(&ConsulSuite{})
|
||||
check.Suite(&ConsulCatalogSuite{})
|
||||
check.Suite(&EtcdSuite{})
|
||||
check.Suite(&MarathonSuite{})
|
||||
}
|
||||
|
||||
@@ -50,6 +51,13 @@ func (s *ConsulSuite) SetUpSuite(c *check.C) {
|
||||
s.createComposeProject(c, "consul")
|
||||
}
|
||||
|
||||
// Etcd test suites (using libcompose)
|
||||
type EtcdSuite struct{ BaseSuite }
|
||||
|
||||
func (s *EtcdSuite) SetUpSuite(c *check.C) {
|
||||
s.createComposeProject(c, "etcd")
|
||||
}
|
||||
|
||||
// Marathon test suites (using libcompose)
|
||||
type MarathonSuite struct{ BaseSuite }
|
||||
|
||||
|
||||
30
integration/resources/compose/etcd.yml
Normal file
30
integration/resources/compose/etcd.yml
Normal file
@@ -0,0 +1,30 @@
|
||||
etcd1:
|
||||
image: quay.io/coreos/etcd:v2.2.0
|
||||
net: "host"
|
||||
command: >
|
||||
--name etcd1
|
||||
--listen-peer-urls http://localhost:7001
|
||||
--listen-client-urls http://localhost:4001
|
||||
--initial-advertise-peer-urls http://localhost:7001
|
||||
--advertise-client-urls http://localhost:4001
|
||||
--initial-cluster etcd1=http://localhost:7001,etcd2=http://localhost:7002,etcd3=http://localhost:7003
|
||||
etcd2:
|
||||
image: quay.io/coreos/etcd:v2.2.0
|
||||
net: "host"
|
||||
command: >
|
||||
--name etcd2
|
||||
--listen-peer-urls http://localhost:7002
|
||||
--listen-client-urls http://localhost:4002
|
||||
--initial-advertise-peer-urls http://localhost:7002
|
||||
--advertise-client-urls http://localhost:4002
|
||||
--initial-cluster etcd1=http://localhost:7001,etcd2=http://localhost:7002,etcd3=http://localhost:7003
|
||||
etcd3:
|
||||
image: quay.io/coreos/etcd:v2.2.0
|
||||
net: "host"
|
||||
command: >
|
||||
--name etcd3
|
||||
--listen-peer-urls http://localhost:7003
|
||||
--listen-client-urls http://localhost:4003
|
||||
--initial-advertise-peer-urls http://localhost:7003
|
||||
--advertise-client-urls http://localhost:4003
|
||||
--initial-cluster etcd1=http://localhost:7001,etcd2=http://localhost:7002,etcd3=http://localhost:7003
|
||||
22
middlewares/StripPrefix.go
Normal file
22
middlewares/StripPrefix.go
Normal file
@@ -0,0 +1,22 @@
|
||||
package middlewares
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// StripPrefix is a middleware used to strip prefix from an URL request
|
||||
type StripPrefix struct {
|
||||
Handler http.Handler
|
||||
Prefix 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)
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
package provider
|
||||
|
||||
import (
|
||||
"github.com/containous/traefik/types"
|
||||
"github.com/docker/libkv/store"
|
||||
"github.com/docker/libkv/store/boltdb"
|
||||
"github.com/emilevauge/traefik/types"
|
||||
)
|
||||
|
||||
// BoltDb holds configurations of the BoltDb provider.
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package provider
|
||||
|
||||
import (
|
||||
"github.com/containous/traefik/types"
|
||||
"github.com/docker/libkv/store"
|
||||
"github.com/docker/libkv/store/consul"
|
||||
"github.com/emilevauge/traefik/types"
|
||||
)
|
||||
|
||||
// Consul holds configurations of the Consul provider.
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/cenkalti/backoff"
|
||||
"github.com/emilevauge/traefik/types"
|
||||
"github.com/containous/traefik/types"
|
||||
"github.com/hashicorp/consul/api"
|
||||
)
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/emilevauge/traefik/types"
|
||||
"github.com/containous/traefik/types"
|
||||
"github.com/hashicorp/consul/api"
|
||||
)
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
"github.com/BurntSushi/ty/fun"
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/cenkalti/backoff"
|
||||
"github.com/emilevauge/traefik/types"
|
||||
"github.com/containous/traefik/types"
|
||||
"github.com/fsouza/go-dockerclient"
|
||||
)
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/emilevauge/traefik/types"
|
||||
"github.com/containous/traefik/types"
|
||||
"github.com/fsouza/go-dockerclient"
|
||||
)
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package provider
|
||||
|
||||
import (
|
||||
"github.com/containous/traefik/types"
|
||||
"github.com/docker/libkv/store"
|
||||
"github.com/docker/libkv/store/etcd"
|
||||
"github.com/emilevauge/traefik/types"
|
||||
)
|
||||
|
||||
// Etcd holds configurations of the Etcd provider.
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
|
||||
"github.com/BurntSushi/toml"
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/emilevauge/traefik/types"
|
||||
"github.com/containous/traefik/types"
|
||||
"gopkg.in/fsnotify.v1"
|
||||
)
|
||||
|
||||
|
||||
@@ -2,15 +2,19 @@
|
||||
package provider
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
"text/template"
|
||||
"time"
|
||||
|
||||
"github.com/BurntSushi/ty/fun"
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/containous/traefik/types"
|
||||
"github.com/docker/libkv"
|
||||
"github.com/docker/libkv/store"
|
||||
"github.com/emilevauge/traefik/types"
|
||||
)
|
||||
|
||||
// Kv holds common configurations of key-value providers.
|
||||
@@ -18,18 +22,55 @@ type Kv struct {
|
||||
BaseProvider `mapstructure:",squash"`
|
||||
Endpoint string
|
||||
Prefix string
|
||||
TLS *KvTLS
|
||||
storeType store.Backend
|
||||
kvclient store.Store
|
||||
}
|
||||
|
||||
// KvTLS holds TLS specific configurations
|
||||
type KvTLS struct {
|
||||
CA string
|
||||
Cert string
|
||||
Key string
|
||||
InsecureSkipVerify bool
|
||||
}
|
||||
|
||||
func (provider *Kv) provide(configurationChan chan<- types.ConfigMessage) error {
|
||||
storeConfig := &store.Config{
|
||||
ConnectionTimeout: 30 * time.Second,
|
||||
Bucket: "traefik",
|
||||
}
|
||||
|
||||
if provider.TLS != nil {
|
||||
caPool := x509.NewCertPool()
|
||||
|
||||
if provider.TLS.CA != "" {
|
||||
ca, err := ioutil.ReadFile(provider.TLS.CA)
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to read CA. %s", err)
|
||||
}
|
||||
|
||||
caPool.AppendCertsFromPEM(ca)
|
||||
}
|
||||
|
||||
cert, err := tls.LoadX509KeyPair(provider.TLS.Cert, provider.TLS.Key)
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to load keypair. %s", err)
|
||||
}
|
||||
|
||||
storeConfig.TLS = &tls.Config{
|
||||
Certificates: []tls.Certificate{cert},
|
||||
RootCAs: caPool,
|
||||
InsecureSkipVerify: provider.TLS.InsecureSkipVerify,
|
||||
}
|
||||
}
|
||||
|
||||
kv, err := libkv.NewStore(
|
||||
provider.storeType,
|
||||
[]string{provider.Endpoint},
|
||||
&store.Config{
|
||||
ConnectionTimeout: 30 * time.Second,
|
||||
Bucket: "traefik",
|
||||
},
|
||||
strings.Split(provider.Endpoint, ","),
|
||||
storeConfig,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
"crypto/tls"
|
||||
"github.com/BurntSushi/ty/fun"
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/emilevauge/traefik/types"
|
||||
"github.com/containous/traefik/types"
|
||||
"github.com/gambol99/go-marathon"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
@@ -5,8 +5,8 @@ import (
|
||||
"testing"
|
||||
|
||||
"errors"
|
||||
"github.com/emilevauge/traefik/mocks"
|
||||
"github.com/emilevauge/traefik/types"
|
||||
"github.com/containous/traefik/mocks"
|
||||
"github.com/containous/traefik/types"
|
||||
"github.com/gambol99/go-marathon"
|
||||
"github.com/stretchr/testify/mock"
|
||||
)
|
||||
|
||||
@@ -7,8 +7,8 @@ import (
|
||||
"text/template"
|
||||
|
||||
"github.com/BurntSushi/toml"
|
||||
"github.com/emilevauge/traefik/autogen"
|
||||
"github.com/emilevauge/traefik/types"
|
||||
"github.com/containous/traefik/autogen"
|
||||
"github.com/containous/traefik/types"
|
||||
)
|
||||
|
||||
// Provider defines methods of a provider.
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package provider
|
||||
|
||||
import (
|
||||
"github.com/containous/traefik/types"
|
||||
"github.com/docker/libkv/store"
|
||||
"github.com/docker/libkv/store/zookeeper"
|
||||
"github.com/emilevauge/traefik/types"
|
||||
)
|
||||
|
||||
// Zookepper holds configurations of the Zookepper provider.
|
||||
|
||||
@@ -4,7 +4,7 @@ if [ -z "$VALIDATE_UPSTREAM" ]; then
|
||||
# this is kind of an expensive check, so let's not do this twice if we
|
||||
# are running more than one validate bundlescript
|
||||
|
||||
VALIDATE_REPO='https://github.com/emilevauge/traefik.git'
|
||||
VALIDATE_REPO='https://github.com/containous/traefik.git'
|
||||
VALIDATE_BRANCH='master'
|
||||
|
||||
# Should not be needed for now O:)
|
||||
|
||||
@@ -13,8 +13,29 @@ unzip -q linux_amd64.zip
|
||||
sudo mv ghr /usr/bin/ghr
|
||||
sudo chmod +x /usr/bin/ghr
|
||||
|
||||
ghr -t $GITHUB_TOKEN -u emilevauge -r traefik --prerelease ${VERSION} dist/
|
||||
# github release and tag
|
||||
ghr -t $GITHUB_TOKEN -u containous -r traefik --prerelease ${VERSION} dist/
|
||||
|
||||
# update traefik-library-image repo (official Docker image)
|
||||
git config --global user.email "emile@vauge.com"
|
||||
git config --global user.name "Emile Vauge"
|
||||
git clone https://github.com/containous/traefik-library-image.git
|
||||
cd traefik-library-image
|
||||
git remote rm origin
|
||||
git remote add origin https://emilevauge:${GITHUB_TOKEN}@github.com/containous/traefik-library-image.git
|
||||
./update.sh $VERSION
|
||||
git add -A
|
||||
echo $VERSION | git commit --file -
|
||||
echo $VERSION | git tag -a $VERSION --file -
|
||||
git push --follow-tags -u origin master
|
||||
|
||||
# create docker image emilevauge/traefik (compatibility)
|
||||
docker login -e $DOCKER_EMAIL -u $DOCKER_USER -p $DOCKER_PASS
|
||||
docker push ${REPO,,}:latest
|
||||
docker tag ${REPO,,}:latest ${REPO,,}:${VERSION}
|
||||
docker push ${REPO,,}:${VERSION}
|
||||
|
||||
cd ..
|
||||
rm -Rf traefik-library-image/
|
||||
|
||||
echo "Deployed"
|
||||
|
||||
73
server.go
73
server.go
@@ -7,17 +7,6 @@ import (
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/codegangsta/negroni"
|
||||
"github.com/emilevauge/traefik/middlewares"
|
||||
"github.com/emilevauge/traefik/provider"
|
||||
"github.com/emilevauge/traefik/types"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/mailgun/manners"
|
||||
"github.com/mailgun/oxy/cbreaker"
|
||||
"github.com/mailgun/oxy/forward"
|
||||
"github.com/mailgun/oxy/roundrobin"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
@@ -28,6 +17,17 @@ import (
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/codegangsta/negroni"
|
||||
"github.com/containous/traefik/middlewares"
|
||||
"github.com/containous/traefik/provider"
|
||||
"github.com/containous/traefik/types"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/mailgun/manners"
|
||||
"github.com/mailgun/oxy/cbreaker"
|
||||
"github.com/mailgun/oxy/forward"
|
||||
"github.com/mailgun/oxy/roundrobin"
|
||||
)
|
||||
|
||||
var oxyLogger = &OxyLogger{}
|
||||
@@ -335,11 +335,11 @@ func (server *Server) loadConfig(configurations configs, globalConfiguration Glo
|
||||
newRoute := serverEntryPoints[entryPointName].httpRouter.NewRoute().Name(frontendName)
|
||||
for routeName, route := range frontend.Routes {
|
||||
log.Debugf("Creating route %s %s:%s", routeName, route.Rule, route.Value)
|
||||
newRouteReflect, err := invoke(newRoute, route.Rule, route.Value)
|
||||
route, err := getRoute(newRoute, route.Rule, route.Value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
newRoute = newRouteReflect[0].Interface().(*mux.Route)
|
||||
newRoute = route
|
||||
}
|
||||
entryPoint := globalConfiguration.EntryPoints[entryPointName]
|
||||
if entryPoint.Redirect != nil {
|
||||
@@ -399,7 +399,7 @@ func (server *Server) loadConfig(configurations configs, globalConfiguration Glo
|
||||
} else {
|
||||
log.Debugf("Reusing backend %s", frontend.Backend)
|
||||
}
|
||||
newRoute.Handler(backends[frontend.Backend])
|
||||
server.wireFrontendBackend(frontend.Routes, newRoute, backends[frontend.Backend])
|
||||
}
|
||||
err := newRoute.GetError()
|
||||
if err != nil {
|
||||
@@ -411,6 +411,32 @@ func (server *Server) loadConfig(configurations configs, globalConfiguration Glo
|
||||
return serverEntryPoints, nil
|
||||
}
|
||||
|
||||
func (server *Server) wireFrontendBackend(routes map[string]types.Route, newRoute *mux.Route, handler http.Handler) {
|
||||
// strip prefix
|
||||
var strip bool
|
||||
for _, route := range routes {
|
||||
switch route.Rule {
|
||||
case "PathStrip":
|
||||
newRoute.Handler(&middlewares.StripPrefix{
|
||||
Prefix: route.Value,
|
||||
Handler: handler,
|
||||
})
|
||||
strip = true
|
||||
break
|
||||
case "PathPrefixStrip":
|
||||
newRoute.Handler(&middlewares.StripPrefix{
|
||||
Prefix: route.Value,
|
||||
Handler: handler,
|
||||
})
|
||||
strip = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !strip {
|
||||
newRoute.Handler(handler)
|
||||
}
|
||||
}
|
||||
|
||||
func (server *Server) loadEntryPointConfig(entryPointName string, entryPoint *EntryPoint) (http.Handler, error) {
|
||||
regex := entryPoint.Redirect.Regex
|
||||
replacement := entryPoint.Redirect.Replacement
|
||||
@@ -443,9 +469,28 @@ func (server *Server) loadEntryPointConfig(entryPointName string, entryPoint *En
|
||||
func (server *Server) buildDefaultHTTPRouter() *mux.Router {
|
||||
router := mux.NewRouter()
|
||||
router.NotFoundHandler = http.HandlerFunc(notFoundHandler)
|
||||
router.StrictSlash(true)
|
||||
return router
|
||||
}
|
||||
|
||||
func getRoute(any interface{}, rule string, value ...interface{}) (*mux.Route, error) {
|
||||
switch rule {
|
||||
case "PathStrip":
|
||||
rule = "Path"
|
||||
case "PathPrefixStrip":
|
||||
rule = "PathPrefix"
|
||||
}
|
||||
inputs := make([]reflect.Value, len(value))
|
||||
for i := range value {
|
||||
inputs[i] = reflect.ValueOf(value[i])
|
||||
}
|
||||
method := reflect.ValueOf(any).MethodByName(rule)
|
||||
if method.IsValid() {
|
||||
return method.Call(inputs)[0].Interface().(*mux.Route), nil
|
||||
}
|
||||
return nil, errors.New("Method not found: " + rule)
|
||||
}
|
||||
|
||||
func sortedFrontendNamesForConfig(configuration *types.Configuration) []string {
|
||||
keys := []string{}
|
||||
|
||||
|
||||
@@ -281,6 +281,16 @@
|
||||
#
|
||||
# filename = "consul.tmpl"
|
||||
|
||||
# Enable consul TLS connection
|
||||
#
|
||||
# Optional
|
||||
#
|
||||
# [consul.tls]
|
||||
# ca = "/etc/ssl/ca.crt"
|
||||
# cert = "/etc/ssl/consul.crt"
|
||||
# key = "/etc/ssl/consul.key"
|
||||
# insecureskipverify = true
|
||||
|
||||
|
||||
################################################################
|
||||
# Etcd configuration backend
|
||||
@@ -316,6 +326,16 @@
|
||||
#
|
||||
# filename = "etcd.tmpl"
|
||||
|
||||
# Enable etcd TLS connection
|
||||
#
|
||||
# Optional
|
||||
#
|
||||
# [etcd.tls]
|
||||
# ca = "/etc/ssl/ca.crt"
|
||||
# cert = "/etc/ssl/etcd.crt"
|
||||
# key = "/etc/ssl/etcd.key"
|
||||
# insecureskipverify = true
|
||||
|
||||
|
||||
################################################################
|
||||
# Zookeeper configuration backend
|
||||
|
||||
23
utils.go
23
utils.go
@@ -1,23 +0,0 @@
|
||||
/*
|
||||
Copyright
|
||||
*/
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// Invoke calls the specified method with the specified arguments on the specified interface.
|
||||
// It uses the go(lang) reflect package.
|
||||
func invoke(any interface{}, name string, args ...interface{}) ([]reflect.Value, error) {
|
||||
inputs := make([]reflect.Value, len(args))
|
||||
for i := range args {
|
||||
inputs[i] = reflect.ValueOf(args[i])
|
||||
}
|
||||
method := reflect.ValueOf(any).MethodByName(name)
|
||||
if method.IsValid() {
|
||||
return method.Call(inputs), nil
|
||||
}
|
||||
return nil, errors.New("Method not found: " + name)
|
||||
}
|
||||
4
web.go
4
web.go
@@ -7,9 +7,9 @@ import (
|
||||
"net/http"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/containous/traefik/autogen"
|
||||
"github.com/containous/traefik/types"
|
||||
"github.com/elazarl/go-bindata-assetfs"
|
||||
"github.com/emilevauge/traefik/autogen"
|
||||
"github.com/emilevauge/traefik/types"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/thoas/stats"
|
||||
"github.com/unrolled/render"
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
</ul>
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<li>
|
||||
<a href="https://github.com/EmileVauge/traefik/blob/master/docs/index.md" target="_blank">Documentation</a>
|
||||
<a href="https://github.com/containous/traefik/blob/master/docs/index.md" target="_blank">Documentation</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="http://traefik.io" target="_blank"><span class="traefik-blue">traefik.io</span></a>
|
||||
|
||||
Reference in New Issue
Block a user