2014-08-02 22:44:45 +00:00
|
|
|
package hcl
|
|
|
|
|
|
|
|
import (
|
|
|
|
"io/ioutil"
|
|
|
|
"path/filepath"
|
|
|
|
"reflect"
|
|
|
|
"testing"
|
|
|
|
)
|
|
|
|
|
2014-08-11 23:38:36 +00:00
|
|
|
func TestDecode_interface(t *testing.T) {
|
2014-08-02 22:44:45 +00:00
|
|
|
cases := []struct {
|
|
|
|
File string
|
|
|
|
Err bool
|
|
|
|
Out interface{}
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
"basic.hcl",
|
|
|
|
false,
|
|
|
|
map[string]interface{}{
|
|
|
|
"foo": "bar",
|
2014-08-28 23:56:08 +00:00
|
|
|
"bar": "${file(\"bing/bong.txt\")}",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"basic_squish.hcl",
|
|
|
|
false,
|
|
|
|
map[string]interface{}{
|
2014-08-29 00:03:42 +00:00
|
|
|
"foo": "bar",
|
|
|
|
"bar": "${file(\"bing/bong.txt\")}",
|
|
|
|
"foo-bar": "baz",
|
2014-08-02 22:44:45 +00:00
|
|
|
},
|
|
|
|
},
|
2014-08-12 04:49:12 +00:00
|
|
|
{
|
|
|
|
"empty.hcl",
|
|
|
|
false,
|
|
|
|
map[string]interface{}{
|
2014-08-18 06:49:10 +00:00
|
|
|
"resource": []map[string]interface{}{
|
|
|
|
map[string]interface{}{
|
|
|
|
"foo": []map[string]interface{}{
|
|
|
|
map[string]interface{}{},
|
|
|
|
},
|
2014-08-12 04:49:12 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2014-10-10 23:07:25 +00:00
|
|
|
{
|
|
|
|
"escape.hcl",
|
|
|
|
false,
|
|
|
|
map[string]interface{}{
|
2014-11-13 05:12:45 +00:00
|
|
|
"foo": "bar\"baz\\n",
|
2014-10-10 23:07:25 +00:00
|
|
|
},
|
|
|
|
},
|
2014-11-13 05:29:07 +00:00
|
|
|
{
|
|
|
|
"float.hcl",
|
|
|
|
false,
|
|
|
|
map[string]interface{}{
|
|
|
|
"a": 1.02,
|
|
|
|
},
|
|
|
|
},
|
2014-08-21 21:42:02 +00:00
|
|
|
{
|
|
|
|
"multiline_bad.hcl",
|
|
|
|
false,
|
2014-08-21 21:54:13 +00:00
|
|
|
map[string]interface{}{"foo": "bar\nbaz\n"},
|
2014-08-21 21:42:02 +00:00
|
|
|
},
|
|
|
|
{
|
|
|
|
"multiline.json",
|
|
|
|
false,
|
|
|
|
map[string]interface{}{"foo": "bar\nbaz"},
|
|
|
|
},
|
2014-08-21 18:22:37 +00:00
|
|
|
{
|
|
|
|
"scientific.json",
|
|
|
|
false,
|
|
|
|
map[string]interface{}{
|
|
|
|
"a": 1e-10,
|
|
|
|
"b": 1e+10,
|
|
|
|
"c": 1e10,
|
2014-08-22 08:34:03 +00:00
|
|
|
"d": 1.2e-10,
|
|
|
|
"e": 1.2e+10,
|
|
|
|
"f": 1.2e10,
|
2014-08-21 18:22:37 +00:00
|
|
|
},
|
|
|
|
},
|
2014-08-21 18:29:33 +00:00
|
|
|
{
|
|
|
|
"scientific.hcl",
|
|
|
|
false,
|
|
|
|
map[string]interface{}{
|
|
|
|
"a": 1e-10,
|
|
|
|
"b": 1e+10,
|
|
|
|
"c": 1e10,
|
2014-08-22 08:34:03 +00:00
|
|
|
"d": 1.2e-10,
|
|
|
|
"e": 1.2e+10,
|
|
|
|
"f": 1.2e10,
|
2014-08-21 18:29:33 +00:00
|
|
|
},
|
|
|
|
},
|
2014-08-18 06:49:10 +00:00
|
|
|
{
|
|
|
|
"terraform_heroku.hcl",
|
|
|
|
false,
|
|
|
|
map[string]interface{}{
|
|
|
|
"name": "terraform-test-app",
|
|
|
|
"config_vars": []map[string]interface{}{
|
|
|
|
map[string]interface{}{
|
|
|
|
"FOO": "bar",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"structure_multi.hcl",
|
|
|
|
false,
|
|
|
|
map[string]interface{}{
|
|
|
|
"foo": []map[string]interface{}{
|
|
|
|
map[string]interface{}{
|
|
|
|
"baz": []map[string]interface{}{
|
|
|
|
map[string]interface{}{"key": 7},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
map[string]interface{}{
|
|
|
|
"bar": []map[string]interface{}{
|
|
|
|
map[string]interface{}{"key": 12},
|
2014-08-03 03:40:41 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2014-08-02 23:07:54 +00:00
|
|
|
},
|
2014-08-18 06:49:10 +00:00
|
|
|
},
|
|
|
|
{
|
|
|
|
"structure_multi.json",
|
|
|
|
false,
|
|
|
|
map[string]interface{}{
|
|
|
|
"foo": []map[string]interface{}{
|
|
|
|
map[string]interface{}{
|
|
|
|
"baz": []map[string]interface{}{
|
|
|
|
map[string]interface{}{"key": 7},
|
|
|
|
},
|
|
|
|
"bar": []map[string]interface{}{
|
|
|
|
map[string]interface{}{"key": 12},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2014-08-21 20:32:31 +00:00
|
|
|
{
|
|
|
|
"structure_list.hcl",
|
|
|
|
false,
|
|
|
|
map[string]interface{}{
|
|
|
|
"foo": []map[string]interface{}{
|
|
|
|
map[string]interface{}{
|
|
|
|
"key": 7,
|
|
|
|
},
|
|
|
|
map[string]interface{}{
|
|
|
|
"key": 12,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"structure_list.json",
|
|
|
|
false,
|
|
|
|
map[string]interface{}{
|
2014-08-21 21:02:29 +00:00
|
|
|
"foo": []interface{}{
|
2014-08-21 20:32:31 +00:00
|
|
|
map[string]interface{}{
|
|
|
|
"key": 7,
|
|
|
|
},
|
|
|
|
map[string]interface{}{
|
|
|
|
"key": 12,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2014-08-21 21:02:29 +00:00
|
|
|
{
|
|
|
|
"structure_list_deep.json",
|
|
|
|
false,
|
|
|
|
map[string]interface{}{
|
|
|
|
"bar": []map[string]interface{}{
|
|
|
|
map[string]interface{}{
|
|
|
|
"foo": []map[string]interface{}{
|
|
|
|
map[string]interface{}{
|
|
|
|
"name": "terraform_example",
|
|
|
|
"ingress": []interface{}{
|
|
|
|
map[string]interface{}{
|
|
|
|
"from_port": 22,
|
|
|
|
},
|
|
|
|
map[string]interface{}{
|
|
|
|
"from_port": 80,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2015-06-24 04:34:40 +00:00
|
|
|
|
|
|
|
{
|
|
|
|
"nested_block_comment.hcl",
|
|
|
|
false,
|
|
|
|
map[string]interface{}{
|
|
|
|
"bar": "value",
|
|
|
|
},
|
|
|
|
},
|
2015-06-24 04:38:11 +00:00
|
|
|
|
|
|
|
{
|
|
|
|
"unterminated_block_comment.hcl",
|
|
|
|
true,
|
|
|
|
nil,
|
|
|
|
},
|
2014-08-02 22:44:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
for _, tc := range cases {
|
|
|
|
d, err := ioutil.ReadFile(filepath.Join(fixtureDir, tc.File))
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
|
2014-08-03 05:05:21 +00:00
|
|
|
var out interface{}
|
2014-08-02 22:44:45 +00:00
|
|
|
err = Decode(&out, string(d))
|
|
|
|
if (err != nil) != tc.Err {
|
|
|
|
t.Fatalf("Input: %s\n\nError: %s", tc.File, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if !reflect.DeepEqual(out, tc.Out) {
|
2014-11-13 05:12:45 +00:00
|
|
|
t.Fatalf("Input: %s\n\nActual: %#v\n\nExpected: %#v", tc.File, out, tc.Out)
|
2014-08-02 22:44:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2014-08-03 03:48:36 +00:00
|
|
|
|
|
|
|
func TestDecode_equal(t *testing.T) {
|
|
|
|
cases := []struct {
|
|
|
|
One, Two string
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
"basic.hcl",
|
|
|
|
"basic.json",
|
|
|
|
},
|
2014-11-13 05:29:07 +00:00
|
|
|
{
|
|
|
|
"float.hcl",
|
|
|
|
"float.json",
|
|
|
|
},
|
2014-08-12 03:58:20 +00:00
|
|
|
/*
|
2014-08-12 04:49:12 +00:00
|
|
|
{
|
|
|
|
"structure.hcl",
|
|
|
|
"structure.json",
|
|
|
|
},
|
2014-08-12 03:58:20 +00:00
|
|
|
*/
|
2014-08-03 04:29:59 +00:00
|
|
|
{
|
|
|
|
"structure.hcl",
|
|
|
|
"structure_flat.json",
|
|
|
|
},
|
2014-08-03 05:05:21 +00:00
|
|
|
{
|
2014-08-18 06:49:10 +00:00
|
|
|
"terraform_heroku.hcl",
|
|
|
|
"terraform_heroku.json",
|
2014-08-03 05:18:39 +00:00
|
|
|
},
|
2014-08-03 03:48:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
for _, tc := range cases {
|
|
|
|
p1 := filepath.Join(fixtureDir, tc.One)
|
|
|
|
p2 := filepath.Join(fixtureDir, tc.Two)
|
|
|
|
|
|
|
|
d1, err := ioutil.ReadFile(p1)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
d2, err := ioutil.ReadFile(p2)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
var i1, i2 interface{}
|
|
|
|
err = Decode(&i1, string(d1))
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
err = Decode(&i2, string(d2))
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if !reflect.DeepEqual(i1, i2) {
|
2014-08-03 04:29:59 +00:00
|
|
|
t.Fatalf(
|
|
|
|
"%s != %s\n\n%#v\n\n%#v",
|
|
|
|
tc.One, tc.Two,
|
|
|
|
i1, i2)
|
2014-08-03 03:48:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2014-08-03 20:09:08 +00:00
|
|
|
|
|
|
|
func TestDecode_flatMap(t *testing.T) {
|
|
|
|
var val map[string]map[string]string
|
|
|
|
|
|
|
|
err := Decode(&val, testReadFile(t, "structure_flatmap.hcl"))
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
expected := map[string]map[string]string{
|
|
|
|
"foo": map[string]string{
|
|
|
|
"foo": "bar",
|
|
|
|
"key": "7",
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
if !reflect.DeepEqual(val, expected) {
|
|
|
|
t.Fatalf("Actual: %#v\n\nExpected: %#v", val, expected)
|
|
|
|
}
|
|
|
|
}
|
2014-08-03 21:06:18 +00:00
|
|
|
|
|
|
|
func TestDecode_structure(t *testing.T) {
|
|
|
|
type V struct {
|
|
|
|
Key int
|
|
|
|
Foo string
|
|
|
|
}
|
|
|
|
|
|
|
|
var actual V
|
|
|
|
|
|
|
|
err := Decode(&actual, testReadFile(t, "flat.hcl"))
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
expected := V{
|
|
|
|
Key: 7,
|
|
|
|
Foo: "bar",
|
|
|
|
}
|
|
|
|
|
|
|
|
if !reflect.DeepEqual(actual, expected) {
|
|
|
|
t.Fatalf("Actual: %#v\n\nExpected: %#v", actual, expected)
|
|
|
|
}
|
|
|
|
}
|
2014-08-04 00:17:17 +00:00
|
|
|
|
|
|
|
func TestDecode_structurePtr(t *testing.T) {
|
|
|
|
type V struct {
|
|
|
|
Key int
|
|
|
|
Foo string
|
|
|
|
}
|
|
|
|
|
|
|
|
var actual *V
|
|
|
|
|
|
|
|
err := Decode(&actual, testReadFile(t, "flat.hcl"))
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
expected := &V{
|
|
|
|
Key: 7,
|
|
|
|
Foo: "bar",
|
|
|
|
}
|
|
|
|
|
|
|
|
if !reflect.DeepEqual(actual, expected) {
|
|
|
|
t.Fatalf("Actual: %#v\n\nExpected: %#v", actual, expected)
|
|
|
|
}
|
|
|
|
}
|
2014-08-08 21:59:28 +00:00
|
|
|
|
|
|
|
func TestDecode_structureArray(t *testing.T) {
|
|
|
|
// This test is extracted from a failure in Consul (consul.io),
|
|
|
|
// hence the interesting structure naming.
|
|
|
|
|
2014-08-08 23:07:08 +00:00
|
|
|
type KeyPolicyType string
|
|
|
|
|
2014-08-08 21:59:28 +00:00
|
|
|
type KeyPolicy struct {
|
|
|
|
Prefix string `hcl:",key"`
|
2014-08-08 23:07:08 +00:00
|
|
|
Policy KeyPolicyType
|
2014-08-08 21:59:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type Policy struct {
|
2014-08-18 06:49:10 +00:00
|
|
|
Keys []KeyPolicy `hcl:"key,expand"`
|
2014-08-08 21:59:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
expected := Policy{
|
|
|
|
Keys: []KeyPolicy{
|
|
|
|
KeyPolicy{
|
|
|
|
Prefix: "",
|
|
|
|
Policy: "read",
|
|
|
|
},
|
|
|
|
KeyPolicy{
|
|
|
|
Prefix: "foo/",
|
|
|
|
Policy: "write",
|
|
|
|
},
|
|
|
|
KeyPolicy{
|
|
|
|
Prefix: "foo/bar/",
|
|
|
|
Policy: "read",
|
|
|
|
},
|
|
|
|
KeyPolicy{
|
|
|
|
Prefix: "foo/bar/baz",
|
|
|
|
Policy: "deny",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2014-08-08 22:58:34 +00:00
|
|
|
files := []string{
|
|
|
|
"decode_policy.hcl",
|
|
|
|
"decode_policy.json",
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, f := range files {
|
|
|
|
var actual Policy
|
|
|
|
|
|
|
|
err := Decode(&actual, testReadFile(t, f))
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if !reflect.DeepEqual(actual, expected) {
|
|
|
|
t.Fatalf("Input: %s\n\nActual: %#v\n\nExpected: %#v", f, actual, expected)
|
|
|
|
}
|
2014-08-08 21:59:28 +00:00
|
|
|
}
|
|
|
|
}
|
2014-08-11 21:19:23 +00:00
|
|
|
|
|
|
|
func TestDecode_structureMap(t *testing.T) {
|
|
|
|
// This test is extracted from a failure in Terraform (terraform.io),
|
|
|
|
// hence the interesting structure naming.
|
|
|
|
|
|
|
|
type hclVariable struct {
|
|
|
|
Default interface{}
|
|
|
|
Description string
|
|
|
|
Fields []string `hcl:",decodedFields"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type rawConfig struct {
|
|
|
|
Variable map[string]hclVariable
|
|
|
|
}
|
|
|
|
|
|
|
|
expected := rawConfig{
|
|
|
|
Variable: map[string]hclVariable{
|
|
|
|
"foo": hclVariable{
|
|
|
|
Default: "bar",
|
|
|
|
Description: "bar",
|
2014-08-12 03:58:20 +00:00
|
|
|
Fields: []string{"Default", "Description"},
|
|
|
|
},
|
|
|
|
|
|
|
|
"amis": hclVariable{
|
2014-08-18 06:49:10 +00:00
|
|
|
Default: []map[string]interface{}{
|
|
|
|
map[string]interface{}{
|
|
|
|
"east": "foo",
|
|
|
|
},
|
2014-08-12 03:58:20 +00:00
|
|
|
},
|
|
|
|
Fields: []string{"Default"},
|
2014-08-11 21:19:23 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
files := []string{
|
2014-08-12 03:58:20 +00:00
|
|
|
"decode_tf_variable.hcl",
|
2014-08-11 21:19:23 +00:00
|
|
|
"decode_tf_variable.json",
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, f := range files {
|
|
|
|
var actual rawConfig
|
|
|
|
|
|
|
|
err := Decode(&actual, testReadFile(t, f))
|
|
|
|
if err != nil {
|
2014-08-18 06:49:10 +00:00
|
|
|
t.Fatalf("Input: %s\n\nerr: %s", f, err)
|
2014-08-11 21:19:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if !reflect.DeepEqual(actual, expected) {
|
|
|
|
t.Fatalf("Input: %s\n\nActual: %#v\n\nExpected: %#v", f, actual, expected)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2014-10-01 05:29:21 +00:00
|
|
|
|
2014-10-10 03:57:32 +00:00
|
|
|
func TestDecode_interfaceNonPointer(t *testing.T) {
|
|
|
|
var value interface{}
|
|
|
|
err := Decode(value, testReadFile(t, "basic_int_string.hcl"))
|
|
|
|
if err == nil {
|
|
|
|
t.Fatal("should error")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-10-01 05:29:21 +00:00
|
|
|
func TestDecode_intString(t *testing.T) {
|
|
|
|
var value struct {
|
|
|
|
Count int
|
|
|
|
}
|
|
|
|
|
|
|
|
err := Decode(&value, testReadFile(t, "basic_int_string.hcl"))
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if value.Count != 3 {
|
|
|
|
t.Fatalf("bad: %#v", value.Count)
|
|
|
|
}
|
|
|
|
}
|