github.com/hugorut/terraform@v1.1.3/src/initwd/from_module_test.go (about)

     1  package initwd
     2  
     3  import (
     4  	"context"
     5  	"io/ioutil"
     6  	"os"
     7  	"path/filepath"
     8  	"strings"
     9  	"testing"
    10  
    11  	"github.com/google/go-cmp/cmp"
    12  	version "github.com/hashicorp/go-version"
    13  	"github.com/hugorut/terraform/src/configs"
    14  	"github.com/hugorut/terraform/src/configs/configload"
    15  	"github.com/hugorut/terraform/src/copy"
    16  	"github.com/hugorut/terraform/src/registry"
    17  	"github.com/hugorut/terraform/src/tfdiags"
    18  )
    19  
    20  func TestDirFromModule_registry(t *testing.T) {
    21  	if os.Getenv("TF_ACC") == "" {
    22  		t.Skip("this test accesses registry.terraform.io and github.com; set TF_ACC=1 to run it")
    23  	}
    24  
    25  	fixtureDir := filepath.Clean("testdata/empty")
    26  	tmpDir, done := tempChdir(t, fixtureDir)
    27  	defer done()
    28  
    29  	// the module installer runs filepath.EvalSymlinks() on the destination
    30  	// directory before copying files, and the resultant directory is what is
    31  	// returned by the install hooks. Without this, tests could fail on machines
    32  	// where the default temp dir was a symlink.
    33  	dir, err := filepath.EvalSymlinks(tmpDir)
    34  	if err != nil {
    35  		t.Error(err)
    36  	}
    37  	modsDir := filepath.Join(dir, ".terraform/modules")
    38  
    39  	hooks := &testInstallHooks{}
    40  
    41  	reg := registry.NewClient(nil, nil)
    42  	diags := DirFromModule(context.Background(), dir, modsDir, "hashicorp/module-installer-acctest/aws//examples/main", reg, hooks)
    43  	assertNoDiagnostics(t, diags)
    44  
    45  	v := version.Must(version.NewVersion("0.0.2"))
    46  
    47  	wantCalls := []testInstallHookCall{
    48  		// The module specified to populate the root directory is not mentioned
    49  		// here, because the hook mechanism is defined to talk about descendent
    50  		// modules only and so a caller to InitDirFromModule is expected to
    51  		// produce its own user-facing announcement about the root module being
    52  		// installed.
    53  
    54  		// Note that "root" in the following examples is, confusingly, the
    55  		// label on the module block in the example we've installed here:
    56  		//     module "root" {
    57  
    58  		{
    59  			Name:        "Download",
    60  			ModuleAddr:  "root",
    61  			PackageAddr: "registry.terraform.io/hashicorp/module-installer-acctest/aws",
    62  			Version:     v,
    63  		},
    64  		{
    65  			Name:       "Install",
    66  			ModuleAddr: "root",
    67  			Version:    v,
    68  			// NOTE: This local path and the other paths derived from it below
    69  			// can vary depending on how the registry is implemented. At the
    70  			// time of writing this test, registry.terraform.io returns
    71  			// git repository source addresses and so this path refers to the
    72  			// root of the git clone, but historically the registry referred
    73  			// to GitHub-provided tar archives which meant that there was an
    74  			// extra level of subdirectory here for the typical directory
    75  			// nesting in tar archives, which would've been reflected as
    76  			// an extra segment on this path. If this test fails due to an
    77  			// additional path segment in future, then a change to the upstream
    78  			// registry might be the root cause.
    79  			LocalPath: filepath.Join(dir, ".terraform/modules/root"),
    80  		},
    81  		{
    82  			Name:       "Install",
    83  			ModuleAddr: "root.child_a",
    84  			LocalPath:  filepath.Join(dir, ".terraform/modules/root/modules/child_a"),
    85  		},
    86  		{
    87  			Name:       "Install",
    88  			ModuleAddr: "root.child_a.child_b",
    89  			LocalPath:  filepath.Join(dir, ".terraform/modules/root/modules/child_b"),
    90  		},
    91  	}
    92  
    93  	if diff := cmp.Diff(wantCalls, hooks.Calls); diff != "" {
    94  		t.Fatalf("wrong installer calls\n%s", diff)
    95  	}
    96  
    97  	loader, err := configload.NewLoader(&configload.Config{
    98  		ModulesDir: modsDir,
    99  	})
   100  	if err != nil {
   101  		t.Fatal(err)
   102  	}
   103  
   104  	// Make sure the configuration is loadable now.
   105  	// (This ensures that correct information is recorded in the manifest.)
   106  	config, loadDiags := loader.LoadConfig(".")
   107  	if assertNoDiagnostics(t, tfdiags.Diagnostics{}.Append(loadDiags)) {
   108  		return
   109  	}
   110  
   111  	wantTraces := map[string]string{
   112  		"":                     "in example",
   113  		"root":                 "in root module",
   114  		"root.child_a":         "in child_a module",
   115  		"root.child_a.child_b": "in child_b module",
   116  	}
   117  	gotTraces := map[string]string{}
   118  	config.DeepEach(func(c *configs.Config) {
   119  		path := strings.Join(c.Path, ".")
   120  		if c.Module.Variables["v"] == nil {
   121  			gotTraces[path] = "<missing>"
   122  			return
   123  		}
   124  		varDesc := c.Module.Variables["v"].Description
   125  		gotTraces[path] = varDesc
   126  	})
   127  	assertResultDeepEqual(t, gotTraces, wantTraces)
   128  }
   129  
   130  func TestDirFromModule_submodules(t *testing.T) {
   131  	fixtureDir := filepath.Clean("testdata/empty")
   132  	fromModuleDir, err := filepath.Abs("./testdata/local-modules")
   133  	if err != nil {
   134  		t.Fatal(err)
   135  	}
   136  
   137  	// DirFromModule will expand ("canonicalize") the pathnames, so we must do
   138  	// the same for our "wantCalls" comparison values. Otherwise this test
   139  	// will fail when building in a source tree with symlinks in $PWD.
   140  	//
   141  	// See also: https://github.com/hugorut/terraform/issues/26014
   142  	//
   143  	fromModuleDirRealpath, err := filepath.EvalSymlinks(fromModuleDir)
   144  	if err != nil {
   145  		t.Error(err)
   146  	}
   147  
   148  	tmpDir, done := tempChdir(t, fixtureDir)
   149  	defer done()
   150  
   151  	hooks := &testInstallHooks{}
   152  	dir, err := filepath.EvalSymlinks(tmpDir)
   153  	if err != nil {
   154  		t.Error(err)
   155  	}
   156  	modInstallDir := filepath.Join(dir, ".terraform/modules")
   157  
   158  	diags := DirFromModule(context.Background(), dir, modInstallDir, fromModuleDir, nil, hooks)
   159  	assertNoDiagnostics(t, diags)
   160  	wantCalls := []testInstallHookCall{
   161  		{
   162  			Name:       "Install",
   163  			ModuleAddr: "child_a",
   164  			LocalPath:  filepath.Join(fromModuleDirRealpath, "child_a"),
   165  		},
   166  		{
   167  			Name:       "Install",
   168  			ModuleAddr: "child_a.child_b",
   169  			LocalPath:  filepath.Join(fromModuleDirRealpath, "child_a/child_b"),
   170  		},
   171  	}
   172  
   173  	if assertResultDeepEqual(t, hooks.Calls, wantCalls) {
   174  		return
   175  	}
   176  
   177  	loader, err := configload.NewLoader(&configload.Config{
   178  		ModulesDir: modInstallDir,
   179  	})
   180  	if err != nil {
   181  		t.Fatal(err)
   182  	}
   183  
   184  	// Make sure the configuration is loadable now.
   185  	// (This ensures that correct information is recorded in the manifest.)
   186  	config, loadDiags := loader.LoadConfig(".")
   187  	if assertNoDiagnostics(t, tfdiags.Diagnostics{}.Append(loadDiags)) {
   188  		return
   189  	}
   190  	wantTraces := map[string]string{
   191  		"":                "in root module",
   192  		"child_a":         "in child_a module",
   193  		"child_a.child_b": "in child_b module",
   194  	}
   195  	gotTraces := map[string]string{}
   196  
   197  	config.DeepEach(func(c *configs.Config) {
   198  		path := strings.Join(c.Path, ".")
   199  		if c.Module.Variables["v"] == nil {
   200  			gotTraces[path] = "<missing>"
   201  			return
   202  		}
   203  		varDesc := c.Module.Variables["v"].Description
   204  		gotTraces[path] = varDesc
   205  	})
   206  	assertResultDeepEqual(t, gotTraces, wantTraces)
   207  }
   208  
   209  // TestDirFromModule_rel_submodules is similar to the test above, but the
   210  // from-module is relative to the install dir ("../"):
   211  // https://github.com/hugorut/terraform/issues/23010
   212  func TestDirFromModule_rel_submodules(t *testing.T) {
   213  	// This test creates a tmpdir with the following directory structure:
   214  	// - tmpdir/local-modules (with contents of testdata/local-modules)
   215  	// - tmpdir/empty: the workDir we CD into for the test
   216  	// - tmpdir/empty/target (target, the destination for init -from-module)
   217  	tmpDir, err := ioutil.TempDir("", "terraform-configload")
   218  	if err != nil {
   219  		t.Fatal(err)
   220  	}
   221  	fromModuleDir := filepath.Join(tmpDir, "local-modules")
   222  	workDir := filepath.Join(tmpDir, "empty")
   223  	if err := os.Mkdir(fromModuleDir, os.ModePerm); err != nil {
   224  		t.Fatal(err)
   225  	}
   226  	if err := copy.CopyDir(fromModuleDir, "testdata/local-modules"); err != nil {
   227  		t.Fatal(err)
   228  	}
   229  	if err := os.Mkdir(workDir, os.ModePerm); err != nil {
   230  		t.Fatal(err)
   231  	}
   232  
   233  	targetDir := filepath.Join(tmpDir, "target")
   234  	if err := os.Mkdir(targetDir, os.ModePerm); err != nil {
   235  		t.Fatal(err)
   236  	}
   237  	oldDir, err := os.Getwd()
   238  	if err != nil {
   239  		t.Fatal(err)
   240  	}
   241  	err = os.Chdir(targetDir)
   242  	if err != nil {
   243  		t.Fatalf("failed to switch to temp dir %s: %s", tmpDir, err)
   244  	}
   245  	defer os.Chdir(oldDir)
   246  	defer os.RemoveAll(tmpDir)
   247  
   248  	hooks := &testInstallHooks{}
   249  
   250  	modInstallDir := ".terraform/modules"
   251  	sourceDir := "../local-modules"
   252  	diags := DirFromModule(context.Background(), ".", modInstallDir, sourceDir, nil, hooks)
   253  	assertNoDiagnostics(t, diags)
   254  	wantCalls := []testInstallHookCall{
   255  		{
   256  			Name:       "Install",
   257  			ModuleAddr: "child_a",
   258  			LocalPath:  filepath.Join(sourceDir, "child_a"),
   259  		},
   260  		{
   261  			Name:       "Install",
   262  			ModuleAddr: "child_a.child_b",
   263  			LocalPath:  filepath.Join(sourceDir, "child_a/child_b"),
   264  		},
   265  	}
   266  
   267  	if assertResultDeepEqual(t, hooks.Calls, wantCalls) {
   268  		return
   269  	}
   270  
   271  	loader, err := configload.NewLoader(&configload.Config{
   272  		ModulesDir: modInstallDir,
   273  	})
   274  	if err != nil {
   275  		t.Fatal(err)
   276  	}
   277  
   278  	// Make sure the configuration is loadable now.
   279  	// (This ensures that correct information is recorded in the manifest.)
   280  	config, loadDiags := loader.LoadConfig(".")
   281  	if assertNoDiagnostics(t, tfdiags.Diagnostics{}.Append(loadDiags)) {
   282  		return
   283  	}
   284  	wantTraces := map[string]string{
   285  		"":                "in root module",
   286  		"child_a":         "in child_a module",
   287  		"child_a.child_b": "in child_b module",
   288  	}
   289  	gotTraces := map[string]string{}
   290  
   291  	config.DeepEach(func(c *configs.Config) {
   292  		path := strings.Join(c.Path, ".")
   293  		if c.Module.Variables["v"] == nil {
   294  			gotTraces[path] = "<missing>"
   295  			return
   296  		}
   297  		varDesc := c.Module.Variables["v"].Description
   298  		gotTraces[path] = varDesc
   299  	})
   300  	assertResultDeepEqual(t, gotTraces, wantTraces)
   301  }