github.com/jorgemarey/terraform@v0.6.7-0.20151113041428-536ba76b21bb/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 TestIsEmptyDir(t *testing.T) {
    12  	val, err := IsEmptyDir(fixtureDir)
    13  	if err != nil {
    14  		t.Fatalf("err: %s", err)
    15  	}
    16  	if val {
    17  		t.Fatal("should not be empty")
    18  	}
    19  }
    20  
    21  func TestIsEmptyDir_noExist(t *testing.T) {
    22  	val, err := IsEmptyDir(filepath.Join(fixtureDir, "nopenopenope"))
    23  	if err != nil {
    24  		t.Fatalf("err: %s", err)
    25  	}
    26  	if !val {
    27  		t.Fatal("should be empty")
    28  	}
    29  }
    30  
    31  func TestIsEmptyDir_noConfigs(t *testing.T) {
    32  	val, err := IsEmptyDir(filepath.Join(fixtureDir, "dir-empty"))
    33  	if err != nil {
    34  		t.Fatalf("err: %s", err)
    35  	}
    36  	if !val {
    37  		t.Fatal("should be empty")
    38  	}
    39  }
    40  
    41  func TestLoadFile_badType(t *testing.T) {
    42  	_, err := LoadFile(filepath.Join(fixtureDir, "bad_type.tf.nope"))
    43  	if err == nil {
    44  		t.Fatal("should have error")
    45  	}
    46  }
    47  
    48  func TestLoadFileHeredoc(t *testing.T) {
    49  	c, err := LoadFile(filepath.Join(fixtureDir, "heredoc.tf"))
    50  	if err != nil {
    51  		t.Fatalf("err: %s", err)
    52  	}
    53  
    54  	if c == nil {
    55  		t.Fatal("config should not be nil")
    56  	}
    57  
    58  	if c.Dir != "" {
    59  		t.Fatalf("bad: %#v", c.Dir)
    60  	}
    61  
    62  	actual := providerConfigsStr(c.ProviderConfigs)
    63  	if actual != strings.TrimSpace(heredocProvidersStr) {
    64  		t.Fatalf("bad:\n%s", actual)
    65  	}
    66  
    67  	actual = resourcesStr(c.Resources)
    68  	if actual != strings.TrimSpace(heredocResourcesStr) {
    69  		t.Fatalf("bad:\n%s", actual)
    70  	}
    71  }
    72  
    73  func TestLoadFileBasic(t *testing.T) {
    74  	c, err := LoadFile(filepath.Join(fixtureDir, "basic.tf"))
    75  	if err != nil {
    76  		t.Fatalf("err: %s", err)
    77  	}
    78  
    79  	if c == nil {
    80  		t.Fatal("config should not be nil")
    81  	}
    82  
    83  	if c.Dir != "" {
    84  		t.Fatalf("bad: %#v", c.Dir)
    85  	}
    86  
    87  	expectedAtlas := &AtlasConfig{Name: "mitchellh/foo"}
    88  	if !reflect.DeepEqual(c.Atlas, expectedAtlas) {
    89  		t.Fatalf("bad: %#v", c.Atlas)
    90  	}
    91  
    92  	actual := variablesStr(c.Variables)
    93  	if actual != strings.TrimSpace(basicVariablesStr) {
    94  		t.Fatalf("bad:\n%s", actual)
    95  	}
    96  
    97  	actual = providerConfigsStr(c.ProviderConfigs)
    98  	if actual != strings.TrimSpace(basicProvidersStr) {
    99  		t.Fatalf("bad:\n%s", actual)
   100  	}
   101  
   102  	actual = resourcesStr(c.Resources)
   103  	if actual != strings.TrimSpace(basicResourcesStr) {
   104  		t.Fatalf("bad:\n%s", actual)
   105  	}
   106  
   107  	actual = outputsStr(c.Outputs)
   108  	if actual != strings.TrimSpace(basicOutputsStr) {
   109  		t.Fatalf("bad:\n%s", actual)
   110  	}
   111  }
   112  
   113  func TestLoadFileBasic_empty(t *testing.T) {
   114  	c, err := LoadFile(filepath.Join(fixtureDir, "empty.tf"))
   115  	if err != nil {
   116  		t.Fatalf("err: %s", err)
   117  	}
   118  
   119  	if c == nil {
   120  		t.Fatal("config should not be nil")
   121  	}
   122  }
   123  
   124  func TestLoadFileBasic_import(t *testing.T) {
   125  	// Skip because we disabled importing
   126  	t.Skip()
   127  
   128  	c, err := LoadFile(filepath.Join(fixtureDir, "import.tf"))
   129  	if err != nil {
   130  		t.Fatalf("err: %s", err)
   131  	}
   132  
   133  	if c == nil {
   134  		t.Fatal("config should not be nil")
   135  	}
   136  
   137  	actual := variablesStr(c.Variables)
   138  	if actual != strings.TrimSpace(importVariablesStr) {
   139  		t.Fatalf("bad:\n%s", actual)
   140  	}
   141  
   142  	actual = providerConfigsStr(c.ProviderConfigs)
   143  	if actual != strings.TrimSpace(importProvidersStr) {
   144  		t.Fatalf("bad:\n%s", actual)
   145  	}
   146  
   147  	actual = resourcesStr(c.Resources)
   148  	if actual != strings.TrimSpace(importResourcesStr) {
   149  		t.Fatalf("bad:\n%s", actual)
   150  	}
   151  }
   152  
   153  func TestLoadFileBasic_json(t *testing.T) {
   154  	c, err := LoadFile(filepath.Join(fixtureDir, "basic.tf.json"))
   155  	if err != nil {
   156  		t.Fatalf("err: %s", err)
   157  	}
   158  
   159  	if c == nil {
   160  		t.Fatal("config should not be nil")
   161  	}
   162  
   163  	if c.Dir != "" {
   164  		t.Fatalf("bad: %#v", c.Dir)
   165  	}
   166  
   167  	expectedAtlas := &AtlasConfig{Name: "mitchellh/foo"}
   168  	if !reflect.DeepEqual(c.Atlas, expectedAtlas) {
   169  		t.Fatalf("bad: %#v", c.Atlas)
   170  	}
   171  
   172  	actual := variablesStr(c.Variables)
   173  	if actual != strings.TrimSpace(basicVariablesStr) {
   174  		t.Fatalf("bad:\n%s", actual)
   175  	}
   176  
   177  	actual = providerConfigsStr(c.ProviderConfigs)
   178  	if actual != strings.TrimSpace(basicProvidersStr) {
   179  		t.Fatalf("bad:\n%s", actual)
   180  	}
   181  
   182  	actual = resourcesStr(c.Resources)
   183  	if actual != strings.TrimSpace(basicResourcesStr) {
   184  		t.Fatalf("bad:\n%s", actual)
   185  	}
   186  
   187  	actual = outputsStr(c.Outputs)
   188  	if actual != strings.TrimSpace(basicOutputsStr) {
   189  		t.Fatalf("bad:\n%s", actual)
   190  	}
   191  }
   192  
   193  func TestLoadFileBasic_modules(t *testing.T) {
   194  	c, err := LoadFile(filepath.Join(fixtureDir, "modules.tf"))
   195  	if err != nil {
   196  		t.Fatalf("err: %s", err)
   197  	}
   198  
   199  	if c == nil {
   200  		t.Fatal("config should not be nil")
   201  	}
   202  
   203  	if c.Dir != "" {
   204  		t.Fatalf("bad: %#v", c.Dir)
   205  	}
   206  
   207  	actual := modulesStr(c.Modules)
   208  	if actual != strings.TrimSpace(modulesModulesStr) {
   209  		t.Fatalf("bad:\n%s", actual)
   210  	}
   211  }
   212  
   213  func TestLoadJSONBasic(t *testing.T) {
   214  	raw, err := ioutil.ReadFile(filepath.Join(fixtureDir, "basic.tf.json"))
   215  	if err != nil {
   216  		t.Fatalf("err: %s", err)
   217  	}
   218  
   219  	c, err := LoadJSON(raw)
   220  	if err != nil {
   221  		t.Fatalf("err: %s", err)
   222  	}
   223  
   224  	if c == nil {
   225  		t.Fatal("config should not be nil")
   226  	}
   227  
   228  	if c.Dir != "" {
   229  		t.Fatalf("bad: %#v", c.Dir)
   230  	}
   231  
   232  	expectedAtlas := &AtlasConfig{Name: "mitchellh/foo"}
   233  	if !reflect.DeepEqual(c.Atlas, expectedAtlas) {
   234  		t.Fatalf("bad: %#v", c.Atlas)
   235  	}
   236  
   237  	actual := variablesStr(c.Variables)
   238  	if actual != strings.TrimSpace(basicVariablesStr) {
   239  		t.Fatalf("bad:\n%s", actual)
   240  	}
   241  
   242  	actual = providerConfigsStr(c.ProviderConfigs)
   243  	if actual != strings.TrimSpace(basicProvidersStr) {
   244  		t.Fatalf("bad:\n%s", actual)
   245  	}
   246  
   247  	actual = resourcesStr(c.Resources)
   248  	if actual != strings.TrimSpace(basicResourcesStr) {
   249  		t.Fatalf("bad:\n%s", actual)
   250  	}
   251  
   252  	actual = outputsStr(c.Outputs)
   253  	if actual != strings.TrimSpace(basicOutputsStr) {
   254  		t.Fatalf("bad:\n%s", actual)
   255  	}
   256  }
   257  
   258  func TestLoadFile_variables(t *testing.T) {
   259  	c, err := LoadFile(filepath.Join(fixtureDir, "variables.tf"))
   260  	if err != nil {
   261  		t.Fatalf("err: %s", err)
   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  	actual := variablesStr(c.Variables)
   272  	if actual != strings.TrimSpace(variablesVariablesStr) {
   273  		t.Fatalf("bad:\n%s", actual)
   274  	}
   275  }
   276  
   277  func TestLoadDir_basic(t *testing.T) {
   278  	dir := filepath.Join(fixtureDir, "dir-basic")
   279  	c, err := LoadDir(dir)
   280  	if err != nil {
   281  		t.Fatalf("err: %s", err)
   282  	}
   283  
   284  	if c == nil {
   285  		t.Fatal("config should not be nil")
   286  	}
   287  
   288  	dirAbs, err := filepath.Abs(dir)
   289  	if err != nil {
   290  		t.Fatalf("err: %s", err)
   291  	}
   292  	if c.Dir != dirAbs {
   293  		t.Fatalf("bad: %#v", c.Dir)
   294  	}
   295  
   296  	actual := variablesStr(c.Variables)
   297  	if actual != strings.TrimSpace(dirBasicVariablesStr) {
   298  		t.Fatalf("bad:\n%s", actual)
   299  	}
   300  
   301  	actual = providerConfigsStr(c.ProviderConfigs)
   302  	if actual != strings.TrimSpace(dirBasicProvidersStr) {
   303  		t.Fatalf("bad:\n%s", actual)
   304  	}
   305  
   306  	actual = resourcesStr(c.Resources)
   307  	if actual != strings.TrimSpace(dirBasicResourcesStr) {
   308  		t.Fatalf("bad:\n%s", actual)
   309  	}
   310  
   311  	actual = outputsStr(c.Outputs)
   312  	if actual != strings.TrimSpace(dirBasicOutputsStr) {
   313  		t.Fatalf("bad:\n%s", actual)
   314  	}
   315  }
   316  
   317  func TestLoadDir_file(t *testing.T) {
   318  	_, err := LoadDir(filepath.Join(fixtureDir, "variables.tf"))
   319  	if err == nil {
   320  		t.Fatal("should error")
   321  	}
   322  }
   323  
   324  func TestLoadDir_noConfigs(t *testing.T) {
   325  	_, err := LoadDir(filepath.Join(fixtureDir, "dir-empty"))
   326  	if err == nil {
   327  		t.Fatal("should error")
   328  	}
   329  }
   330  
   331  func TestLoadDir_noMerge(t *testing.T) {
   332  	c, err := LoadDir(filepath.Join(fixtureDir, "dir-merge"))
   333  	if err != nil {
   334  		t.Fatalf("err: %s", err)
   335  	}
   336  
   337  	if c == nil {
   338  		t.Fatal("config should not be nil")
   339  	}
   340  
   341  	if err := c.Validate(); err == nil {
   342  		t.Fatal("should not be valid")
   343  	}
   344  }
   345  
   346  func TestLoadDir_override(t *testing.T) {
   347  	c, err := LoadDir(filepath.Join(fixtureDir, "dir-override"))
   348  	if err != nil {
   349  		t.Fatalf("err: %s", err)
   350  	}
   351  
   352  	if c == nil {
   353  		t.Fatal("config should not be nil")
   354  	}
   355  
   356  	actual := variablesStr(c.Variables)
   357  	if actual != strings.TrimSpace(dirOverrideVariablesStr) {
   358  		t.Fatalf("bad:\n%s", actual)
   359  	}
   360  
   361  	actual = providerConfigsStr(c.ProviderConfigs)
   362  	if actual != strings.TrimSpace(dirOverrideProvidersStr) {
   363  		t.Fatalf("bad:\n%s", actual)
   364  	}
   365  
   366  	actual = resourcesStr(c.Resources)
   367  	if actual != strings.TrimSpace(dirOverrideResourcesStr) {
   368  		t.Fatalf("bad:\n%s", actual)
   369  	}
   370  
   371  	actual = outputsStr(c.Outputs)
   372  	if actual != strings.TrimSpace(dirOverrideOutputsStr) {
   373  		t.Fatalf("bad:\n%s", actual)
   374  	}
   375  }
   376  
   377  func TestLoadFile_provisioners(t *testing.T) {
   378  	c, err := LoadFile(filepath.Join(fixtureDir, "provisioners.tf"))
   379  	if err != nil {
   380  		t.Fatalf("err: %s", err)
   381  	}
   382  
   383  	if c == nil {
   384  		t.Fatal("config should not be nil")
   385  	}
   386  
   387  	actual := resourcesStr(c.Resources)
   388  	if actual != strings.TrimSpace(provisionerResourcesStr) {
   389  		t.Fatalf("bad:\n%s", actual)
   390  	}
   391  }
   392  
   393  func TestLoadFile_connections(t *testing.T) {
   394  	c, err := LoadFile(filepath.Join(fixtureDir, "connection.tf"))
   395  	if err != nil {
   396  		t.Fatalf("err: %s", err)
   397  	}
   398  
   399  	if c == nil {
   400  		t.Fatal("config should not be nil")
   401  	}
   402  
   403  	actual := resourcesStr(c.Resources)
   404  	if actual != strings.TrimSpace(connectionResourcesStr) {
   405  		t.Fatalf("bad:\n%s", actual)
   406  	}
   407  
   408  	// Check for the connection info
   409  	r := c.Resources[0]
   410  	if r.Name != "web" && r.Type != "aws_instance" {
   411  		t.Fatalf("Bad: %#v", r)
   412  	}
   413  
   414  	p1 := r.Provisioners[0]
   415  	if p1.ConnInfo == nil || len(p1.ConnInfo.Raw) != 2 {
   416  		t.Fatalf("Bad: %#v", p1.ConnInfo)
   417  	}
   418  	if p1.ConnInfo.Raw["user"] != "nobody" {
   419  		t.Fatalf("Bad: %#v", p1.ConnInfo)
   420  	}
   421  
   422  	p2 := r.Provisioners[1]
   423  	if p2.ConnInfo == nil || len(p2.ConnInfo.Raw) != 2 {
   424  		t.Fatalf("Bad: %#v", p2.ConnInfo)
   425  	}
   426  	if p2.ConnInfo.Raw["user"] != "root" {
   427  		t.Fatalf("Bad: %#v", p2.ConnInfo)
   428  	}
   429  }
   430  
   431  func TestLoadFile_createBeforeDestroy(t *testing.T) {
   432  	c, err := LoadFile(filepath.Join(fixtureDir, "create-before-destroy.tf"))
   433  	if err != nil {
   434  		t.Fatalf("err: %s", err)
   435  	}
   436  
   437  	if c == nil {
   438  		t.Fatal("config should not be nil")
   439  	}
   440  
   441  	actual := resourcesStr(c.Resources)
   442  	if actual != strings.TrimSpace(createBeforeDestroyResourcesStr) {
   443  		t.Fatalf("bad:\n%s", actual)
   444  	}
   445  
   446  	// Check for the flag value
   447  	r := c.Resources[0]
   448  	if r.Name != "web" && r.Type != "aws_instance" {
   449  		t.Fatalf("Bad: %#v", r)
   450  	}
   451  
   452  	// Should enable create before destroy
   453  	if !r.Lifecycle.CreateBeforeDestroy {
   454  		t.Fatalf("Bad: %#v", r)
   455  	}
   456  
   457  	r = c.Resources[1]
   458  	if r.Name != "bar" && r.Type != "aws_instance" {
   459  		t.Fatalf("Bad: %#v", r)
   460  	}
   461  
   462  	// Should not enable create before destroy
   463  	if r.Lifecycle.CreateBeforeDestroy {
   464  		t.Fatalf("Bad: %#v", r)
   465  	}
   466  }
   467  
   468  func TestLoadFile_ignoreChanges(t *testing.T) {
   469  	c, err := LoadFile(filepath.Join(fixtureDir, "ignore-changes.tf"))
   470  	if err != nil {
   471  		t.Fatalf("err: %s", err)
   472  	}
   473  
   474  	if c == nil {
   475  		t.Fatal("config should not be nil")
   476  	}
   477  
   478  	actual := resourcesStr(c.Resources)
   479  	print(actual)
   480  	if actual != strings.TrimSpace(ignoreChangesResourcesStr) {
   481  		t.Fatalf("bad:\n%s", actual)
   482  	}
   483  
   484  	// Check for the flag value
   485  	r := c.Resources[0]
   486  	if r.Name != "web" && r.Type != "aws_instance" {
   487  		t.Fatalf("Bad: %#v", r)
   488  	}
   489  
   490  	// Should populate ignore changes
   491  	if len(r.Lifecycle.IgnoreChanges) == 0 {
   492  		t.Fatalf("Bad: %#v", r)
   493  	}
   494  
   495  	r = c.Resources[1]
   496  	if r.Name != "bar" && r.Type != "aws_instance" {
   497  		t.Fatalf("Bad: %#v", r)
   498  	}
   499  
   500  	// Should not populate ignore changes
   501  	if len(r.Lifecycle.IgnoreChanges) > 0 {
   502  		t.Fatalf("Bad: %#v", r)
   503  	}
   504  
   505  	r = c.Resources[2]
   506  	if r.Name != "baz" && r.Type != "aws_instance" {
   507  		t.Fatalf("Bad: %#v", r)
   508  	}
   509  
   510  	// Should not populate ignore changes
   511  	if len(r.Lifecycle.IgnoreChanges) > 0 {
   512  		t.Fatalf("Bad: %#v", r)
   513  	}
   514  }
   515  
   516  func TestLoad_preventDestroyString(t *testing.T) {
   517  	c, err := LoadFile(filepath.Join(fixtureDir, "prevent-destroy-string.tf"))
   518  	if err != nil {
   519  		t.Fatalf("err: %s", err)
   520  	}
   521  
   522  	if c == nil {
   523  		t.Fatal("config should not be nil")
   524  	}
   525  
   526  	actual := resourcesStr(c.Resources)
   527  	if actual != strings.TrimSpace(createBeforeDestroyResourcesStr) {
   528  		t.Fatalf("bad:\n%s", actual)
   529  	}
   530  
   531  	// Check for the flag value
   532  	r := c.Resources[0]
   533  	if r.Name != "web" && r.Type != "aws_instance" {
   534  		t.Fatalf("Bad: %#v", r)
   535  	}
   536  
   537  	// Should enable create before destroy
   538  	if !r.Lifecycle.PreventDestroy {
   539  		t.Fatalf("Bad: %#v", r)
   540  	}
   541  
   542  	r = c.Resources[1]
   543  	if r.Name != "bar" && r.Type != "aws_instance" {
   544  		t.Fatalf("Bad: %#v", r)
   545  	}
   546  
   547  	// Should not enable create before destroy
   548  	if r.Lifecycle.PreventDestroy {
   549  		t.Fatalf("Bad: %#v", r)
   550  	}
   551  }
   552  
   553  func TestLoad_temporary_files(t *testing.T) {
   554  	_, err := LoadDir(filepath.Join(fixtureDir, "dir-temporary-files"))
   555  	if err == nil {
   556  		t.Fatalf("Expected to see an error stating no config files found")
   557  	}
   558  }
   559  
   560  const heredocProvidersStr = `
   561  aws
   562    access_key
   563    secret_key
   564  `
   565  
   566  const heredocResourcesStr = `
   567  aws_iam_policy[policy] (x1)
   568    description
   569    name
   570    path
   571    policy
   572  `
   573  
   574  const basicOutputsStr = `
   575  web_ip
   576    vars
   577      resource: aws_instance.web.private_ip
   578  `
   579  
   580  const basicProvidersStr = `
   581  aws
   582    access_key
   583    secret_key
   584  do
   585    api_key
   586    vars
   587      user: var.foo
   588  `
   589  
   590  const basicResourcesStr = `
   591  aws_instance[db] (x1)
   592    VPC
   593    security_groups
   594    provisioners
   595      file
   596        destination
   597        source
   598    dependsOn
   599      aws_instance.web
   600    vars
   601      resource: aws_security_group.firewall.*.id
   602  aws_instance[web] (x1)
   603    ami
   604    network_interface
   605    security_groups
   606    provisioners
   607      file
   608        destination
   609        source
   610    vars
   611      resource: aws_security_group.firewall.foo
   612      user: var.foo
   613  aws_security_group[firewall] (x5)
   614  `
   615  
   616  const basicVariablesStr = `
   617  foo
   618    bar
   619    bar
   620  `
   621  
   622  const dirBasicOutputsStr = `
   623  web_ip
   624    vars
   625      resource: aws_instance.web.private_ip
   626  `
   627  
   628  const dirBasicProvidersStr = `
   629  aws
   630    access_key
   631    secret_key
   632  do
   633    api_key
   634    vars
   635      user: var.foo
   636  `
   637  
   638  const dirBasicResourcesStr = `
   639  aws_instance[db] (x1)
   640    security_groups
   641    vars
   642      resource: aws_security_group.firewall.*.id
   643  aws_instance[web] (x1)
   644    ami
   645    network_interface
   646    security_groups
   647    vars
   648      resource: aws_security_group.firewall.foo
   649      user: var.foo
   650  aws_security_group[firewall] (x5)
   651  `
   652  
   653  const dirBasicVariablesStr = `
   654  foo
   655    bar
   656    bar
   657  `
   658  
   659  const dirOverrideOutputsStr = `
   660  web_ip
   661    vars
   662      resource: aws_instance.web.private_ip
   663  `
   664  
   665  const dirOverrideProvidersStr = `
   666  aws
   667    access_key
   668    secret_key
   669  do
   670    api_key
   671    vars
   672      user: var.foo
   673  `
   674  
   675  const dirOverrideResourcesStr = `
   676  aws_instance[db] (x1)
   677    ami
   678    security_groups
   679  aws_instance[web] (x1)
   680    ami
   681    foo
   682    network_interface
   683    security_groups
   684    vars
   685      resource: aws_security_group.firewall.foo
   686      user: var.foo
   687  aws_security_group[firewall] (x5)
   688  `
   689  
   690  const dirOverrideVariablesStr = `
   691  foo
   692    bar
   693    bar
   694  `
   695  
   696  const importProvidersStr = `
   697  aws
   698    bar
   699    foo
   700  `
   701  
   702  const importResourcesStr = `
   703  aws_security_group[db] (x1)
   704  aws_security_group[web] (x1)
   705  `
   706  
   707  const importVariablesStr = `
   708  bar (required)
   709    <>
   710    <>
   711  foo
   712    bar
   713    bar
   714  `
   715  
   716  const modulesModulesStr = `
   717  bar
   718    source = baz
   719    memory
   720  `
   721  
   722  const provisionerResourcesStr = `
   723  aws_instance[web] (x1)
   724    ami
   725    security_groups
   726    provisioners
   727      shell
   728        path
   729    vars
   730      resource: aws_security_group.firewall.foo
   731      user: var.foo
   732  `
   733  
   734  const connectionResourcesStr = `
   735  aws_instance[web] (x1)
   736    ami
   737    security_groups
   738    provisioners
   739      shell
   740        path
   741      shell
   742        path
   743    vars
   744      resource: aws_security_group.firewall.foo
   745      user: var.foo
   746  `
   747  
   748  const variablesVariablesStr = `
   749  bar
   750    <>
   751    <>
   752  baz
   753    foo
   754    <>
   755  foo (required)
   756    <>
   757    <>
   758  `
   759  
   760  const createBeforeDestroyResourcesStr = `
   761  aws_instance[bar] (x1)
   762    ami
   763  aws_instance[web] (x1)
   764    ami
   765  `
   766  
   767  const ignoreChangesResourcesStr = `
   768  aws_instance[bar] (x1)
   769    ami
   770  aws_instance[baz] (x1)
   771    ami
   772  aws_instance[web] (x1)
   773    ami
   774  `