Filter env vars configuration
This commit is contained in:
parent
adc9a65ae3
commit
a918dcd5a4
19 changed files with 284 additions and 79 deletions
|
@ -70,6 +70,12 @@ docker run traefik[:version] --help
|
||||||
# ex: docker run traefik:2.0 --help
|
# ex: docker run traefik:2.0 --help
|
||||||
```
|
```
|
||||||
|
|
||||||
|
All available arguments can also be found [here](../reference/static-configuration/cli.md).
|
||||||
|
|
||||||
|
### Environment Variables
|
||||||
|
|
||||||
|
All available environment variables can be found [here](../reference/static-configuration/env.md)
|
||||||
|
|
||||||
## Available Configuration Options
|
## Available Configuration Options
|
||||||
|
|
||||||
All the configuration options are documented in their related section.
|
All the configuration options are documented in their related section.
|
||||||
|
|
|
@ -3,7 +3,6 @@ package cli
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/containous/traefik/pkg/config/env"
|
"github.com/containous/traefik/pkg/config/env"
|
||||||
"github.com/containous/traefik/pkg/log"
|
"github.com/containous/traefik/pkg/log"
|
||||||
|
@ -14,23 +13,12 @@ type EnvLoader struct{}
|
||||||
|
|
||||||
// Load loads the command's configuration from the environment variables.
|
// Load loads the command's configuration from the environment variables.
|
||||||
func (e *EnvLoader) Load(_ []string, cmd *Command) (bool, error) {
|
func (e *EnvLoader) Load(_ []string, cmd *Command) (bool, error) {
|
||||||
return e.load(os.Environ(), cmd)
|
vars := env.FindPrefixedEnvVars(os.Environ(), env.DefaultNamePrefix, cmd.Configuration)
|
||||||
}
|
if len(vars) == 0 {
|
||||||
|
|
||||||
func (*EnvLoader) load(environ []string, cmd *Command) (bool, error) {
|
|
||||||
var found bool
|
|
||||||
for _, value := range environ {
|
|
||||||
if strings.HasPrefix(value, "TRAEFIK_") {
|
|
||||||
found = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !found {
|
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := env.Decode(environ, cmd.Configuration); err != nil {
|
if err := env.Decode(vars, env.DefaultNamePrefix, cmd.Configuration); err != nil {
|
||||||
return false, fmt.Errorf("failed to decode configuration from environment variables: %v", err)
|
return false, fmt.Errorf("failed to decode configuration from environment variables: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
32
pkg/config/env/env.go
vendored
32
pkg/config/env/env.go
vendored
|
@ -2,28 +2,38 @@
|
||||||
package env
|
package env
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/containous/traefik/pkg/config/parser"
|
"github.com/containous/traefik/pkg/config/parser"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// DefaultNamePrefix is the default prefix for environment variable names.
|
||||||
|
const DefaultNamePrefix = "TRAEFIK_"
|
||||||
|
|
||||||
// Decode decodes the given environment variables into the given element.
|
// Decode decodes the given environment variables into the given element.
|
||||||
// The operation goes through four stages roughly summarized as:
|
// The operation goes through four stages roughly summarized as:
|
||||||
// env vars -> map
|
// env vars -> map
|
||||||
// map -> tree of untyped nodes
|
// map -> tree of untyped nodes
|
||||||
// untyped nodes -> nodes augmented with metadata such as kind (inferred from element)
|
// untyped nodes -> nodes augmented with metadata such as kind (inferred from element)
|
||||||
// "typed" nodes -> typed element
|
// "typed" nodes -> typed element
|
||||||
func Decode(environ []string, element interface{}) error {
|
func Decode(environ []string, prefix string, element interface{}) error {
|
||||||
|
if err := checkPrefix(prefix); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
vars := make(map[string]string)
|
vars := make(map[string]string)
|
||||||
for _, evr := range environ {
|
for _, evr := range environ {
|
||||||
n := strings.SplitN(evr, "=", 2)
|
n := strings.SplitN(evr, "=", 2)
|
||||||
if strings.HasPrefix(strings.ToUpper(n[0]), "TRAEFIK_") {
|
if strings.HasPrefix(strings.ToUpper(n[0]), prefix) {
|
||||||
key := strings.ReplaceAll(strings.ToLower(n[0]), "_", ".")
|
key := strings.ReplaceAll(strings.ToLower(n[0]), "_", ".")
|
||||||
vars[key] = n[1]
|
vars[key] = n[1]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return parser.Decode(vars, element)
|
rootName := strings.ToLower(prefix[:len(prefix)-1])
|
||||||
|
return parser.Decode(vars, element, rootName)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Encode encodes the configuration in element into the environment variables represented in the returned Flats.
|
// Encode encodes the configuration in element into the environment variables represented in the returned Flats.
|
||||||
|
@ -36,7 +46,7 @@ func Encode(element interface{}) ([]parser.Flat, error) {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
node, err := parser.EncodeToNode(element, false)
|
node, err := parser.EncodeToNode(element, parser.DefaultRootName, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -48,3 +58,17 @@ func Encode(element interface{}) ([]parser.Flat, error) {
|
||||||
|
|
||||||
return parser.EncodeToFlat(element, node, parser.FlatOpts{Case: "upper", Separator: "_"})
|
return parser.EncodeToFlat(element, node, parser.FlatOpts{Case: "upper", Separator: "_"})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func checkPrefix(prefix string) error {
|
||||||
|
prefixPattern := `[a-zA-Z0-9]+_`
|
||||||
|
matched, err := regexp.MatchString(prefixPattern, prefix)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !matched {
|
||||||
|
return fmt.Errorf("invalid prefix %q, the prefix pattern must match the following pattern: %s", prefix, prefixPattern)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
38
pkg/config/env/env_test.go
vendored
38
pkg/config/env/env_test.go
vendored
|
@ -173,7 +173,7 @@ func TestDecode(t *testing.T) {
|
||||||
t.Run(test.desc, func(t *testing.T) {
|
t.Run(test.desc, func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
err := Decode(test.environ, test.element)
|
err := Decode(test.environ, DefaultNamePrefix, test.element)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
assert.Equal(t, test.expected, test.element)
|
assert.Equal(t, test.expected, test.element)
|
||||||
|
@ -460,39 +460,3 @@ func TestEncode(t *testing.T) {
|
||||||
|
|
||||||
assert.Equal(t, expected, flats)
|
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
|
|
||||||
}
|
|
||||||
|
|
64
pkg/config/env/filter.go
vendored
Normal file
64
pkg/config/env/filter.go
vendored
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
package env
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/containous/traefik/pkg/config/parser"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FindPrefixedEnvVars finds prefixed environment variables.
|
||||||
|
func FindPrefixedEnvVars(environ []string, prefix string, element interface{}) []string {
|
||||||
|
prefixes := getRootPrefixes(element, prefix)
|
||||||
|
|
||||||
|
var values []string
|
||||||
|
for _, px := range prefixes {
|
||||||
|
for _, value := range environ {
|
||||||
|
if strings.HasPrefix(value, px) {
|
||||||
|
values = append(values, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return values
|
||||||
|
}
|
||||||
|
|
||||||
|
func getRootPrefixes(element interface{}, prefix string) []string {
|
||||||
|
if element == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
rootType := reflect.TypeOf(element)
|
||||||
|
|
||||||
|
return getPrefixes(prefix, rootType)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getPrefixes(prefix string, rootType reflect.Type) []string {
|
||||||
|
var names []string
|
||||||
|
|
||||||
|
if rootType.Kind() == reflect.Ptr {
|
||||||
|
rootType = rootType.Elem()
|
||||||
|
}
|
||||||
|
|
||||||
|
if rootType.Kind() != reflect.Struct {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < rootType.NumField(); i++ {
|
||||||
|
field := rootType.Field(i)
|
||||||
|
|
||||||
|
if !parser.IsExported(field) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if field.Anonymous &&
|
||||||
|
(field.Type.Kind() == reflect.Ptr && field.Type.Elem().Kind() == reflect.Struct || field.Type.Kind() == reflect.Struct) {
|
||||||
|
names = append(names, getPrefixes(prefix, field.Type)...)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
names = append(names, prefix+strings.ToUpper(field.Name))
|
||||||
|
}
|
||||||
|
|
||||||
|
return names
|
||||||
|
}
|
87
pkg/config/env/filter_test.go
vendored
Normal file
87
pkg/config/env/filter_test.go
vendored
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
package env
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestFindPrefixedEnvVars(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
desc string
|
||||||
|
environ []string
|
||||||
|
element interface{}
|
||||||
|
expected []string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
desc: "exact name",
|
||||||
|
environ: []string{"TRAEFIK_FOO"},
|
||||||
|
element: &Yo{},
|
||||||
|
expected: []string{"TRAEFIK_FOO"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "prefixed name",
|
||||||
|
environ: []string{"TRAEFIK_FII01"},
|
||||||
|
element: &Yo{},
|
||||||
|
expected: []string{"TRAEFIK_FII01"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "excluded env vars",
|
||||||
|
environ: []string{"TRAEFIK_NOPE", "TRAEFIK_NO"},
|
||||||
|
element: &Yo{},
|
||||||
|
expected: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "filter",
|
||||||
|
environ: []string{"TRAEFIK_NOPE", "TRAEFIK_NO", "TRAEFIK_FOO", "TRAEFIK_FII01"},
|
||||||
|
element: &Yo{},
|
||||||
|
expected: []string{"TRAEFIK_FOO", "TRAEFIK_FII01"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range testCases {
|
||||||
|
test := test
|
||||||
|
t.Run(test.desc, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
vars := FindPrefixedEnvVars(test.environ, DefaultNamePrefix, test.element)
|
||||||
|
|
||||||
|
assert.Equal(t, test.expected, vars)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_getRootFieldNames(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
desc string
|
||||||
|
element interface{}
|
||||||
|
expected []string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
desc: "simple fields",
|
||||||
|
element: &Yo{},
|
||||||
|
expected: []string{"TRAEFIK_FOO", "TRAEFIK_FII", "TRAEFIK_FUU", "TRAEFIK_YI", "TRAEFIK_YU"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "embedded struct",
|
||||||
|
element: &Yu{},
|
||||||
|
expected: []string{"TRAEFIK_FOO", "TRAEFIK_FII", "TRAEFIK_FUU"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "embedded struct pointer",
|
||||||
|
element: &Ye{},
|
||||||
|
expected: []string{"TRAEFIK_FOO", "TRAEFIK_FII", "TRAEFIK_FUU"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range testCases {
|
||||||
|
test := test
|
||||||
|
t.Run(test.desc, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
names := getRootPrefixes(test.element, DefaultNamePrefix)
|
||||||
|
|
||||||
|
assert.Equal(t, test.expected, names)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
69
pkg/config/env/fixtures_test.go
vendored
Normal file
69
pkg/config/env/fixtures_test.go
vendored
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
package env
|
||||||
|
|
||||||
|
type Ya struct {
|
||||||
|
Foo *Yaa
|
||||||
|
Field1 string
|
||||||
|
Field2 bool
|
||||||
|
Field3 int
|
||||||
|
Field4 map[string]string
|
||||||
|
Field5 map[string]int
|
||||||
|
Field6 map[string]struct{ Field string }
|
||||||
|
Field7 map[string]struct{ Field map[string]string }
|
||||||
|
Field8 map[string]*struct{ Field string }
|
||||||
|
Field9 map[string]*struct{ Field map[string]string }
|
||||||
|
Field10 struct{ Field string }
|
||||||
|
Field11 *struct{ Field string }
|
||||||
|
Field12 *string
|
||||||
|
Field13 *bool
|
||||||
|
Field14 *int
|
||||||
|
Field15 []int
|
||||||
|
}
|
||||||
|
|
||||||
|
type Yaa struct {
|
||||||
|
FieldIn1 string
|
||||||
|
FieldIn2 bool
|
||||||
|
FieldIn3 int
|
||||||
|
FieldIn4 map[string]string
|
||||||
|
FieldIn5 map[string]int
|
||||||
|
FieldIn6 map[string]struct{ Field string }
|
||||||
|
FieldIn7 map[string]struct{ Field map[string]string }
|
||||||
|
FieldIn8 map[string]*struct{ Field string }
|
||||||
|
FieldIn9 map[string]*struct{ Field map[string]string }
|
||||||
|
FieldIn10 struct{ Field string }
|
||||||
|
FieldIn11 *struct{ Field string }
|
||||||
|
FieldIn12 *string
|
||||||
|
FieldIn13 *bool
|
||||||
|
FieldIn14 *int
|
||||||
|
}
|
||||||
|
|
||||||
|
type Yo struct {
|
||||||
|
Foo string `description:"Foo description"`
|
||||||
|
Fii string `description:"Fii description"`
|
||||||
|
Fuu string `description:"Fuu description"`
|
||||||
|
Yi *Yi `label:"allowEmpty"`
|
||||||
|
Yu *Yi
|
||||||
|
}
|
||||||
|
|
||||||
|
func (y *Yo) SetDefaults() {
|
||||||
|
y.Foo = "foo"
|
||||||
|
y.Fii = "fii"
|
||||||
|
}
|
||||||
|
|
||||||
|
type Yi struct {
|
||||||
|
Foo string
|
||||||
|
Fii string
|
||||||
|
Fuu string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (y *Yi) SetDefaults() {
|
||||||
|
y.Foo = "foo"
|
||||||
|
y.Fii = "fii"
|
||||||
|
}
|
||||||
|
|
||||||
|
type Yu struct {
|
||||||
|
Yi
|
||||||
|
}
|
||||||
|
|
||||||
|
type Ye struct {
|
||||||
|
*Yi
|
||||||
|
}
|
|
@ -36,13 +36,13 @@ func decodeFileToNode(filePath string, filters ...string) (*parser.Node, error)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return decodeRawToNode(data, filters...)
|
return decodeRawToNode(data, parser.DefaultRootName, filters...)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("unsupported file extension: %s", filePath)
|
return nil, fmt.Errorf("unsupported file extension: %s", filePath)
|
||||||
}
|
}
|
||||||
|
|
||||||
return decodeRawToNode(data, filters...)
|
return decodeRawToNode(data, parser.DefaultRootName, filters...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getRootFieldNames(element interface{}) []string {
|
func getRootFieldNames(element interface{}) []string {
|
||||||
|
|
|
@ -9,9 +9,9 @@ import (
|
||||||
"github.com/containous/traefik/pkg/config/parser"
|
"github.com/containous/traefik/pkg/config/parser"
|
||||||
)
|
)
|
||||||
|
|
||||||
func decodeRawToNode(data map[string]interface{}, filters ...string) (*parser.Node, error) {
|
func decodeRawToNode(data map[string]interface{}, rootName string, filters ...string) (*parser.Node, error) {
|
||||||
root := &parser.Node{
|
root := &parser.Node{
|
||||||
Name: "traefik",
|
Name: rootName,
|
||||||
}
|
}
|
||||||
|
|
||||||
vData := reflect.ValueOf(data)
|
vData := reflect.ValueOf(data)
|
||||||
|
|
|
@ -531,7 +531,7 @@ func Test_decodeRawToNode(t *testing.T) {
|
||||||
t.Run(test.desc, func(t *testing.T) {
|
t.Run(test.desc, func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
node, err := decodeRawToNode(test.data)
|
node, err := decodeRawToNode(test.data, parser.DefaultRootName)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
assert.Equal(t, test.expected, node)
|
assert.Equal(t, test.expected, node)
|
||||||
|
|
|
@ -17,7 +17,7 @@ func Decode(args []string, element interface{}) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return parser.Decode(ref, element)
|
return parser.Decode(ref, element, parser.DefaultRootName)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Encode encodes the configuration in element into the flags represented in the returned Flats.
|
// Encode encodes the configuration in element into the flags represented in the returned Flats.
|
||||||
|
@ -30,7 +30,7 @@ func Encode(element interface{}) ([]parser.Flat, error) {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
node, err := parser.EncodeToNode(element, false)
|
node, err := parser.EncodeToNode(element, parser.DefaultRootName, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,8 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/containous/traefik/pkg/config/parser"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Parse parses the command-line flag arguments into a map,
|
// Parse parses the command-line flag arguments into a map,
|
||||||
|
@ -96,7 +98,7 @@ func (f *flagSet) parseOne() (bool, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *flagSet) setValue(name string, value string) {
|
func (f *flagSet) setValue(name string, value string) {
|
||||||
n := strings.ToLower("traefik." + name)
|
n := strings.ToLower(parser.DefaultRootName + "." + name)
|
||||||
v, ok := f.values[n]
|
v, ok := f.values[n]
|
||||||
|
|
||||||
if ok && f.flagTypes[name] == reflect.Slice {
|
if ok && f.flagTypes[name] == reflect.Slice {
|
||||||
|
|
|
@ -13,7 +13,7 @@ func DecodeConfiguration(labels map[string]string) (*config.Configuration, error
|
||||||
TCP: &config.TCPConfiguration{},
|
TCP: &config.TCPConfiguration{},
|
||||||
}
|
}
|
||||||
|
|
||||||
err := parser.Decode(labels, conf, "traefik.http", "traefik.tcp")
|
err := parser.Decode(labels, conf, parser.DefaultRootName, "traefik.http", "traefik.tcp")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -23,11 +23,11 @@ func DecodeConfiguration(labels map[string]string) (*config.Configuration, error
|
||||||
|
|
||||||
// EncodeConfiguration converts a configuration to labels.
|
// EncodeConfiguration converts a configuration to labels.
|
||||||
func EncodeConfiguration(conf *config.Configuration) (map[string]string, error) {
|
func EncodeConfiguration(conf *config.Configuration) (map[string]string, error) {
|
||||||
return parser.Encode(conf)
|
return parser.Encode(conf, parser.DefaultRootName)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decode converts the labels to an element.
|
// Decode converts the labels to an element.
|
||||||
// labels -> [ node -> node + metadata (type) ] -> element (node)
|
// labels -> [ node -> node + metadata (type) ] -> element (node)
|
||||||
func Decode(labels map[string]string, element interface{}, filters ...string) error {
|
func Decode(labels map[string]string, element interface{}, filters ...string) error {
|
||||||
return parser.Decode(labels, element, filters...)
|
return parser.Decode(labels, element, parser.DefaultRootName, filters...)
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,9 +9,9 @@ import (
|
||||||
|
|
||||||
// EncodeToNode converts an element to a node.
|
// EncodeToNode converts an element to a node.
|
||||||
// element -> nodes
|
// element -> nodes
|
||||||
func EncodeToNode(element interface{}, omitEmpty bool) (*Node, error) {
|
func EncodeToNode(element interface{}, rootName string, omitEmpty bool) (*Node, error) {
|
||||||
rValue := reflect.ValueOf(element)
|
rValue := reflect.ValueOf(element)
|
||||||
node := &Node{Name: "traefik"}
|
node := &Node{Name: rootName}
|
||||||
|
|
||||||
encoder := encoderToNode{omitEmpty: omitEmpty}
|
encoder := encoderToNode{omitEmpty: omitEmpty}
|
||||||
|
|
||||||
|
|
|
@ -723,7 +723,7 @@ func TestEncodeToNode(t *testing.T) {
|
||||||
t.Run(test.desc, func(t *testing.T) {
|
t.Run(test.desc, func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
node, err := EncodeToNode(test.element, true)
|
node, err := EncodeToNode(test.element, DefaultRootName, true)
|
||||||
|
|
||||||
if test.expected.error {
|
if test.expected.error {
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
|
|
|
@ -6,18 +6,16 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
const labelRoot = "traefik"
|
|
||||||
|
|
||||||
// DecodeToNode converts the labels to a tree of nodes.
|
// DecodeToNode converts the labels to a tree of nodes.
|
||||||
// If any filters are present, labels which do not match the filters are skipped.
|
// If any filters are present, labels which do not match the filters are skipped.
|
||||||
func DecodeToNode(labels map[string]string, filters ...string) (*Node, error) {
|
func DecodeToNode(labels map[string]string, rootName string, filters ...string) (*Node, error) {
|
||||||
sortedKeys := sortKeys(labels, filters)
|
sortedKeys := sortKeys(labels, filters)
|
||||||
|
|
||||||
var node *Node
|
var node *Node
|
||||||
for i, key := range sortedKeys {
|
for i, key := range sortedKeys {
|
||||||
split := strings.Split(key, ".")
|
split := strings.Split(key, ".")
|
||||||
|
|
||||||
if split[0] != labelRoot {
|
if split[0] != rootName {
|
||||||
return nil, fmt.Errorf("invalid label root %s", split[0])
|
return nil, fmt.Errorf("invalid label root %s", split[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -218,7 +218,7 @@ func TestDecodeToNode(t *testing.T) {
|
||||||
t.Run(test.desc, func(t *testing.T) {
|
t.Run(test.desc, func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
out, err := DecodeToNode(test.in, test.filters...)
|
out, err := DecodeToNode(test.in, DefaultRootName, test.filters...)
|
||||||
|
|
||||||
if test.expected.error {
|
if test.expected.error {
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
|
|
|
@ -2,6 +2,9 @@ package parser
|
||||||
|
|
||||||
import "reflect"
|
import "reflect"
|
||||||
|
|
||||||
|
// DefaultRootName is the default name of the root node and the prefix of element name from the resources.
|
||||||
|
const DefaultRootName = "traefik"
|
||||||
|
|
||||||
// MapNamePlaceholder is the placeholder for the map name.
|
// MapNamePlaceholder is the placeholder for the map name.
|
||||||
const MapNamePlaceholder = "<name>"
|
const MapNamePlaceholder = "<name>"
|
||||||
|
|
||||||
|
|
|
@ -7,8 +7,8 @@ package parser
|
||||||
// labels -> tree of untyped nodes
|
// labels -> tree of untyped nodes
|
||||||
// untyped nodes -> nodes augmented with metadata such as kind (inferred from element)
|
// untyped nodes -> nodes augmented with metadata such as kind (inferred from element)
|
||||||
// "typed" nodes -> typed element
|
// "typed" nodes -> typed element
|
||||||
func Decode(labels map[string]string, element interface{}, filters ...string) error {
|
func Decode(labels map[string]string, element interface{}, rootName string, filters ...string) error {
|
||||||
node, err := DecodeToNode(labels, filters...)
|
node, err := DecodeToNode(labels, rootName, filters...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -28,8 +28,8 @@ func Decode(labels map[string]string, element interface{}, filters ...string) er
|
||||||
|
|
||||||
// Encode converts an element to labels.
|
// Encode converts an element to labels.
|
||||||
// element -> node (value) -> label (node)
|
// element -> node (value) -> label (node)
|
||||||
func Encode(element interface{}) (map[string]string, error) {
|
func Encode(element interface{}, rootName string) (map[string]string, error) {
|
||||||
node, err := EncodeToNode(element, true)
|
node, err := EncodeToNode(element, rootName, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue