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