internal/initwd: follow local module path symlink (#21063)

* internal/initwd: follow local module path symlink

Fixes #21060

While a previous commit fixed a problem when the local module directory
contained a symlink, it did not account for the possibility that the
entire directory was a symlink.
This commit is contained in:
Kristin Laemmert 2019-04-24 08:19:27 -04:00 committed by GitHub
parent 64f419ac76
commit 9327eedb04
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 101 additions and 1 deletions

View File

@ -2,7 +2,6 @@ package initwd
import ( import (
"fmt" "fmt"
"github.com/hashicorp/terraform/registry"
"log" "log"
"os" "os"
"path/filepath" "path/filepath"
@ -13,6 +12,7 @@ import (
"github.com/hashicorp/terraform/addrs" "github.com/hashicorp/terraform/addrs"
"github.com/hashicorp/terraform/internal/earlyconfig" "github.com/hashicorp/terraform/internal/earlyconfig"
"github.com/hashicorp/terraform/internal/modsdir" "github.com/hashicorp/terraform/internal/modsdir"
"github.com/hashicorp/terraform/registry"
"github.com/hashicorp/terraform/registry/regsrc" "github.com/hashicorp/terraform/registry/regsrc"
"github.com/hashicorp/terraform/tfdiags" "github.com/hashicorp/terraform/tfdiags"
) )
@ -245,7 +245,18 @@ func (i *ModuleInstaller) installLocalModule(req *earlyconfig.ModuleRequest, key
// filesystem at all because the parent already wrote // filesystem at all because the parent already wrote
// the files we need, and so we just load up what's already here. // the files we need, and so we just load up what's already here.
newDir := filepath.Join(parentRecord.Dir, req.SourceAddr) newDir := filepath.Join(parentRecord.Dir, req.SourceAddr)
log.Printf("[TRACE] ModuleInstaller: %s uses directory from parent: %s", key, newDir) log.Printf("[TRACE] ModuleInstaller: %s uses directory from parent: %s", key, newDir)
// it is possible that the local directory is a symlink
newDir, err := filepath.EvalSymlinks(newDir)
if err != nil {
diags = diags.Append(tfdiags.Sourceless(
tfdiags.Error,
"Unreadable module directory",
fmt.Sprintf("Unable to evaluate directory symlink: %s", err.Error()),
))
}
mod, mDiags := earlyconfig.LoadModule(newDir) mod, mDiags := earlyconfig.LoadModule(newDir)
if mod == nil { if mod == nil {
// nil indicates missing or unreadable directory, so we'll // nil indicates missing or unreadable directory, so we'll

View File

@ -111,6 +111,67 @@ func TestModuleInstaller_error(t *testing.T) {
} }
} }
func TestModuleInstaller_symlink(t *testing.T) {
fixtureDir := filepath.Clean("test-fixtures/local-module-symlink")
dir, done := tempChdir(t, fixtureDir)
defer done()
hooks := &testInstallHooks{}
modulesDir := filepath.Join(dir, ".terraform/modules")
inst := NewModuleInstaller(modulesDir, nil)
_, diags := inst.InstallModules(".", false, hooks)
assertNoDiagnostics(t, diags)
wantCalls := []testInstallHookCall{
{
Name: "Install",
ModuleAddr: "child_a",
PackageAddr: "",
LocalPath: "child_a",
},
{
Name: "Install",
ModuleAddr: "child_a.child_b",
PackageAddr: "",
LocalPath: "child_a/child_b",
},
}
if assertResultDeepEqual(t, hooks.Calls, wantCalls) {
return
}
loader, err := configload.NewLoader(&configload.Config{
ModulesDir: modulesDir,
})
if err != nil {
t.Fatal(err)
}
// Make sure the configuration is loadable now.
// (This ensures that correct information is recorded in the manifest.)
config, loadDiags := loader.LoadConfig(".")
assertNoDiagnostics(t, tfdiags.Diagnostics{}.Append(loadDiags))
wantTraces := map[string]string{
"": "in root module",
"child_a": "in child_a module",
"child_a.child_b": "in child_b module",
}
gotTraces := map[string]string{}
config.DeepEach(func(c *configs.Config) {
path := strings.Join(c.Path, ".")
if c.Module.Variables["v"] == nil {
gotTraces[path] = "<missing>"
return
}
varDesc := c.Module.Variables["v"].Description
gotTraces[path] = varDesc
})
assertResultDeepEqual(t, gotTraces, wantTraces)
}
func TestLoaderInstallModules_registry(t *testing.T) { func TestLoaderInstallModules_registry(t *testing.T) {
if os.Getenv("TF_ACC") == "" { if os.Getenv("TF_ACC") == "" {
t.Skip("this test accesses registry.terraform.io and github.com; set TF_ACC=1 to run it") t.Skip("this test accesses registry.terraform.io and github.com; set TF_ACC=1 to run it")

View File

@ -0,0 +1,9 @@
variable "v" {
description = "in child_a module"
default = ""
}
module "child_b" {
source = "./child_b"
}

View File

@ -0,0 +1,9 @@
variable "v" {
description = "in child_b module"
default = ""
}
output "hello" {
value = "Hello from child_b!"
}

View File

@ -0,0 +1 @@
../child_a/

View File

@ -0,0 +1,9 @@
variable "v" {
description = "in root module"
default = ""
}
module "child_a" {
source = "./modules/child_a"
}