feat: use parser to load dynamic config from file.

This commit is contained in:
Ludovic Fernandez 2020-06-17 16:48:04 +02:00 committed by GitHub
parent 7affeae480
commit cb1d0441e9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
27 changed files with 187 additions and 88 deletions

View file

@ -302,3 +302,9 @@ providers:
--entryPoints.websecure.address=:443
--providers.kubernetesIngress=true
```
## v2.2 to v2.3
### File Provider
The file parser has been changed, since v2.3 the unknown options/fields in a dynamic configuration file are treated as errors.

View file

@ -238,7 +238,7 @@ Thus, it's possible to define easily lot of routers, services and TLS certificat
[[tls.certificates]]
certFile = "/etc/traefik/cert-{{ $e }}.pem"
keyFile = "/etc/traefik/cert-{{ $e }}.key"
store = ["my-store-foo-{{ $e }}", "my-store-bar-{{ $e }}"]
stores = ["my-store-foo-{{ $e }}", "my-store-bar-{{ $e }}"]
{{ end }}
[tls.config]

View file

@ -11,6 +11,6 @@
[http.routers.test.tls]
[[tls.certificates]]
store = ["default"]
stores = ["default"]
certFile = "fixtures/acme/ssl/wildcard.crt"
keyFile = "fixtures/acme/ssl/wildcard.key"

View file

@ -22,7 +22,7 @@
[http.routers.my-router]
rule = "Path(`/test`)"
service = "whoami"
entrypoint=["tcp"]
entrypoints = ["tcp"]
[http.routers.my-https-router]
entryPoints=["tcp"]
@ -41,14 +41,14 @@
service = "whoami-a"
entryPoints = [ "tcp" ]
[tcp.routers.to-whoami-a.tls]
passthrough=true
passthrough = true
[tcp.routers.to-whoami-b]
rule = "HostSNI(`whoami-b.test`)"
service = "whoami-b"
entryPoints = [ "tcp" ]
[tcp.routers.to-whoami-b.tls]
passthrough=true
passthrough = true
[tcp.routers.to-whoami-no-cert]
rule = "HostSNI(`whoami-c.test`)"

View file

@ -31,4 +31,3 @@
passHostHeader = true
[[http.services.keepalive.loadBalancer.servers]]
url = "{{ .KeepAliveServer }}"
weight = 1

View file

@ -58,13 +58,13 @@
url = "http://{{.WhoAmiIP}}:{{.WhoAmiPort}}"
[http.services.service2]
passHostHeader = true
[http.services.service2.loadBalancer]
passHostHeader = true
[[http.services.service2.loadBalancer.servers]]
url = "http://{{.WhoAmiIP}}:{{.WhoAmiPort}}"
[http.services.service3]
passHostHeader = true
[http.services.service3.loadBalancer]
passHostHeader = true
[[http.services.service3.loadBalancer.servers]]
url = "http://{{.WhoAmiIP}}:{{.WhoAmiPort}}"

View file

@ -57,13 +57,13 @@
url = "http://{{.WhoAmiIP}}:{{.WhoAmiPort}}"
[http.services.service2]
passHostHeader = true
[http.services.service2.loadBalancer]
passHostHeader = true
[[http.services.service2.loadBalancer.servers]]
url = "http://{{.WhoAmiIP}}:{{.WhoAmiPort}}"
[http.services.service3]
passHostHeader = true
[http.services.service3.loadBalancer]
passHostHeader = true
[[http.services.service3.loadBalancer.servers]]
url = "http://{{.WhoAmiIP}}:{{.WhoAmiPort}}"

View file

@ -53,13 +53,13 @@
url = "http://{{.WhoAmiIP}}:{{.WhoAmiPort}}"
[http.services.service2]
passHostHeader = true
[http.services.service2.loadBalancer]
passHostHeader = true
[[http.services.service2.loadBalancer.servers]]
url = "http://{{.WhoAmiIP}}:{{.WhoAmiPort}}"
[http.services.service3]
passHostHeader = true
[http.services.service3.loadBalancer]
passHostHeader = true
[[http.services.service3.loadBalancer.servers]]
url = "http://{{.WhoAmiIP}}:{{.WhoAmiPort}}"

View file

@ -4,7 +4,7 @@ type Yo struct {
Foo string `description:"Foo description"`
Fii string `description:"Fii description"`
Fuu string `description:"Fuu description"`
Yi *Yi `label:"allowEmpty"`
Yi *Yi `label:"allowEmpty" file:"allowEmpty"`
Yu *Yi
}

View file

@ -21,7 +21,7 @@ type HTTPConfiguration struct {
// Model is a set of default router's values.
type Model struct {
Middlewares []string `json:"middlewares,omitempty" toml:"middlewares,omitempty" yaml:"middlewares,omitempty"`
TLS *RouterTLSConfig `json:"tls,omitempty" toml:"tls,omitempty" yaml:"tls,omitempty" label:"allowEmpty"`
TLS *RouterTLSConfig `json:"tls,omitempty" toml:"tls,omitempty" yaml:"tls,omitempty" label:"allowEmpty" file:"allowEmpty"`
}
// +k8s:deepcopy-gen=true
@ -42,7 +42,7 @@ type Router struct {
Service string `json:"service,omitempty" toml:"service,omitempty" yaml:"service,omitempty"`
Rule string `json:"rule,omitempty" toml:"rule,omitempty" yaml:"rule,omitempty"`
Priority int `json:"priority,omitempty" toml:"priority,omitempty,omitzero" yaml:"priority,omitempty"`
TLS *RouterTLSConfig `json:"tls,omitempty" toml:"tls,omitempty" yaml:"tls,omitempty" label:"allowEmpty"`
TLS *RouterTLSConfig `json:"tls,omitempty" toml:"tls,omitempty" yaml:"tls,omitempty" label:"allowEmpty" file:"allowEmpty"`
}
// +k8s:deepcopy-gen=true
@ -103,7 +103,7 @@ func (w *WRRService) SetDefaults() {
// Sticky holds the sticky configuration.
type Sticky struct {
Cookie *Cookie `json:"cookie,omitempty" toml:"cookie,omitempty" yaml:"cookie,omitempty" label:"allowEmpty"`
Cookie *Cookie `json:"cookie,omitempty" toml:"cookie,omitempty" yaml:"cookie,omitempty" label:"allowEmpty" file:"allowEmpty"`
}
// +k8s:deepcopy-gen=true
@ -120,7 +120,7 @@ type Cookie struct {
// ServersLoadBalancer holds the ServersLoadBalancer configuration.
type ServersLoadBalancer struct {
Sticky *Sticky `json:"sticky,omitempty" toml:"sticky,omitempty" yaml:"sticky,omitempty" label:"allowEmpty"`
Sticky *Sticky `json:"sticky,omitempty" toml:"sticky,omitempty" yaml:"sticky,omitempty" label:"allowEmpty" file:"allowEmpty"`
Servers []Server `json:"servers,omitempty" toml:"servers,omitempty" yaml:"servers,omitempty" label-slice-as-struct:"server"`
HealthCheck *HealthCheck `json:"healthCheck,omitempty" toml:"healthCheck,omitempty" yaml:"healthCheck,omitempty"`
PassHostHeader *bool `json:"passHostHeader" toml:"passHostHeader" yaml:"passHostHeader"`
@ -162,8 +162,8 @@ type ResponseForwarding struct {
// Server holds the server configuration.
type Server struct {
URL string `json:"url,omitempty" toml:"url,omitempty" yaml:"url,omitempty" label:"-"`
Scheme string `toml:"-" json:"-" yaml:"-"`
Port string `toml:"-" json:"-" yaml:"-"`
Scheme string `toml:"-" json:"-" yaml:"-" file:"-"`
Port string `toml:"-" json:"-" yaml:"-" file:"-"`
}
// SetDefaults Default values for a Server.

View file

@ -34,7 +34,7 @@ type Middleware struct {
InFlightReq *InFlightReq `json:"inFlightReq,omitempty" toml:"inFlightReq,omitempty" yaml:"inFlightReq,omitempty"`
Buffering *Buffering `json:"buffering,omitempty" toml:"buffering,omitempty" yaml:"buffering,omitempty"`
CircuitBreaker *CircuitBreaker `json:"circuitBreaker,omitempty" toml:"circuitBreaker,omitempty" yaml:"circuitBreaker,omitempty"`
Compress *Compress `json:"compress,omitempty" toml:"compress,omitempty" yaml:"compress,omitempty" label:"allowEmpty"`
Compress *Compress `json:"compress,omitempty" toml:"compress,omitempty" yaml:"compress,omitempty" label:"allowEmpty" file:"allowEmpty"`
PassTLSClientCert *PassTLSClientCert `json:"passTLSClientCert,omitempty" toml:"passTLSClientCert,omitempty" yaml:"passTLSClientCert,omitempty"`
Retry *Retry `json:"retry,omitempty" toml:"retry,omitempty" yaml:"retry,omitempty"`
ContentType *ContentType `json:"contentType,omitempty" toml:"contentType,omitempty" yaml:"contentType,omitempty"`
@ -275,7 +275,7 @@ func (s *IPStrategy) Get() (ip.Strategy, error) {
// IPWhiteList holds the ip white list configuration.
type IPWhiteList struct {
SourceRange []string `json:"sourceRange,omitempty" toml:"sourceRange,omitempty" yaml:"sourceRange,omitempty"`
IPStrategy *IPStrategy `json:"ipStrategy,omitempty" toml:"ipStrategy,omitempty" yaml:"ipStrategy,omitempty" label:"allowEmpty"`
IPStrategy *IPStrategy `json:"ipStrategy,omitempty" toml:"ipStrategy,omitempty" yaml:"ipStrategy,omitempty" label:"allowEmpty" file:"allowEmpty"`
}
// +k8s:deepcopy-gen=true

View file

@ -50,7 +50,7 @@ type TCPRouter struct {
EntryPoints []string `json:"entryPoints,omitempty" toml:"entryPoints,omitempty" yaml:"entryPoints,omitempty"`
Service string `json:"service,omitempty" toml:"service,omitempty" yaml:"service,omitempty"`
Rule string `json:"rule,omitempty" toml:"rule,omitempty" yaml:"rule,omitempty"`
TLS *RouterTCPTLSConfig `json:"tls,omitempty" toml:"tls,omitempty" yaml:"tls,omitempty" label:"allowEmpty"`
TLS *RouterTCPTLSConfig `json:"tls,omitempty" toml:"tls,omitempty" yaml:"tls,omitempty" label:"allowEmpty" file:"allowEmpty"`
}
// +k8s:deepcopy-gen=true

View file

@ -78,5 +78,5 @@ func (l *UDPServersLoadBalancer) Mergeable(loadBalancer *UDPServersLoadBalancer)
// UDPServer defines a UDP server configuration.
type UDPServer struct {
Address string `json:"address,omitempty" toml:"address,omitempty" yaml:"address,omitempty" label:"-"`
Port string `toml:"-" json:"-" yaml:"-"`
Port string `toml:"-" json:"-" yaml:"-" file:"-"`
}

View file

@ -2,7 +2,11 @@
package file
import (
"fmt"
"github.com/BurntSushi/toml"
"github.com/containous/traefik/v2/pkg/config/parser"
"gopkg.in/yaml.v2"
)
// Decode decodes the given configuration file into the given element.
@ -22,11 +26,57 @@ func Decode(filePath string, element interface{}) error {
return err
}
metaOpts := parser.MetadataOpts{TagName: parser.TagLabel, AllowSliceAsStruct: true}
metaOpts := parser.MetadataOpts{TagName: parser.TagFile, AllowSliceAsStruct: false}
err = parser.AddMetadata(element, root, metaOpts)
if err != nil {
return err
}
return parser.Fill(element, root, parser.FillerOpts{AllowSliceAsStruct: true})
return parser.Fill(element, root, parser.FillerOpts{AllowSliceAsStruct: false})
}
// DecodeContent decodes the given configuration file content into the given element.
// The operation goes through three stages roughly summarized as:
// file contents -> tree of untyped nodes
// untyped nodes -> nodes augmented with metadata such as kind (inferred from element)
// "typed" nodes -> typed element.
func DecodeContent(content string, 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

@ -42,6 +42,32 @@ fii = "bir"
assert.Equal(t, expected, element)
}
func TestDecodeContent_TOML(t *testing.T) {
content := `
foo = "bar"
fii = "bir"
[yi]
`
element := &Yo{
Fuu: "test",
}
err := DecodeContent(content, ".toml", element)
require.NoError(t, err)
expected := &Yo{
Foo: "bar",
Fii: "bir",
Fuu: "test",
Yi: &Yi{
Foo: "foo",
Fii: "fii",
},
}
assert.Equal(t, expected, element)
}
func TestDecode_YAML(t *testing.T) {
f, err := ioutil.TempFile("", "traefik-config-*.yaml")
require.NoError(t, err)
@ -74,3 +100,29 @@ yi: {}
}
assert.Equal(t, expected, element)
}
func TestDecodeContent_YAML(t *testing.T) {
content := `
foo: bar
fii: bir
yi: {}
`
element := &Yo{
Fuu: "test",
}
err := DecodeContent(content, ".yaml", element)
require.NoError(t, err)
expected := &Yo{
Foo: "bar",
Fii: "bir",
Fuu: "test",
Yi: &Yi{
Foo: "foo",
Fii: "fii",
},
}
assert.Equal(t, expected, element)
}

View file

@ -6,7 +6,7 @@ type Yo struct {
Foo string
Fii string
Fuu string
Yi *Yi `label:"allowEmpty"`
Yi *Yi `file:"allowEmpty"`
}
func (y *Yo) SetDefaults() {

View file

@ -6,6 +6,11 @@ const (
// - "-": ignore the field.
TagLabel = "label"
// TagFile allows to apply a custom behavior.
// - "allowEmpty": allows to create an empty struct.
// - "-": ignore the field.
TagFile = "file"
// TagLabelSliceAsStruct allows to use a slice of struct by creating one entry into the slice.
// The value is the substitution name used in the label to access the slice.
TagLabelSliceAsStruct = "label-slice-as-struct"

View file

@ -12,7 +12,7 @@ import (
type EntryPoint struct {
Address string `description:"Entry point address." json:"address,omitempty" toml:"address,omitempty" yaml:"address,omitempty"`
Transport *EntryPointsTransport `description:"Configures communication between clients and Traefik." json:"transport,omitempty" toml:"transport,omitempty" yaml:"transport,omitempty"`
ProxyProtocol *ProxyProtocol `description:"Proxy-Protocol configuration." json:"proxyProtocol,omitempty" toml:"proxyProtocol,omitempty" yaml:"proxyProtocol,omitempty" label:"allowEmpty"`
ProxyProtocol *ProxyProtocol `description:"Proxy-Protocol configuration." json:"proxyProtocol,omitempty" toml:"proxyProtocol,omitempty" yaml:"proxyProtocol,omitempty" label:"allowEmpty" file:"allowEmpty"`
ForwardedHeaders *ForwardedHeaders `description:"Trust client forwarding headers." json:"forwardedHeaders,omitempty" toml:"forwardedHeaders,omitempty" yaml:"forwardedHeaders,omitempty"`
HTTP HTTPConfig `description:"HTTP configuration." json:"http,omitempty" toml:"http,omitempty" yaml:"http,omitempty"`
}
@ -51,7 +51,7 @@ func (ep *EntryPoint) SetDefaults() {
type HTTPConfig struct {
Redirections *Redirections `description:"Set of redirection" json:"redirections,omitempty" toml:"redirections,omitempty" yaml:"redirections,omitempty"`
Middlewares []string `description:"Default middlewares for the routers linked to the entry point." json:"middlewares,omitempty" toml:"middlewares,omitempty" yaml:"middlewares,omitempty"`
TLS *TLSConfig `description:"Default TLS configuration for the routers linked to the entry point." json:"tls,omitempty" toml:"tls,omitempty" yaml:"tls,omitempty" label:"allowEmpty"`
TLS *TLSConfig `description:"Default TLS configuration for the routers linked to the entry point." json:"tls,omitempty" toml:"tls,omitempty" yaml:"tls,omitempty" label:"allowEmpty" file:"allowEmpty"`
}
// Redirections is a set of redirection for an entry point.

View file

@ -57,15 +57,15 @@ type Configuration struct {
EntryPoints EntryPoints `description:"Entry points definition." json:"entryPoints,omitempty" toml:"entryPoints,omitempty" yaml:"entryPoints,omitempty" export:"true"`
Providers *Providers `description:"Providers configuration." json:"providers,omitempty" toml:"providers,omitempty" yaml:"providers,omitempty" export:"true"`
API *API `description:"Enable api/dashboard." json:"api,omitempty" toml:"api,omitempty" yaml:"api,omitempty" label:"allowEmpty" export:"true"`
API *API `description:"Enable api/dashboard." json:"api,omitempty" toml:"api,omitempty" yaml:"api,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
Metrics *types.Metrics `description:"Enable a metrics exporter." json:"metrics,omitempty" toml:"metrics,omitempty" yaml:"metrics,omitempty" export:"true"`
Ping *ping.Handler `description:"Enable ping." json:"ping,omitempty" toml:"ping,omitempty" yaml:"ping,omitempty" label:"allowEmpty" export:"true"`
Ping *ping.Handler `description:"Enable ping." json:"ping,omitempty" toml:"ping,omitempty" yaml:"ping,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
Log *types.TraefikLog `description:"Traefik log settings." json:"log,omitempty" toml:"log,omitempty" yaml:"log,omitempty" label:"allowEmpty" export:"true"`
AccessLog *types.AccessLog `description:"Access log settings." json:"accessLog,omitempty" toml:"accessLog,omitempty" yaml:"accessLog,omitempty" label:"allowEmpty" export:"true"`
Tracing *Tracing `description:"OpenTracing configuration." json:"tracing,omitempty" toml:"tracing,omitempty" yaml:"tracing,omitempty" label:"allowEmpty" export:"true"`
Log *types.TraefikLog `description:"Traefik log settings." json:"log,omitempty" toml:"log,omitempty" yaml:"log,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
AccessLog *types.AccessLog `description:"Access log settings." json:"accessLog,omitempty" toml:"accessLog,omitempty" yaml:"accessLog,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
Tracing *Tracing `description:"OpenTracing configuration." json:"tracing,omitempty" toml:"tracing,omitempty" yaml:"tracing,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
HostResolver *types.HostResolverConfig `description:"Enable CNAME Flattening." json:"hostResolver,omitempty" toml:"hostResolver,omitempty" yaml:"hostResolver,omitempty" label:"allowEmpty" export:"true"`
HostResolver *types.HostResolverConfig `description:"Enable CNAME Flattening." json:"hostResolver,omitempty" toml:"hostResolver,omitempty" yaml:"hostResolver,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
CertificatesResolvers map[string]CertificateResolver `description:"Certificates resolvers configuration." json:"certificatesResolvers,omitempty" toml:"certificatesResolvers,omitempty" yaml:"certificatesResolvers,omitempty" export:"true"`
}
@ -77,8 +77,8 @@ type CertificateResolver struct {
// Global holds the global configuration.
type Global struct {
CheckNewVersion bool `description:"Periodically check if a new version has been released." json:"checkNewVersion,omitempty" toml:"checkNewVersion,omitempty" yaml:"checkNewVersion,omitempty" label:"allowEmpty" export:"true"`
SendAnonymousUsage bool `description:"Periodically send anonymous usage statistics. If the option is not specified, it will be enabled by default." json:"sendAnonymousUsage,omitempty" toml:"sendAnonymousUsage,omitempty" yaml:"sendAnonymousUsage,omitempty" label:"allowEmpty" export:"true"`
CheckNewVersion bool `description:"Periodically check if a new version has been released." json:"checkNewVersion,omitempty" toml:"checkNewVersion,omitempty" yaml:"checkNewVersion,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
SendAnonymousUsage bool `description:"Periodically send anonymous usage statistics. If the option is not specified, it will be enabled by default." json:"sendAnonymousUsage,omitempty" toml:"sendAnonymousUsage,omitempty" yaml:"sendAnonymousUsage,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
}
// ServersTransport options to configure communication between Traefik and the servers.
@ -95,8 +95,8 @@ type API struct {
Dashboard bool `description:"Activate dashboard." json:"dashboard,omitempty" toml:"dashboard,omitempty" yaml:"dashboard,omitempty" export:"true"`
Debug bool `description:"Enable additional endpoints for debugging and profiling." json:"debug,omitempty" toml:"debug,omitempty" yaml:"debug,omitempty" export:"true"`
// TODO: Re-enable statistics
// Statistics *types.Statistics `description:"Enable more detailed statistics." json:"statistics,omitempty" toml:"statistics,omitempty" yaml:"statistics,omitempty" export:"true" label:"allowEmpty"`
DashboardAssets *assetfs.AssetFS `json:"-" toml:"-" yaml:"-" label:"-"`
// Statistics *types.Statistics `description:"Enable more detailed statistics." json:"statistics,omitempty" toml:"statistics,omitempty" yaml:"statistics,omitempty" export:"true" label:"allowEmpty" file:"allowEmpty"`
DashboardAssets *assetfs.AssetFS `json:"-" toml:"-" yaml:"-" label:"-" file:"-"`
}
// SetDefaults sets the default values.
@ -144,12 +144,12 @@ func (a *LifeCycle) SetDefaults() {
type Tracing struct {
ServiceName string `description:"Set the name for this service." json:"serviceName,omitempty" toml:"serviceName,omitempty" yaml:"serviceName,omitempty" export:"true"`
SpanNameLimit int `description:"Set the maximum character limit for Span names (default 0 = no limit)." json:"spanNameLimit,omitempty" toml:"spanNameLimit,omitempty" yaml:"spanNameLimit,omitempty" export:"true"`
Jaeger *jaeger.Config `description:"Settings for Jaeger." json:"jaeger,omitempty" toml:"jaeger,omitempty" yaml:"jaeger,omitempty" export:"true" label:"allowEmpty"`
Zipkin *zipkin.Config `description:"Settings for Zipkin." json:"zipkin,omitempty" toml:"zipkin,omitempty" yaml:"zipkin,omitempty" export:"true" label:"allowEmpty"`
Datadog *datadog.Config `description:"Settings for Datadog." json:"datadog,omitempty" toml:"datadog,omitempty" yaml:"datadog,omitempty" export:"true" label:"allowEmpty"`
Instana *instana.Config `description:"Settings for Instana." json:"instana,omitempty" toml:"instana,omitempty" yaml:"instana,omitempty" export:"true" label:"allowEmpty"`
Haystack *haystack.Config `description:"Settings for Haystack." json:"haystack,omitempty" toml:"haystack,omitempty" yaml:"haystack,omitempty" export:"true" label:"allowEmpty"`
Elastic *elastic.Config `description:"Settings for Elastic." json:"elastic,omitempty" toml:"elastic,omitempty" yaml:"elastic,omitempty" export:"true" label:"allowEmpty"`
Jaeger *jaeger.Config `description:"Settings for Jaeger." json:"jaeger,omitempty" toml:"jaeger,omitempty" yaml:"jaeger,omitempty" export:"true" label:"allowEmpty" file:"allowEmpty"`
Zipkin *zipkin.Config `description:"Settings for Zipkin." json:"zipkin,omitempty" toml:"zipkin,omitempty" yaml:"zipkin,omitempty" export:"true" label:"allowEmpty" file:"allowEmpty"`
Datadog *datadog.Config `description:"Settings for Datadog." json:"datadog,omitempty" toml:"datadog,omitempty" yaml:"datadog,omitempty" export:"true" label:"allowEmpty" file:"allowEmpty"`
Instana *instana.Config `description:"Settings for Instana." json:"instana,omitempty" toml:"instana,omitempty" yaml:"instana,omitempty" export:"true" label:"allowEmpty" file:"allowEmpty"`
Haystack *haystack.Config `description:"Settings for Haystack." json:"haystack,omitempty" toml:"haystack,omitempty" yaml:"haystack,omitempty" export:"true" label:"allowEmpty" file:"allowEmpty"`
Elastic *elastic.Config `description:"Settings for Elastic." json:"elastic,omitempty" toml:"elastic,omitempty" yaml:"elastic,omitempty" export:"true" label:"allowEmpty" file:"allowEmpty"`
}
// SetDefaults sets the default values.
@ -162,19 +162,19 @@ func (t *Tracing) SetDefaults() {
type Providers struct {
ProvidersThrottleDuration types.Duration `description:"Backends throttle duration: minimum duration between 2 events from providers before applying a new configuration. It avoids unnecessary reloads if multiples events are sent in a short amount of time." json:"providersThrottleDuration,omitempty" toml:"providersThrottleDuration,omitempty" yaml:"providersThrottleDuration,omitempty" export:"true"`
Docker *docker.Provider `description:"Enable Docker backend with default settings." json:"docker,omitempty" toml:"docker,omitempty" yaml:"docker,omitempty" export:"true" label:"allowEmpty"`
Docker *docker.Provider `description:"Enable Docker backend with default settings." json:"docker,omitempty" toml:"docker,omitempty" yaml:"docker,omitempty" export:"true" label:"allowEmpty" file:"allowEmpty"`
File *file.Provider `description:"Enable File backend with default settings." json:"file,omitempty" toml:"file,omitempty" yaml:"file,omitempty" export:"true"`
Marathon *marathon.Provider `description:"Enable Marathon backend with default settings." json:"marathon,omitempty" toml:"marathon,omitempty" yaml:"marathon,omitempty" export:"true" label:"allowEmpty"`
KubernetesIngress *ingress.Provider `description:"Enable Kubernetes backend with default settings." json:"kubernetesIngress,omitempty" toml:"kubernetesIngress,omitempty" yaml:"kubernetesIngress,omitempty" export:"true" label:"allowEmpty"`
KubernetesCRD *crd.Provider `description:"Enable Kubernetes backend with default settings." json:"kubernetesCRD,omitempty" toml:"kubernetesCRD,omitempty" yaml:"kubernetesCRD,omitempty" export:"true" label:"allowEmpty"`
Rest *rest.Provider `description:"Enable Rest backend with default settings." json:"rest,omitempty" toml:"rest,omitempty" yaml:"rest,omitempty" export:"true" label:"allowEmpty"`
Rancher *rancher.Provider `description:"Enable Rancher backend with default settings." json:"rancher,omitempty" toml:"rancher,omitempty" yaml:"rancher,omitempty" export:"true" label:"allowEmpty"`
Marathon *marathon.Provider `description:"Enable Marathon backend with default settings." json:"marathon,omitempty" toml:"marathon,omitempty" yaml:"marathon,omitempty" export:"true" label:"allowEmpty" file:"allowEmpty"`
KubernetesIngress *ingress.Provider `description:"Enable Kubernetes backend with default settings." json:"kubernetesIngress,omitempty" toml:"kubernetesIngress,omitempty" yaml:"kubernetesIngress,omitempty" export:"true" label:"allowEmpty" file:"allowEmpty"`
KubernetesCRD *crd.Provider `description:"Enable Kubernetes backend with default settings." json:"kubernetesCRD,omitempty" toml:"kubernetesCRD,omitempty" yaml:"kubernetesCRD,omitempty" export:"true" label:"allowEmpty" file:"allowEmpty"`
Rest *rest.Provider `description:"Enable Rest backend with default settings." json:"rest,omitempty" toml:"rest,omitempty" yaml:"rest,omitempty" export:"true" label:"allowEmpty" file:"allowEmpty"`
Rancher *rancher.Provider `description:"Enable Rancher backend with default settings." json:"rancher,omitempty" toml:"rancher,omitempty" yaml:"rancher,omitempty" export:"true" label:"allowEmpty" file:"allowEmpty"`
ConsulCatalog *consulcatalog.Provider `description:"Enable ConsulCatalog backend with default settings." json:"consulCatalog,omitempty" toml:"consulCatalog,omitempty" yaml:"consulCatalog,omitempty"`
Consul *consul.Provider `description:"Enable Consul backend with default settings." json:"consul,omitempty" toml:"consul,omitempty" yaml:"consul,omitempty" export:"true" label:"allowEmpty"`
Etcd *etcd.Provider `description:"Enable Etcd backend with default settings." json:"etcd,omitempty" toml:"etcd,omitempty" yaml:"etcd,omitempty" export:"true" label:"allowEmpty"`
ZooKeeper *zk.Provider `description:"Enable ZooKeeper backend with default settings." json:"zooKeeper,omitempty" toml:"zooKeeper,omitempty" yaml:"zooKeeper,omitempty" export:"true" label:"allowEmpty"`
Redis *redis.Provider `description:"Enable Redis backend with default settings." json:"redis,omitempty" toml:"redis,omitempty" yaml:"redis,omitempty" export:"true" label:"allowEmpty"`
Consul *consul.Provider `description:"Enable Consul backend with default settings." json:"consul,omitempty" toml:"consul,omitempty" yaml:"consul,omitempty" export:"true" label:"allowEmpty" file:"allowEmpty"`
Etcd *etcd.Provider `description:"Enable Etcd backend with default settings." json:"etcd,omitempty" toml:"etcd,omitempty" yaml:"etcd,omitempty" export:"true" label:"allowEmpty" file:"allowEmpty"`
ZooKeeper *zk.Provider `description:"Enable ZooKeeper backend with default settings." json:"zooKeeper,omitempty" toml:"zooKeeper,omitempty" yaml:"zooKeeper,omitempty" export:"true" label:"allowEmpty" file:"allowEmpty"`
Redis *redis.Provider `description:"Enable Redis backend with default settings." json:"redis,omitempty" toml:"redis,omitempty" yaml:"redis,omitempty" export:"true" label:"allowEmpty" file:"allowEmpty"`
}
// SetEffectiveConfiguration adds missing configuration parameters derived from existing ones.

View file

@ -38,9 +38,9 @@ type Configuration struct {
CAServer string `description:"CA server to use." json:"caServer,omitempty" toml:"caServer,omitempty" yaml:"caServer,omitempty"`
Storage string `description:"Storage to use." json:"storage,omitempty" toml:"storage,omitempty" yaml:"storage,omitempty"`
KeyType string `description:"KeyType used for generating certificate private key. Allow value 'EC256', 'EC384', 'RSA2048', 'RSA4096', 'RSA8192'." json:"keyType,omitempty" toml:"keyType,omitempty" yaml:"keyType,omitempty"`
DNSChallenge *DNSChallenge `description:"Activate DNS-01 Challenge." json:"dnsChallenge,omitempty" toml:"dnsChallenge,omitempty" yaml:"dnsChallenge,omitempty" label:"allowEmpty"`
HTTPChallenge *HTTPChallenge `description:"Activate HTTP-01 Challenge." json:"httpChallenge,omitempty" toml:"httpChallenge,omitempty" yaml:"httpChallenge,omitempty" label:"allowEmpty"`
TLSChallenge *TLSChallenge `description:"Activate TLS-ALPN-01 Challenge." json:"tlsChallenge,omitempty" toml:"tlsChallenge,omitempty" yaml:"tlsChallenge,omitempty" label:"allowEmpty"`
DNSChallenge *DNSChallenge `description:"Activate DNS-01 Challenge." json:"dnsChallenge,omitempty" toml:"dnsChallenge,omitempty" yaml:"dnsChallenge,omitempty" label:"allowEmpty" file:"allowEmpty"`
HTTPChallenge *HTTPChallenge `description:"Activate HTTP-01 Challenge." json:"httpChallenge,omitempty" toml:"httpChallenge,omitempty" yaml:"httpChallenge,omitempty" label:"allowEmpty" file:"allowEmpty"`
TLSChallenge *TLSChallenge `description:"Activate TLS-ALPN-01 Challenge." json:"tlsChallenge,omitempty" toml:"tlsChallenge,omitempty" yaml:"tlsChallenge,omitempty" label:"allowEmpty" file:"allowEmpty"`
}
// SetDefaults sets the default values.

View file

@ -11,15 +11,14 @@ import (
"strings"
"text/template"
"github.com/BurntSushi/toml"
"github.com/Masterminds/sprig"
"github.com/containous/traefik/v2/pkg/config/dynamic"
"github.com/containous/traefik/v2/pkg/config/file"
"github.com/containous/traefik/v2/pkg/log"
"github.com/containous/traefik/v2/pkg/provider"
"github.com/containous/traefik/v2/pkg/safe"
"github.com/containous/traefik/v2/pkg/tls"
"gopkg.in/fsnotify.v1"
"gopkg.in/yaml.v2"
)
const providerName = "file"
@ -418,22 +417,9 @@ func (p *Provider) decodeConfiguration(filePath string, content string) (*dynami
},
}
switch strings.ToLower(filepath.Ext(filePath)) {
case ".toml":
_, err := toml.Decode(content, configuration)
if err != nil {
return nil, err
}
case ".yml", ".yaml":
var err error
err = yaml.Unmarshal([]byte(content), configuration)
if err != nil {
return nil, err
}
default:
return nil, fmt.Errorf("unsupported file extension: %s", filePath)
err := file.DecodeContent(content, strings.ToLower(filepath.Ext(filePath)), configuration)
if err != nil {
return nil, err
}
return configuration, nil

View file

@ -90,11 +90,12 @@ func TestProvideWithoutWatch(t *testing.T) {
timeout := time.After(time.Second)
select {
case conf := <-configChan:
require.NotNil(t, conf.Configuration.HTTP)
numServices := len(conf.Configuration.HTTP.Services) + len(conf.Configuration.TCP.Services) + len(conf.Configuration.UDP.Services)
numRouters := len(conf.Configuration.HTTP.Routers) + len(conf.Configuration.TCP.Routers) + len(conf.Configuration.UDP.Routers)
assert.Equal(t, numServices, test.expectedNumService)
assert.Equal(t, numRouters, test.expectedNumRouter)
assert.Equal(t, test.expectedNumService, numServices)
assert.Equal(t, test.expectedNumRouter, numRouters)
require.NotNil(t, conf.Configuration.TLS)
assert.Len(t, conf.Configuration.TLS.Certificates, test.expectedNumTLSConf)
assert.Len(t, conf.Configuration.TLS.Options, test.expectedNumTLSOptions)

View file

@ -1,5 +1,5 @@
[http.routers]
[http.routers."router1"]
service = "application-1"

View file

@ -66,7 +66,7 @@
[tcp.services.applicationtcp-1.loadBalancer]
[[tcp.services.applicationtcp-1.loadBalancer.servers]]
url = "http://172.17.0.9:80"
address = "http://172.17.0.9:80"
[udp.routers]
@ -77,4 +77,4 @@
[udp.services.applicationudp-1.loadBalancer]
[[udp.services.applicationudp-1.loadBalancer.servers]]
url = "http://172.17.0.10:80"
address = "http://172.17.0.10:80"

View file

@ -1,6 +1,6 @@
[http.services]
{{ range $i, $e := until 20 }}
[http.services.application-{{ $e }}]
[[http.services.application-{{ $e }}.servers]]
url="http://127.0.0.1"
[http.services.application-{{ $e }}.loadBalancer]
[[http.services.application-{{ $e }}.loadBalancer.servers]]
url = "http://127.0.0.1"
{{ end }}

View file

@ -1,6 +1,6 @@
http:
{{ range $i, $e := until 20 }}
routers:
{{ range $i, $e := until 20 }}
router{{ $e }}:
service: application-1
{{ end }}

View file

@ -6,10 +6,10 @@ import (
// Metrics provides options to expose and send Traefik metrics to different third party monitoring systems.
type Metrics struct {
Prometheus *Prometheus `description:"Prometheus metrics exporter type." json:"prometheus,omitempty" toml:"prometheus,omitempty" yaml:"prometheus,omitempty" export:"true" label:"allowEmpty"`
Datadog *Datadog `description:"Datadog metrics exporter type." json:"datadog,omitempty" toml:"datadog,omitempty" yaml:"datadog,omitempty" export:"true" label:"allowEmpty"`
StatsD *Statsd `description:"StatsD metrics exporter type." json:"statsD,omitempty" toml:"statsD,omitempty" yaml:"statsD,omitempty" export:"true" label:"allowEmpty"`
InfluxDB *InfluxDB `description:"InfluxDB metrics exporter type." json:"influxDB,omitempty" toml:"influxDB,omitempty" yaml:"influxDB,omitempty" label:"allowEmpty"`
Prometheus *Prometheus `description:"Prometheus metrics exporter type." json:"prometheus,omitempty" toml:"prometheus,omitempty" yaml:"prometheus,omitempty" export:"true" label:"allowEmpty" file:"allowEmpty"`
Datadog *Datadog `description:"Datadog metrics exporter type." json:"datadog,omitempty" toml:"datadog,omitempty" yaml:"datadog,omitempty" export:"true" label:"allowEmpty" file:"allowEmpty"`
StatsD *Statsd `description:"StatsD metrics exporter type." json:"statsD,omitempty" toml:"statsD,omitempty" yaml:"statsD,omitempty" export:"true" label:"allowEmpty" file:"allowEmpty"`
InfluxDB *InfluxDB `description:"InfluxDB metrics exporter type." json:"influxDB,omitempty" toml:"influxDB,omitempty" yaml:"influxDB,omitempty" label:"allowEmpty" file:"allowEmpty"`
}
// Prometheus can contain specific configuration used by the Prometheus Metrics exporter.