fix: use yaml.v2 as yaml parser (#37423)

issue: #34298 
Viper uses yaml.v2 as the parser. This PR will adopt the parsing logic
from Viper to handle YAML files, ensuring maximum consistency in
parsing.

Signed-off-by: xianliang.li <xianliang.li@zilliz.com>
This commit is contained in:
foxspy 2024-11-11 21:26:27 +08:00 committed by GitHub
parent 1b6edd0b4b
commit 81141bd18d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 40 additions and 61 deletions

View File

@ -17,10 +17,11 @@
package config
import (
"fmt"
"strings"
"github.com/cockroachdb/errors"
"gopkg.in/yaml.v3"
"github.com/spf13/cast"
"github.com/milvus-io/milvus/pkg/util/typeutil"
)
@ -80,48 +81,41 @@ func formatKey(key string) string {
return result
}
func flattenNode(node *yaml.Node, parentKey string, result map[string]string) {
// The content of the node should contain key-value pairs in a MappingNode
if node.Kind == yaml.MappingNode {
for i := 0; i < len(node.Content); i += 2 {
keyNode := node.Content[i]
valueNode := node.Content[i+1]
func flattenAndMergeMap(prefix string, m map[string]interface{}, result map[string]string) {
for k, v := range m {
fullKey := k
if prefix != "" {
fullKey = prefix + "." + k
}
key := keyNode.Value
// Construct the full key with parent hierarchy
fullKey := key
if parentKey != "" {
fullKey = parentKey + "." + key
}
switch valueNode.Kind {
case yaml.ScalarNode:
// handle null value
if valueNode.Tag == "!!null" {
result[lowerKey(fullKey)] = ""
result[formatKey(fullKey)] = ""
switch val := v.(type) {
case map[string]interface{}:
flattenAndMergeMap(fullKey, val, result)
case map[interface{}]interface{}:
flattenAndMergeMap(fullKey, cast.ToStringMap(val), result)
case []interface{}:
str := ""
for i, item := range val {
itemStr, err := cast.ToStringE(item)
if err != nil {
continue
}
if i == 0 {
str = itemStr
} else {
// Scalar value, store it as a string
result[lowerKey(fullKey)] = valueNode.Value
result[formatKey(fullKey)] = valueNode.Value
str = str + "," + itemStr
}
case yaml.MappingNode:
// Nested map, process recursively
flattenNode(valueNode, fullKey, result)
case yaml.SequenceNode:
// List (sequence), process elements
var listStr string
for j, item := range valueNode.Content {
if j > 0 {
listStr += ","
}
if item.Kind == yaml.ScalarNode {
listStr += item.Value
}
}
result[lowerKey(fullKey)] = listStr
result[formatKey(fullKey)] = listStr
}
result[lowerKey(fullKey)] = str
result[formatKey(fullKey)] = str
default:
str, err := cast.ToStringE(val)
if err != nil {
fmt.Printf("cast to string failed %s, error = %s\n", fullKey, err.Error())
continue
}
result[lowerKey(fullKey)] = str
result[formatKey(fullKey)] = str
}
}
}

View File

@ -17,9 +17,7 @@
package config
import (
"bytes"
"fmt"
"io"
"os"
"path/filepath"
"sync"
@ -27,7 +25,7 @@ import (
"github.com/cockroachdb/errors"
"github.com/samber/lo"
"go.uber.org/zap"
"gopkg.in/yaml.v3"
"gopkg.in/yaml.v2"
"github.com/milvus-io/milvus/pkg/log"
)
@ -131,7 +129,7 @@ func (fs *FileSource) loadFromFile() error {
}
ext := filepath.Ext(configFile)
if len(ext) == 0 || ext[1:] != "yaml" {
if len(ext) == 0 || (ext[1:] != "yaml" && ext[1:] != "yml") {
return fmt.Errorf("Unsupported Config Type: " + ext)
}
@ -140,26 +138,13 @@ func (fs *FileSource) loadFromFile() error {
return errors.Wrap(err, "Read config failed: "+configFile)
}
// handle empty file
if len(data) == 0 {
continue
var config map[string]interface{}
err = yaml.Unmarshal(data, &config)
if err != nil {
return errors.Wrap(err, "unmarshal yaml file "+configFile+" failed")
}
var node yaml.Node
decoder := yaml.NewDecoder(bytes.NewReader(data))
if err := decoder.Decode(&node); err != nil && !errors.Is(err, io.EOF) {
return errors.Wrap(err, "YAML unmarshal failed: "+configFile)
}
if node.Kind == yaml.DocumentNode && len(node.Content) > 0 {
// Get the content of the Document Node
contentNode := node.Content[0]
// Recursively process the content of the Document Node
flattenNode(contentNode, "", newConfig)
} else if node.Kind == yaml.MappingNode {
flattenNode(&node, "", newConfig)
}
flattenAndMergeMap("", config, newConfig)
}
return fs.update(newConfig)