diff --git a/go.mod b/go.mod index 3518bd8..ff974c9 100644 --- a/go.mod +++ b/go.mod @@ -82,6 +82,7 @@ require ( github.com/jackc/pgx/v4 v4.18.3 // indirect github.com/jacobweinstock/iamt v0.0.0-20230502042727-d7cdbe67d9ef // indirect github.com/jacobweinstock/registrar v0.4.7 // indirect + github.com/jeremywohl/flatten v1.0.1 // indirect github.com/jmoiron/sqlx v1.4.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/compress v1.17.9 // indirect diff --git a/go.sum b/go.sum index b39d758..7b34e8e 100644 --- a/go.sum +++ b/go.sum @@ -472,6 +472,8 @@ github.com/jacobweinstock/iamt v0.0.0-20230502042727-d7cdbe67d9ef h1:G4k02HGmBUf github.com/jacobweinstock/iamt v0.0.0-20230502042727-d7cdbe67d9ef/go.mod h1:FgmiLTU6cJewV4Xgrq6m5o8CUlTQOJtqzaFLGA0mG+E= github.com/jacobweinstock/registrar v0.4.7 h1:s4dOExccgD+Pc7rJC+f3Mc3D+NXHcXUaOibtcEsPxOc= github.com/jacobweinstock/registrar v0.4.7/go.mod h1:PWmkdGFG5/ZdCqgMo7pvB3pXABOLHc5l8oQ0sgmBNDU= +github.com/jeremywohl/flatten v1.0.1 h1:LrsxmB3hfwJuE+ptGOijix1PIfOoKLJ3Uee/mzbgtrs= +github.com/jeremywohl/flatten v1.0.1/go.mod h1:4AmD/VxjWcI5SRB0n6szE2A6s2fsNHDLO0nAlMHgfLQ= github.com/jmoiron/sqlx v1.4.0 h1:1PLqN7S1UYp5t4SrVVnt4nUVNemrDAtxlulVe+Qgm3o= github.com/jmoiron/sqlx v1.4.0/go.mod h1:ZrZ7UsYB/weZdl2Bxg6jCRO9c3YHl8r3ahlKmRT4JLY= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= diff --git a/internal/app/config.go b/internal/app/config.go index 89fd03a..3f572c5 100644 --- a/internal/app/config.go +++ b/internal/app/config.go @@ -2,14 +2,14 @@ package app import ( "os" - "reflect" "strings" + "github.com/jeremywohl/flatten" "github.com/metal-toolbox/flipflop/internal/model" "github.com/metal-toolbox/flipflop/internal/store/fleetdb" "github.com/metal-toolbox/rivets/events" + "github.com/mitchellh/mapstructure" "github.com/pkg/errors" - "github.com/spf13/viper" ) var ( @@ -56,7 +56,7 @@ func (a *App) LoadConfiguration(cfgFilePath, loglevel string) error { cfg := &Configuration{} a.Config = cfg - err := setConfigDefaults(a.v, cfg, "", ".") + err := a.envBindVars() if err != nil { return err } @@ -130,40 +130,29 @@ func (cfg *Configuration) validate() error { return nil } -// setConfigDefaults sets all values in the struct to empty. This is to get around a weird quirk of viper. -// Viper will not override from a ENV variable if the value isnt set first. -// Resulting in ENV variables never getting loaded in if they arent in the config.yaml, even empty in the config. -// https://github.com/spf13/viper/issues/584#issuecomment-1210957041 -func setConfigDefaults(v *viper.Viper, i interface{}, parent, delim string) error { - // Retrieve the underlying type of variable `i`. - r := reflect.TypeOf(i) - - // If `i` is of type pointer, retrieve the referenced type. - if r.Kind() == reflect.Ptr { - r = r.Elem() +// envBindVars binds environment variables to the struct +// without a configuration file being unmarshalled, +// this is a workaround for a viper bug, +// +// This can be replaced by the solution in https://github.com/spf13/viper/pull/1429 +// once that PR is merged. +func (a *App) envBindVars() error { + envKeysMap := map[string]interface{}{} + if err := mapstructure.Decode(a.Config, &envKeysMap); err != nil { + return err } - // Iterate over each field for the type. By default, there is a single field. - for i := 0; i < r.NumField(); i++ { - // Retrieve the current field and get the `mapstructure` tag. - f := r.Field(i) - env := f.Tag.Get("mapstructure") - - // By default, set the key to the current tag value. If a parent value was passed in - // prepend the parent and the delimiter. - if parent != "" { - env = parent + delim + env - } + // Flatten nested conf map + flat, err := flatten.Flatten(envKeysMap, "", flatten.DotStyle) + if err != nil { + return errors.Wrap(err, "Unable to flatten config") + } - // If it's a struct, only bind properties. - if f.Type.Kind() == reflect.Struct { - t := reflect.New(f.Type).Elem().Interface() - _ = setConfigDefaults(v, t, env, delim) - continue + for k := range flat { + if err := a.v.BindEnv(k); err != nil { + return errors.Wrap(ErrConfig, "env var bind error: "+err.Error()) } - - // Bind the environment variable. - v.SetDefault(env, nil) } - return v.Unmarshal(i) + + return nil }