Merging nested configs doesn't work as expected
michaeladler opened this issue · comments
Describe the bug
I'm having two sources for my configuration:
- a config file (yaml)
- pflags
The idea is that pflags wins over the values set in the config file (unless pflag is the default value).
In this particular example, I will try to set --log-level
using the config file.
To Reproduce
- Create
config.yaml
:
# uncommenting the following line fixes the bug:
# log-level: debug
log:
level: debug
- Create this as
main.go
and run it:
package main
import (
"fmt"
"log"
"os"
"github.com/knadh/koanf/parsers/yaml"
"github.com/knadh/koanf/providers/file"
"github.com/knadh/koanf/providers/posflag"
"github.com/knadh/koanf/v2"
flag "github.com/spf13/pflag"
)
var k = koanf.New(".")
func main() {
if err := k.Load(file.Provider("config.yaml"), yaml.Parser()); err != nil {
log.Fatalf("error loading config: %v", err)
}
// log.level is debug
k.Print()
f := flag.NewFlagSet("config", flag.ContinueOnError)
f.Usage = func() {
fmt.Println(f.FlagUsages())
os.Exit(0)
}
f.String("log-level", "info", "set log level")
f.Parse(os.Args[1:])
if err := k.Load(posflag.Provider(f, "-", k), nil); err != nil {
log.Fatalf("error loading config: %v", err)
}
// now log.level is info
k.Print()
level := k.String("log.level")
fmt.Println("log level is:", level)
}
Expected behavior
The final log level is info
but it should be debug
because I set it in the config file and didn't specify any flags. The output should be:
log.level -> debug
log.level -> debug
log level is: debug
Please provide the following information):
- OS: linux
- Koanf Version: v2.0.0
Additional context
If I change delim
of posflag.Provider
to .
(and thus use the flag --log.level
) it works as expected. However, I prefer the style --log-level
. I guess koanf doesn't like that the string starts with --
.
This has to do with how pflag handles default values. This is documented here
koanf/providers/posflag/posflag.go
Line 35 in af9ea08
--
in flags is irrelevant. The flag library strips that away and only returns the key.
Yes, I understand that, but: using the default pflag value is wrong because I have a value set in my config file.
I think you need to provide a cb function that replaces your flag delimiter -
with the koanf delimiter (usually .
).
Yes, I understand that, but: using the default pflag value is wrong because I have a value set in my config file.
Please refer to the comments on the function I'd linked in the previous comment. This behaviour can be overridden by passing an additional config interface.
Got it, thanks. This is the fix for the above code:
if err := k.Load(posflag.ProviderWithValue(f, ".", k, func(key, value string) (string, interface{}) {
return strings.ReplaceAll(key, "-", "."), value
}), nil); err != nil {
log.Fatalf("error loading config: %v", err)
}