1
0
Fork 0

New static configuration loading system.

Co-authored-by: Mathieu Lonjaret <mathieu.lonjaret@gmail.com>
This commit is contained in:
Ludovic Fernandez 2019-06-17 11:48:05 +02:00 committed by Traefiker Bot
parent d18edd6f77
commit 8d7eccad5d
165 changed files with 10894 additions and 6076 deletions

50
pkg/config/env/env.go vendored Normal file
View file

@ -0,0 +1,50 @@
// Package env implements encoding and decoding between environment variable and a typed Configuration.
package env
import (
"strings"
"github.com/containous/traefik/pkg/config/parser"
)
// Decode decodes the given environment variables into the given element.
// The operation goes through four stages roughly summarized as:
// env vars -> map
// map -> tree of untyped nodes
// untyped nodes -> nodes augmented with metadata such as kind (inferred from element)
// "typed" nodes -> typed element
func Decode(environ []string, element interface{}) error {
vars := make(map[string]string)
for _, evr := range environ {
n := strings.SplitN(evr, "=", 2)
if strings.HasPrefix(strings.ToUpper(n[0]), "TRAEFIK_") {
key := strings.ReplaceAll(strings.ToLower(n[0]), "_", ".")
vars[key] = n[1]
}
}
return parser.Decode(vars, element)
}
// Encode encodes the configuration in element into the environment variables represented in the returned Flats.
// The operation goes through three stages roughly summarized as:
// typed configuration in element -> tree of untyped nodes
// untyped nodes -> nodes augmented with metadata such as kind (inferred from element)
// "typed" nodes -> environment variables with default values (determined by type/kind)
func Encode(element interface{}) ([]parser.Flat, error) {
if element == nil {
return nil, nil
}
node, err := parser.EncodeToNode(element, false)
if err != nil {
return nil, err
}
err = parser.AddMetadata(element, node)
if err != nil {
return nil, err
}
return parser.EncodeToFlat(element, node, parser.FlatOpts{Case: "upper", Separator: "_"})
}

498
pkg/config/env/env_test.go vendored Normal file
View file

