Allow overriding of configuration parameters using environment variables
This allows all config variable to be set via environment variables using a similar naming convention for the toml config. For example, to change the HTTP API port using the config, you would set: [http] bind-address = ":8086" To change it with an environment variable, you would use: HTTP_BIND_ADDRESS=":8086" influxd The section name is used as the env variable prefix and the config key name is the suffix. The only change to the config name is that "-" should be replaced with "_" to avoid shell interpretation issues. This makes it much easier to configure docker instances within a docker container or adhoc instances at the command-line. For slice config sections like graphite, you can currently only override the first entry since the default config only has 1 entry. To do that use, GRAPHITE_0 as the prefix. You cannot currently add new entries like GRAPHITE_1. A future PR might address this issue. The environment variable values should be the same as the config values. The order that configuration values are applied is as follows: * Default config * Config file * Environment variables * Command-line arguments Fixes #3246pull/3592/head
parent
15ab23c17e
commit
6f9c18fe95
|
@ -78,6 +78,11 @@ func (cmd *Command) Run(args ...string) error {
|
|||
return fmt.Errorf("parse config: %s", err)
|
||||
}
|
||||
|
||||
// Apply any environment variables on top of the parsed config
|
||||
if err := config.ApplyEnvOverrides(); err != nil {
|
||||
return fmt.Errorf("apply env config: %v", err)
|
||||
}
|
||||
|
||||
// Override config hostname if specified in the command line args.
|
||||
if options.Hostname != "" {
|
||||
config.Meta.Hostname = options.Hostname
|
||||
|
|
|
@ -6,6 +6,10 @@ import (
|
|||
"os"
|
||||
"os/user"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/influxdb/influxdb/cluster"
|
||||
"github.com/influxdb/influxdb/meta"
|
||||
|
@ -112,3 +116,104 @@ func (c *Config) Validate() error {
|
|||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Config) ApplyEnvOverrides() error {
|
||||
return c.applyEnvOverrides("", reflect.ValueOf(c))
|
||||
}
|
||||
|
||||
func (c *Config) applyEnvOverrides(prefix string, spec reflect.Value) error {
|
||||
// If we have a pointer, dereference it
|
||||
s := spec
|
||||
if spec.Kind() == reflect.Ptr {
|
||||
s = spec.Elem()
|
||||
}
|
||||
|
||||
typeOfSpec := s.Type()
|
||||
for i := 0; i < s.NumField(); i++ {
|
||||
f := s.Field(i)
|
||||
// Get the toml tag to determine what env var name to use
|
||||
configName := typeOfSpec.Field(i).Tag.Get("toml")
|
||||
// Replace hyphens with underscores to avoid issues with shells
|
||||
configName = strings.Replace(configName, "-", "_", -1)
|
||||
fieldName := typeOfSpec.Field(i).Name
|
||||
|
||||
// Skip any fields that we cannot set
|
||||
if f.CanSet() || f.Kind() == reflect.Slice {
|
||||
|
||||
// Use the upper-case prefix and toml name for the env var
|
||||
key := strings.ToUpper(configName)
|
||||
if prefix != "" {
|
||||
key = strings.ToUpper(fmt.Sprintf("%s_%s", prefix, configName))
|
||||
}
|
||||
value := os.Getenv(key)
|
||||
|
||||
// If the type is s slice, apply to each using the index as a suffix
|
||||
// e.g. GRAPHITE_0
|
||||
if f.Kind() == reflect.Slice || f.Kind() == reflect.Array {
|
||||
for i := 0; i < f.Len(); i++ {
|
||||
if err := c.applyEnvOverrides(fmt.Sprintf("%s_%d", key, i), f.Index(i)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// If it's a sub-config, recursively apply
|
||||
if f.Kind() == reflect.Struct || f.Kind() == reflect.Ptr {
|
||||
if err := c.applyEnvOverrides(key, f); err != nil {
|
||||
return err
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// Skip any fields we don't have a value to set
|
||||
if value == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
switch f.Kind() {
|
||||
case reflect.String:
|
||||
f.SetString(value)
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
|
||||
var intValue int64
|
||||
|
||||
// Handle toml.Duration
|
||||
if f.Type().Name() == "Duration" {
|
||||
dur, err := time.ParseDuration(value)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to apply %v to %v using type %v and value '%v'", key, fieldName, f.Type().String(), value)
|
||||
}
|
||||
intValue = dur.Nanoseconds()
|
||||
} else {
|
||||
var err error
|
||||
intValue, err = strconv.ParseInt(value, 0, f.Type().Bits())
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to apply %v to %v using type %v and value '%v'", key, fieldName, f.Type().String(), value)
|
||||
}
|
||||
}
|
||||
|
||||
f.SetInt(intValue)
|
||||
case reflect.Bool:
|
||||
boolValue, err := strconv.ParseBool(value)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to apply %v to %v using type %v and value '%v'", key, fieldName, f.Type().String(), value)
|
||||
|
||||
}
|
||||
f.SetBool(boolValue)
|
||||
case reflect.Float32, reflect.Float64:
|
||||
floatValue, err := strconv.ParseFloat(value, f.Type().Bits())
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to apply %v to %v using type %v and value '%v'", key, fieldName, f.Type().String(), value)
|
||||
|
||||
}
|
||||
f.SetFloat(floatValue)
|
||||
default:
|
||||
if err := c.applyEnvOverrides(key, f); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue