Merge branch v2.3 into master

This commit is contained in:
Fernandez Ludovic 2020-08-26 12:22:39 +02:00
commit 483e2c43cf
153 changed files with 2466 additions and 14841 deletions

View file

@ -54,6 +54,7 @@
"nestif", # Too many false-positive.
"noctx", # Too strict
"exhaustive", # Too strict
"nlreturn", # Too strict
]
[issues]

View file

@ -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

View file

@ -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:

View file

@ -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)

View file

@ -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

View file

@ -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,

View file

@ -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.

View file

@ -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 {

View file

@ -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}}

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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;
}
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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

View file

@ -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.

View file

@ -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`

View file

@ -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 \

View file

@ -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`.

View file

@ -16,6 +16,7 @@ rules:
- watch
- apiGroups:
- extensions
- networking.k8s.io
resources:
- ingresses
- ingressclasses

View file

@ -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```)

View file

@ -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```)

View file

@ -290,6 +290,7 @@
gen128Bit = true
propagation = "foobar"
traceContextHeaderName = "foobar"
disableAttemptReconnecting = true
[tracing.jaeger.collector]
endpoint = "foobar"
user = "foobar"

View file

@ -308,6 +308,7 @@ tracing:
gen128Bit: true
propagation: foobar
traceContextHeaderName: foobar
disableAttemptReconnecting: true
collector:
endpoint: foobar
user: foobar

View file

@ -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"

View file

@ -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:

View file

@ -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

44
docs/theme/main.html vendored
View file

@ -10,6 +10,46 @@
<!-- End Google Tag Manager -->
{% endblock %}
{% block header %}
{% include "partials/containous-header.html" %}
{% include "partials/header.html" %}
{% endblock %}
{% block site_nav %}
{% if nav %}
{% include "partials/product-switcher.html" %}
<div class="md-sidebar md-sidebar--primary" data-md-component="navigation">
{% include "partials/search.html" %}
{% if "search" in config["plugins"] %}
<label class="md-icon md-icon--search md-header-nav__button" for="__search"></label>
{% endif %}
<div class="md-sidebar__scrollwrap">
<div class="md-sidebar__inner">
{% include "partials/nav.html" %}
</div>
</div>
</div>
{% endif %}
{% if page.toc %}
<div class="md-sidebar md-sidebar--secondary" data-md-component="toc">
{% if config.repo_url %}
<div class="md-flex__cell md-flex__cell--shrink repo_url">
<div class="md-header-nav__source">
{% include "partials/source.html" %}
</div>
</div>
{% endif %}
<div class="md-sidebar__scrollwrap">
<div class="md-sidebar__inner">
{% include "partials/toc.html" %}
</div>
</div>
</div>
{% endif %}
{% endblock %}
{% block footer %}
{% import "partials/language.html" as lang with context %}
@ -36,6 +76,10 @@
Material for MkDocs</a>
</div>
<div class="md-footer-privacy-policy">
<a href="https://containo.us/legal/privacy-and-cookie-policy/" title="Privacy Policy">Privacy Policy</a>
</div>
<!-- Social links -->
{% block social %}
{% include "partials/social.html" %}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -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 \

12
go.mod
View file

@ -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

29
go.sum
View file

@ -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=

View file

@ -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"

View file

@ -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-----

View file

@ -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-----

View file

@ -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-----

View file

@ -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-----

View file

@ -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-----

View file

@ -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-----

View file

@ -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-----

View file

@ -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-----

View file

@ -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-----

View file

@ -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-----

View file

@ -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-----

View file

@ -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-----

View file

@ -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
)

View file

@ -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")
}

View file

@ -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),
},
}

View file

@ -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] == '-'
}

View file

@ -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)
}

View file

@ -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
}

View file

@ -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)
})
}
}

View file

@ -1,3 +0,0 @@
foo = "bar"
fii = "bir"
[yi]

View file

@ -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"
}

View file

@ -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]")
}

View file

@ -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())
})
}
}

View file

@ -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)
}

View file

@ -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

View file

@ -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"},
}

View file

@ -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
}

View file

@ -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"`

View file

@ -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

77
pkg/config/env/env.go vendored
View file

@ -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
}

View file

@ -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)
}

View file

@ -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
}

View file

@ -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)
})
}
}

View file

@ -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
}

View file

@ -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})
}

View file

@ -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
}

View file

@ -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)
}

View file

@ -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)
}

View file

@ -1,2 +0,0 @@
[foo]
bar = "test"

View file

@ -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"

View file

@ -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: {}

View file

@ -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
}

View file

@ -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
}

View file

@ -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)
})
}
}

View file

@ -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)
}

View file

@ -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.<name>",
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.<name>",
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.<name>",
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.<name>",
Description: "foo description",
Default: "false",
},
{
Name: "foo.<name>.field",
Description: "field description",
Default: "",
},
{
Name: "foo.<name>.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.<name>",
Description: "foo description",
Default: "false",
},
{
Name: "foo.<name>.field",
Description: "field description",
Default: "",
},
{
Name: "foo.<name>.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.<name>",
Description: "foo description",
Default: "false",
},
{
Name: "foo.<name>.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.<name>",
Description: "foo description",
Default: "false",
},
{
Name: "foo.<name>.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)
})
}
}

View file

@ -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
}

View file

@ -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)
})
}
}

View file

@ -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, ".")), ".")
}

View file

@ -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
}

View file

@ -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)
}

View file

@ -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{"<name>": ""},
Fee: map[string]Hi{"<name>": {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
}

View file

@ -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.

View file

@ -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.

View file

@ -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) {

View file

@ -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.

View file

@ -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,

View file

@ -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]))
}

File diff suppressed because it is too large Load diff

View file

@ -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
}

View file

@ -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)
}
})
}
}

View file

@ -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)
}

Some files were not shown because too many files have changed in this diff Show more