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