github.com/nathanielks/terraform@v0.6.1-0.20170509030759-13e1a62319dc/config/loader_test.go (about)

     1  package config
     2  
     3  import (
     4  	"io/ioutil"
     5  	"path/filepath"
     6  	"reflect"
     7  	"strings"
     8  	"testing"
     9  )
    10  
    11  func TestErrNoConfigsFound_impl(t *testing.T) {
    12  	var _ error = new(ErrNoConfigsFound)
    13  }
    14  
    15  func TestIsEmptyDir(t *testing.T) {
    16  	val, err := IsEmptyDir(fixtureDir)
    17  	if err != nil {
    18  		t.Fatalf("err: %s", err)
    19  	}
    20  	if val {
    21  		t.Fatal("should not be empty")
    22  	}
    23  }
    24  
    25  func TestIsEmptyDir_noExist(t *testing.T) {
    26  	val, err := IsEmptyDir(filepath.Join(fixtureDir, "nopenopenope"))
    27  	if err != nil {
    28  		t.Fatalf("err: %s", err)
    29  	}
    30  	if !val {
    31  		t.Fatal("should be empty")
    32  	}
    33  }
    34  
    35  func TestIsEmptyDir_noConfigs(t *testing.T) {
    36  	val, err := IsEmptyDir(filepath.Join(fixtureDir, "dir-empty"))
    37  	if err != nil {
    38  		t.Fatalf("err: %s", err)
    39  	}
    40  	if !val {
    41  		t.Fatal("should be empty")
    42  	}
    43  }
    44  
    45  func TestLoadFile_badType(t *testing.T) {
    46  	_, err := LoadFile(filepath.Join(fixtureDir, "bad_type.tf.nope"))
    47  	if err == nil {
    48  		t.Fatal("should have error")
    49  	}
    50  }
    51  
    52  func TestLoadFile_gitCrypt(t *testing.T) {
    53  	_, err := LoadFile(filepath.Join(fixtureDir, "git-crypt.tf"))
    54  	if err == nil {
    55  		t.Fatal("should have error")
    56  	}
    57  
    58  	t.Logf("err: %s", err)
    59  }
    60  
    61  func TestLoadFile_lifecycleKeyCheck(t *testing.T) {
    62  	_, err := LoadFile(filepath.Join(fixtureDir, "lifecycle_cbd_typo.tf"))
    63  	if err == nil {
    64  		t.Fatal("should have error")
    65  	}
    66  
    67  	t.Logf("err: %s", err)
    68  }
    69  
    70  func TestLoadFile_varInvalidKey(t *testing.T) {
    71  	_, err := LoadFile(filepath.Join(fixtureDir, "var-invalid-key.tf"))
    72  	if err == nil {
    73  		t.Fatal("should have error")
    74  	}
    75  }
    76  
    77  func TestLoadFile_resourceArityMistake(t *testing.T) {
    78  	_, err := LoadFile(filepath.Join(fixtureDir, "resource-arity-mistake.tf"))
    79  	if err == nil {
    80  		t.Fatal("should have error")
    81  	}
    82  	expected := "Error loading test-fixtures/resource-arity-mistake.tf: position 2:10: resource must be followed by exactly two strings, a type and a name"
    83  	if err.Error() != expected {
    84  		t.Fatalf("expected:\n%s\ngot:\n%s", expected, err)
    85  	}
    86  }
    87  
    88  func TestLoadFile_resourceMultiLifecycle(t *testing.T) {
    89  	_, err := LoadFile(filepath.Join(fixtureDir, "resource-multi-lifecycle.tf"))
    90  	if err == nil {
    91  		t.Fatal("should have error")
    92  	}
    93  }
    94  
    95  func TestLoadFile_dataSourceArityMistake(t *testing.T) {
    96  	_, err := LoadFile(filepath.Join(fixtureDir, "data-source-arity-mistake.tf"))
    97  	if err == nil {
    98  		t.Fatal("should have error")
    99  	}
   100  	expected := "Error loading test-fixtures/data-source-arity-mistake.tf: position 2:6: 'data' must be followed by exactly two strings: a type and a name"
   101  	if err.Error() != expected {
   102  		t.Fatalf("expected:\n%s\ngot:\n%s", expected, err)
   103  	}
   104  }
   105  
   106  func TestLoadFileWindowsLineEndings(t *testing.T) {
   107  	testFile := filepath.Join(fixtureDir, "windows-line-endings.tf")
   108  
   109  	contents, err := ioutil.ReadFile(testFile)
   110  	if err != nil {
   111  		t.Fatalf("err: %s", err)
   112  	}
   113  	if !strings.Contains(string(contents), "\r\n") {
   114  		t.Fatalf("Windows line endings test file %s contains no windows line endings - this may be an autocrlf related issue.", testFile)
   115  	}
   116  
   117  	c, err := LoadFile(testFile)
   118  	if err != nil {
   119  		t.Fatalf("err: %s", err)
   120  	}
   121  
   122  	if c == nil {
   123  		t.Fatal("config should not be nil")
   124  	}
   125  
   126  	if c.Dir != "" {
   127  		t.Fatalf("bad: %#v", c.Dir)
   128  	}
   129  
   130  	actual := resourcesStr(c.Resources)
   131  	if actual != strings.TrimSpace(windowsHeredocResourcesStr) {
   132  		t.Fatalf("bad:\n%s", actual)
   133  	}
   134  }
   135  
   136  func TestLoadFileHeredoc(t *testing.T) {
   137  	c, err := LoadFile(filepath.Join(fixtureDir, "heredoc.tf"))
   138  	if err != nil {
   139  		t.Fatalf("err: %s", err)
   140  	}
   141  
   142  	if c == nil {
   143  		t.Fatal("config should not be nil")
   144  	}
   145  
   146  	if c.Dir != "" {
   147  		t.Fatalf("bad: %#v", c.Dir)
   148  	}
   149  
   150  	actual := providerConfigsStr(c.ProviderConfigs)
   151  	if actual != strings.TrimSpace(heredocProvidersStr) {
   152  		t.Fatalf("bad:\n%s", actual)
   153  	}
   154  
   155  	actual = resourcesStr(c.Resources)
   156  	if actual != strings.TrimSpace(heredocResourcesStr) {
   157  		t.Fatalf("bad:\n%s", actual)
   158  	}
   159  }
   160  
   161  func TestLoadFileEscapedQuotes(t *testing.T) {
   162  	_, err := LoadFile(filepath.Join(fixtureDir, "escapedquotes.tf"))
   163  	if err == nil {
   164  		t.Fatalf("expected syntax error as escaped quotes are no longer supported")
   165  	}
   166  
   167  	if !strings.Contains(err.Error(), "parse error") {
   168  		t.Fatalf("expected \"syntax error\", got: %s", err)
   169  	}
   170  }
   171  
   172  func TestLoadFileBasic(t *testing.T) {
   173  	c, err := LoadFile(filepath.Join(fixtureDir, "basic.tf"))
   174  	if err != nil {
   175  		t.Fatalf("err: %s", err)
   176  	}
   177  
   178  	if c == nil {
   179  		t.Fatal("config should not be nil")
   180  	}
   181  
   182  	if c.Dir != "" {
   183  		t.Fatalf("bad: %#v", c.Dir)
   184  	}
   185  
   186  	expectedTF := &Terraform{RequiredVersion: "foo"}
   187  	if !reflect.DeepEqual(c.Terraform, expectedTF) {
   188  		t.Fatalf("bad: %#v", c.Terraform)
   189  	}
   190  
   191  	expectedAtlas := &AtlasConfig{Name: "mitchellh/foo"}
   192  	if !reflect.DeepEqual(c.Atlas, expectedAtlas) {
   193  		t.Fatalf("bad: %#v", c.Atlas)
   194  	}
   195  
   196  	actual := variablesStr(c.Variables)
   197  	if actual != strings.TrimSpace(basicVariablesStr) {
   198  		t.Fatalf("bad:\n%s", actual)
   199  	}
   200  
   201  	actual = providerConfigsStr(c.ProviderConfigs)
   202  	if actual != strings.TrimSpace(basicProvidersStr) {
   203  		t.Fatalf("bad:\n%s", actual)
   204  	}
   205  
   206  	actual = resourcesStr(c.Resources)
   207  	if actual != strings.TrimSpace(basicResourcesStr) {
   208  		t.Fatalf("bad:\n%s", actual)
   209  	}
   210  
   211  	actual = outputsStr(c.Outputs)
   212  	if actual != strings.TrimSpace(basicOutputsStr) {
   213  		t.Fatalf("bad:\n%s", actual)
   214  	}
   215  }
   216  
   217  func TestLoadFileBasic_empty(t *testing.T) {
   218  	c, err := LoadFile(filepath.Join(fixtureDir, "empty.tf"))
   219  	if err != nil {
   220  		t.Fatalf("err: %s", err)
   221  	}
   222  
   223  	if c == nil {
   224  		t.Fatal("config should not be nil")
   225  	}
   226  }
   227  
   228  func TestLoadFileBasic_import(t *testing.T) {
   229  	// Skip because we disabled importing
   230  	t.Skip()
   231  
   232  	c, err := LoadFile(filepath.Join(fixtureDir, "import.tf"))
   233  	if err != nil {
   234  		t.Fatalf("err: %s", err)
   235  	}
   236  
   237  	if c == nil {
   238  		t.Fatal("config should not be nil")
   239  	}
   240  
   241  	actual := variablesStr(c.Variables)
   242  	if actual != strings.TrimSpace(importVariablesStr) {
   243  		t.Fatalf("bad:\n%s", actual)
   244  	}
   245  
   246  	actual = providerConfigsStr(c.ProviderConfigs)
   247  	if actual != strings.TrimSpace(importProvidersStr) {
   248  		t.Fatalf("bad:\n%s", actual)
   249  	}
   250  
   251  	actual = resourcesStr(c.Resources)
   252  	if actual != strings.TrimSpace(importResourcesStr) {
   253  		t.Fatalf("bad:\n%s", actual)
   254  	}
   255  }
   256  
   257  func TestLoadFileBasic_json(t *testing.T) {
   258  	c, err := LoadFile(filepath.Join(fixtureDir, "basic.tf.json"))
   259  	if err != nil {
   260  		t.Fatalf("err: %s", err)
   261  	}
   262  
   263  	if c == nil {
   264  		t.Fatal("config should not be nil")
   265  	}
   266  
   267  	if c.Dir != "" {
   268  		t.Fatalf("bad: %#v", c.Dir)
   269  	}
   270  
   271  	expectedAtlas := &AtlasConfig{Name: "mitchellh/foo"}
   272  	if !reflect.DeepEqual(c.Atlas, expectedAtlas) {
   273  		t.Fatalf("bad: %#v", c.Atlas)
   274  	}
   275  
   276  	actual := variablesStr(c.Variables)
   277  	if actual != strings.TrimSpace(basicVariablesStr) {
   278  		t.Fatalf("bad:\n%s", actual)
   279  	}
   280  
   281  	actual = providerConfigsStr(c.ProviderConfigs)
   282  	if actual != strings.TrimSpace(basicProvidersStr) {
   283  		t.Fatalf("bad:\n%s", actual)
   284  	}
   285  
   286  	actual = resourcesStr(c.Resources)
   287  	if actual != strings.TrimSpace(basicResourcesStr) {
   288  		t.Fatalf("bad:\n%s", actual)
   289  	}
   290  
   291  	actual = outputsStr(c.Outputs)
   292  	if actual != strings.TrimSpace(basicOutputsStr) {
   293  		t.Fatalf("bad:\n%s", actual)
   294  	}
   295  }
   296  
   297  func TestLoadFileBasic_modules(t *testing.T) {
   298  	c, err := LoadFile(filepath.Join(fixtureDir, "modules.tf"))
   299  	if err != nil {
   300  		t.Fatalf("err: %s", err)
   301  	}
   302  
   303  	if c == nil {
   304  		t.Fatal("config should not be nil")
   305  	}
   306  
   307  	if c.Dir != "" {
   308  		t.Fatalf("bad: %#v", c.Dir)
   309  	}
   310  
   311  	actual := modulesStr(c.Modules)
   312  	if actual != strings.TrimSpace(modulesModulesStr) {
   313  		t.Fatalf("bad:\n%s", actual)
   314  	}
   315  }
   316  
   317  func TestLoadFile_unnamedModule(t *testing.T) {
   318  	_, err := LoadFile(filepath.Join(fixtureDir, "module-unnamed.tf"))
   319  	if err == nil {
   320  		t.Fatalf("bad: expected error")
   321  	}
   322  
   323  	errorStr := err.Error()
   324  	if !strings.Contains(errorStr, `"module" must be followed`) {
   325  		t.Fatalf("bad: expected error has wrong text: %s", errorStr)
   326  	}
   327  }
   328  
   329  func TestLoadFile_outputDependsOn(t *testing.T) {
   330  	c, err := LoadFile(filepath.Join(fixtureDir, "output-depends-on.tf"))
   331  	if err != nil {
   332  		t.Fatalf("err: %s", err)
   333  	}
   334  
   335  	if c == nil {
   336  		t.Fatal("config should not be nil")
   337  	}
   338  
   339  	if c.Dir != "" {
   340  		t.Fatalf("bad: %#v", c.Dir)
   341  	}
   342  
   343  	actual := outputsStr(c.Outputs)
   344  	if actual != strings.TrimSpace(outputDependsOnStr) {
   345  		t.Fatalf("bad:\n%s", actual)
   346  	}
   347  }
   348  
   349  func TestLoadFile_terraformBackend(t *testing.T) {
   350  	c, err := LoadFile(filepath.Join(fixtureDir, "terraform-backend.tf"))
   351  	if err != nil {
   352  		t.Fatalf("err: %s", err)
   353  	}
   354  
   355  	if c == nil {
   356  		t.Fatal("config should not be nil")
   357  	}
   358  
   359  	if c.Dir != "" {
   360  		t.Fatalf("bad: %#v", c.Dir)
   361  	}
   362  
   363  	{
   364  		actual := terraformStr(c.Terraform)
   365  		expected := strings.TrimSpace(`
   366  backend (s3)
   367    foo`)
   368  		if actual != expected {
   369  			t.Fatalf("bad:\n%s", actual)
   370  		}
   371  	}
   372  }
   373  
   374  func TestLoadFile_terraformBackendJSON(t *testing.T) {
   375  	c, err := LoadFile(filepath.Join(fixtureDir, "terraform-backend.tf.json"))
   376  	if err != nil {
   377  		t.Fatalf("err: %s", err)
   378  	}
   379  
   380  	if c == nil {
   381  		t.Fatal("config should not be nil")
   382  	}
   383  
   384  	if c.Dir != "" {
   385  		t.Fatalf("bad: %#v", c.Dir)
   386  	}
   387  
   388  	{
   389  		actual := terraformStr(c.Terraform)
   390  		expected := strings.TrimSpace(`
   391  backend (s3)
   392    foo`)
   393  		if actual != expected {
   394  			t.Fatalf("bad:\n%s", actual)
   395  		}
   396  	}
   397  }
   398  
   399  // test that the alternate, more obvious JSON format also decodes properly
   400  func TestLoadFile_terraformBackendJSON2(t *testing.T) {
   401  	c, err := LoadFile(filepath.Join(fixtureDir, "terraform-backend-2.tf.json"))
   402  	if err != nil {
   403  		t.Fatalf("err: %s", err)
   404  	}
   405  
   406  	if c == nil {
   407  		t.Fatal("config should not be nil")
   408  	}
   409  
   410  	if c.Dir != "" {
   411  		t.Fatalf("bad: %#v", c.Dir)
   412  	}
   413  
   414  	{
   415  		actual := terraformStr(c.Terraform)
   416  		expected := strings.TrimSpace(`
   417  backend (s3)
   418    foo`)
   419  		if actual != expected {
   420  			t.Fatalf("bad:\n%s", actual)
   421  		}
   422  	}
   423  }
   424  
   425  func TestLoadFile_terraformBackendMulti(t *testing.T) {
   426  	_, err := LoadFile(filepath.Join(fixtureDir, "terraform-backend-multi.tf"))
   427  	if err == nil {
   428  		t.Fatal("expected error")
   429  	}
   430  
   431  	errorStr := err.Error()
   432  	if !strings.Contains(errorStr, "only one 'backend'") {
   433  		t.Fatalf("bad: expected error has wrong text: %s", errorStr)
   434  	}
   435  }
   436  
   437  func TestLoadJSONBasic(t *testing.T) {
   438  	raw, err := ioutil.ReadFile(filepath.Join(fixtureDir, "basic.tf.json"))
   439  	if err != nil {
   440  		t.Fatalf("err: %s", err)
   441  	}
   442  
   443  	c, err := LoadJSON(raw)
   444  	if err != nil {
   445  		t.Fatalf("err: %s", err)
   446  	}
   447  
   448  	if c == nil {
   449  		t.Fatal("config should not be nil")
   450  	}
   451  
   452  	if c.Dir != "" {
   453  		t.Fatalf("bad: %#v", c.Dir)
   454  	}
   455  
   456  	expectedAtlas := &AtlasConfig{Name: "mitchellh/foo"}
   457  	if !reflect.DeepEqual(c.Atlas, expectedAtlas) {
   458  		t.Fatalf("bad: %#v", c.Atlas)
   459  	}
   460  
   461  	actual := variablesStr(c.Variables)
   462  	if actual != strings.TrimSpace(basicVariablesStr) {
   463  		t.Fatalf("bad:\n%s", actual)
   464  	}
   465  
   466  	actual = providerConfigsStr(c.ProviderConfigs)
   467  	if actual != strings.TrimSpace(basicProvidersStr) {
   468  		t.Fatalf("bad:\n%s", actual)
   469  	}
   470  
   471  	actual = resourcesStr(c.Resources)
   472  	if actual != strings.TrimSpace(basicResourcesStr) {
   473  		t.Fatalf("bad:\n%s", actual)
   474  	}
   475  
   476  	actual = outputsStr(c.Outputs)
   477  	if actual != strings.TrimSpace(basicOutputsStr) {
   478  		t.Fatalf("bad:\n%s", actual)
   479  	}
   480  }
   481  
   482  func TestLoadJSONAmbiguous(t *testing.T) {
   483  	js := `
   484  {
   485    "variable": {
   486      "first": {
   487        "default": {
   488          "key": "val"
   489        }
   490      },
   491      "second": {
   492        "description": "Described",
   493        "default": {
   494          "key": "val"
   495        }
   496      }
   497    }
   498  }
   499  `
   500  
   501  	c, err := LoadJSON([]byte(js))
   502  	if err != nil {
   503  		t.Fatalf("err: %s", err)
   504  	}
   505  
   506  	if len(c.Variables) != 2 {
   507  		t.Fatal("config should have 2 variables, found", len(c.Variables))
   508  	}
   509  
   510  	first := &Variable{
   511  		Name:    "first",
   512  		Default: map[string]interface{}{"key": "val"},
   513  	}
   514  	second := &Variable{
   515  		Name:        "second",
   516  		Description: "Described",
   517  		Default:     map[string]interface{}{"key": "val"},
   518  	}
   519  
   520  	if !reflect.DeepEqual(first, c.Variables[0]) {
   521  		t.Fatalf("\nexpected: %#v\ngot:      %#v", first, c.Variables[0])
   522  	}
   523  
   524  	if !reflect.DeepEqual(second, c.Variables[1]) {
   525  		t.Fatalf("\nexpected: %#v\ngot:      %#v", second, c.Variables[1])
   526  	}
   527  }
   528  
   529  func TestLoadFileBasic_jsonNoName(t *testing.T) {
   530  	c, err := LoadFile(filepath.Join(fixtureDir, "resource-no-name.tf.json"))
   531  	if err != nil {
   532  		t.Fatalf("err: %s", err)
   533  	}
   534  
   535  	if c == nil {
   536  		t.Fatal("config should not be nil")
   537  	}
   538  
   539  	actual := resourcesStr(c.Resources)
   540  	if actual != strings.TrimSpace(basicJsonNoNameResourcesStr) {
   541  		t.Fatalf("bad:\n%s", actual)
   542  	}
   543  }
   544  
   545  func TestLoadFile_variables(t *testing.T) {
   546  	c, err := LoadFile(filepath.Join(fixtureDir, "variables.tf"))
   547  	if err != nil {
   548  		t.Fatalf("err: %s", err)
   549  	}
   550  	if c == nil {
   551  		t.Fatal("config should not be nil")
   552  	}
   553  
   554  	if c.Dir != "" {
   555  		t.Fatalf("bad: %#v", c.Dir)
   556  	}
   557  
   558  	actual := variablesStr(c.Variables)
   559  	if actual != strings.TrimSpace(variablesVariablesStr) {
   560  		t.Fatalf("bad:\n%s", actual)
   561  	}
   562  }
   563  
   564  func TestLoadDir_basic(t *testing.T) {
   565  	dir := filepath.Join(fixtureDir, "dir-basic")
   566  	c, err := LoadDir(dir)
   567  	if err != nil {
   568  		t.Fatalf("err: %s", err)
   569  	}
   570  
   571  	if c == nil {
   572  		t.Fatal("config should not be nil")
   573  	}
   574  
   575  	dirAbs, err := filepath.Abs(dir)
   576  	if err != nil {
   577  		t.Fatalf("err: %s", err)
   578  	}
   579  	if c.Dir != dirAbs {
   580  		t.Fatalf("bad: %#v", c.Dir)
   581  	}
   582  
   583  	actual := variablesStr(c.Variables)
   584  	if actual != strings.TrimSpace(dirBasicVariablesStr) {
   585  		t.Fatalf("bad:\n%s", actual)
   586  	}
   587  
   588  	actual = providerConfigsStr(c.ProviderConfigs)
   589  	if actual != strings.TrimSpace(dirBasicProvidersStr) {
   590  		t.Fatalf("bad:\n%s", actual)
   591  	}
   592  
   593  	actual = resourcesStr(c.Resources)
   594  	if actual != strings.TrimSpace(dirBasicResourcesStr) {
   595  		t.Fatalf("bad:\n%s", actual)
   596  	}
   597  
   598  	actual = outputsStr(c.Outputs)
   599  	if actual != strings.TrimSpace(dirBasicOutputsStr) {
   600  		t.Fatalf("bad:\n%s", actual)
   601  	}
   602  }
   603  
   604  func TestLoadDir_file(t *testing.T) {
   605  	_, err := LoadDir(filepath.Join(fixtureDir, "variables.tf"))
   606  	if err == nil {
   607  		t.Fatal("should error")
   608  	}
   609  }
   610  
   611  func TestLoadDir_noConfigs(t *testing.T) {
   612  	_, err := LoadDir(filepath.Join(fixtureDir, "dir-empty"))
   613  	if err == nil {
   614  		t.Fatal("should error")
   615  	}
   616  }
   617  
   618  func TestLoadDir_noMerge(t *testing.T) {
   619  	c, err := LoadDir(filepath.Join(fixtureDir, "dir-merge"))
   620  	if err != nil {
   621  		t.Fatalf("err: %s", err)
   622  	}
   623  
   624  	if c == nil {
   625  		t.Fatal("config should not be nil")
   626  	}
   627  
   628  	if err := c.Validate(); err == nil {
   629  		t.Fatal("should not be valid")
   630  	}
   631  }
   632  
   633  func TestLoadDir_override(t *testing.T) {
   634  	c, err := LoadDir(filepath.Join(fixtureDir, "dir-override"))
   635  	if err != nil {
   636  		t.Fatalf("err: %s", err)
   637  	}
   638  
   639  	if c == nil {
   640  		t.Fatal("config should not be nil")
   641  	}
   642  
   643  	actual := variablesStr(c.Variables)
   644  	if actual != strings.TrimSpace(dirOverrideVariablesStr) {
   645  		t.Fatalf("bad:\n%s", actual)
   646  	}
   647  
   648  	actual = providerConfigsStr(c.ProviderConfigs)
   649  	if actual != strings.TrimSpace(dirOverrideProvidersStr) {
   650  		t.Fatalf("bad:\n%s", actual)
   651  	}
   652  
   653  	actual = resourcesStr(c.Resources)
   654  	if actual != strings.TrimSpace(dirOverrideResourcesStr) {
   655  		t.Fatalf("bad:\n%s", actual)
   656  	}
   657  
   658  	actual = outputsStr(c.Outputs)
   659  	if actual != strings.TrimSpace(dirOverrideOutputsStr) {
   660  		t.Fatalf("bad:\n%s", actual)
   661  	}
   662  }
   663  
   664  func TestLoadDir_overrideVar(t *testing.T) {
   665  	c, err := LoadDir(filepath.Join(fixtureDir, "dir-override-var"))
   666  	if err != nil {
   667  		t.Fatalf("err: %s", err)
   668  	}
   669  
   670  	if c == nil {
   671  		t.Fatal("config should not be nil")
   672  	}
   673  
   674  	actual := variablesStr(c.Variables)
   675  	if actual != strings.TrimSpace(dirOverrideVarsVariablesStr) {
   676  		t.Fatalf("bad:\n%s", actual)
   677  	}
   678  }
   679  
   680  func TestLoadFile_mismatchedVariableTypes(t *testing.T) {
   681  	_, err := LoadFile(filepath.Join(fixtureDir, "variable-mismatched-type.tf"))
   682  	if err == nil {
   683  		t.Fatalf("bad: expected error")
   684  	}
   685  
   686  	errorStr := err.Error()
   687  	if !strings.Contains(errorStr, "'not_a_map' has a default value which is not of type 'string'") {
   688  		t.Fatalf("bad: expected error has wrong text: %s", errorStr)
   689  	}
   690  }
   691  
   692  func TestLoadFile_badVariableTypes(t *testing.T) {
   693  	_, err := LoadFile(filepath.Join(fixtureDir, "bad-variable-type.tf"))
   694  	if err == nil {
   695  		t.Fatalf("bad: expected error")
   696  	}
   697  
   698  	errorStr := err.Error()
   699  	if !strings.Contains(errorStr, "'bad_type' type must be one of") {
   700  		t.Fatalf("bad: expected error has wrong text: %s", errorStr)
   701  	}
   702  }
   703  
   704  func TestLoadFile_variableNoName(t *testing.T) {
   705  	_, err := LoadFile(filepath.Join(fixtureDir, "variable-no-name.tf"))
   706  	if err == nil {
   707  		t.Fatalf("bad: expected error")
   708  	}
   709  
   710  	errorStr := err.Error()
   711  	if !strings.Contains(errorStr, `"variable" must be followed`) {
   712  		t.Fatalf("bad: expected error has wrong text: %s", errorStr)
   713  	}
   714  }
   715  
   716  func TestLoadFile_provisioners(t *testing.T) {
   717  	c, err := LoadFile(filepath.Join(fixtureDir, "provisioners.tf"))
   718  	if err != nil {
   719  		t.Fatalf("err: %s", err)
   720  	}
   721  
   722  	if c == nil {
   723  		t.Fatal("config should not be nil")
   724  	}
   725  
   726  	actual := resourcesStr(c.Resources)
   727  	if actual != strings.TrimSpace(provisionerResourcesStr) {
   728  		t.Fatalf("bad:\n%s", actual)
   729  	}
   730  }
   731  
   732  func TestLoadFile_provisionersDestroy(t *testing.T) {
   733  	c, err := LoadFile(filepath.Join(fixtureDir, "provisioners-destroy.tf"))
   734  	if err != nil {
   735  		t.Fatalf("err: %s", err)
   736  	}
   737  
   738  	if c == nil {
   739  		t.Fatal("config should not be nil")
   740  	}
   741  
   742  	actual := resourcesStr(c.Resources)
   743  	if actual != strings.TrimSpace(provisionerDestroyResourcesStr) {
   744  		t.Fatalf("bad:\n%s", actual)
   745  	}
   746  }
   747  
   748  func TestLoadFile_unnamedOutput(t *testing.T) {
   749  	_, err := LoadFile(filepath.Join(fixtureDir, "output-unnamed.tf"))
   750  	if err == nil {
   751  		t.Fatalf("bad: expected error")
   752  	}
   753  
   754  	errorStr := err.Error()
   755  	if !strings.Contains(errorStr, `"output" must be followed`) {
   756  		t.Fatalf("bad: expected error has wrong text: %s", errorStr)
   757  	}
   758  }
   759  
   760  func TestLoadFile_connections(t *testing.T) {
   761  	c, err := LoadFile(filepath.Join(fixtureDir, "connection.tf"))
   762  	if err != nil {
   763  		t.Fatalf("err: %s", err)
   764  	}
   765  
   766  	if c == nil {
   767  		t.Fatal("config should not be nil")
   768  	}
   769  
   770  	actual := resourcesStr(c.Resources)
   771  	if actual != strings.TrimSpace(connectionResourcesStr) {
   772  		t.Fatalf("bad:\n%s", actual)
   773  	}
   774  
   775  	// Check for the connection info
   776  	r := c.Resources[0]
   777  	if r.Name != "web" && r.Type != "aws_instance" {
   778  		t.Fatalf("Bad: %#v", r)
   779  	}
   780  
   781  	p1 := r.Provisioners[0]
   782  	if p1.ConnInfo == nil || len(p1.ConnInfo.Raw) != 2 {
   783  		t.Fatalf("Bad: %#v", p1.ConnInfo)
   784  	}
   785  	if p1.ConnInfo.Raw["user"] != "nobody" {
   786  		t.Fatalf("Bad: %#v", p1.ConnInfo)
   787  	}
   788  
   789  	p2 := r.Provisioners[1]
   790  	if p2.ConnInfo == nil || len(p2.ConnInfo.Raw) != 2 {
   791  		t.Fatalf("Bad: %#v", p2.ConnInfo)
   792  	}
   793  	if p2.ConnInfo.Raw["user"] != "root" {
   794  		t.Fatalf("Bad: %#v", p2.ConnInfo)
   795  	}
   796  }
   797  
   798  func TestLoadFile_createBeforeDestroy(t *testing.T) {
   799  	c, err := LoadFile(filepath.Join(fixtureDir, "create-before-destroy.tf"))
   800  	if err != nil {
   801  		t.Fatalf("err: %s", err)
   802  	}
   803  
   804  	if c == nil {
   805  		t.Fatal("config should not be nil")
   806  	}
   807  
   808  	actual := resourcesStr(c.Resources)
   809  	if actual != strings.TrimSpace(createBeforeDestroyResourcesStr) {
   810  		t.Fatalf("bad:\n%s", actual)
   811  	}
   812  
   813  	// Check for the flag value
   814  	r := c.Resources[0]
   815  	if r.Name != "web" && r.Type != "aws_instance" {
   816  		t.Fatalf("Bad: %#v", r)
   817  	}
   818  
   819  	// Should enable create before destroy
   820  	if !r.Lifecycle.CreateBeforeDestroy {
   821  		t.Fatalf("Bad: %#v", r)
   822  	}
   823  
   824  	r = c.Resources[1]
   825  	if r.Name != "bar" && r.Type != "aws_instance" {
   826  		t.Fatalf("Bad: %#v", r)
   827  	}
   828  
   829  	// Should not enable create before destroy
   830  	if r.Lifecycle.CreateBeforeDestroy {
   831  		t.Fatalf("Bad: %#v", r)
   832  	}
   833  }
   834  
   835  func TestLoadFile_ignoreChanges(t *testing.T) {
   836  	c, err := LoadFile(filepath.Join(fixtureDir, "ignore-changes.tf"))
   837  	if err != nil {
   838  		t.Fatalf("err: %s", err)
   839  	}
   840  
   841  	if c == nil {
   842  		t.Fatal("config should not be nil")
   843  	}
   844  
   845  	actual := resourcesStr(c.Resources)
   846  	print(actual)
   847  	if actual != strings.TrimSpace(ignoreChangesResourcesStr) {
   848  		t.Fatalf("bad:\n%s", actual)
   849  	}
   850  
   851  	// Check for the flag value
   852  	r := c.Resources[0]
   853  	if r.Name != "web" && r.Type != "aws_instance" {
   854  		t.Fatalf("Bad: %#v", r)
   855  	}
   856  
   857  	// Should populate ignore changes
   858  	if len(r.Lifecycle.IgnoreChanges) == 0 {
   859  		t.Fatalf("Bad: %#v", r)
   860  	}
   861  
   862  	r = c.Resources[1]
   863  	if r.Name != "bar" && r.Type != "aws_instance" {
   864  		t.Fatalf("Bad: %#v", r)
   865  	}
   866  
   867  	// Should not populate ignore changes
   868  	if len(r.Lifecycle.IgnoreChanges) > 0 {
   869  		t.Fatalf("Bad: %#v", r)
   870  	}
   871  
   872  	r = c.Resources[2]
   873  	if r.Name != "baz" && r.Type != "aws_instance" {
   874  		t.Fatalf("Bad: %#v", r)
   875  	}
   876  
   877  	// Should not populate ignore changes
   878  	if len(r.Lifecycle.IgnoreChanges) > 0 {
   879  		t.Fatalf("Bad: %#v", r)
   880  	}
   881  }
   882  
   883  func TestLoad_preventDestroyString(t *testing.T) {
   884  	c, err := LoadFile(filepath.Join(fixtureDir, "prevent-destroy-string.tf"))
   885  	if err != nil {
   886  		t.Fatalf("err: %s", err)
   887  	}
   888  
   889  	if c == nil {
   890  		t.Fatal("config should not be nil")
   891  	}
   892  
   893  	actual := resourcesStr(c.Resources)
   894  	if actual != strings.TrimSpace(createBeforeDestroyResourcesStr) {
   895  		t.Fatalf("bad:\n%s", actual)
   896  	}
   897  
   898  	// Check for the flag value
   899  	r := c.Resources[0]
   900  	if r.Name != "web" && r.Type != "aws_instance" {
   901  		t.Fatalf("Bad: %#v", r)
   902  	}
   903  
   904  	// Should enable create before destroy
   905  	if !r.Lifecycle.PreventDestroy {
   906  		t.Fatalf("Bad: %#v", r)
   907  	}
   908  
   909  	r = c.Resources[1]
   910  	if r.Name != "bar" && r.Type != "aws_instance" {
   911  		t.Fatalf("Bad: %#v", r)
   912  	}
   913  
   914  	// Should not enable create before destroy
   915  	if r.Lifecycle.PreventDestroy {
   916  		t.Fatalf("Bad: %#v", r)
   917  	}
   918  }
   919  
   920  func TestLoad_temporary_files(t *testing.T) {
   921  	_, err := LoadDir(filepath.Join(fixtureDir, "dir-temporary-files"))
   922  	if err == nil {
   923  		t.Fatalf("Expected to see an error stating no config files found")
   924  	}
   925  }
   926  
   927  func TestLoad_hclAttributes(t *testing.T) {
   928  	c, err := LoadFile(filepath.Join(fixtureDir, "attributes.tf"))
   929  	if err != nil {
   930  		t.Fatalf("Bad: %s", err)
   931  	}
   932  
   933  	if c == nil {
   934  		t.Fatal("config should not be nil")
   935  	}
   936  
   937  	actual := resourcesStr(c.Resources)
   938  	print(actual)
   939  	if actual != strings.TrimSpace(jsonAttributeStr) {
   940  		t.Fatalf("bad:\n%s", actual)
   941  	}
   942  
   943  	r := c.Resources[0]
   944  	if r.Name != "test" && r.Type != "cloudstack_firewall" {
   945  		t.Fatalf("Bad: %#v", r)
   946  	}
   947  
   948  	raw := r.RawConfig
   949  	if raw.Raw["ipaddress"] != "192.168.0.1" {
   950  		t.Fatalf("Bad: %s", raw.Raw["ipAddress"])
   951  	}
   952  
   953  	rule := raw.Raw["rule"].([]map[string]interface{})[0]
   954  	if rule["protocol"] != "tcp" {
   955  		t.Fatalf("Bad: %s", rule["protocol"])
   956  	}
   957  
   958  	if rule["source_cidr"] != "10.0.0.0/8" {
   959  		t.Fatalf("Bad: %s", rule["source_cidr"])
   960  	}
   961  
   962  	ports := rule["ports"].([]interface{})
   963  
   964  	if ports[0] != "80" {
   965  		t.Fatalf("Bad ports: %s", ports[0])
   966  	}
   967  	if ports[1] != "1000-2000" {
   968  		t.Fatalf("Bad ports: %s", ports[1])
   969  	}
   970  }
   971  
   972  func TestLoad_jsonAttributes(t *testing.T) {
   973  	c, err := LoadFile(filepath.Join(fixtureDir, "attributes.tf.json"))
   974  	if err != nil {
   975  		t.Fatalf("Bad: %s", err)
   976  	}
   977  
   978  	if c == nil {
   979  		t.Fatal("config should not be nil")
   980  	}
   981  
   982  	actual := resourcesStr(c.Resources)
   983  	print(actual)
   984  	if actual != strings.TrimSpace(jsonAttributeStr) {
   985  		t.Fatalf("bad:\n%s", actual)
   986  	}
   987  
   988  	r := c.Resources[0]
   989  	if r.Name != "test" && r.Type != "cloudstack_firewall" {
   990  		t.Fatalf("Bad: %#v", r)
   991  	}
   992  
   993  	raw := r.RawConfig
   994  	if raw.Raw["ipaddress"] != "192.168.0.1" {
   995  		t.Fatalf("Bad: %s", raw.Raw["ipAddress"])
   996  	}
   997  
   998  	rule := raw.Raw["rule"].([]map[string]interface{})[0]
   999  	if rule["protocol"] != "tcp" {
  1000  		t.Fatalf("Bad: %s", rule["protocol"])
  1001  	}
  1002  
  1003  	if rule["source_cidr"] != "10.0.0.0/8" {
  1004  		t.Fatalf("Bad: %s", rule["source_cidr"])
  1005  	}
  1006  
  1007  	ports := rule["ports"].([]interface{})
  1008  
  1009  	if ports[0] != "80" {
  1010  		t.Fatalf("Bad ports: %s", ports[0])
  1011  	}
  1012  	if ports[1] != "1000-2000" {
  1013  		t.Fatalf("Bad ports: %s", ports[1])
  1014  	}
  1015  }
  1016  
  1017  const jsonAttributeStr = `
  1018  cloudstack_firewall.test (x1)
  1019    ipaddress
  1020    rule
  1021  `
  1022  
  1023  const windowsHeredocResourcesStr = `
  1024  aws_instance.test (x1)
  1025    user_data
  1026  `
  1027  
  1028  const heredocProvidersStr = `
  1029  aws
  1030    access_key
  1031    secret_key
  1032  `
  1033  
  1034  const heredocResourcesStr = `
  1035  aws_iam_policy.policy (x1)
  1036    description
  1037    name
  1038    path
  1039    policy
  1040  aws_instance.heredocwithnumbers (x1)
  1041    ami
  1042    provisioners
  1043      local-exec
  1044        command
  1045  aws_instance.test (x1)
  1046    ami
  1047    provisioners
  1048      remote-exec
  1049        inline
  1050  `
  1051  
  1052  const basicOutputsStr = `
  1053  web_ip
  1054    vars
  1055      resource: aws_instance.web.private_ip
  1056  `
  1057  
  1058  const basicProvidersStr = `
  1059  aws
  1060    access_key
  1061    secret_key
  1062  do
  1063    api_key
  1064    vars
  1065      user: var.foo
  1066  `
  1067  
  1068  const basicResourcesStr = `
  1069  aws_instance.db (x1)
  1070    VPC
  1071    security_groups
  1072    provisioners
  1073      file
  1074        destination
  1075        source
  1076    dependsOn
  1077      aws_instance.web
  1078    vars
  1079      resource: aws_security_group.firewall.*.id
  1080  aws_instance.web (x1)
  1081    ami
  1082    network_interface
  1083    security_groups
  1084    provisioners
  1085      file
  1086        destination
  1087        source
  1088    vars
  1089      resource: aws_security_group.firewall.foo
  1090      user: var.foo
  1091  aws_security_group.firewall (x5)
  1092  data.do.depends (x1)
  1093    dependsOn
  1094      data.do.simple
  1095  data.do.simple (x1)
  1096    foo
  1097  `
  1098  
  1099  const basicVariablesStr = `
  1100  bar (required) (string)
  1101    <>
  1102    <>
  1103  baz (map)
  1104    map[key:value]
  1105    <>
  1106  foo
  1107    bar
  1108    bar
  1109  `
  1110  
  1111  const basicJsonNoNameResourcesStr = `
  1112  aws_security_group.allow_external_http_https (x1)
  1113    tags
  1114  `
  1115  
  1116  const dirBasicOutputsStr = `
  1117  web_ip
  1118    vars
  1119      resource: aws_instance.web.private_ip
  1120  `
  1121  
  1122  const dirBasicProvidersStr = `
  1123  aws
  1124    access_key
  1125    secret_key
  1126  do
  1127    api_key
  1128    vars
  1129      user: var.foo
  1130  `
  1131  
  1132  const dirBasicResourcesStr = `
  1133  aws_instance.db (x1)
  1134    security_groups
  1135    vars
  1136      resource: aws_security_group.firewall.*.id
  1137  aws_instance.web (x1)
  1138    ami
  1139    network_interface
  1140    security_groups
  1141    vars
  1142      resource: aws_security_group.firewall.foo
  1143      user: var.foo
  1144  aws_security_group.firewall (x5)
  1145  data.do.depends (x1)
  1146    dependsOn
  1147      data.do.simple
  1148  data.do.simple (x1)
  1149    foo
  1150  `
  1151  
  1152  const dirBasicVariablesStr = `
  1153  foo
  1154    bar
  1155    bar
  1156  `
  1157  
  1158  const dirOverrideOutputsStr = `
  1159  web_ip
  1160    vars
  1161      resource: aws_instance.web.private_ip
  1162  `
  1163  
  1164  const dirOverrideProvidersStr = `
  1165  aws
  1166    access_key
  1167    secret_key
  1168  do
  1169    api_key
  1170    vars
  1171      user: var.foo
  1172  `
  1173  
  1174  const dirOverrideResourcesStr = `
  1175  aws_instance.db (x1)
  1176    ami
  1177    security_groups
  1178  aws_instance.web (x1)
  1179    ami
  1180    foo
  1181    network_interface
  1182    security_groups
  1183    vars
  1184      resource: aws_security_group.firewall.foo
  1185      user: var.foo
  1186  aws_security_group.firewall (x5)
  1187  data.do.depends (x1)
  1188    hello
  1189    dependsOn
  1190      data.do.simple
  1191  data.do.simple (x1)
  1192    foo
  1193  `
  1194  
  1195  const dirOverrideVariablesStr = `
  1196  foo
  1197    bar
  1198    bar
  1199  `
  1200  
  1201  const dirOverrideVarsVariablesStr = `
  1202  foo
  1203    baz
  1204    bar
  1205  `
  1206  
  1207  const importProvidersStr = `
  1208  aws
  1209    bar
  1210    foo
  1211  `
  1212  
  1213  const importResourcesStr = `
  1214  aws_security_group.db (x1)
  1215  aws_security_group.web (x1)
  1216  `
  1217  
  1218  const importVariablesStr = `
  1219  bar (required)
  1220    <>
  1221    <>
  1222  foo
  1223    bar
  1224    bar
  1225  `
  1226  
  1227  const modulesModulesStr = `
  1228  bar
  1229    source = baz
  1230    memory
  1231  `
  1232  
  1233  const provisionerResourcesStr = `
  1234  aws_instance.web (x1)
  1235    ami
  1236    security_groups
  1237    provisioners
  1238      shell
  1239        path
  1240    vars
  1241      resource: aws_security_group.firewall.foo
  1242      user: var.foo
  1243  `
  1244  
  1245  const provisionerDestroyResourcesStr = `
  1246  aws_instance.web (x1)
  1247    provisioners
  1248      shell
  1249      shell (destroy)
  1250        path
  1251      shell (destroy)
  1252        on_failure = continue
  1253        path
  1254  `
  1255  
  1256  const connectionResourcesStr = `
  1257  aws_instance.web (x1)
  1258    ami
  1259    security_groups
  1260    provisioners
  1261      shell
  1262        path
  1263      shell
  1264        path
  1265    vars
  1266      resource: aws_security_group.firewall.foo
  1267      user: var.foo
  1268  `
  1269  
  1270  const outputDependsOnStr = `
  1271  value
  1272    dependsOn
  1273      foo
  1274  `
  1275  
  1276  const variablesVariablesStr = `
  1277  bar
  1278    <>
  1279    <>
  1280  baz
  1281    foo
  1282    <>
  1283  foo (required)
  1284    <>
  1285    <>
  1286  `
  1287  
  1288  const createBeforeDestroyResourcesStr = `
  1289  aws_instance.bar (x1)
  1290    ami
  1291  aws_instance.web (x1)
  1292    ami
  1293  `
  1294  
  1295  const ignoreChangesResourcesStr = `
  1296  aws_instance.bar (x1)
  1297    ami
  1298  aws_instance.baz (x1)
  1299    ami
  1300  aws_instance.web (x1)
  1301    ami
  1302  `