config/module: use the raw source as part of the key

This changes the key for the storage to be the _raw_ source from the
module, not the fully expanded source. Example: it'll be a relative path
instead of an absolute path.

This allows the ".terraform/modules" directory to be portable when
moving to other machines. This was a behavior that existed in <= 0.7.2
and was broken with #8398. This amends that and adds a test to verify.
This commit is contained in:
Mitchell Hashimoto 2016-09-29 17:47:47 -07:00
parent 654686496c
commit 098225dc0d
No known key found for this signature in database
GPG Key ID: 744E147AA52F5B0A
3 changed files with 184 additions and 1 deletions

View File

@ -170,7 +170,7 @@ func (t *Tree) Load(s getter.Storage, mode GetMode) error {
// Get the directory where this module is so we can load it
key := strings.Join(path, ".")
key = fmt.Sprintf("root.%s-%s", key, source)
key = fmt.Sprintf("root.%s-%s", key, m.Source)
dir, ok, err := getStorage(s, key, source, mode)
if err != nil {
return err

View File

@ -1,9 +1,14 @@
package module
import (
"os"
"reflect"
"strings"
"testing"
"github.com/hashicorp/go-getter"
"github.com/hashicorp/terraform/config"
"github.com/hashicorp/terraform/helper/copy"
)
func TestTreeChild(t *testing.T) {
@ -102,6 +107,63 @@ func TestTreeLoad_duplicate(t *testing.T) {
}
}
func TestTreeLoad_copyable(t *testing.T) {
dir := tempDir(t)
storage := &getter.FolderStorage{StorageDir: dir}
cfg := testConfig(t, "basic")
tree := NewTree("", cfg)
// This should get things
if err := tree.Load(storage, GetModeGet); err != nil {
t.Fatalf("err: %s", err)
}
if !tree.Loaded() {
t.Fatal("should be loaded")
}
// This should no longer error
if err := tree.Load(storage, GetModeNone); err != nil {
t.Fatalf("err: %s", err)
}
// Now we copy the directory, this COPIES symlink values, and
// doesn't create symlinks themselves. That is important.
dir2 := tempDir(t)
os.RemoveAll(dir2)
defer os.RemoveAll(dir2)
if err := copy.CopyDir(dir, dir2); err != nil {
t.Fatalf("err: %s", err)
}
// Now copy the configuration
cfgDir := tempDir(t)
os.RemoveAll(cfgDir)
defer os.RemoveAll(cfgDir)
if err := copy.CopyDir(cfg.Dir, cfgDir); err != nil {
t.Fatalf("err: %s", err)
}
{
cfg, err := config.LoadDir(cfgDir)
if err != nil {
t.Fatalf("err: %s", err)
}
tree := NewTree("", cfg)
storage := &getter.FolderStorage{StorageDir: dir2}
// This should not error since we already got it!
if err := tree.Load(storage, GetModeNone); err != nil {
t.Fatalf("err: %s", err)
}
if !tree.Loaded() {
t.Fatal("should be loaded")
}
}
}
func TestTreeLoad_parentRef(t *testing.T) {
storage := testStorage(t)
tree := NewTree("", testConfig(t, "basic-parent"))

121
helper/copy/copy.go Normal file
View File

@ -0,0 +1,121 @@
package copy
import (
"fmt"
"io"
"io/ioutil"
"os"
"path/filepath"
)
// From: https://gist.github.com/m4ng0squ4sh/92462b38df26839a3ca324697c8cba04
// CopyFile copies the contents of the file named src to the file named
// by dst. The file will be created if it does not already exist. If the
// destination file exists, all it's contents will be replaced by the contents
// of the source file. The file mode will be copied from the source and
// the copied data is synced/flushed to stable storage.
func CopyFile(src, dst string) (err error) {
in, err := os.Open(src)
if err != nil {
return
}
defer in.Close()
out, err := os.Create(dst)
if err != nil {
return
}
defer func() {
if e := out.Close(); e != nil {
err = e
}
}()
_, err = io.Copy(out, in)
if err != nil {
return
}
err = out.Sync()
if err != nil {
return
}
si, err := os.Stat(src)
if err != nil {
return
}
err = os.Chmod(dst, si.Mode())
if err != nil {
return
}
return
}
// CopyDir recursively copies a directory tree, attempting to preserve permissions.
// Source directory must exist, destination directory must *not* exist.
// Symlinks are ignored and skipped.
func CopyDir(src string, dst string) (err error) {
src = filepath.Clean(src)
dst = filepath.Clean(dst)
si, err := os.Stat(src)
if err != nil {
return err
}
if !si.IsDir() {
return fmt.Errorf("source is not a directory")
}
_, err = os.Stat(dst)
if err != nil && !os.IsNotExist(err) {
return
}
if err == nil {
return fmt.Errorf("destination already exists")
}
err = os.MkdirAll(dst, si.Mode())
if err != nil {
return
}
entries, err := ioutil.ReadDir(src)
if err != nil {
return
}
for _, entry := range entries {
srcPath := filepath.Join(src, entry.Name())
dstPath := filepath.Join(dst, entry.Name())
// If the entry is a symlink, we copy the contents
for entry.Mode()&os.ModeSymlink != 0 {
target, err := os.Readlink(srcPath)
if err != nil {
return err
}
entry, err = os.Stat(target)
if err != nil {
return err
}
}
if entry.IsDir() {
err = CopyDir(srcPath, dstPath)
if err != nil {
return
}
} else {
err = CopyFile(srcPath, dstPath)
if err != nil {
return
}
}
}
return
}