opentofu/config/loader_hcl2_test.go
Martin Atkins 39e609d5fd vendor: switch to HCL 2.0 in the HCL repository
Previously we were using the experimental HCL 2 repository, but now we'll
shift over to the v2 import path within the main HCL repository as part of
actually releasing HCL 2.0 as stable.

This is a mechanical search/replace to the new import paths. It also
switches to the v2.0.0 release of HCL, which includes some new code that
Terraform didn't previously have but should not change any behavior that
matters for Terraform's purposes.

For the moment the experimental HCL2 repository is still an indirect
dependency via terraform-config-inspect, so it remains in our go.sum and
vendor directories for the moment. Because terraform-config-inspect uses
a much smaller subset of the HCL2 functionality, this does still manage
to prune the vendor directory a little. A subsequent release of
terraform-config-inspect should allow us to completely remove that old
repository in a future commit.
2019-10-02 15:10:21 -07:00

511 lines
16 KiB
Go

package config
import (
"reflect"
"testing"
"github.com/zclconf/go-cty/cty"
hcl2 "github.com/hashicorp/hcl/v2"
gohcl2 "github.com/hashicorp/hcl/v2/gohcl"
)
func TestHCL2ConfigurableConfigurable(t *testing.T) {
var _ configurable = new(hcl2Configurable)
}
func TestHCL2Basic(t *testing.T) {
loader := globalHCL2Loader
cbl, _, err := loader.loadFile("testdata/basic-hcl2.tf")
if err != nil {
if diags, isDiags := err.(hcl2.Diagnostics); isDiags {
for _, diag := range diags {
t.Logf("- %s", diag.Error())
}
t.Fatalf("unexpected diagnostics in load")
} else {
t.Fatalf("unexpected error in load: %s", err)
}
}
cfg, err := cbl.Config()
if err != nil {
if diags, isDiags := err.(hcl2.Diagnostics); isDiags {
for _, diag := range diags {
t.Logf("- %s", diag.Error())
}
t.Fatalf("unexpected diagnostics in decode")
} else {
t.Fatalf("unexpected error in decode: %s", err)
}
}
// Unfortunately the config structure isn't DeepEqual-friendly because
// of all the nested RawConfig, etc structures, so we'll need to
// hand-assert each item.
// The "terraform" block
if cfg.Terraform == nil {
t.Fatalf("Terraform field is nil")
}
if got, want := cfg.Terraform.RequiredVersion, "foo"; got != want {
t.Errorf("wrong Terraform.RequiredVersion %q; want %q", got, want)
}
if cfg.Terraform.Backend == nil {
t.Fatalf("Terraform.Backend is nil")
}
if got, want := cfg.Terraform.Backend.Type, "baz"; got != want {
t.Errorf("wrong Terraform.Backend.Type %q; want %q", got, want)
}
if got, want := cfg.Terraform.Backend.RawConfig.Raw, map[string]interface{}{"something": "nothing"}; !reflect.DeepEqual(got, want) {
t.Errorf("wrong Terraform.Backend.RawConfig.Raw %#v; want %#v", got, want)
}
// The "atlas" block
if cfg.Atlas == nil {
t.Fatalf("Atlas field is nil")
}
if got, want := cfg.Atlas.Name, "example/foo"; got != want {
t.Errorf("wrong Atlas.Name %q; want %q", got, want)
}
// "module" blocks
if got, want := len(cfg.Modules), 1; got != want {
t.Errorf("Modules slice has wrong length %#v; want %#v", got, want)
} else {
m := cfg.Modules[0]
if got, want := m.Name, "child"; got != want {
t.Errorf("wrong Modules[0].Name %#v; want %#v", got, want)
}
if got, want := m.Source, "./baz"; got != want {
t.Errorf("wrong Modules[0].Source %#v; want %#v", got, want)
}
want := map[string]string{"toasty": "true"}
var got map[string]string
gohcl2.DecodeBody(m.RawConfig.Body, nil, &got)
if !reflect.DeepEqual(got, want) {
t.Errorf("wrong Modules[0].RawConfig.Body %#v; want %#v", got, want)
}
}
// "resource" blocks
if got, want := len(cfg.Resources), 5; got != want {
t.Errorf("Resources slice has wrong length %#v; want %#v", got, want)
} else {
{
r := cfg.Resources[0]
if got, want := r.Id(), "aws_security_group.firewall"; got != want {
t.Errorf("wrong Resources[0].Id() %#v; want %#v", got, want)
}
wantConfig := map[string]string{}
var gotConfig map[string]string
gohcl2.DecodeBody(r.RawConfig.Body, nil, &gotConfig)
if !reflect.DeepEqual(gotConfig, wantConfig) {
t.Errorf("wrong Resources[0].RawConfig.Body %#v; want %#v", gotConfig, wantConfig)
}
wantCount := map[string]string{"count": "5"}
var gotCount map[string]string
gohcl2.DecodeBody(r.RawCount.Body, nil, &gotCount)
if !reflect.DeepEqual(gotCount, wantCount) {
t.Errorf("wrong Resources[0].RawCount.Body %#v; want %#v", gotCount, wantCount)
}
if got, want := r.RawCount.Key, "count"; got != want {
t.Errorf("wrong Resources[0].RawCount.Key %#v; want %#v", got, want)
}
if got, want := len(r.Provisioners), 0; got != want {
t.Errorf("wrong Resources[0].Provisioners length %#v; want %#v", got, want)
}
if got, want := len(r.DependsOn), 0; got != want {
t.Errorf("wrong Resources[0].DependsOn length %#v; want %#v", got, want)
}
if got, want := r.Provider, "another"; got != want {
t.Errorf("wrong Resources[0].Provider %#v; want %#v", got, want)
}
if got, want := r.Lifecycle, (ResourceLifecycle{}); !reflect.DeepEqual(got, want) {
t.Errorf("wrong Resources[0].Lifecycle %#v; want %#v", got, want)
}
}
{
r := cfg.Resources[1]
if got, want := r.Id(), "aws_instance.web"; got != want {
t.Errorf("wrong Resources[1].Id() %#v; want %#v", got, want)
}
if got, want := r.Provider, ""; got != want {
t.Errorf("wrong Resources[1].Provider %#v; want %#v", got, want)
}
if got, want := len(r.Provisioners), 1; got != want {
t.Errorf("wrong Resources[1].Provisioners length %#v; want %#v", got, want)
} else {
p := r.Provisioners[0]
if got, want := p.Type, "file"; got != want {
t.Errorf("wrong Resources[1].Provisioners[0].Type %#v; want %#v", got, want)
}
wantConfig := map[string]string{
"source": "foo",
"destination": "bar",
}
var gotConfig map[string]string
gohcl2.DecodeBody(p.RawConfig.Body, nil, &gotConfig)
if !reflect.DeepEqual(gotConfig, wantConfig) {
t.Errorf("wrong Resources[1].Provisioners[0].RawConfig.Body %#v; want %#v", gotConfig, wantConfig)
}
wantConn := map[string]string{
"default": "true",
}
var gotConn map[string]string
gohcl2.DecodeBody(p.ConnInfo.Body, nil, &gotConn)
if !reflect.DeepEqual(gotConn, wantConn) {
t.Errorf("wrong Resources[1].Provisioners[0].ConnInfo.Body %#v; want %#v", gotConn, wantConn)
}
}
// We'll use these throwaway structs to more easily decode and
// compare the main config body.
type instanceNetworkInterface struct {
DeviceIndex int `hcl:"device_index"`
Description string `hcl:"description"`
}
type instanceConfig struct {
AMI string `hcl:"ami"`
SecurityGroups []string `hcl:"security_groups"`
NetworkInterface instanceNetworkInterface `hcl:"network_interface,block"`
}
var gotConfig instanceConfig
wantConfig := instanceConfig{
AMI: "ami-abc123",
SecurityGroups: []string{"foo", "sg-firewall"},
NetworkInterface: instanceNetworkInterface{
DeviceIndex: 0,
Description: "Main network interface",
},
}
ctx := &hcl2.EvalContext{
Variables: map[string]cty.Value{
"var": cty.ObjectVal(map[string]cty.Value{
"foo": cty.StringVal("ami-abc123"),
}),
"aws_security_group": cty.ObjectVal(map[string]cty.Value{
"firewall": cty.ObjectVal(map[string]cty.Value{
"foo": cty.StringVal("sg-firewall"),
}),
}),
},
}
diags := gohcl2.DecodeBody(r.RawConfig.Body, ctx, &gotConfig)
if len(diags) != 0 {
t.Errorf("unexpected diagnostics decoding Resources[1].RawConfig.Body")
for _, diag := range diags {
t.Logf("- %s", diag.Error())
}
}
if !reflect.DeepEqual(gotConfig, wantConfig) {
t.Errorf("wrong Resources[1].RawConfig.Body %#v; want %#v", gotConfig, wantConfig)
}
}
{
r := cfg.Resources[2]
if got, want := r.Id(), "aws_instance.db"; got != want {
t.Errorf("wrong Resources[2].Id() %#v; want %#v", got, want)
}
if got, want := r.DependsOn, []string{"aws_instance.web"}; !reflect.DeepEqual(got, want) {
t.Errorf("wrong Resources[2].DependsOn %#v; want %#v", got, want)
}
if got, want := len(r.Provisioners), 1; got != want {
t.Errorf("wrong Resources[2].Provisioners length %#v; want %#v", got, want)
} else {
p := r.Provisioners[0]
if got, want := p.Type, "file"; got != want {
t.Errorf("wrong Resources[2].Provisioners[0].Type %#v; want %#v", got, want)
}
wantConfig := map[string]string{
"source": "here",
"destination": "there",
}
var gotConfig map[string]string
gohcl2.DecodeBody(p.RawConfig.Body, nil, &gotConfig)
if !reflect.DeepEqual(gotConfig, wantConfig) {
t.Errorf("wrong Resources[2].Provisioners[0].RawConfig.Body %#v; want %#v", gotConfig, wantConfig)
}
wantConn := map[string]string{
"default": "false",
}
var gotConn map[string]string
gohcl2.DecodeBody(p.ConnInfo.Body, nil, &gotConn)
if !reflect.DeepEqual(gotConn, wantConn) {
t.Errorf("wrong Resources[2].Provisioners[0].ConnInfo.Body %#v; want %#v", gotConn, wantConn)
}
}
}
{
r := cfg.Resources[3]
if got, want := r.Id(), "data.do.simple"; got != want {
t.Errorf("wrong Resources[3].Id() %#v; want %#v", got, want)
}
if got, want := r.DependsOn, []string(nil); !reflect.DeepEqual(got, want) {
t.Errorf("wrong Resources[3].DependsOn %#v; want %#v", got, want)
}
if got, want := r.Provider, "do.foo"; got != want {
t.Errorf("wrong Resources[3].Provider %#v; want %#v", got, want)
}
wantConfig := map[string]string{
"foo": "baz",
}
var gotConfig map[string]string
gohcl2.DecodeBody(r.RawConfig.Body, nil, &gotConfig)
if !reflect.DeepEqual(gotConfig, wantConfig) {
t.Errorf("wrong Resources[3].RawConfig.Body %#v; want %#v", gotConfig, wantConfig)
}
}
{
r := cfg.Resources[4]
if got, want := r.Id(), "data.do.depends"; got != want {
t.Errorf("wrong Resources[4].Id() %#v; want %#v", got, want)
}
if got, want := r.DependsOn, []string{"data.do.simple"}; !reflect.DeepEqual(got, want) {
t.Errorf("wrong Resources[4].DependsOn %#v; want %#v", got, want)
}
if got, want := r.Provider, ""; got != want {
t.Errorf("wrong Resources[4].Provider %#v; want %#v", got, want)
}
wantConfig := map[string]string{}
var gotConfig map[string]string
gohcl2.DecodeBody(r.RawConfig.Body, nil, &gotConfig)
if !reflect.DeepEqual(gotConfig, wantConfig) {
t.Errorf("wrong Resources[4].RawConfig.Body %#v; want %#v", gotConfig, wantConfig)
}
}
}
// "variable" blocks
if got, want := len(cfg.Variables), 3; got != want {
t.Errorf("Variables slice has wrong length %#v; want %#v", got, want)
} else {
{
v := cfg.Variables[0]
if got, want := v.Name, "foo"; got != want {
t.Errorf("wrong Variables[0].Name %#v; want %#v", got, want)
}
if got, want := v.Default, "bar"; got != want {
t.Errorf("wrong Variables[0].Default %#v; want %#v", got, want)
}
if got, want := v.Description, "barbar"; got != want {
t.Errorf("wrong Variables[0].Description %#v; want %#v", got, want)
}
if got, want := v.DeclaredType, ""; got != want {
t.Errorf("wrong Variables[0].DeclaredType %#v; want %#v", got, want)
}
}
{
v := cfg.Variables[1]
if got, want := v.Name, "bar"; got != want {
t.Errorf("wrong Variables[1].Name %#v; want %#v", got, want)
}
if got, want := v.Default, interface{}(nil); got != want {
t.Errorf("wrong Variables[1].Default %#v; want %#v", got, want)
}
if got, want := v.Description, ""; got != want {
t.Errorf("wrong Variables[1].Description %#v; want %#v", got, want)
}
if got, want := v.DeclaredType, "string"; got != want {
t.Errorf("wrong Variables[1].DeclaredType %#v; want %#v", got, want)
}
}
{
v := cfg.Variables[2]
if got, want := v.Name, "baz"; got != want {
t.Errorf("wrong Variables[2].Name %#v; want %#v", got, want)
}
if got, want := v.Default, map[string]interface{}{"key": "value"}; !reflect.DeepEqual(got, want) {
t.Errorf("wrong Variables[2].Default %#v; want %#v", got, want)
}
if got, want := v.Description, ""; got != want {
t.Errorf("wrong Variables[2].Description %#v; want %#v", got, want)
}
if got, want := v.DeclaredType, "map"; got != want {
t.Errorf("wrong Variables[2].DeclaredType %#v; want %#v", got, want)
}
}
}
// "output" blocks
if got, want := len(cfg.Outputs), 2; got != want {
t.Errorf("Outputs slice has wrong length %#v; want %#v", got, want)
} else {
{
o := cfg.Outputs[0]
if got, want := o.Name, "web_ip"; got != want {
t.Errorf("wrong Outputs[0].Name %#v; want %#v", got, want)
}
if got, want := o.DependsOn, []string(nil); !reflect.DeepEqual(got, want) {
t.Errorf("wrong Outputs[0].DependsOn %#v; want %#v", got, want)
}
if got, want := o.Description, ""; got != want {
t.Errorf("wrong Outputs[0].Description %#v; want %#v", got, want)
}
if got, want := o.Sensitive, true; got != want {
t.Errorf("wrong Outputs[0].Sensitive %#v; want %#v", got, want)
}
wantConfig := map[string]string{
"value": "312.213.645.123",
}
var gotConfig map[string]string
ctx := &hcl2.EvalContext{
Variables: map[string]cty.Value{
"aws_instance": cty.ObjectVal(map[string]cty.Value{
"web": cty.ObjectVal(map[string]cty.Value{
"private_ip": cty.StringVal("312.213.645.123"),
}),
}),
},
}
gohcl2.DecodeBody(o.RawConfig.Body, ctx, &gotConfig)
if !reflect.DeepEqual(gotConfig, wantConfig) {
t.Errorf("wrong Outputs[0].RawConfig.Body %#v; want %#v", gotConfig, wantConfig)
}
}
{
o := cfg.Outputs[1]
if got, want := o.Name, "web_id"; got != want {
t.Errorf("wrong Outputs[1].Name %#v; want %#v", got, want)
}
if got, want := o.DependsOn, []string{"aws_instance.db"}; !reflect.DeepEqual(got, want) {
t.Errorf("wrong Outputs[1].DependsOn %#v; want %#v", got, want)
}
if got, want := o.Description, "The ID"; got != want {
t.Errorf("wrong Outputs[1].Description %#v; want %#v", got, want)
}
if got, want := o.Sensitive, false; got != want {
t.Errorf("wrong Outputs[1].Sensitive %#v; want %#v", got, want)
}
}
}
// "provider" blocks
if got, want := len(cfg.ProviderConfigs), 2; got != want {
t.Errorf("ProviderConfigs slice has wrong length %#v; want %#v", got, want)
} else {
{
p := cfg.ProviderConfigs[0]
if got, want := p.Name, "aws"; got != want {
t.Errorf("wrong ProviderConfigs[0].Name %#v; want %#v", got, want)
}
if got, want := p.Alias, ""; got != want {
t.Errorf("wrong ProviderConfigs[0].Alias %#v; want %#v", got, want)
}
if got, want := p.Version, "1.0.0"; got != want {
t.Errorf("wrong ProviderConfigs[0].Version %#v; want %#v", got, want)
}
wantConfig := map[string]string{
"access_key": "foo",
"secret_key": "bar",
}
var gotConfig map[string]string
gohcl2.DecodeBody(p.RawConfig.Body, nil, &gotConfig)
if !reflect.DeepEqual(gotConfig, wantConfig) {
t.Errorf("wrong ProviderConfigs[0].RawConfig.Body %#v; want %#v", gotConfig, wantConfig)
}
}
{
p := cfg.ProviderConfigs[1]
if got, want := p.Name, "do"; got != want {
t.Errorf("wrong ProviderConfigs[1].Name %#v; want %#v", got, want)
}
if got, want := p.Alias, "fum"; got != want {
t.Errorf("wrong ProviderConfigs[1].Alias %#v; want %#v", got, want)
}
if got, want := p.Version, ""; got != want {
t.Errorf("wrong ProviderConfigs[1].Version %#v; want %#v", got, want)
}
}
}
// "locals" definitions
if got, want := len(cfg.Locals), 5; got != want {
t.Errorf("Locals slice has wrong length %#v; want %#v", got, want)
} else {
{
l := cfg.Locals[0]
if got, want := l.Name, "security_group_ids"; got != want {
t.Errorf("wrong Locals[0].Name %#v; want %#v", got, want)
}
wantConfig := map[string][]string{
"value": []string{"sg-abc123"},
}
var gotConfig map[string][]string
ctx := &hcl2.EvalContext{
Variables: map[string]cty.Value{
"aws_security_group": cty.ObjectVal(map[string]cty.Value{
"firewall": cty.ObjectVal(map[string]cty.Value{
"id": cty.StringVal("sg-abc123"),
}),
}),
},
}
gohcl2.DecodeBody(l.RawConfig.Body, ctx, &gotConfig)
if !reflect.DeepEqual(gotConfig, wantConfig) {
t.Errorf("wrong Locals[0].RawConfig.Body %#v; want %#v", gotConfig, wantConfig)
}
}
{
l := cfg.Locals[1]
if got, want := l.Name, "web_ip"; got != want {
t.Errorf("wrong Locals[1].Name %#v; want %#v", got, want)
}
}
{
l := cfg.Locals[2]
if got, want := l.Name, "literal"; got != want {
t.Errorf("wrong Locals[2].Name %#v; want %#v", got, want)
}
}
{
l := cfg.Locals[3]
if got, want := l.Name, "literal_list"; got != want {
t.Errorf("wrong Locals[3].Name %#v; want %#v", got, want)
}
}
{
l := cfg.Locals[4]
if got, want := l.Name, "literal_map"; got != want {
t.Errorf("wrong Locals[4].Name %#v; want %#v", got, want)
}
}
}
}