traefik/pkg/config/kv/kv_node.go
Kevin Pollet b84829336d
Support Consul KV Enterprise namespaces
Co-authored-by: Romain <rtribotte@users.noreply.github.com>
2022-01-12 14:42:21 +01:00

129 lines
3 KiB
Go

package kv
import (
"fmt"
"regexp"
"sort"
"strings"
"github.com/kvtools/valkeyrie/store"
"github.com/traefik/paerser/parser"
)
// DecodeToNode converts the labels to a tree of nodes.
// If any filters are present, labels which do not match the filters are skipped.
func DecodeToNode(pairs []*store.KVPair, rootName string, filters ...string) (*parser.Node, error) {
sortedPairs := filterPairs(pairs, filters)
exp := regexp.MustCompile(`^\d+$`)
var node *parser.Node
for i, pair := range sortedPairs {
if !strings.HasPrefix(pair.Key, rootName+"/") {
return nil, fmt.Errorf("invalid label root %s", rootName)
}
split := strings.FieldsFunc(pair.Key[len(rootName)+1:], func(c rune) bool { return c == '/' })
parts := []string{rootName}
for _, fragment := range split {
if exp.MatchString(fragment) {
parts = append(parts, "["+fragment+"]")
} else {
parts = append(parts, fragment)
}
}
if i == 0 {
node = &parser.Node{}
}
decodeToNode(node, parts, string(pair.Value))
}
return node, nil
}
func decodeToNode(root *parser.Node, path []string, value string) {
if len(root.Name) == 0 {
root.Name = path[0]
}
// it's a leaf or not -> children
if len(path) > 1 {
if n := containsNode(root.Children, path[1]); n != nil {
// the child already exists
decodeToNode(n, path[1:], value)
} else {
// new child
child := &parser.Node{Name: path[1]}
decodeToNode(child, path[1:], value)
root.Children = append(root.Children, child)
}
} else {
root.Value = value
}
}
func containsNode(nodes []*parser.Node, name string) *parser.Node {
for _, n := range nodes {
if strings.EqualFold(name, n.Name) {
return n
}
}
return nil
}
func filterPairs(pairs []*store.KVPair, filters []string) []*store.KVPair {
exp := regexp.MustCompile(`^(.+)/\d+$`)
sort.Slice(pairs, func(i, j int) bool {
return pairs[i].Key < pairs[j].Key
})
simplePairs := map[string]*store.KVPair{}
slicePairs := map[string][]string{}
for _, pair := range pairs {
if len(filters) == 0 {
// Slice of simple type
if exp.MatchString(pair.Key) {
sanitizedKey := exp.FindStringSubmatch(pair.Key)[1]
slicePairs[sanitizedKey] = append(slicePairs[sanitizedKey], string(pair.Value))
} else {
simplePairs[pair.Key] = pair
}
continue
}
for _, filter := range filters {
if len(pair.Key) >= len(filter) && strings.EqualFold(pair.Key[:len(filter)], filter) {
// Slice of simple type
if exp.MatchString(pair.Key) {
sanitizedKey := exp.FindStringSubmatch(pair.Key)[1]
slicePairs[sanitizedKey] = append(slicePairs[sanitizedKey], string(pair.Value))
} else {
simplePairs[pair.Key] = pair
}
continue
}
}
}
var sortedPairs []*store.KVPair
for k, v := range slicePairs {
delete(simplePairs, k)
sortedPairs = append(sortedPairs, &store.KVPair{Key: k, Value: []byte(strings.Join(v, ","))})
}
for _, v := range simplePairs {
sortedPairs = append(sortedPairs, v)
}
sort.Slice(sortedPairs, func(i, j int) bool {
return sortedPairs[i].Key < sortedPairs[j].Key
})
return sortedPairs
}