@ -0,0 +1,498 @@
package env
import (
"testing"
"github.com/containous/traefik/pkg/config/generator"
"github.com/containous/traefik/pkg/config/parser"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestDecode(t *testing.T) {
testCases := []struct {
desc string
environ []string
element interface{}
expected interface{}
}{
{
desc: "no env vars",
environ: nil,
expected: nil,
},
{
desc: "bool value",
environ: []string{"TRAEFIK_FOO=true"},
element: &struct {
Foo bool
}{},
expected: &struct {
Foo bool
}{
Foo: true,
},
},
{
desc: "equal",
environ: []string{"TRAEFIK_FOO=bar"},
element: &struct {
Foo string
}{},
expected: &struct {
Foo string
}{
Foo: "bar",
},
},
{
desc: "multiple bool flags without value",
environ: []string{"TRAEFIK_FOO=true", "TRAEFIK_BAR=true"},
element: &struct {
Foo bool
Bar bool
}{},
expected: &struct {
Foo bool
Bar bool
}{
Foo: true,
Bar: true,
},
},
{
desc: "map string",
environ: []string{"TRAEFIK_FOO_NAME=bar"},
element: &struct {
Foo map[string]string
}{},
expected: &struct {
Foo map[string]string
}{
Foo: map[string]string{
"name": "bar",
},
},
},
{
desc: "map struct",
environ: []string{"TRAEFIK_FOO_NAME_VALUE=bar"},
element: &struct {
Foo map[string]struct{ Value string }
}{},
expected: &struct {
Foo map[string]struct{ Value string }
}{
Foo: map[string]struct{ Value string }{
"name": {
Value: "bar",
},
},
},
},
{
desc: "map struct with sub-struct",
environ: []string{"TRAEFIK_FOO_NAME_BAR_VALUE=bar"},
element: &struct {
Foo map[string]struct {
Bar *struct{ Value string }
}
}{},
expected: &struct {
Foo map[string]struct {
Bar *struct{ Value string }
}
}{
Foo: map[string]struct {
Bar *struct{ Value string }
}{
"name": {
Bar: &struct {
Value string
}{
Value: "bar",
},
},
},
},
},
{
desc: "map struct with sub-map",
environ: []string{"TRAEFIK_FOO_NAME1_BAR_NAME2_VALUE=bar"},
element: &struct {
Foo map[string]struct {
Bar map[string]struct{ Value string }
}
}{},
expected: &struct {
Foo map[string]struct {
Bar map[string]struct{ Value string }
}
}{
Foo: map[string]struct {
Bar map[string]struct{ Value string }
}{
"name1": {
Bar: map[string]struct{ Value string }{
"name2": {
Value: "bar",
},
},
},
},
},
},
{
desc: "slice",
environ: []string{"TRAEFIK_FOO=bar,baz"},
element: &struct {
Foo []string
}{},
expected: &struct {
Foo []string
}{
Foo: []string{"bar", "baz"},
},
},
{
desc: "struct pointer value",
environ: []string{"TRAEFIK_FOO=true"},
element: &struct {
Foo *struct{ Field string } `label:"allowEmpty"`
}{},
expected: &struct {
Foo *struct{ Field string } `label:"allowEmpty"`
}{
Foo: &struct{ Field string }{},
},
},
}
for _, test := range testCases {
test := test
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
err := Decode(test.environ, test.element)
require.NoError(t, err)
assert.Equal(t, test.expected, test.element)
})
}
}
func TestEncode(t *testing.T) {
element := &Ya{
Foo: &Yaa{
FieldIn1: "bar",
FieldIn2: false,
FieldIn3: 1,
FieldIn4: map[string]string{
parser.MapNamePlaceholder: "",
},
FieldIn5: map[string]int{
parser.MapNamePlaceholder: 0,
},
FieldIn6: map[string]struct{ Field string }{
parser.MapNamePlaceholder: {},
},
FieldIn7: map[string]struct{ Field map[string]string }{
parser.MapNamePlaceholder: {
Field: map[string]string{
parser.MapNamePlaceholder: "",
},
},
},
FieldIn8: map[string]*struct{ Field string }{
parser.MapNamePlaceholder: {},
},
FieldIn9: map[string]*struct{ Field map[string]string }{
parser.MapNamePlaceholder: {
Field: map[string]string{
parser.MapNamePlaceholder: "",
},
},
},
FieldIn10: struct{ Field string }{},
FieldIn11: &struct{ Field string }{},
FieldIn12: func(v string) *string { return &v }(""),
FieldIn13: func(v bool) *bool { return &v }(false),
FieldIn14: func(v int) *int { return &v }(0),
},
Field1: "bir",
Field2: true,
Field3: 0,
Field4: map[string]string{
parser.MapNamePlaceholder: "",
},
Field5: map[string]int{
parser.MapNamePlaceholder: 0,
},
Field6: map[string]struct{ Field string }{
parser.MapNamePlaceholder: {},
},
Field7: map[string]struct{ Field map[string]string }{
parser.MapNamePlaceholder: {
Field: map[string]string{
parser.MapNamePlaceholder: "",
},
},
},
Field8: map[string]*struct{ Field string }{
parser.MapNamePlaceholder: {},
},
Field9: map[string]*struct{ Field map[string]string }{
parser.MapNamePlaceholder: {
Field: map[string]string{
parser.MapNamePlaceholder: "",
},
},
},
Field10: struct{ Field string }{},
Field11: &struct{ Field string }{},
Field12: func(v string) *string { return &v }(""),
Field13: func(v bool) *bool { return &v }(false),
Field14: func(v int) *int { return &v }(0),
Field15: []int{7},
}
generator.Generate(element)
flats, err := Encode(element)
require.NoError(t, err)
expected := []parser.Flat{
{
Name: "TRAEFIK_FIELD1",
Description: "",
Default: "bir",
},
{
Name: "TRAEFIK_FIELD10",
Description: "",
Default: "",
},
{
Name: "TRAEFIK_FIELD10_FIELD",
Description: "",
Default: "",
},
{
Name: "TRAEFIK_FIELD11_FIELD",
Description: "",
Default: "",
},
{
Name: "TRAEFIK_FIELD12",
Description: "",
Default: "",
},
{
Name: "TRAEFIK_FIELD13",
Description: "",
Default: "false",
},
{
Name: "TRAEFIK_FIELD14",
Description: "",
Default: "0",
},
{
Name: "TRAEFIK_FIELD15",
Description: "",
Default: "7",
},
{
Name: "TRAEFIK_FIELD2",
Description: "",
Default: "true",
},
{
Name: "TRAEFIK_FIELD3",
Description: "",
Default: "0",
},
{
Name: "TRAEFIK_FIELD4_\u003cNAME\u003e",
Description: "",
Default: "",
},
{
Name: "TRAEFIK_FIELD5_\u003cNAME\u003e",
Description: "",
Default: "0",
},
{
Name: "TRAEFIK_FIELD6_\u003cNAME\u003e",
Description: "",
Default: "false",
},
{
Name: "TRAEFIK_FIELD6_\u003cNAME\u003e_FIELD",
Description: "",
Default: "",
},
{
Name: "TRAEFIK_FIELD7_\u003cNAME\u003e",
Description: "",
Default: "false",
},
{
Name: "TRAEFIK_FIELD7_\u003cNAME\u003e_FIELD_\u003cNAME\u003e",
Description: "",
Default: "",
},
{
Name: "TRAEFIK_FIELD8_\u003cNAME\u003e",
Description: "",
Default: "false",
},
{
Name: "TRAEFIK_FIELD8_\u003cNAME\u003e_FIELD",
Description: "",
Default: "",
},
{
Name: "TRAEFIK_FIELD9_\u003cNAME\u003e",
Description: "",
Default: "false",
},
{
Name: "TRAEFIK_FIELD9_\u003cNAME\u003e_FIELD_\u003cNAME\u003e",
Description: "",
Default: "",
},
{
Name: "TRAEFIK_FOO_FIELDIN1",
Description: "",
Default: "bar",
},
{
Name: "TRAEFIK_FOO_FIELDIN10",
Description: "",
Default: "",
},
{
Name: "TRAEFIK_FOO_FIELDIN10_FIELD",
Description: "",
Default: "",
},
{
Name: "TRAEFIK_FOO_FIELDIN11_FIELD",
Description: "",
Default: "",
},
{
Name: "TRAEFIK_FOO_FIELDIN12",
Description: "",
Default: "",
},
{
Name: "TRAEFIK_FOO_FIELDIN13",
Description: "",
Default: "false",
},
{
Name: "TRAEFIK_FOO_FIELDIN14",
Description: "",
Default: "0",
},
{
Name: "TRAEFIK_FOO_FIELDIN2",
Description: "",
Default: "false",
},
{
Name: "TRAEFIK_FOO_FIELDIN3",
Description: "",
Default: "1",
},
{
Name: "TRAEFIK_FOO_FIELDIN4_\u003cNAME\u003e",
Description: "",
Default: "",
},
{
Name: "TRAEFIK_FOO_FIELDIN5_\u003cNAME\u003e",
Description: "",
Default: "0",
},
{
Name: "TRAEFIK_FOO_FIELDIN6_\u003cNAME\u003e",
Description: "",
Default: "false",
},
{
Name: "TRAEFIK_FOO_FIELDIN6_\u003cNAME\u003e_FIELD",
Description: "",
Default: "",
},
{
Name: "TRAEFIK_FOO_FIELDIN7_\u003cNAME\u003e",
Description: "",
Default: "false",
},
{
Name: "TRAEFIK_FOO_FIELDIN7_\u003cNAME\u003e_FIELD_\u003cNAME\u003e",
Description: "",
Default: "",
},
{
Name: "TRAEFIK_FOO_FIELDIN8_\u003cNAME\u003e",
Description: "",
Default: "false",
},
{
Name: "TRAEFIK_FOO_FIELDIN8_\u003cNAME\u003e_FIELD",
Description: "",
Default: "",
},
{
Name: "TRAEFIK_FOO_FIELDIN9_\u003cNAME\u003e",
Description: "",
Default: "false",
},
{
Name: "TRAEFIK_FOO_FIELDIN9_\u003cNAME\u003e_FIELD_\u003cNAME\u003e",
Description: "",
Default: "",
},
}
assert.Equal(t, expected, flats)
}
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
}