commit
992978215b
23 changed files with 934 additions and 751 deletions
663
Godeps/Godeps.json
generated
663
Godeps/Godeps.json
generated
|
@ -1,365 +1,302 @@
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/emilevauge/traefik",
|
"ImportPath": "github.com/emilevauge/traefik",
|
||||||
"GoVersion": "go1.4.2",
|
"GoVersion": "go1.4.2",
|
||||||
"Packages": [
|
"Packages": [
|
||||||
"./..."
|
"./..."
|
||||||
],
|
],
|
||||||
"Deps": [
|
"Deps": [{
|
||||||
{
|
"ImportPath": "github.com/BurntSushi/toml",
|
||||||
"ImportPath": "github.com/BurntSushi/toml",
|
"Rev": "bd2bdf7f18f849530ef7a1c29a4290217cab32a1"
|
||||||
"Rev": "bd2bdf7f18f849530ef7a1c29a4290217cab32a1"
|
}, {
|
||||||
},
|
"ImportPath": "github.com/BurntSushi/ty",
|
||||||
{
|
"Rev": "6add9cd6ad42d389d6ead1dde60b4ad71e46fd74"
|
||||||
"ImportPath": "github.com/BurntSushi/ty",
|
}, {
|
||||||
"Rev": "6add9cd6ad42d389d6ead1dde60b4ad71e46fd74"
|
"ImportPath": "github.com/Sirupsen/logrus",
|
||||||
},
|
"Comment": "v0.8.7",
|
||||||
{
|
"Rev": "418b41d23a1bf978c06faea5313ba194650ac088"
|
||||||
"ImportPath": "github.com/Sirupsen/logrus",
|
}, {
|
||||||
"Comment": "v0.8.7",
|
"ImportPath": "github.com/alecthomas/template",
|
||||||
"Rev": "418b41d23a1bf978c06faea5313ba194650ac088"
|
"Rev": "b867cc6ab45cece8143cfcc6fc9c77cf3f2c23c0"
|
||||||
},
|
}, {
|
||||||
{
|
"ImportPath": "github.com/alecthomas/units",
|
||||||
"ImportPath": "github.com/alecthomas/template",
|
"Rev": "6b4e7dc5e3143b85ea77909c72caf89416fc2915"
|
||||||
"Rev": "b867cc6ab45cece8143cfcc6fc9c77cf3f2c23c0"
|
}, {
|
||||||
},
|
"ImportPath": "github.com/boltdb/bolt",
|
||||||
{
|
"Rev": "51f99c862475898df9773747d3accd05a7ca33c1"
|
||||||
"ImportPath": "github.com/alecthomas/units",
|
}, {
|
||||||
"Rev": "6b4e7dc5e3143b85ea77909c72caf89416fc2915"
|
"ImportPath": "github.com/cenkalti/backoff",
|
||||||
},
|
"Rev": "4dc77674aceaabba2c7e3da25d4c823edfb73f99"
|
||||||
{
|
}, {
|
||||||
"ImportPath": "github.com/cenkalti/backoff",
|
"ImportPath": "github.com/codahale/hdrhistogram",
|
||||||
"Rev": "4dc77674aceaabba2c7e3da25d4c823edfb73f99"
|
"Rev": "954f16e8b9ef0e5d5189456aa4c1202758e04f17"
|
||||||
},
|
}, {
|
||||||
{
|
"ImportPath": "github.com/codegangsta/negroni",
|
||||||
"ImportPath": "github.com/codahale/hdrhistogram",
|
"Comment": "v0.1-70-gc7477ad",
|
||||||
"Rev": "954f16e8b9ef0e5d5189456aa4c1202758e04f17"
|
"Rev": "c7477ad8e330bef55bf1ebe300cf8aa67c492d1b"
|
||||||
},
|
}, {
|
||||||
{
|
"ImportPath": "github.com/coreos/go-etcd/etcd",
|
||||||
"ImportPath": "github.com/codegangsta/negroni",
|
"Comment": "v2.0.0-11-gcc90c7b",
|
||||||
"Comment": "v0.1-70-gc7477ad",
|
"Rev": "cc90c7b091275e606ad0ca7102a23fb2072f3f5e"
|
||||||
"Rev": "c7477ad8e330bef55bf1ebe300cf8aa67c492d1b"
|
}, {
|
||||||
},
|
"ImportPath": "github.com/davecgh/go-spew/spew",
|
||||||
{
|
"Rev": "2df174808ee097f90d259e432cc04442cf60be21"
|
||||||
"ImportPath": "github.com/docker/distribution",
|
}, {
|
||||||
"Comment": "v2.0.0-467-g9038e48",
|
"ImportPath": "github.com/docker/libkv",
|
||||||
"Rev": "9038e48c3b982f8e82281ea486f078a73731ac4e"
|
"Rev": "3732f7ff1b56057c3158f10bceb1e79133025373"
|
||||||
},
|
}, {
|
||||||
{
|
"ImportPath": "github.com/docker/distribution",
|
||||||
"ImportPath": "github.com/docker/docker/api",
|
"Comment": "v2.0.0-467-g9038e48",
|
||||||
"Comment": "v1.4.1-5200-gf39987a",
|
"Rev": "9038e48c3b982f8e82281ea486f078a73731ac4e"
|
||||||
"Rev": "f39987afe8d611407887b3094c03d6ba6a766a67"
|
}, {
|
||||||
},
|
"ImportPath": "github.com/docker/docker/api",
|
||||||
{
|
"Comment": "v1.4.1-5200-gf39987a",
|
||||||
"ImportPath": "github.com/docker/docker/cliconfig",
|
"Rev": "f39987afe8d611407887b3094c03d6ba6a766a67"
|
||||||
"Comment": "v1.4.1-5200-gf39987a",
|
}, {
|
||||||
"Rev": "f39987afe8d611407887b3094c03d6ba6a766a67"
|
"ImportPath": "github.com/docker/docker/cliconfig",
|
||||||
},
|
"Comment": "v1.4.1-5200-gf39987a",
|
||||||
{
|
"Rev": "f39987afe8d611407887b3094c03d6ba6a766a67"
|
||||||
"ImportPath": "github.com/docker/docker/daemon/network",
|
}, {
|
||||||
"Comment": "v1.4.1-5200-gf39987a",
|
"ImportPath": "github.com/docker/docker/daemon/network",
|
||||||
"Rev": "f39987afe8d611407887b3094c03d6ba6a766a67"
|
"Comment": "v1.4.1-5200-gf39987a",
|
||||||
},
|
"Rev": "f39987afe8d611407887b3094c03d6ba6a766a67"
|
||||||
{
|
}, {
|
||||||
"ImportPath": "github.com/docker/docker/graph/tags",
|
"ImportPath": "github.com/docker/docker/graph/tags",
|
||||||
"Comment": "v1.4.1-5200-gf39987a",
|
"Comment": "v1.4.1-5200-gf39987a",
|
||||||
"Rev": "f39987afe8d611407887b3094c03d6ba6a766a67"
|
"Rev": "f39987afe8d611407887b3094c03d6ba6a766a67"
|
||||||
},
|
}, {
|
||||||
{
|
"ImportPath": "github.com/docker/docker/image",
|
||||||
"ImportPath": "github.com/docker/docker/image",
|
"Comment": "v1.4.1-5200-gf39987a",
|
||||||
"Comment": "v1.4.1-5200-gf39987a",
|
"Rev": "f39987afe8d611407887b3094c03d6ba6a766a67"
|
||||||
"Rev": "f39987afe8d611407887b3094c03d6ba6a766a67"
|
}, {
|
||||||
},
|
"ImportPath": "github.com/docker/docker/opts",
|
||||||
{
|
"Comment": "v1.4.1-5200-gf39987a",
|
||||||
"ImportPath": "github.com/docker/docker/opts",
|
"Rev": "f39987afe8d611407887b3094c03d6ba6a766a67"
|
||||||
"Comment": "v1.4.1-5200-gf39987a",
|
}, {
|
||||||
"Rev": "f39987afe8d611407887b3094c03d6ba6a766a67"
|
"ImportPath": "github.com/docker/docker/pkg/archive",
|
||||||
},
|
"Comment": "v1.4.1-5200-gf39987a",
|
||||||
{
|
"Rev": "f39987afe8d611407887b3094c03d6ba6a766a67"
|
||||||
"ImportPath": "github.com/docker/docker/pkg/archive",
|
}, {
|
||||||
"Comment": "v1.4.1-5200-gf39987a",
|
"ImportPath": "github.com/docker/docker/pkg/fileutils",
|
||||||
"Rev": "f39987afe8d611407887b3094c03d6ba6a766a67"
|
"Comment": "v1.4.1-5200-gf39987a",
|
||||||
},
|
"Rev": "f39987afe8d611407887b3094c03d6ba6a766a67"
|
||||||
{
|
}, {
|
||||||
"ImportPath": "github.com/docker/docker/pkg/fileutils",
|
"ImportPath": "github.com/docker/docker/pkg/homedir",
|
||||||
"Comment": "v1.4.1-5200-gf39987a",
|
"Comment": "v1.4.1-5200-gf39987a",
|
||||||
"Rev": "f39987afe8d611407887b3094c03d6ba6a766a67"
|
"Rev": "f39987afe8d611407887b3094c03d6ba6a766a67"
|
||||||
},
|
}, {
|
||||||
{
|
"ImportPath": "github.com/docker/docker/pkg/httputils",
|
||||||
"ImportPath": "github.com/docker/docker/pkg/homedir",
|
"Comment": "v1.4.1-5200-gf39987a",
|
||||||
"Comment": "v1.4.1-5200-gf39987a",
|
"Rev": "f39987afe8d611407887b3094c03d6ba6a766a67"
|
||||||
"Rev": "f39987afe8d611407887b3094c03d6ba6a766a67"
|
}, {
|
||||||
},
|
"ImportPath": "github.com/docker/docker/pkg/ioutils",
|
||||||
{
|
"Comment": "v1.4.1-5200-gf39987a",
|
||||||
"ImportPath": "github.com/docker/docker/pkg/httputils",
|
"Rev": "f39987afe8d611407887b3094c03d6ba6a766a67"
|
||||||
"Comment": "v1.4.1-5200-gf39987a",
|
}, {
|
||||||
"Rev": "f39987afe8d611407887b3094c03d6ba6a766a67"
|
"ImportPath": "github.com/docker/docker/pkg/jsonmessage",
|
||||||
},
|
"Comment": "v1.4.1-5200-gf39987a",
|
||||||
{
|
"Rev": "f39987afe8d611407887b3094c03d6ba6a766a67"
|
||||||
"ImportPath": "github.com/docker/docker/pkg/ioutils",
|
}, {
|
||||||
"Comment": "v1.4.1-5200-gf39987a",
|
"ImportPath": "github.com/docker/docker/pkg/mflag",
|
||||||
"Rev": "f39987afe8d611407887b3094c03d6ba6a766a67"
|
"Comment": "v1.4.1-5200-gf39987a",
|
||||||
},
|
"Rev": "f39987afe8d611407887b3094c03d6ba6a766a67"
|
||||||
{
|
}, {
|
||||||
"ImportPath": "github.com/docker/docker/pkg/jsonmessage",
|
"ImportPath": "github.com/docker/docker/pkg/nat",
|
||||||
"Comment": "v1.4.1-5200-gf39987a",
|
"Comment": "v1.4.1-5200-gf39987a",
|
||||||
"Rev": "f39987afe8d611407887b3094c03d6ba6a766a67"
|
"Rev": "f39987afe8d611407887b3094c03d6ba6a766a67"
|
||||||
},
|
}, {
|
||||||
{
|
"ImportPath": "github.com/docker/docker/pkg/parsers",
|
||||||
"ImportPath": "github.com/docker/docker/pkg/mflag",
|
"Comment": "v1.4.1-5200-gf39987a",
|
||||||
"Comment": "v1.4.1-5200-gf39987a",
|
"Rev": "f39987afe8d611407887b3094c03d6ba6a766a67"
|
||||||
"Rev": "f39987afe8d611407887b3094c03d6ba6a766a67"
|
}, {
|
||||||
},
|
"ImportPath": "github.com/docker/docker/pkg/pools",
|
||||||
{
|
"Comment": "v1.4.1-5200-gf39987a",
|
||||||
"ImportPath": "github.com/docker/docker/pkg/nat",
|
"Rev": "f39987afe8d611407887b3094c03d6ba6a766a67"
|
||||||
"Comment": "v1.4.1-5200-gf39987a",
|
}, {
|
||||||
"Rev": "f39987afe8d611407887b3094c03d6ba6a766a67"
|
"ImportPath": "github.com/docker/docker/pkg/promise",
|
||||||
},
|
"Comment": "v1.4.1-5200-gf39987a",
|
||||||
{
|
"Rev": "f39987afe8d611407887b3094c03d6ba6a766a67"
|
||||||
"ImportPath": "github.com/docker/docker/pkg/parsers",
|
}, {
|
||||||
"Comment": "v1.4.1-5200-gf39987a",
|
"ImportPath": "github.com/docker/docker/pkg/random",
|
||||||
"Rev": "f39987afe8d611407887b3094c03d6ba6a766a67"
|
"Comment": "v1.4.1-5200-gf39987a",
|
||||||
},
|
"Rev": "f39987afe8d611407887b3094c03d6ba6a766a67"
|
||||||
{
|
}, {
|
||||||
"ImportPath": "github.com/docker/docker/pkg/pools",
|
"ImportPath": "github.com/docker/docker/pkg/stdcopy",
|
||||||
"Comment": "v1.4.1-5200-gf39987a",
|
"Comment": "v1.4.1-5200-gf39987a",
|
||||||
"Rev": "f39987afe8d611407887b3094c03d6ba6a766a67"
|
"Rev": "f39987afe8d611407887b3094c03d6ba6a766a67"
|
||||||
},
|
}, {
|
||||||
{
|
"ImportPath": "github.com/docker/docker/pkg/stringid",
|
||||||
"ImportPath": "github.com/docker/docker/pkg/promise",
|
"Comment": "v1.4.1-5200-gf39987a",
|
||||||
"Comment": "v1.4.1-5200-gf39987a",
|
"Rev": "f39987afe8d611407887b3094c03d6ba6a766a67"
|
||||||
"Rev": "f39987afe8d611407887b3094c03d6ba6a766a67"
|
}, {
|
||||||
},
|
"ImportPath": "github.com/docker/docker/pkg/symlink",
|
||||||
{
|
"Comment": "v1.4.1-5200-gf39987a",
|
||||||
"ImportPath": "github.com/docker/docker/pkg/random",
|
"Rev": "f39987afe8d611407887b3094c03d6ba6a766a67"
|
||||||
"Comment": "v1.4.1-5200-gf39987a",
|
}, {
|
||||||
"Rev": "f39987afe8d611407887b3094c03d6ba6a766a67"
|
"ImportPath": "github.com/docker/docker/pkg/system",
|
||||||
},
|
"Comment": "v1.4.1-5200-gf39987a",
|
||||||
{
|
"Rev": "f39987afe8d611407887b3094c03d6ba6a766a67"
|
||||||
"ImportPath": "github.com/docker/docker/pkg/stdcopy",
|
}, {
|
||||||
"Comment": "v1.4.1-5200-gf39987a",
|
"ImportPath": "github.com/docker/docker/pkg/tarsum",
|
||||||
"Rev": "f39987afe8d611407887b3094c03d6ba6a766a67"
|
"Comment": "v1.4.1-5200-gf39987a",
|
||||||
},
|
"Rev": "f39987afe8d611407887b3094c03d6ba6a766a67"
|
||||||
{
|
}, {
|
||||||
"ImportPath": "github.com/docker/docker/pkg/stringid",
|
"ImportPath": "github.com/docker/docker/pkg/term",
|
||||||
"Comment": "v1.4.1-5200-gf39987a",
|
"Comment": "v1.4.1-5200-gf39987a",
|
||||||
"Rev": "f39987afe8d611407887b3094c03d6ba6a766a67"
|
"Rev": "f39987afe8d611407887b3094c03d6ba6a766a67"
|
||||||
},
|
}, {
|
||||||
{
|
"ImportPath": "github.com/docker/docker/pkg/timeutils",
|
||||||
"ImportPath": "github.com/docker/docker/pkg/symlink",
|
"Comment": "v1.4.1-5200-gf39987a",
|
||||||
"Comment": "v1.4.1-5200-gf39987a",
|
"Rev": "f39987afe8d611407887b3094c03d6ba6a766a67"
|
||||||
"Rev": "f39987afe8d611407887b3094c03d6ba6a766a67"
|
}, {
|
||||||
},
|
"ImportPath": "github.com/docker/docker/pkg/tlsconfig",
|
||||||
{
|
"Comment": "v1.4.1-5200-gf39987a",
|
||||||
"ImportPath": "github.com/docker/docker/pkg/system",
|
"Rev": "f39987afe8d611407887b3094c03d6ba6a766a67"
|
||||||
"Comment": "v1.4.1-5200-gf39987a",
|
}, {
|
||||||
"Rev": "f39987afe8d611407887b3094c03d6ba6a766a67"
|
"ImportPath": "github.com/docker/docker/pkg/ulimit",
|
||||||
},
|
"Comment": "v1.4.1-5200-gf39987a",
|
||||||
{
|
"Rev": "f39987afe8d611407887b3094c03d6ba6a766a67"
|
||||||
"ImportPath": "github.com/docker/docker/pkg/tarsum",
|
}, {
|
||||||
"Comment": "v1.4.1-5200-gf39987a",
|
"ImportPath": "github.com/docker/docker/pkg/units",
|
||||||
"Rev": "f39987afe8d611407887b3094c03d6ba6a766a67"
|
"Comment": "v1.4.1-5200-gf39987a",
|
||||||
},
|
"Rev": "f39987afe8d611407887b3094c03d6ba6a766a67"
|
||||||
{
|
}, {
|
||||||
"ImportPath": "github.com/docker/docker/pkg/term",
|
"ImportPath": "github.com/docker/docker/pkg/urlutil",
|
||||||
"Comment": "v1.4.1-5200-gf39987a",
|
"Comment": "v1.4.1-5200-gf39987a",
|
||||||
"Rev": "f39987afe8d611407887b3094c03d6ba6a766a67"
|
"Rev": "f39987afe8d611407887b3094c03d6ba6a766a67"
|
||||||
},
|
}, {
|
||||||
{
|
"ImportPath": "github.com/docker/docker/pkg/useragent",
|
||||||
"ImportPath": "github.com/docker/docker/pkg/timeutils",
|
"Comment": "v1.4.1-5200-gf39987a",
|
||||||
"Comment": "v1.4.1-5200-gf39987a",
|
"Rev": "f39987afe8d611407887b3094c03d6ba6a766a67"
|
||||||
"Rev": "f39987afe8d611407887b3094c03d6ba6a766a67"
|
}, {
|
||||||
},
|
"ImportPath": "github.com/docker/docker/pkg/version",
|
||||||
{
|
"Comment": "v1.4.1-5200-gf39987a",
|
||||||
"ImportPath": "github.com/docker/docker/pkg/tlsconfig",
|
"Rev": "f39987afe8d611407887b3094c03d6ba6a766a67"
|
||||||
"Comment": "v1.4.1-5200-gf39987a",
|
}, {
|
||||||
"Rev": "f39987afe8d611407887b3094c03d6ba6a766a67"
|
"ImportPath": "github.com/docker/docker/registry",
|
||||||
},
|
"Comment": "v1.4.1-5200-gf39987a",
|
||||||
{
|
"Rev": "f39987afe8d611407887b3094c03d6ba6a766a67"
|
||||||
"ImportPath": "github.com/docker/docker/pkg/ulimit",
|
}, {
|
||||||
"Comment": "v1.4.1-5200-gf39987a",
|
"ImportPath": "github.com/docker/docker/runconfig",
|
||||||
"Rev": "f39987afe8d611407887b3094c03d6ba6a766a67"
|
"Comment": "v1.4.1-5200-gf39987a",
|
||||||
},
|
"Rev": "f39987afe8d611407887b3094c03d6ba6a766a67"
|
||||||
{
|
}, {
|
||||||
"ImportPath": "github.com/docker/docker/pkg/units",
|
"ImportPath": "github.com/docker/docker/utils",
|
||||||
"Comment": "v1.4.1-5200-gf39987a",
|
"Comment": "v1.4.1-5200-gf39987a",
|
||||||
"Rev": "f39987afe8d611407887b3094c03d6ba6a766a67"
|
"Rev": "f39987afe8d611407887b3094c03d6ba6a766a67"
|
||||||
},
|
}, {
|
||||||
{
|
"ImportPath": "github.com/docker/docker/volume",
|
||||||
"ImportPath": "github.com/docker/docker/pkg/urlutil",
|
"Comment": "v1.4.1-5200-gf39987a",
|
||||||
"Comment": "v1.4.1-5200-gf39987a",
|
"Rev": "f39987afe8d611407887b3094c03d6ba6a766a67"
|
||||||
"Rev": "f39987afe8d611407887b3094c03d6ba6a766a67"
|
}, {
|
||||||
},
|
"ImportPath": "github.com/docker/libcompose/docker",
|
||||||
{
|
"Rev": "aad672800904307e96a2c21cad1420f3080e0f35"
|
||||||
"ImportPath": "github.com/docker/docker/pkg/useragent",
|
}, {
|
||||||
"Comment": "v1.4.1-5200-gf39987a",
|
"ImportPath": "github.com/docker/libcompose/logger",
|
||||||
"Rev": "f39987afe8d611407887b3094c03d6ba6a766a67"
|
"Rev": "aad672800904307e96a2c21cad1420f3080e0f35"
|
||||||
},
|
}, {
|
||||||
{
|
"ImportPath": "github.com/docker/libcompose/lookup",
|
||||||
"ImportPath": "github.com/docker/docker/pkg/version",
|
"Rev": "aad672800904307e96a2c21cad1420f3080e0f35"
|
||||||
"Comment": "v1.4.1-5200-gf39987a",
|
}, {
|
||||||
"Rev": "f39987afe8d611407887b3094c03d6ba6a766a67"
|
"ImportPath": "github.com/docker/libcompose/project",
|
||||||
},
|
"Rev": "aad672800904307e96a2c21cad1420f3080e0f35"
|
||||||
{
|
}, {
|
||||||
"ImportPath": "github.com/docker/docker/registry",
|
"ImportPath": "github.com/docker/libcompose/utils",
|
||||||
"Comment": "v1.4.1-5200-gf39987a",
|
"Rev": "aad672800904307e96a2c21cad1420f3080e0f35"
|
||||||
"Rev": "f39987afe8d611407887b3094c03d6ba6a766a67"
|
}, {
|
||||||
},
|
"ImportPath": "github.com/docker/libtrust",
|
||||||
{
|
"Rev": "9cbd2a1374f46905c68a4eb3694a130610adc62a"
|
||||||
"ImportPath": "github.com/docker/docker/runconfig",
|
}, {
|
||||||
"Comment": "v1.4.1-5200-gf39987a",
|
"ImportPath": "github.com/elazarl/go-bindata-assetfs",
|
||||||
"Rev": "f39987afe8d611407887b3094c03d6ba6a766a67"
|
"Rev": "d5cac425555ca5cf00694df246e04f05e6a55150"
|
||||||
},
|
}, {
|
||||||
{
|
"ImportPath": "github.com/flynn/go-shlex",
|
||||||
"ImportPath": "github.com/docker/docker/utils",
|
"Rev": "3f9db97f856818214da2e1057f8ad84803971cff"
|
||||||
"Comment": "v1.4.1-5200-gf39987a",
|
}, {
|
||||||
"Rev": "f39987afe8d611407887b3094c03d6ba6a766a67"
|
"ImportPath": "github.com/fsouza/go-dockerclient",
|
||||||
},
|
"Rev": "0239034d42f665efa17fd77c39f891c2f9f32922"
|
||||||
{
|
}, {
|
||||||
"ImportPath": "github.com/docker/docker/volume",
|
"ImportPath": "github.com/gambol99/go-marathon",
|
||||||
"Comment": "v1.4.1-5200-gf39987a",
|
"Rev": "0ba31bcb0d7633ba1888d744c42990eb15281cf1"
|
||||||
"Rev": "f39987afe8d611407887b3094c03d6ba6a766a67"
|
}, {
|
||||||
},
|
"ImportPath": "github.com/gorilla/context",
|
||||||
{
|
"Rev": "215affda49addc4c8ef7e2534915df2c8c35c6cd"
|
||||||
"ImportPath": "github.com/docker/libcompose/docker",
|
}, {
|
||||||
"Rev": "aad672800904307e96a2c21cad1420f3080e0f35"
|
"ImportPath": "github.com/gorilla/handlers",
|
||||||
},
|
"Rev": "40694b40f4a928c062f56849989d3e9cd0570e5f"
|
||||||
{
|
}, {
|
||||||
"ImportPath": "github.com/docker/libcompose/logger",
|
"ImportPath": "github.com/gorilla/mux",
|
||||||
"Rev": "aad672800904307e96a2c21cad1420f3080e0f35"
|
"Rev": "f15e0c49460fd49eebe2bcc8486b05d1bef68d3a"
|
||||||
},
|
}, {
|
||||||
{
|
"ImportPath": "github.com/hashicorp/consul/api",
|
||||||
"ImportPath": "github.com/docker/libcompose/lookup",
|
"Comment": "v0.5.2-313-gde08067",
|
||||||
"Rev": "aad672800904307e96a2c21cad1420f3080e0f35"
|
"Rev": "de080672fee9e6104572eeea89eccdca135bb918"
|
||||||
},
|
}, {
|
||||||
{
|
"ImportPath": "github.com/mailgun/log",
|
||||||
"ImportPath": "github.com/docker/libcompose/project",
|
"Rev": "44874009257d4d47ba9806f1b7f72a32a015e4d8"
|
||||||
"Rev": "aad672800904307e96a2c21cad1420f3080e0f35"
|
}, {
|
||||||
},
|
"ImportPath": "github.com/mailgun/manners",
|
||||||
{
|
"Comment": "0.3.1-30-g37136f7",
|
||||||
"ImportPath": "github.com/docker/libcompose/utils",
|
"Rev": "37136f736785d7c6aa3b9a27b4b2dd1028ca6d79"
|
||||||
"Rev": "aad672800904307e96a2c21cad1420f3080e0f35"
|
}, {
|
||||||
},
|
"ImportPath": "github.com/mailgun/oxy/cbreaker",
|
||||||
{
|
"Rev": "547c334d658398c05b346c0b79d8f47ba2e1473b"
|
||||||
"ImportPath": "github.com/docker/libtrust",
|
}, {
|
||||||
"Rev": "9cbd2a1374f46905c68a4eb3694a130610adc62a"
|
"ImportPath": "github.com/mailgun/oxy/forward",
|
||||||
},
|
"Rev": "547c334d658398c05b346c0b79d8f47ba2e1473b"
|
||||||
{
|
}, {
|
||||||
"ImportPath": "github.com/elazarl/go-bindata-assetfs",
|
"ImportPath": "github.com/mailgun/oxy/memmetrics",
|
||||||
"Rev": "d5cac425555ca5cf00694df246e04f05e6a55150"
|
"Rev": "547c334d658398c05b346c0b79d8f47ba2e1473b"
|
||||||
},
|
}, {
|
||||||
{
|
"ImportPath": "github.com/mailgun/oxy/roundrobin",
|
||||||
"ImportPath": "github.com/flynn/go-shlex",
|
"Rev": "547c334d658398c05b346c0b79d8f47ba2e1473b"
|
||||||
"Rev": "3f9db97f856818214da2e1057f8ad84803971cff"
|
}, {
|
||||||
},
|
"ImportPath": "github.com/mailgun/oxy/utils",
|
||||||
{
|
"Rev": "547c334d658398c05b346c0b79d8f47ba2e1473b"
|
||||||
"ImportPath": "github.com/fsouza/go-dockerclient",
|
}, {
|
||||||
"Rev": "0239034d42f665efa17fd77c39f891c2f9f32922"
|
"ImportPath": "github.com/mailgun/predicate",
|
||||||
},
|
"Rev": "cb0bff91a7ab7cf7571e661ff883fc997bc554a3"
|
||||||
{
|
}, {
|
||||||
"ImportPath": "github.com/gambol99/go-marathon",
|
"ImportPath": "github.com/mailgun/timetools",
|
||||||
"Rev": "0ba31bcb0d7633ba1888d744c42990eb15281cf1"
|
"Rev": "fd192d755b00c968d312d23f521eb0cdc6f66bd0"
|
||||||
},
|
}, {
|
||||||
{
|
"ImportPath": "github.com/samuel/go-zookeeper/zk",
|
||||||
"ImportPath": "github.com/gorilla/context",
|
"Rev": "fa6674abf3f4580b946a01bf7a1ce4ba8766205b"
|
||||||
"Rev": "215affda49addc4c8ef7e2534915df2c8c35c6cd"
|
}, {
|
||||||
},
|
"ImportPath": "github.com/opencontainers/runc/libcontainer/user",
|
||||||
{
|
"Comment": "v0.0.4-21-g4ab1324",
|
||||||
"ImportPath": "github.com/gorilla/handlers",
|
"Rev": "4ab132458fc3e9dbeea624153e0331952dc4c8d5"
|
||||||
"Rev": "40694b40f4a928c062f56849989d3e9cd0570e5f"
|
}, {
|
||||||
},
|
"ImportPath": "github.com/samalba/dockerclient",
|
||||||
{
|
"Rev": "cfb489c624b635251a93e74e1e90eb0959c5367f"
|
||||||
"ImportPath": "github.com/gorilla/mux",
|
}, {
|
||||||
"Rev": "f15e0c49460fd49eebe2bcc8486b05d1bef68d3a"
|
"ImportPath": "github.com/thoas/stats",
|
||||||
},
|
"Rev": "54ed61c2b47e263ae2f01b86837b0c4bd1da28e8"
|
||||||
{
|
}, {
|
||||||
"ImportPath": "github.com/hashicorp/consul/api",
|
"ImportPath": "github.com/unrolled/render",
|
||||||
"Comment": "v0.5.2-313-gde08067",
|
"Rev": "26b4e3aac686940fe29521545afad9966ddfc80c"
|
||||||
"Rev": "de080672fee9e6104572eeea89eccdca135bb918"
|
}, {
|
||||||
},
|
"ImportPath": "github.com/vdemeester/shakers",
|
||||||
{
|
"Rev": "8fe734f75f3a70b651cbfbf8a55a009da09e8dc5"
|
||||||
"ImportPath": "github.com/mailgun/log",
|
}, {
|
||||||
"Rev": "44874009257d4d47ba9806f1b7f72a32a015e4d8"
|
"ImportPath": "golang.org/x/net/context",
|
||||||
},
|
"Rev": "d9558e5c97f85372afee28cf2b6059d7d3818919"
|
||||||
{
|
}, {
|
||||||
"ImportPath": "github.com/mailgun/manners",
|
"ImportPath": "gopkg.in/alecthomas/kingpin.v2",
|
||||||
"Comment": "0.3.1-30-g37136f7",
|
"Comment": "v2.0.12",
|
||||||
"Rev": "37136f736785d7c6aa3b9a27b4b2dd1028ca6d79"
|
"Rev": "639879d6110b1b0409410c7b737ef0bb18325038"
|
||||||
},
|
}, {
|
||||||
{
|
"ImportPath": "gopkg.in/check.v1",
|
||||||
"ImportPath": "github.com/mailgun/oxy/cbreaker",
|
"Rev": "11d3bc7aa68e238947792f30573146a3231fc0f1"
|
||||||
"Rev": "547c334d658398c05b346c0b79d8f47ba2e1473b"
|
}, {
|
||||||
},
|
"ImportPath": "gopkg.in/fsnotify.v1",
|
||||||
{
|
"Comment": "v1.2.0",
|
||||||
"ImportPath": "github.com/mailgun/oxy/forward",
|
"Rev": "96c060f6a6b7e0d6f75fddd10efeaca3e5d1bcb0"
|
||||||
"Rev": "547c334d658398c05b346c0b79d8f47ba2e1473b"
|
}, {
|
||||||
},
|
"ImportPath": "gopkg.in/mgo.v2/bson",
|
||||||
{
|
"Comment": "r2015.06.03-5-g22287ba",
|
||||||
"ImportPath": "github.com/mailgun/oxy/memmetrics",
|
"Rev": "22287bab4379e1fbf6002fb4eb769888f3fb224c"
|
||||||
"Rev": "547c334d658398c05b346c0b79d8f47ba2e1473b"
|
}, {
|
||||||
},
|
"ImportPath": "gopkg.in/yaml.v2",
|
||||||
{
|
"Rev": "7ad95dd0798a40da1ccdff6dff35fd177b5edf40"
|
||||||
"ImportPath": "github.com/mailgun/oxy/roundrobin",
|
}]
|
||||||
"Rev": "547c334d658398c05b346c0b79d8f47ba2e1473b"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"ImportPath": "github.com/mailgun/oxy/utils",
|
|
||||||
"Rev": "547c334d658398c05b346c0b79d8f47ba2e1473b"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"ImportPath": "github.com/mailgun/predicate",
|
|
||||||
"Rev": "cb0bff91a7ab7cf7571e661ff883fc997bc554a3"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"ImportPath": "github.com/mailgun/timetools",
|
|
||||||
"Rev": "fd192d755b00c968d312d23f521eb0cdc6f66bd0"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"ImportPath": "github.com/opencontainers/runc/libcontainer/user",
|
|
||||||
"Comment": "v0.0.4-21-g4ab1324",
|
|
||||||
"Rev": "4ab132458fc3e9dbeea624153e0331952dc4c8d5"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"ImportPath": "github.com/samalba/dockerclient",
|
|
||||||
"Rev": "cfb489c624b635251a93e74e1e90eb0959c5367f"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"ImportPath": "github.com/thoas/stats",
|
|
||||||
"Rev": "54ed61c2b47e263ae2f01b86837b0c4bd1da28e8"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"ImportPath": "github.com/unrolled/render",
|
|
||||||
"Rev": "26b4e3aac686940fe29521545afad9966ddfc80c"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"ImportPath": "github.com/vdemeester/shakers",
|
|
||||||
"Rev": "8fe734f75f3a70b651cbfbf8a55a009da09e8dc5"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"ImportPath": "golang.org/x/net/context",
|
|
||||||
"Rev": "d9558e5c97f85372afee28cf2b6059d7d3818919"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"ImportPath": "gopkg.in/alecthomas/kingpin.v2",
|
|
||||||
"Comment": "v2.0.12",
|
|
||||||
"Rev": "639879d6110b1b0409410c7b737ef0bb18325038"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"ImportPath": "gopkg.in/check.v1",
|
|
||||||
"Rev": "11d3bc7aa68e238947792f30573146a3231fc0f1"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"ImportPath": "gopkg.in/fsnotify.v1",
|
|
||||||
"Comment": "v1.2.0",
|
|
||||||
"Rev": "96c060f6a6b7e0d6f75fddd10efeaca3e5d1bcb0"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"ImportPath": "gopkg.in/mgo.v2/bson",
|
|
||||||
"Comment": "r2015.06.03-5-g22287ba",
|
|
||||||
"Rev": "22287bab4379e1fbf6002fb4eb769888f3fb224c"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"ImportPath": "gopkg.in/yaml.v2",
|
|
||||||
"Rev": "7ad95dd0798a40da1ccdff6dff35fd177b5edf40"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|
3
Makefile
3
Makefile
|
@ -47,6 +47,9 @@ validate-govet: build
|
||||||
build: dist
|
build: dist
|
||||||
docker build -t "$(TRAEFIK_DEV_IMAGE)" -f build.Dockerfile .
|
docker build -t "$(TRAEFIK_DEV_IMAGE)" -f build.Dockerfile .
|
||||||
|
|
||||||
|
build-no-cache: dist
|
||||||
|
docker build --no-cache -t "$(TRAEFIK_DEV_IMAGE)" -f build.Dockerfile .
|
||||||
|
|
||||||
shell: build
|
shell: build
|
||||||
$(DOCKER_RUN_TRAEFIK) /bin/bash
|
$(DOCKER_RUN_TRAEFIK) /bin/bash
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@ ___
|
||||||
|
|
||||||
|
|
||||||
Træfɪk is a modern HTTP reverse proxy and load balancer made to deploy microservices with ease.
|
Træfɪk is a modern HTTP reverse proxy and load balancer made to deploy microservices with ease.
|
||||||
It supports several backends ([Docker :whale:](https://www.docker.com/), [Mesos/Marathon](https://mesosphere.github.io/marathon/), [Consul](https://consul.io/), [Etcd](https://coreos.com/etcd/), Rest API, file...) to manage its configuration automatically and dynamically.
|
It supports several backends ([Docker :whale:](https://www.docker.com/), [Mesos/Marathon](https://mesosphere.github.io/marathon/), [Consul](https://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.
|
||||||
|
|
||||||
|
|
||||||
# Features
|
# Features
|
||||||
|
|
14
boltdb.go
Normal file
14
boltdb.go
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
type BoltDbProvider struct {
|
||||||
|
Watch bool
|
||||||
|
Endpoint string
|
||||||
|
Prefix string
|
||||||
|
Filename string
|
||||||
|
KvProvider *KvProvider
|
||||||
|
}
|
||||||
|
|
||||||
|
func (provider *BoltDbProvider) Provide(configurationChan chan<- configMessage) error {
|
||||||
|
provider.KvProvider = NewBoltDbProvider(provider)
|
||||||
|
return provider.KvProvider.provide(configurationChan)
|
||||||
|
}
|
|
@ -10,7 +10,6 @@ type GlobalConfiguration struct {
|
||||||
GraceTimeOut int64
|
GraceTimeOut int64
|
||||||
AccessLogsFile string
|
AccessLogsFile string
|
||||||
TraefikLogsFile string
|
TraefikLogsFile string
|
||||||
TraefikLogsStdout bool
|
|
||||||
CertFile, KeyFile string
|
CertFile, KeyFile string
|
||||||
LogLevel string
|
LogLevel string
|
||||||
Docker *DockerProvider
|
Docker *DockerProvider
|
||||||
|
@ -18,6 +17,9 @@ type GlobalConfiguration struct {
|
||||||
Web *WebProvider
|
Web *WebProvider
|
||||||
Marathon *MarathonProvider
|
Marathon *MarathonProvider
|
||||||
Consul *ConsulProvider
|
Consul *ConsulProvider
|
||||||
|
Etcd *EtcdProvider
|
||||||
|
Zookeeper *ZookepperProvider
|
||||||
|
Boltdb *BoltDbProvider
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewGlobalConfiguration() *GlobalConfiguration {
|
func NewGlobalConfiguration() *GlobalConfiguration {
|
||||||
|
@ -26,7 +28,6 @@ func NewGlobalConfiguration() *GlobalConfiguration {
|
||||||
globalConfiguration.Port = ":80"
|
globalConfiguration.Port = ":80"
|
||||||
globalConfiguration.GraceTimeOut = 10
|
globalConfiguration.GraceTimeOut = 10
|
||||||
globalConfiguration.LogLevel = "ERROR"
|
globalConfiguration.LogLevel = "ERROR"
|
||||||
globalConfiguration.TraefikLogsStdout = true
|
|
||||||
|
|
||||||
return globalConfiguration
|
return globalConfiguration
|
||||||
}
|
}
|
||||||
|
|
167
consul.go
167
consul.go
|
@ -1,165 +1,14 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"net/http"
|
|
||||||
"strings"
|
|
||||||
"text/template"
|
|
||||||
|
|
||||||
"github.com/BurntSushi/toml"
|
|
||||||
"github.com/BurntSushi/ty/fun"
|
|
||||||
log "github.com/Sirupsen/logrus"
|
|
||||||
"github.com/hashicorp/consul/api"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Key struct {
|
|
||||||
Value string
|
|
||||||
}
|
|
||||||
|
|
||||||
type ConsulProvider struct {
|
type ConsulProvider struct {
|
||||||
Watch bool
|
Watch bool
|
||||||
Endpoint string
|
Endpoint string
|
||||||
Prefix string
|
Prefix string
|
||||||
Filename string
|
Filename string
|
||||||
consulClient *api.Client
|
KvProvider *KvProvider
|
||||||
}
|
}
|
||||||
|
|
||||||
var kvClient *api.KV
|
func (provider *ConsulProvider) Provide(configurationChan chan<- configMessage) error {
|
||||||
|
provider.KvProvider = NewConsulProvider(provider)
|
||||||
var ConsulFuncMap = template.FuncMap{
|
return provider.KvProvider.provide(configurationChan)
|
||||||
"List": func(keys ...string) []string {
|
|
||||||
joinedKeys := strings.Join(keys, "")
|
|
||||||
keysPairs, _, err := kvClient.Keys(joinedKeys, "/", nil)
|
|
||||||
if err != nil {
|
|
||||||
log.Error("Error getting keys ", joinedKeys, err)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
keysPairs = fun.Filter(func(key string) bool {
|
|
||||||
if key == joinedKeys {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}, keysPairs).([]string)
|
|
||||||
return keysPairs
|
|
||||||
},
|
|
||||||
"Get": func(keys ...string) string {
|
|
||||||
joinedKeys := strings.Join(keys, "")
|
|
||||||
keyPair, _, err := kvClient.Get(joinedKeys, nil)
|
|
||||||
if err != nil {
|
|
||||||
log.Error("Error getting key ", joinedKeys, err)
|
|
||||||
return ""
|
|
||||||
} else if keyPair == nil {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
return string(keyPair.Value)
|
|
||||||
},
|
|
||||||
"Last": func(key string) string {
|
|
||||||
splittedKey := strings.Split(key, "/")
|
|
||||||
return splittedKey[len(splittedKey)-2]
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewConsulProvider() *ConsulProvider {
|
|
||||||
consulProvider := new(ConsulProvider)
|
|
||||||
// default values
|
|
||||||
consulProvider.Watch = true
|
|
||||||
consulProvider.Prefix = "traefik"
|
|
||||||
|
|
||||||
return consulProvider
|
|
||||||
}
|
|
||||||
|
|
||||||
func (provider *ConsulProvider) Provide(configurationChan chan<- configMessage) {
|
|
||||||
config := &api.Config{
|
|
||||||
Address: provider.Endpoint,
|
|
||||||
Scheme: "http",
|
|
||||||
HttpClient: http.DefaultClient,
|
|
||||||
}
|
|
||||||
consulClient, _ := api.NewClient(config)
|
|
||||||
provider.consulClient = consulClient
|
|
||||||
if provider.Watch {
|
|
||||||
keypairs, meta, err := consulClient.KV().Keys("", "", nil)
|
|
||||||
if keypairs == nil {
|
|
||||||
log.Error("Key was not found")
|
|
||||||
} else if err != nil {
|
|
||||||
log.Error("Error connecting to consul %s", err)
|
|
||||||
} else {
|
|
||||||
var waitIndex uint64
|
|
||||||
waitIndex = meta.LastIndex
|
|
||||||
go func() {
|
|
||||||
for {
|
|
||||||
opts := api.QueryOptions{
|
|
||||||
WaitIndex: waitIndex,
|
|
||||||
}
|
|
||||||
keypairs, meta, err := consulClient.KV().Keys("", "", &opts)
|
|
||||||
if keypairs == nil {
|
|
||||||
log.Error("Key was not found")
|
|
||||||
} else if err != nil {
|
|
||||||
log.Error("Error connecting to consul %s", err)
|
|
||||||
} else {
|
|
||||||
waitIndex = meta.LastIndex
|
|
||||||
configuration := provider.loadConsulConfig()
|
|
||||||
if configuration != nil {
|
|
||||||
configurationChan <- configMessage{"consul", configuration}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
configuration := provider.loadConsulConfig()
|
|
||||||
configurationChan <- configMessage{"consul", configuration}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (provider *ConsulProvider) loadConsulConfig() *Configuration {
|
|
||||||
configuration := new(Configuration)
|
|
||||||
services := []*api.CatalogService{}
|
|
||||||
kvClient = provider.consulClient.KV()
|
|
||||||
|
|
||||||
servicesName, _, _ := provider.consulClient.Catalog().Services(nil)
|
|
||||||
for serviceName := range servicesName {
|
|
||||||
catalogServices, _, _ := provider.consulClient.Catalog().Service(serviceName, "", nil)
|
|
||||||
for _, catalogService := range catalogServices {
|
|
||||||
services = append(services, catalogService)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
templateObjects := struct {
|
|
||||||
Services []*api.CatalogService
|
|
||||||
}{
|
|
||||||
services,
|
|
||||||
}
|
|
||||||
|
|
||||||
tmpl := template.New(provider.Filename).Funcs(ConsulFuncMap)
|
|
||||||
if len(provider.Filename) > 0 {
|
|
||||||
_, err := tmpl.ParseFiles(provider.Filename)
|
|
||||||
if err != nil {
|
|
||||||
log.Error("Error reading file", err)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
buf, err := Asset("providerTemplates/consul.tmpl")
|
|
||||||
if err != nil {
|
|
||||||
log.Error("Error reading file", err)
|
|
||||||
}
|
|
||||||
_, err = tmpl.Parse(string(buf))
|
|
||||||
if err != nil {
|
|
||||||
log.Error("Error reading file", err)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var buffer bytes.Buffer
|
|
||||||
|
|
||||||
err := tmpl.Execute(&buffer, templateObjects)
|
|
||||||
if err != nil {
|
|
||||||
log.Error("Error with consul template:", err)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := toml.Decode(buffer.String(), configuration); err != nil {
|
|
||||||
log.Error("Error creating consul configuration:", err)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return configuration
|
|
||||||
}
|
}
|
||||||
|
|
18
docker.go
18
docker.go
|
@ -22,15 +22,6 @@ type DockerProvider struct {
|
||||||
Domain string
|
Domain string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewDockerProvider() *DockerProvider {
|
|
||||||
dockerProvider := new(DockerProvider)
|
|
||||||
// default
|
|
||||||
dockerProvider.Watch = true
|
|
||||||
dockerProvider.Domain = "traefik"
|
|
||||||
|
|
||||||
return dockerProvider
|
|
||||||
}
|
|
||||||
|
|
||||||
var DockerFuncMap = template.FuncMap{
|
var DockerFuncMap = template.FuncMap{
|
||||||
"getBackend": func(container docker.Container) string {
|
"getBackend": func(container docker.Container) string {
|
||||||
for key, value := range container.Config.Labels {
|
for key, value := range container.Config.Labels {
|
||||||
|
@ -65,13 +56,15 @@ var DockerFuncMap = template.FuncMap{
|
||||||
"getHost": getHost,
|
"getHost": getHost,
|
||||||
}
|
}
|
||||||
|
|
||||||
func (provider *DockerProvider) Provide(configurationChan chan<- configMessage) {
|
func (provider *DockerProvider) Provide(configurationChan chan<- configMessage) error {
|
||||||
if dockerClient, err := docker.NewClient(provider.Endpoint); err != nil {
|
if dockerClient, err := docker.NewClient(provider.Endpoint); err != nil {
|
||||||
log.Fatalf("Failed to create a client for docker, error: %s", err)
|
log.Errorf("Failed to create a client for docker, error: %s", err)
|
||||||
|
return err
|
||||||
} else {
|
} else {
|
||||||
err := dockerClient.Ping()
|
err := dockerClient.Ping()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Docker connection error %+v", err)
|
log.Errorf("Docker connection error %+v", err)
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
log.Debug("Docker connection established")
|
log.Debug("Docker connection established")
|
||||||
if provider.Watch {
|
if provider.Watch {
|
||||||
|
@ -108,6 +101,7 @@ func (provider *DockerProvider) Provide(configurationChan chan<- configMessage)
|
||||||
configuration := provider.loadDockerConfig(dockerClient)
|
configuration := provider.loadDockerConfig(dockerClient)
|
||||||
configurationChan <- configMessage{"docker", configuration}
|
configurationChan <- configMessage{"docker", configuration}
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (provider *DockerProvider) loadDockerConfig(dockerClient *docker.Client) *Configuration {
|
func (provider *DockerProvider) loadDockerConfig(dockerClient *docker.Client) *Configuration {
|
||||||
|
|
|
@ -1,35 +0,0 @@
|
||||||
![Træfɪk](http://traefik.github.io/traefik.logo.svg "Træfɪk")
|
|
||||||
___
|
|
||||||
|
|
||||||
# Roadmap
|
|
||||||
|
|
||||||
* Add traefik.protocol label
|
|
||||||
* Providers recovery
|
|
||||||
* tls client verification
|
|
||||||
* Default configuration values
|
|
||||||
* Retry with streams
|
|
||||||
* Licence
|
|
||||||
* Add traefik.indlude all/enabled policy
|
|
||||||
* Consul support
|
|
||||||
* README
|
|
||||||
* API enhancements
|
|
||||||
* Godoc
|
|
||||||
|
|
||||||
|
|
||||||
* Kubernetes support
|
|
||||||
* Etcd support
|
|
||||||
* Smart configuration diff
|
|
||||||
|
|
||||||
|
|
||||||
* ~~GraceTimeout~~
|
|
||||||
* ~~Weights~~
|
|
||||||
* ~~Filter traefik.enabled=false apps~~
|
|
||||||
* ~~Filter no exposed port apps~~
|
|
||||||
* ~~Logs~~
|
|
||||||
* ~~SSL frontend support~~
|
|
||||||
* ~~SSL backends support~~
|
|
||||||
* ~~Static files with go-bindata~~
|
|
||||||
* ~~Metrics~~
|
|
||||||
* ~~Dockerfile~~
|
|
||||||
* ~~Use Negroni middlewares for metrics, grace, logs~~
|
|
||||||
* ~~Website~~
|
|
238
docs/index.md
238
docs/index.md
|
@ -11,6 +11,9 @@ ___
|
||||||
* [Docker backend](#docker)
|
* [Docker backend](#docker)
|
||||||
* [Mesos/Marathon backend](#marathon)
|
* [Mesos/Marathon backend](#marathon)
|
||||||
* [Consul backend](#consul)
|
* [Consul backend](#consul)
|
||||||
|
* [Etcd backend](#etcd)
|
||||||
|
* [Zookeeper backend](#zk)
|
||||||
|
* [Boltdb backend](#boltdb)
|
||||||
* [Benchmarks](#benchmarks)
|
* [Benchmarks](#benchmarks)
|
||||||
|
|
||||||
|
|
||||||
|
@ -257,106 +260,77 @@ $ curl -s "http://localhost:8080/health" | jq .
|
||||||
* ```/api```: ```GET``` configuration for all providers
|
* ```/api```: ```GET``` configuration for all providers
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
$ curl -s "http://localhost:8082/api" | jq .
|
$ curl -s "http://localhost:8080/api" | jq .
|
||||||
{
|
{
|
||||||
"file": {
|
"file": {
|
||||||
"Frontends": {
|
"Frontends": {
|
||||||
"frontend-traefik": {
|
"frontend2": {
|
||||||
"Routes": {
|
"Routes": {
|
||||||
"route-host-traefik": {
|
"test_2": {
|
||||||
"Value": "traefik.docker.localhost",
|
"Value": "/test",
|
||||||
"Rule": "Host"
|
"Rule": "Path"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Backend": "backend-test2"
|
"Backend": "backend1"
|
||||||
},
|
},
|
||||||
"frontend-test": {
|
"frontend1": {
|
||||||
"Routes": {
|
"Routes": {
|
||||||
"route-host-test": {
|
"test_1": {
|
||||||
"Value": "test.docker.localhost",
|
"Value": "test.localhost",
|
||||||
"Rule": "Host"
|
"Rule": "Host"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Backend": "backend-test1"
|
"Backend": "backend2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Backends": {
|
"Backends": {
|
||||||
"backend-test2": {
|
"backend2": {
|
||||||
|
"LoadBalancer": {
|
||||||
|
"Method": "drr"
|
||||||
|
},
|
||||||
|
"CircuitBreaker": null,
|
||||||
"Servers": {
|
"Servers": {
|
||||||
"server-stoic_brattain": {
|
"server2": {
|
||||||
"Weight": 0,
|
"Weight": 2,
|
||||||
"Url": "http://172.17.0.8:80"
|
"URL": "http://172.17.0.5:80"
|
||||||
},
|
},
|
||||||
"server-jovial_khorana": {
|
"server1": {
|
||||||
"Weight": 0,
|
"Weight": 1,
|
||||||
"Url": "http://172.17.0.12:80"
|
"URL": "http://172.17.0.4:80"
|
||||||
},
|
|
||||||
"server-jovial_franklin": {
|
|
||||||
"Weight": 0,
|
|
||||||
"Url": "http://172.17.0.11:80"
|
|
||||||
},
|
|
||||||
"server-elegant_panini": {
|
|
||||||
"Weight": 0,
|
|
||||||
"Url": "http://172.17.0.9:80"
|
|
||||||
},
|
|
||||||
"server-adoring_elion": {
|
|
||||||
"Weight": 0,
|
|
||||||
"Url": "http://172.17.0.10:80"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"backend-test1": {
|
"backend1": {
|
||||||
|
"LoadBalancer": {
|
||||||
|
"Method": "wrr"
|
||||||
|
},
|
||||||
|
"CircuitBreaker": {
|
||||||
|
"Expression": "NetworkErrorRatio() > 0.5"
|
||||||
|
},
|
||||||
"Servers": {
|
"Servers": {
|
||||||
"server-trusting_wozniak": {
|
"server2": {
|
||||||
"Weight": 0,
|
"Weight": 1,
|
||||||
"Url": "http://172.17.0.5:80"
|
"URL": "http://172.17.0.3:80"
|
||||||
},
|
},
|
||||||
"server-sharp_jang": {
|
"server1": {
|
||||||
"Weight": 0,
|
"Weight": 10,
|
||||||
"Url": "http://172.17.0.7:80"
|
"URL": "http://172.17.0.2:80"
|
||||||
},
|
|
||||||
"server-dreamy_feynman": {
|
|
||||||
"Weight": 0,
|
|
||||||
"Url": "http://172.17.0.6:80"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
"marathon": {
|
|
||||||
"Frontends": {
|
|
||||||
"frontend-marathon": {
|
|
||||||
"Routes": {
|
|
||||||
"route-host-marathon": {
|
|
||||||
"Value": "marathon.docker.localhost",
|
|
||||||
"Rule": "Host"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Backend": "backend-marathon"
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"Backends": {
|
|
||||||
"backend-marathon": {
|
|
||||||
"Servers": {
|
|
||||||
"server-marathon-1": {
|
|
||||||
"Weight": 0,
|
|
||||||
"Url": "http://172.17.0.8:802"
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
* ```/api/{provider}```: ```GET``` or ```PUT``` provider
|
* ```/api/providers```: ```GET``` providers
|
||||||
* ```/api/{provider}/backends```: ```GET``` backends
|
* ```/api/providers/{provider}```: ```GET``` or ```PUT``` provider
|
||||||
* ```/api/{provider}/backends/{backend}```: ```GET``` a backend
|
* ```/api/providers/{provider}/backends```: ```GET``` backends
|
||||||
* ```/api/{provider}/backends/{backend}/servers```: ```GET``` servers in a backend
|
* ```/api/providers/{provider}/backends/{backend}```: ```GET``` a backend
|
||||||
* ```/api/{provider}/backends/{backend}/servers/{server}```: ```GET``` a server in a backend
|
* ```/api/providers/{provider}/backends/{backend}/servers```: ```GET``` servers in a backend
|
||||||
* ```/api/{provider}/frontends```: ```GET``` frontends
|
* ```/api/providers/{provider}/backends/{backend}/servers/{server}```: ```GET``` a server in a backend
|
||||||
* ```/api/{provider}/frontends/{frontend}```: ```GET``` a frontend
|
* ```/api/providers/{provider}/frontends```: ```GET``` frontends
|
||||||
|
* ```/api/providers/{provider}/frontends/{frontend}```: ```GET``` a frontend
|
||||||
|
|
||||||
|
|
||||||
## <a id="docker"></a> Docker backend
|
## <a id="docker"></a> Docker backend
|
||||||
|
@ -508,6 +482,126 @@ prefix = "traefik"
|
||||||
# filename = "consul.tmpl"
|
# filename = "consul.tmpl"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## <a id="etcd"></a> Etcd backend
|
||||||
|
|
||||||
|
Træfɪk can be configured to use Etcd as a backend configuration:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
################################################################
|
||||||
|
# Etcd configuration backend
|
||||||
|
################################################################
|
||||||
|
|
||||||
|
# Enable Etcd configuration backend
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
#
|
||||||
|
# [etcd]
|
||||||
|
|
||||||
|
# Etcd server endpoint
|
||||||
|
#
|
||||||
|
# Required
|
||||||
|
#
|
||||||
|
# endpoint = "127.0.0.1:4001"
|
||||||
|
|
||||||
|
# Enable watch Etcd changes
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
#
|
||||||
|
# watch = true
|
||||||
|
|
||||||
|
# Prefix used for KV store.
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
#
|
||||||
|
# prefix = "/traefik"
|
||||||
|
|
||||||
|
# Override default configuration template. For advanced users :)
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
#
|
||||||
|
# filename = "etcd.tmpl"
|
||||||
|
```
|
||||||
|
|
||||||
|
## <a id="zk"></a> Zookeeper backend
|
||||||
|
|
||||||
|
Træfɪk can be configured to use Zookeeper as a backend configuration:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
################################################################
|
||||||
|
# Zookeeper configuration backend
|
||||||
|
################################################################
|
||||||
|
|
||||||
|
# Enable Zookeeperconfiguration backend
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
#
|
||||||
|
# [zookeeper]
|
||||||
|
|
||||||
|
# Zookeeper server endpoint
|
||||||
|
#
|
||||||
|
# Required
|
||||||
|
#
|
||||||
|
# endpoint = "127.0.0.1:2181"
|
||||||
|
|
||||||
|
# Enable watch Zookeeper changes
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
#
|
||||||
|
# watch = true
|
||||||
|
|
||||||
|
# Prefix used for KV store.
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
#
|
||||||
|
# prefix = "/traefik"
|
||||||
|
|
||||||
|
# Override default configuration template. For advanced users :)
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
#
|
||||||
|
# filename = "zookeeper.tmpl"
|
||||||
|
```
|
||||||
|
|
||||||
|
## <a id="boltdb"></a> BoltDB backend
|
||||||
|
|
||||||
|
Træfɪk can be configured to use BoltDB as a backend configuration:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
################################################################
|
||||||
|
# BoltDB configuration backend
|
||||||
|
################################################################
|
||||||
|
|
||||||
|
# Enable BoltDB configuration backend
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
#
|
||||||
|
# [boltdb]
|
||||||
|
|
||||||
|
# BoltDB file
|
||||||
|
#
|
||||||
|
# Required
|
||||||
|
#
|
||||||
|
# endpoint = "/my.db"
|
||||||
|
|
||||||
|
# Enable watch BoltDB changes
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
#
|
||||||
|
# watch = true
|
||||||
|
|
||||||
|
# Prefix used for KV store.
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
#
|
||||||
|
# prefix = "/traefik"
|
||||||
|
|
||||||
|
# Override default configuration template. For advanced users :)
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
#
|
||||||
|
# filename = "boltdb.tmpl"
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
## <a id="benchmarks"></a> Benchmarks
|
## <a id="benchmarks"></a> Benchmarks
|
||||||
|
|
||||||
|
|
14
etcd.go
Normal file
14
etcd.go
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
type EtcdProvider struct {
|
||||||
|
Watch bool
|
||||||
|
Endpoint string
|
||||||
|
Prefix string
|
||||||
|
Filename string
|
||||||
|
KvProvider *KvProvider
|
||||||
|
}
|
||||||
|
|
||||||
|
func (provider *EtcdProvider) Provide(configurationChan chan<- configMessage) error {
|
||||||
|
provider.KvProvider = NewEtcdProvider(provider)
|
||||||
|
return provider.KvProvider.provide(configurationChan)
|
||||||
|
}
|
63
file.go
63
file.go
|
@ -15,60 +15,49 @@ type FileProvider struct {
|
||||||
Filename string
|
Filename string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewFileProvider() *FileProvider {
|
func (provider *FileProvider) Provide(configurationChan chan<- configMessage) error {
|
||||||
fileProvider := new(FileProvider)
|
|
||||||
// default values
|
|
||||||
fileProvider.Watch = true
|
|
||||||
|
|
||||||
return fileProvider
|
|
||||||
}
|
|
||||||
|
|
||||||
func (provider *FileProvider) Provide(configurationChan chan<- configMessage) {
|
|
||||||
watcher, err := fsnotify.NewWatcher()
|
watcher, err := fsnotify.NewWatcher()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("Error creating file watcher", err)
|
log.Error("Error creating file watcher", err)
|
||||||
return
|
return err
|
||||||
}
|
}
|
||||||
defer watcher.Close()
|
|
||||||
|
|
||||||
file, err := os.Open(provider.Filename)
|
file, err := os.Open(provider.Filename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("Error opening file", err)
|
log.Error("Error opening file", err)
|
||||||
return
|
return err
|
||||||
}
|
}
|
||||||
defer file.Close()
|
defer file.Close()
|
||||||
|
|
||||||
done := make(chan bool)
|
|
||||||
// Process events
|
|
||||||
go func() {
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case event := <-watcher.Events:
|
|
||||||
if strings.Contains(event.Name, file.Name()) {
|
|
||||||
log.Debug("File event:", event)
|
|
||||||
configuration := provider.LoadFileConfig(file.Name())
|
|
||||||
if configuration != nil {
|
|
||||||
configurationChan <- configMessage{"file", configuration}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case error := <-watcher.Errors:
|
|
||||||
log.Error("Watcher event error", error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
if provider.Watch {
|
if provider.Watch {
|
||||||
|
// Process events
|
||||||
|
go func() {
|
||||||
|
defer watcher.Close()
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case event := <-watcher.Events:
|
||||||
|
if strings.Contains(event.Name, file.Name()) {
|
||||||
|
log.Debug("File event:", event)
|
||||||
|
configuration := provider.LoadFileConfig(file.Name())
|
||||||
|
if configuration != nil {
|
||||||
|
configurationChan <- configMessage{"file", configuration}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case error := <-watcher.Errors:
|
||||||
|
log.Error("Watcher event error", error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
err = watcher.Add(filepath.Dir(file.Name()))
|
err = watcher.Add(filepath.Dir(file.Name()))
|
||||||
}
|
if err != nil {
|
||||||
|
log.Error("Error adding file watcher", err)
|
||||||
if err != nil {
|
return err
|
||||||
log.Error("Error adding file watcher", err)
|
}
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
configuration := provider.LoadFileConfig(file.Name())
|
configuration := provider.LoadFileConfig(file.Name())
|
||||||
configurationChan <- configMessage{"file", configuration}
|
configurationChan <- configMessage{"file", configuration}
|
||||||
<-done
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (provider *FileProvider) LoadFileConfig(filename string) *Configuration {
|
func (provider *FileProvider) LoadFileConfig(filename string) *Configuration {
|
||||||
|
|
|
@ -15,14 +15,14 @@ func (s *SimpleSuite) TestNoOrInexistentConfigShouldFail(c *check.C) {
|
||||||
output, err := cmd.CombinedOutput()
|
output, err := cmd.CombinedOutput()
|
||||||
|
|
||||||
c.Assert(err, checker.NotNil)
|
c.Assert(err, checker.NotNil)
|
||||||
c.Assert(string(output), checker.Contains, "Error reading file open traefik.toml: no such file or directory")
|
c.Assert(string(output), checker.Contains, "Error reading file: open traefik.toml: no such file or directory")
|
||||||
|
|
||||||
nonExistentFile := "non/existent/file.toml"
|
nonExistentFile := "non/existent/file.toml"
|
||||||
cmd = exec.Command(traefikBinary, nonExistentFile)
|
cmd = exec.Command(traefikBinary, nonExistentFile)
|
||||||
output, err = cmd.CombinedOutput()
|
output, err = cmd.CombinedOutput()
|
||||||
|
|
||||||
c.Assert(err, checker.NotNil)
|
c.Assert(err, checker.NotNil)
|
||||||
c.Assert(string(output), checker.Contains, fmt.Sprintf("Error reading file open %s: no such file or directory", nonExistentFile))
|
c.Assert(string(output), checker.Contains, fmt.Sprintf("Error reading file: open %s: no such file or directory", nonExistentFile))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SimpleSuite) TestInvalidConfigShouldFail(c *check.C) {
|
func (s *SimpleSuite) TestInvalidConfigShouldFail(c *check.C) {
|
||||||
|
@ -30,7 +30,7 @@ func (s *SimpleSuite) TestInvalidConfigShouldFail(c *check.C) {
|
||||||
output, err := cmd.CombinedOutput()
|
output, err := cmd.CombinedOutput()
|
||||||
|
|
||||||
c.Assert(err, checker.NotNil)
|
c.Assert(err, checker.NotNil)
|
||||||
c.Assert(string(output), checker.Contains, "Error reading file Near line 1")
|
c.Assert(string(output), checker.Contains, "Error reading file: Near line 1")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SimpleSuite) TestSimpleDefaultConfig(c *check.C) {
|
func (s *SimpleSuite) TestSimpleDefaultConfig(c *check.C) {
|
||||||
|
|
|
@ -10,12 +10,4 @@ consul:
|
||||||
- "8301"
|
- "8301"
|
||||||
- "8301/udp"
|
- "8301/udp"
|
||||||
- "8302"
|
- "8302"
|
||||||
- "8302/udp"
|
- "8302/udp"
|
||||||
|
|
||||||
registrator:
|
|
||||||
image: gliderlabs/registrator:master
|
|
||||||
command: -internal consulkv://consul:8500/traefik
|
|
||||||
volumes:
|
|
||||||
- /var/run/docker.sock:/tmp/docker.sock
|
|
||||||
links:
|
|
||||||
- consul
|
|
195
kv.go
Normal file
195
kv.go
Normal file
|
@ -0,0 +1,195 @@
|
||||||
|
/*
|
||||||
|
Copyright
|
||||||
|
*/
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"github.com/docker/libkv"
|
||||||
|
"github.com/docker/libkv/store/boltdb"
|
||||||
|
"github.com/docker/libkv/store/consul"
|
||||||
|
"github.com/docker/libkv/store/etcd"
|
||||||
|
"github.com/docker/libkv/store/zookeeper"
|
||||||
|
"strings"
|
||||||
|
"text/template"
|
||||||
|
|
||||||
|
"errors"
|
||||||
|
"github.com/BurntSushi/toml"
|
||||||
|
"github.com/BurntSushi/ty/fun"
|
||||||
|
log "github.com/Sirupsen/logrus"
|
||||||
|
"github.com/docker/libkv/store"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type KvProvider struct {
|
||||||
|
Watch bool
|
||||||
|
Endpoint string
|
||||||
|
Prefix string
|
||||||
|
Filename string
|
||||||
|
StoreType store.Backend
|
||||||
|
kvclient store.Store
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewConsulProvider(provider *ConsulProvider) *KvProvider {
|
||||||
|
kvProvider := new(KvProvider)
|
||||||
|
kvProvider.Watch = provider.Watch
|
||||||
|
kvProvider.Endpoint = provider.Endpoint
|
||||||
|
kvProvider.Prefix = provider.Prefix
|
||||||
|
kvProvider.Filename = provider.Filename
|
||||||
|
kvProvider.StoreType = store.CONSUL
|
||||||
|
return kvProvider
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewEtcdProvider(provider *EtcdProvider) *KvProvider {
|
||||||
|
kvProvider := new(KvProvider)
|
||||||
|
kvProvider.Watch = provider.Watch
|
||||||
|
kvProvider.Endpoint = provider.Endpoint
|
||||||
|
kvProvider.Prefix = provider.Prefix
|
||||||
|
kvProvider.Filename = provider.Filename
|
||||||
|
kvProvider.StoreType = store.ETCD
|
||||||
|
return kvProvider
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewZkProvider(provider *ZookepperProvider) *KvProvider {
|
||||||
|
kvProvider := new(KvProvider)
|
||||||
|
kvProvider.Watch = provider.Watch
|
||||||
|
kvProvider.Endpoint = provider.Endpoint
|
||||||
|
kvProvider.Prefix = provider.Prefix
|
||||||
|
kvProvider.Filename = provider.Filename
|
||||||
|
kvProvider.StoreType = store.ZK
|
||||||
|
return kvProvider
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewBoltDbProvider(provider *BoltDbProvider) *KvProvider {
|
||||||
|
kvProvider := new(KvProvider)
|
||||||
|
kvProvider.Watch = provider.Watch
|
||||||
|
kvProvider.Endpoint = provider.Endpoint
|
||||||
|
kvProvider.Prefix = provider.Prefix
|
||||||
|
kvProvider.Filename = provider.Filename
|
||||||
|
kvProvider.StoreType = store.BOLTDB
|
||||||
|
return kvProvider
|
||||||
|
}
|
||||||
|
|
||||||
|
func (provider *KvProvider) provide(configurationChan chan<- configMessage) error {
|
||||||
|
switch provider.StoreType {
|
||||||
|
case store.CONSUL:
|
||||||
|
consul.Register()
|
||||||
|
case store.ETCD:
|
||||||
|
etcd.Register()
|
||||||
|
case store.ZK:
|
||||||
|
zookeeper.Register()
|
||||||
|
case store.BOLTDB:
|
||||||
|
boltdb.Register()
|
||||||
|
default:
|
||||||
|
return errors.New("Invalid kv store: " + string(provider.StoreType))
|
||||||
|
}
|
||||||
|
kv, err := libkv.NewStore(
|
||||||
|
provider.StoreType,
|
||||||
|
[]string{provider.Endpoint},
|
||||||
|
&store.Config{
|
||||||
|
ConnectionTimeout: 30 * time.Second,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if _, err := kv.List(""); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
provider.kvclient = kv
|
||||||
|
if provider.Watch {
|
||||||
|
stopCh := make(chan struct{})
|
||||||
|
chanKeys, err := kv.WatchTree(provider.Prefix, stopCh)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
<-chanKeys
|
||||||
|
configuration := provider.loadConfig()
|
||||||
|
if configuration != nil {
|
||||||
|
configurationChan <- configMessage{string(provider.StoreType), configuration}
|
||||||
|
}
|
||||||
|
defer close(stopCh)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
configuration := provider.loadConfig()
|
||||||
|
configurationChan <- configMessage{string(provider.StoreType), configuration}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (provider *KvProvider) loadConfig() *Configuration {
|
||||||
|
configuration := new(Configuration)
|
||||||
|
templateObjects := struct {
|
||||||
|
Prefix string
|
||||||
|
}{
|
||||||
|
provider.Prefix,
|
||||||
|
}
|
||||||
|
var KvFuncMap = template.FuncMap{
|
||||||
|
"List": func(keys ...string) []string {
|
||||||
|
joinedKeys := strings.Join(keys, "")
|
||||||
|
keysPairs, err := provider.kvclient.List(joinedKeys)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("Error getting keys: ", joinedKeys, err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
directoryKeys := make(map[string]string)
|
||||||
|
for _, key := range keysPairs {
|
||||||
|
directory := strings.Split(strings.TrimPrefix(key.Key, strings.TrimPrefix(joinedKeys, "/")), "/")[0]
|
||||||
|
directoryKeys[directory] = joinedKeys + directory
|
||||||
|
}
|
||||||
|
return fun.Values(directoryKeys).([]string)
|
||||||
|
},
|
||||||
|
"Get": func(keys ...string) string {
|
||||||
|
joinedKeys := strings.Join(keys, "")
|
||||||
|
keyPair, err := provider.kvclient.Get(joinedKeys)
|
||||||
|
if err != nil {
|
||||||
|
log.Debug("Error getting key: ", joinedKeys, err)
|
||||||
|
return ""
|
||||||
|
} else if keyPair == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return string(keyPair.Value)
|
||||||
|
},
|
||||||
|
"Last": func(key string) string {
|
||||||
|
splittedKey := strings.Split(key, "/")
|
||||||
|
return splittedKey[len(splittedKey)-1]
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
tmpl := template.New(provider.Filename).Funcs(KvFuncMap)
|
||||||
|
if len(provider.Filename) > 0 {
|
||||||
|
_, err := tmpl.ParseFiles(provider.Filename)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("Error reading file", err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
buf, err := Asset("providerTemplates/kv.tmpl")
|
||||||
|
if err != nil {
|
||||||
|
log.Error("Error reading file", err)
|
||||||
|
}
|
||||||
|
_, err = tmpl.Parse(string(buf))
|
||||||
|
if err != nil {
|
||||||
|
log.Error("Error reading file", err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var buffer bytes.Buffer
|
||||||
|
|
||||||
|
err := tmpl.Execute(&buffer, templateObjects)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("Error with kv template:", err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := toml.Decode(buffer.String(), configuration); err != nil {
|
||||||
|
log.Error("Error creating kv configuration:", err)
|
||||||
|
log.Error(buffer.String())
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return configuration
|
||||||
|
}
|
15
marathon.go
15
marathon.go
|
@ -21,16 +21,6 @@ type MarathonProvider struct {
|
||||||
NetworkInterface string
|
NetworkInterface string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewMarathonProvider() *MarathonProvider {
|
|
||||||
marathonProvider := new(MarathonProvider)
|
|
||||||
// default values
|
|
||||||
marathonProvider.Watch = true
|
|
||||||
marathonProvider.Domain = "traefik"
|
|
||||||
marathonProvider.NetworkInterface = "eth0"
|
|
||||||
|
|
||||||
return marathonProvider
|
|
||||||
}
|
|
||||||
|
|
||||||
var MarathonFuncMap = template.FuncMap{
|
var MarathonFuncMap = template.FuncMap{
|
||||||
"getPort": func(task marathon.Task) string {
|
"getPort": func(task marathon.Task) string {
|
||||||
for _, port := range task.Ports {
|
for _, port := range task.Ports {
|
||||||
|
@ -67,14 +57,14 @@ var MarathonFuncMap = template.FuncMap{
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func (provider *MarathonProvider) Provide(configurationChan chan<- configMessage) {
|
func (provider *MarathonProvider) Provide(configurationChan chan<- configMessage) error {
|
||||||
config := marathon.NewDefaultConfig()
|
config := marathon.NewDefaultConfig()
|
||||||
config.URL = provider.Endpoint
|
config.URL = provider.Endpoint
|
||||||
config.EventsInterface = provider.NetworkInterface
|
config.EventsInterface = provider.NetworkInterface
|
||||||
client, err := marathon.NewClient(config)
|
client, err := marathon.NewClient(config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("Failed to create a client for marathon, error: %s", err)
|
log.Errorf("Failed to create a client for marathon, error: %s", err)
|
||||||
return
|
return err
|
||||||
}
|
}
|
||||||
provider.marathonClient = client
|
provider.marathonClient = client
|
||||||
update := make(marathon.EventsChannel, 5)
|
update := make(marathon.EventsChannel, 5)
|
||||||
|
@ -97,6 +87,7 @@ func (provider *MarathonProvider) Provide(configurationChan chan<- configMessage
|
||||||
|
|
||||||
configuration := provider.loadMarathonConfig()
|
configuration := provider.loadMarathonConfig()
|
||||||
configurationChan <- configMessage{"marathon", configuration}
|
configurationChan <- configMessage{"marathon", configuration}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (provider *MarathonProvider) loadMarathonConfig() *Configuration {
|
func (provider *MarathonProvider) loadMarathonConfig() *Configuration {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
type Provider interface {
|
type Provider interface {
|
||||||
Provide(configurationChan chan<- configMessage)
|
Provide(configurationChan chan<- configMessage) error
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,17 +1,17 @@
|
||||||
{{$frontends := "frontends/" | List }}
|
{{$frontends := List .Prefix "/frontends/" }}
|
||||||
{{$backends := "backends/" | List }}
|
{{$backends := List .Prefix "/backends/"}}
|
||||||
|
|
||||||
{{range $backends}}
|
{{range $backends}}
|
||||||
{{$backend := .}}
|
{{$backend := .}}
|
||||||
{{$servers := "servers/" | List $backend }}
|
{{$servers := List $backend "/servers/" }}
|
||||||
|
|
||||||
{{$circuitBreaker := Get . "circuitbreaker/" "expression"}}
|
{{$circuitBreaker := Get . "/circuitbreaker/" "expression"}}
|
||||||
{{with $circuitBreaker}}
|
{{with $circuitBreaker}}
|
||||||
[backends.{{Last $backend}}.circuitBreaker]
|
[backends.{{Last $backend}}.circuitBreaker]
|
||||||
expression = "{{$circuitBreaker}}"
|
expression = "{{$circuitBreaker}}"
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
{{$loadBalancer := Get . "loadbalancer/" "method"}}
|
{{$loadBalancer := Get . "/loadbalancer/" "method"}}
|
||||||
{{with $loadBalancer}}
|
{{with $loadBalancer}}
|
||||||
[backends.{{Last $backend}}.loadBalancer]
|
[backends.{{Last $backend}}.loadBalancer]
|
||||||
method = "{{$loadBalancer}}"
|
method = "{{$loadBalancer}}"
|
||||||
|
@ -28,7 +28,7 @@
|
||||||
{{$frontend := Last .}}
|
{{$frontend := Last .}}
|
||||||
[frontends.{{$frontend}}]
|
[frontends.{{$frontend}}]
|
||||||
backend = "{{Get . "/backend"}}"
|
backend = "{{Get . "/backend"}}"
|
||||||
{{$routes := "routes/" | List .}}
|
{{$routes := List . "/routes/"}}
|
||||||
{{range $routes}}
|
{{range $routes}}
|
||||||
[frontends.{{$frontend}}.routes.{{Last .}}]
|
[frontends.{{$frontend}}.routes.{{Last .}}]
|
||||||
rule = "{{Get . "/rule"}}"
|
rule = "{{Get . "/rule"}}"
|
|
@ -61,7 +61,7 @@
|
||||||
{{range $keyProviders, $valueProviders := .Configurations}}
|
{{range $keyProviders, $valueProviders := .Configurations}}
|
||||||
{{range $keyBackends, $valueBackends := $valueProviders.Backends}}
|
{{range $keyBackends, $valueBackends := $valueProviders.Backends}}
|
||||||
<div class="panel panel-primary" id="{{$keyBackends}}">
|
<div class="panel panel-primary" id="{{$keyBackends}}">
|
||||||
<div class="panel-heading">{{$keyBackends}}({{$keyProviders}})</div>
|
<div class="panel-heading">{{$keyBackends}} - ({{$keyProviders}})</div>
|
||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
{{with $valueBackends.LoadBalancer}}
|
{{with $valueBackends.LoadBalancer}}
|
||||||
<a class="btn btn-info" role="button">
|
<a class="btn btn-info" role="button">
|
||||||
|
|
|
@ -1,25 +1,25 @@
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
# backend 1
|
# backend 1
|
||||||
curl -i -H "Accept: application/json" -X PUT -d "NetworkErrorRatio() > 0.5" http://localhost:8500/v1/kv/backends/backend1/circuitbreaker/expression
|
curl -i -H "Accept: application/json" -X PUT -d "NetworkErrorRatio() > 0.5" http://localhost:8500/v1/kv/traefik/backends/backend1/circuitbreaker/expression
|
||||||
curl -i -H "Accept: application/json" -X PUT -d "http://172.17.0.2:80" http://localhost:8500/v1/kv/backends/backend1/servers/server1/url
|
curl -i -H "Accept: application/json" -X PUT -d "http://172.17.0.2:80" http://localhost:8500/v1/kv/traefik/backends/backend1/servers/server1/url
|
||||||
curl -i -H "Accept: application/json" -X PUT -d "10" http://localhost:8500/v1/kv/backends/backend1/servers/server1/weight
|
curl -i -H "Accept: application/json" -X PUT -d "10" http://localhost:8500/v1/kv/traefik/backends/backend1/servers/server1/weight
|
||||||
curl -i -H "Accept: application/json" -X PUT -d "http://172.17.0.3:80" http://localhost:8500/v1/kv/backends/backend1/servers/server2/url
|
curl -i -H "Accept: application/json" -X PUT -d "http://172.17.0.3:80" http://localhost:8500/v1/kv/traefik/backends/backend1/servers/server2/url
|
||||||
curl -i -H "Accept: application/json" -X PUT -d "1" http://localhost:8500/v1/kv/backends/backend1/servers/server2/weight
|
curl -i -H "Accept: application/json" -X PUT -d "1" http://localhost:8500/v1/kv/traefik/backends/backend1/servers/server2/weight
|
||||||
|
|
||||||
# backend 2
|
# backend 2
|
||||||
curl -i -H "Accept: application/json" -X PUT -d "drr" http://localhost:8500/v1/kv/backends/backend2/loadbalancer/method
|
curl -i -H "Accept: application/json" -X PUT -d "drr" http://localhost:8500/v1/kv/traefik/backends/backend2/loadbalancer/method
|
||||||
curl -i -H "Accept: application/json" -X PUT -d "http://172.17.0.4:80" http://localhost:8500/v1/kv/backends/backend2/servers/server1/url
|
curl -i -H "Accept: application/json" -X PUT -d "http://172.17.0.4:80" http://localhost:8500/v1/kv/traefik/backends/backend2/servers/server1/url
|
||||||
curl -i -H "Accept: application/json" -X PUT -d "1" http://localhost:8500/v1/kv/backends/backend2/servers/server1/weight
|
curl -i -H "Accept: application/json" -X PUT -d "1" http://localhost:8500/v1/kv/traefik/backends/backend2/servers/server1/weight
|
||||||
curl -i -H "Accept: application/json" -X PUT -d "http://172.17.0.5:80" http://localhost:8500/v1/kv/backends/backend2/servers/server2/url
|
curl -i -H "Accept: application/json" -X PUT -d "http://172.17.0.5:80" http://localhost:8500/v1/kv/traefik/backends/backend2/servers/server2/url
|
||||||
curl -i -H "Accept: application/json" -X PUT -d "2" http://localhost:8500/v1/kv/backends/backend2/servers/server2/weight
|
curl -i -H "Accept: application/json" -X PUT -d "2" http://localhost:8500/v1/kv/traefik/backends/backend2/servers/server2/weight
|
||||||
|
|
||||||
# frontend 1
|
# frontend 1
|
||||||
curl -i -H "Accept: application/json" -X PUT -d "backend2" http://localhost:8500/v1/kv/frontends/frontend1/backend
|
curl -i -H "Accept: application/json" -X PUT -d "backend2" http://localhost:8500/v1/kv/traefik/frontends/frontend1/backend
|
||||||
curl -i -H "Accept: application/json" -X PUT -d "Host" http://localhost:8500/v1/kv/frontends/frontend1/routes/test_1/rule
|
curl -i -H "Accept: application/json" -X PUT -d "Host" http://localhost:8500/v1/kv/traefik/frontends/frontend1/routes/test_1/rule
|
||||||
curl -i -H "Accept: application/json" -X PUT -d "test.localhost" http://localhost:8500/v1/kv/frontends/frontend1/routes/test_1/value
|
curl -i -H "Accept: application/json" -X PUT -d "test.localhost" http://localhost:8500/v1/kv/traefik/frontends/frontend1/routes/test_1/value
|
||||||
|
|
||||||
# frontend 2
|
# frontend 2
|
||||||
curl -i -H "Accept: application/json" -X PUT -d "backend1" http://localhost:8500/v1/kv/frontends/frontend2/backend
|
curl -i -H "Accept: application/json" -X PUT -d "backend1" http://localhost:8500/v1/kv/traefik/frontends/frontend2/backend
|
||||||
curl -i -H "Accept: application/json" -X PUT -d "Path" http://localhost:8500/v1/kv/frontends/frontend2/routes/test_2/rule
|
curl -i -H "Accept: application/json" -X PUT -d "Path" http://localhost:8500/v1/kv/traefik/frontends/frontend2/routes/test_2/rule
|
||||||
curl -i -H "Accept: application/json" -X PUT -d "/test" http://localhost:8500/v1/kv/frontends/frontend2/routes/test_2/value
|
curl -i -H "Accept: application/json" -X PUT -d "/test" http://localhost:8500/v1/kv/traefik/frontends/frontend2/routes/test_2/value
|
||||||
|
|
40
traefik.go
40
traefik.go
|
@ -12,9 +12,11 @@ import (
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"errors"
|
||||||
"github.com/BurntSushi/toml"
|
"github.com/BurntSushi/toml"
|
||||||
log "github.com/Sirupsen/logrus"
|
log "github.com/Sirupsen/logrus"
|
||||||
"github.com/codegangsta/negroni"
|
"github.com/codegangsta/negroni"
|
||||||
|
"github.com/davecgh/go-spew/spew"
|
||||||
"github.com/emilevauge/traefik/middlewares"
|
"github.com/emilevauge/traefik/middlewares"
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
"github.com/mailgun/manners"
|
"github.com/mailgun/manners"
|
||||||
|
@ -86,7 +88,7 @@ func main() {
|
||||||
} else {
|
} else {
|
||||||
log.SetFormatter(&log.TextFormatter{FullTimestamp: true, DisableSorting: true})
|
log.SetFormatter(&log.TextFormatter{FullTimestamp: true, DisableSorting: true})
|
||||||
}
|
}
|
||||||
|
log.Debugf("Global configuration loaded %s", spew.Sdump(globalConfiguration))
|
||||||
configurationRouter = LoadDefaultConfig(globalConfiguration)
|
configurationRouter = LoadDefaultConfig(globalConfiguration)
|
||||||
|
|
||||||
// listen new configurations from providers
|
// listen new configurations from providers
|
||||||
|
@ -94,7 +96,8 @@ func main() {
|
||||||
|
|
||||||
for {
|
for {
|
||||||
configMsg := <-configurationChan
|
configMsg := <-configurationChan
|
||||||
log.Infof("Configuration receveived from provider %v: %+v", configMsg.providerName, configMsg.configuration)
|
log.Infof("Configuration receveived from provider %s: %#v", configMsg.providerName, configMsg.configuration)
|
||||||
|
log.Debugf("Configuration %s", spew.Sdump(configMsg.configuration))
|
||||||
if configMsg.configuration == nil {
|
if configMsg.configuration == nil {
|
||||||
log.Info("Skipping empty configuration")
|
log.Info("Skipping empty configuration")
|
||||||
} else if reflect.DeepEqual(currentConfigurations[configMsg.providerName], configMsg.configuration) {
|
} else if reflect.DeepEqual(currentConfigurations[configMsg.providerName], configMsg.configuration) {
|
||||||
|
@ -147,13 +150,25 @@ func main() {
|
||||||
if globalConfiguration.Consul != nil {
|
if globalConfiguration.Consul != nil {
|
||||||
providers = append(providers, globalConfiguration.Consul)
|
providers = append(providers, globalConfiguration.Consul)
|
||||||
}
|
}
|
||||||
|
if globalConfiguration.Etcd != nil {
|
||||||
|
providers = append(providers, globalConfiguration.Etcd)
|
||||||
|
}
|
||||||
|
if globalConfiguration.Zookeeper != nil {
|
||||||
|
providers = append(providers, globalConfiguration.Zookeeper)
|
||||||
|
}
|
||||||
|
if globalConfiguration.Boltdb != nil {
|
||||||
|
providers = append(providers, globalConfiguration.Boltdb)
|
||||||
|
}
|
||||||
|
|
||||||
// start providers
|
// start providers
|
||||||
for _, provider := range providers {
|
for _, provider := range providers {
|
||||||
log.Infof("Starting provider %v %+v", reflect.TypeOf(provider), provider)
|
log.Infof("Starting provider %v %+v", reflect.TypeOf(provider), provider)
|
||||||
currentProvider := provider
|
currentProvider := provider
|
||||||
go func() {
|
go func() {
|
||||||
currentProvider.Provide(configurationChan)
|
err := currentProvider.Provide(configurationChan)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("Error starting provider %s", err)
|
||||||
|
}
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -176,6 +191,7 @@ func main() {
|
||||||
|
|
||||||
func startServer(srv *manners.GracefulServer, globalConfiguration *GlobalConfiguration) {
|
func startServer(srv *manners.GracefulServer, globalConfiguration *GlobalConfiguration) {
|
||||||
log.Info("Starting server")
|
log.Info("Starting server")
|
||||||
|
log.Debugf("Server %s", spew.Sdump(srv))
|
||||||
if len(globalConfiguration.CertFile) > 0 && len(globalConfiguration.KeyFile) > 0 {
|
if len(globalConfiguration.CertFile) > 0 && len(globalConfiguration.KeyFile) > 0 {
|
||||||
err := srv.ListenAndServeTLS(globalConfiguration.CertFile, globalConfiguration.KeyFile)
|
err := srv.ListenAndServeTLS(globalConfiguration.CertFile, globalConfiguration.KeyFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -243,13 +259,16 @@ func LoadConfig(configurations configs, globalConfiguration *GlobalConfiguration
|
||||||
log.Debugf("Creating backend %s", frontend.Backend)
|
log.Debugf("Creating backend %s", frontend.Backend)
|
||||||
var lb http.Handler
|
var lb http.Handler
|
||||||
rr, _ := roundrobin.New(fwd)
|
rr, _ := roundrobin.New(fwd)
|
||||||
|
if configuration.Backends[frontend.Backend] == nil {
|
||||||
|
return nil, errors.New("Backend not found: " + frontend.Backend)
|
||||||
|
}
|
||||||
lbMethod, err := NewLoadBalancerMethod(configuration.Backends[frontend.Backend].LoadBalancer)
|
lbMethod, err := NewLoadBalancerMethod(configuration.Backends[frontend.Backend].LoadBalancer)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
configuration.Backends[frontend.Backend].LoadBalancer = &LoadBalancer{Method: "wrr"}
|
configuration.Backends[frontend.Backend].LoadBalancer = &LoadBalancer{Method: "wrr"}
|
||||||
}
|
}
|
||||||
switch lbMethod {
|
switch lbMethod {
|
||||||
case drr:
|
case drr:
|
||||||
log.Debugf("Creating load-balancer drr")
|
log.Infof("Creating load-balancer drr")
|
||||||
rebalancer, _ := roundrobin.NewRebalancer(rr, roundrobin.RebalancerLogger(oxyLogger))
|
rebalancer, _ := roundrobin.NewRebalancer(rr, roundrobin.RebalancerLogger(oxyLogger))
|
||||||
lb = rebalancer
|
lb = rebalancer
|
||||||
for serverName, server := range configuration.Backends[frontend.Backend].Servers {
|
for serverName, server := range configuration.Backends[frontend.Backend].Servers {
|
||||||
|
@ -257,31 +276,31 @@ func LoadConfig(configurations configs, globalConfiguration *GlobalConfiguration
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
log.Debugf("Creating server %s %s", serverName, url.String())
|
log.Infof("Creating server %s %s", serverName, url.String())
|
||||||
rebalancer.UpsertServer(url, roundrobin.Weight(server.Weight))
|
rebalancer.UpsertServer(url, roundrobin.Weight(server.Weight))
|
||||||
}
|
}
|
||||||
case wrr:
|
case wrr:
|
||||||
log.Debugf("Creating load-balancer wrr")
|
log.Infof("Creating load-balancer wrr")
|
||||||
lb = rr
|
lb = rr
|
||||||
for serverName, server := range configuration.Backends[frontend.Backend].Servers {
|
for serverName, server := range configuration.Backends[frontend.Backend].Servers {
|
||||||
url, err := url.Parse(server.URL)
|
url, err := url.Parse(server.URL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
log.Debugf("Creating server %s %s", serverName, url.String())
|
log.Infof("Creating server %s %s", serverName, url.String())
|
||||||
rr.UpsertServer(url, roundrobin.Weight(server.Weight))
|
rr.UpsertServer(url, roundrobin.Weight(server.Weight))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var negroni = negroni.New()
|
var negroni = negroni.New()
|
||||||
if configuration.Backends[frontend.Backend].CircuitBreaker != nil {
|
if configuration.Backends[frontend.Backend].CircuitBreaker != nil {
|
||||||
log.Debugf("Creating circuit breaker %s", configuration.Backends[frontend.Backend].CircuitBreaker.Expression)
|
log.Infof("Creating circuit breaker %s", configuration.Backends[frontend.Backend].CircuitBreaker.Expression)
|
||||||
negroni.Use(middlewares.NewCircuitBreaker(lb, configuration.Backends[frontend.Backend].CircuitBreaker.Expression, cbreaker.Logger(oxyLogger)))
|
negroni.Use(middlewares.NewCircuitBreaker(lb, configuration.Backends[frontend.Backend].CircuitBreaker.Expression, cbreaker.Logger(oxyLogger)))
|
||||||
} else {
|
} else {
|
||||||
negroni.UseHandler(lb)
|
negroni.UseHandler(lb)
|
||||||
}
|
}
|
||||||
backends[frontend.Backend] = negroni
|
backends[frontend.Backend] = negroni
|
||||||
} else {
|
} else {
|
||||||
log.Debugf("Reusing backend %s", frontend.Backend)
|
log.Infof("Reusing backend %s", frontend.Backend)
|
||||||
}
|
}
|
||||||
// stream.New(backends[frontend.Backend], stream.Retry("IsNetworkError() && Attempts() <= " + strconv.Itoa(globalConfiguration.Replay)), stream.Logger(oxyLogger))
|
// stream.New(backends[frontend.Backend], stream.Retry("IsNetworkError() && Attempts() <= " + strconv.Itoa(globalConfiguration.Replay)), stream.Logger(oxyLogger))
|
||||||
|
|
||||||
|
@ -306,8 +325,7 @@ func Invoke(any interface{}, name string, args ...interface{}) []reflect.Value {
|
||||||
func LoadFileConfig(file string) *GlobalConfiguration {
|
func LoadFileConfig(file string) *GlobalConfiguration {
|
||||||
configuration := NewGlobalConfiguration()
|
configuration := NewGlobalConfiguration()
|
||||||
if _, err := toml.DecodeFile(file, configuration); err != nil {
|
if _, err := toml.DecodeFile(file, configuration); err != nil {
|
||||||
log.Fatal("Error reading file ", err)
|
fmtlog.Fatalf("Error reading file: %s", err)
|
||||||
}
|
}
|
||||||
log.Debugf("Global configuration loaded %+v", configuration)
|
|
||||||
return configuration
|
return configuration
|
||||||
}
|
}
|
||||||
|
|
|
@ -147,7 +147,7 @@
|
||||||
#
|
#
|
||||||
# endpoint = "http://127.0.0.1:8080"
|
# endpoint = "http://127.0.0.1:8080"
|
||||||
|
|
||||||
# Network interface used to call Marathon web services
|
# Network interface used to call Marathon web services. Needed in case of multiple network interfaces.
|
||||||
# Optional
|
# Optional
|
||||||
# Default: "eth0"
|
# Default: "eth0"
|
||||||
#
|
#
|
||||||
|
@ -199,7 +199,7 @@
|
||||||
#
|
#
|
||||||
# Optional
|
# Optional
|
||||||
#
|
#
|
||||||
# prefix = "traefik"
|
# prefix = "/traefik"
|
||||||
|
|
||||||
# Override default configuration template. For advanced users :)
|
# Override default configuration template. For advanced users :)
|
||||||
#
|
#
|
||||||
|
@ -208,6 +208,112 @@
|
||||||
# filename = "consul.tmpl"
|
# filename = "consul.tmpl"
|
||||||
|
|
||||||
|
|
||||||
|
################################################################
|
||||||
|
# Etcd configuration backend
|
||||||
|
################################################################
|
||||||
|
|
||||||
|
# Enable Etcd configuration backend
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
#
|
||||||
|
# [etcd]
|
||||||
|
|
||||||
|
# Etcd server endpoint
|
||||||
|
#
|
||||||
|
# Required
|
||||||
|
#
|
||||||
|
# endpoint = "127.0.0.1:4001"
|
||||||
|
|
||||||
|
# Enable watch Etcd changes
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
#
|
||||||
|
# watch = true
|
||||||
|
|
||||||
|
# Prefix used for KV store.
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
#
|
||||||
|
# prefix = "/traefik"
|
||||||
|
|
||||||
|
# Override default configuration template. For advanced users :)
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
#
|
||||||
|
# filename = "etcd.tmpl"
|
||||||
|
|
||||||
|
|
||||||
|
################################################################
|
||||||
|
# Zookeeper configuration backend
|
||||||
|
################################################################
|
||||||
|
|
||||||
|
# Enable Zookeeperconfiguration backend
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
#
|
||||||
|
# [zookeeper]
|
||||||
|
|
||||||
|
# Zookeeper server endpoint
|
||||||
|
#
|
||||||
|
# Required
|
||||||
|
#
|
||||||
|
# endpoint = "127.0.0.1:2181"
|
||||||
|
|
||||||
|
# Enable watch Zookeeper changes
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
#
|
||||||
|
# watch = true
|
||||||
|
|
||||||
|
# Prefix used for KV store.
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
#
|
||||||
|
# prefix = "/traefik"
|
||||||
|
|
||||||
|
# Override default configuration template. For advanced users :)
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
#
|
||||||
|
# filename = "zookeeper.tmpl"
|
||||||
|
|
||||||
|
|
||||||
|
################################################################
|
||||||
|
# BoltDB configuration backend
|
||||||
|
################################################################
|
||||||
|
|
||||||
|
# Enable BoltDB configuration backend
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
#
|
||||||
|
# [boltdb]
|
||||||
|
|
||||||
|
# BoltDB file
|
||||||
|
#
|
||||||
|
# Required
|
||||||
|
#
|
||||||
|
# endpoint = "/my.db"
|
||||||
|
|
||||||
|
# Enable watch BoltDB changes
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
#
|
||||||
|
# watch = true
|
||||||
|
|
||||||
|
# Prefix used for KV store.
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
#
|
||||||
|
# prefix = "/traefik"
|
||||||
|
|
||||||
|
# Override default configuration template. For advanced users :)
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
#
|
||||||
|
# filename = "boltdb.tmpl"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
################################################################
|
################################################################
|
||||||
# Sample rules
|
# Sample rules
|
||||||
|
|
25
web.go
25
web.go
|
@ -6,6 +6,7 @@ import (
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/BurntSushi/ty/fun"
|
||||||
log "github.com/Sirupsen/logrus"
|
log "github.com/Sirupsen/logrus"
|
||||||
"github.com/elazarl/go-bindata-assetfs"
|
"github.com/elazarl/go-bindata-assetfs"
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
|
@ -20,13 +21,14 @@ type Page struct {
|
||||||
Configurations configs
|
Configurations configs
|
||||||
}
|
}
|
||||||
|
|
||||||
func (provider *WebProvider) Provide(configurationChan chan<- configMessage) {
|
func (provider *WebProvider) Provide(configurationChan chan<- configMessage) error {
|
||||||
systemRouter := mux.NewRouter()
|
systemRouter := mux.NewRouter()
|
||||||
systemRouter.Methods("GET").Path("/").Handler(http.HandlerFunc(GetHTMLConfigHandler))
|
systemRouter.Methods("GET").Path("/").Handler(http.HandlerFunc(GetHTMLConfigHandler))
|
||||||
systemRouter.Methods("GET").Path("/health").Handler(http.HandlerFunc(GetHealthHandler))
|
systemRouter.Methods("GET").Path("/health").Handler(http.HandlerFunc(GetHealthHandler))
|
||||||
systemRouter.Methods("GET").Path("/api").Handler(http.HandlerFunc(GetConfigHandler))
|
systemRouter.Methods("GET").Path("/api").Handler(http.HandlerFunc(GetConfigHandler))
|
||||||
systemRouter.Methods("GET").Path("/api/{provider}").Handler(http.HandlerFunc(GetConfigHandler))
|
systemRouter.Methods("GET").Path("/api/providers").Handler(http.HandlerFunc(GetProvidersHandler))
|
||||||
systemRouter.Methods("PUT").Path("/api/{provider}").Handler(http.HandlerFunc(
|
systemRouter.Methods("GET").Path("/api/providers/{provider}").Handler(http.HandlerFunc(GetConfigHandler))
|
||||||
|
systemRouter.Methods("PUT").Path("/api/providers/{provider}").Handler(http.HandlerFunc(
|
||||||
func(rw http.ResponseWriter, r *http.Request) {
|
func(rw http.ResponseWriter, r *http.Request) {
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
if vars["provider"] != "web" {
|
if vars["provider"] != "web" {
|
||||||
|
@ -46,12 +48,12 @@ func (provider *WebProvider) Provide(configurationChan chan<- configMessage) {
|
||||||
http.Error(rw, fmt.Sprintf("%+v", err), http.StatusBadRequest)
|
http.Error(rw, fmt.Sprintf("%+v", err), http.StatusBadRequest)
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
systemRouter.Methods("GET").Path("/api/{provider}/backends").Handler(http.HandlerFunc(GetBackendsHandler))
|
systemRouter.Methods("GET").Path("/api/providers/{provider}/backends").Handler(http.HandlerFunc(GetBackendsHandler))
|
||||||
systemRouter.Methods("GET").Path("/api/{provider}/backends/{backend}").Handler(http.HandlerFunc(GetBackendHandler))
|
systemRouter.Methods("GET").Path("/api/providers/{provider}/backends/{backend}").Handler(http.HandlerFunc(GetBackendHandler))
|
||||||
systemRouter.Methods("GET").Path("/api/{provider}/backends/{backend}/servers").Handler(http.HandlerFunc(GetServersHandler))
|
systemRouter.Methods("GET").Path("/api/providers/{provider}/backends/{backend}/servers").Handler(http.HandlerFunc(GetServersHandler))
|
||||||
systemRouter.Methods("GET").Path("/api/{provider}/backends/{backend}/servers/{server}").Handler(http.HandlerFunc(GetServerHandler))
|
systemRouter.Methods("GET").Path("/api/providers/{provider}/backends/{backend}/servers/{server}").Handler(http.HandlerFunc(GetServerHandler))
|
||||||
systemRouter.Methods("GET").Path("/api/{provider}/frontends").Handler(http.HandlerFunc(GetFrontendsHandler))
|
systemRouter.Methods("GET").Path("/api/providers/{provider}/frontends").Handler(http.HandlerFunc(GetFrontendsHandler))
|
||||||
systemRouter.Methods("GET").Path("/api/{provider}/frontends/{frontend}").Handler(http.HandlerFunc(GetFrontendHandler))
|
systemRouter.Methods("GET").Path("/api/providers/{provider}/frontends/{frontend}").Handler(http.HandlerFunc(GetFrontendHandler))
|
||||||
systemRouter.Methods("GET").PathPrefix("/static/").Handler(http.StripPrefix("/static/", http.FileServer(&assetfs.AssetFS{Asset: Asset, AssetDir: AssetDir, Prefix: "static"})))
|
systemRouter.Methods("GET").PathPrefix("/static/").Handler(http.StripPrefix("/static/", http.FileServer(&assetfs.AssetFS{Asset: Asset, AssetDir: AssetDir, Prefix: "static"})))
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
|
@ -67,6 +69,7 @@ func (provider *WebProvider) Provide(configurationChan chan<- configMessage) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetConfigHandler(rw http.ResponseWriter, r *http.Request) {
|
func GetConfigHandler(rw http.ResponseWriter, r *http.Request) {
|
||||||
|
@ -81,6 +84,10 @@ func GetHealthHandler(rw http.ResponseWriter, r *http.Request) {
|
||||||
templatesRenderer.JSON(rw, http.StatusOK, metrics.Data())
|
templatesRenderer.JSON(rw, http.StatusOK, metrics.Data())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetProvidersHandler(rw http.ResponseWriter, r *http.Request) {
|
||||||
|
templatesRenderer.JSON(rw, http.StatusOK, fun.Keys(currentConfigurations))
|
||||||
|
}
|
||||||
|
|
||||||
func GetBackendsHandler(rw http.ResponseWriter, r *http.Request) {
|
func GetBackendsHandler(rw http.ResponseWriter, r *http.Request) {
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
providerId := vars["provider"]
|
providerId := vars["provider"]
|
||||||
|
|
14
zk.go
Normal file
14
zk.go
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
type ZookepperProvider struct {
|
||||||
|
Watch bool
|
||||||
|
Endpoint string
|
||||||
|
Prefix string
|
||||||
|
Filename string
|
||||||
|
KvProvider *KvProvider
|
||||||
|
}
|
||||||
|
|
||||||
|
func (provider *ZookepperProvider) Provide(configurationChan chan<- configMessage) error {
|
||||||
|
provider.KvProvider = NewZkProvider(provider)
|
||||||
|
return provider.KvProvider.provide(configurationChan)
|
||||||
|
}
|
Loading…
Reference in a new issue