github.com/chanzuckerberg/terraform@v0.11.12-beta1/config/loader_hcl2_test.go (about)

     1  package config
     2  
     3  import (
     4  	"reflect"
     5  	"testing"
     6  
     7  	"github.com/zclconf/go-cty/cty"
     8  
     9  	gohcl2 "github.com/hashicorp/hcl2/gohcl"
    10  	hcl2 "github.com/hashicorp/hcl2/hcl"
    11  )
    12  
    13  func TestHCL2ConfigurableConfigurable(t *testing.T) {
    14  	var _ configurable = new(hcl2Configurable)
    15  }
    16  
    17  func TestHCL2Basic(t *testing.T) {
    18  	loader := globalHCL2Loader
    19  	cbl, _, err := loader.loadFile("test-fixtures/basic-hcl2.tf")
    20  	if err != nil {
    21  		if diags, isDiags := err.(hcl2.Diagnostics); isDiags {
    22  			for _, diag := range diags {
    23  				t.Logf("- %s", diag.Error())
    24  			}
    25  			t.Fatalf("unexpected diagnostics in load")
    26  		} else {
    27  			t.Fatalf("unexpected error in load: %s", err)
    28  		}
    29  	}
    30  
    31  	cfg, err := cbl.Config()
    32  	if err != nil {
    33  		if diags, isDiags := err.(hcl2.Diagnostics); isDiags {
    34  			for _, diag := range diags {
    35  				t.Logf("- %s", diag.Error())
    36  			}
    37  			t.Fatalf("unexpected diagnostics in decode")
    38  		} else {
    39  			t.Fatalf("unexpected error in decode: %s", err)
    40  		}
    41  	}
    42  
    43  	// Unfortunately the config structure isn't DeepEqual-friendly because
    44  	// of all the nested RawConfig, etc structures, so we'll need to
    45  	// hand-assert each item.
    46  
    47  	// The "terraform" block
    48  	if cfg.Terraform == nil {
    49  		t.Fatalf("Terraform field is nil")
    50  	}
    51  	if got, want := cfg.Terraform.RequiredVersion, "foo"; got != want {
    52  		t.Errorf("wrong Terraform.RequiredVersion %q; want %q", got, want)
    53  	}
    54  	if cfg.Terraform.Backend == nil {
    55  		t.Fatalf("Terraform.Backend is nil")
    56  	}
    57  	if got, want := cfg.Terraform.Backend.Type, "baz"; got != want {
    58  		t.Errorf("wrong Terraform.Backend.Type %q; want %q", got, want)
    59  	}
    60  	if got, want := cfg.Terraform.Backend.RawConfig.Raw, map[string]interface{}{"something": "nothing"}; !reflect.DeepEqual(got, want) {
    61  		t.Errorf("wrong Terraform.Backend.RawConfig.Raw %#v; want %#v", got, want)
    62  	}
    63  
    64  	// The "atlas" block
    65  	if cfg.Atlas == nil {
    66  		t.Fatalf("Atlas field is nil")
    67  	}
    68  	if got, want := cfg.Atlas.Name, "example/foo"; got != want {
    69  		t.Errorf("wrong Atlas.Name %q; want %q", got, want)
    70  	}
    71  
    72  	// "module" blocks
    73  	if got, want := len(cfg.Modules), 1; got != want {
    74  		t.Errorf("Modules slice has wrong length %#v; want %#v", got, want)
    75  	} else {
    76  		m := cfg.Modules[0]
    77  		if got, want := m.Name, "child"; got != want {
    78  			t.Errorf("wrong Modules[0].Name %#v; want %#v", got, want)
    79  		}
    80  		if got, want := m.Source, "./baz"; got != want {
    81  			t.Errorf("wrong Modules[0].Source %#v; want %#v", got, want)
    82  		}
    83  		want := map[string]string{"toasty": "true"}
    84  		var got map[string]string
    85  		gohcl2.DecodeBody(m.RawConfig.Body, nil, &got)
    86  		if !reflect.DeepEqual(got, want) {
    87  			t.Errorf("wrong Modules[0].RawConfig.Body %#v; want %#v", got, want)
    88  		}
    89  	}
    90  
    91  	// "resource" blocks
    92  	if got, want := len(cfg.Resources), 5; got != want {
    93  		t.Errorf("Resources slice has wrong length %#v; want %#v", got, want)
    94  	} else {
    95  		{
    96  			r := cfg.Resources[0]
    97  
    98  			if got, want := r.Id(), "aws_security_group.firewall"; got != want {
    99  				t.Errorf("wrong Resources[0].Id() %#v; want %#v", got, want)
   100  			}
   101  
   102  			wantConfig := map[string]string{}
   103  			var gotConfig map[string]string
   104  			gohcl2.DecodeBody(r.RawConfig.Body, nil, &gotConfig)
   105  			if !reflect.DeepEqual(gotConfig, wantConfig) {
   106  				t.Errorf("wrong Resources[0].RawConfig.Body %#v; want %#v", gotConfig, wantConfig)
   107  			}
   108  
   109  			wantCount := map[string]string{"count": "5"}
   110  			var gotCount map[string]string
   111  			gohcl2.DecodeBody(r.RawCount.Body, nil, &gotCount)
   112  			if !reflect.DeepEqual(gotCount, wantCount) {
   113  				t.Errorf("wrong Resources[0].RawCount.Body %#v; want %#v", gotCount, wantCount)
   114  			}
   115  			if got, want := r.RawCount.Key, "count"; got != want {
   116  				t.Errorf("wrong Resources[0].RawCount.Key %#v; want %#v", got, want)
   117  			}
   118  
   119  			if got, want := len(r.Provisioners), 0; got != want {
   120  				t.Errorf("wrong Resources[0].Provisioners length %#v; want %#v", got, want)
   121  			}
   122  			if got, want := len(r.DependsOn), 0; got != want {
   123  				t.Errorf("wrong Resources[0].DependsOn length %#v; want %#v", got, want)
   124  			}
   125  			if got, want := r.Provider, "another"; got != want {
   126  				t.Errorf("wrong Resources[0].Provider %#v; want %#v", got, want)
   127  			}
   128  			if got, want := r.Lifecycle, (ResourceLifecycle{}); !reflect.DeepEqual(got, want) {
   129  				t.Errorf("wrong Resources[0].Lifecycle %#v; want %#v", got, want)
   130  			}
   131  		}
   132  		{
   133  			r := cfg.Resources[1]
   134  
   135  			if got, want := r.Id(), "aws_instance.web"; got != want {
   136  				t.Errorf("wrong Resources[1].Id() %#v; want %#v", got, want)
   137  			}
   138  			if got, want := r.Provider, ""; got != want {
   139  				t.Errorf("wrong Resources[1].Provider %#v; want %#v", got, want)
   140  			}
   141  
   142  			if got, want := len(r.Provisioners), 1; got != want {
   143  				t.Errorf("wrong Resources[1].Provisioners length %#v; want %#v", got, want)
   144  			} else {
   145  				p := r.Provisioners[0]
   146  
   147  				if got, want := p.Type, "file"; got != want {
   148  					t.Errorf("wrong Resources[1].Provisioners[0].Type %#v; want %#v", got, want)
   149  				}
   150  
   151  				wantConfig := map[string]string{
   152  					"source":      "foo",
   153  					"destination": "bar",
   154  				}
   155  				var gotConfig map[string]string
   156  				gohcl2.DecodeBody(p.RawConfig.Body, nil, &gotConfig)
   157  				if !reflect.DeepEqual(gotConfig, wantConfig) {
   158  					t.Errorf("wrong Resources[1].Provisioners[0].RawConfig.Body %#v; want %#v", gotConfig, wantConfig)
   159  				}
   160  
   161  				wantConn := map[string]string{
   162  					"default": "true",
   163  				}
   164  				var gotConn map[string]string
   165  				gohcl2.DecodeBody(p.ConnInfo.Body, nil, &gotConn)
   166  				if !reflect.DeepEqual(gotConn, wantConn) {
   167  					t.Errorf("wrong Resources[1].Provisioners[0].ConnInfo.Body %#v; want %#v", gotConn, wantConn)
   168  				}
   169  			}
   170  
   171  			// We'll use these throwaway structs to more easily decode and
   172  			// compare the main config body.
   173  			type instanceNetworkInterface struct {
   174  				DeviceIndex int    `hcl:"device_index"`
   175  				Description string `hcl:"description"`
   176  			}
   177  			type instanceConfig struct {
   178  				AMI              string                   `hcl:"ami"`
   179  				SecurityGroups   []string                 `hcl:"security_groups"`
   180  				NetworkInterface instanceNetworkInterface `hcl:"network_interface,block"`
   181  			}
   182  			var gotConfig instanceConfig
   183  			wantConfig := instanceConfig{
   184  				AMI:            "ami-abc123",
   185  				SecurityGroups: []string{"foo", "sg-firewall"},
   186  				NetworkInterface: instanceNetworkInterface{
   187  					DeviceIndex: 0,
   188  					Description: "Main network interface",
   189  				},
   190  			}
   191  			ctx := &hcl2.EvalContext{
   192  				Variables: map[string]cty.Value{
   193  					"var": cty.ObjectVal(map[string]cty.Value{
   194  						"foo": cty.StringVal("ami-abc123"),
   195  					}),
   196  					"aws_security_group": cty.ObjectVal(map[string]cty.Value{
   197  						"firewall": cty.ObjectVal(map[string]cty.Value{
   198  							"foo": cty.StringVal("sg-firewall"),
   199  						}),
   200  					}),
   201  				},
   202  			}
   203  			diags := gohcl2.DecodeBody(r.RawConfig.Body, ctx, &gotConfig)
   204  			if len(diags) != 0 {
   205  				t.Errorf("unexpected diagnostics decoding Resources[1].RawConfig.Body")
   206  				for _, diag := range diags {
   207  					t.Logf("- %s", diag.Error())
   208  				}
   209  			}
   210  			if !reflect.DeepEqual(gotConfig, wantConfig) {
   211  				t.Errorf("wrong Resources[1].RawConfig.Body %#v; want %#v", gotConfig, wantConfig)
   212  			}
   213  
   214  		}
   215  		{
   216  			r := cfg.Resources[2]
   217  
   218  			if got, want := r.Id(), "aws_instance.db"; got != want {
   219  				t.Errorf("wrong Resources[2].Id() %#v; want %#v", got, want)
   220  			}
   221  			if got, want := r.DependsOn, []string{"aws_instance.web"}; !reflect.DeepEqual(got, want) {
   222  				t.Errorf("wrong Resources[2].DependsOn %#v; want %#v", got, want)
   223  			}
   224  
   225  			if got, want := len(r.Provisioners), 1; got != want {
   226  				t.Errorf("wrong Resources[2].Provisioners length %#v; want %#v", got, want)
   227  			} else {
   228  				p := r.Provisioners[0]
   229  
   230  				if got, want := p.Type, "file"; got != want {
   231  					t.Errorf("wrong Resources[2].Provisioners[0].Type %#v; want %#v", got, want)
   232  				}
   233  
   234  				wantConfig := map[string]string{
   235  					"source":      "here",
   236  					"destination": "there",
   237  				}
   238  				var gotConfig map[string]string
   239  				gohcl2.DecodeBody(p.RawConfig.Body, nil, &gotConfig)
   240  				if !reflect.DeepEqual(gotConfig, wantConfig) {
   241  					t.Errorf("wrong Resources[2].Provisioners[0].RawConfig.Body %#v; want %#v", gotConfig, wantConfig)
   242  				}
   243  
   244  				wantConn := map[string]string{
   245  					"default": "false",
   246  				}
   247  				var gotConn map[string]string
   248  				gohcl2.DecodeBody(p.ConnInfo.Body, nil, &gotConn)
   249  				if !reflect.DeepEqual(gotConn, wantConn) {
   250  					t.Errorf("wrong Resources[2].Provisioners[0].ConnInfo.Body %#v; want %#v", gotConn, wantConn)
   251  				}
   252  			}
   253  		}
   254  		{
   255  			r := cfg.Resources[3]
   256  
   257  			if got, want := r.Id(), "data.do.simple"; got != want {
   258  				t.Errorf("wrong Resources[3].Id() %#v; want %#v", got, want)
   259  			}
   260  			if got, want := r.DependsOn, []string(nil); !reflect.DeepEqual(got, want) {
   261  				t.Errorf("wrong Resources[3].DependsOn %#v; want %#v", got, want)
   262  			}
   263  			if got, want := r.Provider, "do.foo"; got != want {
   264  				t.Errorf("wrong Resources[3].Provider %#v; want %#v", got, want)
   265  			}
   266  
   267  			wantConfig := map[string]string{
   268  				"foo": "baz",
   269  			}
   270  			var gotConfig map[string]string
   271  			gohcl2.DecodeBody(r.RawConfig.Body, nil, &gotConfig)
   272  			if !reflect.DeepEqual(gotConfig, wantConfig) {
   273  				t.Errorf("wrong Resources[3].RawConfig.Body %#v; want %#v", gotConfig, wantConfig)
   274  			}
   275  		}
   276  		{
   277  			r := cfg.Resources[4]
   278  
   279  			if got, want := r.Id(), "data.do.depends"; got != want {
   280  				t.Errorf("wrong Resources[4].Id() %#v; want %#v", got, want)
   281  			}
   282  			if got, want := r.DependsOn, []string{"data.do.simple"}; !reflect.DeepEqual(got, want) {
   283  				t.Errorf("wrong Resources[4].DependsOn %#v; want %#v", got, want)
   284  			}
   285  			if got, want := r.Provider, ""; got != want {
   286  				t.Errorf("wrong Resources[4].Provider %#v; want %#v", got, want)
   287  			}
   288  
   289  			wantConfig := map[string]string{}
   290  			var gotConfig map[string]string
   291  			gohcl2.DecodeBody(r.RawConfig.Body, nil, &gotConfig)
   292  			if !reflect.DeepEqual(gotConfig, wantConfig) {
   293  				t.Errorf("wrong Resources[4].RawConfig.Body %#v; want %#v", gotConfig, wantConfig)
   294  			}
   295  		}
   296  	}
   297  
   298  	// "variable" blocks
   299  	if got, want := len(cfg.Variables), 3; got != want {
   300  		t.Errorf("Variables slice has wrong length %#v; want %#v", got, want)
   301  	} else {
   302  		{
   303  			v := cfg.Variables[0]
   304  
   305  			if got, want := v.Name, "foo"; got != want {
   306  				t.Errorf("wrong Variables[0].Name %#v; want %#v", got, want)
   307  			}
   308  			if got, want := v.Default, "bar"; got != want {
   309  				t.Errorf("wrong Variables[0].Default %#v; want %#v", got, want)
   310  			}
   311  			if got, want := v.Description, "barbar"; got != want {
   312  				t.Errorf("wrong Variables[0].Description %#v; want %#v", got, want)
   313  			}
   314  			if got, want := v.DeclaredType, ""; got != want {
   315  				t.Errorf("wrong Variables[0].DeclaredType %#v; want %#v", got, want)
   316  			}
   317  		}
   318  		{
   319  			v := cfg.Variables[1]
   320  
   321  			if got, want := v.Name, "bar"; got != want {
   322  				t.Errorf("wrong Variables[1].Name %#v; want %#v", got, want)
   323  			}
   324  			if got, want := v.Default, interface{}(nil); got != want {
   325  				t.Errorf("wrong Variables[1].Default %#v; want %#v", got, want)
   326  			}
   327  			if got, want := v.Description, ""; got != want {
   328  				t.Errorf("wrong Variables[1].Description %#v; want %#v", got, want)
   329  			}
   330  			if got, want := v.DeclaredType, "string"; got != want {
   331  				t.Errorf("wrong Variables[1].DeclaredType %#v; want %#v", got, want)
   332  			}
   333  		}
   334  		{
   335  			v := cfg.Variables[2]
   336  
   337  			if got, want := v.Name, "baz"; got != want {
   338  				t.Errorf("wrong Variables[2].Name %#v; want %#v", got, want)
   339  			}
   340  			if got, want := v.Default, map[string]interface{}{"key": "value"}; !reflect.DeepEqual(got, want) {
   341  				t.Errorf("wrong Variables[2].Default %#v; want %#v", got, want)
   342  			}
   343  			if got, want := v.Description, ""; got != want {
   344  				t.Errorf("wrong Variables[2].Description %#v; want %#v", got, want)
   345  			}
   346  			if got, want := v.DeclaredType, "map"; got != want {
   347  				t.Errorf("wrong Variables[2].DeclaredType %#v; want %#v", got, want)
   348  			}
   349  		}
   350  	}
   351  
   352  	// "output" blocks
   353  	if got, want := len(cfg.Outputs), 2; got != want {
   354  		t.Errorf("Outputs slice has wrong length %#v; want %#v", got, want)
   355  	} else {
   356  		{
   357  			o := cfg.Outputs[0]
   358  
   359  			if got, want := o.Name, "web_ip"; got != want {
   360  				t.Errorf("wrong Outputs[0].Name %#v; want %#v", got, want)
   361  			}
   362  			if got, want := o.DependsOn, []string(nil); !reflect.DeepEqual(got, want) {
   363  				t.Errorf("wrong Outputs[0].DependsOn %#v; want %#v", got, want)
   364  			}
   365  			if got, want := o.Description, ""; got != want {
   366  				t.Errorf("wrong Outputs[0].Description %#v; want %#v", got, want)
   367  			}
   368  			if got, want := o.Sensitive, true; got != want {
   369  				t.Errorf("wrong Outputs[0].Sensitive %#v; want %#v", got, want)
   370  			}
   371  
   372  			wantConfig := map[string]string{
   373  				"value": "312.213.645.123",
   374  			}
   375  			var gotConfig map[string]string
   376  			ctx := &hcl2.EvalContext{
   377  				Variables: map[string]cty.Value{
   378  					"aws_instance": cty.ObjectVal(map[string]cty.Value{
   379  						"web": cty.ObjectVal(map[string]cty.Value{
   380  							"private_ip": cty.StringVal("312.213.645.123"),
   381  						}),
   382  					}),
   383  				},
   384  			}
   385  			gohcl2.DecodeBody(o.RawConfig.Body, ctx, &gotConfig)
   386  			if !reflect.DeepEqual(gotConfig, wantConfig) {
   387  				t.Errorf("wrong Outputs[0].RawConfig.Body %#v; want %#v", gotConfig, wantConfig)
   388  			}
   389  		}
   390  		{
   391  			o := cfg.Outputs[1]
   392  
   393  			if got, want := o.Name, "web_id"; got != want {
   394  				t.Errorf("wrong Outputs[1].Name %#v; want %#v", got, want)
   395  			}
   396  			if got, want := o.DependsOn, []string{"aws_instance.db"}; !reflect.DeepEqual(got, want) {
   397  				t.Errorf("wrong Outputs[1].DependsOn %#v; want %#v", got, want)
   398  			}
   399  			if got, want := o.Description, "The ID"; got != want {
   400  				t.Errorf("wrong Outputs[1].Description %#v; want %#v", got, want)
   401  			}
   402  			if got, want := o.Sensitive, false; got != want {
   403  				t.Errorf("wrong Outputs[1].Sensitive %#v; want %#v", got, want)
   404  			}
   405  		}
   406  	}
   407  
   408  	// "provider" blocks
   409  	if got, want := len(cfg.ProviderConfigs), 2; got != want {
   410  		t.Errorf("ProviderConfigs slice has wrong length %#v; want %#v", got, want)
   411  	} else {
   412  		{
   413  			p := cfg.ProviderConfigs[0]
   414  
   415  			if got, want := p.Name, "aws"; got != want {
   416  				t.Errorf("wrong ProviderConfigs[0].Name %#v; want %#v", got, want)
   417  			}
   418  			if got, want := p.Alias, ""; got != want {
   419  				t.Errorf("wrong ProviderConfigs[0].Alias %#v; want %#v", got, want)
   420  			}
   421  			if got, want := p.Version, "1.0.0"; got != want {
   422  				t.Errorf("wrong ProviderConfigs[0].Version %#v; want %#v", got, want)
   423  			}
   424  
   425  			wantConfig := map[string]string{
   426  				"access_key": "foo",
   427  				"secret_key": "bar",
   428  			}
   429  			var gotConfig map[string]string
   430  			gohcl2.DecodeBody(p.RawConfig.Body, nil, &gotConfig)
   431  			if !reflect.DeepEqual(gotConfig, wantConfig) {
   432  				t.Errorf("wrong ProviderConfigs[0].RawConfig.Body %#v; want %#v", gotConfig, wantConfig)
   433  			}
   434  
   435  		}
   436  		{
   437  			p := cfg.ProviderConfigs[1]
   438  
   439  			if got, want := p.Name, "do"; got != want {
   440  				t.Errorf("wrong ProviderConfigs[1].Name %#v; want %#v", got, want)
   441  			}
   442  			if got, want := p.Alias, "fum"; got != want {
   443  				t.Errorf("wrong ProviderConfigs[1].Alias %#v; want %#v", got, want)
   444  			}
   445  			if got, want := p.Version, ""; got != want {
   446  				t.Errorf("wrong ProviderConfigs[1].Version %#v; want %#v", got, want)
   447  			}
   448  
   449  		}
   450  	}
   451  
   452  	// "locals" definitions
   453  	if got, want := len(cfg.Locals), 5; got != want {
   454  		t.Errorf("Locals slice has wrong length %#v; want %#v", got, want)
   455  	} else {
   456  		{
   457  			l := cfg.Locals[0]
   458  
   459  			if got, want := l.Name, "security_group_ids"; got != want {
   460  				t.Errorf("wrong Locals[0].Name %#v; want %#v", got, want)
   461  			}
   462  
   463  			wantConfig := map[string][]string{
   464  				"value": []string{"sg-abc123"},
   465  			}
   466  			var gotConfig map[string][]string
   467  			ctx := &hcl2.EvalContext{
   468  				Variables: map[string]cty.Value{
   469  					"aws_security_group": cty.ObjectVal(map[string]cty.Value{
   470  						"firewall": cty.ObjectVal(map[string]cty.Value{
   471  							"id": cty.StringVal("sg-abc123"),
   472  						}),
   473  					}),
   474  				},
   475  			}
   476  			gohcl2.DecodeBody(l.RawConfig.Body, ctx, &gotConfig)
   477  			if !reflect.DeepEqual(gotConfig, wantConfig) {
   478  				t.Errorf("wrong Locals[0].RawConfig.Body %#v; want %#v", gotConfig, wantConfig)
   479  			}
   480  		}
   481  		{
   482  			l := cfg.Locals[1]
   483  
   484  			if got, want := l.Name, "web_ip"; got != want {
   485  				t.Errorf("wrong Locals[1].Name %#v; want %#v", got, want)
   486  			}
   487  		}
   488  		{
   489  			l := cfg.Locals[2]
   490  
   491  			if got, want := l.Name, "literal"; got != want {
   492  				t.Errorf("wrong Locals[2].Name %#v; want %#v", got, want)
   493  			}
   494  		}
   495  		{
   496  			l := cfg.Locals[3]
   497  
   498  			if got, want := l.Name, "literal_list"; got != want {
   499  				t.Errorf("wrong Locals[3].Name %#v; want %#v", got, want)
   500  			}
   501  		}
   502  		{
   503  			l := cfg.Locals[4]
   504  
   505  			if got, want := l.Name, "literal_map"; got != want {
   506  				t.Errorf("wrong Locals[4].Name %#v; want %#v", got, want)
   507  			}
   508  		}
   509  	}
   510  }