diff --git a/pkg/cmd/root.go b/pkg/cmd/root.go index 3bd0963..7af1711 100644 --- a/pkg/cmd/root.go +++ b/pkg/cmd/root.go @@ -3,6 +3,7 @@ package cmd import ( "log/slog" "os" + "strings" "github.com/spf13/cobra" "github.com/spf13/viper" @@ -14,6 +15,8 @@ var ( edmLoggerLevel *slog.LevelVar ) +const envPrefix = "DNSTAPIR_EDM" + // rootCmd represents the base command when called without any subcommands var rootCmd = &cobra.Command{ Use: "dnstapir-edm", @@ -67,7 +70,7 @@ func initConfig() { viper.SetConfigName(".dnstapir-edm") } - viper.AutomaticEnv() // read in environment variables that match + configureEnv() // If a config file is found, read it in. if err := viper.ReadInConfig(); err == nil { @@ -77,3 +80,9 @@ func initConfig() { // Make it so we can detect changes to the cryptopan secret in the config viper.WatchConfig() } + +func configureEnv() { + viper.SetEnvPrefix(envPrefix) + viper.SetEnvKeyReplacer(strings.NewReplacer("-", "_")) + viper.AutomaticEnv() // read in environment variables that match +} diff --git a/pkg/cmd/root_test.go b/pkg/cmd/root_test.go new file mode 100644 index 0000000..0925aa6 --- /dev/null +++ b/pkg/cmd/root_test.go @@ -0,0 +1,67 @@ +package cmd + +import ( + "io" + "log/slog" + "os" + "path/filepath" + "testing" + + "github.com/spf13/viper" +) + +func TestInitConfigIgnoresUnprefixedEnv(t *testing.T) { + initConfigForTest(t, "debug = false\n") + t.Setenv("DEBUG", "release") + + initConfig() + + var conf struct { + Debug bool `mapstructure:"debug"` + } + if err := viper.UnmarshalExact(&conf); err != nil { + t.Fatalf("unprefixed DEBUG should not affect config unmarshalling: %s", err) + } + if conf.Debug { + t.Fatal("unprefixed DEBUG unexpectedly overrode config debug=false") + } +} + +func TestInitConfigUsesPrefixedEnv(t *testing.T) { + initConfigForTest(t, "debug = false\n") + t.Setenv("DEBUG", "release") + debugEnv := envPrefix + "_DEBUG" + t.Setenv(debugEnv, "true") + + initConfig() + + var conf struct { + Debug bool `mapstructure:"debug"` + } + if err := viper.UnmarshalExact(&conf); err != nil { + t.Fatalf("prefixed debug env should unmarshal cleanly: %s", err) + } + if !conf.Debug { + t.Fatalf("%s=true did not override config debug=false", debugEnv) + } +} + +func initConfigForTest(t *testing.T, configData string) { + t.Helper() + + viper.Reset() + oldCfgFile := cfgFile + oldLogger := edmLogger + t.Cleanup(func() { + cfgFile = oldCfgFile + edmLogger = oldLogger + viper.Reset() + }) + + configFile := filepath.Join(t.TempDir(), "dnstapir-edm.toml") + if err := os.WriteFile(configFile, []byte(configData), 0o600); err != nil { + t.Fatalf("unable to write test config: %s", err) + } + cfgFile = configFile + edmLogger = slog.New(slog.NewTextHandler(io.Discard, nil)) +}