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
97
pkg/config/generator/generator.go
Normal file
97
pkg/config/generator/generator.go
Normal file
|
@ -0,0 +1,97 @@
|
|||
// Package generator implements the custom initialization of all the fields of an empty interface.
|
||||
package generator
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"github.com/containous/traefik/pkg/config/parser"
|
||||
)
|
||||
|
||||
type initializer interface {
|
||||
SetDefaults()
|
||||
}
|
||||
|
||||
// Generate recursively initializes an empty structure, calling SetDefaults on each field, when it applies.
|
||||
func Generate(element interface{}) {
|
||||
if element == nil {
|
||||
return
|
||||
}
|
||||
|
||||
generate(element)
|
||||
}
|
||||
|
||||
func generate(element interface{}) {
|
||||
field := reflect.ValueOf(element)
|
||||
|
||||
fill(field)
|
||||
}
|
||||
|
||||
func fill(field reflect.Value) {
|
||||
switch field.Kind() {
|
||||
case reflect.Ptr:
|
||||
setPtr(field)
|
||||
case reflect.Struct:
|
||||
setStruct(field)
|
||||
case reflect.Map:
|
||||
setMap(field)
|
||||
case reflect.Slice:
|
||||
if field.Type().Elem().Kind() == reflect.Struct ||
|
||||
field.Type().Elem().Kind() == reflect.Ptr && field.Type().Elem().Elem().Kind() == reflect.Struct {
|
||||
slice := reflect.MakeSlice(field.Type(), 1, 1)
|
||||
field.Set(slice)
|
||||
|
||||
// use Ptr to allow "SetDefaults"
|
||||
value := reflect.New(reflect.PtrTo(field.Type().Elem()))
|
||||
setPtr(value)
|
||||
|
||||
elem := value.Elem().Elem()
|
||||
field.Index(0).Set(elem)
|
||||
} else if field.Len() == 0 {
|
||||
slice := reflect.MakeSlice(field.Type(), 0, 0)
|
||||
field.Set(slice)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func setPtr(field reflect.Value) {
|
||||
if field.IsNil() {
|
||||
field.Set(reflect.New(field.Type().Elem()))
|
||||
}
|
||||
|
||||
if field.Type().Implements(reflect.TypeOf((*initializer)(nil)).Elem()) {
|
||||
method := field.MethodByName("SetDefaults")
|
||||
if method.IsValid() {
|
||||
method.Call([]reflect.Value{})
|
||||
}
|
||||
}
|
||||
|
||||
fill(field.Elem())
|
||||
}
|
||||
|
||||
func setStruct(field reflect.Value) {
|
||||
for i := 0; i < field.NumField(); i++ {
|
||||
fd := field.Field(i)
|
||||
structField := field.Type().Field(i)
|
||||
|
||||
if structField.Tag.Get(parser.TagLabel) == "-" {
|
||||
continue
|
||||
}
|
||||
|
||||
if parser.IsExported(structField) {
|
||||
fill(fd)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func setMap(field reflect.Value) {
|
||||
if field.IsNil() {
|
||||
field.Set(reflect.MakeMap(field.Type()))
|
||||
}
|
||||
|
||||
ptrValue := reflect.New(reflect.PtrTo(field.Type().Elem()))
|
||||
fill(ptrValue)
|
||||
|
||||
value := ptrValue.Elem().Elem()
|
||||
key := reflect.ValueOf(parser.MapNamePlaceholder)
|
||||
field.SetMapIndex(key, value)
|
||||
}
|
439
pkg/config/generator/generator_test.go
Normal file
439
pkg/config/generator/generator_test.go
Normal file
|
@ -0,0 +1,439 @@
|
|||
package generator
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/containous/traefik/pkg/config/parser"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestGenerate(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
element interface{}
|
||||
expected interface{}
|
||||
}{
|
||||
{
|
||||
desc: "nil",
|
||||
},
|
||||
{
|
||||
desc: "simple",
|
||||
element: &Ya{},
|
||||
expected: &Ya{
|
||||
Foo: &Yaa{
|
||||
FieldIn1: "",
|
||||
FieldIn2: false,
|
||||
FieldIn3: 0,
|
||||
FieldIn4: map[string]string{
|
||||
parser.MapNamePlaceholder: "",
|
||||
},
|
||||
FieldIn5: map[string]int{
|
||||
parser.MapNamePlaceholder: 0,
|
||||
},
|
||||
FieldIn6: map[string]struct{ Field string }{
|
||||
parser.MapNamePlaceholder: {},
|
||||
},
|
||||
FieldIn7: map[string]struct{ Field map[string]string }{
|
||||
parser.MapNamePlaceholder: {
|
||||
Field: map[string]string{
|
||||
parser.MapNamePlaceholder: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
FieldIn8: map[string]*struct{ Field string }{
|
||||
parser.MapNamePlaceholder: {},
|
||||
},
|
||||
FieldIn9: map[string]*struct{ Field map[string]string }{
|
||||
parser.MapNamePlaceholder: {
|
||||
Field: map[string]string{
|
||||
parser.MapNamePlaceholder: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
FieldIn10: struct{ Field string }{},
|
||||
FieldIn11: &struct{ Field string }{},
|
||||
FieldIn12: func(v string) *string { return &v }(""),
|
||||
FieldIn13: func(v bool) *bool { return &v }(false),
|
||||
FieldIn14: func(v int) *int { return &v }(0),
|
||||
},
|
||||
Field1: "",
|
||||
Field2: false,
|
||||
Field3: 0,
|
||||
Field4: map[string]string{
|
||||
parser.MapNamePlaceholder: "",
|
||||
},
|
||||
Field5: map[string]int{
|
||||
parser.MapNamePlaceholder: 0,
|
||||
},
|
||||
Field6: map[string]struct{ Field string }{
|
||||
parser.MapNamePlaceholder: {},
|
||||
},
|
||||
Field7: map[string]struct{ Field map[string]string }{
|
||||
parser.MapNamePlaceholder: {
|
||||
Field: map[string]string{
|
||||
parser.MapNamePlaceholder: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
Field8: map[string]*struct{ Field string }{
|
||||
parser.MapNamePlaceholder: {},
|
||||
},
|
||||
Field9: map[string]*struct{ Field map[string]string }{
|
||||
parser.MapNamePlaceholder: {
|
||||
Field: map[string]string{
|
||||
parser.MapNamePlaceholder: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
Field10: struct{ Field string }{},
|
||||
Field11: &struct{ Field string }{},
|
||||
Field12: func(v string) *string { return &v }(""),
|
||||
Field13: func(v bool) *bool { return &v }(false),
|
||||
Field14: func(v int) *int { return &v }(0),
|
||||
Field15: []int{},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "with initial state",
|
||||
element: &Ya{
|
||||
Foo: &Yaa{
|
||||
FieldIn1: "bar",
|
||||
FieldIn2: false,
|
||||
FieldIn3: 1,
|
||||
FieldIn4: nil,
|
||||
FieldIn5: nil,
|
||||
FieldIn6: nil,
|
||||
FieldIn7: nil,
|
||||
FieldIn8: nil,
|
||||
FieldIn9: nil,
|
||||
FieldIn10: struct{ Field string }{},
|
||||
FieldIn11: nil,
|
||||
FieldIn12: nil,
|
||||
FieldIn13: nil,
|
||||
FieldIn14: nil,
|
||||
},
|
||||
Field1: "bir",
|
||||
Field2: true,
|
||||
Field3: 0,
|
||||
Field4: nil,
|
||||
Field5: nil,
|
||||
Field6: nil,
|
||||
Field7: nil,
|
||||
Field8: nil,
|
||||
Field9: nil,
|
||||
Field10: struct{ Field string }{},
|
||||
Field11: nil,
|
||||
Field12: nil,
|
||||
Field13: nil,
|
||||
Field14: nil,
|
||||
Field15: []int{7},
|
||||
},
|
||||
expected: &Ya{
|
||||
Foo: &Yaa{
|
||||
FieldIn1: "bar",
|
||||
FieldIn2: false,
|
||||
FieldIn3: 1,
|
||||
FieldIn4: map[string]string{
|
||||
parser.MapNamePlaceholder: "",
|
||||
},
|
||||
FieldIn5: map[string]int{
|
||||
parser.MapNamePlaceholder: 0,
|
||||
},
|
||||
FieldIn6: map[string]struct{ Field string }{
|
||||
parser.MapNamePlaceholder: {},
|
||||
},
|
||||
FieldIn7: map[string]struct{ Field map[string]string }{
|
||||
parser.MapNamePlaceholder: {
|
||||
Field: map[string]string{
|
||||
parser.MapNamePlaceholder: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
FieldIn8: map[string]*struct{ Field string }{
|
||||
parser.MapNamePlaceholder: {},
|
||||
},
|
||||
FieldIn9: map[string]*struct{ Field map[string]string }{
|
||||
parser.MapNamePlaceholder: {
|
||||
Field: map[string]string{
|
||||
parser.MapNamePlaceholder: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
FieldIn10: struct{ Field string }{},
|
||||
FieldIn11: &struct{ Field string }{},
|
||||
FieldIn12: func(v string) *string { return &v }(""),
|
||||
FieldIn13: func(v bool) *bool { return &v }(false),
|
||||
FieldIn14: func(v int) *int { return &v }(0),
|
||||
},
|
||||
Field1: "bir",
|
||||
Field2: true,
|
||||
Field3: 0,
|
||||
Field4: map[string]string{
|
||||
parser.MapNamePlaceholder: "",
|
||||
},
|
||||
Field5: map[string]int{
|
||||
parser.MapNamePlaceholder: 0,
|
||||
},
|
||||
Field6: map[string]struct{ Field string }{
|
||||
parser.MapNamePlaceholder: {},
|
||||
},
|
||||
Field7: map[string]struct{ Field map[string]string }{
|
||||
parser.MapNamePlaceholder: {
|
||||
Field: map[string]string{
|
||||
parser.MapNamePlaceholder: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
Field8: map[string]*struct{ Field string }{
|
||||
parser.MapNamePlaceholder: {},
|
||||
},
|
||||
Field9: map[string]*struct{ Field map[string]string }{
|
||||
parser.MapNamePlaceholder: {
|
||||
Field: map[string]string{
|
||||
parser.MapNamePlaceholder: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
Field10: struct{ Field string }{},
|
||||
Field11: &struct{ Field string }{},
|
||||
Field12: func(v string) *string { return &v }(""),
|
||||
Field13: func(v bool) *bool { return &v }(false),
|
||||
Field14: func(v int) *int { return &v }(0),
|
||||
Field15: []int{7},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "setDefault",
|
||||
element: &Hu{},
|
||||
expected: &Hu{
|
||||
Foo: "hu",
|
||||
Fii: &Hi{
|
||||
Field: "hi",
|
||||
},
|
||||
Fuu: map[string]string{"<name>": ""},
|
||||
Fee: map[string]Hi{"<name>": {Field: "hi"}},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
Generate(test.element)
|
||||
|
||||
assert.Equal(t, test.expected, test.element)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_generate(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
element interface{}
|
||||
expected interface{}
|
||||
}{
|
||||
{
|
||||
desc: "struct pointer",
|
||||
element: &struct {
|
||||
Foo string
|
||||
Fii *struct{ Field string }
|
||||
}{},
|
||||
expected: &struct {
|
||||
Foo string
|
||||
Fii *struct{ Field string }
|
||||
}{
|
||||
Foo: "",
|
||||
Fii: &struct{ Field string }{
|
||||
Field: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "string slice",
|
||||
element: &struct {
|
||||
Foo []string
|
||||
}{},
|
||||
expected: &struct {
|
||||
Foo []string
|
||||
}{
|
||||
Foo: []string{},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "int slice",
|
||||
element: &struct {
|
||||
Foo []int
|
||||
}{},
|
||||
expected: &struct {
|
||||
Foo []int
|
||||
}{
|
||||
Foo: []int{},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "struct slice",
|
||||
element: &struct {
|
||||
Foo []struct {
|
||||
Field string
|
||||
}
|
||||
}{},
|
||||
expected: &struct {
|
||||
Foo []struct {
|
||||
Field string
|
||||
}
|
||||
}{
|
||||
Foo: []struct {
|
||||
Field string
|
||||
}{
|
||||
{Field: ""},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "map string",
|
||||
element: &struct {
|
||||
Foo string
|
||||
Fii map[string]string
|
||||
}{},
|
||||
expected: &struct {
|
||||
Foo string
|
||||
Fii map[string]string
|
||||
}{
|
||||
Foo: "",
|
||||
Fii: map[string]string{
|
||||
parser.MapNamePlaceholder: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "map struct",
|
||||
element: &struct {
|
||||
Foo string
|
||||
Fii map[string]struct{ Field string }
|
||||
}{},
|
||||
expected: &struct {
|
||||
Foo string
|
||||
Fii map[string]struct{ Field string }
|
||||
}{
|
||||
Foo: "",
|
||||
Fii: map[string]struct{ Field string }{
|
||||
parser.MapNamePlaceholder: {},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "map struct pointer level 2",
|
||||
element: &struct {
|
||||
Foo string
|
||||
Fuu *struct {
|
||||
Fii map[string]*struct{ Field string }
|
||||
}
|
||||
}{},
|
||||
expected: &struct {
|
||||
Foo string
|
||||
Fuu *struct {
|
||||
Fii map[string]*struct{ Field string }
|
||||
}
|
||||
}{
|
||||
Foo: "",
|
||||
Fuu: &struct {
|
||||
Fii map[string]*struct {
|
||||
Field string
|
||||
}
|
||||
}{
|
||||
Fii: map[string]*struct{ Field string }{
|
||||
parser.MapNamePlaceholder: {
|
||||
Field: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "SetDefaults",
|
||||
element: &Hu{},
|
||||
expected: &Hu{
|
||||
Foo: "hu",
|
||||
Fii: &Hi{
|
||||
Field: "hi",
|
||||
},
|
||||
Fuu: map[string]string{
|
||||
parser.MapNamePlaceholder: "",
|
||||
},
|
||||
Fee: map[string]Hi{
|
||||
parser.MapNamePlaceholder: {
|
||||
Field: "hi",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
generate(test.element)
|
||||
|
||||
assert.Equal(t, test.expected, test.element)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type Hu struct {
|
||||
Foo string
|
||||
Fii *Hi
|
||||
Fuu map[string]string
|
||||
Fee map[string]Hi
|
||||
}
|
||||
|
||||
func (h *Hu) SetDefaults() {
|
||||
h.Foo = "hu"
|
||||
}
|
||||
|
||||
type Hi struct {
|
||||
Field string
|
||||
}
|
||||
|
||||
func (h *Hi) SetDefaults() {
|
||||
h.Field = "hi"
|
||||
}
|
||||
|
||||
type Ya struct {
|
||||
Foo *Yaa
|
||||
Field1 string
|
||||
Field2 bool
|
||||
Field3 int
|
||||
Field4 map[string]string
|
||||
Field5 map[string]int
|
||||
Field6 map[string]struct{ Field string }
|
||||
Field7 map[string]struct{ Field map[string]string }
|
||||
Field8 map[string]*struct{ Field string }
|
||||
Field9 map[string]*struct{ Field map[string]string }
|
||||
Field10 struct{ Field string }
|
||||
Field11 *struct{ Field string }
|
||||
Field12 *string
|
||||
Field13 *bool
|
||||
Field14 *int
|
||||
Field15 []int
|
||||
}
|
||||
|
||||
type Yaa struct {
|
||||
FieldIn1 string
|
||||
FieldIn2 bool
|
||||
FieldIn3 int
|
||||
FieldIn4 map[string]string
|
||||
FieldIn5 map[string]int
|
||||
FieldIn6 map[string]struct{ Field string }
|
||||
FieldIn7 map[string]struct{ Field map[string]string }
|
||||
FieldIn8 map[string]*struct{ Field string }
|
||||
FieldIn9 map[string]*struct{ Field map[string]string }
|
||||
FieldIn10 struct{ Field string }
|
||||
FieldIn11 *struct{ Field string }
|
||||
FieldIn12 *string
|
||||
FieldIn13 *bool
|
||||
FieldIn14 *int
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue