Exemplo n.º 1
0
func TestFnGetAtt_Passthru_NonStringArguments(t *testing.T) {
	stack := deepstack.DeepStack{}
	stack.Push(fallbackmap.DeepMap(map[string]interface{}{
		"FakeResource": map[string]interface{}{
			"FakeProperty": "FakeValue",
		},
	}))
	templateRules := template.Rules{}

	inputs := []interface{}{
		[]interface{}{"FakeResource", 1},
		[]interface{}{1, "FakeProperty"},
	}

	for _, input := range inputs {
		fnGetAtt := MakeFnGetAtt(&stack, &templateRules)
		input := interface{}(map[string]interface{}{
			"Fn::GetAtt": input,
		})

		newKey, newNode := fnGetAtt([]interface{}{"x", "y"}, input)
		if newKey != "y" {
			t.Fatalf("FnGetAtt modified the path (%v instead of %v)", newKey, "y")
		}

		if !reflect.DeepEqual(newNode, input) {
			t.Fatalf("FnGetAtt with non-string arguments modified the data (%#v instead of %#v)", newNode, input)
		}
	}
}
Exemplo n.º 2
0
func TestFnGetAtt_Basic(t *testing.T) {
	stack := deepstack.DeepStack{}
	stack.Push(fallbackmap.DeepMap(map[string]interface{}{
		"FakeResource": map[string]interface{}{
			"FakeProperty": map[string]interface{}{"FakeSub": "FakeValue"},
		},
	}))
	templateRules := template.Rules{}

	inputs := []interface{}{
		[]interface{}{"FakeResource", "FakeProperty"},
		[]interface{}{"FakeResource", "FakeProperty.FakeSub"},
	}

	expected := []interface{}{
		map[string]interface{}{"FakeSub": "FakeValue"},
		"FakeValue",
	}

	for i, input := range inputs {
		fnGetAtt := MakeFnGetAtt(&stack, &templateRules)
		input := interface{}(map[string]interface{}{
			"Fn::GetAtt": input,
		})

		newKey, newNode := fnGetAtt([]interface{}{"x", "y"}, input)
		if newKey != "y" {
			t.Fatalf("FnGetAtt modified the path (%v instead of %v)", newKey, "y")
		}

		if !reflect.DeepEqual(newNode, expected[i]) {
			t.Fatalf("FnGetAtt for %v did not return the expected result (%#v instead of %#v)", input, newNode, expected[i])
		}
	}
}
Exemplo n.º 3
0
// With no aliases involved, returns nothing (to prevent infinite recursion)
func TestGetBasic(t *testing.T) {
	i := DeepAlias{fallbackmap.DeepMap(map[string]interface{}{
		"foo": 1,
		"bar": 2,
	})}

	testGetNil(i, []string{"foo"}, t)
	testGetNil(i, []string{"bar"}, t)
}
Exemplo n.º 4
0
func TestGetAliased(t *testing.T) {
	i := DeepAlias{fallbackmap.DeepMap(map[string]interface{}{
		"foo":          1,
		"bar":          2,
		"anAlias":      "foo",
		"anotherAlias": "bar",
	})}

	testGetInt(i, []string{"[anAlias]"}, 1, t)
	testGetInt(i, []string{"[anotherAlias]"}, 2, t)
}
Exemplo n.º 5
0
func MakeFnWith(sources *deepstack.DeepStack, outerRules *template.Rules) template.Rule {
	return func(path []interface{}, node interface{}) (interface{}, interface{}) {
		key := interface{}(nil)
		if len(path) > 0 {
			key = path[len(path)-1]
		}

		raw, ok := singleKey(node, "Fn::With")
		if !ok {
			return key, node //passthru
		}

		args, ok := collectArgs(
			raw,
			func(argsSoFar []interface{}) bool {
				return len(argsSoFar) < 2
			},
			func(argsSoFar []interface{}, arg interface{}) (bool, interface{}) {
				// unconditionally process the argument, in case it needs to be skipped
				key, node := template.Walk(path, arg, outerRules)
				if skip, ok := key.(bool); ok && skip {
					return true, nil
				}

				if len(argsSoFar) == 1 {
					return false, arg // return unprocessed 2nd arg. It's a template.
				}

				return false, node
			},
		)

		if !ok {
			return key, node //passthru
		}

		var source map[string]interface{}
		if source, ok = args[0].(map[string]interface{}); !ok {
			return key, node //passthru
		}

		sources.Push(fallbackmap.DeepMap(source))
		innerTemplate := interface{}(args[1])
		key, generated := template.Walk(path, innerTemplate, outerRules)
		sources.PopDiscard()

		return key, interface{}(generated)
	}
}
Exemplo n.º 6
0
func TestGetDeepAlias(t *testing.T) {
	i := DeepAlias{fallbackmap.DeepMap(map[string]interface{}{
		"foo": 1,
		"a": map[string]interface{}{
			"aa": 2,
			"ab": map[string]interface{}{
				"aba":     3,
				"anAlias": "bar",
			},
			"bar": 4,
		},
	})}

	testGetInt(i, []string{"a", "[a.ab.anAlias]"}, 4, t)
}
Exemplo n.º 7
0
// With no aliases involved, returns nothing (to prevent infinite recursion)
func TestGetDeepBasic(t *testing.T) {
	i := DeepAlias{fallbackmap.DeepMap(map[string]interface{}{
		"foo": 1,
		"a": map[string]interface{}{
			"aa": 2,
			"ab": map[string]interface{}{
				"aba": 3,
				"abb": 4,
			},
		},
	})}

	testGetNil(i, []string{"a", "aa"}, t)
	testGetNil(i, []string{"a", "ab", "aba"}, t)
}
Exemplo n.º 8
0
func (catalogue *DeepCloudFormationOutputs) Get(path []string) (interface{}, bool) {
	// path should always be in the form: [StackName, "Outputs", OutputParameter]
	if len(path) != 3 || !isValidStackName(path[0]) || path[1] != "Outputs" || !isValidOutputName(path[2]) {
		return nil, false
	}

	if catalogue.cache != nil {
		cached, ok := catalogue.cache[path[0]]
		if ok {
			return cached.Get(path[1:])
		}
	}

	svc := cloudformation.New(&aws.Config{Region: aws.String(catalogue.Region)})
	description, err := svc.DescribeStacks(&cloudformation.DescribeStacksInput{
		StackName: &path[0],
	})

	if err != nil {
		// FIXME: need a better way to handle this.
		// Right now we just ignore the error, as we may not have actually wanted a
		// Stack Output
		fmt.Fprintf(os.Stderr, "Err: %s\n", err)
		return nil, false
	}

	if len(description.Stacks) != 1 {
		panic(fmt.Sprintf(
			"Description of [%s] did not return in exactly one Stack",
			path[0],
		))
	}

	outputs := map[string]interface{}{}
	stack := description.Stacks[0]
	for _, output := range stack.Outputs {
		outputs[*output.OutputKey] = *output.OutputValue
	}

	deep := fallbackmap.DeepMap(map[string]interface{}{
		"Outputs": outputs,
	})
	catalogue.cache[path[0]] = deep

	return deep.Get(path[1:])
}
Exemplo n.º 9
0
func Test_Lazy_traversesTemplateRuleResults(t *testing.T) {
	testRules := template.Rules{}
	testRules.Attach(func(path []interface{}, node interface{}) (interface{}, interface{}) {
		key := interface{}(nil)
		if len(path) > 0 {
			key = path[len(path)-1]
		}

		if key == "replaceThis" {
			return key, map[string]interface{}{
				"replacedDeep": "replacedDeepValue",
			}
		}

		return key, node
	})

	lazy := NewLazyMap(fallbackmap.DeepMap(map[string]interface{}{
		"a": "anA",
		"b": interface{}(map[string]interface{}{
			"innerA":      "anInnerA",
			"replaceThis": "this should be replaced",
		}),
		"replaceThis": "this should also be replaced",
	}), &testRules)

	for path, expected := range map[string]string{
		"b.replaceThis.replacedDeep": "replacedDeepValue",
		"replaceThis.replacedDeep":   "replacedDeepValue",
	} {
		result, has_key := lazy.Get(strings.Split(path, "."))
		if !has_key {
			t.Fatalf("Get() of a rule-modified map did not claim to have a key for path %#v", path)
		}

		resultString, ok := result.(string)
		if !ok {
			t.Fatalf("Get() of a rule-modified map did not return the correct type of value for path %#v", path)
		}

		if resultString != expected {
			t.Fatalf("Get() of a rule-modified map did not return the expected value '%s' (got '%s' instead) for path %#v", expected, resultString, path)
		}
	}
}
Exemplo n.º 10
0
func TestFnGetAtt_ProcessBound(t *testing.T) {
	stack := deepstack.DeepStack{}
	stack.Push(fallbackmap.DeepMap(map[string]interface{}{
		"FakeResource": map[string]interface{}{
			"FakeProperty": map[string]interface{}{"FakeSub": "FakeValue"},
		},
	}))
	templateRules := template.Rules{}
	templateRules.Attach(func(path []interface{}, node interface{}) (interface{}, interface{}) {
		key := interface{}(nil)
		if len(path) > 0 {
			key = path[len(path)-1]
		}

		if stringVal, ok := node.(string); ok && stringVal == "FakeValue" {
			return key, interface{}("ProcessedFakeValue")
		}

		return key, node
	})

	inputs := []interface{}{
		[]interface{}{"FakeResource", "FakeProperty.FakeSub"},
	}

	expected := []interface{}{
		"ProcessedFakeValue",
	}

	for i, input := range inputs {
		fnGetAtt := MakeFnGetAtt(&stack, &templateRules)
		input := interface{}(map[string]interface{}{
			"Fn::GetAtt": input,
		})

		newKey, newNode := fnGetAtt([]interface{}{"x", "y"}, input)
		if newKey != "y" {
			t.Fatalf("FnGetAtt modified the path (%v instead of %v)", newKey, "y")
		}

		if !reflect.DeepEqual(newNode, expected[i]) {
			t.Fatalf("FnGetAtt for %v did not return the expected result (%#v instead of %#v)", input, newNode, expected[i])
		}
	}
}
Exemplo n.º 11
0
func (catalogue *DeepCloudFormationResources) Get(path []string) (interface{}, bool) {
	// path should always be in the form: [StackName, "Outputs", OutputParameter]
	if len(path) != 3 || !isValidStackName(path[0]) || path[1] != "Resources" || !isValidResourceName(path[2]) {
		return nil, false
	}

	if catalogue.cache != nil {
		cached, ok := catalogue.cache[path[0]]
		if ok {
			return cached.Get(path[1:])
		}
	}

	svc := cloudformation.New(&aws.Config{Region: aws.String(catalogue.Region)})
	response, err := svc.DescribeStackResources(&cloudformation.DescribeStackResourcesInput{
		StackName: &path[0],
	})

	if err != nil {
		// FIXME: need a better way to handle this.
		// Right now we just ignore the error, as we may not have actually wanted a
		// Stack Resource
		fmt.Fprintf(os.Stderr, "Err: %s\n", err)
		return nil, false
	}

	resources := map[string]interface{}{}
	for _, resource := range response.StackResources {
		resources[*resource.LogicalResourceId] = *resource.PhysicalResourceId
	}

	deep := fallbackmap.DeepMap(map[string]interface{}{
		"Resources": resources,
	})
	catalogue.cache[path[0]] = deep

	return deep.Get(path[1:])
}
Exemplo n.º 12
0
func TestRef_Basic(t *testing.T) {
	stack := deepstack.DeepStack{}
	stack.Push(fallbackmap.DeepMap(map[string]interface{}{
		"BoundVar": "BoundValue",
	}))
	templateRules := template.Rules{}

	expected := "BoundValue"

	ref := MakeRef(&stack, &templateRules)
	input := interface{}(map[string]interface{}{
		"Ref": "BoundVar",
	})

	newKey, newNode := ref([]interface{}{"x", "y"}, input)
	if newKey != "y" {
		t.Fatalf("Ref modified the path (%v instead of %v)", newKey, "y")
	}

	if !reflect.DeepEqual(newNode, expected) {
		t.Fatalf("Ref for %v did not return the expected result (%#v instead of %#v)", input, newNode, expected)
	}
}
Exemplo n.º 13
0
func Test_Stack(t *testing.T) {
	stack := DeepStack{}
	frameOne := fallbackmap.DeepMap(map[string]interface{}{
		"a": "one:a",
		"b": "one:b",
	})
	frameTwo := fallbackmap.DeepMap(map[string]interface{}{
		"a": "two:a",
		"d": "two:d",
	})

	stack.Push(frameOne)
	stack.Push(frameTwo)

	value, _ := stack.Get([]string{"a"})
	if value != "two:a" {
		t.Fatalf("overridden 'a' value not retrieved (%#v instead of %#v)", value, "two:a")
	}

	value, _ = stack.Get([]string{"d"})
	if value != "two:d" {
		t.Fatalf("appended 'd' value not retrieved (%#v instead of %#v)", value, "two:d")
	}

	value, _ = stack.Get([]string{"b"})
	if value != "one:b" {
		t.Fatalf("original 'b' value not retrieved (%#v instead of %#v)", value, "one:b")
	}

	_, hasKey := stack.Get([]string{"c"})
	if hasKey {
		t.Fatalf("undefined 'c' value claims to be set")
	}

	aFrame := stack.Pop()
	if !reflect.DeepEqual(aFrame, frameTwo) {
		t.Fatalf("Pop did not return the most-recent Frame (%#v instead)", aFrame)
	}

	value, _ = stack.Get([]string{"a"})
	if value != "one:a" {
		t.Fatalf("original 'a' value not retrieved after Pop (%#v instead of %#v)", value, "one:a")
	}

	_, hasKey = stack.Get([]string{"d"})
	if hasKey {
		t.Fatalf("appended 'd' value still present after Pop")
	}

	stack.Push(fallbackmap.DeepMap(frameTwo))

	value, _ = stack.Get([]string{"a"})
	if value != "two:a" {
		t.Fatalf("overridden 'a' value not retrieved [again] (%#v instead of %#v)", value, "two:a")
	}

	stack.PopDiscard()
	value, _ = stack.Get([]string{"a"})
	if value != "one:a" {
		t.Fatalf("original 'a' value not retrieved after PopDiscard (%#v instead of %#v)", value, "one:a")
	}
}
Exemplo n.º 14
0
func (f *InputsFlag) Set(parametersFilename string) (err error) {
	var inputStream io.Reader
	var raw interface{}
	var ok bool
	var gotRaw bool
	var ins []interface{}
	var in map[string]interface{}

	rawJson := strings.NewReader(parametersFilename)
	rawDecoder := json.NewDecoder(rawJson)
	if err := rawDecoder.Decode(&raw); err == nil {
		if ins, ok = raw.([]interface{}); ok {
			gotRaw = true
		} else if in, ok = raw.(map[string]interface{}); ok {
			ins = append(ins, interface{}(in))
			gotRaw = true
		}
	}

	if gotRaw {
		parametersFilename = "[inline]"
	} else {
		// reset, as we cannot rely on the state of raw after decoding into it
		raw = interface{}(nil)

		if inputStream, err = os.Open(parametersFilename); err != nil {
			return err
		}

		inputDecoder := json.NewDecoder(inputStream)
		if err := inputDecoder.Decode(&raw); err != nil {
			return err
		}

		if ins, ok = raw.([]interface{}); !ok {
			if in, ok = raw.(map[string]interface{}); !ok {
				return fmt.Errorf("JSON data does not decode into an array or map")
			}

			ins = append(ins, interface{}(in))
		}
	}

	var i int
	for i, raw = range ins {
		if in, ok = raw.(map[string]interface{}); !ok {
			return fmt.Errorf("JSON data does not decode into a map or array of maps")
		}

		var parametersFilespec string
		if len(ins) == 1 {
			parametersFilespec = parametersFilename
		} else {
			parametersFilespec = fmt.Sprintf("%s[%d]", parametersFilename, i)
		}

		f.inputs.Override(lazymap.NewLazyMap(fallbackmap.DeepMap(in), f.rules))
		f.sources = append(f.sources, inputSource{filename: parametersFilespec, data: in})
	}

	return nil
}
Exemplo n.º 15
0
func main() {
	templateRules := template.Rules{}
	inputParameters := NewInputsFlag(&templateRules)
	var templateFilename string
	var outputWhat OutputWhatFlag

	flag.StringVar(&templateFilename,
		"template", "-",
		"CloudFormation Template to process")

	flag.Var(&inputParameters,
		"parameters",
		"File to use of input parameters (can be specified multiple times)")

	flag.Var(&outputWhat,
		"output",
		"What to output after processing the Template")

	flag.Parse()

	var jsonStream io.Reader
	var err error

	if templateFilename == "-" {
		jsonStream = os.Stdin
	} else if jsonStream, err = os.Open(templateFilename); err != nil {
		panic(err)
	}

	dec := json.NewDecoder(jsonStream)
	t := make(map[string]interface{})
	if err := dec.Decode(&t); err != nil {
		panic(err)
	}

	sources := fallbackmap.FallbackMap{}
	stack := deepstack.DeepStack{}

	sources.Attach(inputParameters.Get())
	sources.Attach(deepalias.DeepAlias{&stack})
	sources.Attach(deepcloudformationoutputs.NewDeepCloudFormationOutputs("eu-west-1"))
	sources.Attach(deepcloudformationresources.NewDeepCloudFormationResources("eu-west-1"))

	stack.Push(&sources)

	templateRules.AttachEarly(rules.ExcludeComments)
	templateRules.AttachEarly(rules.MakeFnFor(&stack, &templateRules))
	templateRules.AttachEarly(rules.MakeFnWith(&stack, &templateRules))
	templateRules.Attach(rules.FnAdd)
	templateRules.Attach(rules.FnIf)
	templateRules.Attach(rules.FnAnd)
	templateRules.Attach(rules.FnOr)
	templateRules.Attach(rules.FnNot)
	templateRules.Attach(rules.FnEquals)
	templateRules.Attach(rules.FnConcat)
	templateRules.Attach(rules.FnFromEntries)
	templateRules.Attach(rules.FnHasKey)
	templateRules.Attach(rules.FnJoin)
	templateRules.Attach(rules.FnKeys)
	templateRules.Attach(rules.FnLength)
	templateRules.Attach(rules.FnMerge)
	templateRules.Attach(rules.FnMergeDeep)
	templateRules.Attach(rules.FnMod)
	templateRules.Attach(rules.FnSplit)
	templateRules.Attach(rules.FnToEntries)
	templateRules.Attach(rules.FnUnique)
	templateRules.Attach(rules.MakeFnGetAtt(&stack, &templateRules))
	templateRules.Attach(rules.MakeRef(&stack, &templateRules))
	templateRules.Attach(rules.MakeFnHasRef(&stack))
	templateRules.Attach(rules.MakeFnIncludeFile(vfs.OS("/"), &templateRules))
	templateRules.Attach(rules.MakeFnIncludeFileRaw(vfs.OS("/")))
	templateRules.Attach(rules.ReduceConditions)

	// First Pass (to collect Parameter names)
	processed := template.Process(t, &templateRules)

	parameterRefs := map[string]interface{}{}
	if processedMap, ok := processed.(map[string]interface{}); ok {
		if processedParameters, ok := processedMap["Parameters"]; ok {
			if processedParametersMap, ok := processedParameters.(map[string]interface{}); ok {
				for parameterName, _ := range processedParametersMap {
					parameterRefs[parameterName] = map[string]interface{}{
						"ParamRef": parameterName,
					}
				}
			}
		}
	}

	stack.Push(fallbackmap.DeepMap(parameterRefs))
	templateRules.Attach(func(path []interface{}, node interface{}) (interface{}, interface{}) {
		key := interface{}(nil)
		if len(path) > 0 {
			key = path[len(path)-1]
		}

		if nodeMap, ok := node.(map[string]interface{}); !ok || len(nodeMap) != 1 {
			return key, node //passthru
		}

		if refName, ok := node.(map[string]interface{})["ParamRef"]; ok {
			return key, interface{}(map[string]interface{}{"Ref": interface{}(refName)})
		}

		return key, node
	})
	processed = template.Process(t, &templateRules)
	stack.PopDiscard()

	switch outputWhat.Get().what {
	case OutputTemplate:
		enc := json.NewEncoder(os.Stdout)
		enc.Encode(processed)
	case OutputCredentials:
		credentials := []interface{}{}
		credentialMap := make(map[string]interface{})
		for _, input := range inputParameters.Sources() {
			if !outputWhat.Get().hasKey || outputWhat.Get().key == input.filename {
				credentialMap = template.Process(input.data, &templateRules).(map[string]interface{})
				credentialMap["$comment"] = map[string]interface{}{"filename": input.filename}
				credentials = append(credentials, credentialMap)
			}
		}

		if len(credentials) == 0 && outputWhat.Get().hasKey {
			panic(fmt.Errorf("No parameters file '%s' was input", outputWhat.Get().key))
		}

		enc := json.NewEncoder(os.Stdout)
		if len(credentials) == 1 {
			enc.Encode(credentials[0])
		} else {
			enc.Encode(credentials)
		}
	case OutputParameters:
		parameters := []cloudformation.Parameter{}

		for name, _ := range parameterRefs {
			value, ok := sources.Get([]string{name})
			if !ok {
				continue
			}

			value = template.Process(value, &templateRules)

			parameters = append(parameters, func(name string, value interface{}) cloudformation.Parameter {
				stringval := fmt.Sprintf("%s", value)
				boolval := false
				return cloudformation.Parameter{
					ParameterKey:     &name,
					ParameterValue:   &stringval,
					UsePreviousValue: &boolval,
				}
			}(name, value))
		}

		enc := json.NewEncoder(os.Stdout)
		enc.Encode(parameters)
	}
}
Exemplo n.º 16
0
func MakeFnFor(sources *deepstack.DeepStack, templateRules *template.Rules) template.Rule {
	return func(path []interface{}, node interface{}) (interface{}, interface{}) {
		key := interface{}(nil)
		if len(path) > 0 {
			key = path[len(path)-1]
		}

		raw, ok := singleKey(node, "Fn::For")
		if !ok {
			return key, node //passthru
		}

		args, ok := collectArgs(
			raw,
			func(argsSoFar []interface{}) bool { return len(argsSoFar) < 3 },
			func(argsSoFar []interface{}, arg interface{}) (skip bool, newNode interface{}) {
				// unconditionally process the argument, in case it needs to be skipped
				key, node := template.Walk(path, arg, templateRules)
				if skip, ok := key.(bool); ok && skip {
					return true, nil
				}

				if len(argsSoFar) == 2 {
					return false, arg // return unprocessed 3rd arg. It's a template.
				}

				return false, node
			},
		)
		if !ok {
			return key, node //passthru
		}

		var refNames []interface{}
		var refName interface{}

		if refNames, ok = args[0].([]interface{}); ok {
			if len(refNames) == 1 {
				refNames = []interface{}{nil, refNames[0]}
			} else if len(refNames) != 2 {
				return key, node //passthru
			}
		} else {
			refNames = []interface{}{nil, args[0]}
		}

		for _, refName = range refNames {
			if _, ok = refName.(string); !ok && refName != nil {
				return key, node //passthru
			}
		}

		valuesInterface := args[1]

		var values []interface{}
		if values, ok = valuesInterface.([]interface{}); !ok {
			return key, node //passthru
		}

		loopTemplate := interface{}(args[2])

		generated := []interface{}{}
		for deepIndex, value := range values {
			refMap := make(map[string]interface{})
			if refNames[0] != nil {
				refMap[refNames[0].(string)] = float64(deepIndex)
			}

			if refNames[1] != nil {
				refMap[refNames[1].(string)] = value
			}

			deepPath := make([]interface{}, len(path)+1)
			copy(deepPath, path)
			deepPath[cap(deepPath)-1] = interface{}(deepIndex)

			sources.Push(fallbackmap.DeepMap(refMap))

			newIndex, processed := template.Walk(deepPath, loopTemplate, templateRules)
			sources.PopDiscard()

			if newIndex != nil {
				generated = append(generated, processed)
			}
		}

		return key, interface{}(generated)
	}
}
Exemplo n.º 17
0
func TestFnFor_StackWithArray(t *testing.T) {
	stack := deepstack.DeepStack{}
	stack.Push(fallbackmap.DeepMap(map[string]interface{}{"outer": "outerValue", "masked": "outerMasked"}))

	testRefNames := []interface{}{
		[]interface{}{"key", "masked"},
		[]interface{}{nil, "masked"},
		[]interface{}{"key", nil},
		[]interface{}{nil, nil},
		[]interface{}{"masked"},
		"masked",
	}

	expected := []interface{}{
		[]interface{}{
			map[string]interface{}{
				"outer":  []interface{}{"outerValue", true},
				"masked": []interface{}{"innerMasking", true},
				"key":    []interface{}{float64(0), true},
			},
		},
		[]interface{}{
			map[string]interface{}{
				"outer":  []interface{}{"outerValue", true},
				"masked": []interface{}{"innerMasking", true},
			},
		},
		[]interface{}{
			map[string]interface{}{
				"outer":  []interface{}{"outerValue", true},
				"masked": []interface{}{"outerMasked", true},
				"key":    []interface{}{float64(0), true},
			},
		},
		[]interface{}{
			map[string]interface{}{
				"outer":  []interface{}{"outerValue", true},
				"masked": []interface{}{"outerMasked", true},
			},
		},
		[]interface{}{
			map[string]interface{}{
				"outer":  []interface{}{"outerValue", true},
				"masked": []interface{}{"innerMasking", true},
			},
		},
		[]interface{}{
			map[string]interface{}{
				"outer":  []interface{}{"outerValue", true},
				"masked": []interface{}{"innerMasking", true},
			},
		},
	}

	for i, refNames := range testRefNames {
		input := interface{}(map[string]interface{}{
			"Fn::For": []interface{}{
				refNames,
				[]interface{}{"innerMasking"},
				"aTemplate",
			},
		})

		templateRules := template.Rules{}
		templateRules.Attach(func(path []interface{}, node interface{}) (interface{}, interface{}) {
			key := interface{}(nil)
			if len(path) > 0 {
				key = path[len(path)-1]
			}
			if stringVal, ok := node.(string); !ok || stringVal != "aTemplate" {
				return key, node
			}

			generated := map[string]interface{}{}
			for binding, _ := range expected[i].([]interface{})[0].(map[string]interface{}) {
				value, has_key := stack.Get([]string{binding})
				generated[binding] = []interface{}{value, has_key}
			}

			return key, generated
		})

		fnFor := MakeFnFor(&stack, &templateRules)
		newKey, newNode := fnFor([]interface{}{"x", "y"}, input)
		if newKey != "y" {
			t.Fatalf("FnFor modified the path (%v instead of %v)", newKey, "y")
		}

		if !reflect.DeepEqual(newNode, expected[i]) {
			t.Fatalf("FnFor did not have the correct stack values with refNames %v during templateRule (%#v instead of %#v)",
				refNames,
				newNode,
				expected[i],
			)
		}
	}
}
Exemplo n.º 18
0
func TestFnWith_StackWithArray(t *testing.T) {
	stack := deepstack.DeepStack{}
	stack.Push(fallbackmap.DeepMap(map[string]interface{}{"outer": "outer-value", "masked": "masked-value"}))

	input := interface{}(map[string]interface{}{
		"Fn::With": []interface{}{
			map[string]interface{}{
				"masked": "masking-value",
				"inner":  "inner-value",
			},
			map[string]interface{}{
				"outer":     "replace-with-outer",
				"masked":    "replace-with-masked",
				"inner":     "replace-with-inner",
				"untouched": "stay-the-same",
			},
		},
	})

	expected := interface{}(map[string]interface{}{
		"outer":     "outer-value",
		"masked":    "masking-value",
		"inner":     "inner-value",
		"untouched": "stay-the-same",
	})

	templateRules := template.Rules{}
	templateRules.Attach(func(path []interface{}, node interface{}) (interface{}, interface{}) {
		key := interface{}(nil)
		if len(path) > 0 {
			key = path[len(path)-1]
		}

		newNode := make(map[string]interface{})
		if nodeMap, ok := node.(map[string]interface{}); ok {
			if _, ok := node.(map[string]interface{})["untouched"]; ok {
				for key, value := range nodeMap {
					newValue, hasKey := stack.Get([]string{key})
					if hasKey {
						newNode[key] = newValue
					} else {
						newNode[key] = value
					}
				}
				return key, newNode
			}
		}

		return key, node
	})

	fnWith := MakeFnWith(&stack, &templateRules)
	newKey, newNode := fnWith([]interface{}{"x", "y"}, input)
	if newKey != "y" {
		t.Fatalf("FnWith modified the path (%v instead of %v)", newKey, "y")
	}

	if !reflect.DeepEqual(newNode, expected) {
		t.Fatalf("FnWith did not have the correct stack values during templateRule (%#v instead of %#v)",
			newNode,
			expected,
		)
	}
}