diff --git a/pkg/strvals/parser.go b/pkg/strvals/parser.go index dae949d8e..006bed5f4 100644 --- a/pkg/strvals/parser.go +++ b/pkg/strvals/parser.go @@ -24,6 +24,7 @@ import ( "strings" "github.com/ghodss/yaml" + "reflect" ) // ErrNotList indicates that a non-list was treated as a list. @@ -172,7 +173,24 @@ func (t *parser) key(data map[string]interface{}) error { // First, create or find the target map. inner := map[string]interface{}{} if _, ok := data[string(k)]; ok { - inner = data[string(k)].(map[string]interface{}) + // when reading nested object yamls, the unmarshal will create a map[interface{}]interface{}) + // below logic tries to account for that and attempts to cast the key into a string again + if simpleStringMapCast, ok := data[string(k)].(map[string]interface{}); ok { + inner = simpleStringMapCast + } else { + if interfaceKeyCasted, ok := data[string(k)].(map[interface{}]interface{}); ok { + for key, val := range interfaceKeyCasted { + keyString, casted := key.(string) + if !casted { + return fmt.Errorf("couldn't cast map key %q to string for value key %q", key, string(k)) + } + inner[keyString] = val + } + } else { + return fmt.Errorf("don't know how to handle map type %q for key %q", + reflect.TypeOf(data[string(k)]), string(k)) + } + } } // Recurse diff --git a/pkg/strvals/parser_test.go b/pkg/strvals/parser_test.go index 22f0e753a..7dc3c3273 100644 --- a/pkg/strvals/parser_test.go +++ b/pkg/strvals/parser_test.go @@ -19,6 +19,7 @@ import ( "testing" "github.com/ghodss/yaml" + "io/ioutil" ) func TestSetIndex(t *testing.T) { @@ -348,7 +349,7 @@ func TestParseSet(t *testing.T) { } } -func TestParseInto(t *testing.T) { +func TestParseIntoHappyPath(t *testing.T) { got := map[string]interface{}{ "outer": map[string]interface{}{ "inner1": "overwrite", @@ -365,10 +366,45 @@ func TestParseInto(t *testing.T) { }, } - if err := ParseInto(input, got); err != nil { + checkParserOutput(input, got, t, expect) +} + +func TestParseIntoNestedInterface(t *testing.T) { + currentMap := map[string]interface{}{} + bytes, err := ioutil.ReadFile("testdata/parser_val_nested.yaml") + if err != nil { + t.Fatal(err) + } + + if err := yaml.Unmarshal(bytes, ¤tMap); err != nil { t.Fatal(err) } + input := "toplevel.flatlevel1=overwritefl1" + + expect := map[string]interface{}{ + "toplevel": map[string]interface{}{ + "env": []interface{}{ + map[string]interface{}{ + "key": "abckey", + "value": "abcval", + }, map[string]interface{}{ + "key": "defkey", + "value": "defval", + }, + }, + "flatlevel1": "overwritefl1", + "flatlevel2": "def", + }, + } + + checkParserOutput(input, currentMap, t, expect) +} + +func checkParserOutput(input string, got map[string]interface{}, t *testing.T, expect map[string]interface{}) { + if err := ParseInto(input, got); err != nil { + t.Fatal(err) + } y1, err := yaml.Marshal(expect) if err != nil { t.Fatal(err) @@ -377,7 +413,6 @@ func TestParseInto(t *testing.T) { if err != nil { t.Fatalf("Error serializing parsed value: %s", err) } - if string(y1) != string(y2) { t.Errorf("%s: Expected:\n%s\nGot:\n%s", input, y1, y2) } diff --git a/pkg/strvals/testdata/parser_val_nested.yaml b/pkg/strvals/testdata/parser_val_nested.yaml new file mode 100644 index 000000000..c7d7ce116 --- /dev/null +++ b/pkg/strvals/testdata/parser_val_nested.yaml @@ -0,0 +1,8 @@ +toplevel: + env: + - key: abckey + value: abcval + - key: defkey + value: defval + flatlevel1: abc + flatlevel2: def