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 }