Skip to content

Commit

Permalink
function/stdlib: Upstream various functions from HashiCorp Terraform
Browse files Browse the repository at this point in the history
HashiCorp implemented additional functions for Terraform that previously
lived in the Terraform codebase. Now that they want to offer a similar
complement of functions in Packer, they are contributing some of the
Terraform functions upstream, where their design was general
enough to be considered as part of the "standard library".
  • Loading branch information
azr authored Feb 19, 2020
1 parent c282fd1 commit 1749c82
Show file tree
Hide file tree
Showing 17 changed files with 2,684 additions and 7 deletions.
1,170 changes: 1,170 additions & 0 deletions cty/function/stdlib/collection.go

Large diffs are not rendered by default.

306 changes: 306 additions & 0 deletions cty/function/stdlib/collection_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,312 @@ func TestHasIndex(t *testing.T) {
}
}

func TestMerge(t *testing.T) {
tests := []struct {
Values []cty.Value
Want cty.Value
Err bool
}{
{
[]cty.Value{
cty.MapVal(map[string]cty.Value{
"a": cty.StringVal("b"),
}),
cty.MapVal(map[string]cty.Value{
"c": cty.StringVal("d"),
}),
},
cty.MapVal(map[string]cty.Value{
"a": cty.StringVal("b"),
"c": cty.StringVal("d"),
}),
false,
},
{ // handle unknowns
[]cty.Value{
cty.MapVal(map[string]cty.Value{
"a": cty.UnknownVal(cty.String),
}),
cty.MapVal(map[string]cty.Value{
"c": cty.StringVal("d"),
}),
},
cty.MapVal(map[string]cty.Value{
"a": cty.UnknownVal(cty.String),
"c": cty.StringVal("d"),
}),
false,
},
{ // handle null map
[]cty.Value{
cty.NullVal(cty.Map(cty.String)),
cty.MapVal(map[string]cty.Value{
"c": cty.StringVal("d"),
}),
},
cty.MapVal(map[string]cty.Value{
"c": cty.StringVal("d"),
}),
false,
},
{ // handle null map
[]cty.Value{
cty.NullVal(cty.Map(cty.String)),
cty.NullVal(cty.Object(map[string]cty.Type{
"a": cty.List(cty.String),
})),
},
cty.NullVal(cty.EmptyObject),
false,
},
{ // handle null object
[]cty.Value{
cty.MapVal(map[string]cty.Value{
"c": cty.StringVal("d"),
}),
cty.NullVal(cty.Object(map[string]cty.Type{
"a": cty.List(cty.String),
})),
},
cty.ObjectVal(map[string]cty.Value{
"c": cty.StringVal("d"),
}),
false,
},
{ // handle unknowns
[]cty.Value{
cty.UnknownVal(cty.Map(cty.String)),
cty.MapVal(map[string]cty.Value{
"c": cty.StringVal("d"),
}),
},
cty.UnknownVal(cty.Map(cty.String)),
false,
},
{ // handle dynamic unknown
[]cty.Value{
cty.UnknownVal(cty.DynamicPseudoType),
cty.MapVal(map[string]cty.Value{
"c": cty.StringVal("d"),
}),
},
cty.DynamicVal,
false,
},
{ // merge with conflicts is ok, last in wins
[]cty.Value{
cty.MapVal(map[string]cty.Value{
"a": cty.StringVal("b"),
"c": cty.StringVal("d"),
}),
cty.MapVal(map[string]cty.Value{
"a": cty.StringVal("x"),
}),
},
cty.MapVal(map[string]cty.Value{
"a": cty.StringVal("x"),
"c": cty.StringVal("d"),
}),
false,
},
{ // only accept maps
[]cty.Value{
cty.MapVal(map[string]cty.Value{
"a": cty.StringVal("b"),
"c": cty.StringVal("d"),
}),
cty.ListVal([]cty.Value{
cty.StringVal("a"),
cty.StringVal("x"),
}),
},
cty.NilVal,
true,
},

{ // argument error, for a null type
[]cty.Value{
cty.MapVal(map[string]cty.Value{
"a": cty.StringVal("b"),
}),
cty.NullVal(cty.String),
},
cty.NilVal,
true,
},
{ // merge maps of maps
[]cty.Value{
cty.MapVal(map[string]cty.Value{
"a": cty.MapVal(map[string]cty.Value{
"b": cty.StringVal("c"),
}),
}),
cty.MapVal(map[string]cty.Value{
"d": cty.MapVal(map[string]cty.Value{
"e": cty.StringVal("f"),
}),
}),
},
cty.MapVal(map[string]cty.Value{
"a": cty.MapVal(map[string]cty.Value{
"b": cty.StringVal("c"),
}),
"d": cty.MapVal(map[string]cty.Value{
"e": cty.StringVal("f"),
}),
}),
false,
},
{ // map of lists
[]cty.Value{
cty.MapVal(map[string]cty.Value{
"a": cty.ListVal([]cty.Value{
cty.StringVal("b"),
cty.StringVal("c"),
}),
}),
cty.MapVal(map[string]cty.Value{
"d": cty.ListVal([]cty.Value{
cty.StringVal("e"),
cty.StringVal("f"),
}),
}),
},
cty.MapVal(map[string]cty.Value{
"a": cty.ListVal([]cty.Value{
cty.StringVal("b"),
cty.StringVal("c"),
}),
"d": cty.ListVal([]cty.Value{
cty.StringVal("e"),
cty.StringVal("f"),
}),
}),
false,
},
{ // merge map of various kinds
[]cty.Value{
cty.MapVal(map[string]cty.Value{
"a": cty.ListVal([]cty.Value{
cty.StringVal("b"),
cty.StringVal("c"),
}),
}),
cty.MapVal(map[string]cty.Value{
"d": cty.MapVal(map[string]cty.Value{
"e": cty.StringVal("f"),
}),
}),
},
cty.ObjectVal(map[string]cty.Value{
"a": cty.ListVal([]cty.Value{
cty.StringVal("b"),
cty.StringVal("c"),
}),
"d": cty.MapVal(map[string]cty.Value{
"e": cty.StringVal("f"),
}),
}),
false,
},
{ // merge objects of various shapes
[]cty.Value{
cty.ObjectVal(map[string]cty.Value{
"a": cty.ListVal([]cty.Value{
cty.StringVal("b"),
}),
}),
cty.ObjectVal(map[string]cty.Value{
"d": cty.DynamicVal,
}),
},
cty.ObjectVal(map[string]cty.Value{
"a": cty.ListVal([]cty.Value{
cty.StringVal("b"),
}),
"d": cty.DynamicVal,
}),
false,
},
{ // merge maps and objects
[]cty.Value{
cty.MapVal(map[string]cty.Value{
"a": cty.ListVal([]cty.Value{
cty.StringVal("b"),
}),
}),
cty.ObjectVal(map[string]cty.Value{
"d": cty.NumberIntVal(2),
}),
},
cty.ObjectVal(map[string]cty.Value{
"a": cty.ListVal([]cty.Value{
cty.StringVal("b"),
}),
"d": cty.NumberIntVal(2),
}),
false,
},
{ // attr a type and value is overridden
[]cty.Value{
cty.ObjectVal(map[string]cty.Value{
"a": cty.ListVal([]cty.Value{
cty.StringVal("b"),
}),
"b": cty.StringVal("b"),
}),
cty.ObjectVal(map[string]cty.Value{
"a": cty.ObjectVal(map[string]cty.Value{
"e": cty.StringVal("f"),
}),
}),
},
cty.ObjectVal(map[string]cty.Value{
"a": cty.ObjectVal(map[string]cty.Value{
"e": cty.StringVal("f"),
}),
"b": cty.StringVal("b"),
}),
false,
},
{ // argument error: non map type
[]cty.Value{
cty.MapVal(map[string]cty.Value{
"a": cty.ListVal([]cty.Value{
cty.StringVal("b"),
cty.StringVal("c"),
}),
}),
cty.ListVal([]cty.Value{
cty.StringVal("d"),
cty.StringVal("e"),
}),
},
cty.NilVal,
true,
},
}

for _, test := range tests {
t.Run(fmt.Sprintf("merge(%#v)", test.Values), func(t *testing.T) {
got, err := Merge(test.Values...)

if test.Err {
if err == nil {
t.Fatal("succeeded; want error")
}
return
} else if err != nil {
t.Fatalf("unexpected error: %s", err)
}

if !got.RawEquals(test.Want) {
t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want)
}
})
}
}

func TestIndex(t *testing.T) {
tests := []struct {
Collection cty.Value
Expand Down
Loading

0 comments on commit 1749c82

Please sign in to comment.