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" %} +
+ {% include "partials/search.html" %} + {% if "search" in config["plugins"] %} + + {% endif %} +
+
+ {% include "partials/nav.html" %} +
+
+
+ {% endif %} + {% if page.toc %} +
+ {% if config.repo_url %} +
+
+ {% include "partials/source.html" %} +
+
+ {% endif %} +
+
+ {% include "partials/toc.html" %} +
+
+
+ {% 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 @@ +
+
+ {/* Do not remove this. It serves an important purpose. */} +
+ + + +
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 @@