ilyakaznacheev / cleanenv

✨Clean and minimalistic environment configuration reader for Golang
MIT License
1.67k stars 116 forks source link

How about adding the ability to use dynamic values in a config file? #112

Open gocruncher opened 1 year ago

gocruncher commented 1 year ago

Example:

psql:
      host: ${DB_HOST}
      port: ${DB_PORT}
      user: ${DB_USER}
      password: ${DB_PASSWORD}
      database: default

where DB_HOST,DB_PORT,... are the environment variables

tahacodes commented 1 year ago

Hi there

So if i understand this currently, you need to load a config file into your application but some of the variables need to be set by environment variables.

I had a similar experience and this little trick may be helpful:

if errConfig := cleanenv.ReadConfig("config.yaml", configuration); errConfig != nil {
    if errEnv := cleanenv.ReadEnv(configuration); errEnv != nil {
        return nil, multierr.Combine(errConfig, errEnv)
    }
}

Basically you can load your configs from a config.yaml file but you can also load some other variables from env after that and you can use the uber's "multierr" package to combine ReadConfig and ReadEnv errors together. Here's an example of the struct that i used:

type Configuration struct {
    Dialer struct {
        UpstreamTimeout    string `env:"DIALER_UPSTREAM_TIMEOUT" env-default:"2s"`
        KeepAliveDuration  string `env:"DIALER_KEEPALIVE_DURATION" env-default:"5s"`
        InsecureSkipVerify bool   `env:"DIALER_INSECURE_SKIP_VERIFY" env-default:"false"`
    }

    DNS struct {
        Resolver string `env:"DNS_RESOLVER" env-default:"8.8.8.8:53"`
        Protocol string `env:"DNS_PROTOCOL" env-default:"udp"`
        Timeout  string `env:"DNS_TIMEOUT" env-default:"10ms"`
    }

    Log struct {
        LogLevel      string `env:"LOG_LEVEL" env-default:"info"`
        SentryDSN     string `env:"SENTRY_DSN"`
        TimePrecision uint   `env:"LOG_TIMEPRECISION" env-default:"3"`
    }

    Storage struct {
        Endpoints []struct {
            Name         string `yaml:"name"`
            Endpoint     string `yaml:"endpoint"`
        } `yaml:"endpoints"`

        Key struct {
            ServiceAlpha struct {
                Access string `env:"STORAGE_ALPHA_ACCESS"`
                Secret string `env:"STORAGE_ALPHA_SECRET"`
            }
            ServiceBeta struct {
                Access string `env:"STORAGE_BETA_ACCESS"`
                Secret string `env:"STORAGE_BETA_SECRET"`
            }
        }
    } `yaml:"storage"`
}

As you can see i have a combination of yaml configs and env configs.

Here's the config.yaml as well:

storage:
  endpoints:
    - name: Main
      endpoint: https://s3.us-east-2.amazonaws.com
    - name: Fallback
      endpoint: https://s3.us-west-2.amazonaws.com

Hope this helps.