Merge pull request #773 from containous/merge-v1.1.0-rc2

Merge v1.1.0 rc2
This commit is contained in:
Emile Vauge 2016-10-25 09:04:07 +02:00 committed by GitHub
commit 14db2343c9
27 changed files with 575 additions and 273 deletions

View file

@ -1,5 +1,38 @@
# Change Log
## [v1.1.0-rc2](https://github.com/containous/traefik/tree/v1.1.0-rc2) (2016-10-17)
[Full Changelog](https://github.com/containous/traefik/compare/v1.1.0-rc1...v1.1.0-rc2)
**Implemented enhancements:**
- Support healthcheck if present for docker [\#666](https://github.com/containous/traefik/issues/666)
**Closed issues:**
- Sensible configuration for consulCatalog [\#737](https://github.com/containous/traefik/issues/737)
- Traefik ignoring container listening in more than one TCP port [\#734](https://github.com/containous/traefik/issues/734)
- Error when using HA acme in kubernetes with etcd [\#725](https://github.com/containous/traefik/issues/725)
- \[Docker swarm mode\] No round robin when using service [\#718](https://github.com/containous/traefik/issues/718)
- Dose it support docker swarm mode [\#712](https://github.com/containous/traefik/issues/712)
- Kubernetes - Undefined backend [\#710](https://github.com/containous/traefik/issues/710)
- Constraints on Consul Catalogue not working as expected [\#703](https://github.com/containous/traefik/issues/703)
- docker run syntax in swarm example has changed [\#528](https://github.com/containous/traefik/issues/528)
- Secure WebSockets [\#467](https://github.com/containous/traefik/issues/467)
**Merged pull requests:**
- Fix case sensitive host [\#733](https://github.com/containous/traefik/pull/733) ([emilevauge](https://github.com/emilevauge))
- Update Kubernetes examples [\#731](https://github.com/containous/traefik/pull/731) ([Starefossen](https://github.com/Starefossen))
- fIx marathon template with dots in ID [\#728](https://github.com/containous/traefik/pull/728) ([emilevauge](https://github.com/emilevauge))
- Fix networkMap construction in ListServices [\#724](https://github.com/containous/traefik/pull/724) ([vincentlepot](https://github.com/vincentlepot))
- Add basic compatibility with marathon-lb [\#720](https://github.com/containous/traefik/pull/720) ([guilhem](https://github.com/guilhem))
- Add Ed's video at ContainerCamp [\#717](https://github.com/containous/traefik/pull/717) ([emilevauge](https://github.com/emilevauge))
- Add documentation for Træfik on docker swarm mode [\#715](https://github.com/containous/traefik/pull/715) ([vdemeester](https://github.com/vdemeester))
- Remove duplicated link to Kubernetes.io in README.md [\#713](https://github.com/containous/traefik/pull/713) ([oscerd](https://github.com/oscerd))
- Show current version in web UI [\#709](https://github.com/containous/traefik/pull/709) ([vhf](https://github.com/vhf))
- Add support for docker healthcheck 👼 [\#708](https://github.com/containous/traefik/pull/708) ([vdemeester](https://github.com/vdemeester))
- Fix syntax in Swarm example. Resolves \#528 [\#707](https://github.com/containous/traefik/pull/707) ([billglover](https://github.com/billglover))
## [v1.1.0-rc1](https://github.com/containous/traefik/tree/v1.1.0-rc1) (2016-09-30)
[Full Changelog](https://github.com/containous/traefik/compare/v1.0.0...v1.1.0-rc1)

View file

@ -13,7 +13,7 @@
Træfɪk is a modern HTTP reverse proxy and load balancer made to deploy microservices with ease.
It supports several backends ([Docker](https://www.docker.com/), [Swarm](https://docs.docker.com/swarm), [Kubernetes](http://kubernetes.io), [Marathon](https://mesosphere.github.io/marathon/), [Mesos](https://github.com/apache/mesos), [Kubernetes](http://kubernetes.io/), [Consul](https://www.consul.io/), [Etcd](https://coreos.com/etcd/), [Zookeeper](https://zookeeper.apache.org), [BoltDB](https://github.com/boltdb/bolt), Rest API, file...) to manage its configuration automatically and dynamically.
It supports several backends ([Docker](https://www.docker.com/), [Swarm](https://docs.docker.com/swarm), [Kubernetes](http://kubernetes.io), [Marathon](https://mesosphere.github.io/marathon/), [Mesos](https://github.com/apache/mesos), [Consul](https://www.consul.io/), [Etcd](https://coreos.com/etcd/), [Zookeeper](https://zookeeper.apache.org), [BoltDB](https://github.com/boltdb/bolt), Rest API, file...) to manage its configuration automatically and dynamically.
## Overview
@ -66,6 +66,11 @@ Run it and forget it!
You can have a quick look at Træfɪk in this [Katacoda tutorial](https://www.katacoda.com/courses/traefik/deploy-load-balancer) that shows how to load balance requests between multiple Docker containers.
Here is a talk given by [Ed Robinson](https://github.com/errm) at the [ContainerCamp UK](https://container.camp) conference.
You will learn fundamental Træfɪk features and see some demos with Kubernetes.
[![Traefik ContainerCamp UK](http://img.youtube.com/vi/aFtpIShV60I/0.jpg)](https://www.youtube.com/watch?v=aFtpIShV60I)
Here is a talk (in French) given by [Emile Vauge](https://github.com/emilevauge) at the [Devoxx France 2016](http://www.devoxx.fr) conference.
You will learn fundamental Træfɪk features and see some demos with Docker, Mesos/Marathon and Let's Encrypt.
@ -86,7 +91,7 @@ You can access to a simple HTML frontend of Træfik.
- [Manners](https://github.com/mailgun/manners): graceful shutdown of http.Handler servers
- [Lego](https://github.com/xenolf/lego): the best [Let's Encrypt](https://letsencrypt.org) library in go
## Quick start
## Test it
- 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):

View file

@ -4,11 +4,13 @@ import (
"crypto/tls"
"errors"
"fmt"
"github.com/BurntSushi/ty/fun"
"github.com/cenk/backoff"
"github.com/containous/staert"
"github.com/containous/traefik/cluster"
"github.com/containous/traefik/log"
"github.com/containous/traefik/safe"
"github.com/containous/traefik/types"
"github.com/xenolf/lego/acme"
"golang.org/x/net/context"
"io/ioutil"
@ -311,22 +313,23 @@ func (a *ACME) CreateLocalConfig(tlsConfig *tls.Config, checkOnDemandDomain func
}
func (a *ACME) getCertificate(clientHello *tls.ClientHelloInfo) (*tls.Certificate, error) {
domain := types.CanonicalDomain(clientHello.ServerName)
account := a.store.Get().(*Account)
if challengeCert, ok := a.challengeProvider.getCertificate(clientHello.ServerName); ok {
log.Debugf("ACME got challenge %s", clientHello.ServerName)
if challengeCert, ok := a.challengeProvider.getCertificate(domain); ok {
log.Debugf("ACME got challenge %s", domain)
return challengeCert, nil
}
if domainCert, ok := account.DomainsCertificate.getCertificateForDomain(clientHello.ServerName); ok {
log.Debugf("ACME got domain cert %s", clientHello.ServerName)
if domainCert, ok := account.DomainsCertificate.getCertificateForDomain(domain); ok {
log.Debugf("ACME got domain cert %s", domain)
return domainCert.tlsCert, nil
}
if a.OnDemand {
if a.checkOnDemandDomain != nil && !a.checkOnDemandDomain(clientHello.ServerName) {
if a.checkOnDemandDomain != nil && !a.checkOnDemandDomain(domain) {
return nil, nil
}
return a.loadCertificateOnDemand(clientHello)
}
log.Debugf("ACME got nothing %s", clientHello.ServerName)
log.Debugf("ACME got nothing %s", domain)
return nil, nil
}
@ -429,22 +432,23 @@ func (a *ACME) buildACMEClient(account *Account) (*acme.Client, error) {
}
func (a *ACME) loadCertificateOnDemand(clientHello *tls.ClientHelloInfo) (*tls.Certificate, error) {
domain := types.CanonicalDomain(clientHello.ServerName)
account := a.store.Get().(*Account)
if certificateResource, ok := account.DomainsCertificate.getCertificateForDomain(clientHello.ServerName); ok {
if certificateResource, ok := account.DomainsCertificate.getCertificateForDomain(domain); ok {
return certificateResource.tlsCert, nil
}
certificate, err := a.getDomainsCertificates([]string{clientHello.ServerName})
certificate, err := a.getDomainsCertificates([]string{domain})
if err != nil {
return nil, err
}
log.Debugf("Got certificate on demand for domain %s", clientHello.ServerName)
log.Debugf("Got certificate on demand for domain %s", domain)
transaction, object, err := a.store.Begin()
if err != nil {
return nil, err
}
account = object.(*Account)
cert, err := account.DomainsCertificate.addCertificateForDomains(certificate, Domain{Main: clientHello.ServerName})
cert, err := account.DomainsCertificate.addCertificateForDomains(certificate, Domain{Main: domain})
if err != nil {
return nil, err
}
@ -456,6 +460,7 @@ func (a *ACME) loadCertificateOnDemand(clientHello *tls.ClientHelloInfo) (*tls.C
// LoadCertificateForDomains loads certificates from ACME for given domains
func (a *ACME) LoadCertificateForDomains(domains []string) {
domains = fun.Map(types.CanonicalDomain, domains).([]string)
safe.Go(func() {
operation := func() error {
if a.client == nil {
@ -514,6 +519,7 @@ func (a *ACME) LoadCertificateForDomains(domains []string) {
}
func (a *ACME) getDomainsCertificates(domains []string) (*Certificate, error) {
domains = fun.Map(types.CanonicalDomain, domains).([]string)
log.Debugf("Loading ACME certificates %s...", domains)
bundle := true
certificate, failures := a.client.ObtainCertificate(domains, bundle, nil)

View file

@ -783,6 +783,13 @@ domain = "marathon.localhost"
#
# groupsAsSubDomains = true
# Enable compatibility with marathon-lb labels
#
# Optional
# Default: false
#
# marathonLBCompatibility = true
# Enable Marathon basic authentication
#
# Optional
@ -944,7 +951,7 @@ Annotations can be used on containers to override default behaviour for the whol
- `traefik.frontend.rule.type: PathPrefixStrip`: override the default frontend rule type (Default: `PathPrefix`).
You can find here an example [ingress](https://raw.githubusercontent.com/containous/traefik/master/examples/k8s.ingress.yaml) and [replication controller](https://raw.githubusercontent.com/containous/traefik/master/examples/k8s.rc.yaml).
You can find here an example [ingress](https://raw.githubusercontent.com/containous/traefik/master/examples/k8s/cheese-ingress.yaml) and [replication controller](https://raw.githubusercontent.com/containous/traefik/master/examples/k8s/traefik.yaml).
## Consul backend

View file

@ -0,0 +1,223 @@
# Docker Swarm (mode) cluster
This section explains how to create a multi-host docker cluster with
swarm mode using [docker-machine](https://docs.docker.com/machine) and
how to deploy Træfɪk on it.
The cluster constist of:
- 3 servers
- 1 manager
- 2 workers
- 1 [overlay](https://docs.docker.com/engine/userguide/networking/dockernetworks/#an-overlay-network) network
(multi-host networking)
## Prerequisites
1. You will need to install [docker-machine](https://docs.docker.com/machine/)
2. You will need the latest [VirtualBox](https://www.virtualbox.org/wiki/Downloads)
## Cluster provisioning
First, let's create all the nodes required. It's a shorter version of
the [swarm tutorial](https://docs.docker.com/engine/swarm/swarm-tutorial/).
```sh
docker-machine create -d virtualbox manager
docker-machine create -d virtualbox worker1
docker-machine create -d virtualbox worker2
```
Then, let's setup the cluster, in order :
1. initialize the cluster
2. get the token for other host to join
3. on both workers, join the cluster with the token
```sh
docker-machine ssh manager "docker swarm init \
--listen-addr $(docker-machine ip manager) \
--advertise-addr $(docker-machine ip manager)"
export worker_token=$(docker-machine ssh manager "docker swarm \
join-token worker -q")
docker-machine ssh worker1 "docker swarm join \
--token=${worker_token} \
--listen-addr $(docker-machine ip worker1) \
--advertise-addr $(docker-machine ip worker1) \
$(docker-machine ip manager)"
docker-machine ssh worker2 "docker swarm join \
--token=${worker_token} \
--listen-addr $(docker-machine ip worker2) \
--advertise-addr $(docker-machine ip worker2) \
$(docker-machine ip manager)"
```
Let's validate the cluster is up and running.
```sh
docker-machine ssh manager docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS
2a770ov9vixeadep674265u1n worker1 Ready Active
dbi3or4q8ii8elbws70g4hkdh * manager Ready Active Leader
esbhhy6vnqv90xomjaomdgy46 worker2 Ready Active
```
Finally, let's create a network for Træfik to use.
```sh
docker-machine ssh manager "docker network create --driver=overlay traefik-net"
```
## Deploy Træfik
Let's deploy Træfik as a docker service in our cluster. The only
requirement for Træfik to work with swarm mode is that it needs to run
on a manager node — we are going to use a
[constraint](https://docs.docker.com/engine/reference/commandline/service_create/#/specify-service-constraints-constraint) for
that.
```
docker-machine ssh manager "docker service create \
--name traefik \
--constraint=node.role==manager \
--publish 80:80 --publish 8080:8080 \
--mount type=bind,source=/var/run/docker.sock,target=/var/run/docker.sock \
--network traefik-net \
traefik \
--docker \
--docker.swarmmode \
--docker.domain=traefik \
--docker.watch \
--web"
```
Let's explain this command:
- `--publish 80:80 --publish 8080:8080`: we publish port `80` and
`8080` on the cluster.
- `--constraint=node.role==manager`: we ask docker to schedule Træfik
on a manager node.
- `--mount type=bind,source=/var/run/docker.sock,target=/var/run/docker.sock`:
we bind mount the docker socket where Træfik is scheduled to be able
to speak to the daemon.
- `--network traefik-net`: we attach the Træfik service (and thus
the underlined container) to the `traefik-net` network.
- `--docker`: enable docker backend, and `--docker.swarmmode` to
enable the swarm mode on Træfik.
- `--web`: activate the webUI on port 8080
## Deploy your apps
We can now deploy our app on the cluster,
here [whoami](https://github.com/emilevauge/whoami), a simple web
server in Go. We start 2 services, on the `traefik-net` network.
```sh
docker-machine ssh manager "docker service create \
--name whoami0 \
--label traefik.port=80 \
--network traefik-net \
emilevauge/whoami"
docker-machine ssh manager "docker service create \
--name whoami1 \
--label traefik.port=80 \
--network traefik-net \
emilevauge/whoami"
```
Check that everything is scheduled and started:
```sh
docker-machine ssh manager "docker service ls"
ID NAME REPLICAS IMAGE COMMAND
ab046gpaqtln whoami0 1/1 emilevauge/whoami
cgfg5ifzrpgm whoami1 1/1 emilevauge/whoami
dtpl249tfghc traefik 1/1 traefik --docker --docker.swarmmode --docker.domain=traefik --docker.watch --web
```
## Access to your apps through Træfɪk
```sh
curl -H Host:whoami0.traefik http://$(docker-machine ip manager)
Hostname: 8147a7746e7a
IP: 127.0.0.1
IP: ::1
IP: 10.0.9.3
IP: fe80::42:aff:fe00:903
IP: 172.18.0.3
IP: fe80::42:acff:fe12:3
GET / HTTP/1.1
Host: 10.0.9.3:80
User-Agent: curl/7.35.0
Accept: */*
Accept-Encoding: gzip
X-Forwarded-For: 192.168.99.1
X-Forwarded-Host: 10.0.9.3:80
X-Forwarded-Proto: http
X-Forwarded-Server: 8fbc39271b4c
curl -H Host:whoami1.traefik http://$(docker-machine ip manager)
Hostname: ba2c21488299
IP: 127.0.0.1
IP: ::1
IP: 10.0.9.4
IP: fe80::42:aff:fe00:904
IP: 172.18.0.2
IP: fe80::42:acff:fe12:2
GET / HTTP/1.1
Host: 10.0.9.4:80
User-Agent: curl/7.35.0
Accept: */*
Accept-Encoding: gzip
X-Forwarded-For: 192.168.99.1
X-Forwarded-Host: 10.0.9.4:80
X-Forwarded-Proto: http
X-Forwarded-Server: 8fbc39271b4c
```
Note that as Træfik is published, you can access it from any machine
and not only the manager.
```sh
curl -H Host:whoami0.traefik http://$(docker-machine ip worker1)
Hostname: 8147a7746e7a
IP: 127.0.0.1
IP: ::1
IP: 10.0.9.3
IP: fe80::42:aff:fe00:903
IP: 172.18.0.3
IP: fe80::42:acff:fe12:3
GET / HTTP/1.1
Host: 10.0.9.3:80
User-Agent: curl/7.35.0
Accept: */*
Accept-Encoding: gzip
X-Forwarded-For: 192.168.99.1
X-Forwarded-Host: 10.0.9.3:80
X-Forwarded-Proto: http
X-Forwarded-Server: 8fbc39271b4c
curl -H Host:whoami1.traefik http://$(docker-machine ip worker2)
Hostname: ba2c21488299
IP: 127.0.0.1
IP: ::1
IP: 10.0.9.4
IP: fe80::42:aff:fe00:904
IP: 172.18.0.2
IP: fe80::42:acff:fe12:2
GET / HTTP/1.1
Host: 10.0.9.4:80
User-Agent: curl/7.35.0
Accept: */*
Accept-Encoding: gzip
X-Forwarded-For: 192.168.99.1
X-Forwarded-Host: 10.0.9.4:80
X-Forwarded-Proto: http
X-Forwarded-Server: 8fbc39271b4c
```
![](http://i.giphy.com/ujUdrdpX7Ok5W.gif)

View file

@ -1,7 +1,7 @@
# Swarm cluster
This section explains how to create a multi-host [swarm](https://docs.docker.com/swarm) cluster using [docker-machine](https://docs.docker.com/machine/) and how to deploy Træfɪk on it.
The cluster will be made of:
The cluster consists of:
- 2 servers
- 1 swarm master
@ -10,16 +10,16 @@ The cluster will be made of:
## Prerequisites
1. You will need to install [docker-machine](https://docs.docker.com/machine/)
2. You will need the latest [VirtualBox](https://www.virtualbox.org/wiki/Downloads)
1. You need to install [docker-machine](https://docs.docker.com/machine/)
2. You need the latest [VirtualBox](https://www.virtualbox.org/wiki/Downloads)
## Cluster provisioning
We will first follow [this guide](https://docs.docker.com/engine/userguide/networking/get-started-overlay/) to create the cluster.
We first follow [this guide](https://docs.docker.com/engine/userguide/networking/get-started-overlay/) to create the cluster.
### Create machine `mh-keystore`
This machine will be the service registry of our cluster.
This machine is the service registry of our cluster.
```sh
docker-machine create -d virtualbox mh-keystore
@ -37,7 +37,7 @@ docker run -d \
### Create machine `mhs-demo0`
This machine will have a swarm master and a swarm agent on it.
This machine is a swarm master and a swarm agent on it.
```sh
docker-machine create -d virtualbox \
@ -50,7 +50,7 @@ docker-machine create -d virtualbox \
### Create machine `mhs-demo1`
This machine will have a swarm agent on it.
This machine have a swarm agent on it.
```sh
docker-machine create -d virtualbox \
@ -84,14 +84,14 @@ docker $(docker-machine config mhs-demo0) run \
-l DEBUG \
-c /dev/null \
--docker \
--docker.domain traefik \
--docker.endpoint tcp://$(docker-machine ip mhs-demo0):3376 \
--docker.domain=traefik \
--docker.endpoint=tcp://$(docker-machine ip mhs-demo0):3376 \
--docker.tls \
--docker.tls.ca /ssl/ca.pem \
--docker.tls.cert /ssl/server.pem \
--docker.tls.key /ssl/server-key.pem \
--docker.tls.ca=/ssl/ca.pem \
--docker.tls.cert=/ssl/server.pem \
--docker.tls.key=/ssl/server-key.pem \
--docker.tls.insecureSkipVerify \
--docker.watch \
--docker.watch \
--web
```
@ -102,7 +102,7 @@ Let's explain this command:
- `-v /var/lib/boot2docker/:/ssl`: mount the ssl keys generated by docker-machine
- `-c /dev/null`: empty config file
- `--docker`: enable docker backend
- `--docker.endpoint tcp://172.18.0.1:3376`: connect to the swarm master using the docker_gwbridge network
- `--docker.endpoint=tcp://172.18.0.1:3376`: connect to the swarm master using the docker_gwbridge network
- `--docker.tls`: enable TLS using the docker-machine keys
- `--web`: activate the webUI on port 8080

View file

@ -1,111 +0,0 @@
# 3 Services for the 3 endpoints of the Ingress
apiVersion: v1
kind: Service
metadata:
name: service1
labels:
app: whoami
spec:
type: NodePort
ports:
- port: 80
nodePort: 30283
targetPort: 80
protocol: TCP
name: https
selector:
app: whoami
---
apiVersion: v1
kind: Service
metadata:
name: service2
labels:
app: whoami
spec:
type: NodePort
ports:
- port: 80
nodePort: 30284
targetPort: 80
protocol: TCP
name: http
selector:
app: whoami
---
apiVersion: v1
kind: Service
metadata:
name: service3
labels:
app: whoami
spec:
type: NodePort
ports:
- port: 80
nodePort: 30285
targetPort: 80
protocol: TCP
name: http
selector:
app: whoami
---
# A single RC matching all Services
apiVersion: v1
kind: ReplicationController
metadata:
name: whoami
spec:
replicas: 1
template:
metadata:
labels:
app: whoami
spec:
containers:
- name: whoami
image: emilevauge/whoami
ports:
- containerPort: 80
---
# An Ingress with 2 hosts and 3 endpoints
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: whoami-ingress
spec:
rules:
- host: foo.localhost
http:
paths:
- path: /bar
backend:
serviceName: service1
servicePort: 80
- host: bar.localhost
http:
paths:
- backend:
serviceName: service2
servicePort: 80
- backend:
serviceName: service3
servicePort: 80
---
# Another Ingress with PathPrefixStrip
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: whoami-ingress-stripped
annotations:
traefik.frontend.rule.type: "PathPrefixStrip"
spec:
rules:
- host: foo.localhost
http:
paths:
- path: /prefixWillBeStripped
backend:
serviceName: service1
servicePort: 80

View file

@ -1,10 +1,6 @@
#!/bin/bash
kubectl create -f - << EOF
kind: Namespace
apiVersion: v1
metadata:
name: kube-system
labels:
name: kube-system
EOF

View file

@ -1,31 +0,0 @@
apiVersion: v1
kind: ReplicationController
metadata:
name: traefik-ingress-controller
labels:
k8s-app: traefik-ingress-lb
spec:
replicas: 1
selector:
k8s-app: traefik-ingress-lb
template:
metadata:
labels:
k8s-app: traefik-ingress-lb
name: traefik-ingress-lb
spec:
terminationGracePeriodSeconds: 60
containers:
- image: traefik
name: traefik-ingress-lb
imagePullPolicy: Always
ports:
- containerPort: 80
hostPort: 80
- containerPort: 443
hostPort: 443
- containerPort: 8080
args:
- --web
- --kubernetes
- --logLevel=DEBUG

View file

@ -16,11 +16,12 @@ spec:
labels:
k8s-app: traefik-ingress-lb
name: traefik-ingress-lb
version: v1.0.0
version: v1.1.0
spec:
terminationGracePeriodSeconds: 60
hostNetwork: true
containers:
- image: traefik:v1.0.0
- image: traefik:v1.1.0
name: traefik-ingress-lb
resources:
limits:

63
glide.lock generated
View file

@ -1,10 +1,10 @@
hash: 39ff28cc1d13d5915a870b14491ece1849c4eaf5a56cecd50a7676ecee6c6143
updated: 2016-09-30T11:27:29.529525636+02:00
updated: 2016-10-06T14:06:39.455848971+02:00
imports:
- name: github.com/abbot/go-http-auth
version: cb4372376e1e00e9f6ab9ec142e029302c9e7140
- name: github.com/boltdb/bolt
version: 5cc10bbbc5c141029940133bb33c9e969512a698
version: f4c032d907f61f08dba2d719c58f108a1abb8e81
- name: github.com/BurntSushi/toml
version: 99064174e013895bbd9b025c31100bd1d9b590ca
- name: github.com/BurntSushi/ty
@ -18,7 +18,7 @@ imports:
- name: github.com/codegangsta/cli
version: 1efa31f08b9333f1bd4882d61f9d668a70cd902e
- name: github.com/codegangsta/negroni
version: dc6b9d037e8dab60cbfc09c61d6932537829be8b
version: 3f7ce7b928e14ff890b067e5bbbc80af73690a9c
- name: github.com/containous/flaeg
version: a731c034dda967333efce5f8d276aeff11f8ff87
- name: github.com/containous/mux
@ -32,11 +32,11 @@ imports:
- pkg/pathutil
- pkg/types
- name: github.com/davecgh/go-spew
version: 5215b55f46b2b919f50a1df0eaa5886afe4e3b3d
version: 6d212800a42e8ab5c146b8ace3490ee17e5225f9
subpackages:
- spew
- name: github.com/docker/distribution
version: 87917f30529e6a7fca8eaff2932424915fb11225
version: 99cb7c0946d2f5a38015443e515dc916295064d7
subpackages:
- context
- digest
@ -116,15 +116,15 @@ imports:
- types/time
- types/versions
- name: github.com/docker/go-connections
version: 990a1a1a70b0da4c4cb70e117971a4f0babfbf1a
version: 988efe982fdecb46f01d53465878ff1f2ff411ce
subpackages:
- nat
- sockets
- tlsconfig
- name: github.com/docker/go-units
version: f2d77a61e3c169b43402a0a1e84f06daf29b8190
version: f2145db703495b2e525c59662db69a7344b00bb8
- name: github.com/docker/leadership
version: bfc7753dd48af19513b29deec23c364bf0f274eb
version: 0a913e2d71a12fd14a028452435cb71ac8d82cb6
- name: github.com/docker/libcompose
version: d1876c1d68527a49c0aac22a0b161acc7296b740
subpackages:
@ -143,7 +143,7 @@ imports:
- version
- yaml
- name: github.com/docker/libkv
version: 35d3e2084c650109e7bcc7282655b1bc8ba924ff
version: 3fce6a0f26e07da3eac45796a8e255547a47a750
subpackages:
- store
- store/boltdb
@ -153,13 +153,13 @@ imports:
- name: github.com/donovanhide/eventsource
version: fd1de70867126402be23c306e1ce32828455d85b
- name: github.com/elazarl/go-bindata-assetfs
version: 57eb5e1fc594ad4b0b1dbea7b286d299e0cb43c2
version: 9a6736ed45b44bf3835afeebb3034b57ed329f3e
- name: github.com/gambol99/go-marathon
version: a558128c87724cd7430060ef5aedf39f83937f55
- name: github.com/go-check/check
version: 4f90aeace3a26ad7021961c297b22c42160c7b25
- name: github.com/gogo/protobuf
version: e33835a643a970c11ac74f6333f5f6866387a101
version: 99cb9b23110011cc45571c901ecae6f6f5e65cd3
subpackages:
- proto
- name: github.com/golang/glog
@ -169,18 +169,17 @@ imports:
subpackages:
- query
- name: github.com/gorilla/context
version: aed02d124ae4a0e94fea4541c8effd05bf0c8296
version: 08b5f424b9271eedf6f9f0ce86cb9396ed337a42
- name: github.com/hashicorp/consul
version: fce7d75609a04eeb9d4bf41c8dc592aac18fc97d
version: d8e2fb7dd594163e25a89bc52c1a4613f5c5bfb8
subpackages:
- api
- name: github.com/hashicorp/go-cleanhttp
version: 875fb671b3ddc66f8e2f0acc33829c8cb989a38d
version: ad28ea4487f05916463e2423a55166280e8254b5
- name: github.com/hashicorp/serf
version: 6c4672d66fc6312ddde18399262943e21175d831
version: b03bf85930b2349eb04b97c8fac437495296e3e7
subpackages:
- coordinate
- serf
- name: github.com/jarcoal/httpmock
version: 145b10d659265440f062c31ea15326166bae56ee
- name: github.com/libkermit/compose
@ -190,7 +189,7 @@ imports:
- name: github.com/libkermit/docker
version: 55e3595409924fcfbb850811e5a7cdbe8960a0b7
- name: github.com/mailgun/manners
version: fada45142db3f93097ca917da107aa3fad0ffcb5
version: a585afd9d65c0e05f6c003f921e71ebc05074f4f
- name: github.com/mailgun/timetools
version: fd192d755b00c968d312d23f521eb0cdc6f66bd0
- name: github.com/mattn/go-shellwords
@ -222,7 +221,7 @@ imports:
- name: github.com/miekg/dns
version: 5d001d020961ae1c184f9f8152fdc73810481677
- name: github.com/mitchellh/mapstructure
version: d2dd0262208475919e1a362f675cfc0e7c10e905
version: ca63d7c062ee3c9f34db231e352b60012b4fd0c1
- name: github.com/moul/http2curl
version: b1479103caacaa39319f75e7f57fc545287fca0d
- name: github.com/NYTimes/gziphandler
@ -230,11 +229,11 @@ imports:
- name: github.com/ogier/pflag
version: 45c278ab3607870051a2ea9040bb85fcb8557481
- name: github.com/opencontainers/runc
version: 1a81e9ab1f138c091fe5c86d0883f87716088527
version: 02f8fa7863dd3f82909a73e2061897828460d52f
subpackages:
- libcontainer/user
- name: github.com/parnurzeal/gorequest
version: 045012d33ef41ea146c1b675df9296d0dc1a212d
version: e30af16d4e485943aab0b0885ad6bdbb8c0d3dc7
- name: github.com/pmezard/go-difflib
version: d8ed2627bdf02c080bf22230dbb337003b7aba2d
subpackages:
@ -242,24 +241,24 @@ imports:
- name: github.com/ryanuber/go-glob
version: 572520ed46dbddaed19ea3d9541bdd0494163693
- name: github.com/samuel/go-zookeeper
version: e64db453f3512cade908163702045e0f31137843
version: 87e1bca4477a3cc767ca71be023ced183d74e538
subpackages:
- zk
- name: github.com/satori/go.uuid
version: 879c5887cd475cd7864858769793b2ceb0d44feb
- name: github.com/Sirupsen/logrus
version: a283a10442df8dc09befd873fab202bf8a253d6a
version: 3ec0642a7fb6488f65b06f9040adc67e3990296a
- name: github.com/streamrail/concurrent-map
version: 65a174a3a4188c0b7099acbc6cfa0c53628d3287
version: 8bf1e9bacbf65b10c81d0f4314cf2b1ebef728b5
- name: github.com/stretchr/objx
version: cbeaeb16a013161a98496fad62933b1d21786672
- name: github.com/stretchr/testify
version: d77da356e56a7428ad25149ca77381849a6a5232
version: 976c720a22c8eb4eb6a0b4348ad85ad12491a506
subpackages:
- assert
- mock
- name: github.com/thoas/stats
version: 79b768ff1780f4e5b0ed132e192bfeefe9f85a9c
version: 152b5d051953fdb6e45f14b6826962aadc032324
- name: github.com/tv42/zbase32
version: 03389da7e0bf9844767f82690f4d68fc097a1306
- name: github.com/ugorji/go
@ -267,7 +266,7 @@ imports:
subpackages:
- codec
- name: github.com/unrolled/render
version: 198ad4d8b8a4612176b804ca10555b222a086b40
version: 526faf80cd4b305bb8134abea8d20d5ced74faa6
- name: github.com/vdemeester/docker-events
version: be74d4929ec1ad118df54349fda4b0cba60f849b
- name: github.com/vdemeester/shakers
@ -289,7 +288,7 @@ imports:
- name: github.com/vulcand/route
version: cb89d787ddbb1c5849a7ac9f79004c1fd12a4a32
- name: github.com/vulcand/vulcand
version: 28a4e5c0892167589737b95ceecbcef00295be50
version: bed092e10989250b48bdb6aa3b0557b207f05c80
subpackages:
- conntracker
- plugin
@ -317,13 +316,13 @@ imports:
- unix
- windows
- name: gopkg.in/fsnotify.v1
version: a8a77c9133d2d6fd8334f3260d06f60e8d80a5fb
version: 944cff21b3baf3ced9a880365682152ba577d348
- name: gopkg.in/mgo.v2
version: 29cc868a5ca65f401ff318143f9408d02f4799cc
version: 22287bab4379e1fbf6002fb4eb769888f3fb224c
subpackages:
- bson
- name: gopkg.in/square/go-jose.v1
version: e3f973b66b91445ec816dd7411ad1b6495a5a2fc
version: aa2e30fdd1fe9dd3394119af66451ae790d50e0d
subpackages:
- cipher
- json
@ -341,9 +340,9 @@ testImports:
- name: github.com/libkermit/docker-check
version: cbe0ef03b3d23070eac4d00ba8828f2cc7f7e5a3
- name: github.com/spf13/pflag
version: 5644820622454e71517561946e3d94b9f9db6842
version: 08b1a584251b5b62f458943640fc8ebd4d50aaa5
- name: github.com/vbatts/tar-split
version: 6810cedb21b2c3d0b9bb8f9af12ff2dc7a2f14df
version: bd4c5d64c3e9297f410025a3b1bd0c58f659e721
subpackages:
- archive/tar
- tar/asm

View file

@ -49,6 +49,7 @@ pages:
- User Guide:
- 'Configuration examples': 'user-guide/examples.md'
- 'Swarm cluster': 'user-guide/swarm.md'
- 'Swarm mode cluster': 'user-guide/swarm-mode.md'
- 'Kubernetes': 'user-guide/kubernetes.md'
- 'Key-value store configuration': 'user-guide/kv-config.md'
- 'Clustering/HA': 'user-guide/cluster.md'

View file

@ -58,6 +58,7 @@ type dockerData struct {
Name string
Labels map[string]string // List of labels set to container or service
NetworkSettings networkSettings
Health string
}
// NetworkSettings holds the networks data to the Docker provider
@ -214,6 +215,9 @@ func (provider *Docker) Provide(configurationChan chan<- types.ConfigMessage, po
}
eventHandler.Handle("start", startStopHandle)
eventHandler.Handle("die", startStopHandle)
eventHandler.Handle("health_status: healthy", startStopHandle)
eventHandler.Handle("health_status: unhealthy", startStopHandle)
eventHandler.Handle("health_status: starting", startStopHandle)
errChan := events.MonitorWithHandler(ctx, dockerClient, options, eventHandler)
if err := <-errChan; err != nil {
@ -378,6 +382,11 @@ func (provider *Docker) containerFilter(container dockerData) bool {
return false
}
if container.Health != "" && container.Health != "healthy" {
log.Debugf("Filtering unhealthy or starting container %s", container.Name)
return false
}
return true
}
@ -578,6 +587,10 @@ func parseContainer(container dockertypes.ContainerJSON) dockerData {
}
if container.State != nil && container.State.Health != nil {
dockerData.Health = container.State.Health.Status
}
return dockerData
}
@ -602,7 +615,8 @@ func listServices(ctx context.Context, dockerClient client.APIClient) ([]dockerD
return []dockerData{}, err
}
for _, network := range networkList {
networkMap[network.ID] = &network
networkToAdd := network
networkMap[network.ID] = &networkToAdd
}
var dockerDataList []dockerData

View file

@ -569,12 +569,18 @@ func TestDockerGetLabel(t *testing.T) {
}{
{
container: docker.ContainerJSON{
ContainerJSONBase: &docker.ContainerJSONBase{
Name: "foo",
},
Config: &container.Config{},
},
expected: "Label not found:",
},
{
container: docker.ContainerJSON{
ContainerJSONBase: &docker.ContainerJSONBase{
Name: "foo",
},
Config: &container.Config{
Labels: map[string]string{
"foo": "bar",
@ -608,6 +614,9 @@ func TestDockerGetLabels(t *testing.T) {
}{
{
container: docker.ContainerJSON{
ContainerJSONBase: &docker.ContainerJSONBase{
Name: "foo",
},
Config: &container.Config{},
},
expectedLabels: map[string]string{},
@ -615,6 +624,9 @@ func TestDockerGetLabels(t *testing.T) {
},
{
container: docker.ContainerJSON{
ContainerJSONBase: &docker.ContainerJSONBase{
Name: "foo",
},
Config: &container.Config{
Labels: map[string]string{
"foo": "fooz",
@ -628,6 +640,9 @@ func TestDockerGetLabels(t *testing.T) {
},
{
container: docker.ContainerJSON{
ContainerJSONBase: &docker.ContainerJSONBase{
Name: "foo",
},
Config: &container.Config{
Labels: map[string]string{
"foo": "fooz",

View file

@ -26,14 +26,15 @@ var _ Provider = (*Marathon)(nil)
// Marathon holds configuration of the Marathon provider.
type Marathon struct {
BaseProvider
Endpoint string `description:"Marathon server endpoint. You can also specify multiple endpoint for Marathon"`
Domain string `description:"Default domain used"`
ExposedByDefault bool `description:"Expose Marathon apps by default"`
GroupsAsSubDomains bool `description:"Convert Marathon groups to subdomains"`
DCOSToken string `description:"DCOSToken for DCOS environment, This will override the Authorization header"`
TLS *ClientTLS `description:"Enable Docker TLS support"`
Basic *MarathonBasic
marathonClient marathon.Marathon
Endpoint string `description:"Marathon server endpoint. You can also specify multiple endpoint for Marathon"`
Domain string `description:"Default domain used"`
ExposedByDefault bool `description:"Expose Marathon apps by default"`
GroupsAsSubDomains bool `description:"Convert Marathon groups to subdomains"`
DCOSToken string `description:"DCOSToken for DCOS environment, This will override the Authorization header"`
MarathonLBCompatibility bool `description:"Add compatibility with marathon-lb labels"`
TLS *ClientTLS `description:"Enable Docker TLS support"`
Basic *MarathonBasic
marathonClient marathon.Marathon
}
// MarathonBasic holds basic authentication specific configurations
@ -194,6 +195,11 @@ func (provider *Marathon) taskFilter(task marathon.Task, applications *marathon.
}
label, _ := provider.getLabel(application, "traefik.tags")
constraintTags := strings.Split(label, ",")
if provider.MarathonLBCompatibility {
if label, err := provider.getLabel(application, "HAPROXY_GROUP"); err == nil {
constraintTags = append(constraintTags, label)
}
}
if ok, failingConstraint := provider.MatchConstraints(constraintTags); !ok {
if failingConstraint != nil {
log.Debugf("Application %v pruned by '%v' constraint", application.ID, failingConstraint.String())
@ -263,6 +269,11 @@ func (provider *Marathon) taskFilter(task marathon.Task, applications *marathon.
func (provider *Marathon) applicationFilter(app marathon.Application, filteredTasks []marathon.Task) bool {
label, _ := provider.getLabel(app, "traefik.tags")
constraintTags := strings.Split(label, ",")
if provider.MarathonLBCompatibility {
if label, err := provider.getLabel(app, "HAPROXY_GROUP"); err == nil {
constraintTags = append(constraintTags, label)
}
}
if ok, failingConstraint := provider.MatchConstraints(constraintTags); !ok {
if failingConstraint != nil {
log.Debugf("Application %v pruned by '%v' constraint", app.ID, failingConstraint.String())
@ -384,6 +395,11 @@ func (provider *Marathon) getFrontendRule(application marathon.Application) stri
if label, err := provider.getLabel(application, "traefik.frontend.rule"); err == nil {
return label
}
if provider.MarathonLBCompatibility {
if label, err := provider.getLabel(application, "HAPROXY_0_VHOST"); err == nil {
return "Host:" + label
}
}
return "Host:" + provider.getSubDomain(application.ID) + "." + provider.Domain
}

View file

@ -114,7 +114,7 @@ func TestMarathonLoadConfig(t *testing.T) {
applications: &marathon.Applications{
Apps: []marathon.Application{
{
ID: "/testLoadBalancerAndCircuitBreaker",
ID: "/testLoadBalancerAndCircuitBreaker.dot",
Ports: []int{80},
Labels: &map[string]string{
"traefik.backend.loadbalancer.method": "drr",
@ -126,29 +126,29 @@ func TestMarathonLoadConfig(t *testing.T) {
tasks: &marathon.Tasks{
Tasks: []marathon.Task{
{
ID: "testLoadBalancerAndCircuitBreaker",
AppID: "/testLoadBalancerAndCircuitBreaker",
ID: "testLoadBalancerAndCircuitBreaker.dot",
AppID: "/testLoadBalancerAndCircuitBreaker.dot",
Host: "127.0.0.1",
Ports: []int{80},
},
},
},
expectedFrontends: map[string]*types.Frontend{
`frontend-testLoadBalancerAndCircuitBreaker`: {
Backend: "backend-testLoadBalancerAndCircuitBreaker",
`frontend-testLoadBalancerAndCircuitBreaker.dot`: {
Backend: "backend-testLoadBalancerAndCircuitBreaker.dot",
PassHostHeader: true,
EntryPoints: []string{},
Routes: map[string]types.Route{
`route-host-testLoadBalancerAndCircuitBreaker`: {
Rule: "Host:testLoadBalancerAndCircuitBreaker.docker.localhost",
`route-host-testLoadBalancerAndCircuitBreaker.dot`: {
Rule: "Host:testLoadBalancerAndCircuitBreaker.dot.docker.localhost",
},
},
},
},
expectedBackends: map[string]*types.Backend{
"backend-testLoadBalancerAndCircuitBreaker": {
"backend-testLoadBalancerAndCircuitBreaker.dot": {
Servers: map[string]types.Server{
"server-testLoadBalancerAndCircuitBreaker": {
"server-testLoadBalancerAndCircuitBreaker-dot": {
URL: "http://127.0.0.1:80",
Weight: 0,
},
@ -684,9 +684,10 @@ func TestMarathonTaskFilter(t *testing.T) {
func TestMarathonAppConstraints(t *testing.T) {
cases := []struct {
application marathon.Application
filteredTasks []marathon.Task
expected bool
application marathon.Application
filteredTasks []marathon.Task
expected bool
marathonLBCompatibility bool
}{
{
application: marathon.Application{
@ -698,28 +699,48 @@ func TestMarathonAppConstraints(t *testing.T) {
AppID: "foo1",
},
},
expected: false,
marathonLBCompatibility: false,
expected: false,
},
{
application: marathon.Application{
ID: "foo",
ID: "foo2",
Labels: &map[string]string{
"traefik.tags": "valid",
},
},
filteredTasks: []marathon.Task{
{
AppID: "foo",
AppID: "foo2",
},
},
expected: true,
marathonLBCompatibility: false,
expected: true,
},
{
application: marathon.Application{
ID: "foo3",
Labels: &map[string]string{
"HAPROXY_GROUP": "valid",
"traefik.tags": "notvalid",
},
},
filteredTasks: []marathon.Task{
{
AppID: "foo3",
},
},
marathonLBCompatibility: true,
expected: true,
},
}
provider := &Marathon{}
constraint, _ := types.NewConstraint("tag==valid")
provider.Constraints = []types.Constraint{*constraint}
for _, c := range cases {
provider := &Marathon{
MarathonLBCompatibility: c.marathonLBCompatibility,
}
constraint, _ := types.NewConstraint("tag==valid")
provider.Constraints = []types.Constraint{*constraint}
actual := provider.applicationFilter(c.application, c.filteredTasks)
if actual != c.expected {
t.Fatalf("expected %v, got %v: %v", c.expected, actual, c.application)
@ -729,9 +750,10 @@ func TestMarathonAppConstraints(t *testing.T) {
}
func TestMarathonTaskConstraints(t *testing.T) {
cases := []struct {
applications []marathon.Application
filteredTask marathon.Task
expected bool
applications []marathon.Application
filteredTask marathon.Task
expected bool
marathonLBCompatibility bool
}{
{
applications: []marathon.Application{
@ -749,7 +771,8 @@ func TestMarathonTaskConstraints(t *testing.T) {
AppID: "foo1",
Ports: []int{80},
},
expected: false,
marathonLBCompatibility: false,
expected: false,
},
{
applications: []marathon.Application{
@ -764,14 +787,40 @@ func TestMarathonTaskConstraints(t *testing.T) {
AppID: "foo2",
Ports: []int{80},
},
expected: true,
marathonLBCompatibility: false,
expected: true,
},
{
applications: []marathon.Application{
{
ID: "foo3",
Labels: &map[string]string{
"HAPROXY_GROUP": "valid",
"traefik.tags": "notvalid",
},
}, {
ID: "foo4",
Labels: &map[string]string{
"HAPROXY_GROUP": "notvalid",
"traefik.tags": "valid",
},
},
},
filteredTask: marathon.Task{
AppID: "foo3",
Ports: []int{80},
},
marathonLBCompatibility: true,
expected: true,
},
}
provider := &Marathon{}
constraint, _ := types.NewConstraint("tag==valid")
provider.Constraints = []types.Constraint{*constraint}
for _, c := range cases {
provider := &Marathon{
MarathonLBCompatibility: c.marathonLBCompatibility,
}
constraint, _ := types.NewConstraint("tag==valid")
provider.Constraints = []types.Constraint{*constraint}
apps := new(marathon.Applications)
apps.Apps = c.applications
actual := provider.taskFilter(c.filteredTask, apps, true)
@ -1152,37 +1201,53 @@ func TestMarathonGetEntryPoints(t *testing.T) {
}
func TestMarathonGetFrontendRule(t *testing.T) {
provider := &Marathon{
Domain: "docker.localhost",
}
applications := []struct {
application marathon.Application
expected string
application marathon.Application
expected string
marathonLBCompatibility bool
}{
{
application: marathon.Application{
Labels: &map[string]string{}},
expected: "Host:.docker.localhost",
marathonLBCompatibility: true,
expected: "Host:.docker.localhost",
},
{
application: marathon.Application{
ID: "test",
Labels: &map[string]string{},
ID: "test",
Labels: &map[string]string{
"HAPROXY_0_VHOST": "foo.bar",
},
},
expected: "Host:test.docker.localhost",
marathonLBCompatibility: false,
expected: "Host:test.docker.localhost",
},
{
application: marathon.Application{
Labels: &map[string]string{
"traefik.frontend.rule": "Host:foo.bar",
"HAPROXY_0_VHOST": "notvalid",
},
},
expected: "Host:foo.bar",
marathonLBCompatibility: true,
expected: "Host:foo.bar",
},
{
application: marathon.Application{
Labels: &map[string]string{
"HAPROXY_0_VHOST": "foo.bar",
},
},
marathonLBCompatibility: true,
expected: "Host:foo.bar",
},
}
for _, a := range applications {
provider := &Marathon{
Domain: "docker.localhost",
MarathonLBCompatibility: a.marathonLBCompatibility,
}
actual := provider.getFrontendRule(a.application)
if actual != a.expected {
t.Fatalf("expected %q, got %q", a.expected, actual)

View file

@ -3,7 +3,9 @@ package main
import (
"errors"
"fmt"
"github.com/BurntSushi/ty/fun"
"github.com/containous/mux"
"github.com/containous/traefik/types"
"net"
"net/http"
"reflect"
@ -24,7 +26,7 @@ func (r *Rules) host(hosts ...string) *mux.Route {
reqHost = req.Host
}
for _, host := range hosts {
if reqHost == strings.TrimSpace(host) {
if types.CanonicalDomain(reqHost) == types.CanonicalDomain(host) {
return true
}
}
@ -35,7 +37,7 @@ func (r *Rules) host(hosts ...string) *mux.Route {
func (r *Rules) hostRegexp(hosts ...string) *mux.Route {
router := r.route.route.Subrouter()
for _, host := range hosts {
router.Host(strings.TrimSpace(host))
router.Host(types.CanonicalDomain(host))
}
return r.route.route
}
@ -43,7 +45,7 @@ func (r *Rules) hostRegexp(hosts ...string) *mux.Route {
func (r *Rules) path(paths ...string) *mux.Route {
router := r.route.route.Subrouter()
for _, path := range paths {
router.Path(strings.TrimSpace(path))
router.Path(types.CanonicalDomain(path))
}
return r.route.route
}
@ -51,7 +53,7 @@ func (r *Rules) path(paths ...string) *mux.Route {
func (r *Rules) pathPrefix(paths ...string) *mux.Route {
router := r.route.route.Subrouter()
for _, path := range paths {
router.PathPrefix(strings.TrimSpace(path))
router.PathPrefix(types.CanonicalDomain(path))
}
return r.route.route
}
@ -67,7 +69,7 @@ func (r *Rules) pathStrip(paths ...string) *mux.Route {
r.route.stripPrefixes = paths
router := r.route.route.Subrouter()
for _, path := range paths {
router.Path(strings.TrimSpace(path))
router.Path(types.CanonicalDomain(path))
}
return r.route.route
}
@ -77,7 +79,7 @@ func (r *Rules) pathPrefixStrip(paths ...string) *mux.Route {
r.route.stripPrefixes = paths
router := r.route.route.Subrouter()
for _, path := range paths {
router.PathPrefix(strings.TrimSpace(path))
router.PathPrefix(types.CanonicalDomain(path))
}
return r.route.route
}
@ -153,7 +155,6 @@ func (r *Rules) parseRules(expression string, onRule func(functionName string, f
}
}
return nil
}
// Parse parses rules expressions
@ -197,5 +198,5 @@ func (r *Rules) ParseDomains(expression string) ([]string, error) {
if err != nil {
return nil, fmt.Errorf("Error parsing domains: %v", err)
}
return domains, nil
return fun.Map(types.CanonicalDomain, domains).([]string), nil
}

View file

@ -36,7 +36,7 @@ func TestParseTwoRules(t *testing.T) {
serverRoute := &serverRoute{route: route}
rules := &Rules{route: serverRoute}
expression := "Host:foo.bar;Path:/foobar"
expression := "Host: Foo.Bar ; Path:/FOObar"
routeResult, err := rules.Parse(expression)
if err != nil {
@ -58,11 +58,13 @@ func TestParseDomains(t *testing.T) {
"Host:foo.bar,test.bar",
"Path:/test",
"Host:foo.bar;Path:/test",
"Host: Foo.Bar ;Path:/test",
}
domainsSlice := [][]string{
{"foo.bar", "test.bar"},
{},
{"foo.bar"},
{"foo.bar"},
}
for i, expression := range expressionsSlice {
domains, err := rules.ParseDomains(expression)

View file

@ -20,8 +20,8 @@ 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
curl -LOs https://github.com/tcnksm/ghr/releases/download/v0.5.0/ghr_v0.5.0_linux_amd64.zip
unzip -q ghr_v0.5.0_linux_amd64.zip
sudo mv ghr /usr/bin/ghr
sudo chmod +x /usr/bin/ghr

View file

@ -1,35 +1,35 @@
{{$apps := .Applications}}
[backends]{{range .Tasks}}
[backends.backend{{getBackend . $apps}}.servers.server-{{.ID | replace "." "-"}}]
[backends."backend{{getBackend . $apps}}".servers."server-{{.ID | replace "." "-"}}"]
url = "{{getProtocol . $apps}}://{{.Host}}:{{getPort . $apps}}"
weight = {{getWeight . $apps}}
{{end}}
{{range .Applications}}
{{ if hasMaxConnLabels . }}
[backends.backend{{getFrontendBackend . }}.maxconn]
[backends."backend{{getFrontendBackend . }}".maxconn]
amount = {{getMaxConnAmount . }}
extractorfunc = "{{getMaxConnExtractorFunc . }}"
{{end}}
{{ if hasLoadBalancerLabels . }}
[backends.backend{{getFrontendBackend . }}.loadbalancer]
[backends."backend{{getFrontendBackend . }}".loadbalancer]
method = "{{getLoadBalancerMethod . }}"
sticky = {{getSticky .}}
{{end}}
{{ if hasCircuitBreakerLabels . }}
[backends.backend{{getFrontendBackend . }}.circuitbreaker]
[backends."backend{{getFrontendBackend . }}".circuitbreaker]
expression = "{{getCircuitBreakerExpression . }}"
{{end}}
{{end}}
[frontends]{{range .Applications}}
[frontends.frontend{{.ID | replace "/" "-"}}]
[frontends."frontend{{.ID | replace "/" "-"}}"]
backend = "backend{{getFrontendBackend .}}"
passHostHeader = {{getPassHostHeader .}}
priority = {{getPriority .}}
entryPoints = [{{range getEntryPoints .}}
"{{.}}",
{{end}}]
[frontends.frontend{{.ID | replace "/" "-"}}.routes.route-host{{.ID | replace "/" "-"}}]
[frontends."frontend{{.ID | replace "/" "-"}}".routes."route-host{{.ID | replace "/" "-"}}"]
rule = "{{getFrontendRule .}}"
{{end}}

View file

@ -223,3 +223,8 @@ type Basic struct {
type Digest struct {
Users
}
// CanonicalDomain returns a lower case domain with trim space
func CanonicalDomain(domain string) string {
return strings.ToLower(strings.TrimSpace(domain))
}

13
web.go
View file

@ -15,6 +15,7 @@ import (
"github.com/containous/traefik/middlewares"
"github.com/containous/traefik/safe"
"github.com/containous/traefik/types"
"github.com/containous/traefik/version"
"github.com/elazarl/go-bindata-assetfs"
"github.com/thoas/stats"
"github.com/unrolled/render"
@ -60,6 +61,7 @@ func (provider *WebProvider) Provide(configurationChan chan<- types.ConfigMessag
systemRouter.Methods("GET").Path("/ping").HandlerFunc(provider.getPingHandler)
// API routes
systemRouter.Methods("GET").Path("/api").HandlerFunc(provider.getConfigHandler)
systemRouter.Methods("GET").Path("/api/version").HandlerFunc(provider.getVersionHandler)
systemRouter.Methods("GET").Path("/api/providers").HandlerFunc(provider.getConfigHandler)
systemRouter.Methods("GET").Path("/api/providers/{provider}").HandlerFunc(provider.getProviderHandler)
systemRouter.Methods("PUT").Path("/api/providers/{provider}").HandlerFunc(func(response http.ResponseWriter, request *http.Request) {
@ -144,6 +146,17 @@ func (provider *WebProvider) getConfigHandler(response http.ResponseWriter, requ
templatesRenderer.JSON(response, http.StatusOK, currentConfigurations)
}
func (provider *WebProvider) getVersionHandler(response http.ResponseWriter, request *http.Request) {
v := struct {
Version string
Codename string
}{
Version: version.Version,
Codename: version.Codename,
}
templatesRenderer.JSON(response, http.StatusOK, v)
}
func (provider *WebProvider) getProviderHandler(response http.ResponseWriter, request *http.Request) {
vars := mux.Vars(request)
providerID := vars["provider"]

View file

@ -0,0 +1,14 @@
'use strict';
var angular = require('angular');
var traefikCoreVersion = 'traefik.core.version';
module.exports = traefikCoreVersion;
angular
.module(traefikCoreVersion, ['ngResource'])
.factory('Version', Version);
/** @ngInject */
function Version($resource) {
return $resource('../api/version');
}

View file

@ -0,0 +1,10 @@
'use strict';
/** @ngInject */
function VersionController($scope, $interval, $log, Version) {
Version.get(function (version) {
$scope.version = version;
});
}
module.exports = VersionController;

View file

@ -0,0 +1,11 @@
'use strict';
var angular = require('angular');
var traefikCoreVersion = require('../core/version.resource');
var VersionController = require('./version.controller');
var traefikVersion = 'traefik.version';
module.exports = traefikVersion;
angular
.module(traefikVersion, [traefikCoreVersion])
.controller('VersionController', VersionController);

View file

@ -27,6 +27,11 @@
<li><a ui-sref="health">Health</a></li>
</ul>
<ul class="nav navbar-nav navbar-right">
<li>
<a ng-controller="VersionController" href="https://github.com/containous/traefik/tree/{{version.Version}}" target="_blank">
<small>{{version.Version}} / {{version.Codename}}</small>
</a>
</li>
<li>
<a href="https://docs.traefik.io" target="_blank">Documentation</a>
</li>

View file

@ -10,6 +10,7 @@ var uiRouter = require('angular-ui-router');
var uiBootstrap = require('angular-ui-bootstrap');
var moment = require('moment');
var traefikSection = require('./app/sections/sections');
var traefikVersion = require('./app/version/version.module');
require('./index.scss');
require('animate.css/animate.css');
require('nvd3/build/nv.d3.css');
@ -28,7 +29,8 @@ angular
ngResource,
uiRouter,
uiBootstrap,
traefikSection
traefikSection,
traefikVersion
])
.run(runBlock)
.constant('moment', moment)