diff --git a/config/config_test.go b/config/config_test.go index 8e944c54..cb7bbbf0 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -153,6 +153,52 @@ func TestFilterDottedKeys(t *testing.T) { assert.Equal(expectedRest, rest) } +func TestUnmarshalOrReturnString(t *testing.T) { + assert := require.New(t) + + assert.Equal("ab", unmarshalOrReturnString("ab")) + assert.Equal("a\nb", unmarshalOrReturnString("a\nb")) + assert.Equal("a\n", unmarshalOrReturnString("a\n")) + assert.Equal("\nb", unmarshalOrReturnString("\nb")) + assert.Equal("a,b", unmarshalOrReturnString("a,b")) + assert.Equal("a,", unmarshalOrReturnString("a,")) + assert.Equal(",b", unmarshalOrReturnString(",b")) + + assert.Equal(int64(10), unmarshalOrReturnString("10")) + assert.Equal(true, unmarshalOrReturnString("true")) + assert.Equal(false, unmarshalOrReturnString("false")) + + assert.Equal([]interface{}{"a"}, unmarshalOrReturnString("[a]")) + assert.Equal([]interface{}{"a"}, unmarshalOrReturnString("[\"a\"]")) + + assert.Equal([]interface{}{"a,"}, unmarshalOrReturnString("[\"a,\"]")) + assert.Equal([]interface{}{" a, "}, unmarshalOrReturnString("[\" a, \"]")) + assert.Equal([]interface{}{",a"}, unmarshalOrReturnString("[\",a\"]")) + assert.Equal([]interface{}{" ,a "}, unmarshalOrReturnString("[\" ,a \"]")) + + assert.Equal([]interface{}{"a\n"}, unmarshalOrReturnString("[\"a\n\"]")) + assert.Equal([]interface{}{" a\n "}, unmarshalOrReturnString("[\" a\n \"]")) + assert.Equal([]interface{}{"\na"}, unmarshalOrReturnString("[\"\na\"]")) + assert.Equal([]interface{}{" \na "}, unmarshalOrReturnString("[\" \na \"]")) + + assert.Equal([]interface{}{"a", "b"}, unmarshalOrReturnString("[a,b]")) + assert.Equal([]interface{}{"a", "b"}, unmarshalOrReturnString("[\"a\",\"b\"]")) + + assert.Equal([]interface{}{"a,", "b"}, unmarshalOrReturnString("[\"a,\",b]")) + assert.Equal([]interface{}{"a", ",b"}, unmarshalOrReturnString("[a,\",b\"]")) + assert.Equal([]interface{}{" a, ", " ,b "}, unmarshalOrReturnString("[\" a, \",\" ,b \"]")) + + assert.Equal([]interface{}{"a\n", "b"}, unmarshalOrReturnString("[\"a\n\",b]")) + assert.Equal([]interface{}{"a", "\nb"}, unmarshalOrReturnString("[a,\"\nb\"]")) + assert.Equal([]interface{}{" a\n ", " \nb "}, unmarshalOrReturnString("[\" a\n \",\" \nb \"]")) + + assert.Equal([]interface{}{"a", int64(10)}, unmarshalOrReturnString("[a,10]")) + assert.Equal([]interface{}{int64(10), "a"}, unmarshalOrReturnString("[10,a]")) + + assert.Equal([]interface{}{"a", true}, unmarshalOrReturnString("[a,true]")) + assert.Equal([]interface{}{false, "a"}, unmarshalOrReturnString("[false,a]")) +} + func TestParseCmdline(t *testing.T) { assert := require.New(t) @@ -161,18 +207,20 @@ func TestParseCmdline(t *testing.T) { "rescue": true, "key1": "value1", "key2": "value2", - "keyArray": []interface{}{"1", "2"}, + "keyArray": []interface{}{int64(1), int64(2)}, "obj1": map[interface{}]interface{}{ "key3": "3value", "obj2": map[interface{}]interface{}{ "key4": true, }, }, - "key5": 5, + "key5": int64(5), + "key6": "a,b", + "key7": "a\nb", }, } - actual := parseCmdline("a b rancher.rescue rancher.keyArray=[1,2] rancher.key1=value1 c rancher.key2=value2 rancher.obj1.key3=3value rancher.obj1.obj2.key4 rancher.key5=5") + actual := parseCmdline("a b rancher.rescue rancher.keyArray=[1,2] rancher.key1=value1 c rancher.key2=value2 rancher.obj1.key3=3value rancher.obj1.obj2.key4 rancher.key5=5 rancher.key6=a,b rancher.key7=a\nb") assert.Equal(expected, actual) } diff --git a/config/data_funcs.go b/config/data_funcs.go index 37328a29..2ab8bee1 100644 --- a/config/data_funcs.go +++ b/config/data_funcs.go @@ -2,10 +2,9 @@ package config import ( log "github.com/Sirupsen/logrus" + yaml "github.com/cloudfoundry-incubator/candiedyaml" "github.com/rancher/os/util" - "regexp" - "strconv" "strings" ) @@ -87,7 +86,7 @@ func getOrSetVal(args string, data map[interface{}]interface{}, value interface{ // Reached end, set the value if last && value != nil { if s, ok := value.(string); ok { - value = DummyMarshall(s) + value = unmarshalOrReturnString(s) } t[part] = value @@ -121,28 +120,36 @@ func getOrSetVal(args string, data map[interface{}]interface{}, value interface{ return "", tData } -func DummyMarshall(value string) interface{} { - if strings.HasPrefix(value, "[") && strings.HasSuffix(value, "]") { - result := []interface{}{} - for _, i := range strings.Split(value[1:len(value)-1], ",") { - result = append(result, strings.TrimSpace(i)) +// YAML parsers will remove newlines, but we'd like to keep those +// replace newlines with magicString, and then undo after unmarshaling +var magicString = "9XsJcx6dR5EERYCC" + +func reverseReplacement(result interface{}) interface{} { + switch val := result.(type) { + case map[interface{}]interface{}: + for k, v := range val { + val[k] = reverseReplacement(v) } - return result + return val + case []interface{}: + for i, item := range val { + val[i] = reverseReplacement(item) + } + return val + case string: + return strings.Replace(val, magicString, "\n", -1) } - if value == "true" { - return true - } else if value == "false" { - return false - } else if ok, _ := regexp.MatchString("^[0-9]+$", value); ok { - i, err := strconv.Atoi(value) - if err != nil { - panic(err) - } - return i - } + return result +} - return value +func unmarshalOrReturnString(value string) (result interface{}) { + value = strings.Replace(value, "\n", magicString, -1) + if err := yaml.Unmarshal([]byte(value), &result); err != nil { + result = value + } + result = reverseReplacement(result) + return } func parseCmdline(cmdLine string) map[interface{}]interface{} { @@ -167,7 +174,7 @@ outer: keys := strings.Split(kv[0], ".") for i, key := range keys { if i == len(keys)-1 { - current[key] = DummyMarshall(value) + current[key] = unmarshalOrReturnString(value) } else { if obj, ok := current[key]; ok { if newCurrent, ok := obj.(map[interface{}]interface{}); ok {