Skip to content

Commit

Permalink
Merge branch 'jss-348-apply-env-to-array-elements'
Browse files Browse the repository at this point in the history
  • Loading branch information
jonseymour committed Mar 17, 2016
2 parents 3b05b8a + d6c32c1 commit 8f2928b
Showing 1 changed file with 84 additions and 72 deletions.
156 changes: 84 additions & 72 deletions cmd/kapacitord/run/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -215,97 +215,109 @@ func (c *Config) applyEnvOverrides(prefix string, spec reflect.Value) error {
s = spec.Elem()
}

// Make sure we have struct
if s.Kind() != reflect.Struct {
return nil
}
apply := func(key string, fieldDesc string, f reflect.Value) error {
value := os.Getenv(key)
// Skip any fields we don't have a value to set
if value == "" {
return nil
}

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 fieldDesc != "" {
fieldDesc = " to " + fieldDesc
}

// 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
}
switch f.Kind() {
case reflect.String:
f.SetString(value)
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:

// 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
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%v using type %v and value '%v'", key, fieldDesc, 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%v using type %v and value '%v'", key, fieldDesc, f.Type().String(), value)
}
continue
}

// Skip any fields we don't have a value to set
if value == "" {
continue
f.SetInt(intValue)
case reflect.Bool:
boolValue, err := strconv.ParseBool(value)
if err != nil {
return fmt.Errorf("failed to apply %v%v using type %v and value '%v'", key, fieldDesc, 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%v using type %v and value '%v'", key, fieldDesc, f.Type().String(), value)

switch f.Kind() {
case reflect.String:
f.SetString(value)
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
}
f.SetFloat(floatValue)
default:
if err := c.applyEnvOverrides(key, f); err != nil {
return err
}
}
return nil
}

var intValue int64
switch s.Kind() {
case reflect.String, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Bool, reflect.Float32, reflect.Float64:
return apply(prefix, "", spec)
case reflect.Struct:
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))
}

// 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)
// 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
}

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)

// 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
}
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 {
if err := apply(key, fieldName, f); err != nil {
return err
}
}
}
}

return nil
}

0 comments on commit 8f2928b

Please sign in to comment.