github.com/sfdevops1/terrra4orm@v0.11.12-beta1/config/module/tree_test.go (about)

     1  package module
     2  
     3  import (
     4  	"fmt"
     5  	"io/ioutil"
     6  	"os"
     7  	"path/filepath"
     8  	"reflect"
     9  	"strings"
    10  	"testing"
    11  
    12  	"github.com/hashicorp/terraform/config"
    13  	"github.com/hashicorp/terraform/helper/copy"
    14  )
    15  
    16  func TestTreeChild(t *testing.T) {
    17  	var nilTree *Tree
    18  	if nilTree.Child(nil) != nil {
    19  		t.Fatal("child should be nil")
    20  	}
    21  
    22  	storage := testStorage(t, nil)
    23  	storage.Mode = GetModeGet
    24  	tree := NewTree("", testConfig(t, "child"))
    25  	if err := tree.Load(storage); err != nil {
    26  		t.Fatalf("err: %s", err)
    27  	}
    28  
    29  	// Should be able to get the root child
    30  	if c := tree.Child([]string{}); c == nil {
    31  		t.Fatal("should not be nil")
    32  	} else if c.Name() != "root" {
    33  		t.Fatalf("bad: %#v", c.Name())
    34  	} else if !reflect.DeepEqual(c.Path(), []string(nil)) {
    35  		t.Fatalf("bad: %#v", c.Path())
    36  	}
    37  
    38  	// Should be able to get the root child
    39  	if c := tree.Child(nil); c == nil {
    40  		t.Fatal("should not be nil")
    41  	} else if c.Name() != "root" {
    42  		t.Fatalf("bad: %#v", c.Name())
    43  	} else if !reflect.DeepEqual(c.Path(), []string(nil)) {
    44  		t.Fatalf("bad: %#v", c.Path())
    45  	}
    46  
    47  	// Should be able to get the foo child
    48  	if c := tree.Child([]string{"foo"}); c == nil {
    49  		t.Fatal("should not be nil")
    50  	} else if c.Name() != "foo" {
    51  		t.Fatalf("bad: %#v", c.Name())
    52  	} else if !reflect.DeepEqual(c.Path(), []string{"foo"}) {
    53  		t.Fatalf("bad: %#v", c.Path())
    54  	}
    55  
    56  	// Should be able to get the nested child
    57  	if c := tree.Child([]string{"foo", "bar"}); c == nil {
    58  		t.Fatal("should not be nil")
    59  	} else if c.Name() != "bar" {
    60  		t.Fatalf("bad: %#v", c.Name())
    61  	} else if !reflect.DeepEqual(c.Path(), []string{"foo", "bar"}) {
    62  		t.Fatalf("bad: %#v", c.Path())
    63  	}
    64  }
    65  
    66  func TestTreeLoad(t *testing.T) {
    67  	storage := testStorage(t, nil)
    68  	tree := NewTree("", testConfig(t, "basic"))
    69  
    70  	if tree.Loaded() {
    71  		t.Fatal("should not be loaded")
    72  	}
    73  
    74  	// This should error because we haven't gotten things yet
    75  	if err := tree.Load(storage); err == nil {
    76  		t.Fatal("should error")
    77  	}
    78  
    79  	if tree.Loaded() {
    80  		t.Fatal("should not be loaded")
    81  	}
    82  
    83  	// This should get things
    84  	storage.Mode = GetModeGet
    85  	if err := tree.Load(storage); err != nil {
    86  		t.Fatalf("err: %s", err)
    87  	}
    88  
    89  	if !tree.Loaded() {
    90  		t.Fatal("should be loaded")
    91  	}
    92  
    93  	// This should no longer error
    94  	storage.Mode = GetModeNone
    95  	if err := tree.Load(storage); err != nil {
    96  		t.Fatalf("err: %s", err)
    97  	}
    98  
    99  	actual := strings.TrimSpace(tree.String())
   100  	expected := strings.TrimSpace(treeLoadStr)
   101  	if actual != expected {
   102  		t.Fatalf("bad: \n\n%s", actual)
   103  	}
   104  }
   105  
   106  func TestTreeLoad_duplicate(t *testing.T) {
   107  	storage := testStorage(t, nil)
   108  	tree := NewTree("", testConfig(t, "dup"))
   109  
   110  	if tree.Loaded() {
   111  		t.Fatal("should not be loaded")
   112  	}
   113  
   114  	// This should get things
   115  	storage.Mode = GetModeGet
   116  	if err := tree.Load(storage); err == nil {
   117  		t.Fatalf("should error")
   118  	}
   119  }
   120  
   121  func TestTreeLoad_copyable(t *testing.T) {
   122  	dir := tempDir(t)
   123  	storage := &Storage{
   124  		StorageDir: dir,
   125  		Mode:       GetModeGet,
   126  	}
   127  	cfg := testConfig(t, "basic")
   128  	tree := NewTree("", cfg)
   129  
   130  	// This should get things
   131  	if err := tree.Load(storage); err != nil {
   132  		t.Fatalf("err: %s", err)
   133  	}
   134  
   135  	if !tree.Loaded() {
   136  		t.Fatal("should be loaded")
   137  	}
   138  
   139  	// This should no longer error
   140  	storage.Mode = GetModeNone
   141  	if err := tree.Load(storage); err != nil {
   142  		t.Fatalf("err: %s", err)
   143  	}
   144  
   145  	// Now we copy the directory, this COPIES symlink values, and
   146  	// doesn't create symlinks themselves. That is important.
   147  	dir2 := tempDir(t)
   148  	os.RemoveAll(dir2)
   149  	defer os.RemoveAll(dir2)
   150  	if err := copy.CopyDir(dir, dir2); err != nil {
   151  		t.Fatalf("err: %s", err)
   152  	}
   153  
   154  	// Now copy the configuration
   155  	cfgDir := tempDir(t)
   156  	os.RemoveAll(cfgDir)
   157  	defer os.RemoveAll(cfgDir)
   158  	if err := copy.CopyDir(cfg.Dir, cfgDir); err != nil {
   159  		t.Fatalf("err: %s", err)
   160  	}
   161  
   162  	{
   163  		cfg, err := config.LoadDir(cfgDir)
   164  		if err != nil {
   165  			t.Fatalf("err: %s", err)
   166  		}
   167  
   168  		tree := NewTree("", cfg)
   169  		storage := &Storage{
   170  			StorageDir: dir2,
   171  			Mode:       GetModeNone,
   172  		}
   173  
   174  		// This should not error since we already got it!
   175  		if err := tree.Load(storage); err != nil {
   176  			t.Fatalf("err: %s", err)
   177  		}
   178  
   179  		if !tree.Loaded() {
   180  			t.Fatal("should be loaded")
   181  		}
   182  	}
   183  }
   184  
   185  func TestTreeLoad_parentRef(t *testing.T) {
   186  	storage := testStorage(t, nil)
   187  	tree := NewTree("", testConfig(t, "basic-parent"))
   188  
   189  	if tree.Loaded() {
   190  		t.Fatal("should not be loaded")
   191  	}
   192  
   193  	// This should error because we haven't gotten things yet
   194  	storage.Mode = GetModeNone
   195  	if err := tree.Load(storage); err == nil {
   196  		t.Fatal("should error")
   197  	}
   198  
   199  	if tree.Loaded() {
   200  		t.Fatal("should not be loaded")
   201  	}
   202  
   203  	// This should get things
   204  	storage.Mode = GetModeGet
   205  	if err := tree.Load(storage); err != nil {
   206  		t.Fatalf("err: %s", err)
   207  	}
   208  
   209  	if !tree.Loaded() {
   210  		t.Fatal("should be loaded")
   211  	}
   212  
   213  	// This should no longer error
   214  	storage.Mode = GetModeNone
   215  	if err := tree.Load(storage); err != nil {
   216  		t.Fatalf("err: %s", err)
   217  	}
   218  
   219  	actual := strings.TrimSpace(tree.String())
   220  	expected := strings.TrimSpace(treeLoadParentStr)
   221  	if actual != expected {
   222  		t.Fatalf("bad: \n\n%s", actual)
   223  	}
   224  }
   225  
   226  func TestTreeLoad_subdir(t *testing.T) {
   227  	fixtures := []string{
   228  		"basic-subdir",
   229  		"basic-tar-subdir",
   230  		"tar-subdir-to-parent",
   231  	}
   232  
   233  	for _, tc := range fixtures {
   234  		t.Run(tc, func(t *testing.T) {
   235  			storage := testStorage(t, nil)
   236  			tree := NewTree("", testConfig(t, tc))
   237  
   238  			if tree.Loaded() {
   239  				t.Fatal("should not be loaded")
   240  			}
   241  
   242  			// This should error because we haven't gotten things yet
   243  			storage.Mode = GetModeNone
   244  			if err := tree.Load(storage); err == nil {
   245  				t.Fatal("should error")
   246  			}
   247  
   248  			if tree.Loaded() {
   249  				t.Fatal("should not be loaded")
   250  			}
   251  
   252  			// This should get things
   253  			storage.Mode = GetModeGet
   254  			if err := tree.Load(storage); err != nil {
   255  				t.Fatalf("err: %s", err)
   256  			}
   257  
   258  			if !tree.Loaded() {
   259  				t.Fatal("should be loaded")
   260  			}
   261  
   262  			// This should no longer error
   263  			storage.Mode = GetModeNone
   264  			if err := tree.Load(storage); err != nil {
   265  				t.Fatalf("err: %s", err)
   266  			}
   267  
   268  			actual := strings.TrimSpace(tree.String())
   269  			expected := strings.TrimSpace(treeLoadSubdirStr)
   270  			if actual != expected {
   271  				t.Fatalf("bad: \n\n%s", actual)
   272  			}
   273  		})
   274  	}
   275  }
   276  
   277  func TestTree_recordManifest(t *testing.T) {
   278  	td, err := ioutil.TempDir("", "tf-module")
   279  	if err != nil {
   280  		t.Fatal(err)
   281  	}
   282  	defer os.RemoveAll(td)
   283  
   284  	storage := Storage{StorageDir: td}
   285  
   286  	dir := filepath.Join(td, "0131bf0fef686e090b16bdbab4910ddf")
   287  
   288  	subDir := "subDirName"
   289  
   290  	// record and read the subdir path
   291  	if err := storage.recordModuleRoot(dir, subDir); err != nil {
   292  		t.Fatal(err)
   293  	}
   294  	actual, err := storage.getModuleRoot(dir)
   295  	if err != nil {
   296  		t.Fatal(err)
   297  	}
   298  
   299  	if actual != subDir {
   300  		t.Fatalf("expected subDir %q, got %q", subDir, actual)
   301  	}
   302  
   303  	// overwrite the path, and nmake sure we get the new one
   304  	subDir = "newSubDir"
   305  	if err := storage.recordModuleRoot(dir, subDir); err != nil {
   306  		t.Fatal(err)
   307  	}
   308  	actual, err = storage.getModuleRoot(dir)
   309  	if err != nil {
   310  		t.Fatal(err)
   311  	}
   312  
   313  	if actual != subDir {
   314  		t.Fatalf("expected subDir %q, got %q", subDir, actual)
   315  	}
   316  
   317  	// create a fake entry
   318  	if err := ioutil.WriteFile(filepath.Join(td, manifestName), []byte("BAD DATA"), 0644); err != nil {
   319  		t.Fatal(err)
   320  	}
   321  
   322  	// this should fail because there aare now 2 entries
   323  	actual, err = storage.getModuleRoot(dir)
   324  	if err == nil {
   325  		t.Fatal("expected multiple subdir entries")
   326  	}
   327  
   328  	// writing the subdir entry should remove the incorrect value
   329  	if err := storage.recordModuleRoot(dir, subDir); err != nil {
   330  		t.Fatal(err)
   331  	}
   332  	actual, err = storage.getModuleRoot(dir)
   333  	if err != nil {
   334  		t.Fatal(err)
   335  	}
   336  
   337  	if actual != subDir {
   338  		t.Fatalf("expected subDir %q, got %q", subDir, actual)
   339  	}
   340  }
   341  
   342  func TestTreeModules(t *testing.T) {
   343  	tree := NewTree("", testConfig(t, "basic"))
   344  	actual := tree.Modules()
   345  
   346  	expected := []*Module{
   347  		&Module{Name: "foo", Source: "./foo"},
   348  	}
   349  
   350  	if !reflect.DeepEqual(actual, expected) {
   351  		t.Fatalf("bad: %#v", actual)
   352  	}
   353  }
   354  
   355  func TestTreeName(t *testing.T) {
   356  	tree := NewTree("", testConfig(t, "basic"))
   357  	actual := tree.Name()
   358  
   359  	if actual != RootName {
   360  		t.Fatalf("bad: %#v", actual)
   361  	}
   362  }
   363  
   364  // This is a table-driven test for tree validation. This is the preferred
   365  // way to test Validate. Non table-driven tests exist historically but
   366  // that style shouldn't be done anymore.
   367  func TestTreeValidate_table(t *testing.T) {
   368  	cases := []struct {
   369  		Name    string
   370  		Fixture string
   371  		Err     string
   372  	}{
   373  		{
   374  			"provider alias in child",
   375  			"validate-alias-good",
   376  			"",
   377  		},
   378  
   379  		{
   380  			"undefined provider alias in child",
   381  			"validate-alias-bad",
   382  			"alias must be defined",
   383  		},
   384  
   385  		{
   386  			"root module named root",
   387  			"validate-module-root",
   388  			"cannot contain module",
   389  		},
   390  
   391  		{
   392  			"grandchild module named root",
   393  			"validate-module-root-grandchild",
   394  			"",
   395  		},
   396  	}
   397  
   398  	for i, tc := range cases {
   399  		t.Run(fmt.Sprintf("%d-%s", i, tc.Name), func(t *testing.T) {
   400  			tree := NewTree("", testConfig(t, tc.Fixture))
   401  			storage := testStorage(t, nil)
   402  			storage.Mode = GetModeGet
   403  			if err := tree.Load(storage); err != nil {
   404  				t.Fatalf("err: %s", err)
   405  			}
   406  
   407  			diags := tree.Validate()
   408  			if (diags.HasErrors()) != (tc.Err != "") {
   409  				t.Fatalf("err: %s", diags.Err())
   410  			}
   411  			if len(diags) == 0 {
   412  				return
   413  			}
   414  			if !strings.Contains(diags.Err().Error(), tc.Err) {
   415  				t.Fatalf("err should contain %q: %s", tc.Err, diags.Err().Error())
   416  			}
   417  		})
   418  	}
   419  }
   420  
   421  func TestTreeValidate_badChild(t *testing.T) {
   422  	tree := NewTree("", testConfig(t, "validate-child-bad"))
   423  
   424  	storage := testStorage(t, nil)
   425  	storage.Mode = GetModeGet
   426  	if err := tree.Load(storage); err != nil {
   427  		t.Fatalf("err: %s", err)
   428  	}
   429  
   430  	if err := tree.Validate(); err == nil {
   431  		t.Fatal("should error")
   432  	}
   433  }
   434  
   435  func TestTreeValidate_badChildOutput(t *testing.T) {
   436  	tree := NewTree("", testConfig(t, "validate-bad-output"))
   437  
   438  	storage := testStorage(t, nil)
   439  	storage.Mode = GetModeGet
   440  	if err := tree.Load(storage); err != nil {
   441  		t.Fatalf("err: %s", err)
   442  	}
   443  
   444  	if err := tree.Validate(); err == nil {
   445  		t.Fatal("should error")
   446  	}
   447  }
   448  
   449  func TestTreeValidate_badChildOutputToModule(t *testing.T) {
   450  	tree := NewTree("", testConfig(t, "validate-bad-output-to-module"))
   451  
   452  	storage := testStorage(t, nil)
   453  	storage.Mode = GetModeGet
   454  	if err := tree.Load(storage); err != nil {
   455  		t.Fatalf("err: %s", err)
   456  	}
   457  
   458  	if err := tree.Validate(); err == nil {
   459  		t.Fatal("should error")
   460  	}
   461  }
   462  
   463  func TestTreeValidate_badChildVar(t *testing.T) {
   464  	tree := NewTree("", testConfig(t, "validate-bad-var"))
   465  
   466  	storage := testStorage(t, nil)
   467  	storage.Mode = GetModeGet
   468  	if err := tree.Load(storage); err != nil {
   469  		t.Fatalf("err: %s", err)
   470  	}
   471  
   472  	if err := tree.Validate(); err == nil {
   473  		t.Fatal("should error")
   474  	}
   475  }
   476  
   477  func TestTreeValidate_badRoot(t *testing.T) {
   478  	tree := NewTree("", testConfig(t, "validate-root-bad"))
   479  
   480  	storage := testStorage(t, nil)
   481  	storage.Mode = GetModeGet
   482  	if err := tree.Load(storage); err != nil {
   483  		t.Fatalf("err: %s", err)
   484  	}
   485  
   486  	if err := tree.Validate(); err == nil {
   487  		t.Fatal("should error")
   488  	}
   489  }
   490  
   491  func TestTreeValidate_good(t *testing.T) {
   492  	tree := NewTree("", testConfig(t, "validate-child-good"))
   493  
   494  	storage := testStorage(t, nil)
   495  	storage.Mode = GetModeGet
   496  	if err := tree.Load(storage); err != nil {
   497  		t.Fatalf("err: %s", err)
   498  	}
   499  
   500  	if err := tree.Validate(); err != nil {
   501  		t.Fatalf("err: %s", err)
   502  	}
   503  }
   504  
   505  func TestTreeValidate_notLoaded(t *testing.T) {
   506  	tree := NewTree("", testConfig(t, "basic"))
   507  
   508  	if err := tree.Validate(); err == nil {
   509  		t.Fatal("should error")
   510  	}
   511  }
   512  
   513  func TestTreeValidate_requiredChildVar(t *testing.T) {
   514  	tree := NewTree("", testConfig(t, "validate-required-var"))
   515  
   516  	storage := testStorage(t, nil)
   517  	storage.Mode = GetModeGet
   518  	if err := tree.Load(storage); err != nil {
   519  		t.Fatalf("err: %s", err)
   520  	}
   521  
   522  	diags := tree.Validate()
   523  	if !diags.HasErrors() {
   524  		t.Fatal("should error")
   525  	}
   526  
   527  	// ensure both variables are mentioned in the output
   528  	errMsg := diags.Err().Error()
   529  	for _, v := range []string{"feature", "memory"} {
   530  		if !strings.Contains(errMsg, v) {
   531  			t.Fatalf("no mention of missing variable %q", v)
   532  		}
   533  	}
   534  }
   535  
   536  func TestTreeValidate_unknownModule(t *testing.T) {
   537  	tree := NewTree("", testConfig(t, "validate-module-unknown"))
   538  
   539  	storage := testStorage(t, nil)
   540  	storage.Mode = GetModeNone
   541  	if err := tree.Load(storage); err != nil {
   542  		t.Fatalf("err: %s", err)
   543  	}
   544  
   545  	if err := tree.Validate(); err == nil {
   546  		t.Fatal("should error")
   547  	}
   548  }
   549  
   550  func TestTreeLoad_conflictingSubmoduleNames(t *testing.T) {
   551  	storage := testStorage(t, nil)
   552  	tree := NewTree("", testConfig(t, "conficting-submodule-names"))
   553  
   554  	storage.Mode = GetModeGet
   555  	if err := tree.Load(storage); err != nil {
   556  		t.Fatalf("load failed: %s", err)
   557  	}
   558  
   559  	if !tree.Loaded() {
   560  		t.Fatal("should be loaded")
   561  	}
   562  
   563  	// Try to reload
   564  	storage.Mode = GetModeNone
   565  	if err := tree.Load(storage); err != nil {
   566  		t.Fatalf("reload failed: %s", err)
   567  	}
   568  
   569  	// verify that the grand-children are correctly loaded
   570  	for _, c := range tree.Children() {
   571  		for _, gc := range c.Children() {
   572  			if len(gc.config.Resources) != 1 {
   573  				t.Fatalf("expected 1 resource in %s, got %d", gc.name, len(gc.config.Resources))
   574  			}
   575  			res := gc.config.Resources[0]
   576  			switch gc.path[0] {
   577  			case "a":
   578  				if res.Name != "a-c" {
   579  					t.Fatal("found wrong resource in a/c:", res.Name)
   580  				}
   581  			case "b":
   582  				if res.Name != "b-c" {
   583  					t.Fatal("found wrong resource in b/c:", res.Name)
   584  				}
   585  			}
   586  		}
   587  	}
   588  }
   589  
   590  // changing the source for a module but not the module "path"
   591  func TestTreeLoad_changeIntermediateSource(t *testing.T) {
   592  	// copy the config to our tempdir this time, since we're going to edit it
   593  	td, err := ioutil.TempDir("", "tf")
   594  	if err != nil {
   595  		t.Fatalf("err: %s", err)
   596  	}
   597  	defer os.RemoveAll(td)
   598  
   599  	if err := copyDir(td, filepath.Join(fixtureDir, "change-intermediate-source")); err != nil {
   600  		t.Fatal(err)
   601  	}
   602  
   603  	wd, err := os.Getwd()
   604  	if err != nil {
   605  		t.Fatal(err)
   606  	}
   607  	if err := os.Chdir(td); err != nil {
   608  		t.Fatal(err)
   609  	}
   610  	defer os.Chdir(wd)
   611  
   612  	if err := os.MkdirAll(".terraform/modules", 0777); err != nil {
   613  		t.Fatal(err)
   614  	}
   615  	storage := &Storage{StorageDir: ".terraform/modules"}
   616  	cfg, err := config.LoadDir("./")
   617  	if err != nil {
   618  		t.Fatal(err)
   619  	}
   620  	tree := NewTree("", cfg)
   621  	storage.Mode = GetModeGet
   622  	if err := tree.Load(storage); err != nil {
   623  		t.Fatalf("load failed: %s", err)
   624  	}
   625  
   626  	// now we change the source of our module, without changing its path
   627  	if err := os.Rename("main.tf.disabled", "main.tf"); err != nil {
   628  		t.Fatal(err)
   629  	}
   630  
   631  	// reload the tree
   632  	cfg, err = config.LoadDir("./")
   633  	if err != nil {
   634  		t.Fatal(err)
   635  	}
   636  	tree = NewTree("", cfg)
   637  	if err := tree.Load(storage); err != nil {
   638  		t.Fatalf("load failed: %s", err)
   639  	}
   640  
   641  	// check for our resource in b
   642  	for _, c := range tree.Children() {
   643  		for _, gc := range c.Children() {
   644  			if len(gc.config.Resources) != 1 {
   645  				t.Fatalf("expected 1 resource in %s, got %d", gc.name, len(gc.config.Resources))
   646  			}
   647  			res := gc.config.Resources[0]
   648  			expected := "c-b"
   649  			if res.Name != expected {
   650  				t.Fatalf("expexted resource %q, got %q", expected, res.Name)
   651  			}
   652  		}
   653  	}
   654  }
   655  
   656  const treeLoadStr = `
   657  root
   658    foo (path: foo)
   659  `
   660  
   661  const treeLoadParentStr = `
   662  root
   663    a (path: a)
   664      b (path: a, b)
   665  `
   666  const treeLoadSubdirStr = `
   667  root
   668    foo (path: foo)
   669      bar (path: foo, bar)
   670  `
   671  
   672  const treeLoadRegistrySubdirStr = `
   673  root
   674    foo (path: foo)
   675  `