-
-
Notifications
You must be signed in to change notification settings - Fork 3.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Non-string type config with default value can't work #1565
Comments
Hi @jeffcaiz, I think it is a bug, because of we store all config value as a // config.go:136
func (c *config) Scan(v interface{}) error {
data, err := c.reader.Source()
if err != nil {
return err
}
return unmarshalJSON(data, v)
} therefore, the we will consider fixing it as soon as possible, if you have any other suggestion, welcome to discuss |
Hi @kagaya85 , Thanks for your reply. Further check on the source shows that it's PKG I believe we can make some change to the the |
Dirty fix snippet: // V2.1.1
// Kratos的配置解码器其实有点问题
// 提了Issue,官方也认了。自己动手修复一下
// https://github.com/go-kratos/kratos/issues/1565
func CustomResolver(input map[string]interface{}) error {
mapper := func(name string) string {
args := strings.SplitN(strings.TrimSpace(name), ":", 2) //nolint:gomnd
if v, has := readValue(input, args[0]); has {
s, _ := v.String()
return s
} else if len(args) > 1 { // default value
return args[1]
}
return ""
}
var resolve func(map[string]interface{}) error
resolve = func(sub map[string]interface{}) error {
for k, v := range sub {
switch vt := v.(type) {
case string:
vs := expand(vt, mapper)
// 如果被单引号括住,去掉单引号,保留为string
if vst := strings.Trim(vs, "'"); len(vst) == len(vs)-1 {
sub[k] = vst
} else if vs == "true" || vs == "false" {
// 如果是true/false,转为boolean。其他形式我们不支持
vb, _ := strconv.ParseBool(vs)
sub[k] = vb
} else if vi, err := strconv.ParseInt(vs, 0, 32); err == nil {
// 如果可以转整数,转
sub[k] = vi
} else if vf, err := strconv.ParseFloat(vs, 32); err == nil {
// 如果可以转浮点,转
sub[k] = vf
} else {
// 保留原来
sub[k] = vs
}
case map[string]interface{}:
if err := resolve(vt); err != nil {
return err
}
case []interface{}:
for i, iface := range vt {
switch it := iface.(type) {
case string:
vt[i] = expand(it, mapper)
case map[string]interface{}:
if err := resolve(it); err != nil {
return err
}
}
}
sub[k] = vt
}
}
return nil
}
return resolve(input)
}
// =============================================
// Copy from kratos and make no change
func expand(s string, mapping func(string) string) string {
r := regexp.MustCompile(`\${(.*?)}`)
re := r.FindAllStringSubmatch(s, -1)
for _, i := range re {
if len(i) == 2 { //nolint:gomnd
s = strings.ReplaceAll(s, i[0], mapping(i[1]))
}
}
return s
}
type atomicValue struct {
atomic.Value
}
type ValueLite interface {
String() (string, error)
Store(interface{})
Load() interface{}
}
func (v *atomicValue) String() (string, error) {
switch val := v.Load().(type) {
case string:
return val, nil
case bool, int, int32, int64, float64:
return fmt.Sprint(val), nil
case []byte:
return string(val), nil
default:
if s, ok := val.(fmt.Stringer); ok {
return s.String(), nil
}
}
return "", fmt.Errorf("type assert to %v failed", reflect.TypeOf(v.Load()))
}
// readValue read Value in given map[string]interface{}
// by the given path, will return false if not found.
func readValue(values map[string]interface{}, path string) (ValueLite, bool) {
var (
next = values
keys = strings.Split(path, ".")
last = len(keys) - 1
)
for idx, key := range keys {
value, ok := next[key]
if !ok {
return nil, false
}
if idx == last {
av := &atomicValue{}
av.Store(value)
return av, true
}
switch vm := value.(type) {
case map[string]interface{}:
next = vm
default:
return nil, false
}
}
return nil, false
} To use: c := config.New(
config.WithSource(
file.NewSource(flagconf),
),
config.WithResolver(CustomResolver), // Add this
) |
I am glad to see I prefer decide value's type when READ it from what do you think, PTAL @ymh199478 @tonybase |
Current default value analyze is performed after In the other words,in
but in
Parsing during |
What happened:
It seems there is no way to apply default value to non-string type of config variable. It will always consider as a string and complain about wrong type like below.
What you expected to happen:
For
booltype: ${SOME_VAR:true}
, it should works. If I say 'true' and it will be consider a boolean true, not panic with wrong typeHow to reproduce it (as minimally and precisely as possible):
conf.proto
with attachedconfig.yaml
with attachedAnything else we need to know?:
Environment:
kratos -v
): v2.1.0go version
): v1.17.1 linuxcat /etc/os-release
): WSL2 on Windows 10Attachment
config.yaml
conf.proto
The text was updated successfully, but these errors were encountered: