diff --git a/.golangci.toml b/.golangci.toml
index 14307c954..9e939601c 100644
--- a/.golangci.toml
+++ b/.golangci.toml
@@ -54,6 +54,7 @@
"nestif", # Too many false-positive.
"noctx", # Too strict
"exhaustive", # Too strict
+ "nlreturn", # Too strict
]
[issues]
diff --git a/.semaphoreci/setup.sh b/.semaphoreci/setup.sh
index b7574b1fd..5cf007dc2 100755
--- a/.semaphoreci/setup.sh
+++ b/.semaphoreci/setup.sh
@@ -20,7 +20,7 @@ echo ${SHOULD_TEST}
if [ -n "$SHOULD_TEST" ]; then docker version; fi
export GO_VERSION=1.13
if [ -f "./go.mod" ]; then GO_VERSION="$(grep '^go .*' go.mod | awk '{print $2}')"; export GO_VERSION; fi
-#if [ "${GO_VERSION}" == '1.14' ]; then export GO_VERSION=1.14rc2; fi
+#if [ "${GO_VERSION}" == '1.15' ]; then export GO_VERSION=1.15rc2; fi
echo "Selected Go version: ${GO_VERSION}"
if [ -f "./.semaphoreci/golang.sh" ]; then ./.semaphoreci/golang.sh; fi
diff --git a/.travis.yml b/.travis.yml
index cc4435910..e09f8b802 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -30,7 +30,9 @@ before_deploy:
make release-packages;
fi;
curl -sfL https://raw.githubusercontent.com/containous/structor/master/godownloader.sh | bash -s -- -b "${GOPATH}/bin" ${STRUCTOR_VERSION}
+ curl -sSfL https://raw.githubusercontent.com/traefik/mixtus/master/godownloader.sh | sh -s -- -b "${GOPATH}/bin" ${MIXTUS_VERSION}
structor -o containous -r traefik --dockerfile-url="https://raw.githubusercontent.com/containous/traefik/v1.7/docs.Dockerfile" --menu.js-url="https://raw.githubusercontent.com/containous/structor/master/traefik-menu.js.gotmpl" --rqts-url="https://raw.githubusercontent.com/containous/structor/master/requirements-override.txt" --force-edit-url --exp-branch=master --debug;
+ mixtus --dst-doc-path="./traefik" --dst-owner=traefik --dst-repo-name=doc --git-user-email="30906710+traefiker@users.noreply.github.com" --git-user-name=traefiker --src-doc-path="./site" --src-owner=containous --src-repo-name=traefik;
fi
deploy:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index d193e1a66..671055716 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,20 @@
+## [v2.3.0-rc4](https://github.com/containous/traefik/tree/v2.3.0-rc4) (2020-08-19)
+[All Commits](https://github.com/containous/traefik/compare/v2.3.0-rc3...v2.3.0-rc4)
+
+**Enhancements:**
+- **[metrics,pilot]** Pilot metrics provider ([#7139](https://github.com/containous/traefik/pull/7139) by [rtribotte](https://github.com/rtribotte))
+
+**Bug fixes:**
+- **[ecs]** Improve region resolution for ECS provider ([#7145](https://github.com/containous/traefik/pull/7145) by [kevinpollet](https://github.com/kevinpollet))
+- **[tracing]** Update jaeger-client-go dependency to v2.25.0 ([#7198](https://github.com/containous/traefik/pull/7198) by [kevinpollet](https://github.com/kevinpollet))
+
+**Documentation:**
+- **[k8s]** docs: add missing apigroup to Kubernetes RBAC ([#7199](https://github.com/containous/traefik/pull/7199) by [kevinpollet](https://github.com/kevinpollet))
+
+**Misc:**
+- Merge current v2.2 branch into v2.3 ([#7175](https://github.com/containous/traefik/pull/7175) by [ldez](https://github.com/ldez))
+- Merge current v2.2 branch into v2.3 ([#7160](https://github.com/containous/traefik/pull/7160) by [ldez](https://github.com/ldez))
+
## [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)
diff --git a/build.Dockerfile b/build.Dockerfile
index 497663beb..b0999f841 100644
--- a/build.Dockerfile
+++ b/build.Dockerfile
@@ -1,4 +1,4 @@
-FROM golang:1.14-alpine
+FROM golang:1.15-alpine
RUN apk --update upgrade \
&& apk --no-cache --no-progress add git mercurial bash gcc musl-dev curl tar ca-certificates tzdata \
@@ -19,7 +19,7 @@ RUN mkdir -p /usr/local/bin \
&& chmod +x /usr/local/bin/go-bindata
# Download golangci-lint binary to bin folder in $GOPATH
-RUN curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | bash -s -- -b $GOPATH/bin v1.28.0
+RUN curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | bash -s -- -b $GOPATH/bin v1.30.0
# Download misspell binary to bin folder in $GOPATH
RUN curl -sfL https://raw.githubusercontent.com/client9/misspell/master/install-misspell.sh | bash -s -- -b $GOPATH/bin v0.3.4
diff --git a/cmd/configuration.go b/cmd/configuration.go
index 31e730e12..fe8e1b79f 100644
--- a/cmd/configuration.go
+++ b/cmd/configuration.go
@@ -4,7 +4,7 @@ import (
"time"
"github.com/containous/traefik/v2/pkg/config/static"
- "github.com/containous/traefik/v2/pkg/types"
+ ptypes "github.com/traefik/paerser/types"
)
// TraefikCmdConfiguration wraps the static configuration and extra parameters.
@@ -23,7 +23,7 @@ func NewTraefikConfiguration() *TraefikCmdConfiguration {
},
EntryPoints: make(static.EntryPoints),
Providers: &static.Providers{
- ProvidersThrottleDuration: types.Duration(2 * time.Second),
+ ProvidersThrottleDuration: ptypes.Duration(2 * time.Second),
},
ServersTransport: &static.ServersTransport{
MaxIdleConnsPerHost: 200,
diff --git a/cmd/healthcheck/healthcheck.go b/cmd/healthcheck/healthcheck.go
index d71f01281..1360673b7 100644
--- a/cmd/healthcheck/healthcheck.go
+++ b/cmd/healthcheck/healthcheck.go
@@ -7,8 +7,8 @@ import (
"os"
"time"
- "github.com/containous/traefik/v2/pkg/cli"
"github.com/containous/traefik/v2/pkg/config/static"
+ "github.com/traefik/paerser/cli"
)
// NewCmd builds a new HealthCheck command.
diff --git a/cmd/traefik/traefik.go b/cmd/traefik/traefik.go
index cb34c7fc5..bb6ff11b2 100644
--- a/cmd/traefik/traefik.go
+++ b/cmd/traefik/traefik.go
@@ -15,7 +15,7 @@ import (
"github.com/containous/traefik/v2/cmd"
"github.com/containous/traefik/v2/cmd/healthcheck"
cmdVersion "github.com/containous/traefik/v2/cmd/version"
- "github.com/containous/traefik/v2/pkg/cli"
+ tcli "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"
@@ -38,6 +38,7 @@ import (
"github.com/coreos/go-systemd/daemon"
assetfs "github.com/elazarl/go-bindata-assetfs"
"github.com/sirupsen/logrus"
+ "github.com/traefik/paerser/cli"
"github.com/vulcand/oxy/roundrobin"
)
@@ -45,7 +46,7 @@ func main() {
// traefik config inits
tConfig := cmd.NewTraefikConfiguration()
- loaders := []cli.ResourceLoader{&cli.FileLoader{}, &cli.FlagLoader{}, &cli.EnvLoader{}}
+ loaders := []cli.ResourceLoader{&tcli.FileLoader{}, &tcli.FlagLoader{}, &tcli.EnvLoader{}}
cmdTraefik := &cli.Command{
Name: "traefik",
@@ -195,7 +196,21 @@ func setupServer(staticConfiguration *static.Configuration) (*server.Server, err
ctx := context.Background()
routinesPool := safe.NewPool(ctx)
- metricsRegistry := registerMetricClients(staticConfiguration.Metrics)
+ metricRegistries := registerMetricClients(staticConfiguration.Metrics)
+
+ var aviator *pilot.Pilot
+ if isPilotEnabled(staticConfiguration) {
+ pilotRegistry := metrics.RegisterPilot()
+
+ aviator = pilot.New(staticConfiguration.Experimental.Pilot.Token, pilotRegistry, routinesPool)
+ routinesPool.GoCtx(func(ctx context.Context) {
+ aviator.Tick(ctx)
+ })
+
+ metricRegistries = append(metricRegistries, pilotRegistry)
+ }
+
+ metricsRegistry := metrics.NewMultiRegistry(metricRegistries)
accessLog := setupAccessLog(staticConfiguration.AccessLog)
chainBuilder := middleware.NewChainBuilder(*staticConfiguration, metricsRegistry, accessLog)
managerFactory := service.NewManagerFactory(*staticConfiguration, routinesPool, metricsRegistry)
@@ -244,15 +259,6 @@ func setupServer(staticConfiguration *static.Configuration) (*server.Server, err
metricsRegistry.LastConfigReloadSuccessGauge().Set(float64(time.Now().Unix()))
})
- 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) {
@@ -349,9 +355,9 @@ func initACMEProvider(c *static.Configuration, providerAggregator *aggregator.Pr
return resolvers
}
-func registerMetricClients(metricsConfig *types.Metrics) metrics.Registry {
+func registerMetricClients(metricsConfig *types.Metrics) []metrics.Registry {
if metricsConfig == nil {
- return metrics.NewVoidRegistry()
+ return nil
}
var registries []metrics.Registry
@@ -386,7 +392,7 @@ func registerMetricClients(metricsConfig *types.Metrics) metrics.Registry {
metricsConfig.InfluxDB.Address, metricsConfig.InfluxDB.PushInterval)
}
- return metrics.NewMultiRegistry(registries)
+ return registries
}
func setupAccessLog(conf *types.AccessLog) *accesslog.Handler {
diff --git a/cmd/version/version.go b/cmd/version/version.go
index 3b917086e..2bbd802ca 100644
--- a/cmd/version/version.go
+++ b/cmd/version/version.go
@@ -7,8 +7,8 @@ import (
"runtime"
"text/template"
- "github.com/containous/traefik/v2/pkg/cli"
"github.com/containous/traefik/v2/pkg/version"
+ "github.com/traefik/paerser/cli"
)
var versionTemplate = `Version: {{.Version}}
diff --git a/docs/content/assets/img/traefik.logo.horizontal.png b/docs/content/assets/img/traefik.logo.horizontal.png
new file mode 100644
index 000000000..0e440c37a
Binary files /dev/null and b/docs/content/assets/img/traefik.logo.horizontal.png differ
diff --git a/docs/content/assets/styles/content.css b/docs/content/assets/styles/content.css
new file mode 100644
index 000000000..ba0d24e0d
--- /dev/null
+++ b/docs/content/assets/styles/content.css
@@ -0,0 +1,70 @@
+.md-container {
+ padding-top: 0;
+}
+
+.md-content h1 {
+ color: var(--dark) !important;
+ font-weight: bold !important;
+}
+
+.md-content a {
+ color: var(--blue) !important;
+}
+
+.md-content a:hover {
+ font-weight: bold !important;
+}
+
+.md-typeset p code,
+.md-typeset .codehilite,
+.md-typeset .highlight {
+ background-color: var(--light-blue) !important;
+}
+
+.md-typeset table:not([class]) th {
+ background: var(--dark) !important;
+ color: white !important;
+}
+
+/* Front page image size */
+img[src$='#small'] {
+ width: 150px;
+}
+img[src$='#medium'] {
+ width: 300px;
+}
+
+/* Center table and objects */
+.center,
+img,
+.md-typeset__table {
+ display: block !important;
+ margin: 0 auto;
+}
+
+.md-typeset table:not([class]) tr td:first-child {
+ text-align: left;
+}
+.md-typeset table:not([class]) th:not([align]),
+.md-typeset table:not([class]) td:not([align]) {
+ text-align: center;
+}
+article p:not([class]),
+article ul:not([class]),
+article ol:not([class]) {
+ padding-left: 0.8em !important;
+}
+
+/* Fix for Chrome */
+.md-typeset__table td code {
+ word-break: unset;
+}
+
+.md-typeset__table tr :nth-child(1) {
+ word-wrap: break-word;
+ max-width: 30em;
+}
+
+p {
+ text-align: justify;
+}
\ No newline at end of file
diff --git a/docs/content/assets/styles/extra.css b/docs/content/assets/styles/extra.css
deleted file mode 100644
index f865034b0..000000000
--- a/docs/content/assets/styles/extra.css
+++ /dev/null
@@ -1,63 +0,0 @@
-@import url('https://fonts.googleapis.com/css?family=Noto+Sans|Noto+Serif');
-
-.md-logo img {
- background-color: white;
- border-radius: 50%;
- width: 30px;
- height: 30px;
-}
-
-/* Fix for Chrome */
-.md-typeset__table td code {
- word-break: unset;
-}
-
-.md-typeset__table tr :nth-child(1) {
- word-wrap: break-word;
- max-width: 30em;
-}
-
-body {
- font-family: 'Noto Sans', sans-serif;
-}
-
-h1 {
- font-weight: bold !important;
- color: rgba(0,0,0,.9) !important;
-}
-
-h2 {
- font-weight: bold !important;
-}
-
-h3 {
- font-weight: bold !important;
-}
-
-.md-typeset h5 {
- text-transform: none;
-}
-
-figcaption {
- text-align: center;
- font-size: 0.8em;
- font-style: italic;
- color: #8D909F;
-}
-
-p.subtitle {
- color: rgba(0,0,0,.54);
- padding-top: 0;
- margin-top: -2em;
- font-weight: bold;
- font-size: 1.25em;
-}
-
-.markdown-body .task-list-item {
- list-style-type: none !important;
-}
-
-.markdown-body .task-list-item input[type="checkbox"] {
- margin: 0 4px 0.25em -20px;
- vertical-align: middle;
-}
\ No newline at end of file
diff --git a/docs/content/assets/styles/footer.css b/docs/content/assets/styles/footer.css
new file mode 100644
index 000000000..d71e88262
--- /dev/null
+++ b/docs/content/assets/styles/footer.css
@@ -0,0 +1,10 @@
+.md-footer-meta {
+ background-color: var(--dark);
+}
+
+.md-footer-privacy-policy {
+ margin: 0 .6rem;
+ padding: .4rem 0;
+ color: hsla(0,0%,100%,.3);
+ font-size: .64rem;
+}
\ No newline at end of file
diff --git a/docs/content/assets/styles/header.css b/docs/content/assets/styles/header.css
new file mode 100644
index 000000000..5c539a5ca
--- /dev/null
+++ b/docs/content/assets/styles/header.css
@@ -0,0 +1,462 @@
+@import url('https://fonts.googleapis.com/css?family=Rubik:300i,400,400i,500,500i,700&display=swap');
+
+.wrapper-1200 {
+ width: 100%;
+ max-width: 61rem;
+ margin: 0 auto;
+ padding: 0 .6rem;
+}
+
+@media (max-width: 700px) {
+ .wrapper-1200 {
+ padding: 0 20px;
+ }
+}
+
+.btn-type-1 {
+ outline: none;
+ border: none;
+ background-color: #1e54d5;
+ line-height: 1em;
+ border-radius: 8px;
+ padding: 12px 15px;
+ text-transform: uppercase;
+ letter-spacing: 0.05em;
+ font-size: 1.25rem;
+ background-image: linear-gradient(to top, rgba(0, 0, 0, 0.28) 1%, #1e54d5 99%);
+ font-weight: 500;
+ text-align: center;
+ color: white;
+ transition: all 0.2s;
+}
+
+.button--secondary {
+ outline: none;
+ border: 2px solid #1e54d5 !important;
+ background: transparent;
+ line-height: 1em;
+ border-radius: 8px;
+ padding: 9px 13px;
+ letter-spacing: 0;
+ font-size: 1.3rem;
+ font-weight: 500;
+ text-align: center;
+ color: #1e54d5;
+ transition: all 0.2s;
+ display: inline-block;
+}
+.button--secondary:hover {
+ color: white !important;
+ background: #1e54d5;
+}
+.button--secondary:focus {
+ color: white !important;
+ background: #1e54d5;
+}
+
+.site-header-and-placeholder-wrapper {
+ position: relative;
+ height: 64px;
+}
+
+.site-header {
+ position: fixed;
+ width: 100%;
+ top: 0;
+ left: 0;
+ transition: height 0.1s;
+ z-index: 100;
+ background: white;
+ box-shadow: 0 0 7px 0 #00000021;
+ border-bottom: 1px solid #e2e2e2;
+ height: 64px;
+ display: flex;
+ align-items: center;
+ font-size: 10px;
+ font-family: 'Rubik', -apple-system, 'BlinkMacSystemFont', 'Segoe UI',
+ 'Helvetica Neue', sans-serif;
+ color: #06102a;
+ -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
+}
+
+.site-header.scrolled {
+ box-shadow: 0 0 5px 0 #00000028;
+ position: fixed;
+ top: 0;
+ height: 52px;
+}
+.site-header.scrolled .site-header__title a {
+ font-size: 2.2em;
+}
+
+.header-placeholder {
+ background: none;
+ width: 100%;
+ height: 64px;
+ position: absolute;
+}
+.header-placeholder.active {
+ display: block;
+}
+
+.site-header .wrapper-1200 {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+}
+.site-header .wrapper-1200 .left {
+ display: flex;
+ align-items: center;
+ justify-content: flex-start;
+}
+
+.site-header__logo {
+ max-width: 145px;
+}
+
+.site-header__title a {
+ color: #06102a;
+ font-size: 2.2em;
+ font-weight: 500;
+ transition: all 0.2s;
+ text-transform: uppercase;
+ letter-spacing: 0.02em;
+}
+
+/* Navigation */
+.site-header__nav .menu-item-wrapper {
+ display: inline-block;
+ padding-left: 30px;
+}
+.site-header__nav .menu-item {
+ color: #06102a;
+ transition: all 0.05s;
+ font-size: 1.45em;
+ line-height: 1em;
+ font-weight: 500;
+}
+.site-header__nav .menu-item:hover {
+ color: #8a959e;
+}
+.site-header__nav .menu-item--with-icon {
+ display: flex;
+ align-items: center;
+ justify-content: flex-start;
+}
+.site-header__nav .menu-item--with-icon .title {
+ margin-right: 3px;
+}
+.site-header__nav .menu-item--with-icon .icon {
+ width: 20px;
+ height: 20px;
+ transition: all 0.1s;
+}
+.site-header__nav .menu-item--with-icon .icon svg {
+ stroke-width: 2.5 !important;
+ width: 100%;
+ height: 100%;
+}
+.site-header__nav .menu-item-wrapper--dropdown {
+ position: relative;
+}
+.site-header__nav .menu-item-wrapper--dropdown:hover .nav-dropdown-menu {
+ display: block;
+}
+.site-header__nav .nav-dropdown-menu {
+ display: none;
+}
+
+.nav-dropdown-menu {
+ position: absolute;
+ z-index: 500;
+ background: transparent;
+}
+
+.nav-dropdown-menu-wrapper {
+ border-radius: 8px;
+ box-shadow: 0 12px 40px 0 rgba(1, 10, 32, 0.24);
+ background: white;
+ margin: 8px 0;
+ overflow: hidden;
+}
+
+/* Products, Solutions dropdown menu */
+.nav-dropdown-menu--products,
+.nav-dropdown-menu--solutions {
+ width: 500px;
+}
+.nav-dropdown-menu--products .nav-dropdown-menu-wrapper,
+.nav-dropdown-menu--solutions .nav-dropdown-menu-wrapper {
+ padding: 20px;
+}
+.nav-dropdown-menu--products .dm-header,
+.nav-dropdown-menu--solutions .dm-header {
+ font-size: 1.1em;
+ font-weight: 500;
+ font-stretch: normal;
+ font-style: normal;
+ line-height: normal;
+ letter-spacing: 3.67px;
+ color: #505769;
+ margin-bottom: 20px;
+ text-transform: uppercase;
+}
+.nav-dropdown-menu--products .dm-item,
+.nav-dropdown-menu--solutions .dm-item {
+ border: none;
+ margin: 0 0 20px;
+ color: #06102a;
+ transition: all 0.1s;
+ position: relative;
+ width: 100%;
+}
+.nav-dropdown-menu--products .dm-item:last-child,
+.nav-dropdown-menu--solutions .dm-item:last-child {
+ margin-bottom: 0;
+}
+.nav-dropdown-menu--products .dm-item .dmi-image,
+.nav-dropdown-menu--solutions .dm-item .dmi-image {
+ width: 118px;
+ height: 92px;
+ position: absolute;
+ background: #f4f4f4;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ padding: 20px;
+ border-radius: 4px;
+ z-index: 0;
+}
+.nav-dropdown-menu--products .dm-item .dmi-image img,
+.nav-dropdown-menu--solutions .dm-item .dmi-image img {
+ width: 100%;
+}
+.nav-dropdown-menu--products .dm-item .dmi-details,
+.nav-dropdown-menu--solutions .dm-item .dmi-details {
+ padding: 8px 0 10px 135px;
+ width: 100%;
+ background: transparent;
+ display: block;
+ color: #06102a;
+ position: relative;
+ z-index: 1;
+}
+.nav-dropdown-menu--products .dm-item .dmi-details:hover,
+.nav-dropdown-menu--solutions .dm-item .dmi-details:hover {
+ color: #1e54d5;
+}
+.nav-dropdown-menu--products .dm-item .dmi-title,
+.nav-dropdown-menu--solutions .dm-item .dmi-title {
+ font-size: 1.6em;
+ font-weight: 500;
+ margin: 0 0 2px;
+}
+.nav-dropdown-menu--products .dm-item .dmi-description,
+.nav-dropdown-menu--solutions .dm-item .dmi-description {
+ font-size: 1.4em;
+ opacity: 0.7;
+ line-height: 1.6em;
+}
+.nav-dropdown-menu--products .dm-item--traefikee .dmi-image img,
+.nav-dropdown-menu--solutions .dm-item--traefikee .dmi-image img {
+ transform: scale(1.1);
+}
+
+.nav-dropdown-menu--solutions .dm-item .dmi-image {
+ width: 65px;
+ padding: 10px;
+ background: white;
+ height: auto;
+}
+.nav-dropdown-menu--solutions .dm-item .dmi-details {
+ padding: 5px 0 0 80px;
+}
+.nav-dropdown-menu--solutions .dm-item:last-child {
+ margin-bottom: 10px;
+}
+
+/* Dropdown menu: Learn */
+.nav-dropdown-menu--learn {
+ width: 250px;
+}
+.nav-dropdown-menu--company {
+ width: 500px;
+}
+.nav-dropdown-menu--company .nav-dropdown-menu-wrapper {
+ display: grid;
+ grid-template-columns: 50% 50%;
+}
+.nav-dropdown-menu--learn .dm-left,
+.nav-dropdown-menu--company .dm-left {
+ padding: 25px;
+}
+.nav-dropdown-menu--learn .dm-header,
+.nav-dropdown-menu--company .dm-header {
+ font-size: 1.1em;
+ font-weight: 500;
+ font-stretch: normal;
+ font-style: normal;
+ line-height: normal;
+ letter-spacing: 3.67px;
+ color: #505769;
+ margin-bottom: 20px;
+ text-transform: uppercase;
+}
+.nav-dropdown-menu--learn .dm-item,
+.nav-dropdown-menu--company .dm-item {
+ display: block;
+ font-size: 1.6em;
+ font-weight: 500;
+ color: #06102a;
+ margin-bottom: 15px;
+}
+.nav-dropdown-menu--learn .dm-item:last-child,
+.nav-dropdown-menu--company .dm-item:last-child {
+ margin-bottom: 0;
+}
+.nav-dropdown-menu--learn .dm-item:hover,
+.nav-dropdown-menu--company .dm-item:hover {
+ color: #1e54d5;
+}
+
+.dm-preview {
+ background: #edeff4;
+ overflow: hidden;
+ height: 100%;
+ display: flex;
+ flex-direction: column;
+}
+
+.dm-preview__feature-image {
+ overflow: hidden;
+ display: block;
+}
+
+.dm-preview__feature-image img {
+ width: 100%;
+ height: 145px;
+ background: #ffffff no-repeat 50%;
+ object-fit: cover;
+ vertical-align: middle;
+}
+
+.dm-preview__content {
+ padding: 15px;
+ display: flex;
+ justify-content: flex-start;
+ align-items: flex-start;
+ flex-direction: column;
+ flex: 1;
+ position: relative;
+}
+
+.dm-preview__tag {
+ display: block;
+ font-size: 1.2em;
+ color: #db7d11;
+ letter-spacing: 2.5px;
+ font-weight: 500;
+ margin: 0 0;
+ text-transform: uppercase;
+}
+
+.dm-preview__title {
+ font-size: 1.6em;
+ font-weight: 500;
+ line-height: 1.6em;
+ margin: 0;
+ color: #06102a;
+ display: block;
+ flex: 1;
+ position: relative;
+ z-index: 1;
+ padding-bottom: 20px;
+}
+
+.dm-preview .arrow-link {
+ justify-content: flex-start;
+ font-size: 1.4em;
+ position: absolute;
+ bottom: 12px;
+ z-index: 0;
+}
+
+/* Dropdown menu: Company */
+.nav-dropdown-menu--company {
+ width: 450px;
+}
+
+.nav-dropdown-menu--company .dm-right {
+ background: #06102a;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ flex-direction: column;
+ color: white;
+ padding: 20px;
+}
+.nav-dropdown-menu--company .dm-right p {
+ font-size: 1.6em;
+ font-weight: 500;
+ margin: 0 0 15px;
+ text-align: center;
+}
+.nav-dropdown-menu--company .dm-right a {
+ text-transform: uppercase;
+ line-height: 1.5em;
+ padding: 9px 12px;
+ font-size: 1.2em;
+}
+
+/* Demo */
+.site-header__demo-button .button--secondary {
+ font-size: 1.4em;
+ padding: 8px 12px;
+ border-radius: 6px;
+}
+
+/* Drawer */
+.site-header .drawer {
+ display: none;
+}
+
+@media (max-width: 1060px) {
+ .site-header__nav .menu-item-wrapper {
+ padding-left: 20px;
+ }
+}
+@media (max-width: 980px) {
+ .site-header__nav {
+ display: none;
+ }
+
+ .site-header .drawer {
+ display: block;
+ }
+
+ .site-header .right .site-header__demo-button {
+ display: none;
+ }
+
+ html [data-md-color-primary=indigo] .md-nav--primary .md-nav__title--site {
+ background-color: #06102a;
+ }
+
+ html .md-nav--primary .md-nav__title {
+ padding: 64px .8rem .2rem;
+ }
+
+ .md-search__inner {
+ top: 64px;
+ right: 0;
+ }
+}
+
+.md-header .md-search {
+ margin-left: 12.1rem;
+}
+
+.site-header__main {
+ display: flex;
+ align-items: center;
+}
diff --git a/docs/content/assets/styles/menu.css b/docs/content/assets/styles/menu.css
new file mode 100644
index 000000000..da46bae00
--- /dev/null
+++ b/docs/content/assets/styles/menu.css
@@ -0,0 +1,101 @@
+.md-nav__link {
+ margin-left: -0.4rem;
+ padding: 0 0.4rem;
+ line-height: 32px;
+ color: var(--dark) !important;
+}
+
+.md-nav__link::after {
+ font-size: 16px;
+ vertical-align: -.25em;
+}
+
+.md-nav__toggle:checked + .md-nav__link,
+.md-nav__link--active,
+.md-nav__link:hover {
+ border-radius: 8px;
+ background-color: var(--light-blue) !important;
+ color: var(--dark) !important;
+ transition: background-color 0.3s ease;
+}
+
+.md-nav__link--active {
+ color: var(--blue) !important;
+ font-weight: bold;
+}
+
+.md-sidebar--primary {
+ background-color: white;
+}
+
+.md-sidebar--secondary .md-nav__title {
+ font-size: 12px;
+ text-transform: uppercase;
+ margin-bottom: 0.4rem;
+ padding: 0;
+}
+
+.md-sidebar--secondary .md-sidebar__scrollwrap {
+ border-radius: 8px;
+ background-color: var(--light-blue) !important;
+}
+
+.md-sidebar--secondary .md-nav__title {
+ padding: 0.8rem 0.4rem 0.8rem;
+}
+
+.md-sidebar--secondary .md-nav__list {
+ padding: 0 0.4rem 0.8rem 1.2rem;
+}
+
+.md-sidebar--secondary .md-sidebar__scrollwrap .md-nav__link {
+ font-weight: 300;
+}
+
+.md-sidebar--secondary
+ .md-sidebar__scrollwrap
+ .md-nav__link[data-md-state='blur'],
+.md-sidebar--secondary .md-sidebar__scrollwrap .md-nav__link:hover {
+ color: var(--blue) !important;
+ font-weight: bold;
+}
+
+.md-sidebar--secondary .md-nav__item {
+ padding: 0 0 0 0.4rem;
+}
+
+.md-sidebar--secondary .md-nav__link {
+ margin-top: 0.225em;
+ padding: 0.1rem 0.2rem;
+}
+
+.md-sidebar--secondary li {
+ list-style-type: disc;
+}
+
+.md-sidebar--secondary .repo_url {
+ padding: 10px 0 14px 0;
+}
+
+
+.md-search__inner {
+ width: inherit;
+ float: inherit;
+}
+
+.md-search__input {
+ margin-bottom: 10px;
+ border-radius: 4px;
+ background-color: inherit;
+ border: 1px solid rgba(0,0,0,.07);
+}
+
+.md-search__input::placeholder {
+ color: rgba(0,0,0,.07);
+}
+
+@media only screen and (min-width: 60em) {
+ [data-md-toggle=search]:checked~.md-header .md-search__inner {
+ margin-top: 100px;
+ }
+}
diff --git a/docs/content/assets/styles/product-switcher.css b/docs/content/assets/styles/product-switcher.css
new file mode 100644
index 000000000..a4bfcb9bb
--- /dev/null
+++ b/docs/content/assets/styles/product-switcher.css
@@ -0,0 +1,11 @@
+.product-switcher {
+ font-size: 10px;
+ font-family: 'Rubik', -apple-system, 'BlinkMacSystemFont', 'Segoe UI',
+ 'Helvetica Neue', sans-serif;
+ color: #06102a;
+ -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
+}
+
+.product-switcher img {
+ margin-right: 10px;
+}
diff --git a/docs/content/assets/styles/root.css b/docs/content/assets/styles/root.css
new file mode 100644
index 000000000..8668ace71
--- /dev/null
+++ b/docs/content/assets/styles/root.css
@@ -0,0 +1,10 @@
+:root {
+ --dark: #06102a;
+ --blue: #04B5D1;
+ --light-blue: #E4F7FA;
+
+ --input-bg-color: white;
+ --input-color: black;
+ --input-placeholder-color: #bbb;
+ --input-border-color: #dcdcdc;
+}
\ No newline at end of file
diff --git a/docs/content/migration/v1-to-v2.md b/docs/content/migration/v1-to-v2.md
index ea680a746..8dbc4686a 100644
--- a/docs/content/migration/v1-to-v2.md
+++ b/docs/content/migration/v1-to-v2.md
@@ -453,7 +453,7 @@ To apply a redirection:
- name: whoami
port: 80
middlewares:
- - name: https_redirect
+ - name: https-redirect
---
apiVersion: traefik.containo.us/v1alpha1
@@ -476,7 +476,7 @@ To apply a redirection:
apiVersion: traefik.containo.us/v1alpha1
kind: Middleware
metadata:
- name: https_redirect
+ name: https-redirect
spec:
redirectScheme:
scheme: https
@@ -501,7 +501,7 @@ To apply a redirection:
[http.routers.router1.tls]
[http.middlewares]
- [http.middlewares.https_redirect.redirectScheme]
+ [http.middlewares.https-redirect.redirectScheme]
scheme = "https"
permanent = true
```
@@ -528,7 +528,7 @@ To apply a redirection:
tls: {}
middlewares:
- https_redirect:
+ https-redirect:
redirectScheme:
scheme: https
permanent: true
diff --git a/docs/content/migration/v2.md b/docs/content/migration/v2.md
index f8ced1a5e..1923dc517 100644
--- a/docs/content/migration/v2.md
+++ b/docs/content/migration/v2.md
@@ -317,6 +317,17 @@ Since `v2.2.5` this rule has been removed, and you should not use it anymore.
## v2.2 to v2.3
+### X.509 CommonName Deprecation
+
+The deprecated, legacy behavior of treating the CommonName field on X.509 certificates as a host name when no Subject Alternative Names are present, is now disabled by default.
+
+It means that if one is using https with your backend servers, and a certificate with only a CommonName,
+Traefik will not try to match the server name indication with the CommonName anymore.
+
+It can be temporarily re-enabled by adding the value `x509ignoreCN=0` to the `GODEBUG` environment variable.
+
+More information: https://golang.org/doc/go1.15#commonname
+
### File Provider
The file parser has been changed, since v2.3 the unknown options/fields in a dynamic configuration file are treated as errors.
diff --git a/docs/content/observability/tracing/jaeger.md b/docs/content/observability/tracing/jaeger.md
index af4397e05..5d4b747a3 100644
--- a/docs/content/observability/tracing/jaeger.md
+++ b/docs/content/observability/tracing/jaeger.md
@@ -185,6 +185,29 @@ tracing:
--tracing.jaeger.traceContextHeaderName=uber-trace-id
```
+### disableAttemptReconnecting
+
+_Optional, Default=true_
+
+Disable the UDP connection helper that periodically re-resolves the agent's hostname and reconnects if there was a change.
+Enabling the re-resolving of UDP address make the client more robust in Kubernetes deployments.
+
+```toml tab="File (TOML)"
+[tracing]
+ [tracing.jaeger]
+ disableAttemptReconnecting = false
+```
+
+```yaml tab="File (YAML)"
+tracing:
+ jaeger:
+ disableAttemptReconnecting: false
+```
+
+```bash tab="CLI"
+--tracing.jaeger.disableAttemptReconnecting=false
+```
+
### `collector`
#### `endpoint`
diff --git a/docs/content/providers/docker.md b/docs/content/providers/docker.md
index acdd46005..6fd3a5b2e 100644
--- a/docs/content/providers/docker.md
+++ b/docs/content/providers/docker.md
@@ -199,7 +199,7 @@ Therefore you **must** specify the port to use for communication by using the la
Docker Swarm Mode follows the same rules as Docker [API Access](#docker-api-access).
As the Swarm API is only exposed on the [manager nodes](https://docs.docker.com/engine/swarm/how-swarm-mode-works/nodes/#manager-nodes), you should schedule Traefik on the Swarm manager nodes by default,
-by deploying Traefik with a [constraint](https://success.docker.com/article/using-contraints-and-labels-to-control-the-placement-of-containers) on the node's "role":
+by deploying Traefik with a constraint on the node's "role":
```shell tab="With Docker CLI"
docker service create \
diff --git a/docs/content/providers/ecs.md b/docs/content/providers/ecs.md
index 22e4fd57a..feb3f6454 100644
--- a/docs/content/providers/ecs.md
+++ b/docs/content/providers/ecs.md
@@ -213,7 +213,10 @@ providers:
# ...
```
-If `accessKeyID` / `secretAccessKey` is not provided credentials will be resolved in the following order:
+If `region` is not provided, it will be resolved from the EC2 metadata endpoint for EC2 tasks.
+In a FARGATE context it will be resolved from the `AWS_REGION` env variable.
+
+If `accessKeyID` / `secretAccessKey` are 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`.
diff --git a/docs/content/reference/dynamic-configuration/kubernetes-crd-rbac.yml b/docs/content/reference/dynamic-configuration/kubernetes-crd-rbac.yml
index 88b788089..875f0c03d 100644
--- a/docs/content/reference/dynamic-configuration/kubernetes-crd-rbac.yml
+++ b/docs/content/reference/dynamic-configuration/kubernetes-crd-rbac.yml
@@ -16,6 +16,7 @@ rules:
- watch
- apiGroups:
- extensions
+ - networking.k8s.io
resources:
- ingresses
- ingressclasses
diff --git a/docs/content/reference/static-configuration/cli-ref.md b/docs/content/reference/static-configuration/cli-ref.md
index 2dc91f248..b5c7b0b82 100644
--- a/docs/content/reference/static-configuration/cli-ref.md
+++ b/docs/content/reference/static-configuration/cli-ref.md
@@ -858,6 +858,9 @@ Password for basic http authentication when sending spans to jaeger-collector.
`--tracing.jaeger.collector.user`:
User for basic http authentication when sending spans to jaeger-collector.
+`--tracing.jaeger.disableattemptreconnecting`:
+Disable the periodic re-resolution of the agent's hostname and reconnection if there was a change. (Default: ```true```)
+
`--tracing.jaeger.gen128bit`:
Generate 128 bit span IDs. (Default: ```false```)
diff --git a/docs/content/reference/static-configuration/env-ref.md b/docs/content/reference/static-configuration/env-ref.md
index 9681eef08..62e9b1334 100644
--- a/docs/content/reference/static-configuration/env-ref.md
+++ b/docs/content/reference/static-configuration/env-ref.md
@@ -858,6 +858,9 @@ Password for basic http authentication when sending spans to jaeger-collector.
`TRAEFIK_TRACING_JAEGER_COLLECTOR_USER`:
User for basic http authentication when sending spans to jaeger-collector.
+`TRAEFIK_TRACING_JAEGER_DISABLEATTEMPTRECONNECTING`:
+Disable the periodic re-resolution of the agent's hostname and reconnection if there was a change. (Default: ```true```)
+
`TRAEFIK_TRACING_JAEGER_GEN128BIT`:
Generate 128 bit span IDs. (Default: ```false```)
diff --git a/docs/content/reference/static-configuration/file.toml b/docs/content/reference/static-configuration/file.toml
index 74537d226..b4f944dce 100644
--- a/docs/content/reference/static-configuration/file.toml
+++ b/docs/content/reference/static-configuration/file.toml
@@ -290,6 +290,7 @@
gen128Bit = true
propagation = "foobar"
traceContextHeaderName = "foobar"
+ disableAttemptReconnecting = true
[tracing.jaeger.collector]
endpoint = "foobar"
user = "foobar"
diff --git a/docs/content/reference/static-configuration/file.yaml b/docs/content/reference/static-configuration/file.yaml
index 247c20d98..4e36e08df 100644
--- a/docs/content/reference/static-configuration/file.yaml
+++ b/docs/content/reference/static-configuration/file.yaml
@@ -308,6 +308,7 @@ tracing:
gen128Bit: true
propagation: foobar
traceContextHeaderName: foobar
+ disableAttemptReconnecting: true
collector:
endpoint: foobar
user: foobar
diff --git a/docs/content/routing/providers/kubernetes-crd.md b/docs/content/routing/providers/kubernetes-crd.md
index 8804125da..64d22e2bc 100644
--- a/docs/content/routing/providers/kubernetes-crd.md
+++ b/docs/content/routing/providers/kubernetes-crd.md
@@ -108,16 +108,16 @@ The Kubernetes Ingress Controller, The Custom Resource Way.
name: myingressroute
namespace: default
- spec:
- entryPoints:
- - web
+ spec:
+ entryPoints:
+ - web
- routes:
- - match: Host(`foo`) && PathPrefix(`/bar`)
- kind: Rule
- services:
- - name: whoami
- port: 80
+ routes:
+ - match: Host(`foo`) && PathPrefix(`/bar`)
+ kind: Rule
+ services:
+ - name: whoami
+ port: 80
---
apiVersion: traefik.containo.us/v1alpha1
@@ -126,15 +126,15 @@ The Kubernetes Ingress Controller, The Custom Resource Way.
name: ingressroute.tcp
namespace: default
- spec:
- entryPoints:
- - tcpep
- routes:
- - match: HostSNI(`bar`)
- kind: Rule
- services:
- - name: whoamitcp
- port: 8080
+ spec:
+ entryPoints:
+ - tcpep
+ routes:
+ - match: HostSNI(`bar`)
+ kind: Rule
+ services:
+ - name: whoamitcp
+ port: 8080
---
apiVersion: traefik.containo.us/v1alpha1
@@ -143,14 +143,14 @@ The Kubernetes Ingress Controller, The Custom Resource Way.
name: ingressroute.udp
namespace: default
- spec:
- entryPoints:
- - fooudp
- routes:
- - kind: Rule
- services:
- - name: whoamiudp
- port: 8080
+ spec:
+ entryPoints:
+ - fooudp
+ routes:
+ - kind: Rule
+ services:
+ - name: whoamiudp
+ port: 8080
```
```yaml tab="Whoami"
diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml
index 7a288b61c..0e5feebbf 100644
--- a/docs/mkdocs.yml
+++ b/docs/mkdocs.yml
@@ -29,7 +29,12 @@ theme:
copyright: "Copyright © 2016-2020 Containous"
extra_css:
- - assets/styles/extra.css # Our custom styles
+ - assets/styles/root.css
+ - assets/styles/header.css
+ - assets/styles/footer.css
+ - assets/styles/menu.css
+ - assets/styles/content.css
+ - assets/styles/product-switcher.css
- assets/styles/atom-one-light.css # HightlightJS's CSS theme
extra_javascript:
diff --git a/docs/scripts/verify.sh b/docs/scripts/verify.sh
index 840232d2c..b4ba696fe 100755
--- a/docs/scripts/verify.sh
+++ b/docs/scripts/verify.sh
@@ -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/,/pilot.traefik.io\/profile/" \
+ --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/,/containo.us/,/docs.mae.sh/,/www.mkdocs.org/,/squidfunk.github.io/,/ietf.org/" \
'{}' 1>/dev/null
## HTML-proofer options at https://github.com/gjtorikian/html-proofer#configuration
diff --git a/docs/theme/main.html b/docs/theme/main.html
index 5b1a18c99..176befcce 100644
--- a/docs/theme/main.html
+++ b/docs/theme/main.html
@@ -10,6 +10,46 @@
{% endblock %}
+{% block header %}
+ {% include "partials/containous-header.html" %}
+ {% include "partials/header.html" %}
+{% endblock %}
+
+
+{% block site_nav %}
+ {% if nav %}
+ {% include "partials/product-switcher.html" %}
+
+ {% endif %}
+ {% if page.toc %}
+
+ {% endif %}
+{% endblock %}
+
+
{% block footer %}
{% import "partials/language.html" as lang with context %}
@@ -36,6 +76,10 @@
Material for MkDocs
+
+
{% block social %}
{% include "partials/social.html" %}
diff --git a/docs/theme/partials/containous-header.html b/docs/theme/partials/containous-header.html
new file mode 100644
index 000000000..ade0a1ace
--- /dev/null
+++ b/docs/theme/partials/containous-header.html
@@ -0,0 +1,238 @@
+
+
+
+
+
+
diff --git a/docs/theme/partials/product-switcher.html b/docs/theme/partials/product-switcher.html
new file mode 100644
index 000000000..b57a9a14a
--- /dev/null
+++ b/docs/theme/partials/product-switcher.html
@@ -0,0 +1,49 @@
+
\ No newline at end of file
diff --git a/exp.Dockerfile b/exp.Dockerfile
index c02db50ad..def3c4baa 100644
--- a/exp.Dockerfile
+++ b/exp.Dockerfile
@@ -12,7 +12,7 @@ RUN npm install
RUN npm run build
# BUILD
-FROM golang:1.14-alpine as gobuild
+FROM golang:1.15-alpine as gobuild
RUN apk --update upgrade \
&& apk --no-cache --no-progress add git mercurial bash gcc musl-dev curl tar ca-certificates tzdata \
diff --git a/go.mod b/go.mod
index 95a23d7d9..5986d1366 100644
--- a/go.mod
+++ b/go.mod
@@ -1,12 +1,11 @@
module github.com/containous/traefik/v2
-go 1.14
+go 1.15
require (
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 // indirect
github.com/BurntSushi/toml v0.3.1
github.com/ExpediaDotCom/haystack-client-go v0.0.0-20190315171017-e7edbdf53a61
- github.com/Masterminds/goutils v1.1.0 // indirect
github.com/Masterminds/semver v1.4.2 // indirect
github.com/Masterminds/sprig v2.22.0+incompatible
github.com/Microsoft/hcsshim v0.8.7 // indirect
@@ -20,7 +19,7 @@ require (
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/containous/yaegi v0.8.14
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
@@ -47,7 +46,6 @@ require (
github.com/gorilla/websocket v1.4.2
github.com/hashicorp/consul/api v1.3.0
github.com/hashicorp/go-version v1.2.0
- github.com/huandu/xstrings v1.2.0 // indirect
github.com/influxdata/influxdb1-client v0.0.0-20190809212627-fc22c7df067e
github.com/instana/go-sensor v1.5.1
github.com/libkermit/compose v0.0.0-20171122111507-c04e39c026ad
@@ -73,10 +71,11 @@ require (
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4
github.com/rancher/go-rancher-metadata v0.0.0-20200311180630-7f4c936a06ac
github.com/sirupsen/logrus v1.4.2
- github.com/stretchr/testify v1.5.1
+ github.com/stretchr/testify v1.6.1
github.com/stvp/go-udp-testing v0.0.0-20191102171040-06b61409b154
github.com/tinylib/msgp v1.0.2 // indirect
- github.com/uber/jaeger-client-go v2.22.1+incompatible
+ github.com/traefik/paerser v0.1.0
+ github.com/uber/jaeger-client-go v2.25.0+incompatible
github.com/uber/jaeger-lib v2.2.0+incompatible
github.com/unrolled/render v1.0.2
github.com/unrolled/secure v1.0.7
@@ -92,7 +91,6 @@ require (
gopkg.in/DataDog/dd-trace-go.v1 v1.19.0
gopkg.in/fsnotify.v1 v1.4.7
gopkg.in/jcmturner/goidentity.v3 v3.0.0 // indirect
- gopkg.in/yaml.v2 v2.2.8
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776
k8s.io/api v0.18.2
k8s.io/apimachinery v0.18.2
diff --git a/go.sum b/go.sum
index 2db321779..117034e94 100644
--- a/go.sum
+++ b/go.sum
@@ -68,8 +68,12 @@ github.com/Masterminds/goutils v1.1.0 h1:zukEsf/1JZwCMgHiK3GZftabmxiCw4apj3a28RP
github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
github.com/Masterminds/semver v1.4.2 h1:WBLTQ37jOCzSLtXNdoo8bNM8876KhNqOKvrlGITgsTc=
github.com/Masterminds/semver v1.4.2/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
+github.com/Masterminds/semver/v3 v3.1.0 h1:Y2lUDsFKVRSYGojLJ1yLxSXdMmMYTYls0rCvoqmMUQk=
+github.com/Masterminds/semver/v3 v3.1.0/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
github.com/Masterminds/sprig v2.22.0+incompatible h1:z4yfnGrZ7netVz+0EDJ0Wi+5VZCSYp4Z0m2dk6cEM60=
github.com/Masterminds/sprig v2.22.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o=
+github.com/Masterminds/sprig/v3 v3.1.0 h1:j7GpgZ7PdFqNsmncycTHsLmVPf5/3wJtlgW9TNDYD9Y=
+github.com/Masterminds/sprig/v3 v3.1.0/go.mod h1:ONGMf7UfYGAbMXCZmQLy8x3lCDIPrEZE/rU8pmrbihA=
github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5 h1:ygIc8M6trr62pF5DucadTWGdEB4mEyvzi0e2nbcmcyA=
github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw=
github.com/Microsoft/hcsshim v0.8.7 h1:ptnOoufxGSzauVTsdE+wMYnCWA301PdoN4xg5oRdZpg=
@@ -160,8 +164,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/containous/yaegi v0.8.14 h1:SUVs88S6YwXbDgcujg3swokscGa93lozWm6myJ5MRug=
+github.com/containous/yaegi v0.8.14/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=
@@ -418,13 +422,15 @@ github.com/hashicorp/serf v0.8.2 h1:YZ7UKsJv+hKjqGVUUbtE3HNj79Eln2oQ75tniF6iPt0=
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
-github.com/huandu/xstrings v1.2.0 h1:yPeWdRnmynF7p+lLYz0H2tthW9lqhMJrQV/U7yy4wX0=
-github.com/huandu/xstrings v1.2.0/go.mod h1:DvyZB1rfVYsBIigL8HwpZgxHwXozlTgGqn63UyNX5k4=
+github.com/huandu/xstrings v1.3.1 h1:4jgBlKK6tLKFvO8u5pmYjG91cqytmDCDvGh7ECVFfFs=
+github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/iij/doapi v0.0.0-20190504054126-0bbf12d6d7df h1:MZf03xP9WdakyXhOWuAD5uPK3wHh96wCsqe3hCMKh8E=
github.com/iij/doapi v0.0.0-20190504054126-0bbf12d6d7df/go.mod h1:QMZY7/J/KSQEhKWFeDesPjMj+wCHReeknARU3wqlyN4=
github.com/imdario/mergo v0.3.5 h1:JboBksRwiiAJWvIYJVo46AfV+IAIKZpfrSzVKj42R4Q=
github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
+github.com/imdario/mergo v0.3.8 h1:CGgOkSJeqMRmt0D9XLWExdT4m4F1vd3FV3VPt+0VxkQ=
+github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/influxdata/influxdb1-client v0.0.0-20190809212627-fc22c7df067e h1:txQltCyjXAqVVSZDArPEhUTg35hKwVIuXwtQo7eAMNQ=
github.com/influxdata/influxdb1-client v0.0.0-20190809212627-fc22c7df067e/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo=
github.com/instana/go-sensor v1.5.1 h1:GLxYsYiDWD15RSXDHS70VvTVU/CbwUimWrK6/e4eBPQ=
@@ -665,6 +671,8 @@ github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9
github.com/soheilhy/cmux v0.1.4 h1:0HKaf1o97UwFjHH9o5XsHUOF+tqmdA7KEzXLpiyaw0E=
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
+github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng=
+github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
@@ -678,6 +686,8 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV
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=
+github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
+github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stvp/go-udp-testing v0.0.0-20191102171040-06b61409b154 h1:XGopsea1Dw7ecQ8JscCNQXDGYAKDiWjDeXnpN/+BY9g=
github.com/stvp/go-udp-testing v0.0.0-20191102171040-06b61409b154/go.mod h1:7jxmlfBCDBXRzr0eAQJ48XC1hBu1np4CS5+cHEYfwpc=
github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
@@ -687,13 +697,15 @@ github.com/tinylib/msgp v1.0.2 h1:DfdQrzQa7Yh2es9SuLkixqxuXS2SxsdYn0KbdrOGWD8=
github.com/tinylib/msgp v1.0.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5 h1:LnC5Kc/wtumK+WB441p7ynQJzVuNRJiqddSIE3IlSEQ=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
+github.com/traefik/paerser v0.1.0 h1:B4v1tbvd8YnHsA7spwHKEWJoGrRP+2jYpIozsCMHhl0=
+github.com/traefik/paerser v0.1.0/go.mod h1:yYnAgdEC2wJH5CgG75qGWC8SsFDEapg09o9RrA6FfrE=
github.com/transip/gotransip/v6 v6.0.2 h1:rOCMY607PYF+YvMHHtJt7eZRd0mx/uhyz6dsXWPmn+4=
github.com/transip/gotransip/v6 v6.0.2/go.mod h1:pQZ36hWWRahCUXkFWlx9Hs711gLd8J4qdgLdRzmtY+g=
github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
github.com/uber-go/atomic v1.3.2 h1:Azu9lPBWRNKzYXSIwRfgRuDuS0YKsK4NFhiQv98gkxo=
github.com/uber-go/atomic v1.3.2/go.mod h1:/Ct5t2lcmbJ4OSe/waGBoaVvVqtO0bmtfVNex1PFV8g=
-github.com/uber/jaeger-client-go v2.22.1+incompatible h1:NHcubEkVbahf9t3p75TOCR83gdUHXjRJvjoBh1yACsM=
-github.com/uber/jaeger-client-go v2.22.1+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk=
+github.com/uber/jaeger-client-go v2.25.0+incompatible h1:IxcNZ7WRY1Y3G4poYlx24szfsn/3LvK9QHCq9oQw8+U=
+github.com/uber/jaeger-client-go v2.25.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk=
github.com/uber/jaeger-lib v2.2.0+incompatible h1:MxZXOiR2JuoANZ3J6DE/U0kSFv/eJ/GfSYVCjK7dyaw=
github.com/uber/jaeger-lib v2.2.0+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U=
github.com/unrolled/render v1.0.2 h1:dGS3EmChQP3yOi1YeFNO/Dx+MbWZhdvhQJTXochM5bs=
@@ -765,6 +777,8 @@ golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975/go.mod h1:LzIPMQfyMNhhGPh
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/crypto v0.0.0-20200414173820-0848c9571904 h1:bXoxMPcSLOq08zI3/c5dEBT6lE4eh+jOh886GHrn6V8=
+golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/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-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -1023,6 +1037,9 @@ gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
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.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
+gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
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=
diff --git a/integration/fixtures/https/generate_cert.sh b/integration/fixtures/https/generate_cert.sh
new file mode 100644
index 000000000..47efcfed8
--- /dev/null
+++ b/integration/fixtures/https/generate_cert.sh
@@ -0,0 +1,48 @@
+openssl req -newkey rsa:2048 \
+ -new -nodes -x509 \
+ -days 3650 \
+ -out snitest.com.cert \
+ -keyout snitest.com.key \
+ -subj "/CN=snitest.com" \
+ -addext "subjectAltName = DNS:snitest.com"
+
+openssl req -newkey rsa:2048 \
+ -new -nodes -x509 \
+ -days 3650 \
+ -out www.snitest.com.cert \
+ -keyout www.snitest.com.key \
+ -subj "/CN=www.snitest.com" \
+ -addext "subjectAltName = DNS:www.snitest.com"
+
+openssl req -newkey rsa:2048 \
+ -new -nodes -x509 \
+ -days 3650 \
+ -out snitest.org.cert \
+ -keyout snitest.org.key \
+ -subj "/CN=snitest.org" \
+ -addext "subjectAltName = DNS:snitest.org"
+
+openssl req -newkey rsa:2048 \
+ -new -nodes -x509 \
+ -days 3650 \
+ -out uppercase_wildcard.www.snitest.com.cert \
+ -keyout uppercase_wildcard.www.snitest.com.key \
+ -subj "/CN=FOO.WWW.SNITEST.COM" \
+ -addext "subjectAltName = DNS:*.WWW.SNITEST.COM"
+
+openssl req -newkey rsa:2048 \
+ -new -nodes -x509 \
+ -days 3650 \
+ -out wildcard.www.snitest.com.cert \
+ -keyout wildcard.www.snitest.com.key \
+ -subj "/CN=*.www.snitest.com" \
+ -addext "subjectAltName = DNS:*.www.snitest.com"
+
+openssl req -newkey rsa:2048 \
+ -new -nodes -x509 \
+ -days 3650 \
+ -out wildcard.snitest.com.cert \
+ -keyout wildcard.snitest.com.key \
+ -subj "/CN=*.snitest.com" \
+ -addext "subjectAltName = DNS:*.snitest.com"
+
diff --git a/integration/fixtures/https/snitest.com.cert b/integration/fixtures/https/snitest.com.cert
index 1efb82d17..1974a4906 100644
--- a/integration/fixtures/https/snitest.com.cert
+++ b/integration/fixtures/https/snitest.com.cert
@@ -1,19 +1,19 @@
-----BEGIN CERTIFICATE-----
-MIIC/zCCAeegAwIBAgIJAL858pci5XyjMA0GCSqGSIb3DQEBBQUAMBYxFDASBgNV
-BAMMC3NuaXRlc3QuY29tMB4XDTE1MTEyMzIyMDU1NloXDTI1MTEyMDIyMDU1Nlow
-FjEUMBIGA1UEAwwLc25pdGVzdC5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
-ggEKAoIBAQDVF25wEroSIUO/dNgHxlyt8pZFVpJ8fNaJw7cnlZ1JP2hLuEbmjAFT
-dHqS8wKDNYHktsBEOUfN/qbk0AiGb+SvhQw6kfM/QSj9fXVQ7KhYP9XYOekTOH7d
-M0Z2L3RGgqs8z+83exOOnAFVvIJCMZJXEeijV6iJlmpCcJa0Kg/JHlxhoWTEeZuU
-G+hITafk1yWOKorTCPlMhB30wuQoWfbHP+3G0bsERLXFiMANE8EtQu8+ZhfseBUh
-5Tu5gIC4Fnria7mRixAZeEiAblFP9h0vrNRcP3nmuVz0tHPIeQsJQiEhxaZ09oUW
-h9WqTsYCP1821+SVazM9oFRTpy6chZyTAgMBAAGjUDBOMB0GA1UdDgQWBBSz9mbX
-ia1TM5FG4Zgagaet24S8HDAfBgNVHSMEGDAWgBSz9mbXia1TM5FG4Zgagaet24S8
-HDAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4IBAQB79W8XTxlozh9w/W7T
-5vDev67G4T/wetABSb68CRrqojt78PMJuS89JarA8I3ts00O+0JYnsHxp+9qC7pf
-jWHcDSiLwRUMu7MXW/KIen1EB8BQNA0xWbAiQaWYPHzsBlX48+9wBe0HTDx7Lcxb
-OsmnXHBF5fd2EY+R8qJu+PyTDDL1WLItFJpzHiFiGiYF8Tyic3kkPjje6eIOxRmT
-hq+qbwApzbzz6h/VD5xR3zBDFBo2Xs5tdP264KIw/YXDpaXVBiJ5DDjQ3dtJw1G5
-yzgrHQZWJN8Gs8ZZgGdgRf7PHox8xEZtqPiMkChDz6T7Ha3U0xYN6TZGNZOR6DHs
-K9/8
+MIIDJTCCAg2gAwIBAgIUe+ERKBLSt2oK4Ogvu8PS/6gb5zcwDQYJKoZIhvcNAQEL
+BQAwFjEUMBIGA1UEAwwLc25pdGVzdC5jb20wHhcNMjAwODEzMTYwMDA2WhcNMzAw
+ODExMTYwMDA2WjAWMRQwEgYDVQQDDAtzbml0ZXN0LmNvbTCCASIwDQYJKoZIhvcN
+AQEBBQADggEPADCCAQoCggEBAJyHO81nD+2JyjDCpPgKFxvO5KGofQvvjO+5k4TL
+XASlypiA0GkEVXoRQsCjdaSBZ9JuePHoxr0TnQj1MeGU36r9oZ479haWQ4h3/yLl
+9jGqsGdwc2tlJ7oDSkGkPij4g3+GT3pLWG365A8EQ8HvsiqvKn8/WU1tA/bs9YaT
+n3BU6no31TEa5mFHhbef5eG4wq3JX9N2n7E181rGM48MRO8D5Vl3ffco348thsmO
+7v5zy2N3+YmbfTd8n/9e6cKfYnuVUilfaqbRBiF9MYEFWStp+sCTdoM8UmGNvin3
+Iyzduyv9ILlDVx8CCoM2xUKhujl2IpoGxKLDsrky+WHIOusCAwEAAaNrMGkwHQYD
+VR0OBBYEFOBwZGZOFYx0Sgwm4kX5b3m+Adx2MB8GA1UdIwQYMBaAFOBwZGZOFYx0
+Sgwm4kX5b3m+Adx2MA8GA1UdEwEB/wQFMAMBAf8wFgYDVR0RBA8wDYILc25pdGVz
+dC5jb20wDQYJKoZIhvcNAQELBQADggEBACV7Bo9JoHOGoiLw+gNJuX8Jwycry7SB
+8GX8CHwth04T1LNKqgDlwbYQbWgAuxd7KlzJ6i2+1xCBrloTvDJyeN/ZfMLJxSvN
+ldSbPz8FyKkVhapMM4sBt12isqR6dyD3vHo5rRnR24c80qS+B01314zOJ26+xHRk
+oFTaxG2adgby/7Mvffbz816aEPyFL4cU5ZMP1tduIOLiQDgQ7G+0WbkP5PsPh0+7
+IFVZOi89h5Ixa7mCvqQj4ZOVrbWl+PyCbrCNk7c8AZBr+FSGUmVJbEesfYxAJjC0
+ijuTnquu8WO7ttyZKEQh3FaB1rngvEXDhOW4NJOC9c3Hcaij4oDsp60=
-----END CERTIFICATE-----
diff --git a/integration/fixtures/https/snitest.com.key b/integration/fixtures/https/snitest.com.key
index 35638a803..e47372d4e 100644
--- a/integration/fixtures/https/snitest.com.key
+++ b/integration/fixtures/https/snitest.com.key
@@ -1,27 +1,28 @@
------BEGIN RSA PRIVATE KEY-----
-MIIEpQIBAAKCAQEA1RducBK6EiFDv3TYB8ZcrfKWRVaSfHzWicO3J5WdST9oS7hG
-5owBU3R6kvMCgzWB5LbARDlHzf6m5NAIhm/kr4UMOpHzP0Eo/X11UOyoWD/V2Dnp
-Ezh+3TNGdi90RoKrPM/vN3sTjpwBVbyCQjGSVxHoo1eoiZZqQnCWtCoPyR5cYaFk
-xHmblBvoSE2n5NcljiqK0wj5TIQd9MLkKFn2xz/txtG7BES1xYjADRPBLULvPmYX
-7HgVIeU7uYCAuBZ64mu5kYsQGXhIgG5RT/YdL6zUXD955rlc9LRzyHkLCUIhIcWm
-dPaFFofVqk7GAj9fNtfklWszPaBUU6cunIWckwIDAQABAoIBABAdQYDAKcoNMe5c
-i6mq2n9dBPghX9qCJkcswcEAk3BilySCvvnYRJFnEY3jSqFZfoUpPMjr+/4b78sF
-4F8qPwT27sHPH7H833ir8B86hlCGI0nCt1l4wD9CDWYKmKRsZT6oCtMLP6NdMMyn
-AMK4tPRYqlsP2fLtqQN1ODBPrfnraoNHtOVE784iBCD5dewICA5RIQG2i/d2+CGF
-+bahFqUXVCqHoxBz4AVvrRFL99VcP7P2iZyk6hDQ7fci7Xay8Wb/HutRxuqvF0aU
-bG6Enk6CCtNZHLwNPp4Hqft0Udvg2tG8okYwbEmoEO40nQsCSzRCpq5Uvzi+LX1k
-LykQ6+ECgYEA7x8vQoyOK60Q3LPpJFGDec2+XJPoesTfJTT6idaP7ukUL8p3FsUo
-9vtxRRfhSOdPoAINmrL0TyMekO2B6zXx0pmWVpMrFwZW6zMwZAnLp/w+3USpbGCy
-K12IIwvRYzTzKwoMTVAKTXm36b6oqr2La4bTdJR7REY6G374FrJb/H0CgYEA5CHk
-Ym0h7cf00fw9UEHRfzUZxmCfRWY6K8InOuHdLi+u4TiyXzs8x5s0e/DN/raNmTGx
-QO81UzuS3nKwc4n5QyXjVnhzR5DbbSACDwHtdnxZByL0D1KvPjtRF8F+rWXViXv2
-TM7UiOmn6R375FPSAPxeyMx8Womc3EnAAfLWGk8CgYEAv8I2WBv3dzcWqqbsdF+a
-G/fOjNdgO/PdLy1JLXiPfHwV4C1xSyVZMJd7wnjgBWLaC+sZldGk8kGrpXWSFlnw
-T38zfMIQcCp5Uax/RfpFA7XZhAAoDe2NdBFRtyknBXPU/dLVArsJSBAwWJa5FBNk
-1xoMQRVBtQLMXnh341utQNECgYEA4o1R2/ka16NaWmpPjXM/lD9skFgF84p4vFn8
-UXpaB3LtDdcbNH2Ed4mHToouWAR8jCUQLTcg0r53tRdaafMcKfXnVUka2nhdoHpH
-8RVt99u3IeIxU0I+q+OGPbw3jAV0UStcxpwj7q9zw4q2SuJ+y+HUUz7XQ6Yjs5Q9
-7PF2c/sCgYEAhdVn5gZ5FvYKrBi46t3pxPsWK476HmQEVHVi5+od7wg+araDelAe
-8QE8hc8qdZGbjdB/AHSPCeUxfO2vnpsCoSRs29o6pDvQuqvHYs+M53l5LEYeOjof
-t6J/DK5Pim2CAFjYFcZk8/Gyl5HjTw3PpdWxoPD5v2Xw3bbY57IIbm4=
------END RSA PRIVATE KEY-----
+-----BEGIN PRIVATE KEY-----
+MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCchzvNZw/ticow
+wqT4ChcbzuShqH0L74zvuZOEy1wEpcqYgNBpBFV6EULAo3WkgWfSbnjx6Ma9E50I
+9THhlN+q/aGeO/YWlkOId/8i5fYxqrBncHNrZSe6A0pBpD4o+IN/hk96S1ht+uQP
+BEPB77Iqryp/P1lNbQP27PWGk59wVOp6N9UxGuZhR4W3n+XhuMKtyV/Tdp+xNfNa
+xjOPDETvA+VZd333KN+PLYbJju7+c8tjd/mJm303fJ//XunCn2J7lVIpX2qm0QYh
+fTGBBVkrafrAk3aDPFJhjb4p9yMs3bsr/SC5Q1cfAgqDNsVCobo5diKaBsSiw7K5
+MvlhyDrrAgMBAAECggEANa+0dTjaH7DY5mx8RI7BakpF5KRzbzqUbkyKKkvNndJ3
+cGMOGFbiMmHlMUlfOjMgNu6pEN+z+nnVrJLcrvBu3qjrW+hY+VpEg3QU0BVroZFX
+u8K386fYPlaOi4EXyNmj0INykxFwnRgmTHg7/TKHO1ilDMvfh0zSxUh01dmgH9iW
+zzmjztyje4E0kn2x+DtCFRegQBqJbwAaTsqkZglGxIT4qUPr+d7iWuTvd0QTBd7I
+JMZU96rhH/+5cBm22bmmft7YaJ++EyLQekJEzcby6gDHxvhCPUs1EcjO7x5QSIEi
+yAkunNSTpxpOqE/EmSaKBV6m/SYIYcxUEbR/qZM40QKBgQDNT5aGgVoeIDlDqNLt
+Izx3p7l57fcPPF5F1x843+WaUVb/tDSEs2dFETOexON7Vq21S5+czDSOhdbwtaKN
+gJea6nRolnsr9QrkkABSCJxySf7kS12ip/a/Vx2ziBAMD4feZVfikHzhwS5e5Egu
+44VpH8gTy2OnZVf5OwY2nW7TvwKBgQDDLGbTLe/n2GgR9FpB3JtKRJ0Dn+cvlmwb
+Ij5TCBom/DTSWZwcqe2EPZ04ICUgrSbNAQ/aG+i2IGf2lan3xL8jeDHA4Dh4JyNR
+0oKOJyoBIe24l+Cxa+rSyNyXrG9DUSlc16NeHc44XwMBZSPJOVT/WgrZwofPF1QD
+rgwgnF0z1QKBgE9bCtBUYPOx+tBHCbf03aNWJd+V9d9iswGFo6DFEaby95CAI6iC
++1B71JryeQasOxoO79OYLlxEKQ8C9aCB6pdLNdFTEKqt3Rb4/da3U1jtduLgqoF7
+MGlGj8lbtvAidJ6mRzDgLhTIKVcuHyRYdrFqQlg8Tflet4EyqjUj5rlbAoGAJouL
+VyOYYLJlgz494Qn/hUBdvgyDbOuJOCagJmfQOmjOycg2w5ZvuREuDpqSmox3wD74
+cXvXjriBr7pJEgzCLvAa61uc62XXXN51ODvV2NlpiG4+SuvkOXK/1hiQ9Lt/rr1L
+n1fFlW52On1N/vyao7+VqwH3LlgOGJfz/zDCW20CgYAxhCb0XU1BYa490Ec/cHkn
+EYLITuNUIuSRQhIKCu6M90mdnUkRd/FmakVfbPCMdrSR7wpwrceoMkR1clShxAR9
+56UMilH0jw1p88vMB1L9OK6Xj7ve3x6yxfDZa4MAinQQNJqTRCa900Skj+wt1Qpq
+Tt5jlFmHa0RjHx/9FD+90g==
+-----END PRIVATE KEY-----
diff --git a/integration/fixtures/https/snitest.org.cert b/integration/fixtures/https/snitest.org.cert
index 251ca939c..d4eba6f23 100644
--- a/integration/fixtures/https/snitest.org.cert
+++ b/integration/fixtures/https/snitest.org.cert
@@ -1,19 +1,19 @@
-----BEGIN CERTIFICATE-----
-MIIC/zCCAeegAwIBAgIJALAYHG/vGqWEMA0GCSqGSIb3DQEBBQUAMBYxFDASBgNV
-BAMMC3NuaXRlc3Qub3JnMB4XDTE1MTEyMzIyMDU0NFoXDTI1MTEyMDIyMDU0NFow
-FjEUMBIGA1UEAwwLc25pdGVzdC5vcmcwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
-ggEKAoIBAQC8b2Qv68Xnv4wgJ6HNupxABSUA5KXmv9g7pwwsFMSOK15o2qGFzx/x
-9loIi5pMIYIy4SVwJNrYUi772nCYMqSIVXlwct/CE70j2Jb2geIHu3jHbFWXruWb
-W1tGGUYzvnsOUziPE3rLWa/NObNYLLlUKJaxfHrxnpuKpQUsXzoLl25cJEVr4jg2
-ZITpdraxaBLisdlWY7EwwHBLu2nxH5Rn+nIjenFfdUwKF9s5dGy63tfBc8LX9yJk
-+kOwy1al/Wxa0DUb6rSt0QDCcD+rXnjk2zWPtsHz1btwtqM+FLtN5z0Lmnx7DF3C
-tCf1TMzduzZ6aeHk77zc664ZQun5cH33AgMBAAGjUDBOMB0GA1UdDgQWBBRn/nNz
-PUsmDKmKv3GGo3km5KKvUDAfBgNVHSMEGDAWgBRn/nNzPUsmDKmKv3GGo3km5KKv
-UDAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4IBAQBkuutIcbBdESgvNLLr
-k/8HUDuFm72lYHZFE+c76CxqYN52w02NCTiq1InoDUvqZXb/StATBwRRduTUPCj9
-KUkC7pOjAFxjzjExsHrtZSq01WinrxNI+qSKvI8jFngMHnwN1omTt7/D7nxeW5Of
-FJTkElnxtELAGHoIwZ+bKprnexefpn9UW84VJvJ2crSR63vBvdTrgsrEGW6kQj1I
-62laDpax4+x8t2h+sfG6uNIA1cFrG8Sk+O2Bi3ogB7Y/4e8r6WA23IRP+aSv0J2b
-k5fvuuXbIc979pQOoO03zG0S7Wpmpsw+9dQB9TOxGITOLfCZwEuIhnv+M9lLqCks
-7H2A
+MIIDJTCCAg2gAwIBAgIUHoCzmb8xZbjZxBO0TBHg95L8SRYwDQYJKoZIhvcNAQEL
+BQAwFjEUMBIGA1UEAwwLc25pdGVzdC5vcmcwHhcNMjAwODEzMTYwMDA3WhcNMzAw
+ODExMTYwMDA3WjAWMRQwEgYDVQQDDAtzbml0ZXN0Lm9yZzCCASIwDQYJKoZIhvcN
+AQEBBQADggEPADCCAQoCggEBAJOEIrko2LFNy+jcoZwQpNk/x2VhMln7qPD3jIrJ
+MnpzOKMrXe3LsKL8A/usXZ+3WU9prY2h7uFgWBPe9buFXlHdwakBHbjLOKjVzOjc
+l6Y9McMILYwe5q1QyiTnGVWsaoxi35ZwXArK7mgglTjlSjiYlDqTffGQU6wI41Lb
+iMDhBYtF5oLtOz+5u7PWV8vo+TjvtwY46i4TRyeQW0XVP1nlx8RZsZYB4PYU18nD
+SkCYAskfhzgmd1OGeMY+K/+PV3BhzxNjt5sJCRtTbTp09KiOkeFc4icGbwPKZBSf
+LVRDeUrw19lxNIKN0ektDTyChHmh2SphvyEapY2ydM0UCIUCAwEAAaNrMGkwHQYD
+VR0OBBYEFMjIHm8ePfILBtRLFcMcEE5fsFfJMB8GA1UdIwQYMBaAFMjIHm8ePfIL
+BtRLFcMcEE5fsFfJMA8GA1UdEwEB/wQFMAMBAf8wFgYDVR0RBA8wDYILc25pdGVz
+dC5vcmcwDQYJKoZIhvcNAQELBQADggEBAI2MwWjc9VL8i413rha22B4dGJWTVtj+
+VRbkyXZnGeSBq2zfWkdnNby9/a+azV8HNzFxiPXyfmDu2G6xnen9Y4sfDqGZUvLh
+IPFnq6f8mPKth0ooN6+Rb4cV47lBf+h2NMHl+apu+G124VDj2oJQuf8Kx+L/dDzn
+nfyist323UIKkNBg86uWMVcLnvi5FTEgH++CeStjiFNFMBbwVCaqbHAF+0Phvc+n
+z48G1XQ9cb+g4YW000OVOXiVnzAE31uGG8JJdvRtVobgyIIup9dl71M2rxcBh4mF
+KN8qIRW7uQOGKrz7eExH8fuEX1ybwJnFocR8r3N8dXl39fnEqDrHrsA=
-----END CERTIFICATE-----
diff --git a/integration/fixtures/https/snitest.org.key b/integration/fixtures/https/snitest.org.key
index 7714e49e3..e265ed06c 100644
--- a/integration/fixtures/https/snitest.org.key
+++ b/integration/fixtures/https/snitest.org.key
@@ -1,27 +1,28 @@
------BEGIN RSA PRIVATE KEY-----
-MIIEogIBAAKCAQEAvG9kL+vF57+MICehzbqcQAUlAOSl5r/YO6cMLBTEjiteaNqh
-hc8f8fZaCIuaTCGCMuElcCTa2FIu+9pwmDKkiFV5cHLfwhO9I9iW9oHiB7t4x2xV
-l67lm1tbRhlGM757DlM4jxN6y1mvzTmzWCy5VCiWsXx68Z6biqUFLF86C5duXCRF
-a+I4NmSE6Xa2sWgS4rHZVmOxMMBwS7tp8R+UZ/pyI3pxX3VMChfbOXRsut7XwXPC
-1/ciZPpDsMtWpf1sWtA1G+q0rdEAwnA/q1545Ns1j7bB89W7cLajPhS7Tec9C5p8
-ewxdwrQn9UzM3bs2emnh5O+83OuuGULp+XB99wIDAQABAoIBAGOn9bByXQQnhZAr
-5aLMIn6pOdyzEBptM4q42fMmOJ2HyjJiDjKaTCbHRu5mBoBk6FrIP+iDVUo6jKad
-7BZSEjoYGlWiKzyU+97NWWmdX1D/kOzHGq1RzhTPyAHWtA4Bm0sEMFFa2AJbuGIt
-NfBYFtuva6MKVmsamuBETewdoLEnxzzDFcuOaxXRfTC/ikWcYyB4KEWA5fjroUQC
-Llo9/UTGTkh1Hynv9AXY6Qia/RbrIQjKveKCRj6PjxyE/qN9qfmngczz2pK0hRhL
-Z+K06y8G+Yj1I1zm5jNg1kakVQKoBsnaYkmIUBUSmWv6ERotedOWtOAMlOKa+0l2
-DS7Ou2ECgYEA91doi+3XrMVsgyTEm/ArzEyRUfM5dCSvBCRFhO7QQp2OYAbjJk5S
-pmdpqmwTsXNNMU+XNkWCLug5pk0PTJwP0mVLE2fLYqCCXoyaMltQ0Yk2gaun/RwE
-w5EfyMwOQakLFY/ODvduQfyNpaoWgFz4/WPNTVNCGs04LepSGKaFNy0CgYEAwwgV
-jKeFA+QZGooTInyk07ZlAbenEPu/c2y3UUFxclP0CjP2/VBOpz9B62vhzCKbjD1c
-/L3x1CKC4n4lbeyHi4vrF69LX9SHr1Jm0SUtyKeV3g0EAzIWI0HFhVUkMvtbibQ4
-HXrLVCJO77xetQ7RQszss1z9g3WotAAiBMiQgDMCgYBTLjoilOIrYFmV4Q+dwa95
-DWbxwHJZ9NxG8EvQ4N95B7OR578Matqwy6ZlgeM9kiErrDCWN9oIHGEG5HN4uCM6
-BoaxB/8GNCSj13Uj6kHLtfF2ulvMa1fOzUd7J+TDgC4SGkKaFewmlOCuDf1zPdEe
-pimtD4rzqIB0MJFbaOT0IQKBgDBPjlb7IB3ooLdMQJUoXwP6iGa2gXHZioEjCv3b
-wihZ13e3i5UQEYuoRcH1RUd1wyYoBSKuQnsT2WwVZ1wlXSYaELAbQgaI9NtfBA0G
-sqKjsKICg13vSECPiEgQ4Rin3vLra4MR6c/7d6Y2+RbMhtWPQYrkm/+2Y4XDCqo4
-rGK1AoGAOFZ3RVhuwXzFdKNe32LM1wm1eZ7waxjI4bQS2xUN/3C/uWS7A3LaSlc3
-eRG3DaVpez4DQVupZDHMgxJUYqqKynUj6GD1YiaxGROj3TYCu6e7OxyhalhCllSu
-w/X5M802XqzLjeec5zHoZDfknnAkgR9MsxZYmZPFaDyL6GOKUB8=
------END RSA PRIVATE KEY-----
+-----BEGIN PRIVATE KEY-----
+MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCThCK5KNixTcvo
+3KGcEKTZP8dlYTJZ+6jw94yKyTJ6czijK13ty7Ci/AP7rF2ft1lPaa2Noe7hYFgT
+3vW7hV5R3cGpAR24yzio1czo3JemPTHDCC2MHuatUMok5xlVrGqMYt+WcFwKyu5o
+IJU45Uo4mJQ6k33xkFOsCONS24jA4QWLReaC7Ts/ubuz1lfL6Pk477cGOOouE0cn
+kFtF1T9Z5cfEWbGWAeD2FNfJw0pAmALJH4c4JndThnjGPiv/j1dwYc8TY7ebCQkb
+U206dPSojpHhXOInBm8DymQUny1UQ3lK8NfZcTSCjdHpLQ08goR5odkqYb8hGqWN
+snTNFAiFAgMBAAECggEARGU1fEgWAE8jxfnV7HhDFn9dwgGIyPPH5sHAJfEXOHMH
+xY+r4z9XzJmwdxX56r2l6Q4Ea/Cyw0haTgxIPwOdMbPxwJxF3ZJL2OwYL7dnkKo6
+0kSOabNCHQWiU+3ab0Cbvi9xn1zFMDPayeihh1m4SflkcKxF7qSORutwCtDC3TpD
+k4oYIOoZKOh9zB3uhDtt+WgfrW8xGNrFgSaOWSyBP1f4W00K4YcaM6bW3qTctEjd
+YyynuewD25snVllTh0+V1+2sUXZstnaKkqc5qAcBR0I1jHMEyM4KidbFE1QEjRTq
+SCverKtS3hxhDsblxSpkSprM64oTO5oJzQAi+xmGgQKBgQDENXZ9dJt64BHqD+HY
+TIW0aHAfIGbZAnithFSl1GCkTbtnuIh21TdLj80ayaIp6XpgMK8N/twS2was+Uxs
+amBLHXPUCknpEG4hoNm6Nmm7JUIV3zkxSyGSHby0kjcaRskMVo0jKwxfzTI9ayqZ
+ImPChqcg7fZZS4QjnylLaI1MkQKBgQDAeBXputfb4YmIpEcKSd+ASZSp1GhHdc/T
+ap3/jRlTjgi2MFYFkfLoWFYsmfze9AIsMO4mKntTii7btgXePn+qPra+otUtAtod
+veS1zWTnzkBUL8OaP3hBEnVsKN++QT7Zj0n8WyOCA5kWzKGlfQtZ5JBEJ4i+ZmKM
+L0GzmgmGtQKBgCppPAGGMQ1wXa7wW+r7L3N9l67CvDhwNlGbZwipTXJ2cunKk4TM
+NbWZkcYWi3zvwMoF+/DUhBF3UowVHxXYMJRL+3t79dnw+T0/nZCIrnYLaat8osoq
+6UjCMMX/TPXbEx4dn0hYx8X1fsST45NSQaoMnRebGELY9ekVQEHSiahhAoGARO69
+amiwxt+QEpBDoTu0AI5Z4Dli37UiKeYVkGFIoNRN6nKFXJlZFj4NHwxHtNJPtOQR
+Zznv0Yu8HJSkpWRc9OxhJwXKs8RBukU4lKRrb1o/T+eLdsMknqmtxH8fI1Ta3bCk
+Hu3FWdtQCQphGpQebKOfx8khAJBK20pRjbGtl8ECgYB12xxIAkbT/8+URBHL7ANQ
+724csbJDPBgWMY6o10H0wC5sOSaNRYXyd7Hq4wyNq4kPeWFziLCBLZ2mLaH3MwAl
+JCneHBP83xMSt9TH7d2uNKdZ4DFBa22SYQQdpTtkxQdbbcckeoA5B9a+RKP90trN
+daKjpKaKkohrM6wEbGRNng==
+-----END PRIVATE KEY-----
diff --git a/integration/fixtures/https/uppercase_wildcard.www.snitest.com.cert b/integration/fixtures/https/uppercase_wildcard.www.snitest.com.cert
index dfe9440ee..d9347f6a8 100644
--- a/integration/fixtures/https/uppercase_wildcard.www.snitest.com.cert
+++ b/integration/fixtures/https/uppercase_wildcard.www.snitest.com.cert
@@ -1,19 +1,20 @@
-----BEGIN CERTIFICATE-----
-MIIDDDCCAfSgAwIBAgIJAI1YpPACcsQnMA0GCSqGSIb3DQEBCwUAMB4xHDAaBgNV
-BAMME0ZPTy5XV1cuU05JVEVTVC5DT00wHhcNMTgxMDI5MTU1NDI4WhcNMjgxMDI2
-MTU1NDI4WjAeMRwwGgYDVQQDDBNGT08uV1dXLlNOSVRFU1QuQ09NMIIBIjANBgkq
-hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxyWr+1O/tf4yjwhfp3/SDGT5fD0chhGs
-Qc+QM7Ewb5SOmIL5UskxT5pCKc6Kuie5qqEp9xH8Rrfo18iEJQPdhFC1YkaBEI0L
-l1qvN4jmXzAK/E/u4+X+FFprHyruXCmuXqsWQt/qEOqU1ciN47GE9+ZW4R+q70uB
-zrEQ+dzN7IBsyf1lzzS3/TwDgj085QmiZYxKxX40d5hZW6AHxPEKJa2p+Gweqg74
-SpzBWL1DYQLcqHUuMKlbigHg+gleqcO8NiHT5UdeSPVokD5VJPO1La1PMqkLmJYr
-3vVkQ9YzNQ615bX98VMIi17cmE7LE+vz+v287cdFT2f1pNXr3pCGzQIDAQABo00w
-SzALBgNVHQ8EBAMCBDAwCQYDVR0TBAIwADATBgNVHSUEDDAKBggrBgEFBQcDATAc
-BgNVHREEFTATghEqLldXVy5TTklURVNULkNPTTANBgkqhkiG9w0BAQsFAAOCAQEA
-HJyMCj9oHwECmSGWHnYHkO42zeyj24RKlhNG5skUCqZmpmeDc2BRMYH4fjP75MD2
-kuasZBMAxyQnur/DEn8TzQ1mlKxYCqoza1ql5PkfcwNUp/tvQ7Jhf45Z5mQVeUM7
-RSiBhpeetjHY5/xQb7gXHa97+OjDoRJ6NL/dzGxqypf37kiQPw2jWI5RTFBkP+h/
-sPbeAZJjmiEzvw31SAw9IGj3VvIwcuTxbsdJQITU7hCXDSd1EIocmzAoobY7WRcT
-B1pLmHlP/BaIsM7m0NF/HgUsgo/kgSsxnGA2MHMYQiTImR2DUgrJYzKlJ5acscLK
-sMq9xUnjr6KF1C15R2FpDw==
+MIIDOzCCAiOgAwIBAgIUGD2rMD4+tW3xtACvzFho4csuM28wDQYJKoZIhvcNAQEL
+BQAwHjEcMBoGA1UEAwwTRk9PLldXVy5TTklURVNULkNPTTAeFw0yMDA4MTMxNjAw
+MDdaFw0zMDA4MTExNjAwMDdaMB4xHDAaBgNVBAMME0ZPTy5XV1cuU05JVEVTVC5D
+T00wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDHJ7Tq9OT7e223vyBY
+ERypzmauQpVb4MB7//2L1iiqFqFbCWix1TEsY5wPJNnFz7m652/zhVzEeqLuz68r
+GkM3p6vBz+COnENHIvbc+VYfq7kaFjzjKYXeEqkZNC3vaTk84FWquK25OVuShbTA
+uf1C/o0wTfez7gO3NkC1/BM99mknJocu4HicyNDDWdpz4lHuJTOEa5lH4oWso9+6
+JXRgIwwbhSOcp4eaI7f1mDmiPLV9oIY3FL/g1hOFPbtpVkjhm1HDFXSckiIWCPPd
+Uy8yAEynzdXA41B47/uZmSo6XHCeJj9AWnT5RckcraqBWY4V3KYuOFlu6clEB2ri
+ZBKjAgMBAAGjcTBvMB0GA1UdDgQWBBQf19vS/IjR5E7kqJ7iedHX1v17mDAfBgNV
+HSMEGDAWgBQf19vS/IjR5E7kqJ7iedHX1v17mDAPBgNVHRMBAf8EBTADAQH/MBwG
+A1UdEQQVMBOCESouV1dXLlNOSVRFU1QuQ09NMA0GCSqGSIb3DQEBCwUAA4IBAQBp
+2I/4h4B64QtHxKm6Qve8acjkjjaFo6miDBbbH8QEt/lY8JPjmkZrAaAonNF5DG02
+MSMVranoekwUGMFAAA/89ihnTgPRtJVQNwLuSAcOuRy4+Sv/ndQTHo0ApcoZ7Bau
+pGggY8asiGXqpmi2CGAE93dCfnGLD/+5WYpvyPetsS0qO+0hE/KX/6G+/9rpwD3Q
+aZ6aQhWYnLVyzPGStQ7ujXAqoSNqPnQenYoJLggKaphCfTUGUl+KBDATDp2+JHCg
+wPG1WQbxs8/38fZ79+2dMoSosXr4mBbIoMOmYkicwNI7/dOuhHIJXH5hlrTG5C+a
+3DnjjBFsHpVQncoOSHpA
-----END CERTIFICATE-----
diff --git a/integration/fixtures/https/uppercase_wildcard.www.snitest.com.key b/integration/fixtures/https/uppercase_wildcard.www.snitest.com.key
index 5b9a16173..d153d48c2 100644
--- a/integration/fixtures/https/uppercase_wildcard.www.snitest.com.key
+++ b/integration/fixtures/https/uppercase_wildcard.www.snitest.com.key
@@ -1,28 +1,28 @@
-----BEGIN PRIVATE KEY-----
-MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDHJav7U7+1/jKP
-CF+nf9IMZPl8PRyGEaxBz5AzsTBvlI6YgvlSyTFPmkIpzoq6J7mqoSn3EfxGt+jX
-yIQlA92EULViRoEQjQuXWq83iOZfMAr8T+7j5f4UWmsfKu5cKa5eqxZC3+oQ6pTV
-yI3jsYT35lbhH6rvS4HOsRD53M3sgGzJ/WXPNLf9PAOCPTzlCaJljErFfjR3mFlb
-oAfE8Qolran4bB6qDvhKnMFYvUNhAtyodS4wqVuKAeD6CV6pw7w2IdPlR15I9WiQ
-PlUk87UtrU8yqQuYlive9WRD1jM1DrXltf3xUwiLXtyYTssT6/P6/bztx0VPZ/Wk
-1evekIbNAgMBAAECggEAVOFEnTmD47D1oasjAgRj5a5/+6kcaDROJDqwrqeeCmDa
-KjzgwZ1JLDGGc8U5scBOzWAlv83lpcqrLpWjZRdxqfywYrPEPOaxAxC+z7/E2Ntk
-Q0hafL5BfjFPqRgmQhft3yGyukwvuogRadEyUNMP5o1BiHBz7cxUBmHH54dqKZuO
-ueUMgqraJX/GK+Om2rIUst0oOT9yUED+f6ciIjVAmCx1EVxZmX7sxKig10e70eOJ
-rfHlRguJWtxy0+Wl8R8TVrpI5r7qsE8y2fet9RqFOof/4ds8uA2nlZ3NpGkAq3Oo
-+65h/2fjD5uQ7jmT+XZcbC7SGhboV42zIrmn0DyNIQKBgQDneeqzMlooNzLD6x+v
-bXo6BJAHXuml440zS5i5RawKc3+/GxGQjBvnfhFH6AQ7cL4ohYyfuAo4srgifRle
-x3Gl8yvFf0uLaQHj811HPWV0fU8bwekI77jmH7WZi2ED/qX7X06R2vvUPGshvJi5
-yPCmJpDQQA6wmxBG1U4SqNw0xQKBgQDcPu2DMAJpbMWWeb5xWv5/6h6TUF4tV7fV
-eIBWuVfe9Jry3gAnb6YUOKYmA5xYJJ+fTz4Nhe4+LQbFS1esT/7ZIATvILogZc3S
-X9+ZCYG/tmDDZvhZqIWWSzzdrjb7dseP1RI4Wp6OnRqHWErrkfzDJKuN15qgW5vR
-FUR2ykV6aQKBgQCv5ZQ00dly3+ciu+QbAb00o0zzXOt91Lnytcp7V3dRhc0YYrBp
-QB7gPYtSMfwtUxIdZsaihE64IQ8NnjSOMk6pRW0Iqh+083mtR7ylKwGSkLpxpFu6
-H7hInuX3pNN3HqXwq87fxSFCeRsLyu3fl9NO3tWCenrvNxYaTXMDeO/E5QKBgE7D
-XlMU/zfOg1bN0PJe1TbPdgG+sv9KKF76CgN5otgD58nE5I812VHP9HMRxX6sEj15
-rDpP1CR+G7bAu+jObtgdIEaYEJf3cES0rpTfFnyF71LR5yzBHIzj+S9Z1yXUk4d3
-bl2i4qMjwdH3HEvkWF09JvDB0vVX7YA3N9W3fmNJAoGBALRi9EbkEBW1vMPwMzps
-YoJ1lp/YyDGTFcg6KFgTfNaOYccb6EXL2Cd21qvDsJw6wthXS+cSqX3qlTLAVLY8
-az/NfyFmW1fUtGjs2s0ZtplStGBhv8VR+2fpt9fgDOOrGYiN2dtmPm7jCAmyQQq7
-JCg7Vq6f0q95DUwiUAo24CBn
+MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDHJ7Tq9OT7e223
+vyBYERypzmauQpVb4MB7//2L1iiqFqFbCWix1TEsY5wPJNnFz7m652/zhVzEeqLu
+z68rGkM3p6vBz+COnENHIvbc+VYfq7kaFjzjKYXeEqkZNC3vaTk84FWquK25OVuS
+hbTAuf1C/o0wTfez7gO3NkC1/BM99mknJocu4HicyNDDWdpz4lHuJTOEa5lH4oWs
+o9+6JXRgIwwbhSOcp4eaI7f1mDmiPLV9oIY3FL/g1hOFPbtpVkjhm1HDFXSckiIW
+CPPdUy8yAEynzdXA41B47/uZmSo6XHCeJj9AWnT5RckcraqBWY4V3KYuOFlu6clE
+B2riZBKjAgMBAAECggEBAJHu56Rv1UbuOAS3+aRuzZkkVJuG0NllhMaYW5tXT67r
+LEuARoZhGUpUp2t1blqED64En0VLySF0V+Y0q0AjW3Px2msHv53gpXAAD/L2hgRp
+GhciinVO3PhQpjaAxNQ1P6r7G+sgrrQWcqKYdU3wO8QWsTDfQQ5I79IV4C86SoNQ
+upF5I9nRwvZ6YOlFZ/p67QN89S9mYypvUKBneDC/X0quM4AdRiAyo57Qk7r9PWYH
+qfuCf9n4lqmCwKazKWTVPt8Joj3+sOl0XzTPuBIvFCfMUhdLodI08bOlQV0Zd1xu
+F8jqQh1LHhH2zVPcE4AcQVOrOYujw56CQxzH7oxJTakCgYEA/mcx4+11+/rVlM9E
+BxCTGdn+gVWEKOHijdP/1q5xeHnYGnXIX7e48xz1KKkBsYPpCgtXqtqBWH2etovD
+zrXq4gJAMOCrCQYHO5XX0+eUJMYznUjx9dVg34CEySVNmt92Xfk62qqMwfDzfjim
+XORUJBypGIoAAS+loa1oKpuChkUCgYEAyGe7l2D3MEdR2ntmakVZ2fC2WKVCoo/g
+O9NqhLU9ewW7Jomn8SD1MPCJ8/vhU2gCLvCYRzo4zylLjijBhZURaMIQ4/oYatoE
+oA20AbrIHgHMv+XjlRZcDgnkrMGmp7u4IC3UZVFgW5i8/0fgj/8elAdC0yfTILnB
+659Iy3T/l8cCgYBuXThQiACKKvwjDXOWmQMn0OFPHFk0HyJYouK3MIA7Yqh0eipO
+E/bhSOu2EvOQHgin+dmzXg2E5SJIUQPGFjdqis1sJtssE/hpSg0SecFT3sQfdLi7
+DDF1ZFy2oj8zc8P26ps0p45eyHfphwvs+oTiEajt41gzK4dwRzkMWuBZ/QKBgHyY
+0e76IHVpwiBeXHxyBb0UhapzhQxg+372oOFFdaYJGCSLrVijrw3wXzTMQ3mBzum0
+OFkAf9V8zTbja/5Lgflpvuqe0ZONXRbNrj7tDAJsCAq/OVG2ByHLUNt/wsfCVGXd
+WJJtl8UbH1ZwL9QXP8G1Cf0SY5eTJlgCPlYQteB9AoGADRywKcx1D6TxrgctBmc8
+P2rE4PKUoHjAwf0sxZAATOa3i08pxjYHsEKDqIncZ2Khc7XTdbG9alsPucEapFL3
+pBLekXV8xkuuNmlLS+h+42H7HHhG30UU0rJvBWOB+9Xx7TZmD9YWERvrYNKy5LrI
+d6xRf3UOhpmxySj0u21Ln0Q=
-----END PRIVATE KEY-----
diff --git a/integration/fixtures/https/wildcard.snitest.com.cert b/integration/fixtures/https/wildcard.snitest.com.cert
index fa20c99dd..695068249 100644
--- a/integration/fixtures/https/wildcard.snitest.com.cert
+++ b/integration/fixtures/https/wildcard.snitest.com.cert
@@ -1,29 +1,19 @@
-----BEGIN CERTIFICATE-----
-MIIE4DCCAsgCCQCBCSnAJ0he3jANBgkqhkiG9w0BAQsFADAyMQswCQYDVQQGEwJV
-UzELMAkGA1UECAwCQUwxFjAUBgNVBAMMDSouc25pdGVzdC5jb20wHhcNMTgwNjE5
-MjAyMTEzWhcNMTgwNzE5MjAyMTEzWjAyMQswCQYDVQQGEwJVUzELMAkGA1UECAwC
-QUwxFjAUBgNVBAMMDSouc25pdGVzdC5jb20wggIiMA0GCSqGSIb3DQEBAQUAA4IC
-DwAwggIKAoICAQDOuys7ZjGVxwY/Rp5OMECkYNTOfZ1CEyAL0+pod5cd8et7k0cc
-T+tEP/25rNR9N0d3/AmtPX3XTy1MA05bYGD07cD+8meANJ+rLGMfcWh8QbBCFAWZ
-+zWkOSwpwW/DvI+67FvxHNa04u3Wv2qUld6qb0mSuZi9hKQ2s6/L3o/SxtDL/G4N
-rW71ZCkIwfzkIqvh6KWYQCZvOAIF+BFVZ2UgLbRD7/RYDrIIOrNAkW8O26C7Tck/
-JhDHDCOmfNkYqfhUW+D+GgoCi38uJqngZyxypscKdaA6SM2oFoo1jCShxDrOhvJU
-rE/l+T2Xtyr+FupJUv93iowibUwHWR5YwRNYOPkdDSG3oSKxz5xzi7Qa8L2fI7wo
-A50TDVh2AvmMCUufd5adS70bLYBfxdFNmnUhH+LHbg4v83K1eR4xMiWjpvLZ6Oub
-ufVJF6s5QEw+3K3s31UPbjzam073afSMLBpfHOsbwvcb1MBYWvf0intQo3a8MvYZ
-DCj3Y7W1Vw8lbn4v1N37KSLSNMMX1SyKxK6386t/AHwFuCM9ygI6f/l/XERL1B61
-qj9rZngKOo2cW3Yjj+oUETF3nHmcCwKBYTiWLswBI3fg6oFHTMocypY3eKhiyVaU
-mf9kBMgkDGUjWrAfOEuW9jCaDnag+Yy4XUXOlc/XaT9M2Ajvpfh9gYxWIQIDAQAB
-MA0GCSqGSIb3DQEBCwUAA4ICAQC3ut8Qeq3pYt/OGTATaUcYxrfqezW5hJN6bVfr
-/+UN+B0DfGd1/gKRUmb/t3RtmeMctTW6F21c8jyXBObjOhYVV6aE6iF61Uopozux
-+VZq8H3VJ5Qiu/Yfb5dh0iGf9srREeSAkUHBuJ9qAosM6iJsoaxQuhw/yDSxrhhg
-3jS850EZYEt9ZFjz3IdSnPCiLYqu+wMOCfT2sqBD3S8JCohTdpzuvKI2KaaY5drW
-NK4mrpJIjucfZIqbA1hbd/lCqzI4jW6i86GFhoikxxWbJCEuSWOiLJtxVnOvQgxi
-qOnOIMx88ivIxrZUHTvy2ncv/RH0q5qsaQddPkY+ll1E+1T7L7CeMTAMANS2vdlH
-nwJgsQqowisLYJQ4ztsbvpZung2szwx4ImASICYF5aVkbuNJd+lRVUoHEfSYNIYM
-Rtjteu48lYFzoBMwl6TFJA2yvL1LNaTE4/zTDgGx21aDHK14J3eIG+05wZE8AXkC
-lNGsY6n2Fn6yLK9nLxcOkpGyY62ndZwDEezqr0liOz+CKBeSZwk+VFxcgE3uGo+r
-DUcfLaU7Lx5KovP1DYAKJR3caSPAIVPnQth2kunCNs4kD7370JmmnvTlS7CTeUBT
-1P7wLh3QMrq826lGtLXNMasR1w+Q6jVx7HoOD2HFRbJHFv10R/GuWpAWAD8X5m92
-a/HFDg==
+MIIDKzCCAhOgAwIBAgIUaJuCQEFLTTX1Xkl9mLGw/VUejPowDQYJKoZIhvcNAQEL
+BQAwGDEWMBQGA1UEAwwNKi5zbml0ZXN0LmNvbTAeFw0yMDA4MTMxNjAwMDdaFw0z
+MDA4MTExNjAwMDdaMBgxFjAUBgNVBAMMDSouc25pdGVzdC5jb20wggEiMA0GCSqG
+SIb3DQEBAQUAA4IBDwAwggEKAoIBAQC2kjsjyfUt+lC1K7LSGR5ijqjhCcwkXRFA
+EVy05GedH2yKMn7E/L01F75/tv5dhPqXp1tChuNoIl54XJ6ZDHstJEaqWqqUiyvp
+NIiSLf1cpqZfV2xh2w9ykK0u6zql7+JIDNFWtBcVC5yCG3yhnxyj/4cINa5hUUYD
+CR0LOyw133snYQtVNt5++/woaFrz0XwOzkMx2vP875ms2/gPcIXgyA89esQmcJWM
+KaHCupusWVaSsWS1X1JXUvJ/EGjKv52pXuw/r6XNkjOsyqwBBaRi7V/eJ0ceDNAg
+XL1WRDq7YM/KNDhu1piCWntJ9mTPitTo1rNi97TEMzF5vIW63JkHAgMBAAGjbTBr
+MB0GA1UdDgQWBBSvvWyeyJBacNz/qfLxyBhA8uUc4DAfBgNVHSMEGDAWgBSvvWye
+yJBacNz/qfLxyBhA8uUc4DAPBgNVHRMBAf8EBTADAQH/MBgGA1UdEQQRMA+CDSou
+c25pdGVzdC5jb20wDQYJKoZIhvcNAQELBQADggEBAD/Xqkzxn3uVZ8hPdxmzO/V7
+h5aLeb9iBudQqd7RAqOuaTJPnyYQUVFvFti4QSVfvn/E9/JFN6AKB5/n+AXKoTaW
+/VAkw3rkBZ4e2l/VQvL46UuNTN+PoWbeDQHOVc7pRBF+bEVsAQQrgFfDETSuaK+/
+ZWMZIqyiZbmJOWyv8VxTkhl9nEJRA5c87WSuFCvZjwgmQ/2CeDrgANBFZqDfCdbI
+12MQDfsbWWtsceF90IvtHkmamun+rQV1v6SPSkYzLLGbXrEz7tKVUGViUbGUvsg7
+/Eb4oQpcpR4IlEO2ErHZ8xvaCLUqCiXw6jvGAkWa7OEtZZUlGcuf2GE9nQ/1aE0=
-----END CERTIFICATE-----
diff --git a/integration/fixtures/https/wildcard.snitest.com.key b/integration/fixtures/https/wildcard.snitest.com.key
index 3084b7e88..706637fb5 100644
--- a/integration/fixtures/https/wildcard.snitest.com.key
+++ b/integration/fixtures/https/wildcard.snitest.com.key
@@ -1,52 +1,28 @@
-----BEGIN PRIVATE KEY-----
-MIIJQgIBADANBgkqhkiG9w0BAQEFAASCCSwwggkoAgEAAoICAQDOuys7ZjGVxwY/
-Rp5OMECkYNTOfZ1CEyAL0+pod5cd8et7k0ccT+tEP/25rNR9N0d3/AmtPX3XTy1M
-A05bYGD07cD+8meANJ+rLGMfcWh8QbBCFAWZ+zWkOSwpwW/DvI+67FvxHNa04u3W
-v2qUld6qb0mSuZi9hKQ2s6/L3o/SxtDL/G4NrW71ZCkIwfzkIqvh6KWYQCZvOAIF
-+BFVZ2UgLbRD7/RYDrIIOrNAkW8O26C7Tck/JhDHDCOmfNkYqfhUW+D+GgoCi38u
-JqngZyxypscKdaA6SM2oFoo1jCShxDrOhvJUrE/l+T2Xtyr+FupJUv93iowibUwH
-WR5YwRNYOPkdDSG3oSKxz5xzi7Qa8L2fI7woA50TDVh2AvmMCUufd5adS70bLYBf
-xdFNmnUhH+LHbg4v83K1eR4xMiWjpvLZ6OubufVJF6s5QEw+3K3s31UPbjzam073
-afSMLBpfHOsbwvcb1MBYWvf0intQo3a8MvYZDCj3Y7W1Vw8lbn4v1N37KSLSNMMX
-1SyKxK6386t/AHwFuCM9ygI6f/l/XERL1B61qj9rZngKOo2cW3Yjj+oUETF3nHmc
-CwKBYTiWLswBI3fg6oFHTMocypY3eKhiyVaUmf9kBMgkDGUjWrAfOEuW9jCaDnag
-+Yy4XUXOlc/XaT9M2Ajvpfh9gYxWIQIDAQABAoICAHEDax/evw6lLaobveD6iewS
-r2Nu0jBT6jntEIEpl2gcX2I/4ij9G51E6jy92a/WL3DNTLDzI783noimagiUCIz9
-CHuXIrO4kOzvqASBZ+A9vNByx5kk9m8ffiAZijLT+zLxkVWfMVTTlbfHDsnJoF9F
-1U+rvG8meusYker+cVuFqpFJHxTFEhp+Ndx+x/QjbBlkqFox/5DfamO++CLbEjJk
-Kd7V55rX9cV/6YxLtQ3HTPf4DyNBePyHi1mxeLD+Ai6Dx9zBeWVoww8EvetaG7dV
-qwvxv7T9JchVAhtB0KjKcGeE6CcXx9ntxhkRXiRnfI63G8dK607KtzxxIKDec+bU
-O0A9F3DCU1qQcNsHhKButgN3SAKu8lERTpa1Y/Wu9YOmXRwexRtS8D2ktFjYyERJ
-NUkU1WST707avYxNi5SfVr2tCpMtiERzqVdsgExBoQcliJmtb2r3qhz4TI0Q6MjT
-R1icUYfQv4+xzO0TMP3+8DLWxg2t7f3082b2ig29N6z/jD67U6jzc8hxwrPqvq2b
-ubD7YcIfRWwRbaieypEymtqTW7uc+Qs6z4brC8hTdAjOlOn8HN92gN8E9ilpyjam
-QZQpMD5OSeF0cDfrgMkXvcv5xrHUjfWf0KSMYqVDz2mp3101WExKpMiBv9dHJmVm
-XsCa5UW5o4CGK4SM6LmNAoIBAQDoyymGgFL+DMvp1dlpakZxz4mE/+qK03/YKaOU
-TSY/g1szwnB1z+cKybsCdRWfqQVq2N23dOq/3Afu2hs4F71ZbNZpRn2JBuH8wa7S
-V6K8N95He/zjr/lz3p6qOxcmG1m84HDJEJPE4aNcq/qpBSQ/hzc8YGdNxthBiSQ4
-FgJMCnnyOYAwYZvOQqdGI/LTGh+vr/CaEn5Zco9CZPLuOGSTtvblKKY1zSwttg4/
-ZLb2ebq9HK9zoD0IDMx7zfPC+P/MhHPGR2HHPSHh653k7TRjpD3u30DNQDF0YWvK
-uYgLI1MReofShBA8I/rwEPe8nDn6KLmv3KoLt6M6Ied6YLEPAoIBAQDjVuYC67FL
-i7wDLSLLjWBYGTS6r1XwKppc73wQv7YTNvPUWrhxvRXlTv6laBVUmbmllDuDdmXI
-TOyQB4rKN2KIv+Cdt+itmEAMcbfVM7wIIP32MOyZP3D/nd95MfSS06+M/D30DCFA
-U+Oi4XA3NN6reXbASYjt0wNsgXDuYHrZpOB00LHEHIWvfLtf9VMWQPgVSPU1T4Bf
-0LSKRkE6Zl1ZY9RoH9U25APuCEpR1+SBusMqhZdtNTogfrEtmrOs15FoRdzm1E9G
-E9Zt7C06A6tTN9YOcckIjHMCrwPKdiAgiQVy7gThbMZk2qHuhz38xtgmAlBLhl9+
-6pwwMA5j2iXPAoIBAQCHqV2ZtE6pHmv26Vi5xeUnjfpmN31HSdnG7v0U/6C6gqIz
-l6xR+8Z40vbYh8MCOE2f5qHOt6PWCzPUTeZu2ebOpk6NKzcdE5W+5mAq1EdRyH0Q
-y4Ckb3i/vYxZR/ZFjsrM9z7C7ZYvtg6tgsuglA57tyDJXqTU/nwoNPOWe7z681/9
-eOTrTPavTMiOZ4Sq4R52E+Hy57QaDFjQKGQpz1NNgeJ/ySCTWe3U9bN33gmBuY7J
-hl340/i9KDhCLdNQXCs11Dpj4lVo9oc4UUbCkjlll+E/w3rQIgiv+dYHXfeaBgvy
-s6VTWQLdCVrDbB/zGlfvIKyVf9LY4TuONRPgjVihAoIBAATG0aRkEWCV+ghTDXUb
-blfLh8kYYATg0Ed9nKy5anjy4aKnmVKCd5BO3ZjaHACgDj+FYs67UR4pR5srHWZs
-TXy0E2Mc9x2Wolnglc07/gpprwxaMM5zf8tPJN/mBc6D9h9POXoEOzqfyJumgvYV
-/Uu7DJyzrtXYZi0Edzv6+PnTtgeeTu3g74olY8Z7YBiKmuvPkZ9iIT9iIjj5iutQ
-NUvohhD+AjvaBJ8eu3kGwT1ckDc3gVwBD0yZfN2Jb5cFHIAFX8PV2CiPyCSdHsIm
-S5Y/CRdamq+8S7pVtQ2u97PXTS8CA0Y9Q9ngoiBh5RKHlwkNaWR82UrQYSG+EL9W
-WQ8CggEAONeHx+9BeeIpu6jXjs2GqGuLgYbPSwoAo3StO5O+3Q1EgORv6n29xasv
-s+/IJBqhKeYFSNhXFvsyaacOMRwY8+vpr8FgKrytSlc86OEjGPXss6Zl7RuLvk/8
-S3wm593Lx3GLIfVIX+S2naurxq4Td9oDeKukjD7sKtOy8DhmLbLC48t5P/FoThZH
-PUqyLJ5XDf+pSV31Z2LqTwYdgKOqqTTJFvZLUzYZDL5Yd2nHrfvwF6H70zzuee0t
-Hp7QFDD14ZSMv+QOjkwenqyj1O87JJpPKH7NLRaaEk+gI7yttwavbxJYjFUWOrRB
-F6gCgvoJLFw+v5SAX5kx3hxj+QYH5w==
+MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC2kjsjyfUt+lC1
+K7LSGR5ijqjhCcwkXRFAEVy05GedH2yKMn7E/L01F75/tv5dhPqXp1tChuNoIl54
+XJ6ZDHstJEaqWqqUiyvpNIiSLf1cpqZfV2xh2w9ykK0u6zql7+JIDNFWtBcVC5yC
+G3yhnxyj/4cINa5hUUYDCR0LOyw133snYQtVNt5++/woaFrz0XwOzkMx2vP875ms
+2/gPcIXgyA89esQmcJWMKaHCupusWVaSsWS1X1JXUvJ/EGjKv52pXuw/r6XNkjOs
+yqwBBaRi7V/eJ0ceDNAgXL1WRDq7YM/KNDhu1piCWntJ9mTPitTo1rNi97TEMzF5
+vIW63JkHAgMBAAECggEAeyrZ9QIJgzQtAJxzbiP+CLV/nxoR+7JBsfxEk9DHSgvq
+GK2KMnDjYI8dOGIG5qaQvIDuI0qG3jjZjM/HOy9dZg3kEodKLx5YyGJlhGC8XaYb
+9ZQwDTBREpQiEdYl4c6c1Mi47YpfTBZiRrAue4axNr5rpZE5jHPdDK04R9xqrghr
+/1VDxlYl9iqnLzvt56wODpBYFTxGUN7/J36sYXe8Sdm1m/a6OeAuASg8poTXwIwR
+GLRtiQAlsgU/MgQQxTBfSgu1Q76+NkabykvwuVkJNhlNr6CNEbZRVM41KYTlQ00y
+xFOuVNYyMb4iAUlT4caDZRDy+yL4xaVDGWc3qwHaIQKBgQDa28wspzRuMGamWUW+
+jhcYJ45C75YMFX0qdOO45fFiwzgSEvSX/TF00DdSfSzaj2H8rNI3LCvZ5hE3C9Iu
+Q8ybBhNzbdCnk3a0QJQyAnkCMJxnN7s1BeUje9ASd9+5Hh4rPU5DQTDwwl1wVBEl
+9I+afZUD7i5obqav1gFpYjsvcQKBgQDVjfJc4N7kJ43dJhdOyF6N4Nr4fygFerZB
+HVYE2r0BoKy3jZGJl+NlKoCWHZlMYCl3jPYBzmUsxpYpuNtILckvbZXbMpnvJjme
+I9ffAfYNOI+WVW2Z3blsgSmpbbXKK2foZXb5XJFDHhIb3YdSjwYbXmr2jZAKNn2C
+6lQRmquD9wKBgByLtqHMXUZSAdGIq1QjbFe2MRQB8+w3kU/MVzdypn1fQRz6Y/S/
+DYhS8CRON196fClc0T5pAH83Rt+LJyrppPJDZJ4VoLV6wmzYT3wGXooulPfRiRVt
+Y2mxaH5ALGoUx5KUOHUFN8DvWQihrmro7yITZzBQDLWKWStuGlkIevfBAoGBAKhL
+H+ef8yd8IGlafl3Au/s1a3sPwjtnND/mXYaf1vl0ZQiWEU6Mm1B7iTBEDS5KTgN/
+29Mak1MRAP/KRU0BgB3XT5SZsQPtbNS6TIHbasuKemWv1xq9yEXBGFMEOZIkWNOl
+OnhTQcrR5NV+5Ajk25wv8T1x3ESgIpNMFYp2CJqLAoGBAM2XhmwZLylHn+hLa6rg
+q/3IsZY44q7XybslZnJMhQmr26YRXK0EHqBJmrA/IsLmZwtiSr58LqjI23n8xZuj
+eQ6g2ye2sWOT6ikBycsXitB3ibEFQaZV2kwlwp5ad7BQ1XnNlBrYDMJV2Izu4FUC
+OhCoyfY6oaZQBdST/Fxk7Zhp
-----END PRIVATE KEY-----
diff --git a/integration/fixtures/https/wildcard.www.snitest.com.cert b/integration/fixtures/https/wildcard.www.snitest.com.cert
index 9f0575f3a..270bc409a 100644
--- a/integration/fixtures/https/wildcard.www.snitest.com.cert
+++ b/integration/fixtures/https/wildcard.www.snitest.com.cert
@@ -1,20 +1,20 @@
-----BEGIN CERTIFICATE-----
-MIIDMDCCAhgCCQC425NNs+WWZzANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJG
-UjELMAkGA1UECAwCTFkxDTALBgNVBAcMBEx5b24xEzARBgNVBAoMCkNvbnRhaW5v
-dXMxGjAYBgNVBAMMESoud3d3LnNuaXRlc3QuY29tMB4XDTE4MTAyMjE0MjcxNFoX
-DTI4MTAxOTE0MjcxNFowWjELMAkGA1UEBhMCRlIxCzAJBgNVBAgMAkxZMQ0wCwYD
-VQQHDARMeW9uMRMwEQYDVQQKDApDb250YWlub3VzMRowGAYDVQQDDBEqLnd3dy5z
-bml0ZXN0LmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMwFbc/I
-gpOVNoefnIQrAy2wqK3VKSjFT5z5E8MVrHSU9PpC8bGQb0hTULmfHSzRTsajRLjv
-rLM/EZDrJL+PQHcCG+XVYbqMmVis4qsevuOyFdFdfe66LIsV+zmsSUbMyssGS2Qw
-AZx2D8RDtY35VcSA845gjQH+KfF1ST4s/73sr8ID5ZEEn4J6fbmrVfbxhygsx036
-VNw8OKby+7Gx3irz1ZC6JZ6jmzqlsu4EuDY1cjHCZSUD/JQ1jHz3gIRLV9OiglN/
-PAPu8zZZ/vtalEGytpLUcbjmvNg24Yc94vd3W3r4Ne13FhDLnB3w8Gz4pYZsEgkk
-18LzttWcqHnNwg8CAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAW1XJBk7oCGkzF4nR
-0l2cEpG2QkHAUuXRa4PqH9QALUj2taAZHGiFF0UsknjbCnTsX6rzSLy1NFiJxyuO
-CmaiZ9Y9mcYw+T+SXo862Yu1Jch48LoD5x1vW/F8ZT+Fnl+gXoh7ssAtjQ4YViWy
-Z3A1y54Mb6JhuVjfOBuzbGwI9DDAetKZgTVY7SCm7MTrF5z/YMly5rixV5th1XCj
-4bqZ9p4CZyP++Y4RffKuCf35cyD/9Y7Boq5A3E8LoxMRFzszyn9RhKdkKLOevGgc
-r4H/w92uaQqQGRTxQfNWfphBdNuc+ZgXYIGiexcpqxJfA0Ei7XSsKVxxXNxLoJe5
-3xs+Lg==
+MIIDNzCCAh+gAwIBAgIUY9P9nS/QGE7o01LrST40Hs43haEwDQYJKoZIhvcNAQEL
+BQAwHDEaMBgGA1UEAwwRKi53d3cuc25pdGVzdC5jb20wHhcNMjAwODEzMTYwMDA3
+WhcNMzAwODExMTYwMDA3WjAcMRowGAYDVQQDDBEqLnd3dy5zbml0ZXN0LmNvbTCC
+ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMwFfoOx9RtyEronDqrI5glG
+0r97I5SQa6cIwT1xSi4E5fRpAIvMrnTZr7SEnWrO9C0r6PS93Lpmy9GOUmB6taBv
+t0dZ8mgsRmrc/DyH5yOMzQO1ewISV28td4HLZMdhdyo82qfYY/zHXGsj9uHlfC9f
+HEEKg0NaEEIY22TByT78x+zQUf1Q8zUtzu9ERnSuezEw3yt++PKux5KzRsus/mmM
+ppCJLPwj6A73pfa75SBLRBPwL0E0uGHvl2O/tURNwHuvu/SfhzkssL+IOFybL8gF
+mETmAUEXUKTd6XDPxTqlXSNjqM5Abi4qzvQDFY8obFJICejPTLWTXvsfAmNoeisC
+AwEAAaNxMG8wHQYDVR0OBBYEFDa5qwKVAYnLUk7Bk/EVcjAhtkE7MB8GA1UdIwQY
+MBaAFDa5qwKVAYnLUk7Bk/EVcjAhtkE7MA8GA1UdEwEB/wQFMAMBAf8wHAYDVR0R
+BBUwE4IRKi53d3cuc25pdGVzdC5jb20wDQYJKoZIhvcNAQELBQADggEBAER2X+uG
+0JhPaGamgNj+6epvyG123PRvY/qCM9U2cyAk56ne/ICUU45buRfpcmkUXJKi4e+7
+Di40LtHgkHN4a79GJh95QsPTSSwt9zXWG7e4CWhDTPAtLO2hyGlG5M31kITKpOF1
+SHnP0gEKnUG87u2iwNHsi0tdvurx8kIflKFmTsnkxHJCdrKk9lHxYhbrN0A84tKm
+Ec/Oj7mfEiMcjDkLodd/SRHTq4y72zwXUyPnDXFx+nmpVo/hM9TR3A9dDIjtfY/o
+biqDS5s5fz5ROCaS86CtwLsE1byyBZX+YKUuKI4gfttLHss2s+gkPh45JVLGzRC9
+WmQcQ6WeTjo3eE0=
-----END CERTIFICATE-----
diff --git a/integration/fixtures/https/wildcard.www.snitest.com.key b/integration/fixtures/https/wildcard.www.snitest.com.key
index 9ad9fe643..11049b4f8 100644
--- a/integration/fixtures/https/wildcard.www.snitest.com.key
+++ b/integration/fixtures/https/wildcard.www.snitest.com.key
@@ -1,28 +1,28 @@
-----BEGIN PRIVATE KEY-----
-MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDMBW3PyIKTlTaH
-n5yEKwMtsKit1SkoxU+c+RPDFax0lPT6QvGxkG9IU1C5nx0s0U7Go0S476yzPxGQ
-6yS/j0B3Ahvl1WG6jJlYrOKrHr7jshXRXX3uuiyLFfs5rElGzMrLBktkMAGcdg/E
-Q7WN+VXEgPOOYI0B/inxdUk+LP+97K/CA+WRBJ+Cen25q1X28YcoLMdN+lTcPDim
-8vuxsd4q89WQuiWeo5s6pbLuBLg2NXIxwmUlA/yUNYx894CES1fTooJTfzwD7vM2
-Wf77WpRBsraS1HG45rzYNuGHPeL3d1t6+DXtdxYQy5wd8PBs+KWGbBIJJNfC87bV
-nKh5zcIPAgMBAAECggEAW6lEwMmRAMVVDnHDXA4HC4wG/LJ8H3kmX5v4KPmf1XDm
-71kMRX5iwNfNuNenv+75uXy4722e5Zk8RyOeCwJNMCqeZhAMLEfmzVQ/MipKEPp9
-muaqIYs7X/GsQSkKcuinY7ecP5Lh5m2Uf9T7yKFwyyw0QI9YSsDqDzVmhqyo6aaT
-ob4Bua9mTOTMCjEaIk06SkS0Z5sCqtvKMMx/fI2XYSmxQvbwYPHInpyu2LQAvKTw
-wpwDLF4Zetw1Tutbk8TSTaoC2rn6ZH5DYdJ9pk55/+UqVPo8tu/M//8JN0t9GY1/
-aqJ25juHjj0pfp+0830NOs4n6symBcR4bSbDn7r/4QKBgQDnTOdo09jtzJimGlbH
-zEqYOi0NrWU/mLkpqbczjKqx8BnTyfF3FudhY7Gp2v1WX/ofjYS/P/2nY6sXKvig
-9htqLRCe0Tk9vavY3eSEyaHu9Tbeixx7lM4pQfHCASreMp37RyhIisSPkzdCChNb
-OuqYpTW4C2u9schMlmCVaWYtTQKBgQDhzsoIlWAAD//h2xqCGpcar0SzgPCHdUH6
-4ejVhmWPfy5Jlk1CwStlsO4BlcTW7ahN81GqIlyiqpi3O2JZ4HfdoZgKNdMK6YD5
-TkmXnABa42RrQtYHltvJCthctmjP7qoRxvDrDKLBY481AZjC1MNgPlpSrfALMibx
-wyd6rjQuywKBgH+nuAfo8866nnz+CGsY2wqNARSNYFXrKjZOTqgKuKKgCwEScUvy
-vhzH8uP10t/69Ia5ikwrOwlJPsH4m2PqsFK3MHcWrerfZZq5TEflKJRDjdbhHAUw
-qV+n34/dKRWdBggKy7bNr5I2A8dU3D37lEJO3AkJdJsrJYrva7rKgvP5AoGAXNer
-VfAk8qGhcfmmYowQSNZ7htqjCu75W+/6zaBerat7GqKDzcii0UL3+QrdTgmVQ8eh
-cjSCphdCh0QRYiba4fOJEdmjlj7/2oGH3KA1vSj1puxqF+C9KWIeJ7CQU74rivej
-IuGlIaKPxRmM976HPlEkzg3aPqA2Rv0YhGaP6hUCgYA3hEG6daHOj6/P+rR28wTp
-xyraym7/8BOVWLweUFVM7YKOKrLAa7lhd254Twy0wUvTgiIw/XamhiVmdSh80gI9
-hooqYern7WGoL9zU2spVaEe2AzhSRvTuLqlRRyLLnPC6uaGVeC+SYD7zIDB2cwyC
-bbvXmg15uPp02YpLtm8wyw==
+MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDMBX6DsfUbchK6
+Jw6qyOYJRtK/eyOUkGunCME9cUouBOX0aQCLzK502a+0hJ1qzvQtK+j0vdy6ZsvR
+jlJgerWgb7dHWfJoLEZq3Pw8h+cjjM0DtXsCEldvLXeBy2THYXcqPNqn2GP8x1xr
+I/bh5XwvXxxBCoNDWhBCGNtkwck+/Mfs0FH9UPM1Lc7vREZ0rnsxMN8rfvjyrseS
+s0bLrP5pjKaQiSz8I+gO96X2u+UgS0QT8C9BNLhh75djv7VETcB7r7v0n4c5LLC/
+iDhcmy/IBZhE5gFBF1Ck3elwz8U6pV0jY6jOQG4uKs70AxWPKGxSSAnoz0y1k177
+HwJjaHorAgMBAAECggEBAKNu50jx/oos2EmczggLhRL7VAmlx/vTsRkOT1Y78ZNp
+wxgGITTcEwhx9WmjLkMIY9vnp+aeTTpY7H8DBoRY6DQMKgKTUDcY+JPUUI7gw0zc
+FsMY1t/y922NO4WS/Dknn4ELXJFGK9qrjSIankOZRODhzM1hcYM7m3iHVwCgp+i2
+wHw4+vFwrU9NIm053Zfui3dIzwmbqPID4kutpRWYuth4BvbW1ZKEne5YxDyp5xor
+ZfKSybcRTzRJ0hEgZGR7oiWrCw+Zi3XocZ0oD5fpDQ2ZpayUc9spWyTqLwlbMMyj
+nzNKRHIw5wqxJKAAh/ICzmDltkQ7CZlK5+j5dWGrBHkCgYEA+j5k7Jq6yKbFwbQM
+w2w1ySDS88ZODs/rb58WCPSA/uwW+VfvRleSrDbYINuRJhv896G8yvOlT2EdyCLG
+dYmbn7/EWGoZvDWs+WcQb5QLmCoZisA2H1/w9TBXu+w+PT9ltK1+k7CbXIsOF2Lm
++A4qLFQbi2dtlc7i3m3j8DT4nd0CgYEA0LbpZzxtOKdZ8igL9689W+fc9bJjlNOp
+K8jM/cUU7lgWpUD/xjV+ke4pGBg+0q1575eQak5u1JSylwLRevZMak+SihXBz12w
+65PQWT0U2LyJbvq/AaZ98syPF4Xux5Lp5Q77xer4AOsZj4CpTuwgVpJOGweNGD6o
+96GQJBOlC6cCgYEA7rNprdACb7e1V4oZyqeDvrDSyLnWZ4ape7XyXORtz70ARSQ+
+F4kdXayLCPyoPgPgj431+lnizA9FS0zwpdl3WzCfZ/79QWyp7NUE9onvbVxrNNEY
+T2FDBX7xkU5OKDo7F9eOr+58pA/4hxJOYzuPuu0XwSgNfiL0gVutW8oLhPkCgYBN
+PpTtd0i5Af7hB38+YOXloM9/Q1FloFxo7v0nIoyXlZRz/rVq5xt76NfeqgJ+OGP7
+j22MWpDcLlhWWV2sE4G+isqiPmEnXPaAiYk/rcKMYGLA5x5P/w14EcsXmqL/y9iy
+xLPb0RWOSXSBTs3afruNLsKncEkA4xfn5iyRnY+k2wKBgGVpdvKC6lMoT0NrrBli
+Odv6VBklC9aSHdkQB7b1Ned4rE7KDDK7VQUOURKXYXuGvc5q4lIoADVrX0QUiu21
++qAEIOKBb9lMZDFvZEqZXHvBWeUJEfpRMoCXV5Sf2r7Mz9sm2b1XYVeCGddpdw9O
+yqUEfm8nwXMi52DYxWmvLsEF
-----END PRIVATE KEY-----
diff --git a/integration/fixtures/https/www.snitest.com.cert b/integration/fixtures/https/www.snitest.com.cert
index 8e3a1546e..3400fbb28 100644
--- a/integration/fixtures/https/www.snitest.com.cert
+++ b/integration/fixtures/https/www.snitest.com.cert
@@ -1,29 +1,20 @@
-----BEGIN CERTIFICATE-----
-MIIE5DCCAswCCQDXCA89wY62zzANBgkqhkiG9w0BAQsFADA0MQswCQYDVQQGEwJV
-UzELMAkGA1UECAwCQUwxGDAWBgNVBAMMD3d3dy5zbml0ZXN0LmNvbTAeFw0xODA2
-MTkyMDIyMTRaFw0xODA3MTkyMDIyMTRaMDQxCzAJBgNVBAYTAlVTMQswCQYDVQQI
-DAJBTDEYMBYGA1UEAwwPd3d3LnNuaXRlc3QuY29tMIICIjANBgkqhkiG9w0BAQEF
-AAOCAg8AMIICCgKCAgEA+vPeeTESpGmzGHvyR4kCdGlmJjA9x230ghFU2tdCMl1C
-aAR3uaZWxg9ldAiu54yvX3ViV/BMpNyQu6Knb293W5wcxidi8aHXcqACRLNtwwmn
-NMX48Su3OvnU7Dc/fi0mpQLyblxXloCyOG/gtNjzZXVwrn3weMCe/XsvxkpcAOJz
-7ZZrXCsrQ8pk5V0vMgryQ19zMc+uK3aAPQ+ePFjraWlVH2rOxtzRBGnVM864J9XR
-tL0ZOAD2gdu4CVIt4xiU24E7W8jfZ3CTePERKhSCBGnkO4roPmRiNwgnP0Wk5lrR
-kOQkhh4JF+GPMy4IDf6elCmEpnCT39+p36vRSP9sip1OctdfuVyCJMYgb1YCh4k2
-5CMR+MrkzxzrB2Spl46he5mGkVWXssr70F/gFrIeZPUweh7OBDHnS7twWfhhsElP
-QYOXpJBWjWJkUKANDqWxM+ObUA+Kjdgk5NEOvQs7yVxpGB8Z9yK+OIJ0k77QDazD
-VIWhjxjlwgpJW4KALn9xXkUKLhsn7P3hrEDkpTYnr0g22cgPjsgnAFfVVkcloeRi
-pSfFINIJUBFLGtU0GSyqPJ9aj8CpZZe798nyt6FpSq9AuA2DF0MoECjNbch6C2gi
-VUqNyuCVjUezw9VtKy3M16GYtnMSsNOY6tnkvfXeXmLrQlfsBs01a8DQBcmOK2MC
-AwEAATANBgkqhkiG9w0BAQsFAAOCAgEAkugSNyzc+Y6MBE4/Y+Bz5HrGtKIweuar
-7F70fBk9PgWpKIBJC8s+xJRgBXMFAy5HXZir1tNWvCeJhjCbBZRnpvKvDD61gBcM
-odde6BLc4r8cRT5l0rILA01cVwyr3C3TzRREThInqNLSsnf845jA9TB9YKN2P6QB
-TT4j3VMVRlR6OL9EaAUpIWHgKPfqXfbgPQ6rfPrQQGxvZbkL2g85IkpPH+DecN42
-PK53YZG6NW1+V2Z3agvc2/4qskqoVNdpe3JkafNicokDXTVd24MNtUemWzP3gq0i
-tv75zgcwLBVVOP43mVFo5e+xZgdS65ZrWyJVL2PG929gARJSEXjLHs9avRXlpXeE
-tBpCRC5gwvq2fnC7tVbKcbYyH3lr5u3nlfRlfsomoSACC6fw2cQKnp+us/+BsVyA
-ntqrGxqC/WbQ/LHtk/YJfwFSnuzEPGClKx/F7+EoDETZGAf526VkxLTKtDPmwh5N
-HFJpeczPE2IdxdaNdOnERUB5xeSDXnObTe3e8jIfpxF0rppGo5Dxw2tfhFscQGBM
-Cs6cT9gkfX71P81JjrFrbx0bWWDf8N5meNqKqcZNTI15+dDGKXfjr9YbgtI9HHYa
-Dhb+ondnii+KAcFchC7vCgDvG+bOuWxfM9N808bsBoPXvrKF+iWsOFeKmiV1B2OT
-w0ZLNJ3AW5o=
+MIIDMTCCAhmgAwIBAgIUK5xTOkW1VCDOKhzzlW2Zto6nDmwwDQYJKoZIhvcNAQEL
+BQAwGjEYMBYGA1UEAwwPd3d3LnNuaXRlc3QuY29tMB4XDTIwMDgxMzE2MDAwN1oX
+DTMwMDgxMTE2MDAwN1owGjEYMBYGA1UEAwwPd3d3LnNuaXRlc3QuY29tMIIBIjAN
+BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuxKIdXx63cbgCz48IZsS6etDZozC
+14VXotRet1aHCKloZOYnp6inrEXuBRFJ9qhPN8+bqFklazNTWIG3nTilCfGC2vRJ
+fGY9j8Kz/1aWwblr42JdwJzSKQYmM2VV1IFR8LA/hmAT42ahTBEMkG1svCClvj4A
+R66bvzSB1iBMXXSl/Ya18cv5QWkPbKnpu1icDVHt81d4g8CKVXlfDX9CCgHJkXz4
+BQLadyOzxSx5bV5+0ioEai/+ReYEXCQegB6effQDsuKz7O7MIDiYNuaWTYjVVl9H
+1ciIL4QowUNm9vp3zTA8F+Nz3ZN/gpuDdp8+RjPA2M58mj5Dzpyk5a2HUQIDAQAB
+o28wbTAdBgNVHQ4EFgQU+SZwK2Pzqiym3j/yhBl0cmi6MzQwHwYDVR0jBBgwFoAU
++SZwK2Pzqiym3j/yhBl0cmi6MzQwDwYDVR0TAQH/BAUwAwEB/zAaBgNVHREEEzAR
+gg93d3cuc25pdGVzdC5jb20wDQYJKoZIhvcNAQELBQADggEBAGM5CXIFjVUuDZvO
+e7NpKg9azU48C+XVSHof97YgkUtJRVIvlsRhcy3yblFnRRfDKzIFasxfih1mMnhK
+q+McqZmXkK5BTWXR5mzXTJS6AY6Bkb4gusk6GJx+3dQVbBNZBHl23nPBk6fUhjF2
+Hec98S8O9woWsvEpeTai7h4X+4paJcp/oAkD8bumGZ1O/0YAgysa3RgxqIHRhjTO
+ff4z7Co6FiRRAfKBoK+YAILtwFiLMDvmyp09sTQQXciAPvfHj+2FQp4i0nq1i7GG
+scjCQ68vQxdIxYNfYS3vvhyTQomUwSY1IJhPa5GjcguA4swORd4Vyikcs2XBymij
+DJxRPYQ=
-----END CERTIFICATE-----
diff --git a/integration/fixtures/https/www.snitest.com.key b/integration/fixtures/https/www.snitest.com.key
index c12820aa6..2b763592c 100644
--- a/integration/fixtures/https/www.snitest.com.key
+++ b/integration/fixtures/https/www.snitest.com.key
@@ -1,52 +1,28 @@
-----BEGIN PRIVATE KEY-----
-MIIJRAIBADANBgkqhkiG9w0BAQEFAASCCS4wggkqAgEAAoICAQD68955MRKkabMY
-e/JHiQJ0aWYmMD3HbfSCEVTa10IyXUJoBHe5plbGD2V0CK7njK9fdWJX8Eyk3JC7
-oqdvb3dbnBzGJ2LxoddyoAJEs23DCac0xfjxK7c6+dTsNz9+LSalAvJuXFeWgLI4
-b+C02PNldXCuffB4wJ79ey/GSlwA4nPtlmtcKytDymTlXS8yCvJDX3Mxz64rdoA9
-D548WOtpaVUfas7G3NEEadUzzrgn1dG0vRk4APaB27gJUi3jGJTbgTtbyN9ncJN4
-8REqFIIEaeQ7iug+ZGI3CCc/RaTmWtGQ5CSGHgkX4Y8zLggN/p6UKYSmcJPf36nf
-q9FI/2yKnU5y11+5XIIkxiBvVgKHiTbkIxH4yuTPHOsHZKmXjqF7mYaRVZeyyvvQ
-X+AWsh5k9TB6Hs4EMedLu3BZ+GGwSU9Bg5ekkFaNYmRQoA0OpbEz45tQD4qN2CTk
-0Q69CzvJXGkYHxn3Ir44gnSTvtANrMNUhaGPGOXCCklbgoAuf3FeRQouGyfs/eGs
-QOSlNievSDbZyA+OyCcAV9VWRyWh5GKlJ8Ug0glQEUsa1TQZLKo8n1qPwKlll7v3
-yfK3oWlKr0C4DYMXQygQKM1tyHoLaCJVSo3K4JWNR7PD1W0rLczXoZi2cxKw05jq
-2eS99d5eYutCV+wGzTVrwNAFyY4rYwIDAQABAoICAEJ7bMrKd1fbMLkhzPOqll3k
-tk0Tpqo4tPfoQ4SeVkklb7xCwr0KFh7uYUA2NK/fE27EmEMXxBZA4I707kqVSxeX
-6f+M26eL6pnRTgiJSGDNI+DVObgajrYvDXtuv4Fb0MsSVstp50JV4eEVsn/2obSV
-Qj7X2mcDEJuykNuFQ45wb6nXmaWXQiT5b3VcFG67e6bhmJDvpgKZqCuFAbSXEfah
-Ew35q8H/KdhzeSn6b8sN2Dp7hjzR9Hw+iyjc/o8VKgpk2CbetmCe8FKv+o4dVLx6
-mR41FIXC7koJ/OvENYVZNf+ekRZ+yoXrGZbDcRrUA4rY3O2DEYnTpRs+V3lxQX2J
-FX/UPt/2Z5Mwaj4DX8llslO2qNvgV0WnFzm7HjXulfYVaYqrz0npWZyLWVETIov+
-56V45dAXOGpTeORmgRMaasNHOTFjwyf4ffi+DAr7xf944rZLL4eIF0fjD9yEOrn9
-3hKADWa5MYP1bBf+pTY5PTYFaoavBQ0vATNCyqI3QvuETIF0MeGY/Ui8u3fnI+PT
-IcvWKx86z7TMMvhhq5Ym5uK9W6HrLEs8CdusJ7vFX7VXjS8FQ3LEycFbmT+D/Xvt
-cfMDQwjM1FZCiy07G+wZxe/cSvx529QXy0yDsorpkwduAj6IjiCTYzG+GjjX2NKB
-JdPuOcp24BeiJasyQEVBAoIBAQD/4x0j2rQxFL+CpEP165GtLKxGgSA84jFZ1Scd
-aYHkIelveGzPZEFpTGND6HURls+anXFYsjELK36nWpmVSK9R5LUeVAVqG/5rYe0G
-XcZ1XkUqEqBdq4cgl+1aumO7q97iJbYBDhjygwh4y/iQyGSnUQXATCoU1kDvKWIj
-GfAMbItqiI29F8DDjWCB9mIHRNickXtyA7XeBZU+7Jr+pIbVv95TaNr0FoeLJCyy
-YYA9kYQHtftkHGELU7yL8o3atz/YrRKWmFmTBNUsRu2/8PxC3B3fA+o2zu2VWEdo
-sAtinLtFZiyej8Sc7JV2WlO+k7URdqRGWSA7H0GbYWVSXaZzAoIBAQD7EDK68M1M
-G9VD/qeuF8ZUa7/S8Zf77kxG1RrH4p3HVz+pTwxaLGYi97yKUd0SVf6cT+kBZLz9
-Q31mIwYUg/BuJMfCLeD0y8UGULmXjMOL/jC1qwY/oXh+asnWLLiPp4iuV1AY6qau
-FvUS8nT60Wp3jOsWIJO79lEvM6PLL44hyVxnb++vMvlBv2gOQUQ8Xa2qLVTIL83b
-XzR72bZ3inTgJRFCBvC/c/Evdzwi1Nb2xYUkzWKEcYhsQXNIOYETraZLTHshA0aF
-r2iI9q6m/vh49yj3e/J2Znz2oo04HRMchXf4JMnIDplEJ0JoaDFjacSRLYgJ/8Q0
-kQppaomVMDtRAoIBAQC6sTIGgb9r+85J+50V5DwR1AERI46ovQLynsB+BgddsZxF
-1t/UZDoRIElgN06KebSYAvy6kK+VjbNHWKOrNi+rmSjHqteUdj4mjHjJZ0uvQAtI
-SfS0wrvA/PeQdWLkft4LsyXaGTX8YbuhnneI8pv1Mvj2NtuQ/ky78T6Hi5oHBn6l
-SGHZL2ZVhmV+DIuy7/j2KnKdWbWr+fjMwwXGebViaC1GP79XzMQxsT/nGZnd0bg5
-g/2ZKddn0z1CAcKba41qgcOJGjhoOmNpfYpiuujhwwUMPCf6uvi+OH1JFQAJf35m
-gMhXG1+AemAFzJtC9TNrPVtXdBk+6WwNeH7bHDafAoIBAQC6sXrn5HTlabUXEODj
-5q4GzPEiDaF1J+j0qzd0+CFXwJuIbU3EKEvzKMG9Ic8A+Y2R8yJTdPPMaUlwkA7P
-ZqV9YkBhNviXUIe8gH7iITywd18FWJ4W5x3Q89wPNcYwnOZYrnjTbnpv7oZjhoRS
-lzNSnymZlLQHC82nCgF88GoC2deq22QipgcQSyM3pnT1ZrvjVj47dsDfplZC2syC
-7CSpISdKMBsKY08wervvMtJ/QrYVfd0Km9pUlf8B8DD5zyFf0QmmrObeNmfHoZiS
-efuPCEwgbL0KKoA2bv4Qgh5aES37CnA6IhD6yy7osMI5KMeRJYiJ1vWyGUDizuRs
-WidhAoIBAQDXoWKEK5UigmP2QCmY/8aDan3AvZhuZ7iVgZESPXHlDYzzTKmXf0Vi
-y3KL9ox1uEWOnm+j4mmhwrLObIASR7G8soOKe+zT8HfxHBW//XHYJFrufZfAGT6b
-SusgLPaFl1LoaKDLKW4qfrai0hrW1QyfJCYZi3nK7SqxYrG/KKJgtxo0TDSr/0KR
-blAUDTF9tmRoajZqcS9uFys8fxXJfNcqfKlOEjeVEC2hzK3Gqi905OAHSO7lWALs
-L3R4pskqRFnlLEhy0VcDMV5t/vCxqqBiKwSREorEwnCkEupQDJ+FybCOZbbLx8ed
-3zJ/pivaO6YjG22SZ5fXH5BkKWnnwGa9
+MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC7Eoh1fHrdxuAL
+PjwhmxLp60NmjMLXhVei1F63VocIqWhk5ienqKesRe4FEUn2qE83z5uoWSVrM1NY
+gbedOKUJ8YLa9El8Zj2PwrP/VpbBuWvjYl3AnNIpBiYzZVXUgVHwsD+GYBPjZqFM
+EQyQbWy8IKW+PgBHrpu/NIHWIExddKX9hrXxy/lBaQ9sqem7WJwNUe3zV3iDwIpV
+eV8Nf0IKAcmRfPgFAtp3I7PFLHltXn7SKgRqL/5F5gRcJB6AHp599AOy4rPs7swg
+OJg25pZNiNVWX0fVyIgvhCjBQ2b2+nfNMDwX43Pdk3+Cm4N2nz5GM8DYznyaPkPO
+nKTlrYdRAgMBAAECggEARcHVbKnsTwK3zLl6RrXCycNWsMjQZvlsAmXao9b6J3q0
+WNuR8ezsCUtSREV8JSQB1jfuPUpwr5bF/3zvcQsMT7blKqxpMqAVWKQr5oztTnPM
+DVBC0W4o1+9ZE1U4OjO20F+IcJ0nDWbZwrmgy8JiB4zNATGxfQlZPmVf/O6ySy5u
+9bnc5YAVrjNo1gMyF9uWckCU/izpdIX00obZL3O6qyKR8CKOJZc2JH80Uzq6UqF/
+Gk8Ss5XfY9FFySQJMdHtlqHGhpJoGBjN4+JvufcxEj+PrGDfn8AlS6sBlmHryq6p
+1gdDchfA/wY8Az24OOoGz5avzf72cAxknlhxC6zUGQKBgQDd/YrWZ9Hb0xStQhDh
+LCokJWdYRfBIQqqEsfqknbsMGHF0iT5upm6nGz6tKnxTLJ1NeoHbQ92j23nVaYZ8
+mui64F0eKEGmy+jdMCatiyvhFpU6q5L5ws8U5lPmcFbw8fC98wEOYCDITVCG/cuR
+HMGjF7zOx8GExj1lKRpk333gZwKBgQDXu4HpOZlJwp/8FA3gPRP2U2ew3SFiOm4F
+4o+YyUJrC5fHivkk3bdWlKMAKvILgVkx46Dmr7D1JFWqTYDCKU90nIcRCDhvop3T
+/NNh0JfrGz9tNEXMt1uzEAf1CMtrZCaR7vTF1CLtspZdojry7le2G4e70T80OB6S
+OwZaNi+nhwKBgQC/sd7/cN2e1zMGvoM2/t1RI1+Psa1hqPf46HxJRM+uDTmD6bQY
+9u2crgE8pfCAHomjy0NxpbQyeCdIK/zvgxGvtmmSzi6LK5Bl2Nu9hu2YTNkGtfyQ
+YzJRmOggmSfqnvJGBR8HW+lTLrh4KZdtMwjfnnLzmTm6PznrLkhVjUz0xwKBgAnl
++ZT45paiznJjfSt/we24b/aGfuSh57Bvb8VD3r9IneMy9jGB1sqrMTttvQNYIP6Q
+Ai7zerwUubWBrtm5yYKBOBVI+kXtAS7b19sSFhZcfrGFB3rYIuX5Y1QT3jmEM9zY
+SPTWwe+EFTAgpHHHC9E4PCca97pupMBy4ty6xb77AoGAC2bqfamygEKC18XaJRPQ
+cnxfi1Wutfsck3mywkGwuJIDvt1GFzEZHtxbsmIlt+wueGNcxtOA6ms9sqqmUxPI
+InXUbVfYocWw36WrJos64rZtOWFGRz8aWbEnbh6zY7Nf91ffRCEjY12vUADdlMzo
+L87jIT1rNK5YTHEwr2Kdjn8=
-----END PRIVATE KEY-----
diff --git a/integration/try/try.go b/integration/try/try.go
index 98231626b..3922f7f14 100644
--- a/integration/try/try.go
+++ b/integration/try/try.go
@@ -11,7 +11,7 @@ import (
)
const (
- // CITimeoutMultiplier is the multiplier for all timeout in the CI
+ // CITimeoutMultiplier is the multiplier for all timeout in the CI.
CITimeoutMultiplier = 3
maxInterval = 5 * time.Second
)
diff --git a/internal/gendoc.go b/internal/gendoc.go
index 4175d6345..d36f15a71 100644
--- a/internal/gendoc.go
+++ b/internal/gendoc.go
@@ -10,16 +10,18 @@ import (
"strings"
"github.com/BurntSushi/toml"
- "github.com/containous/traefik/v2/pkg/config/env"
- "github.com/containous/traefik/v2/pkg/config/flag"
- "github.com/containous/traefik/v2/pkg/config/generator"
- "github.com/containous/traefik/v2/pkg/config/parser"
"github.com/containous/traefik/v2/pkg/config/static"
"github.com/containous/traefik/v2/pkg/log"
+ "github.com/traefik/paerser/env"
+ "github.com/traefik/paerser/flag"
+ "github.com/traefik/paerser/generator"
+ "github.com/traefik/paerser/parser"
)
func main() {
- genStaticConfDoc("./docs/content/reference/static-configuration/env-ref.md", "", env.Encode)
+ genStaticConfDoc("./docs/content/reference/static-configuration/env-ref.md", "", func(i interface{}) ([]parser.Flat, error) {
+ return env.Encode(env.DefaultNamePrefix, i)
+ })
genStaticConfDoc("./docs/content/reference/static-configuration/cli-ref.md", "--", flag.Encode)
genKVDynConfDoc("./docs/content/reference/dynamic-configuration/kv-ref.md")
}
diff --git a/pkg/anonymize/anonymize_config_test.go b/pkg/anonymize/anonymize_config_test.go
index ccbc744ce..df88d5bbd 100644
--- a/pkg/anonymize/anonymize_config_test.go
+++ b/pkg/anonymize/anonymize_config_test.go
@@ -8,7 +8,6 @@ import (
"github.com/containous/traefik/v2/pkg/config/static"
"github.com/containous/traefik/v2/pkg/ping"
"github.com/containous/traefik/v2/pkg/provider/acme"
- acmeprovider "github.com/containous/traefik/v2/pkg/provider/acme"
"github.com/containous/traefik/v2/pkg/provider/docker"
"github.com/containous/traefik/v2/pkg/provider/file"
"github.com/containous/traefik/v2/pkg/provider/kubernetes/crd"
@@ -21,6 +20,7 @@ import (
"github.com/containous/traefik/v2/pkg/tracing/zipkin"
"github.com/containous/traefik/v2/pkg/types"
assetfs "github.com/elazarl/go-bindata-assetfs"
+ ptypes "github.com/traefik/paerser/types"
)
func TestDo_globalConfiguration(t *testing.T) {
@@ -65,9 +65,9 @@ func TestDo_globalConfiguration(t *testing.T) {
Address: "foo Address",
Transport: &static.EntryPointsTransport{
RespondingTimeouts: &static.RespondingTimeouts{
- ReadTimeout: types.Duration(111 * time.Second),
- WriteTimeout: types.Duration(111 * time.Second),
- IdleTimeout: types.Duration(111 * time.Second),
+ ReadTimeout: ptypes.Duration(111 * time.Second),
+ WriteTimeout: ptypes.Duration(111 * time.Second),
+ IdleTimeout: ptypes.Duration(111 * time.Second),
},
},
ProxyProtocol: &static.ProxyProtocol{
@@ -78,9 +78,9 @@ func TestDo_globalConfiguration(t *testing.T) {
Address: "fii Address",
Transport: &static.EntryPointsTransport{
RespondingTimeouts: &static.RespondingTimeouts{
- ReadTimeout: types.Duration(111 * time.Second),
- WriteTimeout: types.Duration(111 * time.Second),
- IdleTimeout: types.Duration(111 * time.Second),
+ ReadTimeout: ptypes.Duration(111 * time.Second),
+ WriteTimeout: ptypes.Duration(111 * time.Second),
+ IdleTimeout: ptypes.Duration(111 * time.Second),
},
},
ProxyProtocol: &static.ProxyProtocol{
@@ -95,16 +95,16 @@ func TestDo_globalConfiguration(t *testing.T) {
CAServer: "CAServer",
Storage: "Storage",
KeyType: "MyKeyType",
- DNSChallenge: &acmeprovider.DNSChallenge{Provider: "DNSProvider"},
- HTTPChallenge: &acmeprovider.HTTPChallenge{
+ DNSChallenge: &acme.DNSChallenge{Provider: "DNSProvider"},
+ HTTPChallenge: &acme.HTTPChallenge{
EntryPoint: "MyEntryPoint",
},
- TLSChallenge: &acmeprovider.TLSChallenge{},
+ TLSChallenge: &acme.TLSChallenge{},
},
},
}
config.Providers = &static.Providers{
- ProvidersThrottleDuration: types.Duration(111 * time.Second),
+ ProvidersThrottleDuration: ptypes.Duration(111 * time.Second),
}
config.ServersTransport = &static.ServersTransport{
@@ -112,8 +112,8 @@ func TestDo_globalConfiguration(t *testing.T) {
RootCAs: []traefiktls.FileOrContent{"RootCAs 1", "RootCAs 2", "RootCAs 3"},
MaxIdleConnsPerHost: 111,
ForwardingTimeouts: &static.ForwardingTimeouts{
- DialTimeout: types.Duration(111 * time.Second),
- ResponseHeaderTimeout: types.Duration(111 * time.Second),
+ DialTimeout: ptypes.Duration(111 * time.Second),
+ ResponseHeaderTimeout: ptypes.Duration(111 * time.Second),
},
}
diff --git a/pkg/cli/commands.go b/pkg/cli/commands.go
deleted file mode 100644
index 917c1bfff..000000000
--- a/pkg/cli/commands.go
+++ /dev/null
@@ -1,148 +0,0 @@
-// Package cli provides tools to create commands that support advanced configuration features,
-// sub-commands, and allowing configuration from command-line flags, configuration files, and environment variables.
-package cli
-
-import (
- "fmt"
- "io"
- "os"
- "path/filepath"
-)
-
-// Command structure contains program/command information (command name and description).
-type Command struct {
- Name string
- Description string
- Configuration interface{}
- Resources []ResourceLoader
- Run func([]string) error
- CustomHelpFunc func(io.Writer, *Command) error
- Hidden bool
- // AllowArg if not set, disallows any argument that is not a known command or a sub-command.
- AllowArg bool
- subCommands []*Command
-}
-
-// AddCommand Adds a sub command.
-func (c *Command) AddCommand(cmd *Command) error {
- if c == nil || cmd == nil {
- return nil
- }
-
- if c.Name == cmd.Name {
- return fmt.Errorf("child command cannot have the same name as their parent: %s", cmd.Name)
- }
-
- c.subCommands = append(c.subCommands, cmd)
- return nil
-}
-
-// PrintHelp calls the custom help function of the command if it's set.
-// Otherwise, it calls the default help function.
-func (c *Command) PrintHelp(w io.Writer) error {
- if c.CustomHelpFunc != nil {
- return c.CustomHelpFunc(w, c)
- }
- return PrintHelp(w, c)
-}
-
-// Execute Executes a command.
-func Execute(cmd *Command) error {
- return execute(cmd, os.Args, true)
-}
-
-func execute(cmd *Command, args []string, root bool) error {
- // Calls command without args.
- if len(args) == 1 {
- if err := run(cmd, args[1:]); err != nil {
- return fmt.Errorf("command %s error: %w", args[0], err)
- }
- return nil
- }
-
- // Special case: if the command is the top level one,
- // and the first arg (`args[1]`) is not the command name or a known sub-command,
- // then we run the top level command itself.
- if root && cmd.Name != args[1] && !contains(cmd.subCommands, args[1]) {
- if err := run(cmd, args[1:]); err != nil {
- return fmt.Errorf("command %s error: %w", filepath.Base(args[0]), err)
- }
- return nil
- }
-
- // Calls command by its name.
- if len(args) >= 2 && cmd.Name == args[1] {
- if len(args) < 3 || !contains(cmd.subCommands, args[2]) {
- if err := run(cmd, args[2:]); err != nil {
- return fmt.Errorf("command %s error: %w", cmd.Name, err)
- }
- return nil
- }
- }
-
- // No sub-command, calls the current command.
- if len(cmd.subCommands) == 0 {
- if err := run(cmd, args[1:]); err != nil {
- return fmt.Errorf("command %s error: %w", cmd.Name, err)
- }
- return nil
- }
-
- // Trying to find the sub-command.
- for _, subCmd := range cmd.subCommands {
- if len(args) >= 2 && subCmd.Name == args[1] {
- return execute(subCmd, args, false)
- }
- if len(args) >= 3 && subCmd.Name == args[2] {
- return execute(subCmd, args[1:], false)
- }
- }
-
- return fmt.Errorf("command not found: %v", args)
-}
-
-func run(cmd *Command, args []string) error {
- if len(args) > 0 && !isFlag(args[0]) && !cmd.AllowArg {
- _ = cmd.PrintHelp(os.Stdout)
- return fmt.Errorf("command not found: %s", args[0])
- }
-
- if isHelp(args) {
- return cmd.PrintHelp(os.Stdout)
- }
-
- if cmd.Run == nil {
- _ = cmd.PrintHelp(os.Stdout)
- return fmt.Errorf("command %s is not runnable", cmd.Name)
- }
-
- if cmd.Configuration == nil {
- return cmd.Run(args)
- }
-
- for _, resource := range cmd.Resources {
- done, err := resource.Load(args, cmd)
- if err != nil {
- return err
- }
- if done {
- break
- }
- }
-
- return cmd.Run(args)
-}
-
-func contains(cmds []*Command, name string) bool {
- for _, cmd := range cmds {
- if cmd.Name == name {
- return true
- }
- }
-
- return false
-}
-
-func isFlag(arg string) bool {
- return len(arg) > 0 && arg[0] == '-'
-}
diff --git a/pkg/cli/commands_test.go b/pkg/cli/commands_test.go
deleted file mode 100644
index 39fa80a34..000000000
--- a/pkg/cli/commands_test.go
+++ /dev/null
@@ -1,941 +0,0 @@
-package cli
-
-import (
- "bytes"
- "errors"
- "fmt"
- "io"
- "io/ioutil"
- "os"
- "strings"
- "testing"
-
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
-)
-
-func TestCommand_AddCommand(t *testing.T) {
- testCases := []struct {
- desc string
- subCommand *Command
- expectedError bool
- }{
- {
- desc: "sub command nil",
- subCommand: nil,
- },
- {
- desc: "add a simple command",
- subCommand: &Command{
- Name: "sub",
- },
- },
- {
- desc: "add a sub command with the same name as their parent",
- subCommand: &Command{
- Name: "root",
- },
- expectedError: true,
- },
- }
-
- for _, test := range testCases {
- test := test
- t.Run(test.desc, func(t *testing.T) {
- t.Parallel()
-
- rootCmd := &Command{
- Name: "root",
- }
-
- err := rootCmd.AddCommand(test.subCommand)
-
- if test.expectedError {
- require.Error(t, err)
- } else {
- require.NoError(t, err)
- }
- })
- }
-}
-
-func TestCommand_PrintHelp(t *testing.T) {
- testCases := []struct {
- desc string
- command *Command
- expectedOutput string
- expectedError error
- }{
- {
- desc: "print default help",
- command: &Command{},
- expectedOutput: " \n\nUsage: [command] [flags] [arguments]\n\nUse \" [command] --help\" for help on any command.\n\n",
- },
- {
- desc: "print custom help",
- command: &Command{
- Name: "root",
- Description: "Description for root",
- Configuration: &struct {
- Foo []struct {
- Field string
- }
- }{},
- Run: func(args []string) error {
- return nil
- },
- CustomHelpFunc: func(w io.Writer, _ *Command) error {
- _, _ = fmt.Fprintln(w, "test")
- return nil
- },
- },
- expectedOutput: "test\n",
- },
- {
- desc: "error is returned from called help",
- command: &Command{
- CustomHelpFunc: func(_ io.Writer, _ *Command) error {
- return errors.New("test")
- },
- },
- expectedError: errors.New("test"),
- },
- }
-
- for _, test := range testCases {
- test := test
- t.Run(test.desc, func(t *testing.T) {
- t.Parallel()
-
- buffer := &bytes.Buffer{}
- err := test.command.PrintHelp(buffer)
-
- assert.Equal(t, test.expectedError, err)
- assert.Equal(t, test.expectedOutput, buffer.String())
- })
- }
-}
-
-func Test_execute(t *testing.T) {
- var called string
-
- type expected struct {
- result string
- error bool
- }
-
- testCases := []struct {
- desc string
- args []string
- command func() *Command
- expected expected
- }{
- {
- desc: "root command",
- args: []string{""},
- command: func() *Command {
- return &Command{
- Name: "root",
- Description: "This is a test",
- Configuration: nil,
- Run: func(_ []string) error {
- called = "root"
- return nil
- },
- }
- },
- expected: expected{result: "root"},
- },
- {
- desc: "root command, with argument, command not found",
- args: []string{"", "echo"},
- command: func() *Command {
- return &Command{
- Name: "root",
- Description: "This is a test",
- Configuration: nil,
- Run: func(_ []string) error {
- called = "root"
- return nil
- },
- }
- },
- expected: expected{error: true},
- },
- {
- desc: "root command, call help, with argument, command not found",
- args: []string{"", "echo", "--help"},
- command: func() *Command {
- return &Command{
- Name: "root",
- Description: "This is a test",
- Configuration: nil,
- Run: func(_ []string) error {
- called = "root"
- return nil
- },
- }
- },
- expected: expected{error: true},
- },
- {
- desc: "one sub command",
- args: []string{"", "sub1"},
- command: func() *Command {
- rootCmd := &Command{
- Name: "test",
- Description: "This is a test",
- Configuration: nil,
- Run: func(_ []string) error {
- called += "root"
- return nil
- },
- }
-
- _ = rootCmd.AddCommand(&Command{
- Name: "sub1",
- Description: "sub1",
- Configuration: nil,
- Run: func(_ []string) error {
- called += "sub1"
- return nil
- },
- })
-
- return rootCmd
- },
- expected: expected{result: "sub1"},
- },
- {
- desc: "one sub command, with argument, command not found",
- args: []string{"", "sub1", "echo"},
- command: func() *Command {
- rootCmd := &Command{
- Name: "test",
- Description: "This is a test",
- Configuration: nil,
- Run: func(_ []string) error {
- called += "root"
- return nil
- },
- }
-
- _ = rootCmd.AddCommand(&Command{
- Name: "sub1",
- Description: "sub1",
- Configuration: nil,
- Run: func(_ []string) error {
- called += "sub1"
- return nil
- },
- })
-
- return rootCmd
- },
- expected: expected{error: true},
- },
- {
- desc: "two sub commands",
- args: []string{"", "sub2"},
- command: func() *Command {
- rootCmd := &Command{
- Name: "test",
- Description: "This is a test",
- Configuration: nil,
- Run: func(_ []string) error {
- called += "root"
- return nil
- },
- }
-
- _ = rootCmd.AddCommand(&Command{
- Name: "sub1",
- Description: "sub1",
- Configuration: nil,
- Run: func(_ []string) error {
- called += "sub1"
- return nil
- },
- })
-
- _ = rootCmd.AddCommand(&Command{
- Name: "sub2",
- Description: "sub2",
- Configuration: nil,
- Run: func(_ []string) error {
- called += "sub2"
- return nil
- },
- })
-
- return rootCmd
- },
- expected: expected{result: "sub2"},
- },
- {
- desc: "command with sub sub command, call sub command",
- args: []string{"", "sub1"},
- command: func() *Command {
- rootCmd := &Command{
- Name: "test",
- Description: "This is a test",
- Configuration: nil,
- Run: func(_ []string) error {
- called += "root"
- return nil
- },
- }
-
- sub1 := &Command{
- Name: "sub1",
- Description: "sub1",
- Configuration: nil,
- Run: func(_ []string) error {
- called += "sub1"
- return nil
- },
- }
- _ = rootCmd.AddCommand(sub1)
-
- _ = sub1.AddCommand(&Command{
- Name: "sub2",
- Description: "sub2",
- Configuration: nil,
- Run: func(_ []string) error {
- called += "sub2"
- return nil
- },
- })
-
- return rootCmd
- },
- expected: expected{result: "sub1"},
- },
- {
- desc: "command with sub sub command, call sub sub command",
- args: []string{"", "sub1", "sub2"},
- command: func() *Command {
- rootCmd := &Command{
- Name: "test",
- Description: "This is a test",
- Configuration: nil,
- Run: func(_ []string) error {
- called += "root"
- return nil
- },
- }
-
- sub1 := &Command{
- Name: "sub1",
- Description: "sub1",
- Configuration: nil,
- Run: func(_ []string) error {
- called += "sub1"
- return nil
- },
- }
- _ = rootCmd.AddCommand(sub1)
-
- _ = sub1.AddCommand(&Command{
- Name: "sub2",
- Description: "sub2",
- Configuration: nil,
- Run: func(_ []string) error {
- called += "sub2"
- return nil
- },
- })
-
- return rootCmd
- },
- expected: expected{result: "sub2"},
- },
- {
- desc: "command with sub command, call root command explicitly",
- args: []string{"", "root"},
- command: func() *Command {
- rootCmd := &Command{
- Name: "root",
- Description: "This is a test",
- Configuration: nil,
- Run: func(_ []string) error {
- called += "root"
- return nil
- },
- }
-
- _ = rootCmd.AddCommand(&Command{
- Name: "sub1",
- Description: "sub1",
- Configuration: nil,
- Run: func(_ []string) error {
- called += "sub1"
- return nil
- },
- })
-
- return rootCmd
- },
- expected: expected{result: "root"},
- },
- {
- desc: "command with sub command, call root command implicitly",
- args: []string{""},
- command: func() *Command {
- rootCmd := &Command{
- Name: "root",
- Description: "This is a test",
- Configuration: nil,
- Run: func(_ []string) error {
- called += "root"
- return nil
- },
- }
-
- _ = rootCmd.AddCommand(&Command{
- Name: "sub1",
- Description: "sub1",
- Configuration: nil,
- Run: func(_ []string) error {
- called += "sub1"
- return nil
- },
- })
-
- return rootCmd
- },
- expected: expected{result: "root"},
- },
- {
- desc: "command with sub command, call sub command which has no run",
- args: []string{"", "sub1"},
- command: func() *Command {
- rootCmd := &Command{
- Name: "root",
- Description: "This is a test",
- Configuration: nil,
- Run: func(_ []string) error {
- called += "root"
- return nil
- },
- }
-
- _ = rootCmd.AddCommand(&Command{
- Name: "sub1",
- Description: "sub1",
- Configuration: nil,
- })
-
- return rootCmd
- },
- expected: expected{error: true},
- },
- {
- desc: "command with sub command, call root command which has no run",
- args: []string{"", "root"},
- command: func() *Command {
- rootCmd := &Command{
- Name: "root",
- Description: "This is a test",
- Configuration: nil,
- }
-
- _ = rootCmd.AddCommand(&Command{
- Name: "sub1",
- Description: "sub1",
- Configuration: nil,
- Run: func(_ []string) error {
- called += "sub1"
- return nil
- },
- })
-
- return rootCmd
- },
- expected: expected{error: true},
- },
- {
- desc: "command with sub command, call implicitly root command which has no run",
- args: []string{""},
- command: func() *Command {
- rootCmd := &Command{
- Name: "root",
- Description: "This is a test",
- Configuration: nil,
- }
-
- _ = rootCmd.AddCommand(&Command{
- Name: "sub1",
- Description: "sub1",
- Configuration: nil,
- Run: func(_ []string) error {
- called += "sub1"
- return nil
- },
- })
-
- return rootCmd
- },
- expected: expected{error: true},
- },
- {
- desc: "command with sub command, call sub command with arguments",
- args: []string{"", "sub1", "foobar.txt"},
- command: func() *Command {
- rootCmd := &Command{
- Name: "root",
- Description: "This is a test",
- Configuration: nil,
- Run: func(_ []string) error {
- called = "root"
- return nil
- },
- }
-
- _ = rootCmd.AddCommand(&Command{
- Name: "sub1",
- Description: "sub1",
- Configuration: nil,
- AllowArg: true,
- Run: func(args []string) error {
- called += "sub1-" + strings.Join(args, "-")
- return nil
- },
- })
-
- return rootCmd
- },
- expected: expected{result: "sub1-foobar.txt"},
- },
- {
- desc: "command with sub command, call root command with arguments",
- args: []string{"", "foobar.txt"},
- command: func() *Command {
- rootCmd := &Command{
- Name: "root",
- Description: "This is a test",
- Configuration: nil,
- AllowArg: true,
- Run: func(args []string) error {
- called += "root-" + strings.Join(args, "-")
- return nil
- },
- }
-
- _ = rootCmd.AddCommand(&Command{
- Name: "sub1",
- Description: "sub1",
- Configuration: nil,
- Run: func(args []string) error {
- called += "sub1-" + strings.Join(args, "-")
- return nil
- },
- })
-
- return rootCmd
- },
- expected: expected{result: "root-foobar.txt"},
- },
- {
- desc: "command with sub command, call sub command with flags",
- args: []string{"", "sub1", "--foo=bar", "--fii=bir"},
- command: func() *Command {
- rootCmd := &Command{
- Name: "root",
- Description: "This is a test",
- Configuration: nil,
- Run: func(_ []string) error {
- called = "root"
- return nil
- },
- }
-
- _ = rootCmd.AddCommand(&Command{
- Name: "sub1",
- Description: "sub1",
- Configuration: nil,
- Run: func(args []string) error {
- called += "sub1-" + strings.Join(args, "")
- return nil
- },
- })
-
- return rootCmd
- },
- expected: expected{result: "sub1---foo=bar--fii=bir"},
- },
- {
- desc: "command with sub command, call explicitly root command with flags",
- args: []string{"", "root", "--foo=bar", "--fii=bir"},
- command: func() *Command {
- rootCmd := &Command{
- Name: "root",
- Description: "This is a test",
- Configuration: nil,
- Run: func(args []string) error {
- called += "root-" + strings.Join(args, "")
- return nil
- },
- }
-
- _ = rootCmd.AddCommand(&Command{
- Name: "sub1",
- Description: "sub1",
- Configuration: nil,
- Run: func(args []string) error {
- called += "sub1-" + strings.Join(args, "")
- return nil
- },
- })
-
- return rootCmd
- },
- expected: expected{result: "root---foo=bar--fii=bir"},
- },
- {
- desc: "command with sub command, call implicitly root command with flags",
- args: []string{"", "--foo=bar", "--fii=bir"},
- command: func() *Command {
- rootCmd := &Command{
- Name: "root",
- Description: "This is a test",
- Configuration: nil,
- Run: func(args []string) error {
- called += "root-" + strings.Join(args, "")
- return nil
- },
- }
-
- _ = rootCmd.AddCommand(&Command{
- Name: "sub1",
- Description: "sub1",
- Configuration: nil,
- Run: func(args []string) error {
- called += "sub1-" + strings.Join(args, "")
- return nil
- },
- })
-
- return rootCmd
- },
- expected: expected{result: "root---foo=bar--fii=bir"},
- },
- {
- desc: "sub command help",
- args: []string{"", "test", "subtest", "--help"},
- command: func() *Command {
- rootCmd := &Command{
- Name: "test",
- Resources: []ResourceLoader{&FlagLoader{}},
- }
-
- subCmd := &Command{
- Name: "subtest",
- Resources: []ResourceLoader{&FlagLoader{}},
- }
-
- err := rootCmd.AddCommand(subCmd)
- require.NoError(t, err)
-
- subSubCmd := &Command{
- Name: "subsubtest",
- Resources: []ResourceLoader{&FlagLoader{}},
- }
-
- err = subCmd.AddCommand(subSubCmd)
- require.NoError(t, err)
-
- subSubSubCmd := &Command{
- Name: "subsubsubtest",
- Resources: []ResourceLoader{&FlagLoader{}},
- Run: func([]string) error {
- called = "subsubsubtest"
- return nil
- },
- }
-
- err = subSubCmd.AddCommand(subSubSubCmd)
- require.NoError(t, err)
-
- return rootCmd
- },
- expected: expected{},
- },
- {
- desc: "sub sub command help",
- args: []string{"", "test", "subtest", "subsubtest", "--help"},
- command: func() *Command {
- rootCmd := &Command{
- Name: "test",
- Resources: []ResourceLoader{&FlagLoader{}},
- }
-
- subCmd := &Command{
- Name: "subtest",
- Resources: []ResourceLoader{&FlagLoader{}},
- }
-
- err := rootCmd.AddCommand(subCmd)
- require.NoError(t, err)
-
- subSubCmd := &Command{
- Name: "subsubtest",
- Resources: []ResourceLoader{&FlagLoader{}},
- }
-
- err = subCmd.AddCommand(subSubCmd)
- require.NoError(t, err)
-
- subSubSubCmd := &Command{
- Name: "subsubsubtest",
- Resources: []ResourceLoader{&FlagLoader{}},
- Run: func([]string) error {
- called = "subsubsubtest"
- return nil
- },
- }
-
- err = subSubCmd.AddCommand(subSubSubCmd)
- require.NoError(t, err)
-
- return rootCmd
- },
- expected: expected{},
- },
- }
-
- for _, test := range testCases {
- t.Run(test.desc, func(t *testing.T) {
- defer func() {
- called = ""
- }()
-
- err := execute(test.command(), test.args, true)
-
- if test.expected.error {
- require.Error(t, err)
- } else {
- require.NoError(t, err)
- assert.Equal(t, test.expected.result, called)
- }
- })
- }
-}
-
-func Test_execute_configuration(t *testing.T) {
- rootCmd := &Command{
- Name: "root",
- Description: "This is a test",
- Configuration: nil,
- Run: func(_ []string) error {
- return nil
- },
- }
-
- element := &Yo{
- Fuu: "test",
- }
-
- sub1 := &Command{
- Name: "sub1",
- Description: "sub1",
- Configuration: element,
- Resources: []ResourceLoader{&FlagLoader{}},
- Run: func(args []string) error {
- return nil
- },
- }
- err := rootCmd.AddCommand(sub1)
- require.NoError(t, err)
-
- args := []string{"", "sub1", "--foo=bar", "--fii=bir", "--yi"}
-
- err = execute(rootCmd, args, true)
- require.NoError(t, err)
-
- expected := &Yo{
- Foo: "bar",
- Fii: "bir",
- Fuu: "test",
- Yi: &Yi{
- Foo: "foo",
- Fii: "fii",
- },
- }
- assert.Equal(t, expected, element)
-}
-
-func Test_execute_configuration_file(t *testing.T) {
- testCases := []struct {
- desc string
- args []string
- }{
- {
- desc: "configFile arg in camel case",
- args: []string{"", "sub1", "--configFile=./fixtures/config.toml"},
- },
- {
- desc: "configfile arg in lower case",
- args: []string{"", "sub1", "--configfile=./fixtures/config.toml"},
- },
- }
-
- for _, test := range testCases {
- t.Run(test.desc, func(t *testing.T) {
- rootCmd := &Command{
- Name: "root",
- Description: "This is a test",
- Configuration: nil,
- Run: func(_ []string) error {
- return nil
- },
- }
-
- element := &Yo{
- Fuu: "test",
- }
-
- sub1 := &Command{
- Name: "sub1",
- Description: "sub1",
- Configuration: element,
- Resources: []ResourceLoader{&FileLoader{}, &FlagLoader{}},
- Run: func(args []string) error {
- return nil
- },
- }
- err := rootCmd.AddCommand(sub1)
- require.NoError(t, err)
-
- err = execute(rootCmd, test.args, true)
- require.NoError(t, err)
-
- expected := &Yo{
- Foo: "bar",
- Fii: "bir",
- Fuu: "test",
- Yi: &Yi{
- Foo: "foo",
- Fii: "fii",
- },
- }
- assert.Equal(t, expected, element)
- })
- }
-}
-
-func Test_execute_help(t *testing.T) {
- element := &Yo{
- Fuu: "test",
- }
-
- rooCmd := &Command{
- Name: "root",
- Description: "Description for root",
- Configuration: element,
- Run: func(args []string) error {
- return nil
- },
- }
-
- args := []string{"", "--help", "--foo"}
-
- backupStdout := os.Stdout
- defer func() {
- os.Stdout = backupStdout
- }()
-
- r, w, _ := os.Pipe()
- os.Stdout = w
-
- err := execute(rooCmd, args, true)
- if err != nil {
- return
- }
-
- // read and restore stdout
- if err = w.Close(); err != nil {
- t.Fatal(err)
- }
- out, err := ioutil.ReadAll(r)
- if err != nil {
- t.Fatal(err)
- }
-
- os.Stdout = backupStdout
-
- assert.Equal(t, `root Description for root
-
-Usage: root [command] [flags] [arguments]
-
-Use "root [command] --help" for help on any command.
-
-Flag's usage: root [--flag=flag_argument] [-f [flag_argument]] # set flag_argument to flag(s)
- or: root [--flag[=true|false| ]] [-f [true|false| ]] # set true/false to boolean flag(s)
-
-Flags:
- --fii (Default: "fii")
- Fii description
-
- --foo (Default: "foo")
- Foo description
-
- --fuu (Default: "test")
- Fuu description
-
- --yi (Default: "false")
-
- --yi.fii (Default: "fii")
-
- --yi.foo (Default: "foo")
-
- --yi.fuu (Default: "")
-
- --yu.fii (Default: "fii")
-
- --yu.foo (Default: "foo")
-
- --yu.fuu (Default: "")
-
-`, string(out))
-}
-
-func TestName(t *testing.T) {
- rootCmd := &Command{
- Name: "test",
- Resources: []ResourceLoader{&FlagLoader{}},
- }
-
- subCmd := &Command{
- Name: "subtest",
- Resources: []ResourceLoader{&FlagLoader{}},
- }
-
- err := rootCmd.AddCommand(subCmd)
- require.NoError(t, err)
-
- subSubCmd := &Command{
- Name: "subsubtest",
- Resources: []ResourceLoader{&FlagLoader{}},
- Run: func([]string) error {
- return nil
- },
- }
-
- err = subCmd.AddCommand(subSubCmd)
- require.NoError(t, err)
-
- subSubSubCmd := &Command{
- Name: "subsubsubtest",
- Resources: []ResourceLoader{&FlagLoader{}},
- Run: func([]string) error {
- return nil
- },
- }
-
- err = subSubCmd.AddCommand(subSubSubCmd)
- require.NoError(t, err)
-
- err = execute(rootCmd, []string{"", "test", "subtest", "subsubtest", "subsubsubtest", "--help"}, true)
- require.NoError(t, err)
-}
diff --git a/pkg/cli/file_finder.go b/pkg/cli/file_finder.go
deleted file mode 100644
index b0b724aa3..000000000
--- a/pkg/cli/file_finder.go
+++ /dev/null
@@ -1,50 +0,0 @@
-package cli
-
-import (
- "os"
- "path/filepath"
- "strings"
-)
-
-// Finder holds a list of file paths.
-type Finder struct {
- BasePaths []string
- Extensions []string
-}
-
-// Find returns the first valid existing file among configFile
-// and the paths already registered with Finder.
-func (f Finder) Find(configFile string) (string, error) {
- paths := f.getPaths(configFile)
-
- for _, filePath := range paths {
- fp := os.ExpandEnv(filePath)
-
- _, err := os.Stat(fp)
- if os.IsNotExist(err) {
- continue
- }
- if err != nil {
- return "", err
- }
-
- return filepath.Abs(fp)
- }
-
- return "", nil
-}
-
-func (f Finder) getPaths(configFile string) []string {
- var paths []string
- if strings.TrimSpace(configFile) != "" {
- paths = append(paths, configFile)
- }
-
- for _, basePath := range f.BasePaths {
- for _, ext := range f.Extensions {
- paths = append(paths, basePath+"."+ext)
- }
- }
-
- return paths
-}
diff --git a/pkg/cli/file_finder_test.go b/pkg/cli/file_finder_test.go
deleted file mode 100644
index c0b8971c1..000000000
--- a/pkg/cli/file_finder_test.go
+++ /dev/null
@@ -1,161 +0,0 @@
-package cli
-
-import (
- "io/ioutil"
- "os"
- "path/filepath"
- "testing"
-
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
-)
-
-func TestFinder_Find(t *testing.T) {
- configFile, err := ioutil.TempFile("", "traefik-file-finder-test-*.toml")
- require.NoError(t, err)
-
- defer func() {
- _ = os.Remove(configFile.Name())
- }()
-
- dir, err := ioutil.TempDir("", "traefik-file-finder-test")
- require.NoError(t, err)
-
- defer func() {
- _ = os.RemoveAll(dir)
- }()
-
- fooFile, err := os.Create(filepath.Join(dir, "foo.toml"))
- require.NoError(t, err)
-
- _, err = os.Create(filepath.Join(dir, "bar.toml"))
- require.NoError(t, err)
-
- type expected struct {
- error bool
- path string
- }
-
- testCases := []struct {
- desc string
- basePaths []string
- configFile string
- expected expected
- }{
- {
- desc: "not found: no config file",
- configFile: "",
- expected: expected{path: ""},
- },
- {
- desc: "not found: no config file, no other paths available",
- configFile: "",
- basePaths: []string{"/my/path/traefik", "$HOME/my/path/traefik", "./my-traefik"},
- expected: expected{path: ""},
- },
- {
- desc: "not found: with non existing config file",
- configFile: "/my/path/config.toml",
- expected: expected{path: ""},
- },
- {
- desc: "found: with config file",
- configFile: configFile.Name(),
- expected: expected{path: configFile.Name()},
- },
- {
- desc: "found: no config file, first base path",
- configFile: "",
- basePaths: []string{filepath.Join(dir, "foo"), filepath.Join(dir, "bar")},
- expected: expected{path: fooFile.Name()},
- },
- {
- desc: "found: no config file, base path",
- configFile: "",
- basePaths: []string{"/my/path/traefik", "$HOME/my/path/traefik", filepath.Join(dir, "foo")},
- expected: expected{path: fooFile.Name()},
- },
- {
- desc: "found: config file over base path",
- configFile: configFile.Name(),
- basePaths: []string{filepath.Join(dir, "foo"), filepath.Join(dir, "bar")},
- expected: expected{path: configFile.Name()},
- },
- }
-
- for _, test := range testCases {
- t.Run(test.desc, func(t *testing.T) {
- finder := Finder{
- BasePaths: test.basePaths,
- Extensions: []string{"toml", "yaml", "yml"},
- }
-
- path, err := finder.Find(test.configFile)
-
- if test.expected.error {
- require.Error(t, err)
- } else {
- require.NoError(t, err)
- assert.Equal(t, test.expected.path, path)
- }
- })
- }
-}
-
-func TestFinder_getPaths(t *testing.T) {
- testCases := []struct {
- desc string
- basePaths []string
- configFile string
- expected []string
- }{
- {
- desc: "no config file",
- basePaths: []string{"/etc/traefik/traefik", "$HOME/.config/traefik", "./traefik"},
- configFile: "",
- expected: []string{
- "/etc/traefik/traefik.toml",
- "/etc/traefik/traefik.yaml",
- "/etc/traefik/traefik.yml",
- "$HOME/.config/traefik.toml",
- "$HOME/.config/traefik.yaml",
- "$HOME/.config/traefik.yml",
- "./traefik.toml",
- "./traefik.yaml",
- "./traefik.yml",
- },
- },
- {
- desc: "with config file",
- basePaths: []string{"/etc/traefik/traefik", "$HOME/.config/traefik", "./traefik"},
- configFile: "/my/path/config.toml",
- expected: []string{
- "/my/path/config.toml",
- "/etc/traefik/traefik.toml",
- "/etc/traefik/traefik.yaml",
- "/etc/traefik/traefik.yml",
- "$HOME/.config/traefik.toml",
- "$HOME/.config/traefik.yaml",
- "$HOME/.config/traefik.yml",
- "./traefik.toml",
- "./traefik.yaml",
- "./traefik.yml",
- },
- },
- }
-
- for _, test := range testCases {
- test := test
- t.Run(test.desc, func(t *testing.T) {
- t.Parallel()
-
- finder := Finder{
- BasePaths: test.basePaths,
- Extensions: []string{"toml", "yaml", "yml"},
- }
- paths := finder.getPaths(test.configFile)
-
- assert.Equal(t, test.expected, paths)
- })
- }
-}
diff --git a/pkg/cli/fixtures/config.toml b/pkg/cli/fixtures/config.toml
deleted file mode 100644
index 72153e418..000000000
--- a/pkg/cli/fixtures/config.toml
+++ /dev/null
@@ -1,3 +0,0 @@
-foo = "bar"
-fii = "bir"
-[yi]
diff --git a/pkg/cli/fixtures_test.go b/pkg/cli/fixtures_test.go
deleted file mode 100644
index 95718636f..000000000
--- a/pkg/cli/fixtures_test.go
+++ /dev/null
@@ -1,25 +0,0 @@
-package cli
-
-type Yo struct {
- Foo string `description:"Foo description"`
- Fii string `description:"Fii description"`
- Fuu string `description:"Fuu description"`
- Yi *Yi `label:"allowEmpty" file:"allowEmpty"`
- Yu *Yi
-}
-
-func (y *Yo) SetDefaults() {
- y.Foo = "foo"
- y.Fii = "fii"
-}
-
-type Yi struct {
- Foo string
- Fii string
- Fuu string
-}
-
-func (y *Yi) SetDefaults() {
- y.Foo = "foo"
- y.Fii = "fii"
-}
diff --git a/pkg/cli/help.go b/pkg/cli/help.go
deleted file mode 100644
index 7926ca7ec..000000000
--- a/pkg/cli/help.go
+++ /dev/null
@@ -1,89 +0,0 @@
-package cli
-
-import (
- "io"
- "strings"
- "text/tabwriter"
- "text/template"
-
- "github.com/Masterminds/sprig"
- "github.com/containous/traefik/v2/pkg/config/flag"
- "github.com/containous/traefik/v2/pkg/config/generator"
- "github.com/containous/traefik/v2/pkg/config/parser"
-)
-
-const tmplHelp = `{{ .Cmd.Name }} {{ .Cmd.Description }}
-
-Usage: {{ .Cmd.Name }} [command] [flags] [arguments]
-
-Use "{{ .Cmd.Name }} [command] --help" for help on any command.
-{{if .SubCommands }}
-Commands:
-{{- range $i, $subCmd := .SubCommands }}
-{{ if not $subCmd.Hidden }} {{ $subCmd.Name }} {{ $subCmd.Description }}{{end}}{{end}}
-{{end}}
-{{- if .Flags }}
-Flag's usage: {{ .Cmd.Name }} [--flag=flag_argument] [-f [flag_argument]] # set flag_argument to flag(s)
- or: {{ .Cmd.Name }} [--flag[=true|false| ]] [-f [true|false| ]] # set true/false to boolean flag(s)
-
-Flags:
-{{- range $i, $flag := .Flags }}
- --{{ SliceIndexN $flag.Name }} {{if ne $flag.Name "global.sendanonymoususage"}}(Default: "{{ $flag.Default}}"){{end}}
-{{if $flag.Description }} {{ wrapWith 80 "\n\t\t" $flag.Description }}
-{{else}}
-{{- end}}
-{{- end}}
-{{- end}}
-`
-
-func isHelp(args []string) bool {
- for _, name := range args {
- if name == "--help" || name == "-help" || name == "-h" {
- return true
- }
- }
- return false
-}
-
-// PrintHelp prints the help for the command given as argument.
-func PrintHelp(w io.Writer, cmd *Command) error {
- var flags []parser.Flat
- if cmd.Configuration != nil {
- generator.Generate(cmd.Configuration)
-
- var err error
- flags, err = flag.Encode(cmd.Configuration)
- if err != nil {
- return err
- }
- }
-
- model := map[string]interface{}{
- "Cmd": cmd,
- "Flags": flags,
- "SubCommands": cmd.subCommands,
- }
-
- funcs := sprig.TxtFuncMap()
- funcs["SliceIndexN"] = sliceIndexN
-
- tmpl, err := template.New("flags").
- Funcs(funcs).
- Parse(tmplHelp)
- if err != nil {
- return err
- }
-
- tw := tabwriter.NewWriter(w, 4, 0, 4, ' ', 0)
-
- err = tmpl.Execute(tw, model)
- if err != nil {
- return err
- }
-
- return tw.Flush()
-}
-
-func sliceIndexN(flag string) string {
- return strings.ReplaceAll(flag, "[0]", "[n]")
-}
diff --git a/pkg/cli/help_test.go b/pkg/cli/help_test.go
deleted file mode 100644
index 768f74a2a..000000000
--- a/pkg/cli/help_test.go
+++ /dev/null
@@ -1,211 +0,0 @@
-package cli
-
-import (
- "bytes"
- "testing"
-
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
-)
-
-func TestPrintHelp(t *testing.T) {
- testCases := []struct {
- desc string
- command *Command
- expected string
- }{
- {
- desc: "no sub-command, with flags",
- command: func() *Command {
- element := &Yo{
- Fuu: "test",
- }
-
- return &Command{
- Name: "root",
- Description: "Description for root",
- Configuration: element,
- Run: func(args []string) error {
- return nil
- },
- }
- }(),
- expected: `root Description for root
-
-Usage: root [command] [flags] [arguments]
-
-Use "root [command] --help" for help on any command.
-
-Flag's usage: root [--flag=flag_argument] [-f [flag_argument]] # set flag_argument to flag(s)
- or: root [--flag[=true|false| ]] [-f [true|false| ]] # set true/false to boolean flag(s)
-
-Flags:
- --fii (Default: "fii")
- Fii description
-
- --foo (Default: "foo")
- Foo description
-
- --fuu (Default: "test")
- Fuu description
-
- --yi (Default: "false")
-
- --yi.fii (Default: "fii")
-
- --yi.foo (Default: "foo")
-
- --yi.fuu (Default: "")
-
- --yu.fii (Default: "fii")
-
- --yu.foo (Default: "foo")
-
- --yu.fuu (Default: "")
-
-`,
- },
- {
- desc: "with sub-commands, with flags, call root help",
- command: func() *Command {
- element := &Yo{
- Fuu: "test",
- }
-
- rootCmd := &Command{
- Name: "root",
- Description: "Description for root",
- Configuration: element,
- Run: func(_ []string) error {
- return nil
- },
- }
-
- err := rootCmd.AddCommand(&Command{
- Name: "sub1",
- Description: "Description for sub1",
- Configuration: element,
- Run: func(args []string) error {
- return nil
- },
- })
- require.NoError(t, err)
-
- err = rootCmd.AddCommand(&Command{
- Name: "sub2",
- Description: "Description for sub2",
- Configuration: element,
- Run: func(args []string) error {
- return nil
- },
- })
- require.NoError(t, err)
-
- return rootCmd
- }(),
- expected: `root Description for root
-
-Usage: root [command] [flags] [arguments]
-
-Use "root [command] --help" for help on any command.
-
-Commands:
- sub1 Description for sub1
- sub2 Description for sub2
-
-Flag's usage: root [--flag=flag_argument] [-f [flag_argument]] # set flag_argument to flag(s)
- or: root [--flag[=true|false| ]] [-f [true|false| ]] # set true/false to boolean flag(s)
-
-Flags:
- --fii (Default: "fii")
- Fii description
-
- --foo (Default: "foo")
- Foo description
-
- --fuu (Default: "test")
- Fuu description
-
- --yi (Default: "false")
-
- --yi.fii (Default: "fii")
-
- --yi.foo (Default: "foo")
-
- --yi.fuu (Default: "")
-
- --yu.fii (Default: "fii")
-
- --yu.foo (Default: "foo")
-
- --yu.fuu (Default: "")
-
-`,
- },
- {
- desc: "no sub-command, no flags",
- command: func() *Command {
- return &Command{
- Name: "root",
- Description: "Description for root",
- Configuration: nil,
- Run: func(args []string) error {
- return nil
- },
- }
- }(),
- expected: `root Description for root
-
-Usage: root [command] [flags] [arguments]
-
-Use "root [command] --help" for help on any command.
-
-`,
- },
- {
- desc: "no sub-command, slice flags",
- command: func() *Command {
- return &Command{
- Name: "root",
- Description: "Description for root",
- Configuration: &struct {
- Foo []struct {
- Field string
- }
- }{},
- Run: func(args []string) error {
- return nil
- },
- }
- }(),
- expected: `root Description for root
-
-Usage: root [command] [flags] [arguments]
-
-Use "root [command] --help" for help on any command.
-
-Flag's usage: root [--flag=flag_argument] [-f [flag_argument]] # set flag_argument to flag(s)
- or: root [--flag[=true|false| ]] [-f [true|false| ]] # set true/false to boolean flag(s)
-
-Flags:
- --foo (Default: "")
-
- --foo[n].field (Default: "")
-
-`,
- },
- }
-
- for _, test := range testCases {
- test := test
- t.Run(test.desc, func(t *testing.T) {
- t.Parallel()
-
- buffer := &bytes.Buffer{}
- err := PrintHelp(buffer, test.command)
- require.NoError(t, err)
-
- assert.Equal(t, test.expected, buffer.String())
- })
- }
-}
diff --git a/pkg/cli/loader.go b/pkg/cli/loader.go
deleted file mode 100644
index 84f08d68d..000000000
--- a/pkg/cli/loader.go
+++ /dev/null
@@ -1,7 +0,0 @@
-package cli
-
-// ResourceLoader is a configuration resource loader.
-type ResourceLoader interface {
- // Load populates cmd.Configuration, optionally using args to do so.
- Load(args []string, cmd *Command) (bool, error)
-}
diff --git a/pkg/cli/loader_env.go b/pkg/cli/loader_env.go
index ae605f684..79a6a85ce 100644
--- a/pkg/cli/loader_env.go
+++ b/pkg/cli/loader_env.go
@@ -5,15 +5,16 @@ import (
"os"
"strings"
- "github.com/containous/traefik/v2/pkg/config/env"
"github.com/containous/traefik/v2/pkg/log"
+ "github.com/traefik/paerser/cli"
+ "github.com/traefik/paerser/env"
)
// EnvLoader loads a configuration from all the environment variables prefixed with "TRAEFIK_".
type EnvLoader struct{}
// Load loads the command's configuration from the environment variables.
-func (e *EnvLoader) Load(_ []string, cmd *Command) (bool, error) {
+func (e *EnvLoader) Load(_ []string, cmd *cli.Command) (bool, error) {
vars := env.FindPrefixedEnvVars(os.Environ(), env.DefaultNamePrefix, cmd.Configuration)
if len(vars) == 0 {
return false, nil
diff --git a/pkg/cli/loader_file.go b/pkg/cli/loader_file.go
index d6ad58b7b..4da7285db 100644
--- a/pkg/cli/loader_file.go
+++ b/pkg/cli/loader_file.go
@@ -5,9 +5,10 @@ import (
"os"
"strings"
- "github.com/containous/traefik/v2/pkg/config/file"
- "github.com/containous/traefik/v2/pkg/config/flag"
"github.com/containous/traefik/v2/pkg/log"
+ "github.com/traefik/paerser/cli"
+ "github.com/traefik/paerser/file"
+ "github.com/traefik/paerser/flag"
)
// FileLoader loads a configuration from a file.
@@ -22,7 +23,7 @@ func (f *FileLoader) GetFilename() string {
}
// Load loads the command's configuration from a file either specified with the -traefik.configfile flag, or from default locations.
-func (f *FileLoader) Load(args []string, cmd *Command) (bool, error) {
+func (f *FileLoader) Load(args []string, cmd *cli.Command) (bool, error) {
ref, err := flag.Parse(args, cmd.Configuration)
if err != nil {
_ = cmd.PrintHelp(os.Stdout)
@@ -64,7 +65,7 @@ func (f *FileLoader) Load(args []string, cmd *Command) (bool, error) {
// loadConfigFiles tries to decode the given configuration file and all default locations for the configuration file.
// It stops as soon as decoding one of them is successful.
func loadConfigFiles(configFile string, element interface{}) (string, error) {
- finder := Finder{
+ finder := cli.Finder{
BasePaths: []string{"/etc/traefik/traefik", "$XDG_CONFIG_HOME/traefik", "$HOME/.config/traefik", "./traefik"},
Extensions: []string{"toml", "yaml", "yml"},
}
diff --git a/pkg/cli/loader_flag.go b/pkg/cli/loader_flag.go
index 494f7ad5f..343975c03 100644
--- a/pkg/cli/loader_flag.go
+++ b/pkg/cli/loader_flag.go
@@ -3,15 +3,16 @@ package cli
import (
"fmt"
- "github.com/containous/traefik/v2/pkg/config/flag"
"github.com/containous/traefik/v2/pkg/log"
+ "github.com/traefik/paerser/cli"
+ "github.com/traefik/paerser/flag"
)
// FlagLoader loads configuration from flags.
type FlagLoader struct{}
// Load loads the command's configuration from flag arguments.
-func (*FlagLoader) Load(args []string, cmd *Command) (bool, error) {
+func (*FlagLoader) Load(args []string, cmd *cli.Command) (bool, error) {
if len(args) == 0 {
return false, nil
}
diff --git a/pkg/config/dynamic/http_config.go b/pkg/config/dynamic/http_config.go
index ad99ba8e8..6488af069 100644
--- a/pkg/config/dynamic/http_config.go
+++ b/pkg/config/dynamic/http_config.go
@@ -178,9 +178,9 @@ type HealthCheck struct {
Scheme string `json:"scheme,omitempty" toml:"scheme,omitempty" yaml:"scheme,omitempty"`
Path string `json:"path,omitempty" toml:"path,omitempty" yaml:"path,omitempty"`
Port int `json:"port,omitempty" toml:"port,omitempty,omitzero" yaml:"port,omitempty"`
- // FIXME change string to types.Duration
+ // FIXME change string to ptypes.Duration
Interval string `json:"interval,omitempty" toml:"interval,omitempty" yaml:"interval,omitempty"`
- // FIXME change string to types.Duration
+ // FIXME change string to ptypes.Duration
Timeout string `json:"timeout,omitempty" toml:"timeout,omitempty" yaml:"timeout,omitempty"`
Hostname string `json:"hostname,omitempty" toml:"hostname,omitempty" yaml:"hostname,omitempty"`
FollowRedirects *bool `json:"followRedirects" toml:"followRedirects" yaml:"followRedirects"`
diff --git a/pkg/config/dynamic/middlewares.go b/pkg/config/dynamic/middlewares.go
index 18114cc8e..9490d7eb9 100644
--- a/pkg/config/dynamic/middlewares.go
+++ b/pkg/config/dynamic/middlewares.go
@@ -9,7 +9,7 @@ import (
"time"
"github.com/containous/traefik/v2/pkg/ip"
- "github.com/containous/traefik/v2/pkg/types"
+ ptypes "github.com/traefik/paerser/types"
)
// +k8s:deepcopy-gen=true
@@ -319,7 +319,7 @@ type RateLimit struct {
// Period, in combination with Average, defines the actual maximum rate, such as:
// r = Average / Period. It defaults to a second.
- Period types.Duration `json:"period,omitempty" toml:"period,omitempty" yaml:"period,omitempty"`
+ Period ptypes.Duration `json:"period,omitempty" toml:"period,omitempty" yaml:"period,omitempty"`
// Burst is the maximum number of requests allowed to arrive in the same arbitrarily small period of time.
// It defaults to 1.
@@ -331,7 +331,7 @@ type RateLimit struct {
// SetDefaults sets the default values on a RateLimit.
func (r *RateLimit) SetDefaults() {
r.Burst = 1
- r.Period = types.Duration(time.Second)
+ r.Period = ptypes.Duration(time.Second)
}
// +k8s:deepcopy-gen=true
diff --git a/pkg/config/env/env.go b/pkg/config/env/env.go
deleted file mode 100644
index 3e851f432..000000000
--- a/pkg/config/env/env.go
+++ /dev/null
@@ -1,77 +0,0 @@
-// Package env implements encoding and decoding between environment variable and a typed Configuration.
-package env
-
-import (
- "fmt"
- "regexp"
- "strings"
-
- "github.com/containous/traefik/v2/pkg/config/parser"
-)
-
-// DefaultNamePrefix is the default prefix for environment variable names.
-const DefaultNamePrefix = "TRAEFIK_"
-
-// Decode decodes the given environment variables into the given element.
-// The operation goes through four stages roughly summarized as:
-// env vars -> map
-// map -> tree of untyped nodes
-// untyped nodes -> nodes augmented with metadata such as kind (inferred from element)
-// "typed" nodes -> typed element.
-func Decode(environ []string, prefix string, element interface{}) error {
- if err := checkPrefix(prefix); err != nil {
- return err
- }
-
- vars := make(map[string]string)
- for _, evr := range environ {
- n := strings.SplitN(evr, "=", 2)
- if strings.HasPrefix(strings.ToUpper(n[0]), prefix) {
- key := strings.ReplaceAll(strings.ToLower(n[0]), "_", ".")
- vars[key] = n[1]
- }
- }
-
- rootName := strings.ToLower(prefix[:len(prefix)-1])
- return parser.Decode(vars, element, rootName)
-}
-
-// Encode encodes the configuration in element into the environment variables represented in the returned Flats.
-// The operation goes through three stages roughly summarized as:
-// typed configuration in element -> tree of untyped nodes
-// untyped nodes -> nodes augmented with metadata such as kind (inferred from element)
-// "typed" nodes -> environment variables with default values (determined by type/kind).
-func Encode(element interface{}) ([]parser.Flat, error) {
- if element == nil {
- return nil, nil
- }
-
- etnOpts := parser.EncoderToNodeOpts{OmitEmpty: false, TagName: parser.TagLabel, AllowSliceAsStruct: true}
- node, err := parser.EncodeToNode(element, parser.DefaultRootName, etnOpts)
- if err != nil {
- return nil, err
- }
-
- metaOpts := parser.MetadataOpts{TagName: parser.TagLabel, AllowSliceAsStruct: true}
- err = parser.AddMetadata(element, node, metaOpts)
- if err != nil {
- return nil, err
- }
-
- flatOpts := parser.FlatOpts{Case: "upper", Separator: "_", TagName: parser.TagLabel}
- return parser.EncodeToFlat(element, node, flatOpts)
-}
-
-func checkPrefix(prefix string) error {
- prefixPattern := `[a-zA-Z0-9]+_`
- matched, err := regexp.MatchString(prefixPattern, prefix)
- if err != nil {
- return err
- }
-
- if !matched {
- return fmt.Errorf("invalid prefix %q, the prefix pattern must match the following pattern: %s", prefix, prefixPattern)
- }
-
- return nil
-}
diff --git a/pkg/config/env/env_test.go b/pkg/config/env/env_test.go
deleted file mode 100644
index 9e399d283..000000000
--- a/pkg/config/env/env_test.go
+++ /dev/null
@@ -1,462 +0,0 @@
-package env
-
-import (
- "testing"
-
- "github.com/containous/traefik/v2/pkg/config/generator"
- "github.com/containous/traefik/v2/pkg/config/parser"
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
-)
-
-func TestDecode(t *testing.T) {
- testCases := []struct {
- desc string
- environ []string
- element interface{}
- expected interface{}
- }{
- {
- desc: "no env vars",
- environ: nil,
- expected: nil,
- },
- {
- desc: "bool value",
- environ: []string{"TRAEFIK_FOO=true"},
- element: &struct {
- Foo bool
- }{},
- expected: &struct {
- Foo bool
- }{
- Foo: true,
- },
- },
- {
- desc: "equal",
- environ: []string{"TRAEFIK_FOO=bar"},
- element: &struct {
- Foo string
- }{},
- expected: &struct {
- Foo string
- }{
- Foo: "bar",
- },
- },
- {
- desc: "multiple bool flags without value",
- environ: []string{"TRAEFIK_FOO=true", "TRAEFIK_BAR=true"},
- element: &struct {
- Foo bool
- Bar bool
- }{},
- expected: &struct {
- Foo bool
- Bar bool
- }{
- Foo: true,
- Bar: true,
- },
- },
- {
- desc: "map string",
- environ: []string{"TRAEFIK_FOO_NAME=bar"},
- element: &struct {
- Foo map[string]string
- }{},
- expected: &struct {
- Foo map[string]string
- }{
- Foo: map[string]string{
- "name": "bar",
- },
- },
- },
- {
- desc: "map struct",
- environ: []string{"TRAEFIK_FOO_NAME_VALUE=bar"},
- element: &struct {
- Foo map[string]struct{ Value string }
- }{},
- expected: &struct {
- Foo map[string]struct{ Value string }
- }{
- Foo: map[string]struct{ Value string }{
- "name": {
- Value: "bar",
- },
- },
- },
- },
- {
- desc: "map struct with sub-struct",
- environ: []string{"TRAEFIK_FOO_NAME_BAR_VALUE=bar"},
- element: &struct {
- Foo map[string]struct {
- Bar *struct{ Value string }
- }
- }{},
- expected: &struct {
- Foo map[string]struct {
- Bar *struct{ Value string }
- }
- }{
- Foo: map[string]struct {
- Bar *struct{ Value string }
- }{
- "name": {
- Bar: &struct {
- Value string
- }{
- Value: "bar",
- },
- },
- },
- },
- },
- {
- desc: "map struct with sub-map",
- environ: []string{"TRAEFIK_FOO_NAME1_BAR_NAME2_VALUE=bar"},
- element: &struct {
- Foo map[string]struct {
- Bar map[string]struct{ Value string }
- }
- }{},
- expected: &struct {
- Foo map[string]struct {
- Bar map[string]struct{ Value string }
- }
- }{
- Foo: map[string]struct {
- Bar map[string]struct{ Value string }
- }{
- "name1": {
- Bar: map[string]struct{ Value string }{
- "name2": {
- Value: "bar",
- },
- },
- },
- },
- },
- },
- {
- desc: "slice",
- environ: []string{"TRAEFIK_FOO=bar,baz"},
- element: &struct {
- Foo []string
- }{},
- expected: &struct {
- Foo []string
- }{
- Foo: []string{"bar", "baz"},
- },
- },
- {
- desc: "struct pointer value",
- environ: []string{"TRAEFIK_FOO=true"},
- element: &struct {
- Foo *struct{ Field string } `label:"allowEmpty"`
- }{},
- expected: &struct {
- Foo *struct{ Field string } `label:"allowEmpty"`
- }{
- Foo: &struct{ Field string }{},
- },
- },
- }
-
- for _, test := range testCases {
- test := test
- t.Run(test.desc, func(t *testing.T) {
- t.Parallel()
-
- err := Decode(test.environ, DefaultNamePrefix, test.element)
- require.NoError(t, err)
-
- assert.Equal(t, test.expected, test.element)
- })
- }
-}
-
-func TestEncode(t *testing.T) {
- element := &Ya{
- Foo: &Yaa{
- FieldIn1: "bar",
- FieldIn2: false,
- FieldIn3: 1,
- FieldIn4: map[string]string{
- parser.MapNamePlaceholder: "",
- },
- FieldIn5: map[string]int{
- parser.MapNamePlaceholder: 0,
- },
- FieldIn6: map[string]struct{ Field string }{
- parser.MapNamePlaceholder: {},
- },
- FieldIn7: map[string]struct{ Field map[string]string }{
- parser.MapNamePlaceholder: {
- Field: map[string]string{
- parser.MapNamePlaceholder: "",
- },
- },
- },
- FieldIn8: map[string]*struct{ Field string }{
- parser.MapNamePlaceholder: {},
- },
- FieldIn9: map[string]*struct{ Field map[string]string }{
- parser.MapNamePlaceholder: {
- Field: map[string]string{
- parser.MapNamePlaceholder: "",
- },
- },
- },
- FieldIn10: struct{ Field string }{},
- FieldIn11: &struct{ Field string }{},
- FieldIn12: func(v string) *string { return &v }(""),
- FieldIn13: func(v bool) *bool { return &v }(false),
- FieldIn14: func(v int) *int { return &v }(0),
- },
- Field1: "bir",
- Field2: true,
- Field3: 0,
- Field4: map[string]string{
- parser.MapNamePlaceholder: "",
- },
- Field5: map[string]int{
- parser.MapNamePlaceholder: 0,
- },
- Field6: map[string]struct{ Field string }{
- parser.MapNamePlaceholder: {},
- },
- Field7: map[string]struct{ Field map[string]string }{
- parser.MapNamePlaceholder: {
- Field: map[string]string{
- parser.MapNamePlaceholder: "",
- },
- },
- },
- Field8: map[string]*struct{ Field string }{
- parser.MapNamePlaceholder: {},
- },
- Field9: map[string]*struct{ Field map[string]string }{
- parser.MapNamePlaceholder: {
- Field: map[string]string{
- parser.MapNamePlaceholder: "",
- },
- },
- },
- Field10: struct{ Field string }{},
- Field11: &struct{ Field string }{},
- Field12: func(v string) *string { return &v }(""),
- Field13: func(v bool) *bool { return &v }(false),
- Field14: func(v int) *int { return &v }(0),
- Field15: []int{7},
- }
- generator.Generate(element)
-
- flats, err := Encode(element)
- require.NoError(t, err)
-
- expected := []parser.Flat{
- {
- Name: "TRAEFIK_FIELD1",
- Description: "",
- Default: "bir",
- },
- {
- Name: "TRAEFIK_FIELD10",
- Description: "",
- Default: "",
- },
- {
- Name: "TRAEFIK_FIELD10_FIELD",
- Description: "",
- Default: "",
- },
- {
- Name: "TRAEFIK_FIELD11_FIELD",
- Description: "",
- Default: "",
- },
- {
- Name: "TRAEFIK_FIELD12",
- Description: "",
- Default: "",
- },
- {
- Name: "TRAEFIK_FIELD13",
- Description: "",
- Default: "false",
- },
- {
- Name: "TRAEFIK_FIELD14",
- Description: "",
- Default: "0",
- },
- {
- Name: "TRAEFIK_FIELD15",
- Description: "",
- Default: "7",
- },
- {
- Name: "TRAEFIK_FIELD2",
- Description: "",
- Default: "true",
- },
- {
- Name: "TRAEFIK_FIELD3",
- Description: "",
- Default: "0",
- },
- {
- Name: "TRAEFIK_FIELD4_\u003cNAME\u003e",
- Description: "",
- Default: "",
- },
- {
- Name: "TRAEFIK_FIELD5_\u003cNAME\u003e",
- Description: "",
- Default: "0",
- },
- {
- Name: "TRAEFIK_FIELD6_\u003cNAME\u003e",
- Description: "",
- Default: "false",
- },
- {
- Name: "TRAEFIK_FIELD6_\u003cNAME\u003e_FIELD",
- Description: "",
- Default: "",
- },
- {
- Name: "TRAEFIK_FIELD7_\u003cNAME\u003e",
- Description: "",
- Default: "false",
- },
- {
- Name: "TRAEFIK_FIELD7_\u003cNAME\u003e_FIELD_\u003cNAME\u003e",
- Description: "",
- Default: "",
- },
- {
- Name: "TRAEFIK_FIELD8_\u003cNAME\u003e",
- Description: "",
- Default: "false",
- },
- {
- Name: "TRAEFIK_FIELD8_\u003cNAME\u003e_FIELD",
- Description: "",
- Default: "",
- },
- {
- Name: "TRAEFIK_FIELD9_\u003cNAME\u003e",
- Description: "",
- Default: "false",
- },
- {
- Name: "TRAEFIK_FIELD9_\u003cNAME\u003e_FIELD_\u003cNAME\u003e",
- Description: "",
- Default: "",
- },
- {
- Name: "TRAEFIK_FOO_FIELDIN1",
- Description: "",
- Default: "bar",
- },
- {
- Name: "TRAEFIK_FOO_FIELDIN10",
- Description: "",
- Default: "",
- },
- {
- Name: "TRAEFIK_FOO_FIELDIN10_FIELD",
- Description: "",
- Default: "",
- },
- {
- Name: "TRAEFIK_FOO_FIELDIN11_FIELD",
- Description: "",
- Default: "",
- },
- {
- Name: "TRAEFIK_FOO_FIELDIN12",
- Description: "",
- Default: "",
- },
- {
- Name: "TRAEFIK_FOO_FIELDIN13",
- Description: "",
- Default: "false",
- },
- {
- Name: "TRAEFIK_FOO_FIELDIN14",
- Description: "",
- Default: "0",
- },
- {
- Name: "TRAEFIK_FOO_FIELDIN2",
- Description: "",
- Default: "false",
- },
- {
- Name: "TRAEFIK_FOO_FIELDIN3",
- Description: "",
- Default: "1",
- },
- {
- Name: "TRAEFIK_FOO_FIELDIN4_\u003cNAME\u003e",
- Description: "",
- Default: "",
- },
- {
- Name: "TRAEFIK_FOO_FIELDIN5_\u003cNAME\u003e",
- Description: "",
- Default: "0",
- },
- {
- Name: "TRAEFIK_FOO_FIELDIN6_\u003cNAME\u003e",
- Description: "",
- Default: "false",
- },
- {
- Name: "TRAEFIK_FOO_FIELDIN6_\u003cNAME\u003e_FIELD",
- Description: "",
- Default: "",
- },
- {
- Name: "TRAEFIK_FOO_FIELDIN7_\u003cNAME\u003e",
- Description: "",
- Default: "false",
- },
- {
- Name: "TRAEFIK_FOO_FIELDIN7_\u003cNAME\u003e_FIELD_\u003cNAME\u003e",
- Description: "",
- Default: "",
- },
- {
- Name: "TRAEFIK_FOO_FIELDIN8_\u003cNAME\u003e",
- Description: "",
- Default: "false",
- },
- {
- Name: "TRAEFIK_FOO_FIELDIN8_\u003cNAME\u003e_FIELD",
- Description: "",
- Default: "",
- },
- {
- Name: "TRAEFIK_FOO_FIELDIN9_\u003cNAME\u003e",
- Description: "",
- Default: "false",
- },
- {
- Name: "TRAEFIK_FOO_FIELDIN9_\u003cNAME\u003e_FIELD_\u003cNAME\u003e",
- Description: "",
- Default: "",
- },
- }
-
- assert.Equal(t, expected, flats)
-}
diff --git a/pkg/config/env/filter.go b/pkg/config/env/filter.go
deleted file mode 100644
index 56914d3b7..000000000
--- a/pkg/config/env/filter.go
+++ /dev/null
@@ -1,64 +0,0 @@
-package env
-
-import (
- "reflect"
- "strings"
-
- "github.com/containous/traefik/v2/pkg/config/parser"
-)
-
-// FindPrefixedEnvVars finds prefixed environment variables.
-func FindPrefixedEnvVars(environ []string, prefix string, element interface{}) []string {
- prefixes := getRootPrefixes(element, prefix)
-
- var values []string
- for _, px := range prefixes {
- for _, value := range environ {
- if strings.HasPrefix(value, px) {
- values = append(values, value)
- }
- }
- }
-
- return values
-}
-
-func getRootPrefixes(element interface{}, prefix string) []string {
- if element == nil {
- return nil
- }
-
- rootType := reflect.TypeOf(element)
-
- return getPrefixes(prefix, rootType)
-}
-
-func getPrefixes(prefix string, rootType reflect.Type) []string {
- var names []string
-
- if rootType.Kind() == reflect.Ptr {
- rootType = rootType.Elem()
- }
-
- if rootType.Kind() != reflect.Struct {
- return nil
- }
-
- for i := 0; i < rootType.NumField(); i++ {
- field := rootType.Field(i)
-
- if !parser.IsExported(field) {
- continue
- }
-
- if field.Anonymous &&
- (field.Type.Kind() == reflect.Ptr && field.Type.Elem().Kind() == reflect.Struct || field.Type.Kind() == reflect.Struct) {
- names = append(names, getPrefixes(prefix, field.Type)...)
- continue
- }
-
- names = append(names, prefix+strings.ToUpper(field.Name))
- }
-
- return names
-}
diff --git a/pkg/config/env/filter_test.go b/pkg/config/env/filter_test.go
deleted file mode 100644
index ecabc8982..000000000
--- a/pkg/config/env/filter_test.go
+++ /dev/null
@@ -1,87 +0,0 @@
-package env
-
-import (
- "testing"
-
- "github.com/stretchr/testify/assert"
-)
-
-func TestFindPrefixedEnvVars(t *testing.T) {
- testCases := []struct {
- desc string
- environ []string
- element interface{}
- expected []string
- }{
- {
- desc: "exact name",
- environ: []string{"TRAEFIK_FOO"},
- element: &Yo{},
- expected: []string{"TRAEFIK_FOO"},
- },
- {
- desc: "prefixed name",
- environ: []string{"TRAEFIK_FII01"},
- element: &Yo{},
- expected: []string{"TRAEFIK_FII01"},
- },
- {
- desc: "excluded env vars",
- environ: []string{"TRAEFIK_NOPE", "TRAEFIK_NO"},
- element: &Yo{},
- expected: nil,
- },
- {
- desc: "filter",
- environ: []string{"TRAEFIK_NOPE", "TRAEFIK_NO", "TRAEFIK_FOO", "TRAEFIK_FII01"},
- element: &Yo{},
- expected: []string{"TRAEFIK_FOO", "TRAEFIK_FII01"},
- },
- }
-
- for _, test := range testCases {
- test := test
- t.Run(test.desc, func(t *testing.T) {
- t.Parallel()
-
- vars := FindPrefixedEnvVars(test.environ, DefaultNamePrefix, test.element)
-
- assert.Equal(t, test.expected, vars)
- })
- }
-}
-
-func Test_getRootFieldNames(t *testing.T) {
- testCases := []struct {
- desc string
- element interface{}
- expected []string
- }{
- {
- desc: "simple fields",
- element: &Yo{},
- expected: []string{"TRAEFIK_FOO", "TRAEFIK_FII", "TRAEFIK_FUU", "TRAEFIK_YI", "TRAEFIK_YU"},
- },
- {
- desc: "embedded struct",
- element: &Yu{},
- expected: []string{"TRAEFIK_FOO", "TRAEFIK_FII", "TRAEFIK_FUU"},
- },
- {
- desc: "embedded struct pointer",
- element: &Ye{},
- expected: []string{"TRAEFIK_FOO", "TRAEFIK_FII", "TRAEFIK_FUU"},
- },
- }
-
- for _, test := range testCases {
- test := test
- t.Run(test.desc, func(t *testing.T) {
- t.Parallel()
-
- names := getRootPrefixes(test.element, DefaultNamePrefix)
-
- assert.Equal(t, test.expected, names)
- })
- }
-}
diff --git a/pkg/config/env/fixtures_test.go b/pkg/config/env/fixtures_test.go
deleted file mode 100644
index bbcb4c469..000000000
--- a/pkg/config/env/fixtures_test.go
+++ /dev/null
@@ -1,69 +0,0 @@
-package env
-
-type Ya struct {
- Foo *Yaa
- Field1 string
- Field2 bool
- Field3 int
- Field4 map[string]string
- Field5 map[string]int
- Field6 map[string]struct{ Field string }
- Field7 map[string]struct{ Field map[string]string }
- Field8 map[string]*struct{ Field string }
- Field9 map[string]*struct{ Field map[string]string }
- Field10 struct{ Field string }
- Field11 *struct{ Field string }
- Field12 *string
- Field13 *bool
- Field14 *int
- Field15 []int
-}
-
-type Yaa struct {
- FieldIn1 string
- FieldIn2 bool
- FieldIn3 int
- FieldIn4 map[string]string
- FieldIn5 map[string]int
- FieldIn6 map[string]struct{ Field string }
- FieldIn7 map[string]struct{ Field map[string]string }
- FieldIn8 map[string]*struct{ Field string }
- FieldIn9 map[string]*struct{ Field map[string]string }
- FieldIn10 struct{ Field string }
- FieldIn11 *struct{ Field string }
- FieldIn12 *string
- FieldIn13 *bool
- FieldIn14 *int
-}
-
-type Yo struct {
- Foo string `description:"Foo description"`
- Fii string `description:"Fii description"`
- Fuu string `description:"Fuu description"`
- Yi *Yi `label:"allowEmpty"`
- Yu *Yi
-}
-
-func (y *Yo) SetDefaults() {
- y.Foo = "foo"
- y.Fii = "fii"
-}
-
-type Yi struct {
- Foo string
- Fii string
- Fuu string
-}
-
-func (y *Yi) SetDefaults() {
- y.Foo = "foo"
- y.Fii = "fii"
-}
-
-type Yu struct {
- Yi
-}
-
-type Ye struct {
- *Yi
-}
diff --git a/pkg/config/file/file.go b/pkg/config/file/file.go
deleted file mode 100644
index 9024a23a8..000000000
--- a/pkg/config/file/file.go
+++ /dev/null
@@ -1,82 +0,0 @@
-// Package file implements decoding between configuration in a file and a typed Configuration.
-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.
-// 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 Decode(filePath string, element interface{}) error {
- if element == nil {
- return nil
- }
-
- filters := getRootFieldNames(element)
-
- root, err := decodeFileToNode(filePath, filters...)
- if err != nil {
- return err
- }
-
- 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: 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})
-}
diff --git a/pkg/config/file/file_node.go b/pkg/config/file/file_node.go
deleted file mode 100644
index 3570c64d4..000000000
--- a/pkg/config/file/file_node.go
+++ /dev/null
@@ -1,96 +0,0 @@
-package file
-
-import (
- "fmt"
- "io/ioutil"
- "path/filepath"
- "reflect"
- "strings"
-
- "github.com/BurntSushi/toml"
- "github.com/containous/traefik/v2/pkg/config/parser"
- "gopkg.in/yaml.v2"
-)
-
-// decodeFileToNode decodes the configuration in filePath in a tree of untyped nodes.
-// If filters is not empty, it skips any configuration element whose name is not among filters.
-func decodeFileToNode(filePath string, filters ...string) (*parser.Node, error) {
- content, err := ioutil.ReadFile(filePath)
- if err != nil {
- return nil, err
- }
-
- data := make(map[string]interface{})
-
- switch strings.ToLower(filepath.Ext(filePath)) {
- case ".toml":
- err = toml.Unmarshal(content, &data)
- if err != nil {
- return nil, err
- }
-
- case ".yml", ".yaml":
- err = yaml.Unmarshal(content, data)
- if err != nil {
- return nil, err
- }
-
- default:
- return nil, fmt.Errorf("unsupported file extension: %s", filePath)
- }
-
- if len(data) == 0 {
- return nil, fmt.Errorf("no configuration found in file: %s", filePath)
- }
-
- node, err := decodeRawToNode(data, parser.DefaultRootName, filters...)
- if err != nil {
- return nil, err
- }
-
- if len(node.Children) == 0 {
- return nil, fmt.Errorf("no valid configuration found in file: %s", filePath)
- }
-
- return node, nil
-}
-
-func getRootFieldNames(element interface{}) []string {
- if element == nil {
- return nil
- }
-
- rootType := reflect.TypeOf(element)
-
- return getFieldNames(rootType)
-}
-
-func getFieldNames(rootType reflect.Type) []string {
- var names []string
-
- if rootType.Kind() == reflect.Ptr {
- rootType = rootType.Elem()
- }
-
- if rootType.Kind() != reflect.Struct {
- return nil
- }
-
- for i := 0; i < rootType.NumField(); i++ {
- field := rootType.Field(i)
-
- if !parser.IsExported(field) {
- continue
- }
-
- if field.Anonymous &&
- (field.Type.Kind() == reflect.Ptr && field.Type.Elem().Kind() == reflect.Struct || field.Type.Kind() == reflect.Struct) {
- names = append(names, getFieldNames(field.Type)...)
- continue
- }
-
- names = append(names, field.Name)
- }
-
- return names
-}
diff --git a/pkg/config/file/file_node_test.go b/pkg/config/file/file_node_test.go
deleted file mode 100644
index 927e3828f..000000000
--- a/pkg/config/file/file_node_test.go
+++ /dev/null
@@ -1,646 +0,0 @@
-package file
-
-import (
- "testing"
-
- "github.com/containous/traefik/v2/pkg/config/parser"
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
-)
-
-func Test_getRootFieldNames(t *testing.T) {
- testCases := []struct {
- desc string
- element interface{}
- expected []string
- }{
- {
- desc: "simple fields",
- element: &Yo{},
- expected: []string{"Foo", "Fii", "Fuu", "Yi"},
- },
- {
- desc: "embedded struct",
- element: &Yu{},
- expected: []string{"Foo", "Fii", "Fuu"},
- },
- {
- desc: "embedded struct pointer",
- element: &Ye{},
- expected: []string{"Foo", "Fii", "Fuu"},
- },
- }
-
- for _, test := range testCases {
- test := test
- t.Run(test.desc, func(t *testing.T) {
- t.Parallel()
-
- names := getRootFieldNames(test.element)
-
- assert.Equal(t, test.expected, names)
- })
- }
-}
-
-func Test_decodeFileToNode_errors(t *testing.T) {
- testCases := []struct {
- desc string
- confFile string
- }{
- {
- desc: "non existing file",
- confFile: "./fixtures/not_existing.toml",
- },
- {
- desc: "file without content",
- confFile: "./fixtures/empty.toml",
- },
- {
- desc: "file without any valid configuration",
- confFile: "./fixtures/no_conf.toml",
- },
- }
-
- for _, test := range testCases {
- t.Run(test.desc, func(t *testing.T) {
- node, err := decodeFileToNode(test.confFile,
- "Global", "ServersTransport", "EntryPoints", "Providers", "API", "Metrics", "Ping", "Log", "AccessLog", "Tracing", "HostResolver", "CertificatesResolvers")
-
- require.Error(t, err)
- assert.Nil(t, node)
- })
- }
-}
-
-func Test_decodeFileToNode_compare(t *testing.T) {
- nodeToml, err := decodeFileToNode("./fixtures/sample.toml",
- "Global", "ServersTransport", "EntryPoints", "Providers", "API", "Metrics", "Ping", "Log", "AccessLog", "Tracing", "HostResolver", "CertificatesResolvers")
- require.NoError(t, err)
-
- nodeYaml, err := decodeFileToNode("./fixtures/sample.yml")
- require.NoError(t, err)
-
- assert.Equal(t, nodeToml, nodeYaml)
-}
-
-func Test_decodeFileToNode_Toml(t *testing.T) {
- node, err := decodeFileToNode("./fixtures/sample.toml",
- "Global", "ServersTransport", "EntryPoints", "Providers", "API", "Metrics", "Ping", "Log", "AccessLog", "Tracing", "HostResolver", "CertificatesResolvers")
- require.NoError(t, err)
-
- expected := &parser.Node{
- Name: "traefik",
- Children: []*parser.Node{
- {Name: "accessLog", Children: []*parser.Node{
- {Name: "bufferingSize", Value: "42"},
- {Name: "fields", Children: []*parser.Node{
- {Name: "defaultMode", Value: "foobar"},
- {Name: "headers", Children: []*parser.Node{
- {Name: "defaultMode", Value: "foobar"},
- {Name: "names", Children: []*parser.Node{
- {Name: "name0", Value: "foobar"},
- {Name: "name1", Value: "foobar"},
- }},
- }},
- {Name: "names", Children: []*parser.Node{
- {Name: "name0", Value: "foobar"},
- {Name: "name1", Value: "foobar"},
- }},
- }},
- {Name: "filePath", Value: "foobar"},
- {Name: "filters", Children: []*parser.Node{
- {Name: "minDuration", Value: "42"},
- {Name: "retryAttempts", Value: "true"},
- {Name: "statusCodes", Value: "foobar,foobar"},
- }},
- {Name: "format", Value: "foobar"},
- }},
- {Name: "api", Children: []*parser.Node{
- {Name: "dashboard", Value: "true"},
- {Name: "entryPoint", Value: "foobar"},
- {Name: "middlewares", Value: "foobar,foobar"},
- {Name: "statistics", Children: []*parser.Node{
- {Name: "recentErrors", Value: "42"},
- }},
- }},
- {Name: "certificatesResolvers", Children: []*parser.Node{
- {Name: "default", Children: []*parser.Node{
- {
- Name: "acme",
- Children: []*parser.Node{
- {Name: "acmeLogging", Value: "true"},
- {Name: "caServer", Value: "foobar"},
- {Name: "dnsChallenge", Children: []*parser.Node{
- {Name: "delayBeforeCheck", Value: "42"},
- {Name: "disablePropagationCheck", Value: "true"},
- {Name: "provider", Value: "foobar"},
- {Name: "resolvers", Value: "foobar,foobar"},
- }},
- {Name: "email", Value: "foobar"},
- {Name: "entryPoint", Value: "foobar"},
- {Name: "httpChallenge", Children: []*parser.Node{
- {Name: "entryPoint", Value: "foobar"},
- }},
- {Name: "keyType", Value: "foobar"},
- {Name: "storage", Value: "foobar"},
- {Name: "tlsChallenge"},
- },
- },
- }},
- }},
- {Name: "entryPoints", Children: []*parser.Node{
- {Name: "EntryPoint0", Children: []*parser.Node{
- {Name: "address", Value: "foobar"},
- {Name: "forwardedHeaders", Children: []*parser.Node{
- {Name: "insecure", Value: "true"},
- {Name: "trustedIPs", Value: "foobar,foobar"},
- }},
- {Name: "proxyProtocol", Children: []*parser.Node{
- {Name: "insecure", Value: "true"},
- {Name: "trustedIPs", Value: "foobar,foobar"},
- }},
- {Name: "transport", Children: []*parser.Node{
- {Name: "lifeCycle", Children: []*parser.Node{
- {Name: "graceTimeOut", Value: "42"},
- {Name: "requestAcceptGraceTimeout", Value: "42"},
- }},
- {Name: "respondingTimeouts", Children: []*parser.Node{
- {Name: "idleTimeout", Value: "42"},
- {Name: "readTimeout", Value: "42"},
- {Name: "writeTimeout", Value: "42"},
- }},
- }},
- }},
- }},
- {Name: "global", Children: []*parser.Node{
- {Name: "checkNewVersion", Value: "true"},
- {Name: "sendAnonymousUsage", Value: "true"},
- }},
- {Name: "hostResolver", Children: []*parser.Node{
- {Name: "cnameFlattening", Value: "true"},
- {Name: "resolvConfig", Value: "foobar"},
- {Name: "resolvDepth", Value: "42"},
- }},
- {Name: "log", Children: []*parser.Node{
- {Name: "filePath", Value: "foobar"},
- {Name: "format", Value: "foobar"},
- {Name: "level", Value: "foobar"},
- }},
- {Name: "metrics", Children: []*parser.Node{
- {Name: "datadog", Children: []*parser.Node{
- {Name: "address", Value: "foobar"},
- {Name: "pushInterval", Value: "10s"},
- }},
- {Name: "influxDB", Children: []*parser.Node{
- {Name: "address", Value: "foobar"},
- {Name: "database", Value: "foobar"},
- {Name: "password", Value: "foobar"},
- {Name: "protocol", Value: "foobar"},
- {Name: "pushInterval", Value: "10s"},
- {Name: "retentionPolicy", Value: "foobar"},
- {Name: "username", Value: "foobar"},
- }},
- {Name: "prometheus", Children: []*parser.Node{
- {Name: "buckets", Value: "42,42"},
- {Name: "entryPoint", Value: "foobar"},
- {Name: "middlewares", Value: "foobar,foobar"},
- }},
- {Name: "statsD", Children: []*parser.Node{
- {Name: "address", Value: "foobar"},
- {Name: "pushInterval", Value: "10s"},
- }},
- }},
- {Name: "ping", Children: []*parser.Node{
- {Name: "entryPoint", Value: "foobar"},
- {Name: "middlewares", Value: "foobar,foobar"},
- }},
- {Name: "providers", Children: []*parser.Node{
- {Name: "docker", Children: []*parser.Node{
- {Name: "constraints", Value: "foobar"},
- {Name: "defaultRule", Value: "foobar"},
- {Name: "endpoint", Value: "foobar"},
- {Name: "exposedByDefault", Value: "true"},
- {Name: "network", Value: "foobar"},
- {Name: "swarmMode", Value: "true"},
- {Name: "swarmModeRefreshSeconds", Value: "42"},
- {Name: "tls", Children: []*parser.Node{
- {Name: "ca", Value: "foobar"},
- {Name: "caOptional", Value: "true"},
- {Name: "cert", Value: "foobar"},
- {Name: "insecureSkipVerify", Value: "true"},
- {Name: "key", Value: "foobar"},
- }},
- {Name: "useBindPortIP", Value: "true"},
- {Name: "watch", Value: "true"},
- }},
- {Name: "file", Children: []*parser.Node{
- {Name: "debugLogGeneratedTemplate", Value: "true"},
- {Name: "directory", Value: "foobar"},
- {Name: "filename", Value: "foobar"},
- {Name: "watch", Value: "true"},
- }},
- {
- Name: "kubernetesCRD",
- Children: []*parser.Node{
- {Name: "certAuthFilePath", Value: "foobar"},
- {Name: "disablePassHostHeaders", Value: "true"},
- {Name: "endpoint", Value: "foobar"},
- {Name: "ingressClass", Value: "foobar"},
- {Name: "labelSelector", Value: "foobar"},
- {Name: "namespaces", Value: "foobar,foobar"},
- {Name: "token", Value: "foobar"},
- },
- },
- {Name: "kubernetesIngress", Children: []*parser.Node{
- {Name: "certAuthFilePath", Value: "foobar"},
- {Name: "disablePassHostHeaders", Value: "true"},
- {Name: "endpoint", Value: "foobar"},
- {Name: "ingressClass", Value: "foobar"},
- {Name: "ingressEndpoint", Children: []*parser.Node{
- {Name: "hostname", Value: "foobar"},
- {Name: "ip", Value: "foobar"},
- {Name: "publishedService", Value: "foobar"},
- }},
- {Name: "labelSelector", Value: "foobar"},
- {Name: "namespaces", Value: "foobar,foobar"},
- {Name: "token", Value: "foobar"},
- }},
- {Name: "marathon", Children: []*parser.Node{
- {Name: "basic", Children: []*parser.Node{
- {Name: "httpBasicAuthUser", Value: "foobar"},
- {Name: "httpBasicPassword", Value: "foobar"},
- }},
- {Name: "constraints", Value: "foobar"},
- {Name: "dcosToken", Value: "foobar"},
- {Name: "defaultRule", Value: "foobar"},
- {Name: "dialerTimeout", Value: "42"},
- {Name: "endpoint", Value: "foobar"},
- {Name: "exposedByDefault", Value: "true"},
- {Name: "forceTaskHostname", Value: "true"},
- {Name: "keepAlive", Value: "42"},
- {Name: "respectReadinessChecks", Value: "true"},
- {Name: "responseHeaderTimeout", Value: "42"},
- {Name: "tls", Children: []*parser.Node{
- {Name: "ca", Value: "foobar"},
- {Name: "caOptional", Value: "true"},
- {Name: "cert", Value: "foobar"},
- {Name: "insecureSkipVerify", Value: "true"},
- {Name: "key", Value: "foobar"},
- }},
- {Name: "tlsHandshakeTimeout", Value: "42"},
- {Name: "trace", Value: "true"},
- {Name: "watch", Value: "true"},
- }},
- {Name: "providersThrottleDuration", Value: "42"},
- {Name: "rancher", Children: []*parser.Node{
- {Name: "constraints", Value: "foobar"},
- {Name: "defaultRule", Value: "foobar"},
- {Name: "enableServiceHealthFilter", Value: "true"},
- {Name: "exposedByDefault", Value: "true"},
- {Name: "intervalPoll", Value: "true"},
- {Name: "prefix", Value: "foobar"},
- {Name: "refreshSeconds", Value: "42"},
- {Name: "watch", Value: "true"},
- }},
- {Name: "rest", Children: []*parser.Node{
- {Name: "entryPoint", Value: "foobar"},
- }},
- }},
- {Name: "serversTransport", Children: []*parser.Node{
- {Name: "forwardingTimeouts", Children: []*parser.Node{
- {Name: "dialTimeout", Value: "42"},
- {Name: "idleConnTimeout", Value: "42"},
- {Name: "responseHeaderTimeout", Value: "42"},
- }},
- {Name: "insecureSkipVerify", Value: "true"},
- {Name: "maxIdleConnsPerHost", Value: "42"},
- {Name: "rootCAs", Value: "foobar,foobar"},
- }},
- {Name: "tracing", Children: []*parser.Node{
- {Name: "datadog", Children: []*parser.Node{
- {Name: "bagagePrefixHeaderName", Value: "foobar"},
- {Name: "debug", Value: "true"},
- {Name: "globalTag", Value: "foobar"},
- {Name: "localAgentHostPort", Value: "foobar"},
- {Name: "parentIDHeaderName", Value: "foobar"},
- {Name: "prioritySampling", Value: "true"},
- {Name: "samplingPriorityHeaderName", Value: "foobar"},
- {Name: "traceIDHeaderName", Value: "foobar"},
- }},
- {Name: "haystack", Children: []*parser.Node{
- {Name: "globalTag", Value: "foobar"},
- {Name: "localAgentHost", Value: "foobar"},
- {Name: "localAgentPort", Value: "42"},
- {Name: "parentIDHeaderName", Value: "foobar"},
- {Name: "spanIDHeaderName", Value: "foobar"},
- {Name: "traceIDHeaderName", Value: "foobar"},
- }},
- {Name: "instana", Children: []*parser.Node{
- {Name: "localAgentHost", Value: "foobar"},
- {Name: "localAgentPort", Value: "42"},
- {Name: "logLevel", Value: "foobar"},
- }},
- {Name: "jaeger", Children: []*parser.Node{
- {Name: "gen128Bit", Value: "true"},
- {Name: "localAgentHostPort", Value: "foobar"},
- {Name: "propagation", Value: "foobar"},
- {Name: "samplingParam", Value: "42"},
- {Name: "samplingServerURL", Value: "foobar"},
- {Name: "samplingType", Value: "foobar"},
- {Name: "traceContextHeaderName", Value: "foobar"},
- }},
- {Name: "serviceName", Value: "foobar"},
- {Name: "spanNameLimit", Value: "42"},
- {Name: "zipkin", Children: []*parser.Node{
- {Name: "httpEndpoint", Value: "foobar"},
- {Name: "id128Bit", Value: "true"},
- {Name: "sameSpan", Value: "true"},
- {Name: "sampleRate", Value: "42"},
- }},
- }},
- },
- }
-
- assert.Equal(t, expected, node)
-}
-
-func Test_decodeFileToNode_Yaml(t *testing.T) {
- node, err := decodeFileToNode("./fixtures/sample.yml")
- require.NoError(t, err)
-
- expected := &parser.Node{
- Name: "traefik",
- Children: []*parser.Node{
- {Name: "accessLog", Children: []*parser.Node{
- {Name: "bufferingSize", Value: "42"},
- {Name: "fields", Children: []*parser.Node{
- {Name: "defaultMode", Value: "foobar"},
- {Name: "headers", Children: []*parser.Node{
- {Name: "defaultMode", Value: "foobar"},
- {Name: "names", Children: []*parser.Node{
- {Name: "name0", Value: "foobar"},
- {Name: "name1", Value: "foobar"},
- }},
- }},
- {Name: "names", Children: []*parser.Node{
- {Name: "name0", Value: "foobar"},
- {Name: "name1", Value: "foobar"},
- }},
- }},
- {Name: "filePath", Value: "foobar"},
- {Name: "filters", Children: []*parser.Node{
- {Name: "minDuration", Value: "42"},
- {Name: "retryAttempts", Value: "true"},
- {Name: "statusCodes", Value: "foobar,foobar"},
- }},
- {Name: "format", Value: "foobar"},
- }},
- {Name: "api", Children: []*parser.Node{
- {Name: "dashboard", Value: "true"},
- {Name: "entryPoint", Value: "foobar"},
- {Name: "middlewares", Value: "foobar,foobar"},
- {Name: "statistics", Children: []*parser.Node{
- {Name: "recentErrors", Value: "42"},
- }},
- }},
- {Name: "certificatesResolvers", Children: []*parser.Node{
- {Name: "default", Children: []*parser.Node{
- {
- Name: "acme",
- Children: []*parser.Node{
- {Name: "acmeLogging", Value: "true"},
- {Name: "caServer", Value: "foobar"},
- {Name: "dnsChallenge", Children: []*parser.Node{
- {Name: "delayBeforeCheck", Value: "42"},
- {Name: "disablePropagationCheck", Value: "true"},
- {Name: "provider", Value: "foobar"},
- {Name: "resolvers", Value: "foobar,foobar"},
- }},
- {Name: "email", Value: "foobar"},
- {Name: "entryPoint", Value: "foobar"},
- {Name: "httpChallenge", Children: []*parser.Node{
- {Name: "entryPoint", Value: "foobar"},
- }},
- {Name: "keyType", Value: "foobar"},
- {Name: "storage", Value: "foobar"},
- {Name: "tlsChallenge"},
- },
- },
- }},
- }},
- {Name: "entryPoints", Children: []*parser.Node{
- {Name: "EntryPoint0", Children: []*parser.Node{
- {Name: "address", Value: "foobar"},
- {Name: "forwardedHeaders", Children: []*parser.Node{
- {Name: "insecure", Value: "true"},
- {Name: "trustedIPs", Value: "foobar,foobar"},
- }},
- {Name: "proxyProtocol", Children: []*parser.Node{
- {Name: "insecure", Value: "true"},
- {Name: "trustedIPs", Value: "foobar,foobar"},
- }},
- {Name: "transport", Children: []*parser.Node{
- {Name: "lifeCycle", Children: []*parser.Node{
- {Name: "graceTimeOut", Value: "42"},
- {Name: "requestAcceptGraceTimeout", Value: "42"},
- }},
- {Name: "respondingTimeouts", Children: []*parser.Node{
- {Name: "idleTimeout", Value: "42"},
- {Name: "readTimeout", Value: "42"},
- {Name: "writeTimeout", Value: "42"},
- }},
- }},
- }},
- }},
- {Name: "global", Children: []*parser.Node{
- {Name: "checkNewVersion", Value: "true"},
- {Name: "sendAnonymousUsage", Value: "true"},
- }},
- {Name: "hostResolver", Children: []*parser.Node{
- {Name: "cnameFlattening", Value: "true"},
- {Name: "resolvConfig", Value: "foobar"},
- {Name: "resolvDepth", Value: "42"},
- }},
- {Name: "log", Children: []*parser.Node{
- {Name: "filePath", Value: "foobar"},
- {Name: "format", Value: "foobar"},
- {Name: "level", Value: "foobar"},
- }},
- {Name: "metrics", Children: []*parser.Node{
- {Name: "datadog", Children: []*parser.Node{
- {Name: "address", Value: "foobar"},
- {Name: "pushInterval", Value: "10s"},
- }},
- {Name: "influxDB", Children: []*parser.Node{
- {Name: "address", Value: "foobar"},
- {Name: "database", Value: "foobar"},
- {Name: "password", Value: "foobar"},
- {Name: "protocol", Value: "foobar"},
- {Name: "pushInterval", Value: "10s"},
- {Name: "retentionPolicy", Value: "foobar"},
- {Name: "username", Value: "foobar"},
- }},
- {Name: "prometheus", Children: []*parser.Node{
- {Name: "buckets", Value: "42,42"},
- {Name: "entryPoint", Value: "foobar"},
- {Name: "middlewares", Value: "foobar,foobar"},
- }},
- {Name: "statsD", Children: []*parser.Node{
- {Name: "address", Value: "foobar"},
- {Name: "pushInterval", Value: "10s"},
- }},
- }},
- {Name: "ping", Children: []*parser.Node{
- {Name: "entryPoint", Value: "foobar"},
- {Name: "middlewares", Value: "foobar,foobar"},
- }},
- {Name: "providers", Children: []*parser.Node{
- {Name: "docker", Children: []*parser.Node{
- {Name: "constraints", Value: "foobar"},
- {Name: "defaultRule", Value: "foobar"},
- {Name: "endpoint", Value: "foobar"},
- {Name: "exposedByDefault", Value: "true"},
- {Name: "network", Value: "foobar"},
- {Name: "swarmMode", Value: "true"},
- {Name: "swarmModeRefreshSeconds", Value: "42"},
- {Name: "tls", Children: []*parser.Node{
- {Name: "ca", Value: "foobar"},
- {Name: "caOptional", Value: "true"},
- {Name: "cert", Value: "foobar"},
- {Name: "insecureSkipVerify", Value: "true"},
- {Name: "key", Value: "foobar"},
- }},
- {Name: "useBindPortIP", Value: "true"},
- {Name: "watch", Value: "true"},
- }},
- {Name: "file", Children: []*parser.Node{
- {Name: "debugLogGeneratedTemplate", Value: "true"},
- {Name: "directory", Value: "foobar"},
- {Name: "filename", Value: "foobar"},
- {Name: "watch", Value: "true"},
- }},
- {
- Name: "kubernetesCRD",
- Children: []*parser.Node{
- {Name: "certAuthFilePath", Value: "foobar"},
- {Name: "disablePassHostHeaders", Value: "true"},
- {Name: "endpoint", Value: "foobar"},
- {Name: "ingressClass", Value: "foobar"},
- {Name: "labelSelector", Value: "foobar"},
- {Name: "namespaces", Value: "foobar,foobar"},
- {Name: "token", Value: "foobar"},
- },
- },
- {Name: "kubernetesIngress", Children: []*parser.Node{
- {Name: "certAuthFilePath", Value: "foobar"},
- {Name: "disablePassHostHeaders", Value: "true"},
- {Name: "endpoint", Value: "foobar"},
- {Name: "ingressClass", Value: "foobar"},
- {Name: "ingressEndpoint", Children: []*parser.Node{
- {Name: "hostname", Value: "foobar"},
- {Name: "ip", Value: "foobar"},
- {Name: "publishedService", Value: "foobar"},
- }},
- {Name: "labelSelector", Value: "foobar"},
- {Name: "namespaces", Value: "foobar,foobar"},
- {Name: "token", Value: "foobar"},
- }},
- {Name: "marathon", Children: []*parser.Node{
- {Name: "basic", Children: []*parser.Node{
- {Name: "httpBasicAuthUser", Value: "foobar"},
- {Name: "httpBasicPassword", Value: "foobar"},
- }},
- {Name: "constraints", Value: "foobar"},
- {Name: "dcosToken", Value: "foobar"},
- {Name: "defaultRule", Value: "foobar"},
- {Name: "dialerTimeout", Value: "42"},
- {Name: "endpoint", Value: "foobar"},
- {Name: "exposedByDefault", Value: "true"},
- {Name: "forceTaskHostname", Value: "true"},
- {Name: "keepAlive", Value: "42"},
- {Name: "respectReadinessChecks", Value: "true"},
- {Name: "responseHeaderTimeout", Value: "42"},
- {Name: "tls", Children: []*parser.Node{
- {Name: "ca", Value: "foobar"},
- {Name: "caOptional", Value: "true"},
- {Name: "cert", Value: "foobar"},
- {Name: "insecureSkipVerify", Value: "true"},
- {Name: "key", Value: "foobar"},
- }},
- {Name: "tlsHandshakeTimeout", Value: "42"},
- {Name: "trace", Value: "true"},
- {Name: "watch", Value: "true"},
- }},
- {Name: "providersThrottleDuration", Value: "42"},
- {Name: "rancher", Children: []*parser.Node{
- {Name: "constraints", Value: "foobar"},
- {Name: "defaultRule", Value: "foobar"},
- {Name: "enableServiceHealthFilter", Value: "true"},
- {Name: "exposedByDefault", Value: "true"},
- {Name: "intervalPoll", Value: "true"},
- {Name: "prefix", Value: "foobar"},
- {Name: "refreshSeconds", Value: "42"},
- {Name: "watch", Value: "true"},
- }},
- {Name: "rest", Children: []*parser.Node{
- {Name: "entryPoint", Value: "foobar"},
- }},
- }},
- {Name: "serversTransport", Children: []*parser.Node{
- {Name: "forwardingTimeouts", Children: []*parser.Node{
- {Name: "dialTimeout", Value: "42"},
- {Name: "idleConnTimeout", Value: "42"},
- {Name: "responseHeaderTimeout", Value: "42"},
- }},
- {Name: "insecureSkipVerify", Value: "true"},
- {Name: "maxIdleConnsPerHost", Value: "42"},
- {Name: "rootCAs", Value: "foobar,foobar"},
- }},
- {Name: "tracing", Children: []*parser.Node{
- {Name: "datadog", Children: []*parser.Node{
- {Name: "bagagePrefixHeaderName", Value: "foobar"},
- {Name: "debug", Value: "true"},
- {Name: "globalTag", Value: "foobar"},
- {Name: "localAgentHostPort", Value: "foobar"},
- {Name: "parentIDHeaderName", Value: "foobar"},
- {Name: "prioritySampling", Value: "true"},
- {Name: "samplingPriorityHeaderName", Value: "foobar"},
- {Name: "traceIDHeaderName", Value: "foobar"},
- }},
- {Name: "haystack", Children: []*parser.Node{
- {Name: "globalTag", Value: "foobar"},
- {Name: "localAgentHost", Value: "foobar"},
- {Name: "localAgentPort", Value: "42"},
- {Name: "parentIDHeaderName", Value: "foobar"},
- {Name: "spanIDHeaderName", Value: "foobar"},
- {Name: "traceIDHeaderName", Value: "foobar"},
- }},
- {Name: "instana", Children: []*parser.Node{
- {Name: "localAgentHost", Value: "foobar"},
- {Name: "localAgentPort", Value: "42"},
- {Name: "logLevel", Value: "foobar"},
- }},
- {Name: "jaeger", Children: []*parser.Node{
- {Name: "gen128Bit", Value: "true"},
- {Name: "localAgentHostPort", Value: "foobar"},
- {Name: "propagation", Value: "foobar"},
- {Name: "samplingParam", Value: "42"},
- {Name: "samplingServerURL", Value: "foobar"},
- {Name: "samplingType", Value: "foobar"},
- {Name: "traceContextHeaderName", Value: "foobar"},
- }},
- {Name: "serviceName", Value: "foobar"},
- {Name: "spanNameLimit", Value: "42"},
- {Name: "zipkin", Children: []*parser.Node{
- {Name: "httpEndpoint", Value: "foobar"},
- {Name: "id128Bit", Value: "true"},
- {Name: "sameSpan", Value: "true"},
- {Name: "sampleRate", Value: "42"},
- }},
- }},
- },
- }
-
- assert.Equal(t, expected, node)
-}
diff --git a/pkg/config/file/file_test.go b/pkg/config/file/file_test.go
deleted file mode 100644
index 0fb348636..000000000
--- a/pkg/config/file/file_test.go
+++ /dev/null
@@ -1,177 +0,0 @@
-package file
-
-import (
- "io/ioutil"
- "os"
- "testing"
-
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
-)
-
-func TestDecode_TOML(t *testing.T) {
- f, err := ioutil.TempFile("", "traefik-config-*.toml")
- require.NoError(t, err)
- defer func() {
- _ = os.Remove(f.Name())
- }()
-
- _, err = f.Write([]byte(`
-foo = "bar"
-fii = "bir"
-[yi]
-`))
- require.NoError(t, err)
-
- element := &Yo{
- Fuu: "test",
- }
-
- err = Decode(f.Name(), 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(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)
- defer func() {
- _ = os.Remove(f.Name())
- }()
-
- _, err = f.Write([]byte(`
-foo: bar
-fii: bir
-yi: {}
-`))
- require.NoError(t, err)
-
- element := &Yo{
- Fuu: "test",
- }
-
- err = Decode(f.Name(), 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(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)
-}
diff --git a/pkg/config/file/fixtures/empty.toml b/pkg/config/file/fixtures/empty.toml
deleted file mode 100644
index e69de29bb..000000000
diff --git a/pkg/config/file/fixtures/no_conf.toml b/pkg/config/file/fixtures/no_conf.toml
deleted file mode 100644
index fd45fbc06..000000000
--- a/pkg/config/file/fixtures/no_conf.toml
+++ /dev/null
@@ -1,2 +0,0 @@
-[foo]
- bar = "test"
diff --git a/pkg/config/file/fixtures/sample.toml b/pkg/config/file/fixtures/sample.toml
deleted file mode 100644
index 4da227f2b..000000000
--- a/pkg/config/file/fixtures/sample.toml
+++ /dev/null
@@ -1,474 +0,0 @@
-[global]
- checkNewVersion = true
- sendAnonymousUsage = true
-
-[serversTransport]
- insecureSkipVerify = true
- rootCAs = ["foobar", "foobar"]
- maxIdleConnsPerHost = 42
- [serversTransport.forwardingTimeouts]
- dialTimeout = 42
- responseHeaderTimeout = 42
- idleConnTimeout = 42
-
-[entryPoints]
- [entryPoints.EntryPoint0]
- address = "foobar"
- [entryPoints.EntryPoint0.transport]
- [entryPoints.EntryPoint0.transport.lifeCycle]
- requestAcceptGraceTimeout = 42
- graceTimeOut = 42
- [entryPoints.EntryPoint0.transport.respondingTimeouts]
- readTimeout = 42
- writeTimeout = 42
- idleTimeout = 42
- [entryPoints.EntryPoint0.proxyProtocol]
- insecure = true
- trustedIPs = ["foobar", "foobar"]
- [entryPoints.EntryPoint0.forwardedHeaders]
- insecure = true
- trustedIPs = ["foobar", "foobar"]
-
-[providers]
- providersThrottleDuration = 42
- [providers.docker]
- constraints = "foobar"
- watch = true
- endpoint = "foobar"
- defaultRule = "foobar"
- exposedByDefault = true
- useBindPortIP = true
- swarmMode = true
- network = "foobar"
- swarmModeRefreshSeconds = 42
- [providers.docker.tls]
- ca = "foobar"
- caOptional = true
- cert = "foobar"
- key = "foobar"
- insecureSkipVerify = true
- [providers.file]
- directory = "foobar"
- watch = true
- filename = "foobar"
- debugLogGeneratedTemplate = true
- [providers.marathon]
- constraints = "foobar"
- trace = true
- watch = true
- endpoint = "foobar"
- defaultRule = "foobar"
- exposedByDefault = true
- dcosToken = "foobar"
- dialerTimeout = 42
- responseHeaderTimeout = 42
- tlsHandshakeTimeout = 42
- keepAlive = 42
- forceTaskHostname = true
- respectReadinessChecks = true
- [providers.marathon.tls]
- ca = "foobar"
- caOptional = true
- cert = "foobar"
- key = "foobar"
- insecureSkipVerify = true
- [providers.marathon.basic]
- httpBasicAuthUser = "foobar"
- httpBasicPassword = "foobar"
- [providers.kubernetesIngress]
- endpoint = "foobar"
- token = "foobar"
- certAuthFilePath = "foobar"
- disablePassHostHeaders = true
- namespaces = ["foobar", "foobar"]
- labelSelector = "foobar"
- ingressClass = "foobar"
- [providers.kubernetesIngress.ingressEndpoint]
- ip = "foobar"
- hostname = "foobar"
- publishedService = "foobar"
- [providers.kubernetesCRD]
- endpoint = "foobar"
- token = "foobar"
- certAuthFilePath = "foobar"
- disablePassHostHeaders = true
- namespaces = ["foobar", "foobar"]
- labelSelector = "foobar"
- ingressClass = "foobar"
- [providers.rest]
- entryPoint = "foobar"
- [providers.rancher]
- constraints = "foobar"
- watch = true
- defaultRule = "foobar"
- exposedByDefault = true
- enableServiceHealthFilter = true
- refreshSeconds = 42
- intervalPoll = true
- prefix = "foobar"
-
-[api]
- entryPoint = "foobar"
- dashboard = true
- middlewares = ["foobar", "foobar"]
- [api.statistics]
- recentErrors = 42
-
-[metrics]
- [metrics.prometheus]
- buckets = [42.0, 42.0]
- entryPoint = "foobar"
- middlewares = ["foobar", "foobar"]
- [metrics.datadog]
- address = "foobar"
- pushInterval = "10s"
- [metrics.statsD]
- address = "foobar"
- pushInterval = "10s"
- [metrics.influxDB]
- address = "foobar"
- protocol = "foobar"
- pushInterval = "10s"
- database = "foobar"
- retentionPolicy = "foobar"
- username = "foobar"
- password = "foobar"
-
-[ping]
- entryPoint = "foobar"
- middlewares = ["foobar", "foobar"]
-
-[log]
- level = "foobar"
- filePath = "foobar"
- format = "foobar"
-
-[accessLog]
- filePath = "foobar"
- format = "foobar"
- bufferingSize = 42
- [accessLog.filters]
- statusCodes = ["foobar", "foobar"]
- retryAttempts = true
- minDuration = 42
- [accessLog.fields]
- defaultMode = "foobar"
- [accessLog.fields.names]
- name0 = "foobar"
- name1 = "foobar"
- [accessLog.fields.headers]
- defaultMode = "foobar"
- [accessLog.fields.headers.names]
- name0 = "foobar"
- name1 = "foobar"
-
-[tracing]
- serviceName = "foobar"
- spanNameLimit = 42
- [tracing.jaeger]
- samplingServerURL = "foobar"
- samplingType = "foobar"
- samplingParam = 42.0
- localAgentHostPort = "foobar"
- gen128Bit = true
- propagation = "foobar"
- traceContextHeaderName = "foobar"
- [tracing.zipkin]
- httpEndpoint = "foobar"
- sameSpan = true
- id128Bit = true
- sampleRate = 42.0
- [tracing.datadog]
- localAgentHostPort = "foobar"
- globalTag = "foobar"
- debug = true
- prioritySampling = true
- traceIDHeaderName = "foobar"
- parentIDHeaderName = "foobar"
- samplingPriorityHeaderName = "foobar"
- bagagePrefixHeaderName = "foobar"
- [tracing.instana]
- localAgentHost = "foobar"
- localAgentPort = 42
- logLevel = "foobar"
- [tracing.haystack]
- localAgentHost = "foobar"
- localAgentPort = 42
- globalTag = "foobar"
- traceIDHeaderName = "foobar"
- parentIDHeaderName = "foobar"
- spanIDHeaderName = "foobar"
-
-[hostResolver]
- cnameFlattening = true
- resolvConfig = "foobar"
- resolvDepth = 42
-
-[certificatesResolvers.default.acme]
- email = "foobar"
- acmeLogging = true
- caServer = "foobar"
- storage = "foobar"
- entryPoint = "foobar"
- keyType = "foobar"
- [certificatesResolvers.default.acme.dnsChallenge]
- provider = "foobar"
- delayBeforeCheck = 42
- resolvers = ["foobar", "foobar"]
- disablePropagationCheck = true
- [certificatesResolvers.default.acme.httpChallenge]
- entryPoint = "foobar"
- [certificatesResolvers.default.acme.tlsChallenge]
-
-## Dynamic configuration
-
-[http]
- [http.routers]
- [http.routers.Router0]
- entryPoints = ["foobar", "foobar"]
- middlewares = ["foobar", "foobar"]
- service = "foobar"
- rule = "foobar"
- priority = 42
- [http.routers.Router0.tls]
- [http.middlewares]
- [http.middlewares.Middleware0]
- [http.middlewares.Middleware0.addPrefix]
- prefix = "foobar"
- [http.middlewares.Middleware1]
- [http.middlewares.Middleware1.stripPrefix]
- prefixes = ["foobar", "foobar"]
- [http.middlewares.Middleware10]
- [http.middlewares.Middleware10.rateLimit]
- average = 42
- burst = 42
- [http.middlewares.Middleware10.rateLimit.sourceCriterion]
- requestHeaderName = "foobar"
- requestHost = true
- [http.middlewares.Middleware10.rateLimit.sourceCriterion.ipStrategy]
- depth = 42
- excludedIPs = ["foobar", "foobar"]
- [http.middlewares.Middleware11]
- [http.middlewares.Middleware11.redirectRegex]
- regex = "foobar"
- replacement = "foobar"
- permanent = true
- [http.middlewares.Middleware12]
- [http.middlewares.Middleware12.redirectScheme]
- scheme = "foobar"
- port = "foobar"
- permanent = true
- [http.middlewares.Middleware13]
- [http.middlewares.Middleware13.basicAuth]
- users = ["foobar", "foobar"]
- usersFile = "foobar"
- realm = "foobar"
- removeHeader = true
- headerField = "foobar"
- [http.middlewares.Middleware14]
- [http.middlewares.Middleware14.digestAuth]
- users = ["foobar", "foobar"]
- usersFile = "foobar"
- removeHeader = true
- realm = "foobar"
- headerField = "foobar"
- [http.middlewares.Middleware15]
- [http.middlewares.Middleware15.forwardAuth]
- address = "foobar"
- trustForwardHeader = true
- authResponseHeaders = ["foobar", "foobar"]
- [http.middlewares.Middleware15.forwardAuth.tls]
- ca = "foobar"
- caOptional = true
- cert = "foobar"
- key = "foobar"
- insecureSkipVerify = true
- [http.middlewares.Middleware16]
- [http.middlewares.Middleware16.inFlightReq]
- amount = 42
- [http.middlewares.Middleware16.inFlightReq.sourceCriterion]
- requestHeaderName = "foobar"
- requestHost = true
- [http.middlewares.Middleware16.inFlightReq.sourceCriterion.ipStrategy]
- depth = 42
- excludedIPs = ["foobar", "foobar"]
- [http.middlewares.Middleware17]
- [http.middlewares.Middleware17.buffering]
- maxRequestBodyBytes = 42
- memRequestBodyBytes = 42
- maxResponseBodyBytes = 42
- memResponseBodyBytes = 42
- retryExpression = "foobar"
- [http.middlewares.Middleware18]
- [http.middlewares.Middleware18.circuitBreaker]
- expression = "foobar"
- [http.middlewares.Middleware19]
- [http.middlewares.Middleware19.compress]
- [http.middlewares.Middleware2]
- [http.middlewares.Middleware2.stripPrefixRegex]
- regex = ["foobar", "foobar"]
- [http.middlewares.Middleware20]
- [http.middlewares.Middleware20.passTLSClientCert]
- pem = true
- [http.middlewares.Middleware20.passTLSClientCert.info]
- notAfter = true
- notBefore = true
- sans = true
- [http.middlewares.Middleware20.passTLSClientCert.info.subject]
- country = true
- province = true
- locality = true
- organization = true
- commonName = true
- serialNumber = true
- domainComponent = true
- [http.middlewares.Middleware20.passTLSClientCert.info.issuer]
- country = true
- province = true
- locality = true
- organization = true
- commonName = true
- serialNumber = true
- domainComponent = true
- [http.middlewares.Middleware21]
- [http.middlewares.Middleware21.retry]
- regex = 0
- [http.middlewares.Middleware3]
- [http.middlewares.Middleware3.replacePath]
- path = "foobar"
- [http.middlewares.Middleware4]
- [http.middlewares.Middleware4.replacePathRegex]
- regex = "foobar"
- replacement = "foobar"
- [http.middlewares.Middleware5]
- [http.middlewares.Middleware5.chain]
- middlewares = ["foobar", "foobar"]
- [http.middlewares.Middleware6]
- [http.middlewares.Middleware6.ipWhiteList]
- sourceRange = ["foobar", "foobar"]
- [http.middlewares.Middleware7]
- [http.middlewares.Middleware7.ipWhiteList]
- [http.middlewares.Middleware7.ipWhiteList.ipStrategy]
- depth = 42
- excludedIPs = ["foobar", "foobar"]
- [http.middlewares.Middleware8]
- [http.middlewares.Middleware8.headers]
- accessControlAllowCredentials = true
- accessControlAllowHeaders = ["foobar", "foobar"]
- accessControlAllowMethods = ["foobar", "foobar"]
- accessControlAllowOrigin = "foobar"
- accessControlExposeHeaders = ["foobar", "foobar"]
- accessControlMaxAge = 42
- addVaryHeader = true
- allowedHosts = ["foobar", "foobar"]
- hostsProxyHeaders = ["foobar", "foobar"]
- sslRedirect = true
- sslTemporaryRedirect = true
- sslHost = "foobar"
- sslForceHost = true
- stsSeconds = 42
- stsIncludeSubdomains = true
- stsPreload = true
- forceSTSHeader = true
- frameDeny = true
- customFrameOptionsValue = "foobar"
- contentTypeNosniff = true
- browserXssFilter = true
- customBrowserXSSValue = "foobar"
- contentSecurityPolicy = "foobar"
- publicKey = "foobar"
- referrerPolicy = "foobar"
- featurePolicy = "foobar"
- isDevelopment = true
- [http.middlewares.Middleware8.headers.customRequestHeaders]
- name0 = "foobar"
- name1 = "foobar"
- [http.middlewares.Middleware8.headers.customResponseHeaders]
- name0 = "foobar"
- name1 = "foobar"
- [http.middlewares.Middleware8.headers.sslProxyHeaders]
- name0 = "foobar"
- name1 = "foobar"
- [http.middlewares.Middleware9]
- [http.middlewares.Middleware9.errors]
- status = ["foobar", "foobar"]
- service = "foobar"
- query = "foobar"
- [http.services]
- [http.services.Service0]
- [http.services.Service0.loadBalancer]
- passHostHeader = true
- [http.services.Service0.loadBalancer.sticky.cookie]
- name = "foobar"
-
- [[http.services.Service0.loadBalancer.servers]]
- url = "foobar"
-
- [[http.services.Service0.loadBalancer.servers]]
- url = "foobar"
- [http.services.Service0.loadBalancer.healthCheck]
- scheme = "foobar"
- path = "foobar"
- port = 42
- interval = "foobar"
- timeout = "foobar"
- hostname = "foobar"
- [http.services.Service0.loadBalancer.healthCheck.headers]
- name0 = "foobar"
- name1 = "foobar"
- [http.services.Service0.loadBalancer.responseForwarding]
- flushInterval = "foobar"
-
-[tcp]
- [tcp.routers]
- [tcp.routers.TCPRouter0]
- entryPoints = ["foobar", "foobar"]
- service = "foobar"
- rule = "foobar"
- [tcp.routers.TCPRouter0.tls]
- passthrough = true
- [tcp.services]
- [tcp.services.TCPService0]
- [tcp.services.TCPService0.loadBalancer]
-
- [[tcp.services.TCPService0.loadBalancer.servers]]
- address = "foobar"
-
- [[tcp.services.TCPService0.loadBalancer.servers]]
- address = "foobar"
-
-[tls]
-
- [[tls.Certificates]]
- certFile = "foobar"
- keyFile = "foobar"
- stores = ["foobar", "foobar"]
-
- [[tls.Certificates]]
- certFile = "foobar"
- keyFile = "foobar"
- stores = ["foobar", "foobar"]
- [tls.options]
- [tls.options.TLS0]
- minVersion = "foobar"
- cipherSuites = ["foobar", "foobar"]
- sniStrict = true
- [tls.options.TLS0.clientAuth]
- caFiles = ["foobar", "foobar"]
- clientAuthType = "VerifyClientCertIfGiven"
- [tls.options.TLS1]
- minVersion = "foobar"
- cipherSuites = ["foobar", "foobar"]
- sniStrict = true
- [tls.options.TLS1.clientAuth]
- caFiles = ["foobar", "foobar"]
- clientAuthType = "VerifyClientCertIfGiven"
- [tls.stores]
- [tls.stores.Store0]
- [tls.stores.Store0.defaultCertificate]
- certFile = "foobar"
- keyFile = "foobar"
- [tls.stores.Store1]
- [tls.stores.Store1.defaultCertificate]
- certFile = "foobar"
- keyFile = "foobar"
diff --git a/pkg/config/file/fixtures/sample.yml b/pkg/config/file/fixtures/sample.yml
deleted file mode 100644
index b1bf1ac47..000000000
--- a/pkg/config/file/fixtures/sample.yml
+++ /dev/null
@@ -1,235 +0,0 @@
-global:
- checkNewVersion: true
- sendAnonymousUsage: true
-serversTransport:
- insecureSkipVerify: true
- rootCAs:
- - foobar
- - foobar
- maxIdleConnsPerHost: 42
- forwardingTimeouts:
- dialTimeout: 42
- responseHeaderTimeout: 42
- idleConnTimeout: 42
-entryPoints:
- EntryPoint0:
- address: foobar
- transport:
- lifeCycle:
- requestAcceptGraceTimeout: 42
- graceTimeOut: 42
- respondingTimeouts:
- readTimeout: 42
- writeTimeout: 42
- idleTimeout: 42
- proxyProtocol:
- insecure: true
- trustedIPs:
- - foobar
- - foobar
- forwardedHeaders:
- insecure: true
- trustedIPs:
- - foobar
- - foobar
-providers:
- providersThrottleDuration: 42
- docker:
- constraints: foobar
- watch: true
- endpoint: foobar
- defaultRule: foobar
- tls:
- ca: foobar
- caOptional: true
- cert: foobar
- key: foobar
- insecureSkipVerify: true
- exposedByDefault: true
- useBindPortIP: true
- swarmMode: true
- network: foobar
- swarmModeRefreshSeconds: 42
- file:
- directory: foobar
- watch: true
- filename: foobar
- debugLogGeneratedTemplate: true
- marathon:
- constraints: foobar
- trace: true
- watch: true
- endpoint: foobar
- defaultRule: foobar
- exposedByDefault: true
- dcosToken: foobar
- tls:
- ca: foobar
- caOptional: true
- cert: foobar
- key: foobar
- insecureSkipVerify: true
- dialerTimeout: 42
- responseHeaderTimeout: 42
- tlsHandshakeTimeout: 42
- keepAlive: 42
- forceTaskHostname: true
- basic:
- httpBasicAuthUser: foobar
- httpBasicPassword: foobar
- respectReadinessChecks: true
- kubernetesIngress:
- endpoint: foobar
- token: foobar
- certAuthFilePath: foobar
- disablePassHostHeaders: true
- namespaces:
- - foobar
- - foobar
- labelSelector: foobar
- ingressClass: foobar
- ingressEndpoint:
- ip: foobar
- hostname: foobar
- publishedService: foobar
- kubernetesCRD:
- endpoint: foobar
- token: foobar
- certAuthFilePath: foobar
- disablePassHostHeaders: true
- namespaces:
- - foobar
- - foobar
- labelSelector: foobar
- ingressClass: foobar
- rest:
- entryPoint: foobar
- rancher:
- constraints: foobar
- watch: true
- defaultRule: foobar
- exposedByDefault: true
- enableServiceHealthFilter: true
- refreshSeconds: 42
- intervalPoll: true
- prefix: foobar
-api:
- entryPoint: foobar
- dashboard: true
- statistics:
- recentErrors: 42
- middlewares:
- - foobar
- - foobar
-metrics:
- prometheus:
- buckets:
- - 42
- - 42
- entryPoint: foobar
- middlewares:
- - foobar
- - foobar
- datadog:
- address: foobar
- pushInterval: 10s
- statsD:
- address: foobar
- pushInterval: 10s
- influxDB:
- address: foobar
- protocol: foobar
- pushInterval: 10s
- database: foobar
- retentionPolicy: foobar
- username: foobar
- password: foobar
-ping:
- entryPoint: foobar
- middlewares:
- - foobar
- - foobar
-log:
- level: foobar
- filePath: foobar
- format: foobar
-accessLog:
- filePath: foobar
- format: foobar
- filters:
- statusCodes:
- - foobar
- - foobar
- retryAttempts: true
- minDuration: 42
- fields:
- defaultMode: foobar
- names:
- name0: foobar
- name1: foobar
- headers:
- defaultMode: foobar
- names:
- name0: foobar
- name1: foobar
- bufferingSize: 42
-tracing:
- serviceName: foobar
- spanNameLimit: 42
- jaeger:
- samplingServerURL: foobar
- samplingType: foobar
- samplingParam: 42
- localAgentHostPort: foobar
- gen128Bit: true
- propagation: foobar
- traceContextHeaderName: foobar
- zipkin:
- httpEndpoint: foobar
- sameSpan: true
- id128Bit: true
- sampleRate: 42
- datadog:
- localAgentHostPort: foobar
- globalTag: foobar
- debug: true
- prioritySampling: true
- traceIDHeaderName: foobar
- parentIDHeaderName: foobar
- samplingPriorityHeaderName: foobar
- bagagePrefixHeaderName: foobar
- instana:
- localAgentHost: foobar
- localAgentPort: 42
- logLevel: foobar
- haystack:
- localAgentHost: foobar
- localAgentPort: 42
- globalTag: foobar
- traceIDHeaderName: foobar
- parentIDHeaderName: foobar
- spanIDHeaderName: foobar
-hostResolver:
- cnameFlattening: true
- resolvConfig: foobar
- resolvDepth: 42
-
-certificatesResolvers:
- default:
- acme:
- email: foobar
- acmeLogging: true
- caServer: foobar
- storage: foobar
- entryPoint: foobar
- keyType: foobar
- dnsChallenge:
- provider: foobar
- delayBeforeCheck: 42
- resolvers:
- - foobar
- - foobar
- disablePropagationCheck: true
- httpChallenge:
- entryPoint: foobar
- tlsChallenge: {}
diff --git a/pkg/config/file/fixtures_test.go b/pkg/config/file/fixtures_test.go
deleted file mode 100644
index 9904f82d1..000000000
--- a/pkg/config/file/fixtures_test.go
+++ /dev/null
@@ -1,34 +0,0 @@
-package file
-
-type bar string
-
-type Yo struct {
- Foo string
- Fii string
- Fuu string
- Yi *Yi `file:"allowEmpty"`
-}
-
-func (y *Yo) SetDefaults() {
- y.Foo = "foo"
- y.Fii = "fii"
-}
-
-type Yi struct {
- Foo string
- Fii string
- Fuu string
-}
-
-func (y *Yi) SetDefaults() {
- y.Foo = "foo"
- y.Fii = "fii"
-}
-
-type Yu struct {
- Yi
-}
-
-type Ye struct {
- *Yi
-}
diff --git a/pkg/config/file/raw_node.go b/pkg/config/file/raw_node.go
deleted file mode 100644
index 9f9b3857b..000000000
--- a/pkg/config/file/raw_node.go
+++ /dev/null
@@ -1,152 +0,0 @@
-package file
-
-import (
- "fmt"
- "reflect"
- "sort"
- "strconv"
- "strings"
-
- "github.com/containous/traefik/v2/pkg/config/parser"
-)
-
-func decodeRawToNode(data map[string]interface{}, rootName string, filters ...string) (*parser.Node, error) {
- root := &parser.Node{
- Name: rootName,
- }
-
- vData := reflect.ValueOf(data)
- err := decodeRaw(root, vData, filters...)
- if err != nil {
- return nil, err
- }
-
- return root, nil
-}
-
-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()}
-
- switch value.Kind() {
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- fallthrough
- case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
- fallthrough
- case reflect.Float32, reflect.Float64:
- fallthrough
- case reflect.Bool:
- fallthrough
- case reflect.String:
- value, err := getSimpleValue(value)
- if err != nil {
- return err
- }
- child.Value = value
- case reflect.Slice:
- var values []string
-
- for i := 0; i < value.Len(); i++ {
- item := value.Index(i)
- switch item.Kind() {
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- fallthrough
- case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
- fallthrough
- case reflect.Bool:
- fallthrough
- case reflect.String:
- fallthrough
- case reflect.Map:
- fallthrough
- case reflect.Interface:
- sValue := reflect.ValueOf(item.Interface())
- if sValue.Kind() == reflect.Map {
- ch := &parser.Node{
- Name: "[" + strconv.Itoa(i) + "]",
- }
-
- child.Children = append(child.Children, ch)
- err := decodeRaw(ch, sValue)
- if err != nil {
- return err
- }
- } else {
- val, err := getSimpleValue(sValue)
- if err != nil {
- return err
- }
- values = append(values, val)
- }
- default:
- return fmt.Errorf("field %s uses unsupported slice type: %s", child.Name, item.Kind().String())
- }
- }
-
- child.Value = strings.Join(values, ",")
- case reflect.Map:
- err := decodeRaw(child, value)
- if err != nil {
- return err
- }
- default:
- return fmt.Errorf("field %s uses unsupported type: %s", child.Name, value.Kind().String())
- }
-
- node.Children = append(node.Children, child)
- }
-
- return nil
-}
-
-func getSimpleValue(item reflect.Value) (string, error) {
- switch item.Kind() {
- case reflect.String:
- return item.String(), nil
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- return strconv.FormatInt(item.Int(), 10), nil
- case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
- return strconv.FormatUint(item.Uint(), 10), nil
- case reflect.Float32, reflect.Float64:
- return strings.TrimSuffix(strconv.FormatFloat(item.Float(), 'f', 6, 64), ".000000"), nil
- case reflect.Bool:
- return strconv.FormatBool(item.Bool()), nil
- default:
- return "", fmt.Errorf("unsupported simple value type: %s", item.Kind().String())
- }
-}
-
-func sortKeys(vData reflect.Value, filters []string) []reflect.Value {
- var sortedKeys []reflect.Value
-
- for _, v := range vData.MapKeys() {
- rValue := reflect.ValueOf(v.Interface())
- key := rValue.String()
-
- if len(filters) == 0 {
- sortedKeys = append(sortedKeys, rValue)
- continue
- }
-
- for _, filter := range filters {
- if strings.EqualFold(key, filter) {
- sortedKeys = append(sortedKeys, rValue)
- continue
- }
- }
- }
-
- sort.Slice(sortedKeys, func(i, j int) bool {
- return sortedKeys[i].String() < sortedKeys[j].String()
- })
-
- return sortedKeys
-}
diff --git a/pkg/config/file/raw_node_test.go b/pkg/config/file/raw_node_test.go
deleted file mode 100644
index 6811543cf..000000000
--- a/pkg/config/file/raw_node_test.go
+++ /dev/null
@@ -1,578 +0,0 @@
-package file
-
-import (
- "testing"
-
- "github.com/containous/traefik/v2/pkg/config/parser"
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
-)
-
-func Test_decodeRawToNode(t *testing.T) {
- testCases := []struct {
- desc string
- data map[string]interface{}
- expected *parser.Node
- }{
- {
- desc: "empty",
- data: map[string]interface{}{},
- expected: &parser.Node{
- Name: "traefik",
- },
- },
- {
- desc: "string",
- data: map[string]interface{}{
- "foo": "bar",
- },
- expected: &parser.Node{
- Name: "traefik",
- Children: []*parser.Node{
- {Name: "foo", Value: "bar"},
- },
- },
- },
- {
- desc: "string named type",
- data: map[string]interface{}{
- "foo": bar("bar"),
- },
- expected: &parser.Node{
- Name: "traefik",
- Children: []*parser.Node{
- {Name: "foo", Value: "bar"},
- },
- },
- },
- {
- desc: "bool",
- data: map[string]interface{}{
- "foo": true,
- },
- expected: &parser.Node{
- Name: "traefik",
- Children: []*parser.Node{
- {Name: "foo", Value: "true"},
- },
- },
- },
- {
- desc: "int",
- data: map[string]interface{}{
- "foo": 1,
- },
- expected: &parser.Node{
- Name: "traefik",
- Children: []*parser.Node{
- {Name: "foo", Value: "1"},
- },
- },
- },
- {
- desc: "int8",
- data: map[string]interface{}{
- "foo": int8(1),
- },
- expected: &parser.Node{
- Name: "traefik",
- Children: []*parser.Node{
- {Name: "foo", Value: "1"},
- },
- },
- },
- {
- desc: "int16",
- data: map[string]interface{}{
- "foo": int16(1),
- },
- expected: &parser.Node{
- Name: "traefik",
- Children: []*parser.Node{
- {Name: "foo", Value: "1"},
- },
- },
- },
- {
- desc: "int32",
- data: map[string]interface{}{
- "foo": int32(1),
- },
- expected: &parser.Node{
- Name: "traefik",
- Children: []*parser.Node{
- {Name: "foo", Value: "1"},
- },
- },
- },
- {
- desc: "int64",
- data: map[string]interface{}{
- "foo": int64(1),
- },
- expected: &parser.Node{
- Name: "traefik",
- Children: []*parser.Node{
- {Name: "foo", Value: "1"},
- },
- },
- },
- {
- desc: "uint",
- data: map[string]interface{}{
- "foo": uint(1),
- },
- expected: &parser.Node{
- Name: "traefik",
- Children: []*parser.Node{
- {Name: "foo", Value: "1"},
- },
- },
- },
- {
- desc: "uint8",
- data: map[string]interface{}{
- "foo": uint8(1),
- },
- expected: &parser.Node{
- Name: "traefik",
- Children: []*parser.Node{
- {Name: "foo", Value: "1"},
- },
- },
- },
- {
- desc: "uint16",
- data: map[string]interface{}{
- "foo": uint16(1),
- },
- expected: &parser.Node{
- Name: "traefik",
- Children: []*parser.Node{
- {Name: "foo", Value: "1"},
- },
- },
- },
- {
- desc: "uint32",
- data: map[string]interface{}{
- "foo": uint32(1),
- },
- expected: &parser.Node{
- Name: "traefik",
- Children: []*parser.Node{
- {Name: "foo", Value: "1"},
- },
- },
- },
- {
- desc: "uint64",
- data: map[string]interface{}{
- "foo": uint64(1),
- },
- expected: &parser.Node{
- Name: "traefik",
- Children: []*parser.Node{
- {Name: "foo", Value: "1"},
- },
- },
- },
- {
- desc: "float32",
- data: map[string]interface{}{
- "foo": float32(1),
- },
- expected: &parser.Node{
- Name: "traefik",
- Children: []*parser.Node{
- {Name: "foo", Value: "1"},
- },
- },
- },
- {
- desc: "float64",
- data: map[string]interface{}{
- "foo": float64(1),
- },
- expected: &parser.Node{
- Name: "traefik",
- Children: []*parser.Node{
- {Name: "foo", Value: "1"},
- },
- },
- },
- {
- desc: "string slice",
- data: map[string]interface{}{
- "foo": []string{"A", "B"},
- },
- expected: &parser.Node{
- Name: "traefik",
- Children: []*parser.Node{
- {Name: "foo", Value: "A,B"},
- },
- },
- },
- {
- desc: "int slice",
- data: map[string]interface{}{
- "foo": []int{1, 2},
- },
- expected: &parser.Node{
- Name: "traefik",
- Children: []*parser.Node{
- {Name: "foo", Value: "1,2"},
- },
- },
- },
- {
- desc: "int8 slice",
- data: map[string]interface{}{
- "foo": []int8{1, 2},
- },
- expected: &parser.Node{
- Name: "traefik",
- Children: []*parser.Node{
- {Name: "foo", Value: "1,2"},
- },
- },
- },
- {
- desc: "int16 slice",
- data: map[string]interface{}{
- "foo": []int16{1, 2},
- },
- expected: &parser.Node{
- Name: "traefik",
- Children: []*parser.Node{
- {Name: "foo", Value: "1,2"},
- },
- },
- },
- {
- desc: "int32 slice",
- data: map[string]interface{}{
- "foo": []int32{1, 2},
- },
- expected: &parser.Node{
- Name: "traefik",
- Children: []*parser.Node{
- {Name: "foo", Value: "1,2"},
- },
- },
- },
- {
- desc: "int64 slice",
- data: map[string]interface{}{
- "foo": []int64{1, 2},
- },
- expected: &parser.Node{
- Name: "traefik",
- Children: []*parser.Node{
- {Name: "foo", Value: "1,2"},
- },
- },
- },
- {
- desc: "bool slice",
- data: map[string]interface{}{
- "foo": []bool{true, false},
- },
- expected: &parser.Node{
- Name: "traefik",
- Children: []*parser.Node{
- {Name: "foo", Value: "true,false"},
- },
- },
- },
- {
- desc: "interface (string) slice",
- data: map[string]interface{}{
- "foo": []interface{}{"A", "B"},
- },
- expected: &parser.Node{
- Name: "traefik",
- Children: []*parser.Node{
- {Name: "foo", Value: "A,B"},
- },
- },
- },
- {
- desc: "interface (int) slice",
- data: map[string]interface{}{
- "foo": []interface{}{1, 2},
- },
- expected: &parser.Node{
- Name: "traefik",
- Children: []*parser.Node{
- {Name: "foo", Value: "1,2"},
- },
- },
- },
- {
- desc: "2 strings",
- data: map[string]interface{}{
- "foo": "bar",
- "fii": "bir",
- },
- expected: &parser.Node{
- Name: "traefik",
- Children: []*parser.Node{
- {Name: "fii", Value: "bir"},
- {Name: "foo", Value: "bar"},
- },
- },
- },
- {
- desc: "string, level 2",
- data: map[string]interface{}{
- "fii": map[interface{}]interface{}{
- "fuu": "bur",
- },
- },
- expected: &parser.Node{
- Name: "traefik",
- Children: []*parser.Node{
- {Name: "fii", Children: []*parser.Node{{Name: "fuu", Value: "bur"}}},
- },
- },
- },
- {
- desc: "int, level 2",
- data: map[string]interface{}{
- "fii": map[interface{}]interface{}{
- "fuu": 1,
- },
- },
- expected: &parser.Node{
- Name: "traefik",
- Children: []*parser.Node{
- {Name: "fii", Children: []*parser.Node{{Name: "fuu", Value: "1"}}},
- },
- },
- },
- {
- desc: "uint, level 2",
- data: map[string]interface{}{
- "fii": map[interface{}]interface{}{
- "fuu": uint(1),
- },
- },
- expected: &parser.Node{
- Name: "traefik",
- Children: []*parser.Node{
- {Name: "fii", Children: []*parser.Node{{Name: "fuu", Value: "1"}}},
- },
- },
- },
- {
- desc: "bool, level 2",
- data: map[string]interface{}{
- "fii": map[interface{}]interface{}{
- "fuu": true,
- },
- },
- expected: &parser.Node{
- Name: "traefik",
- Children: []*parser.Node{
- {Name: "fii", Children: []*parser.Node{{Name: "fuu", Value: "true"}}},
- },
- },
- },
- {
- desc: "string, level 3",
- data: map[string]interface{}{
- "foo": map[interface{}]interface{}{
- "fii": map[interface{}]interface{}{
- "fuu": "bur",
- },
- },
- },
- expected: &parser.Node{
- Name: "traefik",
- Children: []*parser.Node{
- {Name: "foo", Children: []*parser.Node{
- {Name: "fii", Children: []*parser.Node{{Name: "fuu", Value: "bur"}}},
- }},
- },
- },
- },
- {
- desc: "int, level 3",
- data: map[string]interface{}{
- "fii": map[interface{}]interface{}{
- "fuu": 1,
- },
- },
- expected: &parser.Node{
- Name: "traefik",
- Children: []*parser.Node{
- {Name: "fii", Children: []*parser.Node{{Name: "fuu", Value: "1"}}},
- },
- },
- },
- {
- desc: "uint, level 3",
- data: map[string]interface{}{
- "fii": map[interface{}]interface{}{
- "fuu": uint(1),
- },
- },
- expected: &parser.Node{
- Name: "traefik",
- Children: []*parser.Node{
- {Name: "fii", Children: []*parser.Node{{Name: "fuu", Value: "1"}}},
- },
- },
- },
- {
- desc: "bool, level 3",
- data: map[string]interface{}{
- "fii": map[interface{}]interface{}{
- "fuu": true,
- },
- },
- expected: &parser.Node{
- Name: "traefik",
- Children: []*parser.Node{
- {Name: "fii", Children: []*parser.Node{{Name: "fuu", Value: "true"}}},
- },
- },
- },
- {
- desc: "struct",
- data: map[string]interface{}{
- "foo": map[interface{}]interface{}{
- "field1": "C",
- "field2": "C",
- },
- },
- expected: &parser.Node{
- Name: "traefik",
- Children: []*parser.Node{
- {Name: "foo", Children: []*parser.Node{
- {Name: "field1", Value: "C"},
- {Name: "field2", Value: "C"},
- }},
- },
- },
- },
- {
- desc: "slice struct 1",
- data: map[string]interface{}{
- "foo": []map[string]interface{}{
- {"field1": "A", "field2": "A"},
- {"field1": "B", "field2": "B"},
- {"field2": "C", "field1": "C"},
- },
- },
- expected: &parser.Node{
- Name: "traefik",
- Children: []*parser.Node{
- {Name: "foo", Children: []*parser.Node{
- {Name: "[0]", Children: []*parser.Node{
- {Name: "field1", Value: "A"},
- {Name: "field2", Value: "A"},
- }},
- {Name: "[1]", Children: []*parser.Node{
- {Name: "field1", Value: "B"},
- {Name: "field2", Value: "B"},
- }},
- {Name: "[2]", Children: []*parser.Node{
- {Name: "field1", Value: "C"},
- {Name: "field2", Value: "C"},
- }},
- }},
- },
- },
- },
- {
- desc: "slice struct 2",
- data: map[string]interface{}{
- "foo": []interface{}{
- map[interface{}]interface{}{
- "field2": "A",
- "field1": "A",
- },
- map[interface{}]interface{}{
- "field1": "B",
- "field2": "B",
- },
- map[interface{}]interface{}{
- "field1": "C",
- "field2": "C",
- },
- },
- },
- expected: &parser.Node{
- Name: "traefik",
- Children: []*parser.Node{
- {Name: "foo", Children: []*parser.Node{
- {Name: "[0]", Children: []*parser.Node{
- {Name: "field1", Value: "A"},
- {Name: "field2", Value: "A"},
- }},
- {Name: "[1]", Children: []*parser.Node{
- {Name: "field1", Value: "B"},
- {Name: "field2", Value: "B"},
- }},
- {Name: "[2]", Children: []*parser.Node{
- {Name: "field1", Value: "C"},
- {Name: "field2", Value: "C"},
- }},
- }},
- },
- },
- },
- {
- 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 {
- test := test
- t.Run(test.desc, func(t *testing.T) {
- t.Parallel()
-
- node, err := decodeRawToNode(test.data, parser.DefaultRootName)
- require.NoError(t, err)
-
- assert.Equal(t, test.expected, node)
- })
- }
-}
-
-func Test_decodeRawToNode_errors(t *testing.T) {
- testCases := []struct {
- desc string
- data map[string]interface{}
- }{
- {
- desc: "invalid type",
- data: map[string]interface{}{
- "foo": struct{}{},
- },
- },
- }
-
- for _, test := range testCases {
- test := test
- t.Run(test.desc, func(t *testing.T) {
- t.Parallel()
-
- _, err := decodeRawToNode(test.data, parser.DefaultRootName)
- require.Error(t, err)
- })
- }
-}
diff --git a/pkg/config/flag/flag.go b/pkg/config/flag/flag.go
deleted file mode 100644
index fd508a787..000000000
--- a/pkg/config/flag/flag.go
+++ /dev/null
@@ -1,47 +0,0 @@
-// Package flag implements encoding and decoding between flag arguments and a typed Configuration.
-package flag
-
-import (
- "github.com/containous/traefik/v2/pkg/config/parser"
-)
-
-// Decode decodes the given flag arguments into the given element.
-// The operation goes through four stages roughly summarized as:
-// flag arguments -> parsed map of flags
-// map -> tree of untyped nodes
-// untyped nodes -> nodes augmented with metadata such as kind (inferred from element)
-// "typed" nodes -> typed element.
-func Decode(args []string, element interface{}) error {
- ref, err := Parse(args, element)
- if err != nil {
- return err
- }
-
- return parser.Decode(ref, element, parser.DefaultRootName)
-}
-
-// Encode encodes the configuration in element into the flags represented in the returned Flats.
-// The operation goes through three stages roughly summarized as:
-// typed configuration in element -> tree of untyped nodes
-// untyped nodes -> nodes augmented with metadata such as kind (inferred from element)
-// "typed" nodes -> flags with default values (determined by type/kind).
-func Encode(element interface{}) ([]parser.Flat, error) {
- if element == nil {
- return nil, nil
- }
-
- etnOpts := parser.EncoderToNodeOpts{OmitEmpty: false, TagName: parser.TagLabel, AllowSliceAsStruct: true}
- node, err := parser.EncodeToNode(element, parser.DefaultRootName, etnOpts)
- if err != nil {
- return nil, err
- }
-
- metaOpts := parser.MetadataOpts{TagName: parser.TagLabel, AllowSliceAsStruct: true}
- err = parser.AddMetadata(element, node, metaOpts)
- if err != nil {
- return nil, err
- }
-
- flatOpts := parser.FlatOpts{Separator: ".", SkipRoot: true, TagName: parser.TagLabel}
- return parser.EncodeToFlat(element, node, flatOpts)
-}
diff --git a/pkg/config/flag/flag_test.go b/pkg/config/flag/flag_test.go
deleted file mode 100644
index 27112020a..000000000
--- a/pkg/config/flag/flag_test.go
+++ /dev/null
@@ -1,940 +0,0 @@
-package flag
-
-import (
- "testing"
- "time"
-
- "github.com/containous/traefik/v2/pkg/config/generator"
- "github.com/containous/traefik/v2/pkg/config/parser"
- "github.com/containous/traefik/v2/pkg/types"
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
-)
-
-func TestDecode(t *testing.T) {
- testCases := []struct {
- desc string
- args []string
- element interface{}
- expected interface{}
- }{
- {
- desc: "no args",
- args: nil,
- expected: nil,
- },
- {
- desc: "types.Duration value",
- args: []string{"--foo=1"},
- element: &struct {
- Foo types.Duration
- }{},
- expected: &struct {
- Foo types.Duration
- }{
- Foo: types.Duration(1 * time.Second),
- },
- },
- {
- desc: "time.Duration value",
- args: []string{"--foo=1"},
- element: &struct {
- Foo time.Duration
- }{},
- expected: &struct {
- Foo time.Duration
- }{
- Foo: 1 * time.Nanosecond,
- },
- },
- {
- desc: "bool value",
- args: []string{"--foo"},
- element: &struct {
- Foo bool
- }{},
- expected: &struct {
- Foo bool
- }{
- Foo: true,
- },
- },
- {
- desc: "equal",
- args: []string{"--foo=bar"},
- element: &struct {
- Foo string
- }{},
- expected: &struct {
- Foo string
- }{
- Foo: "bar",
- },
- },
- {
- desc: "space separated",
- args: []string{"--foo", "bar"},
- element: &struct {
- Foo string
- }{},
- expected: &struct {
- Foo string
- }{
- Foo: "bar",
- },
- },
- {
- desc: "space separated with end of parameter",
- args: []string{"--foo=bir", "--", "--bar"},
- element: &struct {
- Foo string
- }{},
- expected: &struct {
- Foo string
- }{
- Foo: "bir",
- },
- },
- {
- desc: "multiple bool flags without value",
- args: []string{"--foo", "--bar"},
- element: &struct {
- Foo bool
- Bar bool
- }{},
- expected: &struct {
- Foo bool
- Bar bool
- }{
- Foo: true,
- Bar: true,
- },
- },
- {
- desc: "slice with several flags",
- args: []string{"--foo=bar", "--foo=baz"},
- element: &struct {
- Foo []string
- }{},
- expected: &struct {
- Foo []string
- }{
- Foo: []string{"bar", "baz"},
- },
- },
- {
- desc: "map string",
- args: []string{"--foo.name=bar"},
- element: &struct {
- Foo map[string]string
- }{},
- expected: &struct {
- Foo map[string]string
- }{
- Foo: map[string]string{
- "name": "bar",
- },
- },
- },
- {
- desc: "map string case sensitive",
- args: []string{"--foo.caseSensitiveName=barBoo"},
- element: &struct {
- Foo map[string]string
- }{},
- expected: &struct {
- Foo map[string]string
- }{
- Foo: map[string]string{
- "caseSensitiveName": "barBoo",
- },
- },
- },
- {
- desc: "map struct",
- args: []string{"--foo.name.value=bar"},
- element: &struct {
- Foo map[string]struct{ Value string }
- }{},
- expected: &struct {
- Foo map[string]struct{ Value string }
- }{
- Foo: map[string]struct{ Value string }{
- "name": {
- Value: "bar",
- },
- },
- },
- },
- {
- desc: "map struct with sub-struct",
- args: []string{"--foo.name.bar.value=bar"},
- element: &struct {
- Foo map[string]struct {
- Bar *struct{ Value string }
- }
- }{},
- expected: &struct {
- Foo map[string]struct {
- Bar *struct{ Value string }
- }
- }{
- Foo: map[string]struct {
- Bar *struct{ Value string }
- }{
- "name": {
- Bar: &struct {
- Value string
- }{
- Value: "bar",
- },
- },
- },
- },
- },
- {
- desc: "map struct with sub-map",
- args: []string{"--foo.name1.bar.name2.value=bar"},
- element: &struct {
- Foo map[string]struct {
- Bar map[string]struct{ Value string }
- }
- }{},
- expected: &struct {
- Foo map[string]struct {
- Bar map[string]struct{ Value string }
- }
- }{
- Foo: map[string]struct {
- Bar map[string]struct{ Value string }
- }{
- "name1": {
- Bar: map[string]struct{ Value string }{
- "name2": {
- Value: "bar",
- },
- },
- },
- },
- },
- },
- {
- desc: "slice with several flags 2",
- args: []string{"--foo", "bar", "--foo", "baz"},
- element: &struct {
- Foo []string
- }{},
- expected: &struct {
- Foo []string
- }{
- Foo: []string{"bar", "baz"},
- },
- },
- {
- desc: "slice with several flags 3",
- args: []string{"--foo", "bar", "--foo=", "--baz"},
- element: &struct {
- Foo []string
- Baz bool
- }{},
- expected: &struct {
- Foo []string
- Baz bool
- }{
- Foo: []string{"bar", ""},
- Baz: true,
- },
- },
- {
- desc: "slice with several flags 4",
- args: []string{"--foo", "bar", "--foo", "--baz"},
- element: &struct {
- Foo []string
- Baz bool
- }{},
- expected: &struct {
- Foo []string
- Baz bool
- }{
- Foo: []string{"bar", "--baz"},
- },
- },
- {
- desc: "slice of struct",
- args: []string{
- "--foo[0].Field1", "bar", "--foo[0].Field2", "6",
- "--foo[1].Field1", "bur", "--foo[1].Field2", "2",
- },
- element: &struct {
- Foo []struct {
- Field1 string
- Field2 int
- }
- }{},
- expected: &struct {
- Foo []struct {
- Field1 string
- Field2 int
- }
- }{
- Foo: []struct {
- Field1 string
- Field2 int
- }{
- {
- Field1: "bar",
- Field2: 6,
- },
- {
- Field1: "bur",
- Field2: 2,
- },
- },
- },
- },
- {
- desc: "slice of pointer of struct",
- args: []string{
- "--foo[0].Field1", "bar", "--foo[0].Field2", "6",
- "--foo[1].Field1", "bur", "--foo[1].Field2", "2",
- },
- element: &struct {
- Foo []*struct {
- Field1 string
- Field2 int
- }
- }{},
- expected: &struct {
- Foo []*struct {
- Field1 string
- Field2 int
- }
- }{
- Foo: []*struct {
- Field1 string
- Field2 int
- }{
- {
- Field1: "bar",
- Field2: 6,
- },
- {
- Field1: "bur",
- Field2: 2,
- },
- },
- },
- },
- {
- desc: "multiple string flag",
- element: &struct {
- Foo string
- }{},
- args: []string{"--foo=bar", "--foo=baz"},
- expected: &struct {
- Foo string
- }{
- Foo: "baz",
- },
- },
- {
- desc: "multiple string flag 2",
- element: &struct {
- Foo string
- }{},
- args: []string{"--foo", "bar", "--foo", "baz"},
- expected: &struct {
- Foo string
- }{
- Foo: "baz",
- },
- },
- {
- desc: "string without value",
- element: &struct {
- Foo string
- Bar bool
- }{},
- args: []string{"--foo", "--bar"},
- expected: &struct {
- Foo string
- Bar bool
- }{
- Foo: "--bar",
- },
- },
- {
- desc: "struct pointer value",
- args: []string{"--foo"},
- element: &struct {
- Foo *struct{ Field string } `label:"allowEmpty"`
- }{},
- expected: &struct {
- Foo *struct{ Field string } `label:"allowEmpty"`
- }{
- Foo: &struct{ Field string }{},
- },
- },
- }
-
- for _, test := range testCases {
- test := test
- t.Run(test.desc, func(t *testing.T) {
- t.Parallel()
-
- err := Decode(test.args, test.element)
- require.NoError(t, err)
-
- assert.Equal(t, test.expected, test.element)
- })
- }
-}
-
-func TestEncode(t *testing.T) {
- testCases := []struct {
- desc string
- element interface{}
- expected []parser.Flat
- }{
- {
- desc: "string field",
- element: &struct {
- Field string `description:"field description"`
- }{
- Field: "test",
- },
- expected: []parser.Flat{{
- Name: "field",
- Description: "field description",
- Default: "test",
- }},
- },
- {
- desc: "int field",
- element: &struct {
- Field int `description:"field description"`
- }{
- Field: 6,
- },
- expected: []parser.Flat{{
- Name: "field",
- Description: "field description",
- Default: "6",
- }},
- },
- {
- desc: "bool field",
- element: &struct {
- Field bool `description:"field description"`
- }{
- Field: true,
- },
- expected: []parser.Flat{{
- Name: "field",
- Description: "field description",
- Default: "true",
- }},
- },
- {
- desc: "string pointer field",
- element: &struct {
- Field *string `description:"field description"`
- }{
- Field: func(v string) *string { return &v }("test"),
- },
- expected: []parser.Flat{{
- Name: "field",
- Description: "field description",
- Default: "test",
- }},
- },
- {
- desc: "int pointer field",
- element: &struct {
- Field *int `description:"field description"`
- }{
- Field: func(v int) *int { return &v }(6),
- },
- expected: []parser.Flat{{
- Name: "field",
- Description: "field description",
- Default: "6",
- }},
- },
- {
- desc: "bool pointer field",
- element: &struct {
- Field *bool `description:"field description"`
- }{
- Field: func(v bool) *bool { return &v }(true),
- },
- expected: []parser.Flat{{
- Name: "field",
- Description: "field description",
- Default: "true",
- }},
- },
- {
- desc: "slice of string field, no initial value",
- element: &struct {
- Field []string `description:"field description"`
- }{},
- expected: []parser.Flat{{
- Name: "field",
- Description: "field description",
- Default: "",
- }},
- },
- {
- desc: "slice of string field, with initial value",
- element: &struct {
- Field []string `description:"field description"`
- }{
- Field: []string{"foo", "bar"},
- },
- expected: []parser.Flat{{
- Name: "field",
- Description: "field description",
- Default: "foo, bar",
- }},
- },
- {
- desc: "slice of int field, no initial value",
- element: &struct {
- Field []int `description:"field description"`
- }{},
- expected: []parser.Flat{{
- Name: "field",
- Description: "field description",
- Default: "",
- }},
- },
- {
- desc: "slice of int field, with initial value",
- element: &struct {
- Field []int `description:"field description"`
- }{
- Field: []int{6, 3},
- },
- expected: []parser.Flat{{
- Name: "field",
- Description: "field description",
- Default: "6, 3",
- }},
- },
- {
- desc: "map string field",
- element: &struct {
- Field map[string]string `description:"field description"`
- }{
- Field: map[string]string{
- parser.MapNamePlaceholder: "",
- },
- },
- expected: []parser.Flat{{
- Name: "field.",
- Description: "field description",
- Default: "",
- }},
- },
- {
- desc: "struct pointer field",
- element: &struct {
- Foo *struct {
- Field string `description:"field description"`
- } `description:"foo description"`
- }{
- Foo: &struct {
- Field string `description:"field description"`
- }{
- Field: "test",
- },
- },
- expected: []parser.Flat{
- {
- Name: "foo.field",
- Description: "field description",
- Default: "test",
- },
- },
- },
- {
- desc: "struct pointer field, allow empty",
- element: &struct {
- Foo *struct {
- Field string `description:"field description"`
- } `description:"foo description" label:"allowEmpty"`
- }{
- Foo: &struct {
- Field string `description:"field description"`
- }{
- Field: "test",
- },
- },
- expected: []parser.Flat{
- {
- Name: "foo",
- Description: "foo description",
- Default: "false",
- },
- {
- Name: "foo.field",
- Description: "field description",
- Default: "test",
- },
- },
- },
- {
- desc: "struct pointer field level 2",
- element: &struct {
- Foo *struct {
- Fii *struct {
- Field string `description:"field description"`
- } `description:"fii description"`
- } `description:"foo description"`
- }{
- Foo: &struct {
- Fii *struct {
- Field string `description:"field description"`
- } `description:"fii description"`
- }{
- Fii: &struct {
- Field string `description:"field description"`
- }{
- Field: "test",
- },
- },
- },
- expected: []parser.Flat{
- {
- Name: "foo.fii.field",
- Description: "field description",
- Default: "test",
- },
- },
- },
- {
- desc: "struct pointer field level 2, allow empty",
- element: &struct {
- Foo *struct {
- Fii *struct {
- Field string `description:"field description"`
- } `description:"fii description" label:"allowEmpty"`
- } `description:"foo description" label:"allowEmpty"`
- }{
- Foo: &struct {
- Fii *struct {
- Field string `description:"field description"`
- } `description:"fii description" label:"allowEmpty"`
- }{
- Fii: &struct {
- Field string `description:"field description"`
- }{
- Field: "test",
- },
- },
- },
- expected: []parser.Flat{
- {
- Name: "foo",
- Description: "foo description",
- Default: "false",
- },
- {
- Name: "foo.fii",
- Description: "fii description",
- Default: "false",
- },
- {
- Name: "foo.fii.field",
- Description: "field description",
- Default: "test",
- },
- },
- },
- {
- desc: "map string field level 2",
- element: &struct {
- Foo *struct {
- Fii map[string]string `description:"fii description"`
- } `description:"foo description"`
- }{
- Foo: &struct {
- Fii map[string]string `description:"fii description"`
- }{
- Fii: map[string]string{
- parser.MapNamePlaceholder: "",
- },
- },
- },
- expected: []parser.Flat{
- {
- Name: "foo.fii.",
- Description: "fii description",
- Default: "",
- },
- },
- },
- {
- desc: "map string pointer field level 2",
- element: &struct {
- Foo *struct {
- Fii map[string]*string `description:"fii description"`
- } `description:"foo description"`
- }{
- Foo: &struct {
- Fii map[string]*string `description:"fii description"`
- }{
- Fii: map[string]*string{
- parser.MapNamePlaceholder: func(v string) *string { return &v }(""),
- },
- },
- },
- expected: []parser.Flat{
- {
- Name: "foo.fii.",
- Description: "fii description",
- Default: "",
- },
- },
- },
- {
- desc: "map struct level 1",
- element: &struct {
- Foo map[string]struct {
- Field string `description:"field description"`
- Yo int `description:"yo description"`
- } `description:"foo description"`
- }{},
- expected: []parser.Flat{
- {
- Name: "foo.",
- Description: "foo description",
- Default: "false",
- },
- {
- Name: "foo..field",
- Description: "field description",
- Default: "",
- },
- {
- Name: "foo..yo",
- Description: "yo description",
- Default: "0",
- },
- },
- },
- {
- desc: "map struct pointer level 1",
- element: &struct {
- Foo map[string]*struct {
- Field string `description:"field description"`
- Yo string `description:"yo description"`
- } `description:"foo description"`
- }{},
- expected: []parser.Flat{
- {
- Name: "foo.",
- Description: "foo description",
- Default: "false",
- },
- {
- Name: "foo..field",
- Description: "field description",
- Default: "",
- },
- {
- Name: "foo..yo",
- Description: "yo description",
- Default: "",
- },
- },
- },
- {
- desc: "time duration field",
- element: &struct {
- Field time.Duration `description:"field description"`
- }{
- Field: 1 * time.Second,
- },
- expected: []parser.Flat{{
- Name: "field",
- Description: "field description",
- Default: "1s",
- }},
- },
- {
- desc: "time duration field map",
- element: &struct {
- Foo map[string]*struct {
- Field time.Duration `description:"field description"`
- } `description:"foo description"`
- }{
- Foo: map[string]*struct {
- Field time.Duration `description:"field description"`
- }{},
- },
- expected: []parser.Flat{
- {
- Name: "foo.",
- Description: "foo description",
- Default: "false",
- },
- {
- Name: "foo..field",
- Description: "field description",
- Default: "0s",
- },
- },
- },
- {
- desc: "time duration field map 2",
- element: &struct {
- Foo map[string]*struct {
- Fii *struct {
- Field time.Duration `description:"field description"`
- }
- } `description:"foo description"`
- }{
- Foo: map[string]*struct {
- Fii *struct {
- Field time.Duration `description:"field description"`
- }
- }{},
- },
- expected: []parser.Flat{
- {
- Name: "foo.",
- Description: "foo description",
- Default: "false",
- },
- {
- Name: "foo..fii.field",
- Description: "field description",
- Default: "0s",
- },
- },
- },
- {
- desc: "time duration field 2",
- element: &struct {
- Foo *struct {
- Field time.Duration `description:"field description"`
- }
- }{
- Foo: &struct {
- Field time.Duration `description:"field description"`
- }{
- Field: 1 * time.Second,
- },
- },
- expected: []parser.Flat{{
- Name: "foo.field",
- Description: "field description",
- Default: "1s",
- }},
- },
- {
- desc: "time duration field 3",
- element: &struct {
- Foo *struct {
- Fii *struct {
- Field time.Duration `description:"field description"`
- }
- }
- }{
- Foo: &struct {
- Fii *struct {
- Field time.Duration `description:"field description"`
- }
- }{
- Fii: &struct {
- Field time.Duration `description:"field description"`
- }{
- Field: 1 * time.Second,
- },
- },
- },
- expected: []parser.Flat{{
- Name: "foo.fii.field",
- Description: "field description",
- Default: "1s",
- }},
- },
- {
- desc: "time duration field",
- element: &struct {
- Field types.Duration `description:"field description"`
- }{
- Field: types.Duration(180 * time.Second),
- },
- expected: []parser.Flat{{
- Name: "field",
- Description: "field description",
- Default: "180",
- }},
- },
- {
- desc: "slice of struct",
- element: &struct {
- Foo *struct {
- Fii []struct {
- Field1 string `description:"field1 description"`
- Field2 int `description:"field2 description"`
- } `description:"fii description"`
- } `description:"foo description"`
- }{},
- expected: []parser.Flat{
- {
- Name: "foo.fii",
- Description: "fii description",
- Default: "",
- },
- {
- Name: "foo.fii[0].field1",
- Description: "field1 description",
- Default: "",
- },
- {
- Name: "foo.fii[0].field2",
- Description: "field2 description",
- Default: "0",
- },
- },
- },
- // Skipped: because realistically not needed in Traefik for now.
- // {
- // desc: "map of map field level 2",
- // element: &struct {
- // Foo *struct {
- // Fii map[string]map[string]string `description:"fii description"`
- // } `description:"foo description"`
- // }{
- // Foo: &struct {
- // Fii map[string]map[string]string `description:"fii description"`
- // }{
- // Fii: map[string]map[string]string{
- // parser.MapNamePlaceholder: {
- // parser.MapNamePlaceholder: "test",
- // },
- // },
- // },
- // },
- // expected: `XXX`,
- // },
- }
-
- for _, test := range testCases {
- test := test
- t.Run(test.desc, func(t *testing.T) {
- t.Parallel()
-
- generator.Generate(test.element)
-
- entries, err := Encode(test.element)
- require.NoError(t, err)
-
- assert.Equal(t, test.expected, entries)
- })
- }
-}
diff --git a/pkg/config/flag/flagparser.go b/pkg/config/flag/flagparser.go
deleted file mode 100644
index 4571bed44..000000000
--- a/pkg/config/flag/flagparser.go
+++ /dev/null
@@ -1,141 +0,0 @@
-package flag
-
-import (
- "fmt"
- "reflect"
- "regexp"
- "strings"
-
- "github.com/containous/traefik/v2/pkg/config/parser"
-)
-
-// Parse parses the command-line flag arguments into a map,
-// using the type information in element to discriminate whether a flag is supposed to be a bool,
-// and other such ambiguities.
-func Parse(args []string, element interface{}) (map[string]string, error) {
- f := flagSet{
- flagTypes: getFlagTypes(element),
- args: args,
- values: make(map[string]string),
- keys: make(map[string]string),
- }
-
- for {
- seen, err := f.parseOne()
- if seen {
- continue
- }
- if err == nil {
- break
- }
- return nil, err
- }
- return f.values, nil
-}
-
-type flagSet struct {
- flagTypes map[string]reflect.Kind
- args []string
- values map[string]string
- keys map[string]string
-}
-
-func (f *flagSet) parseOne() (bool, error) {
- if len(f.args) == 0 {
- return false, nil
- }
-
- s := f.args[0]
- if len(s) < 2 || s[0] != '-' {
- return false, nil
- }
- numMinuses := 1
- if s[1] == '-' {
- numMinuses++
- if len(s) == 2 { // "--" terminates the flags
- f.args = f.args[1:]
- return false, nil
- }
- }
-
- name := s[numMinuses:]
- if len(name) == 0 || name[0] == '-' || name[0] == '=' {
- return false, fmt.Errorf("bad flag syntax: %s", s)
- }
-
- // it's a flag. does it have an argument?
- f.args = f.args[1:]
- hasValue := false
- value := ""
- for i := 1; i < len(name); i++ { // equals cannot be first
- if name[i] == '=' {
- value = name[i+1:]
- hasValue = true
- name = name[0:i]
- break
- }
- }
-
- if hasValue {
- f.setValue(name, value)
- return true, nil
- }
-
- flagType := f.getFlagType(name)
- if flagType == reflect.Bool || flagType == reflect.Ptr {
- f.setValue(name, "true")
- return true, nil
- }
-
- if len(f.args) > 0 {
- // value is the next arg
- hasValue = true
- value, f.args = f.args[0], f.args[1:]
- }
-
- if !hasValue {
- return false, fmt.Errorf("flag needs an argument: -%s", name)
- }
-
- f.setValue(name, value)
- return true, nil
-}
-
-func (f *flagSet) setValue(name, value string) {
- srcKey := parser.DefaultRootName + "." + name
- neutralKey := strings.ToLower(srcKey)
-
- key, ok := f.keys[neutralKey]
- if !ok {
- f.keys[neutralKey] = srcKey
- key = srcKey
- }
-
- v, ok := f.values[key]
- if ok && f.getFlagType(name) == reflect.Slice {
- f.values[key] = v + "," + value
- return
- }
-
- f.values[key] = value
-}
-
-func (f *flagSet) getFlagType(name string) reflect.Kind {
- neutral := strings.ToLower(name)
-
- kind, ok := f.flagTypes[neutral]
- if ok {
- return kind
- }
-
- for n, k := range f.flagTypes {
- if strings.Contains(n, parser.MapNamePlaceholder) {
- p := strings.NewReplacer(".", `\.`, parser.MapNamePlaceholder, `([^.]+)`).Replace(n)
- if regexp.MustCompile(p).MatchString(neutral) {
- return k
- }
- }
- }
-
- return reflect.Invalid
-}
diff --git a/pkg/config/flag/flagparser_test.go b/pkg/config/flag/flagparser_test.go
deleted file mode 100644
index 461dfad70..000000000
--- a/pkg/config/flag/flagparser_test.go
+++ /dev/null
@@ -1,353 +0,0 @@
-package flag
-
-import (
- "testing"
-
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
-)
-
-func TestParse(t *testing.T) {
- testCases := []struct {
- desc string
- args []string
- element interface{}
- expected map[string]string
- }{
- {
- desc: "no args",
- args: nil,
- expected: map[string]string{},
- },
- {
- desc: "bool value",
- args: []string{"--foo"},
- element: &struct {
- Foo bool
- }{},
- expected: map[string]string{
- "traefik.foo": "true",
- },
- },
- {
- desc: "bool value capitalized",
- args: []string{"--Foo"},
- element: &struct {
- Foo bool
- }{},
- expected: map[string]string{
- "traefik.Foo": "true",
- },
- },
- {
- desc: "equal",
- args: []string{"--foo=bar"},
- element: &struct {
- Foo string
- }{},
- expected: map[string]string{
- "traefik.foo": "bar",
- },
- },
- {
- desc: "equal",
- args: []string{"--Foo=Bar"},
- element: &struct {
- Foo string
- }{},
- expected: map[string]string{
- "traefik.Foo": "Bar",
- },
- },
- {
- desc: "space separated",
- args: []string{"--foo", "bar"},
- element: &struct {
- Foo string
- }{},
- expected: map[string]string{
- "traefik.foo": "bar",
- },
- },
- {
- desc: "space separated capitalized",
- args: []string{"--Foo", "Bar"},
- element: &struct {
- Foo string
- }{},
- expected: map[string]string{
- "traefik.Foo": "Bar",
- },
- },
- {
- desc: "space separated with end of parameter",
- args: []string{"--foo=bir", "--", "--bar"},
- element: &struct {
- Foo string
- }{},
- expected: map[string]string{
- "traefik.foo": "bir",
- },
- },
- {
- desc: "multiple bool flags without value",
- args: []string{"--foo", "--bar"},
- element: &struct {
- Foo bool
- Bar bool
- }{},
- expected: map[string]string{
- "traefik.foo": "true",
- "traefik.bar": "true",
- },
- },
- {
- desc: "slice with several flags",
- args: []string{"--foo=bar", "--foo=baz"},
- element: &struct {
- Foo []string
- }{},
- expected: map[string]string{
- "traefik.foo": "bar,baz",
- },
- },
- {
- desc: "map string",
- args: []string{"--foo.name=bar"},
- element: &struct {
- Foo map[string]string
- }{},
- expected: map[string]string{
- "traefik.foo.name": "bar",
- },
- },
- {
- desc: "map string capitalized",
- args: []string{"--foo.Name=Bar"},
- element: &struct {
- Foo map[string]string
- }{},
- expected: map[string]string{
- "traefik.foo.Name": "Bar",
- },
- },
- {
- desc: "map struct",
- args: []string{"--foo.name.value=bar"},
- element: &struct {
- Foo map[string]struct{ Value string }
- }{},
- expected: map[string]string{
- "traefik.foo.name.value": "bar",
- },
- },
- {
- desc: "map struct with sub-struct",
- args: []string{"--foo.name.bar.value=bar"},
- element: &struct {
- Foo map[string]struct {
- Bar *struct{ Value string }
- }
- }{},
- expected: map[string]string{
- "traefik.foo.name.bar.value": "bar",
- },
- },
- {
- desc: "map struct with sub-map",
- args: []string{"--foo.name1.bar.name2.value=bar"},
- element: &struct {
- Foo map[string]struct {
- Bar map[string]struct{ Value string }
- }
- }{},
- expected: map[string]string{
- "traefik.foo.name1.bar.name2.value": "bar",
- },
- },
- {
- desc: "slice with several flags 2",
- args: []string{"--foo", "bar", "--foo", "baz"},
- element: &struct {
- Foo []string
- }{},
- expected: map[string]string{
- "traefik.foo": "bar,baz",
- },
- },
- {
- desc: "slice with several flags 3",
- args: []string{"--foo", "bar", "--foo=", "--baz"},
- element: &struct {
- Foo []string
- Baz bool
- }{},
- expected: map[string]string{
- "traefik.foo": "bar,",
- "traefik.baz": "true",
- },
- },
- {
- desc: "slice with several flags 4",
- args: []string{"--foo", "bar", "--foo", "--baz"},
- element: &struct {
- Foo []string
- Baz bool
- }{},
- expected: map[string]string{
- "traefik.foo": "bar,--baz",
- },
- },
- {
- desc: "multiple string flag",
- element: &struct {
- Foo string
- }{},
- args: []string{"--foo=bar", "--foo=baz"},
- expected: map[string]string{
- "traefik.foo": "baz",
- },
- },
- {
- desc: "multiple string flag 2",
- element: &struct {
- Foo string
- }{},
- args: []string{"--foo", "bar", "--foo", "baz"},
- expected: map[string]string{
- "traefik.foo": "baz",
- },
- },
- {
- desc: "string without value",
- element: &struct {
- Foo string
- Bar bool
- }{},
- args: []string{"--foo", "--bar"},
- expected: map[string]string{
- "traefik.foo": "--bar",
- },
- },
- {
- desc: "struct pointer value",
- args: []string{"--foo"},
- element: &struct {
- Foo *struct{ Field string }
- }{},
- expected: map[string]string{
- "traefik.foo": "true",
- },
- },
- {
- desc: "map string case sensitive",
- args: []string{"--foo.caseSensitiveName=barBoo"},
- element: &struct {
- Foo map[string]string
- }{},
- expected: map[string]string{
- "traefik.foo.caseSensitiveName": "barBoo",
- },
- },
- {
- desc: "map struct with sub-map case sensitive",
- args: []string{"--foo.Name1.bar.name2.value=firstValue", "--foo.naMe1.bar.name2.value=secondValue"},
- element: &struct {
- Foo map[string]struct {
- Bar map[string]struct{ Value string }
- }
- }{},
- expected: map[string]string{
- "traefik.foo.Name1.bar.name2.value": "secondValue",
- },
- },
- {
- desc: "map struct with sub-map and different case",
- args: []string{"--foo.Name1.bar.name2.value=firstValue", "--foo.naMe1.bar.name2.value=secondValue"},
- element: &struct {
- Foo map[string]struct {
- Bar map[string]struct{ Value string }
- }
- }{},
- expected: map[string]string{
- "traefik.foo.Name1.bar.name2.value": "secondValue",
- },
- },
- {
- desc: "pointer of struct and map without explicit value",
- args: []string{"--foo.default.bar.fuu"},
- element: &struct {
- Foo map[string]struct {
- Bar *struct {
- Fuu *struct{ Value string }
- }
- }
- }{},
- expected: map[string]string{
- "traefik.foo.default.bar.fuu": "true",
- },
- },
- {
- desc: "slice with several flags 2 and different cases.",
- args: []string{"--foo", "bar", "--Foo", "baz"},
- element: &struct {
- Foo []string
- }{},
- expected: map[string]string{
- "traefik.foo": "bar,baz",
- },
- },
- }
-
- for _, test := range testCases {
- test := test
- t.Run(test.desc, func(t *testing.T) {
- t.Parallel()
-
- fl, err := Parse(test.args, test.element)
- require.NoError(t, err)
- assert.Equal(t, test.expected, fl)
- })
- }
-}
-
-func TestParse_Errors(t *testing.T) {
- testCases := []struct {
- desc string
- args []string
- element interface{}
- }{
- {
- desc: "triple hyphen",
- args: []string{"---foo"},
- element: &struct {
- Foo bool
- }{},
- },
- {
- desc: "equal",
- args: []string{"--=foo"},
- element: &struct {
- Foo bool
- }{},
- },
- {
- desc: "string without value",
- element: &struct {
- Foo string
- Bar bool
- }{},
- args: []string{"--foo"},
- },
- }
-
- for _, test := range testCases {
- test := test
- t.Run(test.desc, func(t *testing.T) {
- t.Parallel()
-
- _, err := Parse(test.args, test.element)
- require.Error(t, err)
- })
- }
-}
diff --git a/pkg/config/flag/flagtype.go b/pkg/config/flag/flagtype.go
deleted file mode 100644
index 12abd152d..000000000
--- a/pkg/config/flag/flagtype.go
+++ /dev/null
@@ -1,60 +0,0 @@
-package flag
-
-import (
- "reflect"
- "strings"
-
- "github.com/containous/traefik/v2/pkg/config/parser"
-)
-
-func getFlagTypes(element interface{}) map[string]reflect.Kind {
- ref := map[string]reflect.Kind{}
-
- if element == nil {
- return ref
- }
-
- tp := reflect.TypeOf(element).Elem()
-
- addFlagType(ref, "", tp)
-
- return ref
-}
-
-func addFlagType(ref map[string]reflect.Kind, name string, typ reflect.Type) {
- switch typ.Kind() {
- case reflect.Bool, reflect.Slice:
- ref[name] = typ.Kind()
-
- case reflect.Map:
- addFlagType(ref, getName(name, parser.MapNamePlaceholder), typ.Elem())
-
- case reflect.Ptr:
- if typ.Elem().Kind() == reflect.Struct {
- ref[name] = typ.Kind()
- }
- addFlagType(ref, name, typ.Elem())
-
- case reflect.Struct:
- for j := 0; j < typ.NumField(); j++ {
- subField := typ.Field(j)
-
- if !parser.IsExported(subField) {
- continue
- }
-
- if subField.Anonymous {
- addFlagType(ref, getName(name), subField.Type)
- } else {
- addFlagType(ref, getName(name, subField.Name), subField.Type)
- }
- }
-
- default:
- // noop
- }
-}
-
-func getName(names ...string) string {
- return strings.TrimPrefix(strings.ToLower(strings.Join(names, ".")), ".")
-}
diff --git a/pkg/config/flag/flagtype_test.go b/pkg/config/flag/flagtype_test.go
deleted file mode 100644
index 93dcb6fca..000000000
--- a/pkg/config/flag/flagtype_test.go
+++ /dev/null
@@ -1,226 +0,0 @@
-package flag
-
-import (
- "reflect"
- "testing"
-
- "github.com/containous/traefik/v2/pkg/config/parser"
- "github.com/stretchr/testify/assert"
-)
-
-func Test_getFlagTypes(t *testing.T) {
- testCases := []struct {
- desc string
- element interface{}
- expected map[string]reflect.Kind
- }{
- {
- desc: "nil",
- element: nil,
- expected: map[string]reflect.Kind{},
- },
- {
- desc: "no fields",
- element: &struct {
- }{},
- expected: map[string]reflect.Kind{},
- },
- {
- desc: "string field",
- element: &struct {
- Foo string
- }{},
- expected: map[string]reflect.Kind{},
- },
- {
- desc: "bool field level 0",
- element: &struct {
- Foo bool
- fii bool
- }{},
- expected: map[string]reflect.Kind{
- "foo": reflect.Bool,
- },
- },
- {
- desc: "bool field level 1",
- element: &struct {
- Foo struct {
- Field bool
- }
- }{},
- expected: map[string]reflect.Kind{
- "foo.field": reflect.Bool,
- },
- },
- {
- desc: "bool field level 2",
- element: &struct {
- Foo *struct {
- Fii *struct {
- Field bool
- }
- }
- }{},
- expected: map[string]reflect.Kind{
- "foo": reflect.Ptr,
- "foo.fii": reflect.Ptr,
- "foo.fii.field": reflect.Bool,
- },
- },
- {
- desc: "pointer field",
- element: &struct {
- Foo *struct {
- Field string
- }
- }{},
- expected: map[string]reflect.Kind{
- "foo": reflect.Ptr,
- },
- },
- {
- desc: "bool field level 3",
- element: &struct {
- Foo *struct {
- Fii *struct {
- Fuu *struct {
- Field bool
- }
- }
- }
- }{},
- expected: map[string]reflect.Kind{
- "foo": reflect.Ptr,
- "foo.fii": reflect.Ptr,
- "foo.fii.fuu": reflect.Ptr,
- "foo.fii.fuu.field": reflect.Bool,
- },
- },
- {
- desc: "map string",
- element: &struct {
- Foo map[string]string
- }{},
- expected: map[string]reflect.Kind{},
- },
- {
- desc: "map bool",
- element: &struct {
- Foo map[string]bool
- Fii struct{}
- }{},
- expected: map[string]reflect.Kind{
- "foo." + parser.MapNamePlaceholder: reflect.Bool,
- },
- },
- {
- desc: "map struct",
- element: &struct {
- Foo map[string]struct {
- Field bool
- }
- }{},
- expected: map[string]reflect.Kind{
- "foo." + parser.MapNamePlaceholder + ".field": reflect.Bool,
- },
- },
- {
- desc: "map map bool",
- element: &struct {
- Foo map[string]map[string]bool
- }{},
- expected: map[string]reflect.Kind{
- "foo." + parser.MapNamePlaceholder + "." + parser.MapNamePlaceholder: reflect.Bool,
- },
- },
- {
- desc: "map struct map",
- element: &struct {
- Foo map[string]struct {
- Fii map[string]bool
- }
- }{},
- expected: map[string]reflect.Kind{
- "foo." + parser.MapNamePlaceholder + ".fii." + parser.MapNamePlaceholder: reflect.Bool,
- },
- },
- {
- desc: "pointer bool field level 0",
- element: &struct {
- Foo *bool
- }{},
- expected: map[string]reflect.Kind{
- "foo": reflect.Bool,
- },
- },
- {
- desc: "pointer int field level 0",
- element: &struct {
- Foo *int
- }{},
- expected: map[string]reflect.Kind{},
- },
- {
- desc: "bool slice field level 0",
- element: &struct {
- Foo []bool
- }{},
- expected: map[string]reflect.Kind{
- "foo": reflect.Slice,
- },
- },
- {
- desc: "string slice field level 0",
- element: &struct {
- Foo []string
- }{},
- expected: map[string]reflect.Kind{
- "foo": reflect.Slice,
- },
- },
- {
- desc: "slice field level 1",
- element: &struct {
- Foo struct {
- Field []string
- }
- }{},
- expected: map[string]reflect.Kind{
- "foo.field": reflect.Slice,
- },
- },
- {
- desc: "map slice string",
- element: &struct {
- Foo map[string][]string
- }{},
- expected: map[string]reflect.Kind{
- "foo." + parser.MapNamePlaceholder: reflect.Slice,
- },
- },
- {
- desc: "embedded struct",
- element: &struct {
- Yo
- }{},
- expected: map[string]reflect.Kind{
- "foo": reflect.Bool,
- },
- },
- }
-
- for _, test := range testCases {
- test := test
- t.Run(test.desc, func(t *testing.T) {
- t.Parallel()
-
- actual := getFlagTypes(test.element)
- assert.Equal(t, test.expected, actual)
- })
- }
-}
-
-type Yo struct {
- Foo bool
-}
diff --git a/pkg/config/generator/generator.go b/pkg/config/generator/generator.go
deleted file mode 100644
index 3615169fd..000000000
--- a/pkg/config/generator/generator.go
+++ /dev/null
@@ -1,97 +0,0 @@
-// Package generator implements the custom initialization of all the fields of an empty interface.
-package generator
-
-import (
- "reflect"
-
- "github.com/containous/traefik/v2/pkg/config/parser"
-)
-
-type initializer interface {
- SetDefaults()
-}
-
-// Generate recursively initializes an empty structure, calling SetDefaults on each field, when it applies.
-func Generate(element interface{}) {
- if element == nil {
- return
- }
-
- generate(element)
-}
-
-func generate(element interface{}) {
- field := reflect.ValueOf(element)
-
- fill(field)
-}
-
-func fill(field reflect.Value) {
- switch field.Kind() {
- case reflect.Ptr:
- setPtr(field)
- case reflect.Struct:
- setStruct(field)
- case reflect.Map:
- setMap(field)
- case reflect.Slice:
- if field.Type().Elem().Kind() == reflect.Struct ||
- field.Type().Elem().Kind() == reflect.Ptr && field.Type().Elem().Elem().Kind() == reflect.Struct {
- slice := reflect.MakeSlice(field.Type(), 1, 1)
- field.Set(slice)
-
- // use Ptr to allow "SetDefaults"
- value := reflect.New(reflect.PtrTo(field.Type().Elem()))
- setPtr(value)
-
- elem := value.Elem().Elem()
- field.Index(0).Set(elem)
- } else if field.Len() == 0 {
- slice := reflect.MakeSlice(field.Type(), 0, 0)
- field.Set(slice)
- }
- }
-}
-
-func setPtr(field reflect.Value) {
- if field.IsNil() {
- field.Set(reflect.New(field.Type().Elem()))
- }
-
- if field.Type().Implements(reflect.TypeOf((*initializer)(nil)).Elem()) {
- method := field.MethodByName("SetDefaults")
- if method.IsValid() {
- method.Call([]reflect.Value{})
- }
- }
-
- fill(field.Elem())
-}
-
-func setStruct(field reflect.Value) {
- for i := 0; i < field.NumField(); i++ {
- fd := field.Field(i)
- structField := field.Type().Field(i)
-
- if structField.Tag.Get(parser.TagLabel) == "-" {
- continue
- }
-
- if parser.IsExported(structField) {
- fill(fd)
- }
- }
-}
-
-func setMap(field reflect.Value) {
- if field.IsNil() {
- field.Set(reflect.MakeMap(field.Type()))
- }
-
- ptrValue := reflect.New(reflect.PtrTo(field.Type().Elem()))
- fill(ptrValue)
-
- value := ptrValue.Elem().Elem()
- key := reflect.ValueOf(parser.MapNamePlaceholder)
- field.SetMapIndex(key, value)
-}
diff --git a/pkg/config/generator/generator_test.go b/pkg/config/generator/generator_test.go
deleted file mode 100644
index 70551db79..000000000
--- a/pkg/config/generator/generator_test.go
+++ /dev/null
@@ -1,439 +0,0 @@
-package generator
-
-import (
- "testing"
-
- "github.com/containous/traefik/v2/pkg/config/parser"
- "github.com/stretchr/testify/assert"
-)
-
-func TestGenerate(t *testing.T) {
- testCases := []struct {
- desc string
- element interface{}
- expected interface{}
- }{
- {
- desc: "nil",
- },
- {
- desc: "simple",
- element: &Ya{},
- expected: &Ya{
- Foo: &Yaa{
- FieldIn1: "",
- FieldIn2: false,
- FieldIn3: 0,
- FieldIn4: map[string]string{
- parser.MapNamePlaceholder: "",
- },
- FieldIn5: map[string]int{
- parser.MapNamePlaceholder: 0,
- },
- FieldIn6: map[string]struct{ Field string }{
- parser.MapNamePlaceholder: {},
- },
- FieldIn7: map[string]struct{ Field map[string]string }{
- parser.MapNamePlaceholder: {
- Field: map[string]string{
- parser.MapNamePlaceholder: "",
- },
- },
- },
- FieldIn8: map[string]*struct{ Field string }{
- parser.MapNamePlaceholder: {},
- },
- FieldIn9: map[string]*struct{ Field map[string]string }{
- parser.MapNamePlaceholder: {
- Field: map[string]string{
- parser.MapNamePlaceholder: "",
- },
- },
- },
- FieldIn10: struct{ Field string }{},
- FieldIn11: &struct{ Field string }{},
- FieldIn12: func(v string) *string { return &v }(""),
- FieldIn13: func(v bool) *bool { return &v }(false),
- FieldIn14: func(v int) *int { return &v }(0),
- },
- Field1: "",
- Field2: false,
- Field3: 0,
- Field4: map[string]string{
- parser.MapNamePlaceholder: "",
- },
- Field5: map[string]int{
- parser.MapNamePlaceholder: 0,
- },
- Field6: map[string]struct{ Field string }{
- parser.MapNamePlaceholder: {},
- },
- Field7: map[string]struct{ Field map[string]string }{
- parser.MapNamePlaceholder: {
- Field: map[string]string{
- parser.MapNamePlaceholder: "",
- },
- },
- },
- Field8: map[string]*struct{ Field string }{
- parser.MapNamePlaceholder: {},
- },
- Field9: map[string]*struct{ Field map[string]string }{
- parser.MapNamePlaceholder: {
- Field: map[string]string{
- parser.MapNamePlaceholder: "",
- },
- },
- },
- Field10: struct{ Field string }{},
- Field11: &struct{ Field string }{},
- Field12: func(v string) *string { return &v }(""),
- Field13: func(v bool) *bool { return &v }(false),
- Field14: func(v int) *int { return &v }(0),
- Field15: []int{},
- },
- },
- {
- desc: "with initial state",
- element: &Ya{
- Foo: &Yaa{
- FieldIn1: "bar",
- FieldIn2: false,
- FieldIn3: 1,
- FieldIn4: nil,
- FieldIn5: nil,
- FieldIn6: nil,
- FieldIn7: nil,
- FieldIn8: nil,
- FieldIn9: nil,
- FieldIn10: struct{ Field string }{},
- FieldIn11: nil,
- FieldIn12: nil,
- FieldIn13: nil,
- FieldIn14: nil,
- },
- Field1: "bir",
- Field2: true,
- Field3: 0,
- Field4: nil,
- Field5: nil,
- Field6: nil,
- Field7: nil,
- Field8: nil,
- Field9: nil,
- Field10: struct{ Field string }{},
- Field11: nil,
- Field12: nil,
- Field13: nil,
- Field14: nil,
- Field15: []int{7},
- },
- expected: &Ya{
- Foo: &Yaa{
- FieldIn1: "bar",
- FieldIn2: false,
- FieldIn3: 1,
- FieldIn4: map[string]string{
- parser.MapNamePlaceholder: "",
- },
- FieldIn5: map[string]int{
- parser.MapNamePlaceholder: 0,
- },
- FieldIn6: map[string]struct{ Field string }{
- parser.MapNamePlaceholder: {},
- },
- FieldIn7: map[string]struct{ Field map[string]string }{
- parser.MapNamePlaceholder: {
- Field: map[string]string{
- parser.MapNamePlaceholder: "",
- },
- },
- },
- FieldIn8: map[string]*struct{ Field string }{
- parser.MapNamePlaceholder: {},
- },
- FieldIn9: map[string]*struct{ Field map[string]string }{
- parser.MapNamePlaceholder: {
- Field: map[string]string{
- parser.MapNamePlaceholder: "",
- },
- },
- },
- FieldIn10: struct{ Field string }{},
- FieldIn11: &struct{ Field string }{},
- FieldIn12: func(v string) *string { return &v }(""),
- FieldIn13: func(v bool) *bool { return &v }(false),
- FieldIn14: func(v int) *int { return &v }(0),
- },
- Field1: "bir",
- Field2: true,
- Field3: 0,
- Field4: map[string]string{
- parser.MapNamePlaceholder: "",
- },
- Field5: map[string]int{
- parser.MapNamePlaceholder: 0,
- },
- Field6: map[string]struct{ Field string }{
- parser.MapNamePlaceholder: {},
- },
- Field7: map[string]struct{ Field map[string]string }{
- parser.MapNamePlaceholder: {
- Field: map[string]string{
- parser.MapNamePlaceholder: "",
- },
- },
- },
- Field8: map[string]*struct{ Field string }{
- parser.MapNamePlaceholder: {},
- },
- Field9: map[string]*struct{ Field map[string]string }{
- parser.MapNamePlaceholder: {
- Field: map[string]string{
- parser.MapNamePlaceholder: "",
- },
- },
- },
- Field10: struct{ Field string }{},
- Field11: &struct{ Field string }{},
- Field12: func(v string) *string { return &v }(""),
- Field13: func(v bool) *bool { return &v }(false),
- Field14: func(v int) *int { return &v }(0),
- Field15: []int{7},
- },
- },
- {
- desc: "setDefault",
- element: &Hu{},
- expected: &Hu{
- Foo: "hu",
- Fii: &Hi{
- Field: "hi",
- },
- Fuu: map[string]string{"": ""},
- Fee: map[string]Hi{"": {Field: "hi"}},
- },
- },
- }
-
- for _, test := range testCases {
- test := test
- t.Run(test.desc, func(t *testing.T) {
- t.Parallel()
-
- Generate(test.element)
-
- assert.Equal(t, test.expected, test.element)
- })
- }
-}
-
-func Test_generate(t *testing.T) {
- testCases := []struct {
- desc string
- element interface{}
- expected interface{}
- }{
- {
- desc: "struct pointer",
- element: &struct {
- Foo string
- Fii *struct{ Field string }
- }{},
- expected: &struct {
- Foo string
- Fii *struct{ Field string }
- }{
- Foo: "",
- Fii: &struct{ Field string }{
- Field: "",
- },
- },
- },
- {
- desc: "string slice",
- element: &struct {
- Foo []string
- }{},
- expected: &struct {
- Foo []string
- }{
- Foo: []string{},
- },
- },
- {
- desc: "int slice",
- element: &struct {
- Foo []int
- }{},
- expected: &struct {
- Foo []int
- }{
- Foo: []int{},
- },
- },
- {
- desc: "struct slice",
- element: &struct {
- Foo []struct {
- Field string
- }
- }{},
- expected: &struct {
- Foo []struct {
- Field string
- }
- }{
- Foo: []struct {
- Field string
- }{
- {Field: ""},
- },
- },
- },
- {
- desc: "map string",
- element: &struct {
- Foo string
- Fii map[string]string
- }{},
- expected: &struct {
- Foo string
- Fii map[string]string
- }{
- Foo: "",
- Fii: map[string]string{
- parser.MapNamePlaceholder: "",
- },
- },
- },
- {
- desc: "map struct",
- element: &struct {
- Foo string
- Fii map[string]struct{ Field string }
- }{},
- expected: &struct {
- Foo string
- Fii map[string]struct{ Field string }
- }{
- Foo: "",
- Fii: map[string]struct{ Field string }{
- parser.MapNamePlaceholder: {},
- },
- },
- },
- {
- desc: "map struct pointer level 2",
- element: &struct {
- Foo string
- Fuu *struct {
- Fii map[string]*struct{ Field string }
- }
- }{},
- expected: &struct {
- Foo string
- Fuu *struct {
- Fii map[string]*struct{ Field string }
- }
- }{
- Foo: "",
- Fuu: &struct {
- Fii map[string]*struct {
- Field string
- }
- }{
- Fii: map[string]*struct{ Field string }{
- parser.MapNamePlaceholder: {
- Field: "",
- },
- },
- },
- },
- },
- {
- desc: "SetDefaults",
- element: &Hu{},
- expected: &Hu{
- Foo: "hu",
- Fii: &Hi{
- Field: "hi",
- },
- Fuu: map[string]string{
- parser.MapNamePlaceholder: "",
- },
- Fee: map[string]Hi{
- parser.MapNamePlaceholder: {
- Field: "hi",
- },
- },
- },
- },
- }
-
- for _, test := range testCases {
- test := test
- t.Run(test.desc, func(t *testing.T) {
- t.Parallel()
-
- generate(test.element)
-
- assert.Equal(t, test.expected, test.element)
- })
- }
-}
-
-type Hu struct {
- Foo string
- Fii *Hi
- Fuu map[string]string
- Fee map[string]Hi
-}
-
-func (h *Hu) SetDefaults() {
- h.Foo = "hu"
-}
-
-type Hi struct {
- Field string
-}
-
-func (h *Hi) SetDefaults() {
- h.Field = "hi"
-}
-
-type Ya struct {
- Foo *Yaa
- Field1 string
- Field2 bool
- Field3 int
- Field4 map[string]string
- Field5 map[string]int
- Field6 map[string]struct{ Field string }
- Field7 map[string]struct{ Field map[string]string }
- Field8 map[string]*struct{ Field string }
- Field9 map[string]*struct{ Field map[string]string }
- Field10 struct{ Field string }
- Field11 *struct{ Field string }
- Field12 *string
- Field13 *bool
- Field14 *int
- Field15 []int
-}
-
-type Yaa struct {
- FieldIn1 string
- FieldIn2 bool
- FieldIn3 int
- FieldIn4 map[string]string
- FieldIn5 map[string]int
- FieldIn6 map[string]struct{ Field string }
- FieldIn7 map[string]struct{ Field map[string]string }
- FieldIn8 map[string]*struct{ Field string }
- FieldIn9 map[string]*struct{ Field map[string]string }
- FieldIn10 struct{ Field string }
- FieldIn11 *struct{ Field string }
- FieldIn12 *string
- FieldIn13 *bool
- FieldIn14 *int
-}
diff --git a/pkg/config/kv/kv.go b/pkg/config/kv/kv.go
index e4e99d8c6..9ca265a21 100644
--- a/pkg/config/kv/kv.go
+++ b/pkg/config/kv/kv.go
@@ -5,7 +5,7 @@ import (
"reflect"
"github.com/abronan/valkeyrie/store"
- "github.com/containous/traefik/v2/pkg/config/parser"
+ "github.com/traefik/paerser/parser"
)
// Decode decodes the given KV pairs into the given element.
diff --git a/pkg/config/kv/kv_node.go b/pkg/config/kv/kv_node.go
index 4b5a83594..ef1571c1e 100644
--- a/pkg/config/kv/kv_node.go
+++ b/pkg/config/kv/kv_node.go
@@ -7,7 +7,7 @@ import (
"strings"
"github.com/abronan/valkeyrie/store"
- "github.com/containous/traefik/v2/pkg/config/parser"
+ "github.com/traefik/paerser/parser"
)
// DecodeToNode converts the labels to a tree of nodes.
diff --git a/pkg/config/kv/kv_node_test.go b/pkg/config/kv/kv_node_test.go
index 7125a4467..7f3d547ec 100644
--- a/pkg/config/kv/kv_node_test.go
+++ b/pkg/config/kv/kv_node_test.go
@@ -6,9 +6,9 @@ import (
"testing"
"github.com/abronan/valkeyrie/store"
- "github.com/containous/traefik/v2/pkg/config/parser"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/traefik/paerser/parser"
)
func TestDecodeToNode(t *testing.T) {
diff --git a/pkg/config/label/label.go b/pkg/config/label/label.go
index f2a773531..bc245b14d 100644
--- a/pkg/config/label/label.go
+++ b/pkg/config/label/label.go
@@ -3,7 +3,7 @@ package label
import (
"github.com/containous/traefik/v2/pkg/config/dynamic"
- "github.com/containous/traefik/v2/pkg/config/parser"
+ "github.com/traefik/paerser/parser"
)
// DecodeConfiguration converts the labels to a configuration.
diff --git a/pkg/config/label/label_test.go b/pkg/config/label/label_test.go
index ba1bff2bb..5e50cb164 100644
--- a/pkg/config/label/label_test.go
+++ b/pkg/config/label/label_test.go
@@ -6,9 +6,9 @@ import (
"time"
"github.com/containous/traefik/v2/pkg/config/dynamic"
- "github.com/containous/traefik/v2/pkg/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ ptypes "github.com/traefik/paerser/types"
)
func TestDecodeConfiguration(t *testing.T) {
@@ -376,7 +376,7 @@ func TestDecodeConfiguration(t *testing.T) {
RateLimit: &dynamic.RateLimit{
Average: 42,
Burst: 42,
- Period: types.Duration(time.Second),
+ Period: ptypes.Duration(time.Second),
SourceCriterion: &dynamic.SourceCriterion{
IPStrategy: &dynamic.IPStrategy{
Depth: 42,
@@ -836,7 +836,7 @@ func TestEncodeConfiguration(t *testing.T) {
RateLimit: &dynamic.RateLimit{
Average: 42,
Burst: 42,
- Period: types.Duration(time.Second),
+ Period: ptypes.Duration(time.Second),
SourceCriterion: &dynamic.SourceCriterion{
IPStrategy: &dynamic.IPStrategy{
Depth: 42,
diff --git a/pkg/config/parser/element_fill.go b/pkg/config/parser/element_fill.go
deleted file mode 100644
index 3a1840c8d..000000000
--- a/pkg/config/parser/element_fill.go
+++ /dev/null
@@ -1,372 +0,0 @@
-package parser
-
-import (
- "fmt"
- "reflect"
- "strconv"
- "strings"
- "time"
-
- "github.com/containous/traefik/v2/pkg/types"
-)
-
-type initializer interface {
- SetDefaults()
-}
-
-// FillerOpts Options for the filler.
-type FillerOpts struct {
- AllowSliceAsStruct bool
-}
-
-// Fill populates the fields of the element using the information in node.
-func Fill(element interface{}, node *Node, opts FillerOpts) error {
- return filler{FillerOpts: opts}.Fill(element, node)
-}
-
-type filler struct {
- FillerOpts
-}
-
-// Fill populates the fields of the element using the information in node.
-func (f filler) Fill(element interface{}, node *Node) error {
- if element == nil || node == nil {
- return nil
- }
-
- if node.Kind == 0 {
- return fmt.Errorf("missing node type: %s", node.Name)
- }
-
- root := reflect.ValueOf(element)
- if root.Kind() == reflect.Struct {
- return fmt.Errorf("struct are not supported, use pointer instead")
- }
-
- return f.fill(root.Elem(), node)
-}
-
-func (f filler) fill(field reflect.Value, node *Node) error {
- // related to allow-empty tag
- if node.Disabled {
- return nil
- }
-
- switch field.Kind() {
- case reflect.String:
- field.SetString(node.Value)
- return nil
- case reflect.Bool:
- val, err := strconv.ParseBool(node.Value)
- if err != nil {
- return err
- }
- field.SetBool(val)
- return nil
- case reflect.Int8:
- return setInt(field, node.Value, 8)
- case reflect.Int16:
- return setInt(field, node.Value, 16)
- case reflect.Int32:
- return setInt(field, node.Value, 32)
- case reflect.Int64, reflect.Int:
- return setInt(field, node.Value, 64)
- case reflect.Uint8:
- return setUint(field, node.Value, 8)
- case reflect.Uint16:
- return setUint(field, node.Value, 16)
- case reflect.Uint32:
- return setUint(field, node.Value, 32)
- case reflect.Uint64, reflect.Uint:
- return setUint(field, node.Value, 64)
- case reflect.Float32:
- return setFloat(field, node.Value, 32)
- case reflect.Float64:
- return setFloat(field, node.Value, 64)
- case reflect.Struct:
- return f.setStruct(field, node)
- case reflect.Ptr:
- return f.setPtr(field, node)
- case reflect.Map:
- return f.setMap(field, node)
- case reflect.Slice:
- return f.setSlice(field, node)
- default:
- return nil
- }
-}
-
-func (f filler) setPtr(field reflect.Value, node *Node) error {
- if field.IsNil() {
- field.Set(reflect.New(field.Type().Elem()))
-
- if field.Type().Implements(reflect.TypeOf((*initializer)(nil)).Elem()) {
- method := field.MethodByName("SetDefaults")
- if method.IsValid() {
- method.Call([]reflect.Value{})
- }
- }
- }
-
- return f.fill(field.Elem(), node)
-}
-
-func (f filler) setStruct(field reflect.Value, node *Node) error {
- for _, child := range node.Children {
- fd := field.FieldByName(child.FieldName)
-
- zeroValue := reflect.Value{}
- if fd == zeroValue {
- return fmt.Errorf("field not found, node: %s (%s)", child.Name, child.FieldName)
- }
-
- err := f.fill(fd, child)
- if err != nil {
- return err
- }
- }
-
- return nil
-}
-
-func (f filler) setSlice(field reflect.Value, node *Node) error {
- if field.Type().Elem().Kind() == reflect.Struct ||
- field.Type().Elem().Kind() == reflect.Ptr && field.Type().Elem().Elem().Kind() == reflect.Struct {
- return f.setSliceStruct(field, node)
- }
-
- if len(node.Value) == 0 {
- return nil
- }
-
- values := strings.Split(node.Value, ",")
-
- slice := reflect.MakeSlice(field.Type(), len(values), len(values))
- field.Set(slice)
-
- for i := 0; i < len(values); i++ {
- value := strings.TrimSpace(values[i])
-
- switch field.Type().Elem().Kind() {
- case reflect.String:
- field.Index(i).SetString(value)
- case reflect.Int:
- val, err := strconv.ParseInt(value, 10, 64)
- if err != nil {
- return err
- }
- field.Index(i).SetInt(val)
- case reflect.Int8:
- err := setInt(field.Index(i), value, 8)
- if err != nil {
- return err
- }
- case reflect.Int16:
- err := setInt(field.Index(i), value, 16)
- if err != nil {
- return err
- }
- case reflect.Int32:
- err := setInt(field.Index(i), value, 32)
- if err != nil {
- return err
- }
- case reflect.Int64:
- err := setInt(field.Index(i), value, 64)
- if err != nil {
- return err
- }
- case reflect.Uint:
- val, err := strconv.ParseUint(value, 10, 64)
- if err != nil {
- return err
- }
- field.Index(i).SetUint(val)
- case reflect.Uint8:
- err := setUint(field.Index(i), value, 8)
- if err != nil {
- return err
- }
- case reflect.Uint16:
- err := setUint(field.Index(i), value, 16)
- if err != nil {
- return err
- }
- case reflect.Uint32:
- err := setUint(field.Index(i), value, 32)
- if err != nil {
- return err
- }
- case reflect.Uint64:
- err := setUint(field.Index(i), value, 64)
- if err != nil {
- return err
- }
- case reflect.Float32:
- err := setFloat(field.Index(i), value, 32)
- if err != nil {
- return err
- }
- case reflect.Float64:
- err := setFloat(field.Index(i), value, 64)
- if err != nil {
- return err
- }
- case reflect.Bool:
- val, err := strconv.ParseBool(value)
- if err != nil {
- return err
- }
- field.Index(i).SetBool(val)
- default:
- return fmt.Errorf("unsupported type: %s", field.Type().Elem())
- }
- }
- return nil
-}
-
-func (f filler) setSliceStruct(field reflect.Value, node *Node) error {
- if f.AllowSliceAsStruct && node.Tag.Get(TagLabelSliceAsStruct) != "" {
- return f.setSliceAsStruct(field, node)
- }
-
- field.Set(reflect.MakeSlice(field.Type(), len(node.Children), len(node.Children)))
-
- for i, child := range node.Children {
- // use Ptr to allow "SetDefaults"
- value := reflect.New(reflect.PtrTo(field.Type().Elem()))
- err := f.setPtr(value, child)
- if err != nil {
- return err
- }
-
- field.Index(i).Set(value.Elem().Elem())
- }
-
- return nil
-}
-
-func (f filler) setSliceAsStruct(field reflect.Value, node *Node) error {
- if len(node.Children) == 0 {
- return fmt.Errorf("invalid slice: node %s", node.Name)
- }
-
- // use Ptr to allow "SetDefaults"
- value := reflect.New(reflect.PtrTo(field.Type().Elem()))
- err := f.setPtr(value, node)
- if err != nil {
- return err
- }
-
- elem := value.Elem().Elem()
-
- field.Set(reflect.MakeSlice(field.Type(), 1, 1))
- field.Index(0).Set(elem)
-
- return nil
-}
-
-func (f filler) setMap(field reflect.Value, node *Node) error {
- if field.IsNil() {
- 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()))
-
- err := f.fill(ptrValue, child)
- if err != nil {
- return err
- }
-
- value := ptrValue.Elem().Elem()
-
- key := reflect.ValueOf(child.Name)
- field.SetMapIndex(key, value)
- }
-
- return nil
-}
-
-func setInt(field reflect.Value, value string, bitSize int) error {
- switch field.Type() {
- case reflect.TypeOf(types.Duration(0)):
- return setDuration(field, value, bitSize, time.Second)
- case reflect.TypeOf(time.Duration(0)):
- return setDuration(field, value, bitSize, time.Nanosecond)
- default:
- val, err := strconv.ParseInt(value, 10, bitSize)
- if err != nil {
- return err
- }
-
- field.Set(reflect.ValueOf(val).Convert(field.Type()))
- return nil
- }
-}
-
-func setDuration(field reflect.Value, value string, bitSize int, defaultUnit time.Duration) error {
- val, err := strconv.ParseInt(value, 10, bitSize)
- if err == nil {
- field.Set(reflect.ValueOf(time.Duration(val) * defaultUnit).Convert(field.Type()))
- return nil
- }
-
- duration, err := time.ParseDuration(value)
- if err != nil {
- return err
- }
-
- field.Set(reflect.ValueOf(duration).Convert(field.Type()))
- return nil
-}
-
-func setUint(field reflect.Value, value string, bitSize int) error {
- val, err := strconv.ParseUint(value, 10, bitSize)
- if err != nil {
- return err
- }
-
- field.Set(reflect.ValueOf(val).Convert(field.Type()))
- return nil
-}
-
-func setFloat(field reflect.Value, value string, bitSize int) error {
- val, err := strconv.ParseFloat(value, bitSize)
- if err != nil {
- return err
- }
-
- 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]))
-}
diff --git a/pkg/config/parser/element_fill_test.go b/pkg/config/parser/element_fill_test.go
deleted file mode 100644
index 628127416..000000000
--- a/pkg/config/parser/element_fill_test.go
+++ /dev/null
@@ -1,1541 +0,0 @@
-package parser
-
-import (
- "reflect"
- "testing"
- "time"
-
- "github.com/containous/traefik/v2/pkg/types"
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
-)
-
-func TestFill(t *testing.T) {
- type expected struct {
- element interface{}
- error bool
- }
-
- testCases := []struct {
- desc string
- node *Node
- element interface{}
- expected expected
- }{
- {
- desc: "empty node",
- node: &Node{},
- element: &struct{ Foo string }{},
- expected: expected{error: true},
- },
- {
- desc: "empty element",
- node: &Node{Name: "traefik", Kind: reflect.Struct},
- element: &struct{}{},
- expected: expected{element: &struct{}{}},
- },
- {
- desc: "type struct as root",
- node: &Node{Name: "traefik", Kind: reflect.Struct},
- element: struct{}{},
- expected: expected{error: true},
- },
- {
- desc: "nil node",
- node: nil,
- element: &struct{ Foo string }{},
- expected: expected{element: &struct{ Foo string }{}},
- },
- {
- desc: "nil element",
- node: &Node{Name: "traefik", Kind: reflect.Struct},
- element: nil,
- expected: expected{element: nil},
- },
- {
- desc: "string",
- node: &Node{
- Name: "traefik",
- Kind: reflect.Struct,
- Children: []*Node{
- {Name: "Foo", FieldName: "Foo", Value: "bar", Kind: reflect.String},
- },
- },
- element: &struct{ Foo string }{},
- expected: expected{element: &struct{ Foo string }{Foo: "bar"}},
- },
- {
- desc: "field not found",
- node: &Node{
- Name: "traefik",
- Kind: reflect.Struct,
- Children: []*Node{
- {Name: "Foo", FieldName: "Fii", Value: "bar", Kind: reflect.String},
- },
- },
- element: &struct{ Foo string }{},
- expected: expected{error: true},
- },
- {
- desc: "2 children",
- node: &Node{
- Name: "traefik",
- Kind: reflect.Struct,
- Children: []*Node{
- {Name: "Fii", FieldName: "Fii", Value: "bir", Kind: reflect.String},
- {Name: "Foo", FieldName: "Foo", Value: "4", Kind: reflect.Int},
- },
- },
- element: &struct {
- Fii string
- Foo int
- }{},
- expected: expected{element: &struct {
- Fii string
- Foo int
- }{Fii: "bir", Foo: 4}},
- },
- {
- desc: "case insensitive",
- node: &Node{
- Name: "traefik",
- Kind: reflect.Struct,
- Children: []*Node{
- {Name: "foo", FieldName: "Foo", Value: "bir", Kind: reflect.String},
- },
- },
- element: &struct {
- Foo string
- foo int
- }{},
- expected: expected{element: &struct {
- Foo string
- foo int
- }{Foo: "bir"}},
- },
- {
- desc: "func",
- node: &Node{
- Name: "traefik",
- Kind: reflect.Struct,
- Children: []*Node{
- {Name: "Foo", FieldName: "Foo", Kind: reflect.Func},
- },
- },
- element: &struct{ Foo func() }{},
- expected: expected{element: &struct{ Foo func() }{}},
- },
- {
- desc: "int",
- node: &Node{
- Name: "traefik",
- Kind: reflect.Struct,
- Children: []*Node{
- {Name: "Foo", FieldName: "Foo", Value: "4", Kind: reflect.Int},
- },
- },
- element: &struct{ Foo int }{},
- expected: expected{element: &struct{ Foo int }{Foo: 4}},
- },
- {
- desc: "invalid int",
- node: &Node{
- Name: "traefik",
- Kind: reflect.Struct,
- Children: []*Node{
- {Name: "Foo", FieldName: "Foo", Value: "four", Kind: reflect.Int},
- },
- },
- element: &struct{ Foo int }{},
- expected: expected{error: true},
- },
- {
- desc: "int8",
- node: &Node{
- Name: "traefik",
- Kind: reflect.Struct,
- Children: []*Node{
- {Name: "Foo", FieldName: "Foo", Value: "4", Kind: reflect.Int8},
- },
- },
- element: &struct{ Foo int8 }{},
- expected: expected{element: &struct{ Foo int8 }{Foo: 4}},
- },
- {
- desc: "invalid int8",
- node: &Node{
- Name: "traefik",
- Kind: reflect.Struct,
- Children: []*Node{
- {Name: "Foo", FieldName: "Foo", Value: "four", Kind: reflect.Int8},
- },
- },
- element: &struct{ Foo int8 }{},
- expected: expected{error: true},
- },
- {
- desc: "int16",
- node: &Node{
- Name: "traefik",
- Kind: reflect.Struct,
- Children: []*Node{
- {Name: "Foo", FieldName: "Foo", Value: "4", Kind: reflect.Int16},
- },
- },
- element: &struct{ Foo int16 }{},
- expected: expected{element: &struct{ Foo int16 }{Foo: 4}},
- },
- {
- desc: "invalid int16",
- node: &Node{
- Name: "traefik",
- Kind: reflect.Struct,
- Children: []*Node{
- {Name: "Foo", FieldName: "Foo", Value: "four", Kind: reflect.Int16},
- },
- },
- element: &struct{ Foo int16 }{},
- expected: expected{error: true},
- },
- {
- desc: "int32",
- node: &Node{
- Name: "traefik",
- Kind: reflect.Struct,
- Children: []*Node{
- {Name: "Foo", FieldName: "Foo", Value: "4", Kind: reflect.Int32},
- },
- },
- element: &struct{ Foo int32 }{},
- expected: expected{element: &struct{ Foo int32 }{Foo: 4}},
- },
- {
- desc: "invalid int32",
- node: &Node{
- Name: "traefik",
- Kind: reflect.Struct,
- Children: []*Node{
- {Name: "Foo", FieldName: "Foo", Value: "four", Kind: reflect.Int32},
- },
- },
- element: &struct{ Foo int32 }{},
- expected: expected{error: true},
- },
- {
- desc: "int64",
- node: &Node{
- Name: "traefik",
- Kind: reflect.Struct,
- Children: []*Node{
- {Name: "Foo", FieldName: "Foo", Value: "4", Kind: reflect.Int64},
- },
- },
- element: &struct{ Foo int64 }{},
- expected: expected{element: &struct{ Foo int64 }{Foo: 4}},
- },
- {
- desc: "invalid int64",
- node: &Node{
- Name: "traefik",
- Kind: reflect.Struct,
- Children: []*Node{
- {Name: "Foo", FieldName: "Foo", Value: "four", Kind: reflect.Int64},
- },
- },
- element: &struct{ Foo int64 }{},
- expected: expected{error: true},
- },
- {
- desc: "uint",
- node: &Node{
- Name: "traefik",
- Kind: reflect.Struct,
- Children: []*Node{
- {Name: "Foo", FieldName: "Foo", Value: "4", Kind: reflect.Uint},
- },
- },
- element: &struct{ Foo uint }{},
- expected: expected{element: &struct{ Foo uint }{Foo: 4}},
- },
- {
- desc: "invalid uint",
- node: &Node{
- Name: "traefik",
- Kind: reflect.Struct,
- Children: []*Node{
- {Name: "Foo", FieldName: "Foo", Value: "four", Kind: reflect.Uint},
- },
- },
- element: &struct{ Foo uint }{},
- expected: expected{error: true},
- },
- {
- desc: "uint8",
- node: &Node{
- Name: "traefik",
- Kind: reflect.Struct,
- Children: []*Node{
- {Name: "Foo", FieldName: "Foo", Value: "4", Kind: reflect.Uint8},
- },
- },
- element: &struct{ Foo uint8 }{},
- expected: expected{element: &struct{ Foo uint8 }{Foo: 4}},
- },
- {
- desc: "invalid uint8",
- node: &Node{
- Name: "traefik",
- Kind: reflect.Struct,
- Children: []*Node{
- {Name: "Foo", FieldName: "Foo", Value: "four", Kind: reflect.Uint8},
- },
- },
- element: &struct{ Foo uint8 }{},
- expected: expected{error: true},
- },
- {
- desc: "uint16",
- node: &Node{
- Name: "traefik",
- Kind: reflect.Struct,
- Children: []*Node{
- {Name: "Foo", FieldName: "Foo", Value: "4", Kind: reflect.Uint16},
- },
- },
- element: &struct{ Foo uint16 }{},
- expected: expected{element: &struct{ Foo uint16 }{Foo: 4}},
- },
- {
- desc: "invalid uint16",
- node: &Node{
- Name: "traefik",
- Kind: reflect.Struct,
- Children: []*Node{
- {Name: "Foo", FieldName: "Foo", Value: "four", Kind: reflect.Uint16},
- },
- },
- element: &struct{ Foo uint16 }{},
- expected: expected{error: true},
- },
- {
- desc: "uint32",
- node: &Node{
- Name: "traefik",
- Kind: reflect.Struct,
- Children: []*Node{
- {Name: "Foo", FieldName: "Foo", Value: "4", Kind: reflect.Uint32},
- },
- },
- element: &struct{ Foo uint32 }{},
- expected: expected{element: &struct{ Foo uint32 }{Foo: 4}},
- },
- {
- desc: "invalid uint32",
- node: &Node{
- Name: "traefik",
- Kind: reflect.Struct,
- Children: []*Node{
- {Name: "Foo", FieldName: "Foo", Value: "four", Kind: reflect.Uint32},
- },
- },
- element: &struct{ Foo uint32 }{},
- expected: expected{error: true},
- },
- {
- desc: "uint64",
- node: &Node{
- Name: "traefik",
- Kind: reflect.Struct,
- Children: []*Node{
- {Name: "Foo", FieldName: "Foo", Value: "4", Kind: reflect.Uint64},
- },
- },
- element: &struct{ Foo uint64 }{},
- expected: expected{element: &struct{ Foo uint64 }{Foo: 4}},
- },
- {
- desc: "invalid uint64",
- node: &Node{
- Name: "traefik",
- Kind: reflect.Struct,
- Children: []*Node{
- {Name: "Foo", FieldName: "Foo", Value: "four", Kind: reflect.Uint64},
- },
- },
- element: &struct{ Foo uint64 }{},
- expected: expected{error: true},
- },
- {
- desc: "time.Duration with unit",
- node: &Node{
- Name: "traefik",
- Kind: reflect.Struct,
- Children: []*Node{
- {Name: "Foo", FieldName: "Foo", Value: "4s", Kind: reflect.Int64},
- },
- },
- element: &struct{ Foo time.Duration }{},
- expected: expected{element: &struct{ Foo time.Duration }{Foo: 4 * time.Second}},
- },
- {
- desc: "time.Duration without unit",
- node: &Node{
- Name: "traefik",
- Kind: reflect.Struct,
- Children: []*Node{
- {Name: "Foo", FieldName: "Foo", Value: "4", Kind: reflect.Int64},
- },
- },
- element: &struct{ Foo time.Duration }{},
- expected: expected{element: &struct{ Foo time.Duration }{Foo: 4 * time.Nanosecond}},
- },
- {
- desc: "types.Duration with unit",
- node: &Node{
- Name: "traefik",
- Kind: reflect.Struct,
- Children: []*Node{
- {Name: "Foo", FieldName: "Foo", Value: "4s", Kind: reflect.Int64},
- },
- },
- element: &struct{ Foo types.Duration }{},
- expected: expected{element: &struct{ Foo types.Duration }{Foo: types.Duration(4 * time.Second)}},
- },
- {
- desc: "types.Duration without unit",
- node: &Node{
- Name: "traefik",
- Kind: reflect.Struct,
- Children: []*Node{
- {Name: "Foo", FieldName: "Foo", Value: "4", Kind: reflect.Int64},
- },
- },
- element: &struct{ Foo types.Duration }{},
- expected: expected{element: &struct{ Foo types.Duration }{Foo: types.Duration(4 * time.Second)}},
- },
- {
- desc: "bool",
- node: &Node{
- Name: "traefik",
- Kind: reflect.Struct,
- Children: []*Node{
- {Name: "Foo", FieldName: "Foo", Value: "true", Kind: reflect.Bool},
- },
- },
- element: &struct{ Foo bool }{},
- expected: expected{element: &struct{ Foo bool }{Foo: true}},
- },
- {
- desc: "invalid bool",
- node: &Node{
- Name: "traefik",
- Kind: reflect.Struct,
- Children: []*Node{
- {Name: "Foo", FieldName: "Foo", Value: "bool", Kind: reflect.Bool},
- },
- },
- element: &struct{ Foo bool }{},
- expected: expected{error: true},
- },
- {
- desc: "float32",
- node: &Node{
- Name: "traefik",
- Kind: reflect.Struct,
- Children: []*Node{
- {Name: "Foo", FieldName: "Foo", Value: "2.1", Kind: reflect.Float32},
- },
- },
- element: &struct{ Foo float32 }{},
- expected: expected{element: &struct{ Foo float32 }{Foo: 2.1}},
- },
- {
- desc: "invalid float32",
- node: &Node{
- Name: "traefik",
- Kind: reflect.Struct,
- Children: []*Node{
- {Name: "Foo", FieldName: "Foo", Value: "two dot one", Kind: reflect.Float32},
- },
- },
- element: &struct{ Foo float32 }{},
- expected: expected{error: true},
- },
- {
- desc: "float64",
- node: &Node{
- Name: "traefik",
- Kind: reflect.Struct,
- Children: []*Node{
- {Name: "Foo", FieldName: "Foo", Value: "2.1", Kind: reflect.Float64},
- },
- },
- element: &struct{ Foo float64 }{},
- expected: expected{element: &struct{ Foo float64 }{Foo: 2.1}},
- },
- {
- desc: "invalid float64",
- node: &Node{
- Name: "traefik",
- Kind: reflect.Struct,
- Children: []*Node{
- {Name: "Foo", FieldName: "Foo", Value: "two dot one", Kind: reflect.Float64},
- },
- },
- element: &struct{ Foo float64 }{},
- expected: expected{error: true},
- },
- {
- desc: "struct",
- node: &Node{
- Name: "traefik",
- Kind: reflect.Struct,
- Children: []*Node{
- {
- Name: "Foo",
- FieldName: "Foo",
- Kind: reflect.Struct,
- Children: []*Node{
- {Name: "Fii", FieldName: "Fii", Value: "huu", Kind: reflect.String},
- {Name: "Fuu", FieldName: "Fuu", Value: "6", Kind: reflect.Int},
- },
- },
- },
- },
- element: &struct {
- Foo struct {
- Fii string
- Fuu int
- }
- }{},
- expected: expected{
- element: &struct {
- Foo struct {
- Fii string
- Fuu int
- }
- }{
- Foo: struct {
- Fii string
- Fuu int
- }{
- Fii: "huu",
- Fuu: 6,
- },
- },
- },
- },
- {
- desc: "pointer",
- node: &Node{
- Name: "traefik",
- Kind: reflect.Struct,
- Children: []*Node{
- {
- Name: "Foo",
- FieldName: "Foo",
- Kind: reflect.Struct,
- Children: []*Node{
- {Name: "Fii", FieldName: "Fii", Value: "huu", Kind: reflect.String},
- {Name: "Fuu", FieldName: "Fuu", Value: "6", Kind: reflect.Int},
- },
- },
- },
- },
- element: &struct {
- Foo *struct {
- Fii string
- Fuu int
- }
- }{},
- expected: expected{
- element: &struct {
- Foo *struct {
- Fii string
- Fuu int
- }
- }{
- Foo: &struct {
- Fii string
- Fuu int
- }{
- Fii: "huu",
- Fuu: 6,
- },
- },
- },
- },
- {
- desc: "pointer disabled false without children",
- node: &Node{
- Name: "traefik",
- Kind: reflect.Struct,
- Children: []*Node{
- {
- Name: "Foo",
- FieldName: "Foo",
- Kind: reflect.Ptr,
- },
- },
- },
- element: &struct {
- Foo *struct {
- Fii string
- Fuu int
- } `label:"allowEmpty"`
- }{},
- expected: expected{
- element: &struct {
- Foo *struct {
- Fii string
- Fuu int
- } `label:"allowEmpty"`
- }{
- Foo: &struct {
- Fii string
- Fuu int
- }{},
- },
- },
- },
- {
- desc: "pointer disabled true without children",
- node: &Node{
- Name: "traefik",
- Kind: reflect.Struct,
- Children: []*Node{
- {
- Name: "Foo",
- FieldName: "Foo",
- Kind: reflect.Ptr,
- Disabled: true,
- },
- },
- },
- element: &struct {
- Foo *struct {
- Fii string
- Fuu int
- } `label:"allowEmpty"`
- }{},
- expected: expected{
- element: &struct {
- Foo *struct {
- Fii string
- Fuu int
- } `label:"allowEmpty"`
- }{},
- },
- },
- {
- desc: "pointer disabled true with children",
- node: &Node{
- Name: "traefik",
- Kind: reflect.Struct,
- Children: []*Node{
- {
- Name: "Foo",
- FieldName: "Foo",
- Disabled: true,
- Kind: reflect.Ptr,
- Children: []*Node{
- {Name: "Fii", FieldName: "Fii", Value: "huu", Kind: reflect.String},
- {Name: "Fuu", FieldName: "Fuu", Value: "6", Kind: reflect.Int},
- },
- },
- },
- },
- element: &struct {
- Foo *struct {
- Fii string
- Fuu int
- } `label:"allowEmpty"`
- }{},
- expected: expected{
- element: &struct {
- Foo *struct {
- Fii string
- Fuu int
- } `label:"allowEmpty"`
- }{},
- },
- },
- {
- desc: "map string",
- node: &Node{
- Name: "traefik",
- Kind: reflect.Struct,
- Children: []*Node{
- {
- Name: "Foo",
- FieldName: "Foo",
- Kind: reflect.Map,
- Children: []*Node{
- {Name: "name1", Value: "hii", Kind: reflect.String},
- {Name: "name2", Value: "huu", Kind: reflect.String},
- },
- },
- },
- },
- element: &struct {
- Foo map[string]string
- }{},
- expected: expected{
- element: &struct {
- Foo map[string]string
- }{
- Foo: map[string]string{
- "name1": "hii",
- "name2": "huu",
- },
- },
- },
- },
- {
- desc: "map struct",
- node: &Node{
- Name: "traefik",
- Kind: reflect.Struct,
- Children: []*Node{
- {
- Name: "Foo",
- FieldName: "Foo",
- Kind: reflect.Map,
- Children: []*Node{
- {
- Name: "name1",
- Kind: reflect.Struct,
- Children: []*Node{
- {Name: "Fii", FieldName: "Fii", Kind: reflect.String, Value: "hii"},
- },
- },
- {
- Name: "name2",
- Kind: reflect.Struct,
- Children: []*Node{
- {Name: "Fii", FieldName: "Fii", Kind: reflect.String, Value: "huu"},
- },
- },
- },
- },
- },
- },
- element: &struct {
- Foo map[string]struct{ Fii string }
- }{},
- expected: expected{
- element: &struct {
- Foo map[string]struct{ Fii string }
- }{
- Foo: map[string]struct{ Fii string }{
- "name1": {Fii: "hii"},
- "name2": {Fii: "huu"},
- },
- },
- },
- },
- {
- desc: "slice string",
- node: &Node{
- Name: "traefik",
- Kind: reflect.Struct,
- Children: []*Node{
- {Name: "Foo", FieldName: "Foo", Value: "huu,hii,hoo", Kind: reflect.Slice},
- },
- },
- element: &struct{ Foo []string }{},
- expected: expected{element: &struct{ Foo []string }{Foo: []string{"huu", "hii", "hoo"}}},
- },
- {
- desc: "slice named type",
- node: &Node{
- Name: "traefik",
- Kind: reflect.Struct,
- Children: []*Node{
- {Name: "Foo", FieldName: "Foo", Value: "huu,hii,hoo", Kind: reflect.Slice},
- },
- },
- element: &struct{ Foo []NamedType }{},
- expected: expected{element: &struct{ Foo []NamedType }{Foo: []NamedType{"huu", "hii", "hoo"}}},
- },
- {
- desc: "slice named type int",
- node: &Node{
- Name: "traefik",
- Kind: reflect.Struct,
- Children: []*Node{
- {Name: "Foo", FieldName: "Foo", Value: "1,2,3", Kind: reflect.Slice},
- },
- },
- element: &struct{ Foo []NamedTypeInt }{},
- expected: expected{element: &struct{ Foo []NamedTypeInt }{Foo: []NamedTypeInt{1, 2, 3}}},
- },
- {
- desc: "empty slice",
- node: &Node{
- Name: "traefik",
- Kind: reflect.Struct,
- Children: []*Node{
- {Name: "Foo", FieldName: "Foo", Value: "", Kind: reflect.Slice},
- },
- },
- element: &struct{ Foo []string }{},
- expected: expected{element: &struct{ Foo []string }{Foo: nil}},
- },
- {
- desc: "slice int",
- node: &Node{
- Name: "traefik",
- Kind: reflect.Struct,
- Children: []*Node{
- {Name: "Foo", FieldName: "Foo", Value: "4,3,6", Kind: reflect.Slice},
- },
- },
- element: &struct{ Foo []int }{},
- expected: expected{element: &struct{ Foo []int }{Foo: []int{4, 3, 6}}},
- },
- {
- desc: "slice invalid int",
- node: &Node{
- Name: "traefik",
- Kind: reflect.Struct,
- Children: []*Node{
- {Name: "Foo", FieldName: "Foo", Value: "four,three,six", Kind: reflect.Slice},
- },
- },
- element: &struct{ Foo []int }{},
- expected: expected{error: true},
- },
- {
- desc: "slice int8",
- node: &Node{
- Name: "traefik",
- Kind: reflect.Slice,
- Children: []*Node{
- {Name: "Foo", FieldName: "Foo", Value: "4,3,6", Kind: reflect.Slice},
- },
- },
- element: &struct{ Foo []int8 }{},
- expected: expected{element: &struct{ Foo []int8 }{Foo: []int8{4, 3, 6}}},
- },
- {
- desc: "slice invalid int8",
- node: &Node{
- Name: "traefik",
- Kind: reflect.Struct,
- Children: []*Node{
- {Name: "Foo", FieldName: "Foo", Value: "four,three,six", Kind: reflect.Slice},
- },
- },
- element: &struct{ Foo []int8 }{},
- expected: expected{error: true},
- },
- {
- desc: "slice int16",
- node: &Node{
- Name: "traefik",
- Kind: reflect.Struct,
- Children: []*Node{
- {Name: "Foo", FieldName: "Foo", Value: "4,3,6", Kind: reflect.Slice},
- },
- },
- element: &struct{ Foo []int16 }{},
- expected: expected{element: &struct{ Foo []int16 }{Foo: []int16{4, 3, 6}}},
- },
- {
- desc: "slice invalid int16",
- node: &Node{
- Name: "traefik",
- Kind: reflect.Struct,
- Children: []*Node{
- {Name: "Foo", FieldName: "Foo", Value: "four,three,six", Kind: reflect.Slice},
- },
- },
- element: &struct{ Foo []int16 }{},
- expected: expected{error: true},
- },
- {
- desc: "slice int32",
- node: &Node{
- Name: "traefik",
- Kind: reflect.Struct,
- Children: []*Node{
- {Name: "Foo", FieldName: "Foo", Value: "4,3,6", Kind: reflect.Slice},
- },
- },
- element: &struct{ Foo []int32 }{},
- expected: expected{element: &struct{ Foo []int32 }{Foo: []int32{4, 3, 6}}},
- },
- {
- desc: "slice invalid int32",
- node: &Node{
- Name: "traefik",
- Kind: reflect.Struct,
- Children: []*Node{
- {Name: "Foo", FieldName: "Foo", Value: "four,three,six", Kind: reflect.Slice},
- },
- },
- element: &struct{ Foo []int32 }{},
- expected: expected{error: true},
- },
- {
- desc: "slice int64",
- node: &Node{
- Name: "traefik",
- Kind: reflect.Struct,
- Children: []*Node{
- {Name: "Foo", FieldName: "Foo", Value: "4,3,6", Kind: reflect.Slice},
- },
- },
- element: &struct{ Foo []int64 }{},
- expected: expected{element: &struct{ Foo []int64 }{Foo: []int64{4, 3, 6}}},
- },
- {
- desc: "slice invalid int64",
- node: &Node{
- Name: "traefik",
- Kind: reflect.Struct,
- Children: []*Node{
- {Name: "Foo", FieldName: "Foo", Value: "four,three,six", Kind: reflect.Slice},
- },
- },
- element: &struct{ Foo []int64 }{},
- expected: expected{error: true},
- },
- {
- desc: "slice uint",
- node: &Node{
- Name: "traefik",
- Kind: reflect.Struct,
- Children: []*Node{
- {Name: "Foo", FieldName: "Foo", Value: "4,3,6", Kind: reflect.Slice},
- },
- },
- element: &struct{ Foo []uint }{},
- expected: expected{element: &struct{ Foo []uint }{Foo: []uint{4, 3, 6}}},
- },
- {
- desc: "slice invalid uint",
- node: &Node{
- Name: "traefik",
- Kind: reflect.Struct,
- Children: []*Node{
- {Name: "Foo", FieldName: "Foo", Value: "four,three,six", Kind: reflect.Slice},
- },
- },
- element: &struct{ Foo []uint }{},
- expected: expected{error: true},
- },
- {
- desc: "slice uint8",
- node: &Node{
- Name: "traefik",
- Kind: reflect.Struct,
- Children: []*Node{
- {Name: "Foo", FieldName: "Foo", Value: "4,3,6", Kind: reflect.Slice},
- },
- },
- element: &struct{ Foo []uint8 }{},
- expected: expected{element: &struct{ Foo []uint8 }{Foo: []uint8{4, 3, 6}}},
- },
- {
- desc: "slice invalid uint8",
- node: &Node{
- Name: "traefik",
- Kind: reflect.Struct,
- Children: []*Node{
- {Name: "Foo", FieldName: "Foo", Value: "four,three,six", Kind: reflect.Slice},
- },
- },
- element: &struct{ Foo []uint8 }{},
- expected: expected{error: true},
- },
- {
- desc: "slice uint16",
- node: &Node{
- Name: "traefik",
- Kind: reflect.Struct,
- Children: []*Node{
- {Name: "Foo", FieldName: "Foo", Value: "4,3,6", Kind: reflect.Slice},
- },
- },
- element: &struct{ Foo []uint16 }{},
- expected: expected{element: &struct{ Foo []uint16 }{Foo: []uint16{4, 3, 6}}},
- },
- {
- desc: "slice invalid uint16",
- node: &Node{
- Name: "traefik",
- Kind: reflect.Struct,
- Children: []*Node{
- {Name: "Foo", FieldName: "Foo", Value: "four,three,six", Kind: reflect.Slice},
- },
- },
- element: &struct{ Foo []uint16 }{},
- expected: expected{error: true},
- },
- {
- desc: "slice uint32",
- node: &Node{
- Name: "traefik",
- Kind: reflect.Struct,
- Children: []*Node{
- {Name: "Foo", FieldName: "Foo", Value: "4,3,6", Kind: reflect.Slice},
- },
- },
- element: &struct{ Foo []uint32 }{},
- expected: expected{element: &struct{ Foo []uint32 }{Foo: []uint32{4, 3, 6}}},
- },
- {
- desc: "slice invalid uint32",
- node: &Node{
- Name: "traefik",
- Kind: reflect.Struct,
- Children: []*Node{
- {Name: "Foo", FieldName: "Foo", Value: "four,three,six", Kind: reflect.Slice},
- },
- },
- element: &struct{ Foo []uint32 }{},
- expected: expected{error: true},
- },
- {
- desc: "slice uint64",
- node: &Node{
- Name: "traefik",
- Kind: reflect.Struct,
- Children: []*Node{
- {Name: "Foo", FieldName: "Foo", Value: "4,3,6", Kind: reflect.Slice},
- },
- },
- element: &struct{ Foo []uint64 }{},
- expected: expected{element: &struct{ Foo []uint64 }{Foo: []uint64{4, 3, 6}}},
- },
- {
- desc: "slice invalid uint64",
- node: &Node{
- Name: "traefik",
- Kind: reflect.Struct,
- Children: []*Node{
- {Name: "Foo", FieldName: "Foo", Value: "four,three,six", Kind: reflect.Slice},
- },
- },
- element: &struct{ Foo []uint64 }{},
- expected: expected{error: true},
- },
- {
- desc: "slice float32",
- node: &Node{
- Name: "traefik",
- Kind: reflect.Struct,
- Children: []*Node{
- {Name: "Foo", FieldName: "Foo", Value: "4,3,6", Kind: reflect.Slice},
- },
- },
- element: &struct{ Foo []float32 }{},
- expected: expected{element: &struct{ Foo []float32 }{Foo: []float32{4, 3, 6}}},
- },
- {
- desc: "slice invalid float32",
- node: &Node{
- Name: "traefik",
- Kind: reflect.Struct,
- Children: []*Node{
- {Name: "Foo", FieldName: "Foo", Value: "four,three,six", Kind: reflect.Slice},
- },
- },
- element: &struct{ Foo []float32 }{},
- expected: expected{error: true},
- },
- {
- desc: "slice float64",
- node: &Node{
- Name: "traefik",
- Kind: reflect.Struct,
- Children: []*Node{
- {Name: "Foo", FieldName: "Foo", Value: "4,3,6", Kind: reflect.Slice},
- },
- },
- element: &struct{ Foo []float64 }{},
- expected: expected{element: &struct{ Foo []float64 }{Foo: []float64{4, 3, 6}}},
- },
- {
- desc: "slice invalid float64",
- node: &Node{
- Name: "traefik",
- Kind: reflect.Struct,
- Children: []*Node{
- {Name: "Foo", FieldName: "Foo", Value: "four,three,six", Kind: reflect.Slice},
- },
- },
- element: &struct{ Foo []float64 }{},
- expected: expected{error: true},
- },
- {
- desc: "slice bool",
- node: &Node{
- Name: "traefik",
- Kind: reflect.Struct,
- Children: []*Node{
- {Name: "Foo", FieldName: "Foo", Value: "true, false, true", Kind: reflect.Slice},
- },
- },
- element: &struct{ Foo []bool }{},
- expected: expected{element: &struct{ Foo []bool }{Foo: []bool{true, false, true}}},
- },
- {
- desc: "slice invalid bool",
- node: &Node{
- Name: "traefik",
- Kind: reflect.Struct,
- Children: []*Node{
- {Name: "Foo", FieldName: "Foo", Value: "bool, false, true", Kind: reflect.Slice},
- },
- },
- element: &struct{ Foo []bool }{},
- expected: expected{error: true},
- },
- {
- desc: "slice slice-as-struct",
- node: &Node{
- Name: "traefik",
- Kind: reflect.Struct,
- Children: []*Node{
- {
- Name: "Fii",
- FieldName: "Foo",
- Kind: reflect.Slice,
- Tag: `label-slice-as-struct:"Fii"`,
- Children: []*Node{
- {Name: "bar", FieldName: "Bar", Kind: reflect.String, Value: "haa"},
- {Name: "bir", FieldName: "Bir", Kind: reflect.String, Value: "hii"},
- },
- },
- },
- },
- element: &struct {
- Foo []struct {
- Bar string
- Bir string
- } `label-slice-as-struct:"Fii"`
- }{},
- expected: expected{element: &struct {
- Foo []struct {
- Bar string
- Bir string
- } `label-slice-as-struct:"Fii"`
- }{
- Foo: []struct {
- Bar string
- Bir string
- }{
- {
- Bar: "haa",
- Bir: "hii",
- },
- },
- }},
- },
- {
- desc: "slice slice-as-struct pointer",
- node: &Node{
- Name: "traefik",
- Kind: reflect.Struct,
- Children: []*Node{
- {
- Name: "Fii",
- FieldName: "Foo",
- Kind: reflect.Slice,
- Tag: `label-slice-as-struct:"Fii"`,
- Children: []*Node{
- {Name: "bar", FieldName: "Bar", Kind: reflect.String, Value: "haa"},
- {Name: "bir", FieldName: "Bir", Kind: reflect.String, Value: "hii"},
- },
- },
- },
- },
- element: &struct {
- Foo []*struct {
- Bar string
- Bir string
- } `label-slice-as-struct:"Fii"`
- }{},
- expected: expected{element: &struct {
- Foo []*struct {
- Bar string
- Bir string
- } `label-slice-as-struct:"Fii"`
- }{
- Foo: []*struct {
- Bar string
- Bir string
- }{
- {
- Bar: "haa",
- Bir: "hii",
- },
- },
- }},
- },
- {
- desc: "slice slice-as-struct without children",
- node: &Node{
- Name: "traefik",
- Kind: reflect.Struct,
- Children: []*Node{
- {
- Name: "Fii",
- FieldName: "Foo",
- Tag: `label-slice-as-struct:"Fii"`,
- Kind: reflect.Slice,
- },
- },
- },
- element: &struct {
- Foo []struct {
- Bar string
- Bir string
- } `label-slice-as-struct:"Fii"`
- }{},
- expected: expected{error: true},
- },
- {
- desc: "pointer SetDefaults method",
- node: &Node{
- Name: "traefik",
- Kind: reflect.Struct,
- Children: []*Node{
- {
- Name: "Foo",
- FieldName: "Foo",
- Kind: reflect.Struct,
- Children: []*Node{
- {Name: "Fuu", FieldName: "Fuu", Value: "huu", Kind: reflect.String},
- },
- },
- },
- },
- element: &struct {
- Foo *InitializedFoo
- }{},
- expected: expected{element: &struct {
- Foo *InitializedFoo
- }{
- Foo: &InitializedFoo{
- Fii: "default",
- Fuu: "huu",
- },
- }},
- },
- {
- desc: "pointer wrong SetDefaults method",
- node: &Node{
- Name: "traefik",
- Kind: reflect.Struct,
- Children: []*Node{
- {
- Name: "Foo",
- FieldName: "Foo",
- Kind: reflect.Struct,
- Children: []*Node{
- {Name: "Fuu", FieldName: "Fuu", Value: "huu", Kind: reflect.String},
- },
- },
- },
- },
- element: &struct {
- Foo *wrongInitialledFoo
- }{},
- expected: expected{element: &struct {
- Foo *wrongInitialledFoo
- }{
- Foo: &wrongInitialledFoo{
- Fuu: "huu",
- },
- }},
- },
- {
- desc: "int pointer",
- node: &Node{
- Name: "traefik",
- Kind: reflect.Struct,
- Children: []*Node{
- {Name: "Foo", FieldName: "Foo", Value: "4", Kind: reflect.Ptr},
- },
- },
- element: &struct{ Foo *int }{},
- expected: expected{element: &struct{ Foo *int }{Foo: func(v int) *int { return &v }(4)}},
- },
- {
- desc: "bool pointer",
- node: &Node{
- Name: "traefik",
- Kind: reflect.Struct,
- Children: []*Node{
- {Name: "Foo", FieldName: "Foo", Value: "true", Kind: reflect.Ptr},
- },
- },
- element: &struct{ Foo *bool }{},
- expected: expected{element: &struct{ Foo *bool }{Foo: func(v bool) *bool { return &v }(true)}},
- },
- {
- desc: "string pointer",
- node: &Node{
- Name: "traefik",
- Kind: reflect.Struct,
- Children: []*Node{
- {Name: "Foo", FieldName: "Foo", Value: "bar", Kind: reflect.Ptr},
- },
- },
- element: &struct{ Foo *string }{},
- expected: expected{element: &struct{ Foo *string }{Foo: func(v string) *string { return &v }("bar")}},
- },
- {
- desc: "embedded",
- node: &Node{
- Name: "traefik",
- Kind: reflect.Struct,
- Children: []*Node{
- {
- Name: "Foo",
- FieldName: "Foo",
- Kind: reflect.Struct,
- Children: []*Node{
- {Name: "Fuu", FieldName: "Fuu", Value: "huu", Kind: reflect.String},
- },
- },
- },
- },
- element: &struct {
- Foo struct {
- FiiFoo
- }
- }{},
- expected: expected{element: &struct {
- Foo struct {
- FiiFoo
- }
- }{
- Foo: struct {
- FiiFoo
- }{
- FiiFoo: FiiFoo{
- Fii: "",
- Fuu: "huu",
- },
- },
- }},
- },
- {
- desc: "slice struct",
- node: &Node{
- Name: "traefik",
- Kind: reflect.Struct,
- Children: []*Node{
- {Name: "Foo", FieldName: "Foo", Kind: reflect.Slice, Children: []*Node{
- {Name: "[0]", Kind: reflect.Struct, Children: []*Node{
- {Name: "Field1", FieldName: "Field1", Value: "A", Kind: reflect.String},
- {Name: "Field2", FieldName: "Field2", Value: "A", Kind: reflect.String},
- }},
- {Name: "[1]", Kind: reflect.Struct, Children: []*Node{
- {Name: "Field1", FieldName: "Field1", Value: "B", Kind: reflect.String},
- {Name: "Field2", FieldName: "Field2", Value: "B", Kind: reflect.String},
- }},
- {Name: "[2]", Kind: reflect.Struct, Children: []*Node{
- {Name: "Field1", FieldName: "Field1", Value: "C", Kind: reflect.String},
- {Name: "Field2", FieldName: "Field2", Value: "C", Kind: reflect.String},
- }},
- }},
- },
- },
- element: &struct {
- Foo []struct {
- Field1 string
- Field2 string
- }
- }{},
- expected: expected{element: &struct {
- Foo []struct {
- Field1 string
- Field2 string
- }
- }{
- Foo: []struct {
- Field1 string
- Field2 string
- }{
- {Field1: "A", Field2: "A"},
- {Field1: "B", Field2: "B"},
- {Field1: "C", Field2: "C"},
- },
- }},
- },
- {
- desc: "slice pointer struct",
- node: &Node{
- Name: "traefik",
- Kind: reflect.Struct,
- Children: []*Node{
- {Name: "Foo", FieldName: "Foo", Kind: reflect.Slice, Children: []*Node{
- {Name: "[0]", Kind: reflect.Ptr, Children: []*Node{
- {Name: "Field1", FieldName: "Field1", Value: "A", Kind: reflect.String},
- {Name: "Field2", FieldName: "Field2", Value: "A", Kind: reflect.String},
- }},
- {Name: "[1]", Kind: reflect.Ptr, Children: []*Node{
- {Name: "Field1", FieldName: "Field1", Value: "B", Kind: reflect.String},
- {Name: "Field2", FieldName: "Field2", Value: "B", Kind: reflect.String},
- }},
- {Name: "[2]", Kind: reflect.Ptr, Children: []*Node{
- {Name: "Field1", FieldName: "Field1", Value: "C", Kind: reflect.String},
- {Name: "Field2", FieldName: "Field2", Value: "C", Kind: reflect.String},
- }},
- }},
- },
- },
- element: &struct {
- Foo []*struct {
- Field1 string
- Field2 string
- }
- }{},
- expected: expected{element: &struct {
- Foo []*struct {
- Field1 string
- Field2 string
- }
- }{
- Foo: []*struct {
- Field1 string
- Field2 string
- }{
- {Field1: "A", Field2: "A"},
- {Field1: "B", Field2: "B"},
- {Field1: "C", Field2: "C"},
- },
- }},
- },
- {
- 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 {
- test := test
- t.Run(test.desc, func(t *testing.T) {
- t.Parallel()
-
- err := filler{FillerOpts: FillerOpts{AllowSliceAsStruct: true}}.Fill(test.element, test.node)
- if test.expected.error {
- require.Error(t, err)
- } else {
- require.NoError(t, err)
- assert.Equal(t, test.expected.element, test.element)
- }
- })
- }
-}
-
-type (
- NamedType string
- NamedTypeInt int
-)
-
-type InitializedFoo struct {
- Fii string
- Fuu string
-}
-
-func (t *InitializedFoo) SetDefaults() {
- t.Fii = "default"
-}
-
-type wrongInitialledFoo struct {
- Fii string
- Fuu string
-}
-
-func (t *wrongInitialledFoo) SetDefaults() error {
- t.Fii = "default"
- return nil
-}
-
-type Bouya string
-
-type FiiFoo struct {
- Fii string
- Fuu Bouya
-}
diff --git a/pkg/config/parser/element_nodes.go b/pkg/config/parser/element_nodes.go
deleted file mode 100644
index 788975cd2..000000000
--- a/pkg/config/parser/element_nodes.go
+++ /dev/null
@@ -1,215 +0,0 @@
-package parser
-
-import (
- "fmt"
- "reflect"
- "strconv"
- "strings"
-)
-
-// EncoderToNodeOpts Options for the encoderToNode.
-type EncoderToNodeOpts struct {
- OmitEmpty bool
- TagName string
- AllowSliceAsStruct bool
-}
-
-// EncodeToNode converts an element to a node.
-// element -> nodes.
-func EncodeToNode(element interface{}, rootName string, opts EncoderToNodeOpts) (*Node, error) {
- rValue := reflect.ValueOf(element)
- node := &Node{Name: rootName}
-
- encoder := encoderToNode{EncoderToNodeOpts: opts}
-
- err := encoder.setNodeValue(node, rValue)
- if err != nil {
- return nil, err
- }
-
- return node, nil
-}
-
-type encoderToNode struct {
- EncoderToNodeOpts
-}
-
-func (e encoderToNode) setNodeValue(node *Node, rValue reflect.Value) error {
- switch rValue.Kind() {
- case reflect.String:
- node.Value = rValue.String()
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- node.Value = strconv.FormatInt(rValue.Int(), 10)
- case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
- node.Value = strconv.FormatUint(rValue.Uint(), 10)
- case reflect.Float32, reflect.Float64:
- node.Value = strconv.FormatFloat(rValue.Float(), 'f', 6, 64)
- case reflect.Bool:
- node.Value = strconv.FormatBool(rValue.Bool())
- case reflect.Struct:
- return e.setStructValue(node, rValue)
- case reflect.Ptr:
- return e.setNodeValue(node, rValue.Elem())
- case reflect.Map:
- return e.setMapValue(node, rValue)
- case reflect.Slice:
- return e.setSliceValue(node, rValue)
- default:
- // noop
- }
-
- return nil
-}
-
-func (e encoderToNode) setStructValue(node *Node, rValue reflect.Value) error {
- rType := rValue.Type()
-
- for i := 0; i < rValue.NumField(); i++ {
- field := rType.Field(i)
- fieldValue := rValue.Field(i)
-
- if !IsExported(field) {
- continue
- }
-
- if field.Tag.Get(e.TagName) == "-" {
- continue
- }
-
- if err := isSupportedType(field); err != nil {
- return err
- }
-
- if e.isSkippedField(field, fieldValue) {
- continue
- }
-
- nodeName := field.Name
- if e.AllowSliceAsStruct && field.Type.Kind() == reflect.Slice && len(field.Tag.Get(TagLabelSliceAsStruct)) != 0 {
- nodeName = field.Tag.Get(TagLabelSliceAsStruct)
- }
-
- if field.Anonymous {
- if err := e.setNodeValue(node, fieldValue); err != nil {
- return err
- }
- continue
- }
-
- child := &Node{Name: nodeName, FieldName: field.Name, Description: field.Tag.Get(TagDescription)}
-
- if err := e.setNodeValue(child, fieldValue); err != nil {
- return err
- }
-
- if field.Type.Kind() == reflect.Ptr {
- if field.Type.Elem().Kind() != reflect.Struct && fieldValue.IsNil() {
- continue
- }
-
- if field.Type.Elem().Kind() == reflect.Struct && len(child.Children) == 0 {
- if field.Tag.Get(e.TagName) != TagLabelAllowEmpty {
- continue
- }
-
- child.Value = "true"
- }
- }
-
- node.Children = append(node.Children, child)
- }
-
- return nil
-}
-
-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)
-
- if err := e.setNodeValue(child, rValue.MapIndex(key)); err != nil {
- return err
- }
- }
- return nil
-}
-
-func (e encoderToNode) setSliceValue(node *Node, rValue reflect.Value) error {
- // label-slice-as-struct
- if rValue.Type().Elem().Kind() == reflect.Struct && !strings.EqualFold(node.Name, node.FieldName) {
- if rValue.Len() > 1 {
- return fmt.Errorf("node %s has too many slice entries: %d", node.Name, rValue.Len())
- }
-
- return e.setNodeValue(node, rValue.Index(0))
- }
-
- if rValue.Type().Elem().Kind() == reflect.Struct ||
- rValue.Type().Elem().Kind() == reflect.Ptr && rValue.Type().Elem().Elem().Kind() == reflect.Struct {
- for i := 0; i < rValue.Len(); i++ {
- child := &Node{Name: "[" + strconv.Itoa(i) + "]"}
-
- eValue := rValue.Index(i)
-
- err := e.setNodeValue(child, eValue)
- if err != nil {
- return err
- }
-
- node.Children = append(node.Children, child)
- }
-
- return nil
- }
-
- var values []string
-
- for i := 0; i < rValue.Len(); i++ {
- eValue := rValue.Index(i)
-
- switch eValue.Kind() {
- case reflect.String:
- values = append(values, eValue.String())
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- values = append(values, strconv.FormatInt(eValue.Int(), 10))
- case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
- values = append(values, strconv.FormatUint(eValue.Uint(), 10))
- case reflect.Float32, reflect.Float64:
- values = append(values, strconv.FormatFloat(eValue.Float(), 'f', 6, 64))
- case reflect.Bool:
- values = append(values, strconv.FormatBool(eValue.Bool()))
- default:
- // noop
- }
- }
-
- node.Value = strings.Join(values, ", ")
- return nil
-}
-
-func (e encoderToNode) isSkippedField(field reflect.StructField, fieldValue reflect.Value) bool {
- if e.OmitEmpty && field.Type.Kind() == reflect.String && fieldValue.Len() == 0 {
- return true
- }
-
- if field.Type.Kind() == reflect.Ptr && field.Type.Elem().Kind() == reflect.Struct && fieldValue.IsNil() {
- return true
- }
-
- if e.OmitEmpty && (field.Type.Kind() == reflect.Slice) &&
- (fieldValue.IsNil() || fieldValue.Len() == 0) {
- return true
- }
-
- if (field.Type.Kind() == reflect.Map) &&
- (fieldValue.IsNil() || fieldValue.Len() == 0) {
- return true
- }
-
- return false
-}
diff --git a/pkg/config/parser/element_nodes_test.go b/pkg/config/parser/element_nodes_test.go
deleted file mode 100644
index b48d5201c..000000000
--- a/pkg/config/parser/element_nodes_test.go
+++ /dev/null
@@ -1,813 +0,0 @@
-package parser
-
-import (
- "testing"
-
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
-)
-
-func TestEncodeToNode(t *testing.T) {
- type expected struct {
- node *Node
- error bool
- }
-
- testCases := []struct {
- desc string
- element interface{}
- expected expected
- }{
- {
- desc: "Description",
- element: struct {
- Foo string `description:"text"`
- }{Foo: "bar"},
- expected: expected{
- node: &Node{Name: "traefik", Children: []*Node{
- {Name: "Foo", FieldName: "Foo", Value: "bar", Description: "text"},
- }},
- },
- },
- {
- desc: "string",
- element: struct {
- Foo string
- }{Foo: "bar"},
- expected: expected{
- node: &Node{Name: "traefik", Children: []*Node{
- {Name: "Foo", FieldName: "Foo", Value: "bar"},
- }},
- },
- },
- {
- desc: "2 string fields",
- element: struct {
- Foo string
- Fii string
- }{Foo: "bar", Fii: "hii"},
- expected: expected{
- node: &Node{Name: "traefik", Children: []*Node{
- {Name: "Foo", FieldName: "Foo", Value: "bar"},
- {Name: "Fii", FieldName: "Fii", Value: "hii"},
- }},
- },
- },
- {
- desc: "int",
- element: struct {
- Foo int
- }{Foo: 1},
- expected: expected{
- node: &Node{Name: "traefik", Children: []*Node{
- {Name: "Foo", FieldName: "Foo", Value: "1"},
- }},
- },
- },
- {
- desc: "int8",
- element: struct {
- Foo int8
- }{Foo: 2},
- expected: expected{
- node: &Node{Name: "traefik", Children: []*Node{
- {Name: "Foo", FieldName: "Foo", Value: "2"},
- }},
- },
- },
- {
- desc: "int16",
- element: struct {
- Foo int16
- }{Foo: 2},
- expected: expected{
- node: &Node{Name: "traefik", Children: []*Node{
- {Name: "Foo", FieldName: "Foo", Value: "2"},
- }},
- },
- },
- {
- desc: "int32",
- element: struct {
- Foo int32
- }{Foo: 2},
- expected: expected{
- node: &Node{Name: "traefik", Children: []*Node{
- {Name: "Foo", FieldName: "Foo", Value: "2"},
- }},
- },
- },
- {
- desc: "int64",
- element: struct {
- Foo int64
- }{Foo: 2},
- expected: expected{
- node: &Node{Name: "traefik", Children: []*Node{
- {Name: "Foo", FieldName: "Foo", Value: "2"},
- }},
- },
- },
- {
- desc: "uint",
- element: struct {
- Foo uint
- }{Foo: 1},
- expected: expected{
- node: &Node{Name: "traefik", Children: []*Node{
- {Name: "Foo", FieldName: "Foo", Value: "1"},
- }},
- },
- },
- {
- desc: "uint8",
- element: struct {
- Foo uint8
- }{Foo: 2},
- expected: expected{
- node: &Node{Name: "traefik", Children: []*Node{
- {Name: "Foo", FieldName: "Foo", Value: "2"},
- }},
- },
- },
- {
- desc: "uint16",
- element: struct {
- Foo uint16
- }{Foo: 2},
- expected: expected{
- node: &Node{Name: "traefik", Children: []*Node{
- {Name: "Foo", FieldName: "Foo", Value: "2"},
- }},
- },
- },
- {
- desc: "uint32",
- element: struct {
- Foo uint32
- }{Foo: 2},
- expected: expected{
- node: &Node{Name: "traefik", Children: []*Node{
- {Name: "Foo", FieldName: "Foo", Value: "2"},
- }},
- },
- },
- {
- desc: "uint64",
- element: struct {
- Foo uint64
- }{Foo: 2},
- expected: expected{
- node: &Node{Name: "traefik", Children: []*Node{
- {Name: "Foo", FieldName: "Foo", Value: "2"},
- }},
- },
- },
- {
- desc: "float32",
- element: struct {
- Foo float32
- }{Foo: 1.12},
- expected: expected{
- node: &Node{Name: "traefik", Children: []*Node{
- {Name: "Foo", FieldName: "Foo", Value: "1.120000"},
- }},
- },
- },
- {
- desc: "float64",
- element: struct {
- Foo float64
- }{Foo: 1.12},
- expected: expected{
- node: &Node{Name: "traefik", Children: []*Node{
- {Name: "Foo", FieldName: "Foo", Value: "1.120000"},
- }},
- },
- },
- {
- desc: "bool",
- element: struct {
- Foo bool
- }{Foo: true},
- expected: expected{
- node: &Node{Name: "traefik", Children: []*Node{
- {Name: "Foo", FieldName: "Foo", Value: "true"},
- }},
- },
- },
- {
- desc: "struct",
- element: struct {
- Foo struct {
- Fii string
- Fuu string
- }
- }{
- Foo: struct {
- Fii string
- Fuu string
- }{
- Fii: "hii",
- Fuu: "huu",
- },
- },
- expected: expected{
- node: &Node{Name: "traefik", Children: []*Node{
- {Name: "Foo", FieldName: "Foo", Children: []*Node{
- {Name: "Fii", FieldName: "Fii", Value: "hii"},
- {Name: "Fuu", FieldName: "Fuu", Value: "huu"},
- }},
- }},
- },
- },
- {
- desc: "struct unexported field",
- element: struct {
- Foo struct {
- Fii string
- fuu string
- }
- }{
- Foo: struct {
- Fii string
- fuu string
- }{
- Fii: "hii",
- fuu: "huu",
- },
- },
- expected: expected{
- node: &Node{Name: "traefik", Children: []*Node{
- {Name: "Foo", FieldName: "Foo", Children: []*Node{
- {Name: "Fii", FieldName: "Fii", Value: "hii"},
- }},
- }},
- },
- },
- {
- desc: "struct pointer",
- element: struct {
- Foo *struct {
- Fii string
- Fuu string
- }
- }{
- Foo: &struct {
- Fii string
- Fuu string
- }{
- Fii: "hii",
- Fuu: "huu",
- },
- },
- expected: expected{
- node: &Node{Name: "traefik", Children: []*Node{
- {Name: "Foo", FieldName: "Foo", Children: []*Node{
- {Name: "Fii", FieldName: "Fii", Value: "hii"},
- {Name: "Fuu", FieldName: "Fuu", Value: "huu"},
- }},
- }},
- },
- },
- {
- desc: "string pointer",
- element: struct {
- Foo *struct {
- Fii *string
- Fuu string
- }
- }{
- Foo: &struct {
- Fii *string
- Fuu string
- }{
- Fii: func(v string) *string { return &v }("hii"),
- Fuu: "huu",
- },
- },
- expected: expected{
- node: &Node{Name: "traefik", Children: []*Node{
- {Name: "Foo", FieldName: "Foo", Children: []*Node{
- {Name: "Fii", FieldName: "Fii", Value: "hii"},
- {Name: "Fuu", FieldName: "Fuu", Value: "huu"},
- }},
- }},
- },
- },
- {
- desc: "string nil pointer",
- element: struct {
- Foo *struct {
- Fii *string
- Fuu string
- }
- }{
- Foo: &struct {
- Fii *string
- Fuu string
- }{
- Fii: nil,
- Fuu: "huu",
- },
- },
- expected: expected{
- node: &Node{Name: "traefik", Children: []*Node{
- {Name: "Foo", FieldName: "Foo", Children: []*Node{
- {Name: "Fuu", FieldName: "Fuu", Value: "huu"},
- }},
- }},
- },
- },
- {
- desc: "int pointer",
- element: struct {
- Foo *struct {
- Fii *int
- Fuu int
- }
- }{
- Foo: &struct {
- Fii *int
- Fuu int
- }{
- Fii: func(v int) *int { return &v }(6),
- Fuu: 4,
- },
- },
- expected: expected{
- node: &Node{Name: "traefik", Children: []*Node{
- {Name: "Foo", FieldName: "Foo", Children: []*Node{
- {Name: "Fii", FieldName: "Fii", Value: "6"},
- {Name: "Fuu", FieldName: "Fuu", Value: "4"},
- }},
- }},
- },
- },
- {
- desc: "bool pointer",
- element: struct {
- Foo *struct {
- Fii *bool
- Fuu bool
- }
- }{
- Foo: &struct {
- Fii *bool
- Fuu bool
- }{
- Fii: func(v bool) *bool { return &v }(true),
- Fuu: true,
- },
- },
- expected: expected{
- node: &Node{Name: "traefik", Children: []*Node{
- {Name: "Foo", FieldName: "Foo", Children: []*Node{
- {Name: "Fii", FieldName: "Fii", Value: "true"},
- {Name: "Fuu", FieldName: "Fuu", Value: "true"},
- }},
- }},
- },
- },
- {
- desc: "struct nil struct pointer",
- element: struct {
- Foo *struct {
- Fii *string
- Fuu string
- }
- }{
- Foo: nil,
- },
- expected: expected{node: &Node{Name: "traefik"}},
- },
- {
- desc: "struct pointer, not allowEmpty",
- element: struct {
- Foo *struct {
- Fii string
- Fuu string
- }
- }{
- Foo: &struct {
- Fii string
- Fuu string
- }{},
- },
- expected: expected{node: &Node{Name: "traefik"}},
- },
- {
- desc: "struct pointer, allowEmpty",
- element: struct {
- Foo *struct {
- Fii string
- Fuu string
- } `label:"allowEmpty"`
- }{
- Foo: &struct {
- Fii string
- Fuu string
- }{},
- },
- expected: expected{
- node: &Node{Name: "traefik", Children: []*Node{
- {Name: "Foo", FieldName: "Foo", Value: "true"},
- }},
- },
- },
- {
- desc: "map",
- element: struct {
- Foo struct {
- Bar map[string]string
- }
- }{
- Foo: struct {
- Bar map[string]string
- }{
- Bar: map[string]string{
- "name1": "huu",
- },
- },
- },
- expected: expected{node: &Node{Name: "traefik", Children: []*Node{
- {Name: "Foo", FieldName: "Foo", Children: []*Node{
- {Name: "Bar", FieldName: "Bar", Children: []*Node{
- {Name: "name1", FieldName: "name1", Value: "huu"},
- }},
- }},
- }}},
- },
- {
- desc: "empty map",
- element: struct {
- Bar map[string]string
- }{
- Bar: map[string]string{},
- },
- expected: expected{node: &Node{Name: "traefik"}},
- },
- {
- desc: "map nil",
- element: struct {
- Bar map[string]string
- }{
- Bar: nil,
- },
- expected: expected{node: &Node{Name: "traefik"}},
- },
- {
- desc: "map with non string key",
- element: struct {
- Foo struct {
- Bar map[int]string
- }
- }{
- Foo: struct {
- Bar map[int]string
- }{
- Bar: map[int]string{
- 1: "huu",
- },
- },
- },
- expected: expected{error: true},
- },
- {
- desc: "slice of string",
- element: struct{ Bar []string }{Bar: []string{"huu", "hii"}},
- expected: expected{
- node: &Node{Name: "traefik", Children: []*Node{
- {Name: "Bar", FieldName: "Bar", Value: "huu, hii"},
- }},
- },
- },
- {
- desc: "slice of int",
- element: struct{ Bar []int }{Bar: []int{4, 2, 3}},
- expected: expected{
- node: &Node{Name: "traefik", Children: []*Node{
- {Name: "Bar", FieldName: "Bar", Value: "4, 2, 3"},
- }},
- },
- },
- {
- desc: "slice of int8",
- element: struct{ Bar []int8 }{Bar: []int8{4, 2, 3}},
- expected: expected{
- node: &Node{Name: "traefik", Children: []*Node{
- {Name: "Bar", FieldName: "Bar", Value: "4, 2, 3"},
- }},
- },
- },
- {
- desc: "slice of int16",
- element: struct{ Bar []int16 }{Bar: []int16{4, 2, 3}},
- expected: expected{
- node: &Node{Name: "traefik", Children: []*Node{
- {Name: "Bar", FieldName: "Bar", Value: "4, 2, 3"},
- }},
- },
- },
- {
- desc: "slice of int32",
- element: struct{ Bar []int32 }{Bar: []int32{4, 2, 3}},
- expected: expected{
- node: &Node{Name: "traefik", Children: []*Node{
- {Name: "Bar", FieldName: "Bar", Value: "4, 2, 3"},
- }},
- },
- },
- {
- desc: "slice of int64",
- element: struct{ Bar []int64 }{Bar: []int64{4, 2, 3}},
- expected: expected{
- node: &Node{Name: "traefik", Children: []*Node{
- {Name: "Bar", FieldName: "Bar", Value: "4, 2, 3"},
- }},
- },
- },
- {
- desc: "slice of uint",
- element: struct{ Bar []uint }{Bar: []uint{4, 2, 3}},
- expected: expected{
- node: &Node{Name: "traefik", Children: []*Node{
- {Name: "Bar", FieldName: "Bar", Value: "4, 2, 3"},
- }},
- },
- },
- {
- desc: "slice of uint8",
- element: struct{ Bar []uint8 }{Bar: []uint8{4, 2, 3}},
- expected: expected{
- node: &Node{Name: "traefik", Children: []*Node{
- {Name: "Bar", FieldName: "Bar", Value: "4, 2, 3"},
- }},
- },
- },
- {
- desc: "slice of uint16",
- element: struct{ Bar []uint16 }{Bar: []uint16{4, 2, 3}},
- expected: expected{
- node: &Node{Name: "traefik", Children: []*Node{
- {Name: "Bar", FieldName: "Bar", Value: "4, 2, 3"},
- }},
- },
- },
- {
- desc: "slice of uint32",
- element: struct{ Bar []uint32 }{Bar: []uint32{4, 2, 3}},
- expected: expected{
- node: &Node{Name: "traefik", Children: []*Node{
- {Name: "Bar", FieldName: "Bar", Value: "4, 2, 3"},
- }},
- },
- },
- {
- desc: "slice of uint64",
- element: struct{ Bar []uint64 }{Bar: []uint64{4, 2, 3}},
- expected: expected{
- node: &Node{Name: "traefik", Children: []*Node{
- {Name: "Bar", FieldName: "Bar", Value: "4, 2, 3"},
- }},
- },
- },
- {
- desc: "slice of float32",
- element: struct{ Bar []float32 }{Bar: []float32{4.1, 2, 3.2}},
- expected: expected{
- node: &Node{Name: "traefik", Children: []*Node{
- {Name: "Bar", FieldName: "Bar", Value: "4.100000, 2.000000, 3.200000"},
- }},
- },
- },
- {
- desc: "slice of float64",
- element: struct{ Bar []float64 }{Bar: []float64{4.1, 2, 3.2}},
- expected: expected{
- node: &Node{Name: "traefik", Children: []*Node{
- {Name: "Bar", FieldName: "Bar", Value: "4.100000, 2.000000, 3.200000"},
- }},
- },
- },
- {
- desc: "slice of bool",
- element: struct{ Bar []bool }{Bar: []bool{true, false, true}},
- expected: expected{
- node: &Node{Name: "traefik", Children: []*Node{
- {Name: "Bar", FieldName: "Bar", Value: "true, false, true"},
- }},
- },
- },
- {
- desc: "slice label-slice-as-struct",
- element: &struct {
- Foo []struct {
- Bar string
- Bir string
- } `label-slice-as-struct:"Fii"`
- }{
- Foo: []struct {
- Bar string
- Bir string
- }{
- {
- Bar: "haa",
- Bir: "hii",
- },
- },
- },
- expected: expected{node: &Node{
- Name: "traefik",
- Children: []*Node{{
- Name: "Fii",
- FieldName: "Foo",
- Children: []*Node{
- {Name: "Bar", FieldName: "Bar", Value: "haa"},
- {Name: "Bir", FieldName: "Bir", Value: "hii"},
- },
- }},
- }},
- },
- {
- desc: "slice label-slice-as-struct several slice entries",
- element: &struct {
- Foo []struct {
- Bar string
- Bir string
- } `label-slice-as-struct:"Fii"`
- }{
- Foo: []struct {
- Bar string
- Bir string
- }{
- {
- Bar: "haa",
- Bir: "hii",
- },
- {
- Bar: "haa",
- Bir: "hii",
- },
- },
- },
- expected: expected{error: true},
- },
- {
- desc: "slice of struct",
- element: struct {
- Foo []struct {
- Field string
- }
- }{
- Foo: []struct {
- Field string
- }{
- {
- Field: "bar",
- },
- {
- Field: "bir",
- },
- },
- },
- expected: expected{node: &Node{Name: "traefik", Children: []*Node{
- {Name: "Foo", FieldName: "Foo", Children: []*Node{
- {Name: "[0]", Children: []*Node{
- {Name: "Field", FieldName: "Field", Value: "bar"},
- }},
- {Name: "[1]", Children: []*Node{
- {Name: "Field", FieldName: "Field", Value: "bir"},
- }},
- }},
- }}},
- },
- {
- desc: "slice of pointer of struct",
- element: struct {
- Foo []*struct {
- Field string
- }
- }{
- Foo: []*struct {
- Field string
- }{
- {Field: "bar"},
- {Field: "bir"},
- },
- },
- expected: expected{node: &Node{Name: "traefik", Children: []*Node{
- {Name: "Foo", FieldName: "Foo", Children: []*Node{
- {Name: "[0]", Children: []*Node{
- {Name: "Field", FieldName: "Field", Value: "bar"},
- }},
- {Name: "[1]", Children: []*Node{
- {Name: "Field", FieldName: "Field", Value: "bir"},
- }},
- }},
- }}},
- },
- {
- desc: "empty slice",
- element: struct {
- Bar []string
- }{
- Bar: []string{},
- },
- expected: expected{node: &Node{Name: "traefik"}},
- },
- {
- desc: "nil slice",
- element: struct {
- Bar []string
- }{
- Bar: nil,
- },
- expected: expected{node: &Node{Name: "traefik"}},
- },
- {
- desc: "ignore slice",
- element: struct {
- Bar []string `label:"-"`
- }{
- Bar: []string{"huu", "hii"},
- },
- expected: expected{node: &Node{Name: "traefik"}},
- },
- {
- desc: "embedded",
- element: struct {
- Foo struct{ FiiFoo }
- }{
- Foo: struct{ FiiFoo }{
- FiiFoo: FiiFoo{
- Fii: "hii",
- Fuu: "huu",
- },
- },
- },
- expected: expected{
- node: &Node{Name: "traefik", Children: []*Node{
- {Name: "Foo", FieldName: "Foo", Children: []*Node{
- {Name: "Fii", FieldName: "Fii", Value: "hii"},
- {Name: "Fuu", FieldName: "Fuu", Value: "huu"},
- }},
- }},
- },
- },
- {
- 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 {
- test := test
- t.Run(test.desc, func(t *testing.T) {
- t.Parallel()
-
- etnOpts := EncoderToNodeOpts{OmitEmpty: true, TagName: TagLabel, AllowSliceAsStruct: true}
- node, err := EncodeToNode(test.element, DefaultRootName, etnOpts)
-
- if test.expected.error {
- require.Error(t, err)
- } else {
- require.NoError(t, err)
-
- assert.Equal(t, test.expected.node, node)
- }
- })
- }
-}
diff --git a/pkg/config/parser/flat_encode.go b/pkg/config/parser/flat_encode.go
deleted file mode 100644
index 7ac8c70f9..000000000
--- a/pkg/config/parser/flat_encode.go
+++ /dev/null
@@ -1,167 +0,0 @@
-package parser
-
-import (
- "fmt"
- "reflect"
- "sort"
- "strconv"
- "strings"
- "time"
-
- "github.com/containous/traefik/v2/pkg/types"
-)
-
-const defaultPtrValue = "false"
-
-// FlatOpts holds options used when encoding to Flat.
-type FlatOpts struct {
- Case string // "lower" or "upper", defaults to "lower".
- Separator string
- SkipRoot bool
- TagName string
-}
-
-// Flat is a configuration item representation.
-type Flat struct {
- Name string
- Description string
- Default string
-}
-
-// EncodeToFlat encodes a node to a Flat representation.
-// Even though the given node argument should have already been augmented with metadata such as kind,
-// the element (and its type information) is still needed to treat remaining edge cases.
-func EncodeToFlat(element interface{}, node *Node, opts FlatOpts) ([]Flat, error) {
- if element == nil || node == nil {
- return nil, nil
- }
-
- if node.Kind == 0 {
- return nil, fmt.Errorf("missing node type: %s", node.Name)
- }
-
- elem := reflect.ValueOf(element)
- if elem.Kind() == reflect.Struct {
- return nil, fmt.Errorf("structs are not supported, use pointer instead")
- }
-
- encoder := encoderToFlat{FlatOpts: opts}
-
- var entries []Flat
- if encoder.SkipRoot {
- for _, child := range node.Children {
- field := encoder.getField(elem.Elem(), child)
- entries = append(entries, encoder.createFlat(field, child.Name, child)...)
- }
- } else {
- entries = encoder.createFlat(elem, strings.ToLower(node.Name), node)
- }
-
- sort.Slice(entries, func(i, j int) bool { return entries[i].Name < entries[j].Name })
-
- return entries, nil
-}
-
-type encoderToFlat struct {
- FlatOpts
-}
-
-func (e encoderToFlat) createFlat(field reflect.Value, name string, node *Node) []Flat {
- var entries []Flat
- if node.Kind != reflect.Map && node.Description != "-" {
- if !(node.Kind == reflect.Ptr && len(node.Children) > 0) ||
- (node.Kind == reflect.Ptr && node.Tag.Get(e.TagName) == TagLabelAllowEmpty) {
- if node.Name[0] != '[' {
- entries = append(entries, Flat{
- Name: e.getName(name),
- Description: node.Description,
- Default: e.getNodeValue(e.getField(field, node), node),
- })
- }
- }
- }
-
- for _, child := range node.Children {
- if node.Kind == reflect.Map {
- fChild := e.getField(field, child)
-
- var v string
- if child.Kind == reflect.Struct {
- v = defaultPtrValue
- } else {
- v = e.getNodeValue(fChild, child)
- }
-
- if node.Description != "-" {
- entries = append(entries, Flat{
- Name: e.getName(name, child.Name),
- Description: node.Description,
- Default: v,
- })
- }
-
- if child.Kind == reflect.Struct || child.Kind == reflect.Ptr {
- for _, ch := range child.Children {
- f := e.getField(fChild, ch)
- n := e.getName(name, child.Name, ch.Name)
- entries = append(entries, e.createFlat(f, n, ch)...)
- }
- }
- } else {
- f := e.getField(field, child)
- n := e.getName(name, child.Name)
- entries = append(entries, e.createFlat(f, n, child)...)
- }
- }
-
- return entries
-}
-
-func (e encoderToFlat) getField(field reflect.Value, node *Node) reflect.Value {
- switch field.Kind() {
- case reflect.Struct:
- return field.FieldByName(node.FieldName)
- case reflect.Ptr:
- if field.Elem().Kind() == reflect.Struct {
- return field.Elem().FieldByName(node.FieldName)
- }
- return field.Elem()
- case reflect.Map:
- return field.MapIndex(reflect.ValueOf(node.FieldName))
- default:
- return field
- }
-}
-
-func (e encoderToFlat) getNodeValue(field reflect.Value, node *Node) string {
- if node.Kind == reflect.Ptr && len(node.Children) > 0 {
- return defaultPtrValue
- }
-
- if field.Kind() == reflect.Int64 {
- i, _ := strconv.ParseInt(node.Value, 10, 64)
-
- switch field.Type() {
- case reflect.TypeOf(types.Duration(time.Second)):
- return strconv.Itoa(int(i) / int(time.Second))
- case reflect.TypeOf(time.Second):
- return time.Duration(i).String()
- }
- }
-
- return node.Value
-}
-
-func (e encoderToFlat) getName(names ...string) string {
- var name string
- if names[len(names)-1][0] == '[' {
- name = strings.Join(names, "")
- } else {
- name = strings.Join(names, e.Separator)
- }
-
- if strings.EqualFold(e.Case, "upper") {
- return strings.ToUpper(name)
- }
- return strings.ToLower(name)
-}
diff --git a/pkg/config/parser/flat_encode_test.go b/pkg/config/parser/flat_encode_test.go
deleted file mode 100644
index ba48aab0c..000000000
--- a/pkg/config/parser/flat_encode_test.go
+++ /dev/null
@@ -1,1251 +0,0 @@
-package parser
-
-import (
- "reflect"
- "testing"
- "time"
-
- "github.com/containous/traefik/v2/pkg/types"
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
-)
-
-func TestEncodeToFlat(t *testing.T) {
- testCases := []struct {
- desc string
- element interface{}
- node *Node
- opts *FlatOpts
- expected []Flat
- }{
- {
- desc: "string field",
- element: &struct {
- Field string `description:"field description"`
- }{
- Field: "test",
- },
- node: &Node{
- Name: "traefik",
- FieldName: "",
- Kind: reflect.Ptr,
- Children: []*Node{
- {
- Name: "Field",
- FieldName: "Field",
- Description: "field description",
- Value: "test",
- Kind: reflect.String,
- Tag: `description:"field description"`,
- },
- },
- },
- expected: []Flat{{
- Name: "field",
- Description: "field description",
- Default: "test",
- }},
- },
- {
- desc: "int field",
- element: &struct {
- Field int `description:"field description"`
- }{
- Field: 6,
- },
- node: &Node{
- Name: "traefik",
- FieldName: "",
- Kind: reflect.Ptr,
- Children: []*Node{
- {
- Name: "Field",
- Description: "field description",
- FieldName: "Field",
- Value: "6",
- Kind: reflect.Int,
- Tag: `description:"field description"`,
- },
- },
- },
- expected: []Flat{{
- Name: "field",
- Description: "field description",
- Default: "6",
- }},
- },
- {
- desc: "bool field",
- element: &struct {
- Field bool `description:"field description"`
- }{
- Field: true,
- },
- node: &Node{
- Name: "traefik",
- FieldName: "",
- Kind: reflect.Ptr,
- Children: []*Node{
- {
- Name: "Field",
- Description: "field description",
- FieldName: "Field",
- Value: "true",
- Kind: reflect.Bool,
- Tag: `description:"field description"`,
- },
- },
- },
- expected: []Flat{{
- Name: "field",
- Description: "field description",
- Default: "true",
- }},
- },
- {
- desc: "string pointer field",
- element: &struct {
- Field *string `description:"field description"`
- }{
- Field: func(v string) *string { return &v }("test"),
- },
- node: &Node{
- Name: "traefik",
- FieldName: "",
- Kind: reflect.Ptr,
- Children: []*Node{
- {
- Name: "Field",
- Description: "field description",
- FieldName: "Field",
- Value: "test",
- Kind: reflect.Ptr,
- Tag: `description:"field description"`,
- },
- },
- },
- expected: []Flat{{
- Name: "field",
- Description: "field description",
- Default: "test",
- }},
- },
- {
- desc: "string pointer field, custom option",
- element: &struct {
- Field *string `description:"field description"`
- }{
- Field: func(v string) *string { return &v }("test"),
- },
- node: &Node{
- Name: "traefik",
- FieldName: "",
- Kind: reflect.Ptr,
- Children: []*Node{
- {
- Name: "Field",
- Description: "field description",
- FieldName: "Field",
- Value: "test",
- Kind: reflect.Ptr,
- Tag: `description:"field description"`,
- },
- },
- },
- opts: &FlatOpts{
- Case: "upper",
- Separator: "_",
- SkipRoot: false,
- TagName: TagLabel,
- },
- expected: []Flat{{
- Name: "TRAEFIK_FIELD",
- Description: "field description",
- Default: "test",
- }},
- },
- {
- desc: "int pointer field",
- element: &struct {
- Field *int `description:"field description"`
- }{
- Field: func(v int) *int { return &v }(6),
- },
- node: &Node{
- Name: "traefik",
- FieldName: "",
- Kind: reflect.Ptr,
- Children: []*Node{
- {
- Name: "Field",
- Description: "field description",
- FieldName: "Field",
- Value: "6",
- Kind: reflect.Ptr,
- Tag: `description:"field description"`,
- },
- },
- },
- expected: []Flat{{
- Name: "field",
- Description: "field description",
- Default: "6",
- }},
- },
- {
- desc: "bool pointer field",
- element: &struct {
- Field *bool `description:"field description"`
- }{
- Field: func(v bool) *bool { return &v }(true),
- },
- node: &Node{
- Name: "traefik",
- FieldName: "",
- Kind: reflect.Ptr,
- Children: []*Node{
- {
- Name: "Field",
- Description: "field description",
- FieldName: "Field",
- Value: "true",
- Kind: reflect.Ptr,
- Tag: `description:"field description"`,
- },
- },
- },
- expected: []Flat{{
- Name: "field",
- Description: "field description",
- Default: "true",
- }},
- },
- {
- desc: "slice of string field, no initial value",
- element: &struct {
- Field []string `description:"field description"`
- }{},
- node: &Node{
- Name: "traefik",
- FieldName: "",
- Kind: reflect.Ptr,
- Children: []*Node{
- {
- Name: "Field",
- Description: "field description",
- FieldName: "Field",
- Kind: reflect.Slice,
- Tag: `description:"field description"`,
- },
- },
- },
- expected: []Flat{{
- Name: "field",
- Description: "field description",
- Default: "",
- }},
- },
- {
- desc: "slice of string field, with initial value",
- element: &struct {
- Field []string `description:"field description"`
- }{
- Field: []string{"foo", "bar"},
- },
- node: &Node{
- Name: "traefik",
- FieldName: "",
- Kind: reflect.Ptr,
- Children: []*Node{
- {
- Name: "Field",
- Description: "field description",
- FieldName: "Field",
- Value: "foo, bar",
- Kind: reflect.Slice,
- Tag: `description:"field description"`,
- },
- },
- },
- expected: []Flat{{
- Name: "field",
- Description: "field description",
- Default: "foo, bar",
- }},
- },
- {
- desc: "slice of int field, no initial value",
- element: &struct {
- Field []int `description:"field description"`
- }{},
- node: &Node{
- Name: "traefik",
- FieldName: "",
- Kind: reflect.Ptr,
- Children: []*Node{
- {
- Name: "Field",
- Description: "field description",
- FieldName: "Field",
- Kind: reflect.Slice,
- Tag: `description:"field description"`,
- },
- },
- },
- expected: []Flat{{
- Name: "field",
- Description: "field description",
- Default: "",
- }},
- },
- {
- desc: "slice of int field, with initial value",
- element: &struct {
- Field []int `description:"field description"`
- }{
- Field: []int{6, 3},
- },
- node: &Node{
- Name: "traefik",
- FieldName: "",
- Kind: reflect.Ptr,
- Children: []*Node{
- {
- Name: "Field",
- Description: "field description",
- FieldName: "Field",
- Value: "6, 3",
- Kind: reflect.Slice,
- Tag: `description:"field description"`,
- },
- },
- },
- expected: []Flat{{
- Name: "field",
- Description: "field description",
- Default: "6, 3",
- }},
- },
- {
- desc: "map string field",
- element: &struct {
- Field map[string]string `description:"field description"`
- }{
- Field: map[string]string{
- MapNamePlaceholder: "",
- },
- },
- node: &Node{
- Name: "traefik",
- FieldName: "",
- Kind: reflect.Ptr,
- Children: []*Node{
- {
- Name: "Field",
- Description: "field description",
- FieldName: "Field",
- Kind: reflect.Map,
- Tag: `description:"field description"`,
- Children: []*Node{
- {
- Name: "\u003cname\u003e",
- FieldName: "\u003cname\u003e",
- Kind: reflect.String,
- },
- },
- },
- },
- },
- expected: []Flat{{
- Name: "field.",
- Description: "field description",
- Default: "",
- }},
- },
- {
- desc: "struct pointer field",
- element: &struct {
- Foo *struct {
- Field string `description:"field description"`
- } `description:"foo description"`
- }{
- Foo: &struct {
- Field string `description:"field description"`
- }{
- Field: "test",
- },
- },
- node: &Node{
- Name: "traefik",
- FieldName: "",
- Kind: reflect.Ptr,
- Children: []*Node{
- {
- Name: "Foo",
- Description: "foo description",
- FieldName: "Foo",
- Kind: reflect.Ptr,
- Tag: `description:"foo description"`,
- Children: []*Node{
- {
- Name: "Field",
- Description: "field description",
- FieldName: "Field",
- Value: "test",
- Kind: reflect.String,
- Tag: `description:"field description"`,
- },
- },
- },
- },
- },
- expected: []Flat{
- {
- Name: "foo.field",
- Description: "field description",
- Default: "test",
- },
- },
- },
- {
- desc: "struct pointer field, hide field",
- element: &struct {
- Foo *struct {
- Field string `description:"-"`
- } `description:"foo description"`
- }{
- Foo: &struct {
- Field string `description:"-"`
- }{
- Field: "test",
- },
- },
- node: &Node{
- Name: "traefik",
- FieldName: "",
- Kind: reflect.Ptr,
- Children: []*Node{
- {
- Name: "Foo",
- Description: "foo description",
- FieldName: "Foo",
- Kind: reflect.Ptr,
- Tag: `description:"foo description"`,
- Children: []*Node{
- {
- Name: "Field",
- Description: "-",
- FieldName: "Field",
- Value: "test",
- Kind: reflect.String,
- Tag: `description:"-"`,
- },
- },
- },
- },
- },
- expected: nil,
- },
- {
- desc: "struct pointer field, allow empty",
- element: &struct {
- Foo *struct {
- Field string `description:"field description"`
- } `description:"foo description" label:"allowEmpty"`
- }{
- Foo: &struct {
- Field string `description:"field description"`
- }{
- Field: "test",
- },
- },
- node: &Node{
- Name: "traefik",
- FieldName: "",
- Kind: reflect.Ptr,
- Children: []*Node{
- {
- Name: "Foo",
- Description: "foo description",
- FieldName: "Foo",
- Kind: reflect.Ptr,
- Tag: `description:"foo description" label:"allowEmpty"`,
- Children: []*Node{
- {
- Name: "Field",
- Description: "field description",
- FieldName: "Field",
- Value: "test",
- Kind: reflect.String,
- Tag: `description:"field description"`,
- },
- },
- },
- },
- },
- expected: []Flat{
- {
- Name: "foo",
- Description: "foo description",
- Default: "false",
- },
- {
- Name: "foo.field",
- Description: "field description",
- Default: "test",
- },
- },
- },
- {
- desc: "struct pointer field level 2",
- element: &struct {
- Foo *struct {
- Fii *struct {
- Field string `description:"field description"`
- } `description:"fii description"`
- } `description:"foo description"`
- }{
- Foo: &struct {
- Fii *struct {
- Field string `description:"field description"`
- } `description:"fii description"`
- }{
- Fii: &struct {
- Field string `description:"field description"`
- }{
- Field: "test",
- },
- },
- },
- node: &Node{
- Name: "traefik",
- FieldName: "",
- Kind: reflect.Ptr,
- Children: []*Node{
- {
- Name: "Foo",
- Description: "foo description",
- FieldName: "Foo",
- Kind: reflect.Ptr,
- Tag: `description:"foo description"`,
- Children: []*Node{
- {
- Name: "Fii",
- Description: "fii description",
- FieldName: "Fii",
- Kind: reflect.Ptr,
- Tag: `description:"fii description"`,
- Children: []*Node{
- {
- Name: "Field",
- Description: "field description",
- FieldName: "Field",
- Value: "test",
- Kind: reflect.String,
- Tag: `description:"field description"`,
- },
- },
- },
- },
- },
- },
- },
- expected: []Flat{
- {
- Name: "foo.fii.field",
- Description: "field description",
- Default: "test",
- },
- },
- },
- {
- desc: "struct pointer field level 2, allow empty",
- element: &struct {
- Foo *struct {
- Fii *struct {
- Field string `description:"field description"`
- } `description:"fii description" label:"allowEmpty"`
- } `description:"foo description" label:"allowEmpty"`
- }{
- Foo: &struct {
- Fii *struct {
- Field string `description:"field description"`
- } `description:"fii description" label:"allowEmpty"`
- }{
- Fii: &struct {
- Field string `description:"field description"`
- }{
- Field: "test",
- },
- },
- },
- node: &Node{
- Name: "traefik",
- FieldName: "",
- Kind: reflect.Ptr,
- Children: []*Node{
- {
- Name: "Foo",
- Description: "foo description",
- FieldName: "Foo",
- Kind: reflect.Ptr,
- Tag: `description:"foo description" label:"allowEmpty"`,
- Children: []*Node{
- {
- Name: "Fii",
- Description: "fii description",
- FieldName: "Fii",
- Kind: reflect.Ptr,
- Tag: `description:"fii description" label:"allowEmpty"`,
- Children: []*Node{
- {
- Name: "Field",
- Description: "field description",
- FieldName: "Field",
- Value: "test",
- Kind: reflect.String,
- Tag: `description:"field description"`,
- },
- },
- },
- },
- },
- },
- },
- expected: []Flat{
- {
- Name: "foo",
- Description: "foo description",
- Default: "false",
- },
- {
- Name: "foo.fii",
- Description: "fii description",
- Default: "false",
- },
- {
- Name: "foo.fii.field",
- Description: "field description",
- Default: "test",
- },
- },
- },
- {
- desc: "map string field level 2",
- element: &struct {
- Foo *struct {
- Fii map[string]string `description:"fii description"`
- } `description:"foo description"`
- }{
- Foo: &struct {
- Fii map[string]string `description:"fii description"`
- }{
- Fii: map[string]string{
- MapNamePlaceholder: "",
- },
- },
- },
- node: &Node{
- Name: "traefik",
- FieldName: "",
- Kind: reflect.Ptr,
- Children: []*Node{
- {
- Name: "Foo",
- Description: "foo description",
- FieldName: "Foo",
- Kind: reflect.Ptr,
- Tag: `description:"foo description"`,
- Children: []*Node{
- {
- Name: "Fii",
- Description: "fii description",
- FieldName: "Fii",
- Kind: reflect.Map,
- Tag: `description:"fii description"`,
- Children: []*Node{
- {
- Name: "\u003cname\u003e",
- FieldName: "\u003cname\u003e",
- Kind: reflect.String,
- },
- },
- },
- },
- },
- },
- },
- expected: []Flat{
- {
- Name: "foo.fii.",
- Description: "fii description",
- Default: "",
- },
- },
- },
- {
- desc: "map string pointer field level 2",
- element: &struct {
- Foo *struct {
- Fii map[string]*string `description:"fii description"`
- } `description:"foo description"`
- }{
- Foo: &struct {
- Fii map[string]*string `description:"fii description"`
- }{
- Fii: map[string]*string{
- MapNamePlaceholder: func(v string) *string { return &v }(""),
- },
- },
- },
- node: &Node{
- Name: "traefik",
- FieldName: "",
- Kind: reflect.Ptr,
- Children: []*Node{
- {
- Name: "Foo",
- Description: "foo description",
- FieldName: "Foo",
- Kind: reflect.Ptr,
- Tag: `description:"foo description"`,
- Children: []*Node{
- {
- Name: "Fii",
- Description: "fii description",
- FieldName: "Fii",
- Kind: reflect.Map,
- Tag: `description:"fii description"`,
- Children: []*Node{
- {
- Name: "\u003cname\u003e",
- FieldName: "\u003cname\u003e",
- Kind: reflect.Ptr,
- },
- },
- },
- },
- },
- },
- },
- expected: []Flat{
- {
- Name: "foo.fii.",
- Description: "fii description",
- Default: "",
- },
- },
- },
- {
- desc: "map struct level 1",
- element: &struct {
- Foo map[string]struct {
- Field string `description:"field description"`
- Yo int `description:"yo description"`
- } `description:"foo description"`
- }{},
- node: &Node{
- Name: "traefik",
- FieldName: "",
- Kind: reflect.Ptr,
- Children: []*Node{
- {
- Name: "Foo",
- Description: "foo description",
- FieldName: "Foo",
- Kind: reflect.Map,
- Tag: `description:"foo description"`,
- Children: []*Node{
- {
- Name: "\u003cname\u003e",
- FieldName: "\u003cname\u003e",
- Kind: reflect.Struct,
- Children: []*Node{
- {
- Name: "Field",
- Description: "field description",
- FieldName: "Field",
- Kind: reflect.String,
- Tag: `description:"field description"`,
- },
- {
- Name: "Yo",
- Description: "yo description",
- FieldName: "Yo",
- Value: "0",
- Kind: reflect.Int,
- Tag: `description:"yo description"`,
- },
- },
- },
- },
- },
- },
- },
- expected: []Flat{
- {
- Name: "foo.",
- Description: "foo description",
- Default: "false",
- },
- {
- Name: "foo..field",
- Description: "field description",
- Default: "",
- },
- {
- Name: "foo..yo",
- Description: "yo description",
- Default: "0",
- },
- },
- },
- {
- desc: "map struct pointer level 1",
- element: &struct {
- Foo map[string]*struct {
- Field string `description:"field description"`
- Yo string `description:"yo description"`
- } `description:"foo description"`
- }{},
- node: &Node{
- Name: "traefik",
- FieldName: "",
- Kind: reflect.Ptr,
- Children: []*Node{
- {
- Name: "Foo",
- Description: "foo description",
- FieldName: "Foo",
- Kind: reflect.Map,
- Tag: `description:"foo description"`,
- Children: []*Node{
- {
- Name: "\u003cname\u003e",
- FieldName: "\u003cname\u003e",
- Kind: reflect.Ptr,
- Children: []*Node{
- {
- Name: "Field",
- Description: "field description",
- FieldName: "Field",
- Kind: reflect.String,
- Tag: `description:"field description"`,
- },
- {
- Name: "Yo",
- Description: "yo description",
- FieldName: "Yo",
- Kind: reflect.String,
- Tag: `description:"yo description"`,
- },
- },
- },
- },
- },
- },
- },
- expected: []Flat{
- {
- Name: "foo.",
- Description: "foo description",
- Default: "false",
- },
- {
- Name: "foo..field",
- Description: "field description",
- Default: "",
- },
- {
- Name: "foo..yo",
- Description: "yo description",
- Default: "",
- },
- },
- },
- {
- desc: "time duration field",
- element: &struct {
- Field time.Duration `description:"field description"`
- }{
- Field: 1 * time.Second,
- },
- node: &Node{
- Name: "traefik",
- FieldName: "",
- Kind: reflect.Ptr,
- Children: []*Node{
- {
- Name: "Field",
- Description: "field description",
- FieldName: "Field",
- Value: "1000000000",
- Kind: reflect.Int64,
- Tag: `description:"field description"`,
- },
- },
- },
- expected: []Flat{{
- Name: "field",
- Description: "field description",
- Default: "1s",
- }},
- },
- {
- desc: "time duration field map",
- element: &struct {
- Foo map[string]*struct {
- Field time.Duration `description:"field description"`
- } `description:"foo description"`
- }{
- Foo: map[string]*struct {
- Field time.Duration `description:"field description"`
- }{
- "": {
- Field: 0,
- },
- },
- },
- node: &Node{
- Name: "traefik",
- FieldName: "",
- Kind: reflect.Ptr,
- Children: []*Node{
- {
- Name: "Foo",
- Description: "foo description",
- FieldName: "Foo",
- Kind: reflect.Map,
- Tag: `description:"foo description"`,
- Children: []*Node{
- {
- Name: "\u003cname\u003e",
- FieldName: "\u003cname\u003e",
- Kind: reflect.Ptr,
- Children: []*Node{
- {
- Name: "Field",
- Description: "field description",
- FieldName: "Field",
- Value: "0",
- Kind: reflect.Int64,
- Tag: `description:"field description"`,
- },
- },
- },
- },
- },
- },
- },
- expected: []Flat{
- {
- Name: "foo.",
- Description: "foo description",
- Default: "false",
- },
- {
- Name: "foo..field",
- Description: "field description",
- Default: "0s",
- },
- },
- },
- {
- desc: "time duration field map 2",
- element: &struct {
- Foo map[string]*struct {
- Fii *struct {
- Field time.Duration `description:"field description"`
- }
- } `description:"foo description"`
- }{
- Foo: map[string]*struct {
- Fii *struct {
- Field time.Duration `description:"field description"`
- }
- }{
- "": {
- Fii: &struct {
- Field time.Duration `description:"field description"`
- }{
- Field: 0,
- },
- },
- },
- },
- node: &Node{
- Name: "traefik",
- FieldName: "",
- Kind: reflect.Ptr,
- Children: []*Node{
- {
- Name: "Foo",
- Description: "foo description",
- FieldName: "Foo",
- Kind: reflect.Map,
- Tag: `description:"foo description"`,
- Children: []*Node{
- {
- Name: "\u003cname\u003e",
- FieldName: "\u003cname\u003e",
- Kind: reflect.Ptr,
- Children: []*Node{
- {
- Name: "Fii",
- FieldName: "Fii",
- Kind: reflect.Ptr,
- Children: []*Node{
- {
- Name: "Field",
- Description: "field description",
- FieldName: "Field",
- Value: "0",
- Kind: reflect.Int64,
- Tag: `description:"field description"`,
- },
- },
- },
- },
- },
- },
- },
- },
- },
- expected: []Flat{
- {
- Name: "foo.",
- Description: "foo description",
- Default: "false",
- },
- {
- Name: "foo..fii.field",
- Description: "field description",
- Default: "0s",
- },
- },
- },
- {
- desc: "time duration field 2",
- element: &struct {
- Foo *struct {
- Field time.Duration `description:"field description"`
- }
- }{
- Foo: &struct {
- Field time.Duration `description:"field description"`
- }{
- Field: 1 * time.Second,
- },
- },
- node: &Node{
- Name: "traefik",
- FieldName: "",
- Kind: reflect.Ptr,
- Children: []*Node{
- {
- Name: "Foo",
- FieldName: "Foo",
- Kind: reflect.Ptr,
- Children: []*Node{
- {
- Name: "Field",
- Description: "field description",
- FieldName: "Field",
- Value: "1000000000",
- Kind: reflect.Int64,
- Tag: `description:"field description"`,
- },
- },
- },
- },
- },
- expected: []Flat{{
- Name: "foo.field",
- Description: "field description",
- Default: "1s",
- }},
- },
- {
- desc: "time duration field 3",
- element: &struct {
- Foo *struct {
- Fii *struct {
- Field time.Duration `description:"field description"`
- }
- }
- }{
- Foo: &struct {
- Fii *struct {
- Field time.Duration `description:"field description"`
- }
- }{
- Fii: &struct {
- Field time.Duration `description:"field description"`
- }{
- Field: 1 * time.Second,
- },
- },
- },
- node: &Node{
- Name: "traefik",
- FieldName: "",
- Kind: reflect.Ptr,
- Children: []*Node{
- {
- Name: "Foo",
- FieldName: "Foo",
- Kind: reflect.Ptr,
- Children: []*Node{
- {
- Name: "Fii",
- FieldName: "Fii",
- Kind: reflect.Ptr,
- Children: []*Node{
- {
- Name: "Field",
- Description: "field description",
- FieldName: "Field",
- Value: "1000000000",
- Kind: reflect.Int64,
- Tag: `description:"field description"`,
- },
- },
- },
- },
- },
- },
- },
- expected: []Flat{{
- Name: "foo.fii.field",
- Description: "field description",
- Default: "1s",
- }},
- },
- {
- desc: "time duration field",
- element: &struct {
- Field types.Duration `description:"field description"`
- }{
- Field: types.Duration(180 * time.Second),
- },
- node: &Node{
- Name: "traefik",
- FieldName: "",
- Kind: reflect.Struct,
- Children: []*Node{
- {
- Name: "Field",
- Description: "field description",
- FieldName: "Field",
- Value: "180000000000",
- Kind: reflect.Int64,
- Tag: `description:"field description"`,
- },
- },
- },
- expected: []Flat{{
- Name: "field",
- Description: "field description",
- Default: "180",
- }},
- },
- {
- desc: "slice of struct",
- element: &struct {
- Foo *struct {
- Fii []struct {
- Field1 string `description:"field1 description"`
- Field2 int `description:"field2 description"`
- } `description:"fii description"`
- } `description:"foo description"`
- }{
- Foo: &struct {
- Fii []struct {
- Field1 string `description:"field1 description"`
- Field2 int `description:"field2 description"`
- } `description:"fii description"`
- }{
- Fii: []struct {
- Field1 string `description:"field1 description"`
- Field2 int `description:"field2 description"`
- }{
- {
- Field1: "",
- Field2: 0,
- },
- },
- },
- },
- node: &Node{
- Name: "traefik",
- Kind: reflect.Struct,
- Children: []*Node{
- {Name: "Foo", Kind: reflect.Ptr, Description: "foo description", Children: []*Node{
- {Name: "Fii", Kind: reflect.Slice, Description: "fii description", Children: []*Node{
- {Name: "[0]", Kind: reflect.Struct, Children: []*Node{
- {Name: "Field1", Value: "", Kind: reflect.String, Description: "field1 description"},
- {Name: "Field2", Value: "0", Kind: reflect.Int, Description: "field2 description"},
- }},
- }},
- }},
- },
- },
- expected: []Flat{
- {
- Name: "foo.fii",
- Description: "fii description",
- Default: "",
- },
- {
- Name: "foo.fii[0].field1",
- Description: "field1 description",
- Default: "",
- },
- {
- Name: "foo.fii[0].field2",
- Description: "field2 description",
- Default: "0",
- },
- },
- },
- // Skipped: because realistically not needed in Traefik for now.
- // {
- // desc: "map of map field level 2",
- // element: &struct {
- // Foo *struct {
- // Fii map[string]map[string]string `description:"fii description"`
- // } `description:"foo description"`
- // }{
- // Foo: &struct {
- // Fii map[string]map[string]string `description:"fii description"`
- // }{
- // Fii: map[string]map[string]string{
- // MapNamePlaceholder: {
- // MapNamePlaceholder: "test",
- // },
- // },
- // },
- // },
- // expected: `XXX`,
- // },
- }
-
- for _, test := range testCases {
- test := test
- t.Run(test.desc, func(t *testing.T) {
- t.Parallel()
-
- var opts FlatOpts
- if test.opts == nil {
- opts = FlatOpts{Separator: ".", SkipRoot: true, TagName: TagLabel}
- } else {
- opts = *test.opts
- }
-
- entries, err := EncodeToFlat(test.element, test.node, opts)
- require.NoError(t, err)
-
- assert.Equal(t, test.expected, entries)
- })
- }
-}
diff --git a/pkg/config/parser/labels_decode.go b/pkg/config/parser/labels_decode.go
deleted file mode 100644
index 4ecc5be46..000000000
--- a/pkg/config/parser/labels_decode.go
+++ /dev/null
@@ -1,97 +0,0 @@
-package parser
-
-import (
- "fmt"
- "sort"
- "strings"
-)
-
-// DecodeToNode converts the labels to a tree of nodes.
-// If any filters are present, labels which do not match the filters are skipped.
-func DecodeToNode(labels map[string]string, rootName string, filters ...string) (*Node, error) {
- sortedKeys := sortKeys(labels, filters)
-
- var node *Node
- for i, key := range sortedKeys {
- split := strings.Split(key, ".")
-
- if split[0] != rootName {
- return nil, fmt.Errorf("invalid label root %s", split[0])
- }
-
- var parts []string
- for _, v := range split {
- if v == "" {
- return nil, fmt.Errorf("invalid element: %s", key)
- }
-
- if v[0] == '[' {
- return nil, fmt.Errorf("invalid leading character '[' in field name (bracket is a slice delimiter): %s", v)
- }
-
- if strings.HasSuffix(v, "]") && v[0] != '[' {
- indexLeft := strings.Index(v, "[")
- parts = append(parts, v[:indexLeft], v[indexLeft:])
- } else {
- parts = append(parts, v)
- }
- }
-
- if i == 0 {
- node = &Node{}
- }
- decodeToNode(node, parts, labels[key])
- }
-
- return node, nil
-}
-
-func decodeToNode(root *Node, path []string, value string) {
- if len(root.Name) == 0 {
- root.Name = path[0]
- }
-
- // it's a leaf or not -> children
- if len(path) > 1 {
- if n := containsNode(root.Children, path[1]); n != nil {
- // the child already exists
- decodeToNode(n, path[1:], value)
- } else {
- // new child
- child := &Node{Name: path[1]}
- decodeToNode(child, path[1:], value)
- root.Children = append(root.Children, child)
- }
- } else {
- root.Value = value
- }
-}
-
-func containsNode(nodes []*Node, name string) *Node {
- for _, n := range nodes {
- if strings.EqualFold(name, n.Name) {
- return n
- }
- }
- return nil
-}
-
-func sortKeys(labels map[string]string, filters []string) []string {
- var sortedKeys []string
- for key := range labels {
- if len(filters) == 0 {
- sortedKeys = append(sortedKeys, key)
- continue
- }
-
- for _, filter := range filters {
- if len(key) >= len(filter) && strings.EqualFold(key[:len(filter)], filter) {
- sortedKeys = append(sortedKeys, key)
- continue
- }
- }
- }
- sort.Strings(sortedKeys)
-
- return sortedKeys
-}
diff --git a/pkg/config/parser/labels_decode_test.go b/pkg/config/parser/labels_decode_test.go
deleted file mode 100644
index b742a615d..000000000
--- a/pkg/config/parser/labels_decode_test.go
+++ /dev/null
@@ -1,261 +0,0 @@
-package parser
-
-import (
- "encoding/json"
- "fmt"
- "testing"
-
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
-)
-
-func TestDecodeToNode(t *testing.T) {
- type expected struct {
- error bool
- node *Node
- }
-
- testCases := []struct {
- desc string
- in map[string]string
- filters []string
- expected expected
- }{
- {
- desc: "no label",
- in: map[string]string{},
- expected: expected{node: nil},
- },
- {
- desc: "invalid label, ending by a dot",
- in: map[string]string{
- "traefik.http.": "bar",
- },
- expected: expected{
- error: true,
- },
- },
- {
- desc: "level 1",
- in: map[string]string{
- "traefik.foo": "bar",
- },
- expected: expected{node: &Node{
- Name: "traefik",
- Children: []*Node{
- {Name: "foo", Value: "bar"},
- },
- }},
- },
- {
- desc: "level 1 empty value",
- in: map[string]string{
- "traefik.foo": "",
- },
- expected: expected{node: &Node{
- Name: "traefik",
- Children: []*Node{
- {Name: "foo", Value: ""},
- },
- }},
- },
- {
- desc: "level 2",
- in: map[string]string{
- "traefik.foo.bar": "bar",
- },
- expected: expected{node: &Node{
- Name: "traefik",
- Children: []*Node{{
- Name: "foo",
- Children: []*Node{
- {Name: "bar", Value: "bar"},
- },
- }},
- }},
- },
- {
- desc: "several entries, level 0",
- in: map[string]string{
- "traefik": "bar",
- "traefic": "bur",
- },
- expected: expected{error: true},
- },
- {
- desc: "several entries, prefix filter",
- in: map[string]string{
- "traefik.foo": "bar",
- "traefik.fii": "bir",
- },
- filters: []string{"traefik.Foo"},
- expected: expected{node: &Node{
- Name: "traefik",
- Children: []*Node{
- {Name: "foo", Value: "bar"},
- },
- }},
- },
- {
- desc: "several entries, level 1",
- in: map[string]string{
- "traefik.foo": "bar",
- "traefik.fii": "bur",
- },
- expected: expected{node: &Node{
- Name: "traefik",
- Children: []*Node{
- {Name: "fii", Value: "bur"},
- {Name: "foo", Value: "bar"},
- },
- }},
- },
- {
- desc: "several entries, level 2",
- in: map[string]string{
- "traefik.foo.aaa": "bar",
- "traefik.foo.bbb": "bur",
- },
- expected: expected{node: &Node{
- Name: "traefik",
- Children: []*Node{
- {Name: "foo", Children: []*Node{
- {Name: "aaa", Value: "bar"},
- {Name: "bbb", Value: "bur"},
- }},
- },
- }},
- },
- {
- desc: "several entries, level 2, case insensitive",
- in: map[string]string{
- "traefik.foo.aaa": "bar",
- "traefik.Foo.bbb": "bur",
- },
- expected: expected{node: &Node{
- Name: "traefik",
- Children: []*Node{
- {Name: "Foo", Children: []*Node{
- {Name: "bbb", Value: "bur"},
- {Name: "aaa", Value: "bar"},
- }},
- },
- }},
- },
- {
- desc: "several entries, level 2, 3 children",
- in: map[string]string{
- "traefik.foo.aaa": "bar",
- "traefik.foo.bbb": "bur",
- "traefik.foo.ccc": "bir",
- },
- expected: expected{node: &Node{
- Name: "traefik",
- Children: []*Node{
- {Name: "foo", Children: []*Node{
- {Name: "aaa", Value: "bar"},
- {Name: "bbb", Value: "bur"},
- {Name: "ccc", Value: "bir"},
- }},
- },
- }},
- },
- {
- desc: "several entries, level 3",
- in: map[string]string{
- "traefik.foo.bar.aaa": "bar",
- "traefik.foo.bar.bbb": "bur",
- },
- expected: expected{node: &Node{
- Name: "traefik",
- Children: []*Node{
- {Name: "foo", Children: []*Node{
- {Name: "bar", Children: []*Node{
- {Name: "aaa", Value: "bar"},
- {Name: "bbb", Value: "bur"},
- }},
- }},
- },
- }},
- },
- {
- desc: "several entries, level 3, 2 children level 1",
- in: map[string]string{
- "traefik.foo.bar.aaa": "bar",
- "traefik.foo.bar.bbb": "bur",
- "traefik.bar.foo.bbb": "bir",
- },
- expected: expected{node: &Node{
- Name: "traefik",
- Children: []*Node{
- {Name: "bar", Children: []*Node{
- {Name: "foo", Children: []*Node{
- {Name: "bbb", Value: "bir"},
- }},
- }},
- {Name: "foo", Children: []*Node{
- {Name: "bar", Children: []*Node{
- {Name: "aaa", Value: "bar"},
- {Name: "bbb", Value: "bur"},
- }},
- }},
- },
- }},
- },
- {
- desc: "several entries, slice syntax",
- in: map[string]string{
- "traefik.foo[0].aaa": "bar0",
- "traefik.foo[0].bbb": "bur0",
- "traefik.foo[1].aaa": "bar1",
- "traefik.foo[1].bbb": "bur1",
- },
- expected: expected{node: &Node{
- Name: "traefik",
- Children: []*Node{
- {Name: "foo", Children: []*Node{
- {Name: "[0]", Children: []*Node{
- {Name: "aaa", Value: "bar0"},
- {Name: "bbb", Value: "bur0"},
- }},
- {Name: "[1]", Children: []*Node{
- {Name: "aaa", Value: "bar1"},
- {Name: "bbb", Value: "bur1"},
- }},
- }},
- },
- }},
- },
- {
- desc: "several entries, invalid slice syntax",
- in: map[string]string{
- "traefik.foo.[0].aaa": "bar0",
- "traefik.foo.[0].bbb": "bur0",
- "traefik.foo.[1].aaa": "bar1",
- "traefik.foo.[1].bbb": "bur1",
- },
- expected: expected{error: true},
- },
- }
-
- for _, test := range testCases {
- test := test
- t.Run(test.desc, func(t *testing.T) {
- t.Parallel()
-
- out, err := DecodeToNode(test.in, DefaultRootName, test.filters...)
-
- if test.expected.error {
- require.Error(t, err)
- } else {
- require.NoError(t, err)
-
- if !assert.Equal(t, test.expected.node, out) {
- bytes, err := json.MarshalIndent(out, "", " ")
- require.NoError(t, err)
- fmt.Println(string(bytes))
- }
- }
- })
- }
-}
diff --git a/pkg/config/parser/labels_encode.go b/pkg/config/parser/labels_encode.go
deleted file mode 100644
index 9e9199679..000000000
--- a/pkg/config/parser/labels_encode.go
+++ /dev/null
@@ -1,67 +0,0 @@
-package parser
-
-import (
- "fmt"
- "reflect"
-)
-
-// EncodeNode Converts a node to labels.
-// nodes -> labels.
-func EncodeNode(node *Node) map[string]string {
- labels := make(map[string]string)
- encodeNode(labels, node.Name, node)
- return labels
-}
-
-func encodeNode(labels map[string]string, root string, node *Node) {
- for _, child := range node.Children {
- if child.Disabled {
- continue
- }
-
- var sep string
- if child.Name[0] != '[' {
- sep = "."
- }
-
- 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 {
- labels[childName] = child.Value
- }
- }
-}
-
-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)
- }
- }
- }
-}
diff --git a/pkg/config/parser/labels_encode_test.go b/pkg/config/parser/labels_encode_test.go
deleted file mode 100644
index 3c6b6184d..000000000
--- a/pkg/config/parser/labels_encode_test.go
+++ /dev/null
@@ -1,234 +0,0 @@
-package parser
-
-import (
- "testing"
-
- "github.com/stretchr/testify/assert"
-)
-
-func TestEncodeNode(t *testing.T) {
- testCases := []struct {
- desc string
- node *Node
- expected map[string]string
- }{
- {
- desc: "1 label",
- node: &Node{
- Name: "traefik",
- Children: []*Node{
- {Name: "aaa", Value: "bar"},
- },
- },
- expected: map[string]string{
- "traefik.aaa": "bar",
- },
- },
- {
- desc: "2 labels",
- node: &Node{
- Name: "traefik",
- Children: []*Node{
- {Name: "aaa", Value: "bar"},
- {Name: "bbb", Value: "bur"},
- },
- },
- expected: map[string]string{
- "traefik.aaa": "bar",
- "traefik.bbb": "bur",
- },
- },
- {
- desc: "2 labels, 1 disabled",
- node: &Node{
- Name: "traefik",
- Children: []*Node{
- {Name: "aaa", Value: "bar"},
- {Name: "bbb", Value: "bur", Disabled: true},
- },
- },
- expected: map[string]string{
- "traefik.aaa": "bar",
- },
- },
- {
- desc: "2 levels",
- node: &Node{
- Name: "traefik",
- Children: []*Node{
- {Name: "foo", Children: []*Node{
- {Name: "aaa", Value: "bar"},
- }},
- },
- },
- expected: map[string]string{
- "traefik.foo.aaa": "bar",
- },
- },
- {
- desc: "3 levels",
- node: &Node{
- Name: "traefik",
- Children: []*Node{
- {Name: "foo", Children: []*Node{
- {Name: "bar", Children: []*Node{
- {Name: "aaa", Value: "bar"},
- }},
- }},
- },
- },
- expected: map[string]string{
- "traefik.foo.bar.aaa": "bar",
- },
- },
- {
- desc: "2 levels, same root",
- node: &Node{
- Name: "traefik",
- Children: []*Node{
- {Name: "foo", Children: []*Node{
- {Name: "bar", Children: []*Node{
- {Name: "aaa", Value: "bar"},
- {Name: "bbb", Value: "bur"},
- }},
- }},
- },
- },
- expected: map[string]string{
- "traefik.foo.bar.aaa": "bar",
- "traefik.foo.bar.bbb": "bur",
- },
- },
- {
- desc: "several levels, different root",
- node: &Node{
- Name: "traefik",
- Children: []*Node{
- {Name: "bar", Children: []*Node{
- {Name: "ccc", Value: "bir"},
- }},
- {Name: "foo", Children: []*Node{
- {Name: "bar", Children: []*Node{
- {Name: "aaa", Value: "bar"},
- }},
- }},
- },
- },
- expected: map[string]string{
- "traefik.foo.bar.aaa": "bar",
- "traefik.bar.ccc": "bir",
- },
- },
- {
- desc: "multiple labels, multiple levels",
- node: &Node{
- Name: "traefik",
- Children: []*Node{
- {Name: "bar", Children: []*Node{
- {Name: "ccc", Value: "bir"},
- }},
- {Name: "foo", Children: []*Node{
- {Name: "bar", Children: []*Node{
- {Name: "aaa", Value: "bar"},
- {Name: "bbb", Value: "bur"},
- }},
- }},
- },
- },
- expected: map[string]string{
- "traefik.foo.bar.aaa": "bar",
- "traefik.foo.bar.bbb": "bur",
- "traefik.bar.ccc": "bir",
- },
- },
- {
- desc: "slice of struct syntax",
- node: &Node{
- Name: "traefik",
- Children: []*Node{
- {Name: "foo", Children: []*Node{
- {Name: "[0]", Children: []*Node{
- {Name: "aaa", Value: "bar0"},
- {Name: "bbb", Value: "bur0"},
- }},
- {Name: "[1]", Children: []*Node{
- {Name: "aaa", Value: "bar1"},
- {Name: "bbb", Value: "bur1"},
- }},
- }},
- },
- },
- expected: map[string]string{
- "traefik.foo[0].aaa": "bar0",
- "traefik.foo[0].bbb": "bur0",
- "traefik.foo[1].aaa": "bar1",
- "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 {
- test := test
- t.Run(test.desc, func(t *testing.T) {
- t.Parallel()
-
- labels := EncodeNode(test.node)
-
- assert.Equal(t, test.expected, labels)
- })
- }
-}
diff --git a/pkg/config/parser/node.go b/pkg/config/parser/node.go
deleted file mode 100644
index 8e3b58f09..000000000
--- a/pkg/config/parser/node.go
+++ /dev/null
@@ -1,22 +0,0 @@
-package parser
-
-import "reflect"
-
-// DefaultRootName is the default name of the root node and the prefix of element name from the resources.
-const DefaultRootName = "traefik"
-
-// MapNamePlaceholder is the placeholder for the map name.
-const MapNamePlaceholder = ""
-
-// Node is a label node.
-type Node struct {
- Name string `json:"name"`
- 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"`
- Children []*Node `json:"children,omitempty"`
-}
diff --git a/pkg/config/parser/nodes_metadata.go b/pkg/config/parser/nodes_metadata.go
deleted file mode 100644
index a5a2b3f08..000000000
--- a/pkg/config/parser/nodes_metadata.go
+++ /dev/null
@@ -1,278 +0,0 @@
-package parser
-
-import (
- "errors"
- "fmt"
- "reflect"
- "strings"
-)
-
-// MetadataOpts Options for the metadata.
-type MetadataOpts struct {
- TagName string
- AllowSliceAsStruct bool
-}
-
-// AddMetadata adds metadata such as type, inferred from element, to a node.
-func AddMetadata(element interface{}, node *Node, opts MetadataOpts) error {
- return metadata{MetadataOpts: opts}.Add(element, node)
-}
-
-type metadata struct {
- MetadataOpts
-}
-
-// Add adds metadata such as type, inferred from element, to a node.
-func (m metadata) Add(element interface{}, node *Node) error {
- if node == nil {
- return nil
- }
-
- if len(node.Children) == 0 {
- return fmt.Errorf("invalid node %s: no child", node.Name)
- }
-
- if element == nil {
- return errors.New("nil structure")
- }
-
- rootType := reflect.TypeOf(element)
- node.Kind = rootType.Kind()
-
- return m.browseChildren(rootType, node)
-}
-
-func (m metadata) browseChildren(fType reflect.Type, node *Node) error {
- for _, child := range node.Children {
- if err := m.add(fType, child); err != nil {
- return err
- }
- }
- return nil
-}
-
-func (m metadata) add(rootType reflect.Type, node *Node) error {
- rType := rootType
- if rootType.Kind() == reflect.Ptr {
- 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
- }
-
- if err = isSupportedType(field); err != nil {
- return err
- }
-
- fType := field.Type
- node.Kind = fType.Kind()
- node.Tag = field.Tag
-
- if fType.Kind() == reflect.Struct || fType.Kind() == reflect.Ptr && fType.Elem().Kind() == reflect.Struct ||
- fType.Kind() == reflect.Map {
- if len(node.Children) == 0 && field.Tag.Get(m.TagName) != TagLabelAllowEmpty {
- return fmt.Errorf("%s cannot be a standalone element (type %s)", node.Name, fType)
- }
-
- node.Disabled = len(node.Value) > 0 && !strings.EqualFold(node.Value, "true") && field.Tag.Get(m.TagName) == TagLabelAllowEmpty
- }
-
- if len(node.Children) == 0 {
- return nil
- }
-
- if fType.Kind() == reflect.Struct || fType.Kind() == reflect.Ptr && fType.Elem().Kind() == reflect.Struct {
- return m.browseChildren(fType, node)
- }
-
- 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()
- child.Kind = elem.Kind()
-
- if elem.Kind() == reflect.Map || elem.Kind() == reflect.Struct ||
- (elem.Kind() == reflect.Ptr && elem.Elem().Kind() == reflect.Struct) {
- if err = m.browseChildren(elem, child); err != nil {
- return err
- }
- }
- }
- return nil
- }
-
- if fType.Kind() == reflect.Slice {
- if m.AllowSliceAsStruct && field.Tag.Get(TagLabelSliceAsStruct) != "" {
- return m.browseChildren(fType.Elem(), node)
- }
-
- for _, ch := range node.Children {
- ch.Kind = fType.Elem().Kind()
- if err = m.browseChildren(fType.Elem(), ch); err != nil {
- return err
- }
- }
- return nil
- }
-
- return fmt.Errorf("invalid node %s: %v", node.Name, fType.Kind())
-}
-
-func (m metadata) findTypedField(rType reflect.Type, node *Node) (reflect.StructField, error) {
- for i := 0; i < rType.NumField(); i++ {
- cField := rType.Field(i)
-
- fieldName := cField.Tag.Get(TagLabelSliceAsStruct)
- if !m.AllowSliceAsStruct || len(fieldName) == 0 {
- fieldName = cField.Name
- }
-
- if IsExported(cField) {
- if cField.Anonymous {
- if cField.Type.Kind() == reflect.Struct {
- structField, err := m.findTypedField(cField.Type, node)
- if err != nil {
- continue
- }
- return structField, nil
- }
- }
-
- if strings.EqualFold(fieldName, node.Name) {
- node.FieldName = cField.Name
- return cField, nil
- }
- }
- }
-
- return reflect.StructField{}, fmt.Errorf("field not found, node: %s", node.Name)
-}
-
-// IsExported reports whether f is exported.
-// https://golang.org/pkg/reflect/#StructField
-func IsExported(f reflect.StructField) bool {
- return f.PkgPath == ""
-}
-
-func isSupportedType(field reflect.StructField) error {
- fType := field.Type
-
- if fType.Kind() == reflect.Slice {
- switch fType.Elem().Kind() {
- case reflect.String,
- reflect.Bool,
- reflect.Int,
- reflect.Int8,
- reflect.Int16,
- reflect.Int32,
- reflect.Int64,
- reflect.Uint,
- reflect.Uint8,
- reflect.Uint16,
- reflect.Uint32,
- reflect.Uint64,
- reflect.Uintptr,
- reflect.Float32,
- reflect.Float64,
- reflect.Struct,
- reflect.Ptr:
- return nil
- default:
- return fmt.Errorf("unsupported slice type: %v", fType)
- }
- }
-
- if fType.Kind() == reflect.Map && fType.Key().Kind() != reflect.String {
- return fmt.Errorf("unsupported map key type: %v", fType.Key())
- }
-
- if fType.Kind() == reflect.Func {
- return fmt.Errorf("unsupported type: %v", fType)
- }
-
- 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] == ']'
-}
diff --git a/pkg/config/parser/nodes_metadata_test.go b/pkg/config/parser/nodes_metadata_test.go
deleted file mode 100644
index 642cf3837..000000000
--- a/pkg/config/parser/nodes_metadata_test.go
+++ /dev/null
@@ -1,1168 +0,0 @@
-package parser
-
-import (
- "encoding/json"
- "fmt"
- "reflect"
- "testing"
-
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
-)
-
-func TestAddMetadata(t *testing.T) {
- type expected struct {
- node *Node
- error bool
- }
-
- type interf interface{}
-
- testCases := []struct {
- desc string
- tree *Node
- structure interface{}
- expected expected
- }{
- {
- desc: "Node Nil",
- tree: nil,
- structure: nil,
- expected: expected{node: nil},
- },
- {
- desc: "Empty Node",
- tree: &Node{},
- structure: nil,
- expected: expected{error: true},
- },
- {
- desc: "Nil structure",
- tree: &Node{
- Name: "traefik",
- Children: []*Node{
- {Name: "Foo", Value: "bar"},
- },
- },
- structure: nil,
- expected: expected{error: true},
- },
- {
- desc: "level 0",
- tree: &Node{Name: "traefik", Value: "bar"},
- expected: expected{error: true},
- },
- {
- desc: "level 1",
- tree: &Node{
- Name: "traefik",
- Children: []*Node{
- {Name: "Foo", FieldName: "Foo", Value: "bar"},
- },
- },
- structure: struct{ Foo string }{},
- expected: expected{
- node: &Node{
- Name: "traefik",
- Kind: reflect.Struct,
- Children: []*Node{
- {Name: "Foo", FieldName: "Foo", Value: "bar", Kind: reflect.String},
- },
- },
- },
- },
- {
- desc: "level 1, pointer",
- tree: &Node{
- Name: "traefik",
- Children: []*Node{
- {Name: "Foo", Value: "bar"},
- },
- },
- structure: &struct{ Foo string }{},
- expected: expected{
- node: &Node{
- Name: "traefik",
- Kind: reflect.Ptr,
- Children: []*Node{
- {Name: "Foo", FieldName: "Foo", Value: "bar", Kind: reflect.String},
- },
- },
- },
- },
- {
- desc: "level 1, slice",
- tree: &Node{
- Name: "traefik",
- Children: []*Node{
- {Name: "Foo", Value: "bar,bur"},
- },
- },
- structure: struct{ Foo []string }{},
- expected: expected{
- node: &Node{
- Name: "traefik",
- Kind: reflect.Struct,
- Children: []*Node{
- {Name: "Foo", FieldName: "Foo", Value: "bar,bur", Kind: reflect.Slice},
- },
- },
- },
- },
- {
- desc: "level 1, interface",
- tree: &Node{
- Name: "traefik",
- Children: []*Node{
- {Name: "Foo", Value: "", Children: []*Node{
- {Name: "Fii", Value: "hii"},
- }},
- },
- },
- structure: struct{ Foo interf }{},
- expected: expected{error: true},
- },
- {
- desc: "level 1, map string",
- tree: &Node{
- Name: "traefik",
- Children: []*Node{
- {Name: "Foo", Children: []*Node{
- {Name: "name1", Value: "bar"},
- {Name: "name2", Value: "bur"},
- }},
- },
- },
- structure: struct{ Foo map[string]string }{},
- expected: expected{
- node: &Node{
- Name: "traefik",
- Kind: reflect.Struct,
- Children: []*Node{
- {Name: "Foo", FieldName: "Foo", Kind: reflect.Map, Children: []*Node{
- {Name: "name1", Value: "bar", Kind: reflect.String},
- {Name: "name2", Value: "bur", Kind: reflect.String},
- }},
- },
- },
- },
- },
- {
- desc: "level 1, map struct",
- tree: &Node{
- Name: "traefik",
- Children: []*Node{
- {Name: "Foo", Children: []*Node{
- {Name: "name1", Children: []*Node{
- {Name: "Fii", Value: "bar"},
- }},
- }},
- },
- },
- structure: struct {
- Foo map[string]struct{ Fii string }
- }{},
- expected: expected{
- node: &Node{
- Name: "traefik",
- Kind: reflect.Struct,
- Children: []*Node{
- {Name: "Foo", FieldName: "Foo", Kind: reflect.Map, Children: []*Node{
- {Name: "name1", Kind: reflect.Struct, Children: []*Node{
- {Name: "Fii", FieldName: "Fii", Value: "bar", Kind: reflect.String},
- }},
- }},
- },
- },
- },
- },
- {
- desc: "level 1, map int as key",
- tree: &Node{
- Name: "traefik",
- Children: []*Node{
- {Name: "Foo", Children: []*Node{
- {Name: "name1", Children: []*Node{
- {Name: "Fii", Value: "bar"},
- }},
- }},
- },
- },
- structure: struct {
- Foo map[int]struct{ Fii string }
- }{},
- expected: expected{error: true},
- },
- {
- desc: "level 1, int pointer",
- tree: &Node{
- Name: "traefik",
- Children: []*Node{
- {Name: "Foo", Value: "0"},
- },
- },
- structure: struct {
- Foo *int
- }{},
- expected: expected{
- node: &Node{
- Name: "traefik",
- Kind: reflect.Struct,
- Children: []*Node{
- {Name: "Foo", FieldName: "Foo", Value: "0", Kind: reflect.Ptr},
- },
- },
- },
- },
- {
- desc: "level 1, bool pointer",
- tree: &Node{
- Name: "traefik",
- Children: []*Node{
- {Name: "Foo", Value: "0"},
- },
- },
- structure: struct {
- Foo *bool
- }{},
- expected: expected{
- node: &Node{
- Name: "traefik",
- Kind: reflect.Struct,
- Children: []*Node{
- {Name: "Foo", FieldName: "Foo", Value: "0", Kind: reflect.Ptr},
- },
- },
- },
- },
- {
- desc: "level 1, string pointer",
- tree: &Node{
- Name: "traefik",
- Children: []*Node{
- {Name: "Foo", Value: "0"},
- },
- },
- structure: struct {
- Foo *string
- }{},
- expected: expected{
- node: &Node{
- Name: "traefik",
- Kind: reflect.Struct,
- Children: []*Node{
- {Name: "Foo", FieldName: "Foo", Value: "0", Kind: reflect.Ptr},
- },
- },
- },
- },
- {
- desc: "level 1, 2 children with different types",
- tree: &Node{
- Name: "traefik",
- Children: []*Node{
- {Name: "Foo", Value: "bar"},
- {Name: "Fii", Value: "1"},
- },
- },
- structure: struct {
- Foo string
- Fii int
- }{},
- expected: expected{
- node: &Node{
- Name: "traefik",
- Kind: reflect.Struct,
- Children: []*Node{
- {Name: "Foo", FieldName: "Foo", Value: "bar", Kind: reflect.String},
- {Name: "Fii", FieldName: "Fii", Value: "1", Kind: reflect.Int},
- },
- },
- },
- },
- {
- desc: "level 1, use exported instead of unexported",
- tree: &Node{
- Name: "traefik",
- Children: []*Node{
- {Name: "foo", Value: "bar"},
- },
- },
- structure: struct {
- foo int
- Foo string
- }{},
- expected: expected{
- node: &Node{
- Name: "traefik",
- Kind: reflect.Struct,
- Children: []*Node{
- {Name: "foo", Value: "bar", FieldName: "Foo", Kind: reflect.String},
- },
- },
- },
- },
- {
- desc: "level 1, unexported",
- tree: &Node{
- Name: "traefik",
- Children: []*Node{
- {Name: "foo", Value: "bar"},
- },
- },
- structure: struct {
- foo string
- }{},
- expected: expected{error: true},
- },
- {
- desc: "level 1, 3 children with different types",
- tree: &Node{
- Name: "traefik",
- Children: []*Node{
- {Name: "Foo", Value: "bar"},
- {Name: "Fii", Value: "1"},
- {Name: "Fuu", Value: "true"},
- },
- },
- structure: struct {
- Foo string
- Fii int
- Fuu bool
- }{},
- expected: expected{
- node: &Node{
- Name: "traefik",
- Kind: reflect.Struct,
- Children: []*Node{
- {Name: "Foo", FieldName: "Foo", Value: "bar", Kind: reflect.String},
- {Name: "Fii", FieldName: "Fii", Value: "1", Kind: reflect.Int},
- {Name: "Fuu", FieldName: "Fuu", Value: "true", Kind: reflect.Bool},
- },
- },
- },
- },
- {
- desc: "level 2",
- tree: &Node{
- Name: "traefik",
- Children: []*Node{
- {Name: "Foo", Children: []*Node{
- {Name: "Bar", Value: "bir"},
- }},
- },
- },
- structure: struct {
- Foo struct {
- Bar string
- }
- }{
- Foo: struct {
- Bar string
- }{},
- },
- expected: expected{
- node: &Node{
- Name: "traefik",
- Kind: reflect.Struct,
- Children: []*Node{
- {Name: "Foo", FieldName: "Foo", Kind: reflect.Struct, Children: []*Node{
- {Name: "Bar", FieldName: "Bar", Value: "bir", Kind: reflect.String},
- }},
- },
- },
- },
- },
- {
- desc: "level 2, struct without children",
- tree: &Node{
- Name: "traefik",
- Children: []*Node{
- {Name: "Foo"},
- },
- },
- structure: struct {
- Foo struct {
- Bar string
- }
- }{
- Foo: struct {
- Bar string
- }{},
- },
- expected: expected{error: true},
- },
- {
- desc: "level 2, slice-as-struct",
- tree: &Node{
- Name: "traefik",
- Children: []*Node{
- {Name: "Fii", Children: []*Node{
- {Name: "bar", Value: "haa"},
- {Name: "bir", Value: "hii"},
- }},
- },
- },
- structure: struct {
- Foo []struct {
- Bar string
- Bir string
- } `label-slice-as-struct:"Fii"`
- }{
- Foo: []struct {
- Bar string
- Bir string
- }{},
- },
- expected: expected{node: &Node{
- Name: "traefik",
- Kind: reflect.Struct,
- Children: []*Node{
- {
- Name: "Fii",
- FieldName: "Foo",
- Kind: reflect.Slice,
- Tag: reflect.StructTag(`label-slice-as-struct:"Fii"`),
- Children: []*Node{
- {Name: "bar", FieldName: "Bar", Kind: reflect.String, Value: "haa"},
- {Name: "bir", FieldName: "Bir", Kind: reflect.String, Value: "hii"},
- },
- },
- },
- }},
- },
- {
- desc: "level 2, slice-as-struct without children",
- tree: &Node{
- Name: "traefik",
- Children: []*Node{
- {Name: "Fii"},
- },
- },
- structure: struct {
- Foo []struct {
- Bar string
- Bir string
- } `label-slice-as-struct:"Fii"`
- }{
- Foo: []struct {
- Bar string
- Bir string
- }{},
- },
- expected: expected{node: &Node{
- Name: "traefik",
- Kind: reflect.Struct,
- Children: []*Node{
- {
- Name: "Fii",
- FieldName: "Foo",
- Kind: reflect.Slice,
- Tag: reflect.StructTag(`label-slice-as-struct:"Fii"`),
- },
- },
- }},
- },
- {
- desc: "level 2, struct with allowEmpty, value true",
- tree: &Node{
- Name: "traefik",
- Children: []*Node{
- {Name: "Foo", Value: "true"},
- },
- },
- structure: struct {
- Foo struct {
- Bar string
- } `label:"allowEmpty"`
- }{
- Foo: struct {
- Bar string
- }{},
- },
- expected: expected{
- node: &Node{
- Name: "traefik",
- Kind: reflect.Struct,
- Children: []*Node{
- {Name: "Foo", FieldName: "Foo", Value: "true", Kind: reflect.Struct, Tag: reflect.StructTag(`label:"allowEmpty"`)},
- },
- },
- },
- },
- {
- desc: "level 2, struct with allowEmpty, value true with case variation",
- tree: &Node{
- Name: "traefik",
- Children: []*Node{
- {Name: "Foo", Value: "TruE"},
- },
- },
- structure: struct {
- Foo struct {
- Bar string
- } `label:"allowEmpty"`
- }{
- Foo: struct {
- Bar string
- }{},
- },
- expected: expected{
- node: &Node{
- Name: "traefik",
- Kind: reflect.Struct,
- Children: []*Node{
- {Name: "Foo", FieldName: "Foo", Value: "TruE", Kind: reflect.Struct, Tag: reflect.StructTag(`label:"allowEmpty"`)},
- },
- },
- },
- },
- {
- desc: "level 2, struct with allowEmpty, value false",
- tree: &Node{
- Name: "traefik",
- Children: []*Node{
- {Name: "Foo", Value: "false"},
- },
- },
- structure: struct {
- Foo struct {
- Bar string
- } `label:"allowEmpty"`
- }{
- Foo: struct {
- Bar string
- }{},
- },
- expected: expected{
- node: &Node{
- Name: "traefik",
- Kind: reflect.Struct,
- Children: []*Node{
- {Name: "Foo", FieldName: "Foo", Value: "false", Disabled: true, Kind: reflect.Struct, Tag: reflect.StructTag(`label:"allowEmpty"`)},
- },
- },
- },
- },
- {
- desc: "level 2, struct with allowEmpty with children, value false",
- tree: &Node{
- Name: "traefik",
- Children: []*Node{
- {Name: "Foo", Value: "false", Children: []*Node{
- {Name: "Bar", Value: "hii"},
- }},
- },
- },
- structure: struct {
- Foo struct {
- Bar string
- } `label:"allowEmpty"`
- }{
- Foo: struct {
- Bar string
- }{},
- },
- expected: expected{
- node: &Node{
- Name: "traefik",
- Kind: reflect.Struct,
- Children: []*Node{
- {
- Name: "Foo",
- FieldName: "Foo",
- Value: "false",
- Disabled: true,
- Kind: reflect.Struct,
- Tag: reflect.StructTag(`label:"allowEmpty"`),
- Children: []*Node{
- {Name: "Bar", FieldName: "Bar", Value: "hii", Kind: reflect.String},
- },
- },
- },
- },
- },
- },
- {
- desc: "level 2, struct pointer without children",
- tree: &Node{
- Name: "traefik",
- Children: []*Node{
- {Name: "Foo"},
- },
- },
- structure: struct {
- Foo *struct {
- Bar string
- }
- }{
- Foo: &struct {
- Bar string
- }{},
- },
- expected: expected{error: true},
- },
- {
- desc: "level 2, map without children",
- tree: &Node{
- Name: "traefik",
- Children: []*Node{
- {Name: "Foo"},
- },
- },
- structure: struct {
- Foo map[string]string
- }{
- Foo: map[string]string{},
- },
- expected: expected{error: true},
- },
- {
- desc: "level 2, pointer",
- tree: &Node{
- Name: "traefik",
- Children: []*Node{
- {Name: "Foo", Children: []*Node{
- {Name: "Bar", Value: "bir"},
- }},
- },
- },
- structure: struct {
- Foo *struct {
- Bar string
- }
- }{
- Foo: &struct {
- Bar string
- }{},
- },
- expected: expected{
- node: &Node{
- Name: "traefik",
- Kind: reflect.Struct,
- Children: []*Node{
- {Name: "Foo", FieldName: "Foo", Kind: reflect.Ptr, Children: []*Node{
- {Name: "Bar", FieldName: "Bar", Value: "bir", Kind: reflect.String},
- }},
- },
- },
- },
- },
- {
- desc: "level 2, 2 children",
- tree: &Node{
- Name: "traefik",
- Children: []*Node{
- {Name: "Foo", Children: []*Node{
- {Name: "Bar", Value: "bir"},
- {Name: "Bur", Value: "fuu"},
- }},
- },
- },
- structure: struct {
- Foo struct {
- Bar string
- Bur string
- }
- }{
- Foo: struct {
- Bar string
- Bur string
- }{},
- },
- expected: expected{
- node: &Node{
- Name: "traefik",
- Kind: reflect.Struct,
- Children: []*Node{
- {
- Name: "Foo",
- FieldName: "Foo",
- Kind: reflect.Struct,
- Children: []*Node{
- {Name: "Bar", FieldName: "Bar", Value: "bir", Kind: reflect.String},
- {Name: "Bur", FieldName: "Bur", Value: "fuu", Kind: reflect.String},
- },
- },
- },
- },
- },
- },
- {
- desc: "level 3",
- tree: &Node{
- Name: "traefik",
- Children: []*Node{
- {Name: "Foo", Children: []*Node{
- {Name: "Bar", Children: []*Node{
- {Name: "Bur", Value: "fuu"},
- }},
- }},
- },
- },
- structure: struct {
- Foo struct {
- Bar struct {
- Bur string
- }
- }
- }{
- Foo: struct {
- Bar struct {
- Bur string
- }
- }{},
- },
- expected: expected{
- node: &Node{
- Name: "traefik",
- Kind: reflect.Struct,
- Children: []*Node{
- {
- Name: "Foo",
- FieldName: "Foo",
- Kind: reflect.Struct,
- Children: []*Node{
- {
- Name: "Bar",
- FieldName: "Bar",
- Kind: reflect.Struct,
- Children: []*Node{
- {Name: "Bur", FieldName: "Bur", Value: "fuu", Kind: reflect.String},
- },
- },
- },
- },
- },
- },
- },
- },
- {
- desc: "level 3, 2 children level 1, 2 children level 2, 2 children level 3",
- tree: &Node{
- Name: "traefik",
- Children: []*Node{
- {Name: "Foo", Children: []*Node{
- {Name: "Bar", Children: []*Node{
- {Name: "Fii", Value: "fii"},
- {Name: "Fee", Value: "1"},
- }},
- {Name: "Bur", Children: []*Node{
- {Name: "Faa", Value: "faa"},
- }},
- }},
- {Name: "Fii", Children: []*Node{
- {Name: "FiiBar", Value: "fiiBar"},
- }},
- },
- },
- structure: struct {
- Foo struct {
- Bar struct {
- Fii string
- Fee int
- }
- Bur struct {
- Faa string
- }
- }
- Fii struct {
- FiiBar string
- }
- }{
- Foo: struct {
- Bar struct {
- Fii string
- Fee int
- }
- Bur struct {
- Faa string
- }
- }{},
- Fii: struct {
- FiiBar string
- }{},
- },
- expected: expected{
- node: &Node{
- Name: "traefik",
- Kind: reflect.Struct,
- Children: []*Node{
- {
- Name: "Foo",
- FieldName: "Foo",
- Kind: reflect.Struct,
- Children: []*Node{
- {
- Name: "Bar",
- FieldName: "Bar",
- Kind: reflect.Struct,
- Children: []*Node{
- {Name: "Fii", FieldName: "Fii", Kind: reflect.String, Value: "fii"},
- {Name: "Fee", FieldName: "Fee", Kind: reflect.Int, Value: "1"},
- },
- },
- {
- Name: "Bur",
- FieldName: "Bur",
- Kind: reflect.Struct,
- Children: []*Node{
- {Name: "Faa", FieldName: "Faa", Kind: reflect.String, Value: "faa"},
- },
- },
- },
- },
- {
- Name: "Fii",
- FieldName: "Fii",
- Kind: reflect.Struct,
- Children: []*Node{
- {Name: "FiiBar", FieldName: "FiiBar", Kind: reflect.String, Value: "fiiBar"},
- },
- },
- },
- },
- },
- },
- {
- desc: "Slice struct",
- tree: &Node{
- Name: "traefik",
- Children: []*Node{
- {Name: "Foo", Children: []*Node{
- {Name: "[0]", Children: []*Node{
- {Name: "Field1", Value: "A"},
- {Name: "Field2", Value: "A"},
- }},
- {Name: "[1]", Children: []*Node{
- {Name: "Field1", Value: "B"},
- {Name: "Field2", Value: "B"},
- }},
- {Name: "[2]", Children: []*Node{
- {Name: "Field1", Value: "C"},
- {Name: "Field2", Value: "C"},
- }},
- }},
- },
- },
- structure: struct {
- Foo []struct {
- Field1 string
- Field2 string
- }
- }{},
- expected: expected{node: &Node{
- Name: "traefik",
- Kind: reflect.Struct,
- Children: []*Node{
- {Name: "Foo", FieldName: "Foo", Kind: reflect.Slice, Children: []*Node{
- {Name: "[0]", Kind: reflect.Struct, Children: []*Node{
- {Name: "Field1", FieldName: "Field1", Value: "A", Kind: reflect.String},
- {Name: "Field2", FieldName: "Field2", Value: "A", Kind: reflect.String},
- }},
- {Name: "[1]", Kind: reflect.Struct, Children: []*Node{
- {Name: "Field1", FieldName: "Field1", Value: "B", Kind: reflect.String},
- {Name: "Field2", FieldName: "Field2", Value: "B", Kind: reflect.String},
- }},
- {Name: "[2]", Kind: reflect.Struct, Children: []*Node{
- {Name: "Field1", FieldName: "Field1", Value: "C", Kind: reflect.String},
- {Name: "Field2", FieldName: "Field2", Value: "C", Kind: reflect.String},
- }},
- }},
- },
- }},
- },
- {
- desc: "Slice pointer struct",
- tree: &Node{
- Name: "traefik",
- Children: []*Node{
- {Name: "Foo", Children: []*Node{
- {Name: "[0]", Children: []*Node{
- {Name: "Field1", Value: "A"},
- {Name: "Field2", Value: "A"},
- }},
- {Name: "[1]", Children: []*Node{
- {Name: "Field1", Value: "B"},
- {Name: "Field2", Value: "B"},
- }},
- {Name: "[2]", Children: []*Node{
- {Name: "Field1", Value: "C"},
- {Name: "Field2", Value: "C"},
- }},
- }},
- },
- },
- structure: struct {
- Foo []*struct {
- Field1 string
- Field2 string
- }
- }{},
- expected: expected{node: &Node{
- Name: "traefik",
- Kind: reflect.Struct,
- Children: []*Node{
- {Name: "Foo", FieldName: "Foo", Kind: reflect.Slice, Children: []*Node{
- {Name: "[0]", Kind: reflect.Ptr, Children: []*Node{
- {Name: "Field1", FieldName: "Field1", Value: "A", Kind: reflect.String},
- {Name: "Field2", FieldName: "Field2", Value: "A", Kind: reflect.String},
- }},
- {Name: "[1]", Kind: reflect.Ptr, Children: []*Node{
- {Name: "Field1", FieldName: "Field1", Value: "B", Kind: reflect.String},
- {Name: "Field2", FieldName: "Field2", Value: "B", Kind: reflect.String},
- }},
- {Name: "[2]", Kind: reflect.Ptr, Children: []*Node{
- {Name: "Field1", FieldName: "Field1", Value: "C", Kind: reflect.String},
- {Name: "Field2", FieldName: "Field2", Value: "C", Kind: reflect.String},
- }},
- }},
- },
- }},
- },
- {
- desc: "embedded",
- tree: &Node{
- Name: "traefik",
- Children: []*Node{
- {Name: "Foo", Children: []*Node{
- {Name: "Fii", Value: "bir"},
- {Name: "Fuu", Value: "bur"},
- }},
- },
- },
- structure: struct {
- Foo struct {
- FiiFoo
- }
- }{},
- expected: expected{node: &Node{
- Name: "traefik",
- Kind: reflect.Struct,
- Children: []*Node{
- {Name: "Foo", FieldName: "Foo", Kind: reflect.Struct, Children: []*Node{
- {Name: "Fii", FieldName: "Fii", Value: "bir", Kind: reflect.String},
- {Name: "Fuu", FieldName: "Fuu", Value: "bur", Kind: reflect.String},
- }},
- },
- }},
- },
- {
- desc: "embedded slice",
- tree: &Node{
- Name: "traefik",
- Children: []*Node{
- {Name: "MySliceType", Value: "foo,fii"},
- },
- },
- structure: struct {
- MySliceType
- }{},
- expected: expected{node: &Node{
- Name: "traefik",
- Kind: reflect.Struct,
- Children: []*Node{
- {Name: "MySliceType", FieldName: "MySliceType", Value: "foo,fii", Kind: reflect.Slice},
- },
- }},
- },
- {
- desc: "embedded slice 2",
- tree: &Node{
- Name: "traefik",
- Children: []*Node{
- {Name: "Foo", Children: []*Node{
- {Name: "MySliceType", Value: "foo,fii"},
- }},
- },
- },
- structure: struct {
- Foo struct {
- MySliceType
- }
- }{},
- expected: expected{node: &Node{
- Name: "traefik",
- Kind: reflect.Struct,
- Children: []*Node{
- {Name: "Foo", FieldName: "Foo", Kind: reflect.Struct, Children: []*Node{
- {Name: "MySliceType", FieldName: "MySliceType", Value: "foo,fii", Kind: reflect.Slice},
- }},
- },
- }},
- },
- {
- 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 {
- test := test
- t.Run(test.desc, func(t *testing.T) {
- t.Parallel()
-
- err := metadata{MetadataOpts{TagName: TagLabel, AllowSliceAsStruct: true}}.Add(test.structure, test.tree)
-
- if test.expected.error {
- assert.Error(t, err)
- } else {
- require.NoError(t, err)
-
- if !assert.Equal(t, test.expected.node, test.tree) {
- bytes, errM := json.MarshalIndent(test.tree, "", " ")
- require.NoError(t, errM)
- fmt.Println(string(bytes))
- }
- }
- })
- }
-}
-
-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
diff --git a/pkg/config/parser/parser.go b/pkg/config/parser/parser.go
deleted file mode 100644
index a693152fd..000000000
--- a/pkg/config/parser/parser.go
+++ /dev/null
@@ -1,35 +0,0 @@
-// Package parser implements decoding and encoding between a flat map of labels and a typed Configuration.
-package parser
-
-// Decode decodes the given map of labels into the given element.
-// If any filters are present, labels which do not match the filters are skipped.
-// The operation goes through three stages roughly summarized as:
-// labels -> tree of untyped nodes
-// untyped nodes -> nodes augmented with metadata such as kind (inferred from element)
-// "typed" nodes -> typed element.
-func Decode(labels map[string]string, element interface{}, rootName string, filters ...string) error {
- node, err := DecodeToNode(labels, rootName, filters...)
- if err != nil {
- return err
- }
-
- metaOpts := MetadataOpts{TagName: TagLabel, AllowSliceAsStruct: true}
- err = AddMetadata(element, node, metaOpts)
- if err != nil {
- return err
- }
-
- return Fill(element, node, FillerOpts{AllowSliceAsStruct: true})
-}
-
-// Encode converts an element to labels.
-// element -> node (value) -> label (node).
-func Encode(element interface{}, rootName string) (map[string]string, error) {
- etnOpts := EncoderToNodeOpts{OmitEmpty: true, TagName: TagLabel, AllowSliceAsStruct: true}
- node, err := EncodeToNode(element, rootName, etnOpts)
- if err != nil {
- return nil, err
- }
-
- return EncodeNode(node), nil
-}
diff --git a/pkg/config/parser/parser_test.go b/pkg/config/parser/parser_test.go
deleted file mode 100644
index 2e1393d8f..000000000
--- a/pkg/config/parser/parser_test.go
+++ /dev/null
@@ -1,346 +0,0 @@
-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)
- })
- }
-}
diff --git a/pkg/config/parser/tags.go b/pkg/config/parser/tags.go
deleted file mode 100644
index 3304c45c4..000000000
--- a/pkg/config/parser/tags.go
+++ /dev/null
@@ -1,24 +0,0 @@
-package parser
-
-const (
- // TagLabel allows to apply a custom behavior.
- // - "allowEmpty": allows to create an empty struct.
- // - "-": 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"
-
- // TagDescription is the documentation for the field.
- // - "-": ignore the field.
- TagDescription = "description"
-
- // TagLabelAllowEmpty is related to TagLabel.
- TagLabelAllowEmpty = "allowEmpty"
-)
diff --git a/pkg/config/static/static_config.go b/pkg/config/static/static_config.go
index cc2de50d2..40e2ef9a8 100644
--- a/pkg/config/static/static_config.go
+++ b/pkg/config/static/static_config.go
@@ -34,10 +34,11 @@ import (
assetfs "github.com/elazarl/go-bindata-assetfs"
legolog "github.com/go-acme/lego/v3/log"
"github.com/sirupsen/logrus"
+ ptypes "github.com/traefik/paerser/types"
)
const (
- // DefaultInternalEntryPointName the name of the default internal entry point
+ // DefaultInternalEntryPointName the name of the default internal entry point.
DefaultInternalEntryPointName = "traefik"
// DefaultGraceTimeout controls how long Traefik serves pending requests
@@ -47,7 +48,7 @@ const (
// DefaultIdleTimeout before closing an idle connection.
DefaultIdleTimeout = 180 * time.Second
- // DefaultAcmeCAServer is the default ACME API endpoint
+ // DefaultAcmeCAServer is the default ACME API endpoint.
DefaultAcmeCAServer = "https://acme-v02.api.letsencrypt.org/directory"
)
@@ -110,38 +111,38 @@ func (a *API) SetDefaults() {
// RespondingTimeouts contains timeout configurations for incoming requests to the Traefik instance.
type RespondingTimeouts struct {
- ReadTimeout types.Duration `description:"ReadTimeout is the maximum duration for reading the entire request, including the body. If zero, no timeout is set." json:"readTimeout,omitempty" toml:"readTimeout,omitempty" yaml:"readTimeout,omitempty" export:"true"`
- WriteTimeout types.Duration `description:"WriteTimeout is the maximum duration before timing out writes of the response. If zero, no timeout is set." json:"writeTimeout,omitempty" toml:"writeTimeout,omitempty" yaml:"writeTimeout,omitempty" export:"true"`
- IdleTimeout types.Duration `description:"IdleTimeout is the maximum amount duration an idle (keep-alive) connection will remain idle before closing itself. If zero, no timeout is set." json:"idleTimeout,omitempty" toml:"idleTimeout,omitempty" yaml:"idleTimeout,omitempty" export:"true"`
+ ReadTimeout ptypes.Duration `description:"ReadTimeout is the maximum duration for reading the entire request, including the body. If zero, no timeout is set." json:"readTimeout,omitempty" toml:"readTimeout,omitempty" yaml:"readTimeout,omitempty" export:"true"`
+ WriteTimeout ptypes.Duration `description:"WriteTimeout is the maximum duration before timing out writes of the response. If zero, no timeout is set." json:"writeTimeout,omitempty" toml:"writeTimeout,omitempty" yaml:"writeTimeout,omitempty" export:"true"`
+ IdleTimeout ptypes.Duration `description:"IdleTimeout is the maximum amount duration an idle (keep-alive) connection will remain idle before closing itself. If zero, no timeout is set." json:"idleTimeout,omitempty" toml:"idleTimeout,omitempty" yaml:"idleTimeout,omitempty" export:"true"`
}
// SetDefaults sets the default values.
func (a *RespondingTimeouts) SetDefaults() {
- a.IdleTimeout = types.Duration(DefaultIdleTimeout)
+ a.IdleTimeout = ptypes.Duration(DefaultIdleTimeout)
}
// ForwardingTimeouts contains timeout configurations for forwarding requests to the backend servers.
type ForwardingTimeouts struct {
- DialTimeout types.Duration `description:"The amount of time to wait until a connection to a backend server can be established. If zero, no timeout exists." json:"dialTimeout,omitempty" toml:"dialTimeout,omitempty" yaml:"dialTimeout,omitempty" export:"true"`
- ResponseHeaderTimeout types.Duration `description:"The amount of time to wait for a server's response headers after fully writing the request (including its body, if any). If zero, no timeout exists." json:"responseHeaderTimeout,omitempty" toml:"responseHeaderTimeout,omitempty" yaml:"responseHeaderTimeout,omitempty" export:"true"`
- IdleConnTimeout types.Duration `description:"The maximum period for which an idle HTTP keep-alive connection will remain open before closing itself" json:"idleConnTimeout,omitempty" toml:"idleConnTimeout,omitempty" yaml:"idleConnTimeout,omitempty" export:"true"`
+ DialTimeout ptypes.Duration `description:"The amount of time to wait until a connection to a backend server can be established. If zero, no timeout exists." json:"dialTimeout,omitempty" toml:"dialTimeout,omitempty" yaml:"dialTimeout,omitempty" export:"true"`
+ ResponseHeaderTimeout ptypes.Duration `description:"The amount of time to wait for a server's response headers after fully writing the request (including its body, if any). If zero, no timeout exists." json:"responseHeaderTimeout,omitempty" toml:"responseHeaderTimeout,omitempty" yaml:"responseHeaderTimeout,omitempty" export:"true"`
+ IdleConnTimeout ptypes.Duration `description:"The maximum period for which an idle HTTP keep-alive connection will remain open before closing itself" json:"idleConnTimeout,omitempty" toml:"idleConnTimeout,omitempty" yaml:"idleConnTimeout,omitempty" export:"true"`
}
// SetDefaults sets the default values.
func (f *ForwardingTimeouts) SetDefaults() {
- f.DialTimeout = types.Duration(30 * time.Second)
- f.IdleConnTimeout = types.Duration(90 * time.Second)
+ f.DialTimeout = ptypes.Duration(30 * time.Second)
+ f.IdleConnTimeout = ptypes.Duration(90 * time.Second)
}
// LifeCycle contains configurations relevant to the lifecycle (such as the shutdown phase) of Traefik.
type LifeCycle struct {
- RequestAcceptGraceTimeout types.Duration `description:"Duration to keep accepting requests before Traefik initiates the graceful shutdown procedure." json:"requestAcceptGraceTimeout,omitempty" toml:"requestAcceptGraceTimeout,omitempty" yaml:"requestAcceptGraceTimeout,omitempty" export:"true"`
- GraceTimeOut types.Duration `description:"Duration to give active requests a chance to finish before Traefik stops." json:"graceTimeOut,omitempty" toml:"graceTimeOut,omitempty" yaml:"graceTimeOut,omitempty" export:"true"`
+ RequestAcceptGraceTimeout ptypes.Duration `description:"Duration to keep accepting requests before Traefik initiates the graceful shutdown procedure." json:"requestAcceptGraceTimeout,omitempty" toml:"requestAcceptGraceTimeout,omitempty" yaml:"requestAcceptGraceTimeout,omitempty" export:"true"`
+ GraceTimeOut ptypes.Duration `description:"Duration to give active requests a chance to finish before Traefik stops." json:"graceTimeOut,omitempty" toml:"graceTimeOut,omitempty" yaml:"graceTimeOut,omitempty" export:"true"`
}
// SetDefaults sets the default values.
func (a *LifeCycle) SetDefaults() {
- a.GraceTimeOut = types.Duration(DefaultGraceTimeout)
+ a.GraceTimeOut = ptypes.Duration(DefaultGraceTimeout)
}
// Tracing holds the tracing configuration.
@@ -164,7 +165,7 @@ func (t *Tracing) SetDefaults() {
// Providers contains providers configuration.
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"`
+ ProvidersThrottleDuration ptypes.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" file:"allowEmpty"`
File *file.Provider `description:"Enable File backend with default settings." json:"file,omitempty" toml:"file,omitempty" yaml:"file,omitempty" export:"true"`
@@ -207,7 +208,7 @@ func (c *Configuration) SetEffectiveConfiguration() {
if c.Providers.Docker != nil {
if c.Providers.Docker.SwarmModeRefreshSeconds <= 0 {
- c.Providers.Docker.SwarmModeRefreshSeconds = types.Duration(15 * time.Second)
+ c.Providers.Docker.SwarmModeRefreshSeconds = ptypes.Duration(15 * time.Second)
}
}
diff --git a/pkg/metrics/datadog_test.go b/pkg/metrics/datadog_test.go
index 08a85061c..9c481a6a8 100644
--- a/pkg/metrics/datadog_test.go
+++ b/pkg/metrics/datadog_test.go
@@ -9,6 +9,7 @@ import (
"github.com/containous/traefik/v2/pkg/types"
"github.com/stvp/go-udp-testing"
+ ptypes "github.com/traefik/paerser/types"
)
func TestDatadog(t *testing.T) {
@@ -16,7 +17,7 @@ func TestDatadog(t *testing.T) {
// This is needed to make sure that UDP Listener listens for data a bit longer, otherwise it will quit after a millisecond
udp.Timeout = 5 * time.Second
- datadogRegistry := RegisterDatadog(context.Background(), &types.Datadog{Address: ":18125", PushInterval: types.Duration(time.Second), AddEntryPointsLabels: true, AddServicesLabels: true})
+ datadogRegistry := RegisterDatadog(context.Background(), &types.Datadog{Address: ":18125", PushInterval: ptypes.Duration(time.Second), AddEntryPointsLabels: true, AddServicesLabels: true})
defer StopDatadog()
if !datadogRegistry.IsEpEnabled() || !datadogRegistry.IsSvcEnabled() {
diff --git a/pkg/metrics/influxdb_test.go b/pkg/metrics/influxdb_test.go
index 7ebc09997..da8fbf3ee 100644
--- a/pkg/metrics/influxdb_test.go
+++ b/pkg/metrics/influxdb_test.go
@@ -13,6 +13,7 @@ import (
"github.com/containous/traefik/v2/pkg/types"
"github.com/stvp/go-udp-testing"
+ ptypes "github.com/traefik/paerser/types"
)
func TestInfluxDB(t *testing.T) {
@@ -20,7 +21,7 @@ func TestInfluxDB(t *testing.T) {
// This is needed to make sure that UDP Listener listens for data a bit longer, otherwise it will quit after a millisecond
udp.Timeout = 5 * time.Second
- influxDBRegistry := RegisterInfluxDB(context.Background(), &types.InfluxDB{Address: ":8089", PushInterval: types.Duration(time.Second), AddEntryPointsLabels: true, AddServicesLabels: true})
+ influxDBRegistry := RegisterInfluxDB(context.Background(), &types.InfluxDB{Address: ":8089", PushInterval: ptypes.Duration(time.Second), AddEntryPointsLabels: true, AddServicesLabels: true})
defer StopInfluxDB()
if !influxDBRegistry.IsEpEnabled() || !influxDBRegistry.IsSvcEnabled() {
@@ -79,7 +80,7 @@ func TestInfluxDBHTTP(t *testing.T) {
}))
defer ts.Close()
- influxDBRegistry := RegisterInfluxDB(context.Background(), &types.InfluxDB{Address: ts.URL, Protocol: "http", PushInterval: types.Duration(time.Second), Database: "test", RetentionPolicy: "autogen", AddEntryPointsLabels: true, AddServicesLabels: true})
+ influxDBRegistry := RegisterInfluxDB(context.Background(), &types.InfluxDB{Address: ts.URL, Protocol: "http", PushInterval: ptypes.Duration(time.Second), Database: "test", RetentionPolicy: "autogen", AddEntryPointsLabels: true, AddServicesLabels: true})
defer StopInfluxDB()
if !influxDBRegistry.IsEpEnabled() || !influxDBRegistry.IsSvcEnabled() {
diff --git a/pkg/metrics/pilot.go b/pkg/metrics/pilot.go
new file mode 100644
index 000000000..9ed199246
--- /dev/null
+++ b/pkg/metrics/pilot.go
@@ -0,0 +1,317 @@
+package metrics
+
+import (
+ "strings"
+ "sync"
+ "time"
+
+ "github.com/go-kit/kit/metrics"
+ "github.com/go-kit/kit/metrics/generic"
+)
+
+const (
+ // server meta information.
+ pilotConfigPrefix = "config"
+ pilotConfigReloadsTotalName = pilotConfigPrefix + "ReloadsTotal"
+ pilotConfigReloadsFailuresTotalName = pilotConfigPrefix + "ReloadsFailureTotal"
+ pilotConfigLastReloadSuccessName = pilotConfigPrefix + "LastReloadSuccess"
+ pilotConfigLastReloadFailureName = pilotConfigPrefix + "LastReloadFailure"
+
+ // entry point.
+ pilotEntryPointPrefix = "entrypoint"
+ pilotEntryPointReqsTotalName = pilotEntryPointPrefix + "RequestsTotal"
+ pilotEntryPointReqsTLSTotalName = pilotEntryPointPrefix + "RequestsTLSTotal"
+ pilotEntryPointReqDurationName = pilotEntryPointPrefix + "RequestDurationSeconds"
+ pilotEntryPointOpenConnsName = pilotEntryPointPrefix + "OpenConnections"
+
+ // service level.
+ pilotServicePrefix = "service"
+ pilotServiceReqsTotalName = pilotServicePrefix + "RequestsTotal"
+ pilotServiceReqsTLSTotalName = pilotServicePrefix + "RequestsTLSTotal"
+ pilotServiceReqDurationName = pilotServicePrefix + "RequestDurationSeconds"
+ pilotServiceOpenConnsName = pilotServicePrefix + "OpenConnections"
+ pilotServiceRetriesTotalName = pilotServicePrefix + "RetriesTotal"
+ pilotServiceServerUpName = pilotServicePrefix + "ServerUp"
+)
+
+const root = "value"
+
+// RegisterPilot registers all Pilot metrics.
+func RegisterPilot() *PilotRegistry {
+ standardRegistry := &standardRegistry{
+ epEnabled: true,
+ svcEnabled: true,
+ }
+
+ pr := &PilotRegistry{
+ standardRegistry: standardRegistry,
+ counters: make(map[string]*pilotCounter),
+ gauges: make(map[string]*pilotGauge),
+ histograms: make(map[string]*pilotHistogram),
+ }
+
+ standardRegistry.configReloadsCounter = pr.newCounter(pilotConfigReloadsTotalName)
+ standardRegistry.configReloadsFailureCounter = pr.newCounter(pilotConfigReloadsFailuresTotalName)
+ standardRegistry.lastConfigReloadSuccessGauge = pr.newGauge(pilotConfigLastReloadSuccessName)
+ standardRegistry.lastConfigReloadFailureGauge = pr.newGauge(pilotConfigLastReloadFailureName)
+
+ standardRegistry.entryPointReqsCounter = pr.newCounter(pilotEntryPointReqsTotalName)
+ standardRegistry.entryPointReqsTLSCounter = pr.newCounter(pilotEntryPointReqsTLSTotalName)
+ standardRegistry.entryPointReqDurationHistogram, _ = NewHistogramWithScale(pr.newHistogram(pilotEntryPointReqDurationName), time.Second)
+ standardRegistry.entryPointOpenConnsGauge = pr.newGauge(pilotEntryPointOpenConnsName)
+
+ standardRegistry.serviceReqsCounter = pr.newCounter(pilotServiceReqsTotalName)
+ standardRegistry.serviceReqsTLSCounter = pr.newCounter(pilotServiceReqsTLSTotalName)
+ standardRegistry.serviceReqDurationHistogram, _ = NewHistogramWithScale(pr.newHistogram(pilotServiceReqDurationName), time.Second)
+ standardRegistry.serviceOpenConnsGauge = pr.newGauge(pilotServiceOpenConnsName)
+ standardRegistry.serviceRetriesCounter = pr.newCounter(pilotServiceRetriesTotalName)
+ standardRegistry.serviceServerUpGauge = pr.newGauge(pilotServiceServerUpName)
+
+ return pr
+}
+
+// PilotMetric is a representation of a metric.
+type PilotMetric struct {
+ Name string `json:"name"`
+ Type string `json:"type"`
+ Observations map[string]interface{} `json:"observations"`
+}
+
+type pilotHistogramObservation struct {
+ Total float64 `json:"total"`
+ Count float64 `json:"count"`
+}
+
+// PilotRegistry represents the pilots metrics registry.
+type PilotRegistry struct {
+ counters map[string]*pilotCounter
+ gauges map[string]*pilotGauge
+ histograms map[string]*pilotHistogram
+
+ *standardRegistry
+}
+
+// newCounter register and returns a new pilotCounter.
+func (pr *PilotRegistry) newCounter(name string) *pilotCounter {
+ c := newPilotCounter(name)
+ pr.counters[name] = c
+
+ return c
+}
+
+// newGauge register and returns a new pilotGauge.
+func (pr *PilotRegistry) newGauge(name string) *pilotGauge {
+ g := newPilotGauge(name)
+ pr.gauges[name] = g
+
+ return g
+}
+
+// newHistogram register and returns a new pilotHistogram.
+func (pr *PilotRegistry) newHistogram(name string) *pilotHistogram {
+ h := newPilotHistogram(name)
+ pr.histograms[name] = h
+
+ return h
+}
+
+// Data exports the metrics: metrics name -> labels -> values.
+func (pr *PilotRegistry) Data() []PilotMetric {
+ var pilotMetrics []PilotMetric
+
+ for name, counter := range pr.counters {
+ pilotMetric := PilotMetric{
+ Name: name,
+ Type: "COUNTER",
+ Observations: make(map[string]interface{}),
+ }
+ pilotMetrics = append(pilotMetrics, pilotMetric)
+
+ counter.counters.Range(func(key, value interface{}) bool {
+ labels := key.(string)
+ pc := value.(*pilotCounter)
+
+ if labels == "" {
+ labels = root
+ }
+
+ if labels == root || len(pc.c.LabelValues())%2 == 0 {
+ pilotMetric.Observations[labels] = pc.c.Value()
+ }
+
+ return true
+ })
+ }
+
+ for name, gauge := range pr.gauges {
+ pilotMetric := PilotMetric{
+ Name: name,
+ Type: "GAUGE",
+ Observations: make(map[string]interface{}),
+ }
+ pilotMetrics = append(pilotMetrics, pilotMetric)
+
+ gauge.gauges.Range(func(key, value interface{}) bool {
+ labels := key.(string)
+ pg := value.(*pilotGauge)
+
+ if labels == "" {
+ labels = root
+ }
+
+ if labels == root || len(pg.g.LabelValues())%2 == 0 {
+ pilotMetric.Observations[labels] = pg.g.Value()
+ }
+
+ return true
+ })
+ }
+
+ for name, histogram := range pr.histograms {
+ pilotMetric := PilotMetric{
+ Name: name,
+ Type: "HISTOGRAM",
+ Observations: make(map[string]interface{}),
+ }
+ pilotMetrics = append(pilotMetrics, pilotMetric)
+
+ histogram.histograms.Range(func(key, value interface{}) bool {
+ labels := key.(string)
+ ph := value.(*pilotHistogram)
+
+ if labels == "" {
+ labels = root
+ }
+
+ if labels == root || len(ph.labels)%2 == 0 {
+ pilotMetric.Observations[labels] = &pilotHistogramObservation{
+ Total: ph.total.Value(),
+ Count: ph.count.Value(),
+ }
+ }
+
+ return true
+ })
+ }
+
+ return pilotMetrics
+}
+
+type pilotCounter struct {
+ c *generic.Counter
+ counters *sync.Map
+}
+
+func newPilotCounter(name string) *pilotCounter {
+ return &pilotCounter{
+ c: generic.NewCounter(name),
+ counters: &sync.Map{},
+ }
+}
+
+// With returns a new pilotCounter with the given labels.
+func (c *pilotCounter) With(labels ...string) metrics.Counter {
+ newCounter := c.c.With(labels...).(*generic.Counter)
+ newCounter.ValueReset()
+
+ return &pilotCounter{
+ c: newCounter,
+ counters: c.counters,
+ }
+}
+
+// Add adds the given delta to the counter.
+func (c *pilotCounter) Add(delta float64) {
+ labelsKey := strings.Join(c.c.LabelValues(), ",")
+
+ pc, _ := c.counters.LoadOrStore(labelsKey, c)
+
+ pc.(*pilotCounter).c.Add(delta)
+}
+
+type pilotGauge struct {
+ g *generic.Gauge
+ gauges *sync.Map
+}
+
+func newPilotGauge(name string) *pilotGauge {
+ return &pilotGauge{
+ g: generic.NewGauge(name),
+ gauges: &sync.Map{},
+ }
+}
+
+// With returns a new pilotGauge with the given labels.
+func (g *pilotGauge) With(labels ...string) metrics.Gauge {
+ newGauge := g.g.With(labels...).(*generic.Gauge)
+ newGauge.Set(0)
+
+ return &pilotGauge{
+ g: newGauge,
+ gauges: g.gauges,
+ }
+}
+
+// Set sets the given value to the gauge.
+func (g *pilotGauge) Set(value float64) {
+ labelsKey := strings.Join(g.g.LabelValues(), ",")
+
+ pg, _ := g.gauges.LoadOrStore(labelsKey, g)
+
+ pg.(*pilotGauge).g.Set(value)
+}
+
+// Add adds the given delta to the gauge.
+func (g *pilotGauge) Add(delta float64) {
+ labelsKey := strings.Join(g.g.LabelValues(), ",")
+
+ pg, _ := g.gauges.LoadOrStore(labelsKey, g)
+
+ pg.(*pilotGauge).g.Add(delta)
+}
+
+type pilotHistogram struct {
+ name string
+ labels []string
+ count *generic.Counter
+ total *generic.Counter
+ histograms *sync.Map
+}
+
+func newPilotHistogram(name string) *pilotHistogram {
+ return &pilotHistogram{
+ name: name,
+ labels: make([]string, 0),
+ count: &generic.Counter{},
+ total: &generic.Counter{},
+ histograms: &sync.Map{},
+ }
+}
+
+// With returns a new pilotHistogram with the given labels.
+func (h *pilotHistogram) With(labels ...string) metrics.Histogram {
+ var newLabels []string
+
+ newLabels = append(newLabels, h.labels...)
+ newLabels = append(newLabels, labels...)
+
+ return &pilotHistogram{
+ name: h.name,
+ labels: newLabels,
+ count: &generic.Counter{},
+ total: &generic.Counter{},
+ histograms: h.histograms,
+ }
+}
+
+// Observe records a new value into the histogram.
+func (h *pilotHistogram) Observe(value float64) {
+ labelsKey := strings.Join(h.labels, ",")
+
+ ph, _ := h.histograms.LoadOrStore(labelsKey, h)
+
+ pHisto := ph.(*pilotHistogram)
+
+ pHisto.count.Add(1)
+ pHisto.total.Add(value)
+}
diff --git a/pkg/metrics/pilot_test.go b/pkg/metrics/pilot_test.go
new file mode 100644
index 000000000..73ffb0335
--- /dev/null
+++ b/pkg/metrics/pilot_test.go
@@ -0,0 +1,355 @@
+package metrics
+
+import (
+ "net/http"
+ "strconv"
+ "strings"
+ "testing"
+ "time"
+
+ "github.com/go-kit/kit/metrics"
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+)
+
+func TestPilotCounter(t *testing.T) {
+ rootCounter := newPilotCounter("rootCounter")
+
+ // Checks that a counter without labels can be incremented.
+ rootCounter.Add(1)
+ assertPilotCounterValue(t, 1.0, "", rootCounter)
+
+ // Checks that a counter with labels can be incremented.
+ counterWithLabels := rootCounter.With("foo", "bar", "foo", "buz")
+
+ counterWithLabels.Add(1)
+ assertPilotCounterValue(t, 1.0, "foo,bar,foo,buz", counterWithLabels)
+
+ // Checks that the derived counter value has not changed.
+ assertPilotCounterValue(t, 1.0, "", rootCounter)
+
+ // Checks that an existing counter (with the same labels) can be incremented.
+ existingCounterWithLabels := rootCounter.With("foo", "bar").With("foo", "buz")
+
+ existingCounterWithLabels.Add(1)
+ assertPilotCounterValue(t, 2.0, "foo,bar,foo,buz", existingCounterWithLabels)
+}
+
+func assertPilotCounterValue(t *testing.T, expValue float64, labels string, c metrics.Counter) {
+ t.Helper()
+ counter, ok := c.(*pilotCounter).counters.Load(labels)
+
+ require.True(t, ok)
+ assert.Equal(t, expValue, counter.(*pilotCounter).c.Value())
+}
+
+func TestPilotGauge(t *testing.T) {
+ rootGauge := newPilotGauge("rootGauge")
+
+ // Checks that a gauge without labels can be incremented.
+ rootGauge.Add(1)
+
+ assertPilotGaugeValue(t, 1.0, "", rootGauge)
+
+ // Checks that a gauge (without labels) value can be set.
+ rootGauge.Set(5.0)
+
+ assertPilotGaugeValue(t, 5.0, "", rootGauge)
+
+ // Checks that a gauge with labels can be incremented.
+ gaugeWithLabels := rootGauge.With("foo", "bar", "foo", "buz")
+ gaugeWithLabels.Add(1)
+
+ assertPilotGaugeValue(t, 1.0, "foo,bar,foo,buz", gaugeWithLabels)
+
+ // Checks that the derived gauge value has not changed.
+ assertPilotGaugeValue(t, 5.0, "", rootGauge)
+
+ // Checks that an existing gauge (with the same labels) can be incremented.
+ existingGaugeWithLabels := rootGauge.With("foo", "bar").With("foo", "buz")
+ existingGaugeWithLabels.Add(1)
+
+ assertPilotGaugeValue(t, 2.0, "foo,bar,foo,buz", existingGaugeWithLabels)
+}
+
+func assertPilotGaugeValue(t *testing.T, expValue float64, labels string, g metrics.Gauge) {
+ t.Helper()
+ gauge, ok := g.(*pilotGauge).gauges.Load(labels)
+
+ require.True(t, ok)
+ assert.Equal(t, expValue, gauge.(*pilotGauge).g.Value())
+}
+
+func TestPilotHistogram(t *testing.T) {
+ rootHistogram := newPilotHistogram("rootHistogram")
+
+ // Checks that an histogram without labels can be updated.
+ rootHistogram.Observe(1)
+
+ assertPilotHistogramValues(t, 1.0, 1.0, "", rootHistogram)
+
+ rootHistogram.Observe(2)
+
+ assertPilotHistogramValues(t, 2.0, 3.0, "", rootHistogram)
+
+ // Checks that an histogram with labels can be updated.
+ histogramWithLabels := rootHistogram.With("foo", "bar", "foo", "buz")
+ histogramWithLabels.Observe(1)
+
+ assertPilotHistogramValues(t, 1.0, 1.0, "foo,bar,foo,buz", histogramWithLabels)
+
+ // Checks that the derived histogram has not changed.
+ assertPilotHistogramValues(t, 2.0, 3.0, "", rootHistogram)
+
+ // Checks that an existing histogram (with the same labels) can be updated.
+ existingHistogramWithLabels := rootHistogram.With("foo", "bar").With("foo", "buz")
+ existingHistogramWithLabels.Observe(1)
+
+ assertPilotHistogramValues(t, 2.0, 2.0, "foo,bar,foo,buz", existingHistogramWithLabels)
+}
+
+func assertPilotHistogramValues(t *testing.T, expCount, expTotal float64, labels string, h metrics.Histogram) {
+ t.Helper()
+ histogram, ok := h.(*pilotHistogram).histograms.Load(labels)
+
+ require.True(t, ok)
+ assert.Equal(t, expCount, histogram.(*pilotHistogram).count.Value())
+ assert.Equal(t, expTotal, histogram.(*pilotHistogram).total.Value())
+}
+
+func TestPilotMetrics(t *testing.T) {
+ pilotRegistry := RegisterPilot()
+
+ if !pilotRegistry.IsEpEnabled() || !pilotRegistry.IsSvcEnabled() {
+ t.Errorf("PilotRegistry should return true for IsEnabled() and IsSvcEnabled()")
+ }
+
+ pilotRegistry.ConfigReloadsCounter().Add(1)
+ pilotRegistry.ConfigReloadsFailureCounter().Add(1)
+ pilotRegistry.LastConfigReloadSuccessGauge().Set(float64(time.Now().Unix()))
+ pilotRegistry.LastConfigReloadFailureGauge().Set(float64(time.Now().Unix()))
+
+ pilotRegistry.
+ EntryPointReqsCounter().
+ With("code", strconv.Itoa(http.StatusOK), "method", http.MethodGet, "protocol", "http", "entrypoint", "http").
+ Add(1)
+ pilotRegistry.
+ EntryPointReqDurationHistogram().
+ With("code", strconv.Itoa(http.StatusOK), "method", http.MethodGet, "protocol", "http", "entrypoint", "http").
+ Observe(1)
+ pilotRegistry.
+ EntryPointOpenConnsGauge().
+ With("method", http.MethodGet, "protocol", "http", "entrypoint", "http").
+ Set(1)
+
+ pilotRegistry.
+ ServiceReqsCounter().
+ With("service", "service1", "code", strconv.Itoa(http.StatusOK), "method", http.MethodGet, "protocol", "http").
+ Add(1)
+ pilotRegistry.
+ ServiceReqDurationHistogram().
+ With("service", "service1", "code", strconv.Itoa(http.StatusOK), "method", http.MethodGet, "protocol", "http").
+ Observe(10000)
+ pilotRegistry.
+ ServiceOpenConnsGauge().
+ With("service", "service1", "method", http.MethodGet, "protocol", "http").
+ Set(1)
+ pilotRegistry.
+ ServiceRetriesCounter().
+ With("service", "service1").
+ Add(1)
+ pilotRegistry.
+ ServiceServerUpGauge().
+ With("service", "service1", "url", "http://127.0.0.10:80").
+ Set(1)
+
+ data := pilotRegistry.Data()
+
+ testCases := []struct {
+ name string
+ labels map[string]string
+ assert func(*PilotMetric)
+ }{
+ {
+ name: pilotConfigReloadsTotalName,
+ assert: buildPilotCounterAssert(t, pilotConfigReloadsTotalName, 1),
+ },
+ {
+ name: pilotConfigReloadsFailuresTotalName,
+ assert: buildPilotCounterAssert(t, pilotConfigReloadsFailuresTotalName, 1),
+ },
+ {
+ name: pilotConfigLastReloadSuccessName,
+ assert: buildPilotTimestampAssert(t, pilotConfigLastReloadSuccessName),
+ },
+ {
+ name: pilotConfigLastReloadFailureName,
+ assert: buildPilotTimestampAssert(t, pilotConfigLastReloadFailureName),
+ },
+ {
+ name: pilotEntryPointReqsTotalName,
+ labels: map[string]string{
+ "code": "200",
+ "method": http.MethodGet,
+ "protocol": "http",
+ "entrypoint": "http",
+ },
+ assert: buildPilotCounterAssert(t, pilotEntryPointReqsTotalName, 1),
+ },
+ {
+ name: pilotEntryPointReqDurationName,
+ labels: map[string]string{
+ "code": "200",
+ "method": http.MethodGet,
+ "protocol": "http",
+ "entrypoint": "http",
+ },
+ assert: buildPilotHistogramAssert(t, pilotEntryPointReqDurationName, 1),
+ },
+ {
+ name: pilotEntryPointOpenConnsName,
+ labels: map[string]string{
+ "method": http.MethodGet,
+ "protocol": "http",
+ "entrypoint": "http",
+ },
+ assert: buildPilotGaugeAssert(t, pilotEntryPointOpenConnsName, 1),
+ },
+ {
+ name: pilotServiceReqsTotalName,
+ labels: map[string]string{
+ "code": "200",
+ "method": http.MethodGet,
+ "protocol": "http",
+ "service": "service1",
+ },
+ assert: buildPilotCounterAssert(t, pilotServiceReqsTotalName, 1),
+ },
+ {
+ name: pilotServiceReqDurationName,
+ labels: map[string]string{
+ "code": "200",
+ "method": http.MethodGet,
+ "protocol": "http",
+ "service": "service1",
+ },
+ assert: buildPilotHistogramAssert(t, pilotServiceReqDurationName, 1),
+ },
+ {
+ name: pilotServiceOpenConnsName,
+ labels: map[string]string{
+ "method": http.MethodGet,
+ "protocol": "http",
+ "service": "service1",
+ },
+ assert: buildPilotGaugeAssert(t, pilotServiceOpenConnsName, 1),
+ },
+ {
+ name: pilotServiceRetriesTotalName,
+ labels: map[string]string{
+ "service": "service1",
+ },
+ assert: buildPilotGreaterThanCounterAssert(t, pilotServiceRetriesTotalName, 1),
+ },
+ {
+ name: pilotServiceServerUpName,
+ labels: map[string]string{
+ "service": "service1",
+ "url": "http://127.0.0.10:80",
+ },
+ assert: buildPilotGaugeAssert(t, pilotServiceServerUpName, 1),
+ },
+ }
+
+ for _, test := range testCases {
+ test := test
+ t.Run(test.name, func(t *testing.T) {
+ metric := findPilotMetric(test.name, data)
+ if metric == nil {
+ t.Errorf("metrics do not contain %q", test.name)
+ return
+ }
+
+ for labels := range metric.Observations {
+ if len(labels)%2 == 0 {
+ splitLabels := strings.Split(labels, ",")
+ for i := 0; i < len(splitLabels); i += 2 {
+ label := splitLabels[i]
+ value := splitLabels[i+1]
+ val, ok := test.labels[label]
+ if !ok {
+ t.Errorf("%q metric contains unexpected label %q", test.name, label)
+ } else if val != value {
+ t.Errorf("label %q in metric %q has wrong value %q, expected %q", label, test.name, value, val)
+ }
+ }
+ }
+ }
+ test.assert(metric)
+ })
+ }
+}
+
+func findPilotMetric(name string, metrics []PilotMetric) *PilotMetric {
+ for _, metric := range metrics {
+ if metric.Name == name {
+ return &metric
+ }
+ }
+ return nil
+}
+
+func buildPilotCounterAssert(t *testing.T, metricName string, expectedValue float64) func(metric *PilotMetric) {
+ return func(metric *PilotMetric) {
+ for _, value := range metric.Observations {
+ if cv := value.(float64); cv != expectedValue {
+ t.Errorf("metric %s has value %f, want %f", metricName, cv, expectedValue)
+ }
+ break
+ }
+ }
+}
+
+func buildPilotGreaterThanCounterAssert(t *testing.T, metricName string, expectedMinValue float64) func(metric *PilotMetric) {
+ return func(metric *PilotMetric) {
+ for _, value := range metric.Observations {
+ if cv := value.(float64); cv < expectedMinValue {
+ t.Errorf("metric %s has value %f, want at least %f", metricName, cv, expectedMinValue)
+ }
+ break
+ }
+ }
+}
+
+func buildPilotHistogramAssert(t *testing.T, metricName string, expectedSampleCount float64) func(metric *PilotMetric) {
+ return func(metric *PilotMetric) {
+ for _, value := range metric.Observations {
+ if pho := value.(*pilotHistogramObservation); pho.Count != expectedSampleCount {
+ t.Errorf("metric %s has sample count value %f, want %f", metricName, pho, expectedSampleCount)
+ }
+ break
+ }
+ }
+}
+
+func buildPilotGaugeAssert(t *testing.T, metricName string, expectedValue float64) func(metric *PilotMetric) {
+ return func(metric *PilotMetric) {
+ for _, value := range metric.Observations {
+ if gv := value.(float64); gv != expectedValue {
+ t.Errorf("metric %s has value %f, want %f", metricName, gv, expectedValue)
+ }
+ break
+ }
+ }
+}
+
+func buildPilotTimestampAssert(t *testing.T, metricName string) func(metric *PilotMetric) {
+ return func(metric *PilotMetric) {
+ for _, value := range metric.Observations {
+ if ts := time.Unix(int64(value.(float64)), 0); time.Since(ts) > time.Minute {
+ t.Errorf("metric %s has wrong timestamp %v", metricName, ts)
+ }
+ break
+ }
+ }
+}
diff --git a/pkg/metrics/prometheus.go b/pkg/metrics/prometheus.go
index 4cebea505..9c46736a2 100644
--- a/pkg/metrics/prometheus.go
+++ b/pkg/metrics/prometheus.go
@@ -18,17 +18,17 @@ import (
)
const (
- // MetricNamePrefix prefix of all metric names
+ // MetricNamePrefix prefix of all metric names.
MetricNamePrefix = "traefik_"
- // server meta information
+ // server meta information.
metricConfigPrefix = MetricNamePrefix + "config_"
configReloadsTotalName = metricConfigPrefix + "reloads_total"
configReloadsFailuresTotalName = metricConfigPrefix + "reloads_failure_total"
configLastReloadSuccessName = metricConfigPrefix + "last_reload_success"
configLastReloadFailureName = metricConfigPrefix + "last_reload_failure"
- // entry point
+ // entry point.
metricEntryPointPrefix = MetricNamePrefix + "entrypoint_"
entryPointReqsTotalName = metricEntryPointPrefix + "requests_total"
entryPointReqsTLSTotalName = metricEntryPointPrefix + "requests_tls_total"
@@ -37,7 +37,7 @@ const (
// service level.
- // MetricServicePrefix prefix of all service metric names
+ // MetricServicePrefix prefix of all service metric names.
MetricServicePrefix = MetricNamePrefix + "service_"
serviceReqsTotalName = MetricServicePrefix + "requests_total"
serviceReqsTLSTotalName = MetricServicePrefix + "requests_tls_total"
diff --git a/pkg/metrics/statsd_test.go b/pkg/metrics/statsd_test.go
index 11955a50a..0d07d804b 100644
--- a/pkg/metrics/statsd_test.go
+++ b/pkg/metrics/statsd_test.go
@@ -3,11 +3,13 @@ package metrics
import (
"context"
"net/http"
+ "strconv"
"testing"
"time"
"github.com/containous/traefik/v2/pkg/types"
"github.com/stvp/go-udp-testing"
+ ptypes "github.com/traefik/paerser/types"
)
func TestStatsD(t *testing.T) {
@@ -15,7 +17,7 @@ func TestStatsD(t *testing.T) {
// This is needed to make sure that UDP Listener listens for data a bit longer, otherwise it will quit after a millisecond
udp.Timeout = 5 * time.Second
- statsdRegistry := RegisterStatsd(context.Background(), &types.Statsd{Address: ":18125", PushInterval: types.Duration(time.Second), AddEntryPointsLabels: true, AddServicesLabels: true})
+ statsdRegistry := RegisterStatsd(context.Background(), &types.Statsd{Address: ":18125", PushInterval: ptypes.Duration(time.Second), AddEntryPointsLabels: true, AddServicesLabels: true})
defer StopStatsd()
if !statsdRegistry.IsEpEnabled() || !statsdRegistry.IsSvcEnabled() {
@@ -36,11 +38,11 @@ func TestStatsD(t *testing.T) {
}
udp.ShouldReceiveAll(t, expected, func() {
- statsdRegistry.ServiceReqsCounter().With("service", "test", "code", string(http.StatusOK), "method", http.MethodGet).Add(1)
- statsdRegistry.ServiceReqsCounter().With("service", "test", "code", string(http.StatusNotFound), "method", http.MethodGet).Add(1)
+ statsdRegistry.ServiceReqsCounter().With("service", "test", "code", strconv.Itoa(http.StatusOK), "method", http.MethodGet).Add(1)
+ statsdRegistry.ServiceReqsCounter().With("service", "test", "code", strconv.Itoa(http.StatusNotFound), "method", http.MethodGet).Add(1)
statsdRegistry.ServiceRetriesCounter().With("service", "test").Add(1)
statsdRegistry.ServiceRetriesCounter().With("service", "test").Add(1)
- statsdRegistry.ServiceReqDurationHistogram().With("service", "test", "code", string(http.StatusOK)).Observe(10000)
+ statsdRegistry.ServiceReqDurationHistogram().With("service", "test", "code", strconv.Itoa(http.StatusOK)).Observe(10000)
statsdRegistry.ConfigReloadsCounter().Add(1)
statsdRegistry.ConfigReloadsFailureCounter().Add(1)
statsdRegistry.EntryPointReqsCounter().With("entrypoint", "test").Add(1)
@@ -55,7 +57,7 @@ func TestStatsDWithPrefix(t *testing.T) {
// This is needed to make sure that UDP Listener listens for data a bit longer, otherwise it will quit after a millisecond
udp.Timeout = 5 * time.Second
- statsdRegistry := RegisterStatsd(context.Background(), &types.Statsd{Address: ":18125", PushInterval: types.Duration(time.Second), AddEntryPointsLabels: true, AddServicesLabels: true, Prefix: "testPrefix"})
+ statsdRegistry := RegisterStatsd(context.Background(), &types.Statsd{Address: ":18125", PushInterval: ptypes.Duration(time.Second), AddEntryPointsLabels: true, AddServicesLabels: true, Prefix: "testPrefix"})
defer StopStatsd()
if !statsdRegistry.IsEpEnabled() || !statsdRegistry.IsSvcEnabled() {
@@ -76,11 +78,11 @@ func TestStatsDWithPrefix(t *testing.T) {
}
udp.ShouldReceiveAll(t, expected, func() {
- statsdRegistry.ServiceReqsCounter().With("service", "test", "code", string(http.StatusOK), "method", http.MethodGet).Add(1)
- statsdRegistry.ServiceReqsCounter().With("service", "test", "code", string(http.StatusNotFound), "method", http.MethodGet).Add(1)
+ statsdRegistry.ServiceReqsCounter().With("service", "test", "code", strconv.Itoa(http.StatusOK), "method", http.MethodGet).Add(1)
+ statsdRegistry.ServiceReqsCounter().With("service", "test", "code", strconv.Itoa(http.StatusNotFound), "method", http.MethodGet).Add(1)
statsdRegistry.ServiceRetriesCounter().With("service", "test").Add(1)
statsdRegistry.ServiceRetriesCounter().With("service", "test").Add(1)
- statsdRegistry.ServiceReqDurationHistogram().With("service", "test", "code", string(http.StatusOK)).Observe(10000)
+ statsdRegistry.ServiceReqDurationHistogram().With("service", "test", "code", strconv.Itoa(http.StatusOK)).Observe(10000)
statsdRegistry.ConfigReloadsCounter().Add(1)
statsdRegistry.ConfigReloadsFailureCounter().Add(1)
statsdRegistry.EntryPointReqsCounter().With("entrypoint", "test").Add(1)
diff --git a/pkg/middlewares/accesslog/logdata.go b/pkg/middlewares/accesslog/logdata.go
index 5347d1b8e..37f415ed5 100644
--- a/pkg/middlewares/accesslog/logdata.go
+++ b/pkg/middlewares/accesslog/logdata.go
@@ -19,7 +19,7 @@ const (
ServiceName = "ServiceName"
// ServiceURL is the map key used for the URL of the Traefik backend.
ServiceURL = "ServiceURL"
- // ServiceAddr is the map key used for the IP:port of the Traefik backend (extracted from BackendURL)
+ // ServiceAddr is the map key used for the IP:port of the Traefik backend (extracted from BackendURL).
ServiceAddr = "ServiceAddr"
// ClientAddr is the map key used for the remote address in its original form (usually IP:port).
@@ -46,9 +46,9 @@ const (
RequestScheme = "RequestScheme"
// RequestContentSize is the map key used for the number of bytes in the request entity (a.k.a. body) sent by the client.
RequestContentSize = "RequestContentSize"
- // RequestRefererHeader is the Referer header in the request
+ // RequestRefererHeader is the Referer header in the request.
RequestRefererHeader = "request_Referer"
- // RequestUserAgentHeader is the User-Agent header in the request
+ // RequestUserAgentHeader is the User-Agent header in the request.
RequestUserAgentHeader = "request_User-Agent"
// OriginDuration is the map key used for the time taken by the origin server ('upstream') to return its response.
OriginDuration = "OriginDuration"
diff --git a/pkg/middlewares/accesslog/logger.go b/pkg/middlewares/accesslog/logger.go
index 13af24c9b..1c4f747bf 100644
--- a/pkg/middlewares/accesslog/logger.go
+++ b/pkg/middlewares/accesslog/logger.go
@@ -18,6 +18,7 @@ import (
"github.com/containous/traefik/v2/pkg/log"
"github.com/containous/traefik/v2/pkg/types"
"github.com/sirupsen/logrus"
+ ptypes "github.com/traefik/paerser/types"
)
type key string
@@ -369,7 +370,7 @@ func (h *Handler) keepAccessLog(statusCode, retryAttempts int, duration time.Dur
return true
}
- if h.config.Filters.MinDuration > 0 && (types.Duration(duration) > h.config.Filters.MinDuration) {
+ if h.config.Filters.MinDuration > 0 && (ptypes.Duration(duration) > h.config.Filters.MinDuration) {
return true
}
diff --git a/pkg/middlewares/accesslog/logger_test.go b/pkg/middlewares/accesslog/logger_test.go
index eeeceba7b..20547d05d 100644
--- a/pkg/middlewares/accesslog/logger_test.go
+++ b/pkg/middlewares/accesslog/logger_test.go
@@ -18,6 +18,7 @@ import (
"github.com/containous/traefik/v2/pkg/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ ptypes "github.com/traefik/paerser/types"
)
var (
@@ -527,7 +528,7 @@ func TestNewLogHandlerOutputStdout(t *testing.T) {
FilePath: "",
Format: CommonFormat,
Filters: &types.AccessLogFilters{
- MinDuration: types.Duration(1 * time.Hour),
+ MinDuration: ptypes.Duration(1 * time.Hour),
},
},
expectedLog: ``,
@@ -538,7 +539,7 @@ func TestNewLogHandlerOutputStdout(t *testing.T) {
FilePath: "",
Format: CommonFormat,
Filters: &types.AccessLogFilters{
- MinDuration: types.Duration(1 * time.Millisecond),
+ MinDuration: ptypes.Duration(1 * time.Millisecond),
},
},
expectedLog: `TestHost - TestUser [13/Apr/2016:07:14:19 -0700] "POST testpath HTTP/0.0" 123 12 "testReferer" "testUserAgent" 23 "testRouter" "http://127.0.0.1/testService" 1ms`,
diff --git a/pkg/middlewares/auth/forward_test.go b/pkg/middlewares/auth/forward_test.go
index 7674a1384..44486379a 100644
--- a/pkg/middlewares/auth/forward_test.go
+++ b/pkg/middlewares/auth/forward_test.go
@@ -28,7 +28,7 @@ func TestForwardAuthFail(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
http.Error(w, "Forbidden", http.StatusForbidden)
}))
- defer server.Close()
+ t.Cleanup(server.Close)
middleware, err := NewForward(context.Background(), next, dynamic.ForwardAuth{
Address: server.URL,
@@ -36,7 +36,7 @@ func TestForwardAuthFail(t *testing.T) {
require.NoError(t, err)
ts := httptest.NewServer(middleware)
- defer ts.Close()
+ t.Cleanup(ts.Close)
req := testhelpers.MustNewRequest(http.MethodGet, ts.URL, nil)
res, err := http.DefaultClient.Do(req)
@@ -59,7 +59,7 @@ func TestForwardAuthSuccess(t *testing.T) {
w.Header().Add("X-Auth-Group", "group2")
fmt.Fprintln(w, "Success")
}))
- defer server.Close()
+ t.Cleanup(server.Close)
next := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "user@example.com", r.Header.Get("X-Auth-User"))
@@ -76,7 +76,7 @@ func TestForwardAuthSuccess(t *testing.T) {
require.NoError(t, err)
ts := httptest.NewServer(middleware)
- defer ts.Close()
+ t.Cleanup(ts.Close)
req := testhelpers.MustNewRequest(http.MethodGet, ts.URL, nil)
req.Header.Set("X-Auth-Group", "admin_group")
@@ -95,20 +95,19 @@ func TestForwardAuthRedirect(t *testing.T) {
authTs := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, "http://example.com/redirect-test", http.StatusFound)
}))
- defer authTs.Close()
+ t.Cleanup(authTs.Close)
next := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "traefik")
})
- auth := dynamic.ForwardAuth{
- Address: authTs.URL,
- }
+ auth := dynamic.ForwardAuth{Address: authTs.URL}
+
authMiddleware, err := NewForward(context.Background(), next, auth, "authTest")
require.NoError(t, err)
ts := httptest.NewServer(authMiddleware)
- defer ts.Close()
+ t.Cleanup(ts.Close)
client := &http.Client{
CheckRedirect: func(r *http.Request, via []*http.Request) error {
@@ -139,7 +138,7 @@ func TestForwardAuthRemoveHopByHopHeaders(t *testing.T) {
headers := w.Header()
for _, header := range forward.HopHeaders {
if header == forward.TransferEncoding {
- headers.Add(header, "identity")
+ headers.Set(header, "chunked")
} else {
headers.Add(header, "test")
}
@@ -147,29 +146,29 @@ func TestForwardAuthRemoveHopByHopHeaders(t *testing.T) {
http.Redirect(w, r, "http://example.com/redirect-test", http.StatusFound)
}))
- defer authTs.Close()
+ t.Cleanup(authTs.Close)
next := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "traefik")
})
- auth := dynamic.ForwardAuth{
- Address: authTs.URL,
- }
- authMiddleware, err := NewForward(context.Background(), next, auth, "authTest")
- assert.NoError(t, err, "there should be no error")
+ auth := dynamic.ForwardAuth{Address: authTs.URL}
+
+ authMiddleware, err := NewForward(context.Background(), next, auth, "authTest")
+ require.NoError(t, err)
ts := httptest.NewServer(authMiddleware)
- defer ts.Close()
+ t.Cleanup(ts.Close)
client := &http.Client{
CheckRedirect: func(r *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse
},
}
+
req := testhelpers.MustNewRequest(http.MethodGet, ts.URL, nil)
res, err := client.Do(req)
- assert.NoError(t, err, "there should be no error")
+ require.NoError(t, err)
assert.Equal(t, http.StatusFound, res.StatusCode, "they should be equal")
for _, header := range forward.HopHeaders {
@@ -177,11 +176,11 @@ func TestForwardAuthRemoveHopByHopHeaders(t *testing.T) {
}
location, err := res.Location()
- assert.NoError(t, err, "there should be no error")
+ require.NoError(t, err)
assert.Equal(t, "http://example.com/redirect-test", location.String(), "they should be equal")
body, err := ioutil.ReadAll(res.Body)
- assert.NoError(t, err, "there should be no error")
+ require.NoError(t, err)
assert.NotEmpty(t, string(body), "there should be something in the body")
}
@@ -192,7 +191,7 @@ func TestForwardAuthFailResponseHeaders(t *testing.T) {
w.Header().Add("X-Foo", "bar")
http.Error(w, "Forbidden", http.StatusForbidden)
}))
- defer authTs.Close()
+ t.Cleanup(authTs.Close)
next := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "traefik")
@@ -205,7 +204,7 @@ func TestForwardAuthFailResponseHeaders(t *testing.T) {
require.NoError(t, err)
ts := httptest.NewServer(authMiddleware)
- defer ts.Close()
+ t.Cleanup(ts.Close)
req := testhelpers.MustNewRequest(http.MethodGet, ts.URL, nil)
@@ -407,7 +406,7 @@ func TestForwardAuthUsesTracing(t *testing.T) {
t.Errorf("expected Mockpfx-Ids-Traceid header to be present in request")
}
}))
- defer server.Close()
+ t.Cleanup(server.Close)
next := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}))
@@ -426,7 +425,7 @@ func TestForwardAuthUsesTracing(t *testing.T) {
next = tracingMiddleware.NewEntryPoint(context.Background(), tr, "tracingTest", next)
ts := httptest.NewServer(next)
- defer ts.Close()
+ t.Cleanup(ts.Close)
req := testhelpers.MustNewRequest(http.MethodGet, ts.URL, nil)
res, err := http.DefaultClient.Do(req)
diff --git a/pkg/middlewares/ratelimiter/rate_limiter_test.go b/pkg/middlewares/ratelimiter/rate_limiter_test.go
index c0f4be1b2..0f2dc49a2 100644
--- a/pkg/middlewares/ratelimiter/rate_limiter_test.go
+++ b/pkg/middlewares/ratelimiter/rate_limiter_test.go
@@ -10,9 +10,9 @@ import (
"github.com/containous/traefik/v2/pkg/config/dynamic"
"github.com/containous/traefik/v2/pkg/testhelpers"
- "github.com/containous/traefik/v2/pkg/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ ptypes "github.com/traefik/paerser/types"
"github.com/vulcand/oxy/utils"
)
@@ -37,7 +37,7 @@ func TestNewRateLimiter(t *testing.T) {
desc: "maxDelay computation, low rate regime",
config: dynamic.RateLimit{
Average: 2,
- Period: types.Duration(10 * time.Second),
+ Period: ptypes.Duration(10 * time.Second),
Burst: 10,
},
expectedMaxDelay: 500 * time.Millisecond,
@@ -183,7 +183,7 @@ func TestRateLimit(t *testing.T) {
desc: "lower than 1/s",
config: dynamic.RateLimit{
Average: 5,
- Period: types.Duration(10 * time.Second),
+ Period: ptypes.Duration(10 * time.Second),
},
loadDuration: 2 * time.Second,
incomingLoad: 100,
@@ -193,7 +193,7 @@ func TestRateLimit(t *testing.T) {
desc: "lower than 1/s, longer",
config: dynamic.RateLimit{
Average: 5,
- Period: types.Duration(10 * time.Second),
+ Period: ptypes.Duration(10 * time.Second),
},
loadDuration: time.Minute,
incomingLoad: 100,
@@ -203,7 +203,7 @@ func TestRateLimit(t *testing.T) {
desc: "lower than 1/s, longer, harsher",
config: dynamic.RateLimit{
Average: 1,
- Period: types.Duration(time.Minute),
+ Period: ptypes.Duration(time.Minute),
},
loadDuration: time.Minute,
incomingLoad: 100,
@@ -213,7 +213,7 @@ func TestRateLimit(t *testing.T) {
desc: "period below 1 second",
config: dynamic.RateLimit{
Average: 50,
- Period: types.Duration(500 * time.Millisecond),
+ Period: ptypes.Duration(500 * time.Millisecond),
},
loadDuration: 2 * time.Second,
incomingLoad: 300,
diff --git a/pkg/middlewares/requestdecorator/request_decorator_test.go b/pkg/middlewares/requestdecorator/request_decorator_test.go
index a757c1f60..8c6550e3c 100644
--- a/pkg/middlewares/requestdecorator/request_decorator_test.go
+++ b/pkg/middlewares/requestdecorator/request_decorator_test.go
@@ -4,9 +4,8 @@ import (
"net/http"
"testing"
- "github.com/containous/traefik/v2/pkg/types"
-
"github.com/containous/traefik/v2/pkg/testhelpers"
+ "github.com/containous/traefik/v2/pkg/types"
"github.com/stretchr/testify/assert"
)
diff --git a/pkg/pilot/pilot.go b/pkg/pilot/pilot.go
index 5be1d6fe1..9f230916f 100644
--- a/pkg/pilot/pilot.go
+++ b/pkg/pilot/pilot.go
@@ -12,6 +12,7 @@ import (
"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/metrics"
"github.com/containous/traefik/v2/pkg/safe"
"github.com/containous/traefik/v2/pkg/version"
)
@@ -44,10 +45,11 @@ type serviceInfoRepresentation struct {
type instanceInfo struct {
ID string `json:"id,omitempty"`
Configuration RunTimeRepresentation `json:"configuration,omitempty"`
+ Metrics []metrics.PilotMetric `json:"metrics,omitempty"`
}
// New creates a new Pilot.
-func New(token string, pool *safe.Pool) *Pilot {
+func New(token string, metricsRegistry *metrics.PilotRegistry, pool *safe.Pool) *Pilot {
return &Pilot{
rtConfChan: make(chan *runtime.Configuration),
client: &client{
@@ -55,7 +57,8 @@ func New(token string, pool *safe.Pool) *Pilot {
httpClient: &http.Client{Timeout: 5 * time.Second},
baseURL: baseURL,
},
- routinesPool: pool,
+ routinesPool: pool,
+ metricsRegistry: metricsRegistry,
}
}
@@ -64,8 +67,9 @@ type Pilot struct {
routinesPool *safe.Pool
client *client
- rtConf *runtime.Configuration
- rtConfChan chan *runtime.Configuration
+ rtConf *runtime.Configuration
+ rtConfChan chan *runtime.Configuration
+ metricsRegistry *metrics.PilotRegistry
}
// SetRuntimeConfiguration stores the runtime configuration.
@@ -99,8 +103,8 @@ func (p *Pilot) getRepresentation() RunTimeRepresentation {
return result
}
-func (p *Pilot) sendData(ctx context.Context, conf RunTimeRepresentation) {
- err := p.client.SendData(ctx, conf)
+func (p *Pilot) sendData(ctx context.Context, conf RunTimeRepresentation, pilotMetrics []metrics.PilotMetric) {
+ err := p.client.SendData(ctx, conf, pilotMetrics)
if err != nil {
log.WithoutContext().Error(err)
}
@@ -117,9 +121,10 @@ func (p *Pilot) Tick(ctx context.Context) {
}
conf := p.getRepresentation()
+ pilotMetrics := p.metricsRegistry.Data()
p.routinesPool.GoCtx(func(ctxRt context.Context) {
- p.sendData(ctxRt, conf)
+ p.sendData(ctxRt, conf, pilotMetrics)
})
ticker := time.NewTicker(pilotTimer)
@@ -129,9 +134,10 @@ func (p *Pilot) Tick(ctx context.Context) {
log.WithoutContext().Debugf("Send to pilot: %s", tick)
conf := p.getRepresentation()
+ pilotMetrics := p.metricsRegistry.Data()
p.routinesPool.GoCtx(func(ctxRt context.Context) {
- p.sendData(ctxRt, conf)
+ p.sendData(ctxRt, conf, pilotMetrics)
})
case rtConf := <-p.rtConfChan:
p.rtConf = rtConf
@@ -184,13 +190,13 @@ func (c *client) createUUID() (string, error) {
}
// SendData sends data to Pilot.
-func (c *client) SendData(ctx context.Context, rtConf RunTimeRepresentation) error {
+func (c *client) SendData(ctx context.Context, rtConf RunTimeRepresentation, pilotMetrics []metrics.PilotMetric) error {
exponentialBackOff := backoff.NewExponentialBackOff()
exponentialBackOff.MaxElapsedTime = maxElapsedTime
return backoff.RetryNotify(
func() error {
- return c.sendData(rtConf)
+ return c.sendData(rtConf, pilotMetrics)
},
backoff.WithContext(exponentialBackOff, ctx),
func(err error, duration time.Duration) {
@@ -198,7 +204,7 @@ func (c *client) SendData(ctx context.Context, rtConf RunTimeRepresentation) err
})
}
-func (c *client) sendData(_ RunTimeRepresentation) error {
+func (c *client) sendData(_ RunTimeRepresentation, pilotMetrics []metrics.PilotMetric) error {
if len(c.uuid) == 0 {
var err error
c.uuid, err = c.createUUID()
@@ -210,7 +216,8 @@ func (c *client) sendData(_ RunTimeRepresentation) error {
}
info := instanceInfo{
- ID: c.uuid,
+ ID: c.uuid,
+ Metrics: pilotMetrics,
}
b, err := json.Marshal(info)
diff --git a/pkg/pilot/pilot_test.go b/pkg/pilot/pilot_test.go
index 8ba61ec94..8c0aa8d99 100644
--- a/pkg/pilot/pilot_test.go
+++ b/pkg/pilot/pilot_test.go
@@ -10,6 +10,7 @@ import (
"time"
"github.com/containous/traefik/v2/pkg/config/runtime"
+ "github.com/containous/traefik/v2/pkg/metrics"
"github.com/containous/traefik/v2/pkg/safe"
"github.com/stretchr/testify/require"
)
@@ -43,7 +44,7 @@ func TestTick(t *testing.T) {
receivedConfig <- true
})
- pilot := New("token", safe.NewPool(context.Background()))
+ pilot := New("token", metrics.RegisterPilot(), safe.NewPool(context.Background()))
pilot.client.baseURL = server.URL
ctx, cancel := context.WithCancel(context.Background())
@@ -118,6 +119,6 @@ func TestClient_SendConfiguration(t *testing.T) {
token: myToken,
}
- err := client.SendData(context.Background(), RunTimeRepresentation{})
+ err := client.SendData(context.Background(), RunTimeRepresentation{}, []metrics.PilotMetric{})
require.NoError(t, err)
}
diff --git a/pkg/provider/acme/provider.go b/pkg/provider/acme/provider.go
index d95cbd0c0..33e090c2d 100644
--- a/pkg/provider/acme/provider.go
+++ b/pkg/provider/acme/provider.go
@@ -25,6 +25,7 @@ import (
"github.com/go-acme/lego/v3/lego"
"github.com/go-acme/lego/v3/providers/dns"
"github.com/go-acme/lego/v3/registration"
+ ptypes "github.com/traefik/paerser/types"
)
// oscpMustStaple enables OSCP stapling as from https://github.com/go-acme/lego/issues/270.
@@ -63,10 +64,10 @@ type Certificate struct {
// DNSChallenge contains DNS challenge Configuration.
type DNSChallenge struct {
- Provider string `description:"Use a DNS-01 based challenge provider rather than HTTPS." json:"provider,omitempty" toml:"provider,omitempty" yaml:"provider,omitempty"`
- DelayBeforeCheck types.Duration `description:"Assume DNS propagates after a delay in seconds rather than finding and querying nameservers." json:"delayBeforeCheck,omitempty" toml:"delayBeforeCheck,omitempty" yaml:"delayBeforeCheck,omitempty"`
- Resolvers []string `description:"Use following DNS servers to resolve the FQDN authority." json:"resolvers,omitempty" toml:"resolvers,omitempty" yaml:"resolvers,omitempty"`
- DisablePropagationCheck bool `description:"Disable the DNS propagation checks before notifying ACME that the DNS challenge is ready. [not recommended]" json:"disablePropagationCheck,omitempty" toml:"disablePropagationCheck,omitempty" yaml:"disablePropagationCheck,omitempty"`
+ Provider string `description:"Use a DNS-01 based challenge provider rather than HTTPS." json:"provider,omitempty" toml:"provider,omitempty" yaml:"provider,omitempty"`
+ DelayBeforeCheck ptypes.Duration `description:"Assume DNS propagates after a delay in seconds rather than finding and querying nameservers." json:"delayBeforeCheck,omitempty" toml:"delayBeforeCheck,omitempty" yaml:"delayBeforeCheck,omitempty"`
+ Resolvers []string `description:"Use following DNS servers to resolve the FQDN authority." json:"resolvers,omitempty" toml:"resolvers,omitempty" yaml:"resolvers,omitempty"`
+ DisablePropagationCheck bool `description:"Disable the DNS propagation checks before notifying ACME that the DNS challenge is ready. [not recommended]" json:"disablePropagationCheck,omitempty" toml:"disablePropagationCheck,omitempty" yaml:"disablePropagationCheck,omitempty"`
}
// HTTPChallenge contains HTTP challenge Configuration.
diff --git a/pkg/provider/consulcatalog/consul_catalog.go b/pkg/provider/consulcatalog/consul_catalog.go
index 01b70579e..12aeb1456 100644
--- a/pkg/provider/consulcatalog/consul_catalog.go
+++ b/pkg/provider/consulcatalog/consul_catalog.go
@@ -17,6 +17,7 @@ import (
"github.com/containous/traefik/v2/pkg/safe"
"github.com/containous/traefik/v2/pkg/types"
"github.com/hashicorp/consul/api"
+ ptypes "github.com/traefik/paerser/types"
)
// DefaultTemplateRule The default template for the default rule.
@@ -41,7 +42,7 @@ type Provider struct {
Constraints string `description:"Constraints is an expression that Traefik matches against the container's labels to determine whether to create any route for that container." json:"constraints,omitempty" toml:"constraints,omitempty" yaml:"constraints,omitempty" export:"true"`
Endpoint *EndpointConfig `description:"Consul endpoint settings" json:"endpoint,omitempty" toml:"endpoint,omitempty" yaml:"endpoint,omitempty" export:"true"`
Prefix string `description:"Prefix for consul service tags. Default 'traefik'" json:"prefix,omitempty" toml:"prefix,omitempty" yaml:"prefix,omitempty" export:"true"`
- RefreshInterval types.Duration `description:"Interval for check Consul API. Default 100ms" json:"refreshInterval,omitempty" toml:"refreshInterval,omitempty" yaml:"refreshInterval,omitempty" export:"true"`
+ RefreshInterval ptypes.Duration `description:"Interval for check Consul API. Default 100ms" json:"refreshInterval,omitempty" toml:"refreshInterval,omitempty" yaml:"refreshInterval,omitempty" export:"true"`
RequireConsistent bool `description:"Forces the read to be fully consistent." json:"requireConsistent,omitempty" toml:"requireConsistent,omitempty" yaml:"requireConsistent,omitempty" export:"true"`
Stale bool `description:"Use stale consistency for catalog reads." json:"stale,omitempty" toml:"stale,omitempty" yaml:"stale,omitempty" export:"true"`
Cache bool `description:"Use local agent caching for catalog reads." json:"cache,omitempty" toml:"cache,omitempty" yaml:"cache,omitempty" export:"true"`
@@ -60,7 +61,7 @@ type EndpointConfig struct {
Token string `description:"Token is used to provide a per-request ACL token which overrides the agent's default token" json:"token,omitempty" toml:"token,omitempty" yaml:"token,omitempty" export:"true"`
TLS *types.ClientTLS `description:"Enable TLS support." json:"tls,omitempty" toml:"tls,omitempty" yaml:"tls,omitempty" export:"true"`
HTTPAuth *EndpointHTTPAuthConfig `description:"Auth info to use for http access" json:"httpAuth,omitempty" toml:"httpAuth,omitempty" yaml:"httpAuth,omitempty" export:"true"`
- EndpointWaitTime types.Duration `description:"WaitTime limits how long a Watch will block. If not provided, the agent default values will be used" json:"endpointWaitTime,omitempty" toml:"endpointWaitTime,omitempty" yaml:"endpointWaitTime,omitempty" export:"true"`
+ EndpointWaitTime ptypes.Duration `description:"WaitTime limits how long a Watch will block. If not provided, the agent default values will be used" json:"endpointWaitTime,omitempty" toml:"endpointWaitTime,omitempty" yaml:"endpointWaitTime,omitempty" export:"true"`
}
// SetDefaults sets the default values.
@@ -79,7 +80,7 @@ func (p *Provider) SetDefaults() {
endpoint := &EndpointConfig{}
endpoint.SetDefaults()
p.Endpoint = endpoint
- p.RefreshInterval = types.Duration(15 * time.Second)
+ p.RefreshInterval = ptypes.Duration(15 * time.Second)
p.Prefix = "traefik"
p.ExposedByDefault = true
p.DefaultRule = DefaultTemplateRule
diff --git a/pkg/provider/docker/docker.go b/pkg/provider/docker/docker.go
index 8ca9405fe..49f51eded 100644
--- a/pkg/provider/docker/docker.go
+++ b/pkg/provider/docker/docker.go
@@ -29,10 +29,11 @@ import (
"github.com/docker/docker/client"
"github.com/docker/go-connections/nat"
"github.com/docker/go-connections/sockets"
+ ptypes "github.com/traefik/paerser/types"
)
const (
- // DockerAPIVersion is a constant holding the version of the Provider API traefik will use
+ // DockerAPIVersion is a constant holding the version of the Provider API traefik will use.
DockerAPIVersion = "1.24"
// SwarmAPIVersion is a constant holding the version of the Provider API traefik will use.
@@ -55,7 +56,7 @@ type Provider struct {
UseBindPortIP bool `description:"Use the ip address from the bound port, rather than from the inner network." json:"useBindPortIP,omitempty" toml:"useBindPortIP,omitempty" yaml:"useBindPortIP,omitempty" export:"true"`
SwarmMode bool `description:"Use Docker on Swarm Mode." json:"swarmMode,omitempty" toml:"swarmMode,omitempty" yaml:"swarmMode,omitempty" export:"true"`
Network string `description:"Default Docker network used." json:"network,omitempty" toml:"network,omitempty" yaml:"network,omitempty" export:"true"`
- SwarmModeRefreshSeconds types.Duration `description:"Polling interval for swarm mode." json:"swarmModeRefreshSeconds,omitempty" toml:"swarmModeRefreshSeconds,omitempty" yaml:"swarmModeRefreshSeconds,omitempty" export:"true"`
+ SwarmModeRefreshSeconds ptypes.Duration `description:"Polling interval for swarm mode." json:"swarmModeRefreshSeconds,omitempty" toml:"swarmModeRefreshSeconds,omitempty" yaml:"swarmModeRefreshSeconds,omitempty" export:"true"`
defaultRuleTpl *template.Template
}
@@ -65,7 +66,7 @@ func (p *Provider) SetDefaults() {
p.ExposedByDefault = true
p.Endpoint = "unix:///var/run/docker.sock"
p.SwarmMode = false
- p.SwarmModeRefreshSeconds = types.Duration(15 * time.Second)
+ p.SwarmModeRefreshSeconds = ptypes.Duration(15 * time.Second)
p.DefaultRule = DefaultTemplateRule
}
diff --git a/pkg/provider/docker/swarm_test.go b/pkg/provider/docker/swarm_test.go
index 45d7957a2..541af22f2 100644
--- a/pkg/provider/docker/swarm_test.go
+++ b/pkg/provider/docker/swarm_test.go
@@ -7,7 +7,6 @@ import (
"time"
"github.com/davecgh/go-spew/spew"
- docker "github.com/docker/docker/api/types"
dockertypes "github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/swarm"
dockerclient "github.com/docker/docker/client"
@@ -36,7 +35,7 @@ func TestListTasks(t *testing.T) {
tasks []swarm.Task
isGlobalSVC bool
expectedTasks []string
- networks map[string]*docker.NetworkResource
+ networks map[string]*dockertypes.NetworkResource
}{
{
service: swarmService(serviceName("container")),
@@ -71,7 +70,7 @@ func TestListTasks(t *testing.T) {
"container.1",
"container.4",
},
- networks: map[string]*docker.NetworkResource{
+ networks: map[string]*dockertypes.NetworkResource{
"1": {
Name: "foo",
},
@@ -300,7 +299,7 @@ func TestSwarmTaskParsing(t *testing.T) {
tasks []swarm.Task
isGlobalSVC bool
expected map[string]dockerData
- networks map[string]*docker.NetworkResource
+ networks map[string]*dockertypes.NetworkResource
}{
{
service: swarmService(serviceName("container")),
@@ -321,7 +320,7 @@ func TestSwarmTaskParsing(t *testing.T) {
Name: "container.3",
},
},
- networks: map[string]*docker.NetworkResource{
+ networks: map[string]*dockertypes.NetworkResource{
"1": {
Name: "foo",
},
@@ -346,7 +345,7 @@ func TestSwarmTaskParsing(t *testing.T) {
Name: "container.id3",
},
},
- networks: map[string]*docker.NetworkResource{
+ networks: map[string]*dockertypes.NetworkResource{
"1": {
Name: "foo",
},
@@ -384,7 +383,7 @@ func TestSwarmTaskParsing(t *testing.T) {
},
},
},
- networks: map[string]*docker.NetworkResource{
+ networks: map[string]*dockertypes.NetworkResource{
"1": {
Name: "vlan",
},
diff --git a/pkg/provider/ecs/config.go b/pkg/provider/ecs/config.go
index c274082b6..2bb0ad712 100644
--- a/pkg/provider/ecs/config.go
+++ b/pkg/provider/ecs/config.go
@@ -9,7 +9,6 @@ import (
"strings"
"github.com/aws/aws-sdk-go/service/ec2"
-
"github.com/containous/traefik/v2/pkg/config/dynamic"
"github.com/containous/traefik/v2/pkg/config/label"
"github.com/containous/traefik/v2/pkg/log"
diff --git a/pkg/provider/ecs/ecs.go b/pkg/provider/ecs/ecs.go
index 08549d398..cfc62ef3e 100644
--- a/pkg/provider/ecs/ecs.go
+++ b/pkg/provider/ecs/ecs.go
@@ -14,15 +14,13 @@ import (
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/aws/aws-sdk-go/service/ecs"
-
"github.com/cenkalti/backoff/v4"
- "github.com/patrickmn/go-cache"
-
"github.com/containous/traefik/v2/pkg/config/dynamic"
"github.com/containous/traefik/v2/pkg/job"
"github.com/containous/traefik/v2/pkg/log"
"github.com/containous/traefik/v2/pkg/provider"
"github.com/containous/traefik/v2/pkg/safe"
+ "github.com/patrickmn/go-cache"
)
// Provider holds configurations of the provider.
@@ -97,14 +95,16 @@ func (p *Provider) Init() error {
}
func (p *Provider) createClient(logger log.Logger) (*awsClient, error) {
- sess, err := session.NewSession()
+ sess, err := session.NewSessionWithOptions(session.Options{
+ SharedConfigState: session.SharedConfigEnable,
+ })
if err != nil {
return nil, err
}
ec2meta := ec2metadata.New(sess)
- if p.Region == "" {
- logger.Infoln("No EC2 region provided, querying instance metadata endpoint...")
+ if p.Region == "" && ec2meta.Available() {
+ logger.Infoln("No region provided, querying instance metadata endpoint...")
identity, err := ec2meta.GetInstanceIdentityDocument()
if err != nil {
return nil, err
@@ -113,7 +113,6 @@ func (p *Provider) createClient(logger log.Logger) (*awsClient, error) {
}
cfg := &aws.Config{
- Region: &p.Region,
Credentials: credentials.NewChainCredentials(
[]credentials.Provider{
&credentials.StaticProvider{
@@ -128,6 +127,11 @@ func (p *Provider) createClient(logger log.Logger) (*awsClient, error) {
}),
}
+ // Set the region if it is defined by the user or resolved from the EC2 metadata.
+ if p.Region != "" {
+ cfg.Region = &p.Region
+ }
+
cfg.WithLogger(aws.LoggerFunc(func(args ...interface{}) {
logger.Debug(args...)
}))
diff --git a/pkg/provider/file/file.go b/pkg/provider/file/file.go
index 35bd0c75c..da59d5df3 100644
--- a/pkg/provider/file/file.go
+++ b/pkg/provider/file/file.go
@@ -13,11 +13,11 @@ import (
"github.com/Masterminds/sprig"
"github.com/containous/traefik/v2/pkg/config/dynamic"
- "github.com/containous/traefik/v2/pkg/config/file"
"github.com/containous/traefik/v2/pkg/log"
"github.com/containous/traefik/v2/pkg/provider"
"github.com/containous/traefik/v2/pkg/safe"
"github.com/containous/traefik/v2/pkg/tls"
+ "github.com/traefik/paerser/file"
"gopkg.in/fsnotify.v1"
)
diff --git a/pkg/provider/http/http.go b/pkg/provider/http/http.go
index a42b3ae1c..067952b81 100644
--- a/pkg/provider/http/http.go
+++ b/pkg/provider/http/http.go
@@ -10,13 +10,14 @@ import (
"github.com/cenkalti/backoff/v4"
"github.com/containous/traefik/v2/pkg/config/dynamic"
- "github.com/containous/traefik/v2/pkg/config/file"
"github.com/containous/traefik/v2/pkg/job"
"github.com/containous/traefik/v2/pkg/log"
"github.com/containous/traefik/v2/pkg/provider"
"github.com/containous/traefik/v2/pkg/safe"
"github.com/containous/traefik/v2/pkg/tls"
"github.com/containous/traefik/v2/pkg/types"
+ "github.com/traefik/paerser/file"
+ ptypes "github.com/traefik/paerser/types"
)
var _ provider.Provider = (*Provider)(nil)
@@ -24,8 +25,8 @@ var _ provider.Provider = (*Provider)(nil)
// Provider is a provider.Provider implementation that queries an HTTP(s) endpoint for a configuration.
type Provider struct {
Endpoint string `description:"Load configuration from this endpoint." json:"endpoint" toml:"endpoint" yaml:"endpoint" export:"true"`
- PollInterval types.Duration `description:"Polling interval for endpoint." json:"pollInterval,omitempty" toml:"pollInterval,omitempty" yaml:"pollInterval,omitempty"`
- PollTimeout types.Duration `description:"Polling timeout for endpoint." json:"pollTimeout,omitempty" toml:"pollTimeout,omitempty" yaml:"pollTimeout,omitempty"`
+ PollInterval ptypes.Duration `description:"Polling interval for endpoint." json:"pollInterval,omitempty" toml:"pollInterval,omitempty" yaml:"pollInterval,omitempty"`
+ PollTimeout ptypes.Duration `description:"Polling timeout for endpoint." json:"pollTimeout,omitempty" toml:"pollTimeout,omitempty" yaml:"pollTimeout,omitempty"`
TLS *types.ClientTLS `description:"Enable TLS support." json:"tls,omitempty" toml:"tls,omitempty" yaml:"tls,omitempty" export:"true"`
httpClient *http.Client
lastConfigurationHash uint64
@@ -33,8 +34,8 @@ type Provider struct {
// SetDefaults sets the default values.
func (p *Provider) SetDefaults() {
- p.PollInterval = types.Duration(5 * time.Second)
- p.PollTimeout = types.Duration(5 * time.Second)
+ p.PollInterval = ptypes.Duration(5 * time.Second)
+ p.PollTimeout = ptypes.Duration(5 * time.Second)
}
// Init the provider.
diff --git a/pkg/provider/http/http_test.go b/pkg/provider/http/http_test.go
index 3ab0b2fd6..9a11f5c1c 100644
--- a/pkg/provider/http/http_test.go
+++ b/pkg/provider/http/http_test.go
@@ -11,16 +11,16 @@ import (
"github.com/containous/traefik/v2/pkg/config/dynamic"
"github.com/containous/traefik/v2/pkg/safe"
"github.com/containous/traefik/v2/pkg/tls"
- "github.com/containous/traefik/v2/pkg/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ ptypes "github.com/traefik/paerser/types"
)
func TestProvider_Init(t *testing.T) {
tests := []struct {
desc string
endpoint string
- pollInterval types.Duration
+ pollInterval ptypes.Duration
expErr bool
}{
{
@@ -35,7 +35,7 @@ func TestProvider_Init(t *testing.T) {
{
desc: "should not return an error",
endpoint: "http://localhost:8080",
- pollInterval: types.Duration(time.Second),
+ pollInterval: ptypes.Duration(time.Second),
expErr: false,
},
}
@@ -63,8 +63,8 @@ func TestProvider_SetDefaults(t *testing.T) {
provider.SetDefaults()
- assert.Equal(t, provider.PollInterval, types.Duration(5*time.Second))
- assert.Equal(t, provider.PollTimeout, types.Duration(5*time.Second))
+ assert.Equal(t, provider.PollInterval, ptypes.Duration(5*time.Second))
+ assert.Equal(t, provider.PollTimeout, ptypes.Duration(5*time.Second))
}
func TestProvider_fetchConfigurationData(t *testing.T) {
@@ -98,8 +98,8 @@ func TestProvider_fetchConfigurationData(t *testing.T) {
provider := Provider{
Endpoint: server.URL,
- PollInterval: types.Duration(1 * time.Second),
- PollTimeout: types.Duration(1 * time.Second),
+ PollInterval: ptypes.Duration(1 * time.Second),
+ PollTimeout: ptypes.Duration(1 * time.Second),
}
err := provider.Init()
@@ -181,8 +181,8 @@ func TestProvider_Provide(t *testing.T) {
provider := Provider{
Endpoint: server.URL,
- PollTimeout: types.Duration(1 * time.Second),
- PollInterval: types.Duration(100 * time.Millisecond),
+ PollTimeout: ptypes.Duration(1 * time.Second),
+ PollInterval: ptypes.Duration(100 * time.Millisecond),
}
err := provider.Init()
@@ -235,8 +235,8 @@ func TestProvider_ProvideConfigurationOnlyOnceIfUnchanged(t *testing.T) {
provider := Provider{
Endpoint: server.URL + "/endpoint",
- PollTimeout: types.Duration(1 * time.Second),
- PollInterval: types.Duration(100 * time.Millisecond),
+ PollTimeout: ptypes.Duration(1 * time.Second),
+ PollInterval: ptypes.Duration(100 * time.Millisecond),
}
err := provider.Init()
diff --git a/pkg/provider/kubernetes/crd/kubernetes.go b/pkg/provider/kubernetes/crd/kubernetes.go
index 51880b864..630be4239 100644
--- a/pkg/provider/kubernetes/crd/kubernetes.go
+++ b/pkg/provider/kubernetes/crd/kubernetes.go
@@ -20,8 +20,8 @@ import (
"github.com/containous/traefik/v2/pkg/provider/kubernetes/crd/traefik/v1alpha1"
"github.com/containous/traefik/v2/pkg/safe"
"github.com/containous/traefik/v2/pkg/tls"
- "github.com/containous/traefik/v2/pkg/types"
"github.com/mitchellh/hashstructure"
+ ptypes "github.com/traefik/paerser/types"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/labels"
)
@@ -38,14 +38,14 @@ const (
// Provider holds configurations of the provider.
type Provider struct {
- Endpoint string `description:"Kubernetes server endpoint (required for external cluster client)." json:"endpoint,omitempty" toml:"endpoint,omitempty" yaml:"endpoint,omitempty"`
- Token string `description:"Kubernetes bearer token (not needed for in-cluster client)." json:"token,omitempty" toml:"token,omitempty" yaml:"token,omitempty"`
- CertAuthFilePath string `description:"Kubernetes certificate authority file path (not needed for in-cluster client)." json:"certAuthFilePath,omitempty" toml:"certAuthFilePath,omitempty" yaml:"certAuthFilePath,omitempty"`
- DisablePassHostHeaders bool `description:"Kubernetes disable PassHost Headers." json:"disablePassHostHeaders,omitempty" toml:"disablePassHostHeaders,omitempty" yaml:"disablePassHostHeaders,omitempty" export:"true"`
- Namespaces []string `description:"Kubernetes namespaces." json:"namespaces,omitempty" toml:"namespaces,omitempty" yaml:"namespaces,omitempty" export:"true"`
- LabelSelector string `description:"Kubernetes label selector to use." json:"labelSelector,omitempty" toml:"labelSelector,omitempty" yaml:"labelSelector,omitempty" export:"true"`
- IngressClass string `description:"Value of kubernetes.io/ingress.class annotation to watch for." json:"ingressClass,omitempty" toml:"ingressClass,omitempty" yaml:"ingressClass,omitempty" export:"true"`
- ThrottleDuration types.Duration `description:"Ingress refresh throttle duration" json:"throttleDuration,omitempty" toml:"throttleDuration,omitempty" yaml:"throttleDuration,omitempty"`
+ Endpoint string `description:"Kubernetes server endpoint (required for external cluster client)." json:"endpoint,omitempty" toml:"endpoint,omitempty" yaml:"endpoint,omitempty"`
+ Token string `description:"Kubernetes bearer token (not needed for in-cluster client)." json:"token,omitempty" toml:"token,omitempty" yaml:"token,omitempty"`
+ CertAuthFilePath string `description:"Kubernetes certificate authority file path (not needed for in-cluster client)." json:"certAuthFilePath,omitempty" toml:"certAuthFilePath,omitempty" yaml:"certAuthFilePath,omitempty"`
+ DisablePassHostHeaders bool `description:"Kubernetes disable PassHost Headers." json:"disablePassHostHeaders,omitempty" toml:"disablePassHostHeaders,omitempty" yaml:"disablePassHostHeaders,omitempty" export:"true"`
+ Namespaces []string `description:"Kubernetes namespaces." json:"namespaces,omitempty" toml:"namespaces,omitempty" yaml:"namespaces,omitempty" export:"true"`
+ LabelSelector string `description:"Kubernetes label selector to use." json:"labelSelector,omitempty" toml:"labelSelector,omitempty" yaml:"labelSelector,omitempty" export:"true"`
+ IngressClass string `description:"Value of kubernetes.io/ingress.class annotation to watch for." json:"ingressClass,omitempty" toml:"ingressClass,omitempty" yaml:"ingressClass,omitempty" export:"true"`
+ ThrottleDuration ptypes.Duration `description:"Ingress refresh throttle duration" json:"throttleDuration,omitempty" toml:"throttleDuration,omitempty" yaml:"throttleDuration,omitempty"`
lastConfiguration safe.Safe
}
diff --git a/pkg/provider/kubernetes/crd/kubernetes_test.go b/pkg/provider/kubernetes/crd/kubernetes_test.go
index eba2ad1b3..e8d68affb 100644
--- a/pkg/provider/kubernetes/crd/kubernetes_test.go
+++ b/pkg/provider/kubernetes/crd/kubernetes_test.go
@@ -4,12 +4,11 @@ import (
"context"
"testing"
- corev1 "k8s.io/api/core/v1"
-
"github.com/containous/traefik/v2/pkg/config/dynamic"
"github.com/containous/traefik/v2/pkg/provider"
"github.com/containous/traefik/v2/pkg/tls"
"github.com/stretchr/testify/assert"
+ corev1 "k8s.io/api/core/v1"
)
var _ provider.Provider = (*Provider)(nil)
diff --git a/pkg/provider/kubernetes/ingress/client_mock_test.go b/pkg/provider/kubernetes/ingress/client_mock_test.go
index 410c96d52..3ef9093db 100644
--- a/pkg/provider/kubernetes/ingress/client_mock_test.go
+++ b/pkg/provider/kubernetes/ingress/client_mock_test.go
@@ -8,14 +8,13 @@ import (
"github.com/hashicorp/go-version"
corev1 "k8s.io/api/core/v1"
extensionsv1beta1 "k8s.io/api/extensions/v1beta1"
- "k8s.io/api/networking/v1beta1"
networkingv1beta1 "k8s.io/api/networking/v1beta1"
)
var _ Client = (*clientMock)(nil)
type clientMock struct {
- ingresses []*v1beta1.Ingress
+ ingresses []*networkingv1beta1.Ingress
services []*corev1.Service
secrets []*corev1.Secret
endpoints []*corev1.Endpoints
@@ -51,7 +50,7 @@ func newClientMock(serverVersion string, paths ...string) clientMock {
c.secrets = append(c.secrets, o)
case *corev1.Endpoints:
c.endpoints = append(c.endpoints, o)
- case *v1beta1.Ingress:
+ case *networkingv1beta1.Ingress:
c.ingresses = append(c.ingresses, o)
case *extensionsv1beta1.Ingress:
ing, err := extensionsToNetworking(o)
@@ -70,7 +69,7 @@ func newClientMock(serverVersion string, paths ...string) clientMock {
return c
}
-func (c clientMock) GetIngresses() []*v1beta1.Ingress {
+func (c clientMock) GetIngresses() []*networkingv1beta1.Ingress {
return c.ingresses
}
@@ -126,6 +125,6 @@ func (c clientMock) WatchAll(namespaces []string, stopCh <-chan struct{}) (<-cha
return c.watchChan, nil
}
-func (c clientMock) UpdateIngressStatus(_ *v1beta1.Ingress, _, _ string) error {
+func (c clientMock) UpdateIngressStatus(_ *networkingv1beta1.Ingress, _, _ string) error {
return c.apiIngressStatusError
}
diff --git a/pkg/provider/kubernetes/ingress/kubernetes.go b/pkg/provider/kubernetes/ingress/kubernetes.go
index 119c0115e..eefc68679 100644
--- a/pkg/provider/kubernetes/ingress/kubernetes.go
+++ b/pkg/provider/kubernetes/ingress/kubernetes.go
@@ -17,10 +17,9 @@ import (
"github.com/containous/traefik/v2/pkg/provider"
"github.com/containous/traefik/v2/pkg/safe"
"github.com/containous/traefik/v2/pkg/tls"
- "github.com/containous/traefik/v2/pkg/types"
"github.com/mitchellh/hashstructure"
+ ptypes "github.com/traefik/paerser/types"
corev1 "k8s.io/api/core/v1"
- "k8s.io/api/networking/v1beta1"
networkingv1beta1 "k8s.io/api/networking/v1beta1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/util/intstr"
@@ -43,7 +42,7 @@ type Provider struct {
LabelSelector string `description:"Kubernetes Ingress label selector to use." json:"labelSelector,omitempty" toml:"labelSelector,omitempty" yaml:"labelSelector,omitempty" export:"true"`
IngressClass string `description:"Value of kubernetes.io/ingress.class annotation to watch for." json:"ingressClass,omitempty" toml:"ingressClass,omitempty" yaml:"ingressClass,omitempty" export:"true"`
IngressEndpoint *EndpointIngress `description:"Kubernetes Ingress Endpoint." json:"ingressEndpoint,omitempty" toml:"ingressEndpoint,omitempty" yaml:"ingressEndpoint,omitempty"`
- ThrottleDuration types.Duration `description:"Ingress refresh throttle duration" json:"throttleDuration,omitempty" toml:"throttleDuration,omitempty" yaml:"throttleDuration,omitempty"`
+ ThrottleDuration ptypes.Duration `description:"Ingress refresh throttle duration" json:"throttleDuration,omitempty" toml:"throttleDuration,omitempty" yaml:"throttleDuration,omitempty"`
lastConfiguration safe.Safe
}
@@ -291,7 +290,7 @@ func (p *Provider) loadConfigurationFromIngresses(ctx context.Context, client Cl
return conf
}
-func (p *Provider) updateIngressStatus(ing *v1beta1.Ingress, k8sClient Client) error {
+func (p *Provider) updateIngressStatus(ing *networkingv1beta1.Ingress, k8sClient Client) error {
// Only process if an EndpointIngress has been configured.
if p.IngressEndpoint == nil {
return nil
@@ -348,7 +347,7 @@ func buildHostRule(host string) string {
return "Host(`" + host + "`)"
}
-func getCertificates(ctx context.Context, ingress *v1beta1.Ingress, k8sClient Client, tlsConfigs map[string]*tls.CertAndStores) error {
+func getCertificates(ctx context.Context, ingress *networkingv1beta1.Ingress, k8sClient Client, tlsConfigs map[string]*tls.CertAndStores) error {
for _, t := range ingress.Spec.TLS {
if t.SecretName == "" {
log.FromContext(ctx).Debugf("Skipping TLS sub-section: No secret name provided")
@@ -433,7 +432,7 @@ func getTLSConfig(tlsConfigs map[string]*tls.CertAndStores) []*tls.CertAndStores
return configs
}
-func loadService(client Client, namespace string, backend v1beta1.IngressBackend) (*dynamic.Service, error) {
+func loadService(client Client, namespace string, backend networkingv1beta1.IngressBackend) (*dynamic.Service, error) {
service, exists, err := client.GetService(namespace, backend.ServiceName)
if err != nil {
return nil, err
@@ -539,7 +538,7 @@ func getProtocol(portSpec corev1.ServicePort, portName string, svcConfig *Servic
return protocol
}
-func loadRouter(rule v1beta1.IngressRule, pa v1beta1.HTTPIngressPath, rtConfig *RouterConfig, serviceName string) *dynamic.Router {
+func loadRouter(rule networkingv1beta1.IngressRule, pa networkingv1beta1.HTTPIngressPath, rtConfig *RouterConfig, serviceName string) *dynamic.Router {
var rules []string
if len(rule.Host) > 0 {
rules = []string{buildHostRule(rule.Host)}
@@ -548,11 +547,11 @@ func loadRouter(rule v1beta1.IngressRule, pa v1beta1.HTTPIngressPath, rtConfig *
if len(pa.Path) > 0 {
matcher := defaultPathMatcher
- if pa.PathType == nil || *pa.PathType == "" || *pa.PathType == v1beta1.PathTypeImplementationSpecific {
+ if pa.PathType == nil || *pa.PathType == "" || *pa.PathType == networkingv1beta1.PathTypeImplementationSpecific {
if rtConfig != nil && rtConfig.Router != nil && rtConfig.Router.PathMatcher != "" {
matcher = rtConfig.Router.PathMatcher
}
- } else if *pa.PathType == v1beta1.PathTypeExact {
+ } else if *pa.PathType == networkingv1beta1.PathTypeExact {
matcher = "Path"
}
diff --git a/pkg/provider/kv/kv_test.go b/pkg/provider/kv/kv_test.go
index 3ff8feb5d..aa2ebf304 100644
--- a/pkg/provider/kv/kv_test.go
+++ b/pkg/provider/kv/kv_test.go
@@ -12,6 +12,7 @@ import (
"github.com/containous/traefik/v2/pkg/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ ptypes "github.com/traefik/paerser/types"
)
func Test_buildConfiguration(t *testing.T) {
@@ -346,7 +347,7 @@ func Test_buildConfiguration(t *testing.T) {
RateLimit: &dynamic.RateLimit{
Average: 42,
Burst: 42,
- Period: types.Duration(time.Second),
+ Period: ptypes.Duration(time.Second),
SourceCriterion: &dynamic.SourceCriterion{
IPStrategy: &dynamic.IPStrategy{
Depth: 42,
diff --git a/pkg/provider/marathon/marathon.go b/pkg/provider/marathon/marathon.go
index 2c0f13670..2ba0f0fcd 100644
--- a/pkg/provider/marathon/marathon.go
+++ b/pkg/provider/marathon/marathon.go
@@ -18,6 +18,7 @@ import (
"github.com/containous/traefik/v2/pkg/types"
"github.com/gambol99/go-marathon"
"github.com/sirupsen/logrus"
+ ptypes "github.com/traefik/paerser/types"
)
const (
@@ -53,10 +54,10 @@ type Provider struct {
ExposedByDefault bool `description:"Expose Marathon apps by default." json:"exposedByDefault,omitempty" toml:"exposedByDefault,omitempty" yaml:"exposedByDefault,omitempty" export:"true"`
DCOSToken string `description:"DCOSToken for DCOS environment, This will override the Authorization header." json:"dcosToken,omitempty" toml:"dcosToken,omitempty" yaml:"dcosToken,omitempty" export:"true"`
TLS *types.ClientTLS `description:"Enable TLS support." json:"tls,omitempty" toml:"tls,omitempty" yaml:"tls,omitempty" export:"true"`
- DialerTimeout types.Duration `description:"Set a dialer timeout for Marathon." json:"dialerTimeout,omitempty" toml:"dialerTimeout,omitempty" yaml:"dialerTimeout,omitempty" export:"true"`
- ResponseHeaderTimeout types.Duration `description:"Set a response header timeout for Marathon." json:"responseHeaderTimeout,omitempty" toml:"responseHeaderTimeout,omitempty" yaml:"responseHeaderTimeout,omitempty" export:"true"`
- TLSHandshakeTimeout types.Duration `description:"Set a TLS handshake timeout for Marathon." json:"tlsHandshakeTimeout,omitempty" toml:"tlsHandshakeTimeout,omitempty" yaml:"tlsHandshakeTimeout,omitempty" export:"true"`
- KeepAlive types.Duration `description:"Set a TCP Keep Alive time." json:"keepAlive,omitempty" toml:"keepAlive,omitempty" yaml:"keepAlive,omitempty" export:"true"`
+ DialerTimeout ptypes.Duration `description:"Set a dialer timeout for Marathon." json:"dialerTimeout,omitempty" toml:"dialerTimeout,omitempty" yaml:"dialerTimeout,omitempty" export:"true"`
+ ResponseHeaderTimeout ptypes.Duration `description:"Set a response header timeout for Marathon." json:"responseHeaderTimeout,omitempty" toml:"responseHeaderTimeout,omitempty" yaml:"responseHeaderTimeout,omitempty" export:"true"`
+ TLSHandshakeTimeout ptypes.Duration `description:"Set a TLS handshake timeout for Marathon." json:"tlsHandshakeTimeout,omitempty" toml:"tlsHandshakeTimeout,omitempty" yaml:"tlsHandshakeTimeout,omitempty" export:"true"`
+ KeepAlive ptypes.Duration `description:"Set a TCP Keep Alive time." json:"keepAlive,omitempty" toml:"keepAlive,omitempty" yaml:"keepAlive,omitempty" export:"true"`
ForceTaskHostname bool `description:"Force to use the task's hostname." json:"forceTaskHostname,omitempty" toml:"forceTaskHostname,omitempty" yaml:"forceTaskHostname,omitempty" export:"true"`
Basic *Basic `description:"Enable basic authentication." json:"basic,omitempty" toml:"basic,omitempty" yaml:"basic,omitempty" export:"true"`
RespectReadinessChecks bool `description:"Filter out tasks with non-successful readiness checks during deployments." json:"respectReadinessChecks,omitempty" toml:"respectReadinessChecks,omitempty" yaml:"respectReadinessChecks,omitempty" export:"true"`
@@ -70,10 +71,10 @@ func (p *Provider) SetDefaults() {
p.Watch = true
p.Endpoint = "http://127.0.0.1:8080"
p.ExposedByDefault = true
- p.DialerTimeout = types.Duration(5 * time.Second)
- p.ResponseHeaderTimeout = types.Duration(60 * time.Second)
- p.TLSHandshakeTimeout = types.Duration(5 * time.Second)
- p.KeepAlive = types.Duration(10 * time.Second)
+ p.DialerTimeout = ptypes.Duration(5 * time.Second)
+ p.ResponseHeaderTimeout = ptypes.Duration(60 * time.Second)
+ p.TLSHandshakeTimeout = ptypes.Duration(5 * time.Second)
+ p.KeepAlive = ptypes.Duration(10 * time.Second)
p.DefaultRule = DefaultTemplateRule
}
diff --git a/pkg/server/server_entrypoint_tcp.go b/pkg/server/server_entrypoint_tcp.go
index bf8a5315b..3b7efdf4c 100644
--- a/pkg/server/server_entrypoint_tcp.go
+++ b/pkg/server/server_entrypoint_tcp.go
@@ -28,12 +28,14 @@ var httpServerLogger = stdlog.New(log.WithoutContext().WriterLevel(logrus.DebugL
type httpForwarder struct {
net.Listener
connChan chan net.Conn
+ errChan chan error
}
func newHTTPForwarder(ln net.Listener) *httpForwarder {
return &httpForwarder{
Listener: ln,
connChan: make(chan net.Conn),
+ errChan: make(chan error),
}
}
@@ -44,8 +46,12 @@ func (h *httpForwarder) ServeTCP(conn tcp.WriteCloser) {
// Accept retrieves a served connection in ServeTCP.
func (h *httpForwarder) Accept() (net.Conn, error) {
- conn := <-h.connChan
- return conn, nil
+ select {
+ case conn := <-h.connChan:
+ return conn, nil
+ case err := <-h.errChan:
+ return nil, err
+ }
}
// TCPEntryPoints holds a map of TCPEntryPoint (the entrypoint names being the keys).
@@ -169,7 +175,8 @@ func (e *TCPEntryPoint) Start(ctx context.Context) {
if netErr, ok := err.(net.Error); ok && netErr.Temporary() {
continue
}
-
+ e.httpServer.Forwarder.errChan <- err
+ e.httpsServer.Forwarder.errChan <- err
return
}
diff --git a/pkg/server/server_entrypoint_tcp_test.go b/pkg/server/server_entrypoint_tcp_test.go
index b35dce36f..af1dd5aef 100644
--- a/pkg/server/server_entrypoint_tcp_test.go
+++ b/pkg/server/server_entrypoint_tcp_test.go
@@ -13,9 +13,9 @@ import (
"github.com/containous/traefik/v2/pkg/config/static"
"github.com/containous/traefik/v2/pkg/tcp"
- "github.com/containous/traefik/v2/pkg/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ ptypes "github.com/traefik/paerser/types"
)
func TestShutdownHijacked(t *testing.T) {
@@ -65,7 +65,7 @@ func testShutdown(t *testing.T, router *tcp.Router) {
epConfig.SetDefaults()
epConfig.LifeCycle.RequestAcceptGraceTimeout = 0
- epConfig.LifeCycle.GraceTimeOut = types.Duration(5 * time.Second)
+ epConfig.LifeCycle.GraceTimeOut = ptypes.Duration(5 * time.Second)
entryPoint, err := NewTCPEntryPoint(context.Background(), &static.EntryPoint{
// We explicitly use an IPV4 address because on Alpine, with an IPV6 address
@@ -150,7 +150,7 @@ func startEntrypoint(entryPoint *TCPEntryPoint, router *tcp.Router) (net.Conn, e
func TestReadTimeoutWithoutFirstByte(t *testing.T) {
epConfig := &static.EntryPointsTransport{}
epConfig.SetDefaults()
- epConfig.RespondingTimeouts.ReadTimeout = types.Duration(2 * time.Second)
+ epConfig.RespondingTimeouts.ReadTimeout = ptypes.Duration(2 * time.Second)
entryPoint, err := NewTCPEntryPoint(context.Background(), &static.EntryPoint{
Address: ":0",
@@ -186,7 +186,7 @@ func TestReadTimeoutWithoutFirstByte(t *testing.T) {
func TestReadTimeoutWithFirstByte(t *testing.T) {
epConfig := &static.EntryPointsTransport{}
epConfig.SetDefaults()
- epConfig.RespondingTimeouts.ReadTimeout = types.Duration(2 * time.Second)
+ epConfig.RespondingTimeouts.ReadTimeout = ptypes.Duration(2 * time.Second)
entryPoint, err := NewTCPEntryPoint(context.Background(), &static.EntryPoint{
Address: ":0",
diff --git a/pkg/server/server_entrypoint_udp_test.go b/pkg/server/server_entrypoint_udp_test.go
index 6f468e901..032e7c2c1 100644
--- a/pkg/server/server_entrypoint_udp_test.go
+++ b/pkg/server/server_entrypoint_udp_test.go
@@ -8,9 +8,9 @@ import (
"time"
"github.com/containous/traefik/v2/pkg/config/static"
- "github.com/containous/traefik/v2/pkg/types"
"github.com/containous/traefik/v2/pkg/udp"
"github.com/stretchr/testify/require"
+ ptypes "github.com/traefik/paerser/types"
)
func TestShutdownUDPConn(t *testing.T) {
@@ -18,7 +18,7 @@ func TestShutdownUDPConn(t *testing.T) {
Address: ":0",
Transport: &static.EntryPointsTransport{
LifeCycle: &static.LifeCycle{
- GraceTimeOut: types.Duration(5 * time.Second),
+ GraceTimeOut: ptypes.Duration(5 * time.Second),
},
},
})
diff --git a/pkg/server/service/proxy.go b/pkg/server/service/proxy.go
index 21a67329f..2347f7671 100644
--- a/pkg/server/service/proxy.go
+++ b/pkg/server/service/proxy.go
@@ -12,7 +12,7 @@ import (
"github.com/containous/traefik/v2/pkg/config/dynamic"
"github.com/containous/traefik/v2/pkg/log"
- "github.com/containous/traefik/v2/pkg/types"
+ ptypes "github.com/traefik/paerser/types"
)
// StatusClientClosedRequest non-standard HTTP status code for client disconnection.
@@ -22,7 +22,7 @@ const StatusClientClosedRequest = 499
const StatusClientClosedRequestText = "Client Closed Request"
func buildProxy(passHostHeader *bool, responseForwarding *dynamic.ResponseForwarding, defaultRoundTripper http.RoundTripper, bufferPool httputil.BufferPool, responseModifier func(*http.Response) error) (http.Handler, error) {
- var flushInterval types.Duration
+ var flushInterval ptypes.Duration
if responseForwarding != nil {
err := flushInterval.Set(responseForwarding.FlushInterval)
if err != nil {
@@ -30,7 +30,7 @@ func buildProxy(passHostHeader *bool, responseForwarding *dynamic.ResponseForwar
}
}
if flushInterval == 0 {
- flushInterval = types.Duration(100 * time.Millisecond)
+ flushInterval = ptypes.Duration(100 * time.Millisecond)
}
proxy := &httputil.ReverseProxy{
diff --git a/pkg/tracing/jaeger/jaeger.go b/pkg/tracing/jaeger/jaeger.go
index d8f73eccc..08ed8b974 100644
--- a/pkg/tracing/jaeger/jaeger.go
+++ b/pkg/tracing/jaeger/jaeger.go
@@ -17,14 +17,15 @@ const Name = "jaeger"
// Config provides configuration settings for a jaeger tracer.
type Config struct {
- SamplingServerURL string `description:"Set the sampling server url." json:"samplingServerURL,omitempty" toml:"samplingServerURL,omitempty" yaml:"samplingServerURL,omitempty"`
- SamplingType string `description:"Set the sampling type." json:"samplingType,omitempty" toml:"samplingType,omitempty" yaml:"samplingType,omitempty" export:"true"`
- SamplingParam float64 `description:"Set the sampling parameter." json:"samplingParam,omitempty" toml:"samplingParam,omitempty" yaml:"samplingParam,omitempty" export:"true"`
- LocalAgentHostPort string `description:"Set jaeger-agent's host:port that the reporter will used." json:"localAgentHostPort,omitempty" toml:"localAgentHostPort,omitempty" yaml:"localAgentHostPort,omitempty"`
- Gen128Bit bool `description:"Generate 128 bit span IDs." json:"gen128Bit,omitempty" toml:"gen128Bit,omitempty" yaml:"gen128Bit,omitempty" export:"true"`
- Propagation string `description:"Which propagation format to use (jaeger/b3)." json:"propagation,omitempty" toml:"propagation,omitempty" yaml:"propagation,omitempty" export:"true"`
- TraceContextHeaderName string `description:"Set the header to use for the trace-id." json:"traceContextHeaderName,omitempty" toml:"traceContextHeaderName,omitempty" yaml:"traceContextHeaderName,omitempty" export:"true"`
- Collector *Collector `description:"Define the collector information" json:"collector,omitempty" toml:"collector,omitempty" yaml:"collector,omitempty" export:"true"`
+ SamplingServerURL string `description:"Set the sampling server url." json:"samplingServerURL,omitempty" toml:"samplingServerURL,omitempty" yaml:"samplingServerURL,omitempty"`
+ SamplingType string `description:"Set the sampling type." json:"samplingType,omitempty" toml:"samplingType,omitempty" yaml:"samplingType,omitempty" export:"true"`
+ SamplingParam float64 `description:"Set the sampling parameter." json:"samplingParam,omitempty" toml:"samplingParam,omitempty" yaml:"samplingParam,omitempty" export:"true"`
+ LocalAgentHostPort string `description:"Set jaeger-agent's host:port that the reporter will used." json:"localAgentHostPort,omitempty" toml:"localAgentHostPort,omitempty" yaml:"localAgentHostPort,omitempty"`
+ Gen128Bit bool `description:"Generate 128 bit span IDs." json:"gen128Bit,omitempty" toml:"gen128Bit,omitempty" yaml:"gen128Bit,omitempty" export:"true"`
+ Propagation string `description:"Which propagation format to use (jaeger/b3)." json:"propagation,omitempty" toml:"propagation,omitempty" yaml:"propagation,omitempty" export:"true"`
+ TraceContextHeaderName string `description:"Set the header to use for the trace-id." json:"traceContextHeaderName,omitempty" toml:"traceContextHeaderName,omitempty" yaml:"traceContextHeaderName,omitempty" export:"true"`
+ Collector *Collector `description:"Define the collector information" json:"collector,omitempty" toml:"collector,omitempty" yaml:"collector,omitempty" export:"true"`
+ DisableAttemptReconnecting bool `description:"Disable the periodic re-resolution of the agent's hostname and reconnection if there was a change." json:"disableAttemptReconnecting,omitempty" toml:"disableAttemptReconnecting,omitempty" yaml:"disableAttemptReconnecting,omitempty" export:"true"`
}
// SetDefaults sets the default values.
@@ -36,6 +37,7 @@ func (c *Config) SetDefaults() {
c.Propagation = "jaeger"
c.Gen128Bit = false
c.TraceContextHeaderName = jaegercli.TraceContextHeaderName
+ c.DisableAttemptReconnecting = true
}
// Collector provides configuration settings for jaeger collector.
@@ -55,8 +57,9 @@ func (c *Collector) SetDefaults() {
// Setup sets up the tracer.
func (c *Config) Setup(componentName string) (opentracing.Tracer, io.Closer, error) {
reporter := &jaegercfg.ReporterConfig{
- LogSpans: true,
- LocalAgentHostPort: c.LocalAgentHostPort,
+ LogSpans: true,
+ LocalAgentHostPort: c.LocalAgentHostPort,
+ DisableAttemptReconnecting: c.DisableAttemptReconnecting,
}
if c.Collector != nil {
diff --git a/pkg/types/duration.go b/pkg/types/duration.go
deleted file mode 100644
index 936f1f0fb..000000000
--- a/pkg/types/duration.go
+++ /dev/null
@@ -1,61 +0,0 @@
-package types
-
-import (
- "encoding/json"
- "strconv"
- "time"
-)
-
-// Duration is a custom type suitable for parsing duration values.
-// It supports `time.ParseDuration`-compatible values and suffix-less digits; in
-// the latter case, seconds are assumed.
-type Duration time.Duration
-
-// Set sets the duration from the given string value.
-func (d *Duration) Set(s string) error {
- if v, err := strconv.ParseInt(s, 10, 64); err == nil {
- *d = Duration(time.Duration(v) * time.Second)
- return nil
- }
-
- v, err := time.ParseDuration(s)
- *d = Duration(v)
- return err
-}
-
-// String returns a string representation of the duration value.
-func (d Duration) String() string { return (time.Duration)(d).String() }
-
-// MarshalText serialize the given duration value into a text.
-func (d Duration) MarshalText() ([]byte, error) {
- return []byte(d.String()), nil
-}
-
-// UnmarshalText deserializes the given text into a duration value.
-// It is meant to support TOML decoding of durations.
-func (d *Duration) UnmarshalText(text []byte) error {
- return d.Set(string(text))
-}
-
-// MarshalJSON serializes the given duration value.
-func (d Duration) MarshalJSON() ([]byte, error) {
- return json.Marshal(time.Duration(d))
-}
-
-// UnmarshalJSON deserializes the given text into a duration value.
-func (d *Duration) UnmarshalJSON(text []byte) error {
- if v, err := strconv.ParseInt(string(text), 10, 64); err == nil {
- *d = Duration(time.Duration(v))
- return nil
- }
-
- // We use json unmarshal on value because we have the quoted version
- var value string
- err := json.Unmarshal(text, &value)
- if err != nil {
- return err
- }
- v, err := time.ParseDuration(value)
- *d = Duration(v)
- return err
-}
diff --git a/pkg/types/logs.go b/pkg/types/logs.go
index b84ec77f1..aeb7ee872 100644
--- a/pkg/types/logs.go
+++ b/pkg/types/logs.go
@@ -1,5 +1,7 @@
package types
+import "github.com/traefik/paerser/types"
+
const (
// AccessLogKeep is the keep string value.
AccessLogKeep = "keep"
@@ -50,9 +52,9 @@ func (l *AccessLog) SetDefaults() {
// AccessLogFilters holds filters configuration.
type AccessLogFilters struct {
- StatusCodes []string `description:"Keep access logs with status codes in the specified range." json:"statusCodes,omitempty" toml:"statusCodes,omitempty" yaml:"statusCodes,omitempty" export:"true"`
- RetryAttempts bool `description:"Keep access logs when at least one retry happened." json:"retryAttempts,omitempty" toml:"retryAttempts,omitempty" yaml:"retryAttempts,omitempty" export:"true"`
- MinDuration Duration `description:"Keep access logs when request took longer than the specified duration." json:"minDuration,omitempty" toml:"minDuration,omitempty" yaml:"minDuration,omitempty" export:"true"`
+ StatusCodes []string `description:"Keep access logs with status codes in the specified range." json:"statusCodes,omitempty" toml:"statusCodes,omitempty" yaml:"statusCodes,omitempty" export:"true"`
+ RetryAttempts bool `description:"Keep access logs when at least one retry happened." json:"retryAttempts,omitempty" toml:"retryAttempts,omitempty" yaml:"retryAttempts,omitempty" export:"true"`
+ MinDuration types.Duration `description:"Keep access logs when request took longer than the specified duration." json:"minDuration,omitempty" toml:"minDuration,omitempty" yaml:"minDuration,omitempty" export:"true"`
}
// FieldHeaders holds configuration for access log headers.
diff --git a/pkg/types/metrics.go b/pkg/types/metrics.go
index 41d835249..e0916e720 100644
--- a/pkg/types/metrics.go
+++ b/pkg/types/metrics.go
@@ -2,6 +2,8 @@ package types
import (
"time"
+
+ "github.com/traefik/paerser/types"
)
// Metrics provides options to expose and send Traefik metrics to different third party monitoring systems.
@@ -31,33 +33,33 @@ func (p *Prometheus) SetDefaults() {
// Datadog contains address and metrics pushing interval configuration.
type Datadog struct {
- Address string `description:"Datadog's address." json:"address,omitempty" toml:"address,omitempty" yaml:"address,omitempty"`
- PushInterval Duration `description:"Datadog push interval." json:"pushInterval,omitempty" toml:"pushInterval,omitempty" yaml:"pushInterval,omitempty" export:"true"`
- AddEntryPointsLabels bool `description:"Enable metrics on entry points." json:"addEntryPointsLabels,omitempty" toml:"addEntryPointsLabels,omitempty" yaml:"addEntryPointsLabels,omitempty" export:"true"`
- AddServicesLabels bool `description:"Enable metrics on services." json:"addServicesLabels,omitempty" toml:"addServicesLabels,omitempty" yaml:"addServicesLabels,omitempty" export:"true"`
+ Address string `description:"Datadog's address." json:"address,omitempty" toml:"address,omitempty" yaml:"address,omitempty"`
+ PushInterval types.Duration `description:"Datadog push interval." json:"pushInterval,omitempty" toml:"pushInterval,omitempty" yaml:"pushInterval,omitempty" export:"true"`
+ AddEntryPointsLabels bool `description:"Enable metrics on entry points." json:"addEntryPointsLabels,omitempty" toml:"addEntryPointsLabels,omitempty" yaml:"addEntryPointsLabels,omitempty" export:"true"`
+ AddServicesLabels bool `description:"Enable metrics on services." json:"addServicesLabels,omitempty" toml:"addServicesLabels,omitempty" yaml:"addServicesLabels,omitempty" export:"true"`
}
// SetDefaults sets the default values.
func (d *Datadog) SetDefaults() {
d.Address = "localhost:8125"
- d.PushInterval = Duration(10 * time.Second)
+ d.PushInterval = types.Duration(10 * time.Second)
d.AddEntryPointsLabels = true
d.AddServicesLabels = true
}
// Statsd contains address and metrics pushing interval configuration.
type Statsd struct {
- Address string `description:"StatsD address." json:"address,omitempty" toml:"address,omitempty" yaml:"address,omitempty"`
- PushInterval Duration `description:"StatsD push interval." json:"pushInterval,omitempty" toml:"pushInterval,omitempty" yaml:"pushInterval,omitempty" export:"true"`
- AddEntryPointsLabels bool `description:"Enable metrics on entry points." json:"addEntryPointsLabels,omitempty" toml:"addEntryPointsLabels,omitempty" yaml:"addEntryPointsLabels,omitempty" export:"true"`
- AddServicesLabels bool `description:"Enable metrics on services." json:"addServicesLabels,omitempty" toml:"addServicesLabels,omitempty" yaml:"addServicesLabels,omitempty" export:"true"`
- Prefix string `description:"Prefix to use for metrics collection." json:"prefix,omitempty" toml:"prefix,omitempty" yaml:"prefix,omitempty" export:"true"`
+ Address string `description:"StatsD address." json:"address,omitempty" toml:"address,omitempty" yaml:"address,omitempty"`
+ PushInterval types.Duration `description:"StatsD push interval." json:"pushInterval,omitempty" toml:"pushInterval,omitempty" yaml:"pushInterval,omitempty" export:"true"`
+ AddEntryPointsLabels bool `description:"Enable metrics on entry points." json:"addEntryPointsLabels,omitempty" toml:"addEntryPointsLabels,omitempty" yaml:"addEntryPointsLabels,omitempty" export:"true"`
+ AddServicesLabels bool `description:"Enable metrics on services." json:"addServicesLabels,omitempty" toml:"addServicesLabels,omitempty" yaml:"addServicesLabels,omitempty" export:"true"`
+ Prefix string `description:"Prefix to use for metrics collection." json:"prefix,omitempty" toml:"prefix,omitempty" yaml:"prefix,omitempty" export:"true"`
}
// SetDefaults sets the default values.
func (s *Statsd) SetDefaults() {
s.Address = "localhost:8125"
- s.PushInterval = Duration(10 * time.Second)
+ s.PushInterval = types.Duration(10 * time.Second)
s.AddEntryPointsLabels = true
s.AddServicesLabels = true
s.Prefix = "traefik"
@@ -65,22 +67,22 @@ func (s *Statsd) SetDefaults() {
// InfluxDB contains address, login and metrics pushing interval configuration.
type InfluxDB struct {
- Address string `description:"InfluxDB address." json:"address,omitempty" toml:"address,omitempty" yaml:"address,omitempty"`
- Protocol string `description:"InfluxDB address protocol (udp or http)." json:"protocol,omitempty" toml:"protocol,omitempty" yaml:"protocol,omitempty"`
- PushInterval Duration `description:"InfluxDB push interval." json:"pushInterval,omitempty" toml:"pushInterval,omitempty" yaml:"pushInterval,omitempty" export:"true"`
- Database string `description:"InfluxDB database used when protocol is http." json:"database,omitempty" toml:"database,omitempty" yaml:"database,omitempty" export:"true"`
- RetentionPolicy string `description:"InfluxDB retention policy used when protocol is http." json:"retentionPolicy,omitempty" toml:"retentionPolicy,omitempty" yaml:"retentionPolicy,omitempty" export:"true"`
- Username string `description:"InfluxDB username (only with http)." json:"username,omitempty" toml:"username,omitempty" yaml:"username,omitempty" export:"true"`
- Password string `description:"InfluxDB password (only with http)." json:"password,omitempty" toml:"password,omitempty" yaml:"password,omitempty" export:"true"`
- AddEntryPointsLabels bool `description:"Enable metrics on entry points." json:"addEntryPointsLabels,omitempty" toml:"addEntryPointsLabels,omitempty" yaml:"addEntryPointsLabels,omitempty" export:"true"`
- AddServicesLabels bool `description:"Enable metrics on services." json:"addServicesLabels,omitempty" toml:"addServicesLabels,omitempty" yaml:"addServicesLabels,omitempty" export:"true"`
+ Address string `description:"InfluxDB address." json:"address,omitempty" toml:"address,omitempty" yaml:"address,omitempty"`
+ Protocol string `description:"InfluxDB address protocol (udp or http)." json:"protocol,omitempty" toml:"protocol,omitempty" yaml:"protocol,omitempty"`
+ PushInterval types.Duration `description:"InfluxDB push interval." json:"pushInterval,omitempty" toml:"pushInterval,omitempty" yaml:"pushInterval,omitempty" export:"true"`
+ Database string `description:"InfluxDB database used when protocol is http." json:"database,omitempty" toml:"database,omitempty" yaml:"database,omitempty" export:"true"`
+ RetentionPolicy string `description:"InfluxDB retention policy used when protocol is http." json:"retentionPolicy,omitempty" toml:"retentionPolicy,omitempty" yaml:"retentionPolicy,omitempty" export:"true"`
+ Username string `description:"InfluxDB username (only with http)." json:"username,omitempty" toml:"username,omitempty" yaml:"username,omitempty" export:"true"`
+ Password string `description:"InfluxDB password (only with http)." json:"password,omitempty" toml:"password,omitempty" yaml:"password,omitempty" export:"true"`
+ AddEntryPointsLabels bool `description:"Enable metrics on entry points." json:"addEntryPointsLabels,omitempty" toml:"addEntryPointsLabels,omitempty" yaml:"addEntryPointsLabels,omitempty" export:"true"`
+ AddServicesLabels bool `description:"Enable metrics on services." json:"addServicesLabels,omitempty" toml:"addServicesLabels,omitempty" yaml:"addServicesLabels,omitempty" export:"true"`
}
// SetDefaults sets the default values.
func (i *InfluxDB) SetDefaults() {
i.Address = "localhost:8089"
i.Protocol = "udp"
- i.PushInterval = Duration(10 * time.Second)
+ i.PushInterval = types.Duration(10 * time.Second)
i.AddEntryPointsLabels = true
i.AddServicesLabels = true
}
diff --git a/pkg/version/version.go b/pkg/version/version.go
index 5e5e0ff0c..db613ca48 100644
--- a/pkg/version/version.go
+++ b/pkg/version/version.go
@@ -41,7 +41,7 @@ func (v Handler) Append(router *mux.Router) {
Version string
Codename string
StartDate time.Time `json:"startDate"`
- UUID string `json:"uuid"`
+ UUID string `json:"uuid,omitempty"`
}{
Version: Version,
Codename: Codename,
diff --git a/webui/Dockerfile b/webui/Dockerfile
index 67e491e5d..f7d42974e 100644
--- a/webui/Dockerfile
+++ b/webui/Dockerfile
@@ -1,4 +1,4 @@
-FROM node:12.11
+FROM node:12.18
ENV WEBUI_DIR /src/webui
RUN mkdir -p $WEBUI_DIR
diff --git a/webui/src/components/platform/PlatformNotification.vue b/webui/src/components/platform/PlatformNotification.vue
index 1cd9c7f69..fa55dbbbc 100644
--- a/webui/src/components/platform/PlatformNotification.vue
+++ b/webui/src/components/platform/PlatformNotification.vue
@@ -1,8 +1,8 @@
-
+
-
-
+
@@ -25,9 +26,17 @@ export default {
components: { PlatformActionButton },
created () {
this.getInstanceInfos()
+ console.log(`localStorage.getItem('platform_notification-is-hidden')`, localStorage.getItem('platform_notification-is-hidden'))
+ try {
+ if (localStorage.getItem('platform_notification-is-hidden') === 'true') {
+ this.hidePlatformNotification()
+ }
+ } catch (e) {
+ console.error('error reading localStorage', e)
+ }
},
computed: {
- ...mapGetters('platform', { isPlatformOpen: 'isOpen' }),
+ ...mapGetters('platform', { isPlatformOpen: 'isOpen', platformNotificationIsHidden: 'notificationIsHidden' }),
...mapGetters('core', { instanceInfos: 'version' }),
instanceIsRegistered () {
return !!(this.instanceInfos && this.instanceInfos.uuid && this.instanceInfos.uuid.length > 0)
@@ -37,7 +46,7 @@ export default {
}
},
methods: {
- ...mapActions('platform', { openPlatform: 'open' }),
+ ...mapActions('platform', { openPlatform: 'open', hidePlatformNotification: 'hideNotification' }),
...mapActions('core', { getInstanceInfos: 'getVersion' })
},
watch: {
@@ -46,6 +55,15 @@ export default {
if (isClosed) {
this.getInstanceInfos()
}
+ },
+ platformNotificationIsHidden (newValue, oldValue) {
+ if (newValue !== oldValue) {
+ try {
+ localStorage.setItem('platform_notification-is-hidden', newValue ? 'true' : 'false')
+ } catch (e) {
+ console.error('error writing localStorage', e)
+ }
+ }
}
}
}
@@ -55,8 +73,7 @@ export default {
@import "../../css/sass/variables";
.platform-notification {
- min-height: 100px;
- padding: 40px 36px;
+ position: relative;
display: flex;
justify-content: space-between;
align-items: center;
@@ -66,6 +83,12 @@ export default {
font-size: 16px;
}
+ .close-btn {
+ position: absolute;
+ top: -8px;
+ right: -8px;
+ }
+
p {
margin: 0;
}
diff --git a/webui/src/store/platform/index.js b/webui/src/store/platform/index.js
index cfe4c07dd..0ad4a1192 100644
--- a/webui/src/store/platform/index.js
+++ b/webui/src/store/platform/index.js
@@ -3,11 +3,17 @@ export default {
getters: {
isOpen (state) {
return state.isOpen
+ },
+ notificationIsHidden (state) {
+ return state.notificationIsHidden
}
},
mutations: {
toggle (state, isOpen) {
state.isOpen = isOpen || !state.isOpen
+ },
+ toggleNotifVisibility (state, isHidden) {
+ state.notificationIsHidden = isHidden || !state.isHidden
}
},
actions: {
@@ -19,9 +25,13 @@ export default {
},
close ({ commit }) {
commit('toggle', false)
+ },
+ hideNotification ({ commit }) {
+ commit('toggleNotifVisibility', true)
}
},
state: {
- isOpen: false
+ isOpen: false,
+ notificationIsHidden: false
}
}