Browse Source

Fix bugs: Wrong type of object failed silently and object graph with cycles caused stack overflow

master
forestjohnson 5 years ago
parent
commit
995c531e55
2 changed files with 69 additions and 27 deletions
  1. +18
    -9
      override.go
  2. +51
    -18
      override_test.go

+ 18
- 9
override.go View File

@ -1,6 +1,7 @@
package influxStyleEnvOverride
import (
"errors"
"fmt"
"os"
"reflect"
@ -20,21 +21,29 @@ func (this environmentVariableKeyValueRetriever) get(key string) string {
}
// ApplyEnvOverrides apply any convention-driven environment varibles on top of the original object.
func ApplyInfluxStyleEnvOverrides(prefix string, originalObject *interface{}) error {
return applyEnvOverrides(environmentVariableKeyValueRetriever{}, prefix, reflect.ValueOf(originalObject))
func ApplyInfluxStyleEnvOverrides(prefix string, reflectedStruct reflect.Value) error {
return applyEnvOverrides(environmentVariableKeyValueRetriever{}, prefix, reflectedStruct, 0)
}
func applyEnvOverrides(kv keyValueRetriever, prefix string, spec reflect.Value) error {
func applyEnvOverrides(kv keyValueRetriever, prefix string, spec reflect.Value, depth int) error {
if depth > 100 {
return errors.New("failed to apply environment overrides: recursive overflow. Your object must be a DAG.")
}
// If we have a pointer, dereference it
s := spec
if spec.Kind() == reflect.Ptr {
for s.Kind() == reflect.Ptr {
s = spec.Elem()
}
// Make sure we have struct
if s.Kind() != reflect.Struct {
return nil
if depth == 0 {
return fmt.Errorf("failed to apply environment overrides: expected a Struct, but a %v was passed", s.Kind())
} else {
return nil
}
}
typeOfSpec := s.Type()
@ -62,7 +71,7 @@ func applyEnvOverrides(kv keyValueRetriever, prefix string, spec reflect.Value)
// If it's a sub-config, recursively apply
if f.Kind() == reflect.Struct || f.Kind() == reflect.Ptr {
if err := applyEnvOverrides(kv, key, f); err != nil {
if err := applyEnvOverrides(kv, key, f, depth+1); err != nil {
return err
}
continue
@ -86,10 +95,10 @@ func applyEnvOverrides(kv keyValueRetriever, prefix string, spec reflect.Value)
// e.g. GRAPHITE_0
if f.Kind() == reflect.Slice || f.Kind() == reflect.Array {
for i := 0; i < f.Len(); i++ {
if err := applyEnvOverrides(kv, key, f.Index(i)); err != nil {
if err := applyEnvOverrides(kv, key, f.Index(i), depth+1); err != nil {
return err
}
if err := applyEnvOverrides(kv, fmt.Sprintf("%s_%d", key, i), f.Index(i)); err != nil {
if err := applyEnvOverrides(kv, fmt.Sprintf("%s_%d", key, i), f.Index(i), depth+1); err != nil {
return err
}
}
@ -148,7 +157,7 @@ func applyEnvOverrides(kv keyValueRetriever, prefix string, spec reflect.Value)
}
f.SetFloat(floatValue)
default:
if err := applyEnvOverrides(kv, key, f); err != nil {
if err := applyEnvOverrides(kv, key, f, depth+1); err != nil {
return err
}
}


+ 51
- 18
override_test.go View File

@ -17,6 +17,7 @@ type ExampleSubObject struct {
Integer int
B string
unexported int
Other *ExampleSubObject
Thing interface{}
Things []interface{}
}
@ -86,32 +87,64 @@ func TestApplyEnvOverridesWithUnsettableField(t *testing.T) {
toTest.execute(t)
}
// Note currently this test is expected to fail. Haven't implemented additional slice elements yet.
func TestApplyEnvOverridesWithNonExistentObject(t *testing.T) {
toTest := testCase{
mutateExampleObject: func(example *ExampleObject) {
example.Others = append(example.Others, ExampleSubObject{
B: "asd2",
})
},
environment: map[string]string{
"TEST_OTHERS_1_B": "asd2",
},
// Note currently this test fails. Haven't implemented additional slice elements yet.
//func TestApplyEnvOverridesWithNonExistentObject(t *testing.T) {
// toTest := testCase{
// mutateExampleObject: func(example *ExampleObject) {
// example.Others = append(example.Others, ExampleSubObject{
// B: "asd2",
// })
// },
// environment: map[string]string{
// "TEST_OTHERS_1_B": "asd2",
// },
// }
// toTest.execute(t)
//}
func TestApplyEnvOverridesWithWrongType(t *testing.T) {
exampleObjectUnderTest := newExampleObject()
var exampleObjectUnderTestInterface interface{}
exampleObjectUnderTestInterface = exampleObjectUnderTest
err := applyEnvOverrides(mockKeyValueRetriever{}, "TEST", reflect.ValueOf(&exampleObjectUnderTestInterface), 0)
expectedError := "expected a Struct"
actualErrorDisplay := "nil"
if err != nil {
actualErrorDisplay = err.Error()
}
if !strings.Contains(actualErrorDisplay, expectedError) {
t.Errorf("Expected Error: %s, Actual Error: %s", expectedError, actualErrorDisplay)
}
}
toTest.execute(t)
func TestApplyEnvOverridesWithNonDAG(t *testing.T) {
exampleObjectUnderTest := newExampleObject()
exampleObjectUnderTest.Other.Other = exampleObjectUnderTest.Other
err := applyEnvOverrides(mockKeyValueRetriever{}, "TEST", reflect.ValueOf(exampleObjectUnderTest), 0)
expectedError := "recursive overflow"
actualErrorDisplay := "nil"
if err != nil {
actualErrorDisplay = err.Error()
}
if !strings.Contains(actualErrorDisplay, expectedError) {
t.Errorf("Expected Error: %s, Actual Error: %s", expectedError, actualErrorDisplay)
}
}
func (this testCase) execute(t *testing.T) {
ExampleObjectUnderTest := newExampleObject()
ExampleObjectForComparison := newExampleObject()
exampleObjectUnderTest := newExampleObject()
exampleObjectForComparison := newExampleObject()
exampleKeyValueRetriever := mockKeyValueRetriever{
KeyValues: this.environment,
}
err := applyEnvOverrides(exampleKeyValueRetriever, "TEST", reflect.ValueOf(&ExampleObjectUnderTest))
err := applyEnvOverrides(exampleKeyValueRetriever, "TEST", reflect.ValueOf(&exampleObjectUnderTest), 0)
this.mutateExampleObject(&ExampleObjectForComparison)
this.mutateExampleObject(&exampleObjectForComparison)
if err != nil || this.expectedError != "" {
actualError := ""
@ -128,11 +161,11 @@ func (this testCase) execute(t *testing.T) {
}
}
jsonA, err := json.MarshalIndent(ExampleObjectUnderTest, "", " ")
jsonA, err := json.MarshalIndent(exampleObjectUnderTest, "", " ")
if err != nil {
t.Error(err)
}
jsonB, err := json.MarshalIndent(ExampleObjectForComparison, "", " ")
jsonB, err := json.MarshalIndent(exampleObjectForComparison, "", " ")
if err != nil {
t.Error(err)
}


Loading…
Cancel
Save