Add KV store providers (dynamic configuration only)
Co-authored-by: Jean-Baptiste Doumenjou <jb.doumenjou@gmail.com>
This commit is contained in:
parent
028683666d
commit
9b9f4be6a4
61 changed files with 5825 additions and 70 deletions
|
@ -14,8 +14,22 @@ type initializer interface {
|
|||
SetDefaults()
|
||||
}
|
||||
|
||||
// FillerOpts Options for the filler.
|
||||
type FillerOpts struct {
|
||||
AllowSliceAsStruct bool
|
||||
}
|
||||
|
||||
// Fill populates the fields of the element using the information in node.
|
||||
func Fill(element interface{}, node *Node) error {
|
||||
func Fill(element interface{}, node *Node, opts FillerOpts) error {
|
||||
return filler{FillerOpts: opts}.Fill(element, node)
|
||||
}
|
||||
|
||||
type filler struct {
|
||||
FillerOpts
|
||||
}
|
||||
|
||||
// Fill populates the fields of the element using the information in node.
|
||||
func (f filler) Fill(element interface{}, node *Node) error {
|
||||
if element == nil || node == nil {
|
||||
return nil
|
||||
}
|
||||
|
@ -29,10 +43,10 @@ func Fill(element interface{}, node *Node) error {
|
|||
return fmt.Errorf("struct are not supported, use pointer instead")
|
||||
}
|
||||
|
||||
return fill(root.Elem(), node)
|
||||
return f.fill(root.Elem(), node)
|
||||
}
|
||||
|
||||
func fill(field reflect.Value, node *Node) error {
|
||||
func (f filler) fill(field reflect.Value, node *Node) error {
|
||||
// related to allow-empty tag
|
||||
if node.Disabled {
|
||||
return nil
|
||||
|
@ -70,19 +84,19 @@ func fill(field reflect.Value, node *Node) error {
|
|||
case reflect.Float64:
|
||||
return setFloat(field, node.Value, 64)
|
||||
case reflect.Struct:
|
||||
return setStruct(field, node)
|
||||
return f.setStruct(field, node)
|
||||
case reflect.Ptr:
|
||||
return setPtr(field, node)
|
||||
return f.setPtr(field, node)
|
||||
case reflect.Map:
|
||||
return setMap(field, node)
|
||||
return f.setMap(field, node)
|
||||
case reflect.Slice:
|
||||
return setSlice(field, node)
|
||||
return f.setSlice(field, node)
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func setPtr(field reflect.Value, node *Node) error {
|
||||
func (f filler) setPtr(field reflect.Value, node *Node) error {
|
||||
if field.IsNil() {
|
||||
field.Set(reflect.New(field.Type().Elem()))
|
||||
|
||||
|
@ -94,10 +108,10 @@ func setPtr(field reflect.Value, node *Node) error {
|
|||
}
|
||||
}
|
||||
|
||||
return fill(field.Elem(), node)
|
||||
return f.fill(field.Elem(), node)
|
||||
}
|
||||
|
||||
func setStruct(field reflect.Value, node *Node) error {
|
||||
func (f filler) setStruct(field reflect.Value, node *Node) error {
|
||||
for _, child := range node.Children {
|
||||
fd := field.FieldByName(child.FieldName)
|
||||
|
||||
|
@ -106,7 +120,7 @@ func setStruct(field reflect.Value, node *Node) error {
|
|||
return fmt.Errorf("field not found, node: %s (%s)", child.Name, child.FieldName)
|
||||
}
|
||||
|
||||
err := fill(fd, child)
|
||||
err := f.fill(fd, child)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -115,10 +129,10 @@ func setStruct(field reflect.Value, node *Node) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func setSlice(field reflect.Value, node *Node) error {
|
||||
func (f filler) setSlice(field reflect.Value, node *Node) error {
|
||||
if field.Type().Elem().Kind() == reflect.Struct ||
|
||||
field.Type().Elem().Kind() == reflect.Ptr && field.Type().Elem().Elem().Kind() == reflect.Struct {
|
||||
return setSliceStruct(field, node)
|
||||
return f.setSliceStruct(field, node)
|
||||
}
|
||||
|
||||
if len(node.Value) == 0 {
|
||||
|
@ -211,9 +225,9 @@ func setSlice(field reflect.Value, node *Node) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func setSliceStruct(field reflect.Value, node *Node) error {
|
||||
if node.Tag.Get(TagLabelSliceAsStruct) != "" {
|
||||
return setSliceAsStruct(field, node)
|
||||
func (f filler) setSliceStruct(field reflect.Value, node *Node) error {
|
||||
if f.AllowSliceAsStruct && node.Tag.Get(TagLabelSliceAsStruct) != "" {
|
||||
return f.setSliceAsStruct(field, node)
|
||||
}
|
||||
|
||||
field.Set(reflect.MakeSlice(field.Type(), len(node.Children), len(node.Children)))
|
||||
|
@ -221,7 +235,7 @@ func setSliceStruct(field reflect.Value, node *Node) error {
|
|||
for i, child := range node.Children {
|
||||
// use Ptr to allow "SetDefaults"
|
||||
value := reflect.New(reflect.PtrTo(field.Type().Elem()))
|
||||
err := setPtr(value, child)
|
||||
err := f.setPtr(value, child)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -232,14 +246,14 @@ func setSliceStruct(field reflect.Value, node *Node) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func setSliceAsStruct(field reflect.Value, node *Node) error {
|
||||
func (f filler) setSliceAsStruct(field reflect.Value, node *Node) error {
|
||||
if len(node.Children) == 0 {
|
||||
return fmt.Errorf("invalid slice: node %s", node.Name)
|
||||
}
|
||||
|
||||
// use Ptr to allow "SetDefaults"
|
||||
value := reflect.New(reflect.PtrTo(field.Type().Elem()))
|
||||
err := setPtr(value, node)
|
||||
err := f.setPtr(value, node)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -252,7 +266,7 @@ func setSliceAsStruct(field reflect.Value, node *Node) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func setMap(field reflect.Value, node *Node) error {
|
||||
func (f filler) setMap(field reflect.Value, node *Node) error {
|
||||
if field.IsNil() {
|
||||
field.Set(reflect.MakeMap(field.Type()))
|
||||
}
|
||||
|
@ -260,7 +274,7 @@ func setMap(field reflect.Value, node *Node) error {
|
|||
for _, child := range node.Children {
|
||||
ptrValue := reflect.New(reflect.PtrTo(field.Type().Elem()))
|
||||
|
||||
err := fill(ptrValue, child)
|
||||
err := f.fill(ptrValue, child)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -1390,7 +1390,7 @@ func TestFill(t *testing.T) {
|
|||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
err := Fill(test.element, test.node)
|
||||
err := filler{FillerOpts: FillerOpts{AllowSliceAsStruct: true}}.Fill(test.element, test.node)
|
||||
if test.expected.error {
|
||||
require.Error(t, err)
|
||||
} else {
|
||||
|
|
|
@ -7,13 +7,20 @@ import (
|
|||
"strings"
|
||||
)
|
||||
|
||||
// EncoderToNodeOpts Options for the encoderToNode.
|
||||
type EncoderToNodeOpts struct {
|
||||
OmitEmpty bool
|
||||
TagName string
|
||||
AllowSliceAsStruct bool
|
||||
}
|
||||
|
||||
// EncodeToNode converts an element to a node.
|
||||
// element -> nodes
|
||||
func EncodeToNode(element interface{}, rootName string, omitEmpty bool) (*Node, error) {
|
||||
func EncodeToNode(element interface{}, rootName string, opts EncoderToNodeOpts) (*Node, error) {
|
||||
rValue := reflect.ValueOf(element)
|
||||
node := &Node{Name: rootName}
|
||||
|
||||
encoder := encoderToNode{omitEmpty: omitEmpty}
|
||||
encoder := encoderToNode{EncoderToNodeOpts: opts}
|
||||
|
||||
err := encoder.setNodeValue(node, rValue)
|
||||
if err != nil {
|
||||
|
@ -24,7 +31,7 @@ func EncodeToNode(element interface{}, rootName string, omitEmpty bool) (*Node,
|
|||
}
|
||||
|
||||
type encoderToNode struct {
|
||||
omitEmpty bool
|
||||
EncoderToNodeOpts
|
||||
}
|
||||
|
||||
func (e encoderToNode) setNodeValue(node *Node, rValue reflect.Value) error {
|
||||
|
@ -65,7 +72,7 @@ func (e encoderToNode) setStructValue(node *Node, rValue reflect.Value) error {
|
|||
continue
|
||||
}
|
||||
|
||||
if field.Tag.Get(TagLabel) == "-" {
|
||||
if field.Tag.Get(e.TagName) == "-" {
|
||||
continue
|
||||
}
|
||||
|
||||
|
@ -78,7 +85,7 @@ func (e encoderToNode) setStructValue(node *Node, rValue reflect.Value) error {
|
|||
}
|
||||
|
||||
nodeName := field.Name
|
||||
if field.Type.Kind() == reflect.Slice && len(field.Tag.Get(TagLabelSliceAsStruct)) != 0 {
|
||||
if e.AllowSliceAsStruct && field.Type.Kind() == reflect.Slice && len(field.Tag.Get(TagLabelSliceAsStruct)) != 0 {
|
||||
nodeName = field.Tag.Get(TagLabelSliceAsStruct)
|
||||
}
|
||||
|
||||
|
@ -101,7 +108,7 @@ func (e encoderToNode) setStructValue(node *Node, rValue reflect.Value) error {
|
|||
}
|
||||
|
||||
if field.Type.Elem().Kind() == reflect.Struct && len(child.Children) == 0 {
|
||||
if field.Tag.Get(TagLabel) != TagLabelAllowEmpty {
|
||||
if field.Tag.Get(e.TagName) != TagLabelAllowEmpty {
|
||||
continue
|
||||
}
|
||||
|
||||
|
@ -181,7 +188,7 @@ func (e encoderToNode) setSliceValue(node *Node, rValue reflect.Value) error {
|
|||
}
|
||||
|
||||
func (e encoderToNode) isSkippedField(field reflect.StructField, fieldValue reflect.Value) bool {
|
||||
if e.omitEmpty && field.Type.Kind() == reflect.String && fieldValue.Len() == 0 {
|
||||
if e.OmitEmpty && field.Type.Kind() == reflect.String && fieldValue.Len() == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
|
@ -189,7 +196,7 @@ func (e encoderToNode) isSkippedField(field reflect.StructField, fieldValue refl
|
|||
return true
|
||||
}
|
||||
|
||||
if e.omitEmpty && (field.Type.Kind() == reflect.Slice) &&
|
||||
if e.OmitEmpty && (field.Type.Kind() == reflect.Slice) &&
|
||||
(fieldValue.IsNil() || fieldValue.Len() == 0) {
|
||||
return true
|
||||
}
|
||||
|
|
|
@ -723,7 +723,8 @@ func TestEncodeToNode(t *testing.T) {
|
|||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
node, err := EncodeToNode(test.element, DefaultRootName, true)
|
||||
etnOpts := EncoderToNodeOpts{OmitEmpty: true, TagName: TagLabel, AllowSliceAsStruct: true}
|
||||
node, err := EncodeToNode(test.element, DefaultRootName, etnOpts)
|
||||
|
||||
if test.expected.error {
|
||||
require.Error(t, err)
|
||||
|
|
|
@ -18,6 +18,7 @@ type FlatOpts struct {
|
|||
Case string // "lower" or "upper", defaults to "lower".
|
||||
Separator string
|
||||
SkipRoot bool
|
||||
TagName string
|
||||
}
|
||||
|
||||
// Flat is a configuration item representation.
|
||||
|
@ -69,7 +70,7 @@ func (e encoderToFlat) createFlat(field reflect.Value, name string, node *Node)
|
|||
var entries []Flat
|
||||
if node.Kind != reflect.Map && node.Description != "-" {
|
||||
if !(node.Kind == reflect.Ptr && len(node.Children) > 0) ||
|
||||
(node.Kind == reflect.Ptr && node.Tag.Get("label") == TagLabelAllowEmpty) {
|
||||
(node.Kind == reflect.Ptr && node.Tag.Get(e.TagName) == TagLabelAllowEmpty) {
|
||||
if node.Name[0] != '[' {
|
||||
entries = append(entries, Flat{
|
||||
Name: e.getName(name),
|
||||
|
|
|
@ -156,6 +156,7 @@ func TestEncodeToFlat(t *testing.T) {
|
|||
Case: "upper",
|
||||
Separator: "_",
|
||||
SkipRoot: false,
|
||||
TagName: TagLabel,
|
||||
},
|
||||
expected: []Flat{{
|
||||
Name: "TRAEFIK_FIELD",
|
||||
|
@ -1236,7 +1237,7 @@ func TestEncodeToFlat(t *testing.T) {
|
|||
|
||||
var opts FlatOpts
|
||||
if test.opts == nil {
|
||||
opts = FlatOpts{Separator: ".", SkipRoot: true}
|
||||
opts = FlatOpts{Separator: ".", SkipRoot: true, TagName: TagLabel}
|
||||
} else {
|
||||
opts = *test.opts
|
||||
}
|
||||
|
|
|
@ -7,8 +7,23 @@ import (
|
|||
"strings"
|
||||
)
|
||||
|
||||
// MetadataOpts Options for the metadata.
|
||||
type MetadataOpts struct {
|
||||
TagName string
|
||||
AllowSliceAsStruct bool
|
||||
}
|
||||
|
||||
// AddMetadata adds metadata such as type, inferred from element, to a node.
|
||||
func AddMetadata(element interface{}, node *Node) error {
|
||||
func AddMetadata(element interface{}, node *Node, opts MetadataOpts) error {
|
||||
return metadata{MetadataOpts: opts}.Add(element, node)
|
||||
}
|
||||
|
||||
type metadata struct {
|
||||
MetadataOpts
|
||||
}
|
||||
|
||||
// Add adds metadata such as type, inferred from element, to a node.
|
||||
func (m metadata) Add(element interface{}, node *Node) error {
|
||||
if node == nil {
|
||||
return nil
|
||||
}
|
||||
|
@ -24,25 +39,25 @@ func AddMetadata(element interface{}, node *Node) error {
|
|||
rootType := reflect.TypeOf(element)
|
||||
node.Kind = rootType.Kind()
|
||||
|
||||
return browseChildren(rootType, node)
|
||||
return m.browseChildren(rootType, node)
|
||||
}
|
||||
|
||||
func browseChildren(fType reflect.Type, node *Node) error {
|
||||
func (m metadata) browseChildren(fType reflect.Type, node *Node) error {
|
||||
for _, child := range node.Children {
|
||||
if err := addMetadata(fType, child); err != nil {
|
||||
if err := m.add(fType, child); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func addMetadata(rootType reflect.Type, node *Node) error {
|
||||
func (m metadata) add(rootType reflect.Type, node *Node) error {
|
||||
rType := rootType
|
||||
if rootType.Kind() == reflect.Ptr {
|
||||
rType = rootType.Elem()
|
||||
}
|
||||
|
||||
field, err := findTypedField(rType, node)
|
||||
field, err := m.findTypedField(rType, node)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -57,11 +72,11 @@ func addMetadata(rootType reflect.Type, node *Node) error {
|
|||
|
||||
if fType.Kind() == reflect.Struct || fType.Kind() == reflect.Ptr && fType.Elem().Kind() == reflect.Struct ||
|
||||
fType.Kind() == reflect.Map {
|
||||
if len(node.Children) == 0 && field.Tag.Get(TagLabel) != TagLabelAllowEmpty {
|
||||
if len(node.Children) == 0 && field.Tag.Get(m.TagName) != TagLabelAllowEmpty {
|
||||
return fmt.Errorf("%s cannot be a standalone element (type %s)", node.Name, fType)
|
||||
}
|
||||
|
||||
node.Disabled = len(node.Value) > 0 && !strings.EqualFold(node.Value, "true") && field.Tag.Get(TagLabel) == TagLabelAllowEmpty
|
||||
node.Disabled = len(node.Value) > 0 && !strings.EqualFold(node.Value, "true") && field.Tag.Get(m.TagName) == TagLabelAllowEmpty
|
||||
}
|
||||
|
||||
if len(node.Children) == 0 {
|
||||
|
@ -69,7 +84,7 @@ func addMetadata(rootType reflect.Type, node *Node) error {
|
|||
}
|
||||
|
||||
if fType.Kind() == reflect.Struct || fType.Kind() == reflect.Ptr && fType.Elem().Kind() == reflect.Struct {
|
||||
return browseChildren(fType, node)
|
||||
return m.browseChildren(fType, node)
|
||||
}
|
||||
|
||||
if fType.Kind() == reflect.Map {
|
||||
|
@ -80,7 +95,7 @@ func addMetadata(rootType reflect.Type, node *Node) error {
|
|||
|
||||
if elem.Kind() == reflect.Map || elem.Kind() == reflect.Struct ||
|
||||
(elem.Kind() == reflect.Ptr && elem.Elem().Kind() == reflect.Struct) {
|
||||
if err = browseChildren(elem, child); err != nil {
|
||||
if err = m.browseChildren(elem, child); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
@ -89,13 +104,13 @@ func addMetadata(rootType reflect.Type, node *Node) error {
|
|||
}
|
||||
|
||||
if fType.Kind() == reflect.Slice {
|
||||
if field.Tag.Get(TagLabelSliceAsStruct) != "" {
|
||||
return browseChildren(fType.Elem(), node)
|
||||
if m.AllowSliceAsStruct && field.Tag.Get(TagLabelSliceAsStruct) != "" {
|
||||
return m.browseChildren(fType.Elem(), node)
|
||||
}
|
||||
|
||||
for _, ch := range node.Children {
|
||||
ch.Kind = fType.Elem().Kind()
|
||||
if err = browseChildren(fType.Elem(), ch); err != nil {
|
||||
if err = m.browseChildren(fType.Elem(), ch); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
@ -105,19 +120,19 @@ func addMetadata(rootType reflect.Type, node *Node) error {
|
|||
return fmt.Errorf("invalid node %s: %v", node.Name, fType.Kind())
|
||||
}
|
||||
|
||||
func findTypedField(rType reflect.Type, node *Node) (reflect.StructField, error) {
|
||||
func (m metadata) findTypedField(rType reflect.Type, node *Node) (reflect.StructField, error) {
|
||||
for i := 0; i < rType.NumField(); i++ {
|
||||
cField := rType.Field(i)
|
||||
|
||||
fieldName := cField.Tag.Get(TagLabelSliceAsStruct)
|
||||
if len(fieldName) == 0 {
|
||||
if !m.AllowSliceAsStruct || len(fieldName) == 0 {
|
||||
fieldName = cField.Name
|
||||
}
|
||||
|
||||
if IsExported(cField) {
|
||||
if cField.Anonymous {
|
||||
if cField.Type.Kind() == reflect.Struct {
|
||||
structField, err := findTypedField(cField.Type, node)
|
||||
structField, err := m.findTypedField(cField.Type, node)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
|
|
@ -991,7 +991,7 @@ func TestAddMetadata(t *testing.T) {
|
|||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
err := AddMetadata(test.structure, test.tree)
|
||||
err := metadata{MetadataOpts{TagName: TagLabel, AllowSliceAsStruct: true}}.Add(test.structure, test.tree)
|
||||
|
||||
if test.expected.error {
|
||||
assert.Error(t, err)
|
||||
|
|
|
@ -13,12 +13,13 @@ func Decode(labels map[string]string, element interface{}, rootName string, filt
|
|||
return err
|
||||
}
|
||||
|
||||
err = AddMetadata(element, node)
|
||||
metaOpts := MetadataOpts{TagName: TagLabel, AllowSliceAsStruct: true}
|
||||
err = AddMetadata(element, node, metaOpts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = Fill(element, node)
|
||||
err = Fill(element, node, FillerOpts{AllowSliceAsStruct: true})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -29,7 +30,8 @@ func Decode(labels map[string]string, element interface{}, rootName string, filt
|
|||
// Encode converts an element to labels.
|
||||
// element -> node (value) -> label (node)
|
||||
func Encode(element interface{}, rootName string) (map[string]string, error) {
|
||||
node, err := EncodeToNode(element, rootName, true)
|
||||
etnOpts := EncoderToNodeOpts{OmitEmpty: true, TagName: TagLabel, AllowSliceAsStruct: true}
|
||||
node, err := EncodeToNode(element, rootName, etnOpts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue