diff --git a/.travis.yml b/.travis.yml index 6c554ed48..0bca84c36 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,7 +9,7 @@ env: - secure: btt4r13t09gQlHb6gYrvGC2yGCMMHfnp1Mz1RQedc4Mpf/FfT8aE6xmK2a2i9CCvskjrP0t/BFaS4yxIURjnFRn+ugQIEa0pLspB9UJArW/vgOSpIWM9/OQ/fg8z5XuMxN6Md4DL1/iLypMNSageA1x0TRdt89+D1N1dALpg5XRCXLFbC84TLi0gjlFuib9ibPKzEhLT+anCRJ6iZMzeupDSoaCVbAtJMoDvXw4+4AcRZ1+k4MybBLyCib5boaEOt4pTT88mz4Kk0YaMwPVJyg9Qv36VqyUcPS09Yd95LuyVQ4+tZt8Y1ccbIzULsK+sLM3hLCzxlmlpN3dQBlZJiiRtQde0mgGAKyC0P0A1XjuDTywcsa5edB+fTk1Dsewz9xZ9V0NmMz8t+UNZnaSsAPga9i86jULbXUUwMVSzVRc+Xgx02liB/8qI1xYC9FM6ilStt7rn7mF0k3KbiWhcptgeXjO6Lah9FjEKd5w4MXsdUSTi/86rQaLo+kj+XdaTrXCTulKHyRyQEUj+8V1w0oVz7pcGjePHd7y5oU9ByifVQy6sytuFBfRZvugM5bKHo+i0pcWvixrZS42DrzwxZJsspANOvqSe5ifVbvOkfUppQdCBIwptxV5N1b49XPKU3W/w34QJ8xGmKp3TFA7WwVCztriFHjPgiRpB3EG99Bg= - REPO: $TRAVIS_REPO_SLUG - VERSION: $TRAVIS_TAG - - CODENAME: camembert + - CODENAME: morbier matrix: fast_finish: true diff --git a/CHANGELOG.md b/CHANGELOG.md index 541ad148d..cc112af85 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,151 @@ # Change Log + +## [v1.2.0-rc2](https://github.com/containous/traefik/tree/v1.2.0-rc2) (2017-03-01) +[Full Changelog](https://github.com/containous/traefik/compare/v1.2.0-rc1...v1.2.0-rc2) + +**Implemented enhancements:** + +- Are there plans to support the service type ExternalName in Kubernetes? [\#1142](https://github.com/containous/traefik/issues/1142) +- Kubernetes Ingress and sticky support [\#911](https://github.com/containous/traefik/issues/911) +- kubernetes client does not support InsecureSkipVerify [\#876](https://github.com/containous/traefik/issues/876) +- Support active health checking like HAProxy [\#824](https://github.com/containous/traefik/issues/824) +- Allow k8s ingress controller serviceAccountToken and serviceAccountCACert to be changed [\#611](https://github.com/containous/traefik/issues/611) + +**Fixed bugs:** + +- \[rancher\] invalid memory address or nil pointer dereference [\#1134](https://github.com/containous/traefik/issues/1134) +- Kubernetes default backend should work [\#1073](https://github.com/containous/traefik/issues/1073) + +**Closed issues:** + +- Are release Download links broken? [\#1201](https://github.com/containous/traefik/issues/1201) +- Bind to specific ip address [\#1193](https://github.com/containous/traefik/issues/1193) +- DNS01 challenge use the wrong zone through route53 [\#1192](https://github.com/containous/traefik/issues/1192) +- Reverse proxy https to http backends fails [\#1180](https://github.com/containous/traefik/issues/1180) +- Swarm Mode + Letsecrypt + KV Store [\#1176](https://github.com/containous/traefik/issues/1176) +- docker deploy -c example.yml e [\#1169](https://github.com/containous/traefik/issues/1169) +- Traefik not finding dynamically added services \(Docker Swarm Mode\) [\#1168](https://github.com/containous/traefik/issues/1168) +- Traefik with Kubernetes backend - keep getting 401 on all GET requests to kube-apiserver [\#1166](https://github.com/containous/traefik/issues/1166) +- Near line 15 \(last key parsed 'backends.backend-monitor-viz.servers'\): Key 'backends.backend-monitor-viz.servers.server-monitor\_viz-1' has already been defined. [\#1154](https://github.com/containous/traefik/issues/1154) +- How to reuse SSL certificates automatically fetched from Let´s encrypt? [\#1152](https://github.com/containous/traefik/issues/1152) +- Dynamically ban ip when backend repeatedly returns specified status code. \( 403 \) [\#1136](https://github.com/containous/traefik/issues/1136) +- Always get 404 accessing my nginx backend service [\#1112](https://github.com/containous/traefik/issues/1112) +- Incomplete Docu [\#1091](https://github.com/containous/traefik/issues/1091) +- LoadCertificateForDomains: runtime error: invalid memory address [\#1069](https://github.com/containous/traefik/issues/1069) +- Traefik creating backends & mappings for ingress annotated with ingress.class: nginx [\#1058](https://github.com/containous/traefik/issues/1058) +- ACME file format description [\#1012](https://github.com/containous/traefik/issues/1012) +- SwarmMode - Not routing on worker node [\#838](https://github.com/containous/traefik/issues/838) +- Migrate k8s to kubernetes/client-go [\#678](https://github.com/containous/traefik/issues/678) +- Support for sticky session with kubernetes ingress as backend [\#674](https://github.com/containous/traefik/issues/674) + +**Merged pull requests:** + +- Revert "Ensure that we don't add balancees with no health check runs … [\#1198](https://github.com/containous/traefik/pull/1198) ([jangie](https://github.com/jangie)) +- Small fixes and improvments [\#1173](https://github.com/containous/traefik/pull/1173) ([SantoDE](https://github.com/SantoDE)) +- Fix docker issues with global and dead tasks [\#1167](https://github.com/containous/traefik/pull/1167) ([christopherobin](https://github.com/christopherobin)) +- Better ECS error checking [\#1143](https://github.com/containous/traefik/pull/1143) ([lpetre](https://github.com/lpetre)) +- Fix stats race condition [\#1141](https://github.com/containous/traefik/pull/1141) ([emilevauge](https://github.com/emilevauge)) +- ECS: Docs - info about cred. resolution and required access policies [\#1137](https://github.com/containous/traefik/pull/1137) ([rickard-von-essen](https://github.com/rickard-von-essen)) +- Healthcheck tests and doc [\#1132](https://github.com/containous/traefik/pull/1132) ([Juliens](https://github.com/Juliens)) + +## [v1.2.0-rc1](https://github.com/containous/traefik/tree/v1.2.0-rc1) (2017-02-06) +[Full Changelog](https://github.com/containous/traefik/compare/v1.1.2...v1.2.0-rc1) + +**Implemented enhancements:** + +- Add FreeBSD and OpenBSD to release builds [\#923](https://github.com/containous/traefik/issues/923) +- Write authenticated user to header key [\#802](https://github.com/containous/traefik/issues/802) +- Question: Wildcard Host for Kubernetes Ingress [\#792](https://github.com/containous/traefik/issues/792) +- First commit prometheus middleware. [\#1022](https://github.com/containous/traefik/pull/1022) ([enxebre](https://github.com/enxebre)) +- Use deployment primitives from travis [\#843](https://github.com/containous/traefik/pull/843) ([guilhem](https://github.com/guilhem)) + +**Fixed bugs:** + +- Increase Docker API version to work with Windows Containers [\#1094](https://github.com/containous/traefik/issues/1094) + +**Closed issues:** + +- How could I know whether forwarding path is correctly set? [\#1111](https://github.com/containous/traefik/issues/1111) +- ACME + Docker-compose labels [\#1099](https://github.com/containous/traefik/issues/1099) +- Loadbalance between 2 containers in Docker Swarm Mode [\#1095](https://github.com/containous/traefik/issues/1095) +- Add DNS01 letsencrypt challenge support through AWS. [\#1093](https://github.com/containous/traefik/issues/1093) +- New Release Cut [\#1092](https://github.com/containous/traefik/issues/1092) +- Marathon integration changed default backend server port from task-level to application-level [\#1072](https://github.com/containous/traefik/issues/1072) +- websockets not working when compress = true in toml config. [\#1059](https://github.com/containous/traefik/issues/1059) +- Proxying 403 http status into the application [\#1044](https://github.com/containous/traefik/issues/1044) +- Normalize auto generated frontend-rule \(docker\) [\#1043](https://github.com/containous/traefik/issues/1043) +- Traefik with Consul catalog backend + Registrator [\#1039](https://github.com/containous/traefik/issues/1039) +- \[Configuration help\] Can't connect to docker containers under a domain path [\#1032](https://github.com/containous/traefik/issues/1032) +- Kubernetes and etcd backend : `storeconfig` fails. [\#1031](https://github.com/containous/traefik/issues/1031) +- kubernetes: Undefined backend 'X/' for frontend X/" [\#1026](https://github.com/containous/traefik/issues/1026) +- TLS handshake error [\#1025](https://github.com/containous/traefik/issues/1025) +- Traefik failing on POST request [\#1008](https://github.com/containous/traefik/issues/1008) +- how config traffic.toml http 80 without basic auth, traefik WebUI 8080 with basic auth [\#1001](https://github.com/containous/traefik/issues/1001) +- Docs 404 [\#995](https://github.com/containous/traefik/issues/995) +- Disable acme for non https endpoints [\#989](https://github.com/containous/traefik/issues/989) +- Add parameter to configure TLS entrypoints with ca-bundle file [\#984](https://github.com/containous/traefik/issues/984) +- docker multiple networks routing [\#970](https://github.com/containous/traefik/issues/970) +- don't add Docker containers not on the same network as traefik [\#959](https://github.com/containous/traefik/issues/959) +- Multiple frontend routes [\#957](https://github.com/containous/traefik/issues/957) +- SNI based routing without TLS offloading [\#933](https://github.com/containous/traefik/issues/933) +- NEO4J + traefik proxy Issues [\#907](https://github.com/containous/traefik/issues/907) +- ACME OnDemand ignores entrypoint certificate [\#672](https://github.com/containous/traefik/issues/672) +- Ability to use self-signed certificates for local development [\#399](https://github.com/containous/traefik/issues/399) + +**Merged pull requests:** + +- Fix checkout initial before calling rmpr [\#1124](https://github.com/containous/traefik/pull/1124) ([emilevauge](https://github.com/emilevauge)) +- Feature rancher integration [\#1120](https://github.com/containous/traefik/pull/1120) ([SantoDE](https://github.com/SantoDE)) +- Fix glide go units [\#1119](https://github.com/containous/traefik/pull/1119) ([emilevauge](https://github.com/emilevauge)) +- Carry \#818 — Add systemd watchdog feature [\#1116](https://github.com/containous/traefik/pull/1116) ([vdemeester](https://github.com/vdemeester)) +- Skip file permission check on Windows [\#1115](https://github.com/containous/traefik/pull/1115) ([StefanScherer](https://github.com/StefanScherer)) +- Fix Docker API version for Windows [\#1113](https://github.com/containous/traefik/pull/1113) ([StefanScherer](https://github.com/StefanScherer)) +- Fix git rpr [\#1109](https://github.com/containous/traefik/pull/1109) ([emilevauge](https://github.com/emilevauge)) +- Fix docker version specifier [\#1108](https://github.com/containous/traefik/pull/1108) ([timoreimann](https://github.com/timoreimann)) +- Merge v1.1.2 master [\#1105](https://github.com/containous/traefik/pull/1105) ([emilevauge](https://github.com/emilevauge)) +- add sh before script in deploy... [\#1103](https://github.com/containous/traefik/pull/1103) ([emilevauge](https://github.com/emilevauge)) +- \[doc\] typo fixes for kubernetes user guide [\#1102](https://github.com/containous/traefik/pull/1102) ([bamarni](https://github.com/bamarni)) +- add skip\_cleanup in deploy [\#1101](https://github.com/containous/traefik/pull/1101) ([emilevauge](https://github.com/emilevauge)) +- Fix k8s example UI port. [\#1098](https://github.com/containous/traefik/pull/1098) ([ddunkin](https://github.com/ddunkin)) +- Fix marathon provider [\#1090](https://github.com/containous/traefik/pull/1090) ([diegooliveira](https://github.com/diegooliveira)) +- Add an ECS provider [\#1088](https://github.com/containous/traefik/pull/1088) ([lpetre](https://github.com/lpetre)) +- Update comment to reflect the code [\#1087](https://github.com/containous/traefik/pull/1087) ([np](https://github.com/np)) +- update NYTimes/gziphandler fixes \#1059 [\#1084](https://github.com/containous/traefik/pull/1084) ([JamesKyburz](https://github.com/JamesKyburz)) +- Ensure that we don't add balancees with no health check runs if there is a health check defined on it [\#1080](https://github.com/containous/traefik/pull/1080) ([jangie](https://github.com/jangie)) +- Add FreeBSD & OpenBSD to crossbinary [\#1078](https://github.com/containous/traefik/pull/1078) ([geoffgarside](https://github.com/geoffgarside)) +- Fix metrics for multiple entry points [\#1071](https://github.com/containous/traefik/pull/1071) ([matevzmihalic](https://github.com/matevzmihalic)) +- Allow setting load balancer method and sticky using service annotations [\#1068](https://github.com/containous/traefik/pull/1068) ([bakins](https://github.com/bakins)) +- Fix travis script [\#1067](https://github.com/containous/traefik/pull/1067) ([emilevauge](https://github.com/emilevauge)) +- Add missing fmt verb specifier in k8s provider. [\#1066](https://github.com/containous/traefik/pull/1066) ([timoreimann](https://github.com/timoreimann)) +- Add git rpr command [\#1063](https://github.com/containous/traefik/pull/1063) ([emilevauge](https://github.com/emilevauge)) +- Fix k8s example [\#1062](https://github.com/containous/traefik/pull/1062) ([emilevauge](https://github.com/emilevauge)) +- Replace underscores to dash in autogenerated urls \(docker provider\) [\#1061](https://github.com/containous/traefik/pull/1061) ([WTFKr0](https://github.com/WTFKr0)) +- Don't run go test on .glide cache folder [\#1057](https://github.com/containous/traefik/pull/1057) ([vdemeester](https://github.com/vdemeester)) +- Allow setting circuitbreaker expression via Kubernetes annotation [\#1056](https://github.com/containous/traefik/pull/1056) ([bakins](https://github.com/bakins)) +- Improving instrumentation. [\#1042](https://github.com/containous/traefik/pull/1042) ([enxebre](https://github.com/enxebre)) +- Update user guide for upcoming `docker stack deploy` [\#1041](https://github.com/containous/traefik/pull/1041) ([twelvelabs](https://github.com/twelvelabs)) +- Support sticky sessions under SWARM Mode. \#1024 [\#1033](https://github.com/containous/traefik/pull/1033) ([foleymic](https://github.com/foleymic)) +- Allow for wildcards in k8s ingress host, fixes \#792 [\#1029](https://github.com/containous/traefik/pull/1029) ([sheerun](https://github.com/sheerun)) +- Don't fetch ACME certificates for frontends using non-TLS entrypoints \(\#989\) [\#1023](https://github.com/containous/traefik/pull/1023) ([syfonseq](https://github.com/syfonseq)) +- Return Proper Non-ACME certificate - Fixes Issue 672 [\#1018](https://github.com/containous/traefik/pull/1018) ([dtomcej](https://github.com/dtomcej)) +- Fix docs build and add missing benchmarks page [\#1017](https://github.com/containous/traefik/pull/1017) ([csabapalfi](https://github.com/csabapalfi)) +- Set a NopCloser request body with retry middleware [\#1016](https://github.com/containous/traefik/pull/1016) ([bamarni](https://github.com/bamarni)) +- instruct to flatten dependencies with glide [\#1010](https://github.com/containous/traefik/pull/1010) ([bamarni](https://github.com/bamarni)) +- check permissions on acme.json during startup [\#1009](https://github.com/containous/traefik/pull/1009) ([bamarni](https://github.com/bamarni)) +- \[doc\] few tweaks on the basics page [\#1005](https://github.com/containous/traefik/pull/1005) ([bamarni](https://github.com/bamarni)) +- Import order as goimports does [\#1004](https://github.com/containous/traefik/pull/1004) ([vdemeester](https://github.com/vdemeester)) +- See the right go report badge [\#991](https://github.com/containous/traefik/pull/991) ([guilhem](https://github.com/guilhem)) +- Add multiple values for one rule to docs [\#978](https://github.com/containous/traefik/pull/978) ([j0hnsmith](https://github.com/j0hnsmith)) +- Add ACME/Let’s Encrypt integration tests [\#975](https://github.com/containous/traefik/pull/975) ([trecloux](https://github.com/trecloux)) +- deploy.sh: upload release source tarball [\#969](https://github.com/containous/traefik/pull/969) ([Mic92](https://github.com/Mic92)) +- toml zookeeper doc fix [\#948](https://github.com/containous/traefik/pull/948) ([brdude](https://github.com/brdude)) +- Add Rule AddPrefix [\#931](https://github.com/containous/traefik/pull/931) ([Juliens](https://github.com/Juliens)) +- Add bug command [\#921](https://github.com/containous/traefik/pull/921) ([emilevauge](https://github.com/emilevauge)) +- \(WIP\) feat: HealthCheck [\#918](https://github.com/containous/traefik/pull/918) ([Juliens](https://github.com/Juliens)) +- Add ability to set authenticated user in request header [\#889](https://github.com/containous/traefik/pull/889) ([ViViDboarder](https://github.com/ViViDboarder)) +- IP-per-task: [\#841](https://github.com/containous/traefik/pull/841) ([diegooliveira](https://github.com/diegooliveira)) + ## [v1.1.2](https://github.com/containous/traefik/tree/v1.1.2) (2016-12-15) [Full Changelog](https://github.com/containous/traefik/compare/v1.1.1...v1.1.2) diff --git a/docs/basics.md b/docs/basics.md index 787604451..c183a2c84 100644 --- a/docs/basics.md +++ b/docs/basics.md @@ -239,6 +239,20 @@ For example: [backends.backend1.loadbalancer] sticky = true ``` + +Healthcheck URL can be configured with a relative URL for `healthcheck.URL`. +Interval between healthcheck can be configured by using `healthcheck.interval` +(default: 30s) + +For example: +```toml +[backends] + [backends.backend1] + [backends.backend1.healthcheck] + URL = "/health" + interval = "10s" +``` + ## Servers Servers are simply defined using a `URL`. You can also apply a custom `weight` to each server (this will be used by load-balancing). diff --git a/docs/toml.md b/docs/toml.md index 093c8b94b..fabbebc50 100644 --- a/docs/toml.md +++ b/docs/toml.md @@ -1423,3 +1423,102 @@ Labels can be used on task containers to override default behaviour: - `traefik.frontend.passHostHeader=true`: forward client `Host` header to the backend. - `traefik.frontend.priority=10`: override default frontend priority - `traefik.frontend.entryPoints=http,https`: assign this frontend to entry points `http` and `https`. Overrides `defaultEntryPoints`. + +If `AccessKeyID`/`SecretAccessKey` is not given credentials will be resolved in the following order: + +- From environment variables; `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, and `AWS_SESSION_TOKEN`. +- Shared credentials, determined by `AWS_PROFILE` and `AWS_SHARED_CREDENTIALS_FILE`, defaults to `default` and `~/.aws/credentials`. +- EC2 instance role or ECS task role + +Træfɪk needs the following policy to read ECS information: + +```json +{ + "Version": "2012-10-17", + "Statement": [ + { + "Sid": "Traefik ECS read access", + "Effect": "Allow", + "Action": [ + "ecs:ListTasks", + "ecs:DescribeTasks", + "ecs:DescribeContainerInstances", + "ecs:DescribeTaskDefinition", + "ec2:DescribeInstances" + ], + "Resource": [ + "*" + ] + } + ] +} +``` + +# Rancher backend + +Træfɪk can be configured to use Rancher as a backend configuration: + + +```toml +################################################################ +# Rancher configuration backend +################################################################ + +# Enable Rancher configuration backend +# +# Optional +# +[rancher] + +# Default domain used. +# Can be overridden by setting the "traefik.domain" label on an service. +# +# Required +# +domain = "rancher.localhost" + +# Enable watch Rancher changes +# +# Optional +# Default: true +# +Watch = true + +# Expose Rancher services by default in traefik +# +# Optional +# Default: true +# +ExposedByDefault = false + +# Endpoint to use when connecting to Rancher +# +# Optional +# Endpoint = "http://rancherserver.example.com" + +# AccessKey to use when connecting to Rancher +# +# Optional +# AccessKey = "XXXXXXXXX" + +# SecretKey to use when connecting to Rancher +# +# Optional +# SecretKey = "XXXXXXXXXXX" + +``` + +If you're deploying traefik as a service within rancher, you can alternatively set these labels on the service to let it only fetch data of its current environment. The settings `endpoint`, `accesskey` and `secretkey` can be omitted then. + +- `io.rancher.container.create_agent=true` +- `io.rancher.container.agent.role=environment` + +Labels can be used on task containers to override default behaviour: + +- `traefik.protocol=https`: override the default `http` protocol +- `traefik.weight=10`: assign this weight to the container +- `traefik.enable=false`: disable this container in Træfɪk +- `traefik.frontend.rule=Host:test.traefik.io`: override the default frontend rule (Default: `Host:{containerName}.{domain}`). +- `traefik.frontend.passHostHeader=true`: forward client `Host` header to the backend. +- `traefik.frontend.priority=10`: override default frontend priority +- `traefik.frontend.entryPoints=http,https`: assign this frontend to entry points `http` and `https`. Overrides `defaultEntryPoints`. diff --git a/examples/compose-rancher.yml b/examples/compose-rancher.yml new file mode 100644 index 000000000..cd4e0f677 --- /dev/null +++ b/examples/compose-rancher.yml @@ -0,0 +1,10 @@ +traefik: + image: traefik + command: --web --rancher --rancher.domain=rancher.localhost --logLevel=DEBUG + labels: + io.rancher.container.agent.role: environment + io.rancher.container.create_agent: 'true' + ports: + - "80:80" + - "443:443" + - "8080:8080" diff --git a/glide.lock b/glide.lock index ecbfcf043..5e0d06f53 100644 --- a/glide.lock +++ b/glide.lock @@ -1,5 +1,5 @@ -hash: b2ac93355c3f551a75216a800337cee9321f6c9a04a18ab1fa8d8152e89b7595 -updated: 2017-02-20T16:04:18.834166585+01:00 +hash: f2a0b9af55c8312762e4148bd876e5bd2451c240b407fbb6d4a5dbc56bf46c05 +updated: 2017-03-03T10:21:14.720631882+01:00 imports: - name: bitbucket.org/ww/goautoneg version: 75cd24fc2f2c2a2088577d12123ddee5f54e0675 @@ -444,7 +444,7 @@ imports: - assert - mock - name: github.com/thoas/stats - version: 79b768ff1780f4e5b0ed132e192bfeefe9f85a9c + version: 152b5d051953fdb6e45f14b6826962aadc032324 - name: github.com/timewasted/linode version: 37e84520dcf74488f67654f9c775b9752c232dc1 subpackages: diff --git a/glide.yaml b/glide.yaml index a4ec033e0..a26da9b0d 100644 --- a/glide.yaml +++ b/glide.yaml @@ -53,6 +53,7 @@ import: subpackages: - mock - package: github.com/thoas/stats + version: 152b5d051953fdb6e45f14b6826962aadc032324 - package: github.com/unrolled/render - package: github.com/vdemeester/docker-events version: be74d4929ec1ad118df54349fda4b0cba60f849b diff --git a/healthcheck/healthcheck.go b/healthcheck/healthcheck.go index cbae33cc4..30b4af495 100644 --- a/healthcheck/healthcheck.go +++ b/healthcheck/healthcheck.go @@ -26,6 +26,7 @@ func GetHealthCheck() *HealthCheck { // BackendHealthCheck HealthCheck configuration for a backend type BackendHealthCheck struct { URL string + Interval time.Duration DisabledURLs []*url.URL lb loadBalancer } @@ -49,8 +50,8 @@ func newHealthCheck() *HealthCheck { } // NewBackendHealthCheck Instantiate a new BackendHealthCheck -func NewBackendHealthCheck(URL string, lb loadBalancer) *BackendHealthCheck { - return &BackendHealthCheck{URL, nil, lb} +func NewBackendHealthCheck(URL string, interval time.Duration, lb loadBalancer) *BackendHealthCheck { + return &BackendHealthCheck{URL, interval, nil, lb} } //SetBackendsConfiguration set backends configuration @@ -70,7 +71,7 @@ func (hc *HealthCheck) execute(ctx context.Context) { currentBackendID := backendID safe.Go(func() { for { - ticker := time.NewTicker(time.Second * 30) + ticker := time.NewTicker(currentBackend.Interval) select { case <-ctx.Done(): log.Debugf("Stopping all current Healthcheck goroutines") diff --git a/integration/fixtures/healthcheck/simple.toml b/integration/fixtures/healthcheck/simple.toml new file mode 100644 index 000000000..c3ebb33e3 --- /dev/null +++ b/integration/fixtures/healthcheck/simple.toml @@ -0,0 +1,27 @@ +defaultEntryPoints = ["http"] + +logLevel = "DEBUG" + +[entryPoints] + [entryPoints.http] + address = ":8000" + +[web] + address = ":8080" + +[file] +[backends] + [backends.backend1] + [backends.backend1.healthcheck] + url = "/health" + interval = "1s" + [backends.backend1.servers.server1] + url = "http://{{.Server1}}:80" + [backends.backend1.servers.server2] + url = "http://{{.Server2}}:80" + +[frontends] + [frontends.frontend1] + backend = "backend1" + [frontends.frontend1.routes.test_1] + rule = "Host:test.localhost" diff --git a/integration/healthcheck_test.go b/integration/healthcheck_test.go new file mode 100644 index 000000000..532a431bd --- /dev/null +++ b/integration/healthcheck_test.go @@ -0,0 +1,91 @@ +package main + +import ( + "bytes" + "errors" + "io/ioutil" + "net/http" + "os" + "os/exec" + "strings" + "time" + + "github.com/containous/traefik/integration/utils" + "github.com/go-check/check" + + checker "github.com/vdemeester/shakers" +) + +// HealchCheck test suites (using libcompose) +type HealchCheckSuite struct{ BaseSuite } + +func (s *HealchCheckSuite) SetUpSuite(c *check.C) { + s.createComposeProject(c, "healthcheck") + s.composeProject.Start(c) + +} + +func (s *HealchCheckSuite) TestSimpleConfiguration(c *check.C) { + + whoami1Host := s.composeProject.Container(c, "whoami1").NetworkSettings.IPAddress + whoami2Host := s.composeProject.Container(c, "whoami2").NetworkSettings.IPAddress + + file := s.adaptFile(c, "fixtures/healthcheck/simple.toml", struct { + Server1 string + Server2 string + }{whoami1Host, whoami2Host}) + defer os.Remove(file) + cmd := exec.Command(traefikBinary, "--configFile="+file) + + err := cmd.Start() + c.Assert(err, checker.IsNil) + defer cmd.Process.Kill() + + // wait for traefik + err = utils.TryRequest("http://127.0.0.1:8080/api/providers", 60*time.Second, func(res *http.Response) error { + body, err := ioutil.ReadAll(res.Body) + if err != nil { + return err + } + if !strings.Contains(string(body), "Host:test.localhost") { + return errors.New("Incorrect traefik config: " + string(body)) + } + return nil + }) + c.Assert(err, checker.IsNil) + + client := &http.Client{} + req, err := http.NewRequest("GET", "http://127.0.0.1:8000/health", nil) + c.Assert(err, checker.IsNil) + req.Host = "test.localhost" + + resp, err := client.Do(req) + c.Assert(err, checker.IsNil) + c.Assert(resp.StatusCode, checker.Equals, 200) + + resp, err = client.Do(req) + c.Assert(err, checker.IsNil) + c.Assert(resp.StatusCode, checker.Equals, 200) + + healthReq, err := http.NewRequest("POST", "http://"+whoami1Host+"/health", bytes.NewBuffer([]byte("500"))) + c.Assert(err, checker.IsNil) + _, err = client.Do(healthReq) + c.Assert(err, checker.IsNil) + + time.Sleep(time.Second * 3) + + resp, err = client.Do(req) + c.Assert(err, checker.IsNil) + c.Assert(resp.StatusCode, checker.Equals, 200) + + resp, err = client.Do(req) + c.Assert(err, checker.IsNil) + c.Assert(resp.StatusCode, checker.Equals, 200) + + // TODO validate : run on 80 + resp, err = http.Get("http://127.0.0.1:8000/") + + // Expected a 404 as we did not configure anything + c.Assert(err, checker.IsNil) + c.Assert(resp.StatusCode, checker.Equals, 404) +} diff --git a/integration/resources/compose/healthcheck.yml b/integration/resources/compose/healthcheck.yml new file mode 100644 index 000000000..cbe13d553 --- /dev/null +++ b/integration/resources/compose/healthcheck.yml @@ -0,0 +1,5 @@ +whoami1: + image: emilevauge/whoami + +whoami2: + image: emilevauge/whoami diff --git a/provider/docker.go b/provider/docker.go index 1871d1c1f..4b182a917 100644 --- a/provider/docker.go +++ b/provider/docker.go @@ -626,11 +626,12 @@ func (provider *Docker) listServices(ctx context.Context, dockerClient client.AP for _, service := range serviceList { dockerData := parseService(service, networkMap) useSwarmLB, _ := strconv.ParseBool(provider.getIsBackendLBSwarm(dockerData)) + isGlobalSvc := service.Spec.Mode.Global != nil if useSwarmLB { dockerDataList = append(dockerDataList, dockerData) } else { - dockerDataListTasks, err = listTasks(ctx, dockerClient, service.ID, dockerData, networkMap) + dockerDataListTasks, err = listTasks(ctx, dockerClient, service.ID, dockerData, networkMap, isGlobalSvc) for _, dockerDataTask := range dockerDataListTasks { dockerDataList = append(dockerDataList, dockerDataTask) @@ -675,9 +676,10 @@ func parseService(service swarmtypes.Service, networkMap map[string]*dockertypes } func listTasks(ctx context.Context, dockerClient client.APIClient, serviceID string, - serviceDockerData dockerData, networkMap map[string]*dockertypes.NetworkResource) ([]dockerData, error) { + serviceDockerData dockerData, networkMap map[string]*dockertypes.NetworkResource, isGlobalSvc bool) ([]dockerData, error) { serviceIDFilter := filters.NewArgs() serviceIDFilter.Add("service", serviceID) + serviceIDFilter.Add("desired-state", "running") taskList, err := dockerClient.TaskList(ctx, dockertypes.TaskListOptions{Filter: serviceIDFilter}) if err != nil { @@ -686,13 +688,13 @@ func listTasks(ctx context.Context, dockerClient client.APIClient, serviceID str var dockerDataList []dockerData for _, task := range taskList { - dockerData := parseTasks(task, serviceDockerData, networkMap) + dockerData := parseTasks(task, serviceDockerData, networkMap, isGlobalSvc) dockerDataList = append(dockerDataList, dockerData) } return dockerDataList, err } -func parseTasks(task swarmtypes.Task, serviceDockerData dockerData, networkMap map[string]*dockertypes.NetworkResource) dockerData { +func parseTasks(task swarmtypes.Task, serviceDockerData dockerData, networkMap map[string]*dockertypes.NetworkResource, isGlobalSvc bool) dockerData { dockerData := dockerData{ ServiceName: serviceDockerData.Name, Name: serviceDockerData.Name + "." + strconv.Itoa(task.Slot), @@ -700,6 +702,10 @@ func parseTasks(task swarmtypes.Task, serviceDockerData dockerData, networkMap m NetworkSettings: networkSettings{}, } + if isGlobalSvc == true { + dockerData.Name = serviceDockerData.Name + "." + task.ID + } + if task.NetworksAttachments != nil { dockerData.NetworkSettings.Networks = make(map[string]*networkData) for _, virtualIP := range task.NetworksAttachments { diff --git a/provider/docker_test.go b/provider/docker_test.go index 4c10be819..7ed3fb9d8 100644 --- a/provider/docker_test.go +++ b/provider/docker_test.go @@ -2184,3 +2184,90 @@ func TestSwarmLoadDockerConfig(t *testing.T) { } } } + +func TestSwarmTaskParsing(t *testing.T) { + cases := []struct { + service swarm.Service + tasks []swarm.Task + isGlobalSvc bool + expectedNames map[string]string + networks map[string]*docker.NetworkResource + }{ + { + service: swarm.Service{ + Spec: swarm.ServiceSpec{ + Annotations: swarm.Annotations{ + Name: "container", + }, + }, + }, + tasks: []swarm.Task{ + { + ID: "id1", + Slot: 1, + }, + { + ID: "id2", + Slot: 2, + }, + { + ID: "id3", + Slot: 3, + }, + }, + isGlobalSvc: false, + expectedNames: map[string]string{ + "id1": "container.1", + "id2": "container.2", + "id3": "container.3", + }, + networks: map[string]*docker.NetworkResource{ + "1": { + Name: "foo", + }, + }, + }, + { + service: swarm.Service{ + Spec: swarm.ServiceSpec{ + Annotations: swarm.Annotations{ + Name: "container", + }, + }, + }, + tasks: []swarm.Task{ + { + ID: "id1", + }, + { + ID: "id2", + }, + { + ID: "id3", + }, + }, + isGlobalSvc: true, + expectedNames: map[string]string{ + "id1": "container.id1", + "id2": "container.id2", + "id3": "container.id3", + }, + networks: map[string]*docker.NetworkResource{ + "1": { + Name: "foo", + }, + }, + }, + } + + for _, e := range cases { + dockerData := parseService(e.service, e.networks) + + for _, task := range e.tasks { + taskDockerData := parseTasks(task, dockerData, map[string]*docker.NetworkResource{}, e.isGlobalSvc) + if !reflect.DeepEqual(taskDockerData.Name, e.expectedNames[task.ID]) { + t.Fatalf("expect %v, got %v", e.expectedNames[task.ID], taskDockerData.Name) + } + } + } +} diff --git a/provider/ecs.go b/provider/ecs.go index be38d79d0..a85c14c30 100644 --- a/provider/ecs.go +++ b/provider/ecs.go @@ -275,37 +275,48 @@ func (provider *ECS) listInstances(ctx context.Context, client *awsClient) ([]ec } func (provider *ECS) lookupEc2Instances(ctx context.Context, client *awsClient, containerArns []*string) ([]*ec2.Instance, error) { - req, containerResp := client.ecs.DescribeContainerInstancesRequest(&ecs.DescribeContainerInstancesInput{ - ContainerInstances: containerArns, - Cluster: &provider.Cluster, - }) - - if err := wrapAws(ctx, req); err != nil { - return nil, err - } order := make(map[string]int) + instanceIds := make([]*string, len(containerArns)) + instances := make([]*ec2.Instance, len(containerArns)) for i, arn := range containerArns { order[*arn] = i } - instanceIds := make([]*string, len(containerArns)) - for i, container := range containerResp.ContainerInstances { - order[*container.Ec2InstanceId] = order[*container.ContainerInstanceArn] - instanceIds[i] = container.Ec2InstanceId + req, _ := client.ecs.DescribeContainerInstancesRequest(&ecs.DescribeContainerInstancesInput{ + ContainerInstances: containerArns, + Cluster: &provider.Cluster, + }) + + for ; req != nil; req = req.NextPage() { + if err := wrapAws(ctx, req); err != nil { + return nil, err + } + + containerResp := req.Data.(*ecs.DescribeContainerInstancesOutput) + for i, container := range containerResp.ContainerInstances { + order[*container.Ec2InstanceId] = order[*container.ContainerInstanceArn] + instanceIds[i] = container.Ec2InstanceId + } } - req, instancesResp := client.ec2.DescribeInstancesRequest(&ec2.DescribeInstancesInput{ + req, _ = client.ec2.DescribeInstancesRequest(&ec2.DescribeInstancesInput{ InstanceIds: instanceIds, }) - if err := wrapAws(ctx, req); err != nil { - return nil, err - } + for ; req != nil; req = req.NextPage() { + if err := wrapAws(ctx, req); err != nil { + return nil, err + } - instances := make([]*ec2.Instance, len(containerArns)) - for _, r := range instancesResp.Reservations { - instances[order[*r.Instances[0].InstanceId]] = r.Instances[0] + instancesResp := req.Data.(*ec2.DescribeInstancesOutput) + for _, r := range instancesResp.Reservations { + for _, i := range r.Instances { + if i.InstanceId != nil { + instances[order[*i.InstanceId]] = i + } + } + } } return instances, nil } @@ -340,6 +351,23 @@ func (provider *ECS) filterInstance(i ecsInstance) bool { return false } + if i.machine == nil || + i.machine.State == nil || + i.machine.State.Name == nil { + log.Debugf("Filtering ecs instance in an missing ec2 information %s (%s)", i.Name, i.ID) + return false + } + + if *i.machine.State.Name != ec2.InstanceStateNameRunning { + log.Debugf("Filtering ecs instance in an incorrect state %s (%s) (state = %s)", i.Name, i.ID, *i.machine.State.Name) + return false + } + + if i.machine.PrivateIpAddress == nil { + log.Debugf("Filtering ecs instance without an ip address %s (%s)", i.Name, i.ID) + return false + } + label := i.label("traefik.enable") enabled := provider.ExposedByDefault && label != "false" || label == "true" if !enabled { diff --git a/provider/ecs_test.go b/provider/ecs_test.go index cf2a9b090..0365819b3 100644 --- a/provider/ecs_test.go +++ b/provider/ecs_test.go @@ -37,6 +37,9 @@ func makeEcsInstance(containerDef *ecs.ContainerDefinition) ecsInstance { containerDefinition: containerDef, machine: &ec2.Instance{ PrivateIpAddress: aws.String("10.0.0.0"), + State: &ec2.InstanceState{ + Name: aws.String(ec2.InstanceStateNameRunning), + }, }, } } @@ -70,10 +73,10 @@ func TestEcsProtocol(t *testing.T) { }, } - for _, c := range cases { + for i, c := range cases { value := c.instanceInfo.Protocol() if value != c.expected { - t.Fatalf("Should have been %s, got %s", c.expected, value) + t.Fatalf("Should have been %v, got %v (case %d)", c.expected, value, i) } } } @@ -89,10 +92,10 @@ func TestEcsHost(t *testing.T) { }, } - for _, c := range cases { + for i, c := range cases { value := c.instanceInfo.Host() if value != c.expected { - t.Fatalf("Should have been %s, got %s", c.expected, value) + t.Fatalf("Should have been %v, got %v (case %d)", c.expected, value, i) } } } @@ -108,10 +111,10 @@ func TestEcsPort(t *testing.T) { }, } - for _, c := range cases { + for i, c := range cases { value := c.instanceInfo.Port() if value != c.expected { - t.Fatalf("Should have been %s, got %s", c.expected, value) + t.Fatalf("Should have been %v, got %v (case %d)", c.expected, value, i) } } } @@ -133,10 +136,10 @@ func TestEcsWeight(t *testing.T) { }, } - for _, c := range cases { + for i, c := range cases { value := c.instanceInfo.Weight() if value != c.expected { - t.Fatalf("Should have been %s, got %s", c.expected, value) + t.Fatalf("Should have been %v, got %v (case %d)", c.expected, value, i) } } } @@ -158,10 +161,10 @@ func TestEcsPassHostHeader(t *testing.T) { }, } - for _, c := range cases { + for i, c := range cases { value := c.instanceInfo.PassHostHeader() if value != c.expected { - t.Fatalf("Should have been %s, got %s", c.expected, value) + t.Fatalf("Should have been %v, got %v (case %d)", c.expected, value, i) } } } @@ -183,10 +186,10 @@ func TestEcsPriority(t *testing.T) { }, } - for _, c := range cases { + for i, c := range cases { value := c.instanceInfo.Priority() if value != c.expected { - t.Fatalf("Should have been %s, got %s", c.expected, value) + t.Fatalf("Should have been %v, got %v (case %d)", c.expected, value, i) } } } @@ -214,10 +217,94 @@ func TestEcsEntryPoints(t *testing.T) { }, } - for _, c := range cases { + for i, c := range cases { value := c.instanceInfo.EntryPoints() if !reflect.DeepEqual(value, c.expected) { - t.Fatalf("Should have been %s, got %s", c.expected, value) + t.Fatalf("Should have been %v, got %v (case %d)", c.expected, value, i) + } + } +} + +func TestFilterInstance(t *testing.T) { + + nilPrivateIP := simpleEcsInstance(map[string]*string{}) + nilPrivateIP.machine.PrivateIpAddress = nil + + nilMachine := simpleEcsInstance(map[string]*string{}) + nilMachine.machine = nil + + nilMachineState := simpleEcsInstance(map[string]*string{}) + nilMachineState.machine.State = nil + + nilMachineStateName := simpleEcsInstance(map[string]*string{}) + nilMachineStateName.machine.State.Name = nil + + invalidMachineState := simpleEcsInstance(map[string]*string{}) + invalidMachineState.machine.State.Name = aws.String(ec2.InstanceStateNameStopped) + + cases := []struct { + expected bool + exposedByDefault bool + instanceInfo ecsInstance + }{ + { + expected: true, + exposedByDefault: true, + instanceInfo: simpleEcsInstance(map[string]*string{}), + }, + { + expected: false, + exposedByDefault: false, + instanceInfo: simpleEcsInstance(map[string]*string{}), + }, + { + expected: false, + exposedByDefault: true, + instanceInfo: simpleEcsInstance(map[string]*string{ + "traefik.enable": aws.String("false"), + }), + }, + { + expected: true, + exposedByDefault: false, + instanceInfo: simpleEcsInstance(map[string]*string{ + "traefik.enable": aws.String("true"), + }), + }, + { + expected: false, + exposedByDefault: true, + instanceInfo: nilPrivateIP, + }, + { + expected: false, + exposedByDefault: true, + instanceInfo: nilMachine, + }, + { + expected: false, + exposedByDefault: true, + instanceInfo: nilMachineState, + }, + { + expected: false, + exposedByDefault: true, + instanceInfo: nilMachineStateName, + }, + { + expected: false, + exposedByDefault: true, + instanceInfo: invalidMachineState, + }, + } + + for i, c := range cases { + provider := &ECS{ + ExposedByDefault: c.exposedByDefault, + } + value := provider.filterInstance(c.instanceInfo) + if value != c.expected { + t.Fatalf("Should have been %v, got %v (case %d)", c.expected, value, i) } } } diff --git a/provider/marathon.go b/provider/marathon.go index fa78ff03a..2e54248f3 100644 --- a/provider/marathon.go +++ b/provider/marathon.go @@ -264,9 +264,6 @@ func (provider *Marathon) taskFilter(task marathon.Task, applications *marathon. return false } } - } else { - log.Debugf("Filtering marathon task %s with defined healthcheck as no healthcheck has run yet", task.AppID) - return false } } return true diff --git a/provider/marathon_test.go b/provider/marathon_test.go index c4a5b5f98..3fe74b41e 100644 --- a/provider/marathon_test.go +++ b/provider/marathon_test.go @@ -629,7 +629,7 @@ func TestMarathonTaskFilter(t *testing.T) { }, }, }, - expected: false, + expected: true, exposedByDefault: true, }, { diff --git a/provider/rancher.go b/provider/rancher.go index 77072f0a9..72b2231d4 100644 --- a/provider/rancher.go +++ b/provider/rancher.go @@ -4,6 +4,13 @@ import ( "context" "errors" "fmt" + "math" + "os" + "strconv" + "strings" + "text/template" + "time" + "github.com/BurntSushi/ty/fun" "github.com/cenk/backoff" "github.com/containous/traefik/job" @@ -11,11 +18,6 @@ import ( "github.com/containous/traefik/safe" "github.com/containous/traefik/types" rancher "github.com/rancher/go-rancher/client" - "math" - "strconv" - "strings" - "text/template" - "time" ) const ( @@ -72,7 +74,7 @@ func (provider *Rancher) getFrontendRule(service rancherData) string { if label, err := getServiceLabel(service, "traefik.frontend.rule"); err == nil { return label } - return "Host:" + strings.ToLower(strings.Replace(service.Name, "/", "_", -1)) + "." + provider.Domain + return "Host:" + strings.ToLower(strings.Replace(service.Name, "/", ".", -1)) + "." + provider.Domain } func (provider *Rancher) getFrontendName(service rancherData) string { @@ -193,13 +195,26 @@ func getServiceLabel(service rancherData, label string) (string, error) { } func (provider *Rancher) createClient() (*rancher.RancherClient, error) { + + rancherURL := getenv("CATTLE_URL", provider.Endpoint) + accessKey := getenv("CATTLE_ACCESS_KEY", provider.AccessKey) + secretKey := getenv("CATTLE_SECRET_KEY", provider.SecretKey) + return rancher.NewRancherClient(&rancher.ClientOpts{ - Url: provider.Endpoint, - AccessKey: provider.AccessKey, - SecretKey: provider.SecretKey, + Url: rancherURL, + AccessKey: accessKey, + SecretKey: secretKey, }) } +func getenv(key, fallback string) string { + value := os.Getenv(key) + if len(value) == 0 { + return fallback + } + return value +} + // Provide allows the provider to provide configurations to traefik // using the given configuration channel. func (provider *Rancher) Provide(configurationChan chan<- types.ConfigMessage, pool *safe.Pool, constraints types.Constraints) error { @@ -207,6 +222,12 @@ func (provider *Rancher) Provide(configurationChan chan<- types.ConfigMessage, p safe.Go(func() { operation := func() error { rancherClient, err := provider.createClient() + + if err != nil { + log.Errorf("Failed to create a client for rancher, error: %s", err) + return err + } + ctx := context.Background() var environments = listRancherEnvironments(rancherClient) var services = listRancherServices(rancherClient) @@ -214,11 +235,6 @@ func (provider *Rancher) Provide(configurationChan chan<- types.ConfigMessage, p var rancherData = parseRancherData(environments, services, container) - if err != nil { - log.Errorf("Failed to create a client for rancher, error: %s", err) - return err - } - configuration := provider.loadRancherConfig(rancherData) configurationChan <- types.ConfigMessage{ ProviderName: "rancher", diff --git a/provider/rancher_test.go b/provider/rancher_test.go index d0b920f2d..9497a9139 100644 --- a/provider/rancher_test.go +++ b/provider/rancher_test.go @@ -87,6 +87,12 @@ func TestRancherGetFrontendRule(t *testing.T) { }, expected: "Host:foo.rancher.localhost", }, + { + service: rancherData{ + Name: "foo/bar", + }, + expected: "Host:foo.bar.rancher.localhost", + }, { service: rancherData{ Name: "test-service", @@ -388,7 +394,7 @@ func TestRancherLoadRancherConfig(t *testing.T) { { services: []rancherData{ { - Name: "test-service", + Name: "test/service", Labels: map[string]string{ "traefik.port": "80", }, @@ -405,7 +411,7 @@ func TestRancherLoadRancherConfig(t *testing.T) { Routes: map[string]types.Route{ "route-frontend-Host-test-service-rancher-localhost": { - Rule: "Host:test-service.rancher.localhost", + Rule: "Host:test.service.rancher.localhost", }, }, }, diff --git a/script/deploy.sh b/script/deploy.sh index 346b2a09f..4ee707905 100755 --- a/script/deploy.sh +++ b/script/deploy.sh @@ -1,7 +1,7 @@ #!/bin/bash set -e -if [ -n "$TRAVIS_TAG" ] && [ "$DOCKER_VERSION" = "1.10.1" ]; then +if [ -n "$TRAVIS_TAG" ] && [ "$DOCKER_VERSION" = "1.10.3" ]; then echo "Deploying..." else echo "Skipping deploy" diff --git a/server.go b/server.go index a2d3a406f..457093eb2 100644 --- a/server.go +++ b/server.go @@ -658,7 +658,15 @@ func (server *Server) loadConfig(configurations configs, globalConfiguration Glo continue frontend } if configuration.Backends[frontend.Backend].HealthCheck != nil { - backendsHealthcheck[frontend.Backend] = healthcheck.NewBackendHealthCheck(configuration.Backends[frontend.Backend].HealthCheck.URL, rebalancer) + var interval time.Duration + if configuration.Backends[frontend.Backend].HealthCheck.Interval != "" { + interval, err = time.ParseDuration(configuration.Backends[frontend.Backend].HealthCheck.Interval) + if err != nil { + log.Errorf("Wrong healthcheck interval: %s", err) + interval = time.Second * 30 + } + } + backendsHealthcheck[frontend.Backend] = healthcheck.NewBackendHealthCheck(configuration.Backends[frontend.Backend].HealthCheck.URL, interval, rebalancer) } } case types.Wrr: @@ -684,7 +692,15 @@ func (server *Server) loadConfig(configurations configs, globalConfiguration Glo } } if configuration.Backends[frontend.Backend].HealthCheck != nil { - backendsHealthcheck[frontend.Backend] = healthcheck.NewBackendHealthCheck(configuration.Backends[frontend.Backend].HealthCheck.URL, rr) + var interval time.Duration + if configuration.Backends[frontend.Backend].HealthCheck.Interval != "" { + interval, err = time.ParseDuration(configuration.Backends[frontend.Backend].HealthCheck.Interval) + if err != nil { + log.Errorf("Wrong healthcheck interval: %s", err) + interval = time.Second * 30 + } + } + backendsHealthcheck[frontend.Backend] = healthcheck.NewBackendHealthCheck(configuration.Backends[frontend.Backend].HealthCheck.URL, interval, rr) } } maxConns := configuration.Backends[frontend.Backend].MaxConn diff --git a/traefik.sample.toml b/traefik.sample.toml index e2c19f408..15d1d97d3 100644 --- a/traefik.sample.toml +++ b/traefik.sample.toml @@ -935,6 +935,52 @@ # # filename = "ecs.tmpl" +################################################################ +# Rancher configuration backend +################################################################ + +# Enable Rancher configuration backend +# +# Optional +# +[rancher] + +# Default domain used. +# Can be overridden by setting the "traefik.domain" label on an service. +# +# Required +# +domain = "rancher.localhost" + +# Enable watch Rancher changes +# +# Optional +# Default: true +# +Watch = true + +# Expose Rancher services by default in traefik +# +# Optional +# Default: true +# +ExposedByDefault = false + +# Endpoint to use when connecting to Rancher +# +# Optional +# Endpoint = "http://rancherserver.example.com" + +# AccessKey to use when connecting to Rancher +# +# Optional +# AccessKey = "XXXXXXXXX" + +# SecretKey to use when connecting to Rancher +# +# Optional +# SecretKey = "XXXXXXXXXXX" + ################################################################ # Sample rules diff --git a/types/types.go b/types/types.go index 630263164..308a71e25 100644 --- a/types/types.go +++ b/types/types.go @@ -39,7 +39,8 @@ type CircuitBreaker struct { // HealthCheck holds HealthCheck configuration type HealthCheck struct { - URL string `json:"url,omitempty"` + URL string `json:"url,omitempty"` + Interval string `json:"interval,omitempty"` } // Server holds server configuration.