forked from SW/traefik
Compare commits
26 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e3671cbb04 | ||
|
|
a525d02cc5 | ||
|
|
1cc1a4e6e2 | ||
|
|
3f0af3fe09 | ||
|
|
bc26d9f0de | ||
|
|
0ba28bbc8b | ||
|
|
550184275a | ||
|
|
c376083ecb | ||
|
|
1db5fcf200 | ||
|
|
16b2555ab3 | ||
|
|
d9a5258f40 | ||
|
|
190ebbed27 | ||
|
|
a0872c9e31 | ||
|
|
f5b306e7ff | ||
|
|
7a1feb3c51 | ||
|
|
1e8df9f245 | ||
|
|
b72937e8fb | ||
|
|
67847c3117 | ||
|
|
a2a0c80acb | ||
|
|
b3fd06fb45 | ||
|
|
c5db8d903c | ||
|
|
8fcd242494 | ||
|
|
ebd9af900e | ||
|
|
b02381c2d5 | ||
|
|
9b199ea756 | ||
|
|
ec3b913ee4 |
154
CHANGELOG.md
154
CHANGELOG.md
@@ -1,20 +1,55 @@
|
||||
# Change Log
|
||||
|
||||
## [v1.6.2](https://github.com/containous/traefik/tree/v1.6.2) (2018-05-22)
|
||||
[All Commits](https://github.com/containous/traefik/compare/v1.6.1...v1.6.2)
|
||||
|
||||
**Bug fixes:**
|
||||
- **[acme]** fix: acme errors management. ([#3329](https://github.com/containous/traefik/pull/3329) by [ldez](https://github.com/ldez))
|
||||
- **[acme]** Force to use ACME v02 endpoint. ([#3358](https://github.com/containous/traefik/pull/3358) by [ldez](https://github.com/ldez))
|
||||
- **[file]** No template parsing on traefik configuration file ([#3347](https://github.com/containous/traefik/pull/3347) by [Juliens](https://github.com/Juliens))
|
||||
- **[k8s]** Add redirect-permanent to kubernetes template ([#3332](https://github.com/containous/traefik/pull/3332) by [dtomcej](https://github.com/dtomcej))
|
||||
- **[logs]** Enhance Load-balancing method validation log. ([#3361](https://github.com/containous/traefik/pull/3361) by [ldez](https://github.com/ldez))
|
||||
- **[middleware]** Fix error pages content. ([#3337](https://github.com/containous/traefik/pull/3337) by [ldez](https://github.com/ldez))
|
||||
- **[webui]** Route rules overlaps in UI ([#3333](https://github.com/containous/traefik/pull/3333) by [ldez](https://github.com/ldez))
|
||||
- **[webui]** WebUI typo into the buffering section. ([#3363](https://github.com/containous/traefik/pull/3363) by [ldez](https://github.com/ldez))
|
||||
|
||||
**Documentation:**
|
||||
- **[acme]** Update caServer to letsencrypt one in examples ([#3339](https://github.com/containous/traefik/pull/3339) by [woernfl](https://github.com/woernfl))
|
||||
- **[docker]** Add command for basic auth with Docker Compose ([#3346](https://github.com/containous/traefik/pull/3346) by [DeamonMV](https://github.com/DeamonMV))
|
||||
- **[docker]** Removes ambiguity with the word 'default' ([#3344](https://github.com/containous/traefik/pull/3344) by [ldez](https://github.com/ldez))
|
||||
- **[kv]** Add basicAuth example for KV ([#3274](https://github.com/containous/traefik/pull/3274) by [MichaelErmer](https://github.com/MichaelErmer))
|
||||
- **[provider]** Update docs to reflect Provider wording ([#3331](https://github.com/containous/traefik/pull/3331) by [dtomcej](https://github.com/dtomcej))
|
||||
- **[servicefabric]** Update docs to match SF provider labels ([#3335](https://github.com/containous/traefik/pull/3335) by [jjcollinge](https://github.com/jjcollinge))
|
||||
|
||||
## [v1.6.1](https://github.com/containous/traefik/tree/v1.6.1) (2018-05-14)
|
||||
[All Commits](https://github.com/containous/traefik/compare/v1.6.0...v1.6.1)
|
||||
|
||||
**Bug fixes:**
|
||||
- **[acme]** Add missing deprecation info in CLI help. ([#3291](https://github.com/containous/traefik/pull/3291) by [ldez](https://github.com/ldez))
|
||||
- **[docker,marathon,rancher]** Fix segment backend name ([#3317](https://github.com/containous/traefik/pull/3317) by [ldez](https://github.com/ldez))
|
||||
- **[logs,middleware]** Error when accesslog and error pages ([#3314](https://github.com/containous/traefik/pull/3314) by [ldez](https://github.com/ldez))
|
||||
- **[middleware,tracing]** Fix wrong tag in forward span in tracing middleware ([#3279](https://github.com/containous/traefik/pull/3279) by [mmatur](https://github.com/mmatur))
|
||||
- **[webui]** Fix webui ([#3299](https://github.com/containous/traefik/pull/3299) by [ldez](https://github.com/ldez))
|
||||
|
||||
**Documentation:**
|
||||
- **[k8s]** Add Documentation update for Kubernetes Ingress ([#3294](https://github.com/containous/traefik/pull/3294) by [dtomcej](https://github.com/dtomcej))
|
||||
- **[tls]** Enhance entry point TLS CLI reference. ([#3290](https://github.com/containous/traefik/pull/3290) by [ldez](https://github.com/ldez))
|
||||
- Typo in documentation ([#3261](https://github.com/containous/traefik/pull/3261) by [blakethepatton](https://github.com/blakethepatton))
|
||||
|
||||
## [v1.6.0](https://github.com/containous/traefik/tree/v1.6.0) (2018-04-30)
|
||||
[All Commits](https://github.com/containous/traefik/compare/v1.5.0-rc1...v1.6.0)
|
||||
[Commits](https://github.com/containous/traefik/compare/v1.5.0-rc1...v1.6.0)
|
||||
[Commits pre RC](https://github.com/containous/traefik/compare/v1.5.0-rc1...v1.6.0-rc1)
|
||||
|
||||
**Enhancements:**
|
||||
- **[acme]** Create backup file during migration from ACME V1 to ACME V2 ([#3191](https://github.com/containous/traefik/pull/3191) by [nmengin](https://github.com/nmengin))
|
||||
- **[acme]** Generate wildcard certificate with SANs in ACME ([#3167](https://github.com/containous/traefik/pull/3167) by [nmengin](https://github.com/nmengin))
|
||||
- **[acme]** ACME V2 Integration ([#3063](https://github.com/containous/traefik/pull/3063) by [nmengin](https://github.com/nmengin))
|
||||
- **[acme]** Create ACME Provider ([#2889](https://github.com/containous/traefik/pull/2889) by [nmengin](https://github.com/nmengin))
|
||||
- **[acme]** Update Lego (Gandi API v5, cloudxns, ...) ([#2844](https://github.com/containous/traefik/pull/2844) by [ldez](https://github.com/ldez))
|
||||
- **[acme]** Simplify storing renewed acme certificate ([#2614](https://github.com/containous/traefik/pull/2614) by [ferhatelmas](https://github.com/ferhatelmas))
|
||||
- **[acme]** ACME V2 Integration ([#3063](https://github.com/containous/traefik/pull/3063) by [nmengin](https://github.com/nmengin))
|
||||
- **[acme]** Bump Lego Version for GoDaddy DNS Provider ([#2482](https://github.com/containous/traefik/pull/2482) by [sjawhar](https://github.com/sjawhar))
|
||||
- **[acme]** Delete TLS-SNI-01 challenge from ACME ([#2971](https://github.com/containous/traefik/pull/2971) by [nmengin](https://github.com/nmengin))
|
||||
- **[acme]** Simplify storing renewed acme certificate ([#2614](https://github.com/containous/traefik/pull/2614) by [ferhatelmas](https://github.com/ferhatelmas))
|
||||
- **[acme]** Update Lego (Gandi API v5, cloudxns, ...) ([#2844](https://github.com/containous/traefik/pull/2844) by [ldez](https://github.com/ldez))
|
||||
- **[acme]** Remove acme provider dependency in server ([#3225](https://github.com/containous/traefik/pull/3225) by [Juliens](https://github.com/Juliens))
|
||||
- **[acme]** Create backup file during migration from ACME V1 to ACME V2 ([#3191](https://github.com/containous/traefik/pull/3191) by [nmengin](https://github.com/nmengin))
|
||||
- **[acme]** Generate wildcard certificate with SANs in ACME ([#3167](https://github.com/containous/traefik/pull/3167) by [nmengin](https://github.com/nmengin))
|
||||
- **[api,cluster]** Added cluster/leader endpoint ([#3009](https://github.com/containous/traefik/pull/3009) by [aantono](https://github.com/aantono))
|
||||
- **[api,cluster]** Improved cluster api to include the current leader node ([#3100](https://github.com/containous/traefik/pull/3100) by [aantono](https://github.com/aantono))
|
||||
- **[authentication]** Forward Authentication: add X-Forwarded-Uri ([#2398](https://github.com/containous/traefik/pull/2398) by [sebastianbauer](https://github.com/sebastianbauer))
|
||||
- **[boltdb,consul,etcd,kv,zk]** Add all available configuration to KV Backend ([#2652](https://github.com/containous/traefik/pull/2652) by [ldez](https://github.com/ldez))
|
||||
- **[boltdb,consul,etcd,kv,zk]** homogenization of templates: KV ([#2661](https://github.com/containous/traefik/pull/2661) by [ldez](https://github.com/ldez))
|
||||
@@ -22,76 +57,72 @@
|
||||
- **[consul,consulcatalog]** Homogenization of templates: Consul Catalog ([#2668](https://github.com/containous/traefik/pull/2668) by [ldez](https://github.com/ldez))
|
||||
- **[consul,consulcatalog]** Split consul and consul catalog. ([#2655](https://github.com/containous/traefik/pull/2655) by [ldez](https://github.com/ldez))
|
||||
- **[consulcatalog,ecs,mesos]** Factorize labels managements. ([#3099](https://github.com/containous/traefik/pull/3099) by [ldez](https://github.com/ldez))
|
||||
- **[consulcatalog]** TLS Support for ConsulCatalog ([#2900](https://github.com/containous/traefik/pull/2900) by [mmatur](https://github.com/mmatur))
|
||||
- **[consulcatalog]** Check for endpoints while detecting Consul service changes ([#2882](https://github.com/containous/traefik/pull/2882) by [caseycs](https://github.com/caseycs))
|
||||
- **[consulcatalog]** TLS Support for ConsulCatalog ([#2900](https://github.com/containous/traefik/pull/2900) by [mmatur](https://github.com/mmatur))
|
||||
- **[consulcatalog]** Add all available tags to Consul Catalog Backend ([#2646](https://github.com/containous/traefik/pull/2646) by [ldez](https://github.com/ldez))
|
||||
- **[docker,docker/swarm]** Fix support for macvlan driver in docker provider ([#2827](https://github.com/containous/traefik/pull/2827) by [mmatur](https://github.com/mmatur))
|
||||
- **[docker,marathon,rancher]** Segments Labels: Rancher & Marathon ([#3073](https://github.com/containous/traefik/pull/3073) by [ldez](https://github.com/ldez))
|
||||
- **[docker]** Custom headers by service labels for docker backends ([#2514](https://github.com/containous/traefik/pull/2514) by [Tiscs](https://github.com/Tiscs))
|
||||
- **[docker]** Homogenization of templates: Docker ([#2659](https://github.com/containous/traefik/pull/2659) by [ldez](https://github.com/ldez))
|
||||
- **[docker]** Add all available labels to Docker Backend ([#2584](https://github.com/containous/traefik/pull/2584) by [ldez](https://github.com/ldez))
|
||||
- **[docker]** Homogenization of templates: Docker ([#2659](https://github.com/containous/traefik/pull/2659) by [ldez](https://github.com/ldez))
|
||||
- **[docker]** Custom headers by service labels for docker backends ([#2514](https://github.com/containous/traefik/pull/2514) by [Tiscs](https://github.com/Tiscs))
|
||||
- **[docker]** Segment labels: Docker ([#3055](https://github.com/containous/traefik/pull/3055) by [ldez](https://github.com/ldez))
|
||||
- **[dynamodb,ecs]** Upgrade AWS SKD to version v1.13.1 ([#2908](https://github.com/containous/traefik/pull/2908) by [mmatur](https://github.com/mmatur))
|
||||
- **[ecs]** Factorize labels managements. ([#3159](https://github.com/containous/traefik/pull/3159) by [ldez](https://github.com/ldez))
|
||||
- **[ecs]** Homogenization of templates: ECS ([#2663](https://github.com/containous/traefik/pull/2663) by [ldez](https://github.com/ldez))
|
||||
- **[ecs]** Add all available labels to ECS Backend ([#2605](https://github.com/containous/traefik/pull/2605) by [ldez](https://github.com/ldez))
|
||||
- **[ecs]** Homogenization of templates: ECS ([#2663](https://github.com/containous/traefik/pull/2663) by [ldez](https://github.com/ldez))
|
||||
- **[ecs]** Factorize labels managements. ([#3159](https://github.com/containous/traefik/pull/3159) by [ldez](https://github.com/ldez))
|
||||
- **[eureka]** Homogenization of templates: Eureka ([#2846](https://github.com/containous/traefik/pull/2846) by [ldez](https://github.com/ldez))
|
||||
- **[eureka]** Replace Delay by RefreshSecond in Eureka ([#2972](https://github.com/containous/traefik/pull/2972) by [ldez](https://github.com/ldez))
|
||||
- **[file]** Added support for templates to file provider ([#2991](https://github.com/containous/traefik/pull/2991) by [aantono](https://github.com/aantono))
|
||||
- **[healthcheck]** Toggle /ping to artificially return unhealthy response on SIGTERM during requestAcceptGraceTimeout interval ([#3062](https://github.com/containous/traefik/pull/3062) by [ravilr](https://github.com/ravilr))
|
||||
- **[healthcheck]** Add HTTP headers to healthcheck. ([#3047](https://github.com/containous/traefik/pull/3047) by [zetaab](https://github.com/zetaab))
|
||||
- **[healthcheck]** Improve logging output for failing healthchecks ([#2443](https://github.com/containous/traefik/pull/2443) by [marco-jantke](https://github.com/marco-jantke))
|
||||
- **[k8s,tls]** Add support for fetching k8s Ingress TLS data from secrets ([#2439](https://github.com/containous/traefik/pull/2439) by [gopenguin](https://github.com/gopenguin))
|
||||
- **[k8s]** Builders in k8s tests ([#2513](https://github.com/containous/traefik/pull/2513) by [ldez](https://github.com/ldez))
|
||||
- **[k8s]** Support multi-port services. ([#3121](https://github.com/containous/traefik/pull/3121) by [timoreimann](https://github.com/timoreimann))
|
||||
- **[k8s]** Bump kubernetes/client-go ([#2848](https://github.com/containous/traefik/pull/2848) by [yue9944882](https://github.com/yue9944882))
|
||||
- **[k8s]** Add all available annotations to k8s Backend ([#2612](https://github.com/containous/traefik/pull/2612) by [ldez](https://github.com/ldez))
|
||||
- **[k8s]** Allow custom value for kubernetes.io/ingress.class annotation ([#2222](https://github.com/containous/traefik/pull/2222) by [yuvipanda](https://github.com/yuvipanda))
|
||||
- **[k8s]** Introduce k8s informer factory ([#2867](https://github.com/containous/traefik/pull/2867) by [yue9944882](https://github.com/yue9944882))
|
||||
- **[k8s]** Add all available annotations to k8s Backend ([#2612](https://github.com/containous/traefik/pull/2612) by [ldez](https://github.com/ldez))
|
||||
- **[k8s]** Bump kubernetes/client-go ([#2848](https://github.com/containous/traefik/pull/2848) by [yue9944882](https://github.com/yue9944882))
|
||||
- **[k8s]** Add app-root annotation support for kubernetes ingress ([#2522](https://github.com/containous/traefik/pull/2522) by [yue9944882](https://github.com/yue9944882))
|
||||
- **[k8s]** Builders in k8s tests ([#2513](https://github.com/containous/traefik/pull/2513) by [ldez](https://github.com/ldez))
|
||||
- **[k8s]** Allow custom value for kubernetes.io/ingress.class annotation ([#2222](https://github.com/containous/traefik/pull/2222) by [yuvipanda](https://github.com/yuvipanda))
|
||||
- **[logs,middleware]** Add access log filter for retry attempts ([#3042](https://github.com/containous/traefik/pull/3042) by [marco-jantke](https://github.com/marco-jantke))
|
||||
- **[logs,middleware]** Ultimate Access log filter ([#2988](https://github.com/containous/traefik/pull/2988) by [mmatur](https://github.com/mmatur))
|
||||
- **[logs,middleware]** Add username in accesslog ([#2111](https://github.com/containous/traefik/pull/2111) by [bastiaanb](https://github.com/bastiaanb))
|
||||
- **[logs]** Display file log when test fails. ([#2801](https://github.com/containous/traefik/pull/2801) by [ldez](https://github.com/ldez))
|
||||
- **[logs,middleware]** Ultimate Access log filter ([#2988](https://github.com/containous/traefik/pull/2988) by [mmatur](https://github.com/mmatur))
|
||||
- **[logs]** Allow overriding the log level in debug mode. ([#3050](https://github.com/containous/traefik/pull/3050) by [timoreimann](https://github.com/timoreimann))
|
||||
- **[logs]** Display file log when test fails. ([#2801](https://github.com/containous/traefik/pull/2801) by [ldez](https://github.com/ldez))
|
||||
- **[marathon]** Remove health check filter from Marathon tasks. ([#2817](https://github.com/containous/traefik/pull/2817) by [timoreimann](https://github.com/timoreimann))
|
||||
- **[marathon]** homogenization of templates: Marathon ([#2665](https://github.com/containous/traefik/pull/2665) by [ldez](https://github.com/ldez))
|
||||
- **[marathon]** Add all available labels to Marathon Backend ([#2602](https://github.com/containous/traefik/pull/2602) by [ldez](https://github.com/ldez))
|
||||
- **[marathon]** homogenization of templates: Marathon ([#2665](https://github.com/containous/traefik/pull/2665) by [ldez](https://github.com/ldez))
|
||||
- **[mesos]** Add all available labels to Mesos Backend ([#2687](https://github.com/containous/traefik/pull/2687) by [ldez](https://github.com/ldez))
|
||||
- **[metrics]** Added missing metrics to registry for DataDog and StatsD ([#2890](https://github.com/containous/traefik/pull/2890) by [aantono](https://github.com/aantono))
|
||||
- **[metrics]** Extend metrics and rebuild prometheus exporting logic ([#2567](https://github.com/containous/traefik/pull/2567) by [marco-jantke](https://github.com/marco-jantke))
|
||||
- **[metrics]** Remove unnecessary conversion ([#2850](https://github.com/containous/traefik/pull/2850) by [ferhatelmas](https://github.com/ferhatelmas))
|
||||
- **[metrics]** Added entrypoint metrics to influxdb ([#2992](https://github.com/containous/traefik/pull/2992) by [adityacs](https://github.com/adityacs))
|
||||
- **[metrics]** Remove unnecessary conversion ([#2850](https://github.com/containous/traefik/pull/2850) by [ferhatelmas](https://github.com/ferhatelmas))
|
||||
- **[metrics]** Extend metrics and rebuild prometheus exporting logic ([#2567](https://github.com/containous/traefik/pull/2567) by [marco-jantke](https://github.com/marco-jantke))
|
||||
- **[metrics]** Added missing metrics to registry for DataDog and StatsD ([#2890](https://github.com/containous/traefik/pull/2890) by [aantono](https://github.com/aantono))
|
||||
- **[middleware,consul,consulcatalog,docker,ecs,k8s,marathon,mesos,rancher]** New option in secure middleware ([#2958](https://github.com/containous/traefik/pull/2958) by [mmatur](https://github.com/mmatur))
|
||||
- **[middleware,consulcatalog,docker,ecs,k8s,kv,marathon,mesos,rancher]** Ability to use "X-Forwarded-For" as a source of IP for white list. ([#3070](https://github.com/containous/traefik/pull/3070) by [ldez](https://github.com/ldez))
|
||||
- **[middleware,docker]** Use pointer of error pages ([#2607](https://github.com/containous/traefik/pull/2607) by [ldez](https://github.com/ldez))
|
||||
- **[middleware,provider]** Redirection: permanent move option. ([#2774](https://github.com/containous/traefik/pull/2774) by [ldez](https://github.com/ldez))
|
||||
- **[middleware]** Add tests on IPWhiteLister. ([#3106](https://github.com/containous/traefik/pull/3106) by [ldez](https://github.com/ldez))
|
||||
- **[middleware]** Add new options to the CLI entrypoint definition. ([#2799](https://github.com/containous/traefik/pull/2799) by [ldez](https://github.com/ldez))
|
||||
- **[middleware]** Change port of traefik for error pages integration test ([#2907](https://github.com/containous/traefik/pull/2907) by [mmatur](https://github.com/mmatur))
|
||||
- **[middleware]** Request buffering middleware ([#2217](https://github.com/containous/traefik/pull/2217) by [harnash](https://github.com/harnash))
|
||||
- **[middleware]** Remove unnecessary returns in tracing setup ([#2880](https://github.com/containous/traefik/pull/2880) by [ferhatelmas](https://github.com/ferhatelmas))
|
||||
- **[middleware]** Extract internal router creation from server ([#3204](https://github.com/containous/traefik/pull/3204) by [Juliens](https://github.com/Juliens))
|
||||
- **[provider]** Homogenization of the providers (part 1) ([#2518](https://github.com/containous/traefik/pull/2518) by [ldez](https://github.com/ldez))
|
||||
- **[middleware]** Request buffering middleware ([#2217](https://github.com/containous/traefik/pull/2217) by [harnash](https://github.com/harnash))
|
||||
- **[middleware]** Add new options to the CLI entrypoint definition. ([#2799](https://github.com/containous/traefik/pull/2799) by [ldez](https://github.com/ldez))
|
||||
- **[provider]** No error pages must return nil. ([#2610](https://github.com/containous/traefik/pull/2610) by [ldez](https://github.com/ldez))
|
||||
- **[provider]** Homogenization of the providers (part 1) ([#2518](https://github.com/containous/traefik/pull/2518) by [ldez](https://github.com/ldez))
|
||||
- **[rancher]** Add all available labels to Rancher Backend ([#2601](https://github.com/containous/traefik/pull/2601) by [ldez](https://github.com/ldez))
|
||||
- **[rancher]** Homogenization of templates: Rancher ([#2662](https://github.com/containous/traefik/pull/2662) by [ldez](https://github.com/ldez))
|
||||
- **[rules]** Externalize Træfik rules in a dedicated package ([#2933](https://github.com/containous/traefik/pull/2933) by [nmengin](https://github.com/nmengin))
|
||||
- **[servicefabric]** Use shared label system ([#3197](https://github.com/containous/traefik/pull/3197) by [ldez](https://github.com/ldez))
|
||||
- **[servicefabric]** Add white list for Service Fabric ([#3079](https://github.com/containous/traefik/pull/3079) by [ldez](https://github.com/ldez))
|
||||
- **[servicefabric]** Update Service Fabric backend. ([#3064](https://github.com/containous/traefik/pull/3064) by [ldez](https://github.com/ldez))
|
||||
- **[servicefabric]** Add HTTP headers to healthcheck. ([#3205](https://github.com/containous/traefik/pull/3205) by [ldez](https://github.com/ldez))
|
||||
- **[tls]** Support TLS MinVersion and CipherSuite as CLI option. ([#3107](https://github.com/containous/traefik/pull/3107) by [ldez](https://github.com/ldez))
|
||||
- **[servicefabric]** Add white list for Service Fabric ([#3079](https://github.com/containous/traefik/pull/3079) by [ldez](https://github.com/ldez))
|
||||
- **[tls]** Use default entryPoints when certificates are added with no entryPoints. ([#2534](https://github.com/containous/traefik/pull/2534) by [nmengin](https://github.com/nmengin))
|
||||
- **[tracing]** Opentracing support ([#2587](https://github.com/containous/traefik/pull/2587) by [mmatur](https://github.com/mmatur))
|
||||
- **[tracing]** Handle zipkin collector creation ([#2860](https://github.com/containous/traefik/pull/2860) by [ferhatelmas](https://github.com/ferhatelmas))
|
||||
- **[tracing]** Opentracing support ([#2587](https://github.com/containous/traefik/pull/2587) by [tcolgate](https://github.com/tcolgate) and [mmatur](https://github.com/mmatur))
|
||||
- **[webui]** New web ui ([#2226](https://github.com/containous/traefik/pull/2226) by [jkuri](https://github.com/jkuri))
|
||||
- **[webui]** Add status code text to webui bar chart tooltip ([#2639](https://github.com/containous/traefik/pull/2639) by [wader](https://github.com/wader))
|
||||
- Separate command from the main package ([#2951](https://github.com/containous/traefik/pull/2951) by [Juliens](https://github.com/Juliens))
|
||||
- Logger and Leaks ([#2847](https://github.com/containous/traefik/pull/2847) by [ldez](https://github.com/ldez))
|
||||
- Separate command from the main package ([#2951](https://github.com/containous/traefik/pull/2951) by [Juliens](https://github.com/Juliens))
|
||||
- Use context in Server ([#3007](https://github.com/containous/traefik/pull/3007) by [Juliens](https://github.com/Juliens))
|
||||
|
||||
**Bug fixes:**
|
||||
- **[acme]** Check all the C/N and SANs of provided certificates before generating ACME certificates in ACME provider ([#2970](https://github.com/containous/traefik/pull/2970) by [nmengin](https://github.com/nmengin))
|
||||
- **[acme]** Update lego. ([#3158](https://github.com/containous/traefik/pull/3158) by [ldez](https://github.com/ldez))
|
||||
- **[acme]** Fix panic with wrong ACME configuration ([#3084](https://github.com/containous/traefik/pull/3084) by [nmengin](https://github.com/nmengin))
|
||||
- **[acme]** Minor updates to dumpcerts.sh ([#3116](https://github.com/containous/traefik/pull/3116) by [mathuin](https://github.com/mathuin))
|
||||
@@ -99,87 +130,84 @@
|
||||
- **[acme]** Add TTL and custom Timeout in DigitalOcean DNS provider ([#3143](https://github.com/containous/traefik/pull/3143) by [ldez](https://github.com/ldez))
|
||||
- **[acme]** Fix acme.json file automatic creation ([#3156](https://github.com/containous/traefik/pull/3156) by [nmengin](https://github.com/nmengin))
|
||||
- **[acme]** Fix wildcard match to ACME domains in cluster mode ([#3080](https://github.com/containous/traefik/pull/3080) by [oldmantaiter](https://github.com/oldmantaiter))
|
||||
- **[acme]** Check all the C/N and SANs of provided certificates before generating ACME certificates in ACME provider ([#2970](https://github.com/containous/traefik/pull/2970) by [nmengin](https://github.com/nmengin))
|
||||
- **[acme]** Does not generate ACME certificate if domain is checked by dynamic certificate ([#3238](https://github.com/containous/traefik/pull/3238) by [Juliens](https://github.com/Juliens))
|
||||
- **[api,cluster]** Moved /api/cluster/leadership handler under public routes (requires no authentication) ([#3101](https://github.com/containous/traefik/pull/3101) by [aantono](https://github.com/aantono))
|
||||
- **[authentication,middleware]** Forward auth: copy response headers when auth failed. ([#3207](https://github.com/containous/traefik/pull/3207) by [ldez](https://github.com/ldez))
|
||||
- **[consul,docker,ecs,eureka,k8s,kv,marathon,mesos,rancher]** Server weight zero ([#3130](https://github.com/containous/traefik/pull/3130) by [ldez](https://github.com/ldez))
|
||||
- **[docker,marathon,mesos,rancher]** Fix: label 'traefik.domain' ([#3201](https://github.com/containous/traefik/pull/3201) by [ldez](https://github.com/ldez))
|
||||
- **[docker,k8s,marathon]** Fix custom headers template ([#2622](https://github.com/containous/traefik/pull/2622) by [ldez](https://github.com/ldez))
|
||||
- **[docker,rancher]** Ignore server for container with empty IP address. ([#3213](https://github.com/containous/traefik/pull/3213) by [ldez](https://github.com/ldez))
|
||||
- **[docker,marathon,mesos,rancher]** Fix: label 'traefik.domain' ([#3201](https://github.com/containous/traefik/pull/3201) by [ldez](https://github.com/ldez))
|
||||
- **[docker,rancher]** Frontend rule and segment labels. ([#3091](https://github.com/containous/traefik/pull/3091) by [ldez](https://github.com/ldez))
|
||||
- **[docker,rancher]** Ignore server for container with empty IP address. ([#3213](https://github.com/containous/traefik/pull/3213) by [ldez](https://github.com/ldez))
|
||||
- **[docker]** Fix multiple frontends with docker-compose --scale ([#3190](https://github.com/containous/traefik/pull/3190) by [jbdoumenjou](https://github.com/jbdoumenjou))
|
||||
- **[k8s]** Fixes prefixed annotations support. ([#3110](https://github.com/containous/traefik/pull/3110) by [ldez](https://github.com/ldez))
|
||||
- **[k8s]** Limit label selector to Ingress factory. ([#3137](https://github.com/containous/traefik/pull/3137) by [timoreimann](https://github.com/timoreimann))
|
||||
- **[healthcheck]** Remove unnecessary mutex usage in health checks ([#2726](https://github.com/containous/traefik/pull/2726) by [marco-jantke](https://github.com/marco-jantke))
|
||||
- **[k8s]** Missing annotation prefix support. ([#2915](https://github.com/containous/traefik/pull/2915) by [ldez](https://github.com/ldez))
|
||||
- **[k8s]** Remove hardcoded frontend prefix in Kubernetes template ([#2914](https://github.com/containous/traefik/pull/2914) by [psalaberria002](https://github.com/psalaberria002))
|
||||
- **[k8s]** Limit label selector to Ingress factory. ([#3137](https://github.com/containous/traefik/pull/3137) by [timoreimann](https://github.com/timoreimann))
|
||||
- **[k8s]** Fixes prefixed annotations support. ([#3110](https://github.com/containous/traefik/pull/3110) by [ldez](https://github.com/ldez))
|
||||
- **[logs,middleware]** Fix bad access log ([#2682](https://github.com/containous/traefik/pull/2682) by [mmatur](https://github.com/mmatur))
|
||||
- **[logs]** Add missing argument in log. ([#3188](https://github.com/containous/traefik/pull/3188) by [chemidy](https://github.com/chemidy))
|
||||
- **[marathon]** Several apps with same backend name in Marathon. ([#3109](https://github.com/containous/traefik/pull/3109) by [ldez](https://github.com/ldez))
|
||||
- **[mesos]** fix: overflow on 32 bits arch. ([#3127](https://github.com/containous/traefik/pull/3127) by [ldez](https://github.com/ldez))
|
||||
- **[metrics]** Fix duplicated tags in InfluxDB ([#3189](https://github.com/containous/traefik/pull/3189) by [mmatur](https://github.com/mmatur))
|
||||
- **[middleware,consul,consulcatalog,docker,ecs,kv,marathon,mesos,rancher]** Fix: error pages ([#3138](https://github.com/containous/traefik/pull/3138) by [ldez](https://github.com/ldez))
|
||||
- **[middleware,tracing]** Fix nil value when tracing is enabled ([#3192](https://github.com/containous/traefik/pull/3192) by [mmatur](https://github.com/mmatur))
|
||||
- **[middleware,tracing]** Fix <nil> tracer value in KV ([#2911](https://github.com/containous/traefik/pull/2911) by [mmatur](https://github.com/mmatur))
|
||||
- **[middleware]** Fix panic in atomic on ARM and x86-32 platforms ([#3195](https://github.com/containous/traefik/pull/3195) by [mmatur](https://github.com/mmatur))
|
||||
- **[middleware]** Redirect to HTTPS first before basic auth if header redirect (secure) is set ([#3187](https://github.com/containous/traefik/pull/3187) by [SantoDE](https://github.com/SantoDE))
|
||||
- **[middleware]** Fix error pages redirect and headers. ([#3217](https://github.com/containous/traefik/pull/3217) by [ldez](https://github.com/ldez))
|
||||
- **[middleware]** Fix whitelist and XFF. ([#3211](https://github.com/containous/traefik/pull/3211) by [ldez](https://github.com/ldez))
|
||||
- **[middleware,tracing]** Fix nil value when tracing is enabled ([#3192](https://github.com/containous/traefik/pull/3192) by [mmatur](https://github.com/mmatur))
|
||||
- **[middleware]** Use responseModifier to override secure headers ([#2946](https://github.com/containous/traefik/pull/2946) by [mmatur](https://github.com/mmatur))
|
||||
- **[middleware]** Correct conditional setting of buffering retry expression. ([#2865](https://github.com/containous/traefik/pull/2865) by [ldez](https://github.com/ldez))
|
||||
- **[middleware]** Fix high memory usage in retry middleware ([#2740](https://github.com/containous/traefik/pull/2740) by [marco-jantke](https://github.com/marco-jantke))
|
||||
- **[middleware]** Fix whitelist and XFF. ([#3211](https://github.com/containous/traefik/pull/3211) by [ldez](https://github.com/ldez))
|
||||
- **[middleware]** Fix panic in atomic on ARM and x86-32 platforms ([#3195](https://github.com/containous/traefik/pull/3195) by [mmatur](https://github.com/mmatur))
|
||||
- **[middleware]** Redirect to HTTPS first before basic auth if header redirect (secure) is set ([#3187](https://github.com/containous/traefik/pull/3187) by [SantoDE](https://github.com/SantoDE))
|
||||
- **[middleware]** Fix error pages redirect and headers. ([#3217](https://github.com/containous/traefik/pull/3217) by [ldez](https://github.com/ldez))
|
||||
- **[provider]** Add some missing quotes in templates ([#2973](https://github.com/containous/traefik/pull/2973) by [ldez](https://github.com/ldez))
|
||||
- **[servicefabric]** Fix backend name for stateful service and more. ([#3183](https://github.com/containous/traefik/pull/3183) by [ldez](https://github.com/ldez))
|
||||
- **[tracing]** Tracing statusCodeTracker need to implement CloseNotify ([#2733](https://github.com/containous/traefik/pull/2733) by [mmatur](https://github.com/mmatur))
|
||||
- **[tracing]** Fix missing configuration for jaeger reporter ([#2720](https://github.com/containous/traefik/pull/2720) by [mmatur](https://github.com/mmatur))
|
||||
- **[tracing]** Tracing statusCodeTracker need to implement CloseNotify ([#2733](https://github.com/containous/traefik/pull/2733) by [mmatur](https://github.com/mmatur))
|
||||
- **[tracing]** Fix integration tests in tracing ([#2759](https://github.com/containous/traefik/pull/2759) by [mmatur](https://github.com/mmatur))
|
||||
- **[webui]** Remove useless ACME tab from UI. ([#3154](https://github.com/containous/traefik/pull/3154) by [ldez](https://github.com/ldez))
|
||||
- **[webui]** Add redirect section. ([#3243](https://github.com/containous/traefik/pull/3243) by [ldez](https://github.com/ldez))
|
||||
- Remove unnecessary mutex usage in health checks ([#2726](https://github.com/containous/traefik/pull/2726) by [marco-jantke](https://github.com/marco-jantke))
|
||||
- Add missing argument in log. ([#3188](https://github.com/containous/traefik/pull/3188) by [chemidy](https://github.com/chemidy))
|
||||
|
||||
**Documentation:**
|
||||
- **[docker]** Add default values for some Docker labels ([#2604](https://github.com/containous/traefik/pull/2604) by [ldez](https://github.com/ldez))
|
||||
- **[file]** Add documentation about Templating in backend file ([#3223](https://github.com/containous/traefik/pull/3223) by [nmengin](https://github.com/nmengin))
|
||||
- **[k8s]** Update kubernetes.md ([#3093](https://github.com/containous/traefik/pull/3093) by [rdrgporto](https://github.com/rdrgporto))
|
||||
- **[k8s]** Update kubernetes.md ([#3171](https://github.com/containous/traefik/pull/3171) by [andreyfedoseev](https://github.com/andreyfedoseev))
|
||||
- **[k8s]** Document custom k8s ingress class usage in guide. ([#3242](https://github.com/containous/traefik/pull/3242) by [timoreimann](https://github.com/timoreimann))
|
||||
- **[k8s]** Update traefik-ds.yaml with --api command line parameter ([#2803](https://github.com/containous/traefik/pull/2803) by [maniankara](https://github.com/maniankara))
|
||||
- **[k8s]** Remove web provider in example ([#2807](https://github.com/containous/traefik/pull/2807) by [pigletfly](https://github.com/pigletfly))
|
||||
- **[k8s]** Drop capabilities in Kubernetes DaemonSet example ([#3028](https://github.com/containous/traefik/pull/3028) by [nogoegst](https://github.com/nogoegst))
|
||||
- **[k8s]** Docs: Fix typos in k8s user-guide ([#2898](https://github.com/containous/traefik/pull/2898) by [cez81](https://github.com/cez81))
|
||||
- **[k8s]** Change boolean annotation values to string ([#2839](https://github.com/containous/traefik/pull/2839) by [hobti01](https://github.com/hobti01))
|
||||
- **[provider]** Fix template version documentation. ([#3184](https://github.com/containous/traefik/pull/3184) by [ldez](https://github.com/ldez))
|
||||
- **[provider]** Cleaning labels/annotations documentation. ([#3245](https://github.com/containous/traefik/pull/3245) by [ldez](https://github.com/ldez))
|
||||
- **[k8s]** Update kubernetes.md ([#3093](https://github.com/containous/traefik/pull/3093) by [rdrgporto](https://github.com/rdrgporto))
|
||||
- **[k8s]** Document custom k8s ingress class usage in guide. ([#3242](https://github.com/containous/traefik/pull/3242) by [timoreimann](https://github.com/timoreimann))
|
||||
- **[k8s]** Update kubernetes.md ([#3171](https://github.com/containous/traefik/pull/3171) by [andreyfedoseev](https://github.com/andreyfedoseev))
|
||||
- **[provider]** Split security labels and custom labels documentation. ([#2872](https://github.com/containous/traefik/pull/2872) by [ldez](https://github.com/ldez))
|
||||
- **[provider]** Remove non-supported label. ([#3065](https://github.com/containous/traefik/pull/3065) by [ldez](https://github.com/ldez))
|
||||
- **[provider]** Remove obsolete paragraph about error pages. ([#2608](https://github.com/containous/traefik/pull/2608) by [ldez](https://github.com/ldez))
|
||||
- **[servicefabric]** Update SF white list documentation section. ([#3082](https://github.com/containous/traefik/pull/3082) by [ldez](https://github.com/ldez))
|
||||
- **[provider]** Cleaning labels/annotations documentation. ([#3245](https://github.com/containous/traefik/pull/3245) by [ldez](https://github.com/ldez))
|
||||
- **[provider]** Fix template version documentation. ([#3184](https://github.com/containous/traefik/pull/3184) by [ldez](https://github.com/ldez))
|
||||
- **[servicefabric]** Add SF to supported backends in docs ([#3033](https://github.com/containous/traefik/pull/3033) by [lawrencegripper](https://github.com/lawrencegripper))
|
||||
- **[servicefabric]** Update SF white list documentation section. ([#3082](https://github.com/containous/traefik/pull/3082) by [ldez](https://github.com/ldez))
|
||||
- **[tracing]** Fix typo in doc for rate limit label ([#2790](https://github.com/containous/traefik/pull/2790) by [mmatur](https://github.com/mmatur))
|
||||
- **[tracing]** Add Tracing entry in the documentation. ([#2713](https://github.com/containous/traefik/pull/2713) by [ldez](https://github.com/ldez))
|
||||
- **[tracing]** Fix documentation for tracing with Jaeger ([#3227](https://github.com/containous/traefik/pull/3227) by [mmatur](https://github.com/mmatur))
|
||||
- **[webui]** doc: update Traefik images. ([#3241](https://github.com/containous/traefik/pull/3241) by [ldez](https://github.com/ldez))
|
||||
- Fix typo in doc for rate limit label ([#2790](https://github.com/containous/traefik/pull/2790) by [mmatur](https://github.com/mmatur))
|
||||
- Fix typo in documentation ([#3215](https://github.com/containous/traefik/pull/3215) by [arnaslu](https://github.com/arnaslu))
|
||||
- Minor improvements to documentation ([#3221](https://github.com/containous/traefik/pull/3221) by [colincoller](https://github.com/colincoller))
|
||||
- Update some examples ([#3150](https://github.com/containous/traefik/pull/3150) by [zaporylie](https://github.com/zaporylie))
|
||||
- Normalize parameter names in configs ([#3132](https://github.com/containous/traefik/pull/3132) by [kachkaev](https://github.com/kachkaev))
|
||||
- Fixed documentation urls on README.md ([#3102](https://github.com/containous/traefik/pull/3102) by [emir](https://github.com/emir))
|
||||
- Minor improvements to documentation ([#3221](https://github.com/containous/traefik/pull/3221) by [colincoller](https://github.com/colincoller))
|
||||
- Fix basic documentation ([#3086](https://github.com/containous/traefik/pull/3086) by [mmatur](https://github.com/mmatur))
|
||||
- Fix typo and tweak formatting in quickstart ([#3250](https://github.com/containous/traefik/pull/3250) by [alexymik](https://github.com/alexymik))
|
||||
- Prepare release v1.6.0-rc5 ([#3179](https://github.com/containous/traefik/pull/3179) by [Juliens](https://github.com/Juliens))
|
||||
- Fix basic documentation ([#3086](https://github.com/containous/traefik/pull/3086) by [mmatur](https://github.com/mmatur))
|
||||
- Prepare release v1.6.0-rc6 ([#3199](https://github.com/containous/traefik/pull/3199) by [mmatur](https://github.com/mmatur))
|
||||
- Prepare release v1.6.0-rc5 ([#3179](https://github.com/containous/traefik/pull/3179) by [Juliens](https://github.com/Juliens))
|
||||
- Prepare release v1.6.0-rc4 ([#3126](https://github.com/containous/traefik/pull/3126) by [ldez](https://github.com/ldez))
|
||||
- Prepare release v1.6.0-rc3 ([#3096](https://github.com/containous/traefik/pull/3096) by [ldez](https://github.com/ldez))
|
||||
- Prepare release v1.6.0-rc2 ([#3087](https://github.com/containous/traefik/pull/3087) by [nmengin](https://github.com/nmengin))
|
||||
- Prepare release v1.6.0-rc1 ([#3078](https://github.com/containous/traefik/pull/3078) by [Juliens](https://github.com/Juliens))
|
||||
- Prepare release v1.6.0 ([#3251](https://github.com/containous/traefik/pull/3251) by [Juliens](https://github.com/Juliens))
|
||||
|
||||
**Misc:**
|
||||
- **[oxy]** Disable closeNotify when method GET for http pipelining ([#3108](https://github.com/containous/traefik/pull/3108) by [Juliens](https://github.com/Juliens))
|
||||
- **[boltdb,consul,etcd,kv,zk]** Migrate from libkv to valkeyrie library ([#2743](https://github.com/containous/traefik/pull/2743) by [nmengin](https://github.com/nmengin))
|
||||
- Fix Service Fabric docs to use v1.6 labels ([#3209](https://github.com/containous/traefik/pull/3209) by [jjcollinge](https://github.com/jjcollinge))
|
||||
- Merge v1.6.0-rc6 into master ([#3203](https://github.com/containous/traefik/pull/3203) by [ldez](https://github.com/ldez))
|
||||
- Merge v1.6.0-rc5 into master ([#3180](https://github.com/containous/traefik/pull/3180) by [ldez](https://github.com/ldez))
|
||||
- Merge v1.6.0-rc4 into master ([#3129](https://github.com/containous/traefik/pull/3129) by [ldez](https://github.com/ldez))
|
||||
- Drop unnecessary type conversions ([#2583](https://github.com/containous/traefik/pull/2583) by [ferhatelmas](https://github.com/ferhatelmas))
|
||||
- Code simplification ([#2516](https://github.com/containous/traefik/pull/2516) by [ferhatelmas](https://github.com/ferhatelmas))
|
||||
- Merge v1.5.4 into master ([#3024](https://github.com/containous/traefik/pull/3024) by [ldez](https://github.com/ldez))
|
||||
- Merge v1.5.3 into master ([#2943](https://github.com/containous/traefik/pull/2943) by [ldez](https://github.com/ldez))
|
||||
- Merge v1.5.2 into master ([#2843](https://github.com/containous/traefik/pull/2843) by [ldez](https://github.com/ldez))
|
||||
@@ -187,8 +215,6 @@
|
||||
- Merge v1.5.0-rc5 into master ([#2708](https://github.com/containous/traefik/pull/2708) by [ldez](https://github.com/ldez))
|
||||
- Merge v1.5.0-rc3 into master ([#2600](https://github.com/containous/traefik/pull/2600) by [ldez](https://github.com/ldez))
|
||||
- Merge v1.5.0-rc2 into master ([#2536](https://github.com/containous/traefik/pull/2536) by [ldez](https://github.com/ldez))
|
||||
- Drop unnecessary type conversions ([#2583](https://github.com/containous/traefik/pull/2583) by [ferhatelmas](https://github.com/ferhatelmas))
|
||||
- Code simplification ([#2516](https://github.com/containous/traefik/pull/2516) by [ferhatelmas](https://github.com/ferhatelmas))
|
||||
|
||||
## [v1.6.0-rc6](https://github.com/containous/traefik/tree/v1.6.0-rc6) (2018-04-17)
|
||||
[All Commits](https://github.com/containous/traefik/compare/v1.6.0-rc5...v1.6.0-rc6)
|
||||
|
||||
2
Gopkg.lock
generated
2
Gopkg.lock
generated
@@ -1278,7 +1278,7 @@
|
||||
"providers/dns/route53",
|
||||
"providers/dns/vultr"
|
||||
]
|
||||
revision = "2817d2131186742bc98830c73a5d9c255b3f4537"
|
||||
revision = "3d653ee2ee38f1d71beb5f09b37b23344eff0ab3"
|
||||
source = "github.com/containous/lego"
|
||||
|
||||
[[projects]]
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
Træfik is a modern HTTP reverse proxy and load balancer that makes deploying microservices easy.
|
||||
Træfik integrates with your existing infrastructure components ([Docker](https://www.docker.com/), [Swarm mode](https://docs.docker.com/engine/swarm/), [Kubernetes](https://kubernetes.io), [Marathon](https://mesosphere.github.io/marathon/), [Consul](https://www.consul.io/), [Etcd](https://coreos.com/etcd/), [Rancher](https://rancher.com), [Amazon ECS](https://aws.amazon.com/ecs), ...) and configures itself automatically and dynamically.
|
||||
Telling Træfik where your orchestrator is could be the _only_ configuration step you need to do.
|
||||
Pointing Træfik at your orchestrator should be the _only_ configuration step you need.
|
||||
|
||||
---
|
||||
|
||||
|
||||
18
acme/acme.go
18
acme/acme.go
@@ -41,15 +41,15 @@ type ACME struct {
|
||||
Email string `description:"Email address used for registration"`
|
||||
Domains []types.Domain `description:"SANs (alternative domains) to each main domain using format: --acme.domains='main.com,san1.com,san2.com' --acme.domains='main.net,san1.net,san2.net'"`
|
||||
Storage string `description:"File or key used for certificates storage."`
|
||||
StorageFile string // deprecated
|
||||
OnDemand bool `description:"Enable on demand certificate generation. This will request a certificate from Let's Encrypt during the first TLS handshake for a hostname that does not yet have a certificate."` //deprecated
|
||||
StorageFile string // Deprecated
|
||||
OnDemand bool `description:"(Deprecated) Enable on demand certificate generation. This will request a certificate from Let's Encrypt during the first TLS handshake for a hostname that does not yet have a certificate."` //deprecated
|
||||
OnHostRule bool `description:"Enable certificate generation on frontends Host rules."`
|
||||
CAServer string `description:"CA server to use."`
|
||||
EntryPoint string `description:"Entrypoint to proxy acme challenge to."`
|
||||
DNSChallenge *acmeprovider.DNSChallenge `description:"Activate DNS-01 Challenge"`
|
||||
HTTPChallenge *acmeprovider.HTTPChallenge `description:"Activate HTTP-01 Challenge"`
|
||||
DNSProvider string `description:"Activate DNS-01 Challenge (Deprecated)"` // deprecated
|
||||
DelayDontCheckDNS flaeg.Duration `description:"Assume DNS propagates after a delay in seconds rather than finding and querying nameservers."` // deprecated
|
||||
DNSProvider string `description:"(Deprecated) Activate DNS-01 Challenge"` // Deprecated
|
||||
DelayDontCheckDNS flaeg.Duration `description:"(Deprecated) Assume DNS propagates after a delay in seconds rather than finding and querying nameservers."` // Deprecated
|
||||
ACMELogging bool `description:"Enable debug logging of ACME actions."`
|
||||
client *acme.Client
|
||||
defaultCertificate *tls.Certificate
|
||||
@@ -611,11 +611,13 @@ func (a *ACME) getDomainsCertificates(domains []string) (*Certificate, error) {
|
||||
domains = fun.Map(types.CanonicalDomain, domains).([]string)
|
||||
log.Debugf("Loading ACME certificates %s...", domains)
|
||||
bundle := true
|
||||
certificate, failures := a.client.ObtainCertificate(domains, bundle, nil, OSCPMustStaple)
|
||||
if len(failures) > 0 {
|
||||
log.Error(failures)
|
||||
return nil, fmt.Errorf("cannot obtain certificates %+v", failures)
|
||||
|
||||
certificate, err := a.client.ObtainCertificate(domains, bundle, nil, OSCPMustStaple)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return nil, fmt.Errorf("cannot obtain certificates: %+v", err)
|
||||
}
|
||||
|
||||
log.Debugf("Loaded ACME certificates %s", domains)
|
||||
return &Certificate{
|
||||
Domain: certificate.Domain,
|
||||
|
||||
@@ -1089,6 +1089,7 @@ var _templatesKubernetesTmpl = []byte(`[backends]
|
||||
entryPoint = "{{ $frontend.Redirect.EntryPoint }}"
|
||||
regex = "{{ $frontend.Redirect.Regex }}"
|
||||
replacement = "{{ $frontend.Redirect.Replacement }}"
|
||||
permanent = {{ $frontend.Redirect.Permanent }}
|
||||
{{end}}
|
||||
|
||||
{{if $frontend.Errors }}
|
||||
|
||||
@@ -50,6 +50,9 @@ const (
|
||||
// DefaultGraceTimeout controls how long Traefik serves pending requests
|
||||
// prior to shutting down.
|
||||
DefaultGraceTimeout = 10 * time.Second
|
||||
|
||||
// DefaultAcmeCAServer is the default ACME API endpoint
|
||||
DefaultAcmeCAServer = "https://acme-v02.api.letsencrypt.org/directory"
|
||||
)
|
||||
|
||||
// GlobalConfiguration holds global configuration (with providers, etc.).
|
||||
@@ -105,13 +108,13 @@ type GlobalConfiguration struct {
|
||||
|
||||
// WebCompatibility is a configuration to handle compatibility with deprecated web provider options
|
||||
type WebCompatibility struct {
|
||||
Address string `description:"Web administration port" export:"true"`
|
||||
CertFile string `description:"SSL certificate" export:"true"`
|
||||
KeyFile string `description:"SSL certificate" export:"true"`
|
||||
ReadOnly bool `description:"Enable read only API" export:"true"`
|
||||
Statistics *types.Statistics `description:"Enable more detailed statistics" export:"true"`
|
||||
Metrics *types.Metrics `description:"Enable a metrics exporter" export:"true"`
|
||||
Path string `description:"Root path for dashboard and API" export:"true"`
|
||||
Address string `description:"(Deprecated) Web administration port" export:"true"`
|
||||
CertFile string `description:"(Deprecated) SSL certificate" export:"true"`
|
||||
KeyFile string `description:"(Deprecated) SSL certificate" export:"true"`
|
||||
ReadOnly bool `description:"(Deprecated) Enable read only API" export:"true"`
|
||||
Statistics *types.Statistics `description:"(Deprecated) Enable more detailed statistics" export:"true"`
|
||||
Metrics *types.Metrics `description:"(Deprecated) Enable a metrics exporter" export:"true"`
|
||||
Path string `description:"(Deprecated) Root path for dashboard and API" export:"true"`
|
||||
Auth *types.Auth `export:"true"`
|
||||
Debug bool `export:"true"`
|
||||
}
|
||||
@@ -304,14 +307,8 @@ func (gc *GlobalConfiguration) SetEffectiveConfiguration(configFile string) {
|
||||
gc.Web.Path += "/"
|
||||
}
|
||||
|
||||
// Try to fallback to traefik config file in case the file provider is enabled
|
||||
// but has no file name configured and is not in a directory mode.
|
||||
if gc.File != nil && len(gc.File.Filename) == 0 && len(gc.File.Directory) == 0 {
|
||||
if len(configFile) > 0 {
|
||||
gc.File.Filename = configFile
|
||||
} else {
|
||||
log.Errorln("Error using file configuration backend, no filename defined")
|
||||
}
|
||||
if gc.File != nil {
|
||||
gc.File.TraefikFile = configFile
|
||||
}
|
||||
|
||||
gc.initACMEProvider()
|
||||
@@ -356,7 +353,14 @@ func (gc *GlobalConfiguration) initTracing() {
|
||||
|
||||
func (gc *GlobalConfiguration) initACMEProvider() {
|
||||
if gc.ACME != nil {
|
||||
// TODO: to remove in the futurs
|
||||
gc.ACME.CAServer = getSafeACMECAServer(gc.ACME.CAServer)
|
||||
|
||||
if gc.ACME.DNSChallenge != nil && gc.ACME.HTTPChallenge != nil {
|
||||
log.Warn("Unable to use DNS challenge and HTTP challenge at the same time. Fallback to DNS challenge.")
|
||||
gc.ACME.HTTPChallenge = nil
|
||||
}
|
||||
|
||||
// TODO: to remove in the future
|
||||
if len(gc.ACME.StorageFile) > 0 && len(gc.ACME.Storage) == 0 {
|
||||
log.Warn("ACME.StorageFile is deprecated, use ACME.Storage instead")
|
||||
gc.ACME.Storage = gc.ACME.StorageFile
|
||||
@@ -391,6 +395,26 @@ func (gc *GlobalConfiguration) initACMEProvider() {
|
||||
}
|
||||
}
|
||||
|
||||
func getSafeACMECAServer(caServerSrc string) string {
|
||||
if len(caServerSrc) == 0 {
|
||||
return DefaultAcmeCAServer
|
||||
}
|
||||
|
||||
if strings.HasPrefix(caServerSrc, "https://acme-v01.api.letsencrypt.org") {
|
||||
caServer := strings.Replace(caServerSrc, "v01", "v02", 1)
|
||||
log.Warnf("The CA server %[1]q refers to a v01 endpoint of the ACME API, please change to %[2]q. Fallback to %[2]q.", caServerSrc, caServer)
|
||||
return caServer
|
||||
}
|
||||
|
||||
if strings.HasPrefix(caServerSrc, "https://acme-staging.api.letsencrypt.org") {
|
||||
caServer := strings.Replace(caServerSrc, "https://acme-staging.api.letsencrypt.org", "https://acme-staging-v02.api.letsencrypt.org", 1)
|
||||
log.Warnf("The CA server %[1]q refers to a v01 endpoint of the ACME API, please change to %[2]q. Fallback to %[2]q.", caServerSrc, caServer)
|
||||
return caServer
|
||||
}
|
||||
|
||||
return caServerSrc
|
||||
}
|
||||
|
||||
// ValidateConfiguration validate that configuration is coherent
|
||||
func (gc *GlobalConfiguration) ValidateConfiguration() {
|
||||
if gc.ACME != nil {
|
||||
|
||||
@@ -65,24 +65,28 @@ func TestSetEffectiveConfigurationGraceTimeout(t *testing.T) {
|
||||
|
||||
func TestSetEffectiveConfigurationFileProviderFilename(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
fileProvider *file.Provider
|
||||
wantFileProviderFilename string
|
||||
desc string
|
||||
fileProvider *file.Provider
|
||||
wantFileProviderFilename string
|
||||
wantFileProviderTraefikFile string
|
||||
}{
|
||||
{
|
||||
desc: "no filename for file provider given",
|
||||
fileProvider: &file.Provider{},
|
||||
wantFileProviderFilename: defaultConfigFile,
|
||||
desc: "no filename for file provider given",
|
||||
fileProvider: &file.Provider{},
|
||||
wantFileProviderFilename: "",
|
||||
wantFileProviderTraefikFile: defaultConfigFile,
|
||||
},
|
||||
{
|
||||
desc: "filename for file provider given",
|
||||
fileProvider: &file.Provider{BaseProvider: provider.BaseProvider{Filename: "other.toml"}},
|
||||
wantFileProviderFilename: "other.toml",
|
||||
desc: "filename for file provider given",
|
||||
fileProvider: &file.Provider{BaseProvider: provider.BaseProvider{Filename: "other.toml"}},
|
||||
wantFileProviderFilename: "other.toml",
|
||||
wantFileProviderTraefikFile: defaultConfigFile,
|
||||
},
|
||||
{
|
||||
desc: "directory for file provider given",
|
||||
fileProvider: &file.Provider{Directory: "/"},
|
||||
wantFileProviderFilename: "",
|
||||
desc: "directory for file provider given",
|
||||
fileProvider: &file.Provider{Directory: "/"},
|
||||
wantFileProviderFilename: "",
|
||||
wantFileProviderTraefikFile: defaultConfigFile,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -98,6 +102,7 @@ func TestSetEffectiveConfigurationFileProviderFilename(t *testing.T) {
|
||||
gc.SetEffectiveConfiguration(defaultConfigFile)
|
||||
|
||||
assert.Equal(t, test.wantFileProviderFilename, gc.File.Filename)
|
||||
assert.Equal(t, test.wantFileProviderTraefikFile, gc.File.TraefikFile)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -170,7 +170,7 @@ func TestEntryPoints_Set(t *testing.T) {
|
||||
name: "all parameters camelcase",
|
||||
expression: "Name:foo " +
|
||||
"Address::8000 " +
|
||||
"TLS:goo,gii " +
|
||||
"TLS:goo,gii;foo,fii " +
|
||||
"TLS " +
|
||||
"CA:car " +
|
||||
"CA.Optional:true " +
|
||||
@@ -203,6 +203,10 @@ func TestEntryPoints_Set(t *testing.T) {
|
||||
CertFile: tls.FileOrContent("goo"),
|
||||
KeyFile: tls.FileOrContent("gii"),
|
||||
},
|
||||
{
|
||||
CertFile: tls.FileOrContent("foo"),
|
||||
KeyFile: tls.FileOrContent("fii"),
|
||||
},
|
||||
},
|
||||
ClientCA: tls.ClientCA{
|
||||
Files: []string{"car"},
|
||||
@@ -272,7 +276,7 @@ func TestEntryPoints_Set(t *testing.T) {
|
||||
name: "all parameters lowercase",
|
||||
expression: "Name:foo " +
|
||||
"address::8000 " +
|
||||
"tls:goo,gii " +
|
||||
"tls:goo,gii;foo,fii " +
|
||||
"tls " +
|
||||
"ca:car " +
|
||||
"ca.Optional:true " +
|
||||
@@ -303,6 +307,10 @@ func TestEntryPoints_Set(t *testing.T) {
|
||||
CertFile: tls.FileOrContent("goo"),
|
||||
KeyFile: tls.FileOrContent("gii"),
|
||||
},
|
||||
{
|
||||
CertFile: tls.FileOrContent("foo"),
|
||||
KeyFile: tls.FileOrContent("fii"),
|
||||
},
|
||||
},
|
||||
ClientCA: tls.ClientCA{
|
||||
Files: []string{"car"},
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
# BoltDB Backend
|
||||
# BoltDB Provider
|
||||
|
||||
Træfik can be configured to use BoltDB as a backend configuration.
|
||||
Træfik can be configured to use BoltDB as a provider.
|
||||
|
||||
```toml
|
||||
################################################################
|
||||
# BoltDB configuration backend
|
||||
# BoltDB Provider
|
||||
################################################################
|
||||
|
||||
# Enable BoltDB configuration backend.
|
||||
# Enable BoltDB Provider.
|
||||
[boltdb]
|
||||
|
||||
# BoltDB file.
|
||||
@@ -56,4 +56,4 @@ filename = "boltdb.tmpl"
|
||||
# insecureSkipVerify = true
|
||||
```
|
||||
|
||||
To enable constraints see [backend-specific constraints section](/configuration/commons/#backend-specific).
|
||||
To enable constraints see [provider-specific constraints section](/configuration/commons/#provider-specific).
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
# Consul Key-Value Backend
|
||||
# Consul Key-Value Provider
|
||||
|
||||
Træfik can be configured to use Consul as a backend configuration.
|
||||
Træfik can be configured to use Consul as a provider.
|
||||
|
||||
```toml
|
||||
################################################################
|
||||
# Consul KV configuration backend
|
||||
# Consul KV Provider
|
||||
################################################################
|
||||
|
||||
# Enable Consul KV configuration backend.
|
||||
# Enable Consul KV Provider.
|
||||
[consul]
|
||||
|
||||
# Consul server endpoint.
|
||||
@@ -56,6 +56,6 @@ prefix = "traefik"
|
||||
# insecureSkipVerify = true
|
||||
```
|
||||
|
||||
To enable constraints see [backend-specific constraints section](/configuration/commons/#backend-specific).
|
||||
To enable constraints see [provider-specific constraints section](/configuration/commons/#provider-specific).
|
||||
|
||||
Please refer to the [Key Value storage structure](/user-guide/kv-config/#key-value-storage-structure) section to get documentation on Traefik KV structure.
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
# Consul Catalog backend
|
||||
# Consul Catalog Provider
|
||||
|
||||
Træfik can be configured to use service discovery catalog of Consul as a backend configuration.
|
||||
Træfik can be configured to use service discovery catalog of Consul as a provider.
|
||||
|
||||
```toml
|
||||
################################################################
|
||||
# Consul Catalog configuration backend
|
||||
# Consul Catalog Provider
|
||||
################################################################
|
||||
|
||||
# Enable Consul Catalog configuration backend.
|
||||
# Enable Consul Catalog Provider.
|
||||
[consulCatalog]
|
||||
|
||||
# Consul server endpoint.
|
||||
@@ -76,9 +76,9 @@ prefix = "traefik"
|
||||
# templateVersion = 2
|
||||
```
|
||||
|
||||
This backend will create routes matching on hostname based on the service name used in Consul.
|
||||
This provider will create routes matching on hostname based on the service name used in Consul.
|
||||
|
||||
To enable constraints see [backend-specific constraints section](/configuration/commons/#backend-specific).
|
||||
To enable constraints see [provider-specific constraints section](/configuration/commons/#provider-specific).
|
||||
|
||||
## Tags
|
||||
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
|
||||
# Docker Backend
|
||||
# Docker Provider
|
||||
|
||||
Træfik can be configured to use Docker as a backend configuration.
|
||||
Træfik can be configured to use Docker as a provider.
|
||||
|
||||
## Docker
|
||||
|
||||
```toml
|
||||
################################################################
|
||||
# Docker configuration backend
|
||||
# Docker Provider
|
||||
################################################################
|
||||
|
||||
# Enable Docker configuration backend.
|
||||
# Enable Docker Provider.
|
||||
[docker]
|
||||
|
||||
# Docker server endpoint. Can be a tcp or a unix socket endpoint.
|
||||
@@ -82,17 +82,17 @@ swarmMode = false
|
||||
# insecureSkipVerify = true
|
||||
```
|
||||
|
||||
To enable constraints see [backend-specific constraints section](/configuration/commons/#backend-specific).
|
||||
To enable constraints see [provider-specific constraints section](/configuration/commons/#provider-specific).
|
||||
|
||||
|
||||
## Docker Swarm Mode
|
||||
|
||||
```toml
|
||||
################################################################
|
||||
# Docker Swarm Mode configuration backend
|
||||
# Docker Swarm Mode Provider
|
||||
################################################################
|
||||
|
||||
# Enable Docker configuration backend.
|
||||
# Enable Docker Provider.
|
||||
[docker]
|
||||
|
||||
# Docker server endpoint.
|
||||
@@ -159,7 +159,7 @@ exposedByDefault = false
|
||||
# insecureSkipVerify = true
|
||||
```
|
||||
|
||||
To enable constraints see [backend-specific constraints section](/configuration/commons/#backend-specific).
|
||||
To enable constraints see [provider-specific constraints section](/configuration/commons/#provider-specific).
|
||||
|
||||
## Labels: overriding default behavior
|
||||
|
||||
@@ -218,7 +218,7 @@ Labels can be used on containers to override default behavior.
|
||||
| `traefik.backend.loadbalancer.swarm=true` | Use Swarm's inbuilt load balancer (only relevant under Swarm Mode). |
|
||||
| `traefik.backend.maxconn.amount=10` | Set a maximum number of connections to the backend.<br>Must be used in conjunction with the below label to take effect. |
|
||||
| `traefik.backend.maxconn.extractorfunc=client.ip` | Set the function to be used against the request to determine what to limit maximum connections to the backend by.<br>Must be used in conjunction with the above label to take effect. |
|
||||
| `traefik.frontend.auth.basic=EXPR` | Sets basic authentication for that frontend in CSV format: `User:Hash,User:Hash` |
|
||||
| `traefik.frontend.auth.basic=EXPR` | Sets basic authentication for that frontend in CSV format: `User:Hash,User:Hash` [2] |
|
||||
| `traefik.frontend.entryPoints=http,https` | Assign this frontend to entry points `http` and `https`.<br>Overrides `defaultEntryPoints` |
|
||||
| `traefik.frontend.errors.<name>.backend=NAME` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
||||
| `traefik.frontend.errors.<name>.query=PATH` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
||||
@@ -243,6 +243,10 @@ If a container is linked to several networks, be sure to set the proper network
|
||||
For instance when deploying docker `stack` from compose files, the compose defined networks will be prefixed with the `stack` name.
|
||||
Or if your service references external network use it's name instead.
|
||||
|
||||
[2] `traefik.frontend.auth.basic=EXPR`:
|
||||
To create `user:password` pair, it's possible to use this command `echo $(htpasswd -nb user password) | sed -e s/\\$/\\$\\$/g`.
|
||||
The result will be `user:$$apr1$$9Cv/OMGj$$ZomWQzuQbL.3TRCS81A1g/`, note additional symbol `$` makes escaping.
|
||||
|
||||
#### Custom Headers
|
||||
|
||||
| Label | Description |
|
||||
@@ -284,12 +288,12 @@ Segment labels override the default behavior.
|
||||
|
||||
| Label | Description |
|
||||
|---------------------------------------------------------------------------|-------------------------------------------------------------|
|
||||
| `traefik.<segment_name>.backend=BACKEND` | Same as `traefik.backend` |
|
||||
| `traefik.<segment_name>.domain=DOMAIN` | Same as `traefik.domain` |
|
||||
| `traefik.<segment_name>.port=PORT` | Same as `traefik.port` |
|
||||
| `traefik.<segment_name>.protocol=http` | Same as `traefik.protocol` |
|
||||
| `traefik.<segment_name>.weight=10` | Same as `traefik.weight` |
|
||||
| `traefik.<segment_name>.frontend.auth.basic=EXPR` | Same as `traefik.frontend.auth.basic` |
|
||||
| `traefik.<segment_name>.frontend.backend=BACKEND` | Same as `traefik.frontend.backend` |
|
||||
| `traefik.<segment_name>.frontend.entryPoints=https` | Same as `traefik.frontend.entryPoints` |
|
||||
| `traefik.<segment_name>.frontend.errors.<name>.backend=NAME` | Same as `traefik.frontend.errors.<name>.backend` |
|
||||
| `traefik.<segment_name>.frontend.errors.<name>.query=PATH` | Same as `traefik.frontend.errors.<name>.query` |
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
# DynamoDB Backend
|
||||
# DynamoDB Provider
|
||||
|
||||
Træfik can be configured to use Amazon DynamoDB as a backend configuration.
|
||||
Træfik can be configured to use Amazon DynamoDB as a provider.
|
||||
|
||||
## Configuration
|
||||
|
||||
```toml
|
||||
################################################################
|
||||
# DynamoDB configuration backend
|
||||
# DynamoDB Provider
|
||||
################################################################
|
||||
|
||||
# Enable DynamoDB configuration backend.
|
||||
# Enable DynamoDB Provider.
|
||||
[dynamodb]
|
||||
|
||||
# Region to use when connecting to AWS.
|
||||
@@ -68,4 +68,3 @@ Items in the `dynamodb` table must have three attributes:
|
||||
See `types/types.go` for details.
|
||||
The presence or absence of this attribute determines its type.
|
||||
So an item should never have both a `frontend` and a `backend` attribute.
|
||||
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
# ECS Backend
|
||||
# ECS Provider
|
||||
|
||||
Træfik can be configured to use Amazon ECS as a backend configuration.
|
||||
Træfik can be configured to use Amazon ECS as a provider.
|
||||
|
||||
## Configuration
|
||||
|
||||
```toml
|
||||
################################################################
|
||||
# ECS configuration backend
|
||||
# ECS Provider
|
||||
################################################################
|
||||
|
||||
# Enable ECS configuration backend.
|
||||
# Enable ECS Provider.
|
||||
[ecs]
|
||||
|
||||
# ECS Cluster Name.
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
# Etcd Backend
|
||||
# Etcd Provider
|
||||
|
||||
Træfik can be configured to use Etcd as a backend configuration.
|
||||
Træfik can be configured to use Etcd as a provider.
|
||||
|
||||
```toml
|
||||
################################################################
|
||||
# Etcd configuration backend
|
||||
# Etcd Provider
|
||||
################################################################
|
||||
|
||||
# Enable Etcd configuration backend.
|
||||
# Enable Etcd Provider.
|
||||
[etcd]
|
||||
|
||||
# Etcd server endpoint.
|
||||
@@ -66,7 +66,7 @@ useAPIV3 = true
|
||||
# insecureSkipVerify = true
|
||||
```
|
||||
|
||||
To enable constraints see [backend-specific constraints section](/configuration/commons/#backend-specific).
|
||||
To enable constraints see [provider-specific constraints section](/configuration/commons/#provider-specific).
|
||||
|
||||
Please refer to the [Key Value storage structure](/user-guide/kv-config/#key-value-storage-structure) section to get documentation on Traefik KV structure.
|
||||
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
# Eureka Backend
|
||||
# Eureka Provider
|
||||
|
||||
Træfik can be configured to use Eureka as a backend configuration.
|
||||
Træfik can be configured to use Eureka as a provider.
|
||||
|
||||
```toml
|
||||
################################################################
|
||||
# Eureka configuration backend
|
||||
# Eureka Provider
|
||||
################################################################
|
||||
|
||||
# Enable Eureka configuration backend.
|
||||
# Enable Eureka Provider.
|
||||
[eureka]
|
||||
|
||||
# Eureka server endpoint.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# File Backends
|
||||
# File Provider
|
||||
|
||||
Træfik can be configured with a file.
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Kubernetes Ingress Backend
|
||||
# Kubernetes Ingress Provider
|
||||
|
||||
Træfik can be configured to use Kubernetes Ingress as a backend configuration.
|
||||
Træfik can be configured to use Kubernetes Ingress as a provider.
|
||||
|
||||
See also [Kubernetes user guide](/user-guide/kubernetes).
|
||||
|
||||
@@ -8,10 +8,10 @@ See also [Kubernetes user guide](/user-guide/kubernetes).
|
||||
|
||||
```toml
|
||||
################################################################
|
||||
# Kubernetes Ingress configuration backend
|
||||
# Kubernetes Ingress Provider
|
||||
################################################################
|
||||
|
||||
# Enable Kubernetes Ingress configuration backend.
|
||||
# Enable Kubernetes Ingress Provider.
|
||||
[kubernetes]
|
||||
|
||||
# Kubernetes server endpoint.
|
||||
@@ -112,7 +112,7 @@ Although traefik will connect directly to the endpoints (pods), it still checks
|
||||
If the service port defined in the ingress spec is 443, then the backend communication protocol is assumed to be TLS, and will connect via TLS automatically.
|
||||
|
||||
!!! note
|
||||
Please note that by enabling TLS communication between traefik and your pods, you will have to have trusted certificates that have the proper trust chain and IP subject name.
|
||||
Please note that by enabling TLS communication between traefik and your pods, you will have to have trusted certificates that have the proper trust chain and IP subject name.
|
||||
If this is not an option, you may need to skip TLS certificate verification.
|
||||
See the [insecureSkipVerify](/configuration/commons/#main-section) setting for more details.
|
||||
|
||||
@@ -137,7 +137,7 @@ The following general annotations are applicable on the Ingress object:
|
||||
| `traefik.ingress.kubernetes.io/redirect-replacement: http://mydomain/$1` | Redirect to another URL for that frontend. Must be set with `traefik.ingress.kubernetes.io/redirect-regex`. |
|
||||
| `traefik.ingress.kubernetes.io/rewrite-target: /users` | Replaces each matched Ingress path with the specified one, and adds the old path to the `X-Replaced-Path` header. |
|
||||
| `traefik.ingress.kubernetes.io/rule-type: PathPrefixStrip` | Override the default frontend rule type. Default: `PathPrefix`. |
|
||||
| `traefik.ingress.kubernetes.io/whitelist-source-range: "1.2.3.0/24, fe80::/16"` | A comma-separated list of IP ranges permitted for access. all source IPs are permitted if the list is empty or a single range is ill-formatted. |
|
||||
| `traefik.ingress.kubernetes.io/whitelist-source-range: "1.2.3.0/24, fe80::/16"` | A comma-separated list of IP ranges permitted for access. all source IPs are permitted if the list is empty or a single range is ill-formatted. Please note, you may have to set `service.spec.externalTrafficPolicy` to the value `Local` to preserve the source IP of the request for filtering. Please see [this link](https://kubernetes.io/docs/tutorials/services/source-ip/) for more information.|
|
||||
| `traefik.ingress.kubernetes.io/app-root: "/index.html"` | Redirects all requests for `/` to the defined path. (4) |
|
||||
|
||||
<1> `traefik.ingress.kubernetes.io/error-pages` example:
|
||||
@@ -261,4 +261,4 @@ More information are available in the [User Guide](/user-guide/kubernetes/#add-
|
||||
|
||||
!!! note
|
||||
Only TLS certificates provided by users can be stored in Kubernetes Secrets.
|
||||
[Let's Encrypt](https://letsencrypt.org) certificates cannot be managed in Kubernets Secrets yet.
|
||||
[Let's Encrypt](https://letsencrypt.org) certificates cannot be managed in Kubernets Secrets yet.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Marathon Backend
|
||||
# Marathon Provider
|
||||
|
||||
Træfik can be configured to use Marathon as a backend configuration.
|
||||
Træfik can be configured to use Marathon as a provider.
|
||||
|
||||
See also [Marathon user guide](/user-guide/marathon).
|
||||
|
||||
@@ -9,10 +9,10 @@ See also [Marathon user guide](/user-guide/marathon).
|
||||
|
||||
```toml
|
||||
################################################################
|
||||
# Mesos/Marathon configuration backend
|
||||
# Mesos/Marathon Provider
|
||||
################################################################
|
||||
|
||||
# Enable Marathon configuration backend.
|
||||
# Enable Marathon Provider.
|
||||
[marathon]
|
||||
|
||||
# Marathon server endpoint.
|
||||
@@ -157,7 +157,7 @@ domain = "marathon.localhost"
|
||||
# respectReadinessChecks = true
|
||||
```
|
||||
|
||||
To enable constraints see [backend-specific constraints section](/configuration/commons/#backend-specific).
|
||||
To enable constraints see [provider-specific constraints section](/configuration/commons/#provider-specific).
|
||||
|
||||
## Labels: overriding default behavior
|
||||
|
||||
@@ -255,13 +255,13 @@ Segment labels override the default behavior.
|
||||
|
||||
| Label | Description |
|
||||
|---------------------------------------------------------------------------|-------------------------------------------------------------|
|
||||
| `traefik.<segment_name>.backend=BACKEND` | Same as `traefik.backend` |
|
||||
| `traefik.<segment_name>.domain=DOMAIN` | Same as `traefik.domain` |
|
||||
| `traefik.<segment_name>.portIndex=1` | Same as `traefik.portIndex` |
|
||||
| `traefik.<segment_name>.port=PORT` | Same as `traefik.port` |
|
||||
| `traefik.<segment_name>.protocol=http` | Same as `traefik.protocol` |
|
||||
| `traefik.<segment_name>.weight=10` | Same as `traefik.weight` |
|
||||
| `traefik.<segment_name>.frontend.auth.basic=EXPR` | Same as `traefik.frontend.auth.basic` |
|
||||
| `traefik.<segment_name>.frontend.backend=BACKEND` | Same as `traefik.frontend.backend` |
|
||||
| `traefik.<segment_name>.frontend.entryPoints=https` | Same as `traefik.frontend.entryPoints` |
|
||||
| `traefik.<segment_name>.frontend.errors.<name>.backend=NAME` | Same as `traefik.frontend.errors.<name>.backend` |
|
||||
| `traefik.<segment_name>.frontend.errors.<name>.query=PATH` | Same as `traefik.frontend.errors.<name>.query` |
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
# Mesos Generic Backend
|
||||
# Mesos Generic Provider
|
||||
|
||||
Træfik can be configured to use Mesos as a backend configuration.
|
||||
Træfik can be configured to use Mesos as a provider.
|
||||
|
||||
```toml
|
||||
################################################################
|
||||
# Mesos configuration backend
|
||||
# Mesos Provider
|
||||
################################################################
|
||||
|
||||
# Enable Mesos configuration backend.
|
||||
# Enable Mesos Provider.
|
||||
[mesos]
|
||||
|
||||
# Mesos server endpoint.
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
# Rancher Backend
|
||||
# Rancher Provider
|
||||
|
||||
Træfik can be configured to use Rancher as a backend configuration.
|
||||
Træfik can be configured to use Rancher as a provider.
|
||||
|
||||
## Global Configuration
|
||||
|
||||
```toml
|
||||
################################################################
|
||||
# Rancher configuration backend
|
||||
# Rancher Provider
|
||||
################################################################
|
||||
|
||||
# Enable Rancher configuration backend.
|
||||
# Enable Rancher Provider.
|
||||
[rancher]
|
||||
|
||||
# Default domain used.
|
||||
@@ -64,13 +64,13 @@ enableServiceHealthFilter = true
|
||||
# templateVersion = 2
|
||||
```
|
||||
|
||||
To enable constraints see [backend-specific constraints section](/configuration/commons/#backend-specific).
|
||||
To enable constraints see [provider-specific constraints section](/configuration/commons/#provider-specific).
|
||||
|
||||
## Rancher Metadata Service
|
||||
|
||||
```toml
|
||||
# Enable Rancher metadata service configuration backend instead of the API
|
||||
# configuration backend.
|
||||
# Enable Rancher metadata service provider instead of the API
|
||||
# provider.
|
||||
#
|
||||
# Optional
|
||||
# Default: false
|
||||
@@ -97,7 +97,7 @@ prefix = "/2016-07-29"
|
||||
## Rancher API
|
||||
|
||||
```toml
|
||||
# Enable Rancher API configuration backend.
|
||||
# Enable Rancher API provider.
|
||||
#
|
||||
# Optional
|
||||
# Default: true
|
||||
@@ -222,12 +222,12 @@ Segment labels override the default behavior.
|
||||
|
||||
| Label | Description |
|
||||
|---------------------------------------------------------------------------|-------------------------------------------------------------|
|
||||
| `traefik.<segment_name>.backend=BACKEND` | Same as `traefik.backend` |
|
||||
| `traefik.<segment_name>.domain=DOMAIN` | Same as `traefik.domain` |
|
||||
| `traefik.<segment_name>.port=PORT` | Same as `traefik.port` |
|
||||
| `traefik.<segment_name>.protocol=http` | Same as `traefik.protocol` |
|
||||
| `traefik.<segment_name>.weight=10` | Same as `traefik.weight` |
|
||||
| `traefik.<segment_name>.frontend.auth.basic=EXPR` | Same as `traefik.frontend.auth.basic` |
|
||||
| `traefik.<segment_name>.frontend.backend=BACKEND` | Same as `traefik.frontend.backend` |
|
||||
| `traefik.<segment_name>.frontend.entryPoints=https` | Same as `traefik.frontend.entryPoints` |
|
||||
| `traefik.<segment_name>.frontend.errors.<name>.backend=NAME` | Same as `traefik.frontend.errors.<name>.backend` |
|
||||
| `traefik.<segment_name>.frontend.errors.<name>.query=PATH` | Same as `traefik.frontend.errors.<name>.query` |
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Rest Backend
|
||||
# Rest Provider
|
||||
|
||||
Træfik can be configured:
|
||||
|
||||
@@ -7,7 +7,7 @@ Træfik can be configured:
|
||||
## Configuration
|
||||
|
||||
```toml
|
||||
# Enable rest backend.
|
||||
# Enable REST Provider.
|
||||
[rest]
|
||||
# Name of the related entry point
|
||||
#
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Azure Service Fabric Backend
|
||||
# Azure Service Fabric Provider
|
||||
|
||||
Træfik can be configured to use Azure Service Fabric as a backend configuration.
|
||||
Træfik can be configured to use Azure Service Fabric as a provider.
|
||||
|
||||
See [this repository for an example deployment package and further documentation.](https://aka.ms/traefikonsf)
|
||||
|
||||
@@ -8,10 +8,10 @@ See [this repository for an example deployment package and further documentation
|
||||
|
||||
```toml
|
||||
################################################################
|
||||
# Azure Service Fabric provider
|
||||
# Azure Service Fabric Provider
|
||||
################################################################
|
||||
|
||||
# Enable Azure Service Fabric configuration backend
|
||||
# Enable Azure Service Fabric Provider
|
||||
[serviceFabric]
|
||||
|
||||
# Azure Service Fabric Management Endpoint
|
||||
@@ -61,7 +61,7 @@ Here is an example of an extension setting Træfik labels:
|
||||
<Extension Name="Traefik">
|
||||
<Labels xmlns="http://schemas.microsoft.com/2015/03/fabact-no-schema">
|
||||
<Label Key="traefik.frontend.rule.example2">PathPrefixStrip: /a/path/to/strip</Label>
|
||||
<Label Key="traefik.enable">true</Label>
|
||||
<Label Key="traefik.enable">true</Label>
|
||||
<Label Key="traefik.frontend.passHostHeader">true</Label>
|
||||
</Labels>
|
||||
</Extension>
|
||||
@@ -98,8 +98,9 @@ Labels, set through extensions or the property manager, can be used on services
|
||||
|------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `traefik.enable=false` | Disable this container in Træfik |
|
||||
| `traefik.backend.circuitbreaker.expression=EXPR` | Create a [circuit breaker](/basics/#backends) to be used against the backend |
|
||||
| `traefik.backend.group.name` | Group all services with the same name into a single backend in Træfik |
|
||||
| `traefik.backend.group.weight` | Set the weighting of the current services nodes in the backend group |
|
||||
| `traefik.servicefabric.groupname` | Group all services with the same name into a single backend in Træfik |
|
||||
| `traefik.servicefabric.groupweight` | Set the weighting of the current services nodes in the backend group |
|
||||
| `traefik.servicefabric.enablelabeloverrides` | Toggle whether labels can be overridden using the Service Fabric Property Manager API |
|
||||
| `traefik.backend.healthcheck.path=/health` | Enable health check for the backend, hitting the container at `path`. |
|
||||
| `traefik.backend.healthcheck.port=8080` | Allow to use a different port for the health check. |
|
||||
| `traefik.backend.healthcheck.interval=1s` | Define the health check interval. |
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Web Backend
|
||||
# Web Provider
|
||||
|
||||
!!! danger "DEPRECATED"
|
||||
The web provider is deprecated, please use the [api](/configuration/api.md), the [ping](/configuration/ping.md), the [metrics](/configuration/metrics) and the [rest](/configuration/backends/rest.md) provider.
|
||||
@@ -12,7 +12,7 @@ Træfik can be configured:
|
||||
## Configuration
|
||||
|
||||
```toml
|
||||
# Enable web backend.
|
||||
# Enable Web Provider.
|
||||
[web]
|
||||
|
||||
# Web administration port.
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
# Zookeeper Backend
|
||||
# Zookeeper Provider
|
||||
|
||||
Træfik can be configured to use Zookeeper as a backend configuration.
|
||||
Træfik can be configured to use Zookeeper as a provider.
|
||||
|
||||
```toml
|
||||
################################################################
|
||||
# Zookeeper configuration backend
|
||||
# Zookeeper Provider
|
||||
################################################################
|
||||
|
||||
# Enable Zookeeperconfiguration backend.
|
||||
# Enable Zookeeper Provider.
|
||||
[zookeeper]
|
||||
|
||||
# Zookeeper server endpoint.
|
||||
@@ -56,6 +56,6 @@ prefix = "traefik"
|
||||
# insecureSkipVerify = true
|
||||
```
|
||||
|
||||
To enable constraints see [backend-specific constraints section](/configuration/commons/#backend-specific).
|
||||
To enable constraints see [provider-specific constraints section](/configuration/commons/#provider-specific).
|
||||
|
||||
Please refer to the [Key Value storage structure](/user-guide/kv-config/#key-value-storage-structure) section to get documentation on Traefik KV structure.
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
#
|
||||
# checkNewVersion = false
|
||||
|
||||
# Backends throttle duration.
|
||||
# Providers throttle duration.
|
||||
#
|
||||
# Optional
|
||||
# Default: "2s"
|
||||
@@ -85,7 +85,7 @@ Can be provided in a format supported by [time.ParseDuration](https://golang.org
|
||||
If no units are provided, the value is parsed assuming seconds.
|
||||
**Note:** in this time frame no new requests are accepted.
|
||||
|
||||
- `providersThrottleDuration`: Backends throttle duration: minimum duration in seconds between 2 events from providers before applying a new configuration.
|
||||
- `providersThrottleDuration`: Providers 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.
|
||||
Can be provided in a format supported by [time.ParseDuration](https://golang.org/pkg/time/#ParseDuration) or as raw values (digits).
|
||||
If no units are provided, the value is parsed assuming seconds.
|
||||
@@ -108,7 +108,7 @@ Each frontend can specify its own entrypoints.
|
||||
|
||||
In a micro-service architecture, with a central service discovery, setting constraints limits Træfik scope to a smaller number of routes.
|
||||
|
||||
Træfik filters services according to service attributes/tags set in your configuration backends.
|
||||
Træfik filters services according to service attributes/tags set in your providers.
|
||||
|
||||
Supported filters:
|
||||
|
||||
@@ -136,9 +136,9 @@ constraints = ["tag==us-*"]
|
||||
constraints = ["tag!=us-*", "tag!=asia-*"]
|
||||
```
|
||||
|
||||
### Backend-specific
|
||||
### provider-specific
|
||||
|
||||
Supported backends:
|
||||
Supported Providers:
|
||||
|
||||
- Docker
|
||||
- Consul K/V
|
||||
@@ -151,12 +151,12 @@ Supported backends:
|
||||
- Kubernetes (using a provider-specific mechanism based on label selectors)
|
||||
|
||||
```toml
|
||||
# Backend-specific constraint
|
||||
# Provider-specific constraint
|
||||
[consulCatalog]
|
||||
# ...
|
||||
constraints = ["tag==api"]
|
||||
|
||||
# Backend-specific constraint
|
||||
# Provider-specific constraint
|
||||
[marathon]
|
||||
# ...
|
||||
constraints = ["tag==api", "tag!=v*-beta"]
|
||||
@@ -421,12 +421,12 @@ idleTimeout = "360s"
|
||||
!!! warning
|
||||
For advanced users only.
|
||||
|
||||
Supported by all backends except: File backend, Web backend and DynamoDB backend.
|
||||
Supported by all providers except: File Provider, Web Provider and DynamoDB Provider.
|
||||
|
||||
```toml
|
||||
[backend_name]
|
||||
[provider_name]
|
||||
|
||||
# Override default configuration template. For advanced users :)
|
||||
# Override default provider configuration template. For advanced users :)
|
||||
#
|
||||
# Optional
|
||||
# Default: ""
|
||||
|
||||
@@ -106,7 +106,7 @@ traefik:
|
||||
```ini
|
||||
Name:foo
|
||||
Address::80
|
||||
TLS:goo,gii
|
||||
TLS:/my/path/foo.cert,/my/path/foo.key;/my/path/goo.cert,/my/path/goo.key;/my/path/hoo.cert,/my/path/hoo.key
|
||||
TLS
|
||||
CA:car
|
||||
CA.Optional:true
|
||||
|
||||
@@ -54,7 +54,7 @@ Træfik supports two backends: Jaeger and Zipkin.
|
||||
```
|
||||
|
||||
!!! warning
|
||||
Træfik is only able to send data over compact thrift protocol to the [Jaeger agent](https://www.jaegertracing.io/docs/deployment/#agent).
|
||||
Træfik is only able to send data over compact thrift protocol to the [Jaeger agent](https://www.jaegertracing.io/docs/deployment/#agent).
|
||||
|
||||
## Zipkin
|
||||
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 189 KiB After Width: | Height: | Size: 208 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 218 KiB After Width: | Height: | Size: 274 KiB |
@@ -12,7 +12,7 @@
|
||||
|
||||
Træfik is a modern HTTP reverse proxy and load balancer that makes deploying microservices easy.
|
||||
Træfik integrates with your existing infrastructure components ([Docker](https://www.docker.com/), [Swarm mode](https://docs.docker.com/engine/swarm/), [Kubernetes](https://kubernetes.io), [Marathon](https://mesosphere.github.io/marathon/), [Consul](https://www.consul.io/), [Etcd](https://coreos.com/etcd/), [Rancher](https://rancher.com), [Amazon ECS](https://aws.amazon.com/ecs), ...) and configures itself automatically and dynamically.
|
||||
Telling Træfik where your orchestrator is could be the _only_ configuration step you need to do.
|
||||
Pointing Træfik at your orchestrator should be the _only_ configuration step you need.
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -47,7 +47,7 @@ _(But if you'd rather configure some of your routes manually, Træfik supports t
|
||||
- Packaged as a single binary file (made with :heart: with go) and available as a [tiny](https://microbadger.com/images/traefik) [official](https://hub.docker.com/r/_/traefik/) docker image
|
||||
|
||||
|
||||
## Supported backends
|
||||
## Supported Providers
|
||||
|
||||
- [Docker](/configuration/backends/docker/) / [Swarm mode](/configuration/backends/docker/#docker-swarm-mode)
|
||||
- [Kubernetes](/configuration/backends/kubernetes/)
|
||||
@@ -166,7 +166,7 @@ IP: 172.27.0.4
|
||||
### 4 — Enjoy Træfik's Magic
|
||||
|
||||
Now that you have a basic understanding of how Træfik can automatically create the routes to your services and load balance them, it might be time to dive into [the documentation](/) and let Træfik work for you!
|
||||
Whatever your infrastructure is, there is probably [an available Træfik backend](/#supported-backends) that will do the job.
|
||||
Whatever your infrastructure is, there is probably [an available Træfik provider](/#supported-providers) that will do the job.
|
||||
|
||||
Our recommendation would be to see for yourself how simple it is to enable HTTPS with [Træfik's let's encrypt integration](/user-guide/examples/#lets-encrypt-support) using the dedicated [user guide](/user-guide/docker-and-lets-encrypt/).
|
||||
|
||||
|
||||
@@ -113,7 +113,7 @@ This is the minimum configuration required to do the following:
|
||||
- Log `ERROR`-level messages (or more severe) to the console, but silence `DEBUG`-level messages
|
||||
- Check for new versions of Træfik periodically
|
||||
- Create two entry points, namely an `HTTP` endpoint on port `80`, and an `HTTPS` endpoint on port `443` where all incoming traffic on port `80` will immediately get redirected to `HTTPS`.
|
||||
- Enable the Docker configuration backend and listen for container events on the Docker unix socket we've mounted earlier. However, **new containers will not be exposed by Træfik by default, we'll get into this in a bit!**
|
||||
- Enable the Docker provider and listen for container events on the Docker unix socket we've mounted earlier. However, **new containers will not be exposed by Træfik by default, we'll get into this in a bit!**
|
||||
- Enable automatic request and configuration of SSL certificates using Let's Encrypt.
|
||||
These certificates will be stored in the `acme.json` file, which you can back-up yourself and store off-premises.
|
||||
|
||||
@@ -123,7 +123,7 @@ Alright, let's boot the container. From the `/opt/traefik` directory, run `docke
|
||||
|
||||
Now that we've fully configured and started Træfik, it's time to get our applications running!
|
||||
|
||||
Let's take a simple example of a micro-service project consisting of various services, where some will be exposed to the outside world and some will not.
|
||||
Let's take a simple example of a micro-service project consisting of various services, where some will be exposed to the outside world and some will not.
|
||||
|
||||
The `docker-compose.yml` of our project looks like this:
|
||||
|
||||
@@ -145,12 +145,11 @@ services:
|
||||
expose:
|
||||
- "9000"
|
||||
labels:
|
||||
- "traefik.backend=my-awesome-app-app"
|
||||
- "traefik.docker.network=web"
|
||||
- "traefik.frontend.rule=Host:app.my-awesome-app.org"
|
||||
- "traefik.enable=true"
|
||||
- "traefik.port=9000"
|
||||
- "traefik.default.protocol=http"
|
||||
- "traefik.basic.frontend.rule=Host:app.my-awesome-app.org"
|
||||
- "traefik.basic.port=9000"
|
||||
- "traefik.basic.protocol=http"
|
||||
- "traefik.admin.frontend.rule=Host:admin-app.my-awesome-app.org"
|
||||
- "traefik.admin.protocol=https"
|
||||
- "traefik.admin.port=9443"
|
||||
@@ -204,12 +203,11 @@ Thanks to Docker labels, we can tell Træfik how to create its internal routing
|
||||
Let's take a look at the labels themselves for the `app` service, which is a HTTP webservice listing on port 9000:
|
||||
|
||||
```yaml
|
||||
- "traefik.backend=my-awesome-app-app"
|
||||
- "traefik.docker.network=web"
|
||||
- "traefik.frontend.rule=Host:app.my-awesome-app.org"
|
||||
- "traefik.enable=true"
|
||||
- "traefik.port=9000"
|
||||
- "traefik.default.protocol=http"
|
||||
- "traefik.basic.frontend.rule=Host:app.my-awesome-app.org"
|
||||
- "traefik.basic.port=9000"
|
||||
- "traefik.basic.protocol=http"
|
||||
- "traefik.admin.frontend.rule=Host:admin-app.my-awesome-app.org"
|
||||
- "traefik.admin.protocol=https"
|
||||
- "traefik.admin.port=9443"
|
||||
@@ -221,11 +219,11 @@ We use both `container labels` and `service labels`.
|
||||
|
||||
First, we specify the `backend` name which corresponds to the actual service we're routing **to**.
|
||||
|
||||
We also tell Træfik to use the `web` network to route HTTP traffic to this container.
|
||||
We also tell Træfik to use the `web` network to route HTTP traffic to this container.
|
||||
With the `traefik.enable` label, we tell Træfik to include this container in its internal configuration.
|
||||
|
||||
With the `frontend.rule` label, we tell Træfik that we want to route to this container if the incoming HTTP request contains the `Host` `app.my-awesome-app.org`.
|
||||
Essentially, this is the actual rule used for Layer-7 load balancing.
|
||||
Essentially, this is the actual rule used for Layer-7 load balancing.
|
||||
|
||||
Finally but not unimportantly, we tell Træfik to route **to** port `9000`, since that is the actual TCP/IP port the container actually listens on.
|
||||
|
||||
@@ -236,11 +234,11 @@ Finally but not unimportantly, we tell Træfik to route **to** port `9000`, sinc
|
||||
When both `container labels` and `service labels` are defined, `container labels` are just used as default values for missing `service labels` but no frontend/backend are going to be defined only with these labels.
|
||||
Obviously, labels `traefik.frontend.rule` and `traefik.port` described above, will only be used to complete information set in `service labels` during the container frontends/bakends creation.
|
||||
|
||||
In the example, two service names are defined : `default` and `admin`.
|
||||
In the example, two service names are defined : `basic` and `admin`.
|
||||
They allow creating two frontends and two backends.
|
||||
|
||||
- `default` has only one `service label` : `traefik.default.protocol`.
|
||||
Træfik will use values set in `traefik.frontend.rule` and `traefik.port` to create the `default` frontend and backend.
|
||||
- `basic` has only one `service label` : `traefik.basic.protocol`.
|
||||
Træfik will use values set in `traefik.frontend.rule` and `traefik.port` to create the `basic` frontend and backend.
|
||||
The frontend listens to incoming HTTP requests which contain the `Host` `app.my-awesome-app.org` and redirect them in `HTTP` to the port `9000` of the backend.
|
||||
- `admin` has all the `services labels` needed to create the `admin` frontend and backend (`traefik.admin.frontend.rule`, `traefik.admin.protocol`, `traefik.admin.port`).
|
||||
Træfik will create a frontend to listen to incoming HTTP requests which contain the `Host` `admin-app.my-awesome-app.org` and redirect them in `HTTPS` to the port `9443` of the backend.
|
||||
|
||||
@@ -68,7 +68,7 @@ defaultEntryPoints = ["http", "https"]
|
||||
[acme]
|
||||
email = "test@traefik.io"
|
||||
storage = "acme.json"
|
||||
caServer = "http://172.18.0.1:4000/directory"
|
||||
caServer = "https://acme-staging-v02.api.letsencrypt.org/directory"
|
||||
entryPoint = "https"
|
||||
[acme.httpChallenge]
|
||||
entryPoint = "http"
|
||||
@@ -103,7 +103,7 @@ Træfik generates these certificates when it starts and it needs to be restart i
|
||||
email = "test@traefik.io"
|
||||
storage = "acme.json"
|
||||
onHostRule = true
|
||||
caServer = "http://172.18.0.1:4000/directory"
|
||||
caServer = "https://acme-staging-v02.api.letsencrypt.org/directory"
|
||||
entryPoint = "https"
|
||||
[acme.httpChallenge]
|
||||
entryPoint = "http"
|
||||
@@ -140,7 +140,7 @@ If a backend is added with a `onHost` rule, Træfik will automatically generate
|
||||
email = "test@traefik.io"
|
||||
storage = "acme.json"
|
||||
onDemand = true
|
||||
caServer = "http://172.18.0.1:4000/directory"
|
||||
caServer = "https://acme-staging-v02.api.letsencrypt.org/directory"
|
||||
entryPoint = "https"
|
||||
[acme.httpChallenge]
|
||||
entryPoint = "http"
|
||||
@@ -167,7 +167,7 @@ This configuration allows generating a Let's Encrypt certificate (thanks to `HTT
|
||||
[acme]
|
||||
email = "test@traefik.io"
|
||||
storage = "acme.json"
|
||||
caServer = "http://172.18.0.1:4000/directory"
|
||||
caServer = "https://acme-staging-v02.api.letsencrypt.org/directory"
|
||||
entryPoint = "https"
|
||||
[acme.dnsChallenge]
|
||||
provider = "digitalocean" # DNS Provider name (cloudflare, OVH, gandi...)
|
||||
|
||||
@@ -76,7 +76,7 @@ defaultEntryPoints = ["http", "https"]
|
||||
address = ":80"
|
||||
[entryPoints.https]
|
||||
address = ":443"
|
||||
|
||||
|
||||
[entryPoints.https.tls]
|
||||
[[entryPoints.https.tls.certificates]]
|
||||
certFile = "integration/fixtures/https/snitest.com.cert"
|
||||
@@ -164,7 +164,7 @@ If a Consul ACL is used to restrict Træfik read/write access, one of the follow
|
||||
key "traefik" {
|
||||
policy = "write"
|
||||
},
|
||||
|
||||
|
||||
session "" {
|
||||
policy = "write"
|
||||
}
|
||||
@@ -266,6 +266,10 @@ Here is the toml configuration we would like to store in the store :
|
||||
backend = "backend1"
|
||||
passHostHeader = true
|
||||
priority = 10
|
||||
basicAuth = [
|
||||
"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/",
|
||||
"test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
|
||||
]
|
||||
entrypoints = ["https"] # overrides defaultEntryPoints
|
||||
[frontends.frontend2.routes.test_1]
|
||||
rule = "Host:{subdomain:[a-z]+}.localhost"
|
||||
@@ -325,13 +329,15 @@ And there, the same dynamic configuration in a KV Store (using `prefix = "traefi
|
||||
|
||||
- frontend 2
|
||||
|
||||
| Key | Value |
|
||||
|----------------------------------------------------|--------------------|
|
||||
| `/traefik/frontends/frontend2/backend` | `backend1` |
|
||||
| `/traefik/frontends/frontend2/passhostheader` | `true` |
|
||||
| `/traefik/frontends/frontend2/priority` | `10` |
|
||||
| `/traefik/frontends/frontend2/entrypoints` | `http,https` |
|
||||
| `/traefik/frontends/frontend2/routes/test_2/rule` | `PathPrefix:/test` |
|
||||
| Key | Value |
|
||||
|----------------------------------------------------|-----------------------------------------------|
|
||||
| `/traefik/frontends/frontend2/backend` | `backend1` |
|
||||
| `/traefik/frontends/frontend2/passhostheader` | `true` |
|
||||
| `/traefik/frontends/frontend2/priority` | `10` |
|
||||
| `/traefik/frontends/frontend2/basicauth/0` | `test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/` |
|
||||
| `/traefik/frontends/frontend2/basicauth/1` | `test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0` |
|
||||
| `/traefik/frontends/frontend2/entrypoints` | `http,https` |
|
||||
| `/traefik/frontends/frontend2/routes/test_2/rule` | `PathPrefix:/test` |
|
||||
|
||||
- certificate 1
|
||||
|
||||
@@ -422,7 +428,7 @@ Træfik will not start but the [static configuration](/basics/#static-trfik-conf
|
||||
|
||||
If you configured ACME (Let's Encrypt), your registration account and your certificates will also be uploaded.
|
||||
|
||||
If you configured a file backend `[file]`, all your dynamic configuration (backends, frontends...) will be uploaded to the Key-value store.
|
||||
If you configured a file provider `[file]`, all your dynamic configuration (backends, frontends...) will be uploaded to the Key-value store.
|
||||
|
||||
To upload your ACME certificates to the KV store, get your Traefik TOML file and add the new `storage` option in the `acme` section:
|
||||
|
||||
|
||||
@@ -101,7 +101,7 @@ Let's explain this command:
|
||||
| `--constraint=node.role==manager` | we ask docker to schedule Træfik on a manager node. |
|
||||
| `--mount type=bind,source=/var/run/docker.sock,target=/var/run/docker.sock` | we bind mount the docker socket where Træfik is scheduled to be able to speak to the daemon. |
|
||||
| `--network traefik-net` | we attach the Træfik service (and thus the underlying container) to the `traefik-net` network. |
|
||||
| `--docker` | enable docker backend, and `--docker.swarmMode` to enable the swarm mode on Træfik. |
|
||||
| `--docker` | enable docker provider, and `--docker.swarmMode` to enable the swarm mode on Træfik. |
|
||||
| `--api | activate the webUI on port 8080 |
|
||||
|
||||
|
||||
|
||||
@@ -104,7 +104,7 @@ Let's explain this command:
|
||||
| `--net=my-net` | run the container on the network my-net |
|
||||
| `-v /var/lib/boot2docker/:/ssl` | mount the ssl keys generated by docker-machine |
|
||||
| `-c /dev/null` | empty config file |
|
||||
| `--docker` | enable docker backend |
|
||||
| `--docker` | enable docker provider |
|
||||
| `--docker.endpoint=tcp://172.18.0.1:2376` | connect to the swarm master using the docker_gwbridge network |
|
||||
| `--docker.tls` | enable TLS using the docker-machine keys |
|
||||
| `--api` | activate the webUI on port 8080 |
|
||||
|
||||
@@ -101,19 +101,25 @@ func openAccessLogFile(filePath string) (*os.File, error) {
|
||||
return file, nil
|
||||
}
|
||||
|
||||
// GetLogDataTable gets the request context object that contains logging data. This accretes
|
||||
// data as the request passes through the middleware chain.
|
||||
// GetLogDataTable gets the request context object that contains logging data.
|
||||
// This creates data as the request passes through the middleware chain.
|
||||
func GetLogDataTable(req *http.Request) *LogData {
|
||||
return req.Context().Value(DataTableKey).(*LogData)
|
||||
if ld, ok := req.Context().Value(DataTableKey).(*LogData); ok {
|
||||
return ld
|
||||
}
|
||||
log.Errorf("%s is nil", DataTableKey)
|
||||
return &LogData{Core: make(CoreLogData)}
|
||||
}
|
||||
|
||||
func (l *LogHandler) ServeHTTP(rw http.ResponseWriter, req *http.Request, next http.HandlerFunc) {
|
||||
now := time.Now().UTC()
|
||||
core := make(CoreLogData)
|
||||
|
||||
core := CoreLogData{
|
||||
StartUTC: now,
|
||||
StartLocal: now.Local(),
|
||||
}
|
||||
|
||||
logDataTable := &LogData{Core: core, Request: req.Header}
|
||||
core[StartUTC] = now
|
||||
core[StartLocal] = now.Local()
|
||||
|
||||
reqWithDataTable := req.WithContext(context.WithValue(req.Context(), DataTableKey, logDataTable))
|
||||
|
||||
|
||||
@@ -43,8 +43,6 @@ func (sb *SaveBackend) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
|
||||
table.Core[OriginContentSize] = crw.Size()
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
|
||||
// SaveFrontend sends the frontend name to the logger. These are sometimes used with a corresponding
|
||||
// SaveBackend handler, but not always. For example, redirected requests don't reach a backend.
|
||||
type SaveFrontend struct {
|
||||
|
||||
@@ -92,14 +92,23 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, req *http.Request, next http.
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
w.WriteHeader(recorder.GetCode())
|
||||
w.Write([]byte(http.StatusText(recorder.GetCode())))
|
||||
fmt.Fprint(w, http.StatusText(recorder.GetCode()))
|
||||
return
|
||||
}
|
||||
|
||||
recorderErrorPage := newResponseRecorder(w)
|
||||
utils.CopyHeaders(pageReq.Header, req.Header)
|
||||
|
||||
h.backendHandler.ServeHTTP(recorderErrorPage, pageReq.WithContext(req.Context()))
|
||||
|
||||
utils.CopyHeaders(w.Header(), recorder.Header())
|
||||
for key := range recorderErrorPage.Header() {
|
||||
w.Header().Del(key)
|
||||
}
|
||||
utils.CopyHeaders(w.Header(), recorderErrorPage.Header())
|
||||
|
||||
w.WriteHeader(recorder.GetCode())
|
||||
h.backendHandler.ServeHTTP(w, pageReq)
|
||||
w.Write(recorderErrorPage.GetBody().Bytes())
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ func (f *forwarderMiddleware) ServeHTTP(w http.ResponseWriter, r *http.Request,
|
||||
span.SetTag("frontend.name", f.frontend)
|
||||
span.SetTag("backend.name", f.backend)
|
||||
ext.HTTPMethod.Set(span, r.Method)
|
||||
ext.HTTPUrl.Set(span, r.URL.String())
|
||||
ext.HTTPUrl.Set(span, fmt.Sprintf("%s%s", r.URL.String(), r.RequestURI))
|
||||
span.SetTag("http.host", r.Host)
|
||||
|
||||
InjectRequestHeaders(r)
|
||||
|
||||
34
mkdocs.yml
34
mkdocs.yml
@@ -69,27 +69,27 @@ pages:
|
||||
- 'Logs': 'configuration/logs.md'
|
||||
- 'EntryPoints': 'configuration/entrypoints.md'
|
||||
- 'Let''s Encrypt': 'configuration/acme.md'
|
||||
- 'Backend: Web': 'configuration/backends/web.md'
|
||||
- 'Backend: BoltDB': 'configuration/backends/boltdb.md'
|
||||
- 'Backend: Consul': 'configuration/backends/consul.md'
|
||||
- 'Backend: Consul Catalog': 'configuration/backends/consulcatalog.md'
|
||||
- 'Backend: Docker': 'configuration/backends/docker.md'
|
||||
- 'Backend: DynamoDB': 'configuration/backends/dynamodb.md'
|
||||
- 'Backend: ECS': 'configuration/backends/ecs.md'
|
||||
- 'Backend: Etcd': 'configuration/backends/etcd.md'
|
||||
- 'Backend: Eureka': 'configuration/backends/eureka.md'
|
||||
- 'Backend: File': 'configuration/backends/file.md'
|
||||
- 'Backend: Kubernetes Ingress': 'configuration/backends/kubernetes.md'
|
||||
- 'Backend: Marathon': 'configuration/backends/marathon.md'
|
||||
- 'Backend: Mesos': 'configuration/backends/mesos.md'
|
||||
- 'Backend: Rancher': 'configuration/backends/rancher.md'
|
||||
- 'Backend: Rest': 'configuration/backends/rest.md'
|
||||
- 'Backend: Azure Service Fabric': 'configuration/backends/servicefabric.md'
|
||||
- 'Backend: Zookeeper': 'configuration/backends/zookeeper.md'
|
||||
- 'API / Dashboard': 'configuration/api.md'
|
||||
- 'BoltDB': 'configuration/backends/boltdb.md'
|
||||
- 'Consul': 'configuration/backends/consul.md'
|
||||
- 'Consul Catalog': 'configuration/backends/consulcatalog.md'
|
||||
- 'Docker': 'configuration/backends/docker.md'
|
||||
- 'DynamoDB': 'configuration/backends/dynamodb.md'
|
||||
- 'ECS': 'configuration/backends/ecs.md'
|
||||
- 'Etcd': 'configuration/backends/etcd.md'
|
||||
- 'Eureka': 'configuration/backends/eureka.md'
|
||||
- 'File': 'configuration/backends/file.md'
|
||||
- 'Kubernetes Ingress': 'configuration/backends/kubernetes.md'
|
||||
- 'Marathon': 'configuration/backends/marathon.md'
|
||||
- 'Mesos': 'configuration/backends/mesos.md'
|
||||
- 'Rancher': 'configuration/backends/rancher.md'
|
||||
- 'Rest': 'configuration/backends/rest.md'
|
||||
- 'Azure Service Fabric': 'configuration/backends/servicefabric.md'
|
||||
- 'Zookeeper': 'configuration/backends/zookeeper.md'
|
||||
- 'Ping': 'configuration/ping.md'
|
||||
- 'Metrics': 'configuration/metrics.md'
|
||||
- 'Tracing': 'configuration/tracing.md'
|
||||
- 'Web (Deprecated)': 'configuration/backends/web.md'
|
||||
- User Guides:
|
||||
- 'Configuration Examples': 'user-guide/examples.md'
|
||||
- 'Swarm Mode Cluster': 'user-guide/swarm-mode.md'
|
||||
|
||||
@@ -226,9 +226,9 @@ func (p *Provider) resolveCertificate(domain types.Domain, domainFromConfigurati
|
||||
|
||||
bundle := true
|
||||
|
||||
certificate, failures := client.ObtainCertificate(uncheckedDomains, bundle, nil, OSCPMustStaple)
|
||||
if len(failures) > 0 {
|
||||
return nil, fmt.Errorf("cannot obtain certificates %+v", failures)
|
||||
certificate, err := client.ObtainCertificate(uncheckedDomains, bundle, nil, OSCPMustStaple)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot obtain certificates: %+v", err)
|
||||
}
|
||||
|
||||
if len(certificate.Certificate) == 0 || len(certificate.PrivateKey) == 0 {
|
||||
|
||||
@@ -262,7 +262,7 @@ func isBackendLBSwarm(container dockerData) bool {
|
||||
}
|
||||
|
||||
func getSegmentBackendName(container dockerData) string {
|
||||
if value := label.GetStringValue(container.SegmentLabels, label.TraefikFrontendBackend, ""); len(value) > 0 {
|
||||
if value := label.GetStringValue(container.SegmentLabels, label.TraefikBackend, ""); len(value) > 0 {
|
||||
return provider.Normalize(container.ServiceName + "-" + value)
|
||||
}
|
||||
|
||||
|
||||
@@ -251,7 +251,7 @@ func TestSegmentBuildConfiguration(t *testing.T) {
|
||||
"traefik.sauternes.port": "2503",
|
||||
"traefik.sauternes.protocol": "https",
|
||||
"traefik.sauternes.weight": "80",
|
||||
"traefik.sauternes.frontend.backend": "foobar",
|
||||
"traefik.sauternes.backend": "foobar",
|
||||
"traefik.sauternes.frontend.passHostHeader": "false",
|
||||
"traefik.sauternes.frontend.rule": "Path:/mypath",
|
||||
"traefik.sauternes.frontend.priority": "5000",
|
||||
|
||||
@@ -88,7 +88,7 @@ func extractServicePortV1(labelName string) []string {
|
||||
// Extract backend from labels for a given service and a given docker container
|
||||
// Deprecated
|
||||
func getServiceBackendNameV1(container dockerData, serviceName string) string {
|
||||
if value, ok := getServiceLabelsV1(container, serviceName)[label.SuffixFrontendBackend]; ok {
|
||||
if value, ok := getServiceLabelsV1(container, serviceName)[label.SuffixBackend]; ok {
|
||||
return provider.Normalize(container.ServiceName + "-" + value)
|
||||
}
|
||||
return provider.Normalize(container.ServiceName + "-" + getBackendNameV1(container) + "-" + serviceName)
|
||||
|
||||
@@ -162,7 +162,7 @@ func TestDockerServiceBuildConfigurationV1(t *testing.T) {
|
||||
"traefik.service.port": "2503",
|
||||
"traefik.service.protocol": "https",
|
||||
"traefik.service.weight": "80",
|
||||
"traefik.service.frontend.backend": "foobar",
|
||||
"traefik.service.backend": "foobar",
|
||||
"traefik.service.frontend.passHostHeader": "false",
|
||||
"traefik.service.frontend.rule": "Path:/mypath",
|
||||
"traefik.service.frontend.priority": "5000",
|
||||
@@ -595,7 +595,7 @@ func TestDockerGetServiceBackendNameV1(t *testing.T) {
|
||||
},
|
||||
{
|
||||
container: containerJSON(labels(map[string]string{
|
||||
"traefik.myservice.frontend.backend": "custom-backend",
|
||||
"traefik.myservice.backend": "custom-backend",
|
||||
})),
|
||||
expected: "fake-custom-backend",
|
||||
},
|
||||
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
"github.com/containous/traefik/safe"
|
||||
"github.com/containous/traefik/tls"
|
||||
"github.com/containous/traefik/types"
|
||||
"github.com/pkg/errors"
|
||||
"gopkg.in/fsnotify.v1"
|
||||
)
|
||||
|
||||
@@ -23,6 +24,7 @@ var _ provider.Provider = (*Provider)(nil)
|
||||
type Provider struct {
|
||||
provider.BaseProvider `mapstructure:",squash" export:"true"`
|
||||
Directory string `description:"Load configuration from one or more .toml files in a directory" export:"true"`
|
||||
TraefikFile string
|
||||
}
|
||||
|
||||
// Provide allows the file provider to provide configurations to traefik
|
||||
@@ -37,10 +39,12 @@ func (p *Provider) Provide(configurationChan chan<- types.ConfigMessage, pool *s
|
||||
if p.Watch {
|
||||
var watchItem string
|
||||
|
||||
if p.Directory != "" {
|
||||
if len(p.Directory) > 0 {
|
||||
watchItem = p.Directory
|
||||
} else {
|
||||
} else if len(p.Filename) > 0 {
|
||||
watchItem = filepath.Dir(p.Filename)
|
||||
} else {
|
||||
watchItem = filepath.Dir(p.TraefikFile)
|
||||
}
|
||||
|
||||
if err := p.addWatcher(pool, watchItem, configurationChan, p.watcherCallback); err != nil {
|
||||
@@ -55,10 +59,19 @@ func (p *Provider) Provide(configurationChan chan<- types.ConfigMessage, pool *s
|
||||
// BuildConfiguration loads configuration either from file or a directory specified by 'Filename'/'Directory'
|
||||
// and returns a 'Configuration' object
|
||||
func (p *Provider) BuildConfiguration() (*types.Configuration, error) {
|
||||
if p.Directory != "" {
|
||||
if len(p.Directory) > 0 {
|
||||
return p.loadFileConfigFromDirectory(p.Directory, nil)
|
||||
}
|
||||
return p.loadFileConfig(p.Filename)
|
||||
|
||||
if len(p.Filename) > 0 {
|
||||
return p.loadFileConfig(p.Filename, true)
|
||||
}
|
||||
|
||||
if len(p.TraefikFile) > 0 {
|
||||
return p.loadFileConfig(p.TraefikFile, false)
|
||||
}
|
||||
|
||||
return nil, errors.New("Error using file configuration backend, no filename defined")
|
||||
}
|
||||
|
||||
func (p *Provider) addWatcher(pool *safe.Pool, directory string, configurationChan chan<- types.ConfigMessage, callback func(chan<- types.ConfigMessage, fsnotify.Event)) error {
|
||||
@@ -67,6 +80,11 @@ func (p *Provider) addWatcher(pool *safe.Pool, directory string, configurationCh
|
||||
return fmt.Errorf("error creating file watcher: %s", err)
|
||||
}
|
||||
|
||||
err = watcher.Add(directory)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error adding file watcher: %s", err)
|
||||
}
|
||||
|
||||
// Process events
|
||||
pool.Go(func(stop chan bool) {
|
||||
defer watcher.Close()
|
||||
@@ -76,8 +94,15 @@ func (p *Provider) addWatcher(pool *safe.Pool, directory string, configurationCh
|
||||
return
|
||||
case evt := <-watcher.Events:
|
||||
if p.Directory == "" {
|
||||
var filename string
|
||||
if len(p.Filename) > 0 {
|
||||
filename = p.Filename
|
||||
} else {
|
||||
filename = p.TraefikFile
|
||||
}
|
||||
|
||||
_, evtFileName := filepath.Split(evt.Name)
|
||||
_, confFileName := filepath.Split(p.Filename)
|
||||
_, confFileName := filepath.Split(filename)
|
||||
if evtFileName == confFileName {
|
||||
callback(configurationChan, evt)
|
||||
}
|
||||
@@ -89,18 +114,15 @@ func (p *Provider) addWatcher(pool *safe.Pool, directory string, configurationCh
|
||||
}
|
||||
}
|
||||
})
|
||||
err = watcher.Add(directory)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error adding file watcher: %s", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Provider) watcherCallback(configurationChan chan<- types.ConfigMessage, event fsnotify.Event) {
|
||||
watchItem := p.Filename
|
||||
if p.Directory != "" {
|
||||
watchItem := p.TraefikFile
|
||||
if len(p.Directory) > 0 {
|
||||
watchItem = p.Directory
|
||||
} else if len(p.Filename) > 0 {
|
||||
watchItem = p.Filename
|
||||
}
|
||||
|
||||
if _, err := os.Stat(watchItem); err != nil {
|
||||
@@ -136,12 +158,19 @@ func readFile(filename string) (string, error) {
|
||||
return "", fmt.Errorf("invalid filename: %s", filename)
|
||||
}
|
||||
|
||||
func (p *Provider) loadFileConfig(filename string) (*types.Configuration, error) {
|
||||
func (p *Provider) loadFileConfig(filename string, parseTemplate bool) (*types.Configuration, error) {
|
||||
fileContent, err := readFile(filename)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error reading configuration file: %s - %s", filename, err)
|
||||
}
|
||||
configuration, err := p.CreateConfiguration(fileContent, template.FuncMap{}, false)
|
||||
|
||||
var configuration *types.Configuration
|
||||
if parseTemplate {
|
||||
configuration, err = p.CreateConfiguration(fileContent, template.FuncMap{}, false)
|
||||
} else {
|
||||
configuration, err = p.DecodeConfiguration(fileContent)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -182,7 +211,7 @@ func (p *Provider) loadFileConfigFromDirectory(directory string, configuration *
|
||||
}
|
||||
|
||||
var c *types.Configuration
|
||||
c, err = p.loadFileConfig(path.Join(directory, item.Name()))
|
||||
c, err = p.loadFileConfig(path.Join(directory, item.Name()), true)
|
||||
|
||||
if err != nil {
|
||||
return configuration, err
|
||||
|
||||
@@ -14,216 +14,6 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestProvideSingleFileAndWatch(t *testing.T) {
|
||||
tempDir := createTempDir(t, "testfile")
|
||||
defer os.RemoveAll(tempDir)
|
||||
|
||||
expectedNumFrontends := 2
|
||||
expectedNumBackends := 2
|
||||
expectedNumTLSConf := 2
|
||||
|
||||
tempFile := createFile(t,
|
||||
tempDir, "simple.toml",
|
||||
createFrontendConfiguration(expectedNumFrontends),
|
||||
createBackendConfiguration(expectedNumBackends),
|
||||
createTLS(expectedNumTLSConf))
|
||||
|
||||
configurationChan, signal := createConfigurationRoutine(t, &expectedNumFrontends, &expectedNumBackends, &expectedNumTLSConf)
|
||||
|
||||
provide(configurationChan, watch, withFile(tempFile))
|
||||
|
||||
// Wait for initial message to be tested
|
||||
err := waitForSignal(signal, 2*time.Second, "initial config")
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Now test again with single frontend and backend
|
||||
expectedNumFrontends = 1
|
||||
expectedNumBackends = 1
|
||||
expectedNumTLSConf = 1
|
||||
|
||||
createFile(t,
|
||||
tempDir, "simple.toml",
|
||||
createFrontendConfiguration(expectedNumFrontends),
|
||||
createBackendConfiguration(expectedNumBackends),
|
||||
createTLS(expectedNumTLSConf))
|
||||
|
||||
err = waitForSignal(signal, 2*time.Second, "single frontend, backend, TLS configuration")
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestProvideSingleFileAndNotWatch(t *testing.T) {
|
||||
tempDir := createTempDir(t, "testfile")
|
||||
defer os.RemoveAll(tempDir)
|
||||
|
||||
expectedNumFrontends := 2
|
||||
expectedNumBackends := 2
|
||||
expectedNumTLSConf := 2
|
||||
|
||||
tempFile := createFile(t,
|
||||
tempDir, "simple.toml",
|
||||
createFrontendConfiguration(expectedNumFrontends),
|
||||
createBackendConfiguration(expectedNumBackends),
|
||||
createTLS(expectedNumTLSConf))
|
||||
|
||||
configurationChan, signal := createConfigurationRoutine(t, &expectedNumFrontends, &expectedNumBackends, &expectedNumTLSConf)
|
||||
|
||||
provide(configurationChan, withFile(tempFile))
|
||||
|
||||
// Wait for initial message to be tested
|
||||
err := waitForSignal(signal, 2*time.Second, "initial config")
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Now test again with single frontend and backend
|
||||
expectedNumFrontends = 1
|
||||
expectedNumBackends = 1
|
||||
expectedNumTLSConf = 1
|
||||
|
||||
createFile(t,
|
||||
tempDir, "simple.toml",
|
||||
createFrontendConfiguration(expectedNumFrontends),
|
||||
createBackendConfiguration(expectedNumBackends),
|
||||
createTLS(expectedNumTLSConf))
|
||||
|
||||
// Must fail because we don't watch the changes
|
||||
err = waitForSignal(signal, 2*time.Second, "single frontend, backend and TLS configuration")
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestProvideDirectoryAndWatch(t *testing.T) {
|
||||
tempDir := createTempDir(t, "testdir")
|
||||
defer os.RemoveAll(tempDir)
|
||||
|
||||
expectedNumFrontends := 2
|
||||
expectedNumBackends := 2
|
||||
expectedNumTLSConf := 2
|
||||
|
||||
tempFile1 := createRandomFile(t, tempDir, createFrontendConfiguration(expectedNumFrontends))
|
||||
tempFile2 := createRandomFile(t, tempDir, createBackendConfiguration(expectedNumBackends))
|
||||
tempFile3 := createRandomFile(t, tempDir, createTLS(expectedNumTLSConf))
|
||||
|
||||
configurationChan, signal := createConfigurationRoutine(t, &expectedNumFrontends, &expectedNumBackends, &expectedNumTLSConf)
|
||||
|
||||
provide(configurationChan, watch, withDirectory(tempDir))
|
||||
|
||||
// Wait for initial config message to be tested
|
||||
err := waitForSignal(signal, 2*time.Second, "initial config")
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Now remove the backends file
|
||||
expectedNumFrontends = 2
|
||||
expectedNumBackends = 0
|
||||
expectedNumTLSConf = 2
|
||||
os.Remove(tempFile2.Name())
|
||||
err = waitForSignal(signal, 2*time.Second, "remove the backends file")
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Now remove the frontends file
|
||||
expectedNumFrontends = 0
|
||||
expectedNumBackends = 0
|
||||
expectedNumTLSConf = 2
|
||||
os.Remove(tempFile1.Name())
|
||||
err = waitForSignal(signal, 2*time.Second, "remove the frontends file")
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Now remove the TLS configuration file
|
||||
expectedNumFrontends = 0
|
||||
expectedNumBackends = 0
|
||||
expectedNumTLSConf = 0
|
||||
os.Remove(tempFile3.Name())
|
||||
err = waitForSignal(signal, 2*time.Second, "remove the TLS configuration file")
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestProvideDirectoryAndNotWatch(t *testing.T) {
|
||||
tempDir := createTempDir(t, "testdir")
|
||||
tempTLSDir := createSubDir(t, tempDir, "tls")
|
||||
defer os.RemoveAll(tempDir)
|
||||
|
||||
expectedNumFrontends := 2
|
||||
expectedNumBackends := 2
|
||||
expectedNumTLSConf := 2
|
||||
|
||||
createRandomFile(t, tempDir, createFrontendConfiguration(expectedNumFrontends))
|
||||
tempFile2 := createRandomFile(t, tempDir, createBackendConfiguration(expectedNumBackends))
|
||||
createRandomFile(t, tempTLSDir, createTLS(expectedNumTLSConf))
|
||||
|
||||
configurationChan, signal := createConfigurationRoutine(t, &expectedNumFrontends, &expectedNumBackends, &expectedNumTLSConf)
|
||||
|
||||
provide(configurationChan, withDirectory(tempDir))
|
||||
|
||||
// Wait for initial config message to be tested
|
||||
err := waitForSignal(signal, 2*time.Second, "initial config")
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Now remove the backends file
|
||||
expectedNumFrontends = 2
|
||||
expectedNumBackends = 0
|
||||
expectedNumTLSConf = 2
|
||||
os.Remove(tempFile2.Name())
|
||||
|
||||
// Must fail because we don't watch the changes
|
||||
err = waitForSignal(signal, 2*time.Second, "remove the backends file")
|
||||
assert.Error(t, err)
|
||||
|
||||
}
|
||||
|
||||
func createConfigurationRoutine(t *testing.T, expectedNumFrontends *int, expectedNumBackends *int, expectedNumTLSes *int) (chan types.ConfigMessage, chan interface{}) {
|
||||
configurationChan := make(chan types.ConfigMessage)
|
||||
signal := make(chan interface{})
|
||||
|
||||
safe.Go(func() {
|
||||
for {
|
||||
data := <-configurationChan
|
||||
assert.Equal(t, "file", data.ProviderName)
|
||||
assert.Len(t, data.Configuration.Frontends, *expectedNumFrontends)
|
||||
assert.Len(t, data.Configuration.Backends, *expectedNumBackends)
|
||||
assert.Len(t, data.Configuration.TLS, *expectedNumTLSes)
|
||||
signal <- nil
|
||||
}
|
||||
})
|
||||
|
||||
return configurationChan, signal
|
||||
}
|
||||
|
||||
func waitForSignal(signal chan interface{}, timeout time.Duration, caseName string) error {
|
||||
timer := time.NewTimer(timeout)
|
||||
defer timer.Stop()
|
||||
|
||||
select {
|
||||
case <-signal:
|
||||
|
||||
case <-timer.C:
|
||||
return fmt.Errorf("Timed out waiting for assertions to be tested: %s", caseName)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func provide(configurationChan chan types.ConfigMessage, builders ...func(p *Provider)) {
|
||||
pvd := &Provider{}
|
||||
|
||||
for _, builder := range builders {
|
||||
builder(pvd)
|
||||
}
|
||||
|
||||
pvd.Provide(configurationChan, safe.NewPool(context.Background()), nil)
|
||||
}
|
||||
|
||||
func watch(pvd *Provider) {
|
||||
pvd.Watch = true
|
||||
}
|
||||
|
||||
func withDirectory(name string) func(*Provider) {
|
||||
return func(pvd *Provider) {
|
||||
pvd.Directory = name
|
||||
}
|
||||
}
|
||||
|
||||
func withFile(tempFile *os.File) func(*Provider) {
|
||||
return func(p *Provider) {
|
||||
p.Filename = tempFile.Name()
|
||||
}
|
||||
}
|
||||
|
||||
// createRandomFile Helper
|
||||
func createRandomFile(t *testing.T, tempDir string, contents ...string) *os.File {
|
||||
return createFile(t, tempDir, fmt.Sprintf("temp%d.toml", time.Now().UnixNano()), contents...)
|
||||
@@ -264,25 +54,12 @@ func createTempDir(t *testing.T, dir string) string {
|
||||
return d
|
||||
}
|
||||
|
||||
// createDir Helper
|
||||
func createSubDir(t *testing.T, rootDir, dir string) string {
|
||||
t.Helper()
|
||||
err := os.Mkdir(rootDir+"/"+dir, 0775)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return rootDir + "/" + dir
|
||||
}
|
||||
|
||||
// createFrontendConfiguration Helper
|
||||
func createFrontendConfiguration(n int) string {
|
||||
conf := "{{$home := env \"HOME\"}}\n[frontends]\n"
|
||||
conf := "[frontends]\n"
|
||||
for i := 1; i <= n; i++ {
|
||||
conf += fmt.Sprintf(` [frontends."frontend%[1]d"]
|
||||
backend = "backend%[1]d"
|
||||
`, i)
|
||||
conf += fmt.Sprintf(` [frontends."frontend%[1]d".headers]
|
||||
"PublicKey" = "{{$home}}/pub.key"
|
||||
`, i)
|
||||
}
|
||||
return conf
|
||||
@@ -313,3 +90,240 @@ func createTLS(n int) string {
|
||||
}
|
||||
return conf
|
||||
}
|
||||
|
||||
type ProvideTestCase struct {
|
||||
desc string
|
||||
directoryContent []string
|
||||
fileContent string
|
||||
traefikFileContent string
|
||||
expectedNumFrontend int
|
||||
expectedNumBackend int
|
||||
expectedNumTLSConf int
|
||||
}
|
||||
|
||||
func getTestCases() []ProvideTestCase {
|
||||
return []ProvideTestCase{
|
||||
{
|
||||
desc: "simple file",
|
||||
fileContent: createFrontendConfiguration(2) + createBackendConfiguration(3) + createTLS(4),
|
||||
expectedNumFrontend: 2,
|
||||
expectedNumBackend: 3,
|
||||
expectedNumTLSConf: 4,
|
||||
},
|
||||
{
|
||||
desc: "simple file and a traefik file",
|
||||
fileContent: createFrontendConfiguration(2) + createBackendConfiguration(3) + createTLS(4),
|
||||
traefikFileContent: `
|
||||
debug=true
|
||||
`,
|
||||
expectedNumFrontend: 2,
|
||||
expectedNumBackend: 3,
|
||||
expectedNumTLSConf: 4,
|
||||
},
|
||||
{
|
||||
desc: "template file",
|
||||
fileContent: `
|
||||
[frontends]
|
||||
{{ range $i, $e := until 20 }}
|
||||
[frontends.frontend{{ $e }}]
|
||||
backend = "backend"
|
||||
{{ end }}
|
||||
`,
|
||||
expectedNumFrontend: 20,
|
||||
},
|
||||
{
|
||||
desc: "simple directory",
|
||||
directoryContent: []string{
|
||||
createFrontendConfiguration(2),
|
||||
createBackendConfiguration(3),
|
||||
createTLS(4),
|
||||
},
|
||||
expectedNumFrontend: 2,
|
||||
expectedNumBackend: 3,
|
||||
expectedNumTLSConf: 4,
|
||||
},
|
||||
{
|
||||
desc: "template in directory",
|
||||
directoryContent: []string{
|
||||
`
|
||||
[frontends]
|
||||
{{ range $i, $e := until 20 }}
|
||||
[frontends.frontend{{ $e }}]
|
||||
backend = "backend"
|
||||
{{ end }}
|
||||
`,
|
||||
`
|
||||
[backends]
|
||||
{{ range $i, $e := until 20 }}
|
||||
[backends.backend{{ $e }}]
|
||||
[backends.backend{{ $e }}.servers.server1]
|
||||
url="http://127.0.0.1"
|
||||
{{ end }}
|
||||
`,
|
||||
},
|
||||
expectedNumFrontend: 20,
|
||||
expectedNumBackend: 20,
|
||||
},
|
||||
{
|
||||
desc: "simple traefik file",
|
||||
traefikFileContent: `
|
||||
debug=true
|
||||
[file]
|
||||
` + createFrontendConfiguration(2) + createBackendConfiguration(3) + createTLS(4),
|
||||
expectedNumFrontend: 2,
|
||||
expectedNumBackend: 3,
|
||||
expectedNumTLSConf: 4,
|
||||
},
|
||||
{
|
||||
desc: "simple traefik file with templating",
|
||||
traefikFileContent: `
|
||||
temp="{{ getTag \"test\" }}"
|
||||
[file]
|
||||
` + createFrontendConfiguration(2) + createBackendConfiguration(3) + createTLS(4),
|
||||
expectedNumFrontend: 2,
|
||||
expectedNumBackend: 3,
|
||||
expectedNumTLSConf: 4,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func TestProvideWithoutWatch(t *testing.T) {
|
||||
for _, test := range getTestCases() {
|
||||
test := test
|
||||
t.Run(test.desc+" without watch", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
provider, clean := createProvider(t, test, false)
|
||||
defer clean()
|
||||
configChan := make(chan types.ConfigMessage)
|
||||
|
||||
go func() {
|
||||
err := provider.Provide(configChan, safe.NewPool(context.Background()), types.Constraints{})
|
||||
assert.NoError(t, err)
|
||||
}()
|
||||
|
||||
timeout := time.After(time.Second)
|
||||
select {
|
||||
case config := <-configChan:
|
||||
assert.Len(t, config.Configuration.Backends, test.expectedNumBackend)
|
||||
assert.Len(t, config.Configuration.Frontends, test.expectedNumFrontend)
|
||||
assert.Len(t, config.Configuration.TLS, test.expectedNumTLSConf)
|
||||
case <-timeout:
|
||||
t.Errorf("timeout while waiting for config")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestProvideWithWatch(t *testing.T) {
|
||||
for _, test := range getTestCases() {
|
||||
test := test
|
||||
t.Run(test.desc+" with watch", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
provider, clean := createProvider(t, test, true)
|
||||
defer clean()
|
||||
configChan := make(chan types.ConfigMessage)
|
||||
|
||||
go func() {
|
||||
err := provider.Provide(configChan, safe.NewPool(context.Background()), types.Constraints{})
|
||||
assert.NoError(t, err)
|
||||
}()
|
||||
|
||||
timeout := time.After(time.Second)
|
||||
select {
|
||||
case config := <-configChan:
|
||||
assert.Len(t, config.Configuration.Backends, 0)
|
||||
assert.Len(t, config.Configuration.Frontends, 0)
|
||||
assert.Len(t, config.Configuration.TLS, 0)
|
||||
case <-timeout:
|
||||
t.Errorf("timeout while waiting for config")
|
||||
}
|
||||
|
||||
if len(test.fileContent) > 0 {
|
||||
ioutil.WriteFile(provider.Filename, []byte(test.fileContent), 0755)
|
||||
}
|
||||
|
||||
if len(test.traefikFileContent) > 0 {
|
||||
ioutil.WriteFile(provider.TraefikFile, []byte(test.traefikFileContent), 0755)
|
||||
}
|
||||
|
||||
if len(test.directoryContent) > 0 {
|
||||
for _, fileContent := range test.directoryContent {
|
||||
createRandomFile(t, provider.Directory, fileContent)
|
||||
}
|
||||
}
|
||||
|
||||
timeout = time.After(time.Second * 1)
|
||||
success := false
|
||||
for !success {
|
||||
select {
|
||||
case config := <-configChan:
|
||||
success = assert.Len(t, config.Configuration.Backends, test.expectedNumBackend)
|
||||
success = success && assert.Len(t, config.Configuration.Frontends, test.expectedNumFrontend)
|
||||
success = success && assert.Len(t, config.Configuration.TLS, test.expectedNumTLSConf)
|
||||
case <-timeout:
|
||||
t.Errorf("timeout while waiting for config")
|
||||
return
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestErrorWhenEmptyConfig(t *testing.T) {
|
||||
provider := &Provider{}
|
||||
configChan := make(chan types.ConfigMessage)
|
||||
errorChan := make(chan struct{})
|
||||
go func() {
|
||||
err := provider.Provide(configChan, safe.NewPool(context.Background()), types.Constraints{})
|
||||
assert.Error(t, err)
|
||||
close(errorChan)
|
||||
}()
|
||||
|
||||
timeout := time.After(time.Second)
|
||||
select {
|
||||
case <-configChan:
|
||||
t.Fatal("We should not receive config message")
|
||||
case <-timeout:
|
||||
t.Fatal("timeout while waiting for config")
|
||||
case <-errorChan:
|
||||
}
|
||||
}
|
||||
|
||||
func createProvider(t *testing.T, test ProvideTestCase, watch bool) (*Provider, func()) {
|
||||
tempDir := createTempDir(t, "testdir")
|
||||
|
||||
provider := &Provider{}
|
||||
provider.Watch = watch
|
||||
|
||||
if len(test.directoryContent) > 0 {
|
||||
if !watch {
|
||||
for _, fileContent := range test.directoryContent {
|
||||
createRandomFile(t, tempDir, fileContent)
|
||||
}
|
||||
}
|
||||
provider.Directory = tempDir
|
||||
}
|
||||
|
||||
if len(test.fileContent) > 0 {
|
||||
if watch {
|
||||
test.fileContent = ""
|
||||
}
|
||||
filename := createRandomFile(t, tempDir, test.fileContent)
|
||||
provider.Filename = filename.Name()
|
||||
|
||||
}
|
||||
|
||||
if len(test.traefikFileContent) > 0 {
|
||||
if watch {
|
||||
test.traefikFileContent = ""
|
||||
}
|
||||
filename := createRandomFile(t, tempDir, test.traefikFileContent)
|
||||
provider.TraefikFile = filename.Name()
|
||||
}
|
||||
|
||||
return provider, func() {
|
||||
os.Remove(tempDir)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,11 +27,6 @@ const (
|
||||
DefaultBackendMaxconnExtractorFunc = "request.host"
|
||||
DefaultBackendLoadbalancerStickinessCookieName = ""
|
||||
DefaultBackendHealthCheckPort = 0
|
||||
|
||||
// TODO need to be remove in extra-service-fabric
|
||||
DefaultWeightInt = 1 // Deprecated
|
||||
DefaultPassHostHeaderBool = true // Deprecated
|
||||
DefaultFrontendPriorityInt = 0 // Deprecated
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -64,6 +59,7 @@ func GetBoolValue(labels map[string]string, labelName string, defaultValue bool)
|
||||
if err == nil {
|
||||
return v
|
||||
}
|
||||
log.Errorf("Unable to parse %q: %q, falling back to %v. %v", labelName, rawValue, defaultValue, err)
|
||||
}
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
@@ -32,7 +32,6 @@ const (
|
||||
SuffixBackendBufferingRetryExpression = SuffixBackendBuffering + ".retryExpression"
|
||||
SuffixFrontend = "frontend"
|
||||
SuffixFrontendAuthBasic = "frontend.auth.basic"
|
||||
SuffixFrontendBackend = "frontend.backend"
|
||||
SuffixFrontendEntryPoints = "frontend.entryPoints"
|
||||
SuffixFrontendHeaders = "frontend.headers."
|
||||
SuffixFrontendRequestHeaders = SuffixFrontendHeaders + "customRequestHeaders"
|
||||
@@ -98,7 +97,6 @@ const (
|
||||
TraefikBackendBufferingRetryExpression = Prefix + SuffixBackendBufferingRetryExpression
|
||||
TraefikFrontend = Prefix + SuffixFrontend
|
||||
TraefikFrontendAuthBasic = Prefix + SuffixFrontendAuthBasic
|
||||
TraefikFrontendBackend = Prefix + SuffixFrontendBackend
|
||||
TraefikFrontendEntryPoints = Prefix + SuffixFrontendEntryPoints
|
||||
TraefikFrontendPassHostHeader = Prefix + SuffixFrontendPassHostHeader
|
||||
TraefikFrontendPassTLSCert = Prefix + SuffixFrontendPassTLSCert
|
||||
|
||||
@@ -62,8 +62,6 @@ func (p *BaseProvider) GetConfiguration(defaultTemplate string, funcMap template
|
||||
|
||||
// CreateConfiguration create a provider configuration from content using templating
|
||||
func (p *BaseProvider) CreateConfiguration(tmplContent string, funcMap template.FuncMap, templateObjects interface{}) (*types.Configuration, error) {
|
||||
configuration := new(types.Configuration)
|
||||
|
||||
var defaultFuncMap = sprig.TxtFuncMap()
|
||||
// tolower is deprecated in favor of sprig's lower function
|
||||
defaultFuncMap["tolower"] = strings.ToLower
|
||||
@@ -91,7 +89,13 @@ func (p *BaseProvider) CreateConfiguration(tmplContent string, funcMap template.
|
||||
log.Debugf("Template content: %s", tmplContent)
|
||||
log.Debugf("Rendering results: %s", renderedTemplate)
|
||||
}
|
||||
if _, err := toml.Decode(renderedTemplate, configuration); err != nil {
|
||||
return p.DecodeConfiguration(renderedTemplate)
|
||||
}
|
||||
|
||||
// DecodeConfiguration Decode a *types.Configuration from a content
|
||||
func (p *BaseProvider) DecodeConfiguration(content string) (*types.Configuration, error) {
|
||||
configuration := new(types.Configuration)
|
||||
if _, err := toml.Decode(content, configuration); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return configuration, nil
|
||||
|
||||
@@ -150,7 +150,7 @@ func getBackendName(service rancherData) string {
|
||||
}
|
||||
|
||||
func getSegmentBackendName(service rancherData) string {
|
||||
if value := label.GetStringValue(service.SegmentLabels, label.TraefikFrontendBackend, ""); len(value) > 0 {
|
||||
if value := label.GetStringValue(service.SegmentLabels, label.TraefikBackend, ""); len(value) > 0 {
|
||||
return provider.Normalize(service.Name + "-" + value)
|
||||
}
|
||||
|
||||
|
||||
@@ -1438,7 +1438,7 @@ func configureBackends(backends map[string]*types.Backend) {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
log.Debugf("Validation of load balancer method for backend %s failed: %s. Using default method wrr.", backendName, err)
|
||||
log.Debugf("Backend %s: %v", backendName, err)
|
||||
|
||||
var stickiness *types.Stickiness
|
||||
if backend.LoadBalancer != nil {
|
||||
|
||||
@@ -69,6 +69,7 @@
|
||||
entryPoint = "{{ $frontend.Redirect.EntryPoint }}"
|
||||
regex = "{{ $frontend.Redirect.Regex }}"
|
||||
replacement = "{{ $frontend.Redirect.Replacement }}"
|
||||
permanent = {{ $frontend.Redirect.Permanent }}
|
||||
{{end}}
|
||||
|
||||
{{if $frontend.Errors }}
|
||||
|
||||
@@ -211,16 +211,21 @@ var loadBalancerMethodNames = []string{
|
||||
|
||||
// NewLoadBalancerMethod create a new LoadBalancerMethod from a given LoadBalancer.
|
||||
func NewLoadBalancerMethod(loadBalancer *LoadBalancer) (LoadBalancerMethod, error) {
|
||||
var method string
|
||||
if loadBalancer != nil {
|
||||
method = loadBalancer.Method
|
||||
for i, name := range loadBalancerMethodNames {
|
||||
if strings.EqualFold(name, method) {
|
||||
return LoadBalancerMethod(i), nil
|
||||
}
|
||||
if loadBalancer == nil {
|
||||
return Wrr, errors.New("no load-balancer defined, fallback to 'wrr' method")
|
||||
}
|
||||
|
||||
if len(loadBalancer.Method) == 0 {
|
||||
return Wrr, errors.New("no load-balancing method defined, fallback to 'wrr' method")
|
||||
}
|
||||
|
||||
for i, name := range loadBalancerMethodNames {
|
||||
if strings.EqualFold(name, loadBalancer.Method) {
|
||||
return LoadBalancerMethod(i), nil
|
||||
}
|
||||
}
|
||||
return Wrr, fmt.Errorf("invalid load-balancing method '%s'", method)
|
||||
|
||||
return Wrr, fmt.Errorf("invalid load-balancing method %q, fallback to 'wrr' method", loadBalancer.Method)
|
||||
}
|
||||
|
||||
// Configurations is for currentConfigurations Map
|
||||
|
||||
102
vendor/github.com/xenolf/lego/acmev2/client.go
generated
vendored
102
vendor/github.com/xenolf/lego/acmev2/client.go
generated
vendored
@@ -189,7 +189,7 @@ func (c *Client) ResolveAccountByKey() (*RegistrationResource, error) {
|
||||
logf("[INFO] acme: Trying to resolve account by key")
|
||||
|
||||
acc := accountMessage{OnlyReturnExisting: true}
|
||||
hdr, err := postJSON(c.jws, c.directory.NewAccountURL, acc, &acc)
|
||||
hdr, err := postJSON(c.jws, c.directory.NewAccountURL, acc, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -265,7 +265,7 @@ func (c *Client) QueryRegistration() (*RegistrationResource, error) {
|
||||
// your issued certificate as a bundle.
|
||||
// This function will never return a partial certificate. If one domain in the list fails,
|
||||
// the whole certificate will fail.
|
||||
func (c *Client) ObtainCertificateForCSR(csr x509.CertificateRequest, bundle bool) (CertificateResource, map[string]error) {
|
||||
func (c *Client) ObtainCertificateForCSR(csr x509.CertificateRequest, bundle bool) (CertificateResource, error) {
|
||||
// figure out what domains it concerns
|
||||
// start with the common name
|
||||
domains := []string{csr.Subject.CommonName}
|
||||
@@ -292,30 +292,26 @@ DNSNames:
|
||||
|
||||
order, err := c.createOrderForIdentifiers(domains)
|
||||
if err != nil {
|
||||
identErrors := make(map[string]error)
|
||||
for _, auth := range order.Identifiers {
|
||||
identErrors[auth.Value] = err
|
||||
}
|
||||
return CertificateResource{}, identErrors
|
||||
return CertificateResource{}, err
|
||||
}
|
||||
authz, failures := c.getAuthzForOrder(order)
|
||||
// If any challenge fails - return. Do not generate partial SAN certificates.
|
||||
if len(failures) > 0 {
|
||||
authz, err := c.getAuthzForOrder(order)
|
||||
if err != nil {
|
||||
// If any challenge fails, return. Do not generate partial SAN certificates.
|
||||
/*for _, auth := range authz {
|
||||
c.disableAuthz(auth)
|
||||
}*/
|
||||
|
||||
return CertificateResource{}, failures
|
||||
return CertificateResource{}, err
|
||||
}
|
||||
|
||||
errs := c.solveChallengeForAuthz(authz)
|
||||
// If any challenge fails - return. Do not generate partial SAN certificates.
|
||||
if len(errs) > 0 {
|
||||
return CertificateResource{}, errs
|
||||
err = c.solveChallengeForAuthz(authz)
|
||||
if err != nil {
|
||||
// If any challenge fails, return. Do not generate partial SAN certificates.
|
||||
return CertificateResource{}, err
|
||||
}
|
||||
|
||||
logf("[INFO][%s] acme: Validations succeeded; requesting certificates", strings.Join(domains, ", "))
|
||||
|
||||
failures := make(ObtainError)
|
||||
cert, err := c.requestCertificateForCsr(order, bundle, csr.Raw, nil)
|
||||
if err != nil {
|
||||
for _, chln := range authz {
|
||||
@@ -326,7 +322,12 @@ DNSNames:
|
||||
// Add the CSR to the certificate so that it can be used for renewals.
|
||||
cert.CSR = pemEncode(&csr)
|
||||
|
||||
return cert, failures
|
||||
// do not return an empty failures map, because
|
||||
// it would still be a non-nil error value
|
||||
if len(failures) > 0 {
|
||||
return cert, failures
|
||||
}
|
||||
return cert, nil
|
||||
}
|
||||
|
||||
// ObtainCertificate tries to obtain a single certificate using all domains passed into it.
|
||||
@@ -338,7 +339,11 @@ DNSNames:
|
||||
// your issued certificate as a bundle.
|
||||
// This function will never return a partial certificate. If one domain in the list fails,
|
||||
// the whole certificate will fail.
|
||||
func (c *Client) ObtainCertificate(domains []string, bundle bool, privKey crypto.PrivateKey, mustStaple bool) (CertificateResource, map[string]error) {
|
||||
func (c *Client) ObtainCertificate(domains []string, bundle bool, privKey crypto.PrivateKey, mustStaple bool) (CertificateResource, error) {
|
||||
if len(domains) == 0 {
|
||||
return CertificateResource{}, errors.New("No domains to obtain a certificate for")
|
||||
}
|
||||
|
||||
if bundle {
|
||||
logf("[INFO][%s] acme: Obtaining bundled SAN certificate", strings.Join(domains, ", "))
|
||||
} else {
|
||||
@@ -347,30 +352,26 @@ func (c *Client) ObtainCertificate(domains []string, bundle bool, privKey crypto
|
||||
|
||||
order, err := c.createOrderForIdentifiers(domains)
|
||||
if err != nil {
|
||||
identErrors := make(map[string]error)
|
||||
for _, auth := range order.Identifiers {
|
||||
identErrors[auth.Value] = err
|
||||
}
|
||||
return CertificateResource{}, identErrors
|
||||
return CertificateResource{}, err
|
||||
}
|
||||
authz, failures := c.getAuthzForOrder(order)
|
||||
// If any challenge fails - return. Do not generate partial SAN certificates.
|
||||
if len(failures) > 0 {
|
||||
authz, err := c.getAuthzForOrder(order)
|
||||
if err != nil {
|
||||
// If any challenge fails, return. Do not generate partial SAN certificates.
|
||||
/*for _, auth := range authz {
|
||||
c.disableAuthz(auth)
|
||||
}*/
|
||||
|
||||
return CertificateResource{}, failures
|
||||
return CertificateResource{}, err
|
||||
}
|
||||
|
||||
errs := c.solveChallengeForAuthz(authz)
|
||||
// If any challenge fails - return. Do not generate partial SAN certificates.
|
||||
if len(errs) > 0 {
|
||||
return CertificateResource{}, errs
|
||||
err = c.solveChallengeForAuthz(authz)
|
||||
if err != nil {
|
||||
// If any challenge fails, return. Do not generate partial SAN certificates.
|
||||
return CertificateResource{}, err
|
||||
}
|
||||
|
||||
logf("[INFO][%s] acme: Validations succeeded; requesting certificates", strings.Join(domains, ", "))
|
||||
|
||||
failures := make(ObtainError)
|
||||
cert, err := c.requestCertificateForOrder(order, bundle, privKey, mustStaple)
|
||||
if err != nil {
|
||||
for _, auth := range authz {
|
||||
@@ -378,7 +379,12 @@ func (c *Client) ObtainCertificate(domains []string, bundle bool, privKey crypto
|
||||
}
|
||||
}
|
||||
|
||||
return cert, failures
|
||||
// do not return an empty failures map, because
|
||||
// it would still be a non-nil error value
|
||||
if len(failures) > 0 {
|
||||
return cert, failures
|
||||
}
|
||||
return cert, nil
|
||||
}
|
||||
|
||||
// RevokeCertificate takes a PEM encoded certificate or bundle and tries to revoke it at the CA.
|
||||
@@ -433,7 +439,7 @@ func (c *Client) RenewCertificate(cert CertificateResource, bundle, mustStaple b
|
||||
return CertificateResource{}, err
|
||||
}
|
||||
newCert, failures := c.ObtainCertificateForCSR(*csr, bundle)
|
||||
return newCert, failures[cert.Domain]
|
||||
return newCert, failures
|
||||
}
|
||||
|
||||
var privKey crypto.PrivateKey
|
||||
@@ -445,7 +451,6 @@ func (c *Client) RenewCertificate(cert CertificateResource, bundle, mustStaple b
|
||||
}
|
||||
|
||||
var domains []string
|
||||
var failures map[string]error
|
||||
// check for SAN certificate
|
||||
if len(x509Cert.DNSNames) > 1 {
|
||||
domains = append(domains, x509Cert.Subject.CommonName)
|
||||
@@ -459,8 +464,8 @@ func (c *Client) RenewCertificate(cert CertificateResource, bundle, mustStaple b
|
||||
domains = append(domains, x509Cert.Subject.CommonName)
|
||||
}
|
||||
|
||||
newCert, failures := c.ObtainCertificate(domains, bundle, privKey, mustStaple)
|
||||
return newCert, failures[cert.Domain]
|
||||
newCert, err := c.ObtainCertificate(domains, bundle, privKey, mustStaple)
|
||||
return newCert, err
|
||||
}
|
||||
|
||||
func (c *Client) createOrderForIdentifiers(domains []string) (orderResource, error) {
|
||||
@@ -490,9 +495,10 @@ func (c *Client) createOrderForIdentifiers(domains []string) (orderResource, err
|
||||
|
||||
// Looks through the challenge combinations to find a solvable match.
|
||||
// Then solves the challenges in series and returns.
|
||||
func (c *Client) solveChallengeForAuthz(authorizations []authorization) map[string]error {
|
||||
func (c *Client) solveChallengeForAuthz(authorizations []authorization) error {
|
||||
failures := make(ObtainError)
|
||||
|
||||
// loop through the resources, basically through the domains.
|
||||
failures := make(map[string]error)
|
||||
for _, authz := range authorizations {
|
||||
if authz.Status == "valid" {
|
||||
// Boulder might recycle recent validated authz (see issue #267)
|
||||
@@ -513,7 +519,12 @@ func (c *Client) solveChallengeForAuthz(authorizations []authorization) map[stri
|
||||
}
|
||||
}
|
||||
|
||||
return failures
|
||||
// be careful not to return an empty failures map, for
|
||||
// even an empty ObtainError is a non-nil error value
|
||||
if len(failures) > 0 {
|
||||
return failures
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Checks all challenges from the server in order and returns the first matching solver.
|
||||
@@ -528,7 +539,7 @@ func (c *Client) chooseSolver(auth authorization, domain string) (int, solver) {
|
||||
}
|
||||
|
||||
// Get the challenges needed to proof our identifier to the ACME server.
|
||||
func (c *Client) getAuthzForOrder(order orderResource) ([]authorization, map[string]error) {
|
||||
func (c *Client) getAuthzForOrder(order orderResource) ([]authorization, error) {
|
||||
resc, errc := make(chan authorization), make(chan domainError)
|
||||
|
||||
delay := time.Second / overallRequestLimit
|
||||
@@ -549,7 +560,7 @@ func (c *Client) getAuthzForOrder(order orderResource) ([]authorization, map[str
|
||||
}
|
||||
|
||||
var responses []authorization
|
||||
failures := make(map[string]error)
|
||||
failures := make(ObtainError)
|
||||
for i := 0; i < len(order.Authorizations); i++ {
|
||||
select {
|
||||
case res := <-resc:
|
||||
@@ -564,7 +575,12 @@ func (c *Client) getAuthzForOrder(order orderResource) ([]authorization, map[str
|
||||
close(resc)
|
||||
close(errc)
|
||||
|
||||
return responses, failures
|
||||
// be careful to not return an empty failures map;
|
||||
// even if empty, they become non-nil error values
|
||||
if len(failures) > 0 {
|
||||
return responses, failures
|
||||
}
|
||||
return responses, nil
|
||||
}
|
||||
|
||||
func logAuthz(order orderResource) {
|
||||
|
||||
13
vendor/github.com/xenolf/lego/acmev2/error.go
generated
vendored
13
vendor/github.com/xenolf/lego/acmev2/error.go
generated
vendored
@@ -1,6 +1,7 @@
|
||||
package acmev2
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
@@ -13,6 +14,18 @@ const (
|
||||
invalidNonceError = "urn:ietf:params:acme:error:badNonce"
|
||||
)
|
||||
|
||||
// ObtainError is returned when there are specific errors available
|
||||
// per domain. For example in ObtainCertificate
|
||||
type ObtainError map[string]error
|
||||
|
||||
func (e ObtainError) Error() string {
|
||||
buffer := bytes.NewBufferString("acme: Error -> One or more domains had a problem:\n")
|
||||
for dom, err := range e {
|
||||
buffer.WriteString(fmt.Sprintf("[%s] %s\n", dom, err))
|
||||
}
|
||||
return buffer.String()
|
||||
}
|
||||
|
||||
// RemoteError is the base type for all errors specific to the ACME protocol.
|
||||
type RemoteError struct {
|
||||
StatusCode int `json:"status,omitempty"`
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
"root": "src",
|
||||
"outDir": "dist",
|
||||
"assets": [
|
||||
"assets",
|
||||
"assets/images",
|
||||
"favicon.ico"
|
||||
],
|
||||
"index": "index.html",
|
||||
@@ -19,7 +19,7 @@
|
||||
"testTsconfig": "tsconfig.spec.json",
|
||||
"prefix": "app",
|
||||
"styles": [
|
||||
"styles/app.sass"
|
||||
"app.sass"
|
||||
],
|
||||
"scripts": [
|
||||
"../node_modules/@fortawesome/fontawesome/index.js",
|
||||
|
||||
@@ -27,9 +27,10 @@
|
||||
"@angular/router": "^5.2.0",
|
||||
"@fortawesome/fontawesome": "^1.1.5",
|
||||
"@fortawesome/fontawesome-free-solid": "^5.0.10",
|
||||
"bulma": "^0.6.2",
|
||||
"bulma": "^0.7.0",
|
||||
"core-js": "^2.4.1",
|
||||
"d3": "^4.13.0",
|
||||
"d3-format": "^1.3.0",
|
||||
"date-fns": "^1.29.0",
|
||||
"lodash": "^4.17.5",
|
||||
"rxjs": "^5.5.6",
|
||||
|
||||
27
webui/src/app.sass
Normal file
27
webui/src/app.sass
Normal file
@@ -0,0 +1,27 @@
|
||||
@charset "utf-8"
|
||||
|
||||
@import 'styles/typography'
|
||||
@import 'styles/variables'
|
||||
@import 'styles/colors'
|
||||
@import '~bulma/sass/utilities/all'
|
||||
@import '~bulma/sass/base/all'
|
||||
@import '~bulma/sass/grid/all'
|
||||
@import '~bulma/sass/elements/container'
|
||||
@import '~bulma/sass/elements/tag'
|
||||
@import '~bulma/sass/elements/other'
|
||||
@import '~bulma/sass/elements/box'
|
||||
@import '~bulma/sass/elements/form'
|
||||
@import '~bulma/sass/elements/table'
|
||||
@import '~bulma/sass/components/navbar'
|
||||
@import '~bulma/sass/components/tabs'
|
||||
@import '~bulma/sass/elements/notification'
|
||||
@import 'styles/nav'
|
||||
@import 'styles/content'
|
||||
@import 'styles/message'
|
||||
@import 'styles/charts'
|
||||
@import 'styles/helper'
|
||||
|
||||
html
|
||||
font-family: $open-sans
|
||||
height: 100%
|
||||
background: $background
|
||||
@@ -1,4 +1,4 @@
|
||||
import { TestBed, async } from '@angular/core/testing';
|
||||
import { async, TestBed } from '@angular/core/testing';
|
||||
import { AppComponent } from './app.component';
|
||||
|
||||
describe('AppComponent', () => {
|
||||
|
||||
@@ -1,18 +1,21 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { RouterModule } from '@angular/router';
|
||||
import { HttpClientModule } from '@angular/common/http';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { RouterModule } from '@angular/router';
|
||||
import { AppComponent } from './app.component';
|
||||
import { BarChartComponent } from './charts/bar-chart/bar-chart.component';
|
||||
import { LineChartComponent } from './charts/line-chart/line-chart.component';
|
||||
import { HeaderComponent } from './components/header/header.component';
|
||||
import { HealthComponent } from './components/health/health.component';
|
||||
import { ProvidersComponent } from './components/providers/providers.component';
|
||||
import { LetDirective } from './directives/let.directive';
|
||||
import { BackendFilterPipe } from './pipes/backend.filter.pipe';
|
||||
import { FrontendFilterPipe } from './pipes/frontend.filter.pipe';
|
||||
import { KeysPipe } from './pipes/keys.pipe';
|
||||
import { ApiService } from './services/api.service';
|
||||
import { WindowService } from './services/window.service';
|
||||
import { AppComponent } from './app.component';
|
||||
import { HeaderComponent } from './components/header/header.component';
|
||||
import { ProvidersComponent } from './components/providers/providers.component';
|
||||
import { HealthComponent } from './components/health/health.component';
|
||||
import { LineChartComponent } from './charts/line-chart/line-chart.component';
|
||||
import { BarChartComponent } from './charts/bar-chart/bar-chart.component';
|
||||
import { KeysPipe } from './pipes/keys.pipe';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
@@ -22,7 +25,10 @@ import { KeysPipe } from './pipes/keys.pipe';
|
||||
HealthComponent,
|
||||
LineChartComponent,
|
||||
BarChartComponent,
|
||||
KeysPipe
|
||||
KeysPipe,
|
||||
FrontendFilterPipe,
|
||||
BackendFilterPipe,
|
||||
LetDirective
|
||||
],
|
||||
imports: [
|
||||
BrowserModule,
|
||||
@@ -30,8 +36,8 @@ import { KeysPipe } from './pipes/keys.pipe';
|
||||
HttpClientModule,
|
||||
FormsModule,
|
||||
RouterModule.forRoot([
|
||||
{ path: '', component: ProvidersComponent, pathMatch: 'full' },
|
||||
{ path: 'status', component: HealthComponent }
|
||||
{path: '', component: ProvidersComponent, pathMatch: 'full'},
|
||||
{path: 'status', component: HealthComponent}
|
||||
])
|
||||
],
|
||||
providers: [
|
||||
|
||||
@@ -1,15 +1,8 @@
|
||||
import { Component, Input, OnInit, ElementRef, OnChanges, SimpleChanges } from '@angular/core';
|
||||
import { Component, ElementRef, Input, OnChanges, OnInit, SimpleChanges } from '@angular/core';
|
||||
import { axisBottom, axisLeft, easeLinear, max, min, scaleBand, scaleLinear, select } from 'd3';
|
||||
import { format } from 'd3-format';
|
||||
import * as _ from 'lodash';
|
||||
import { WindowService } from '../../services/window.service';
|
||||
import {
|
||||
min,
|
||||
max,
|
||||
easeLinear,
|
||||
select,
|
||||
axisLeft,
|
||||
axisBottom,
|
||||
scaleBand,
|
||||
scaleLinear
|
||||
} from 'd3';
|
||||
|
||||
@Component({
|
||||
selector: 'app-bar-chart',
|
||||
@@ -23,12 +16,12 @@ export class BarChartComponent implements OnInit, OnChanges {
|
||||
x: any;
|
||||
y: any;
|
||||
g: any;
|
||||
bars: any;
|
||||
width: number;
|
||||
height: number;
|
||||
margin = { top: 40, right: 40, bottom: 40, left: 40 };
|
||||
margin = {top: 40, right: 40, bottom: 40, left: 40};
|
||||
loading: boolean;
|
||||
data: any[];
|
||||
previousData: any[];
|
||||
|
||||
constructor(public elementRef: ElementRef, public windowService: WindowService) {
|
||||
this.loading = true;
|
||||
@@ -37,7 +30,7 @@ export class BarChartComponent implements OnInit, OnChanges {
|
||||
ngOnInit() {
|
||||
this.barChartEl = this.elementRef.nativeElement.querySelector('.bar-chart');
|
||||
this.setup();
|
||||
setTimeout(() => this.loading = false, 4000);
|
||||
setTimeout(() => this.loading = false, 1000);
|
||||
|
||||
this.windowService.resize.subscribe(w => this.draw());
|
||||
}
|
||||
@@ -47,15 +40,20 @@ export class BarChartComponent implements OnInit, OnChanges {
|
||||
return;
|
||||
}
|
||||
|
||||
this.data = this.value;
|
||||
this.draw();
|
||||
if (!_.isEqual(this.previousData, this.value)) {
|
||||
this.previousData = _.cloneDeep(this.value);
|
||||
this.data = this.value;
|
||||
|
||||
this.draw();
|
||||
}
|
||||
}
|
||||
|
||||
setup(): void {
|
||||
this.width = this.barChartEl.clientWidth - this.margin.left - this.margin.right;
|
||||
this.height = this.barChartEl.clientHeight - this.margin.top - this.margin.bottom;
|
||||
|
||||
this.svg = select(this.barChartEl).append('svg')
|
||||
this.svg = select(this.barChartEl)
|
||||
.append('svg')
|
||||
.attr('width', this.width + this.margin.left + this.margin.right)
|
||||
.attr('height', this.height + this.margin.top + this.margin.bottom);
|
||||
|
||||
@@ -73,11 +71,16 @@ export class BarChartComponent implements OnInit, OnChanges {
|
||||
}
|
||||
|
||||
draw(): void {
|
||||
if (this.barChartEl.clientWidth === 0 || this.barChartEl.clientHeight === 0) {
|
||||
this.previousData = [];
|
||||
} else {
|
||||
this.width = this.barChartEl.clientWidth - this.margin.left - this.margin.right;
|
||||
this.height = this.barChartEl.clientHeight - this.margin.top - this.margin.bottom;
|
||||
}
|
||||
|
||||
this.x.domain(this.data.map((d: any) => d.code));
|
||||
this.y.domain([0, max(this.data, (d: any) => d.count)]);
|
||||
|
||||
this.width = this.barChartEl.clientWidth - this.margin.left - this.margin.right;
|
||||
this.height = this.barChartEl.clientHeight - this.margin.top - this.margin.bottom;
|
||||
|
||||
this.svg
|
||||
.attr('width', this.width + this.margin.left + this.margin.right)
|
||||
@@ -91,19 +94,18 @@ export class BarChartComponent implements OnInit, OnChanges {
|
||||
.call(axisBottom(this.x));
|
||||
|
||||
this.g.select('.axis--y')
|
||||
.call(axisLeft(this.y).tickSize(-this.width));
|
||||
.call(axisLeft(this.y).tickFormat(format('~s')).tickSize(-this.width));
|
||||
|
||||
// Clean previous graph
|
||||
this.g.selectAll('.bar').remove();
|
||||
|
||||
const bars = this.g.selectAll('.bar').data(this.data);
|
||||
|
||||
bars.enter()
|
||||
.append('rect')
|
||||
.attr('class', 'bar')
|
||||
.attr('x', (d: any) => d.code)
|
||||
.attr('y', (d: any) => d.count)
|
||||
.attr('width', this.x.bandwidth())
|
||||
.attr('height', (d: any) => (this.height - this.y(d.count)) < 0 ? 0 : this.height - this.y(d.count));
|
||||
|
||||
bars.attr('x', (d: any) => this.x(d.code))
|
||||
.style('fill', (d: any) => 'hsl(' + Math.floor(((d.code - 100) * 310 / 427) + 50) + ', 50%, 50%)')
|
||||
.attr('x', (d: any) => this.x(d.code))
|
||||
.attr('y', (d: any) => this.y(d.count))
|
||||
.attr('width', this.x.bandwidth())
|
||||
.attr('height', (d: any) => (this.height - this.y(d.count)) < 0 ? 0 : this.height - this.y(d.count));
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<div class="line-chart" [class.is-hidden]="loading"></div>
|
||||
<div class="loading-text" [class.is-hidden]="!loading">
|
||||
<div class="loading-text line-chart-loading" [class.is-hidden]="!loading">
|
||||
<span>
|
||||
<span>Loading, please wait...</span>
|
||||
<img src="./assets/images/loader.svg" class="main-loader">
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
import { Component, Input, OnInit, ElementRef, OnChanges, SimpleChanges } from '@angular/core';
|
||||
import { WindowService } from '../../services/window.service';
|
||||
import { Component, ElementRef, Input, OnChanges, OnInit, SimpleChanges } from '@angular/core';
|
||||
import {
|
||||
range,
|
||||
scaleTime,
|
||||
scaleLinear,
|
||||
min,
|
||||
max,
|
||||
curveLinear,
|
||||
line,
|
||||
easeLinear,
|
||||
select,
|
||||
axisLeft,
|
||||
axisBottom,
|
||||
timeSecond,
|
||||
timeFormat
|
||||
axisLeft,
|
||||
curveLinear,
|
||||
easeLinear,
|
||||
line,
|
||||
max,
|
||||
min,
|
||||
range,
|
||||
scaleLinear,
|
||||
scaleTime,
|
||||
select,
|
||||
timeFormat,
|
||||
timeSecond
|
||||
} from 'd3';
|
||||
import { WindowService } from '../../services/window.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-line-chart',
|
||||
@@ -23,7 +23,10 @@ import {
|
||||
export class LineChartComponent implements OnChanges, OnInit {
|
||||
@Input() value: { count: number, date: string };
|
||||
|
||||
firstDisplay: boolean;
|
||||
dirty: boolean;
|
||||
lineChartEl: HTMLElement;
|
||||
loadingEl: HTMLElement;
|
||||
svg: any;
|
||||
g: any;
|
||||
line: any;
|
||||
@@ -39,15 +42,19 @@ export class LineChartComponent implements OnChanges, OnInit {
|
||||
yAxis: any;
|
||||
height: number;
|
||||
width: number;
|
||||
margin = { top: 40, right: 40, bottom: 60, left: 60 };
|
||||
margin = {top: 40, right: 40, bottom: 60, left: 60};
|
||||
loading = true;
|
||||
|
||||
constructor(private elementRef: ElementRef, public windowService: WindowService) { }
|
||||
|
||||
ngOnInit() {
|
||||
this.lineChartEl = this.elementRef.nativeElement.querySelector('.line-chart');
|
||||
this.loadingEl = this.elementRef.nativeElement.querySelector('.line-chart-loading');
|
||||
this.limit = 40;
|
||||
|
||||
// related to the Observable.timer(0, 3000) in health component
|
||||
this.duration = 3000;
|
||||
|
||||
this.now = new Date(Date.now() - this.duration);
|
||||
|
||||
this.options = {
|
||||
@@ -55,22 +62,37 @@ export class LineChartComponent implements OnChanges, OnInit {
|
||||
color: '#3A84C5'
|
||||
};
|
||||
|
||||
this.firstDisplay = true;
|
||||
this.render();
|
||||
setTimeout(() => this.loading = false, 4000);
|
||||
|
||||
this.windowService.resize.subscribe(w => {
|
||||
if (this.svg) {
|
||||
const el = this.lineChartEl.querySelector('svg');
|
||||
el.parentNode.removeChild(el);
|
||||
this.dirty = true;
|
||||
this.loading = true;
|
||||
this.render();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
this.width = this.lineChartEl.clientWidth - this.margin.left - this.margin.right;
|
||||
this.height = this.lineChartEl.clientHeight - this.margin.top - this.margin.bottom;
|
||||
// When the lineChartEl is not displayed (is-hidden), width and length are equal to 0.
|
||||
let elt;
|
||||
if (this.lineChartEl.clientWidth === 0 || this.lineChartEl.clientHeight === 0) {
|
||||
elt = this.loadingEl;
|
||||
} else {
|
||||
elt = this.lineChartEl;
|
||||
}
|
||||
this.width = elt.clientWidth - this.margin.left - this.margin.right;
|
||||
this.height = elt.clientHeight - this.margin.top - this.margin.bottom;
|
||||
|
||||
this.svg = select(this.lineChartEl).append('svg')
|
||||
|
||||
const el = this.lineChartEl.querySelector('svg');
|
||||
if (el) {
|
||||
el.parentNode.removeChild(el);
|
||||
}
|
||||
|
||||
this.svg = select(this.lineChartEl)
|
||||
.append('svg')
|
||||
.attr('width', this.width + this.margin.left + this.margin.right)
|
||||
.attr('height', this.height + this.margin.top + this.margin.bottom)
|
||||
.append('g')
|
||||
@@ -80,7 +102,7 @@ export class LineChartComponent implements OnChanges, OnInit {
|
||||
this.data = range(this.limit).map(i => 0);
|
||||
}
|
||||
|
||||
this.x = scaleTime().range([0, this.width]);
|
||||
this.x = scaleTime().range([0, this.width - 10]);
|
||||
this.y = scaleLinear().range([this.height, 0]);
|
||||
|
||||
this.x.domain([<any>this.now - (this.limit - 2), <any>this.now - this.duration]);
|
||||
@@ -91,7 +113,9 @@ export class LineChartComponent implements OnChanges, OnInit {
|
||||
.y((d: any) => this.y(d))
|
||||
.curve(curveLinear);
|
||||
|
||||
this.svg.append('defs').append('clipPath')
|
||||
this.svg
|
||||
.append('defs')
|
||||
.append('clipPath')
|
||||
.attr('id', 'clip')
|
||||
.append('rect')
|
||||
.attr('width', this.width)
|
||||
@@ -121,7 +145,7 @@ export class LineChartComponent implements OnChanges, OnInit {
|
||||
this.updateData(this.value.count);
|
||||
}
|
||||
|
||||
updateData = (value: number) => {
|
||||
updateData(value: number) {
|
||||
this.data.push(value * 1000000);
|
||||
this.now = new Date();
|
||||
|
||||
@@ -132,9 +156,13 @@ export class LineChartComponent implements OnChanges, OnInit {
|
||||
|
||||
this.xAxis
|
||||
.transition()
|
||||
.duration(this.duration)
|
||||
.duration(this.firstDisplay || this.dirty ? 0 : this.duration)
|
||||
.ease(easeLinear)
|
||||
.call(axisBottom(this.x).tickSize(-this.height).ticks(timeSecond, 5).tickFormat(timeFormat('%H:%M:%S')))
|
||||
.call(axisBottom(this.x).tickSize(-this.height).ticks(timeSecond, 5).tickFormat(timeFormat('%H:%M:%S')));
|
||||
|
||||
this.xAxis
|
||||
.transition()
|
||||
.duration(0)
|
||||
.selectAll('text')
|
||||
.style('text-anchor', 'end')
|
||||
.attr('dx', '-.8em')
|
||||
@@ -157,6 +185,13 @@ export class LineChartComponent implements OnChanges, OnInit {
|
||||
.ease(easeLinear)
|
||||
.attr('transform', `translate(${this.x(<any>this.now - (this.limit - 1) * this.duration)})`);
|
||||
|
||||
this.firstDisplay = false;
|
||||
this.dirty = false;
|
||||
|
||||
if (this.loading) {
|
||||
this.loading = false;
|
||||
}
|
||||
|
||||
this.data.shift();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,22 +1,27 @@
|
||||
<nav class="navbar is-fixed-top" role="navigation" aria-label="main navigation">
|
||||
<nav class="navbar is-fixed-top is-transparent" role="navigation" aria-label="main navigation">
|
||||
<div class="container">
|
||||
<div class="navbar-menu">
|
||||
<div class="navbar-brand">
|
||||
<a class="navbar-item" routerLink="/">
|
||||
<img src="./assets/images/traefik.logo.svg" alt="Traefik" class="navbar-logo">
|
||||
|
||||
<div class="navbar-brand">
|
||||
<a class="navbar-item" routerLink="/" (click)="burger = false">
|
||||
<img src="./assets/images/traefik.logo.svg" alt="Traefik" class="navbar-logo">
|
||||
</a>
|
||||
<div class="navbar-burger burger" data-target="navbarMain" (click)="burger = !burger" [class.is-active]="burger">
|
||||
<span aria-hidden="true"></span>
|
||||
<span aria-hidden="true"></span>
|
||||
<span aria-hidden="true"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="navbarMain" class="navbar-menu" [class.is-active]="burger">
|
||||
<div class="navbar-start">
|
||||
<a class="navbar-item" routerLink="/" routerLinkActive="is-active" [routerLinkActiveOptions]="{ exact: true }" (click)="burger = false">
|
||||
Providers
|
||||
</a>
|
||||
<a class="navbar-item" routerLink="/status" routerLinkActive="is-active" (click)="burger = false">
|
||||
Health
|
||||
</a>
|
||||
</div>
|
||||
<div class="navbar-start">
|
||||
<div class="navbar-menu">
|
||||
<a class="navbar-item" routerLink="/" routerLinkActive="is-active" [routerLinkActiveOptions]="{ exact: true }">
|
||||
Providers
|
||||
</a>
|
||||
<a class="navbar-item" routerLink="/status" routerLinkActive="is-active">
|
||||
Health
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="navbar-end is-hidden-mobile">
|
||||
<div class="navbar-end">
|
||||
<a class="navbar-item" [href]="releaseLink" target="_blank">
|
||||
{{ version }} / {{ codename }}
|
||||
</a>
|
||||
@@ -25,5 +30,6 @@
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
@@ -9,6 +9,7 @@ export class HeaderComponent implements OnInit {
|
||||
version: string;
|
||||
codename: string;
|
||||
releaseLink: string;
|
||||
burger: boolean;
|
||||
|
||||
constructor(private apiService: ApiService) { }
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<div class="column is-4">
|
||||
<div class="item-data border-right">
|
||||
<span class="data-grey">Total Response Time</span>
|
||||
<span class="data-blue">{{ totalResponseTime }}</span>
|
||||
<span class="data-blue" [title]="exactTotalResponseTime">{{ totalResponseTime }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="column is-4">
|
||||
@@ -33,7 +33,7 @@
|
||||
<div class="column is-4">
|
||||
<div class="item-data border-right">
|
||||
<span class="data-grey">Average Response Time</span>
|
||||
<span class="data-blue">{{ averageResponseTime }}</span>
|
||||
<span class="data-blue" [title]="exactAverageResponseTime">{{ averageResponseTime }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="column is-4">
|
||||
@@ -82,15 +82,15 @@
|
||||
<td>Request</td>
|
||||
<td>Time</td>
|
||||
</tr>
|
||||
<tr *ngFor="let entry of recentErrors">
|
||||
<tr *ngFor="let entry of recentErrors; trackBy: trackRecentErrors;">
|
||||
<td>
|
||||
<span class="tag is-info">{{ entry.status_code }}</span> <span>{{ entry.status }}</span>
|
||||
<span class="tag is-info" [title]="entry.status">{{ entry.status_code }}</span> <span class="is-hidden-mobile is-hidden-desktop-only">{{ entry.status }}</span>
|
||||
</td>
|
||||
<td>
|
||||
<span class="tag">{{ entry.method }}</span> <a>{{ entry.host }}{{ entry.path }}</a>
|
||||
<span class="tag">{{ entry.method }}</span> <span>{{ entry.host }}{{ entry.path }}</span>
|
||||
</td>
|
||||
<td>
|
||||
<span>{{ entry.time }}</span>
|
||||
<span [title]="entry.time | date:'yyyy-MM-dd HH:mm:ss:SSS a z'">{{ entry.time | date:'yyyy-MM-dd HH:mm:ss a z' }}</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr *ngIf="!recentErrors?.length">
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import { Component, OnInit, OnDestroy } from '@angular/core';
|
||||
import { ApiService } from '../../services/api.service';
|
||||
import { Component, OnDestroy, OnInit } from '@angular/core';
|
||||
import { distanceInWordsStrict, format, subSeconds } from 'date-fns';
|
||||
import * as _ from 'lodash';
|
||||
import 'rxjs/add/observable/timer';
|
||||
import 'rxjs/add/operator/map';
|
||||
import 'rxjs/add/operator/mergeMap';
|
||||
import 'rxjs/add/operator/timeInterval';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import { Subscription } from 'rxjs/Subscription';
|
||||
import 'rxjs/add/observable/timer';
|
||||
import 'rxjs/add/operator/timeInterval';
|
||||
import 'rxjs/add/operator/mergeMap';
|
||||
import 'rxjs/add/operator/map';
|
||||
import { format, distanceInWordsStrict, subSeconds } from 'date-fns';
|
||||
import { ApiService } from '../../services/api.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-health',
|
||||
@@ -15,11 +16,14 @@ import { format, distanceInWordsStrict, subSeconds } from 'date-fns';
|
||||
export class HealthComponent implements OnInit, OnDestroy {
|
||||
sub: Subscription;
|
||||
recentErrors: any;
|
||||
previousRecentErrors: any;
|
||||
pid: number;
|
||||
uptime: string;
|
||||
uptimeSince: string;
|
||||
averageResponseTime: string;
|
||||
exactAverageResponseTime: string;
|
||||
totalResponseTime: string;
|
||||
exactTotalResponseTime: string;
|
||||
codeCount: number;
|
||||
totalCodeCount: number;
|
||||
chartValue: any;
|
||||
@@ -33,16 +37,22 @@ export class HealthComponent implements OnInit, OnDestroy {
|
||||
.mergeMap(() => this.apiService.fetchHealthStatus())
|
||||
.subscribe(data => {
|
||||
if (data) {
|
||||
this.recentErrors = data.recent_errors;
|
||||
this.chartValue = { count: data.average_response_time_sec, date: data.time };
|
||||
if (!_.isEqual(this.previousRecentErrors, data.recent_errors)) {
|
||||
this.previousRecentErrors = _.cloneDeep(data.recent_errors);
|
||||
this.recentErrors = data.recent_errors;
|
||||
}
|
||||
|
||||
this.chartValue = {count: data.average_response_time_sec, date: data.time};
|
||||
this.statusCodeValue = Object.keys(data.total_status_code_count)
|
||||
.map(key => ({ code: key, count: data.total_status_code_count[key] }));
|
||||
.map(key => ({code: key, count: data.total_status_code_count[key]}));
|
||||
|
||||
this.pid = data.pid;
|
||||
this.uptime = distanceInWordsStrict(subSeconds(new Date(), data.uptime_sec), new Date());
|
||||
this.uptimeSince = format(subSeconds(new Date(), data.uptime_sec), 'MM/DD/YYYY HH:mm:ss');
|
||||
this.totalResponseTime = data.total_response_time;
|
||||
this.averageResponseTime = data.average_response_time;
|
||||
this.uptimeSince = format(subSeconds(new Date(), data.uptime_sec), 'YYYY-MM-DD HH:mm:ss Z');
|
||||
this.totalResponseTime = distanceInWordsStrict(subSeconds(new Date(), data.total_response_time_sec), new Date());
|
||||
this.exactTotalResponseTime = data.total_response_time;
|
||||
this.averageResponseTime = Math.floor(data.average_response_time_sec * 1000) + ' ms';
|
||||
this.exactAverageResponseTime = data.average_response_time;
|
||||
this.codeCount = data.count;
|
||||
this.totalCodeCount = data.total_count;
|
||||
}
|
||||
@@ -54,4 +64,8 @@ export class HealthComponent implements OnInit, OnDestroy {
|
||||
this.sub.unsubscribe();
|
||||
}
|
||||
}
|
||||
|
||||
trackRecentErrors(index, item): string {
|
||||
return item.status_code + item.method + item.host + item.path + item.time;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,8 +5,9 @@
|
||||
<div class="column is-12">
|
||||
|
||||
<div class="search-container">
|
||||
<span class="icon"><i class="fas fa-search"></i></span>
|
||||
<input type="text" placeholder="Filter by name or id ..." [(ngModel)]="keyword" (ngModelChange)="filter()">
|
||||
<span class="icon search-button" *ngIf="!keyword"><i class="fas fa-search"></i></span>
|
||||
<a class="delete search-button" *ngIf="keyword" (click)="keyword = ''"></a>
|
||||
<input type="text" placeholder="Filter by name or id ..." [(ngModel)]="keyword">
|
||||
</div>
|
||||
|
||||
<div class="tabs" *ngIf="keys?.length">
|
||||
@@ -20,30 +21,17 @@
|
||||
<div *ngIf="keys?.length">
|
||||
<div class="columns">
|
||||
<!-- Frontends -->
|
||||
<div class="column is-6">
|
||||
<h2 class="subtitle"><span class="tag is-info">{{ providers[tab]?.frontends.length }}</span> Frontends</h2>
|
||||
<div class="message" *ngFor="let p of providers[tab]?.frontends; let i = index;">
|
||||
<div class="message-header">
|
||||
<div class="column is-6" *appLet="providers[tab]?.frontends | frontendFilter:keyword as frontends">
|
||||
<h2 class="subtitle"><span class="tag is-info">{{ frontends.length }}</span><span class="subtitle-name">Frontends</span></h2>
|
||||
|
||||
<div *ngIf="frontends.length < maxItem">
|
||||
|
||||
<div class="message" *ngFor="let p of frontends; trackBy: trackItem(tab)">
|
||||
<div class="message-header" [class.has-background-info]="p.backend" [class.has-background-danger]="!p.backend">
|
||||
<h2>
|
||||
<i class="icon fas fa-globe has-text-white"></i>
|
||||
<div>
|
||||
<i class="icon fas fa-globe"></i>
|
||||
<div class="field is-grouped is-grouped-multiline">
|
||||
<div class="control">
|
||||
<div class="tags has-addons">
|
||||
<span class="tag is-info">{{ p.id }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div *ngIf="p.backend">
|
||||
<div class="field is-grouped is-grouped-multiline">
|
||||
<div class="control">
|
||||
<a class="tags has-addons" [href]="'#' + p.backend">
|
||||
<span class="tag is-light">Backend</span>
|
||||
<span class="tag is-primary">{{ p.backend }}</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<span class="has-text-white" [class.is-info]="p.backend" [class.is-danger]="!p.backend">{{ p.id }}</span>
|
||||
</div>
|
||||
</h2>
|
||||
</div>
|
||||
@@ -57,16 +45,16 @@
|
||||
</div>
|
||||
|
||||
<!-- Main -->
|
||||
<div *ngIf="p.section !== 'details'">
|
||||
<div *ngIf="p.section !== 'details'" class="section-container">
|
||||
|
||||
<div *ngIf="p.routes && p.routes.length">
|
||||
<table class="table is-fullwidth is-hoverable">
|
||||
<div *ngIf="p.routes && p.routes.length" class="section-line">
|
||||
<div>
|
||||
<h2>Route Rule</h2>
|
||||
</div>
|
||||
<table class="table is-fullwidth is-hoverable table-fixed-break">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Route Rule</td>
|
||||
</tr>
|
||||
<tr *ngFor="let route of p.routes; let ri = index;">
|
||||
<td><code class="has-text-grey" title="{{ route.title }}">{{ route.rule }}</code></td>
|
||||
<tr *ngFor="let route of p.routes">
|
||||
<td><code class="has-text-grey" [title]="route.id">{{ route.rule }}</code></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
@@ -74,15 +62,15 @@
|
||||
|
||||
<div *ngIf="p.entryPoints && p.entryPoints.length">
|
||||
<hr>
|
||||
<div class="columns">
|
||||
<div class="columns section-line">
|
||||
<div class="column is-3">
|
||||
<h2>Entry Points</h2>
|
||||
<h2 class="section-line-header">Entry Points</h2>
|
||||
</div>
|
||||
<div class="column is-9">
|
||||
<div class="field is-grouped is-grouped-multiline">
|
||||
<div class="control">
|
||||
<div class="tags">
|
||||
<span class="tag is-info" *ngFor="let ep of p.entryPoints; let ri = index;">{{ ep }}</span>
|
||||
<span class="tag is-info" *ngFor="let ep of p.entryPoints">{{ ep }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -90,19 +78,34 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div *ngIf="p.backend">
|
||||
<hr>
|
||||
<div class="columns section-line">
|
||||
<div class="column is-2">
|
||||
<h2 class="section-line-header">Backend</h2>
|
||||
</div>
|
||||
<div class="column is-10">
|
||||
<div class="field">
|
||||
<i class="icon fas fa-server has-text-primary" title="Backend"></i>
|
||||
<span class="has-text-primary">{{ p.backend }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Details -->
|
||||
<div *ngIf="p.section === 'details'">
|
||||
<div *ngIf="p.section === 'details'" class="section-container">
|
||||
|
||||
<div>
|
||||
<div class="section-line">
|
||||
<div class="columns">
|
||||
<div class="column is-3">
|
||||
<h2>Misc.</h2>
|
||||
<h2 class="section-line-header">Misc.</h2>
|
||||
</div>
|
||||
<div class="column is-9">
|
||||
<div class="field is-grouped is-grouped-multiline">
|
||||
<div class="control">
|
||||
<div class="control" *ngIf="p.priority">
|
||||
<div class="tags has-addons">
|
||||
<span class="tag is-light">Priority</span>
|
||||
<span class="tag is-info">{{ p.priority }}</span>
|
||||
@@ -111,7 +114,7 @@
|
||||
<div class="control">
|
||||
<div class="tags has-addons">
|
||||
<span class="tag is-light">Host Header</span>
|
||||
<span class="tag is-info">{{ p.passHostHeader }}</span>
|
||||
<span class="tag is-info">{{ !!p.passHostHeader }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control" *ngIf="p.passTLSCert">
|
||||
@@ -127,9 +130,9 @@
|
||||
|
||||
<div *ngIf="p.redirect">
|
||||
<hr>
|
||||
<div class="columns">
|
||||
<div class="columns section-line">
|
||||
<div class="column is-3">
|
||||
<h2>Redirect</h2>
|
||||
<h2 class="section-line-header">Redirect</h2>
|
||||
</div>
|
||||
<div class="column is-9">
|
||||
<div class="field is-grouped is-grouped-multiline" *ngIf="p.redirect.entryPoint">
|
||||
@@ -160,45 +163,49 @@
|
||||
|
||||
<div *ngIf="p.basicAuth && p.basicAuth.length">
|
||||
<hr/>
|
||||
<h2>Basic Authentication</h2>
|
||||
<div class="tags padding-5-10">
|
||||
<span class="tag is-info" *ngFor="let auth of p.basicAuth; let ri = index;">{{ auth }}</span>
|
||||
<div class="section-line">
|
||||
<h2 class="section-line-header">Basic Authentication</h2>
|
||||
<div class="tags padding-5-10">
|
||||
<span class="tag is-info" *ngFor="let auth of p.basicAuth">{{ auth }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div *ngIf="p.errors">
|
||||
<div *ngIf="p.errors?.length">
|
||||
<hr/>
|
||||
<h2>Error Pages</h2>
|
||||
<table class="table is-fullwidth is-hoverable">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Backend</td>
|
||||
<td>Query</td>
|
||||
<td>Status</td>
|
||||
</tr>
|
||||
<tr *ngFor="let key of p.errors | keys">
|
||||
<td><span class="has-text-grey-light">{{ p.errors[key].backend }}</span></td>
|
||||
<td><span class="has-text-grey">{{ p.errors[key].query }}</span></td>
|
||||
<td>
|
||||
<span class="tag is-light" *ngFor="let state of p.errors[key].status">{{ state }}</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="section-line">
|
||||
<h2 class="section-line-header">Error Pages</h2>
|
||||
<table class="table is-fullwidth is-hoverable">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Backend</td>
|
||||
<td>Query</td>
|
||||
<td>Status</td>
|
||||
</tr>
|
||||
<tr *ngFor="let entry of p.errors">
|
||||
<td><span class="has-text-grey-light">{{ entry.backend }}</span></td>
|
||||
<td><span class="has-text-grey">{{ entry.query }}</span></td>
|
||||
<td>
|
||||
<span class="tag is-light" *ngFor="let state of entry.status">{{ state }}</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div *ngIf="p.whiteList">
|
||||
<hr/>
|
||||
<div class="columns is-gapless is-multiline is-mobile">
|
||||
<div class="columns is-gapless is-multiline is-mobile section-line">
|
||||
<div class="column is-half">
|
||||
<h2>Whitelist</h2>
|
||||
<h2 class="section-line-header">Whitelist</h2>
|
||||
</div>
|
||||
<div class="column is-half">
|
||||
<div class="field">
|
||||
<div class="control">
|
||||
<div class="tags has-addons">
|
||||
<span class="tag is-light">useXForwardedFor</span>
|
||||
<span class="tag is-info">{{ p.whiteList.useXForwardedFor }}</span>
|
||||
<span class="tag is-info">{{ !!p.whiteList.useXForwardedFor }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -207,7 +214,7 @@
|
||||
<div class="field is-grouped is-grouped-multiline">
|
||||
<div class="control">
|
||||
<div class="tags">
|
||||
<span class="tag is-info" *ngFor="let wlRange of p.whiteList.sourceRange; let ri = index;">{{ wlRange }}</span>
|
||||
<span class="tag is-info" *ngFor="let wlRange of p.whiteList.sourceRange">{{ wlRange }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -217,126 +224,137 @@
|
||||
|
||||
<div *ngIf="p.headers">
|
||||
<hr/>
|
||||
<h2>Headers</h2>
|
||||
<div class="columns is-multiline">
|
||||
<div class="section-line">
|
||||
<h2 class="section-line-header">Headers</h2>
|
||||
<div class="columns is-multiline">
|
||||
|
||||
<div class="column is-12" *ngIf="p.headers.customRequestHeaders">
|
||||
<h2>Custom Request Headers</h2>
|
||||
<table class="table is-fullwidth is-hoverable">
|
||||
<tbody>
|
||||
<tr *ngFor="let key of p.headers.customRequestHeaders | keys">
|
||||
<td><span class="has-text-grey-light">{{ key }}</span></td>
|
||||
<td><span class="has-text-grey">{{ p.headers.customRequestHeaders[key] }}</span></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="column is-12" *ngIf="p.headers.customRequestHeaders?.length">
|
||||
<table class="table is-fullwidth is-hoverable table-fixed-break">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td colspan="2">Custom Request Headers</td>
|
||||
</tr>
|
||||
<tr *ngFor="let header of p.headers.customRequestHeaders">
|
||||
<td><span class="has-text-grey-light">{{ header.name }}</span></td>
|
||||
<td><span class="has-text-grey">{{ header.value }}</span></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="column is-12" *ngIf="p.headers.customResponseHeaders">
|
||||
<h2>Custom Response Headers</h2>
|
||||
<table class="table is-fullwidth is-hoverable">
|
||||
<tbody>
|
||||
<tr *ngFor="let key of p.headers.customResponseHeaders | keys">
|
||||
<td><span class="has-text-grey-light">{{ key }}</span></td>
|
||||
<td><span class="has-text-grey">{{ p.headers.customResponseHeaders[key] }}</span></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="column is-12" *ngIf="p.headers.customResponseHeaders?.length">
|
||||
<table class="table is-fullwidth is-hoverable table-fixed-break">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td colspan="2">Custom Response Headers</td>
|
||||
</tr>
|
||||
<tr *ngFor="let header of p.headers.customResponseHeaders">
|
||||
<td><span class="has-text-grey-light">{{ header.name }}</span></td>
|
||||
<td><span class="has-text-grey">{{ header.value }}</span></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="column is-12">
|
||||
<h2>Secure</h2>
|
||||
<table class="table is-fullwidth is-hoverable">
|
||||
<tbody>
|
||||
<tr *ngIf="p.headers.browserXssFilter">
|
||||
<td><span class="has-text-grey">Browser XSS Filter</span></td>
|
||||
<td><span class="has-text-grey">{{ p.headers.browserXssFilter }}</span></td>
|
||||
</tr>
|
||||
<tr *ngIf="p.headers.contentSecurityPolicy">
|
||||
<td><span class="has-text-grey">Content Security Policy</span></td>
|
||||
<td><span class="has-text-grey">{{ p.headers.contentSecurityPolicy }}</span></td>
|
||||
</tr>
|
||||
<tr *ngIf="p.headers.contentTypeNoSniff">
|
||||
<td><span class="has-text-grey">Content Type (No sniff)</span></td>
|
||||
<td><span class="has-text-grey">{{ p.headers.contentTypeNoSniff }}</span></td>
|
||||
</tr>
|
||||
<tr *ngIf="p.headers.customFrameOptionsValue">
|
||||
<td><span class="has-text-grey">Custom Frame Options Value</span></td>
|
||||
<td><span class="has-text-grey">{{ p.headers.customFrameOptionsValue }}</span></td>
|
||||
</tr>
|
||||
<tr *ngIf="p.headers.forceSTSHeader">
|
||||
<td><span class="has-text-grey">Force STS Header</span></td>
|
||||
<td><span class="has-text-grey">{{ p.headers.forceSTSHeader }}</span></td>
|
||||
</tr>
|
||||
<tr *ngIf="p.headers.frameDeny">
|
||||
<td><span class="has-text-grey">Frame Deny</span></td>
|
||||
<td><span class="has-text-grey">{{ p.headers.frameDeny }}</span></td>
|
||||
</tr>
|
||||
<tr *ngIf="p.headers.isDevelopment">
|
||||
<td><span class="has-text-grey">Is Development</span></td>
|
||||
<td><span class="has-text-grey">{{ p.headers.isDevelopment }}</span></td>
|
||||
</tr>
|
||||
<tr *ngIf="p.headers.publicKey">
|
||||
<td><span class="has-text-grey">Public Key</span></td>
|
||||
<td><span class="has-text-grey">{{ p.headers.publicKey }}</span></td>
|
||||
</tr>
|
||||
<tr *ngIf="p.headers.referrerPolicy">
|
||||
<td><span class="has-text-grey">Referrer Policy</span></td>
|
||||
<td><span class="has-text-grey">{{ p.headers.referrerPolicy }}</span></td>
|
||||
</tr>
|
||||
<tr *ngIf="p.headers.sslHost">
|
||||
<td><span class="has-text-grey">SSL Host</span></td>
|
||||
<td><span class="has-text-grey">{{ p.headers.sslHost }}</span></td>
|
||||
</tr>
|
||||
<tr *ngIf="p.headers.sslRedirect">
|
||||
<td><span class="has-text-grey">SSL Redirect</span></td>
|
||||
<td><span class="has-text-grey">{{ p.headers.sslRedirect }}</span></td>
|
||||
</tr>
|
||||
<tr *ngIf="p.headers.sslTemporaryRedirect">
|
||||
<td><span class="has-text-grey">SSL Temporary Redirect</span></td>
|
||||
<td><span class="has-text-grey">{{ p.headers.sslTemporaryRedirect }}</span></td>
|
||||
</tr>
|
||||
<tr *ngIf="p.headers.stsIncludeSubdomains">
|
||||
<td><span class="has-text-grey">STS Include Subdomains</span></td>
|
||||
<td><span class="has-text-grey">{{ p.headers.stsIncludeSubdomains }}</span></td>
|
||||
</tr>
|
||||
<tr *ngIf="p.headers.stsPreload">
|
||||
<td><span class="has-text-grey">STS Preload</span></td>
|
||||
<td><span class="has-text-grey">{{ p.headers.stsPreload }}</span></td>
|
||||
</tr>
|
||||
<tr *ngIf="p.headers.stsSeconds">
|
||||
<td><span class="has-text-grey">STS Seconds</span></td>
|
||||
<td><span class="has-text-grey">{{ p.headers.stsSeconds }}</span></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="column is-12">
|
||||
<table class="table is-fullwidth is-hoverable table-fixed-break">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td colspan="2">Secure</td>
|
||||
</tr>
|
||||
<tr *ngIf="p.headers.browserXssFilter">
|
||||
<td><span class="has-text-grey">Browser XSS Filter</span></td>
|
||||
<td><span class="has-text-grey">{{ p.headers.browserXssFilter }}</span></td>
|
||||
</tr>
|
||||
<tr *ngIf="p.headers.contentSecurityPolicy">
|
||||
<td><span class="has-text-grey">Content Security Policy</span></td>
|
||||
<td><span class="has-text-grey">{{ p.headers.contentSecurityPolicy }}</span></td>
|
||||
</tr>
|
||||
<tr *ngIf="p.headers.contentTypeNoSniff">
|
||||
<td><span class="has-text-grey">Content Type (No sniff)</span></td>
|
||||
<td><span class="has-text-grey">{{ p.headers.contentTypeNoSniff }}</span></td>
|
||||
</tr>
|
||||
<tr *ngIf="p.headers.customFrameOptionsValue">
|
||||
<td><span class="has-text-grey">Custom Frame Options Value</span></td>
|
||||
<td><span class="has-text-grey">{{ p.headers.customFrameOptionsValue }}</span></td>
|
||||
</tr>
|
||||
<tr *ngIf="p.headers.forceSTSHeader">
|
||||
<td><span class="has-text-grey">Force STS Header</span></td>
|
||||
<td><span class="has-text-grey">{{ p.headers.forceSTSHeader }}</span></td>
|
||||
</tr>
|
||||
<tr *ngIf="p.headers.frameDeny">
|
||||
<td><span class="has-text-grey">Frame Deny</span></td>
|
||||
<td><span class="has-text-grey">{{ p.headers.frameDeny }}</span></td>
|
||||
</tr>
|
||||
<tr *ngIf="p.headers.isDevelopment">
|
||||
<td><span class="has-text-grey">Is Development</span></td>
|
||||
<td><span class="has-text-grey">{{ p.headers.isDevelopment }}</span></td>
|
||||
</tr>
|
||||
<tr *ngIf="p.headers.publicKey">
|
||||
<td><span class="has-text-grey">Public Key</span></td>
|
||||
<td><span class="has-text-grey">{{ p.headers.publicKey }}</span></td>
|
||||
</tr>
|
||||
<tr *ngIf="p.headers.referrerPolicy">
|
||||
<td><span class="has-text-grey">Referrer Policy</span></td>
|
||||
<td><span class="has-text-grey">{{ p.headers.referrerPolicy }}</span></td>
|
||||
</tr>
|
||||
<tr *ngIf="p.headers.sslHost">
|
||||
<td><span class="has-text-grey">SSL Host</span></td>
|
||||
<td><span class="has-text-grey">{{ p.headers.sslHost }}</span></td>
|
||||
</tr>
|
||||
<tr *ngIf="p.headers.sslRedirect">
|
||||
<td><span class="has-text-grey">SSL Redirect</span></td>
|
||||
<td><span class="has-text-grey">{{ p.headers.sslRedirect }}</span></td>
|
||||
</tr>
|
||||
<tr *ngIf="p.headers.sslTemporaryRedirect">
|
||||
<td><span class="has-text-grey">SSL Temporary Redirect</span></td>
|
||||
<td><span class="has-text-grey">{{ p.headers.sslTemporaryRedirect }}</span></td>
|
||||
</tr>
|
||||
<tr *ngIf="p.headers.stsIncludeSubdomains">
|
||||
<td><span class="has-text-grey">STS Include Subdomains</span></td>
|
||||
<td><span class="has-text-grey">{{ p.headers.stsIncludeSubdomains }}</span></td>
|
||||
</tr>
|
||||
<tr *ngIf="p.headers.stsPreload">
|
||||
<td><span class="has-text-grey">STS Preload</span></td>
|
||||
<td><span class="has-text-grey">{{ p.headers.stsPreload }}</span></td>
|
||||
</tr>
|
||||
<tr *ngIf="p.headers.stsSeconds">
|
||||
<td><span class="has-text-grey">STS Seconds</span></td>
|
||||
<td><span class="has-text-grey">{{ p.headers.stsSeconds }}</span></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="column is-12" *ngIf="p.headers.allowedHosts">
|
||||
<h2>Allowed Hosts</h2>
|
||||
<div class="tags-list">
|
||||
<span class="tag is-light" *ngFor="let host of p.headers.allowedHosts">{{ host }}</span>
|
||||
<div class="column is-12" *ngIf="p.headers.sslProxyHeaders?.length">
|
||||
<table class="table is-fullwidth is-hoverable table-fixed-break">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td colspan="2">SSL Proxy Headers</td>
|
||||
</tr>
|
||||
<tr *ngFor="let header of p.headers.sslProxyHeaders">
|
||||
<td><span class="has-text-grey-light">{{ header.name }}</span></td>
|
||||
<td><span class="has-text-grey">{{ header.value }}</span></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="column is-12" *ngIf="p.headers.allowedHosts">
|
||||
<h2>Allowed Hosts</h2>
|
||||
<div class="tags-list">
|
||||
<span class="tag is-light" *ngFor="let host of p.headers.allowedHosts">{{ host }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="column is-12" *ngIf="p.headers.hostsProxyHeaders">
|
||||
<h2>Hosts Proxy Headers</h2>
|
||||
<div class="tags-list">
|
||||
<span class="tag is-light" *ngFor="let h of p.headers.hostsProxyHeaders">{{ h }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="column is-12" *ngIf="p.headers.sslProxyHeaders">
|
||||
<h2>SSL Proxy Headers</h2>
|
||||
<table class="table is-fullwidth is-hoverable">
|
||||
<tbody>
|
||||
<tr *ngFor="let key of p.headers.sslProxyHeaders | keys">
|
||||
<td><span class="has-text-grey-light">{{ key }}</span></td>
|
||||
<td><span class="has-text-grey">{{ p.headers.sslProxyHeaders[key] }}</span></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="column is-12" *ngIf="p.headers.hostsProxyHeaders">
|
||||
<h2>Hosts Proxy Headers</h2>
|
||||
<div class="tags-list">
|
||||
<span class="tag is-light" *ngFor="let h of p.headers.hostsProxyHeaders">{{ h }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -344,23 +362,33 @@
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div *ngIf="frontends.length > maxItem">
|
||||
|
||||
<div class="message">
|
||||
<div class="message-header has-background-warning has-text-black">
|
||||
Too many frontends to display, please add a filter.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Backends -->
|
||||
<div class="column is-6">
|
||||
<h2 class="subtitle"><span class="tag is-primary">{{ providers[tab]?.backends.length }}</span> Backends</h2>
|
||||
<div class="message" *ngFor="let p of providers[tab]?.backends; let i = index;">
|
||||
<div class="message-header">
|
||||
<h2 [id]="p.id">
|
||||
<div class="column is-6" *appLet="providers[tab]?.backends | backendFilter:keyword as backends">
|
||||
<h2 class="subtitle"><span class="tag is-primary">{{ backends.length }}</span><span class="subtitle-name">Backends</span></h2>
|
||||
|
||||
<div *ngIf="backends.length < maxItem">
|
||||
|
||||
<div class="message" *ngFor="let p of backends; trackBy: trackItem(tab);">
|
||||
<div class="message-header" [class.has-background-primary]="p.servers?.length" [class.has-background-danger]="!p.servers?.length">
|
||||
<h2>
|
||||
<i class="icon fas fa-server has-text-white"></i>
|
||||
<div>
|
||||
<i class="icon fas fa-server"></i>
|
||||
<div class="field is-grouped is-grouped-multiline">
|
||||
<div class="control">
|
||||
<div class="tags has-addons">
|
||||
<span class="tag is-primary">{{ p.id }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<span class="has-text-white">{{ p.id }}</span>
|
||||
</div>
|
||||
</h2>
|
||||
</div>
|
||||
@@ -374,28 +402,34 @@
|
||||
</div>
|
||||
|
||||
<!-- Main -->
|
||||
<div *ngIf="p.section !== 'details'">
|
||||
<table class="table is-fullwidth is-hoverable">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Server</td>
|
||||
<td>Weight</td>
|
||||
</tr>
|
||||
<tr *ngFor="let server of p.servers; let ri = index;">
|
||||
<td><a href="{{ server.url }}" title="{{ server.title }}">{{ server.url }}</a></td>
|
||||
<td><span class="has-text-grey">{{ server.weight }}</span></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div *ngIf="p.section !== 'details'" class="section-container">
|
||||
<div class="section-line">
|
||||
<table class="table is-fullwidth is-hoverable table-fixed">
|
||||
<colgroup>
|
||||
<col class="table-col-75">
|
||||
<col>
|
||||
</colgroup>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Server</td>
|
||||
<td>Weight</td>
|
||||
</tr>
|
||||
<tr *ngFor="let server of p.servers">
|
||||
<td class="table-cell-limited"><a href="{{ server.url }}" [title]="server.id">{{ server.url }}</a></td>
|
||||
<td><span class="has-text-grey">{{ server.weight }}</span></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Details -->
|
||||
<div *ngIf="p.section === 'details'">
|
||||
<div *ngIf="p.section === 'details'" class="section-container">
|
||||
|
||||
<div *ngIf="p.loadBalancer">
|
||||
<div *ngIf="p.loadBalancer" class="section-line">
|
||||
<div class="columns">
|
||||
<div class="column is-3">
|
||||
<h2>Load Balancer</h2>
|
||||
<h2 class="section-line-header">Load Balancer</h2>
|
||||
</div>
|
||||
<div class="column is-9">
|
||||
<div class="field is-grouped is-grouped-multiline">
|
||||
@@ -424,9 +458,9 @@
|
||||
|
||||
<div *ngIf="p.maxConn">
|
||||
<hr/>
|
||||
<div class="columns">
|
||||
<div class="columns section-line">
|
||||
<div class="column is-3">
|
||||
<h2>Max Connections</h2>
|
||||
<h2 class="section-line-header">Max Connections</h2>
|
||||
</div>
|
||||
<div class="column is-9">
|
||||
<div class="field is-grouped is-grouped-multiline">
|
||||
@@ -449,9 +483,9 @@
|
||||
|
||||
<div *ngIf="p.circuitBreaker">
|
||||
<hr/>
|
||||
<div class="columns">
|
||||
<div class="columns section-line">
|
||||
<div class="column is-3">
|
||||
<h2>Circuit Breaker</h2>
|
||||
<h2 class="section-line-header">Circuit Breaker</h2>
|
||||
</div>
|
||||
<div class="column is-9">
|
||||
<div class="field is-grouped is-grouped-multiline">
|
||||
@@ -468,9 +502,9 @@
|
||||
|
||||
<div *ngIf="p.healthCheck">
|
||||
<hr/>
|
||||
<div class="columns">
|
||||
<div class="columns section-line">
|
||||
<div class="column is-3">
|
||||
<h2>Health Check</h2>
|
||||
<h2 class="section-line-header">Health Check</h2>
|
||||
</div>
|
||||
<div class="column is-9">
|
||||
<div class="field is-grouped is-grouped-multiline">
|
||||
@@ -505,81 +539,79 @@
|
||||
|
||||
<div *ngIf="p.buffering">
|
||||
<hr>
|
||||
<div class="columns list-title">
|
||||
<div class="column is-12">
|
||||
<h2>Buffering</h2>
|
||||
</div>
|
||||
</div>
|
||||
<div class="list-item">
|
||||
<div class="columns">
|
||||
<div class="column is-4">
|
||||
<span>Request Body Bytes</span>
|
||||
</div>
|
||||
<div class="column is-4">
|
||||
<div class="field is-grouped is-grouped-multiline">
|
||||
<div class="control">
|
||||
<div class="tags has-addons">
|
||||
<span class="tag is-light">Max</span>
|
||||
<span class="tag is-info">{{ p.buffering.maxRequestBodyBytes }}</span>
|
||||
<div class="section-line">
|
||||
<h2 class="section-line-header">Buffering</h2>
|
||||
<table class="table is-fullwidth is-hoverable table-fixedd">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><span class="has-text-grey">Request Body Bytes</span></td>
|
||||
<td>
|
||||
<div class="field is-grouped is-grouped-multiline">
|
||||
<div class="control">
|
||||
<div class="tags has-addons">
|
||||
<span class="tag is-light">Max</span>
|
||||
<span class="tag is-info">{{ p.buffering.maxRequestBodyBytes }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="column is-4">
|
||||
<div class="field is-grouped is-grouped-multiline">
|
||||
<div class="control">
|
||||
<div class="tags has-addons">
|
||||
<span class="tag is-light">Men</span>
|
||||
<span class="tag is-info">{{ p.buffering.memRequestBodyBytes }}</span>
|
||||
</td>
|
||||
<td>
|
||||
<div class="field is-grouped is-grouped-multiline">
|
||||
<div class="control">
|
||||
<div class="tags has-addons">
|
||||
<span class="tag is-light">Mem</span>
|
||||
<span class="tag is-info">{{ p.buffering.memRequestBodyBytes }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="list-item">
|
||||
<div class="columns">
|
||||
<div class="column is-4">
|
||||
<span>Response Body Bytes</span>
|
||||
</div>
|
||||
<div class="column is-4">
|
||||
<div class="field is-grouped is-grouped-multiline">
|
||||
<div class="control">
|
||||
<div class="tags has-addons">
|
||||
<span class="tag is-light">Max</span>
|
||||
<span class="tag is-info">{{ p.buffering.maxResponseBodyBytes }}</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><span class="has-text-grey">Response Body Bytes</span></td>
|
||||
<td>
|
||||
<div class="field is-grouped is-grouped-multiline">
|
||||
<div class="control">
|
||||
<div class="tags has-addons">
|
||||
<span class="tag is-light">Max</span>
|
||||
<span class="tag is-info">{{ p.buffering.maxResponseBodyBytes }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="column is-4">
|
||||
<div class="field is-grouped is-grouped-multiline">
|
||||
<div class="control">
|
||||
<div class="tags has-addons">
|
||||
<span class="tag is-light">Men</span>
|
||||
<span class="tag is-info">{{ p.buffering.memResponseBodyBytes }}</span>
|
||||
</td>
|
||||
<td>
|
||||
<div class="field is-grouped is-grouped-multiline">
|
||||
<div class="control">
|
||||
<div class="tags has-addons">
|
||||
<span class="tag is-light">Mem</span>
|
||||
<span class="tag is-info">{{ p.buffering.memResponseBodyBytes }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="list-item">
|
||||
<div class="columns">
|
||||
<div class="column is-4">
|
||||
<span>Retry Expression</span>
|
||||
</div>
|
||||
<div class="column is-8">
|
||||
<span class="tag is-info">{{ p.buffering.retryExpression }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="has-text-grey">Retry Expression</td>
|
||||
<td colspan="2"><span class="tag is-info">{{ p.buffering.retryExpression }}</span></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div *ngIf="backends.length > maxItem">
|
||||
|
||||
<div class="message">
|
||||
<div class="message-header has-background-warning has-text-black">
|
||||
Too many backends to display, please add a filter.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { Component, OnInit, OnDestroy } from '@angular/core';
|
||||
import { ApiService } from '../../services/api.service';
|
||||
import { Subscription } from 'rxjs/Subscription';
|
||||
import { Component, OnDestroy, OnInit } from '@angular/core';
|
||||
import * as _ from 'lodash';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import * as _ from "lodash";
|
||||
import { Subscription } from 'rxjs/Subscription';
|
||||
import { ApiService } from '../../services/api.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-providers',
|
||||
@@ -10,8 +10,9 @@ import * as _ from "lodash";
|
||||
})
|
||||
export class ProvidersComponent implements OnInit, OnDestroy {
|
||||
sub: Subscription;
|
||||
maxItem: number;
|
||||
keys: string[];
|
||||
data: any;
|
||||
previousKeys: string[];
|
||||
previousData: any;
|
||||
providers: any;
|
||||
tab: string;
|
||||
@@ -20,6 +21,7 @@ export class ProvidersComponent implements OnInit, OnDestroy {
|
||||
constructor(private apiService: ApiService) { }
|
||||
|
||||
ngOnInit() {
|
||||
this.maxItem = 100;
|
||||
this.keyword = '';
|
||||
this.sub = Observable.timer(0, 2000)
|
||||
.timeInterval()
|
||||
@@ -27,28 +29,23 @@ export class ProvidersComponent implements OnInit, OnDestroy {
|
||||
.subscribe(data => {
|
||||
if (!_.isEqual(this.previousData, data)) {
|
||||
this.previousData = _.cloneDeep(data);
|
||||
this.data = data;
|
||||
this.providers = data;
|
||||
this.keys = Object.keys(this.providers);
|
||||
this.tab = this.keys[0];
|
||||
|
||||
const keys = Object.keys(this.providers);
|
||||
if (!_.isEqual(this.previousKeys, keys)) {
|
||||
this.keys = keys;
|
||||
|
||||
// keep current tab or set to the first tab
|
||||
if (!this.tab || (this.tab && !this.keys.includes(this.tab))) {
|
||||
this.tab = this.keys[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
filter(): void {
|
||||
const keyword = this.keyword.toLowerCase();
|
||||
this.providers = Object.keys(this.data)
|
||||
.filter(value => value !== 'acme' && value !== 'ACME')
|
||||
.reduce((acc, curr) => {
|
||||
return Object.assign(acc, {
|
||||
[curr]: {
|
||||
backends: this.data[curr].backends.filter(d => d.id.toLowerCase().includes(keyword)),
|
||||
frontends: this.data[curr].frontends.filter(d => {
|
||||
return d.id.toLowerCase().includes(keyword) || d.backend.toLowerCase().includes(keyword);
|
||||
})
|
||||
}
|
||||
});
|
||||
}, {});
|
||||
trackItem(tab): (index, item) => string {
|
||||
return (index, item): string => tab + '-' + item.id;
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
|
||||
21
webui/src/app/directives/let.directive.ts
Normal file
21
webui/src/app/directives/let.directive.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import { Directive, Input, TemplateRef, ViewContainerRef } from '@angular/core';
|
||||
|
||||
interface LetContext<T> {
|
||||
appLet: T;
|
||||
}
|
||||
|
||||
@Directive({
|
||||
selector: '[appLet]'
|
||||
})
|
||||
export class LetDirective<T> {
|
||||
private _context: LetContext<T> = {appLet: null};
|
||||
|
||||
constructor(_viewContainer: ViewContainerRef, _templateRef: TemplateRef<LetContext<T>>) {
|
||||
_viewContainer.createEmbeddedView(_templateRef, this._context);
|
||||
}
|
||||
|
||||
@Input()
|
||||
set appLet(value: T) {
|
||||
this._context.appLet = value;
|
||||
}
|
||||
}
|
||||
17
webui/src/app/pipes/backend.filter.pipe.ts
Normal file
17
webui/src/app/pipes/backend.filter.pipe.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { Pipe, PipeTransform } from '@angular/core';
|
||||
|
||||
@Pipe({
|
||||
name: 'backendFilter',
|
||||
pure: false
|
||||
})
|
||||
export class BackendFilterPipe implements PipeTransform {
|
||||
transform(items: any[], filter: string): any {
|
||||
if (!items || !filter) {
|
||||
return items;
|
||||
}
|
||||
|
||||
const keyword = filter.toLowerCase();
|
||||
return items.filter(d => d.id.toLowerCase().includes(keyword)
|
||||
|| d.servers.some(r => r.url.toLowerCase().includes(keyword)));
|
||||
}
|
||||
}
|
||||
18
webui/src/app/pipes/frontend.filter.pipe.ts
Normal file
18
webui/src/app/pipes/frontend.filter.pipe.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { Pipe, PipeTransform } from '@angular/core';
|
||||
|
||||
@Pipe({
|
||||
name: 'frontendFilter',
|
||||
pure: false
|
||||
})
|
||||
export class FrontendFilterPipe implements PipeTransform {
|
||||
transform(items: any[], filter: string): any {
|
||||
if (!items || !filter) {
|
||||
return items;
|
||||
}
|
||||
|
||||
const keyword = filter.toLowerCase();
|
||||
return items.filter(d => d.id.toLowerCase().includes(keyword)
|
||||
|| d.backend.toLowerCase().includes(keyword)
|
||||
|| d.routes.some(r => r.rule.toLowerCase().includes(keyword)));
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import { PipeTransform, Pipe } from '@angular/core';
|
||||
import { Pipe, PipeTransform } from '@angular/core';
|
||||
|
||||
@Pipe({ name: 'keys' })
|
||||
@Pipe({name: 'keys'})
|
||||
export class KeysPipe implements PipeTransform {
|
||||
transform(value, args: string[]): any {
|
||||
return Object.keys(value);
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { HttpClient, HttpErrorResponse, HttpHeaders } from '@angular/common/http';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { HttpClient, HttpHeaders, HttpErrorResponse } from '@angular/common/http';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import 'rxjs/add/operator/map';
|
||||
import 'rxjs/add/observable/empty';
|
||||
import 'rxjs/add/observable/of';
|
||||
import 'rxjs/add/operator/catch';
|
||||
import 'rxjs/add/operator/map';
|
||||
import 'rxjs/add/operator/retry';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
|
||||
export interface ProviderType {
|
||||
[provider: string]: {
|
||||
@@ -25,7 +25,7 @@ export class ApiService {
|
||||
}
|
||||
|
||||
fetchVersion(): Observable<any> {
|
||||
return this.http.get(`/api/version`, { headers: this.headers })
|
||||
return this.http.get('../api/version', {headers: this.headers})
|
||||
.retry(4)
|
||||
.catch((err: HttpErrorResponse) => {
|
||||
console.error(`[version] returned code ${err.status}, body was: ${err.error}`);
|
||||
@@ -34,7 +34,7 @@ export class ApiService {
|
||||
}
|
||||
|
||||
fetchHealthStatus(): Observable<any> {
|
||||
return this.http.get(`/health`, { headers: this.headers })
|
||||
return this.http.get('../health', {headers: this.headers})
|
||||
.retry(2)
|
||||
.catch((err: HttpErrorResponse) => {
|
||||
console.error(`[health] returned code ${err.status}, body was: ${err.error}`);
|
||||
@@ -43,46 +43,53 @@ export class ApiService {
|
||||
}
|
||||
|
||||
fetchProviders(): Observable<any> {
|
||||
return this.http.get(`/api/providers`, { headers: this.headers })
|
||||
return this.http.get('../api/providers', {headers: this.headers})
|
||||
.retry(2)
|
||||
.catch((err: HttpErrorResponse) => {
|
||||
console.error(`[providers] returned code ${err.status}, body was: ${err.error}`);
|
||||
return Observable.of<any>({});
|
||||
})
|
||||
.map(this.parseProviders);
|
||||
.map((data: any): ProviderType => this.parseProviders(data));
|
||||
}
|
||||
|
||||
parseProviders(data: any): ProviderType {
|
||||
return Object.keys(data)
|
||||
.filter(value => value !== 'acme' && value !== 'ACME')
|
||||
.reduce((acc, curr) => {
|
||||
acc[curr] = {
|
||||
backends: Object.keys(data[curr].backends || {}).map(key => {
|
||||
data[curr].backends[key].id = key;
|
||||
data[curr].backends[key].servers = Object.keys(data[curr].backends[key].servers || {}).map(server => {
|
||||
return {
|
||||
title: server,
|
||||
url: data[curr].backends[key].servers[server].url,
|
||||
weight: data[curr].backends[key].servers[server].weight
|
||||
};
|
||||
acc[curr] = {};
|
||||
|
||||
acc[curr].frontends = this.toArray(data[curr].frontends, 'id')
|
||||
.map(frontend => {
|
||||
frontend.routes = this.toArray(frontend.routes, 'id');
|
||||
frontend.errors = this.toArray(frontend.errors, 'id');
|
||||
if (frontend.headers) {
|
||||
frontend.headers.customRequestHeaders = this.toHeaderArray(frontend.headers.customRequestHeaders);
|
||||
frontend.headers.customResponseHeaders = this.toHeaderArray(frontend.headers.customResponseHeaders);
|
||||
frontend.headers.sslProxyHeaders = this.toHeaderArray(frontend.headers.sslProxyHeaders);
|
||||
}
|
||||
return frontend;
|
||||
});
|
||||
|
||||
return data[curr].backends[key];
|
||||
}),
|
||||
frontends: Object.keys(data[curr].frontends || {}).map(key => {
|
||||
data[curr].frontends[key].id = key;
|
||||
data[curr].frontends[key].routes = Object.keys(data[curr].frontends[key].routes || {}).map(route => {
|
||||
return {
|
||||
title: route,
|
||||
rule: data[curr].frontends[key].routes[route].rule
|
||||
};
|
||||
acc[curr].backends = this.toArray(data[curr].backends, 'id')
|
||||
.map(backend => {
|
||||
backend.servers = this.toArray(backend.servers, 'id');
|
||||
return backend;
|
||||
});
|
||||
|
||||
return data[curr].frontends[key];
|
||||
}),
|
||||
};
|
||||
|
||||
return acc;
|
||||
}, {});
|
||||
return acc;
|
||||
}, {});
|
||||
}
|
||||
|
||||
toHeaderArray(data: any): any[] {
|
||||
return Object.keys(data || {}).map(key => ({name: key, value: data[key]}));
|
||||
}
|
||||
|
||||
toArray(data: any, fieldKeyName: string): any[] {
|
||||
return Object.keys(data || {}).map(key => {
|
||||
data[key][fieldKeyName] = key;
|
||||
return data[key];
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
@charset "utf-8"
|
||||
|
||||
@import 'typography'
|
||||
@import 'colors'
|
||||
@import '../../node_modules/bulma/sass/utilities/all'
|
||||
@import '../../node_modules/bulma/sass/base/all'
|
||||
@import '../../node_modules/bulma/sass/grid/all'
|
||||
@import '../../node_modules/bulma/sass/elements/container'
|
||||
@import '../../node_modules/bulma/sass/elements/tag'
|
||||
@import '../../node_modules/bulma/sass/elements/box'
|
||||
@import '../../node_modules/bulma/sass/elements/form'
|
||||
@import '../../node_modules/bulma/sass/elements/table'
|
||||
@import '../../node_modules/bulma/sass/components/navbar'
|
||||
@import '../../node_modules/bulma/sass/components/tabs'
|
||||
@import '../../node_modules/bulma/sass/elements/notification'
|
||||
@import 'nav'
|
||||
@import 'content'
|
||||
@import 'message'
|
||||
@import 'label'
|
||||
@import 'charts'
|
||||
@import 'helper'
|
||||
|
||||
html
|
||||
font-family: $open-sans
|
||||
height: 100%
|
||||
background: $background
|
||||
@@ -30,12 +30,6 @@
|
||||
height: 320px
|
||||
background-color: $white
|
||||
|
||||
.bar
|
||||
fill: rgba($blue, 0.91)
|
||||
|
||||
&:hover
|
||||
fill: lighten($blue, 10)
|
||||
|
||||
.axis text
|
||||
fill: $text
|
||||
font: 10px sans-serif
|
||||
|
||||
@@ -1,46 +1,21 @@
|
||||
.content
|
||||
background: transparent
|
||||
margin: 40px 0
|
||||
margin: 2rem 0
|
||||
|
||||
.subtitle
|
||||
font-size: 15px
|
||||
text-transform: uppercase
|
||||
color: $black
|
||||
font-size: 0.9rem
|
||||
font-weight: $weight-bold
|
||||
text-transform: uppercase
|
||||
margin: 10px 0 0 0
|
||||
|
||||
.list-title
|
||||
color: $text-dark
|
||||
weight: $weight-semibold
|
||||
margin: 5px 0 0 0
|
||||
|
||||
.list-item
|
||||
width: 100%
|
||||
display: block
|
||||
align-items: center
|
||||
font-size: 12px
|
||||
padding: 6px 10px
|
||||
border-top: 1px solid $border-light
|
||||
|
||||
.columns
|
||||
|
||||
.column
|
||||
display: flex
|
||||
align-items: center
|
||||
|
||||
.icon
|
||||
width: 22px
|
||||
height: 22px
|
||||
display: block
|
||||
float: left
|
||||
margin-right: 10px
|
||||
.subtitle-name
|
||||
padding-left: 0.5rem
|
||||
|
||||
.content-item
|
||||
background: $white
|
||||
border: 1px solid $border-secondary
|
||||
margin: 10px 0
|
||||
border-radius: 4px
|
||||
border-radius: $traefik-border-radius
|
||||
box-shadow: 1px 2px 5px rgba($border, 0.4)
|
||||
|
||||
h2
|
||||
@@ -82,7 +57,7 @@
|
||||
|
||||
img
|
||||
width: 40px
|
||||
heught: 40px
|
||||
height: 40px
|
||||
display: block
|
||||
float: left
|
||||
margin-right: 10px
|
||||
@@ -106,37 +81,27 @@
|
||||
margin: 15px auto
|
||||
|
||||
.search-container
|
||||
height: 50px
|
||||
background: $white
|
||||
border-radius: 4px
|
||||
color: $black
|
||||
margin: 10px 0
|
||||
display: flex
|
||||
align-items: center
|
||||
position: relative
|
||||
border-radius: $traefik-border-radius
|
||||
box-shadow: 1px 2px 5px rgba($border, 0.4)
|
||||
border: 1px solid $border-secondary
|
||||
position: relative
|
||||
height: 3rem
|
||||
|
||||
.icon
|
||||
.search-button
|
||||
position: absolute
|
||||
left: 10px
|
||||
top: 13px
|
||||
left: 1rem
|
||||
top: 0.8rem
|
||||
|
||||
input
|
||||
font-size: 16px
|
||||
color: $text
|
||||
width: 100%
|
||||
height: 48px
|
||||
padding-left: 50px
|
||||
border: none
|
||||
border-radius: $traefik-border-radius
|
||||
outline: none
|
||||
font-size: 1rem
|
||||
font-weight: $weight-light
|
||||
border-radius: 4px
|
||||
|
||||
.notification
|
||||
background: $white
|
||||
border-radius: 4px
|
||||
color: $text
|
||||
font-size: 16px
|
||||
box-shadow: 1px 2px 5px rgba($border, 0.4)
|
||||
border: 1px solid $border-secondary
|
||||
width: 100%
|
||||
padding-left: 2.8rem
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
.label
|
||||
padding: 5px 10px
|
||||
background: $white
|
||||
color: $color
|
||||
font-size: 12px
|
||||
font-family: $weight-semibold
|
||||
width: 100%
|
||||
display: flex
|
||||
align-items: center
|
||||
justify-content: center
|
||||
border: 1px solid $border
|
||||
background: linear-gradient(0deg, #F2F4F7 0%, #FFFFFF 100%)
|
||||
|
||||
&.green
|
||||
background: $green-secondary
|
||||
|
||||
&.red
|
||||
background: $red-secondary
|
||||
|
||||
&.yellow
|
||||
background: $yellow-secondary
|
||||
|
||||
&.blue
|
||||
background: $blue-secondary
|
||||
|
||||
span
|
||||
display: inline-flex
|
||||
float: left
|
||||
align-items: center
|
||||
@@ -1,89 +1,65 @@
|
||||
.message
|
||||
display: block
|
||||
font-size: 14px
|
||||
margin: 20px 0 30px 0
|
||||
font-size: 0.8rem
|
||||
margin: 1rem 0 1.5rem 0
|
||||
padding-bottom: 0.3rem
|
||||
border: 1px solid $border
|
||||
background: $white
|
||||
border-radius: 4px
|
||||
border-radius: $traefik-border-radius
|
||||
box-shadow: 1px 2px 5px rgba($border, 0.4)
|
||||
|
||||
.message-header
|
||||
color: $color-secondary
|
||||
border-bottom: 1px solid $border-secondary
|
||||
padding: 20px 10px
|
||||
background: #f8f9fa
|
||||
border-top-left-radius: 4px
|
||||
border-top-right-radius: 4px
|
||||
padding: 0.6rem
|
||||
border-top-left-radius: $traefik-border-radius
|
||||
border-top-right-radius: $traefik-border-radius
|
||||
|
||||
.icon
|
||||
display: block
|
||||
float: left
|
||||
width: 1.4rem
|
||||
height: 1.4rem
|
||||
margin-right: 0.5rem
|
||||
|
||||
h2
|
||||
font-size: 14px
|
||||
weight: $weight-bold
|
||||
display: flex
|
||||
justify-content: space-between
|
||||
|
||||
&.red
|
||||
background: rgba($red-secondary, 0.4)
|
||||
border-bottom: 1px solid $red-secondary
|
||||
color: $red-secondary
|
||||
|
||||
p
|
||||
color: $red-secondary
|
||||
|
||||
&.green
|
||||
background-color: rgba($green-secondary, 0.4)
|
||||
border-bottom: 1px solid $green-secondary
|
||||
color: $green-secondary
|
||||
|
||||
p
|
||||
color: darken($green-secondary, 10) !important
|
||||
|
||||
&.orange
|
||||
background-color: rgba($orange-secondary, 0.4)
|
||||
border-bottom: 1px solid $orange-secondary
|
||||
color: $orange-secondary
|
||||
|
||||
p
|
||||
color: $orange-secondary
|
||||
|
||||
&.blue
|
||||
background-color: rgba($blue-background, 0.4)
|
||||
border-bottom: 1px solid $blue-background
|
||||
color: $blue-background
|
||||
|
||||
p
|
||||
color: $blue-background !important
|
||||
|
||||
img
|
||||
margin-right: 15px
|
||||
|
||||
.message-body
|
||||
|
||||
.field
|
||||
margin: 5px 10px
|
||||
padding-bottom: 10px
|
||||
.tabs
|
||||
margin-bottom: 0.5rem
|
||||
|
||||
.tags-list
|
||||
margin: 5px 10px
|
||||
.section-container
|
||||
padding: 0.3em 0 0 0
|
||||
|
||||
.control
|
||||
width: 100%
|
||||
margin: 5px 0
|
||||
.section-line
|
||||
padding: 0 0.75em
|
||||
|
||||
.tags
|
||||
width: 100%
|
||||
.section-line-header
|
||||
padding: 0.2em 0 0 0
|
||||
|
||||
.tag
|
||||
width: 50%
|
||||
// required for small screen (without -> table overlapping)
|
||||
.table-fixed
|
||||
table-layout: fixed
|
||||
|
||||
// required for small screen (without -> table overlapping)
|
||||
.table-fixed-break
|
||||
table-layout: fixed
|
||||
word-wrap: break-word
|
||||
|
||||
.table-cell-limited
|
||||
overflow: hidden
|
||||
text-overflow: ellipsis
|
||||
|
||||
.table-col-75
|
||||
width: 75%
|
||||
|
||||
h2
|
||||
margin: 10px 10px 0 10px
|
||||
color: $black
|
||||
|
||||
hr
|
||||
margin: 5px 0
|
||||
|
||||
.message-subheader
|
||||
border-bottom: 1px solid $border-secondary
|
||||
padding: 10px
|
||||
margin-bottom: 5px
|
||||
|
||||
|
||||
@@ -1,16 +1,12 @@
|
||||
.navbar
|
||||
border-bottom: 1px solid $border
|
||||
box-shadow: 1px 2px 5px rgba($border, 0.4)
|
||||
height: 60px
|
||||
|
||||
.navbar-item
|
||||
font-size: 13px
|
||||
font-size: 0.8rem
|
||||
text-transform: uppercase
|
||||
font-weight: $weight-semibold
|
||||
|
||||
.navbar-logo
|
||||
width: 40px
|
||||
min-height: 40px
|
||||
|
||||
&:hover
|
||||
background: transparent
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
=font-face($family, $path, $weight: normal, $style: normal)
|
||||
@font-face
|
||||
font-family: $family
|
||||
src: url('#{$path}.ttf') format('truetype')
|
||||
src: url('./#{$path}.ttf') format('truetype')
|
||||
font-weight: $weight
|
||||
font-style: $style
|
||||
|
||||
+font-face('Open Sans', '/assets/fonts/OpenSans-Light', 300, 'light')
|
||||
+font-face('Open Sans', '/assets/fonts/OpenSans-Regular', 400, 'regular')
|
||||
+font-face('Open Sans', '/assets/fonts/OpenSans-Semibold', 600, 'semibold')
|
||||
+font-face('Open Sans', '/assets/fonts/OpenSans-Bold', 700, 'bold')
|
||||
+font-face('Open Sans', '/assets/fonts/OpenSans-ExtraBold', 800, 'extrabold')
|
||||
+font-face('Open Sans', 'assets/fonts/OpenSans-Light', 300, 'light')
|
||||
+font-face('Open Sans', 'assets/fonts/OpenSans-Regular', 400, 'regular')
|
||||
+font-face('Open Sans', 'assets/fonts/OpenSans-Semibold', 600, 'semibold')
|
||||
+font-face('Open Sans', 'assets/fonts/OpenSans-Bold', 700, 'bold')
|
||||
+font-face('Open Sans', 'assets/fonts/OpenSans-ExtraBold', 800, 'extrabold')
|
||||
|
||||
$open-sans: 'Open Sans', sans-serif
|
||||
|
||||
1
webui/src/styles/variables.sass
Normal file
1
webui/src/styles/variables.sass
Normal file
@@ -0,0 +1 @@
|
||||
$traefik-border-radius: 4px
|
||||
@@ -1031,9 +1031,9 @@ builtin-status-codes@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8"
|
||||
|
||||
bulma@^0.6.2:
|
||||
version "0.6.2"
|
||||
resolved "https://registry.yarnpkg.com/bulma/-/bulma-0.6.2.tgz#f4b1d11d5acc51a79644eb0a2b0b10649d3d71f5"
|
||||
bulma@^0.7.0:
|
||||
version "0.7.1"
|
||||
resolved "https://registry.yarnpkg.com/bulma/-/bulma-0.7.1.tgz#73c2e3b2930c90cc272029cbd19918b493fca486"
|
||||
|
||||
bytes@3.0.0:
|
||||
version "3.0.0"
|
||||
@@ -1698,6 +1698,10 @@ d3-format@1, d3-format@1.2.2:
|
||||
version "1.2.2"
|
||||
resolved "https://registry.yarnpkg.com/d3-format/-/d3-format-1.2.2.tgz#1a39c479c8a57fe5051b2e67a3bee27061a74e7a"
|
||||
|
||||
d3-format@^1.3.0:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/d3-format/-/d3-format-1.3.0.tgz#a3ac44269a2011cdb87c7b5693040c18cddfff11"
|
||||
|
||||
d3-geo@1.9.1:
|
||||
version "1.9.1"
|
||||
resolved "https://registry.yarnpkg.com/d3-geo/-/d3-geo-1.9.1.tgz#157e3b0f917379d0f73bebfff3be537f49fa7356"
|
||||
|
||||
Reference in New Issue
Block a user