config: first pass at replacing lists within a slice
This commit is contained in:
parent
958f2ec094
commit
22908d67ba
|
@ -9,6 +9,11 @@ import (
|
||||||
"github.com/mitchellh/reflectwalk"
|
"github.com/mitchellh/reflectwalk"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// InterpSplitDelim is the delimeter that is looked for to split when
|
||||||
|
// it is returned. This is a comma right now but should eventually become
|
||||||
|
// a value that a user is very unlikely to use (such as UUID).
|
||||||
|
const InterpSplitDelim = `,`
|
||||||
|
|
||||||
// interpRegexp is a regexp that matches interpolations such as ${foo.bar}
|
// interpRegexp is a regexp that matches interpolations such as ${foo.bar}
|
||||||
var interpRegexp *regexp.Regexp = regexp.MustCompile(
|
var interpRegexp *regexp.Regexp = regexp.MustCompile(
|
||||||
`(?i)(\$+)\{([\s*-.,\\/\(\)a-z0-9_"]+)\}`)
|
`(?i)(\$+)\{([\s*-.,\\/\(\)a-z0-9_"]+)\}`)
|
||||||
|
@ -24,7 +29,9 @@ type interpolationWalker struct {
|
||||||
lastValue reflect.Value
|
lastValue reflect.Value
|
||||||
loc reflectwalk.Location
|
loc reflectwalk.Location
|
||||||
cs []reflect.Value
|
cs []reflect.Value
|
||||||
|
csKey []reflect.Value
|
||||||
csData interface{}
|
csData interface{}
|
||||||
|
sliceIndex int
|
||||||
unknownKeys []string
|
unknownKeys []string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,6 +56,13 @@ func (w *interpolationWalker) Exit(loc reflectwalk.Location) error {
|
||||||
w.cs = w.cs[:len(w.cs)-1]
|
w.cs = w.cs[:len(w.cs)-1]
|
||||||
case reflectwalk.MapValue:
|
case reflectwalk.MapValue:
|
||||||
w.key = w.key[:len(w.key)-1]
|
w.key = w.key[:len(w.key)-1]
|
||||||
|
w.csKey = w.csKey[:len(w.csKey)-1]
|
||||||
|
case reflectwalk.Slice:
|
||||||
|
// Split any values that need to be split
|
||||||
|
w.splitSlice()
|
||||||
|
w.cs = w.cs[:len(w.cs)-1]
|
||||||
|
case reflectwalk.SliceElem:
|
||||||
|
w.csKey = w.csKey[:len(w.csKey)-1]
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -61,11 +75,23 @@ func (w *interpolationWalker) Map(m reflect.Value) error {
|
||||||
|
|
||||||
func (w *interpolationWalker) MapElem(m, k, v reflect.Value) error {
|
func (w *interpolationWalker) MapElem(m, k, v reflect.Value) error {
|
||||||
w.csData = k
|
w.csData = k
|
||||||
|
w.csKey = append(w.csKey, k)
|
||||||
w.key = append(w.key, k.String())
|
w.key = append(w.key, k.String())
|
||||||
w.lastValue = v
|
w.lastValue = v
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (w *interpolationWalker) Slice(s reflect.Value) error {
|
||||||
|
w.cs = append(w.cs, s)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *interpolationWalker) SliceElem(i int, elem reflect.Value) error {
|
||||||
|
w.csKey = append(w.csKey, reflect.ValueOf(i))
|
||||||
|
w.sliceIndex = i
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (w *interpolationWalker) Primitive(v reflect.Value) error {
|
func (w *interpolationWalker) Primitive(v reflect.Value) error {
|
||||||
setV := v
|
setV := v
|
||||||
|
|
||||||
|
@ -112,13 +138,28 @@ func (w *interpolationWalker) Primitive(v reflect.Value) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
if w.Replace {
|
if w.Replace {
|
||||||
// If this is an unknown variable, then we remove it from
|
// We need to determine if we need to remove this element
|
||||||
// the configuration.
|
// if the result contains any "UnknownVariableValue" which is
|
||||||
if replaceVal == UnknownVariableValue {
|
// set if it is computed. This behavior is different if we're
|
||||||
|
// splitting (in a SliceElem) or not.
|
||||||
|
remove := false
|
||||||
|
if w.loc == reflectwalk.SliceElem {
|
||||||
|
parts := strings.Split(replaceVal, InterpSplitDelim)
|
||||||
|
for _, p := range parts {
|
||||||
|
if p == UnknownVariableValue {
|
||||||
|
remove = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if replaceVal == UnknownVariableValue {
|
||||||
|
remove = true
|
||||||
|
}
|
||||||
|
if remove {
|
||||||
w.removeCurrent()
|
w.removeCurrent()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Replace in our interpolation and continue on.
|
||||||
result = strings.Replace(result, match[0], replaceVal, -1)
|
result = strings.Replace(result, match[0], replaceVal, -1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -168,3 +209,69 @@ func (w *interpolationWalker) removeCurrent() {
|
||||||
// Append the key to the unknown keys
|
// Append the key to the unknown keys
|
||||||
w.unknownKeys = append(w.unknownKeys, strings.Join(w.key, "."))
|
w.unknownKeys = append(w.unknownKeys, strings.Join(w.key, "."))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (w *interpolationWalker) replace(v reflect.Value, offset int) {
|
||||||
|
c := w.cs[len(w.cs)-1+offset]
|
||||||
|
switch c.Kind() {
|
||||||
|
case reflect.Map:
|
||||||
|
// Get the key and delete it
|
||||||
|
k := w.csKey[len(w.csKey)-1]
|
||||||
|
c.SetMapIndex(k, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *interpolationWalker) splitSlice() {
|
||||||
|
// Get the []interface{} slice so we can do some operations on
|
||||||
|
// it without dealing with reflection. We'll document each step
|
||||||
|
// here to be clear.
|
||||||
|
var s []interface{}
|
||||||
|
raw := w.cs[len(w.cs)-1]
|
||||||
|
switch v := raw.Interface().(type) {
|
||||||
|
case []interface{}:
|
||||||
|
s = v
|
||||||
|
case []map[string]interface{}:
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
panic("Unknown kind: " + raw.Kind().String())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if we have any elements that we need to split. If not, then
|
||||||
|
// just return since we're done.
|
||||||
|
split := false
|
||||||
|
for _, v := range s {
|
||||||
|
sv, ok := v.(string)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if idx := strings.Index(sv, InterpSplitDelim); idx >= 0 {
|
||||||
|
split = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !split {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make a new result slice that is twice the capacity to fit our growth.
|
||||||
|
result := make([]interface{}, 0, len(s)*2)
|
||||||
|
|
||||||
|
// Go over each element of the original slice and start building up
|
||||||
|
// the resulting slice by splitting where we have to.
|
||||||
|
for _, v := range s {
|
||||||
|
sv, ok := v.(string)
|
||||||
|
if !ok {
|
||||||
|
// Not a string, so just set it
|
||||||
|
result = append(result, v)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Split on the delimiter
|
||||||
|
for _, p := range strings.Split(sv, InterpSplitDelim) {
|
||||||
|
result = append(result, p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Our slice is now done, we have to replace the slice now
|
||||||
|
// with this new one that we have.
|
||||||
|
w.replace(reflect.ValueOf(result), -1)
|
||||||
|
}
|
||||||
|
|
|
@ -136,6 +136,7 @@ func TestInterpolationWalker_replace(t *testing.T) {
|
||||||
cases := []struct {
|
cases := []struct {
|
||||||
Input interface{}
|
Input interface{}
|
||||||
Output interface{}
|
Output interface{}
|
||||||
|
Value string
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
Input: map[string]interface{}{
|
Input: map[string]interface{}{
|
||||||
|
@ -144,6 +145,7 @@ func TestInterpolationWalker_replace(t *testing.T) {
|
||||||
Output: map[string]interface{}{
|
Output: map[string]interface{}{
|
||||||
"foo": "$${var.foo}",
|
"foo": "$${var.foo}",
|
||||||
},
|
},
|
||||||
|
Value: "bar",
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -153,6 +155,7 @@ func TestInterpolationWalker_replace(t *testing.T) {
|
||||||
Output: map[string]interface{}{
|
Output: map[string]interface{}{
|
||||||
"foo": "hello, bar",
|
"foo": "hello, bar",
|
||||||
},
|
},
|
||||||
|
Value: "bar",
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -166,12 +169,30 @@ func TestInterpolationWalker_replace(t *testing.T) {
|
||||||
"bar": "bar",
|
"bar": "bar",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
Value: "bar",
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
Input: map[string]interface{}{
|
||||||
|
"foo": []interface{}{
|
||||||
|
"${var.foo}",
|
||||||
|
"bing",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Output: map[string]interface{}{
|
||||||
|
"foo": []interface{}{
|
||||||
|
"bar",
|
||||||
|
"baz",
|
||||||
|
"bing",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Value: "bar,baz",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, tc := range cases {
|
for i, tc := range cases {
|
||||||
fn := func(i Interpolation) (string, error) {
|
fn := func(i Interpolation) (string, error) {
|
||||||
return "bar", nil
|
return tc.Value, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
w := &interpolationWalker{F: fn, Replace: true}
|
w := &interpolationWalker{F: fn, Replace: true}
|
||||||
|
|
Loading…
Reference in New Issue