1
0
Fork 0

Adds Docker provider support

Co-authored-by: Julien Salleyron <julien@containo.us>
This commit is contained in:
Ludovic Fernandez 2019-01-18 15:18:04 +01:00 committed by Traefiker Bot
parent 8735263930
commit b54c956c5e
78 changed files with 3476 additions and 5587 deletions

View file

@ -5,8 +5,15 @@ import (
"reflect"
"strconv"
"strings"
"time"
"github.com/containous/flaeg/parse"
)
type initializer interface {
SetDefaults()
}
// Fill the fields of the element.
// nodes -> element
func Fill(element interface{}, node *Node) error {
@ -79,6 +86,13 @@ func fill(field reflect.Value, node *Node) error {
func setPtr(field reflect.Value, node *Node) error {
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{})
}
}
}
return fill(field.Elem(), node)
@ -202,12 +216,15 @@ func setSliceAsStruct(field reflect.Value, node *Node) error {
return fmt.Errorf("invalid slice: node %s", node.Name)
}
elem := reflect.New(field.Type().Elem()).Elem()
err := setStruct(elem, node)
// use Ptr to allow "SetDefaults"
value := reflect.New(reflect.PtrTo(field.Type().Elem()))
err := setPtr(value, node)
if err != nil {
return err
}
elem := value.Elem().Elem()
field.Set(reflect.MakeSlice(field.Type(), 1, 1))
field.Index(0).Set(elem)
@ -236,12 +253,35 @@ func setMap(field reflect.Value, node *Node) error {
}
func setInt(field reflect.Value, value string, bitSize int) error {
switch field.Type() {
case reflect.TypeOf(parse.Duration(0)):
return setDuration(field, value, bitSize, time.Second)
case reflect.TypeOf(time.Duration(0)):
return setDuration(field, value, bitSize, time.Nanosecond)
default:
val, err := strconv.ParseInt(value, 10, bitSize)
if err != nil {
return err
}
field.Set(reflect.ValueOf(val).Convert(field.Type()))
return nil
}
}
func setDuration(field reflect.Value, value string, bitSize int, defaultUnit time.Duration) error {
val, err := strconv.ParseInt(value, 10, bitSize)
if err == nil {
field.Set(reflect.ValueOf(time.Duration(val) * defaultUnit).Convert(field.Type()))
return nil
}
duration, err := time.ParseDuration(value)
if err != nil {
return err
}
field.Set(reflect.ValueOf(val).Convert(field.Type()))
field.Set(reflect.ValueOf(duration).Convert(field.Type()))
return nil
}

View file

