Skip to content

Commit

Permalink
config.go: refactor to simplify structure and fix support for array e…
Browse files Browse the repository at this point in the history
…lements

The refactor splits the handling of structs into a separate function and adds
a possibly empty fieldDesc parameter to applyEnvOverrides to allow applyEnvOverrides
to handle simple values as well as structs.

Signed-off-by: Jon Seymour <[email protected]>
  • Loading branch information
jonseymour committed Mar 17, 2016
1 parent 72ad726 commit eab25be
Showing 1 changed file with 60 additions and 63 deletions.
123 changes: 60 additions & 63 deletions cmd/kapacitord/run/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -152,21 +152,74 @@ func (c *Config) Validate() error {
}

func (c *Config) ApplyEnvOverrides() error {
return c.applyEnvOverrides("KAPACITOR", reflect.ValueOf(c))
return c.applyEnvOverrides("KAPACITOR", "", reflect.ValueOf(c))
}

func (c *Config) applyEnvOverrides(prefix string, spec reflect.Value) error {
func (c *Config) applyEnvOverrides(prefix string, fieldDesc string, spec reflect.Value) error {
// If we have a pointer, dereference it
s := spec
if spec.Kind() == reflect.Ptr {
s = spec.Elem()
}

// Make sure we have struct
var value string

if s.Kind() != reflect.Struct {
return nil
value = os.Getenv(prefix)
// Skip any fields we don't have a value to set
if value == "" {
return nil
}

if fieldDesc != "" {
fieldDesc = " to " + fieldDesc
}
}

switch s.Kind() {
case reflect.String:
s.SetString(value)
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:

var intValue int64

// Handle toml.Duration
if s.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'", prefix, fieldDesc, s.Type().String(), value)
}
intValue = dur.Nanoseconds()
} else {
var err error
intValue, err = strconv.ParseInt(value, 0, s.Type().Bits())
if err != nil {
return fmt.Errorf("failed to apply %v%v using type %v and value '%v'", prefix, fieldDesc, s.Type().String(), value)
}
}

s.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'", prefix, fieldDesc, s.Type().String(), value)

}
s.SetBool(boolValue)
case reflect.Float32, reflect.Float64:
floatValue, err := strconv.ParseFloat(value, s.Type().Bits())
if err != nil {
return fmt.Errorf("failed to apply %v%v using type %v and value '%v'", prefix, fieldDesc, s.Type().String(), value)

}
s.SetFloat(floatValue)
case reflect.Struct:
c.applyEnvOverridesToStruct(prefix, s)
}
return nil
}

func (c *Config) applyEnvOverridesToStruct(prefix string, s reflect.Value) error {
typeOfSpec := s.Type()
for i := 0; i < s.NumField(); i++ {
f := s.Field(i)
Expand All @@ -184,73 +237,17 @@ func (c *Config) applyEnvOverrides(prefix string, spec reflect.Value) error {
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 {
if err := c.applyEnvOverrides(fmt.Sprintf("%s_%d", key, i), fieldName, 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
}
} else if err := c.applyEnvOverrides(key, fieldName, f); err != nil {
return err
}
}
}
Expand Down

0 comments on commit eab25be

Please sign in to comment.