github.com/dougneal/terraform@v0.6.15-0.20170330092735-b6a3840768a4/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_outputDependsOn(t *testing.T) {
   318  	c, err := LoadFile(filepath.Join(fixtureDir, "output-depends-on.tf"))
   319  	if err != nil {
   320  		t.Fatalf("err: %s", err)
   321  	}
   322  
   323  	if c == nil {
   324  		t.Fatal("config should not be nil")
   325  	}
   326  
   327  	if c.Dir != "" {
   328  		t.Fatalf("bad: %#v", c.Dir)
   329  	}
   330  
   331  	actual := outputsStr(c.Outputs)
   332  	if actual != strings.TrimSpace(outputDependsOnStr) {
   333  		t.Fatalf("bad:\n%s", actual)
   334  	}
   335  }
   336  
   337  func TestLoadFile_terraformBackend(t *testing.T) {
   338  	c, err := LoadFile(filepath.Join(fixtureDir, "terraform-backend.tf"))
   339  	if err != nil {
   340  		t.Fatalf("err: %s", err)
   341  	}
   342  
   343  	if c == nil {
   344  		t.Fatal("config should not be nil")
   345  	}
   346  
   347  	if c.Dir != "" {
   348  		t.Fatalf("bad: %#v", c.Dir)
   349  	}
   350  
   351  	{
   352  		actual := terraformStr(c.Terraform)
   353  		expected := strings.TrimSpace(`
   354  backend (s3)
   355    foo`)
   356  		if actual != expected {
   357  			t.Fatalf("bad:\n%s", actual)
   358  		}
   359  	}
   360  }
   361  
   362  func TestLoadFile_terraformBackendJSON(t *testing.T) {
   363  	c, err := LoadFile(filepath.Join(fixtureDir, "terraform-backend.tf.json"))
   364  	if err != nil {
   365  		t.Fatalf("err: %s", err)
   366  	}
   367  
   368  	if c == nil {
   369  		t.Fatal("config should not be nil")
   370  	}
   371  
   372  	if c.Dir != "" {
   373  		t.Fatalf("bad: %#v", c.Dir)
   374  	}
   375  
   376  	{
   377  		actual := terraformStr(c.Terraform)
   378  		expected := strings.TrimSpace(`
   379  backend (s3)
   380    foo`)
   381  		if actual != expected {
   382  			t.Fatalf("bad:\n%s", actual)
   383  		}
   384  	}
   385  }
   386  
   387  // test that the alternate, more obvious JSON format also decodes properly
   388  func TestLoadFile_terraformBackendJSON2(t *testing.T) {
   389  	c, err := LoadFile(filepath.Join(fixtureDir, "terraform-backend-2.tf.json"))
   390  	if err != nil {
   391  		t.Fatalf("err: %s", err)
   392  	}
   393  
   394  	if c == nil {
   395  		t.Fatal("config should not be nil")
   396  	}
   397  
   398  	if c.Dir != "" {
   399  		t.Fatalf("bad: %#v", c.Dir)
   400  	}
   401  
   402  	{
   403  		actual := terraformStr(c.Terraform)
   404  		expected := strings.TrimSpace(`
   405  backend (s3)
   406    foo`)
   407  		if actual != expected {
   408  			t.Fatalf("bad:\n%s", actual)
   409  		}
   410  	}
   411  }
   412  
   413  func TestLoadFile_terraformBackendMulti(t *testing.T) {
   414  	_, err := LoadFile(filepath.Join(fixtureDir, "terraform-backend-multi.tf"))
   415  	if err == nil {
   416  		t.Fatal("expected error")
   417  	}
   418  
   419  	errorStr := err.Error()
   420  	if !strings.Contains(errorStr, "only one 'backend'") {
   421  		t.Fatalf("bad: expected error has wrong text: %s", errorStr)
   422  	}
   423  }
   424  
   425  func TestLoadJSONBasic(t *testing.T) {
   426  	raw, err := ioutil.ReadFile(filepath.Join(fixtureDir, "basic.tf.json"))
   427  	if err != nil {
   428  		t.Fatalf("err: %s", err)
   429  	}
   430  
   431  	c, err := LoadJSON(raw)
   432  	if err != nil {
   433  		t.Fatalf("err: %s", err)
   434  	}
   435  
   436  	if c == nil {
   437  		t.Fatal("config should not be nil")
   438  	}
   439  
   440  	if c.Dir != "" {
   441  		t.Fatalf("bad: %#v", c.Dir)
   442  	}
   443  
   444  	expectedAtlas := &AtlasConfig{Name: "mitchellh/foo"}
   445  	if !reflect.DeepEqual(c.Atlas, expectedAtlas) {
   446  		t.Fatalf("bad: %#v", c.Atlas)
   447  	}
   448  
   449  	actual := variablesStr(c.Variables)
   450  	if actual != strings.TrimSpace(basicVariablesStr) {
   451  		t.Fatalf("bad:\n%s", actual)
   452  	}
   453  
   454  	actual = providerConfigsStr(c.ProviderConfigs)
   455  	if actual != strings.TrimSpace(basicProvidersStr) {
   456  		t.Fatalf("bad:\n%s", actual)
   457  	}
   458  
   459  	actual = resourcesStr(c.Resources)
   460  	if actual != strings.TrimSpace(basicResourcesStr) {
   461  		t.Fatalf("bad:\n%s", actual)
   462  	}
   463  
   464  	actual = outputsStr(c.Outputs)
   465  	if actual != strings.TrimSpace(basicOutputsStr) {
   466  		t.Fatalf("bad:\n%s", actual)
   467  	}
   468  }
   469  
   470  func TestLoadJSONAmbiguous(t *testing.T) {
   471  	js := `
   472  {
   473    "variable": {
   474      "first": {
   475        "default": {
   476          "key": "val"
   477        }
   478      },
   479      "second": {
   480        "description": "Described",
   481        "default": {
   482          "key": "val"
   483        }
   484      }
   485    }
   486  }
   487  `
   488  
   489  	c, err := LoadJSON([]byte(js))
   490  	if err != nil {
   491  		t.Fatalf("err: %s", err)
   492  	}
   493  
   494  	if len(c.Variables) != 2 {
   495  		t.Fatal("config should have 2 variables, found", len(c.Variables))
   496  	}
   497  
   498  	first := &Variable{
   499  		Name:    "first",
   500  		Default: map[string]interface{}{"key": "val"},
   501  	}
   502  	second := &Variable{
   503  		Name:        "second",
   504  		Description: "Described",
   505  		Default:     map[string]interface{}{"key": "val"},
   506  	}
   507  
   508  	if !reflect.DeepEqual(first, c.Variables[0]) {
   509  		t.Fatalf("\nexpected: %#v\ngot:      %#v", first, c.Variables[0])
   510  	}
   511  
   512  	if !reflect.DeepEqual(second, c.Variables[1]) {
   513  		t.Fatalf("\nexpected: %#v\ngot:      %#v", second, c.Variables[1])
   514  	}
   515  }
   516  
   517  func TestLoadFileBasic_jsonNoName(t *testing.T) {
   518  	c, err := LoadFile(filepath.Join(fixtureDir, "resource-no-name.tf.json"))
   519  	if err != nil {
   520  		t.Fatalf("err: %s", err)
   521  	}
   522  
   523  	if c == nil {
   524  		t.Fatal("config should not be nil")
   525  	}
   526  
   527  	actual := resourcesStr(c.Resources)
   528  	if actual != strings.TrimSpace(basicJsonNoNameResourcesStr) {
   529  		t.Fatalf("bad:\n%s", actual)
   530  	}
   531  }
   532  
   533  func TestLoadFile_variables(t *testing.T) {
   534  	c, err := LoadFile(filepath.Join(fixtureDir, "variables.tf"))
   535  	if err != nil {
   536  		t.Fatalf("err: %s", err)
   537  	}
   538  	if c == nil {
   539  		t.Fatal("config should not be nil")
   540  	}
   541  
   542  	if c.Dir != "" {
   543  		t.Fatalf("bad: %#v", c.Dir)
   544  	}
   545  
   546  	actual := variablesStr(c.Variables)
   547  	if actual != strings.TrimSpace(variablesVariablesStr) {
   548  		t.Fatalf("bad:\n%s", actual)
   549  	}
   550  }
   551  
   552  func TestLoadDir_basic(t *testing.T) {
   553  	dir := filepath.Join(fixtureDir, "dir-basic")
   554  	c, err := LoadDir(dir)
   555  	if err != nil {
   556  		t.Fatalf("err: %s", err)
   557  	}
   558  
   559  	if c == nil {
   560  		t.Fatal("config should not be nil")
   561  	}
   562  
   563  	dirAbs, err := filepath.Abs(dir)
   564  	if err != nil {
   565  		t.Fatalf("err: %s", err)
   566  	}
   567  	if c.Dir != dirAbs {
   568  		t.Fatalf("bad: %#v", c.Dir)
   569  	}
   570  
   571  	actual := variablesStr(c.Variables)
   572  	if actual != strings.TrimSpace(dirBasicVariablesStr) {
   573  		t.Fatalf("bad:\n%s", actual)
   574  	}
   575  
   576  	actual = providerConfigsStr(c.ProviderConfigs)
   577  	if actual != strings.TrimSpace(dirBasicProvidersStr) {
   578  		t.Fatalf("bad:\n%s", actual)
   579  	}
   580  
   581  	actual = resourcesStr(c.Resources)
   582  	if actual != strings.TrimSpace(dirBasicResourcesStr) {
   583  		t.Fatalf("bad:\n%s", actual)
   584  	}
   585  
   586  	actual = outputsStr(c.Outputs)
   587  	if actual != strings.TrimSpace(dirBasicOutputsStr) {
   588  		t.Fatalf("bad:\n%s", actual)
   589  	}
   590  }
   591  
   592  func TestLoadDir_file(t *testing.T) {
   593  	_, err := LoadDir(filepath.Join(fixtureDir, "variables.tf"))
   594  	if err == nil {
   595  		t.Fatal("should error")
   596  	}
   597  }
   598  
   599  func TestLoadDir_noConfigs(t *testing.T) {
   600  	_, err := LoadDir(filepath.Join(fixtureDir, "dir-empty"))
   601  	if err == nil {
   602  		t.Fatal("should error")
   603  	}
   604  }
   605  
   606  func TestLoadDir_noMerge(t *testing.T) {
   607  	c, err := LoadDir(filepath.Join(fixtureDir, "dir-merge"))
   608  	if err != nil {
   609  		t.Fatalf("err: %s", err)
   610  	}
   611  
   612  	if c == nil {
   613  		t.Fatal("config should not be nil")
   614  	}
   615  
   616  	if err := c.Validate(); err == nil {
   617  		t.Fatal("should not be valid")
   618  	}
   619  }
   620  
   621  func TestLoadDir_override(t *testing.T) {
   622  	c, err := LoadDir(filepath.Join(fixtureDir, "dir-override"))
   623  	if err != nil {
   624  		t.Fatalf("err: %s", err)
   625  	}
   626  
   627  	if c == nil {
   628  		t.Fatal("config should not be nil")
   629  	}
   630  
   631  	actual := variablesStr(c.Variables)
   632  	if actual != strings.TrimSpace(dirOverrideVariablesStr) {
   633  		t.Fatalf("bad:\n%s", actual)
   634  	}
   635  
   636  	actual = providerConfigsStr(c.ProviderConfigs)
   637  	if actual != strings.TrimSpace(dirOverrideProvidersStr) {
   638  		t.Fatalf("bad:\n%s", actual)
   639  	}
   640  
   641  	actual = resourcesStr(c.Resources)
   642  	if actual != strings.TrimSpace(dirOverrideResourcesStr) {
   643  		t.Fatalf("bad:\n%s", actual)
   644  	}
   645  
   646  	actual = outputsStr(c.Outputs)
   647  	if actual != strings.TrimSpace(dirOverrideOutputsStr) {
   648  		t.Fatalf("bad:\n%s", actual)
   649  	}
   650  }
   651  
   652  func TestLoadDir_overrideVar(t *testing.T) {
   653  	c, err := LoadDir(filepath.Join(fixtureDir, "dir-override-var"))
   654  	if err != nil {
   655  		t.Fatalf("err: %s", err)
   656  	}
   657  
   658  	if c == nil {
   659  		t.Fatal("config should not be nil")
   660  	}
   661  
   662  	actual := variablesStr(c.Variables)
   663  	if actual != strings.TrimSpace(dirOverrideVarsVariablesStr) {
   664  		t.Fatalf("bad:\n%s", actual)
   665  	}
   666  }
   667  
   668  func TestLoadFile_mismatchedVariableTypes(t *testing.T) {
   669  	_, err := LoadFile(filepath.Join(fixtureDir, "variable-mismatched-type.tf"))
   670  	if err == nil {
   671  		t.Fatalf("bad: expected error")
   672  	}
   673  
   674  	errorStr := err.Error()
   675  	if !strings.Contains(errorStr, "'not_a_map' has a default value which is not of type 'string'") {
   676  		t.Fatalf("bad: expected error has wrong text: %s", errorStr)
   677  	}
   678  }
   679  
   680  func TestLoadFile_badVariableTypes(t *testing.T) {
   681  	_, err := LoadFile(filepath.Join(fixtureDir, "bad-variable-type.tf"))
   682  	if err == nil {
   683  		t.Fatalf("bad: expected error")
   684  	}
   685  
   686  	errorStr := err.Error()
   687  	if !strings.Contains(errorStr, "'bad_type' must be of type string") {
   688  		t.Fatalf("bad: expected error has wrong text: %s", errorStr)
   689  	}
   690  }
   691  
   692  func TestLoadFile_variableNoName(t *testing.T) {
   693  	_, err := LoadFile(filepath.Join(fixtureDir, "variable-no-name.tf"))
   694  	if err == nil {
   695  		t.Fatalf("bad: expected error")
   696  	}
   697  
   698  	errorStr := err.Error()
   699  	if !strings.Contains(errorStr, "'variable' must be followed") {
   700  		t.Fatalf("bad: expected error has wrong text: %s", errorStr)
   701  	}
   702  }
   703  
   704  func TestLoadFile_provisioners(t *testing.T) {
   705  	c, err := LoadFile(filepath.Join(fixtureDir, "provisioners.tf"))
   706  	if err != nil {
   707  		t.Fatalf("err: %s", err)
   708  	}
   709  
   710  	if c == nil {
   711  		t.Fatal("config should not be nil")
   712  	}
   713  
   714  	actual := resourcesStr(c.Resources)
   715  	if actual != strings.TrimSpace(provisionerResourcesStr) {
   716  		t.Fatalf("bad:\n%s", actual)
   717  	}
   718  }
   719  
   720  func TestLoadFile_provisionersDestroy(t *testing.T) {
   721  	c, err := LoadFile(filepath.Join(fixtureDir, "provisioners-destroy.tf"))
   722  	if err != nil {
   723  		t.Fatalf("err: %s", err)
   724  	}
   725  
   726  	if c == nil {
   727  		t.Fatal("config should not be nil")
   728  	}
   729  
   730  	actual := resourcesStr(c.Resources)
   731  	if actual != strings.TrimSpace(provisionerDestroyResourcesStr) {
   732  		t.Fatalf("bad:\n%s", actual)
   733  	}
   734  }
   735  
   736  func TestLoadFile_unnamedOutput(t *testing.T) {
   737  	_, err := LoadFile(filepath.Join(fixtureDir, "output-unnamed.tf"))
   738  	if err == nil {
   739  		t.Fatalf("bad: expected error")
   740  	}
   741  
   742  	errorStr := err.Error()
   743  	if !strings.Contains(errorStr, "'output' must be followed") {
   744  		t.Fatalf("bad: expected error has wrong text: %s", errorStr)
   745  	}
   746  }
   747  
   748  func TestLoadFile_connections(t *testing.T) {
   749  	c, err := LoadFile(filepath.Join(fixtureDir, "connection.tf"))
   750  	if err != nil {
   751  		t.Fatalf("err: %s", err)
   752  	}
   753  
   754  	if c == nil {
   755  		t.Fatal("config should not be nil")
   756  	}
   757  
   758  	actual := resourcesStr(c.Resources)
   759  	if actual != strings.TrimSpace(connectionResourcesStr) {
   760  		t.Fatalf("bad:\n%s", actual)
   761  	}
   762  
   763  	// Check for the connection info
   764  	r := c.Resources[0]
   765  	if r.Name != "web" && r.Type != "aws_instance" {
   766  		t.Fatalf("Bad: %#v", r)
   767  	}
   768  
   769  	p1 := r.Provisioners[0]
   770  	if p1.ConnInfo == nil || len(p1.ConnInfo.Raw) != 2 {
   771  		t.Fatalf("Bad: %#v", p1.ConnInfo)
   772  	}
   773  	if p1.ConnInfo.Raw["user"] != "nobody" {
   774  		t.Fatalf("Bad: %#v", p1.ConnInfo)
   775  	}
   776  
   777  	p2 := r.Provisioners[1]
   778  	if p2.ConnInfo == nil || len(p2.ConnInfo.Raw) != 2 {
   779  		t.Fatalf("Bad: %#v", p2.ConnInfo)
   780  	}
   781  	if p2.ConnInfo.Raw["user"] != "root" {
   782  		t.Fatalf("Bad: %#v", p2.ConnInfo)
   783  	}
   784  }
   785  
   786  func TestLoadFile_createBeforeDestroy(t *testing.T) {
   787  	c, err := LoadFile(filepath.Join(fixtureDir, "create-before-destroy.tf"))
   788  	if err != nil {
   789  		t.Fatalf("err: %s", err)
   790  	}
   791  
   792  	if c == nil {
   793  		t.Fatal("config should not be nil")
   794  	}
   795  
   796  	actual := resourcesStr(c.Resources)
   797  	if actual != strings.TrimSpace(createBeforeDestroyResourcesStr) {
   798  		t.Fatalf("bad:\n%s", actual)
   799  	}
   800  
   801  	// Check for the flag value
   802  	r := c.Resources[0]
   803  	if r.Name != "web" && r.Type != "aws_instance" {
   804  		t.Fatalf("Bad: %#v", r)
   805  	}
   806  
   807  	// Should enable create before destroy
   808  	if !r.Lifecycle.CreateBeforeDestroy {
   809  		t.Fatalf("Bad: %#v", r)
   810  	}
   811  
   812  	r = c.Resources[1]
   813  	if r.Name != "bar" && r.Type != "aws_instance" {
   814  		t.Fatalf("Bad: %#v", r)
   815  	}
   816  
   817  	// Should not enable create before destroy
   818  	if r.Lifecycle.CreateBeforeDestroy {
   819  		t.Fatalf("Bad: %#v", r)
   820  	}
   821  }
   822  
   823  func TestLoadFile_ignoreChanges(t *testing.T) {
   824  	c, err := LoadFile(filepath.Join(fixtureDir, "ignore-changes.tf"))
   825  	if err != nil {
   826  		t.Fatalf("err: %s", err)
   827  	}
   828  
   829  	if c == nil {
   830  		t.Fatal("config should not be nil")
   831  	}
   832  
   833  	actual := resourcesStr(c.Resources)
   834  	print(actual)
   835  	if actual != strings.TrimSpace(ignoreChangesResourcesStr) {
   836  		t.Fatalf("bad:\n%s", actual)
   837  	}
   838  
   839  	// Check for the flag value
   840  	r := c.Resources[0]
   841  	if r.Name != "web" && r.Type != "aws_instance" {
   842  		t.Fatalf("Bad: %#v", r)
   843  	}
   844  
   845  	// Should populate ignore changes
   846  	if len(r.Lifecycle.IgnoreChanges) == 0 {
   847  		t.Fatalf("Bad: %#v", r)
   848  	}
   849  
   850  	r = c.Resources[1]
   851  	if r.Name != "bar" && r.Type != "aws_instance" {
   852  		t.Fatalf("Bad: %#v", r)
   853  	}
   854  
   855  	// Should not populate ignore changes
   856  	if len(r.Lifecycle.IgnoreChanges) > 0 {
   857  		t.Fatalf("Bad: %#v", r)
   858  	}
   859  
   860  	r = c.Resources[2]
   861  	if r.Name != "baz" && r.Type != "aws_instance" {
   862  		t.Fatalf("Bad: %#v", r)
   863  	}
   864  
   865  	// Should not populate ignore changes
   866  	if len(r.Lifecycle.IgnoreChanges) > 0 {
   867  		t.Fatalf("Bad: %#v", r)
   868  	}
   869  }
   870  
   871  func TestLoad_preventDestroyString(t *testing.T) {
   872  	c, err := LoadFile(filepath.Join(fixtureDir, "prevent-destroy-string.tf"))
   873  	if err != nil {
   874  		t.Fatalf("err: %s", err)
   875  	}
   876  
   877  	if c == nil {
   878  		t.Fatal("config should not be nil")
   879  	}
   880  
   881  	actual := resourcesStr(c.Resources)
   882  	if actual != strings.TrimSpace(createBeforeDestroyResourcesStr) {
   883  		t.Fatalf("bad:\n%s", actual)
   884  	}
   885  
   886  	// Check for the flag value
   887  	r := c.Resources[0]
   888  	if r.Name != "web" && r.Type != "aws_instance" {
   889  		t.Fatalf("Bad: %#v", r)
   890  	}
   891  
   892  	// Should enable create before destroy
   893  	if !r.Lifecycle.PreventDestroy {
   894  		t.Fatalf("Bad: %#v", r)
   895  	}
   896  
   897  	r = c.Resources[1]
   898  	if r.Name != "bar" && r.Type != "aws_instance" {
   899  		t.Fatalf("Bad: %#v", r)
   900  	}
   901  
   902  	// Should not enable create before destroy
   903  	if r.Lifecycle.PreventDestroy {
   904  		t.Fatalf("Bad: %#v", r)
   905  	}
   906  }
   907  
   908  func TestLoad_temporary_files(t *testing.T) {
   909  	_, err := LoadDir(filepath.Join(fixtureDir, "dir-temporary-files"))
   910  	if err == nil {
   911  		t.Fatalf("Expected to see an error stating no config files found")
   912  	}
   913  }
   914  
   915  func TestLoad_hclAttributes(t *testing.T) {
   916  	c, err := LoadFile(filepath.Join(fixtureDir, "attributes.tf"))
   917  	if err != nil {
   918  		t.Fatalf("Bad: %s", err)
   919  	}
   920  
   921  	if c == nil {
   922  		t.Fatal("config should not be nil")
   923  	}
   924  
   925  	actual := resourcesStr(c.Resources)
   926  	print(actual)
   927  	if actual != strings.TrimSpace(jsonAttributeStr) {
   928  		t.Fatalf("bad:\n%s", actual)
   929  	}
   930  
   931  	r := c.Resources[0]
   932  	if r.Name != "test" && r.Type != "cloudstack_firewall" {
   933  		t.Fatalf("Bad: %#v", r)
   934  	}
   935  
   936  	raw := r.RawConfig
   937  	if raw.Raw["ipaddress"] != "192.168.0.1" {
   938  		t.Fatalf("Bad: %s", raw.Raw["ipAddress"])
   939  	}
   940  
   941  	rule := raw.Raw["rule"].([]map[string]interface{})[0]
   942  	if rule["protocol"] != "tcp" {
   943  		t.Fatalf("Bad: %s", rule["protocol"])
   944  	}
   945  
   946  	if rule["source_cidr"] != "10.0.0.0/8" {
   947  		t.Fatalf("Bad: %s", rule["source_cidr"])
   948  	}
   949  
   950  	ports := rule["ports"].([]interface{})
   951  
   952  	if ports[0] != "80" {
   953  		t.Fatalf("Bad ports: %s", ports[0])
   954  	}
   955  	if ports[1] != "1000-2000" {
   956  		t.Fatalf("Bad ports: %s", ports[1])
   957  	}
   958  }
   959  
   960  func TestLoad_jsonAttributes(t *testing.T) {
   961  	c, err := LoadFile(filepath.Join(fixtureDir, "attributes.tf.json"))
   962  	if err != nil {
   963  		t.Fatalf("Bad: %s", err)
   964  	}
   965  
   966  	if c == nil {
   967  		t.Fatal("config should not be nil")
   968  	}
   969  
   970  	actual := resourcesStr(c.Resources)
   971  	print(actual)
   972  	if actual != strings.TrimSpace(jsonAttributeStr) {
   973  		t.Fatalf("bad:\n%s", actual)
   974  	}
   975  
   976  	r := c.Resources[0]
   977  	if r.Name != "test" && r.Type != "cloudstack_firewall" {
   978  		t.Fatalf("Bad: %#v", r)
   979  	}
   980  
   981  	raw := r.RawConfig
   982  	if raw.Raw["ipaddress"] != "192.168.0.1" {
   983  		t.Fatalf("Bad: %s", raw.Raw["ipAddress"])
   984  	}
   985  
   986  	rule := raw.Raw["rule"].([]map[string]interface{})[0]
   987  	if rule["protocol"] != "tcp" {
   988  		t.Fatalf("Bad: %s", rule["protocol"])
   989  	}
   990  
   991  	if rule["source_cidr"] != "10.0.0.0/8" {
   992  		t.Fatalf("Bad: %s", rule["source_cidr"])
   993  	}
   994  
   995  	ports := rule["ports"].([]interface{})
   996  
   997  	if ports[0] != "80" {
   998  		t.Fatalf("Bad ports: %s", ports[0])
   999  	}
  1000  	if ports[1] != "1000-2000" {
  1001  		t.Fatalf("Bad ports: %s", ports[1])
  1002  	}
  1003  }
  1004  
  1005  const jsonAttributeStr = `
  1006  cloudstack_firewall.test (x1)
  1007    ipaddress
  1008    rule
  1009  `
  1010  
  1011  const windowsHeredocResourcesStr = `
  1012  aws_instance.test (x1)
  1013    user_data
  1014  `
  1015  
  1016  const heredocProvidersStr = `
  1017  aws
  1018    access_key
  1019    secret_key
  1020  `
  1021  
  1022  const heredocResourcesStr = `
  1023  aws_iam_policy.policy (x1)
  1024    description
  1025    name
  1026    path
  1027    policy
  1028  aws_instance.heredocwithnumbers (x1)
  1029    ami
  1030    provisioners
  1031      local-exec
  1032        command
  1033  aws_instance.test (x1)
  1034    ami
  1035    provisioners
  1036      remote-exec
  1037        inline
  1038  `
  1039  
  1040  const basicOutputsStr = `
  1041  web_ip
  1042    vars
  1043      resource: aws_instance.web.private_ip
  1044  `
  1045  
  1046  const basicProvidersStr = `
  1047  aws
  1048    access_key
  1049    secret_key
  1050  do
  1051    api_key
  1052    vars
  1053      user: var.foo
  1054  `
  1055  
  1056  const basicResourcesStr = `
  1057  aws_instance.db (x1)
  1058    VPC
  1059    security_groups
  1060    provisioners
  1061      file
  1062        destination
  1063        source
  1064    dependsOn
  1065      aws_instance.web
  1066    vars
  1067      resource: aws_security_group.firewall.*.id
  1068  aws_instance.web (x1)
  1069    ami
  1070    network_interface
  1071    security_groups
  1072    provisioners
  1073      file
  1074        destination
  1075        source
  1076    vars
  1077      resource: aws_security_group.firewall.foo
  1078      user: var.foo
  1079  aws_security_group.firewall (x5)
  1080  data.do.depends (x1)
  1081    dependsOn
  1082      data.do.simple
  1083  data.do.simple (x1)
  1084    foo
  1085  `
  1086  
  1087  const basicVariablesStr = `
  1088  bar (required) (string)
  1089    <>
  1090    <>
  1091  baz (map)
  1092    map[key:value]
  1093    <>
  1094  foo
  1095    bar
  1096    bar
  1097  `
  1098  
  1099  const basicJsonNoNameResourcesStr = `
  1100  aws_security_group.allow_external_http_https (x1)
  1101    tags
  1102  `
  1103  
  1104  const dirBasicOutputsStr = `
  1105  web_ip
  1106    vars
  1107      resource: aws_instance.web.private_ip
  1108  `
  1109  
  1110  const dirBasicProvidersStr = `
  1111  aws
  1112    access_key
  1113    secret_key
  1114  do
  1115    api_key
  1116    vars
  1117      user: var.foo
  1118  `
  1119  
  1120  const dirBasicResourcesStr = `
  1121  aws_instance.db (x1)
  1122    security_groups
  1123    vars
  1124      resource: aws_security_group.firewall.*.id
  1125  aws_instance.web (x1)
  1126    ami
  1127    network_interface
  1128    security_groups
  1129    vars
  1130      resource: aws_security_group.firewall.foo
  1131      user: var.foo
  1132  aws_security_group.firewall (x5)
  1133  data.do.depends (x1)
  1134    dependsOn
  1135      data.do.simple
  1136  data.do.simple (x1)
  1137    foo
  1138  `
  1139  
  1140  const dirBasicVariablesStr = `
  1141  foo
  1142    bar
  1143    bar
  1144  `
  1145  
  1146  const dirOverrideOutputsStr = `
  1147  web_ip
  1148    vars
  1149      resource: aws_instance.web.private_ip
  1150  `
  1151  
  1152  const dirOverrideProvidersStr = `
  1153  aws
  1154    access_key
  1155    secret_key
  1156  do
  1157    api_key
  1158    vars
  1159      user: var.foo
  1160  `
  1161  
  1162  const dirOverrideResourcesStr = `
  1163  aws_instance.db (x1)
  1164    ami
  1165    security_groups
  1166  aws_instance.web (x1)
  1167    ami
  1168    foo
  1169    network_interface
  1170    security_groups
  1171    vars
  1172      resource: aws_security_group.firewall.foo
  1173      user: var.foo
  1174  aws_security_group.firewall (x5)
  1175  data.do.depends (x1)
  1176    hello
  1177    dependsOn
  1178      data.do.simple
  1179  data.do.simple (x1)
  1180    foo
  1181  `
  1182  
  1183  const dirOverrideVariablesStr = `
  1184  foo
  1185    bar
  1186    bar
  1187  `
  1188  
  1189  const dirOverrideVarsVariablesStr = `
  1190  foo
  1191    baz
  1192    bar
  1193  `
  1194  
  1195  const importProvidersStr = `
  1196  aws
  1197    bar
  1198    foo
  1199  `
  1200  
  1201  const importResourcesStr = `
  1202  aws_security_group.db (x1)
  1203  aws_security_group.web (x1)
  1204  `
  1205  
  1206  const importVariablesStr = `
  1207  bar (required)
  1208    <>
  1209    <>
  1210  foo
  1211    bar
  1212    bar
  1213  `
  1214  
  1215  const modulesModulesStr = `
  1216  bar
  1217    source = baz
  1218    memory
  1219  `
  1220  
  1221  const provisionerResourcesStr = `
  1222  aws_instance.web (x1)
  1223    ami
  1224    security_groups
  1225    provisioners
  1226      shell
  1227        path
  1228    vars
  1229      resource: aws_security_group.firewall.foo
  1230      user: var.foo
  1231  `
  1232  
  1233  const provisionerDestroyResourcesStr = `
  1234  aws_instance.web (x1)
  1235    provisioners
  1236      shell
  1237      shell (destroy)
  1238        path
  1239      shell (destroy)
  1240        on_failure = continue
  1241        path
  1242  `
  1243  
  1244  const connectionResourcesStr = `
  1245  aws_instance.web (x1)
  1246    ami
  1247    security_groups
  1248    provisioners
  1249      shell
  1250        path
  1251      shell
  1252        path
  1253    vars
  1254      resource: aws_security_group.firewall.foo
  1255      user: var.foo
  1256  `
  1257  
  1258  const outputDependsOnStr = `
  1259  value
  1260    dependsOn
  1261      foo
  1262  `
  1263  
  1264  const variablesVariablesStr = `
  1265  bar
  1266    <>
  1267    <>
  1268  baz
  1269    foo
  1270    <>
  1271  foo (required)
  1272    <>
  1273    <>
  1274  `
  1275  
  1276  const createBeforeDestroyResourcesStr = `
  1277  aws_instance.bar (x1)
  1278    ami
  1279  aws_instance.web (x1)
  1280    ami
  1281  `
  1282  
  1283  const ignoreChangesResourcesStr = `
  1284  aws_instance.bar (x1)
  1285    ami
  1286  aws_instance.baz (x1)
  1287    ami
  1288  aws_instance.web (x1)
  1289    ami
  1290  `