New static configuration loading system.
Co-authored-by: Mathieu Lonjaret <mathieu.lonjaret@gmail.com>
This commit is contained in:
parent
d18edd6f77
commit
8d7eccad5d
165 changed files with 10894 additions and 6076 deletions
31
pkg/config/file/file.go
Normal file
31
pkg/config/file/file.go
Normal file
|
@ -0,0 +1,31 @@
|
|||
// Package file implements decoding between configuration in a file and a typed Configuration.
|
||||
package file
|
||||
|
||||
import (
|
||||
"github.com/containous/traefik/pkg/config/parser"
|
||||
)
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
err = parser.AddMetadata(element, root)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return parser.Fill(element, root)
|
||||
}
|
86
pkg/config/file/file_node.go
Normal file
86
pkg/config/file/file_node.go
Normal file
|
@ -0,0 +1,86 @@
|
|||
package file
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
|
||||
"github.com/BurntSushi/toml"
|
||||
"github.com/containous/traefik/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 filepath.Ext(filePath) {
|
||||
case ".toml":
|
||||
err = toml.Unmarshal(content, &data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
case ".yml", ".yaml":
|
||||
var err error
|
||||
err = yaml.Unmarshal(content, data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return decodeRawToNode(data, filters...)
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported file extension: %s", filePath)
|
||||
}
|
||||
|
||||
return decodeRawToNode(data, filters...)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
599
pkg/config/file/file_node_test.go
Normal file
599
pkg/config/file/file_node_test.go
Normal file
|
@ -0,0 +1,599 @@
|
|||
package file
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/containous/traefik/pkg/config/parser"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
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_compare(t *testing.T) {
|
||||
nodeToml, err := decodeFileToNode("./fixtures/sample.toml",
|
||||
"Global", "ServersTransport", "EntryPoints", "Providers", "API", "Metrics", "Ping", "Log", "AccessLog", "Tracing", "HostResolver", "ACME")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
nodeYaml, err := decodeFileToNode("./fixtures/sample.yml")
|
||||
if err != nil {
|
||||
t.Fatal(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", "ACME")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
expected := &parser.Node{
|
||||
Name: "traefik",
|
||||
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: "Domains", Children: []*parser.Node{
|
||||
{Name: "[0]", Children: []*parser.Node{
|
||||
{Name: "Main", Value: "foobar"},
|
||||
{Name: "SANs", Value: "foobar,foobar"},
|
||||
}},
|
||||
{Name: "[1]", Children: []*parser.Node{
|
||||
{Name: "Main", Value: "foobar"},
|
||||
{Name: "SANs", 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: "OnHostRule", Value: "true"},
|
||||
{Name: "Storage", Value: "foobar"},
|
||||
{Name: "TLSChallenge"},
|
||||
},
|
||||
},
|
||||
{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: "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: "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: "Debug", 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", Children: []*parser.Node{
|
||||
{Name: "[0]", Children: []*parser.Node{
|
||||
{Name: "Key", Value: "foobar"},
|
||||
{Name: "MustMatch", Value: "true"},
|
||||
{Name: "Value", Value: "foobar"},
|
||||
}},
|
||||
{Name: "[1]", Children: []*parser.Node{
|
||||
{Name: "Key", Value: "foobar"},
|
||||
{Name: "MustMatch", Value: "true"},
|
||||
{Name: "Value", 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: "TraefikFile", Value: "foobar"},
|
||||
{Name: "Watch", Value: "true"}}},
|
||||
{Name: "Kubernetes", 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: "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: "Marathon", Children: []*parser.Node{
|
||||
{Name: "Basic", Children: []*parser.Node{
|
||||
{Name: "HTTPBasicAuthUser", Value: "foobar"},
|
||||
{Name: "HTTPBasicPassword", Value: "foobar"}}},
|
||||
{Name: "Constraints", Children: []*parser.Node{
|
||||
{Name: "[0]", Children: []*parser.Node{
|
||||
{Name: "Key", Value: "foobar"},
|
||||
{Name: "MustMatch", Value: "true"},
|
||||
{Name: "Value", Value: "foobar"},
|
||||
}},
|
||||
{Name: "[1]", Children: []*parser.Node{
|
||||
{Name: "Key", Value: "foobar"},
|
||||
{Name: "MustMatch", Value: "true"},
|
||||
{Name: "Value", Value: "foobar"},
|
||||
}},
|
||||
}},
|
||||
{Name: "DCOSToken", Value: "foobar"},
|
||||
{Name: "DefaultRule", Value: "foobar"},
|
||||
{Name: "DialerTimeout", Value: "42"},
|
||||
{Name: "Endpoint", Value: "foobar"},
|
||||
{Name: "ExposedByDefault", Value: "true"},
|
||||
{Name: "FilterMarathonConstraints", 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", Children: []*parser.Node{
|
||||
{Name: "[0]", Children: []*parser.Node{
|
||||
{Name: "Key", Value: "foobar"},
|
||||
{Name: "MustMatch", Value: "true"},
|
||||
{Name: "Value", Value: "foobar"},
|
||||
}},
|
||||
{Name: "[1]", Children: []*parser.Node{
|
||||
{Name: "Key", Value: "foobar"},
|
||||
{Name: "MustMatch", Value: "true"},
|
||||
{Name: "Value", 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: "ResponseHeaderTimeout", Value: "42"}}},
|
||||
{Name: "InsecureSkipVerify", Value: "true"},
|
||||
{Name: "MaxIdleConnsPerHost", Value: "42"},
|
||||
{Name: "RootCAs", Value: "foobar,foobar"}}},
|
||||
{Name: "Tracing", Children: []*parser.Node{
|
||||
{Name: "Backend", Value: "foobar"},
|
||||
{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: "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: "Debug", Value: "true"},
|
||||
{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")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
expected := &parser.Node{
|
||||
Name: "traefik",
|
||||
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: "Domains", Children: []*parser.Node{
|
||||
{Name: "[0]", Children: []*parser.Node{
|
||||
{Name: "Main", Value: "foobar"},
|
||||
{Name: "SANs", Value: "foobar,foobar"},
|
||||
}},
|
||||
{Name: "[1]", Children: []*parser.Node{
|
||||
{Name: "Main", Value: "foobar"},
|
||||
{Name: "SANs", 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: "OnHostRule", Value: "true"},
|
||||
{Name: "Storage", Value: "foobar"},
|
||||
{Name: "TLSChallenge"},
|
||||
},
|
||||
},
|
||||
{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: "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: "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: "Debug", 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", Children: []*parser.Node{
|
||||
{Name: "[0]", Children: []*parser.Node{
|
||||
{Name: "Key", Value: "foobar"},
|
||||
{Name: "MustMatch", Value: "true"},
|
||||
{Name: "Value", Value: "foobar"},
|
||||
}},
|
||||
{Name: "[1]", Children: []*parser.Node{
|
||||
{Name: "Key", Value: "foobar"},
|
||||
{Name: "MustMatch", Value: "true"},
|
||||
{Name: "Value", 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: "TraefikFile", Value: "foobar"},
|
||||
{Name: "Watch", Value: "true"}}},
|
||||
{Name: "Kubernetes", 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: "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: "Marathon", Children: []*parser.Node{
|
||||
{Name: "Basic", Children: []*parser.Node{
|
||||
{Name: "HTTPBasicAuthUser", Value: "foobar"},
|
||||
{Name: "HTTPBasicPassword", Value: "foobar"}}},
|
||||
{Name: "Constraints", Children: []*parser.Node{
|
||||
{Name: "[0]", Children: []*parser.Node{
|
||||
{Name: "Key", Value: "foobar"},
|
||||
{Name: "MustMatch", Value: "true"},
|
||||
{Name: "Value", Value: "foobar"},
|
||||
}},
|
||||
{Name: "[1]", Children: []*parser.Node{
|
||||
{Name: "Key", Value: "foobar"},
|
||||
{Name: "MustMatch", Value: "true"},
|
||||
{Name: "Value", Value: "foobar"},
|
||||
}},
|
||||
}},
|
||||
{Name: "DCOSToken", Value: "foobar"},
|
||||
{Name: "DefaultRule", Value: "foobar"},
|
||||
{Name: "DialerTimeout", Value: "42"},
|
||||
{Name: "Endpoint", Value: "foobar"},
|
||||
{Name: "ExposedByDefault", Value: "true"},
|
||||
{Name: "FilterMarathonConstraints", 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", Children: []*parser.Node{
|
||||
{Name: "[0]", Children: []*parser.Node{
|
||||
{Name: "Key", Value: "foobar"},
|
||||
{Name: "MustMatch", Value: "true"},
|
||||
{Name: "Value", Value: "foobar"},
|
||||
}},
|
||||
{Name: "[1]", Children: []*parser.Node{
|
||||
{Name: "Key", Value: "foobar"},
|
||||
{Name: "MustMatch", Value: "true"},
|
||||
{Name: "Value", 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: "ResponseHeaderTimeout", Value: "42"}}},
|
||||
{Name: "InsecureSkipVerify", Value: "true"},
|
||||
{Name: "MaxIdleConnsPerHost", Value: "42"},
|
||||
{Name: "RootCAs", Value: "foobar,foobar"}}},
|
||||
{Name: "Tracing", Children: []*parser.Node{
|
||||
{Name: "Backend", Value: "foobar"},
|
||||
{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: "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: "Debug", Value: "true"},
|
||||
{Name: "HTTPEndpoint", Value: "foobar"},
|
||||
{Name: "ID128Bit", Value: "true"},
|
||||
{Name: "SameSpan", Value: "true"},
|
||||
{Name: "SampleRate", Value: "42"}}}}}},
|
||||
}
|
||||
|
||||
assert.Equal(t, expected, node)
|
||||
}
|
76
pkg/config/file/file_test.go
Normal file
76
pkg/config/file/file_test.go
Normal file
|
@ -0,0 +1,76 @@
|
|||
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 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)
|
||||
}
|
539
pkg/config/file/fixtures/sample.toml
Normal file
539
pkg/config/file/fixtures/sample.toml
Normal file
|
@ -0,0 +1,539 @@
|
|||
[Global]
|
||||
Debug = true
|
||||
CheckNewVersion = true
|
||||
SendAnonymousUsage = true
|
||||
|
||||
[ServersTransport]
|
||||
InsecureSkipVerify = true
|
||||
RootCAs = ["foobar", "foobar"]
|
||||
MaxIdleConnsPerHost = 42
|
||||
[ServersTransport.ForwardingTimeouts]
|
||||
DialTimeout = 42
|
||||
ResponseHeaderTimeout = 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]
|
||||
Watch = true
|
||||
Endpoint = "foobar"
|
||||
DefaultRule = "foobar"
|
||||
ExposedByDefault = true
|
||||
UseBindPortIP = true
|
||||
SwarmMode = true
|
||||
Network = "foobar"
|
||||
SwarmModeRefreshSeconds = 42
|
||||
|
||||
[[Providers.Docker.Constraints]]
|
||||
Key = "foobar"
|
||||
MustMatch = true
|
||||
Value = "foobar"
|
||||
|
||||
[[Providers.Docker.Constraints]]
|
||||
Key = "foobar"
|
||||
MustMatch = true
|
||||
Value = "foobar"
|
||||
|
||||
[Providers.Docker.TLS]
|
||||
CA = "foobar"
|
||||
CAOptional = true
|
||||
Cert = "foobar"
|
||||
Key = "foobar"
|
||||
InsecureSkipVerify = true
|
||||
|
||||
[Providers.File]
|
||||
Directory = "foobar"
|
||||
Watch = true
|
||||
Filename = "foobar"
|
||||
DebugLogGeneratedTemplate = true
|
||||
TraefikFile = "foobar"
|
||||
|
||||
[Providers.Marathon]
|
||||
Trace = true
|
||||
Watch = true
|
||||
Endpoint = "foobar"
|
||||
DefaultRule = "foobar"
|
||||
ExposedByDefault = true
|
||||
DCOSToken = "foobar"
|
||||
FilterMarathonConstraints = true
|
||||
DialerTimeout = 42
|
||||
ResponseHeaderTimeout = 42
|
||||
TLSHandshakeTimeout = 42
|
||||
KeepAlive = 42
|
||||
ForceTaskHostname = true
|
||||
RespectReadinessChecks = true
|
||||
|
||||
[[Providers.Marathon.Constraints]]
|
||||
Key = "foobar"
|
||||
MustMatch = true
|
||||
Value = "foobar"
|
||||
|
||||
[[Providers.Marathon.Constraints]]
|
||||
Key = "foobar"
|
||||
MustMatch = true
|
||||
Value = "foobar"
|
||||
|
||||
[Providers.Marathon.TLS]
|
||||
CA = "foobar"
|
||||
CAOptional = true
|
||||
Cert = "foobar"
|
||||
Key = "foobar"
|
||||
InsecureSkipVerify = true
|
||||
[Providers.Marathon.Basic]
|
||||
HTTPBasicAuthUser = "foobar"
|
||||
HTTPBasicPassword = "foobar"
|
||||
|
||||
[Providers.Kubernetes]
|
||||
Endpoint = "foobar"
|
||||
Token = "foobar"
|
||||
CertAuthFilePath = "foobar"
|
||||
DisablePassHostHeaders = true
|
||||
Namespaces = ["foobar", "foobar"]
|
||||
LabelSelector = "foobar"
|
||||
IngressClass = "foobar"
|
||||
[Providers.Kubernetes.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]
|
||||
Watch = true
|
||||
DefaultRule = "foobar"
|
||||
ExposedByDefault = true
|
||||
EnableServiceHealthFilter = true
|
||||
RefreshSeconds = 42
|
||||
IntervalPoll = true
|
||||
Prefix = "foobar"
|
||||
|
||||
[[Providers.Rancher.Constraints]]
|
||||
Key = "foobar"
|
||||
MustMatch = true
|
||||
Value = "foobar"
|
||||
|
||||
[[Providers.Rancher.Constraints]]
|
||||
Key = "foobar"
|
||||
MustMatch = true
|
||||
Value = "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]
|
||||
Backend = "foobar"
|
||||
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
|
||||
Debug = 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"
|
||||
|
||||
[HostResolver]
|
||||
CnameFlattening = true
|
||||
ResolvConfig = "foobar"
|
||||
ResolvDepth = 42
|
||||
|
||||
[ACME]
|
||||
Email = "foobar"
|
||||
ACMELogging = true
|
||||
CAServer = "foobar"
|
||||
Storage = "foobar"
|
||||
EntryPoint = "foobar"
|
||||
KeyType = "foobar"
|
||||
OnHostRule = true
|
||||
|
||||
[ACME.DNSChallenge]
|
||||
Provider = "foobar"
|
||||
DelayBeforeCheck = 42
|
||||
Resolvers = ["foobar", "foobar"]
|
||||
DisablePropagationCheck = true
|
||||
|
||||
[ACME.HTTPChallenge]
|
||||
EntryPoint = "foobar"
|
||||
|
||||
[ACME.TLSChallenge]
|
||||
|
||||
[[ACME.Domains]]
|
||||
Main = "foobar"
|
||||
SANs = ["foobar", "foobar"]
|
||||
|
||||
[[ACME.Domains]]
|
||||
Main = "foobar"
|
||||
SANs = ["foobar", "foobar"]
|
||||
|
||||
#### 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.AddPrefix]
|
||||
Prefix = "foobar"
|
||||
|
||||
[HTTP.Middlewares.Middleware1.StripPrefix]
|
||||
Prefixes = ["foobar", "foobar"]
|
||||
|
||||
[HTTP.Middlewares.Middleware2.StripPrefixRegex]
|
||||
Regex = ["foobar", "foobar"]
|
||||
|
||||
[HTTP.Middlewares.Middleware3.ReplacePath]
|
||||
Path = "foobar"
|
||||
|
||||
[HTTP.Middlewares.Middleware4.ReplacePathRegex]
|
||||
Regex = "foobar"
|
||||
Replacement = "foobar"
|
||||
|
||||
[HTTP.Middlewares.Middleware5.Chain]
|
||||
Middlewares = ["foobar", "foobar"]
|
||||
|
||||
[HTTP.Middlewares.Middleware6.IPWhiteList]
|
||||
SourceRange = ["foobar", "foobar"]
|
||||
|
||||
[HTTP.Middlewares.Middleware7.IPWhiteList.IPStrategy]
|
||||
Depth = 42
|
||||
ExcludedIPs = ["foobar", "foobar"]
|
||||
|
||||
[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"
|
||||
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.Errors]
|
||||
Status = ["foobar", "foobar"]
|
||||
Service = "foobar"
|
||||
Query = "foobar"
|
||||
|
||||
[HTTP.Middlewares.Middleware10.RateLimit]
|
||||
ExtractorFunc = "foobar"
|
||||
[HTTP.Middlewares.Middleware10.RateLimit.RateSet]
|
||||
[HTTP.Middlewares.Middleware10.RateLimit.RateSet.Rate0]
|
||||
Period = 42
|
||||
Average = 42
|
||||
Burst = 42
|
||||
[HTTP.Middlewares.Middleware10.RateLimit.RateSet.Rate1]
|
||||
Period = 42
|
||||
Average = 42
|
||||
Burst = 42
|
||||
|
||||
[HTTP.Middlewares.Middleware11.RedirectRegex]
|
||||
Regex = "foobar"
|
||||
Replacement = "foobar"
|
||||
Permanent = true
|
||||
|
||||
[HTTP.Middlewares.Middleware12.RedirectScheme]
|
||||
Scheme = "foobar"
|
||||
Port = "foobar"
|
||||
Permanent = true
|
||||
|
||||
[HTTP.Middlewares.Middleware13.BasicAuth]
|
||||
Users = ["foobar", "foobar"]
|
||||
UsersFile = "foobar"
|
||||
Realm = "foobar"
|
||||
RemoveHeader = true
|
||||
HeaderField = "foobar"
|
||||
|
||||
[HTTP.Middlewares.Middleware14.DigestAuth]
|
||||
Users = ["foobar", "foobar"]
|
||||
UsersFile = "foobar"
|
||||
RemoveHeader = true
|
||||
Realm = "foobar"
|
||||
HeaderField = "foobar"
|
||||
|
||||
[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.MaxConn]
|
||||
Amount = 42
|
||||
ExtractorFunc = "foobar"
|
||||
|
||||
[HTTP.Middlewares.Middleware17.Buffering]
|
||||
MaxRequestBodyBytes = 42
|
||||
MemRequestBodyBytes = 42
|
||||
MaxResponseBodyBytes = 42
|
||||
MemResponseBodyBytes = 42
|
||||
RetryExpression = "foobar"
|
||||
|
||||
[HTTP.Middlewares.Middleware18.CircuitBreaker]
|
||||
Expression = "foobar"
|
||||
|
||||
[HTTP.Middlewares.Middleware19.Compress]
|
||||
|
||||
[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.Retry]
|
||||
Attempts = 42
|
||||
|
||||
[HTTP.Services]
|
||||
[HTTP.Services.Service0]
|
||||
[HTTP.Services.Service0.LoadBalancer]
|
||||
Method = "foobar"
|
||||
PassHostHeader = true
|
||||
|
||||
[[HTTP.Services.Service0.LoadBalancer.Servers]]
|
||||
URL = "foobar"
|
||||
|
||||
[HTTP.Services.Service0.LoadBalancer.Stickiness]
|
||||
CookieName = "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]
|
||||
Method = "foobar"
|
||||
|
||||
[[TCP.Services.TCPService0.LoadBalancer.Servers]]
|
||||
Address = "foobar"
|
||||
|
||||
[[TCP.Services.TCPService0.LoadBalancer.Servers]]
|
||||
Address = "foobar"
|
||||
|
||||
[[TLS]]
|
||||
Stores = ["foobar", "foobar"]
|
||||
[TLS.Certificate]
|
||||
CertFile = "foobar"
|
||||
KeyFile = "foobar"
|
||||
|
||||
[[TLS]]
|
||||
Stores = ["foobar", "foobar"]
|
||||
[TLS.Certificate]
|
||||
CertFile = "foobar"
|
||||
KeyFile = "foobar"
|
||||
|
||||
[TLSOptions]
|
||||
|
||||
[TLSOptions.TLS0]
|
||||
MinVersion = "foobar"
|
||||
CipherSuites = ["foobar", "foobar"]
|
||||
SniStrict = true
|
||||
[TLSOptions.TLS0.ClientCA]
|
||||
Files = ["foobar", "foobar"]
|
||||
Optional = true
|
||||
[TLSOptions.TLS1]
|
||||
MinVersion = "foobar"
|
||||
CipherSuites = ["foobar", "foobar"]
|
||||
SniStrict = true
|
||||
[TLSOptions.TLS1.ClientCA]
|
||||
Files = ["foobar", "foobar"]
|
||||
Optional = true
|
||||
|
||||
[TLSStores]
|
||||
|
||||
[TLSStores.Store0]
|
||||
[TLSStores.Store0.DefaultCertificate]
|
||||
CertFile = "foobar"
|
||||
KeyFile = "foobar"
|
||||
[TLSStores.Store1]
|
||||
[TLSStores.Store1.DefaultCertificate]
|
||||
CertFile = "foobar"
|
||||
KeyFile = "foobar"
|
257
pkg/config/file/fixtures/sample.yml
Normal file
257
pkg/config/file/fixtures/sample.yml
Normal file
|
@ -0,0 +1,257 @@
|
|||
Global:
|
||||
Debug: true
|
||||
CheckNewVersion: true
|
||||
SendAnonymousUsage: true
|
||||
ServersTransport:
|
||||
InsecureSkipVerify: true
|
||||
RootCAs:
|
||||
- foobar
|
||||
- foobar
|
||||
MaxIdleConnsPerHost: 42
|
||||
ForwardingTimeouts:
|
||||
DialTimeout: 42
|
||||
ResponseHeaderTimeout: 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:
|
||||
Watch: true
|
||||
Endpoint: foobar
|
||||
DefaultRule: foobar
|
||||
ExposedByDefault: true
|
||||
UseBindPortIP: true
|
||||
SwarmMode: true
|
||||
Network: foobar
|
||||
SwarmModeRefreshSeconds: 42
|
||||
Constraints:
|
||||
- Key: foobar
|
||||
MustMatch: true
|
||||
Value: foobar
|
||||
- Key: foobar
|
||||
MustMatch: true
|
||||
Value: foobar
|
||||
TLS:
|
||||
CA: foobar
|
||||
CAOptional: true
|
||||
Cert: foobar
|
||||
Key: foobar
|
||||
InsecureSkipVerify: true
|
||||
File:
|
||||
Directory: foobar
|
||||
Watch: true
|
||||
Filename: foobar
|
||||
DebugLogGeneratedTemplate: true
|
||||
TraefikFile: foobar
|
||||
Marathon:
|
||||
Trace: true
|
||||
Watch: true
|
||||
Endpoint: foobar
|
||||
DefaultRule: foobar
|
||||
ExposedByDefault: true
|
||||
DCOSToken: foobar
|
||||
FilterMarathonConstraints: true
|
||||
DialerTimeout: 42
|
||||
ResponseHeaderTimeout: 42
|
||||
TLSHandshakeTimeout: 42
|
||||
KeepAlive: 42
|
||||
ForceTaskHostname: true
|
||||
RespectReadinessChecks: true
|
||||
Constraints:
|
||||
- Key: foobar
|
||||
MustMatch: true
|
||||
Value: foobar
|
||||
- Key: foobar
|
||||
MustMatch: true
|
||||
Value: foobar
|
||||
TLS:
|
||||
CA: foobar
|
||||
CAOptional: true
|
||||
Cert: foobar
|
||||
Key: foobar
|
||||
InsecureSkipVerify: true
|
||||
Basic:
|
||||
HTTPBasicAuthUser: foobar
|
||||
HTTPBasicPassword: foobar
|
||||
Kubernetes:
|
||||
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:
|
||||
Watch: true
|
||||
DefaultRule: foobar
|
||||
ExposedByDefault: true
|
||||
EnableServiceHealthFilter: true
|
||||
RefreshSeconds: 42
|
||||
IntervalPoll: true
|
||||
Prefix: foobar
|
||||
Constraints:
|
||||
- Key: foobar
|
||||
MustMatch: true
|
||||
Value: foobar
|
||||
- Key: foobar
|
||||
MustMatch: true
|
||||
Value: foobar
|
||||
API:
|
||||
EntryPoint: foobar
|
||||
Dashboard: true
|
||||
Middlewares:
|
||||
- foobar
|
||||
- foobar
|
||||
Statistics:
|
||||
RecentErrors: 42
|
||||
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
|
||||
BufferingSize: 42
|
||||
Filters:
|
||||
StatusCodes:
|
||||
- foobar
|
||||
- foobar
|
||||
RetryAttempts: true
|
||||
MinDuration: 42
|
||||
Fields:
|
||||
DefaultMode: foobar
|
||||
Names:
|
||||
name0: foobar
|
||||
name1: foobar
|
||||
Headers:
|
||||
DefaultMode: foobar
|
||||
Names:
|
||||
name0: foobar
|
||||
name1: foobar
|
||||
Tracing:
|
||||
Backend: foobar
|
||||
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
|
||||
Debug: 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
|
||||
HostResolver:
|
||||
CnameFlattening: true
|
||||
ResolvConfig: foobar
|
||||
ResolvDepth: 42
|
||||
ACME:
|
||||
Email: foobar
|
||||
ACMELogging: true
|
||||
CAServer: foobar
|
||||
Storage: foobar
|
||||
EntryPoint: foobar
|
||||
KeyType: foobar
|
||||
OnHostRule: true
|
||||
DNSChallenge:
|
||||
Provider: foobar
|
||||
DelayBeforeCheck: 42
|
||||
Resolvers:
|
||||
- foobar
|
||||
- foobar
|
||||
DisablePropagationCheck: true
|
||||
HTTPChallenge:
|
||||
EntryPoint: foobar
|
||||
TLSChallenge: {}
|
||||
Domains:
|
||||
- Main: foobar
|
||||
SANs:
|
||||
- foobar
|
||||
- foobar
|
||||
- Main: foobar
|
||||
SANs:
|
||||
- foobar
|
||||
- foobar
|
34
pkg/config/file/fixtures_test.go
Normal file
34
pkg/config/file/fixtures_test.go
Normal file
|
@ -0,0 +1,34 @@
|
|||
package file
|
||||
|
||||
type bar string
|
||||
|
||||
type Yo struct {
|
||||
Foo string
|
||||
Fii string
|
||||
Fuu string
|
||||
Yi *Yi `label:"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
|
||||
}
|
128
pkg/config/file/raw_node.go
Normal file
128
pkg/config/file/raw_node.go
Normal file
|
@ -0,0 +1,128 @@
|
|||
package file
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/containous/traefik/pkg/config/parser"
|
||||
)
|
||||
|
||||
func decodeRawToNode(data map[string]interface{}, filters ...string) (*parser.Node, error) {
|
||||
root := &parser.Node{
|
||||
Name: "traefik",
|
||||
}
|
||||
|
||||
vData := reflect.ValueOf(data)
|
||||
decodeRaw(root, vData, filters...)
|
||||
|
||||
return root, nil
|
||||
}
|
||||
|
||||
func decodeRaw(node *parser.Node, vData reflect.Value, filters ...string) {
|
||||
sortedKeys := sortKeys(vData, filters)
|
||||
|
||||
for _, key := range sortedKeys {
|
||||
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:
|
||||
child.Value = getSimpleValue(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)
|
||||
decodeRaw(ch, sValue)
|
||||
} else {
|
||||
values = append(values, getSimpleValue(sValue))
|
||||
}
|
||||
default:
|
||||
panic("Unsupported slice type: " + item.Kind().String())
|
||||
}
|
||||
}
|
||||
|
||||
child.Value = strings.Join(values, ",")
|
||||
case reflect.Map:
|
||||
decodeRaw(child, value)
|
||||
default:
|
||||
panic("Unsupported type: " + value.Kind().String())
|
||||
}
|
||||
|
||||
node.Children = append(node.Children, child)
|
||||
}
|
||||
}
|
||||
|
||||
func getSimpleValue(item reflect.Value) string {
|
||||
switch item.Kind() {
|
||||
case reflect.String:
|
||||
return item.String()
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
return strconv.FormatInt(item.Int(), 10)
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
return strconv.FormatUint(item.Uint(), 10)
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return strings.TrimSuffix(strconv.FormatFloat(item.Float(), 'f', 6, 64), ".000000")
|
||||
case reflect.Bool:
|
||||
return strconv.FormatBool(item.Bool())
|
||||
default:
|
||||
panic("Unsupported Simple value type: " + 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
|
||||
}
|
540
pkg/config/file/raw_node_test.go
Normal file
540
pkg/config/file/raw_node_test.go
Normal file
|
@ -0,0 +1,540 @@
|
|||
package file
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/containous/traefik/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"},
|
||||
}},
|
||||
}},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
node, err := decodeRawToNode(test.data)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, test.expected, node)
|
||||
})
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue