Merge 'v1.5.1' into master
This commit is contained in:
commit
d426126a92
16 changed files with 319 additions and 343 deletions
|
@ -24,9 +24,9 @@ before_deploy:
|
||||||
sudo -E apt-get -yq update;
|
sudo -E apt-get -yq update;
|
||||||
sudo -E apt-get -yq --no-install-suggests --no-install-recommends --force-yes install docker-ce=${DOCKER_VERSION}*;
|
sudo -E apt-get -yq --no-install-suggests --no-install-recommends --force-yes install docker-ce=${DOCKER_VERSION}*;
|
||||||
docker version;
|
docker version;
|
||||||
|
make image;
|
||||||
if [ "$TRAVIS_TAG" ]; then
|
if [ "$TRAVIS_TAG" ]; then
|
||||||
make -j${N_MAKE_JOBS} crossbinary-parallel;
|
make -j${N_MAKE_JOBS} crossbinary-parallel;
|
||||||
make image-dirty;
|
|
||||||
tar cfz dist/traefik-${VERSION}.src.tar.gz --exclude-vcs --exclude dist .;
|
tar cfz dist/traefik-${VERSION}.src.tar.gz --exclude-vcs --exclude dist .;
|
||||||
fi;
|
fi;
|
||||||
curl -sI https://github.com/containous/structor/releases/latest | grep -Fi Location | tr -d '\r' | sed "s/tag/download/g" | awk -F " " '{ print $2 "/structor_linux-amd64"}' | wget --output-document=$GOPATH/bin/structor -i -;
|
curl -sI https://github.com/containous/structor/releases/latest | grep -Fi Location | tr -d '\r' | sed "s/tag/download/g" | awk -F " " '{ print $2 "/structor_linux-amd64"}' | wget --output-document=$GOPATH/bin/structor -i -;
|
||||||
|
|
13
CHANGELOG.md
13
CHANGELOG.md
|
@ -1,5 +1,18 @@
|
||||||
# Change Log
|
# Change Log
|
||||||
|
|
||||||
|
## [v1.5.1](https://github.com/containous/traefik/tree/v1.5.1) (2018-01-29)
|
||||||
|
[All Commits](https://github.com/containous/traefik/compare/v1.5.0...v1.5.1)
|
||||||
|
|
||||||
|
**Bug fixes:**
|
||||||
|
- **[acme]** Handle undefined entrypoint on ACME config and frontend config ([#2756](https://github.com/containous/traefik/pull/2756) by [Juliens](https://github.com/Juliens))
|
||||||
|
- **[k8s]** Fix the k8s redirection template. ([#2748](https://github.com/containous/traefik/pull/2748) by [ldez](https://github.com/ldez))
|
||||||
|
- **[middleware]** Change gzipwriter receiver to implement CloseNotifier ([#2766](https://github.com/containous/traefik/pull/2766) by [Juliens](https://github.com/Juliens))
|
||||||
|
- **[tls]** Fix domain names in dynamic TLS configuration ([#2768](https://github.com/containous/traefik/pull/2768) by [nmengin](https://github.com/nmengin))
|
||||||
|
|
||||||
|
**Documentation:**
|
||||||
|
- **[acme]** Add note on redirect for ACME http challenge ([#2767](https://github.com/containous/traefik/pull/2767) by [Juliens](https://github.com/Juliens))
|
||||||
|
- **[file]** Enhance file provider documentation. ([#2777](https://github.com/containous/traefik/pull/2777) by [ldez](https://github.com/ldez))
|
||||||
|
|
||||||
## [v1.5.0](https://github.com/containous/traefik/tree/v1.5.0) (2018-01-23)
|
## [v1.5.0](https://github.com/containous/traefik/tree/v1.5.0) (2018-01-23)
|
||||||
[All Commits](https://github.com/containous/traefik/compare/v1.4.0-rc1...v1.5.0)
|
[All Commits](https://github.com/containous/traefik/compare/v1.4.0-rc1...v1.5.0)
|
||||||
|
|
||||||
|
|
5
Gopkg.lock
generated
5
Gopkg.lock
generated
|
@ -89,7 +89,7 @@
|
||||||
branch = "master"
|
branch = "master"
|
||||||
name = "github.com/NYTimes/gziphandler"
|
name = "github.com/NYTimes/gziphandler"
|
||||||
packages = ["."]
|
packages = ["."]
|
||||||
revision = "47ca22a0aeea4c9ceddfb935d818d636d934c312"
|
revision = "289a3b81f5aedc99f8d6eb0f67827c142f1310d8"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
name = "github.com/Nvveen/Gotty"
|
name = "github.com/Nvveen/Gotty"
|
||||||
|
@ -576,7 +576,6 @@
|
||||||
"metrics/internal/lv",
|
"metrics/internal/lv",
|
||||||
"metrics/internal/ratemap",
|
"metrics/internal/ratemap",
|
||||||
"metrics/multi",
|
"metrics/multi",
|
||||||
"metrics/prometheus",
|
|
||||||
"metrics/statsd",
|
"metrics/statsd",
|
||||||
"util/conn"
|
"util/conn"
|
||||||
]
|
]
|
||||||
|
@ -1512,6 +1511,6 @@
|
||||||
[solve-meta]
|
[solve-meta]
|
||||||
analyzer-name = "dep"
|
analyzer-name = "dep"
|
||||||
analyzer-version = 1
|
analyzer-version = 1
|
||||||
inputs-digest = "0d3d2cd01e06cfcc86cbd261395d59187d4d27d88398bd7aeb1298676becd3e7"
|
inputs-digest = "b9fee5807e09b19baf3763abc285ea122d64d60dba42aaf47eaf9e96774b46bf"
|
||||||
solver-name = "gps-cdcl"
|
solver-name = "gps-cdcl"
|
||||||
solver-version = 1
|
solver-version = 1
|
||||||
|
|
|
@ -142,6 +142,7 @@ func run(globalConfiguration *configuration.GlobalConfiguration, configFile stri
|
||||||
http.DefaultTransport.(*http.Transport).Proxy = http.ProxyFromEnvironment
|
http.DefaultTransport.(*http.Transport).Proxy = http.ProxyFromEnvironment
|
||||||
|
|
||||||
globalConfiguration.SetEffectiveConfiguration(configFile)
|
globalConfiguration.SetEffectiveConfiguration(configFile)
|
||||||
|
globalConfiguration.ValidateConfiguration()
|
||||||
|
|
||||||
jsonConf, _ := json.Marshal(globalConfiguration)
|
jsonConf, _ := json.Marshal(globalConfiguration)
|
||||||
log.Infof("Traefik version %s built on %s", version.Version, version.BuildDate)
|
log.Infof("Traefik version %s built on %s", version.Version, version.BuildDate)
|
||||||
|
|
|
@ -262,6 +262,19 @@ func (gc *GlobalConfiguration) SetEffectiveConfiguration(configFile string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ValidateConfiguration validate that configuration is coherent
|
||||||
|
func (gc *GlobalConfiguration) ValidateConfiguration() {
|
||||||
|
if gc.ACME != nil {
|
||||||
|
if _, ok := gc.EntryPoints[gc.ACME.EntryPoint]; !ok {
|
||||||
|
log.Fatalf("Unknown entrypoint %q for ACME configuration", gc.ACME.EntryPoint)
|
||||||
|
} else {
|
||||||
|
if gc.EntryPoints[gc.ACME.EntryPoint].TLS == nil {
|
||||||
|
log.Fatalf("Entrypoint without TLS %q for ACME configuration", gc.ACME.EntryPoint)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// DefaultEntryPoints holds default entry points
|
// DefaultEntryPoints holds default entry points
|
||||||
type DefaultEntryPoints []string
|
type DefaultEntryPoints []string
|
||||||
|
|
||||||
|
|
|
@ -612,6 +612,7 @@ Those data help us prioritize our developments and focus on what's more importan
|
||||||
### What ?
|
### What ?
|
||||||
|
|
||||||
Once a day (the first call begins 10 minutes after the start of Træfik), we collect:
|
Once a day (the first call begins 10 minutes after the start of Træfik), we collect:
|
||||||
|
|
||||||
- the Træfik version
|
- the Træfik version
|
||||||
- a hash of the configuration
|
- a hash of the configuration
|
||||||
- an **anonymous version** of the static configuration:
|
- an **anonymous version** of the static configuration:
|
||||||
|
|
|
@ -186,6 +186,9 @@ docker run -v "/my/host/acme:/etc/traefik/acme" traefik
|
||||||
|
|
||||||
Use `HTTP-01` challenge to generate/renew ACME certificates.
|
Use `HTTP-01` challenge to generate/renew ACME certificates.
|
||||||
|
|
||||||
|
The redirection is fully compatible with the HTTP-01 challenge.
|
||||||
|
You can use redirection with HTTP-01 challenge without problem.
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
[acme]
|
[acme]
|
||||||
# ...
|
# ...
|
||||||
|
|
|
@ -1,6 +1,140 @@
|
||||||
# File Backends
|
# File Backends
|
||||||
|
|
||||||
Like any other reverse proxy, Træfik can be configured with a file.
|
Træfik can be configured with a file.
|
||||||
|
|
||||||
|
## Reference
|
||||||
|
|
||||||
|
```toml
|
||||||
|
# Backends
|
||||||
|
[backends]
|
||||||
|
|
||||||
|
[backends.backend1]
|
||||||
|
|
||||||
|
[backends.backend1.servers]
|
||||||
|
[backends.backend1.servers.server0]
|
||||||
|
url = "http://10.10.10.1:80"
|
||||||
|
weight = 1
|
||||||
|
[backends.backend1.servers.server1]
|
||||||
|
url = "http://10.10.10.2:80"
|
||||||
|
weight = 2
|
||||||
|
# ...
|
||||||
|
|
||||||
|
[backends.backend1.circuitBreaker]
|
||||||
|
expression = "NetworkErrorRatio() > 0.5"
|
||||||
|
|
||||||
|
[backends.backend1.loadBalancer]
|
||||||
|
method = "drr"
|
||||||
|
[backends.backend1.loadBalancer.stickiness]
|
||||||
|
cookieName = "foobar"
|
||||||
|
|
||||||
|
[backends.backend1.maxConn]
|
||||||
|
amount = 10
|
||||||
|
extractorfunc = "request.host"
|
||||||
|
|
||||||
|
[backends.backend1.healthCheck]
|
||||||
|
path = "/health"
|
||||||
|
port = 88
|
||||||
|
interval = "30s"
|
||||||
|
|
||||||
|
[backends.backend2]
|
||||||
|
# ...
|
||||||
|
|
||||||
|
# Frontends
|
||||||
|
[frontends]
|
||||||
|
|
||||||
|
[frontends.frontend1]
|
||||||
|
entryPoints = ["http", "https"]
|
||||||
|
backend = "backend1"
|
||||||
|
passHostHeader = true
|
||||||
|
passTLSCert = true
|
||||||
|
priority = 42
|
||||||
|
basicAuth = [
|
||||||
|
"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/",
|
||||||
|
"test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
|
||||||
|
]
|
||||||
|
whitelistSourceRange = ["10.42.0.0/16", "152.89.1.33/32", "afed:be44::/16"]
|
||||||
|
|
||||||
|
[frontends.frontend1.routes]
|
||||||
|
[frontends.frontend1.routes.route0]
|
||||||
|
rule = "Host:test.localhost"
|
||||||
|
[frontends.frontend1.routes.Route1]
|
||||||
|
rule = "Method:GET"
|
||||||
|
# ...
|
||||||
|
|
||||||
|
[frontends.frontend1.headers]
|
||||||
|
allowedHosts = ["foobar", "foobar"]
|
||||||
|
hostsProxyHeaders = ["foobar", "foobar"]
|
||||||
|
SSLRedirect = true
|
||||||
|
SSLTemporaryRedirect = true
|
||||||
|
SSLHost = "foobar"
|
||||||
|
STSSeconds = 42
|
||||||
|
STSIncludeSubdomains = true
|
||||||
|
STSPreload = true
|
||||||
|
forceSTSHeader = true
|
||||||
|
frameDeny = true
|
||||||
|
customFrameOptionsValue = "foobar"
|
||||||
|
contentTypeNosniff = true
|
||||||
|
browserXSSFilter = true
|
||||||
|
contentSecurityPolicy = "foobar"
|
||||||
|
publicKey = "foobar"
|
||||||
|
referrerPolicy = "foobar"
|
||||||
|
isDevelopment = true
|
||||||
|
[frontends.frontend1.headers.customRequestHeaders]
|
||||||
|
X-Foo-Bar-01 = "foobar"
|
||||||
|
X-Foo-Bar-02 = "foobar"
|
||||||
|
# ...
|
||||||
|
[frontends.frontend1.headers.customResponseHeaders]
|
||||||
|
X-Foo-Bar-03 = "foobar"
|
||||||
|
X-Foo-Bar-04 = "foobar"
|
||||||
|
# ...
|
||||||
|
[frontends.frontend1.headers.SSLProxyHeaders]
|
||||||
|
X-Foo-Bar-05 = "foobar"
|
||||||
|
X-Foo-Bar-06 = "foobar"
|
||||||
|
# ...
|
||||||
|
|
||||||
|
[frontends.frontend1.errors]
|
||||||
|
[frontends.frontend1.errors.errorPage0]
|
||||||
|
status = ["500-599"]
|
||||||
|
backend = "error"
|
||||||
|
query = "/{status}.html"
|
||||||
|
[frontends.frontend1.errors.errorPage1]
|
||||||
|
status = ["404", "403"]
|
||||||
|
backend = "error"
|
||||||
|
query = "/{status}.html"
|
||||||
|
# ...
|
||||||
|
|
||||||
|
[frontends.frontend1.ratelimit]
|
||||||
|
extractorfunc = "client.ip"
|
||||||
|
[frontends.frontend1.ratelimit.rateset.rateset1]
|
||||||
|
period = "10s"
|
||||||
|
average = 100
|
||||||
|
burst = 200
|
||||||
|
[frontends.frontend1.ratelimit.rateset.rateset2]
|
||||||
|
period = "3s"
|
||||||
|
average = 5
|
||||||
|
burst = 10
|
||||||
|
# ...
|
||||||
|
|
||||||
|
[frontends.frontend1.redirect]
|
||||||
|
entryPoint = "https"
|
||||||
|
regex = "^http://localhost/(.*)"
|
||||||
|
replacement = "http://mydomain/$1"
|
||||||
|
|
||||||
|
[frontends.frontend2]
|
||||||
|
# ...
|
||||||
|
|
||||||
|
# HTTPS certificates
|
||||||
|
[[tls]]
|
||||||
|
entryPoints = ["https"]
|
||||||
|
[tls.certificate]
|
||||||
|
certFile = "path/to/my.cert"
|
||||||
|
keyFile = "path/to/my.key"
|
||||||
|
|
||||||
|
[[tls]]
|
||||||
|
# ...
|
||||||
|
```
|
||||||
|
|
||||||
|
## Configuration mode
|
||||||
|
|
||||||
You have three choices:
|
You have three choices:
|
||||||
|
|
||||||
|
@ -12,7 +146,7 @@ To enable the file backend, you must either pass the `--file` option to the Træ
|
||||||
|
|
||||||
The configuration file allows managing both backends/frontends and HTTPS certificates (which are not [Let's Encrypt](https://letsencrypt.org) certificates generated through Træfik).
|
The configuration file allows managing both backends/frontends and HTTPS certificates (which are not [Let's Encrypt](https://letsencrypt.org) certificates generated through Træfik).
|
||||||
|
|
||||||
## Simple
|
### Simple
|
||||||
|
|
||||||
Add your configuration at the end of the global configuration file `traefik.toml`:
|
Add your configuration at the end of the global configuration file `traefik.toml`:
|
||||||
|
|
||||||
|
@ -21,80 +155,33 @@ defaultEntryPoints = ["http", "https"]
|
||||||
|
|
||||||
[entryPoints]
|
[entryPoints]
|
||||||
[entryPoints.http]
|
[entryPoints.http]
|
||||||
address = ":80"
|
# ...
|
||||||
[entryPoints.http.redirect]
|
|
||||||
entryPoint = "https"
|
|
||||||
[entryPoints.https]
|
[entryPoints.https]
|
||||||
address = ":443"
|
# ...
|
||||||
[entryPoints.https.tls]
|
|
||||||
[[entryPoints.https.tls.certificates]]
|
|
||||||
certFile = "integration/fixtures/https/snitest.org.cert"
|
|
||||||
keyFile = "integration/fixtures/https/snitest.org.key"
|
|
||||||
|
|
||||||
[file]
|
[file]
|
||||||
|
|
||||||
# rules
|
# rules
|
||||||
[backends]
|
[backends]
|
||||||
[backends.backend1]
|
[backends.backend1]
|
||||||
[backends.backend1.circuitbreaker]
|
# ...
|
||||||
expression = "NetworkErrorRatio() > 0.5"
|
|
||||||
[backends.backend1.servers.server1]
|
|
||||||
url = "http://172.17.0.2:80"
|
|
||||||
weight = 10
|
|
||||||
[backends.backend1.servers.server2]
|
|
||||||
url = "http://172.17.0.3:80"
|
|
||||||
weight = 1
|
|
||||||
[backends.backend2]
|
[backends.backend2]
|
||||||
[backends.backend2.maxconn]
|
# ...
|
||||||
amount = 10
|
|
||||||
extractorfunc = "request.host"
|
|
||||||
[backends.backend2.LoadBalancer]
|
|
||||||
method = "drr"
|
|
||||||
[backends.backend2.servers.server1]
|
|
||||||
url = "http://172.17.0.4:80"
|
|
||||||
weight = 1
|
|
||||||
[backends.backend2.servers.server2]
|
|
||||||
url = "http://172.17.0.5:80"
|
|
||||||
weight = 2
|
|
||||||
|
|
||||||
[frontends]
|
[frontends]
|
||||||
[frontends.frontend1]
|
[frontends.frontend1]
|
||||||
backend = "backend2"
|
# ...
|
||||||
[frontends.frontend1.routes.test_1]
|
|
||||||
rule = "Host:test.localhost"
|
|
||||||
|
|
||||||
[frontends.frontend2]
|
[frontends.frontend2]
|
||||||
backend = "backend1"
|
# ...
|
||||||
passHostHeader = true
|
|
||||||
priority = 10
|
|
||||||
|
|
||||||
# restrict access to this frontend to the specified list of IPv4/IPv6 CIDR Nets
|
|
||||||
# an unset or empty list allows all Source-IPs to access
|
|
||||||
# if one of the Net-Specifications are invalid, the whole list is invalid
|
|
||||||
# and allows all Source-IPs to access.
|
|
||||||
whitelistSourceRange = ["10.42.0.0/16", "152.89.1.33/32", "afed:be44::/16"]
|
|
||||||
|
|
||||||
entrypoints = ["https"] # overrides defaultEntryPoints
|
|
||||||
[frontends.frontend2.routes.test_1]
|
|
||||||
rule = "Host:{subdomain:[a-z]+}.localhost"
|
|
||||||
|
|
||||||
[frontends.frontend3]
|
[frontends.frontend3]
|
||||||
entrypoints = ["http", "https"] # overrides defaultEntryPoints
|
# ...
|
||||||
backend = "backend2"
|
|
||||||
rule = "Path:/test"
|
|
||||||
|
|
||||||
# HTTPS certificate
|
# HTTPS certificate
|
||||||
[[tls]]
|
[[tls]]
|
||||||
entryPoints = ["https"]
|
# ...
|
||||||
[tls.certificate]
|
|
||||||
certFile = "path/to/my.cert"
|
|
||||||
keyFile = "path/to/my.key"
|
|
||||||
|
|
||||||
[[tls]]
|
[[tls]]
|
||||||
entryPoints = ["https"]
|
# ...
|
||||||
[tls.certificate]
|
|
||||||
certFile = "path/to/my/other.cert"
|
|
||||||
keyFile = "path/to/my/other.key"
|
|
||||||
```
|
```
|
||||||
|
|
||||||
!!! note
|
!!! note
|
||||||
|
@ -104,92 +191,60 @@ defaultEntryPoints = ["http", "https"]
|
||||||
Adding certificates directly to the entryPoint is still maintained but certificates declared in this way cannot be managed dynamically.
|
Adding certificates directly to the entryPoint is still maintained but certificates declared in this way cannot be managed dynamically.
|
||||||
It's recommended to use the file provider to declare certificates.
|
It's recommended to use the file provider to declare certificates.
|
||||||
|
|
||||||
## Rules in a Separate File
|
### Rules in a Separate File
|
||||||
|
|
||||||
Put your rules in a separate file, for example `rules.toml`:
|
Put your rules in a separate file, for example `rules.toml`:
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
# traefik.toml
|
# traefik.toml
|
||||||
|
defaultEntryPoints = ["http", "https"]
|
||||||
|
|
||||||
[entryPoints]
|
[entryPoints]
|
||||||
[entryPoints.http]
|
[entryPoints.http]
|
||||||
address = ":80"
|
# ...
|
||||||
[entryPoints.http.redirect]
|
|
||||||
entryPoint = "https"
|
|
||||||
[entryPoints.https]
|
[entryPoints.https]
|
||||||
address = ":443"
|
# ...
|
||||||
[entryPoints.https.tls]
|
|
||||||
|
|
||||||
[file]
|
[file]
|
||||||
filename = "rules.toml"
|
filename = "rules.toml"
|
||||||
```
|
```
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
# rules.toml
|
# rules.toml
|
||||||
[backends]
|
[backends]
|
||||||
[backends.backend1]
|
[backends.backend1]
|
||||||
[backends.backend1.circuitbreaker]
|
# ...
|
||||||
expression = "NetworkErrorRatio() > 0.5"
|
|
||||||
[backends.backend1.servers.server1]
|
|
||||||
url = "http://172.17.0.2:80"
|
|
||||||
weight = 10
|
|
||||||
[backends.backend1.servers.server2]
|
|
||||||
url = "http://172.17.0.3:80"
|
|
||||||
weight = 1
|
|
||||||
[backends.backend2]
|
[backends.backend2]
|
||||||
[backends.backend2.maxconn]
|
# ...
|
||||||
amount = 10
|
|
||||||
extractorfunc = "request.host"
|
|
||||||
[backends.backend2.LoadBalancer]
|
|
||||||
method = "drr"
|
|
||||||
[backends.backend2.servers.server1]
|
|
||||||
url = "http://172.17.0.4:80"
|
|
||||||
weight = 1
|
|
||||||
[backends.backend2.servers.server2]
|
|
||||||
url = "http://172.17.0.5:80"
|
|
||||||
weight = 2
|
|
||||||
|
|
||||||
[frontends]
|
[frontends]
|
||||||
[frontends.frontend1]
|
[frontends.frontend1]
|
||||||
backend = "backend2"
|
# ...
|
||||||
[frontends.frontend1.routes.test_1]
|
|
||||||
rule = "Host:test.localhost"
|
|
||||||
[frontends.frontend2]
|
[frontends.frontend2]
|
||||||
backend = "backend1"
|
# ...
|
||||||
passHostHeader = true
|
|
||||||
priority = 10
|
|
||||||
entrypoints = ["https"] # overrides defaultEntryPoints
|
|
||||||
[frontends.frontend2.routes.test_1]
|
|
||||||
rule = "Host:{subdomain:[a-z]+}.localhost"
|
|
||||||
[frontends.frontend3]
|
[frontends.frontend3]
|
||||||
entrypoints = ["http", "https"] # overrides defaultEntryPoints
|
# ...
|
||||||
backend = "backend2"
|
|
||||||
rule = "Path:/test"
|
|
||||||
|
|
||||||
# HTTPS certificate
|
# HTTPS certificate
|
||||||
[[tls]]
|
[[tls]]
|
||||||
entryPoints = ["https"]
|
# ...
|
||||||
[tls.certificate]
|
|
||||||
certFile = "path/to/my.cert"
|
|
||||||
keyFile = "path/to/my.key"
|
|
||||||
|
|
||||||
[[tls]]
|
|
||||||
entryPoints = ["https"]
|
|
||||||
[tls.certificate]
|
|
||||||
certFile = "path/to/my/other.cert"
|
|
||||||
keyFile = "path/to/my/other.key"
|
|
||||||
|
|
||||||
## Multiple `.toml` Files
|
[[tls]]
|
||||||
|
# ...
|
||||||
|
```
|
||||||
|
|
||||||
|
### Multiple `.toml` Files
|
||||||
|
|
||||||
You could have multiple `.toml` files in a directory (and recursively in its sub-directories):
|
You could have multiple `.toml` files in a directory (and recursively in its sub-directories):
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
[file]
|
[file]
|
||||||
directory = "/path/to/config/"
|
directory = "/path/to/config/"
|
||||||
```
|
```
|
||||||
|
|
||||||
If you want Træfik to watch file changes automatically, just add:
|
If you want Træfik to watch file changes automatically, just add:
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
[file]
|
[file]
|
||||||
watch = true
|
watch = true
|
||||||
```
|
```
|
||||||
|
|
|
@ -150,7 +150,7 @@ The following security annotations are applicable on the Ingress object:
|
||||||
| `ingress.kubernetes.io/ssl-host:HOST` | This setting configures the hostname that redirects will be based on. Default is "", which is the same host as the request. |
|
| `ingress.kubernetes.io/ssl-host:HOST` | This setting configures the hostname that redirects will be based on. Default is "", which is the same host as the request. |
|
||||||
| `ingress.kubernetes.io/ssl-proxy-headers:EXPR` | Header combinations that would signify a proper SSL Request (Such as `X-Forwarded-For:https`). Format: <code>HEADER:value||HEADER2:value2</code> |
|
| `ingress.kubernetes.io/ssl-proxy-headers:EXPR` | Header combinations that would signify a proper SSL Request (Such as `X-Forwarded-For:https`). Format: <code>HEADER:value||HEADER2:value2</code> |
|
||||||
| `ingress.kubernetes.io/hsts-max-age:315360000` | Sets the max-age of the HSTS header. |
|
| `ingress.kubernetes.io/hsts-max-age:315360000` | Sets the max-age of the HSTS header. |
|
||||||
| `ngress.kubernetes.io/hsts-include-subdomains:true` | Adds the IncludeSubdomains section of the STS header. |
|
| `ingress.kubernetes.io/hsts-include-subdomains:true` | Adds the IncludeSubdomains section of the STS header. |
|
||||||
| `ingress.kubernetes.io/hsts-preload:true` | Adds the preload flag to the HSTS header. |
|
| `ingress.kubernetes.io/hsts-preload:true` | Adds the preload flag to the HSTS header. |
|
||||||
| `ingress.kubernetes.io/force-hsts:false` | Adds the STS header to non-SSL requests. |
|
| `ingress.kubernetes.io/force-hsts:false` | Adds the STS header to non-SSL requests. |
|
||||||
| `ingress.kubernetes.io/frame-deny:false` | Adds the `X-Frame-Options` header with the value of `DENY`. |
|
| `ingress.kubernetes.io/frame-deny:false` | Adds the `X-Frame-Options` header with the value of `DENY`. |
|
||||||
|
|
|
@ -282,21 +282,17 @@ Multiple sets of rates can be added to each frontend, but the time periods must
|
||||||
```toml
|
```toml
|
||||||
[frontends]
|
[frontends]
|
||||||
[frontends.frontend1]
|
[frontends.frontend1]
|
||||||
passHostHeader = true
|
# ...
|
||||||
entrypoints = ["http"]
|
[frontends.frontend1.ratelimit]
|
||||||
backend = "backend1"
|
extractorfunc = "client.ip"
|
||||||
[frontends.frontend1.routes.test_1]
|
[frontends.frontend1.ratelimit.rateset.rateset1]
|
||||||
rule = "Path:/"
|
period = "10s"
|
||||||
[frontends.frontend1.ratelimit]
|
average = 100
|
||||||
extractorfunc = "client.ip"
|
burst = 200
|
||||||
[frontends.frontend1.ratelimit.rateset.rateset1]
|
[frontends.frontend1.ratelimit.rateset.rateset2]
|
||||||
period = "10s"
|
period = "3s"
|
||||||
average = 100
|
average = 5
|
||||||
burst = 200
|
burst = 10
|
||||||
[frontends.frontend1.ratelimit.rateset.rateset2]
|
|
||||||
period = "3s"
|
|
||||||
average = 5
|
|
||||||
burst = 10
|
|
||||||
```
|
```
|
||||||
|
|
||||||
In the above example, frontend1 is configured to limit requests by the client's ip address.
|
In the above example, frontend1 is configured to limit requests by the client's ip address.
|
||||||
|
|
|
@ -1,5 +1,72 @@
|
||||||
# Entry Points Definition
|
# Entry Points Definition
|
||||||
|
|
||||||
|
## Reference
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[entryPoints]
|
||||||
|
[entryPoints.http]
|
||||||
|
address = ":80"
|
||||||
|
whitelistSourceRange = ["10.42.0.0/16", "152.89.1.33/32", "afed:be44::/16"]
|
||||||
|
compress = true
|
||||||
|
|
||||||
|
[entryPoints.http.tls]
|
||||||
|
minVersion = "VersionTLS12"
|
||||||
|
cipherSuites = ["TLS_RSA_WITH_AES_256_GCM_SHA384"]
|
||||||
|
[[entryPoints.http.tls.certificates]]
|
||||||
|
certFile = "path/to/my.cert"
|
||||||
|
keyFile = "path/to/my.key"
|
||||||
|
[[entryPoints.http.tls.certificates]]
|
||||||
|
certFile = "path/to/other.cert"
|
||||||
|
keyFile = "path/to/other.key"
|
||||||
|
# ...
|
||||||
|
[entryPoints.http.tls.clientCA]
|
||||||
|
files = ["path/to/ca1.crt", "path/to/ca2.crt"]
|
||||||
|
optional = false
|
||||||
|
|
||||||
|
[entryPoints.http.redirect]
|
||||||
|
entryPoint = "https"
|
||||||
|
regex = "^http://localhost/(.*)"
|
||||||
|
replacement = "http://mydomain/$1"
|
||||||
|
permanent = true
|
||||||
|
|
||||||
|
[entryPoints.http.auth]
|
||||||
|
headerField = "X-WebAuth-User"
|
||||||
|
[entryPoints.http.auth.basic]
|
||||||
|
users = [
|
||||||
|
"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/",
|
||||||
|
"test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
|
||||||
|
]
|
||||||
|
usersFile = "/path/to/.htpasswd"
|
||||||
|
[entryPoints.http.auth.digest]
|
||||||
|
users = [
|
||||||
|
"test:traefik:a2688e031edb4be6a3797f3882655c05",
|
||||||
|
"test2:traefik:518845800f9e2bfb1f1f740ec24f074e",
|
||||||
|
]
|
||||||
|
usersFile = "/path/to/.htdigest"
|
||||||
|
[entryPoints.http.auth.forward]
|
||||||
|
address = "https://authserver.com/auth"
|
||||||
|
trustForwardHeader = true
|
||||||
|
[entryPoints.http.auth.forward.tls]
|
||||||
|
ca = [ "path/to/local.crt"]
|
||||||
|
caOptional = true
|
||||||
|
cert = "path/to/foo.cert"
|
||||||
|
key = "path/to/foo.key"
|
||||||
|
insecureSkipVerify = true
|
||||||
|
|
||||||
|
[entryPoints.http.proxyProtocol]
|
||||||
|
insecure = true
|
||||||
|
trustedIPs = ["10.10.10.1", "10.10.10.2"]
|
||||||
|
|
||||||
|
[entryPoints.http.forwardedHeaders]
|
||||||
|
trustedIPs = ["10.10.10.1", "10.10.10.2"]
|
||||||
|
|
||||||
|
[entryPoints.https]
|
||||||
|
# ...
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Basic
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
# Entrypoints definition
|
# Entrypoints definition
|
||||||
#
|
#
|
||||||
|
@ -71,7 +138,7 @@ Define an entrypoint with SNI support.
|
||||||
|
|
||||||
!!! note
|
!!! note
|
||||||
If an empty TLS configuration is done, default self-signed certificates are generated.
|
If an empty TLS configuration is done, default self-signed certificates are generated.
|
||||||
|
|
||||||
|
|
||||||
### Dynamic Certificates
|
### Dynamic Certificates
|
||||||
|
|
||||||
|
@ -162,7 +229,7 @@ Otherwise, the response from the auth server is returned.
|
||||||
# To enable forward auth on an entrypoint
|
# To enable forward auth on an entrypoint
|
||||||
[entryPoints.http.auth.forward]
|
[entryPoints.http.auth.forward]
|
||||||
address = "https://authserver.com/auth"
|
address = "https://authserver.com/auth"
|
||||||
|
|
||||||
# Trust existing X-Forwarded-* headers.
|
# Trust existing X-Forwarded-* headers.
|
||||||
# Useful with another reverse proxy in front of Traefik.
|
# Useful with another reverse proxy in front of Traefik.
|
||||||
#
|
#
|
||||||
|
@ -170,7 +237,7 @@ Otherwise, the response from the auth server is returned.
|
||||||
# Default: false
|
# Default: false
|
||||||
#
|
#
|
||||||
trustForwardHeader = true
|
trustForwardHeader = true
|
||||||
|
|
||||||
# Enable forward auth TLS connection.
|
# Enable forward auth TLS connection.
|
||||||
#
|
#
|
||||||
# Optional
|
# Optional
|
||||||
|
@ -234,7 +301,7 @@ Only IPs in `trustedIPs` will lead to remote client address replacement: you sho
|
||||||
|
|
||||||
!!! danger
|
!!! danger
|
||||||
When queuing Træfik behind another load-balancer, be sure to carefully configure Proxy Protocol on both sides.
|
When queuing Træfik behind another load-balancer, be sure to carefully configure Proxy Protocol on both sides.
|
||||||
Otherwise, it could introduce a security risk in your system by forging requests.
|
Otherwise, it could introduce a security risk in your system by forging requests.
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
[entryPoints]
|
[entryPoints]
|
||||||
|
|
|
@ -28,14 +28,6 @@ echo $VERSION | git commit --file -
|
||||||
echo $VERSION | git tag -a $VERSION --file -
|
echo $VERSION | git tag -a $VERSION --file -
|
||||||
git push -q --follow-tags -u origin master > /dev/null 2>&1
|
git push -q --follow-tags -u origin master > /dev/null 2>&1
|
||||||
|
|
||||||
# create docker image emilevauge/traefik (compatibility)
|
|
||||||
echo "Updating docker emilevauge/traefik image..."
|
|
||||||
docker login -u $DOCKER_USER -p $DOCKER_PASS
|
|
||||||
docker tag containous/traefik emilevauge/traefik:latest
|
|
||||||
docker push emilevauge/traefik:latest
|
|
||||||
docker tag emilevauge/traefik:latest emilevauge/traefik:${VERSION}
|
|
||||||
docker push emilevauge/traefik:${VERSION}
|
|
||||||
|
|
||||||
cd ..
|
cd ..
|
||||||
rm -Rf traefik-library-image/
|
rm -Rf traefik-library-image/
|
||||||
|
|
||||||
|
|
|
@ -635,31 +635,27 @@ func (s *Server) createTLSConfig(entryPointName string, tlsOption *traefikTls.TL
|
||||||
}
|
}
|
||||||
|
|
||||||
if s.globalConfiguration.ACME != nil {
|
if s.globalConfiguration.ACME != nil {
|
||||||
if _, ok := s.serverEntryPoints[s.globalConfiguration.ACME.EntryPoint]; ok {
|
if entryPointName == s.globalConfiguration.ACME.EntryPoint {
|
||||||
if entryPointName == s.globalConfiguration.ACME.EntryPoint {
|
checkOnDemandDomain := func(domain string) bool {
|
||||||
checkOnDemandDomain := func(domain string) bool {
|
routeMatch := &mux.RouteMatch{}
|
||||||
routeMatch := &mux.RouteMatch{}
|
router := router.GetHandler()
|
||||||
router := router.GetHandler()
|
match := router.Match(&http.Request{URL: &url.URL{}, Host: domain}, routeMatch)
|
||||||
match := router.Match(&http.Request{URL: &url.URL{}, Host: domain}, routeMatch)
|
if match && routeMatch.Route != nil {
|
||||||
if match && routeMatch.Route != nil {
|
return true
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
if s.leadership == nil {
|
return false
|
||||||
err := s.globalConfiguration.ACME.CreateLocalConfig(config, &s.serverEntryPoints[entryPointName].certs, checkOnDemandDomain)
|
}
|
||||||
if err != nil {
|
if s.leadership == nil {
|
||||||
return nil, err
|
err := s.globalConfiguration.ACME.CreateLocalConfig(config, &s.serverEntryPoints[entryPointName].certs, checkOnDemandDomain)
|
||||||
}
|
if err != nil {
|
||||||
} else {
|
return nil, err
|
||||||
err := s.globalConfiguration.ACME.CreateClusterConfig(s.leadership, config, &s.serverEntryPoints[entryPointName].certs, checkOnDemandDomain)
|
}
|
||||||
if err != nil {
|
} else {
|
||||||
return nil, err
|
err := s.globalConfiguration.ACME.CreateClusterConfig(s.leadership, config, &s.serverEntryPoints[entryPointName].certs, checkOnDemandDomain)
|
||||||
}
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
return nil, errors.New("Unknown entrypoint " + s.globalConfiguration.ACME.EntryPoint + " for ACME configuration")
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
config.GetCertificate = s.serverEntryPoints[entryPointName].getCertificate
|
config.GetCertificate = s.serverEntryPoints[entryPointName].getCertificate
|
||||||
|
@ -887,23 +883,23 @@ func (s *Server) loadConfig(configurations types.Configurations, globalConfigura
|
||||||
|
|
||||||
log.Debugf("Creating frontend %s", frontendName)
|
log.Debugf("Creating frontend %s", frontendName)
|
||||||
|
|
||||||
|
var frontendEntryPoints []string
|
||||||
|
for _, entryPointName := range frontend.EntryPoints {
|
||||||
|
if _, ok := serverEntryPoints[entryPointName]; !ok {
|
||||||
|
log.Errorf("Undefined entrypoint '%s' for frontend %s", entryPointName, frontendName)
|
||||||
|
} else {
|
||||||
|
frontendEntryPoints = append(frontendEntryPoints, entryPointName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
frontend.EntryPoints = frontendEntryPoints
|
||||||
|
|
||||||
if len(frontend.EntryPoints) == 0 {
|
if len(frontend.EntryPoints) == 0 {
|
||||||
log.Errorf("No entrypoint defined for frontend %s, defaultEntryPoints:%s", frontendName, globalConfiguration.DefaultEntryPoints)
|
log.Errorf("No entrypoint defined for frontend %s", frontendName)
|
||||||
log.Errorf("Skipping frontend %s...", frontendName)
|
log.Errorf("Skipping frontend %s...", frontendName)
|
||||||
continue frontend
|
continue frontend
|
||||||
}
|
}
|
||||||
var failedEntrypoints int
|
|
||||||
for _, entryPointName := range frontend.EntryPoints {
|
for _, entryPointName := range frontend.EntryPoints {
|
||||||
log.Debugf("Wiring frontend %s to entryPoint %s", frontendName, entryPointName)
|
log.Debugf("Wiring frontend %s to entryPoint %s", frontendName, entryPointName)
|
||||||
if _, ok := serverEntryPoints[entryPointName]; !ok {
|
|
||||||
log.Errorf("Undefined entrypoint '%s' for frontend %s", entryPointName, frontendName)
|
|
||||||
failedEntrypoints++
|
|
||||||
if failedEntrypoints == len(frontend.EntryPoints) {
|
|
||||||
log.Errorf("Skipping frontend %s...", frontendName)
|
|
||||||
continue frontend
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
newServerRoute := &serverRoute{route: serverEntryPoints[entryPointName].httpRouter.GetHandler().NewRoute().Name(frontendName)}
|
newServerRoute := &serverRoute{route: serverEntryPoints[entryPointName].httpRouter.GetHandler().NewRoute().Name(frontendName)}
|
||||||
for routeName, route := range frontend.Routes {
|
for routeName, route := range frontend.Routes {
|
||||||
|
|
|
@ -150,7 +150,12 @@ func (c *Certificate) AppendCertificates(certs map[string]*DomainsCertificates,
|
||||||
certKey := parsedCert.Subject.CommonName
|
certKey := parsedCert.Subject.CommonName
|
||||||
if parsedCert.DNSNames != nil {
|
if parsedCert.DNSNames != nil {
|
||||||
sort.Strings(parsedCert.DNSNames)
|
sort.Strings(parsedCert.DNSNames)
|
||||||
certKey += fmt.Sprintf("%s,%s", parsedCert.Subject.CommonName, strings.Join(parsedCert.DNSNames, ","))
|
for _, dnsName := range parsedCert.DNSNames {
|
||||||
|
if dnsName != parsedCert.Subject.CommonName {
|
||||||
|
certKey += fmt.Sprintf(",%s", dnsName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
certExists := false
|
certExists := false
|
||||||
|
|
2
vendor/github.com/NYTimes/gziphandler/gzip.go
generated
vendored
2
vendor/github.com/NYTimes/gziphandler/gzip.go
generated
vendored
|
@ -88,7 +88,7 @@ type GzipResponseWriterWithCloseNotify struct {
|
||||||
*GzipResponseWriter
|
*GzipResponseWriter
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *GzipResponseWriterWithCloseNotify) CloseNotify() <-chan bool {
|
func (w GzipResponseWriterWithCloseNotify) CloseNotify() <-chan bool {
|
||||||
return w.ResponseWriter.(http.CloseNotifier).CloseNotify()
|
return w.ResponseWriter.(http.CloseNotifier).CloseNotify()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
165
vendor/github.com/go-kit/kit/metrics/prometheus/prometheus.go
generated
vendored
165
vendor/github.com/go-kit/kit/metrics/prometheus/prometheus.go
generated
vendored
|
@ -1,165 +0,0 @@
|
||||||
// Package prometheus provides Prometheus implementations for metrics.
|
|
||||||
// Individual metrics are mapped to their Prometheus counterparts, and
|
|
||||||
// (depending on the constructor used) may be automatically registered in the
|
|
||||||
// global Prometheus metrics registry.
|
|
||||||
package prometheus
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
|
||||||
|
|
||||||
"github.com/go-kit/kit/metrics"
|
|
||||||
"github.com/go-kit/kit/metrics/internal/lv"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Counter implements Counter, via a Prometheus CounterVec.
|
|
||||||
type Counter struct {
|
|
||||||
cv *prometheus.CounterVec
|
|
||||||
lvs lv.LabelValues
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewCounterFrom constructs and registers a Prometheus CounterVec,
|
|
||||||
// and returns a usable Counter object.
|
|
||||||
func NewCounterFrom(opts prometheus.CounterOpts, labelNames []string) *Counter {
|
|
||||||
cv := prometheus.NewCounterVec(opts, labelNames)
|
|
||||||
prometheus.MustRegister(cv)
|
|
||||||
return NewCounter(cv)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewCounter wraps the CounterVec and returns a usable Counter object.
|
|
||||||
func NewCounter(cv *prometheus.CounterVec) *Counter {
|
|
||||||
return &Counter{
|
|
||||||
cv: cv,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// With implements Counter.
|
|
||||||
func (c *Counter) With(labelValues ...string) metrics.Counter {
|
|
||||||
return &Counter{
|
|
||||||
cv: c.cv,
|
|
||||||
lvs: c.lvs.With(labelValues...),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add implements Counter.
|
|
||||||
func (c *Counter) Add(delta float64) {
|
|
||||||
c.cv.With(makeLabels(c.lvs...)).Add(delta)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Gauge implements Gauge, via a Prometheus GaugeVec.
|
|
||||||
type Gauge struct {
|
|
||||||
gv *prometheus.GaugeVec
|
|
||||||
lvs lv.LabelValues
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewGaugeFrom construts and registers a Prometheus GaugeVec,
|
|
||||||
// and returns a usable Gauge object.
|
|
||||||
func NewGaugeFrom(opts prometheus.GaugeOpts, labelNames []string) *Gauge {
|
|
||||||
gv := prometheus.NewGaugeVec(opts, labelNames)
|
|
||||||
prometheus.MustRegister(gv)
|
|
||||||
return NewGauge(gv)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewGauge wraps the GaugeVec and returns a usable Gauge object.
|
|
||||||
func NewGauge(gv *prometheus.GaugeVec) *Gauge {
|
|
||||||
return &Gauge{
|
|
||||||
gv: gv,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// With implements Gauge.
|
|
||||||
func (g *Gauge) With(labelValues ...string) metrics.Gauge {
|
|
||||||
return &Gauge{
|
|
||||||
gv: g.gv,
|
|
||||||
lvs: g.lvs.With(labelValues...),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set implements Gauge.
|
|
||||||
func (g *Gauge) Set(value float64) {
|
|
||||||
g.gv.With(makeLabels(g.lvs...)).Set(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add is supported by Prometheus GaugeVecs.
|
|
||||||
func (g *Gauge) Add(delta float64) {
|
|
||||||
g.gv.With(makeLabels(g.lvs...)).Add(delta)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Summary implements Histogram, via a Prometheus SummaryVec. The difference
|
|
||||||
// between a Summary and a Histogram is that Summaries don't require predefined
|
|
||||||
// quantile buckets, but cannot be statistically aggregated.
|
|
||||||
type Summary struct {
|
|
||||||
sv *prometheus.SummaryVec
|
|
||||||
lvs lv.LabelValues
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewSummaryFrom constructs and registers a Prometheus SummaryVec,
|
|
||||||
// and returns a usable Summary object.
|
|
||||||
func NewSummaryFrom(opts prometheus.SummaryOpts, labelNames []string) *Summary {
|
|
||||||
sv := prometheus.NewSummaryVec(opts, labelNames)
|
|
||||||
prometheus.MustRegister(sv)
|
|
||||||
return NewSummary(sv)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewSummary wraps the SummaryVec and returns a usable Summary object.
|
|
||||||
func NewSummary(sv *prometheus.SummaryVec) *Summary {
|
|
||||||
return &Summary{
|
|
||||||
sv: sv,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// With implements Histogram.
|
|
||||||
func (s *Summary) With(labelValues ...string) metrics.Histogram {
|
|
||||||
return &Summary{
|
|
||||||
sv: s.sv,
|
|
||||||
lvs: s.lvs.With(labelValues...),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Observe implements Histogram.
|
|
||||||
func (s *Summary) Observe(value float64) {
|
|
||||||
s.sv.With(makeLabels(s.lvs...)).Observe(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Histogram implements Histogram via a Prometheus HistogramVec. The difference
|
|
||||||
// between a Histogram and a Summary is that Histograms require predefined
|
|
||||||
// quantile buckets, and can be statistically aggregated.
|
|
||||||
type Histogram struct {
|
|
||||||
hv *prometheus.HistogramVec
|
|
||||||
lvs lv.LabelValues
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewHistogramFrom constructs and registers a Prometheus HistogramVec,
|
|
||||||
// and returns a usable Histogram object.
|
|
||||||
func NewHistogramFrom(opts prometheus.HistogramOpts, labelNames []string) *Histogram {
|
|
||||||
hv := prometheus.NewHistogramVec(opts, labelNames)
|
|
||||||
prometheus.MustRegister(hv)
|
|
||||||
return NewHistogram(hv)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewHistogram wraps the HistogramVec and returns a usable Histogram object.
|
|
||||||
func NewHistogram(hv *prometheus.HistogramVec) *Histogram {
|
|
||||||
return &Histogram{
|
|
||||||
hv: hv,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// With implements Histogram.
|
|
||||||
func (h *Histogram) With(labelValues ...string) metrics.Histogram {
|
|
||||||
return &Histogram{
|
|
||||||
hv: h.hv,
|
|
||||||
lvs: h.lvs.With(labelValues...),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Observe implements Histogram.
|
|
||||||
func (h *Histogram) Observe(value float64) {
|
|
||||||
h.hv.With(makeLabels(h.lvs...)).Observe(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
func makeLabels(labelValues ...string) prometheus.Labels {
|
|
||||||
labels := prometheus.Labels{}
|
|
||||||
for i := 0; i < len(labelValues); i += 2 {
|
|
||||||
labels[labelValues[i]] = labelValues[i+1]
|
|
||||||
}
|
|
||||||
return labels
|
|
||||||
}
|
|
Loading…
Reference in a new issue