mirror of
https://github.com/containous/traefik.git
synced 2025-09-19 01:44:23 +03:00
Compare commits
37 Commits
v2.2.7
...
v2.3.0-rc3
Author | SHA1 | Date | |
---|---|---|---|
|
3942962ef5 | ||
|
675655d437 | ||
|
dafb14ff37 | ||
|
fc52d1cfba | ||
|
fdf2a68a11 | ||
|
3908ef611a | ||
|
e63db782c1 | ||
|
a6c6127e33 | ||
|
207d0bec78 | ||
|
1443c8d4c6 | ||
|
a136c46148 | ||
|
dcd0cda0c6 | ||
|
44a244b1cb | ||
|
1dc6f39b55 | ||
|
4957e498af | ||
|
54ca1abd2b | ||
|
8f2951b275 | ||
|
720bef97e6 | ||
|
c42f1b7a50 | ||
|
0186c31d59 | ||
|
58bf1a2ca5 | ||
|
4a31544024 | ||
|
cb6ec507e2 | ||
|
1ef93fead7 | ||
|
285ded6e49 | ||
|
6e4f5821dc | ||
|
73ca7ad0c1 | ||
|
ed216bea4d | ||
|
7669f41e8e | ||
|
73513f8371 | ||
|
cb1d0441e9 | ||
|
7affeae480 | ||
|
7928e6d0cd | ||
|
a98b726263 | ||
|
5f0b6fde92 | ||
|
6b1158235e | ||
|
f624449ccb |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -16,3 +16,4 @@
|
||||
*.exe
|
||||
cover.out
|
||||
vendor/
|
||||
plugins-storage/
|
||||
|
@@ -100,7 +100,7 @@
|
||||
text = "string `traefik` has (\\d) occurrences, make it a constant"
|
||||
[[issues.exclude-rules]]
|
||||
path = "pkg/server/middleware/middlewares.go"
|
||||
text = "Function 'buildConstructor' is too long \\(\\d+ > 230\\)"
|
||||
text = "Function 'buildConstructor' has too many statements"
|
||||
[[issues.exclude-rules]] # FIXME must be fixed
|
||||
path = "cmd/context.go"
|
||||
text = "S1000: should use a simple channel send/receive instead of `select` with a single case"
|
||||
@@ -112,4 +112,4 @@
|
||||
text = "printf-like formatting function 'SetErrorWithEvent' should be named 'SetErrorWithEventf'"
|
||||
[[issues.exclude-rules]]
|
||||
path = "pkg/log/deprecated.go"
|
||||
linters = ["godot"]
|
||||
linters = ["godot"]
|
@@ -7,7 +7,7 @@ before:
|
||||
builds:
|
||||
- binary: traefik
|
||||
|
||||
main: ./cmd/traefik/traefik.go
|
||||
main: ./cmd/traefik/
|
||||
env:
|
||||
- CGO_ENABLED=0
|
||||
ldflags:
|
||||
|
@@ -10,7 +10,7 @@ else
|
||||
export VERSION=''
|
||||
fi
|
||||
|
||||
export CODENAME=chevrotin
|
||||
export CODENAME=picodon
|
||||
|
||||
export N_MAKE_JOBS=2
|
||||
|
||||
|
@@ -11,7 +11,7 @@ env:
|
||||
global:
|
||||
- REPO=$TRAVIS_REPO_SLUG
|
||||
- VERSION=$TRAVIS_TAG
|
||||
- CODENAME=chevrotin
|
||||
- CODENAME=picodon
|
||||
- GO111MODULE=on
|
||||
|
||||
script:
|
||||
|
62
CHANGELOG.md
62
CHANGELOG.md
@@ -1,3 +1,30 @@
|
||||
## [v2.3.0-rc3](https://github.com/containous/traefik/tree/v2.3.0-rc3) (2020-07-28)
|
||||
[All Commits](https://github.com/containous/traefik/compare/v2.3.0-rc2...v2.3.0-rc3)
|
||||
|
||||
**Bug fixes:**
|
||||
- **[k8s,k8s/ingress]** Support Kubernetes Ingress pathType ([#7087](https://github.com/containous/traefik/pull/7087) by [rtribotte](https://github.com/rtribotte))
|
||||
- **[k8s,k8s/ingress]** Use semantic versioning to enable ingress class support ([#7065](https://github.com/containous/traefik/pull/7065) by [kevinpollet](https://github.com/kevinpollet))
|
||||
- **[provider]** file parser: skip nil value. ([#7058](https://github.com/containous/traefik/pull/7058) by [ldez](https://github.com/ldez))
|
||||
|
||||
**Documentation:**
|
||||
- **[ecs]** Fix documentation for ECS ([#7107](https://github.com/containous/traefik/pull/7107) by [mmatur](https://github.com/mmatur))
|
||||
- **[k8s]** Add migration documentation for IngressClass ([#7083](https://github.com/containous/traefik/pull/7083) by [kevinpollet](https://github.com/kevinpollet))
|
||||
- **[plugins]** Update availability info ([#7060](https://github.com/containous/traefik/pull/7060) by [PCM2](https://github.com/PCM2))
|
||||
|
||||
**Misc:**
|
||||
- Merge current v2.2 branch into v2.3 ([#7116](https://github.com/containous/traefik/pull/7116) by [ldez](https://github.com/ldez))
|
||||
- Merge current v2.2 branch into v2.3 ([#7086](https://github.com/containous/traefik/pull/7086) by [jbdoumenjou](https://github.com/jbdoumenjou))
|
||||
|
||||
## [v2.2.8](https://github.com/containous/traefik/tree/v2.2.8) (2020-07-28)
|
||||
[All Commits](https://github.com/containous/traefik/compare/v2.2.7...v2.2.8)
|
||||
|
||||
**Bug fixes:**
|
||||
- **[webui]** fix: clean X-Forwarded-Prefix header for the dashboard. ([#7109](https://github.com/containous/traefik/pull/7109) by [ldez](https://github.com/ldez))
|
||||
|
||||
**Documentation:**
|
||||
- **[docker]** spelling(docs/content/routing/providers/docker.md) ([#7101](https://github.com/containous/traefik/pull/7101) by [szczot3k](https://github.com/szczot3k))
|
||||
- **[k8s]** doc: add name of used key for kubernetes client auth ([#7068](https://github.com/containous/traefik/pull/7068) by [smueller18](https://github.com/smueller18))
|
||||
|
||||
## [v2.2.7](https://github.com/containous/traefik/tree/v2.2.7) (2020-07-20)
|
||||
[All Commits](https://github.com/containous/traefik/compare/v2.2.6...v2.2.7)
|
||||
|
||||
@@ -16,6 +43,41 @@
|
||||
- fix: documentation references. ([#7049](https://github.com/containous/traefik/pull/7049) by [ldez](https://github.com/ldez))
|
||||
- Add example for entrypoint on one ip address ([#6483](https://github.com/containous/traefik/pull/6483) by [SimonHeimberg](https://github.com/SimonHeimberg))
|
||||
|
||||
## [v2.3.0-rc2](https://github.com/containous/traefik/tree/v2.3.0-rc2) (2020-07-15)
|
||||
[All Commits](https://github.com/containous/traefik/compare/v2.3.0-rc1...v2.3.0-rc2)
|
||||
|
||||
**Misc:**
|
||||
- fix: goreleaser build commands.
|
||||
|
||||
## [v2.3.0-rc1](https://github.com/containous/traefik/tree/v2.3.0-rc1) (2020-07-15)
|
||||
[All Commits](https://github.com/containous/traefik/compare/v2.2.0-rc1...v2.3.0-rc1)
|
||||
|
||||
**Enhancements:**
|
||||
- **[api]** Add custom ping http code when Traefik is terminating ([#6696](https://github.com/containous/traefik/pull/6696) by [L3o-pold](https://github.com/L3o-pold))
|
||||
- **[ecs]** Add AWS ECS provider ([#6749](https://github.com/containous/traefik/pull/6749) by [alekitto](https://github.com/alekitto))
|
||||
- **[file]** feat: use parser to load dynamic config from file. ([#6875](https://github.com/containous/traefik/pull/6875) by [ldez](https://github.com/ldez))
|
||||
- **[k8s,k8s/crd,k8s/ingress]** Upgrade Client-go to 0.18.2 ([#6779](https://github.com/containous/traefik/pull/6779) by [dtomcej](https://github.com/dtomcej))
|
||||
- **[k8s,k8s/ingress]** Add new ingressClass support to ingress provider ([#6831](https://github.com/containous/traefik/pull/6831) by [dtomcej](https://github.com/dtomcej))
|
||||
- **[plugins]** Traefik Pilot: plugins support and alert system (EXPERIMENTAL FEATURES) ([#7041](https://github.com/containous/traefik/pull/7041) by [ldez](https://github.com/ldez))
|
||||
- **[provider]** Add HTTP Provider ([#6976](https://github.com/containous/traefik/pull/6976) by [kevinpollet](https://github.com/kevinpollet))
|
||||
- **[webui]** Add iOS specific icons ([#6946](https://github.com/containous/traefik/pull/6946) by [Heisenberg74](https://github.com/Heisenberg74))
|
||||
|
||||
**Bug fixes:**
|
||||
- **[k8s,k8s/ingress]** Delete an unnecessary warning log ([#6568](https://github.com/containous/traefik/pull/6568) by [jbdoumenjou](https://github.com/jbdoumenjou))
|
||||
- **[middleware]** Allow multiple secure middlewares to operate independently ([#6604](https://github.com/containous/traefik/pull/6604) by [dtomcej](https://github.com/dtomcej))
|
||||
|
||||
**Documentation:**
|
||||
- **[middleware]** Fixes config samples regarding forceSlash option ([#6811](https://github.com/containous/traefik/pull/6811) by [volkerw00](https://github.com/volkerw00))
|
||||
|
||||
**Misc:**
|
||||
- Merge current v2.2 branch into master ([#7052](https://github.com/containous/traefik/pull/7052) by [ldez](https://github.com/ldez))
|
||||
- Merge current v2.2 branch into master ([#7022](https://github.com/containous/traefik/pull/7022) by [jbdoumenjou](https://github.com/jbdoumenjou))
|
||||
- Merge current v2.2 branch into master ([#6921](https://github.com/containous/traefik/pull/6921) by [jbdoumenjou](https://github.com/jbdoumenjou))
|
||||
- Merge current v2.2 branch into master ([#6822](https://github.com/containous/traefik/pull/6822) by [mmatur](https://github.com/mmatur))
|
||||
- Merge current v2.2 branch into master ([#6754](https://github.com/containous/traefik/pull/6754) by [ldez](https://github.com/ldez))
|
||||
- Merge current v2.2 branch into master ([#6533](https://github.com/containous/traefik/pull/6533) by [ldez](https://github.com/ldez))
|
||||
- Merge current v2.2 branch into master ([#6468](https://github.com/containous/traefik/pull/6468) by [ldez](https://github.com/ldez))
|
||||
|
||||
## [v2.2.5](https://github.com/containous/traefik/tree/v2.2.5) (2020-07-13)
|
||||
[All Commits](https://github.com/containous/traefik/compare/v2.2.4...v2.2.5)
|
||||
|
||||
|
42
cmd/traefik/plugins.go
Normal file
42
cmd/traefik/plugins.go
Normal file
@@ -0,0 +1,42 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/containous/traefik/v2/pkg/config/static"
|
||||
"github.com/containous/traefik/v2/pkg/plugins"
|
||||
)
|
||||
|
||||
const outputDir = "./plugins-storage/"
|
||||
|
||||
func initPlugins(staticCfg *static.Configuration) (*plugins.Client, map[string]plugins.Descriptor, *plugins.DevPlugin, error) {
|
||||
if !isPilotEnabled(staticCfg) || !hasPlugins(staticCfg) {
|
||||
return nil, map[string]plugins.Descriptor{}, nil, nil
|
||||
}
|
||||
|
||||
opts := plugins.ClientOptions{
|
||||
Output: outputDir,
|
||||
Token: staticCfg.Experimental.Pilot.Token,
|
||||
}
|
||||
|
||||
client, err := plugins.NewClient(opts)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
err = plugins.Setup(client, staticCfg.Experimental.Plugins, staticCfg.Experimental.DevPlugin)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
return client, staticCfg.Experimental.Plugins, staticCfg.Experimental.DevPlugin, nil
|
||||
}
|
||||
|
||||
func isPilotEnabled(staticCfg *static.Configuration) bool {
|
||||
return staticCfg.Experimental != nil &&
|
||||
staticCfg.Experimental.Pilot != nil &&
|
||||
staticCfg.Experimental.Pilot.Token != ""
|
||||
}
|
||||
|
||||
func hasPlugins(staticCfg *static.Configuration) bool {
|
||||
return staticCfg.Experimental != nil &&
|
||||
len(staticCfg.Experimental.Plugins) > 0 || staticCfg.Experimental.DevPlugin != nil
|
||||
}
|
@@ -18,10 +18,13 @@ import (
|
||||
"github.com/containous/traefik/v2/pkg/cli"
|
||||
"github.com/containous/traefik/v2/pkg/collector"
|
||||
"github.com/containous/traefik/v2/pkg/config/dynamic"
|
||||
"github.com/containous/traefik/v2/pkg/config/runtime"
|
||||
"github.com/containous/traefik/v2/pkg/config/static"
|
||||
"github.com/containous/traefik/v2/pkg/log"
|
||||
"github.com/containous/traefik/v2/pkg/metrics"
|
||||
"github.com/containous/traefik/v2/pkg/middlewares/accesslog"
|
||||
"github.com/containous/traefik/v2/pkg/pilot"
|
||||
"github.com/containous/traefik/v2/pkg/plugins"
|
||||
"github.com/containous/traefik/v2/pkg/provider/acme"
|
||||
"github.com/containous/traefik/v2/pkg/provider/aggregator"
|
||||
"github.com/containous/traefik/v2/pkg/provider/traefik"
|
||||
@@ -117,6 +120,12 @@ func runCmd(staticConfiguration *static.Configuration) error {
|
||||
|
||||
ctx := cmd.ContextWithSignal(context.Background())
|
||||
|
||||
if staticConfiguration.Experimental != nil && staticConfiguration.Experimental.DevPlugin != nil {
|
||||
var cancel context.CancelFunc
|
||||
ctx, cancel = context.WithTimeout(ctx, 30*time.Minute)
|
||||
defer cancel()
|
||||
}
|
||||
|
||||
if staticConfiguration.Ping != nil {
|
||||
staticConfiguration.Ping.WithContext(ctx)
|
||||
}
|
||||
@@ -190,7 +199,18 @@ func setupServer(staticConfiguration *static.Configuration) (*server.Server, err
|
||||
accessLog := setupAccessLog(staticConfiguration.AccessLog)
|
||||
chainBuilder := middleware.NewChainBuilder(*staticConfiguration, metricsRegistry, accessLog)
|
||||
managerFactory := service.NewManagerFactory(*staticConfiguration, routinesPool, metricsRegistry)
|
||||
routerFactory := server.NewRouterFactory(*staticConfiguration, managerFactory, tlsManager, chainBuilder)
|
||||
|
||||
client, plgs, devPlugin, err := initPlugins(staticConfiguration)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pluginBuilder, err := plugins.NewBuilder(client, plgs, devPlugin)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
routerFactory := server.NewRouterFactory(*staticConfiguration, managerFactory, tlsManager, chainBuilder, pluginBuilder)
|
||||
|
||||
var defaultEntryPoints []string
|
||||
for name, cfg := range staticConfiguration.EntryPoints {
|
||||
@@ -224,7 +244,16 @@ func setupServer(staticConfiguration *static.Configuration) (*server.Server, err
|
||||
metricsRegistry.LastConfigReloadSuccessGauge().Set(float64(time.Now().Unix()))
|
||||
})
|
||||
|
||||
watcher.AddListener(switchRouter(routerFactory, acmeProviders, serverEntryPointsTCP, serverEntryPointsUDP))
|
||||
var aviator *pilot.Pilot
|
||||
if staticConfiguration.Experimental != nil && staticConfiguration.Experimental.Pilot != nil &&
|
||||
staticConfiguration.Experimental.Pilot.Token != "" {
|
||||
aviator = pilot.New(staticConfiguration.Experimental.Pilot.Token, routinesPool)
|
||||
routinesPool.GoCtx(func(ctx context.Context) {
|
||||
aviator.Tick(ctx)
|
||||
})
|
||||
}
|
||||
|
||||
watcher.AddListener(switchRouter(routerFactory, acmeProviders, serverEntryPointsTCP, serverEntryPointsUDP, aviator))
|
||||
|
||||
watcher.AddListener(func(conf dynamic.Configuration) {
|
||||
if metricsRegistry.IsEpEnabled() || metricsRegistry.IsSvcEnabled() {
|
||||
@@ -258,9 +287,12 @@ func setupServer(staticConfiguration *static.Configuration) (*server.Server, err
|
||||
return server.NewServer(routinesPool, serverEntryPointsTCP, serverEntryPointsUDP, watcher, chainBuilder, accessLog), nil
|
||||
}
|
||||
|
||||
func switchRouter(routerFactory *server.RouterFactory, acmeProviders []*acme.Provider, serverEntryPointsTCP server.TCPEntryPoints, serverEntryPointsUDP server.UDPEntryPoints) func(conf dynamic.Configuration) {
|
||||
func switchRouter(routerFactory *server.RouterFactory, acmeProviders []*acme.Provider, serverEntryPointsTCP server.TCPEntryPoints, serverEntryPointsUDP server.UDPEntryPoints, aviator *pilot.Pilot) func(conf dynamic.Configuration) {
|
||||
return func(conf dynamic.Configuration) {
|
||||
routers, udpRouters := routerFactory.CreateRouters(conf)
|
||||
rtConf := runtime.NewConfig(conf)
|
||||
|
||||
routers, udpRouters := routerFactory.CreateRouters(rtConf)
|
||||
|
||||
for entryPointName, rt := range routers {
|
||||
for _, p := range acmeProviders {
|
||||
if p != nil && p.HTTPChallenge != nil && p.HTTPChallenge.EntryPoint == entryPointName {
|
||||
@@ -269,6 +301,11 @@ func switchRouter(routerFactory *server.RouterFactory, acmeProviders []*acme.Pro
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if aviator != nil {
|
||||
aviator.SetRuntimeConfiguration(rtConf)
|
||||
}
|
||||
|
||||
serverEntryPointsTCP.Switch(routers)
|
||||
serverEntryPointsUDP.Switch(udpRouters)
|
||||
}
|
||||
|
@@ -9,11 +9,11 @@ You can install Traefik with the following flavors:
|
||||
|
||||
## Use the Official Docker Image
|
||||
|
||||
Choose one of the [official Docker images](https://hub.docker.com/_/traefik) and run it with the [sample configuration file](https://raw.githubusercontent.com/containous/traefik/v2.2/traefik.sample.toml):
|
||||
Choose one of the [official Docker images](https://hub.docker.com/_/traefik) and run it with the [sample configuration file](https://raw.githubusercontent.com/containous/traefik/v2.3/traefik.sample.toml):
|
||||
|
||||
```bash
|
||||
docker run -d -p 8080:8080 -p 80:80 \
|
||||
-v $PWD/traefik.toml:/etc/traefik/traefik.toml traefik:v2.2
|
||||
-v $PWD/traefik.toml:/etc/traefik/traefik.toml traefik:v2.3
|
||||
```
|
||||
|
||||
For more details, go to the [Docker provider documentation](../providers/docker.md)
|
||||
|
@@ -15,7 +15,7 @@ version: '3'
|
||||
services:
|
||||
reverse-proxy:
|
||||
# The official v2 Traefik docker image
|
||||
image: traefik:v2.2
|
||||
image: traefik:v2.3
|
||||
# Enables the web UI and tells Traefik to listen to docker
|
||||
command: --api.insecure=true --providers.docker
|
||||
ports:
|
||||
|
@@ -284,7 +284,7 @@ For complete details, refer to your provider's _Additional configuration_ link.
|
||||
|-------------------------------------------------------------|----------------|---------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------|
|
||||
| [ACME DNS](https://github.com/joohoi/acme-dns) | `acme-dns` | `ACME_DNS_API_BASE`, `ACME_DNS_STORAGE_PATH` | [Additional configuration](https://go-acme.github.io/lego/dns/acme-dns) |
|
||||
| [Alibaba Cloud](https://www.alibabacloud.com) | `alidns` | `ALICLOUD_ACCESS_KEY`, `ALICLOUD_SECRET_KEY`, `ALICLOUD_REGION_ID` | [Additional configuration](https://go-acme.github.io/lego/dns/alidns) |
|
||||
| [ArvanCloud](https://arvancloud.com) | `arvancloud` | `ARVANCLOUD_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/arvancloud) |
|
||||
| [ArvanCloud](https://www.arvancloud.com/en) | `arvancloud` | `ARVANCLOUD_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/arvancloud) |
|
||||
| [Auroradns](https://www.pcextreme.com/dns-health-checks) | `auroradns` | `AURORA_USER_ID`, `AURORA_KEY`, `AURORA_ENDPOINT` | [Additional configuration](https://go-acme.github.io/lego/dns/auroradns) |
|
||||
| [Autodns](https://www.internetx.com/domains/autodns/) | `autodns` | `AUTODNS_API_USER`, `AUTODNS_API_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/autodns) |
|
||||
| [Azure](https://azure.microsoft.com/services/dns/) | `azure` | `AZURE_CLIENT_ID`, `AZURE_CLIENT_SECRET`, `AZURE_SUBSCRIPTION_ID`, `AZURE_TENANT_ID`, `AZURE_RESOURCE_GROUP`, `[AZURE_METADATA_ENDPOINT]` | [Additional configuration](https://go-acme.github.io/lego/dns/azure) |
|
||||
@@ -362,7 +362,7 @@ For complete details, refer to your provider's _Additional configuration_ link.
|
||||
| [Zonomi](https://zonomi.com) | `zonomi` | `ZONOMI_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/zonomi) |
|
||||
|
||||
[^1]: more information about the HTTP message format can be found [here](https://go-acme.github.io/lego/dns/httpreq/)
|
||||
[^2]: [providing_credentials_to_your_application](https://cloud.google.com/docs/authentication/production#providing_credentials_to_your_application)
|
||||
[^2]: [providing_credentials_to_your_application](https://cloud.google.com/docs/authentication/production)
|
||||
[^3]: [google/default.go](https://github.com/golang/oauth2/blob/36a7019397c4c86cf59eeab3bc0d188bac444277/google/default.go#L61-L76)
|
||||
[^4]: `docker stack` remark: there is no way to support terminal attached to container when deploying with `docker stack`, so you might need to run container with `docker run -it` to generate certificates using `manual` provider.
|
||||
[^5]: The `Global API Key` needs to be used, not the `Origin CA Key`.
|
||||
|
@@ -428,6 +428,7 @@ metadata:
|
||||
|
||||
spec:
|
||||
clientAuth:
|
||||
# the CA certificate is extracted from key `tls.ca` of the given secrets.
|
||||
secretNames:
|
||||
- secretCA
|
||||
clientAuthType: RequireAndVerifyClientCert
|
||||
|
@@ -98,7 +98,7 @@ _Optional, Default=true_
|
||||
```yaml tab="Docker"
|
||||
labels:
|
||||
- "traefik.http.middlewares.example.stripprefix.prefixes=/foobar"
|
||||
- "traefik.http.middlewares.example.stripprefix.forceslash=false"
|
||||
- "traefik.http.middlewares.example.stripprefix.forceSlash=false"
|
||||
```
|
||||
|
||||
```yaml tab="Kubernetes"
|
||||
@@ -116,7 +116,7 @@ spec:
|
||||
```json tab="Marathon"
|
||||
"labels": {
|
||||
"traefik.http.middlewares.example.stripprefix.prefixes": "/foobar",
|
||||
"traefik.http.middlewares.example.stripprefix.forceslash": "false"
|
||||
"traefik.http.middlewares.example.stripprefix.forceSlash": "false"
|
||||
}
|
||||
```
|
||||
|
||||
|
@@ -1,17 +1,5 @@
|
||||
# Migration: Steps needed between the versions
|
||||
|
||||
## v2.2.2 to v2.2.5
|
||||
|
||||
### InsecureSNI removal
|
||||
|
||||
In `v2.2.2` we introduced a new flag (`insecureSNI`) which was available as a global option to disable domain fronting.
|
||||
Since `v2.2.5` this global option has been removed, and you should not use it anymore.
|
||||
|
||||
### HostSNI rule matcher removal
|
||||
|
||||
In `v2.2.2` we introduced a new rule matcher (`HostSNI`) which was allowing to match the Server Name Indication at the router level.
|
||||
Since `v2.2.5` this rule has been removed, and you should not use it anymore.
|
||||
|
||||
## v2.0 to v2.1
|
||||
|
||||
### Kubernetes CRD
|
||||
@@ -314,3 +302,26 @@ providers:
|
||||
--entryPoints.websecure.address=:443
|
||||
--providers.kubernetesIngress=true
|
||||
```
|
||||
|
||||
## v2.2.2 to v2.2.5
|
||||
|
||||
### InsecureSNI removal
|
||||
|
||||
In `v2.2.2` we introduced a new flag (`insecureSNI`) which was available as a global option to disable domain fronting.
|
||||
Since `v2.2.5` this global option has been removed, and you should not use it anymore.
|
||||
|
||||
### HostSNI rule matcher removal
|
||||
|
||||
In `v2.2.2` we introduced a new rule matcher (`HostSNI`) which was allowing to match the Server Name Indication at the router level.
|
||||
Since `v2.2.5` this rule has been removed, and you should not use it anymore.
|
||||
|
||||
## v2.2 to v2.3
|
||||
|
||||
### File Provider
|
||||
|
||||
The file parser has been changed, since v2.3 the unknown options/fields in a dynamic configuration file are treated as errors.
|
||||
|
||||
### IngressClass
|
||||
|
||||
In `v2.3`, the support of `IngressClass`, which is available since Kubernetes version `1.18`, has been introduced.
|
||||
In order to be able to use this new resource the [Kubernetes RBAC](../reference/dynamic-configuration/kubernetes-crd.md#rbac) must be updated.
|
||||
|
@@ -81,3 +81,28 @@ ping:
|
||||
```bash tab="CLI"
|
||||
--ping.manualrouting=true
|
||||
```
|
||||
|
||||
### `terminatingStatusCode`
|
||||
|
||||
_Optional, Default=503_
|
||||
|
||||
During the period in which Traefik is gracefully shutting down, the ping handler
|
||||
returns a 503 status code by default. If Traefik is behind e.g. a load-balancer
|
||||
doing health checks (such as the Kubernetes LivenessProbe), another code might
|
||||
be expected as the signal for graceful termination. In which case, the
|
||||
terminatingStatusCode can be used to set the code returned by the ping
|
||||
handler during termination.
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[ping]
|
||||
terminatingStatusCode = 204
|
||||
```
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
ping:
|
||||
terminatingStatusCode: 204
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--ping.terminatingStatusCode=204
|
||||
```
|
||||
|
38
docs/content/plugins/overview.md
Normal file
38
docs/content/plugins/overview.md
Normal file
@@ -0,0 +1,38 @@
|
||||
# Plugins and Traefik Pilot
|
||||
|
||||
Overview
|
||||
{: .subtitle}
|
||||
|
||||
Traefik Pilot is a software-as-a-service (SaaS) platform that connects to Traefik to extend its capabilities.
|
||||
It does this through *plugins*, which are dynamically loaded components that enable new features.
|
||||
|
||||
For example, Traefik plugins can add features to modify requests or headers, issue redirects, add authentication, and so on, providing similar functionality to Traefik [middlewares](https://docs.traefik.io/middlewares/overview/).
|
||||
|
||||
Traefik Pilot can also monitor connected Traefik instances and issue alerts when one is not responding, or when it is subject to security vulnerabilities.
|
||||
|
||||
!!! note "Availability"
|
||||
Plugins are available for Traefik v2.3.0-rc1 and later.
|
||||
|
||||
!!! danger "Experimental Features"
|
||||
Plugins can potentially modify the behavior of Traefik in unforeseen ways.
|
||||
Exercise caution when adding new plugins to production Traefik instances.
|
||||
|
||||
## Connecting to Traefik Pilot
|
||||
|
||||
Plugins are available when a Traefik instance is connected to Traefik Pilot.
|
||||
|
||||
To register a new instance and begin working with plugins, login or create an account at the [Traefik Pilot homepage](https://pilot.traefik.io) and choose **Register New Instance**.
|
||||
|
||||
To complete the connection, Traefik Pilot will issue a token that must be added to your Traefik static configuration by following the instructions provided.
|
||||
|
||||
!!! note "Enabling Alerts"
|
||||
Health and security alerts for registered Traefik instances can be enabled from the Preferences in your [Traefik Pilot Profile](https://pilot.traefik.io/profile).
|
||||
|
||||
## Creating Plugins
|
||||
|
||||
Traefik users can create their own plugins and contribute them to the Traefik Pilot catalog to share them with the community.
|
||||
|
||||
Plugins are written in [Go](https://golang.org/) and their code is executed by an [embedded Go interpreter](https://github.com/containous/yaegi).
|
||||
There is no need to compile binaries and all plugins are 100% cross-platform.
|
||||
|
||||
To learn more and see code for example Traefik plugins, please see the [developer documentation](https://github.com/containous/plugindemo).
|
122
docs/content/plugins/using-plugins.md
Normal file
122
docs/content/plugins/using-plugins.md
Normal file
@@ -0,0 +1,122 @@
|
||||
# Using Plugins
|
||||
|
||||
Plugins are available to any instance of Traefik v2.3 or later that is [registered](overview.md#connecting-to-traefik-pilot) with Traefik Pilot.
|
||||
Plugins are hosted on GitHub, but you can browse plugins to add to your registered Traefik instances from the Traefik Pilot UI.
|
||||
|
||||
!!! danger "Experimental Features"
|
||||
Plugins can potentially modify the behavior of Traefik in unforeseen ways.
|
||||
Exercise caution when adding new plugins to production Traefik instances.
|
||||
|
||||
## Add a Plugin
|
||||
|
||||
To add a new plugin to a Traefik instance, you must modify that instance's static configuration.
|
||||
The code to be added is provided by the Traefik Pilot UI when you choose **Install the Plugin**.
|
||||
|
||||
In the example below, we add the [`blockpath`](http://github.com/containous/plugin-blockpath) and [`rewritebody`](https://github.com/containous/plugin-rewritebody) plugins:
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[entryPoints]
|
||||
[entryPoints.web]
|
||||
address = ":80"
|
||||
|
||||
[experimental]
|
||||
[experimental.pilot]
|
||||
token = "xxxxxxxxx"
|
||||
|
||||
[experimental.plugins]
|
||||
[experimental.plugins.block]
|
||||
modulename = "github.com/containous/plugin-blockpath"
|
||||
version = "v0.1.2"
|
||||
[experimental.plugins.rewrite]
|
||||
modulename = "github.com/containous/plugin-rewritebody"
|
||||
version = "v0.2.0"
|
||||
```
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
entryPoints:
|
||||
web:
|
||||
address: :80
|
||||
|
||||
experimental:
|
||||
pilot:
|
||||
token: xxxxxxxxx
|
||||
|
||||
plugins:
|
||||
block:
|
||||
modulename: github.com/containous/plugin-blockpath
|
||||
version: v0.1.2
|
||||
rewrite:
|
||||
modulename: github.com/containous/plugin-rewritebody
|
||||
version: v0.2.0
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--entryPoints.web.address=:80
|
||||
--experimental.pilot.token=xxxxxxxxx
|
||||
--experimental.plugins.block.modulename=github.com/containous/plugin-blockpath
|
||||
--experimental.plugins.block.version=v0.1.2
|
||||
--experimental.plugins.rewrite.modulename=github.com/containous/plugin-rewritebody
|
||||
--experimental.plugins.rewrite.version=v0.2.0
|
||||
```
|
||||
|
||||
## Configuring Plugins
|
||||
|
||||
Some plugins will need to be configured by adding a dynamic configuration.
|
||||
For the `bodyrewrite` plugin, for example:
|
||||
|
||||
```yaml tab="Docker"
|
||||
labels:
|
||||
- "traefik.http.middlewares.my-rewritebody.plugin.rewrite.rewrites[0].regex=example"
|
||||
- "traefik.http.middlewares.my-rewritebody.plugin.rewrite.rewrites[0].replacement=test"
|
||||
```
|
||||
|
||||
```yaml tab="Kubernetes"
|
||||
apiVersion: traefik.containo.us/v1alpha1
|
||||
kind: Middleware
|
||||
metadata:
|
||||
name: my-rewritebody
|
||||
spec:
|
||||
plugin:
|
||||
rewrite:
|
||||
rewrites:
|
||||
- regex: example
|
||||
replacement: test
|
||||
```
|
||||
|
||||
```yaml tab="Consul Catalog"
|
||||
- "traefik.http.middlewares.my-rewritebody.plugin.rewrite.rewrites[0].regex=example"
|
||||
- "traefik.http.middlewares.my-rewritebody.plugin.rewrite.rewrites[0].replacement=test"
|
||||
```
|
||||
|
||||
```json tab="Marathon"
|
||||
"labels": {
|
||||
"traefik.http.middlewares.my-rewritebody.plugin.rewrite.rewrites[0].regex": "example",
|
||||
"traefik.http.middlewares.my-rewritebody.plugin.rewrite.rewrites[0].replacement": "test"
|
||||
}
|
||||
```
|
||||
|
||||
```yaml tab="Rancher"
|
||||
labels:
|
||||
- "traefik.http.middlewares.my-rewritebody.plugin.rewrite.rewrites[0].regex=example"
|
||||
- "traefik.http.middlewares.my-rewritebody.plugin.rewrite.rewrites[0].replacement=test"
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[http.middlewares]
|
||||
[http.middlewares.my-rewritebody.plugin.rewrite]
|
||||
lastModified = true
|
||||
[[http.middlewares.my-rewritebody.plugin.rewrite.rewrites]]
|
||||
regex = "example"
|
||||
replacement = "test"
|
||||
```
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
http:
|
||||
middlewares:
|
||||
my-rewritebody:
|
||||
plugin:
|
||||
rewrite:
|
||||
rewrites:
|
||||
- regex: example
|
||||
replacement: test
|
||||
```
|
@@ -261,7 +261,7 @@ See the sections [Docker API Access](#docker-api-access) and [Docker Swarm API A
|
||||
|
||||
services:
|
||||
traefik:
|
||||
image: traefik:v2.2 # The official v2 Traefik docker image
|
||||
image: traefik:v2.3 # The official v2 Traefik docker image
|
||||
ports:
|
||||
- "80:80"
|
||||
volumes:
|
||||
|
220
docs/content/providers/ecs.md
Normal file
220
docs/content/providers/ecs.md
Normal file
@@ -0,0 +1,220 @@
|
||||
# Traefik & AWS ECS
|
||||
|
||||
A Story of Labels & Elastic Containers
|
||||
{: .subtitle }
|
||||
|
||||
Attach labels to your ECS containers and let Traefik do the rest!
|
||||
|
||||
## Configuration Examples
|
||||
|
||||
??? example "Configuring ECS provider"
|
||||
|
||||
Enabling the ECS provider:
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[providers.ecs]
|
||||
clusters = ["default"]
|
||||
```
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
providers:
|
||||
ecs:
|
||||
clusters:
|
||||
- default
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--providers.ecs.clusters=default
|
||||
```
|
||||
|
||||
## Policy
|
||||
|
||||
Traefik needs the following policy to read ECS information:
|
||||
|
||||
```json
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Sid": "TraefikECSReadAccess",
|
||||
"Effect": "Allow",
|
||||
"Action": [
|
||||
"ecs:ListClusters",
|
||||
"ecs:DescribeClusters",
|
||||
"ecs:ListTasks",
|
||||
"ecs:DescribeTasks",
|
||||
"ecs:DescribeContainerInstances",
|
||||
"ecs:DescribeTaskDefinition",
|
||||
"ec2:DescribeInstances"
|
||||
],
|
||||
"Resource": [
|
||||
"*"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Provider configuration
|
||||
|
||||
### `autoDiscoverClusters`
|
||||
|
||||
_Optional, Default=false_
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[providers.ecs]
|
||||
autoDiscoverClusters = true
|
||||
# ...
|
||||
```
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
providers:
|
||||
ecs:
|
||||
autoDiscoverClusters: true
|
||||
# ...
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--providers.ecs.autoDiscoverClusters=true
|
||||
# ...
|
||||
```
|
||||
|
||||
Search for services in clusters list.
|
||||
|
||||
- If set to `true` the configured clusters will be ignored and the clusters will be discovered.
|
||||
- If set to `false` the services will be discovered only in configured clusters.
|
||||
|
||||
### `clusters`
|
||||
|
||||
_Optional, Default=["default"]_
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[providers.ecs]
|
||||
cluster = ["default"]
|
||||
# ...
|
||||
```
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
providers:
|
||||
ecs:
|
||||
clusters:
|
||||
- default
|
||||
# ...
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--providers.ecs.clusters=default
|
||||
# ...
|
||||
```
|
||||
|
||||
Search for services in clusters list.
|
||||
|
||||
### `exposedByDefault`
|
||||
|
||||
_Optional, Default=true_
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[providers.ecs]
|
||||
exposedByDefault = false
|
||||
# ...
|
||||
```
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
providers:
|
||||
ecs:
|
||||
exposedByDefault: false
|
||||
# ...
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--providers.ecs.exposedByDefault=false
|
||||
# ...
|
||||
```
|
||||
|
||||
Expose ECS services by default in Traefik.
|
||||
If set to false, services that don't have a `traefik.enable=true` label will be ignored from the resulting routing configuration.
|
||||
|
||||
### `defaultRule`
|
||||
|
||||
_Optional, Default=```Host(`{{ normalize .Name }}`)```_
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[providers.ecs]
|
||||
defaultRule = "Host(`{{ .Name }}.{{ index .Labels \"customLabel\"}}`)"
|
||||
# ...
|
||||
```
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
providers:
|
||||
ecs:
|
||||
defaultRule: "Host(`{{ .Name }}.{{ index .Labels \"customLabel\"}}`)"
|
||||
# ...
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--providers.ecs.defaultRule=Host(`{{ .Name }}.{{ index .Labels \"customLabel\"}}`)
|
||||
# ...
|
||||
```
|
||||
|
||||
For a given container if no routing rule was defined by a label, it is defined by this defaultRule instead.
|
||||
It must be a valid [Go template](https://golang.org/pkg/text/template/),
|
||||
augmented with the [sprig template functions](http://masterminds.github.io/sprig/).
|
||||
The service name can be accessed as the `Name` identifier,
|
||||
and the template has access to all the labels defined on this container.
|
||||
|
||||
### `refreshSeconds`
|
||||
|
||||
_Optional, Default=15_
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[providers.ecs]
|
||||
refreshSeconds = 15
|
||||
# ...
|
||||
```
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
providers:
|
||||
ecs:
|
||||
refreshSeconds: 15
|
||||
# ...
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--providers.ecs.refreshSeconds=15
|
||||
# ...
|
||||
```
|
||||
|
||||
Polling interval (in seconds).
|
||||
|
||||
### Credentials
|
||||
|
||||
_Optional_
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[providers.ecs]
|
||||
region = "us-east-1"
|
||||
accessKeyID = "abc"
|
||||
secretAccessKey = "123"
|
||||
```
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
providers:
|
||||
ecs:
|
||||
region: us-east-1
|
||||
accessKeyID: "abc"
|
||||
secretAccessKey: "123"
|
||||
# ...
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--providers.ecs.region="us-east-1"
|
||||
--providers.ecs.accessKeyID="abc"
|
||||
--providers.ecs.secretAccessKey="123"
|
||||
# ...
|
||||
```
|
||||
|
||||
If `accessKeyID` / `secretAccessKey` is not provided credentials will be resolved in the following order:
|
||||
|
||||
- From environment variables `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, and `AWS_SESSION_TOKEN`.
|
||||
- Shared credentials, determined by `AWS_PROFILE` and `AWS_SHARED_CREDENTIALS_FILE`, defaults to default and `~/.aws/credentials`.
|
||||
- EC2 instance role or ECS task role
|
@@ -238,7 +238,7 @@ To illustrate, it's possible to easily define multiple routers, services, and TL
|
||||
[[tls.certificates]]
|
||||
certFile = "/etc/traefik/cert-{{ $e }}.pem"
|
||||
keyFile = "/etc/traefik/cert-{{ $e }}.key"
|
||||
store = ["my-store-foo-{{ $e }}", "my-store-bar-{{ $e }}"]
|
||||
stores = ["my-store-foo-{{ $e }}", "my-store-bar-{{ $e }}"]
|
||||
{{ end }}
|
||||
|
||||
[tls.config]
|
||||
|
189
docs/content/providers/http.md
Normal file
189
docs/content/providers/http.md
Normal file
@@ -0,0 +1,189 @@
|
||||
# Traefik & HTTP
|
||||
|
||||
Provide your [dynamic configuration](./overview.md) via an HTTP(s) endpoint and let Traefik do the rest!
|
||||
|
||||
## Routing Configuration
|
||||
|
||||
The HTTP provider uses the same configuration as the [File Provider](./file.md) in YAML or JSON format.
|
||||
|
||||
## Provider Configuration
|
||||
|
||||
### `endpoint`
|
||||
|
||||
_Required_
|
||||
|
||||
Defines the HTTP(s) endpoint to poll.
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[providers.http]
|
||||
endpoint = "http://127.0.0.1:9000/api"
|
||||
```
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
providers:
|
||||
http:
|
||||
endpoint:
|
||||
- "http://127.0.0.1:9000/api"
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--providers.http.endpoint=http://127.0.0.1:9000/api
|
||||
```
|
||||
|
||||
### `pollInterval`
|
||||
|
||||
_Optional, Default="5s"_
|
||||
|
||||
Defines the polling interval.
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[providers.http]
|
||||
pollInterval = "5s"
|
||||
```
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
providers:
|
||||
http:
|
||||
pollInterval: "5s"
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--providers.http.pollInterval=5s
|
||||
```
|
||||
|
||||
### `pollTimeout`
|
||||
|
||||
_Optional, Default="5s"_
|
||||
|
||||
Defines the polling timeout when connecting to the configured endpoint.
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[providers.http]
|
||||
pollTimeout = "5s"
|
||||
```
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
providers:
|
||||
http:
|
||||
pollTimeout: "5s"
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--providers.http.pollTimeout=5s
|
||||
```
|
||||
|
||||
### `tls`
|
||||
|
||||
_Optional_
|
||||
|
||||
#### `tls.ca`
|
||||
|
||||
Certificate Authority used for the secured connection to the configured Endpoint.
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[providers.http.tls]
|
||||
ca = "path/to/ca.crt"
|
||||
```
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
providers:
|
||||
http:
|
||||
tls:
|
||||
ca: path/to/ca.crt
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--providers.http.tls.ca=path/to/ca.crt
|
||||
```
|
||||
|
||||
#### `tls.caOptional`
|
||||
|
||||
Policy followed for the secured connection with TLS Client Authentication to the configured Endpoint.
|
||||
Requires `tls.ca` to be defined.
|
||||
|
||||
- `true`: VerifyClientCertIfGiven
|
||||
- `false`: RequireAndVerifyClientCert
|
||||
- if `tls.ca` is undefined NoClientCert
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[providers.http.tls]
|
||||
caOptional = true
|
||||
```
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
providers:
|
||||
http:
|
||||
tls:
|
||||
caOptional: true
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--providers.http.tls.caOptional=true
|
||||
```
|
||||
|
||||
#### `tls.cert`
|
||||
|
||||
Public certificate used for the secured connection to the configured Endpoint.
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[providers.http.tls]
|
||||
cert = "path/to/foo.cert"
|
||||
key = "path/to/foo.key"
|
||||
```
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
providers:
|
||||
http:
|
||||
tls:
|
||||
cert: path/to/foo.cert
|
||||
key: path/to/foo.key
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--providers.http.tls.cert=path/to/foo.cert
|
||||
--providers.http.tls.key=path/to/foo.key
|
||||
```
|
||||
|
||||
#### `tls.key`
|
||||
|
||||
Private certificate used for the secured connection to the configured Endpoint.
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[providers.http.tls]
|
||||
cert = "path/to/foo.cert"
|
||||
key = "path/to/foo.key"
|
||||
```
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
providers:
|
||||
http:
|
||||
tls:
|
||||
cert: path/to/foo.cert
|
||||
key: path/to/foo.key
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--providers.http.tls.cert=path/to/foo.cert
|
||||
--providers.http.tls.key=path/to/foo.key
|
||||
```
|
||||
|
||||
#### `tls.insecureSkipVerify`
|
||||
|
||||
If `insecureSkipVerify` is `true`, TLS connection to the configured Endpoint accepts any certificate presented by the
|
||||
server and any host name in that certificate.
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[providers.http.tls]
|
||||
insecureSkipVerify = true
|
||||
```
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
providers:
|
||||
http:
|
||||
tls:
|
||||
insecureSkipVerify: true
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--providers.http.tls.insecureSkipVerify=true
|
||||
```
|
@@ -258,6 +258,14 @@ Value of `kubernetes.io/ingress.class` annotation that identifies Ingress object
|
||||
If the parameter is non-empty, only Ingresses containing an annotation with the same value are processed.
|
||||
Otherwise, Ingresses missing the annotation, having an empty value, or with the value `traefik` are processed.
|
||||
|
||||
!!! info "Kubernetes 1.18+"
|
||||
|
||||
If the Kubernetes cluster version is 1.18+,
|
||||
the new `IngressClass` resource can be leveraged to identify Ingress objects that should be processed.
|
||||
In that case, Traefik will look for an `IngressClass` in the cluster with the controller value equal to *traefik.io/ingress-controller*.
|
||||
|
||||
Please see [this article](https://kubernetes.io/blog/2020/04/02/improvements-to-the-ingress-api-in-kubernetes-1.18/) for more information.
|
||||
|
||||
### `ingressEndpoint`
|
||||
|
||||
#### `hostname`
|
||||
@@ -357,4 +365,4 @@ providers:
|
||||
### Further
|
||||
|
||||
If one wants to know more about the various aspects of the Ingress spec that Traefik supports,
|
||||
many examples of Ingresses definitions are located in the tests [data](https://github.com/containous/traefik/tree/v2.2/pkg/provider/kubernetes/ingress/fixtures) of the Traefik repository.
|
||||
many examples of Ingresses definitions are located in the tests [data](https://github.com/containous/traefik/tree/v2.3/pkg/provider/kubernetes/ingress/fixtures) of the Traefik repository.
|
||||
|
@@ -35,9 +35,10 @@ Below is the list of the currently supported providers in Traefik.
|
||||
| [Rancher](./rancher.md) | Orchestrator | Label |
|
||||
| [File](./file.md) | Manual | TOML/YAML format |
|
||||
| [Consul](./consul.md) | KV | KV |
|
||||
| [etcd](./etcd.md) | KV | KV |
|
||||
| [Etcd](./etcd.md) | KV | KV |
|
||||
| [Redis](./redis.md) | KV | KV |
|
||||
| [ZooKeeper](./zookeeper.md) | KV | KV |
|
||||
| [HTTP](./http.md) | Manual | JSON format |
|
||||
|
||||
!!! info "More Providers"
|
||||
|
||||
|
@@ -91,26 +91,27 @@
|
||||
- "traefik.http.middlewares.middleware13.passtlsclientcert.info.subject.province=true"
|
||||
- "traefik.http.middlewares.middleware13.passtlsclientcert.info.subject.serialnumber=true"
|
||||
- "traefik.http.middlewares.middleware13.passtlsclientcert.pem=true"
|
||||
- "traefik.http.middlewares.middleware14.ratelimit.average=42"
|
||||
- "traefik.http.middlewares.middleware14.ratelimit.burst=42"
|
||||
- "traefik.http.middlewares.middleware14.ratelimit.period=42"
|
||||
- "traefik.http.middlewares.middleware14.ratelimit.sourcecriterion.ipstrategy.depth=42"
|
||||
- "traefik.http.middlewares.middleware14.ratelimit.sourcecriterion.ipstrategy.excludedips=foobar, foobar"
|
||||
- "traefik.http.middlewares.middleware14.ratelimit.sourcecriterion.requestheadername=foobar"
|
||||
- "traefik.http.middlewares.middleware14.ratelimit.sourcecriterion.requesthost=true"
|
||||
- "traefik.http.middlewares.middleware15.redirectregex.permanent=true"
|
||||
- "traefik.http.middlewares.middleware15.redirectregex.regex=foobar"
|
||||
- "traefik.http.middlewares.middleware15.redirectregex.replacement=foobar"
|
||||
- "traefik.http.middlewares.middleware16.redirectscheme.permanent=true"
|
||||
- "traefik.http.middlewares.middleware16.redirectscheme.port=foobar"
|
||||
- "traefik.http.middlewares.middleware16.redirectscheme.scheme=foobar"
|
||||
- "traefik.http.middlewares.middleware17.replacepath.path=foobar"
|
||||
- "traefik.http.middlewares.middleware18.replacepathregex.regex=foobar"
|
||||
- "traefik.http.middlewares.middleware18.replacepathregex.replacement=foobar"
|
||||
- "traefik.http.middlewares.middleware19.retry.attempts=42"
|
||||
- "traefik.http.middlewares.middleware20.stripprefix.forceslash=true"
|
||||
- "traefik.http.middlewares.middleware20.stripprefix.prefixes=foobar, foobar"
|
||||
- "traefik.http.middlewares.middleware21.stripprefixregex.regex=foobar, foobar"
|
||||
- "traefik.http.middlewares.middleware14.plugin.foobar.foo=bar"
|
||||
- "traefik.http.middlewares.middleware15.ratelimit.average=42"
|
||||
- "traefik.http.middlewares.middleware15.ratelimit.burst=42"
|
||||
- "traefik.http.middlewares.middleware15.ratelimit.period=42"
|
||||
- "traefik.http.middlewares.middleware15.ratelimit.sourcecriterion.ipstrategy.depth=42"
|
||||
- "traefik.http.middlewares.middleware15.ratelimit.sourcecriterion.ipstrategy.excludedips=foobar, foobar"
|
||||
- "traefik.http.middlewares.middleware15.ratelimit.sourcecriterion.requestheadername=foobar"
|
||||
- "traefik.http.middlewares.middleware15.ratelimit.sourcecriterion.requesthost=true"
|
||||
- "traefik.http.middlewares.middleware16.redirectregex.permanent=true"
|
||||
- "traefik.http.middlewares.middleware16.redirectregex.regex=foobar"
|
||||
- "traefik.http.middlewares.middleware16.redirectregex.replacement=foobar"
|
||||
- "traefik.http.middlewares.middleware17.redirectscheme.permanent=true"
|
||||
- "traefik.http.middlewares.middleware17.redirectscheme.port=foobar"
|
||||
- "traefik.http.middlewares.middleware17.redirectscheme.scheme=foobar"
|
||||
- "traefik.http.middlewares.middleware18.replacepath.path=foobar"
|
||||
- "traefik.http.middlewares.middleware19.replacepathregex.regex=foobar"
|
||||
- "traefik.http.middlewares.middleware19.replacepathregex.replacement=foobar"
|
||||
- "traefik.http.middlewares.middleware20.retry.attempts=42"
|
||||
- "traefik.http.middlewares.middleware21.stripprefix.forceslash=true"
|
||||
- "traefik.http.middlewares.middleware21.stripprefix.prefixes=foobar, foobar"
|
||||
- "traefik.http.middlewares.middleware22.stripprefixregex.regex=foobar, foobar"
|
||||
- "traefik.http.routers.router0.entrypoints=foobar, foobar"
|
||||
- "traefik.http.routers.router0.middlewares=foobar, foobar"
|
||||
- "traefik.http.routers.router0.priority=42"
|
||||
@@ -150,8 +151,8 @@
|
||||
- "traefik.http.services.service01.loadbalancer.sticky.cookie=true"
|
||||
- "traefik.http.services.service01.loadbalancer.sticky.cookie.httponly=true"
|
||||
- "traefik.http.services.service01.loadbalancer.sticky.cookie.name=foobar"
|
||||
- "traefik.http.services.service01.loadbalancer.sticky.cookie.secure=true"
|
||||
- "traefik.http.services.service01.loadbalancer.sticky.cookie.samesite=foobar"
|
||||
- "traefik.http.services.service01.loadbalancer.sticky.cookie.secure=true"
|
||||
- "traefik.http.services.service01.loadbalancer.server.port=foobar"
|
||||
- "traefik.http.services.service01.loadbalancer.server.scheme=foobar"
|
||||
- "traefik.tcp.routers.tcprouter0.entrypoints=foobar, foobar"
|
||||
|
11
docs/content/reference/dynamic-configuration/ecs.md
Normal file
11
docs/content/reference/dynamic-configuration/ecs.md
Normal file
@@ -0,0 +1,11 @@
|
||||
# ECS Configuration Reference
|
||||
|
||||
Dynamic configuration with ECS provider
|
||||
{: .subtitle }
|
||||
|
||||
The labels are case insensitive.
|
||||
|
||||
```yaml
|
||||
--8<-- "content/reference/dynamic-configuration/ecs.yml"
|
||||
--8<-- "content/reference/dynamic-configuration/docker-labels.yml"
|
||||
```
|
1
docs/content/reference/dynamic-configuration/ecs.yml
Normal file
1
docs/content/reference/dynamic-configuration/ecs.yml
Normal file
@@ -0,0 +1 @@
|
||||
- "traefik.enable=true"
|
@@ -223,42 +223,46 @@
|
||||
serialNumber = true
|
||||
domainComponent = true
|
||||
[http.middlewares.Middleware14]
|
||||
[http.middlewares.Middleware14.rateLimit]
|
||||
[http.middlewares.Middleware14.plugin]
|
||||
[http.middlewares.Middleware14.plugin.PluginConf]
|
||||
foo = "bar"
|
||||
[http.middlewares.Middleware15]
|
||||
[http.middlewares.Middleware15.rateLimit]
|
||||
average = 42
|
||||
period = 42
|
||||
burst = 42
|
||||
[http.middlewares.Middleware14.rateLimit.sourceCriterion]
|
||||
[http.middlewares.Middleware15.rateLimit.sourceCriterion]
|
||||
requestHeaderName = "foobar"
|
||||
requestHost = true
|
||||
[http.middlewares.Middleware14.rateLimit.sourceCriterion.ipStrategy]
|
||||
[http.middlewares.Middleware15.rateLimit.sourceCriterion.ipStrategy]
|
||||
depth = 42
|
||||
excludedIPs = ["foobar", "foobar"]
|
||||
[http.middlewares.Middleware15]
|
||||
[http.middlewares.Middleware15.redirectRegex]
|
||||
[http.middlewares.Middleware16]
|
||||
[http.middlewares.Middleware16.redirectRegex]
|
||||
regex = "foobar"
|
||||
replacement = "foobar"
|
||||
permanent = true
|
||||
[http.middlewares.Middleware16]
|
||||
[http.middlewares.Middleware16.redirectScheme]
|
||||
[http.middlewares.Middleware17]
|
||||
[http.middlewares.Middleware17.redirectScheme]
|
||||
scheme = "foobar"
|
||||
port = "foobar"
|
||||
permanent = true
|
||||
[http.middlewares.Middleware17]
|
||||
[http.middlewares.Middleware17.replacePath]
|
||||
path = "foobar"
|
||||
[http.middlewares.Middleware18]
|
||||
[http.middlewares.Middleware18.replacePathRegex]
|
||||
[http.middlewares.Middleware18.replacePath]
|
||||
path = "foobar"
|
||||
[http.middlewares.Middleware19]
|
||||
[http.middlewares.Middleware19.replacePathRegex]
|
||||
regex = "foobar"
|
||||
replacement = "foobar"
|
||||
[http.middlewares.Middleware19]
|
||||
[http.middlewares.Middleware19.retry]
|
||||
attempts = 42
|
||||
[http.middlewares.Middleware20]
|
||||
[http.middlewares.Middleware20.stripPrefix]
|
||||
[http.middlewares.Middleware20.retry]
|
||||
attempts = 42
|
||||
[http.middlewares.Middleware21]
|
||||
[http.middlewares.Middleware21.stripPrefix]
|
||||
prefixes = ["foobar", "foobar"]
|
||||
forceSlash = true
|
||||
[http.middlewares.Middleware21]
|
||||
[http.middlewares.Middleware21.stripPrefixRegex]
|
||||
[http.middlewares.Middleware22]
|
||||
[http.middlewares.Middleware22.stripPrefixRegex]
|
||||
regex = ["foobar", "foobar"]
|
||||
|
||||
[tcp]
|
||||
|
@@ -254,6 +254,10 @@ http:
|
||||
domainComponent: true
|
||||
serialNumber: true
|
||||
Middleware14:
|
||||
plugin:
|
||||
PluginConf:
|
||||
foo = "bar"
|
||||
Middleware15:
|
||||
rateLimit:
|
||||
average: 42
|
||||
period: 42
|
||||
@@ -266,33 +270,33 @@ http:
|
||||
- foobar
|
||||
requestHeaderName: foobar
|
||||
requestHost: true
|
||||
Middleware15:
|
||||
Middleware16:
|
||||
redirectRegex:
|
||||
regex: foobar
|
||||
replacement: foobar
|
||||
permanent: true
|
||||
Middleware16:
|
||||
Middleware17:
|
||||
redirectScheme:
|
||||
scheme: foobar
|
||||
port: foobar
|
||||
permanent: true
|
||||
Middleware17:
|
||||
Middleware18:
|
||||
replacePath:
|
||||
path: foobar
|
||||
Middleware18:
|
||||
Middleware19:
|
||||
replacePathRegex:
|
||||
regex: foobar
|
||||
replacement: foobar
|
||||
Middleware19:
|
||||
Middleware20:
|
||||
retry:
|
||||
attempts: 42
|
||||
Middleware20:
|
||||
Middleware21:
|
||||
stripPrefix:
|
||||
prefixes:
|
||||
- foobar
|
||||
- foobar
|
||||
forceSlash: true
|
||||
Middleware21:
|
||||
Middleware22:
|
||||
stripPrefixRegex:
|
||||
regex:
|
||||
- foobar
|
||||
|
@@ -18,6 +18,7 @@ rules:
|
||||
- extensions
|
||||
resources:
|
||||
- ingresses
|
||||
- ingressclasses
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
|
@@ -105,29 +105,30 @@
|
||||
| `traefik/http/middlewares/Middleware13/passTLSClientCert/info/subject/province` | `true` |
|
||||
| `traefik/http/middlewares/Middleware13/passTLSClientCert/info/subject/serialNumber` | `true` |
|
||||
| `traefik/http/middlewares/Middleware13/passTLSClientCert/pem` | `true` |
|
||||
| `traefik/http/middlewares/Middleware14/rateLimit/average` | `42` |
|
||||
| `traefik/http/middlewares/Middleware14/rateLimit/burst` | `42` |
|
||||
| `traefik/http/middlewares/Middleware14/rateLimit/period` | `42` |
|
||||
| `traefik/http/middlewares/Middleware14/rateLimit/sourceCriterion/ipStrategy/depth` | `42` |
|
||||
| `traefik/http/middlewares/Middleware14/rateLimit/sourceCriterion/ipStrategy/excludedIPs/0` | `foobar` |
|
||||
| `traefik/http/middlewares/Middleware14/rateLimit/sourceCriterion/ipStrategy/excludedIPs/1` | `foobar` |
|
||||
| `traefik/http/middlewares/Middleware14/rateLimit/sourceCriterion/requestHeaderName` | `foobar` |
|
||||
| `traefik/http/middlewares/Middleware14/rateLimit/sourceCriterion/requestHost` | `true` |
|
||||
| `traefik/http/middlewares/Middleware15/redirectRegex/permanent` | `true` |
|
||||
| `traefik/http/middlewares/Middleware15/redirectRegex/regex` | `foobar` |
|
||||
| `traefik/http/middlewares/Middleware15/redirectRegex/replacement` | `foobar` |
|
||||
| `traefik/http/middlewares/Middleware16/redirectScheme/permanent` | `true` |
|
||||
| `traefik/http/middlewares/Middleware16/redirectScheme/port` | `foobar` |
|
||||
| `traefik/http/middlewares/Middleware16/redirectScheme/scheme` | `foobar` |
|
||||
| `traefik/http/middlewares/Middleware17/replacePath/path` | `foobar` |
|
||||
| `traefik/http/middlewares/Middleware18/replacePathRegex/regex` | `foobar` |
|
||||
| `traefik/http/middlewares/Middleware18/replacePathRegex/replacement` | `foobar` |
|
||||
| `traefik/http/middlewares/Middleware19/retry/attempts` | `42` |
|
||||
| `traefik/http/middlewares/Middleware20/stripPrefix/forceSlash` | `true` |
|
||||
| `traefik/http/middlewares/Middleware20/stripPrefix/prefixes/0` | `foobar` |
|
||||
| `traefik/http/middlewares/Middleware20/stripPrefix/prefixes/1` | `foobar` |
|
||||
| `traefik/http/middlewares/Middleware21/stripPrefixRegex/regex/0` | `foobar` |
|
||||
| `traefik/http/middlewares/Middleware21/stripPrefixRegex/regex/1` | `foobar` |
|
||||
| `traefik/http/middlewares/Middleware14/plugin/PluginConf/foo` | `bar` |
|
||||
| `traefik/http/middlewares/Middleware15/rateLimit/average` | `42` |
|
||||
| `traefik/http/middlewares/Middleware15/rateLimit/burst` | `42` |
|
||||
| `traefik/http/middlewares/Middleware15/rateLimit/period` | `42` |
|
||||
| `traefik/http/middlewares/Middleware15/rateLimit/sourceCriterion/ipStrategy/depth` | `42` |
|
||||
| `traefik/http/middlewares/Middleware15/rateLimit/sourceCriterion/ipStrategy/excludedIPs/0` | `foobar` |
|
||||
| `traefik/http/middlewares/Middleware15/rateLimit/sourceCriterion/ipStrategy/excludedIPs/1` | `foobar` |
|
||||
| `traefik/http/middlewares/Middleware15/rateLimit/sourceCriterion/requestHeaderName` | `foobar` |
|
||||
| `traefik/http/middlewares/Middleware15/rateLimit/sourceCriterion/requestHost` | `true` |
|
||||
| `traefik/http/middlewares/Middleware16/redirectRegex/permanent` | `true` |
|
||||
| `traefik/http/middlewares/Middleware16/redirectRegex/regex` | `foobar` |
|
||||
| `traefik/http/middlewares/Middleware16/redirectRegex/replacement` | `foobar` |
|
||||
| `traefik/http/middlewares/Middleware17/redirectScheme/permanent` | `true` |
|
||||
| `traefik/http/middlewares/Middleware17/redirectScheme/port` | `foobar` |
|
||||
| `traefik/http/middlewares/Middleware17/redirectScheme/scheme` | `foobar` |
|
||||
| `traefik/http/middlewares/Middleware18/replacePath/path` | `foobar` |
|
||||
| `traefik/http/middlewares/Middleware19/replacePathRegex/regex` | `foobar` |
|
||||
| `traefik/http/middlewares/Middleware19/replacePathRegex/replacement` | `foobar` |
|
||||
| `traefik/http/middlewares/Middleware20/retry/attempts` | `42` |
|
||||
| `traefik/http/middlewares/Middleware21/stripPrefix/forceSlash` | `true` |
|
||||
| `traefik/http/middlewares/Middleware21/stripPrefix/prefixes/0` | `foobar` |
|
||||
| `traefik/http/middlewares/Middleware21/stripPrefix/prefixes/1` | `foobar` |
|
||||
| `traefik/http/middlewares/Middleware22/stripPrefixRegex/regex/0` | `foobar` |
|
||||
| `traefik/http/middlewares/Middleware22/stripPrefixRegex/regex/1` | `foobar` |
|
||||
| `traefik/http/routers/Router0/entryPoints/0` | `foobar` |
|
||||
| `traefik/http/routers/Router0/entryPoints/1` | `foobar` |
|
||||
| `traefik/http/routers/Router0/middlewares/0` | `foobar` |
|
||||
|
@@ -91,26 +91,27 @@
|
||||
"traefik.http.middlewares.middleware13.passtlsclientcert.info.subject.province": "true",
|
||||
"traefik.http.middlewares.middleware13.passtlsclientcert.info.subject.serialnumber": "true",
|
||||
"traefik.http.middlewares.middleware13.passtlsclientcert.pem": "true",
|
||||
"traefik.http.middlewares.middleware14.ratelimit.average": "42",
|
||||
"traefik.http.middlewares.middleware14.ratelimit.burst": "42",
|
||||
"traefik.http.middlewares.middleware14.ratelimit.period": "42",
|
||||
"traefik.http.middlewares.middleware14.ratelimit.sourcecriterion.ipstrategy.depth": "42",
|
||||
"traefik.http.middlewares.middleware14.ratelimit.sourcecriterion.ipstrategy.excludedips": "foobar, foobar",
|
||||
"traefik.http.middlewares.middleware14.ratelimit.sourcecriterion.requestheadername": "foobar",
|
||||
"traefik.http.middlewares.middleware14.ratelimit.sourcecriterion.requesthost": "true",
|
||||
"traefik.http.middlewares.middleware15.redirectregex.permanent": "true",
|
||||
"traefik.http.middlewares.middleware15.redirectregex.regex": "foobar",
|
||||
"traefik.http.middlewares.middleware15.redirectregex.replacement": "foobar",
|
||||
"traefik.http.middlewares.middleware16.redirectscheme.permanent": "true",
|
||||
"traefik.http.middlewares.middleware16.redirectscheme.port": "foobar",
|
||||
"traefik.http.middlewares.middleware16.redirectscheme.scheme": "foobar",
|
||||
"traefik.http.middlewares.middleware17.replacepath.path": "foobar",
|
||||
"traefik.http.middlewares.middleware18.replacepathregex.regex": "foobar",
|
||||
"traefik.http.middlewares.middleware18.replacepathregex.replacement": "foobar",
|
||||
"traefik.http.middlewares.middleware19.retry.attempts": "42",
|
||||
"traefik.http.middlewares.middleware20.stripprefix.forceslash": "true",
|
||||
"traefik.http.middlewares.middleware20.stripprefix.prefixes": "foobar, foobar",
|
||||
"traefik.http.middlewares.middleware21.stripprefixregex.regex": "foobar, foobar",
|
||||
"traefik.http.middlewares.middleware14.plugin.foobar.foo": "bar",
|
||||
"traefik.http.middlewares.middleware15.ratelimit.average": "42",
|
||||
"traefik.http.middlewares.middleware15.ratelimit.burst": "42",
|
||||
"traefik.http.middlewares.middleware15.ratelimit.period": "42",
|
||||
"traefik.http.middlewares.middleware15.ratelimit.sourcecriterion.ipstrategy.depth": "42",
|
||||
"traefik.http.middlewares.middleware15.ratelimit.sourcecriterion.ipstrategy.excludedips": "foobar, foobar",
|
||||
"traefik.http.middlewares.middleware15.ratelimit.sourcecriterion.requestheadername": "foobar",
|
||||
"traefik.http.middlewares.middleware15.ratelimit.sourcecriterion.requesthost": "true",
|
||||
"traefik.http.middlewares.middleware16.redirectregex.permanent": "true",
|
||||
"traefik.http.middlewares.middleware16.redirectregex.regex": "foobar",
|
||||
"traefik.http.middlewares.middleware16.redirectregex.replacement": "foobar",
|
||||
"traefik.http.middlewares.middleware17.redirectscheme.permanent": "true",
|
||||
"traefik.http.middlewares.middleware17.redirectscheme.port": "foobar",
|
||||
"traefik.http.middlewares.middleware17.redirectscheme.scheme": "foobar",
|
||||
"traefik.http.middlewares.middleware18.replacepath.path": "foobar",
|
||||
"traefik.http.middlewares.middleware19.replacepathregex.regex": "foobar",
|
||||
"traefik.http.middlewares.middleware19.replacepathregex.replacement": "foobar",
|
||||
"traefik.http.middlewares.middleware20.retry.attempts": "42",
|
||||
"traefik.http.middlewares.middleware21.stripprefix.forceslash": "true",
|
||||
"traefik.http.middlewares.middleware21.stripprefix.prefixes": "foobar, foobar",
|
||||
"traefik.http.middlewares.middleware22.stripprefixregex.regex": "foobar, foobar",
|
||||
"traefik.http.routers.router0.entrypoints": "foobar, foobar",
|
||||
"traefik.http.routers.router0.middlewares": "foobar, foobar",
|
||||
"traefik.http.routers.router0.priority": "42",
|
||||
@@ -148,8 +149,8 @@
|
||||
"traefik.http.services.service01.loadbalancer.sticky.cookie": "true",
|
||||
"traefik.http.services.service01.loadbalancer.sticky.cookie.httponly": "true",
|
||||
"traefik.http.services.service01.loadbalancer.sticky.cookie.name": "foobar",
|
||||
"traefik.http.services.service01.loadbalancer.sticky.cookie.secure": "true",
|
||||
"traefik.http.services.service01.loadbalancer.sticky.cookie.samesite": "foobar",
|
||||
"traefik.http.services.service01.loadbalancer.sticky.cookie.secure": "true",
|
||||
"traefik.http.services.service01.loadbalancer.server.port": "foobar",
|
||||
"traefik.http.services.service01.loadbalancer.server.scheme": "foobar",
|
||||
"traefik.tcp.routers.tcprouter0.entrypoints": "foobar, foobar",
|
||||
|
@@ -159,6 +159,21 @@ ReadTimeout is the maximum duration for reading the entire request, including th
|
||||
`--entrypoints.<name>.transport.respondingtimeouts.writetimeout`:
|
||||
WriteTimeout is the maximum duration before timing out writes of the response. If zero, no timeout is set. (Default: ```0```)
|
||||
|
||||
`--experimental.devplugin.gopath`:
|
||||
plugin's GOPATH.
|
||||
|
||||
`--experimental.devplugin.modulename`:
|
||||
plugin's module name.
|
||||
|
||||
`--experimental.pilot.token`:
|
||||
Traefik Pilot token.
|
||||
|
||||
`--experimental.plugins.<name>.modulename`:
|
||||
plugin's module name.
|
||||
|
||||
`--experimental.plugins.<name>.version`:
|
||||
plugin's version.
|
||||
|
||||
`--global.checknewversion`:
|
||||
Periodically check if a new version has been released. (Default: ```false```)
|
||||
|
||||
@@ -279,6 +294,9 @@ EntryPoint (Default: ```traefik```)
|
||||
`--ping.manualrouting`:
|
||||
Manual routing (Default: ```false```)
|
||||
|
||||
`--ping.terminatingstatuscode`:
|
||||
Terminating status code (Default: ```503```)
|
||||
|
||||
`--providers.consul`:
|
||||
Enable Consul backend with default settings. (Default: ```false```)
|
||||
|
||||
@@ -414,6 +432,33 @@ Use the ip address from the bound port, rather than from the inner network. (Def
|
||||
`--providers.docker.watch`:
|
||||
Watch Docker Swarm events. (Default: ```true```)
|
||||
|
||||
`--providers.ecs.accesskeyid`:
|
||||
The AWS credentials access key to use for making requests
|
||||
|
||||
`--providers.ecs.autodiscoverclusters`:
|
||||
Auto discover cluster (Default: ```false```)
|
||||
|
||||
`--providers.ecs.clusters`:
|
||||
ECS Clusters name (Default: ```default```)
|
||||
|
||||
`--providers.ecs.constraints`:
|
||||
Constraints is an expression that Traefik matches against the container's labels to determine whether to create any route for that container.
|
||||
|
||||
`--providers.ecs.defaultrule`:
|
||||
Default rule. (Default: ```Host(`{{ normalize .Name }}`)```)
|
||||
|
||||
`--providers.ecs.exposedbydefault`:
|
||||
Expose services by default (Default: ```true```)
|
||||
|
||||
`--providers.ecs.refreshseconds`:
|
||||
Polling interval (in seconds) (Default: ```15```)
|
||||
|
||||
`--providers.ecs.region`:
|
||||
The AWS region to use for requests
|
||||
|
||||
`--providers.ecs.secretaccesskey`:
|
||||
The AWS credentials access key to use for making requests
|
||||
|
||||
`--providers.etcd`:
|
||||
Enable Etcd backend with default settings. (Default: ```false```)
|
||||
|
||||
@@ -456,6 +501,33 @@ Load dynamic configuration from a file.
|
||||
`--providers.file.watch`:
|
||||
Watch provider. (Default: ```true```)
|
||||
|
||||
`--providers.http`:
|
||||
Enable HTTP backend with default settings. (Default: ```false```)
|
||||
|
||||
`--providers.http.endpoint`:
|
||||
Load configuration from this endpoint.
|
||||
|
||||
`--providers.http.pollinterval`:
|
||||
Polling interval for endpoint. (Default: ```5```)
|
||||
|
||||
`--providers.http.polltimeout`:
|
||||
Polling timeout for endpoint. (Default: ```5```)
|
||||
|
||||
`--providers.http.tls.ca`:
|
||||
TLS CA
|
||||
|
||||
`--providers.http.tls.caoptional`:
|
||||
TLS CA.Optional (Default: ```false```)
|
||||
|
||||
`--providers.http.tls.cert`:
|
||||
TLS cert
|
||||
|
||||
`--providers.http.tls.insecureskipverify`:
|
||||
TLS insecure skip verify (Default: ```false```)
|
||||
|
||||
`--providers.http.tls.key`:
|
||||
TLS key
|
||||
|
||||
`--providers.kubernetescrd`:
|
||||
Enable Kubernetes backend with default settings. (Default: ```false```)
|
||||
|
||||
|
@@ -159,6 +159,21 @@ ReadTimeout is the maximum duration for reading the entire request, including th
|
||||
`TRAEFIK_ENTRYPOINTS_<NAME>_TRANSPORT_RESPONDINGTIMEOUTS_WRITETIMEOUT`:
|
||||
WriteTimeout is the maximum duration before timing out writes of the response. If zero, no timeout is set. (Default: ```0```)
|
||||
|
||||
`TRAEFIK_EXPERIMENTAL_DEVPLUGIN_GOPATH`:
|
||||
plugin's GOPATH.
|
||||
|
||||
`TRAEFIK_EXPERIMENTAL_DEVPLUGIN_MODULENAME`:
|
||||
plugin's module name.
|
||||
|
||||
`TRAEFIK_EXPERIMENTAL_PILOT_TOKEN`:
|
||||
Traefik Pilot token.
|
||||
|
||||
`TRAEFIK_EXPERIMENTAL_PLUGINS_<NAME>_MODULENAME`:
|
||||
plugin's module name.
|
||||
|
||||
`TRAEFIK_EXPERIMENTAL_PLUGINS_<NAME>_VERSION`:
|
||||
plugin's version.
|
||||
|
||||
`TRAEFIK_GLOBAL_CHECKNEWVERSION`:
|
||||
Periodically check if a new version has been released. (Default: ```false```)
|
||||
|
||||
@@ -279,6 +294,9 @@ EntryPoint (Default: ```traefik```)
|
||||
`TRAEFIK_PING_MANUALROUTING`:
|
||||
Manual routing (Default: ```false```)
|
||||
|
||||
`TRAEFIK_PING_TERMINATINGSTATUSCODE`:
|
||||
Terminating status code (Default: ```503```)
|
||||
|
||||
`TRAEFIK_PROVIDERS_CONSUL`:
|
||||
Enable Consul backend with default settings. (Default: ```false```)
|
||||
|
||||
@@ -414,6 +432,33 @@ Use the ip address from the bound port, rather than from the inner network. (Def
|
||||
`TRAEFIK_PROVIDERS_DOCKER_WATCH`:
|
||||
Watch Docker Swarm events. (Default: ```true```)
|
||||
|
||||
`TRAEFIK_PROVIDERS_ECS_ACCESSKEYID`:
|
||||
The AWS credentials access key to use for making requests
|
||||
|
||||
`TRAEFIK_PROVIDERS_ECS_AUTODISCOVERCLUSTERS`:
|
||||
Auto discover cluster (Default: ```false```)
|
||||
|
||||
`TRAEFIK_PROVIDERS_ECS_CLUSTERS`:
|
||||
ECS Clusters name (Default: ```default```)
|
||||
|
||||
`TRAEFIK_PROVIDERS_ECS_CONSTRAINTS`:
|
||||
Constraints is an expression that Traefik matches against the container's labels to determine whether to create any route for that container.
|
||||
|
||||
`TRAEFIK_PROVIDERS_ECS_DEFAULTRULE`:
|
||||
Default rule. (Default: ```Host(`{{ normalize .Name }}`)```)
|
||||
|
||||
`TRAEFIK_PROVIDERS_ECS_EXPOSEDBYDEFAULT`:
|
||||
Expose services by default (Default: ```true```)
|
||||
|
||||
`TRAEFIK_PROVIDERS_ECS_REFRESHSECONDS`:
|
||||
Polling interval (in seconds) (Default: ```15```)
|
||||
|
||||
`TRAEFIK_PROVIDERS_ECS_REGION`:
|
||||
The AWS region to use for requests
|
||||
|
||||
`TRAEFIK_PROVIDERS_ECS_SECRETACCESSKEY`:
|
||||
The AWS credentials access key to use for making requests
|
||||
|
||||
`TRAEFIK_PROVIDERS_ETCD`:
|
||||
Enable Etcd backend with default settings. (Default: ```false```)
|
||||
|
||||
@@ -456,6 +501,33 @@ Load dynamic configuration from a file.
|
||||
`TRAEFIK_PROVIDERS_FILE_WATCH`:
|
||||
Watch provider. (Default: ```true```)
|
||||
|
||||
`TRAEFIK_PROVIDERS_HTTP`:
|
||||
Enable HTTP backend with default settings. (Default: ```false```)
|
||||
|
||||
`TRAEFIK_PROVIDERS_HTTP_ENDPOINT`:
|
||||
Load configuration from this endpoint.
|
||||
|
||||
`TRAEFIK_PROVIDERS_HTTP_POLLINTERVAL`:
|
||||
Polling interval for endpoint. (Default: ```5```)
|
||||
|
||||
`TRAEFIK_PROVIDERS_HTTP_POLLTIMEOUT`:
|
||||
Polling timeout for endpoint. (Default: ```5```)
|
||||
|
||||
`TRAEFIK_PROVIDERS_HTTP_TLS_CA`:
|
||||
TLS CA
|
||||
|
||||
`TRAEFIK_PROVIDERS_HTTP_TLS_CAOPTIONAL`:
|
||||
TLS CA.Optional (Default: ```false```)
|
||||
|
||||
`TRAEFIK_PROVIDERS_HTTP_TLS_CERT`:
|
||||
TLS cert
|
||||
|
||||
`TRAEFIK_PROVIDERS_HTTP_TLS_INSECURESKIPVERIFY`:
|
||||
TLS insecure skip verify (Default: ```false```)
|
||||
|
||||
`TRAEFIK_PROVIDERS_HTTP_TLS_KEY`:
|
||||
TLS key
|
||||
|
||||
`TRAEFIK_PROVIDERS_KUBERNETESCRD`:
|
||||
Enable Kubernetes backend with default settings. (Default: ```false```)
|
||||
|
||||
|
@@ -151,6 +151,16 @@
|
||||
[providers.consulCatalog.endpoint.httpAuth]
|
||||
username = "foobar"
|
||||
password = "foobar"
|
||||
[providers.ecs]
|
||||
constraints = "foobar"
|
||||
exposedByDefault = true
|
||||
refreshSeconds = 42
|
||||
defaultRule = "foobar"
|
||||
clusters = ["foobar", "foobar"]
|
||||
autoDiscoverClusters = true
|
||||
region = "foobar"
|
||||
accessKeyID = "foobar"
|
||||
secretAccessKey = "foobar"
|
||||
[providers.consul]
|
||||
rootKey = "foobar"
|
||||
endpoints = ["foobar", "foobar"]
|
||||
@@ -195,6 +205,16 @@
|
||||
cert = "foobar"
|
||||
key = "foobar"
|
||||
insecureSkipVerify = true
|
||||
[providers.http]
|
||||
endpoint = "foobar"
|
||||
pollInterval = 42
|
||||
pollTimeout = 42
|
||||
[providers.http.tls]
|
||||
ca = "foobar"
|
||||
caOptional = true
|
||||
cert = "foobar"
|
||||
key = "foobar"
|
||||
insecureSkipVerify = true
|
||||
|
||||
[api]
|
||||
insecure = true
|
||||
@@ -233,6 +253,7 @@
|
||||
[ping]
|
||||
entryPoint = "foobar"
|
||||
manualRouting = true
|
||||
terminatingStatusCode = 42
|
||||
|
||||
[log]
|
||||
level = "foobar"
|
||||
@@ -338,3 +359,17 @@
|
||||
[certificatesResolvers.CertificateResolver1.acme.httpChallenge]
|
||||
entryPoint = "foobar"
|
||||
[certificatesResolvers.CertificateResolver1.acme.tlsChallenge]
|
||||
|
||||
[experimental]
|
||||
[experimental.pilot]
|
||||
token = "foobar"
|
||||
[experimental.plugins]
|
||||
[experimental.plugins.Descriptor0]
|
||||
moduleName = "foobar"
|
||||
version = "foobar"
|
||||
[experimental.plugins.Descriptor1]
|
||||
moduleName = "foobar"
|
||||
version = "foobar"
|
||||
[experimental.devPlugin]
|
||||
goPath = "foobar"
|
||||
moduleName = "foobar"
|
||||
|
@@ -161,6 +161,18 @@ providers:
|
||||
httpAuth:
|
||||
username: foobar
|
||||
password: foobar
|
||||
ecs:
|
||||
constraints: foobar
|
||||
exposedByDefault: true
|
||||
refreshSeconds: 42
|
||||
defaultRule: foobar
|
||||
clusters:
|
||||
- foobar
|
||||
- foobar
|
||||
autoDiscoverClusters: true
|
||||
region: foobar
|
||||
accessKeyID: foobar
|
||||
secretAccessKey: foobar
|
||||
consul:
|
||||
rootKey: foobar
|
||||
endpoints:
|
||||
@@ -213,6 +225,16 @@ providers:
|
||||
cert: foobar
|
||||
key: foobar
|
||||
insecureSkipVerify: true
|
||||
http:
|
||||
endpoint: foobar
|
||||
pollInterval: 42
|
||||
pollTimeout: 42
|
||||
tls:
|
||||
ca: foobar
|
||||
caOptional: true
|
||||
cert: foobar
|
||||
key: foobar
|
||||
insecureSkipVerify: true
|
||||
api:
|
||||
insecure: true
|
||||
dashboard: true
|
||||
@@ -250,6 +272,7 @@ metrics:
|
||||
ping:
|
||||
entryPoint: foobar
|
||||
manualRouting: true
|
||||
terminatingStatusCode: 42
|
||||
log:
|
||||
level: foobar
|
||||
filePath: foobar
|
||||
@@ -356,3 +379,16 @@ certificatesResolvers:
|
||||
httpChallenge:
|
||||
entryPoint: foobar
|
||||
tlsChallenge: {}
|
||||
experimental:
|
||||
pilot:
|
||||
token: foobar
|
||||
plugins:
|
||||
Descriptor0:
|
||||
moduleName: foobar
|
||||
version: foobar
|
||||
Descriptor1:
|
||||
moduleName: foobar
|
||||
version: foobar
|
||||
devPlugin:
|
||||
goPath: foobar
|
||||
moduleName: foobar
|
||||
|
@@ -535,7 +535,7 @@ You can declare UDP Routers and/or Services using labels.
|
||||
my-container:
|
||||
# ...
|
||||
labels:
|
||||
- "traefik.udp.routers.my-router.entrypoint=udp"
|
||||
- "traefik.udp.routers.my-router.entrypoints=udp"
|
||||
- "traefik.udp.services.my-service.loadbalancer.server.port=4123"
|
||||
```
|
||||
|
||||
|
445
docs/content/routing/providers/ecs.md
Normal file
445
docs/content/routing/providers/ecs.md
Normal file
@@ -0,0 +1,445 @@
|
||||
# Traefik & ECS
|
||||
|
||||
A Story of Labels & Elastic Containers
|
||||
{: .subtitle }
|
||||
|
||||
Attach labels to your containers and let Traefik do the rest!
|
||||
|
||||
## Routing Configuration
|
||||
|
||||
!!! info "labels"
|
||||
|
||||
- labels are case insensitive.
|
||||
- The complete list of labels can be found [the reference page](../../reference/dynamic-configuration/ecs.md)
|
||||
|
||||
### General
|
||||
|
||||
Traefik creates, for each elastic service, a corresponding [service](../services/index.md) and [router](../routers/index.md).
|
||||
|
||||
The Service automatically gets a server per elastic container, and the router gets a default rule attached to it, based on the service name.
|
||||
|
||||
### Routers
|
||||
|
||||
To update the configuration of the Router automatically attached to the service, add labels starting with `traefik.routers.{name-of-your-choice}.` and followed by the option you want to change.
|
||||
|
||||
For example, to change the rule, you could add the label ```traefik.http.routers.my-service.rule=Host(`example.com`)```.
|
||||
|
||||
!!! warning "The character `@` is not authorized in the router name `<router_name>`."
|
||||
|
||||
??? info "`traefik.http.routers.<router_name>.rule`"
|
||||
|
||||
See [rule](../routers/index.md#rule) for more information.
|
||||
|
||||
```yaml
|
||||
traefik.http.routers.myrouter.rule=Host(`example.com`)
|
||||
```
|
||||
|
||||
??? info "`traefik.http.routers.<router_name>.entrypoints`"
|
||||
|
||||
See [entry points](../routers/index.md#entrypoints) for more information.
|
||||
|
||||
```yaml
|
||||
traefik.http.routers.myrouter.entrypoints=web,websecure
|
||||
```
|
||||
|
||||
??? info "`traefik.http.routers.<router_name>.middlewares`"
|
||||
|
||||
See [middlewares](../routers/index.md#middlewares) and [middlewares overview](../../middlewares/overview.md) for more information.
|
||||
|
||||
```yaml
|
||||
traefik.http.routers.myrouter.middlewares=auth,prefix,cb
|
||||
```
|
||||
|
||||
??? info "`traefik.http.routers.<router_name>.service`"
|
||||
|
||||
See [rule](../routers/index.md#service) for more information.
|
||||
|
||||
```yaml
|
||||
traefik.http.routers.myrouter.service=myservice
|
||||
```
|
||||
|
||||
??? info "`traefik.http.routers.<router_name>.tls`"
|
||||
|
||||
See [tls](../routers/index.md#tls) for more information.
|
||||
|
||||
```yaml
|
||||
traefik.http.routers.myrouter>.tls=true
|
||||
```
|
||||
|
||||
??? info "`traefik.http.routers.<router_name>.tls.certresolver`"
|
||||
|
||||
See [certResolver](../routers/index.md#certresolver) for more information.
|
||||
|
||||
```yaml
|
||||
traefik.http.routers.myrouter.tls.certresolver=myresolver
|
||||
```
|
||||
|
||||
??? info "`traefik.http.routers.<router_name>.tls.domains[n].main`"
|
||||
|
||||
See [domains](../routers/index.md#domains) for more information.
|
||||
|
||||
```yaml
|
||||
traefik.http.routers.myrouter.tls.domains[0].main=example.org
|
||||
```
|
||||
|
||||
??? info "`traefik.http.routers.<router_name>.tls.domains[n].sans`"
|
||||
|
||||
See [domains](../routers/index.md#domains) for more information.
|
||||
|
||||
```yaml
|
||||
traefik.http.routers.myrouter.tls.domains[0].sans=test.example.org,dev.example.org
|
||||
```
|
||||
|
||||
??? info "`traefik.http.routers.<router_name>.tls.options`"
|
||||
|
||||
See [options](../routers/index.md#options) for more information.
|
||||
|
||||
```yaml
|
||||
traefik.http.routers.myrouter.tls.options=foobar
|
||||
```
|
||||
|
||||
??? info "`traefik.http.routers.<router_name>.priority`"
|
||||
|
||||
See [priority](../routers/index.md#priority) for more information.
|
||||
|
||||
```yaml
|
||||
traefik.http.routers.myrouter.priority=42
|
||||
```
|
||||
|
||||
### Services
|
||||
|
||||
To update the configuration of the Service automatically attached to the service,
|
||||
add labels starting with `traefik.http.services.{name-of-your-choice}.`, followed by the option you want to change.
|
||||
|
||||
For example, to change the `passHostHeader` behavior,
|
||||
you'd add the label `traefik.http.services.{name-of-your-choice}.loadbalancer.passhostheader=false`.
|
||||
|
||||
!!! warning "The character `@` is not authorized in the service name `<service_name>`."
|
||||
|
||||
??? info "`traefik.http.services.<service_name>.loadbalancer.server.port`"
|
||||
|
||||
Registers a port.
|
||||
Useful when the service exposes multiples ports.
|
||||
|
||||
```yaml
|
||||
traefik.http.services.myservice.loadbalancer.server.port=8080
|
||||
```
|
||||
|
||||
??? info "`traefik.http.services.<service_name>.loadbalancer.server.scheme`"
|
||||
|
||||
Overrides the default scheme.
|
||||
|
||||
```yaml
|
||||
traefik.http.services.myservice.loadbalancer.server.scheme=http
|
||||
```
|
||||
|
||||
??? info "`traefik.http.services.<service_name>.loadbalancer.passhostheader`"
|
||||
|
||||
See [pass Host header](../services/index.md#pass-host-header) for more information.
|
||||
|
||||
```yaml
|
||||
traefik.http.services.myservice.loadbalancer.passhostheader=true
|
||||
```
|
||||
|
||||
??? info "`traefik.http.services.<service_name>.loadbalancer.healthcheck.headers.<header_name>`"
|
||||
|
||||
See [health check](../services/index.md#health-check) for more information.
|
||||
|
||||
```yaml
|
||||
traefik.http.services.myservice.loadbalancer.healthcheck.headers.X-Foo=foobar
|
||||
```
|
||||
|
||||
??? info "`traefik.http.services.<service_name>.loadbalancer.healthcheck.hostname`"
|
||||
|
||||
See [health check](../services/index.md#health-check) for more information.
|
||||
|
||||
```yaml
|
||||
traefik.http.services.myservice.loadbalancer.healthcheck.hostname=example.org
|
||||
```
|
||||
|
||||
??? info "`traefik.http.services.<service_name>.loadbalancer.healthcheck.interval`"
|
||||
|
||||
See [health check](../services/index.md#health-check) for more information.
|
||||
|
||||
```yaml
|
||||
traefik.http.services.myservice.loadbalancer.healthcheck.interval=10
|
||||
```
|
||||
|
||||
??? info "`traefik.http.services.<service_name>.loadbalancer.healthcheck.path`"
|
||||
|
||||
See [health check](../services/index.md#health-check) for more information.
|
||||
|
||||
```yaml
|
||||
traefik.http.services.myservice.loadbalancer.healthcheck.path=/foo
|
||||
```
|
||||
|
||||
??? info "`traefik.http.services.<service_name>.loadbalancer.healthcheck.port`"
|
||||
|
||||
See [health check](../services/index.md#health-check) for more information.
|
||||
|
||||
```yaml
|
||||
traefik.http.services.myservice.loadbalancer.healthcheck.port=42
|
||||
```
|
||||
|
||||
??? info "`traefik.http.services.<service_name>.loadbalancer.healthcheck.scheme`"
|
||||
|
||||
See [health check](../services/index.md#health-check) for more information.
|
||||
|
||||
```yaml
|
||||
traefik.http.services.myservice.loadbalancer.healthcheck.scheme=http
|
||||
```
|
||||
|
||||
??? info "`traefik.http.services.<service_name>.loadbalancer.healthcheck.timeout`"
|
||||
|
||||
See [health check](../services/index.md#health-check) for more information.
|
||||
|
||||
```yaml
|
||||
traefik.http.services.myservice.loadbalancer.healthcheck.timeout=10
|
||||
```
|
||||
|
||||
??? info "`traefik.http.services.<service_name>.loadbalancer.healthcheck.followredirects`"
|
||||
|
||||
See [health check](../services/index.md#health-check) for more information.
|
||||
|
||||
```yaml
|
||||
traefik.http.services.myservice.loadbalancer.healthcheck.followredirects=true
|
||||
```
|
||||
|
||||
??? info "`traefik.http.services.<service_name>.loadbalancer.sticky`"
|
||||
|
||||
See [sticky sessions](../services/index.md#sticky-sessions) for more information.
|
||||
|
||||
```yaml
|
||||
traefik.http.services.myservice.loadbalancer.sticky=true
|
||||
```
|
||||
|
||||
??? info "`traefik.http.services.<service_name>.loadbalancer.sticky.cookie.httponly`"
|
||||
|
||||
See [sticky sessions](../services/index.md#sticky-sessions) for more information.
|
||||
|
||||
```yaml
|
||||
traefik.http.services.myservice.loadbalancer.sticky.cookie.httponly=true
|
||||
```
|
||||
|
||||
??? info "`traefik.http.services.<service_name>.loadbalancer.sticky.cookie.name`"
|
||||
|
||||
See [sticky sessions](../services/index.md#sticky-sessions) for more information.
|
||||
|
||||
```yaml
|
||||
traefik.http.services.myservice.loadbalancer.sticky.cookie.name=foobar
|
||||
```
|
||||
|
||||
??? info "`traefik.http.services.<service_name>.loadbalancer.sticky.cookie.secure`"
|
||||
|
||||
See [sticky sessions](../services/index.md#sticky-sessions) for more information.
|
||||
|
||||
```yaml
|
||||
traefik.http.services.myservice.loadbalancer.sticky.cookie.secure=true
|
||||
```
|
||||
|
||||
??? info "`traefik.http.services.<service_name>.loadbalancer.sticky.cookie.samesite`"
|
||||
|
||||
See [sticky sessions](../services/index.md#sticky-sessions) for more information.
|
||||
|
||||
```yaml
|
||||
traefik.http.services.myservice.loadbalancer.sticky.cookie.samesite=none
|
||||
```
|
||||
|
||||
??? info "`traefik.http.services.<service_name>.loadbalancer.responseforwarding.flushinterval`"
|
||||
|
||||
See [response forwarding](../services/index.md#response-forwarding) for more information.
|
||||
|
||||
FlushInterval specifies the flush interval to flush to the client while copying the response body.
|
||||
|
||||
```yaml
|
||||
traefik.http.services.myservice.loadbalancer.responseforwarding.flushinterval=10
|
||||
```
|
||||
|
||||
### Middleware
|
||||
|
||||
You can declare pieces of middleware using labels starting with `traefik.http.middlewares.{name-of-your-choice}.`, followed by the middleware type/options.
|
||||
|
||||
For example, to declare a middleware [`redirectscheme`](../../middlewares/redirectscheme.md) named `my-redirect`, you'd write `traefik.http.middlewares.my-redirect.redirectscheme.scheme: https`.
|
||||
|
||||
More information about available middlewares in the dedicated [middlewares section](../../middlewares/overview.md).
|
||||
|
||||
!!! warning "The character `@` is not authorized in the middleware name."
|
||||
|
||||
??? example "Declaring and Referencing a Middleware"
|
||||
|
||||
```yaml
|
||||
# ...
|
||||
# Declaring a middleware
|
||||
traefik.http.middlewares.my-redirect.redirectscheme.scheme=https
|
||||
# Referencing a middleware
|
||||
traefik.http.routers.my-service.middlewares=my-redirect
|
||||
```
|
||||
|
||||
!!! warning "Conflicts in Declaration"
|
||||
|
||||
If you declare multiple middleware with the same name but with different parameters, the middleware fails to be declared.
|
||||
|
||||
### TCP
|
||||
|
||||
You can declare TCP Routers and/or Services using labels.
|
||||
|
||||
??? example "Declaring TCP Routers and Services"
|
||||
|
||||
```yaml
|
||||
traefik.tcp.routers.my-router.rule=HostSNI(`example.com`)
|
||||
traefik.tcp.routers.my-router.tls=true
|
||||
traefik.tcp.services.my-service.loadbalancer.server.port=4123
|
||||
```
|
||||
|
||||
!!! warning "TCP and HTTP"
|
||||
|
||||
If you declare a TCP Router/Service, it will prevent Traefik from automatically creating an HTTP Router/Service (like it does by default if no TCP Router/Service is defined).
|
||||
You can declare both a TCP Router/Service and an HTTP Router/Service for the same elastic service (but you have to do so manually).
|
||||
|
||||
#### TCP Routers
|
||||
|
||||
??? info "`traefik.tcp.routers.<router_name>.entrypoints`"
|
||||
|
||||
See [entry points](../routers/index.md#entrypoints_1) for more information.
|
||||
|
||||
```yaml
|
||||
traefik.tcp.routers.mytcprouter.entrypoints=ep1,ep2
|
||||
```
|
||||
|
||||
??? info "`traefik.tcp.routers.<router_name>.rule`"
|
||||
|
||||
See [rule](../routers/index.md#rule_1) for more information.
|
||||
|
||||
```yaml
|
||||
traefik.tcp.routers.mytcprouter.rule=HostSNI(`example.com`)
|
||||
```
|
||||
|
||||
??? info "`traefik.tcp.routers.<router_name>.service`"
|
||||
|
||||
See [service](../routers/index.md#services) for more information.
|
||||
|
||||
```yaml
|
||||
traefik.tcp.routers.mytcprouter.service=myservice
|
||||
```
|
||||
|
||||
??? info "`traefik.tcp.routers.<router_name>.tls`"
|
||||
|
||||
See [TLS](../routers/index.md#tls_1) for more information.
|
||||
|
||||
```yaml
|
||||
traefik.tcp.routers.mytcprouter.tls=true
|
||||
```
|
||||
|
||||
??? info "`traefik.tcp.routers.<router_name>.tls.certresolver`"
|
||||
|
||||
See [certResolver](../routers/index.md#certresolver_1) for more information.
|
||||
|
||||
```yaml
|
||||
traefik.tcp.routers.mytcprouter.tls.certresolver=myresolver
|
||||
```
|
||||
|
||||
??? info "`traefik.tcp.routers.<router_name>.tls.domains[n].main`"
|
||||
|
||||
See [domains](../routers/index.md#domains_1) for more information.
|
||||
|
||||
```yaml
|
||||
traefik.tcp.routers.mytcprouter.tls.domains[0].main=example.org
|
||||
```
|
||||
|
||||
??? info "`traefik.tcp.routers.<router_name>.tls.domains[n].sans`"
|
||||
|
||||
See [domains](../routers/index.md#domains_1) for more information.
|
||||
|
||||
```yaml
|
||||
traefik.tcp.routers.mytcprouter.tls.domains[0].sans=test.example.org,dev.example.org
|
||||
```
|
||||
|
||||
??? info "`traefik.tcp.routers.<router_name>.tls.options`"
|
||||
|
||||
See [options](../routers/index.md#options_1) for more information.
|
||||
|
||||
```yaml
|
||||
traefik.tcp.routers.mytcprouter.tls.options=mysoptions
|
||||
```
|
||||
|
||||
??? info "`traefik.tcp.routers.<router_name>.tls.passthrough`"
|
||||
|
||||
See [TLS](../routers/index.md#tls_1) for more information.
|
||||
|
||||
```yaml
|
||||
traefik.tcp.routers.mytcprouter.tls.passthrough=true
|
||||
```
|
||||
|
||||
#### TCP Services
|
||||
|
||||
??? info "`traefik.tcp.services.<service_name>.loadbalancer.server.port`"
|
||||
|
||||
Registers a port of the application.
|
||||
|
||||
```yaml
|
||||
traefik.tcp.services.mytcpservice.loadbalancer.server.port=423
|
||||
```
|
||||
|
||||
??? info "`traefik.tcp.services.<service_name>.loadbalancer.terminationdelay`"
|
||||
|
||||
See [termination delay](../services/index.md#termination-delay) for more information.
|
||||
|
||||
```yaml
|
||||
traefik.tcp.services.mytcpservice.loadbalancer.terminationdelay=100
|
||||
```
|
||||
|
||||
### UDP
|
||||
|
||||
You can declare UDP Routers and/or Services using tags.
|
||||
|
||||
??? example "Declaring UDP Routers and Services"
|
||||
|
||||
```yaml
|
||||
traefik.udp.routers.my-router.entrypoints=udp
|
||||
traefik.udp.services.my-service.loadbalancer.server.port=4123
|
||||
```
|
||||
|
||||
!!! warning "UDP and HTTP"
|
||||
|
||||
If you declare a UDP Router/Service, it will prevent Traefik from automatically creating an HTTP Router/Service (like it does by default if no UDP Router/Service is defined).
|
||||
You can declare both a UDP Router/Service and an HTTP Router/Service for the same elastic service (but you have to do so manually).
|
||||
|
||||
#### UDP Routers
|
||||
|
||||
??? info "`traefik.udp.routers.<router_name>.entrypoints`"
|
||||
|
||||
See [entry points](../routers/index.md#entrypoints_2) for more information.
|
||||
|
||||
```yaml
|
||||
traefik.udp.routers.myudprouter.entrypoints=ep1,ep2
|
||||
```
|
||||
|
||||
??? info "`traefik.udp.routers.<router_name>.service`"
|
||||
|
||||
See [service](../routers/index.md#services_1) for more information.
|
||||
|
||||
```yaml
|
||||
traefik.udp.routers.myudprouter.service=myservice
|
||||
```
|
||||
|
||||
#### UDP Services
|
||||
|
||||
??? info "`traefik.udp.services.<service_name>.loadbalancer.server.port`"
|
||||
|
||||
Registers a port of the application.
|
||||
|
||||
```yaml
|
||||
traefik.udp.services.myudpservice.loadbalancer.server.port=423
|
||||
```
|
||||
|
||||
### Specific Provider Options
|
||||
|
||||
#### `traefik.enable`
|
||||
|
||||
```yaml
|
||||
traefik.enable=true
|
||||
```
|
||||
|
||||
You can tell Traefik to consider (or not) the ECS service by setting `traefik.enable` to true or false.
|
||||
|
||||
This option overrides the value of `exposedByDefault`.
|
@@ -43,7 +43,7 @@ The Kubernetes Ingress Controller, The Custom Resource Way.
|
||||
serviceAccountName: traefik-ingress-controller
|
||||
containers:
|
||||
- name: traefik
|
||||
image: traefik:v2.2
|
||||
image: traefik:v2.3
|
||||
args:
|
||||
- --log.level=DEBUG
|
||||
- --api
|
||||
|
@@ -110,7 +110,7 @@ which in turn will create the resulting routers, services, handlers, etc.
|
||||
serviceAccountName: traefik-ingress-controller
|
||||
containers:
|
||||
- name: traefik
|
||||
image: traefik:v2.2
|
||||
image: traefik:v2.3
|
||||
args:
|
||||
- --log.level=DEBUG
|
||||
- --api
|
||||
@@ -322,9 +322,23 @@ which in turn will create the resulting routers, services, handlers, etc.
|
||||
traefik.ingress.kubernetes.io/service.sticky.cookie.httponly: "true"
|
||||
```
|
||||
|
||||
### TLS
|
||||
## Path Types on Kubernetes 1.18+
|
||||
|
||||
If the Kubernetes cluster version is 1.18+,
|
||||
the new `pathType` property can be leveraged to define the rules matchers:
|
||||
|
||||
#### Communication Between Traefik and Pods
|
||||
- `Exact`: This path type forces the rule matcher to `Path`
|
||||
- `Prefix`: This path type forces the rule matcher to `PathPrefix`
|
||||
|
||||
Please see [this documentation](https://kubernetes.io/docs/concepts/services-networking/ingress/#path-types) for more information.
|
||||
|
||||
!!! warning "Multiple Matches"
|
||||
In the case of multiple matches, Traefik will not ensure the priority of a Path matcher over a PathPrefix matcher,
|
||||
as stated in [this documentation](https://kubernetes.io/docs/concepts/services-networking/ingress/#multiple-matches).
|
||||
|
||||
## TLS
|
||||
|
||||
### Communication Between Traefik and Pods
|
||||
|
||||
Traefik automatically requests endpoint information based on the service provided in the ingress spec.
|
||||
Although Traefik will connect directly to the endpoints (pods),
|
||||
@@ -346,7 +360,7 @@ and will connect via TLS automatically.
|
||||
If this is not an option, you may need to skip TLS certificate verification.
|
||||
See the [insecureSkipVerify](../../routing/overview.md#insecureskipverify) setting for more details.
|
||||
|
||||
#### Certificates Management
|
||||
### Certificates Management
|
||||
|
||||
??? example "Using a secret"
|
||||
|
||||
|
@@ -26,7 +26,7 @@ spec:
|
||||
serviceAccountName: traefik-ingress-controller
|
||||
containers:
|
||||
- name: traefik
|
||||
image: traefik:v2.2
|
||||
image: traefik:v2.3
|
||||
args:
|
||||
- --api.insecure
|
||||
- --accesslog
|
||||
|
@@ -26,5 +26,5 @@ node:
|
||||
- K3S_CLUSTER_SECRET=somethingtotallyrandom
|
||||
volumes:
|
||||
# this is where you would place a alternative traefik image (saved as a .tar file with
|
||||
# 'docker save'), if you want to use it, instead of the traefik:v2.2 image.
|
||||
# 'docker save'), if you want to use it, instead of the traefik:v2.3 image.
|
||||
- /sowewhere/on/your/host/custom-image:/var/lib/rancher/k3s/agent/images
|
||||
|
@@ -3,7 +3,7 @@ version: "3.3"
|
||||
services:
|
||||
|
||||
traefik:
|
||||
image: "traefik:v2.2"
|
||||
image: "traefik:v2.3"
|
||||
container_name: "traefik"
|
||||
command:
|
||||
#- "--log.level=DEBUG"
|
||||
|
@@ -13,7 +13,7 @@ secrets:
|
||||
services:
|
||||
|
||||
traefik:
|
||||
image: "traefik:v2.2"
|
||||
image: "traefik:v2.3"
|
||||
container_name: "traefik"
|
||||
command:
|
||||
#- "--log.level=DEBUG"
|
||||
|
@@ -3,7 +3,7 @@ version: "3.3"
|
||||
services:
|
||||
|
||||
traefik:
|
||||
image: "traefik:v2.2"
|
||||
image: "traefik:v2.3"
|
||||
container_name: "traefik"
|
||||
command:
|
||||
#- "--log.level=DEBUG"
|
||||
|
@@ -3,7 +3,7 @@ version: "3.3"
|
||||
services:
|
||||
|
||||
traefik:
|
||||
image: "traefik:v2.2"
|
||||
image: "traefik:v2.3"
|
||||
container_name: "traefik"
|
||||
command:
|
||||
#- "--log.level=DEBUG"
|
||||
|
@@ -3,7 +3,7 @@ version: "3.3"
|
||||
services:
|
||||
|
||||
traefik:
|
||||
image: "traefik:v2.2"
|
||||
image: "traefik:v2.3"
|
||||
container_name: "traefik"
|
||||
command:
|
||||
#- "--log.level=DEBUG"
|
||||
|
@@ -77,6 +77,7 @@ nav:
|
||||
- 'Kubernetes IngressRoute': 'providers/kubernetes-crd.md'
|
||||
- 'Kubernetes Ingress': 'providers/kubernetes-ingress.md'
|
||||
- 'Consul Catalog': 'providers/consul-catalog.md'
|
||||
- 'ECS': 'providers/ecs.md'
|
||||
- 'Marathon': 'providers/marathon.md'
|
||||
- 'Rancher': 'providers/rancher.md'
|
||||
- 'File': 'providers/file.md'
|
||||
@@ -84,6 +85,7 @@ nav:
|
||||
- 'Etcd': 'providers/etcd.md'
|
||||
- 'ZooKeeper': 'providers/zookeeper.md'
|
||||
- 'Redis': 'providers/redis.md'
|
||||
- 'HTTP': 'providers/http.md'
|
||||
- 'Routing & Load Balancing':
|
||||
- 'Overview': 'routing/overview.md'
|
||||
- 'EntryPoints': 'routing/entrypoints.md'
|
||||
@@ -94,6 +96,7 @@ nav:
|
||||
- 'Kubernetes IngressRoute': 'routing/providers/kubernetes-crd.md'
|
||||
- 'Kubernetes Ingress': 'routing/providers/kubernetes-ingress.md'
|
||||
- 'Consul Catalog': 'routing/providers/consul-catalog.md'
|
||||
- 'ECS': 'routing/providers/ecs.md'
|
||||
- 'Marathon': 'routing/providers/marathon.md'
|
||||
- 'Rancher': 'routing/providers/rancher.md'
|
||||
- 'KV': 'routing/providers/kv.md'
|
||||
@@ -125,6 +128,9 @@ nav:
|
||||
- 'Retry': 'middlewares/retry.md'
|
||||
- 'StripPrefix': 'middlewares/stripprefix.md'
|
||||
- 'StripPrefixRegex': 'middlewares/stripprefixregex.md'
|
||||
- 'Plugins & Traefik Pilot':
|
||||
- 'Overview': 'plugins/overview.md'
|
||||
- 'Using Plugins': 'plugins/using-plugins.md'
|
||||
- 'Operations':
|
||||
- 'CLI': 'operations/cli.md'
|
||||
- 'Dashboard' : 'operations/dashboard.md'
|
||||
|
@@ -21,7 +21,7 @@ find "${PATH_TO_SITE}" -type f -not -path "/app/site/theme/*" \
|
||||
--check_external_hash \
|
||||
--alt_ignore="/traefik.logo.png/" \
|
||||
--http_status_ignore="0,500,501,503" \
|
||||
--url_ignore="/https://groups.google.com/a/traefik.io/forum/#!forum/security/,/localhost:/,/127.0.0.1:/,/fonts.gstatic.com/,/.minikube/,/github.com\/containous\/traefik\/*edit*/,/github.com\/containous\/traefik\/$/,/docs.traefik.io/,/github\.com\/golang\/oauth2\/blob\/36a7019397c4c86cf59eeab3bc0d188bac444277\/.+/,/www.akamai.com/" \
|
||||
--url_ignore="/https://groups.google.com/a/traefik.io/forum/#!forum/security/,/localhost:/,/127.0.0.1:/,/fonts.gstatic.com/,/.minikube/,/github.com\/containous\/traefik\/*edit*/,/github.com\/containous\/traefik\/$/,/docs.traefik.io/,/github\.com\/golang\/oauth2\/blob\/36a7019397c4c86cf59eeab3bc0d188bac444277\/.+/,/www.akamai.com/,/pilot.traefik.io\/profile/" \
|
||||
'{}' 1>/dev/null
|
||||
## HTML-proofer options at https://github.com/gjtorikian/html-proofer#configuration
|
||||
|
||||
|
15
go.mod
15
go.mod
@@ -15,10 +15,12 @@ require (
|
||||
github.com/VividCortex/gohistogram v1.0.0 // indirect
|
||||
github.com/abbot/go-http-auth v0.0.0-00010101000000-000000000000
|
||||
github.com/abronan/valkeyrie v0.0.0-20200127174252-ef4277a138cd
|
||||
github.com/aws/aws-sdk-go v1.30.20
|
||||
github.com/c0va23/go-proxyprotocol v0.9.1
|
||||
github.com/cenkalti/backoff/v4 v4.0.0
|
||||
github.com/containerd/containerd v1.3.2 // indirect
|
||||
github.com/containous/alice v0.0.0-20181107144136-d83ebdd94cbd
|
||||
github.com/containous/yaegi v0.8.13
|
||||
github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf
|
||||
github.com/davecgh/go-spew v1.1.1
|
||||
github.com/docker/cli v0.0.0-20200221155518-740919cc7fc0
|
||||
@@ -39,10 +41,8 @@ require (
|
||||
github.com/go-acme/lego/v3 v3.8.0
|
||||
github.com/go-check/check v0.0.0-00010101000000-000000000000
|
||||
github.com/go-kit/kit v0.9.0
|
||||
github.com/gogo/protobuf v1.3.0 // indirect
|
||||
github.com/golang/protobuf v1.3.4
|
||||
github.com/google/go-github/v28 v28.1.1
|
||||
github.com/googleapis/gnostic v0.1.0 // indirect
|
||||
github.com/gorilla/mux v1.7.3
|
||||
github.com/gorilla/websocket v1.4.2
|
||||
github.com/hashicorp/consul/api v1.3.0
|
||||
@@ -58,6 +58,7 @@ require (
|
||||
github.com/miekg/dns v1.1.27
|
||||
github.com/mitchellh/copystructure v1.0.0
|
||||
github.com/mitchellh/hashstructure v1.0.0
|
||||
github.com/mitchellh/mapstructure v1.3.2
|
||||
github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c // indirect
|
||||
github.com/opencontainers/go-digest v1.0.0-rc1 // indirect
|
||||
github.com/opencontainers/image-spec v1.0.1 // indirect
|
||||
@@ -84,6 +85,7 @@ require (
|
||||
github.com/vulcand/predicate v1.1.0
|
||||
go.elastic.co/apm v1.7.0
|
||||
go.elastic.co/apm/module/apmot v1.7.0
|
||||
golang.org/x/mod v0.2.0
|
||||
golang.org/x/net v0.0.0-20200301022130-244492dfa37a
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0
|
||||
google.golang.org/grpc v1.27.1
|
||||
@@ -91,10 +93,11 @@ require (
|
||||
gopkg.in/fsnotify.v1 v1.4.7
|
||||
gopkg.in/jcmturner/goidentity.v3 v3.0.0 // indirect
|
||||
gopkg.in/yaml.v2 v2.2.8
|
||||
k8s.io/api v0.17.3
|
||||
k8s.io/apimachinery v0.17.3
|
||||
k8s.io/client-go v0.17.3
|
||||
k8s.io/code-generator v0.17.3
|
||||
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776
|
||||
k8s.io/api v0.18.2
|
||||
k8s.io/apimachinery v0.18.2
|
||||
k8s.io/client-go v0.18.2
|
||||
k8s.io/code-generator v0.18.2
|
||||
mvdan.cc/xurls/v2 v2.1.0
|
||||
)
|
||||
|
||||
|
145
go.sum
145
go.sum
@@ -1,12 +1,10 @@
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.38.0 h1:ROfEUZz+Gh5pa62DJWXSaonyu3StP6EA6lPEXPI6mCo=
|
||||
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
|
||||
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
|
||||
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
|
||||
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
|
||||
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
|
||||
cloud.google.com/go v0.50.0 h1:0E3eE8MX426vUOs7aHfI7aN1BrIzzzf4ccKCSfSjGmc=
|
||||
cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
|
||||
cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
|
||||
cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
|
||||
@@ -23,7 +21,6 @@ cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIA
|
||||
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
|
||||
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
|
||||
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
|
||||
contrib.go.opencensus.io/exporter/ocagent v0.4.12 h1:jGFvw3l57ViIVEPKKEUXPcLYIXJmQxLUh6ey1eJhwyc=
|
||||
contrib.go.opencensus.io/exporter/ocagent v0.4.12/go.mod h1:450APlNTSR6FrvC3CTRqYosuDstRB9un7SOx2k/9ckA=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
github.com/Azure/azure-sdk-for-go v32.4.0+incompatible h1:1JP8SKfroEakYiQU2ZyPDosh8w2Tg9UopKt88VyQPt4=
|
||||
@@ -31,12 +28,10 @@ github.com/Azure/azure-sdk-for-go v32.4.0+incompatible/go.mod h1:9XXNKU+eRnpl9mo
|
||||
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7OZ575w+acHgRric5iCyQh+xv+KJ4HB8=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
|
||||
github.com/Azure/go-autorest/autorest v0.1.0/go.mod h1:AKyIcETwSUFxIcs/Wnq/C+kwCtlEYGUVd7FPNb2slmg=
|
||||
github.com/Azure/go-autorest/autorest v0.5.0 h1:Mlm9qy2fpQ9MvfyI41G2Zf5B4CsgjjNbLOWszfK6KrY=
|
||||
github.com/Azure/go-autorest/autorest v0.5.0/go.mod h1:9HLKlQjVBH6U3oDfsXOeVc56THsLPw1L03yban4xThw=
|
||||
github.com/Azure/go-autorest/autorest v0.9.0 h1:MRvx8gncNaXJqOoLmhNjUAKh33JJF8LyxPhomEtOsjs=
|
||||
github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI=
|
||||
github.com/Azure/go-autorest/autorest/adal v0.1.0/go.mod h1:MeS4XhScH55IST095THyTxElntu7WqB7pNbZo8Q5G3E=
|
||||
github.com/Azure/go-autorest/autorest/adal v0.2.0 h1:7IBDu1jgh+ADHXnEYExkV9RE/ztOOlxdACkkPRthGKw=
|
||||
github.com/Azure/go-autorest/autorest/adal v0.2.0/go.mod h1:MeS4XhScH55IST095THyTxElntu7WqB7pNbZo8Q5G3E=
|
||||
github.com/Azure/go-autorest/autorest/adal v0.5.0 h1:q2gDruN08/guU9vAjuPWff0+QIrpH6ediguzdAzXAUU=
|
||||
github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0=
|
||||
@@ -46,7 +41,6 @@ github.com/Azure/go-autorest/autorest/azure/cli v0.1.0 h1:YTtBrcb6mhA+PoSW8WxFDo
|
||||
github.com/Azure/go-autorest/autorest/azure/cli v0.1.0/go.mod h1:Dk8CUAt/b/PzkfeRsWzVG9Yj3ps8mS8ECztu43rdU8U=
|
||||
github.com/Azure/go-autorest/autorest/date v0.1.0 h1:YGrhWfrgtFs84+h0o46rJrlmsZtyZRg470CqAXTZaGM=
|
||||
github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA=
|
||||
github.com/Azure/go-autorest/autorest/mocks v0.1.0 h1:Kx+AUU2Te+A3JIyYn6Dfs+cFgx5XorQKuIXrZGoq/SI=
|
||||
github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
|
||||
github.com/Azure/go-autorest/autorest/mocks v0.2.0 h1:Ww5g4zThfD/6cLb4z6xxgeyDa7QDkizMkJKe0ysZXp0=
|
||||
github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
|
||||
@@ -56,7 +50,6 @@ github.com/Azure/go-autorest/autorest/validation v0.1.0 h1:ISSNzGUh+ZSzizJWOWzs8
|
||||
github.com/Azure/go-autorest/autorest/validation v0.1.0/go.mod h1:Ha3z/SqBeaalWQvokg3NZAlQTalVMtOIAs1aGK7G6u8=
|
||||
github.com/Azure/go-autorest/logger v0.1.0 h1:ruG4BSDXONFRrZZJ2GUXDiUyVpayPmb1GnWeHDdaNKY=
|
||||
github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc=
|
||||
github.com/Azure/go-autorest/tracing v0.1.0 h1:TRBxC5Pj/fIuh4Qob0ZpkggbfT8RC0SubHbpV3p4/Vc=
|
||||
github.com/Azure/go-autorest/tracing v0.1.0/go.mod h1:ROEEAFwXycQw7Sn3DXNtEedEvdeRAgDr0izn4z5Ij88=
|
||||
github.com/Azure/go-autorest/tracing v0.5.0 h1:TRn4WjSnkcSy5AEG3pnbtFSwNtwzjr4VYyQflFE619k=
|
||||
github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk=
|
||||
@@ -107,7 +100,6 @@ github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuy
|
||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/aliyun/alibaba-cloud-sdk-go v1.61.112 h1:E273ePcLllLIBGg5BHr3T0Fp1BJTvUyh5Y57ziSy81w=
|
||||
github.com/aliyun/alibaba-cloud-sdk-go v1.61.112/go.mod h1:pUKYbK5JQ+1Dfxk80P0qxGqe5dkxDoabbZS7zOcouyA=
|
||||
github.com/apache/thrift v0.12.0 h1:pODnxUFNcjP9UTLZGTdeh+j16A8lJbRvD3rOtrk/7bs=
|
||||
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
|
||||
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
|
||||
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
|
||||
@@ -120,7 +112,6 @@ github.com/aws/aws-sdk-go v1.16.23/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpi
|
||||
github.com/aws/aws-sdk-go v1.30.20 h1:ktsy2vodSZxz/arYqo7DlpkIeNohHL+4Rmjdo7YGtrE=
|
||||
github.com/aws/aws-sdk-go v1.30.20/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/beorn7/perks v1.0.0 h1:HWo1m869IqiPhD389kmkxeTalrjNbbJTC8LXupb+sl0=
|
||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
@@ -132,7 +123,6 @@ github.com/c0va23/go-proxyprotocol v0.9.1 h1:5BCkp0fDJOhzzH1lhjUgHhmZz9VvRMMif1U
|
||||
github.com/c0va23/go-proxyprotocol v0.9.1/go.mod h1:TNjUV+llvk8TvWJxlPYAeAYZgSzT/iicNr3nWBWX320=
|
||||
github.com/cenkalti/backoff/v4 v4.0.0 h1:6VeaLF9aI+MAUQ95106HwWzYZgJJpZ4stumjj6RFYAU=
|
||||
github.com/cenkalti/backoff/v4 v4.0.0/go.mod h1:eEew/i+1Q6OrCDZh3WiXYv3+nJwBASZ8Bog/87DQnVg=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.0 h1:LzQXZOgg4CQfE6bFvXGM30YZL1WW/M337pXml+GrcZ4=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
@@ -170,6 +160,8 @@ github.com/containous/multibuf v0.0.0-20190809014333-8b6c9a7e6bba h1:PhR03pep+5e
|
||||
github.com/containous/multibuf v0.0.0-20190809014333-8b6c9a7e6bba/go.mod h1:zkWcASFUJEst6QwCrxLdkuw1gvaKqmflEipm+iecV5M=
|
||||
github.com/containous/mux v0.0.0-20181024131434-c33f32e26898 h1:1srn9voikJGofblBhWy3WuZWqo14Ou7NaswNG/I2yWc=
|
||||
github.com/containous/mux v0.0.0-20181024131434-c33f32e26898/go.mod h1:z8WW7n06n8/1xF9Jl9WmuDeZuHAhfL+bwarNjsciwwg=
|
||||
github.com/containous/yaegi v0.8.13 h1:ADhAZok9av2wxge4/LxNqZHG/+rwHZcwpSLKmy9cz48=
|
||||
github.com/containous/yaegi v0.8.13/go.mod h1:Yj82MHpXQ9/h3ukzc2numJQ/Wr4+M3C9YLMzNjFtd3o=
|
||||
github.com/coreos/bbolt v1.3.3 h1:n6AiVyVRKQFNb6mJlwESEvvLoDyiTzXX7ORAUlkeBdY=
|
||||
github.com/coreos/bbolt v1.3.3/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
|
||||
github.com/coreos/etcd v3.3.13+incompatible h1:8F3hqu9fGYLBifCmRCJsicFqDx/D68Rt3q1JMazcgBQ=
|
||||
@@ -177,7 +169,6 @@ github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc
|
||||
github.com/coreos/go-semver v0.2.0 h1:3Jm3tLmsgAYcjC+4Up7hJrFBPr+n7rAqYeSw/SZazuY=
|
||||
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f h1:JOrtw2xFKzlg+cbHpyrpLDmnN1HqhBfnX7WDiW7eG2c=
|
||||
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf h1:iW4rZ826su+pqaw19uhpSCzhj44qo35pNgKFGqzDKkU=
|
||||
github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
@@ -186,7 +177,6 @@ github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfc
|
||||
github.com/cpu/goacmedns v0.0.2 h1:hYAgjnPu7HogTgb8trqQouR/RrBgXq1TPBgmxbK9eRA=
|
||||
github.com/cpu/goacmedns v0.0.2/go.mod h1:4MipLkI+qScwqtVxcNO6okBhbgRrr7/tKXUSgSL0teQ=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
@@ -235,7 +225,7 @@ github.com/elastic/go-windows v1.0.0 h1:qLURgZFkkrYyTTkvYpsZIgf83AUsdIHfvlJaqaZ7
|
||||
github.com/elastic/go-windows v1.0.0/go.mod h1:TsU0Nrp7/y3+VwE82FoZF8gC/XFg/Elz6CcloAxnPgU=
|
||||
github.com/elazarl/go-bindata-assetfs v1.0.0 h1:G/bYguwHIzWq9ZoyUQqrjTmJbbYn3j3CKKpKinvZLFk=
|
||||
github.com/elazarl/go-bindata-assetfs v1.0.0/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4=
|
||||
github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
|
||||
github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
|
||||
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
|
||||
github.com/emicklei/go-restful v2.9.5+incompatible h1:spTtZBk5DYEvbxMVutUuTyh1Ao2r4iyvLdACqsl/Ljk=
|
||||
github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
|
||||
@@ -299,38 +289,28 @@ github.com/gofrs/uuid v3.2.0+incompatible h1:y12jRkkFxsd7GpqdSZ+/KCs/fJbqpEXSGd4
|
||||
github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||
github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.2.0 h1:xU6/SpYbvkNYiptHJYEDRseDLvYE7wSqhYYNy0QSUzI=
|
||||
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE=
|
||||
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
|
||||
github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
|
||||
github.com/gogo/protobuf v1.3.0 h1:G8O7TerXerS4F6sx9OV7/nRfJdnXgHZu/S/7F2SN+UE=
|
||||
github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
|
||||
github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls=
|
||||
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
|
||||
github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptGaCkuDUx6wNykzzlUixGxvkme+H/lnzb+A=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903 h1:LbsanbbD6LieFkXbj9YNNBupiGHJgFeLpO0j0Fza1h8=
|
||||
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6 h1:ZgQEtGgCBiWRM39fZuwSd1LwSqqSW0hOdXCYYDX0R3I=
|
||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY=
|
||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.2.0 h1:28o5sBqPkBsMGnC6b4MvE2TzSr5/AT4c/1fLqVGIwlk=
|
||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.3.1 h1:qGJ6qTW+x6xX/my+8YUVl4WNpX9B7+/l2tRsHGZ7f2s=
|
||||
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
|
||||
github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/mock v1.4.1 h1:ocYkMQY5RrXTYgXl7ICpV0IXwlEQGwKIsery4gyXa1U=
|
||||
github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I=
|
||||
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/golang/protobuf v1.3.4 h1:87PNWwrRvUSnqS4dlcBU/ftvOIBep4sYuBLlh6rX2wk=
|
||||
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
@@ -341,9 +321,7 @@ github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Z
|
||||
github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo=
|
||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
@@ -351,9 +329,10 @@ github.com/google/go-github/v28 v28.1.1 h1:kORf5ekX5qwXO2mGzXXOjMe/g6ap8ahVe0sBE
|
||||
github.com/google/go-github/v28 v28.1.1/go.mod h1:bsqJWQX05omyWVmc00nEUql9mhQyv38lDZ8kPZcQVoM=
|
||||
github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
|
||||
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
|
||||
github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI=
|
||||
github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g=
|
||||
github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
@@ -376,7 +355,6 @@ github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGa
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8=
|
||||
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
|
||||
github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q=
|
||||
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
||||
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
|
||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
@@ -387,7 +365,6 @@ github.com/grpc-ecosystem/go-grpc-middleware v1.0.0 h1:Iju5GlWwrvL6UBg4zJJt3btmo
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
|
||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho=
|
||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.8.5 h1:2+KSC78XiO6Qy0hIjfc1OD9H+hsaJdJlb8Kqsd41CTE=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.8.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.9.5 h1:UImYN5qQ8tuGpGE16ZmjvcTtTw24zw1QAp/SlnNrZhI=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
||||
@@ -402,7 +379,6 @@ github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyN
|
||||
github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.0 h1:wvCrVc9TjDls6+YGAF2hAifE1E5U1+b4tH6KdvN3Gig=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
||||
@@ -464,11 +440,8 @@ github.com/joeshaw/multierror v0.0.0-20140124173710-69b34d4ec901 h1:rp+c0RAYOWj8
|
||||
github.com/joeshaw/multierror v0.0.0-20140124173710-69b34d4ec901/go.mod h1:Z86h9688Y0wesXCyonoVr47MasHilkuLMqGhRZ4Hpak=
|
||||
github.com/jonboulle/clockwork v0.1.0 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0hcPo=
|
||||
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
||||
github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.6 h1:MrUvLMLTMxbqFJ9kzlvat/rYZqZnW3u4wkLzWTaFwKs=
|
||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.7 h1:KfgG9LzI+pYjr4xvmz/5H4FXjokeP+rlHLhv3iH62Fo=
|
||||
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/json-iterator/go v1.1.8 h1:QiWkFLKq0T7mpzwOTu6BzNDbfTE8OLrYhVKYMLF46Ok=
|
||||
github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
@@ -549,12 +522,13 @@ github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQz
|
||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/mitchellh/mapstructure v1.3.1 h1:cCBH2gTD2K0OtLlv/Y5H01VQCqmlDxz30kS5Y5bqfLA=
|
||||
github.com/mitchellh/mapstructure v1.3.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/mitchellh/mapstructure v1.3.2 h1:mRS76wmkOn3KkKAyXDu42V+6ebnXWIztFSYGN7GeoRg=
|
||||
github.com/mitchellh/mapstructure v1.3.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/mitchellh/reflectwalk v1.0.0 h1:9D+8oIskB4VJBN5SFlmc27fSlIBZaov1Wpk/IfikLNY=
|
||||
github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180320133207-05fbef0ca5da/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
@@ -579,13 +553,11 @@ github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXW
|
||||
github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.8.0 h1:VkHVNpR4iVnU8XQR6DBm8BqYjN7CRzw+xKUbVVbbW9w=
|
||||
github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.10.1 h1:q/mM8GF/n0shIN8SaAZ0V+jnLPzen6WIVZdiwrRlMlo=
|
||||
github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.11.0 h1:JAKSXpt1YjtLA7YpPiqO9ss6sNXEsPfSGdwN0UHqzrw=
|
||||
github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
|
||||
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/onsi/gomega v1.5.0 h1:izbySO9zDPmjJ8rDjLvkA2zJHIo+HkYXHnf7eN7SSyo=
|
||||
github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/onsi/gomega v1.7.0 h1:XPnZz8VVBHjVsy1vzJmRwIcSwiUO+JFfrv/xGiigmME=
|
||||
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
@@ -608,7 +580,6 @@ github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFSt
|
||||
github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5 h1:ZCnq+JUrvXcDVhX/xRolRBZifmabN1HcS1wrPSvxhrU=
|
||||
github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA=
|
||||
github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
|
||||
github.com/openzipkin/zipkin-go v0.2.1 h1:noL5/5Uf1HpVl3wNsfkZhIKbSWCVi5jgqkONNx8PXcA=
|
||||
github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
|
||||
github.com/openzipkin/zipkin-go v0.2.2 h1:nY8Hti+WKaP0cRsSeQ026wU03QsM762XBeCXBb9NAWI=
|
||||
github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
|
||||
@@ -634,7 +605,6 @@ github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA=
|
||||
github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
|
||||
@@ -643,13 +613,11 @@ github.com/pquerna/otp v1.2.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1
|
||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM=
|
||||
github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=
|
||||
github.com/prometheus/client_golang v1.0.0 h1:vrDKnkGzuGvhNAL56c7DBz29ZL+KxnoR0x7enabFceM=
|
||||
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
|
||||
github.com/prometheus/client_golang v1.1.0 h1:BQ53HtBmfOitExawJ6LokA4x8ov/z0SYYb0+HxJfRI8=
|
||||
github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g=
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 h1:S/YWwWx/RA8rT8tKFRuGUZhuA90OyIBpPCXkcbwU8DE=
|
||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4 h1:gQz4mCbXsO+nc9n1hCxHcGA3Zx3Eo+UHZoInFGUIXNM=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
@@ -663,7 +631,6 @@ github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R
|
||||
github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.0-20190425082905-87a4384529e0/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/prometheus/procfs v0.0.3 h1:CTwfnzjQ+8dS6MhHHu4YswVAD99sL2wjPqP+VkURmKE=
|
||||
github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ=
|
||||
github.com/prometheus/procfs v0.0.5 h1:3+auTFlqw+ZaQYJARz6ArODtkaIwtvBTx3N2NehQlL8=
|
||||
github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ=
|
||||
@@ -672,7 +639,6 @@ github.com/rancher/go-rancher-metadata v0.0.0-20200311180630-7f4c936a06ac h1:wBG
|
||||
github.com/rancher/go-rancher-metadata v0.0.0-20200311180630-7f4c936a06ac/go.mod h1:67sLWL17mVlO1HFROaTBmU71NB4R8UNCesFHhg0f6LQ=
|
||||
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a h1:9ZKAASQSHhDYGoxY8uLVpewe1GDZ2vu2Tr/vTdVAkFQ=
|
||||
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20170806203942-52369c62f446/go.mod h1:uYEyJGbgTkfkS4+E/PavXkNJcbFIpEtjt2B0KDQ5+9M=
|
||||
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
@@ -693,7 +659,6 @@ github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6Mwd
|
||||
github.com/skratchdot/open-golang v0.0.0-20160302144031-75fb7ed4208c/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a h1:pa8hGb/2YqsZKovtsgrwcDH1RZhVbTKCjLp47XpqCDs=
|
||||
github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
|
||||
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||
@@ -705,15 +670,11 @@ github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48=
|
||||
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
||||
github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
@@ -774,14 +735,11 @@ go.etcd.io/etcd v3.3.13+incompatible h1:jCejD5EMnlGxFvcGRyEV4VGlENZc7oPQX6o0t7n3
|
||||
go.etcd.io/etcd v3.3.13+incompatible/go.mod h1:yaeTdrJi5lOmYerz05bd8+V7KubZs8YSFZfzsF9A6aI=
|
||||
go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
|
||||
go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
|
||||
go.opencensus.io v0.21.0 h1:mU6zScU4U1YAFPHEHYk+3JC4SY7JxgkqS10ZOSyksNg=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
go.opencensus.io v0.22.0 h1:C9hSCOW830chIVkdja34wa6Ky+IzWllkUinR+BtRZd4=
|
||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.3 h1:8sGtKOrtQqkN1bp2AtX+misvLIlOmsEsNd+9NIcPEm8=
|
||||
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.uber.org/atomic v1.3.2 h1:2Oa65PReHzfn29GpvgsYwloV9AVFHPDk8tYxt2c2tr4=
|
||||
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/atomic v1.4.0 h1:cxzIVoETapQEqDhQu3QfnvXAV4AlzcvUCxkVUFw3+EU=
|
||||
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
@@ -801,23 +759,17 @@ golang.org/x/crypto v0.0.0-20190418165655-df01cb2cc480/go.mod h1:WFFai1msRO1wXaE
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 h1:HuIa8hRrWRSrqYzx1qI49NNxhdi2PrY7gxVSq1JjLDc=
|
||||
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073 h1:xMPOj6Pz6UipU1wXLkrtqpHbR0AVFnyPEQq/wRWz9lM=
|
||||
golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200317142112-1b76d66859c6 h1:TjszyFsQsyZNHwdVdZ5m7bjmreu0znc2kRYsEml9/Ww=
|
||||
golang.org/x/crypto v0.0.0-20200317142112-1b76d66859c6/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190312203227-4b39c73a6495 h1:I6A9Ag9FpEKOjcKrRNjQkPHawoXIhKyTGfvvjFAiiAk=
|
||||
golang.org/x/exp v0.0.0-20190312203227-4b39c73a6495/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
|
||||
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
|
||||
golang.org/x/exp v0.0.0-20191129062945-2f5052295587 h1:5Uz0rkjCFu9BC9gCRN7EkwVvhNyQgGWb8KNJrPwBoHY=
|
||||
golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
@@ -863,24 +815,20 @@ golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn
|
||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80 h1:Ao/3l156eZf2AW5wK8a7/smtodRU+gha3+BeqJ69lRk=
|
||||
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190930134127-c5a3c61f89f3 h1:6KET3Sqa7fkVfD63QnAM81ZeYg5n4HwApOJkufONnHA=
|
||||
golang.org/x/net v0.0.0-20190930134127-c5a3c61f89f3/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200222125558-5a598a2470a0 h1:MsuvTghUPjX762sGLnGsxC3HM0B5r83wEtYcYR8/vRs=
|
||||
golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200301022130-244492dfa37a h1:GuSPYbZzB5/dcLNCwLQLsg3obCJtX9IJhpXkvY7kzk0=
|
||||
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw=
|
||||
@@ -889,7 +837,6 @@ golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
@@ -910,7 +857,6 @@ golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b h1:ag/x1USPSsqHud38I9BAC88qdNLDHHtQ4mlgQIZPPNA=
|
||||
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190514135907-3a4b5fb9f71f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -918,13 +864,11 @@ golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe h1:6fAMxZRR6sl1Uq8U61gxU+kPTs2tR8uOySCbBP7BN/M=
|
||||
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191025021431-6c3a3bfe00ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e h1:9vRrk9YW2BTzLP0VCB9ZDjU4cPqkg+IDWL7XgxA1yxQ=
|
||||
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -941,9 +885,7 @@ golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3
|
||||
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190921001708-c4c64cad1fd0 h1:xQwXv67TxFo9nC1GJFyab5eq/5B590r6RlnL/G8Sz7w=
|
||||
golang.org/x/time v0.0.0-20190921001708-c4c64cad1fd0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
@@ -953,16 +895,13 @@ golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGm
|
||||
golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c h1:97SnQk1GYRXJgvwZ8fadnxDOWfKvkNQHH3CtZntPSrM=
|
||||
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135 h1:5Beo0mZN8dRzgrMMkDp0jc8YXQKx9DiJ2k1dkvGsn5A=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
@@ -978,7 +917,6 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn
|
||||
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361 h1:RIIXAeV6GvDBuADKumTODatUqANFZ+5BPMnzsy4hulY=
|
||||
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
@@ -990,25 +928,16 @@ golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapK
|
||||
golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb h1:iKlO7ROJc6SttHKlxzwGytRtBUqX4VARrNTgP2YLX5M=
|
||||
golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7 h1:9zdDQZ7Thm29KFXgAX/+yaf3eVbP7djjWp/dXAppNCc=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898 h1:/atklqdjdhuosWIl6AIbOeHJjicWYPqR9bpxqxYG2pA=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gonum.org/v1/gonum v0.0.0-20190331200053-3d26580ed485 h1:OB/uP/Puiu5vS5QMRPrXCDWUPb+kt8f1KW8oQzFejQw=
|
||||
gonum.org/v1/gonum v0.0.0-20190331200053-3d26580ed485/go.mod h1:2ltnJ7xHfj0zHS40VVPYEAAMTa3ZGguvHGBSJeRWqE0=
|
||||
gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw=
|
||||
gonum.org/v1/netlib v0.0.0-20190331212654-76723241ea4e h1:jRyg0XfpwWlhEV8mDfdNGBeSJM2fuyh9Yjrnd8kF2Ts=
|
||||
gonum.org/v1/netlib v0.0.0-20190331212654-76723241ea4e/go.mod h1:kS+toOQn6AQKjmKJ7gzohV1XkqsFehRA2FbsbkopSuQ=
|
||||
google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
|
||||
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
||||
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
|
||||
google.golang.org/api v0.8.0 h1:VGGbLNyPF7dvYHhcUGYBBGCRDDK0RRJAI6KCvo0CL+E=
|
||||
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||
google.golang.org/api v0.14.0 h1:uMf5uLi4eQMRrMKhCplNik4U4H8Z6C1br3zOtAa/aDE=
|
||||
google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||
google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||
google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
@@ -1017,9 +946,7 @@ google.golang.org/api v0.20.0 h1:jz2KixHX7EcCPiQrySzPdnYT7DbINAypCqKZ1Z7GM40=
|
||||
google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.5.0 h1:KxkO13IPW4Lslp2bz+KHP2E3gtFlrIGNThxkZQ3g+4c=
|
||||
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.6.1 h1:QzqyMA1tlu6CgqCDUtU9V+ZKhLFT2dkJuANu5QaxI3I=
|
||||
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
||||
google.golang.org/appengine v1.6.5 h1:tycE03LOZYQNhDpS27tcQdAzLCVMaj7QT2SXxebnpCM=
|
||||
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
@@ -1027,14 +954,12 @@ google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoA
|
||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873 h1:nfPFGzJkUDX6uBmpN/pSw7MbOAWegH5QDQuoXFHedLg=
|
||||
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
|
||||
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1 h1:aQktFqmDE2yjveXJlVIfslDFmFnUXSqG0i6KRcJAeMc=
|
||||
google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
@@ -1048,10 +973,8 @@ google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.19.1/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM=
|
||||
google.golang.org/grpc v1.20.1 h1:Hz2g2wirWK7H0qIIhGIqRGTuMwTE8HEKFnDZZ7lm9NU=
|
||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||
google.golang.org/grpc v1.22.1 h1:/7cs52RnTJmD43s3uxzlq2U7nqVTd/37viQwMrMNlOM=
|
||||
google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
@@ -1097,10 +1020,11 @@ gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkep
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
|
||||
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
|
||||
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
@@ -1112,37 +1036,36 @@ honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt
|
||||
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
howett.net/plist v0.0.0-20181124034731-591f970eefbb h1:jhnBjNi9UFpfpl8YZhA9CrOqpnJdvzuiHsl/dnxl11M=
|
||||
howett.net/plist v0.0.0-20181124034731-591f970eefbb/go.mod h1:vMygbs4qMhSZSc4lCUl2OEE+rDiIIJAIdR4m7MiMcm0=
|
||||
k8s.io/api v0.17.3 h1:XAm3PZp3wnEdzekNkcmj/9Y1zdmQYJ1I4GKSBBZ8aG0=
|
||||
k8s.io/api v0.17.3/go.mod h1:YZ0OTkuw7ipbe305fMpIdf3GLXZKRigjtZaV5gzC2J0=
|
||||
k8s.io/apimachinery v0.17.3 h1:f+uZV6rm4/tHE7xXgLyToprg6xWairaClGVkm2t8omg=
|
||||
k8s.io/apimachinery v0.17.3/go.mod h1:gxLnyZcGNdZTCLnq3fgzyg2A5BVCHTNDFrw8AmuJ+0g=
|
||||
k8s.io/client-go v0.17.3 h1:deUna1Ksx05XeESH6XGCyONNFfiQmDdqeqUvicvP6nU=
|
||||
k8s.io/client-go v0.17.3/go.mod h1:cLXlTMtWHkuK4tD360KpWz2gG2KtdWEr/OT02i3emRQ=
|
||||
k8s.io/code-generator v0.17.3 h1:q/hDMk2cvFzSxol7k/VA1qCssR7VSMXHQHhzuX29VJ8=
|
||||
k8s.io/code-generator v0.17.3/go.mod h1:l8BLVwASXQZTo2xamW5mQNFCe1XPiAesVq7Y1t7PiQQ=
|
||||
k8s.io/api v0.18.2 h1:wG5g5ZmSVgm5B+eHMIbI9EGATS2L8Z72rda19RIEgY8=
|
||||
k8s.io/api v0.18.2/go.mod h1:SJCWI7OLzhZSvbY7U8zwNl9UA4o1fizoug34OV/2r78=
|
||||
k8s.io/apimachinery v0.18.2 h1:44CmtbmkzVDAhCpRVSiP2R5PPrC2RtlIv/MoB8xpdRA=
|
||||
k8s.io/apimachinery v0.18.2/go.mod h1:9SnR/e11v5IbyPCGbvJViimtJ0SwHG4nfZFjU77ftcA=
|
||||
k8s.io/client-go v0.18.2 h1:aLB0iaD4nmwh7arT2wIn+lMnAq7OswjaejkQ8p9bBYE=
|
||||
k8s.io/client-go v0.18.2/go.mod h1:Xcm5wVGXX9HAA2JJ2sSBUn3tCJ+4SVlCbl2MNNv+CIU=
|
||||
k8s.io/code-generator v0.18.2 h1:C1Nn2JiMf244CvBDKVPX0W2mZFJkVBg54T8OV7/Imso=
|
||||
k8s.io/code-generator v0.18.2/go.mod h1:+UHX5rSbxmR8kzS+FAv7um6dtYrZokQvjHpDSYRVkTc=
|
||||
k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
|
||||
k8s.io/gengo v0.0.0-20190822140433-26a664648505 h1:ZY6yclUKVbZ+SdWnkfY+Je5vrMpKOxmGeKRbsXVmqYM=
|
||||
k8s.io/gengo v0.0.0-20190822140433-26a664648505/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
|
||||
k8s.io/gengo v0.0.0-20200114144118-36b2048a9120 h1:RPscN6KhmG54S33L+lr3GS+oD1jmchIU0ll519K6FA4=
|
||||
k8s.io/gengo v0.0.0-20200114144118-36b2048a9120/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
|
||||
k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
|
||||
k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
|
||||
k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8=
|
||||
k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I=
|
||||
k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a h1:UcxjrRMyNx/i/y8G7kPvLyy7rfbeuf1PYyBf973pgyU=
|
||||
k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E=
|
||||
k8s.io/kube-openapi v0.0.0-20200121204235-bf4fb3bd569c h1:/KUFqjjqAcY4Us6luF5RDNZ16KJtb49HfR3ZHB9qYXM=
|
||||
k8s.io/kube-openapi v0.0.0-20200121204235-bf4fb3bd569c/go.mod h1:GRQhZsXIAJ1xR0C9bd8UpWHZ5plfAS9fzPjJuQ6JL3E=
|
||||
k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk=
|
||||
k8s.io/utils v0.0.0-20191114184206-e782cd3c129f h1:GiPwtSzdP43eI1hpPCbROQCCIgCuiMMNF8YUVLF3vJo=
|
||||
k8s.io/utils v0.0.0-20191114184206-e782cd3c129f/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew=
|
||||
k8s.io/utils v0.0.0-20200324210504-a9aa75ae1b89 h1:d4vVOjXm687F1iLSP2q3lyPPuyvTUt3aVoBpi2DqRsU=
|
||||
k8s.io/utils v0.0.0-20200324210504-a9aa75ae1b89/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew=
|
||||
launchpad.net/gocheck v0.0.0-20140225173054-000000000087/go.mod h1:hj7XX3B/0A+80Vse0e+BUHsHMTEhd0O4cpUHr/e/BUM=
|
||||
modernc.org/cc v1.0.0/go.mod h1:1Sk4//wdnYJiUIxnW8ddKpaOJCF37yAdqYnkxUpaYxw=
|
||||
modernc.org/golex v1.0.0/go.mod h1:b/QX9oBD/LhixY6NDh+IdGv17hgB+51fET1i2kPSmvk=
|
||||
modernc.org/mathutil v1.0.0/go.mod h1:wU0vUrJsVWBZ4P6e7xtFJEhFSNsfRLJ8H458uRjg03k=
|
||||
modernc.org/strutil v1.0.0/go.mod h1:lstksw84oURvj9y3tn8lGvRxyRC1S2+g5uuIzNfIOBs=
|
||||
modernc.org/xc v1.0.0/go.mod h1:mRNCo0bvLjGhHO9WsyuKVU4q0ceiDDDoEeWDJHrNx8I=
|
||||
mvdan.cc/xurls/v2 v2.1.0 h1:KaMb5GLhlcSX+e+qhbRJODnUUBvlw01jt4yrjFIHAuA=
|
||||
mvdan.cc/xurls/v2 v2.1.0/go.mod h1:5GrSd9rOnKOpZaji1OZLYL/yeAAtGDlo/cFe+8K5n8E=
|
||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
||||
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
||||
sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
|
||||
sigs.k8s.io/structured-merge-diff/v3 v3.0.0-20200116222232-67a7b8c61874/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw=
|
||||
sigs.k8s.io/structured-merge-diff/v3 v3.0.0 h1:dOmIZBMfhcHS09XZkMyUgkq5trg3/jRyJYFZUiaOp8E=
|
||||
sigs.k8s.io/structured-merge-diff/v3 v3.0.0/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw=
|
||||
sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs=
|
||||
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
|
||||
sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=
|
||||
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
|
||||
|
@@ -11,6 +11,6 @@
|
||||
[http.routers.test.tls]
|
||||
|
||||
[[tls.certificates]]
|
||||
store = ["default"]
|
||||
stores = ["default"]
|
||||
certFile = "fixtures/acme/ssl/wildcard.crt"
|
||||
keyFile = "fixtures/acme/ssl/wildcard.key"
|
||||
|
@@ -0,0 +1,16 @@
|
||||
[global]
|
||||
checkNewVersion = false
|
||||
sendAnonymousUsage = false
|
||||
|
||||
[log]
|
||||
level = "DEBUG"
|
||||
|
||||
[entryPoints]
|
||||
|
||||
[entryPoints.traefik]
|
||||
address = ":8001"
|
||||
[entryPoints.traefik.transport.lifeCycle]
|
||||
requestAcceptGraceTimeout = "10s"
|
||||
|
||||
[ping]
|
||||
terminatingStatusCode = 204
|
33
integration/fixtures/headers/secure_multiple.toml
Normal file
33
integration/fixtures/headers/secure_multiple.toml
Normal file
@@ -0,0 +1,33 @@
|
||||
[global]
|
||||
checkNewVersion = false
|
||||
sendAnonymousUsage = false
|
||||
|
||||
[log]
|
||||
level = "DEBUG"
|
||||
|
||||
[entryPoints]
|
||||
[entryPoints.web]
|
||||
address = ":8000"
|
||||
|
||||
[providers.file]
|
||||
filename = "{{ .SelfFilename }}"
|
||||
|
||||
## dynamic configuration ##
|
||||
|
||||
[http.routers]
|
||||
[http.routers.router1]
|
||||
rule = "Host(`test.localhost`)"
|
||||
middlewares = ["foo", "bar"]
|
||||
service = "service1"
|
||||
|
||||
|
||||
[http.middlewares]
|
||||
[http.middlewares.foo.headers]
|
||||
frameDeny = true
|
||||
[http.middlewares.bar.headers]
|
||||
contentTypeNosniff = true
|
||||
|
||||
[http.services]
|
||||
[http.services.service1.loadBalancer]
|
||||
[[http.services.service1.loadBalancer.servers]]
|
||||
url = "http://127.0.0.1:9000"
|
20
integration/fixtures/http/simple.toml
Normal file
20
integration/fixtures/http/simple.toml
Normal file
@@ -0,0 +1,20 @@
|
||||
[global]
|
||||
checkNewVersion = false
|
||||
sendAnonymousUsage = false
|
||||
|
||||
[log]
|
||||
level = "DEBUG"
|
||||
|
||||
[entryPoints]
|
||||
[entryPoints.web]
|
||||
address = ":8000"
|
||||
[entryPoints.traefik]
|
||||
address = ":9090"
|
||||
|
||||
[api]
|
||||
insecure = true
|
||||
|
||||
[providers]
|
||||
[providers.http]
|
||||
endpoint = "http://127.0.0.1:9000"
|
||||
pollInterval = "100ms"
|
@@ -22,7 +22,7 @@
|
||||
[http.routers.my-router]
|
||||
rule = "Path(`/test`)"
|
||||
service = "whoami"
|
||||
entrypoint=["tcp"]
|
||||
entrypoints = ["tcp"]
|
||||
|
||||
[http.routers.my-https-router]
|
||||
entryPoints=["tcp"]
|
||||
@@ -41,14 +41,14 @@
|
||||
service = "whoami-a"
|
||||
entryPoints = [ "tcp" ]
|
||||
[tcp.routers.to-whoami-a.tls]
|
||||
passthrough=true
|
||||
passthrough = true
|
||||
|
||||
[tcp.routers.to-whoami-b]
|
||||
rule = "HostSNI(`whoami-b.test`)"
|
||||
service = "whoami-b"
|
||||
entryPoints = [ "tcp" ]
|
||||
[tcp.routers.to-whoami-b.tls]
|
||||
passthrough=true
|
||||
passthrough = true
|
||||
|
||||
[tcp.routers.to-whoami-no-cert]
|
||||
rule = "HostSNI(`whoami-c.test`)"
|
||||
|
@@ -31,4 +31,3 @@
|
||||
passHostHeader = true
|
||||
[[http.services.keepalive.loadBalancer.servers]]
|
||||
url = "{{ .KeepAliveServer }}"
|
||||
weight = 1
|
||||
|
@@ -58,13 +58,13 @@
|
||||
url = "http://{{.WhoAmiIP}}:{{.WhoAmiPort}}"
|
||||
|
||||
[http.services.service2]
|
||||
passHostHeader = true
|
||||
[http.services.service2.loadBalancer]
|
||||
passHostHeader = true
|
||||
[[http.services.service2.loadBalancer.servers]]
|
||||
url = "http://{{.WhoAmiIP}}:{{.WhoAmiPort}}"
|
||||
|
||||
[http.services.service3]
|
||||
passHostHeader = true
|
||||
[http.services.service3.loadBalancer]
|
||||
passHostHeader = true
|
||||
[[http.services.service3.loadBalancer.servers]]
|
||||
url = "http://{{.WhoAmiIP}}:{{.WhoAmiPort}}"
|
||||
|
@@ -57,13 +57,13 @@
|
||||
url = "http://{{.WhoAmiIP}}:{{.WhoAmiPort}}"
|
||||
|
||||
[http.services.service2]
|
||||
passHostHeader = true
|
||||
[http.services.service2.loadBalancer]
|
||||
passHostHeader = true
|
||||
[[http.services.service2.loadBalancer.servers]]
|
||||
url = "http://{{.WhoAmiIP}}:{{.WhoAmiPort}}"
|
||||
|
||||
[http.services.service3]
|
||||
passHostHeader = true
|
||||
[http.services.service3.loadBalancer]
|
||||
passHostHeader = true
|
||||
[[http.services.service3.loadBalancer.servers]]
|
||||
url = "http://{{.WhoAmiIP}}:{{.WhoAmiPort}}"
|
||||
|
@@ -53,13 +53,13 @@
|
||||
url = "http://{{.WhoAmiIP}}:{{.WhoAmiPort}}"
|
||||
|
||||
[http.services.service2]
|
||||
passHostHeader = true
|
||||
[http.services.service2.loadBalancer]
|
||||
passHostHeader = true
|
||||
[[http.services.service2.loadBalancer.servers]]
|
||||
url = "http://{{.WhoAmiIP}}:{{.WhoAmiPort}}"
|
||||
|
||||
[http.services.service3]
|
||||
passHostHeader = true
|
||||
[http.services.service3.loadBalancer]
|
||||
passHostHeader = true
|
||||
[[http.services.service3.loadBalancer.servers]]
|
||||
url = "http://{{.WhoAmiIP}}:{{.WhoAmiPort}}"
|
||||
|
@@ -162,3 +162,44 @@ func (s *HeadersSuite) TestSecureHeadersResponses(c *check.C) {
|
||||
c.Assert(err, checker.IsNil)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *HeadersSuite) TestMultipleSecureHeadersResponses(c *check.C) {
|
||||
file := s.adaptFile(c, "fixtures/headers/secure_multiple.toml", struct{}{})
|
||||
defer os.Remove(file)
|
||||
cmd, display := s.traefikCmd(withConfigFile(file))
|
||||
defer display(c)
|
||||
|
||||
err := cmd.Start()
|
||||
c.Assert(err, checker.IsNil)
|
||||
defer cmd.Process.Kill()
|
||||
|
||||
backend := startTestServer("9000", http.StatusOK, "")
|
||||
defer backend.Close()
|
||||
|
||||
err = try.GetRequest(backend.URL, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
testCase := []struct {
|
||||
desc string
|
||||
expected http.Header
|
||||
reqHost string
|
||||
}{
|
||||
{
|
||||
desc: "Feature-Policy Set",
|
||||
expected: http.Header{
|
||||
"X-Frame-Options": {"DENY"},
|
||||
"X-Content-Type-Options": {"nosniff"},
|
||||
},
|
||||
reqHost: "test.localhost",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCase {
|
||||
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/", nil)
|
||||
c.Assert(err, checker.IsNil)
|
||||
req.Host = test.reqHost
|
||||
|
||||
err = try.Request(req, 500*time.Millisecond, try.HasHeaderStruct(test.expected))
|
||||
c.Assert(err, checker.IsNil)
|
||||
}
|
||||
}
|
||||
|
@@ -20,9 +20,7 @@ import (
|
||||
math "math"
|
||||
|
||||
proto "github.com/golang/protobuf/proto"
|
||||
)
|
||||
|
||||
import (
|
||||
context "context"
|
||||
|
||||
grpc "google.golang.org/grpc"
|
||||
|
87
integration/http_test.go
Normal file
87
integration/http_test.go
Normal file
@@ -0,0 +1,87 @@
|
||||
package integration
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"time"
|
||||
|
||||
"github.com/containous/traefik/v2/integration/try"
|
||||
"github.com/containous/traefik/v2/pkg/config/dynamic"
|
||||
"github.com/go-check/check"
|
||||
checker "github.com/vdemeester/shakers"
|
||||
)
|
||||
|
||||
type HTTPSuite struct{ BaseSuite }
|
||||
|
||||
func (s *HTTPSuite) TestSimpleConfiguration(c *check.C) {
|
||||
cmd, display := s.traefikCmd(withConfigFile("fixtures/http/simple.toml"))
|
||||
defer display(c)
|
||||
|
||||
err := cmd.Start()
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
defer cmd.Process.Kill()
|
||||
|
||||
// Expect a 404 as we configured nothing.
|
||||
err = try.GetRequest("http://127.0.0.1:8000/", time.Second, try.StatusCodeIs(http.StatusNotFound))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
// Provide a configuration, fetched by Traefik provider.
|
||||
configuration := &dynamic.Configuration{
|
||||
HTTP: &dynamic.HTTPConfiguration{
|
||||
Routers: map[string]*dynamic.Router{
|
||||
"routerHTTP": {
|
||||
EntryPoints: []string{"web"},
|
||||
Middlewares: []string{},
|
||||
Service: "serviceHTTP",
|
||||
Rule: "PathPrefix(`/`)",
|
||||
},
|
||||
},
|
||||
Services: map[string]*dynamic.Service{
|
||||
"serviceHTTP": {
|
||||
LoadBalancer: &dynamic.ServersLoadBalancer{
|
||||
PassHostHeader: boolRef(true),
|
||||
Servers: []dynamic.Server{
|
||||
{
|
||||
URL: "http://bacon:80",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
configData, err := json.Marshal(configuration)
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
server := startTestServerWithResponse(configData)
|
||||
defer server.Close()
|
||||
|
||||
// Expect configuration to be applied.
|
||||
err = try.GetRequest("http://127.0.0.1:9090/api/rawdata", 3*time.Second, try.BodyContains("routerHTTP@http", "serviceHTTP@http", "http://bacon:80"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
}
|
||||
|
||||
func startTestServerWithResponse(response []byte) (ts *httptest.Server) {
|
||||
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
_, _ = w.Write(response)
|
||||
})
|
||||
listener, err := net.Listen("tcp", "127.0.0.1:9000")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
ts = &httptest.Server{
|
||||
Listener: listener,
|
||||
Config: &http.Server{Handler: handler},
|
||||
}
|
||||
ts.Start()
|
||||
return ts
|
||||
}
|
||||
|
||||
func boolRef(b bool) *bool {
|
||||
return &b
|
||||
}
|
@@ -49,6 +49,7 @@ func Test(t *testing.T) {
|
||||
check.Suite(&HealthCheckSuite{})
|
||||
check.Suite(&HeadersSuite{})
|
||||
check.Suite(&HostResolverSuite{})
|
||||
check.Suite(&HTTPSuite{})
|
||||
check.Suite(&HTTPSSuite{})
|
||||
check.Suite(&KeepAliveSuite{})
|
||||
check.Suite(&LogRotationSuite{})
|
||||
|
@@ -161,6 +161,35 @@ func (s *SimpleSuite) TestRequestAcceptGraceTimeout(c *check.C) {
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SimpleSuite) TestCustomPingTerminationStatusCode(c *check.C) {
|
||||
file := s.adaptFile(c, "fixtures/custom_ping_termination_status_code.toml", struct{}{})
|
||||
defer os.Remove(file)
|
||||
cmd, display := s.traefikCmd(withConfigFile(file))
|
||||
defer display(c)
|
||||
|
||||
err := cmd.Start()
|
||||
c.Assert(err, checker.IsNil)
|
||||
defer cmd.Process.Kill()
|
||||
|
||||
// Wait for Traefik to turn ready.
|
||||
err = try.GetRequest("http://127.0.0.1:8001/", 2*time.Second, try.StatusCodeIs(http.StatusNotFound))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
// Check that /ping endpoint is responding with 200.
|
||||
err = try.GetRequest("http://127.0.0.1:8001/ping", 3*time.Second, try.StatusCodeIs(http.StatusOK))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
// Send SIGTERM to Traefik.
|
||||
proc, err := os.FindProcess(cmd.Process.Pid)
|
||||
c.Assert(err, checker.IsNil)
|
||||
err = proc.Signal(syscall.SIGTERM)
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
// ping endpoint should now return a Service Unavailable.
|
||||
err = try.GetRequest("http://127.0.0.1:8001/ping", 2*time.Second, try.StatusCodeIs(http.StatusNoContent))
|
||||
c.Assert(err, checker.IsNil)
|
||||
}
|
||||
|
||||
func (s *SimpleSuite) TestStatsWithMultipleEntryPoint(c *check.C) {
|
||||
c.Skip("Stats is missing")
|
||||
s.createComposeProject(c, "stats")
|
||||
|
@@ -57,6 +57,11 @@ THIS FILE MUST NOT BE EDITED BY HAND
|
||||
w.writeln()
|
||||
|
||||
for i, flat := range flats {
|
||||
// TODO must be move into the flats creation.
|
||||
if flat.Name == "experimental.plugins.<name>" || flat.Name == "TRAEFIK_EXPERIMENTAL_PLUGINS_<NAME>" {
|
||||
continue
|
||||
}
|
||||
|
||||
w.writeln("`" + prefix + strings.ReplaceAll(flat.Name, "[0]", "[n]") + "`: ")
|
||||
if flat.Default == "" {
|
||||
w.writeln(flat.Description)
|
||||
|
@@ -2,6 +2,7 @@ package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
"github.com/containous/traefik/v2/pkg/log"
|
||||
assetfs "github.com/elazarl/go-bindata-assetfs"
|
||||
@@ -23,11 +24,29 @@ func (g DashboardHandler) Append(router *mux.Router) {
|
||||
// Expose dashboard
|
||||
router.Methods(http.MethodGet).
|
||||
Path("/").
|
||||
HandlerFunc(func(response http.ResponseWriter, request *http.Request) {
|
||||
http.Redirect(response, request, request.Header.Get("X-Forwarded-Prefix")+"/dashboard/", http.StatusFound)
|
||||
HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
|
||||
http.Redirect(resp, req, safePrefix(req)+"/dashboard/", http.StatusFound)
|
||||
})
|
||||
|
||||
router.Methods(http.MethodGet).
|
||||
PathPrefix("/dashboard/").
|
||||
Handler(http.StripPrefix("/dashboard/", http.FileServer(g.Assets)))
|
||||
}
|
||||
|
||||
func safePrefix(req *http.Request) string {
|
||||
prefix := req.Header.Get("X-Forwarded-Prefix")
|
||||
if prefix == "" {
|
||||
return ""
|
||||
}
|
||||
|
||||
parse, err := url.Parse(prefix)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
if parse.Host != "" {
|
||||
return ""
|
||||
}
|
||||
|
||||
return parse.Path
|
||||
}
|
||||
|
54
pkg/api/dashboard_test.go
Normal file
54
pkg/api/dashboard_test.go
Normal file
@@ -0,0 +1,54 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func Test_safePrefix(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
value string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
desc: "host",
|
||||
value: "https://example.com",
|
||||
expected: "",
|
||||
},
|
||||
{
|
||||
desc: "host with path",
|
||||
value: "https://example.com/foo/bar?test",
|
||||
expected: "",
|
||||
},
|
||||
{
|
||||
desc: "path",
|
||||
value: "/foo/bar",
|
||||
expected: "/foo/bar",
|
||||
},
|
||||
{
|
||||
desc: "path without leading slash",
|
||||
value: "foo/bar",
|
||||
expected: "foo/bar",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
req, err := http.NewRequest(http.MethodGet, "http://localhost", nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
req.Header.Set("X-Forwarded-Prefix", test.value)
|
||||
|
||||
prefix := safePrefix(req)
|
||||
|
||||
assert.Equal(t, test.expected, prefix)
|
||||
})
|
||||
}
|
||||
}
|
@@ -4,7 +4,7 @@ type Yo struct {
|
||||
Foo string `description:"Foo description"`
|
||||
Fii string `description:"Fii description"`
|
||||
Fuu string `description:"Fuu description"`
|
||||
Yi *Yi `label:"allowEmpty"`
|
||||
Yi *Yi `label:"allowEmpty" file:"allowEmpty"`
|
||||
Yu *Yi
|
||||
}
|
||||
|
||||
|
@@ -21,7 +21,7 @@ type HTTPConfiguration struct {
|
||||
// Model is a set of default router's values.
|
||||
type Model struct {
|
||||
Middlewares []string `json:"middlewares,omitempty" toml:"middlewares,omitempty" yaml:"middlewares,omitempty"`
|
||||
TLS *RouterTLSConfig `json:"tls,omitempty" toml:"tls,omitempty" yaml:"tls,omitempty" label:"allowEmpty"`
|
||||
TLS *RouterTLSConfig `json:"tls,omitempty" toml:"tls,omitempty" yaml:"tls,omitempty" label:"allowEmpty" file:"allowEmpty"`
|
||||
}
|
||||
|
||||
// +k8s:deepcopy-gen=true
|
||||
@@ -42,7 +42,7 @@ type Router struct {
|
||||
Service string `json:"service,omitempty" toml:"service,omitempty" yaml:"service,omitempty"`
|
||||
Rule string `json:"rule,omitempty" toml:"rule,omitempty" yaml:"rule,omitempty"`
|
||||
Priority int `json:"priority,omitempty" toml:"priority,omitempty,omitzero" yaml:"priority,omitempty"`
|
||||
TLS *RouterTLSConfig `json:"tls,omitempty" toml:"tls,omitempty" yaml:"tls,omitempty" label:"allowEmpty"`
|
||||
TLS *RouterTLSConfig `json:"tls,omitempty" toml:"tls,omitempty" yaml:"tls,omitempty" label:"allowEmpty" file:"allowEmpty"`
|
||||
}
|
||||
|
||||
// +k8s:deepcopy-gen=true
|
||||
@@ -103,7 +103,7 @@ func (w *WRRService) SetDefaults() {
|
||||
|
||||
// Sticky holds the sticky configuration.
|
||||
type Sticky struct {
|
||||
Cookie *Cookie `json:"cookie,omitempty" toml:"cookie,omitempty" yaml:"cookie,omitempty" label:"allowEmpty"`
|
||||
Cookie *Cookie `json:"cookie,omitempty" toml:"cookie,omitempty" yaml:"cookie,omitempty" label:"allowEmpty" file:"allowEmpty"`
|
||||
}
|
||||
|
||||
// +k8s:deepcopy-gen=true
|
||||
@@ -120,7 +120,7 @@ type Cookie struct {
|
||||
|
||||
// ServersLoadBalancer holds the ServersLoadBalancer configuration.
|
||||
type ServersLoadBalancer struct {
|
||||
Sticky *Sticky `json:"sticky,omitempty" toml:"sticky,omitempty" yaml:"sticky,omitempty" label:"allowEmpty"`
|
||||
Sticky *Sticky `json:"sticky,omitempty" toml:"sticky,omitempty" yaml:"sticky,omitempty" label:"allowEmpty" file:"allowEmpty"`
|
||||
Servers []Server `json:"servers,omitempty" toml:"servers,omitempty" yaml:"servers,omitempty" label-slice-as-struct:"server"`
|
||||
HealthCheck *HealthCheck `json:"healthCheck,omitempty" toml:"healthCheck,omitempty" yaml:"healthCheck,omitempty"`
|
||||
PassHostHeader *bool `json:"passHostHeader" toml:"passHostHeader" yaml:"passHostHeader"`
|
||||
@@ -162,8 +162,8 @@ type ResponseForwarding struct {
|
||||
// Server holds the server configuration.
|
||||
type Server struct {
|
||||
URL string `json:"url,omitempty" toml:"url,omitempty" yaml:"url,omitempty" label:"-"`
|
||||
Scheme string `toml:"-" json:"-" yaml:"-"`
|
||||
Port string `toml:"-" json:"-" yaml:"-"`
|
||||
Scheme string `toml:"-" json:"-" yaml:"-" file:"-"`
|
||||
Port string `toml:"-" json:"-" yaml:"-" file:"-"`
|
||||
}
|
||||
|
||||
// SetDefaults Default values for a Server.
|
||||
|
@@ -34,10 +34,12 @@ type Middleware struct {
|
||||
InFlightReq *InFlightReq `json:"inFlightReq,omitempty" toml:"inFlightReq,omitempty" yaml:"inFlightReq,omitempty"`
|
||||
Buffering *Buffering `json:"buffering,omitempty" toml:"buffering,omitempty" yaml:"buffering,omitempty"`
|
||||
CircuitBreaker *CircuitBreaker `json:"circuitBreaker,omitempty" toml:"circuitBreaker,omitempty" yaml:"circuitBreaker,omitempty"`
|
||||
Compress *Compress `json:"compress,omitempty" toml:"compress,omitempty" yaml:"compress,omitempty" label:"allowEmpty"`
|
||||
Compress *Compress `json:"compress,omitempty" toml:"compress,omitempty" yaml:"compress,omitempty" label:"allowEmpty" file:"allowEmpty"`
|
||||
PassTLSClientCert *PassTLSClientCert `json:"passTLSClientCert,omitempty" toml:"passTLSClientCert,omitempty" yaml:"passTLSClientCert,omitempty"`
|
||||
Retry *Retry `json:"retry,omitempty" toml:"retry,omitempty" yaml:"retry,omitempty"`
|
||||
ContentType *ContentType `json:"contentType,omitempty" toml:"contentType,omitempty" yaml:"contentType,omitempty"`
|
||||
|
||||
Plugin map[string]PluginConf `json:"plugin,omitempty" toml:"plugin,omitempty" yaml:"plugin,omitempty"`
|
||||
}
|
||||
|
||||
// +k8s:deepcopy-gen=true
|
||||
@@ -275,7 +277,7 @@ func (s *IPStrategy) Get() (ip.Strategy, error) {
|
||||
// IPWhiteList holds the ip white list configuration.
|
||||
type IPWhiteList struct {
|
||||
SourceRange []string `json:"sourceRange,omitempty" toml:"sourceRange,omitempty" yaml:"sourceRange,omitempty"`
|
||||
IPStrategy *IPStrategy `json:"ipStrategy,omitempty" toml:"ipStrategy,omitempty" yaml:"ipStrategy,omitempty" label:"allowEmpty"`
|
||||
IPStrategy *IPStrategy `json:"ipStrategy,omitempty" toml:"ipStrategy,omitempty" yaml:"ipStrategy,omitempty" label:"allowEmpty" file:"allowEmpty"`
|
||||
}
|
||||
|
||||
// +k8s:deepcopy-gen=true
|
||||
|
27
pkg/config/dynamic/plugins.go
Normal file
27
pkg/config/dynamic/plugins.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package dynamic
|
||||
|
||||
import "k8s.io/apimachinery/pkg/runtime"
|
||||
|
||||
// +k8s:deepcopy-gen=false
|
||||
|
||||
// PluginConf holds the plugin configuration.
|
||||
type PluginConf map[string]interface{}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *PluginConf) DeepCopyInto(out *PluginConf) {
|
||||
if in == nil {
|
||||
*out = nil
|
||||
} else {
|
||||
*out = runtime.DeepCopyJSON(*in)
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PluginConf.
|
||||
func (in *PluginConf) DeepCopy() *PluginConf {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(PluginConf)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
@@ -50,7 +50,7 @@ type TCPRouter struct {
|
||||
EntryPoints []string `json:"entryPoints,omitempty" toml:"entryPoints,omitempty" yaml:"entryPoints,omitempty"`
|
||||
Service string `json:"service,omitempty" toml:"service,omitempty" yaml:"service,omitempty"`
|
||||
Rule string `json:"rule,omitempty" toml:"rule,omitempty" yaml:"rule,omitempty"`
|
||||
TLS *RouterTCPTLSConfig `json:"tls,omitempty" toml:"tls,omitempty" yaml:"tls,omitempty" label:"allowEmpty"`
|
||||
TLS *RouterTCPTLSConfig `json:"tls,omitempty" toml:"tls,omitempty" yaml:"tls,omitempty" label:"allowEmpty" file:"allowEmpty"`
|
||||
}
|
||||
|
||||
// +k8s:deepcopy-gen=true
|
||||
|
@@ -78,5 +78,5 @@ func (l *UDPServersLoadBalancer) Mergeable(loadBalancer *UDPServersLoadBalancer)
|
||||
// UDPServer defines a UDP server configuration.
|
||||
type UDPServer struct {
|
||||
Address string `json:"address,omitempty" toml:"address,omitempty" yaml:"address,omitempty" label:"-"`
|
||||
Port string `toml:"-" json:"-" yaml:"-"`
|
||||
Port string `toml:"-" json:"-" yaml:"-" file:"-"`
|
||||
}
|
||||
|
@@ -730,6 +730,13 @@ func (in *Middleware) DeepCopyInto(out *Middleware) {
|
||||
*out = new(ContentType)
|
||||
**out = **in
|
||||
}
|
||||
if in.Plugin != nil {
|
||||
in, out := &in.Plugin, &out.Plugin
|
||||
*out = make(map[string]PluginConf, len(*in))
|
||||
for key, val := range *in {
|
||||
(*out)[key] = *val.DeepCopy()
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
@@ -2,7 +2,11 @@
|
||||
package file
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/BurntSushi/toml"
|
||||
"github.com/containous/traefik/v2/pkg/config/parser"
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
// Decode decodes the given configuration file into the given element.
|
||||
@@ -22,11 +26,57 @@ func Decode(filePath string, element interface{}) error {
|
||||
return err
|
||||
}
|
||||
|
||||
metaOpts := parser.MetadataOpts{TagName: parser.TagLabel, AllowSliceAsStruct: true}
|
||||
metaOpts := parser.MetadataOpts{TagName: parser.TagFile, AllowSliceAsStruct: false}
|
||||
err = parser.AddMetadata(element, root, metaOpts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return parser.Fill(element, root, parser.FillerOpts{AllowSliceAsStruct: true})
|
||||
return parser.Fill(element, root, parser.FillerOpts{AllowSliceAsStruct: false})
|
||||
}
|
||||
|
||||
// DecodeContent decodes the given configuration file content into the given element.
|
||||
// The operation goes through three stages roughly summarized as:
|
||||
// file contents -> tree of untyped nodes
|
||||
// untyped nodes -> nodes augmented with metadata such as kind (inferred from element)
|
||||
// "typed" nodes -> typed element.
|
||||
func DecodeContent(content, extension string, element interface{}) error {
|
||||
data := make(map[string]interface{})
|
||||
|
||||
switch extension {
|
||||
case ".toml":
|
||||
_, err := toml.Decode(content, &data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
case ".yml", ".yaml":
|
||||
var err error
|
||||
err = yaml.Unmarshal([]byte(content), &data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
default:
|
||||
return fmt.Errorf("unsupported file extension: %s", extension)
|
||||
}
|
||||
|
||||
filters := getRootFieldNames(element)
|
||||
|
||||
node, err := decodeRawToNode(data, parser.DefaultRootName, filters...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(node.Children) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
metaOpts := parser.MetadataOpts{TagName: parser.TagFile, AllowSliceAsStruct: false}
|
||||
err = parser.AddMetadata(element, node, metaOpts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return parser.Fill(element, node, parser.FillerOpts{AllowSliceAsStruct: false})
|
||||
}
|
||||
|
@@ -42,6 +42,56 @@ fii = "bir"
|
||||
assert.Equal(t, expected, element)
|
||||
}
|
||||
|
||||
func TestDecodeContent_TOML(t *testing.T) {
|
||||
content := `
|
||||
foo = "bar"
|
||||
fii = "bir"
|
||||
[yi]
|
||||
`
|
||||
|
||||
element := &Yo{
|
||||
Fuu: "test",
|
||||
}
|
||||
|
||||
err := DecodeContent(content, ".toml", element)
|
||||
require.NoError(t, err)
|
||||
|
||||
expected := &Yo{
|
||||
Foo: "bar",
|
||||
Fii: "bir",
|
||||
Fuu: "test",
|
||||
Yi: &Yi{
|
||||
Foo: "foo",
|
||||
Fii: "fii",
|
||||
},
|
||||
}
|
||||
assert.Equal(t, expected, element)
|
||||
}
|
||||
|
||||
func TestDecodeContent_TOML_rawValue(t *testing.T) {
|
||||
content := `
|
||||
name = "test"
|
||||
[[meta.aaa]]
|
||||
bbb = 1
|
||||
`
|
||||
|
||||
type Foo struct {
|
||||
Name string
|
||||
Meta map[string]interface{}
|
||||
}
|
||||
|
||||
element := &Foo{}
|
||||
|
||||
err := DecodeContent(content, ".toml", element)
|
||||
require.NoError(t, err)
|
||||
|
||||
expected := &Foo{
|
||||
Name: "test",
|
||||
Meta: map[string]interface{}{"aaa": []interface{}{map[string]interface{}{"bbb": "1"}}},
|
||||
}
|
||||
assert.Equal(t, expected, element)
|
||||
}
|
||||
|
||||
func TestDecode_YAML(t *testing.T) {
|
||||
f, err := ioutil.TempFile("", "traefik-config-*.yaml")
|
||||
require.NoError(t, err)
|
||||
@@ -74,3 +124,54 @@ yi: {}
|
||||
}
|
||||
assert.Equal(t, expected, element)
|
||||
}
|
||||
|
||||
func TestDecodeContent_YAML(t *testing.T) {
|
||||
content := `
|
||||
foo: bar
|
||||
fii: bir
|
||||
yi: {}
|
||||
`
|
||||
|
||||
element := &Yo{
|
||||
Fuu: "test",
|
||||
}
|
||||
|
||||
err := DecodeContent(content, ".yaml", element)
|
||||
require.NoError(t, err)
|
||||
|
||||
expected := &Yo{
|
||||
Foo: "bar",
|
||||
Fii: "bir",
|
||||
Fuu: "test",
|
||||
Yi: &Yi{
|
||||
Foo: "foo",
|
||||
Fii: "fii",
|
||||
},
|
||||
}
|
||||
assert.Equal(t, expected, element)
|
||||
}
|
||||
|
||||
func TestDecodeContent_YAML_rawValue(t *testing.T) {
|
||||
content := `
|
||||
name: test
|
||||
meta:
|
||||
aaa:
|
||||
- bbb: 1
|
||||
`
|
||||
|
||||
type Foo struct {
|
||||
Name string
|
||||
Meta map[string]interface{}
|
||||
}
|
||||
|
||||
element := &Foo{}
|
||||
|
||||
err := DecodeContent(content, ".yaml", element)
|
||||
require.NoError(t, err)
|
||||
|
||||
expected := &Foo{
|
||||
Name: "test",
|
||||
Meta: map[string]interface{}{"aaa": []interface{}{map[string]interface{}{"bbb": "1"}}},
|
||||
}
|
||||
assert.Equal(t, expected, element)
|
||||
}
|
||||
|
@@ -6,7 +6,7 @@ type Yo struct {
|
||||
Foo string
|
||||
Fii string
|
||||
Fuu string
|
||||
Yi *Yi `label:"allowEmpty"`
|
||||
Yi *Yi `file:"allowEmpty"`
|
||||
}
|
||||
|
||||
func (y *Yo) SetDefaults() {
|
||||
|
@@ -28,6 +28,10 @@ func decodeRaw(node *parser.Node, vData reflect.Value, filters ...string) error
|
||||
sortedKeys := sortKeys(vData, filters)
|
||||
|
||||
for _, key := range sortedKeys {
|
||||
if vData.MapIndex(key).IsNil() {
|
||||
continue
|
||||
}
|
||||
|
||||
value := reflect.ValueOf(vData.MapIndex(key).Interface())
|
||||
|
||||
child := &parser.Node{Name: key.String()}
|
||||
|
@@ -524,6 +524,20 @@ func Test_decodeRawToNode(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "nil value",
|
||||
data: map[string]interface{}{
|
||||
"fii": map[interface{}]interface{}{
|
||||
"fuu": nil,
|
||||
},
|
||||
},
|
||||
expected: &parser.Node{
|
||||
Name: "traefik",
|
||||
Children: []*parser.Node{
|
||||
{Name: "fii"},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
|
@@ -123,18 +123,19 @@ func TestDecodeConfiguration(t *testing.T) {
|
||||
"traefik.http.middlewares.Middleware17.stripprefix.prefixes": "foobar, fiibar",
|
||||
"traefik.http.middlewares.Middleware18.stripprefixregex.regex": "foobar, fiibar",
|
||||
"traefik.http.middlewares.Middleware19.compress": "true",
|
||||
|
||||
"traefik.http.routers.Router0.entrypoints": "foobar, fiibar",
|
||||
"traefik.http.routers.Router0.middlewares": "foobar, fiibar",
|
||||
"traefik.http.routers.Router0.priority": "42",
|
||||
"traefik.http.routers.Router0.rule": "foobar",
|
||||
"traefik.http.routers.Router0.tls": "true",
|
||||
"traefik.http.routers.Router0.service": "foobar",
|
||||
"traefik.http.routers.Router1.entrypoints": "foobar, fiibar",
|
||||
"traefik.http.routers.Router1.middlewares": "foobar, fiibar",
|
||||
"traefik.http.routers.Router1.priority": "42",
|
||||
"traefik.http.routers.Router1.rule": "foobar",
|
||||
"traefik.http.routers.Router1.service": "foobar",
|
||||
"traefik.http.middlewares.Middleware20.plugin.tomato.aaa": "foo1",
|
||||
"traefik.http.middlewares.Middleware20.plugin.tomato.bbb": "foo2",
|
||||
"traefik.http.routers.Router0.entrypoints": "foobar, fiibar",
|
||||
"traefik.http.routers.Router0.middlewares": "foobar, fiibar",
|
||||
"traefik.http.routers.Router0.priority": "42",
|
||||
"traefik.http.routers.Router0.rule": "foobar",
|
||||
"traefik.http.routers.Router0.tls": "true",
|
||||
"traefik.http.routers.Router0.service": "foobar",
|
||||
"traefik.http.routers.Router1.entrypoints": "foobar, fiibar",
|
||||
"traefik.http.routers.Router1.middlewares": "foobar, fiibar",
|
||||
"traefik.http.routers.Router1.priority": "42",
|
||||
"traefik.http.routers.Router1.rule": "foobar",
|
||||
"traefik.http.routers.Router1.service": "foobar",
|
||||
|
||||
"traefik.http.services.Service0.loadbalancer.healthcheck.headers.name0": "foobar",
|
||||
"traefik.http.services.Service0.loadbalancer.healthcheck.headers.name1": "foobar",
|
||||
@@ -574,6 +575,14 @@ func TestDecodeConfiguration(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
"Middleware20": {
|
||||
Plugin: map[string]dynamic.PluginConf{
|
||||
"tomato": {
|
||||
"aaa": "foo1",
|
||||
"bbb": "foo2",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Services: map[string]*dynamic.Service{
|
||||
"Service0": {
|
||||
@@ -897,6 +906,14 @@ func TestEncodeConfiguration(t *testing.T) {
|
||||
RetryExpression: "foobar",
|
||||
},
|
||||
},
|
||||
"Middleware20": {
|
||||
Plugin: map[string]dynamic.PluginConf{
|
||||
"tomato": {
|
||||
"aaa": "foo1",
|
||||
"bbb": "foo2",
|
||||
},
|
||||
},
|
||||
},
|
||||
"Middleware3": {
|
||||
Chain: &dynamic.Chain{
|
||||
Middlewares: []string{
|
||||
@@ -1205,6 +1222,8 @@ func TestEncodeConfiguration(t *testing.T) {
|
||||
"traefik.HTTP.Middlewares.Middleware17.StripPrefix.ForceSlash": "true",
|
||||
"traefik.HTTP.Middlewares.Middleware18.StripPrefixRegex.Regex": "foobar, fiibar",
|
||||
"traefik.HTTP.Middlewares.Middleware19.Compress": "true",
|
||||
"traefik.HTTP.Middlewares.Middleware20.Plugin.tomato.aaa": "foo1",
|
||||
"traefik.HTTP.Middlewares.Middleware20.Plugin.tomato.bbb": "foo2",
|
||||
|
||||
"traefik.HTTP.Routers.Router0.EntryPoints": "foobar, fiibar",
|
||||
"traefik.HTTP.Routers.Router0.Middlewares": "foobar, fiibar",
|
||||
|
@@ -271,6 +271,16 @@ func (f filler) setMap(field reflect.Value, node *Node) error {
|
||||
field.Set(reflect.MakeMap(field.Type()))
|
||||
}
|
||||
|
||||
if field.Type().Elem().Kind() == reflect.Interface {
|
||||
fillRawValue(field, node, false)
|
||||
|
||||
for _, child := range node.Children {
|
||||
fillRawValue(field, child, true)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, child := range node.Children {
|
||||
ptrValue := reflect.New(reflect.PtrTo(field.Type().Elem()))
|
||||
|
||||
@@ -284,6 +294,7 @@ func (f filler) setMap(field reflect.Value, node *Node) error {
|
||||
key := reflect.ValueOf(child.Name)
|
||||
field.SetMapIndex(key, value)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -339,3 +350,23 @@ func setFloat(field reflect.Value, value string, bitSize int) error {
|
||||
field.Set(reflect.ValueOf(val).Convert(field.Type()))
|
||||
return nil
|
||||
}
|
||||
|
||||
func fillRawValue(field reflect.Value, node *Node, subMap bool) {
|
||||
m, ok := node.RawValue.(map[string]interface{})
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
if _, self := m[node.Name]; self || !subMap {
|
||||
for k, v := range m {
|
||||
field.SetMapIndex(reflect.ValueOf(k), reflect.ValueOf(v))
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
p := map[string]interface{}{node.Name: m}
|
||||
node.RawValue = p
|
||||
|
||||
field.SetMapIndex(reflect.ValueOf(node.Name), reflect.ValueOf(p[node.Name]))
|
||||
}
|
||||
|
@@ -1413,6 +1413,84 @@ func TestFill(t *testing.T) {
|
||||
},
|
||||
}},
|
||||
},
|
||||
{
|
||||
desc: "raw value",
|
||||
node: &Node{
|
||||
Name: "traefik",
|
||||
Kind: reflect.Ptr,
|
||||
Children: []*Node{
|
||||
{Name: "meta", FieldName: "Meta", Kind: reflect.Map, RawValue: map[string]interface{}{
|
||||
"aaa": "test",
|
||||
"bbb": map[string]interface{}{
|
||||
"ccc": "test",
|
||||
"ddd": map[string]interface{}{
|
||||
"eee": "test",
|
||||
},
|
||||
},
|
||||
}},
|
||||
{Name: "name", FieldName: "Name", Value: "test", Kind: reflect.String},
|
||||
},
|
||||
},
|
||||
element: &struct {
|
||||
Name string
|
||||
Meta map[string]interface{}
|
||||
}{},
|
||||
expected: expected{element: &struct {
|
||||
Name string
|
||||
Meta map[string]interface{}
|
||||
}{
|
||||
Name: "test",
|
||||
Meta: map[string]interface{}{
|
||||
"aaa": "test",
|
||||
"bbb": map[string]interface{}{
|
||||
"ccc": "test",
|
||||
"ddd": map[string]interface{}{
|
||||
"eee": "test",
|
||||
},
|
||||
},
|
||||
},
|
||||
}},
|
||||
},
|
||||
{
|
||||
desc: "explicit map of map, raw value",
|
||||
node: &Node{
|
||||
Name: "traefik",
|
||||
Kind: reflect.Ptr,
|
||||
Children: []*Node{
|
||||
{Name: "meta", FieldName: "Meta", Kind: reflect.Map, Children: []*Node{
|
||||
{Name: "aaa", Kind: reflect.Map, Children: []*Node{
|
||||
{Name: "bbb", RawValue: map[string]interface{}{
|
||||
"ccc": "test1",
|
||||
"ddd": "test2",
|
||||
}},
|
||||
{Name: "eee", Value: "test3", RawValue: map[string]interface{}{
|
||||
"eee": "test3",
|
||||
}},
|
||||
}},
|
||||
}},
|
||||
{Name: "name", FieldName: "Name", Value: "test", Kind: reflect.String},
|
||||
},
|
||||
},
|
||||
element: &struct {
|
||||
Name string
|
||||
Meta map[string]map[string]interface{}
|
||||
}{},
|
||||
expected: expected{element: &struct {
|
||||
Name string
|
||||
Meta map[string]map[string]interface{}
|
||||
}{
|
||||
Name: "test",
|
||||
Meta: map[string]map[string]interface{}{
|
||||
"aaa": {
|
||||
"bbb": map[string]interface{}{
|
||||
"ccc": "test1",
|
||||
"ddd": "test2",
|
||||
},
|
||||
"eee": "test3",
|
||||
},
|
||||
},
|
||||
}},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
|
@@ -123,6 +123,11 @@ func (e encoderToNode) setStructValue(node *Node, rValue reflect.Value) error {
|
||||
}
|
||||
|
||||
func (e encoderToNode) setMapValue(node *Node, rValue reflect.Value) error {
|
||||
if rValue.Type().Elem().Kind() == reflect.Interface {
|
||||
node.RawValue = rValue.Interface()
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, key := range rValue.MapKeys() {
|
||||
child := &Node{Name: key.String(), FieldName: key.String()}
|
||||
node.Children = append(node.Children, child)
|
||||
|
@@ -755,6 +755,42 @@ func TestEncodeToNode(t *testing.T) {
|
||||
}},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "raw value",
|
||||
element: struct {
|
||||
Foo *struct {
|
||||
Bar map[string]interface{}
|
||||
}
|
||||
}{
|
||||
Foo: &struct {
|
||||
Bar map[string]interface{}
|
||||
}{
|
||||
Bar: map[string]interface{}{
|
||||
"AAA": "valueA",
|
||||
"BBB": map[string]interface{}{
|
||||
"CCC": map[string]interface{}{
|
||||
"DDD": "valueD",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: expected{node: &Node{
|
||||
Name: "traefik",
|
||||
Children: []*Node{
|
||||
{Name: "Foo", FieldName: "Foo", Children: []*Node{
|
||||
{Name: "Bar", FieldName: "Bar", RawValue: map[string]interface{}{
|
||||
"AAA": "valueA",
|
||||
"BBB": map[string]interface{}{
|
||||
"CCC": map[string]interface{}{
|
||||
"DDD": "valueD",
|
||||
},
|
||||
},
|
||||
}},
|
||||
}},
|
||||
},
|
||||
}},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
|
@@ -1,5 +1,10 @@
|
||||
package parser
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// EncodeNode Converts a node to labels.
|
||||
// nodes -> labels.
|
||||
func EncodeNode(node *Node) map[string]string {
|
||||
@@ -21,6 +26,11 @@ func encodeNode(labels map[string]string, root string, node *Node) {
|
||||
|
||||
childName := root + sep + child.Name
|
||||
|
||||
if child.RawValue != nil {
|
||||
encodeRawValue(labels, childName, child.RawValue)
|
||||
continue
|
||||
}
|
||||
|
||||
if len(child.Children) > 0 {
|
||||
encodeNode(labels, childName, child)
|
||||
} else if len(child.Name) > 0 {
|
||||
@@ -28,3 +38,30 @@ func encodeNode(labels map[string]string, root string, node *Node) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func encodeRawValue(labels map[string]string, root string, rawValue interface{}) {
|
||||
if rawValue == nil {
|
||||
return
|
||||
}
|
||||
|
||||
tValue := reflect.TypeOf(rawValue)
|
||||
|
||||
if tValue.Kind() == reflect.Map && tValue.Elem().Kind() == reflect.Interface {
|
||||
r := reflect.ValueOf(rawValue).
|
||||
Convert(reflect.TypeOf((map[string]interface{})(nil))).
|
||||
Interface().(map[string]interface{})
|
||||
|
||||
for k, v := range r {
|
||||
switch tv := v.(type) {
|
||||
case string:
|
||||
labels[root+"."+k] = tv
|
||||
case []interface{}:
|
||||
for i, e := range tv {
|
||||
encodeRawValue(labels, fmt.Sprintf("%s.%s[%d]", root, k, i), e)
|
||||
}
|
||||
default:
|
||||
encodeRawValue(labels, root+"."+k, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -165,6 +165,60 @@ func TestEncodeNode(t *testing.T) {
|
||||
"traefik.foo[1].bbb": "bur1",
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "raw value, level 1",
|
||||
node: &Node{
|
||||
Name: "traefik",
|
||||
Children: []*Node{
|
||||
{Name: "aaa", RawValue: map[string]interface{}{
|
||||
"bbb": "test1",
|
||||
"ccc": "test2",
|
||||
}},
|
||||
},
|
||||
},
|
||||
expected: map[string]string{
|
||||
"traefik.aaa.bbb": "test1",
|
||||
"traefik.aaa.ccc": "test2",
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "raw value, level 2",
|
||||
node: &Node{
|
||||
Name: "traefik",
|
||||
Children: []*Node{
|
||||
{Name: "aaa", RawValue: map[string]interface{}{
|
||||
"bbb": "test1",
|
||||
"ccc": map[string]interface{}{
|
||||
"ddd": "test2",
|
||||
},
|
||||
}},
|
||||
},
|
||||
},
|
||||
expected: map[string]string{
|
||||
"traefik.aaa.bbb": "test1",
|
||||
"traefik.aaa.ccc.ddd": "test2",
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "raw value, slice of struct",
|
||||
node: &Node{
|
||||
Name: "traefik",
|
||||
Children: []*Node{
|
||||
{Name: "aaa", RawValue: map[string]interface{}{
|
||||
"bbb": []interface{}{
|
||||
map[string]interface{}{
|
||||
"ccc": "test1",
|
||||
"ddd": "test2",
|
||||
},
|
||||
},
|
||||
}},
|
||||
},
|
||||
},
|
||||
expected: map[string]string{
|
||||
"traefik.aaa.bbb[0].ccc": "test1",
|
||||
"traefik.aaa.bbb[0].ddd": "test2",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
|
@@ -14,6 +14,7 @@ type Node struct {
|
||||
Description string `json:"description,omitempty"`
|
||||
FieldName string `json:"fieldName"`
|
||||
Value string `json:"value,omitempty"`
|
||||
RawValue interface{} `json:"rawValue,omitempty"`
|
||||
Disabled bool `json:"disabled,omitempty"`
|
||||
Kind reflect.Kind `json:"kind,omitempty"`
|
||||
Tag reflect.StructTag `json:"tag,omitempty"`
|
||||
|
@@ -57,6 +57,11 @@ func (m metadata) add(rootType reflect.Type, node *Node) error {
|
||||
rType = rootType.Elem()
|
||||
}
|
||||
|
||||
if rType.Kind() == reflect.Map && rType.Elem().Kind() == reflect.Interface {
|
||||
addRawValue(node)
|
||||
return nil
|
||||
}
|
||||
|
||||
field, err := m.findTypedField(rType, node)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -88,6 +93,11 @@ func (m metadata) add(rootType reflect.Type, node *Node) error {
|
||||
}
|
||||
|
||||
if fType.Kind() == reflect.Map {
|
||||
if fType.Elem().Kind() == reflect.Interface {
|
||||
addRawValue(node)
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, child := range node.Children {
|
||||
// elem is a map entry value type
|
||||
elem := fType.Elem()
|
||||
@@ -194,3 +204,75 @@ func isSupportedType(field reflect.StructField) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
/*
|
||||
RawMap section
|
||||
*/
|
||||
|
||||
func addRawValue(node *Node) {
|
||||
if node.RawValue == nil {
|
||||
node.RawValue = nodeToRawMap(node)
|
||||
}
|
||||
|
||||
node.Children = nil
|
||||
}
|
||||
|
||||
func nodeToRawMap(node *Node) map[string]interface{} {
|
||||
result := map[string]interface{}{}
|
||||
|
||||
squashNode(node, result, true)
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func squashNode(node *Node, acc map[string]interface{}, root bool) {
|
||||
if len(node.Children) == 0 {
|
||||
acc[node.Name] = node.Value
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// slice
|
||||
if isArrayKey(node.Children[0].Name) {
|
||||
var accChild []interface{}
|
||||
|
||||
for _, child := range node.Children {
|
||||
tmp := map[string]interface{}{}
|
||||
squashNode(child, tmp, false)
|
||||
accChild = append(accChild, tmp[child.Name])
|
||||
}
|
||||
|
||||
acc[node.Name] = accChild
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// map
|
||||
var accChild map[string]interface{}
|
||||
if root {
|
||||
accChild = acc
|
||||
} else {
|
||||
accChild = typedRawMap(acc, node.Name)
|
||||
}
|
||||
|
||||
for _, child := range node.Children {
|
||||
squashNode(child, accChild, false)
|
||||
}
|
||||
}
|
||||
|
||||
func typedRawMap(m map[string]interface{}, k string) map[string]interface{} {
|
||||
if m[k] == nil {
|
||||
m[k] = map[string]interface{}{}
|
||||
}
|
||||
|
||||
r, ok := m[k].(map[string]interface{})
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("unsupported value (key: %s): %T", k, m[k]))
|
||||
}
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
func isArrayKey(name string) bool {
|
||||
return name[0] == '[' && name[len(name)-1] == ']'
|
||||
}
|
||||
|
@@ -991,6 +991,51 @@ func TestAddMetadata(t *testing.T) {
|
||||
},
|
||||
}},
|
||||
},
|
||||
{
|
||||
desc: "raw value",
|
||||
tree: &Node{
|
||||
Name: "traefik",
|
||||
Children: []*Node{
|
||||
{Name: "Foo", FieldName: "Foo", Children: []*Node{
|
||||
{Name: "Bar", FieldName: "Bar", Children: []*Node{
|
||||
{Name: "AAA", FieldName: "AAA", Value: "valueA"},
|
||||
{Name: "BBB", FieldName: "BBB", Children: []*Node{
|
||||
{Name: "CCC", FieldName: "CCC", Children: []*Node{
|
||||
{Name: "DDD", FieldName: "DDD", Value: "valueD"},
|
||||
}},
|
||||
}},
|
||||
}},
|
||||
}},
|
||||
},
|
||||
},
|
||||
structure: struct {
|
||||
Foo *struct {
|
||||
Bar map[string]interface{}
|
||||
}
|
||||
}{
|
||||
Foo: &struct {
|
||||
Bar map[string]interface{}
|
||||
}{},
|
||||
},
|
||||
expected: expected{
|
||||
node: &Node{
|
||||
Name: "traefik",
|
||||
Kind: reflect.Struct,
|
||||
Children: []*Node{
|
||||
{Name: "Foo", FieldName: "Foo", Kind: reflect.Ptr, Children: []*Node{
|
||||
{Name: "Bar", FieldName: "Bar", Kind: reflect.Map, RawValue: map[string]interface{}{
|
||||
"AAA": "valueA",
|
||||
"BBB": map[string]interface{}{
|
||||
"CCC": map[string]interface{}{
|
||||
"DDD": "valueD",
|
||||
},
|
||||
},
|
||||
}},
|
||||
}},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
@@ -1015,4 +1060,109 @@ func TestAddMetadata(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func Test_nodeToRawMap(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
root *Node
|
||||
expected map[string]interface{}
|
||||
}{
|
||||
{
|
||||
desc: "simple",
|
||||
root: &Node{
|
||||
Name: "traefik",
|
||||
Children: []*Node{
|
||||
{Name: "meta", Children: []*Node{
|
||||
{Name: "aaa", Value: "test1"},
|
||||
{Name: "bbb", Children: []*Node{
|
||||
{Name: "ccc", Value: "test2"},
|
||||
{Name: "ddd", Children: []*Node{
|
||||
{Name: "eee", Value: "test3"},
|
||||
}},
|
||||
}},
|
||||
}},
|
||||
{Name: "name", Value: "bla"},
|
||||
},
|
||||
},
|
||||
expected: map[string]interface{}{
|
||||
"meta": map[string]interface{}{
|
||||
"aaa": "test1",
|
||||
"bbb": map[string]interface{}{
|
||||
"ccc": "test2",
|
||||
"ddd": map[string]interface{}{
|
||||
"eee": "test3",
|
||||
},
|
||||
},
|
||||
},
|
||||
"name": "bla",
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "slice of struct, level 1",
|
||||
root: &Node{
|
||||
Name: "aaa",
|
||||
Children: []*Node{
|
||||
{Name: "[0]", Children: []*Node{
|
||||
{Name: "bbb", Value: "test1"},
|
||||
{Name: "ccc", Value: "test2"},
|
||||
}},
|
||||
},
|
||||
},
|
||||
expected: map[string]interface{}{
|
||||
"aaa": []interface{}{
|
||||
map[string]interface{}{
|
||||
"bbb": "test1",
|
||||
"ccc": "test2",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "slice of struct, level 2",
|
||||
root: &Node{
|
||||
Name: "traefik",
|
||||
Children: []*Node{
|
||||
{Name: "meta", Children: []*Node{{
|
||||
Name: "aaa", Children: []*Node{
|
||||
{Name: "[0]", Children: []*Node{
|
||||
{Name: "bbb", Value: "test2"},
|
||||
{Name: "ccc", Value: "test3"},
|
||||
}},
|
||||
{Name: "[1]", Children: []*Node{
|
||||
{Name: "bbb", Value: "test4"},
|
||||
{Name: "ccc", Value: "test5"},
|
||||
}},
|
||||
},
|
||||
}}},
|
||||
{Name: "name", Value: "test1"},
|
||||
},
|
||||
},
|
||||
expected: map[string]interface{}{
|
||||
"meta": map[string]interface{}{
|
||||
"aaa": []interface{}{
|
||||
map[string]interface{}{
|
||||
"bbb": "test2",
|
||||
"ccc": "test3",
|
||||
},
|
||||
map[string]interface{}{
|
||||
"bbb": "test4",
|
||||
"ccc": "test5",
|
||||
},
|
||||
},
|
||||
},
|
||||
"name": "test1",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
actual := nodeToRawMap(test.root)
|
||||
assert.Equal(t, test.expected, actual)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type MySliceType []string
|
||||
|
@@ -19,12 +19,7 @@ func Decode(labels map[string]string, element interface{}, rootName string, filt
|
||||
return err
|
||||
}
|
||||
|
||||
err = Fill(element, node, FillerOpts{AllowSliceAsStruct: true})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
return Fill(element, node, FillerOpts{AllowSliceAsStruct: true})
|
||||
}
|
||||
|
||||
// Encode converts an element to labels.
|
||||
|
346
pkg/config/parser/parser_test.go
Normal file
346
pkg/config/parser/parser_test.go
Normal file
@@ -0,0 +1,346 @@
|
||||
package parser
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
type Tomato struct {
|
||||
Name string
|
||||
Meta map[string]interface{}
|
||||
}
|
||||
|
||||
type Potato struct {
|
||||
Name string
|
||||
Meta map[string]map[string]interface{}
|
||||
}
|
||||
|
||||
func TestDecode_RawValue(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
labels map[string]string
|
||||
elt interface{}
|
||||
expected interface{}
|
||||
}{
|
||||
{
|
||||
desc: "level 1",
|
||||
elt: &Tomato{},
|
||||
labels: map[string]string{
|
||||
"traefik.name": "test",
|
||||
"traefik.meta.aaa": "test",
|
||||
},
|
||||
expected: &Tomato{
|
||||
Name: "test",
|
||||
Meta: map[string]interface{}{
|
||||
"aaa": "test",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "level 2",
|
||||
labels: map[string]string{
|
||||
"traefik.name": "test",
|
||||
"traefik.meta.aaa": "test",
|
||||
"traefik.meta.bbb.ccc": "test",
|
||||
},
|
||||
elt: &Tomato{},
|
||||
expected: &Tomato{
|
||||
Name: "test",
|
||||
Meta: map[string]interface{}{
|
||||
"aaa": "test",
|
||||
"bbb": map[string]interface{}{
|
||||
"ccc": "test",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "level 3",
|
||||
labels: map[string]string{
|
||||
"traefik.name": "test",
|
||||
"traefik.meta.aaa": "test",
|
||||
"traefik.meta.bbb.ccc": "test",
|
||||
"traefik.meta.bbb.ddd.eee": "test",
|
||||
},
|
||||
elt: &Tomato{},
|
||||
expected: &Tomato{
|
||||
Name: "test",
|
||||
Meta: map[string]interface{}{
|
||||
"aaa": "test",
|
||||
"bbb": map[string]interface{}{
|
||||
"ccc": "test",
|
||||
"ddd": map[string]interface{}{
|
||||
"eee": "test",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "struct slice, one entry",
|
||||
elt: &Tomato{},
|
||||
labels: map[string]string{
|
||||
"traefik.name": "test1",
|
||||
"traefik.meta.aaa[0].bbb": "test2",
|
||||
"traefik.meta.aaa[0].ccc": "test3",
|
||||
},
|
||||
expected: &Tomato{
|
||||
Name: "test1",
|
||||
Meta: map[string]interface{}{
|
||||
"aaa": []interface{}{
|
||||
map[string]interface{}{
|
||||
"bbb": "test2",
|
||||
"ccc": "test3",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "struct slice, multiple entries",
|
||||
elt: &Tomato{},
|
||||
labels: map[string]string{
|
||||
"traefik.name": "test1",
|
||||
"traefik.meta.aaa[0].bbb": "test2",
|
||||
"traefik.meta.aaa[0].ccc": "test3",
|
||||
"traefik.meta.aaa[1].bbb": "test4",
|
||||
"traefik.meta.aaa[1].ccc": "test5",
|
||||
"traefik.meta.aaa[2].bbb": "test6",
|
||||
"traefik.meta.aaa[2].ccc": "test7",
|
||||
},
|
||||
expected: &Tomato{
|
||||
Name: "test1",
|
||||
Meta: map[string]interface{}{
|
||||
"aaa": []interface{}{
|
||||
map[string]interface{}{
|
||||
"bbb": "test2",
|
||||
"ccc": "test3",
|
||||
},
|
||||
map[string]interface{}{
|
||||
"bbb": "test4",
|
||||
"ccc": "test5",
|
||||
},
|
||||
map[string]interface{}{
|
||||
"bbb": "test6",
|
||||
"ccc": "test7",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "explicit map of map, level 1",
|
||||
elt: &Potato{},
|
||||
labels: map[string]string{
|
||||
"traefik.name": "test",
|
||||
"traefik.meta.aaa.bbb": "test1",
|
||||
},
|
||||
expected: &Potato{
|
||||
Name: "test",
|
||||
Meta: map[string]map[string]interface{}{
|
||||
"aaa": {
|
||||
"bbb": "test1",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "explicit map of map, level 2",
|
||||
elt: &Potato{},
|
||||
labels: map[string]string{
|
||||
"traefik.name": "test",
|
||||
"traefik.meta.aaa.bbb": "test1",
|
||||
"traefik.meta.aaa.ccc": "test2",
|
||||
},
|
||||
expected: &Potato{
|
||||
Name: "test",
|
||||
Meta: map[string]map[string]interface{}{
|
||||
"aaa": {
|
||||
"bbb": "test1",
|
||||
"ccc": "test2",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "explicit map of map, level 3",
|
||||
elt: &Potato{},
|
||||
labels: map[string]string{
|
||||
"traefik.name": "test",
|
||||
"traefik.meta.aaa.bbb.ccc": "test1",
|
||||
"traefik.meta.aaa.bbb.ddd": "test2",
|
||||
"traefik.meta.aaa.eee": "test3",
|
||||
},
|
||||
expected: &Potato{
|
||||
Name: "test",
|
||||
Meta: map[string]map[string]interface{}{
|
||||
"aaa": {
|
||||
"bbb": map[string]interface{}{
|
||||
"ccc": "test1",
|
||||
"ddd": "test2",
|
||||
},
|
||||
"eee": "test3",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "explicit map of map, level 4",
|
||||
elt: &Potato{},
|
||||
labels: map[string]string{
|
||||
"traefik.name": "test",
|
||||
"traefik.meta.aaa.bbb.ccc.ddd": "test1",
|
||||
"traefik.meta.aaa.bbb.ccc.eee": "test2",
|
||||
"traefik.meta.aaa.bbb.fff": "test3",
|
||||
"traefik.meta.aaa.ggg": "test4",
|
||||
},
|
||||
expected: &Potato{
|
||||
Name: "test",
|
||||
Meta: map[string]map[string]interface{}{
|
||||
"aaa": {
|
||||
"bbb": map[string]interface{}{
|
||||
"ccc": map[string]interface{}{
|
||||
"ddd": "test1",
|
||||
"eee": "test2",
|
||||
},
|
||||
"fff": "test3",
|
||||
},
|
||||
"ggg": "test4",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "explicit map of map, struct slice, level 1, one entry",
|
||||
elt: &Potato{},
|
||||
labels: map[string]string{
|
||||
"traefik.name": "test1",
|
||||
"traefik.meta.aaa.bbb[0].ccc": "test2",
|
||||
"traefik.meta.aaa.bbb[0].ddd": "test3",
|
||||
},
|
||||
expected: &Potato{
|
||||
Name: "test1",
|
||||
Meta: map[string]map[string]interface{}{
|
||||
"aaa": {
|
||||
"bbb": []interface{}{
|
||||
map[string]interface{}{
|
||||
"ccc": "test2",
|
||||
"ddd": "test3",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "explicit map of map, struct slice, level 1, multiple entries",
|
||||
elt: &Potato{},
|
||||
labels: map[string]string{
|
||||
"traefik.name": "test1",
|
||||
"traefik.meta.aaa.bbb[0].ccc": "test2",
|
||||
"traefik.meta.aaa.bbb[0].ddd": "test3",
|
||||
"traefik.meta.aaa.bbb[1].ccc": "test4",
|
||||
"traefik.meta.aaa.bbb[1].ddd": "test5",
|
||||
"traefik.meta.aaa.bbb[2].ccc": "test6",
|
||||
"traefik.meta.aaa.bbb[2].ddd": "test7",
|
||||
},
|
||||
expected: &Potato{
|
||||
Name: "test1",
|
||||
Meta: map[string]map[string]interface{}{
|
||||
"aaa": {
|
||||
"bbb": []interface{}{
|
||||
map[string]interface{}{
|
||||
"ccc": "test2",
|
||||
"ddd": "test3",
|
||||
},
|
||||
map[string]interface{}{
|
||||
"ccc": "test4",
|
||||
"ddd": "test5",
|
||||
},
|
||||
map[string]interface{}{
|
||||
"ccc": "test6",
|
||||
"ddd": "test7",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "explicit map of map, struct slice, level 2, one entry",
|
||||
elt: &Potato{},
|
||||
labels: map[string]string{
|
||||
"traefik.name": "test1",
|
||||
"traefik.meta.aaa.bbb.ccc[0].ddd": "test2",
|
||||
"traefik.meta.aaa.bbb.ccc[0].eee": "test3",
|
||||
},
|
||||
expected: &Potato{
|
||||
Name: "test1",
|
||||
Meta: map[string]map[string]interface{}{
|
||||
"aaa": {
|
||||
"bbb": map[string]interface{}{
|
||||
"ccc": []interface{}{
|
||||
map[string]interface{}{
|
||||
"ddd": "test2",
|
||||
"eee": "test3",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "explicit map of map, struct slice, level 2, multiple entries",
|
||||
elt: &Potato{},
|
||||
labels: map[string]string{
|
||||
"traefik.name": "test1",
|
||||
"traefik.meta.aaa.bbb.ccc[0].ddd": "test2",
|
||||
"traefik.meta.aaa.bbb.ccc[0].eee": "test3",
|
||||
"traefik.meta.aaa.bbb.ccc[1].ddd": "test4",
|
||||
"traefik.meta.aaa.bbb.ccc[1].eee": "test5",
|
||||
"traefik.meta.aaa.bbb.ccc[2].ddd": "test6",
|
||||
"traefik.meta.aaa.bbb.ccc[2].eee": "test7",
|
||||
},
|
||||
expected: &Potato{
|
||||
Name: "test1",
|
||||
Meta: map[string]map[string]interface{}{
|
||||
"aaa": {
|
||||
"bbb": map[string]interface{}{
|
||||
"ccc": []interface{}{
|
||||
map[string]interface{}{
|
||||
"ddd": "test2",
|
||||
"eee": "test3",
|
||||
},
|
||||
map[string]interface{}{
|
||||
"ddd": "test4",
|
||||
"eee": "test5",
|
||||
},
|
||||
map[string]interface{}{
|
||||
"ddd": "test6",
|
||||
"eee": "test7",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
if test.desc != "level 3" {
|
||||
continue
|
||||
}
|
||||
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
err := Decode(test.labels, test.elt, "traefik")
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, test.expected, test.elt)
|
||||
})
|
||||
}
|
||||
}
|
@@ -6,6 +6,11 @@ const (
|
||||
// - "-": ignore the field.
|
||||
TagLabel = "label"
|
||||
|
||||
// TagFile allows to apply a custom behavior.
|
||||
// - "allowEmpty": allows to create an empty struct.
|
||||
// - "-": ignore the field.
|
||||
TagFile = "file"
|
||||
|
||||
// TagLabelSliceAsStruct allows to use a slice of struct by creating one entry into the slice.
|
||||
// The value is the substitution name used in the label to access the slice.
|
||||
TagLabelSliceAsStruct = "label-slice-as-struct"
|
||||
|
@@ -12,7 +12,7 @@ import (
|
||||
type EntryPoint struct {
|
||||
Address string `description:"Entry point address." json:"address,omitempty" toml:"address,omitempty" yaml:"address,omitempty"`
|
||||
Transport *EntryPointsTransport `description:"Configures communication between clients and Traefik." json:"transport,omitempty" toml:"transport,omitempty" yaml:"transport,omitempty"`
|
||||
ProxyProtocol *ProxyProtocol `description:"Proxy-Protocol configuration." json:"proxyProtocol,omitempty" toml:"proxyProtocol,omitempty" yaml:"proxyProtocol,omitempty" label:"allowEmpty"`
|
||||
ProxyProtocol *ProxyProtocol `description:"Proxy-Protocol configuration." json:"proxyProtocol,omitempty" toml:"proxyProtocol,omitempty" yaml:"proxyProtocol,omitempty" label:"allowEmpty" file:"allowEmpty"`
|
||||
ForwardedHeaders *ForwardedHeaders `description:"Trust client forwarding headers." json:"forwardedHeaders,omitempty" toml:"forwardedHeaders,omitempty" yaml:"forwardedHeaders,omitempty"`
|
||||
HTTP HTTPConfig `description:"HTTP configuration." json:"http,omitempty" toml:"http,omitempty" yaml:"http,omitempty"`
|
||||
}
|
||||
@@ -51,7 +51,7 @@ func (ep *EntryPoint) SetDefaults() {
|
||||
type HTTPConfig struct {
|
||||
Redirections *Redirections `description:"Set of redirection" json:"redirections,omitempty" toml:"redirections,omitempty" yaml:"redirections,omitempty"`
|
||||
Middlewares []string `description:"Default middlewares for the routers linked to the entry point." json:"middlewares,omitempty" toml:"middlewares,omitempty" yaml:"middlewares,omitempty"`
|
||||
TLS *TLSConfig `description:"Default TLS configuration for the routers linked to the entry point." json:"tls,omitempty" toml:"tls,omitempty" yaml:"tls,omitempty" label:"allowEmpty"`
|
||||
TLS *TLSConfig `description:"Default TLS configuration for the routers linked to the entry point." json:"tls,omitempty" toml:"tls,omitempty" yaml:"tls,omitempty" label:"allowEmpty" file:"allowEmpty"`
|
||||
}
|
||||
|
||||
// Redirections is a set of redirection for an entry point.
|
||||
|
16
pkg/config/static/experimental.go
Normal file
16
pkg/config/static/experimental.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package static
|
||||
|
||||
import "github.com/containous/traefik/v2/pkg/plugins"
|
||||
|
||||
// Experimental experimental Traefik features.
|
||||
type Experimental struct {
|
||||
Pilot *Pilot `description:"Traefik Pilot configuration." json:"pilot,omitempty" toml:"pilot,omitempty" yaml:"pilot,omitempty"`
|
||||
|
||||
Plugins map[string]plugins.Descriptor `description:"Plugins configuration." json:"plugins,omitempty" toml:"plugins,omitempty" yaml:"plugins,omitempty"`
|
||||
DevPlugin *plugins.DevPlugin `description:"Dev plugin configuration." json:"devPlugin,omitempty" toml:"devPlugin,omitempty" yaml:"devPlugin,omitempty"`
|
||||
}
|
||||
|
||||
// Pilot Configuration related to Traefik Pilot.
|
||||
type Pilot struct {
|
||||
Token string `description:"Traefik Pilot token." json:"token,omitempty" toml:"token,omitempty" yaml:"token,omitempty"`
|
||||
}
|
@@ -11,7 +11,9 @@ import (
|
||||
acmeprovider "github.com/containous/traefik/v2/pkg/provider/acme"
|
||||
"github.com/containous/traefik/v2/pkg/provider/consulcatalog"
|
||||
"github.com/containous/traefik/v2/pkg/provider/docker"
|
||||
"github.com/containous/traefik/v2/pkg/provider/ecs"
|
||||
"github.com/containous/traefik/v2/pkg/provider/file"
|
||||
"github.com/containous/traefik/v2/pkg/provider/http"
|
||||
"github.com/containous/traefik/v2/pkg/provider/kubernetes/crd"
|
||||
"github.com/containous/traefik/v2/pkg/provider/kubernetes/ingress"
|
||||
"github.com/containous/traefik/v2/pkg/provider/kv/consul"
|
||||
@@ -57,17 +59,19 @@ type Configuration struct {
|
||||
EntryPoints EntryPoints `description:"Entry points definition." json:"entryPoints,omitempty" toml:"entryPoints,omitempty" yaml:"entryPoints,omitempty" export:"true"`
|
||||
Providers *Providers `description:"Providers configuration." json:"providers,omitempty" toml:"providers,omitempty" yaml:"providers,omitempty" export:"true"`
|
||||
|
||||
API *API `description:"Enable api/dashboard." json:"api,omitempty" toml:"api,omitempty" yaml:"api,omitempty" label:"allowEmpty" export:"true"`
|
||||
API *API `description:"Enable api/dashboard." json:"api,omitempty" toml:"api,omitempty" yaml:"api,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
|
||||
Metrics *types.Metrics `description:"Enable a metrics exporter." json:"metrics,omitempty" toml:"metrics,omitempty" yaml:"metrics,omitempty" export:"true"`
|
||||
Ping *ping.Handler `description:"Enable ping." json:"ping,omitempty" toml:"ping,omitempty" yaml:"ping,omitempty" label:"allowEmpty" export:"true"`
|
||||
Ping *ping.Handler `description:"Enable ping." json:"ping,omitempty" toml:"ping,omitempty" yaml:"ping,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
|
||||
|
||||
Log *types.TraefikLog `description:"Traefik log settings." json:"log,omitempty" toml:"log,omitempty" yaml:"log,omitempty" label:"allowEmpty" export:"true"`
|
||||
AccessLog *types.AccessLog `description:"Access log settings." json:"accessLog,omitempty" toml:"accessLog,omitempty" yaml:"accessLog,omitempty" label:"allowEmpty" export:"true"`
|
||||
Tracing *Tracing `description:"OpenTracing configuration." json:"tracing,omitempty" toml:"tracing,omitempty" yaml:"tracing,omitempty" label:"allowEmpty" export:"true"`
|
||||
Log *types.TraefikLog `description:"Traefik log settings." json:"log,omitempty" toml:"log,omitempty" yaml:"log,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
|
||||
AccessLog *types.AccessLog `description:"Access log settings." json:"accessLog,omitempty" toml:"accessLog,omitempty" yaml:"accessLog,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
|
||||
Tracing *Tracing `description:"OpenTracing configuration." json:"tracing,omitempty" toml:"tracing,omitempty" yaml:"tracing,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
|
||||
|
||||
HostResolver *types.HostResolverConfig `description:"Enable CNAME Flattening." json:"hostResolver,omitempty" toml:"hostResolver,omitempty" yaml:"hostResolver,omitempty" label:"allowEmpty" export:"true"`
|
||||
HostResolver *types.HostResolverConfig `description:"Enable CNAME Flattening." json:"hostResolver,omitempty" toml:"hostResolver,omitempty" yaml:"hostResolver,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
|
||||
|
||||
CertificatesResolvers map[string]CertificateResolver `description:"Certificates resolvers configuration." json:"certificatesResolvers,omitempty" toml:"certificatesResolvers,omitempty" yaml:"certificatesResolvers,omitempty" export:"true"`
|
||||
|
||||
Experimental *Experimental `description:"experimental features." json:"experimental,omitempty" toml:"experimental,omitempty" yaml:"experimental,omitempty"`
|
||||
}
|
||||
|
||||
// CertificateResolver contains the configuration for the different types of certificates resolver.
|
||||
@@ -77,8 +81,8 @@ type CertificateResolver struct {
|
||||
|
||||
// Global holds the global configuration.
|
||||
type Global struct {
|
||||
CheckNewVersion bool `description:"Periodically check if a new version has been released." json:"checkNewVersion,omitempty" toml:"checkNewVersion,omitempty" yaml:"checkNewVersion,omitempty" label:"allowEmpty" export:"true"`
|
||||
SendAnonymousUsage bool `description:"Periodically send anonymous usage statistics. If the option is not specified, it will be enabled by default." json:"sendAnonymousUsage,omitempty" toml:"sendAnonymousUsage,omitempty" yaml:"sendAnonymousUsage,omitempty" label:"allowEmpty" export:"true"`
|
||||
CheckNewVersion bool `description:"Periodically check if a new version has been released." json:"checkNewVersion,omitempty" toml:"checkNewVersion,omitempty" yaml:"checkNewVersion,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
|
||||
SendAnonymousUsage bool `description:"Periodically send anonymous usage statistics. If the option is not specified, it will be enabled by default." json:"sendAnonymousUsage,omitempty" toml:"sendAnonymousUsage,omitempty" yaml:"sendAnonymousUsage,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
|
||||
}
|
||||
|
||||
// ServersTransport options to configure communication between Traefik and the servers.
|
||||
@@ -95,8 +99,8 @@ type API struct {
|
||||
Dashboard bool `description:"Activate dashboard." json:"dashboard,omitempty" toml:"dashboard,omitempty" yaml:"dashboard,omitempty" export:"true"`
|
||||
Debug bool `description:"Enable additional endpoints for debugging and profiling." json:"debug,omitempty" toml:"debug,omitempty" yaml:"debug,omitempty" export:"true"`
|
||||
// TODO: Re-enable statistics
|
||||
// Statistics *types.Statistics `description:"Enable more detailed statistics." json:"statistics,omitempty" toml:"statistics,omitempty" yaml:"statistics,omitempty" export:"true" label:"allowEmpty"`
|
||||
DashboardAssets *assetfs.AssetFS `json:"-" toml:"-" yaml:"-" label:"-"`
|
||||
// Statistics *types.Statistics `description:"Enable more detailed statistics." json:"statistics,omitempty" toml:"statistics,omitempty" yaml:"statistics,omitempty" export:"true" label:"allowEmpty" file:"allowEmpty"`
|
||||
DashboardAssets *assetfs.AssetFS `json:"-" toml:"-" yaml:"-" label:"-" file:"-"`
|
||||
}
|
||||
|
||||
// SetDefaults sets the default values.
|
||||
@@ -144,12 +148,12 @@ func (a *LifeCycle) SetDefaults() {
|
||||
type Tracing struct {
|
||||
ServiceName string `description:"Set the name for this service." json:"serviceName,omitempty" toml:"serviceName,omitempty" yaml:"serviceName,omitempty" export:"true"`
|
||||
SpanNameLimit int `description:"Set the maximum character limit for Span names (default 0 = no limit)." json:"spanNameLimit,omitempty" toml:"spanNameLimit,omitempty" yaml:"spanNameLimit,omitempty" export:"true"`
|
||||
Jaeger *jaeger.Config `description:"Settings for Jaeger." json:"jaeger,omitempty" toml:"jaeger,omitempty" yaml:"jaeger,omitempty" export:"true" label:"allowEmpty"`
|
||||
Zipkin *zipkin.Config `description:"Settings for Zipkin." json:"zipkin,omitempty" toml:"zipkin,omitempty" yaml:"zipkin,omitempty" export:"true" label:"allowEmpty"`
|
||||
Datadog *datadog.Config `description:"Settings for Datadog." json:"datadog,omitempty" toml:"datadog,omitempty" yaml:"datadog,omitempty" export:"true" label:"allowEmpty"`
|
||||
Instana *instana.Config `description:"Settings for Instana." json:"instana,omitempty" toml:"instana,omitempty" yaml:"instana,omitempty" export:"true" label:"allowEmpty"`
|
||||
Haystack *haystack.Config `description:"Settings for Haystack." json:"haystack,omitempty" toml:"haystack,omitempty" yaml:"haystack,omitempty" export:"true" label:"allowEmpty"`
|
||||
Elastic *elastic.Config `description:"Settings for Elastic." json:"elastic,omitempty" toml:"elastic,omitempty" yaml:"elastic,omitempty" export:"true" label:"allowEmpty"`
|
||||
Jaeger *jaeger.Config `description:"Settings for Jaeger." json:"jaeger,omitempty" toml:"jaeger,omitempty" yaml:"jaeger,omitempty" export:"true" label:"allowEmpty" file:"allowEmpty"`
|
||||
Zipkin *zipkin.Config `description:"Settings for Zipkin." json:"zipkin,omitempty" toml:"zipkin,omitempty" yaml:"zipkin,omitempty" export:"true" label:"allowEmpty" file:"allowEmpty"`
|
||||
Datadog *datadog.Config `description:"Settings for Datadog." json:"datadog,omitempty" toml:"datadog,omitempty" yaml:"datadog,omitempty" export:"true" label:"allowEmpty" file:"allowEmpty"`
|
||||
Instana *instana.Config `description:"Settings for Instana." json:"instana,omitempty" toml:"instana,omitempty" yaml:"instana,omitempty" export:"true" label:"allowEmpty" file:"allowEmpty"`
|
||||
Haystack *haystack.Config `description:"Settings for Haystack." json:"haystack,omitempty" toml:"haystack,omitempty" yaml:"haystack,omitempty" export:"true" label:"allowEmpty" file:"allowEmpty"`
|
||||
Elastic *elastic.Config `description:"Settings for Elastic." json:"elastic,omitempty" toml:"elastic,omitempty" yaml:"elastic,omitempty" export:"true" label:"allowEmpty" file:"allowEmpty"`
|
||||
}
|
||||
|
||||
// SetDefaults sets the default values.
|
||||
@@ -162,19 +166,21 @@ func (t *Tracing) SetDefaults() {
|
||||
type Providers struct {
|
||||
ProvidersThrottleDuration types.Duration `description:"Backends throttle duration: minimum duration 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." json:"providersThrottleDuration,omitempty" toml:"providersThrottleDuration,omitempty" yaml:"providersThrottleDuration,omitempty" export:"true"`
|
||||
|
||||
Docker *docker.Provider `description:"Enable Docker backend with default settings." json:"docker,omitempty" toml:"docker,omitempty" yaml:"docker,omitempty" export:"true" label:"allowEmpty"`
|
||||
Docker *docker.Provider `description:"Enable Docker backend with default settings." json:"docker,omitempty" toml:"docker,omitempty" yaml:"docker,omitempty" export:"true" label:"allowEmpty" file:"allowEmpty"`
|
||||
File *file.Provider `description:"Enable File backend with default settings." json:"file,omitempty" toml:"file,omitempty" yaml:"file,omitempty" export:"true"`
|
||||
Marathon *marathon.Provider `description:"Enable Marathon backend with default settings." json:"marathon,omitempty" toml:"marathon,omitempty" yaml:"marathon,omitempty" export:"true" label:"allowEmpty"`
|
||||
KubernetesIngress *ingress.Provider `description:"Enable Kubernetes backend with default settings." json:"kubernetesIngress,omitempty" toml:"kubernetesIngress,omitempty" yaml:"kubernetesIngress,omitempty" export:"true" label:"allowEmpty"`
|
||||
KubernetesCRD *crd.Provider `description:"Enable Kubernetes backend with default settings." json:"kubernetesCRD,omitempty" toml:"kubernetesCRD,omitempty" yaml:"kubernetesCRD,omitempty" export:"true" label:"allowEmpty"`
|
||||
Rest *rest.Provider `description:"Enable Rest backend with default settings." json:"rest,omitempty" toml:"rest,omitempty" yaml:"rest,omitempty" export:"true" label:"allowEmpty"`
|
||||
Rancher *rancher.Provider `description:"Enable Rancher backend with default settings." json:"rancher,omitempty" toml:"rancher,omitempty" yaml:"rancher,omitempty" export:"true" label:"allowEmpty"`
|
||||
Marathon *marathon.Provider `description:"Enable Marathon backend with default settings." json:"marathon,omitempty" toml:"marathon,omitempty" yaml:"marathon,omitempty" export:"true" label:"allowEmpty" file:"allowEmpty"`
|
||||
KubernetesIngress *ingress.Provider `description:"Enable Kubernetes backend with default settings." json:"kubernetesIngress,omitempty" toml:"kubernetesIngress,omitempty" yaml:"kubernetesIngress,omitempty" export:"true" label:"allowEmpty" file:"allowEmpty"`
|
||||
KubernetesCRD *crd.Provider `description:"Enable Kubernetes backend with default settings." json:"kubernetesCRD,omitempty" toml:"kubernetesCRD,omitempty" yaml:"kubernetesCRD,omitempty" export:"true" label:"allowEmpty" file:"allowEmpty"`
|
||||
Rest *rest.Provider `description:"Enable Rest backend with default settings." json:"rest,omitempty" toml:"rest,omitempty" yaml:"rest,omitempty" export:"true" label:"allowEmpty" file:"allowEmpty"`
|
||||
Rancher *rancher.Provider `description:"Enable Rancher backend with default settings." json:"rancher,omitempty" toml:"rancher,omitempty" yaml:"rancher,omitempty" export:"true" label:"allowEmpty" file:"allowEmpty"`
|
||||
ConsulCatalog *consulcatalog.Provider `description:"Enable ConsulCatalog backend with default settings." json:"consulCatalog,omitempty" toml:"consulCatalog,omitempty" yaml:"consulCatalog,omitempty"`
|
||||
Ecs *ecs.Provider `description:"Enable AWS ECS backend with default settings." json:"ecs,omitempty" toml:"ecs,omitempty" yaml:"ecs,omitempty"`
|
||||
|
||||
Consul *consul.Provider `description:"Enable Consul backend with default settings." json:"consul,omitempty" toml:"consul,omitempty" yaml:"consul,omitempty" export:"true" label:"allowEmpty"`
|
||||
Etcd *etcd.Provider `description:"Enable Etcd backend with default settings." json:"etcd,omitempty" toml:"etcd,omitempty" yaml:"etcd,omitempty" export:"true" label:"allowEmpty"`
|
||||
ZooKeeper *zk.Provider `description:"Enable ZooKeeper backend with default settings." json:"zooKeeper,omitempty" toml:"zooKeeper,omitempty" yaml:"zooKeeper,omitempty" export:"true" label:"allowEmpty"`
|
||||
Redis *redis.Provider `description:"Enable Redis backend with default settings." json:"redis,omitempty" toml:"redis,omitempty" yaml:"redis,omitempty" export:"true" label:"allowEmpty"`
|
||||
Consul *consul.Provider `description:"Enable Consul backend with default settings." json:"consul,omitempty" toml:"consul,omitempty" yaml:"consul,omitempty" export:"true" label:"allowEmpty" file:"allowEmpty"`
|
||||
Etcd *etcd.Provider `description:"Enable Etcd backend with default settings." json:"etcd,omitempty" toml:"etcd,omitempty" yaml:"etcd,omitempty" export:"true" label:"allowEmpty" file:"allowEmpty"`
|
||||
ZooKeeper *zk.Provider `description:"Enable ZooKeeper backend with default settings." json:"zooKeeper,omitempty" toml:"zooKeeper,omitempty" yaml:"zooKeeper,omitempty" export:"true" label:"allowEmpty" file:"allowEmpty"`
|
||||
Redis *redis.Provider `description:"Enable Redis backend with default settings." json:"redis,omitempty" toml:"redis,omitempty" yaml:"redis,omitempty" export:"true" label:"allowEmpty" file:"allowEmpty"`
|
||||
HTTP *http.Provider `description:"Enable HTTP backend with default settings." json:"http,omitempty" toml:"http,omitempty" yaml:"http,omitempty" export:"true" label:"allowEmpty" file:"allowEmpty"`
|
||||
}
|
||||
|
||||
// SetEffectiveConfiguration adds missing configuration parameters derived from existing ones.
|
||||
|
@@ -448,9 +448,9 @@ func TestLBStatusUpdater(t *testing.T) {
|
||||
svInfo := &runtime.ServiceInfo{}
|
||||
lbsu := NewLBStatusUpdater(lb, svInfo)
|
||||
newServer, err := url.Parse("http://foo.com")
|
||||
assert.Nil(t, err)
|
||||
assert.NoError(t, err)
|
||||
err = lbsu.UpsertServer(newServer, roundrobin.Weight(1))
|
||||
assert.Nil(t, err)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, len(lbsu.Servers()), 1)
|
||||
assert.Equal(t, len(lbsu.BalancerHandler.(*testLoadBalancer).Options()), 1)
|
||||
statuses := svInfo.GetAllStatus()
|
||||
@@ -461,7 +461,7 @@ func TestLBStatusUpdater(t *testing.T) {
|
||||
break
|
||||
}
|
||||
err = lbsu.RemoveServer(newServer)
|
||||
assert.Nil(t, err)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, len(lbsu.Servers()), 0)
|
||||
statuses = svInfo.GetAllStatus()
|
||||
assert.Equal(t, len(statuses), 1)
|
||||
|
@@ -55,7 +55,7 @@ func New(ctx context.Context, next http.Handler, cfg dynamic.Headers, name strin
|
||||
|
||||
if hasSecureHeaders {
|
||||
logger.Debugf("Setting up secureHeaders from %v", cfg)
|
||||
handler = newSecure(next, cfg)
|
||||
handler = newSecure(next, cfg, name)
|
||||
nextHandler = handler
|
||||
}
|
||||
|
||||
@@ -84,7 +84,7 @@ type secureHeader struct {
|
||||
}
|
||||
|
||||
// newSecure constructs a new secure instance with supplied options.
|
||||
func newSecure(next http.Handler, cfg dynamic.Headers) *secureHeader {
|
||||
func newSecure(next http.Handler, cfg dynamic.Headers, contextKey string) *secureHeader {
|
||||
opt := secure.Options{
|
||||
BrowserXssFilter: cfg.BrowserXSSFilter,
|
||||
ContentTypeNosniff: cfg.ContentTypeNosniff,
|
||||
@@ -107,6 +107,7 @@ func newSecure(next http.Handler, cfg dynamic.Headers) *secureHeader {
|
||||
SSLProxyHeaders: cfg.SSLProxyHeaders,
|
||||
STSSeconds: cfg.STSSeconds,
|
||||
FeaturePolicy: cfg.FeaturePolicy,
|
||||
SecureContextKey: contextKey,
|
||||
}
|
||||
|
||||
return &secureHeader{
|
||||
|
@@ -167,7 +167,9 @@ func TestSSLForceHost(t *testing.T) {
|
||||
SSLRedirect: true,
|
||||
SSLForceHost: true,
|
||||
SSLHost: "powpow.example.com",
|
||||
}),
|
||||
},
|
||||
"mymiddleware",
|
||||
),
|
||||
expected: http.StatusMovedPermanently,
|
||||
},
|
||||
{
|
||||
@@ -177,7 +179,9 @@ func TestSSLForceHost(t *testing.T) {
|
||||
SSLRedirect: true,
|
||||
SSLForceHost: true,
|
||||
SSLHost: "powpow.example.com",
|
||||
}),
|
||||
},
|
||||
"mymiddleware",
|
||||
),
|
||||
expected: http.StatusMovedPermanently,
|
||||
},
|
||||
{
|
||||
@@ -187,7 +191,9 @@ func TestSSLForceHost(t *testing.T) {
|
||||
SSLRedirect: true,
|
||||
SSLForceHost: true,
|
||||
SSLHost: "powpow.example.com",
|
||||
}),
|
||||
},
|
||||
"mymiddleware",
|
||||
),
|
||||
expected: http.StatusOK,
|
||||
},
|
||||
{
|
||||
@@ -197,7 +203,9 @@ func TestSSLForceHost(t *testing.T) {
|
||||
SSLRedirect: true,
|
||||
SSLForceHost: true,
|
||||
SSLHost: "powpow.example.com",
|
||||
}),
|
||||
},
|
||||
"mymiddleware",
|
||||
),
|
||||
expected: http.StatusMovedPermanently,
|
||||
},
|
||||
{
|
||||
@@ -207,7 +215,9 @@ func TestSSLForceHost(t *testing.T) {
|
||||
SSLRedirect: true,
|
||||
SSLForceHost: false,
|
||||
SSLHost: "powpow.example.com",
|
||||
}),
|
||||
},
|
||||
"mymiddleware",
|
||||
),
|
||||
expected: http.StatusMovedPermanently,
|
||||
},
|
||||
{
|
||||
@@ -217,7 +227,9 @@ func TestSSLForceHost(t *testing.T) {
|
||||
SSLRedirect: true,
|
||||
SSLForceHost: false,
|
||||
SSLHost: "powpow.example.com",
|
||||
}),
|
||||
},
|
||||
"mymiddleware",
|
||||
),
|
||||
expected: http.StatusOK,
|
||||
},
|
||||
}
|
||||
|
246
pkg/pilot/pilot.go
Normal file
246
pkg/pilot/pilot.go
Normal file
@@ -0,0 +1,246 @@
|
||||
package pilot
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/cenkalti/backoff/v4"
|
||||
"github.com/containous/traefik/v2/pkg/config/runtime"
|
||||
"github.com/containous/traefik/v2/pkg/log"
|
||||
"github.com/containous/traefik/v2/pkg/safe"
|
||||
"github.com/containous/traefik/v2/pkg/version"
|
||||
)
|
||||
|
||||
const baseURL = "https://instance-info.pilot.traefik.io/public"
|
||||
|
||||
const tokenHeader = "X-Token"
|
||||
|
||||
const (
|
||||
pilotTimer = 5 * time.Minute
|
||||
maxElapsedTime = 4 * time.Minute
|
||||
)
|
||||
|
||||
// RunTimeRepresentation is the configuration information exposed by the API handler.
|
||||
type RunTimeRepresentation struct {
|
||||
Routers map[string]*runtime.RouterInfo `json:"routers,omitempty"`
|
||||
Middlewares map[string]*runtime.MiddlewareInfo `json:"middlewares,omitempty"`
|
||||
Services map[string]*serviceInfoRepresentation `json:"services,omitempty"`
|
||||
TCPRouters map[string]*runtime.TCPRouterInfo `json:"tcpRouters,omitempty"`
|
||||
TCPServices map[string]*runtime.TCPServiceInfo `json:"tcpServices,omitempty"`
|
||||
UDPRouters map[string]*runtime.UDPRouterInfo `json:"udpRouters,omitempty"`
|
||||
UDPServices map[string]*runtime.UDPServiceInfo `json:"udpServices,omitempty"`
|
||||
}
|
||||
|
||||
type serviceInfoRepresentation struct {
|
||||
*runtime.ServiceInfo
|
||||
ServerStatus map[string]string `json:"serverStatus,omitempty"`
|
||||
}
|
||||
|
||||
type instanceInfo struct {
|
||||
ID string `json:"id,omitempty"`
|
||||
Configuration RunTimeRepresentation `json:"configuration,omitempty"`
|
||||
}
|
||||
|
||||
// New creates a new Pilot.
|
||||
func New(token string, pool *safe.Pool) *Pilot {
|
||||
return &Pilot{
|
||||
rtConfChan: make(chan *runtime.Configuration),
|
||||
client: &client{
|
||||
token: token,
|
||||
httpClient: &http.Client{Timeout: 5 * time.Second},
|
||||
baseURL: baseURL,
|
||||
},
|
||||
routinesPool: pool,
|
||||
}
|
||||
}
|
||||
|
||||
// Pilot connector with Pilot.
|
||||
type Pilot struct {
|
||||
routinesPool *safe.Pool
|
||||
client *client
|
||||
|
||||
rtConf *runtime.Configuration
|
||||
rtConfChan chan *runtime.Configuration
|
||||
}
|
||||
|
||||
// SetRuntimeConfiguration stores the runtime configuration.
|
||||
func (p *Pilot) SetRuntimeConfiguration(rtConf *runtime.Configuration) {
|
||||
p.rtConfChan <- rtConf
|
||||
}
|
||||
|
||||
func (p *Pilot) getRepresentation() RunTimeRepresentation {
|
||||
if p.rtConf == nil {
|
||||
return RunTimeRepresentation{}
|
||||
}
|
||||
|
||||
siRepr := make(map[string]*serviceInfoRepresentation, len(p.rtConf.Services))
|
||||
for k, v := range p.rtConf.Services {
|
||||
siRepr[k] = &serviceInfoRepresentation{
|
||||
ServiceInfo: v,
|
||||
ServerStatus: v.GetAllStatus(),
|
||||
}
|
||||
}
|
||||
|
||||
result := RunTimeRepresentation{
|
||||
Routers: p.rtConf.Routers,
|
||||
Middlewares: p.rtConf.Middlewares,
|
||||
Services: siRepr,
|
||||
TCPRouters: p.rtConf.TCPRouters,
|
||||
TCPServices: p.rtConf.TCPServices,
|
||||
UDPRouters: p.rtConf.UDPRouters,
|
||||
UDPServices: p.rtConf.UDPServices,
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func (p *Pilot) sendData(ctx context.Context, conf RunTimeRepresentation) {
|
||||
err := p.client.SendData(ctx, conf)
|
||||
if err != nil {
|
||||
log.WithoutContext().Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
// Tick sends data periodically.
|
||||
func (p *Pilot) Tick(ctx context.Context) {
|
||||
select {
|
||||
case rtConf := <-p.rtConfChan:
|
||||
p.rtConf = rtConf
|
||||
break
|
||||
case <-ctx.Done():
|
||||
return
|
||||
}
|
||||
|
||||
conf := p.getRepresentation()
|
||||
|
||||
p.routinesPool.GoCtx(func(ctxRt context.Context) {
|
||||
p.sendData(ctxRt, conf)
|
||||
})
|
||||
|
||||
ticker := time.NewTicker(pilotTimer)
|
||||
for {
|
||||
select {
|
||||
case tick := <-ticker.C:
|
||||
log.WithoutContext().Debugf("Send to pilot: %s", tick)
|
||||
|
||||
conf := p.getRepresentation()
|
||||
|
||||
p.routinesPool.GoCtx(func(ctxRt context.Context) {
|
||||
p.sendData(ctxRt, conf)
|
||||
})
|
||||
case rtConf := <-p.rtConfChan:
|
||||
p.rtConf = rtConf
|
||||
case <-ctx.Done():
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type client struct {
|
||||
httpClient *http.Client
|
||||
baseURL string
|
||||
token string
|
||||
uuid string
|
||||
}
|
||||
|
||||
func (c *client) createUUID() (string, error) {
|
||||
data := []byte(`{"version":"` + version.Version + `","codeName":"` + version.Codename + `"}`)
|
||||
req, err := http.NewRequest(http.MethodPost, c.baseURL+"/", bytes.NewBuffer(data))
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to create request: %w", err)
|
||||
}
|
||||
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
req.Header.Set(tokenHeader, c.token)
|
||||
|
||||
resp, err := c.httpClient.Do(req)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed call Pilot: %w", err)
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed read response body: %w", err)
|
||||
}
|
||||
|
||||
if resp.StatusCode/100 != 2 {
|
||||
return "", fmt.Errorf("wrong status code while sending configuration: %d: %s", resp.StatusCode, body)
|
||||
}
|
||||
|
||||
created := instanceInfo{}
|
||||
err = json.Unmarshal(body, &created)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to unmarshal response body: %w", err)
|
||||
}
|
||||
|
||||
return created.ID, nil
|
||||
}
|
||||
|
||||
// SendData sends data to Pilot.
|
||||
func (c *client) SendData(ctx context.Context, rtConf RunTimeRepresentation) error {
|
||||
exponentialBackOff := backoff.NewExponentialBackOff()
|
||||
exponentialBackOff.MaxElapsedTime = maxElapsedTime
|
||||
|
||||
return backoff.RetryNotify(
|
||||
func() error {
|
||||
return c.sendData(rtConf)
|
||||
},
|
||||
backoff.WithContext(exponentialBackOff, ctx),
|
||||
func(err error, duration time.Duration) {
|
||||
log.WithoutContext().Errorf("retry in %s due to: %v ", duration, err)
|
||||
})
|
||||
}
|
||||
|
||||
func (c *client) sendData(_ RunTimeRepresentation) error {
|
||||
if len(c.uuid) == 0 {
|
||||
var err error
|
||||
c.uuid, err = c.createUUID()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create UUID: %w", err)
|
||||
}
|
||||
|
||||
version.UUID = c.uuid
|
||||
}
|
||||
|
||||
info := instanceInfo{
|
||||
ID: c.uuid,
|
||||
}
|
||||
|
||||
b, err := json.Marshal(info)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to marshall request body: %w", err)
|
||||
}
|
||||
|
||||
request, err := http.NewRequest(http.MethodPost, c.baseURL+"/command", bytes.NewBuffer(b))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create request: %w", err)
|
||||
}
|
||||
|
||||
request.Header.Set("Content-Type", "application/json")
|
||||
request.Header.Set(tokenHeader, c.token)
|
||||
|
||||
resp, err := c.httpClient.Do(request)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to call Pilot: %w", err)
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read response body: %w", err)
|
||||
}
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return fmt.Errorf("wrong status code while sending configuration: %d: %s", resp.StatusCode, body)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
123
pkg/pilot/pilot_test.go
Normal file
123
pkg/pilot/pilot_test.go
Normal file
@@ -0,0 +1,123 @@
|
||||
package pilot
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/containous/traefik/v2/pkg/config/runtime"
|
||||
"github.com/containous/traefik/v2/pkg/safe"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestTick(t *testing.T) {
|
||||
receivedConfig := make(chan bool)
|
||||
|
||||
mux := http.NewServeMux()
|
||||
server := httptest.NewServer(mux)
|
||||
t.Cleanup(server.Close)
|
||||
|
||||
mux.HandleFunc("/", func(rw http.ResponseWriter, req *http.Request) {
|
||||
if req.Method != http.MethodPost {
|
||||
http.Error(rw, "invalid method", http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
|
||||
err := json.NewEncoder(rw).Encode(instanceInfo{ID: "123"})
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
})
|
||||
|
||||
mux.HandleFunc("/command", func(rw http.ResponseWriter, req *http.Request) {
|
||||
if req.Method != http.MethodPost {
|
||||
http.Error(rw, "invalid method", http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
|
||||
receivedConfig <- true
|
||||
})
|
||||
|
||||
pilot := New("token", safe.NewPool(context.Background()))
|
||||
pilot.client.baseURL = server.URL
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
t.Cleanup(cancel)
|
||||
|
||||
go pilot.Tick(ctx)
|
||||
|
||||
pilot.SetRuntimeConfiguration(&runtime.Configuration{})
|
||||
pilot.SetRuntimeConfiguration(&runtime.Configuration{})
|
||||
|
||||
select {
|
||||
case <-time.Tick(10 * time.Second):
|
||||
t.Fatal("Timeout")
|
||||
case <-receivedConfig:
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func TestClient_SendConfiguration(t *testing.T) {
|
||||
myToken := "myToken"
|
||||
|
||||
mux := http.NewServeMux()
|
||||
server := httptest.NewServer(mux)
|
||||
t.Cleanup(server.Close)
|
||||
|
||||
mux.HandleFunc("/", func(rw http.ResponseWriter, req *http.Request) {
|
||||
if req.Method != http.MethodPost {
|
||||
http.Error(rw, "invalid method", http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
|
||||
tk := req.Header.Get(tokenHeader)
|
||||
if tk != myToken {
|
||||
http.Error(rw, fmt.Sprintf("invalid token: %s", tk), http.StatusUnauthorized)
|
||||
}
|
||||
|
||||
err := json.NewEncoder(rw).Encode(instanceInfo{ID: "123"})
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
})
|
||||
|
||||
mux.HandleFunc("/command", func(rw http.ResponseWriter, req *http.Request) {
|
||||
if req.Method != http.MethodPost {
|
||||
http.Error(rw, "invalid method", http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
|
||||
tk := req.Header.Get(tokenHeader)
|
||||
if tk != myToken {
|
||||
http.Error(rw, fmt.Sprintf("invalid token: %s", tk), http.StatusUnauthorized)
|
||||
}
|
||||
|
||||
defer req.Body.Close()
|
||||
|
||||
info := &instanceInfo{}
|
||||
err := json.NewDecoder(req.Body).Decode(info)
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
if info.ID != "123" {
|
||||
http.Error(rw, fmt.Sprintf("invalid ID: %s", info.ID), http.StatusBadRequest)
|
||||
}
|
||||
})
|
||||
|
||||
client := client{
|
||||
baseURL: server.URL,
|
||||
httpClient: http.DefaultClient,
|
||||
token: myToken,
|
||||
}
|
||||
|
||||
err := client.SendData(context.Background(), RunTimeRepresentation{})
|
||||
require.NoError(t, err)
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user