@ -3,7 +3,9 @@ package internal
import (
"reflect"
"testing"
"time"
"github.com/containous/flaeg/parse"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
@ -363,6 +365,54 @@ func TestFill(t *testing.T) {
element: &struct{ Foo uint64 }{},
expected: expected{error: true},
},
{
desc: "time.Duration with unit",
node: &Node{
Name: "traefik",
Kind: reflect.Struct,
Children: []*Node{
{Name: "Foo", FieldName: "Foo", Value: "4s", Kind: reflect.Int64},
},
},
element: &struct{ Foo time.Duration }{},
expected: expected{element: &struct{ Foo time.Duration }{Foo: 4 * time.Second}},
},
{
desc: "time.Duration without unit",
node: &Node{
Name: "traefik",
Kind: reflect.Struct,
Children: []*Node{
{Name: "Foo", FieldName: "Foo", Value: "4", Kind: reflect.Int64},
},
},
element: &struct{ Foo time.Duration }{},
expected: expected{element: &struct{ Foo time.Duration }{Foo: 4 * time.Nanosecond}},
},
{
desc: "parse.Duration with unit",
node: &Node{
Name: "traefik",
Kind: reflect.Struct,
Children: []*Node{
{Name: "Foo", FieldName: "Foo", Value: "4s", Kind: reflect.Int64},
},
},
element: &struct{ Foo parse.Duration }{},
expected: expected{element: &struct{ Foo parse.Duration }{Foo: parse.Duration(4 * time.Second)}},
},
{
desc: "parse.Duration without unit",
node: &Node{
Name: "traefik",
Kind: reflect.Struct,
Children: []*Node{
{Name: "Foo", FieldName: "Foo", Value: "4", Kind: reflect.Int64},
},
},
element: &struct{ Foo parse.Duration }{},
expected: expected{element: &struct{ Foo parse.Duration }{Foo: parse.Duration(4 * time.Second)}},
},
{
desc: "bool",
node: &Node{
@ -1069,6 +1119,57 @@ func TestFill(t *testing.T) {
}{},
expected: expected{error: true},
},
{
desc: "pointer SetDefaults method",
node: &Node{
Name: "traefik",
Kind: reflect.Struct,
Children: []*Node{
{
Name: "Foo",
FieldName: "Foo",
Kind: reflect.Struct,
Children: []*Node{
{Name: "Fuu", FieldName: "Fuu", Value: "huu", Kind: reflect.String},
}},
}},
element: &struct {
Foo *initialledFoo
}{},
expected: expected{element: &struct {
Foo *initialledFoo
}{
Foo: &initialledFoo{
Fii: "default",
Fuu: "huu",
},
}},
},
{
desc: "pointer wrong SetDefaults method",
node: &Node{
Name: "traefik",
Kind: reflect.Struct,
Children: []*Node{
{
Name: "Foo",
FieldName: "Foo",
Kind: reflect.Struct,
Children: []*Node{
{Name: "Fuu", FieldName: "Fuu", Value: "huu", Kind: reflect.String},
}},
}},
element: &struct {
Foo *wrongInitialledFoo
}{},
expected: expected{element: &struct {
Foo *wrongInitialledFoo
}{
Foo: &wrongInitialledFoo{
Fuu: "huu",
},
}},
},
}
for _, test := range testCases {
@ -1086,3 +1187,22 @@ func TestFill(t *testing.T) {
})
}
}
type initialledFoo struct {
Fii string
Fuu string
}
func (t *initialledFoo) SetDefaults() {
t.Fii = "default"
}
type wrongInitialledFoo struct {
Fii string
Fuu string
}
func (t *wrongInitialledFoo) SetDefaults() error {
t.Fii = "default"
return nil
}

View file

@ -8,10 +8,20 @@ import (
// DecodeToNode Converts the labels to a node.
// labels -> nodes
func DecodeToNode(labels map[string]string) (*Node, error) {
func DecodeToNode(labels map[string]string, filters ...string) (*Node, error) {
var sortedKeys []string
for key := range labels {
sortedKeys = append(sortedKeys, key)
if len(filters) == 0 {
sortedKeys = append(sortedKeys, key)
continue
}
for _, filter := range filters {
if len(key) >= len(filter) && strings.EqualFold(key[:len(filter)], filter) {
sortedKeys = append(sortedKeys, key)
continue
}
}
}
sort.Strings(sortedKeys)

View file

@ -18,15 +18,13 @@ func TestDecodeToNode(t *testing.T) {
testCases := []struct {
desc string
in map[string]string
filters []string
expected expected
}{
{
desc: "level 0",
in: map[string]string{"traefik": "bar"},
expected: expected{node: &Node{
Name: "traefik",
Value: "bar",
}},
desc: "no label",
in: map[string]string{},
expected: expected{node: nil},
},
{
desc: "level 1",
@ -75,6 +73,20 @@ func TestDecodeToNode(t *testing.T) {
},
expected: expected{error: true},
},
{
desc: "several entries, prefix filter",
in: map[string]string{
"traefik.foo": "bar",
"traefik.fii": "bir",
},
filters: []string{"traefik.Foo"},
expected: expected{node: &Node{
Name: "traefik",
Children: []*Node{
{Name: "foo", Value: "bar"},
},
}},
},
{
desc: "several entries, level 1",
in: map[string]string{
@ -172,7 +184,7 @@ func TestDecodeToNode(t *testing.T) {
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
out, err := DecodeToNode(test.in)
out, err := DecodeToNode(test.in, test.filters...)
if test.expected.error {
require.Error(t, err)

View file

@ -10,6 +10,10 @@ import (
// AddMetadata Adds metadata to a node.
// nodes + element -> nodes
func AddMetadata(structure interface{}, node *Node) error {
if node == nil {
return nil
}
if len(node.Children) == 0 {
return fmt.Errorf("invalid node %s: no child", node.Name)
}

View file

@ -24,6 +24,12 @@ func TestAddMetadata(t *testing.T) {
structure interface{}
expected expected
}{
{
desc: "Node Nil",
tree: nil,
structure: nil,
expected: expected{node: nil},
},
{
desc: "Empty Node",
tree: &Node{},