mirror of
https://github.com/opentofu/opentofu.git
synced 2024-12-30 10:47:14 -06:00
39e609d5fd
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.
511 lines
16 KiB
Go
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)
|
|
}
|
|
}
|
|
}
|
|
}
|