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