Adds Docker provider support
Co-authored-by: Julien Salleyron <julien@containo.us>
This commit is contained in:
parent
8735263930
commit
b54c956c5e
78 changed files with 3476 additions and 5587 deletions
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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{},
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue