diff --git a/.travis.yml b/.travis.yml index 725e8fc62..3b0e824e1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -20,6 +20,7 @@ install: - docker version - pip install --user mkdocs - pip install --user pymdown-extensions +- pip install --user mkdocs-bootswatch before_script: - make validate - make binary diff --git a/CHANGELOG.md b/CHANGELOG.md index 320724432..d7fbf314d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,312 @@ # Change Log +## [v1.1.1](https://github.com/containous/traefik/tree/v1.1.1) (2016-11-29) +[Full Changelog](https://github.com/containous/traefik/compare/v1.1.0...v1.1.1) + +**Implemented enhancements:** + +- Getting "Kubernetes connection error failed to decode watch event : unexpected EOF" every minute in Traefik log [\#732](https://github.com/containous/traefik/issues/732) + +**Fixed bugs:** + +- 1.1.0 kubernetes panic: send on closed channel [\#877](https://github.com/containous/traefik/issues/877) +- digest auth example is incorrect [\#869](https://github.com/containous/traefik/issues/869) +- Marathon & Mesos providers' GroupsAsSubDomains option broken [\#867](https://github.com/containous/traefik/issues/867) +- 404 responses when a new Marathon leader is elected [\#653](https://github.com/containous/traefik/issues/653) + +**Closed issues:** + +- traefik:latest fails to auto-detect Docker containers [\#901](https://github.com/containous/traefik/issues/901) +- Panic error on bare metal Kubernetes \(installed with Kubeadm\) [\#897](https://github.com/containous/traefik/issues/897) +- api backend readOnly: what is the purpose of this setting [\#893](https://github.com/containous/traefik/issues/893) +- file backend: using external file - doesn't work [\#892](https://github.com/containous/traefik/issues/892) +- auth support for web backend [\#891](https://github.com/containous/traefik/issues/891) +- Basic auth with docker labels [\#890](https://github.com/containous/traefik/issues/890) +- file vs inline config [\#888](https://github.com/containous/traefik/issues/888) +- combine Host and HostRegexp rules [\#882](https://github.com/containous/traefik/issues/882) +- \[Question\] Traefik + Kubernetes + Let's Encrypt \(ssl not used\) [\#881](https://github.com/containous/traefik/issues/881) +- Traefik security for dashboard [\#880](https://github.com/containous/traefik/issues/880) +- Kubernetes Nginx Deployment Panic [\#879](https://github.com/containous/traefik/issues/879) +- Kubernetes Example Address already in use [\#872](https://github.com/containous/traefik/issues/872) +- ETCD Backend - frontend/backends missing [\#866](https://github.com/containous/traefik/issues/866) +- \[Swarm mode\] Dashboard does not work on RC4 [\#864](https://github.com/containous/traefik/issues/864) +- Docker v1.1.0 image does not exist [\#861](https://github.com/containous/traefik/issues/861) +- ConsulService catalog do not support multiple rules [\#859](https://github.com/containous/traefik/issues/859) +- Update official docker repo [\#858](https://github.com/containous/traefik/issues/858) +- Still a memory leak with k8s - 1.1 RC4 [\#844](https://github.com/containous/traefik/issues/844) + +**Merged pull requests:** + +- Fix Swarm panic [\#908](https://github.com/containous/traefik/pull/908) ([emilevauge](https://github.com/emilevauge)) +- Fix k8s panic [\#900](https://github.com/containous/traefik/pull/900) ([emilevauge](https://github.com/emilevauge)) +- Fix missing value for k8s watch request parameter [\#874](https://github.com/containous/traefik/pull/874) ([codablock](https://github.com/codablock)) +- Fix GroupsAsSubDomains option for Mesos and Marathon [\#868](https://github.com/containous/traefik/pull/868) ([ryanleary](https://github.com/ryanleary)) +- Normalize backend even if is user-defined [\#865](https://github.com/containous/traefik/pull/865) ([WTFKr0](https://github.com/WTFKr0)) +- consul/kv.tmpl: weight default value should be a int [\#826](https://github.com/containous/traefik/pull/826) ([klausenbusk](https://github.com/klausenbusk)) + +## [v1.1.0](https://github.com/containous/traefik/tree/v1.1.0) (2016-11-17) +[Full Changelog](https://github.com/containous/traefik/compare/v1.0.0...v1.1.0) + +**Implemented enhancements:** + +- Support healthcheck if present for docker [\#666](https://github.com/containous/traefik/issues/666) +- Standard unit for traefik latency in access log [\#559](https://github.com/containous/traefik/issues/559) +- \[CI\] wiredep marked as unmaintained [\#550](https://github.com/containous/traefik/issues/550) +- Feature Request: Enable Health checks to containers. [\#540](https://github.com/containous/traefik/issues/540) +- Feature Request: SSL Cipher Selection [\#535](https://github.com/containous/traefik/issues/535) +- Error with -consulcatalog and missing load balance method on 1.0.0 [\#524](https://github.com/containous/traefik/issues/524) +- Running Traefik with Docker 1.12 Swarm Mode [\#504](https://github.com/containous/traefik/issues/504) +- Kubernetes provider: should allow the master url to be override [\#501](https://github.com/containous/traefik/issues/501) +- \[FRONTEND\]\[LE\] Pre-generate SSL certificates for "Host:" rules [\#483](https://github.com/containous/traefik/issues/483) +- Frontend Rule evolution [\#437](https://github.com/containous/traefik/issues/437) +- Add a Changelog [\#388](https://github.com/containous/traefik/issues/388) +- Add label matching for kubernetes ingests [\#363](https://github.com/containous/traefik/issues/363) +- Acme in HA Traefik Scenario [\#348](https://github.com/containous/traefik/issues/348) +- HTTP Basic Auth support [\#77](https://github.com/containous/traefik/issues/77) +- Session affinity / stickiness / persistence [\#5](https://github.com/containous/traefik/issues/5) + +**Fixed bugs:** + +- 1.1.0-rc4 dashboard UX not displaying [\#828](https://github.com/containous/traefik/issues/828) +- Traefik stopped serving on upgrade to v1.1.0-rc3 [\#807](https://github.com/containous/traefik/issues/807) +- cannot access webui/dashboard [\#796](https://github.com/containous/traefik/issues/796) +- Traefik cannot read constraints from KV [\#794](https://github.com/containous/traefik/issues/794) +- HTTP2 - configuration [\#790](https://github.com/containous/traefik/issues/790) +- Cannot provide multiple certificates using flag [\#757](https://github.com/containous/traefik/issues/757) +- Allow multiple certificates on a single entrypoint when trying to use TLS? [\#747](https://github.com/containous/traefik/issues/747) +- traefik \* Users: unsupported type: slice [\#743](https://github.com/containous/traefik/issues/743) +- \[Docker swarm mode\] The traefik.docker.network seems to have no effect [\#719](https://github.com/containous/traefik/issues/719) +- traefik hangs - stops handling requests [\#662](https://github.com/containous/traefik/issues/662) +- Add long jobs in exponential backoff providers [\#626](https://github.com/containous/traefik/issues/626) +- Tip of tree crashes on invalid pointer on Marathon provider [\#624](https://github.com/containous/traefik/issues/624) +- ACME: revoke certificate on agreement update [\#579](https://github.com/containous/traefik/issues/579) +- WebUI: Providers tabs disappeared [\#577](https://github.com/containous/traefik/issues/577) +- traefik version command contains incorrect information when building from master branch [\#569](https://github.com/containous/traefik/issues/569) +- Case sensitive domain names breaks routing [\#562](https://github.com/containous/traefik/issues/562) +- Flag --etcd.endpoint default [\#508](https://github.com/containous/traefik/issues/508) +- Conditional ACME on demand generation [\#505](https://github.com/containous/traefik/issues/505) +- Important delay with streams \(Mozilla EventSource\) [\#503](https://github.com/containous/traefik/issues/503) +- Traefik crashing [\#458](https://github.com/containous/traefik/issues/458) +- traefik.toml constraints error: `Expected map but found 'string'.` [\#451](https://github.com/containous/traefik/issues/451) +- Multiple path separators in the url path causing redirect [\#167](https://github.com/containous/traefik/issues/167) + +**Closed issues:** + +- All path rules require paths to be lowercase [\#851](https://github.com/containous/traefik/issues/851) +- The UI stops working after a time and have to restart the service. [\#840](https://github.com/containous/traefik/issues/840) +- Incorrect Dashboard page returned [\#831](https://github.com/containous/traefik/issues/831) +- LoadBalancing doesn't work in single node Swarm-mode [\#815](https://github.com/containous/traefik/issues/815) +- cannot connect to docker daemon [\#813](https://github.com/containous/traefik/issues/813) +- Let's encrypt configuration not working [\#805](https://github.com/containous/traefik/issues/805) +- Multiple subdomains for Marathon backend. [\#785](https://github.com/containous/traefik/issues/785) +- traefik-1.1.0-rc1: build error [\#781](https://github.com/containous/traefik/issues/781) +- dependencies installation error [\#755](https://github.com/containous/traefik/issues/755) +- k8s provider w/ acme? [\#752](https://github.com/containous/traefik/issues/752) +- Swarm Docs - How to use a FQDN [\#744](https://github.com/containous/traefik/issues/744) +- Documented ProvidersThrottleDuration value is invalid [\#741](https://github.com/containous/traefik/issues/741) +- Sensible configuration for consulCatalog [\#737](https://github.com/containous/traefik/issues/737) +- Traefik ignoring container listening in more than one TCP port [\#734](https://github.com/containous/traefik/issues/734) +- Loadbalaning issues with traefik and Docker Swarm cluster [\#730](https://github.com/containous/traefik/issues/730) +- issues with marathon app ids containing a dot [\#726](https://github.com/containous/traefik/issues/726) +- Error when using HA acme in kubernetes with etcd [\#725](https://github.com/containous/traefik/issues/725) +- \[Docker swarm mode\] No round robin when using service [\#718](https://github.com/containous/traefik/issues/718) +- Dose it support docker swarm mode [\#712](https://github.com/containous/traefik/issues/712) +- Kubernetes - Undefined backend [\#710](https://github.com/containous/traefik/issues/710) +- How Routing traffic depending on path not domain in docker [\#706](https://github.com/containous/traefik/issues/706) +- Constraints on Consul Catalogue not working as expected [\#703](https://github.com/containous/traefik/issues/703) +- Global InsecureSkipVerify does not work [\#700](https://github.com/containous/traefik/issues/700) +- Traefik crashes when using Consul catalog [\#699](https://github.com/containous/traefik/issues/699) +- \[documentation/feature\] Consul/etcd support atomic multiple key changes now [\#698](https://github.com/containous/traefik/issues/698) +- How to configure which network to use when starting traefik binary? [\#694](https://github.com/containous/traefik/issues/694) +- How to get multiple host headers working for docker labels? [\#692](https://github.com/containous/traefik/issues/692) +- Requests with URL-encoded characters are not forwarded correctly [\#684](https://github.com/containous/traefik/issues/684) +- File Watcher for rules does not work [\#683](https://github.com/containous/traefik/issues/683) +- Issue with global InsecureSkipVerify = true and self signed certificates [\#667](https://github.com/containous/traefik/issues/667) +- Docker exposedbydefault = false didn't work [\#663](https://github.com/containous/traefik/issues/663) +- swarm documentation needs update [\#656](https://github.com/containous/traefik/issues/656) +- \[ACME\] Auto SAN Detection [\#655](https://github.com/containous/traefik/issues/655) +- Fronting a domain with DNS A-record round-robin & ACME [\#654](https://github.com/containous/traefik/issues/654) +- Overriding toml configuration with environment variables [\#650](https://github.com/containous/traefik/issues/650) +- marathon provider exposedByDefault = false [\#647](https://github.com/containous/traefik/issues/647) +- Add status URL for service up checks [\#642](https://github.com/containous/traefik/issues/642) +- acme's storage file, containing private key, is word readable [\#638](https://github.com/containous/traefik/issues/638) +- wildcard domain with exclusions [\#633](https://github.com/containous/traefik/issues/633) +- Enable evenly distribution among backend [\#631](https://github.com/containous/traefik/issues/631) +- Traefik sporadically failing when proxying requests [\#615](https://github.com/containous/traefik/issues/615) +- TCP Proxy [\#608](https://github.com/containous/traefik/issues/608) +- How to use in Windows? [\#605](https://github.com/containous/traefik/issues/605) +- `ClientCAFiles` ignored [\#604](https://github.com/containous/traefik/issues/604) +- Let`s Encrypt enable in etcd [\#600](https://github.com/containous/traefik/issues/600) +- Support HTTP Basic Auth [\#599](https://github.com/containous/traefik/issues/599) +- Consul KV seem broken [\#587](https://github.com/containous/traefik/issues/587) +- HTTPS entryPoint not working [\#574](https://github.com/containous/traefik/issues/574) +- Traefik stuck when used as frontend for a streaming API [\#560](https://github.com/containous/traefik/issues/560) +- Exclude some frontends in consul catalog [\#555](https://github.com/containous/traefik/issues/555) +- Update docs with new Mesos provider [\#548](https://github.com/containous/traefik/issues/548) +- Can I use Traefik without a domain name? [\#539](https://github.com/containous/traefik/issues/539) +- docker run syntax in swarm example has changed [\#528](https://github.com/containous/traefik/issues/528) +- Priortities in 1.0.0 not behaving [\#506](https://github.com/containous/traefik/issues/506) +- Route by path [\#500](https://github.com/containous/traefik/issues/500) +- Secure WebSockets [\#467](https://github.com/containous/traefik/issues/467) +- Container IP Lost [\#375](https://github.com/containous/traefik/issues/375) +- Multiple routes support with Docker or Marathon labels [\#118](https://github.com/containous/traefik/issues/118) + +**Merged pull requests:** + +- Fix path case sensitive v1.1 [\#855](https://github.com/containous/traefik/pull/855) ([emilevauge](https://github.com/emilevauge)) +- Fix golint in v1.1 [\#849](https://github.com/containous/traefik/pull/849) ([emilevauge](https://github.com/emilevauge)) +- Fix Kubernetes watch leak [\#845](https://github.com/containous/traefik/pull/845) ([emilevauge](https://github.com/emilevauge)) +- Pass Version, Codename and Date to crosscompiled [\#842](https://github.com/containous/traefik/pull/842) ([guilhem](https://github.com/guilhem)) +- Add Nvd3 Dependency to fix UI / Dashboard [\#829](https://github.com/containous/traefik/pull/829) ([SantoDE](https://github.com/SantoDE)) +- Fix mkdoc theme [\#823](https://github.com/containous/traefik/pull/823) ([emilevauge](https://github.com/emilevauge)) +- Prepare release v1.1.0 rc4 [\#822](https://github.com/containous/traefik/pull/822) ([emilevauge](https://github.com/emilevauge)) +- Check that we serve HTTP/2 [\#820](https://github.com/containous/traefik/pull/820) ([trecloux](https://github.com/trecloux)) +- Fix multiple issues [\#814](https://github.com/containous/traefik/pull/814) ([emilevauge](https://github.com/emilevauge)) +- Fix ACME renew & add version check [\#783](https://github.com/containous/traefik/pull/783) ([emilevauge](https://github.com/emilevauge)) +- Use first port by default [\#782](https://github.com/containous/traefik/pull/782) ([guilhem](https://github.com/guilhem)) +- Prepare release v1.1.0-rc3 [\#779](https://github.com/containous/traefik/pull/779) ([emilevauge](https://github.com/emilevauge)) +- Fix ResponseRecorder Flush [\#776](https://github.com/containous/traefik/pull/776) ([emilevauge](https://github.com/emilevauge)) +- Use sdnotify for systemd [\#768](https://github.com/containous/traefik/pull/768) ([guilhem](https://github.com/guilhem)) +- Fix providers throttle duration doc [\#760](https://github.com/containous/traefik/pull/760) ([emilevauge](https://github.com/emilevauge)) +- Fix mapstructure issue with anonymous slice [\#759](https://github.com/containous/traefik/pull/759) ([emilevauge](https://github.com/emilevauge)) +- Fix multiple certificates using flag [\#758](https://github.com/containous/traefik/pull/758) ([emilevauge](https://github.com/emilevauge)) +- Really fix deploy ghr... [\#748](https://github.com/containous/traefik/pull/748) ([emilevauge](https://github.com/emilevauge)) +- Fixes deploy ghr [\#742](https://github.com/containous/traefik/pull/742) ([emilevauge](https://github.com/emilevauge)) +- prepare v1.1.0-rc2 [\#740](https://github.com/containous/traefik/pull/740) ([emilevauge](https://github.com/emilevauge)) +- Fix case sensitive host [\#733](https://github.com/containous/traefik/pull/733) ([emilevauge](https://github.com/emilevauge)) +- Update Kubernetes examples [\#731](https://github.com/containous/traefik/pull/731) ([Starefossen](https://github.com/Starefossen)) +- fIx marathon template with dots in ID [\#728](https://github.com/containous/traefik/pull/728) ([emilevauge](https://github.com/emilevauge)) +- Fix networkMap construction in ListServices [\#724](https://github.com/containous/traefik/pull/724) ([vincentlepot](https://github.com/vincentlepot)) +- Add basic compatibility with marathon-lb [\#720](https://github.com/containous/traefik/pull/720) ([guilhem](https://github.com/guilhem)) +- Add Ed's video at ContainerCamp [\#717](https://github.com/containous/traefik/pull/717) ([emilevauge](https://github.com/emilevauge)) +- Add documentation for Træfik on docker swarm mode [\#715](https://github.com/containous/traefik/pull/715) ([vdemeester](https://github.com/vdemeester)) +- Remove duplicated link to Kubernetes.io in README.md [\#713](https://github.com/containous/traefik/pull/713) ([oscerd](https://github.com/oscerd)) +- Show current version in web UI [\#709](https://github.com/containous/traefik/pull/709) ([vhf](https://github.com/vhf)) +- Add support for docker healthcheck 👼 [\#708](https://github.com/containous/traefik/pull/708) ([vdemeester](https://github.com/vdemeester)) +- Fix syntax in Swarm example. Resolves \#528 [\#707](https://github.com/containous/traefik/pull/707) ([billglover](https://github.com/billglover)) +- Add HTTP compression [\#702](https://github.com/containous/traefik/pull/702) ([tuier](https://github.com/tuier)) +- Carry PR 446 - Add sticky session support \(round two!\) [\#701](https://github.com/containous/traefik/pull/701) ([emilevauge](https://github.com/emilevauge)) +- Remove unused endpoint when using constraints with Marathon provider [\#697](https://github.com/containous/traefik/pull/697) ([tuier](https://github.com/tuier)) +- Replace imagelayers.io with microbadger [\#696](https://github.com/containous/traefik/pull/696) ([solidnerd](https://github.com/solidnerd)) +- Selectable TLS Versions [\#690](https://github.com/containous/traefik/pull/690) ([dtomcej](https://github.com/dtomcej)) +- Carry pr 439 [\#689](https://github.com/containous/traefik/pull/689) ([emilevauge](https://github.com/emilevauge)) +- Disable gorilla/mux URL cleaning to prevent sending redirect [\#688](https://github.com/containous/traefik/pull/688) ([ydubreuil](https://github.com/ydubreuil)) +- Some fixes [\#687](https://github.com/containous/traefik/pull/687) ([emilevauge](https://github.com/emilevauge)) +- feat\(constraints\): Supports constraints for Marathon provider [\#686](https://github.com/containous/traefik/pull/686) ([tuier](https://github.com/tuier)) +- Update docs to improve contribution setup [\#685](https://github.com/containous/traefik/pull/685) ([dtomcej](https://github.com/dtomcej)) +- Add basic auth support for web backend [\#677](https://github.com/containous/traefik/pull/677) ([SantoDE](https://github.com/SantoDE)) +- Document accepted values for logLevel. [\#676](https://github.com/containous/traefik/pull/676) ([jimmycuadra](https://github.com/jimmycuadra)) +- If Marathon doesn't have healthcheck, assume it's ok [\#665](https://github.com/containous/traefik/pull/665) ([gomes](https://github.com/gomes)) +- ACME: renew certificates 30 days before expiry [\#660](https://github.com/containous/traefik/pull/660) ([JayH5](https://github.com/JayH5)) +- Update broken link and add a comment to sample config file [\#658](https://github.com/containous/traefik/pull/658) ([Yggdrasil](https://github.com/Yggdrasil)) +- Add possibility to use BindPort IPAddress 👼 [\#657](https://github.com/containous/traefik/pull/657) ([vdemeester](https://github.com/vdemeester)) +- Update marathon [\#648](https://github.com/containous/traefik/pull/648) ([emilevauge](https://github.com/emilevauge)) +- Add backend features to docker [\#646](https://github.com/containous/traefik/pull/646) ([jangie](https://github.com/jangie)) +- enable consul catalog to use maxconn [\#645](https://github.com/containous/traefik/pull/645) ([jangie](https://github.com/jangie)) +- Adopt the Code Of Coduct from http://contributor-covenant.org [\#641](https://github.com/containous/traefik/pull/641) ([errm](https://github.com/errm)) +- Use secure mode 600 instead of 644 for acme.json [\#639](https://github.com/containous/traefik/pull/639) ([discordianfish](https://github.com/discordianfish)) +- docker clarification, fix dead urls, misc typos [\#637](https://github.com/containous/traefik/pull/637) ([djalal](https://github.com/djalal)) +- add PING handler to dashboard API [\#630](https://github.com/containous/traefik/pull/630) ([jangie](https://github.com/jangie)) +- Migrate to JobBackOff [\#628](https://github.com/containous/traefik/pull/628) ([emilevauge](https://github.com/emilevauge)) +- Add long job exponential backoff [\#627](https://github.com/containous/traefik/pull/627) ([emilevauge](https://github.com/emilevauge)) +- HA acme support [\#625](https://github.com/containous/traefik/pull/625) ([emilevauge](https://github.com/emilevauge)) +- Bump go v1.7 [\#620](https://github.com/containous/traefik/pull/620) ([emilevauge](https://github.com/emilevauge)) +- Make duration logging consistent [\#619](https://github.com/containous/traefik/pull/619) ([jangie](https://github.com/jangie)) +- fix for nil clientTLS causing issue [\#617](https://github.com/containous/traefik/pull/617) ([jangie](https://github.com/jangie)) +- Add ability for marathon provider to set maxconn values, loadbalancer algorithm, and circuit breaker expression [\#616](https://github.com/containous/traefik/pull/616) ([jangie](https://github.com/jangie)) +- Make systemd unit installable [\#613](https://github.com/containous/traefik/pull/613) ([keis](https://github.com/keis)) +- Merge v1.0.2 master [\#610](https://github.com/containous/traefik/pull/610) ([emilevauge](https://github.com/emilevauge)) +- update staert and flaeg [\#609](https://github.com/containous/traefik/pull/609) ([cocap10](https://github.com/cocap10)) +- \#504 Initial support for Docker 1.12 Swarm Mode [\#602](https://github.com/containous/traefik/pull/602) ([diegofernandes](https://github.com/diegofernandes)) +- Add Host cert ACME generation [\#601](https://github.com/containous/traefik/pull/601) ([emilevauge](https://github.com/emilevauge)) +- Fixed binary script so traefik version command doesn't just print default values [\#598](https://github.com/containous/traefik/pull/598) ([keiths-osc](https://github.com/keiths-osc)) +- Name servers after thier pods [\#596](https://github.com/containous/traefik/pull/596) ([errm](https://github.com/errm)) +- Fix Consul prefix [\#589](https://github.com/containous/traefik/pull/589) ([jippi](https://github.com/jippi)) +- Prioritize kubernetes routes by path length [\#588](https://github.com/containous/traefik/pull/588) ([philk](https://github.com/philk)) +- beautify help [\#580](https://github.com/containous/traefik/pull/580) ([cocap10](https://github.com/cocap10)) +- Upgrade directives name since we use angular-ui-bootstrap [\#578](https://github.com/containous/traefik/pull/578) ([micaelmbagira](https://github.com/micaelmbagira)) +- Fix basic docs for configuration of multiple rules [\#576](https://github.com/containous/traefik/pull/576) ([ajaegle](https://github.com/ajaegle)) +- Fix k8s watch [\#573](https://github.com/containous/traefik/pull/573) ([errm](https://github.com/errm)) +- Add requirements.txt for netlify [\#567](https://github.com/containous/traefik/pull/567) ([emilevauge](https://github.com/emilevauge)) +- Merge v1.0.1 master [\#565](https://github.com/containous/traefik/pull/565) ([emilevauge](https://github.com/emilevauge)) +- Move webui to FountainJS with Webpack [\#558](https://github.com/containous/traefik/pull/558) ([micaelmbagira](https://github.com/micaelmbagira)) +- Add global InsecureSkipVerify option to disable certificate checking [\#557](https://github.com/containous/traefik/pull/557) ([stuart-c](https://github.com/stuart-c)) +- Move version.go in its own package… [\#553](https://github.com/containous/traefik/pull/553) ([vdemeester](https://github.com/vdemeester)) +- Upgrade libkermit and dependencies [\#552](https://github.com/containous/traefik/pull/552) ([vdemeester](https://github.com/vdemeester)) +- Add command storeconfig [\#551](https://github.com/containous/traefik/pull/551) ([cocap10](https://github.com/cocap10)) +- Add basic/digest auth [\#547](https://github.com/containous/traefik/pull/547) ([emilevauge](https://github.com/emilevauge)) +- Bump node to 6 for webui [\#546](https://github.com/containous/traefik/pull/546) ([vdemeester](https://github.com/vdemeester)) +- Bump golang to 1.6.3 [\#545](https://github.com/containous/traefik/pull/545) ([vdemeester](https://github.com/vdemeester)) +- Fix typos [\#538](https://github.com/containous/traefik/pull/538) ([jimt](https://github.com/jimt)) +- Kubernetes user-guide [\#519](https://github.com/containous/traefik/pull/519) ([errm](https://github.com/errm)) +- Implement Kubernetes Selectors, minor kube endpoint fix [\#516](https://github.com/containous/traefik/pull/516) ([pnegahdar](https://github.com/pnegahdar)) +- Carry \#358 : Option to disable expose of all docker containers [\#514](https://github.com/containous/traefik/pull/514) ([vdemeester](https://github.com/vdemeester)) +- Remove traefik.frontend.value support in docker… [\#510](https://github.com/containous/traefik/pull/510) ([vdemeester](https://github.com/vdemeester)) +- Use KvStores as global config sources [\#481](https://github.com/containous/traefik/pull/481) ([cocap10](https://github.com/cocap10)) +- Add endpoint option to authenticate by client tls cert. [\#461](https://github.com/containous/traefik/pull/461) ([andersbetner](https://github.com/andersbetner)) +- add mesos provider inspired by mesos-dns & marathon provider [\#353](https://github.com/containous/traefik/pull/353) ([skydjol](https://github.com/skydjol)) + +## [v1.1.0-rc4](https://github.com/containous/traefik/tree/v1.1.0-rc4) (2016-11-10) +[Full Changelog](https://github.com/containous/traefik/compare/v1.1.0-rc3...v1.1.0-rc4) + +**Implemented enhancements:** + +- Feature Request: Enable Health checks to containers. [\#540](https://github.com/containous/traefik/issues/540) + +**Fixed bugs:** + +- Traefik stopped serving on upgrade to v1.1.0-rc3 [\#807](https://github.com/containous/traefik/issues/807) +- Traefik cannot read constraints from KV [\#794](https://github.com/containous/traefik/issues/794) +- HTTP2 - configuration [\#790](https://github.com/containous/traefik/issues/790) +- Allow multiple certificates on a single entrypoint when trying to use TLS? [\#747](https://github.com/containous/traefik/issues/747) + +**Closed issues:** + +- LoadBalancing doesn't work in single node Swarm-mode [\#815](https://github.com/containous/traefik/issues/815) +- cannot connect to docker daemon [\#813](https://github.com/containous/traefik/issues/813) +- Let's encrypt configuration not working [\#805](https://github.com/containous/traefik/issues/805) +- Question: Wildcard Host for Kubernetes Ingress [\#792](https://github.com/containous/traefik/issues/792) +- Multiple subdomains for Marathon backend. [\#785](https://github.com/containous/traefik/issues/785) +- traefik-1.1.0-rc1: build error [\#781](https://github.com/containous/traefik/issues/781) +- Multiple routes support with Docker or Marathon labels [\#118](https://github.com/containous/traefik/issues/118) + +**Merged pull requests:** + +- Prepare release v1.1.0 rc4 [\#822](https://github.com/containous/traefik/pull/822) ([emilevauge](https://github.com/emilevauge)) +- Fix multiple issues [\#814](https://github.com/containous/traefik/pull/814) ([emilevauge](https://github.com/emilevauge)) +- Fix ACME renew & add version check [\#783](https://github.com/containous/traefik/pull/783) ([emilevauge](https://github.com/emilevauge)) +- Use first port by default [\#782](https://github.com/containous/traefik/pull/782) ([guilhem](https://github.com/guilhem)) + +## [v1.1.0-rc3](https://github.com/containous/traefik/tree/v1.1.0-rc3) (2016-10-26) +[Full Changelog](https://github.com/containous/traefik/compare/v1.1.0-rc2...v1.1.0-rc3) + +**Fixed bugs:** + +- Cannot provide multiple certificates using flag [\#757](https://github.com/containous/traefik/issues/757) +- traefik \* Users: unsupported type: slice [\#743](https://github.com/containous/traefik/issues/743) +- \[Docker swarm mode\] The traefik.docker.network seems to have no effect [\#719](https://github.com/containous/traefik/issues/719) +- Case sensitive domain names breaks routing [\#562](https://github.com/containous/traefik/issues/562) + +**Closed issues:** + +- dependencies installation error [\#755](https://github.com/containous/traefik/issues/755) +- k8s provider w/ acme? [\#752](https://github.com/containous/traefik/issues/752) +- Documented ProvidersThrottleDuration value is invalid [\#741](https://github.com/containous/traefik/issues/741) +- Loadbalaning issues with traefik and Docker Swarm cluster [\#730](https://github.com/containous/traefik/issues/730) +- issues with marathon app ids containing a dot [\#726](https://github.com/containous/traefik/issues/726) +- How Routing traffic depending on path not domain in docker [\#706](https://github.com/containous/traefik/issues/706) +- Traefik crashes when using Consul catalog [\#699](https://github.com/containous/traefik/issues/699) +- File Watcher for rules does not work [\#683](https://github.com/containous/traefik/issues/683) + +**Merged pull requests:** + +- Fix ResponseRecorder Flush [\#776](https://github.com/containous/traefik/pull/776) ([emilevauge](https://github.com/emilevauge)) +- Use sdnotify for systemd [\#768](https://github.com/containous/traefik/pull/768) ([guilhem](https://github.com/guilhem)) +- Fix providers throttle duration doc [\#760](https://github.com/containous/traefik/pull/760) ([emilevauge](https://github.com/emilevauge)) +- Fix mapstructure issue with anonymous slice [\#759](https://github.com/containous/traefik/pull/759) ([emilevauge](https://github.com/emilevauge)) +- Fix multiple certificates using flag [\#758](https://github.com/containous/traefik/pull/758) ([emilevauge](https://github.com/emilevauge)) +- Really fix deploy ghr... [\#748](https://github.com/containous/traefik/pull/748) ([emilevauge](https://github.com/emilevauge)) + ## [v1.1.0-rc2](https://github.com/containous/traefik/tree/v1.1.0-rc2) (2016-10-17) [Full Changelog](https://github.com/containous/traefik/compare/v1.1.0-rc1...v1.1.0-rc2) diff --git a/README.md b/README.md index 7bf30bf30..22e7fc232 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,7 @@ Run it and forget it! - Circuit breakers on backends - Round Robin, rebalancer load-balancers - Rest Metrics -- [Tiny](https://imagelayers.io/?images=traefik) [official](https://hub.docker.com/r/_/traefik/) docker image included +- [Tiny](https://microbadger.com/images/traefik) [official](https://hub.docker.com/r/_/traefik/) docker image included - SSL backends support - SSL frontend support (with SNI) - Clean AngularJS Web UI @@ -159,9 +159,10 @@ Founded in 2014, Asteris creates next-generation infrastructure software for the - Emile Vauge [@emilevauge](https://github.com/emilevauge) - Vincent Demeester [@vdemeester](https://github.com/vdemeester) -- Samuel Berthe [@samber](https://github.com/samber) - Russell Clare [@Russell-IO](https://github.com/Russell-IO) - Ed Robinson [@errm](https://github.com/errm) +- Daniel Tomcej [@dtomcej](https://github.com/dtomcej) +- Manuel Laufenberg [@SantoDE](https://github.com/SantoDE) ## Credits diff --git a/acme/acme.go b/acme/acme.go index bbe561e47..79de45746 100644 --- a/acme/acme.go +++ b/acme/acme.go @@ -137,12 +137,14 @@ func (a *ACME) CreateClusterConfig(leadership *cluster.Leadership, tlsConfig *tl leadership.Pool.AddGoCtx(func(ctx context.Context) { log.Infof("Starting ACME renew job...") defer log.Infof("Stopped ACME renew job...") - select { - case <-ctx.Done(): - return - case <-ticker.C: - if err := a.renewCertificates(); err != nil { - log.Errorf("Error renewing ACME certificate: %s", err.Error()) + for { + select { + case <-ctx.Done(): + return + case <-ticker.C: + if err := a.renewCertificates(); err != nil { + log.Errorf("Error renewing ACME certificate: %s", err.Error()) + } } } }) @@ -252,7 +254,6 @@ func (a *ACME) CreateLocalConfig(tlsConfig *tls.Config, checkOnDemandDomain func needRegister = true } - log.Infof("buildACMEClient...") a.client, err = a.buildACMEClient(account) if err != nil { return err @@ -270,7 +271,7 @@ func (a *ACME) CreateLocalConfig(tlsConfig *tls.Config, checkOnDemandDomain func // The client has a URL to the current Let's Encrypt Subscriber // Agreement. The user will need to agree to it. - log.Infof("AgreeToTOS...") + log.Debugf("AgreeToTOS...") err = a.client.AgreeToTOS() if err != nil { // Let's Encrypt Subscriber Agreement renew ? @@ -374,11 +375,6 @@ func (a *ACME) renewCertificates() error { account := a.store.Get().(*Account) for _, certificateResource := range account.DomainsCertificate.Certs { if certificateResource.needRenew() { - transaction, object, err := a.store.Begin() - if err != nil { - return err - } - account = object.(*Account) log.Debugf("Renewing certificate %+v", certificateResource.Domains) renewedCert, err := a.client.RenewCertificate(acme.CertificateResource{ Domain: certificateResource.Certificate.Domain, @@ -399,6 +395,11 @@ func (a *ACME) renewCertificates() error { PrivateKey: renewedCert.PrivateKey, Certificate: renewedCert.Certificate, } + transaction, object, err := a.store.Begin() + if err != nil { + return err + } + account = object.(*Account) err = account.DomainsCertificate.renewCertificates(renewedACMECert, certificateResource.Domains) if err != nil { log.Errorf("Error renewing certificate: %v", err) diff --git a/configuration.go b/configuration.go index 6754d67e8..d8cc2dd96 100644 --- a/configuration.go +++ b/configuration.go @@ -25,10 +25,11 @@ type TraefikConfiguration struct { type GlobalConfiguration struct { GraceTimeOut int64 `short:"g" description:"Duration to give active requests a chance to finish during hot-reload"` Debug bool `short:"d" description:"Enable debug mode"` + CheckNewVersion bool `description:"Periodically check if a new version has been released"` AccessLogsFile string `description:"Access logs file"` TraefikLogsFile string `description:"Traefik logs file"` LogLevel string `short:"l" description:"Log level"` - EntryPoints EntryPoints `description:"Entrypoints definition using format: --entryPoints='Name:http Address::8000 Redirect.EntryPoint:https' --entryPoints='Name:https Address::4442 TLS:tests/traefik.crt,tests/traefik.key'"` + EntryPoints EntryPoints `description:"Entrypoints definition using format: --entryPoints='Name:http Address::8000 Redirect.EntryPoint:https' --entryPoints='Name:https Address::4442 TLS:tests/traefik.crt,tests/traefik.key;prod/traefik.crt,prod/traefik.key'"` Cluster *types.Cluster `description:"Enable clustering"` Constraints types.Constraints `description:"Filter services by constraint, matching with service tags"` ACME *acme.ACME `description:"Enable ACME (Let's Encrypt): automatic SSL"` @@ -264,21 +265,28 @@ func (certs *Certificates) String() string { if len(*certs) == 0 { return "" } - return (*certs)[0].CertFile + "," + (*certs)[0].KeyFile + var result []string + for _, certificate := range *certs { + result = append(result, certificate.CertFile+","+certificate.KeyFile) + } + return strings.Join(result, ";") } // Set is the method to set the flag value, part of the flag.Value interface. // Set's argument is a string to be parsed to set the flag. // It's a comma-separated list, so we split it. func (certs *Certificates) Set(value string) error { - files := strings.Split(value, ",") - if len(files) != 2 { - return errors.New("Bad certificates format: " + value) + certificates := strings.Split(value, ";") + for _, certificate := range certificates { + files := strings.Split(certificate, ",") + if len(files) != 2 { + return errors.New("Bad certificates format: " + value) + } + *certs = append(*certs, Certificate{ + CertFile: files[0], + KeyFile: files[1], + }) } - *certs = append(*certs, Certificate{ - CertFile: files[0], - KeyFile: files[1], - }) return nil } @@ -325,7 +333,7 @@ func NewTraefikDefaultPointersConfiguration() *TraefikConfiguration { defaultMarathon.Watch = true defaultMarathon.Endpoint = "http://127.0.0.1:8080" defaultMarathon.ExposedByDefault = true - defaultMarathon.Constraints = []types.Constraint{} + defaultMarathon.Constraints = types.Constraints{} defaultMarathon.DialerTimeout = 60 defaultMarathon.KeepAlive = 10 @@ -334,47 +342,47 @@ func NewTraefikDefaultPointersConfiguration() *TraefikConfiguration { defaultConsul.Watch = true defaultConsul.Endpoint = "127.0.0.1:8500" defaultConsul.Prefix = "traefik" - defaultConsul.Constraints = []types.Constraint{} + defaultConsul.Constraints = types.Constraints{} // default ConsulCatalog var defaultConsulCatalog provider.ConsulCatalog defaultConsulCatalog.Endpoint = "127.0.0.1:8500" - defaultConsulCatalog.Constraints = []types.Constraint{} + defaultConsulCatalog.Constraints = types.Constraints{} // default Etcd var defaultEtcd provider.Etcd defaultEtcd.Watch = true defaultEtcd.Endpoint = "127.0.0.1:2379" defaultEtcd.Prefix = "/traefik" - defaultEtcd.Constraints = []types.Constraint{} + defaultEtcd.Constraints = types.Constraints{} //default Zookeeper var defaultZookeeper provider.Zookepper defaultZookeeper.Watch = true defaultZookeeper.Endpoint = "127.0.0.1:2181" defaultZookeeper.Prefix = "/traefik" - defaultZookeeper.Constraints = []types.Constraint{} + defaultZookeeper.Constraints = types.Constraints{} //default Boltdb var defaultBoltDb provider.BoltDb defaultBoltDb.Watch = true defaultBoltDb.Endpoint = "127.0.0.1:4001" defaultBoltDb.Prefix = "/traefik" - defaultBoltDb.Constraints = []types.Constraint{} + defaultBoltDb.Constraints = types.Constraints{} //default Kubernetes var defaultKubernetes provider.Kubernetes defaultKubernetes.Watch = true defaultKubernetes.Endpoint = "" defaultKubernetes.LabelSelector = "" - defaultKubernetes.Constraints = []types.Constraint{} + defaultKubernetes.Constraints = types.Constraints{} // default Mesos var defaultMesos provider.Mesos defaultMesos.Watch = true defaultMesos.Endpoint = "http://127.0.0.1:5050" defaultMesos.ExposedByDefault = true - defaultMesos.Constraints = []types.Constraint{} + defaultMesos.Constraints = types.Constraints{} defaultConfiguration := GlobalConfiguration{ Docker: &defaultDocker, @@ -404,10 +412,11 @@ func NewTraefikConfiguration() *TraefikConfiguration { TraefikLogsFile: "", LogLevel: "ERROR", EntryPoints: map[string]*EntryPoint{}, - Constraints: []types.Constraint{}, + Constraints: types.Constraints{}, DefaultEntryPoints: []string{}, ProvidersThrottleDuration: time.Duration(2 * time.Second), MaxIdleConnsPerHost: 200, + CheckNewVersion: true, }, ConfigFile: "", } diff --git a/contrib/systemd/traefik.service b/contrib/systemd/traefik.service index 846d22ee6..c93f8e27d 100644 --- a/contrib/systemd/traefik.service +++ b/contrib/systemd/traefik.service @@ -2,6 +2,7 @@ Description=Traefik [Service] +Type=notify ExecStart=/usr/bin/traefik --configFile=/etc/traefik.toml Restart=on-failure diff --git a/docs/index.md b/docs/index.md index 1941c8615..efaa0f31d 100644 --- a/docs/index.md +++ b/docs/index.md @@ -40,10 +40,15 @@ Run it and forget it! You can have a quick look at Træfɪk in this [Katacoda tutorial](https://www.katacoda.com/courses/traefik/deploy-load-balancer) that shows how to load balance requests between multiple Docker containers. -Here is a talk (in french) given by [Emile Vauge](https://github.com/emilevauge) at the [Devoxx France 2016](http://www.devoxx.fr) conference. -You will learn fundamental Træfɪk features and see some demos with Docker, Mesos/Marathon and Lets'Encrypt. +Here is a talk given by [Ed Robinson](https://github.com/errm) at the [ContainerCamp UK](https://container.camp) conference. +You will learn fundamental Træfɪk features and see some demos with Kubernetes. -[![Traefik Devoxx France](https://img.youtube.com/vi/QvAz9mVx5TI/0.jpg)](https://www.youtube.com/watch?v=QvAz9mVx5TI) +[![Traefik ContainerCamp UK](http://img.youtube.com/vi/aFtpIShV60I/0.jpg)](https://www.youtube.com/watch?v=aFtpIShV60I) + +Here is a talk (in French) given by [Emile Vauge](https://github.com/emilevauge) at the [Devoxx France 2016](http://www.devoxx.fr) conference. +You will learn fundamental Træfɪk features and see some demos with Docker, Mesos/Marathon and Let's Encrypt. + +[![Traefik Devoxx France](http://img.youtube.com/vi/QvAz9mVx5TI/0.jpg)](http://www.youtube.com/watch?v=QvAz9mVx5TI) ## Get it diff --git a/docs/toml.md b/docs/toml.md index e4e60a664..326797b6c 100644 --- a/docs/toml.md +++ b/docs/toml.md @@ -9,6 +9,28 @@ # Global configuration ################################################################ +# Timeout in seconds. +# Duration to give active requests a chance to finish during hot-reloads +# +# Optional +# Default: 10 +# +# graceTimeOut = 10 + +# Enable debug mode +# +# Optional +# Default: false +# +# debug = true + +# Periodically check if a new version has been released +# +# Optional +# Default: true +# +# checkNewVersion = false + # Traefik logs file # If not defined, logs to stdout # @@ -31,14 +53,14 @@ # # logLevel = "ERROR" -# Backends throttle duration: minimum duration between 2 events from providers +# Backends throttle duration: minimum duration in seconds between 2 events from providers # before applying a new configuration. It avoids unnecessary reloads if multiples events # are sent in a short amount of time. # # Optional -# Default: "2s" +# Default: "2" # -# ProvidersThrottleDuration = "5s" +# ProvidersThrottleDuration = "5" # If non-zero, controls the maximum idle (keep-alive) to keep per-host. If zero, DefaultMaxIdleConnsPerHost is used. # If you encounter 'too many open files' errors, you can either change this value, or change `ulimit` value. @@ -493,7 +515,7 @@ address = ":8080" # To enable digest auth on the webui # with 2 user/realm/pass: test:traefik:test and test2:traefik:test2 # You can use htdigest to generate those ones -# [web.auth.basic] +# [web.auth.digest] # users = ["test:traefik:a2688e031edb4be6a3797f3882655c05 ", "test2:traefik:518845800f9e2bfb1f1f740ec24f074e"] ``` diff --git a/glide.lock b/glide.lock index 13aa2c14f..8a84e8de3 100644 --- a/glide.lock +++ b/glide.lock @@ -1,5 +1,5 @@ -hash: 3ff2f92356bc5d286ce4d72835d5ace63c43cc591e561fd2d557e8dc8abd1672 -updated: 2016-11-17T17:17:58.806741449Z +hash: 26bdc224454872acf1a9a58e0f4c33442a807087286043ed7d8d6640f1a2e8fc +updated: 2016-12-05T21:21:43.691375582+01:00 imports: - name: github.com/abbot/go-http-auth version: cb4372376e1e00e9f6ab9ec142e029302c9e7140 @@ -10,7 +10,7 @@ imports: - name: github.com/ArthurHlt/gominlog version: 068c01ce147ad68fca25ef3fa29ae5395ae273ab - name: github.com/blang/semver - version: 60ec3488bfea7cca02b021d106d9911120d25fe9 + version: 3a37c301dda64cbe17f16f661b4c976803c0e2d2 - name: github.com/boltdb/bolt version: f4c032d907f61f08dba2d719c58f108a1abb8e81 - name: github.com/BurntSushi/toml @@ -21,8 +21,6 @@ imports: - fun - name: github.com/cenk/backoff version: 8edc80b07f38c27352fb186d971c628a6c32552b -- name: github.com/cloudfoundry-incubator/candiedyaml - version: 99c3df83b51532e3615f851d8c2dbb638f5313bf - name: github.com/codahale/hdrhistogram version: 9208b142303c12d8899bae836fd524ac9338b4fd - name: github.com/codegangsta/cli @@ -34,7 +32,7 @@ imports: - name: github.com/containous/mux version: a819b77bba13f0c0cbe36e437bc2e948411b3996 - name: github.com/containous/staert - version: 92329254783dc01174f03302d51d7cf2c9ff84cf + version: 1e26a71803e428fd933f5f9c8e50a26878f53147 - name: github.com/coreos/etcd version: c400d05d0aa73e21e431c16145e558d624098018 subpackages: @@ -44,13 +42,17 @@ imports: - pkg/pathutil - pkg/types - name: github.com/coreos/go-oidc - version: 16c5ecc505f1efa0fe4685826fd9962c4d137e87 + version: dedb650fb29c39c2f21aa88c1e4cec66da8754d1 subpackages: - http - jose - key - oauth2 - oidc +- name: github.com/coreos/go-systemd + version: 43e4800a6165b4e02bb2a36673c54b230d6f7b26 + subpackages: + - daemon - name: github.com/coreos/pkg version: 2c77715c4df99b5420ffcae14ead08f52104065d subpackages: @@ -174,13 +176,13 @@ imports: - name: github.com/gambol99/go-marathon version: a558128c87724cd7430060ef5aedf39f83937f55 - name: github.com/ghodss/yaml - version: aa0c862057666179de291b67d9f093d12b5a8473 + version: a54de18a07046d8c4b26e9327698a2ebb9285b36 - name: github.com/go-openapi/jsonpointer version: 8d96a2dc61536b690bd36b2e9df0b3c0b62825b2 - name: github.com/go-openapi/jsonreference version: 36d33bfe519efae5632669801b180bf1a245da3b - name: github.com/go-openapi/spec - version: d1c18b339aece4b16ead6d253b85b6ad7180ea54 + version: f7ae86df5bc115a2744343016c789a89f065a4bd - name: github.com/go-openapi/swag version: 3b6d86cd965820f968760d5d419cb4add096bdd7 - name: github.com/gogo/protobuf @@ -194,22 +196,28 @@ imports: version: 5677a0e3d5e89854c9974e1256839ee23f8233ca subpackages: - proto +- name: github.com/google/go-github + version: 55263f30529cb06f5b478efc333390b791cfe3b1 + subpackages: + - github - name: github.com/google/go-querystring version: 9235644dd9e52eeae6fa48efd539fdc351a0af53 subpackages: - query - name: github.com/google/gofuzz - version: fd52762d25a41827db7ef64c43756fd4b9f7e382 + version: 44d81051d367757e1c7c6a5a86423ece9afcf63c - name: github.com/gorilla/context - version: 14f550f51af52180c2eefed15e5fd18d63c0a64a + version: 08b5f424b9271eedf6f9f0ce86cb9396ed337a42 - name: github.com/hashicorp/consul version: d8e2fb7dd594163e25a89bc52c1a4613f5c5bfb8 subpackages: - api - name: github.com/hashicorp/go-cleanhttp version: ad28ea4487f05916463e2423a55166280e8254b5 +- name: github.com/hashicorp/go-version + version: e96d3840402619007766590ecea8dd7af1292276 - name: github.com/hashicorp/serf - version: 598c54895cc5a7b1a24a398d635e8c0ea0959870 + version: b03bf85930b2349eb04b97c8fac437495296e3e7 subpackages: - coordinate - name: github.com/jarcoal/httpmock @@ -257,19 +265,23 @@ imports: - name: github.com/miekg/dns version: 5d001d020961ae1c184f9f8152fdc73810481677 - name: github.com/mitchellh/mapstructure - version: ca63d7c062ee3c9f34db231e352b60012b4fd0c1 + version: f3009df150dadf309fdee4a54ed65c124afad715 - name: github.com/NYTimes/gziphandler version: f6438dbf4a82c56684964b03956aa727b0d7816b - name: github.com/ogier/pflag version: 45c278ab3607870051a2ea9040bb85fcb8557481 - name: github.com/opencontainers/runc - version: ba1568de399395774ad84c2ace65937814c542ed + version: 02f8fa7863dd3f82909a73e2061897828460d52f subpackages: - libcontainer/user - name: github.com/parnurzeal/gorequest version: e30af16d4e485943aab0b0885ad6bdbb8c0d3dc7 - name: github.com/pborman/uuid version: 3d4f2ba23642d3cfd06bd4b54cf03d99d95c0f1b +- name: github.com/pmezard/go-difflib + version: d8ed2627bdf02c080bf22230dbb337003b7aba2d + subpackages: + - difflib - name: github.com/PuerkitoBio/purell version: 0bcb03f4b4d0a9428594752bd2a3b9aa0a9d4bd4 - name: github.com/PuerkitoBio/urlesc @@ -291,7 +303,7 @@ imports: - name: github.com/stretchr/objx version: cbeaeb16a013161a98496fad62933b1d21786672 - name: github.com/stretchr/testify - version: b8dc1cecf15bdaf1988d9e87aa7cd98d899a06d6 + version: 976c720a22c8eb4eb6a0b4348ad85ad12491a506 subpackages: - assert - mock @@ -305,10 +317,12 @@ imports: - codec - name: github.com/unrolled/render version: 526faf80cd4b305bb8134abea8d20d5ced74faa6 +- name: github.com/urfave/negroni + version: e0e50f7dc431c043cb33f91b09c3419d48b7cff5 - name: github.com/vdemeester/docker-events version: be74d4929ec1ad118df54349fda4b0cba60f849b - name: github.com/vulcand/oxy - version: 4298f24d572dc554eb984f2ffdf6bdd54d4bd613 + version: fcc76b52eb8568540a020b7a99e854d9d752b364 repo: https://github.com/containous/oxy.git vcs: git subpackages: @@ -363,7 +377,7 @@ imports: - unix - windows - name: golang.org/x/text - version: a263ba8db058568bb9beba166777d9c9dbe75d68 + version: 5c6cf4f9a2357d38515014cea8c488ed22bdab90 subpackages: - transform - unicode/norm @@ -390,7 +404,7 @@ imports: - name: gopkg.in/inf.v0 version: 3887ee99ecf07df5b447e9b00d9c0b2adaa9f3e4 - name: gopkg.in/mgo.v2 - version: 29cc868a5ca65f401ff318143f9408d02f4799cc + version: 22287bab4379e1fbf6002fb4eb769888f3fb224c subpackages: - bson - name: gopkg.in/square/go-jose.v1 @@ -398,6 +412,8 @@ imports: subpackages: - cipher - json +- name: gopkg.in/yaml.v2 + version: bef53efd0c76e49e6de55ead051f886bea7e9420 - name: k8s.io/client-go version: 843f7c4f28b1f647f664f883697107d5c02c5acc subpackages: @@ -509,6 +525,8 @@ testImports: version: fa152c58bc15761d0200cb75fe958b89a9d4888e subpackages: - winterm +- name: github.com/cloudfoundry-incubator/candiedyaml + version: 99c3df83b51532e3615f851d8c2dbb638f5313bf - name: github.com/docker/libcompose version: d1876c1d68527a49c0aac22a0b161acc7296b740 subpackages: @@ -529,7 +547,7 @@ testImports: - name: github.com/flynn/go-shlex version: 3f9db97f856818214da2e1057f8ad84803971cff - name: github.com/go-check/check - version: 11d3bc7aa68e238947792f30573146a3231fc0f1 + version: 4f90aeace3a26ad7021961c297b22c42160c7b25 - name: github.com/gorilla/mux version: e444e69cbd2e2e3e0749a2f3c717cec491552bbf - name: github.com/libkermit/compose diff --git a/glide.yaml b/glide.yaml index 3673d1fff..cef792fd5 100644 --- a/glide.yaml +++ b/glide.yaml @@ -6,11 +6,11 @@ import: - fun - package: github.com/Sirupsen/logrus - package: github.com/cenk/backoff -- package: github.com/codegangsta/negroni +- package: github.com/urfave/negroni - package: github.com/containous/flaeg version: a731c034dda967333efce5f8d276aeff11f8ff87 - package: github.com/vulcand/oxy - version: 4298f24d572dc554eb984f2ffdf6bdd54d4bd613 + version: fcc76b52eb8568540a020b7a99e854d9d752b364 repo: https://github.com/containous/oxy.git vcs: git subpackages: @@ -21,7 +21,7 @@ import: - stream - utils - package: github.com/containous/staert - version: 92329254783dc01174f03302d51d7cf2c9ff84cf + version: 1e26a71803e428fd933f5f9c8e50a26878f53147 - package: github.com/docker/engine-api version: 62043eb79d581a32ea849645277023c550732e52 subpackages: @@ -103,3 +103,11 @@ import: - package: github.com/ArthurHlt/go-eureka-client subpackages: - eureka +- package: github.com/mitchellh/mapstructure + version: f3009df150dadf309fdee4a54ed65c124afad715 +- package: github.com/coreos/go-systemd + version: v12 + subpackages: + - daemon +- package: github.com/google/go-github +- package: github.com/hashicorp/go-version \ No newline at end of file diff --git a/integration/https_test.go b/integration/https_test.go index 36f3953f5..7bf2c2a59 100644 --- a/integration/https_test.go +++ b/integration/https_test.go @@ -30,6 +30,7 @@ func (s *HTTPSSuite) TestWithSNIConfigHandshake(c *check.C) { tlsConfig := &tls.Config{ InsecureSkipVerify: true, ServerName: "snitest.com", + NextProtos: []string{"h2", "http/1.1"}, } conn, err := tls.Dial("tcp", "127.0.0.1:4443", tlsConfig) c.Assert(err, checker.IsNil, check.Commentf("failed to connect to server")) @@ -41,6 +42,9 @@ func (s *HTTPSSuite) TestWithSNIConfigHandshake(c *check.C) { cs := conn.ConnectionState() err = cs.PeerCertificates[0].VerifyHostname("snitest.com") c.Assert(err, checker.IsNil, check.Commentf("certificate did not match SNI servername")) + + proto := conn.ConnectionState().NegotiatedProtocol + c.Assert(proto, checker.Equals, "h2") } // TestWithSNIConfigRoute involves a client sending HTTPS requests with diff --git a/middlewares/retry.go b/middlewares/retry.go index 8577a1685..021306304 100644 --- a/middlewares/retry.go +++ b/middlewares/retry.go @@ -9,6 +9,13 @@ import ( "net/http" ) +var ( + _ http.ResponseWriter = &ResponseRecorder{} + _ http.Hijacker = &ResponseRecorder{} + _ http.Flusher = &ResponseRecorder{} + _ http.CloseNotifier = &ResponseRecorder{} +) + // Retry is a middleware that retries requests type Retry struct { attempts int @@ -52,6 +59,7 @@ type ResponseRecorder struct { Body *bytes.Buffer // if non-nil, the bytes.Buffer to append written data to responseWriter http.ResponseWriter + err error } // NewRecorder returns an initialized ResponseRecorder. @@ -75,10 +83,10 @@ func (rw *ResponseRecorder) Header() http.Header { // Write always succeeds and writes to rw.Body, if not nil. func (rw *ResponseRecorder) Write(buf []byte) (int, error) { - if rw.Body != nil { - return rw.Body.Write(buf) + if rw.err != nil { + return 0, rw.err } - return 0, nil + return rw.Body.Write(buf) } // WriteHeader sets rw.Code. @@ -90,3 +98,24 @@ func (rw *ResponseRecorder) WriteHeader(code int) { func (rw *ResponseRecorder) Hijack() (net.Conn, *bufio.ReadWriter, error) { return rw.responseWriter.(http.Hijacker).Hijack() } + +// CloseNotify returns a channel that receives at most a +// single value (true) when the client connection has gone +// away. +func (rw *ResponseRecorder) CloseNotify() <-chan bool { + return rw.responseWriter.(http.CloseNotifier).CloseNotify() +} + +// Flush sends any buffered data to the client. +func (rw *ResponseRecorder) Flush() { + _, err := rw.responseWriter.Write(rw.Body.Bytes()) + if err != nil { + log.Errorf("Error writing response in ResponseRecorder: %s", err) + rw.err = err + } + rw.Body.Reset() + flusher, ok := rw.responseWriter.(http.Flusher) + if ok { + flusher.Flush() + } +} diff --git a/provider/boltdb.go b/provider/boltdb.go index 3347fe340..e604fd428 100644 --- a/provider/boltdb.go +++ b/provider/boltdb.go @@ -17,7 +17,7 @@ type BoltDb struct { // Provide allows the provider to provide configurations to traefik // using the given configuration channel. -func (provider *BoltDb) Provide(configurationChan chan<- types.ConfigMessage, pool *safe.Pool, constraints []types.Constraint) error { +func (provider *BoltDb) Provide(configurationChan chan<- types.ConfigMessage, pool *safe.Pool, constraints types.Constraints) error { store, err := provider.CreateStore() if err != nil { return fmt.Errorf("Failed to Connect to KV store: %v", err) diff --git a/provider/consul.go b/provider/consul.go index 1b628dd4f..9316c513a 100644 --- a/provider/consul.go +++ b/provider/consul.go @@ -17,7 +17,7 @@ type Consul struct { // Provide allows the provider to provide configurations to traefik // using the given configuration channel. -func (provider *Consul) Provide(configurationChan chan<- types.ConfigMessage, pool *safe.Pool, constraints []types.Constraint) error { +func (provider *Consul) Provide(configurationChan chan<- types.ConfigMessage, pool *safe.Pool, constraints types.Constraints) error { store, err := provider.CreateStore() if err != nil { return fmt.Errorf("Failed to Connect to KV store: %v", err) diff --git a/provider/consul_catalog.go b/provider/consul_catalog.go index aad2e8ae5..b83da3b1d 100644 --- a/provider/consul_catalog.go +++ b/provider/consul_catalog.go @@ -317,7 +317,7 @@ func (provider *ConsulCatalog) watch(configurationChan chan<- types.ConfigMessag // Provide allows the provider to provide configurations to traefik // using the given configuration channel. -func (provider *ConsulCatalog) Provide(configurationChan chan<- types.ConfigMessage, pool *safe.Pool, constraints []types.Constraint) error { +func (provider *ConsulCatalog) Provide(configurationChan chan<- types.ConfigMessage, pool *safe.Pool, constraints types.Constraints) error { config := api.DefaultConfig() config.Address = provider.Endpoint client, err := api.NewClient(config) diff --git a/provider/consul_catalog_test.go b/provider/consul_catalog_test.go index d6d41207d..789aa432d 100644 --- a/provider/consul_catalog_test.go +++ b/provider/consul_catalog_test.go @@ -61,7 +61,7 @@ func TestConsulCatalogGetAttribute(t *testing.T) { "traefik.backend.weight=42", }, key: "backend.weight", - defaultValue: "", + defaultValue: "0", expected: "42", }, { @@ -70,8 +70,8 @@ func TestConsulCatalogGetAttribute(t *testing.T) { "traefik.backend.wei=42", }, key: "backend.weight", - defaultValue: "", - expected: "", + defaultValue: "0", + expected: "0", }, } diff --git a/provider/docker.go b/provider/docker.go index bbcd3d931..27c35760d 100644 --- a/provider/docker.go +++ b/provider/docker.go @@ -113,7 +113,7 @@ func (provider *Docker) createClient() (client.APIClient, error) { // Provide allows the provider to provide configurations to traefik // using the given configuration channel. -func (provider *Docker) Provide(configurationChan chan<- types.ConfigMessage, pool *safe.Pool, constraints []types.Constraint) error { +func (provider *Docker) Provide(configurationChan chan<- types.ConfigMessage, pool *safe.Pool, constraints types.Constraints) error { provider.Constraints = append(provider.Constraints, constraints...) // TODO register this routine in pool, and watch for stop channel safe.Go(func() { @@ -363,10 +363,6 @@ func (provider *Docker) containerFilter(container dockerData) bool { log.Debugf("Filtering container without port and no traefik.port label %s", container.Name) return false } - if len(container.NetworkSettings.Ports) > 1 && err != nil { - log.Debugf("Filtering container with more than 1 port and no traefik.port label %s", container.Name) - return false - } if !isContainerEnabled(container, provider.ExposedByDefault) { log.Debugf("Filtering disabled container %s", container.Name) @@ -405,7 +401,7 @@ func (provider *Docker) getFrontendRule(container dockerData) string { func (provider *Docker) getBackend(container dockerData) string { if label, err := getLabel(container, "traefik.backend"); err == nil { - return label + return normalize(label) } return normalize(container.Name) } @@ -458,7 +454,7 @@ func (provider *Docker) getWeight(container dockerData) string { if label, err := getLabel(container, "traefik.weight"); err == nil { return label } - return "1" + return "0" } func (provider *Docker) getSticky(container dockerData) string { @@ -563,6 +559,10 @@ func parseContainer(container dockertypes.ContainerJSON) dockerData { if container.ContainerJSONBase.HostConfig != nil { dockerData.NetworkSettings.NetworkMode = container.ContainerJSONBase.HostConfig.NetworkMode } + + if container.State != nil && container.State.Health != nil { + dockerData.Health = container.State.Health.Status + } } if container.Config != nil && container.Config.Labels != nil { @@ -586,10 +586,6 @@ func parseContainer(container dockertypes.ContainerJSON) dockerData { } - if container.State != nil && container.State.Health != nil { - dockerData.Health = container.State.Health.Status - } - return dockerData } diff --git a/provider/docker_test.go b/provider/docker_test.go index 3d106bd71..43fbcb5e4 100644 --- a/provider/docker_test.go +++ b/provider/docker_test.go @@ -390,6 +390,27 @@ func TestDockerGetPort(t *testing.T) { }, expected: "8080", }, + { + container: docker.ContainerJSON{ + ContainerJSONBase: &docker.ContainerJSONBase{ + Name: "test-multi-ports", + }, + Config: &container.Config{ + Labels: map[string]string{ + "traefik.port": "8080", + }, + }, + NetworkSettings: &docker.NetworkSettings{ + NetworkSettingsBase: docker.NetworkSettingsBase{ + Ports: nat.PortMap{ + "8080/tcp": {}, + "80/tcp": {}, + }, + }, + }, + }, + expected: "8080", + }, } for _, e := range containers { @@ -415,7 +436,7 @@ func TestDockerGetWeight(t *testing.T) { }, Config: &container.Config{}, }, - expected: "1", + expected: "0", }, { container: docker.ContainerJSON{ @@ -735,7 +756,7 @@ func TestDockerTraefikFilter(t *testing.T) { { container: docker.ContainerJSON{ ContainerJSONBase: &docker.ContainerJSONBase{ - Name: "container", + Name: "container-multi-ports", }, Config: &container.Config{}, NetworkSettings: &docker.NetworkSettings{ @@ -748,7 +769,7 @@ func TestDockerTraefikFilter(t *testing.T) { }, }, exposedByDefault: true, - expected: false, + expected: true, }, { container: docker.ContainerJSON{ @@ -951,7 +972,7 @@ func TestDockerLoadDockerConfig(t *testing.T) { Servers: map[string]types.Server{ "server-test": { URL: "http://127.0.0.1:80", - Weight: 1, + Weight: 0, }, }, CircuitBreaker: nil, @@ -1033,11 +1054,11 @@ func TestDockerLoadDockerConfig(t *testing.T) { Servers: map[string]types.Server{ "server-test1": { URL: "http://127.0.0.1:80", - Weight: 1, + Weight: 0, }, "server-test2": { URL: "http://127.0.0.1:80", - Weight: 1, + Weight: 0, }, }, CircuitBreaker: nil, @@ -1091,7 +1112,7 @@ func TestDockerLoadDockerConfig(t *testing.T) { Servers: map[string]types.Server{ "server-test1": { URL: "http://127.0.0.1:80", - Weight: 1, + Weight: 0, }, }, CircuitBreaker: &types.CircuitBreaker{ @@ -1506,7 +1527,7 @@ func TestSwarmGetWeight(t *testing.T) { }, }, }, - expected: "1", + expected: "0", networks: map[string]*docker.NetworkResource{}, }, { @@ -2034,7 +2055,7 @@ func TestSwarmLoadDockerConfig(t *testing.T) { Servers: map[string]types.Server{ "server-test": { URL: "http://127.0.0.1:80", - Weight: 1, + Weight: 0, }, }, CircuitBreaker: nil, @@ -2122,11 +2143,11 @@ func TestSwarmLoadDockerConfig(t *testing.T) { Servers: map[string]types.Server{ "server-test1": { URL: "http://127.0.0.1:80", - Weight: 1, + Weight: 0, }, "server-test2": { URL: "http://127.0.0.1:80", - Weight: 1, + Weight: 0, }, }, CircuitBreaker: nil, diff --git a/provider/etcd.go b/provider/etcd.go index 9c7726baa..7dc49d0aa 100644 --- a/provider/etcd.go +++ b/provider/etcd.go @@ -17,7 +17,7 @@ type Etcd struct { // Provide allows the provider to provide configurations to traefik // using the given configuration channel. -func (provider *Etcd) Provide(configurationChan chan<- types.ConfigMessage, pool *safe.Pool, constraints []types.Constraint) error { +func (provider *Etcd) Provide(configurationChan chan<- types.ConfigMessage, pool *safe.Pool, constraints types.Constraints) error { store, err := provider.CreateStore() if err != nil { return fmt.Errorf("Failed to Connect to KV store: %v", err) diff --git a/provider/eureka.go b/provider/eureka.go index 7ec2a4bfd..50b5f9c6a 100644 --- a/provider/eureka.go +++ b/provider/eureka.go @@ -23,7 +23,7 @@ type Eureka struct { // Provide allows the provider to provide configurations to traefik // using the given configuration channel. -func (provider *Eureka) Provide(configurationChan chan<- types.ConfigMessage, pool *safe.Pool, _ []types.Constraint) error { +func (provider *Eureka) Provide(configurationChan chan<- types.ConfigMessage, pool *safe.Pool, _ types.Constraints) error { operation := func() error { configuration, err := provider.buildConfiguration() diff --git a/provider/file.go b/provider/file.go index aab244a89..c2c513f70 100644 --- a/provider/file.go +++ b/provider/file.go @@ -21,7 +21,7 @@ type File struct { // Provide allows the provider to provide configurations to traefik // using the given configuration channel. -func (provider *File) Provide(configurationChan chan<- types.ConfigMessage, pool *safe.Pool, _ []types.Constraint) error { +func (provider *File) Provide(configurationChan chan<- types.ConfigMessage, pool *safe.Pool, constraints types.Constraints) error { watcher, err := fsnotify.NewWatcher() if err != nil { log.Error("Error creating file watcher", err) diff --git a/provider/kubernetes.go b/provider/kubernetes.go index 32c0a6ba2..af01db13c 100644 --- a/provider/kubernetes.go +++ b/provider/kubernetes.go @@ -42,7 +42,7 @@ func (provider *Kubernetes) newK8sClient() (k8s.Client, error) { // Provide allows the provider to provide configurations to traefik // using the given configuration channel. -func (provider *Kubernetes) Provide(configurationChan chan<- types.ConfigMessage, pool *safe.Pool, constraints []types.Constraint) error { +func (provider *Kubernetes) Provide(configurationChan chan<- types.ConfigMessage, pool *safe.Pool, constraints types.Constraints) error { k8sClient, err := provider.newK8sClient() if err != nil { return err diff --git a/provider/kv.go b/provider/kv.go index db8bac553..0e29ec2be 100644 --- a/provider/kv.go +++ b/provider/kv.go @@ -83,7 +83,7 @@ func (provider *Kv) watchKv(configurationChan chan<- types.ConfigMessage, prefix return nil } -func (provider *Kv) provide(configurationChan chan<- types.ConfigMessage, pool *safe.Pool, constraints []types.Constraint) error { +func (provider *Kv) provide(configurationChan chan<- types.ConfigMessage, pool *safe.Pool, constraints types.Constraints) error { provider.Constraints = append(provider.Constraints, constraints...) operation := func() error { if _, err := provider.kvclient.Exists("qmslkjdfmqlskdjfmqlksjazçueznbvbwzlkajzebvkwjdcqmlsfj"); err != nil { diff --git a/provider/kv_test.go b/provider/kv_test.go index 3e1502529..e0913b110 100644 --- a/provider/kv_test.go +++ b/provider/kv_test.go @@ -408,7 +408,7 @@ func TestKVLoadConfig(t *testing.T) { }, { Key: "traefik/backends/backend.with.dot.too/servers/server.with.dot/weight", - Value: []byte("1"), + Value: []byte("0"), }, }, }, @@ -420,7 +420,7 @@ func TestKVLoadConfig(t *testing.T) { Servers: map[string]types.Server{ "server.with.dot": { URL: "http://172.17.0.2:80", - Weight: 1, + Weight: 0, }, }, CircuitBreaker: nil, diff --git a/provider/marathon.go b/provider/marathon.go index f7960e4c4..3e3ef8d3a 100644 --- a/provider/marathon.go +++ b/provider/marathon.go @@ -4,7 +4,6 @@ import ( "errors" "net" "net/url" - "sort" "strconv" "strings" "text/template" @@ -53,7 +52,7 @@ type lightMarathonClient interface { // Provide allows the provider to provide configurations to traefik // using the given configuration channel. -func (provider *Marathon) Provide(configurationChan chan<- types.ConfigMessage, pool *safe.Pool, constraints []types.Constraint) error { +func (provider *Marathon) Provide(configurationChan chan<- types.ConfigMessage, pool *safe.Pool, constraints types.Constraints) error { provider.Constraints = append(provider.Constraints, constraints...) operation := func() error { config := marathon.NewDefaultConfig() @@ -226,10 +225,7 @@ func (provider *Marathon) taskFilter(task marathon.Task, applications *marathon. log.Debugf("Filtering marathon task %s specifying both traefik.portIndex and traefik.port labels", task.AppID) return false } - if portIndexLabel == "" && portValueLabel == "" && len(application.Ports) > 1 { - log.Debugf("Filtering marathon task %s with more than 1 port and no traefik.portIndex or traefik.port label", task.AppID) - return false - } + if portIndexLabel != "" { index, err := strconv.Atoi((*application.Labels)["traefik.portIndex"]) if err != nil || index < 0 || index > len(application.Ports)-1 { @@ -429,7 +425,7 @@ func (provider *Marathon) getFrontendBackend(application marathon.Application) s func (provider *Marathon) getSubDomain(name string) string { if provider.GroupsAsSubDomains { splitedName := strings.Split(strings.TrimPrefix(name, "/"), "/") - sort.Sort(sort.Reverse(sort.StringSlice(splitedName))) + reverseStringSlice(&splitedName) reverseName := strings.Join(splitedName, ".") return reverseName } diff --git a/provider/marathon_test.go b/provider/marathon_test.go index b9e712ea1..1fa1dbe9a 100644 --- a/provider/marathon_test.go +++ b/provider/marathon_test.go @@ -371,30 +371,30 @@ func TestMarathonTaskFilter(t *testing.T) { }, { task: marathon.Task{ - AppID: "foo", - Ports: []int{80}, + AppID: "multiple-ports", + Ports: []int{80, 443}, }, applications: &marathon.Applications{ Apps: []marathon.Application{ { - ID: "foo", + ID: "multiple-ports", Ports: []int{80, 443}, Labels: &map[string]string{}, }, }, }, - expected: false, + expected: true, exposedByDefault: true, }, { task: marathon.Task{ - AppID: "foo", + AppID: "disable", Ports: []int{80}, }, applications: &marathon.Applications{ Apps: []marathon.Application{ { - ID: "foo", + ID: "disable", Ports: []int{80}, Labels: &map[string]string{ "traefik.enable": "false", @@ -523,7 +523,7 @@ func TestMarathonTaskFilter(t *testing.T) { }, { task: marathon.Task{ - AppID: "foo", + AppID: "healthcheck-false", Ports: []int{80}, HealthCheckResults: []*marathon.HealthCheckResult{ { @@ -534,7 +534,7 @@ func TestMarathonTaskFilter(t *testing.T) { applications: &marathon.Applications{ Apps: []marathon.Application{ { - ID: "foo", + ID: "healthcheck-false", Ports: []int{80}, Labels: &map[string]string{}, HealthChecks: &[]marathon.HealthCheck{ @@ -576,13 +576,13 @@ func TestMarathonTaskFilter(t *testing.T) { }, { task: marathon.Task{ - AppID: "foo", + AppID: "single-port", Ports: []int{80}, }, applications: &marathon.Applications{ Apps: []marathon.Application{ { - ID: "foo", + ID: "single-port", Ports: []int{80}, Labels: &map[string]string{}, }, @@ -593,7 +593,7 @@ func TestMarathonTaskFilter(t *testing.T) { }, { task: marathon.Task{ - AppID: "foo", + AppID: "healthcheck-alive", Ports: []int{80}, HealthCheckResults: []*marathon.HealthCheckResult{ { @@ -604,7 +604,7 @@ func TestMarathonTaskFilter(t *testing.T) { applications: &marathon.Applications{ Apps: []marathon.Application{ { - ID: "foo", + ID: "healthcheck-alive", Ports: []int{80}, Labels: &map[string]string{}, HealthChecks: &[]marathon.HealthCheck{ @@ -677,7 +677,7 @@ func TestMarathonTaskFilter(t *testing.T) { for _, c := range cases { actual := provider.taskFilter(c.task, c.applications, c.exposedByDefault) if actual != c.expected { - t.Fatalf("expected %v, got %v", c.expected, actual) + t.Fatalf("App %s: expected %v, got %v", c.task.AppID, c.expected, actual) } } } @@ -740,7 +740,7 @@ func TestMarathonAppConstraints(t *testing.T) { MarathonLBCompatibility: c.marathonLBCompatibility, } constraint, _ := types.NewConstraint("tag==valid") - provider.Constraints = []types.Constraint{*constraint} + provider.Constraints = types.Constraints{constraint} actual := provider.applicationFilter(c.application, c.filteredTasks) if actual != c.expected { t.Fatalf("expected %v, got %v: %v", c.expected, actual, c.application) @@ -820,7 +820,7 @@ func TestMarathonTaskConstraints(t *testing.T) { MarathonLBCompatibility: c.marathonLBCompatibility, } constraint, _ := types.NewConstraint("tag==valid") - provider.Constraints = []types.Constraint{*constraint} + provider.Constraints = types.Constraints{constraint} apps := new(marathon.Applications) apps.Apps = c.applications actual := provider.taskFilter(c.filteredTask, apps, true) @@ -927,12 +927,12 @@ func TestMarathonGetPort(t *testing.T) { { applications: []marathon.Application{ { - ID: "test1", + ID: "multiple-ports-take-first", Labels: &map[string]string{}, }, }, task: marathon.Task{ - AppID: "test1", + AppID: "multiple-ports-take-first", Ports: []int{80, 443}, }, expected: "80", @@ -1280,3 +1280,33 @@ func TestMarathonGetBackend(t *testing.T) { } } } + +func TestMarathonGetSubDomain(t *testing.T) { + providerGroups := &Marathon{GroupsAsSubDomains: true} + providerNoGroups := &Marathon{GroupsAsSubDomains: false} + + apps := []struct { + path string + expected string + provider *Marathon + }{ + {"/test", "test", providerNoGroups}, + {"/test", "test", providerGroups}, + {"/a/b/c/d", "d.c.b.a", providerGroups}, + {"/b/a/d/c", "c.d.a.b", providerGroups}, + {"/d/c/b/a", "a.b.c.d", providerGroups}, + {"/c/d/a/b", "b.a.d.c", providerGroups}, + {"/a/b/c/d", "a-b-c-d", providerNoGroups}, + {"/b/a/d/c", "b-a-d-c", providerNoGroups}, + {"/d/c/b/a", "d-c-b-a", providerNoGroups}, + {"/c/d/a/b", "c-d-a-b", providerNoGroups}, + } + + for _, a := range apps { + actual := a.provider.getSubDomain(a.path) + + if actual != a.expected { + t.Errorf("expected %q, got %q", a.expected, actual) + } + } +} diff --git a/provider/mesos.go b/provider/mesos.go index 4064c1671..1822a3e60 100644 --- a/provider/mesos.go +++ b/provider/mesos.go @@ -7,6 +7,8 @@ import ( "text/template" "fmt" + "time" + "github.com/BurntSushi/ty/fun" "github.com/cenk/backoff" "github.com/containous/traefik/job" @@ -20,8 +22,6 @@ import ( "github.com/mesosphere/mesos-dns/records" "github.com/mesosphere/mesos-dns/records/state" "github.com/mesosphere/mesos-dns/util" - "sort" - "time" ) var _ Provider = (*Mesos)(nil) @@ -42,7 +42,7 @@ type Mesos struct { // Provide allows the provider to provide configurations to traefik // using the given configuration channel. -func (provider *Mesos) Provide(configurationChan chan<- types.ConfigMessage, pool *safe.Pool, constraints []types.Constraint) error { +func (provider *Mesos) Provide(configurationChan chan<- types.ConfigMessage, pool *safe.Pool, constraints types.Constraints) error { operation := func() error { // initialize logging @@ -212,10 +212,6 @@ func mesosTaskFilter(task state.Task, exposedByDefaultFlag bool) bool { log.Debugf("Filtering mesos task %s specifying both traefik.portIndex and traefik.port labels", task.Name) return false } - if portIndexLabel == "" && portValueLabel == "" && len(task.DiscoveryInfo.Ports.DiscoveryPorts) > 1 { - log.Debugf("Filtering mesos task %s with more than 1 port and no traefik.portIndex or traefik.port label", task.Name) - return false - } if portIndexLabel != "" { index, err := strconv.Atoi(labels(task, "traefik.portIndex")) if err != nil || index < 0 || index > len(task.DiscoveryInfo.Ports.DiscoveryPorts)-1 { @@ -439,7 +435,7 @@ func Ignore(f ErrorFunction) { func (provider *Mesos) getSubDomain(name string) string { if provider.GroupsAsSubDomains { splitedName := strings.Split(strings.TrimPrefix(name, "/"), "/") - sort.Sort(sort.Reverse(sort.StringSlice(splitedName))) + reverseStringSlice(&splitedName) reverseName := strings.Join(splitedName, ".") return reverseName } diff --git a/provider/mesos_test.go b/provider/mesos_test.go index 5968db8e9..ba8ccd4db 100644 --- a/provider/mesos_test.go +++ b/provider/mesos_test.go @@ -1,11 +1,12 @@ package provider import ( + "reflect" + "testing" + "github.com/containous/traefik/log" "github.com/containous/traefik/types" "github.com/mesosphere/mesos-dns/records/state" - "reflect" - "testing" ) func TestMesosTaskFilter(t *testing.T) { @@ -95,7 +96,7 @@ func TestMesosTaskFilter(t *testing.T) { setLabels("traefik.enable", "true"), discovery(setDiscoveryPorts("TCP", 80, "WEB HTTP", "TCP", 443, "WEB HTTPS")), ), - expected: false, // more than 1 discovery port but no traefik.port* label + expected: true, // Default to first index exposedByDefault: true, }, { @@ -244,6 +245,36 @@ func TestMesosLoadConfig(t *testing.T) { } } +func TestMesosGetSubDomain(t *testing.T) { + providerGroups := &Mesos{GroupsAsSubDomains: true} + providerNoGroups := &Mesos{GroupsAsSubDomains: false} + + apps := []struct { + path string + expected string + provider *Mesos + }{ + {"/test", "test", providerNoGroups}, + {"/test", "test", providerGroups}, + {"/a/b/c/d", "d.c.b.a", providerGroups}, + {"/b/a/d/c", "c.d.a.b", providerGroups}, + {"/d/c/b/a", "a.b.c.d", providerGroups}, + {"/c/d/a/b", "b.a.d.c", providerGroups}, + {"/a/b/c/d", "a-b-c-d", providerNoGroups}, + {"/b/a/d/c", "b-a-d-c", providerNoGroups}, + {"/d/c/b/a", "d-c-b-a", providerNoGroups}, + {"/c/d/a/b", "c-d-a-b", providerNoGroups}, + } + + for _, a := range apps { + actual := a.provider.getSubDomain(a.path) + + if actual != a.expected { + t.Errorf("expected %q, got %q", a.expected, actual) + } + } +} + // test helpers type ( diff --git a/provider/provider.go b/provider/provider.go index b2e7d5d09..09982bbec 100644 --- a/provider/provider.go +++ b/provider/provider.go @@ -23,7 +23,7 @@ import ( type Provider interface { // Provide allows the provider to provide configurations to traefik // using the given configuration channel. - Provide(configurationChan chan<- types.ConfigMessage, pool *safe.Pool, constraints []types.Constraint) error + Provide(configurationChan chan<- types.ConfigMessage, pool *safe.Pool, constraints types.Constraints) error } // BaseProvider should be inherited by providers @@ -44,7 +44,7 @@ func (p *BaseProvider) MatchConstraints(tags []string) (bool, *types.Constraint) for _, constraint := range p.Constraints { // xor: if ok and constraint.MustMatch are equal, then no tag is currently matching with the constraint if ok := constraint.MatchConstraintWithAtLeastOneTag(tags); ok != constraint.MustMatch { - return false, &constraint + return false, constraint } } @@ -101,6 +101,12 @@ func normalize(name string) string { return strings.Join(strings.FieldsFunc(name, fargs), "-") } +func reverseStringSlice(slice *[]string) { + for i, j := 0, len(*slice)-1; i < j; i, j = i+1, j-1 { + (*slice)[i], (*slice)[j] = (*slice)[j], (*slice)[i] + } +} + // ClientTLS holds TLS specific configurations as client // CA, Cert and Key can be either path or file contents type ClientTLS struct { diff --git a/provider/provider_test.go b/provider/provider_test.go index d14d971a1..f7f321989 100644 --- a/provider/provider_test.go +++ b/provider/provider_test.go @@ -230,13 +230,13 @@ func TestNilClientTLS(t *testing.T) { func TestMatchingConstraints(t *testing.T) { cases := []struct { - constraints []types.Constraint + constraints types.Constraints tags []string expected bool }{ // simple test: must match { - constraints: []types.Constraint{ + constraints: types.Constraints{ { Key: "tag", MustMatch: true, @@ -250,7 +250,7 @@ func TestMatchingConstraints(t *testing.T) { }, // simple test: must match but does not match { - constraints: []types.Constraint{ + constraints: types.Constraints{ { Key: "tag", MustMatch: true, @@ -264,7 +264,7 @@ func TestMatchingConstraints(t *testing.T) { }, // simple test: must not match { - constraints: []types.Constraint{ + constraints: types.Constraints{ { Key: "tag", MustMatch: false, @@ -278,7 +278,7 @@ func TestMatchingConstraints(t *testing.T) { }, // complex test: globbing { - constraints: []types.Constraint{ + constraints: types.Constraints{ { Key: "tag", MustMatch: true, @@ -292,7 +292,7 @@ func TestMatchingConstraints(t *testing.T) { }, // complex test: multiple constraints { - constraints: []types.Constraint{ + constraints: types.Constraints{ { Key: "tag", MustMatch: true, diff --git a/provider/zk.go b/provider/zk.go index 19f3654e5..57ce396b3 100644 --- a/provider/zk.go +++ b/provider/zk.go @@ -17,7 +17,7 @@ type Zookepper struct { // Provide allows the provider to provide configurations to traefik // using the given configuration channel. -func (provider *Zookepper) Provide(configurationChan chan<- types.ConfigMessage, pool *safe.Pool, constraints []types.Constraint) error { +func (provider *Zookepper) Provide(configurationChan chan<- types.ConfigMessage, pool *safe.Pool, constraints types.Constraints) error { store, err := provider.CreateStore() if err != nil { return fmt.Errorf("Failed to Connect to KV store: %v", err) diff --git a/script/crossbinary b/script/crossbinary index 11537f8e0..86bde97c7 100755 --- a/script/crossbinary +++ b/script/crossbinary @@ -27,7 +27,7 @@ OS_ARCH_ARG=(386 amd64) for OS in ${OS_PLATFORM_ARG[@]}; do for ARCH in ${OS_ARCH_ARG[@]}; do echo "Building binary for $OS/$ARCH..." - GOARCH=$ARCH GOOS=$OS CGO_ENABLED=0 go build -ldflags "-s -w -X main.Version=$VERSION -X main.Codename=$CODENAME -X main.BuildDate=$DATE" -o "dist/traefik_$OS-$ARCH" . + GOARCH=$ARCH GOOS=$OS CGO_ENABLED=0 go build -ldflags "-s -w -X github.com/containous/traefik/version.Version=$VERSION -X github.com/containous/traefik/version.Codename=$CODENAME -X github.com/containous/traefik/version.BuildDate=$DATE" -o "dist/traefik_$OS-$ARCH" . done done @@ -38,6 +38,6 @@ OS_ARCH_ARG=(arm arm64) for OS in ${OS_PLATFORM_ARG[@]}; do for ARCH in ${OS_ARCH_ARG[@]}; do echo "Building binary for $OS/$ARCH..." - GOARCH=$ARCH GOOS=$OS CGO_ENABLED=0 go build -ldflags "-s -w -X main.Version=$VERSION -X main.Codename=$CODENAME -X main.BuildDate=$DATE" -o "dist/traefik_$OS-$ARCH" . + GOARCH=$ARCH GOOS=$OS CGO_ENABLED=0 go build -ldflags "-s -w -X github.com/containous/traefik/version.Version=$VERSION -X github.com/containous/traefik/version.Codename=$CODENAME -X github.com/containous/traefik/version.BuildDate=$DATE" -o "dist/traefik_$OS-$ARCH" . done -done \ No newline at end of file +done diff --git a/server.go b/server.go index 01528f5dd..f771f786f 100644 --- a/server.go +++ b/server.go @@ -90,7 +90,7 @@ func NewServer(globalConfiguration GlobalConfiguration) *Server { return server } -// Start starts the server and blocks until server is shutted down. +// Start starts the server. func (server *Server) Start() { server.startHTTPServers() server.startLeadership() @@ -103,6 +103,10 @@ func (server *Server) Start() { server.configureProviders() server.startProviders() go server.listenSignals() +} + +// Wait blocks until server is shutted down. +func (server *Server) Wait() { <-server.stopChan } @@ -213,11 +217,11 @@ func (server *Server) listenProviders(stop chan bool) { lastConfigs.Set(configMsg.ProviderName, &configMsg) lastReceivedConfigurationValue := lastReceivedConfiguration.Get().(time.Time) if time.Now().After(lastReceivedConfigurationValue.Add(time.Duration(server.globalConfiguration.ProvidersThrottleDuration))) { - log.Debugf("Last %s config received more than %s, OK", configMsg.ProviderName, server.globalConfiguration.ProvidersThrottleDuration) + log.Debugf("Last %s config received more than %s, OK", configMsg.ProviderName, server.globalConfiguration.ProvidersThrottleDuration.String()) // last config received more than n s ago server.configurationValidatedChan <- configMsg } else { - log.Debugf("Last %s config received less than %s, waiting...", configMsg.ProviderName, server.globalConfiguration.ProvidersThrottleDuration) + log.Debugf("Last %s config received less than %s, waiting...", configMsg.ProviderName, server.globalConfiguration.ProvidersThrottleDuration.String()) safe.Go(func() { <-time.After(server.globalConfiguration.ProvidersThrottleDuration) lastReceivedConfigurationValue := lastReceivedConfiguration.Get().(time.Time) @@ -387,6 +391,9 @@ func (server *Server) createTLSConfig(entryPointName string, tlsOption *TLS, rou return nil, err } + // ensure http2 enabled + config.NextProtos = []string{"h2", "http/1.1"} + if len(tlsOption.ClientCAFiles) > 0 { pool := x509.NewCertPool() for _, caFile := range tlsOption.ClientCAFiles { diff --git a/templates/consul_catalog.tmpl b/templates/consul_catalog.tmpl index 546132c81..b47f82a17 100644 --- a/templates/consul_catalog.tmpl +++ b/templates/consul_catalog.tmpl @@ -3,7 +3,7 @@ {{if ne (getAttribute "enable" $node.Service.Tags "true") "false"}} [backends."backend-{{getBackend $node}}".servers."{{getBackendName $node $index}}"] url = "{{getAttribute "protocol" $node.Service.Tags "http"}}://{{getBackendAddress $node}}:{{$node.Service.Port}}" - {{$weight := getAttribute "backend.weight" $node.Service.Tags ""}} + {{$weight := getAttribute "backend.weight" $node.Service.Tags "0"}} {{with $weight}} weight = {{$weight}} {{end}} diff --git a/templates/kv.tmpl b/templates/kv.tmpl index c642ff7b1..333a63653 100644 --- a/templates/kv.tmpl +++ b/templates/kv.tmpl @@ -32,7 +32,7 @@ {{range $servers}} [backends."{{Last $backend}}".servers."{{Last .}}"] url = "{{Get "" . "/url"}}" - weight = {{Get "" . "/weight"}} + weight = {{Get "0" . "/weight"}} {{end}} {{end}} diff --git a/traefik.go b/traefik.go index 142a232c3..a5b20cc97 100644 --- a/traefik.go +++ b/traefik.go @@ -11,6 +11,7 @@ import ( "runtime" "strings" "text/template" + "time" "github.com/Sirupsen/logrus" "github.com/containous/flaeg" @@ -20,8 +21,10 @@ import ( "github.com/containous/traefik/log" "github.com/containous/traefik/middlewares" "github.com/containous/traefik/provider/k8s" + "github.com/containous/traefik/safe" "github.com/containous/traefik/types" "github.com/containous/traefik/version" + "github.com/coreos/go-systemd/daemon" "github.com/docker/libkv/store" "github.com/satori/go.uuid" ) @@ -261,6 +264,20 @@ func run(traefikConfiguration *TraefikConfiguration) { } jsonConf, _ := json.Marshal(globalConfiguration) log.Infof("Traefik version %s built on %s", version.Version, version.BuildDate) + + if globalConfiguration.CheckNewVersion { + ticker := time.NewTicker(24 * time.Hour) + safe.Go(func() { + version.CheckNewVersion() + for { + select { + case <-ticker.C: + version.CheckNewVersion() + } + } + }) + } + if len(traefikConfiguration.ConfigFile) != 0 { log.Infof("Using TOML configuration file %s", traefikConfiguration.ConfigFile) } @@ -268,6 +285,11 @@ func run(traefikConfiguration *TraefikConfiguration) { server := NewServer(globalConfiguration) server.Start() defer server.Close() + sent, err := daemon.SdNotify("READY=1") + if !sent && err != nil { + log.Error("Fail to notify", err) + } + server.Wait() log.Info("Shutting down") } diff --git a/traefik.sample.toml b/traefik.sample.toml index e1c45b82a..c58d52a01 100644 --- a/traefik.sample.toml +++ b/traefik.sample.toml @@ -10,6 +10,20 @@ # # graceTimeOut = 10 +# Enable debug mode +# +# Optional +# Default: false +# +# debug = true + +# Periodically check if a new version has been released +# +# Optional +# Default: true +# +# checkNewVersion = false + # Traefik logs file # If not defined, logs to stdout # @@ -30,14 +44,14 @@ # # logLevel = "ERROR" -# Backends throttle duration: minimum duration between 2 events from providers +# Backends throttle duration: minimum duration in seconds between 2 events from providers # before applying a new configuration. It avoids unnecessary reloads if multiples events # are sent in a short amount of time. # # Optional -# Default: "2s" +# Default: "2" # -# ProvidersThrottleDuration = "5s" +# ProvidersThrottleDuration = "5" # If non-zero, controls the maximum idle (keep-alive) to keep per-host. If zero, DefaultMaxIdleConnsPerHost is used. # If you encounter 'too many open files' errors, you can either change this value, or change `ulimit` value. @@ -284,7 +298,7 @@ # To enable digest auth on the webui # with 2 user/realm/pass: test:traefik:test and test2:traefik:test2 # You can use htdigest to generate those ones -# [web.auth.basic] +# [web.auth.digest] # users = ["test:traefik:a2688e031edb4be6a3797f3882655c05 ", "test2:traefik:518845800f9e2bfb1f1f740ec24f074e"] diff --git a/types/types.go b/types/types.go index ac29a2e6f..f889c5506 100644 --- a/types/types.go +++ b/types/types.go @@ -1,6 +1,7 @@ package types import ( + "encoding" "errors" "fmt" "github.com/docker/libkv/store" @@ -141,11 +142,25 @@ func (c *Constraint) String() string { return c.Key + "!=" + c.Regex } +var _ encoding.TextUnmarshaler = (*Constraint)(nil) + // UnmarshalText define how unmarshal in TOML parsing func (c *Constraint) UnmarshalText(text []byte) error { constraint, err := NewConstraint(string(text)) - *c = *constraint - return err + if err != nil { + return err + } + c.Key = constraint.Key + c.MustMatch = constraint.MustMatch + c.Regex = constraint.Regex + return nil +} + +var _ encoding.TextMarshaler = (*Constraint)(nil) + +// MarshalText encodes the receiver into UTF-8-encoded text and returns the result. +func (c *Constraint) MarshalText() (text []byte, err error) { + return []byte(c.String()), nil } // MatchConstraintWithAtLeastOneTag tests a constraint for one single service @@ -169,16 +184,16 @@ func (cs *Constraints) Set(str string) error { if err != nil { return err } - *cs = append(*cs, *constraint) + *cs = append(*cs, constraint) } return nil } // Constraints holds a Constraint parser -type Constraints []Constraint +type Constraints []*Constraint //Get []*Constraint -func (cs *Constraints) Get() interface{} { return []Constraint(*cs) } +func (cs *Constraints) Get() interface{} { return []*Constraint(*cs) } //String returns []*Constraint in string func (cs *Constraints) String() string { return fmt.Sprintf("%+v", *cs) } @@ -216,12 +231,12 @@ type Users []string // Basic HTTP basic authentication type Basic struct { - Users + Users `mapstructure:","` } // Digest HTTP authentication type Digest struct { - Users + Users `mapstructure:","` } // CanonicalDomain returns a lower case domain with trim space diff --git a/version/version.go b/version/version.go index ba72b1a42..7e4149caf 100644 --- a/version/version.go +++ b/version/version.go @@ -1,5 +1,12 @@ package version +import ( + "github.com/containous/traefik/log" + "github.com/google/go-github/github" + goversion "github.com/hashicorp/go-version" + "net/url" +) + var ( // Version holds the current version of traefik. Version = "dev" @@ -8,3 +15,50 @@ var ( // BuildDate holds the build date of traefik. BuildDate = "I don't remember exactly" ) + +// CheckNewVersion checks if a new version is available +func CheckNewVersion() { + if Version == "dev" { + return + } + client := github.NewClient(nil) + updateURL, err := url.Parse("https://update.traefik.io") + if err != nil { + log.Warnf("Error checking new version: %s", err) + return + } + client.BaseURL = updateURL + releases, resp, err := client.Repositories.ListReleases("containous", "traefik", nil) + if err != nil { + log.Warnf("Error checking new version: %s", err) + return + } + + if resp.StatusCode != 200 { + log.Warnf("Error checking new version: status=%s", resp.Status) + return + } + + currentVersion, err := goversion.NewVersion(Version) + if err != nil { + log.Warnf("Error checking new version: %s", err) + return + } + + for _, release := range releases { + releaseVersion, err := goversion.NewVersion(*release.TagName) + if err != nil { + log.Warnf("Error checking new version: %s", err) + return + } + + if len(currentVersion.Prerelease()) == 0 && len(releaseVersion.Prerelease()) > 0 { + continue + } + + if releaseVersion.GreaterThan(currentVersion) { + log.Warnf("A new release has been found: %s. Please consider updating.", releaseVersion.String()) + return + } + } +} diff --git a/web.go b/web.go index 037e06f27..432283fb0 100644 --- a/web.go +++ b/web.go @@ -54,7 +54,7 @@ func goroutines() interface{} { // Provide allows the provider to provide configurations to traefik // using the given configuration channel. -func (provider *WebProvider) Provide(configurationChan chan<- types.ConfigMessage, pool *safe.Pool, _ []types.Constraint) error { +func (provider *WebProvider) Provide(configurationChan chan<- types.ConfigMessage, pool *safe.Pool, _ types.Constraints) error { systemRouter := mux.NewRouter() diff --git a/webui/package.json b/webui/package.json index 92f25402e..762a57a5e 100644 --- a/webui/package.json +++ b/webui/package.json @@ -21,7 +21,8 @@ "angular-ui-bootstrap": "^2.0.0", "angular-ui-router": "^0.3.1", "bootstrap": "^3.3.6", - "moment": "^2.14.1" + "moment": "^2.14.1", + "nvd3": "^1.8.4" }, "devDependencies": { "angular-mocks": "^1.4.2", @@ -80,11 +81,6 @@ "test": "gulp test", "test:auto": "gulp test:auto" }, - "overrides": { - "angular-nvd3": { - "main": "dist/angular-nvd3.js" - } - }, "eslintConfig": { "globals": { "expect": true