Skip to content

Commit

Permalink
Allow overriding of configuration parameters using environment variables
Browse files Browse the repository at this point in the history
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 #3246
  • Loading branch information
jwilder committed Aug 7, 2015
1 parent 15ab23c commit 6f9c18f
Show file tree
Hide file tree
Showing 2 changed files with 110 additions and 0 deletions.
5 changes: 5 additions & 0 deletions cmd/influxd/run/command.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
105 changes: 105 additions & 0 deletions cmd/influxd/run/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ import (
"os"
"os/user"
"path/filepath"
"reflect"
"strconv"
"strings"
"time"

"github.com/influxdb/influxdb/cluster"
"github.com/influxdb/influxdb/meta"
Expand Down Expand Up @@ -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
}

0 comments on commit 6f9c18f

Please sign in to comment.