Skip to content
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

chore: provide methods to access config init errors #406

Merged
merged 3 commits into from
Apr 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,9 @@ type Config struct {
reloadableVars map[string]any
reloadableVarsMisuses map[string]string
reloadableVarsLock sync.RWMutex // used to protect both the reloadableVars and reloadableVarsMisuses maps
configPath string
configPathErr error
godotEnvErr error
}

// GetBool gets bool value from config
Expand Down
53 changes: 53 additions & 0 deletions config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package config
import (
"context"
"fmt"
"os"
"sync"
"testing"
"time"
Expand Down Expand Up @@ -516,6 +517,15 @@ func TestGetEnvThroughViper(t *testing.T) {
require.Equal(t, expectedValue, tc.GetString("Key.Var1VarVar", ""))
})

t.Run("with env prefix", func(t *testing.T) {
envPrefix := "ANOTHER_PREFIX"

tc := New(WithEnvPrefix(envPrefix))
t.Setenv(envPrefix+"_KEY_VAR1_VAR_VAR", expectedValue)

require.Equal(t, expectedValue, tc.GetString("Key.Var1VarVar", ""))
})

t.Run("detects uppercase env variables", func(t *testing.T) {
t.Setenv("SOMEENVVARIABLE", expectedValue)
tc := New()
Expand Down Expand Up @@ -671,3 +681,46 @@ func TestConfigLocking(t *testing.T) {

require.NoError(t, g.Wait())
}

func TestConfigLoad(t *testing.T) {
// create a temporary file:
f, err := os.CreateTemp("", "*config.yaml")
require.NoError(t, err)
defer os.Remove(f.Name())

t.Setenv("CONFIG_PATH", f.Name())
c := New()
require.NoError(t, err)

t.Run("successfully loaded config file", func(t *testing.T) {
configFile, err := c.ConfigFileUsed()
require.NoError(t, err)
require.Equal(t, f.Name(), configFile)
})

err = os.Remove(f.Name())
require.NoError(t, err)

t.Run("attempt to load non-existent config file", func(t *testing.T) {
c := New()
configFile, err := c.ConfigFileUsed()
require.Error(t, err)
require.Equal(t, f.Name(), configFile)
})

t.Run("dot env error", func(t *testing.T) {
c := New()
err := c.DotEnvLoaded()
require.Error(t, err)
})

t.Run("dot env found", func(t *testing.T) {
c := New()
f, err := os.Create(".env")
require.NoError(t, err)
defer os.Remove(f.Name())

err = c.DotEnvLoaded()
require.Error(t, err)
})
}
38 changes: 19 additions & 19 deletions config/load.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@ package config

import (
"fmt"
"os"
"reflect"
"strings"
"time"

"github.com/fsnotify/fsnotify"
Expand All @@ -17,9 +15,7 @@ func (c *Config) load() {
c.hotReloadableConfig = make(map[string][]*configValue)
c.envs = make(map[string]string)

if err := godotenv.Load(); err != nil && !isTest() {
fmt.Println("INFO: No .env file found.")
}
c.godotEnvErr = godotenv.Load()

configPath := getEnv("CONFIG_PATH", "./config/config.yaml")

Expand All @@ -28,11 +24,12 @@ func (c *Config) load() {
bindLegacyEnv(v)

v.SetConfigFile(configPath)
err := v.ReadInConfig() // Find and read the config file
// Don't panic if config.yaml is not found or error with parsing. Use the default config values instead
if err != nil && !isTest() {
fmt.Printf("[Config] :: Failed to parse config file from path %q, using default values: %v\n", configPath, err)
}

// Find and read the config file
// If config.yaml is not found or error with parsing. Use the default config values instead
c.configPathErr = v.ReadInConfig()
c.configPath = v.ConfigFileUsed()

v.OnConfigChange(func(e fsnotify.Event) {
c.onConfigChange()
})
Expand All @@ -41,6 +38,18 @@ func (c *Config) load() {
c.v = v
}

// ConfigFileUsed returns the file used to load the config.
// If we failed to load the config file, it also returns an error.
func (c *Config) ConfigFileUsed() (string, error) {
return c.configPath, c.configPathErr
}

// DotEnvLoaded returns an error if there was an error loading the .env file.
// It returns nil otherwise.
func (c *Config) DotEnvLoaded() error {
return c.godotEnvErr
}

func (c *Config) onConfigChange() {
defer func() {
if r := recover(); r != nil {
Expand Down Expand Up @@ -230,12 +239,3 @@ func mapDeepEqual[K comparable, V any](a, b map[K]V) bool {
}
return true
}

func isTest() bool {
for _, arg := range os.Args {
if strings.HasPrefix(arg, "-test.") {
return true
}
}
return false
}
Loading