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:
parent
654686496c
commit
098225dc0d
|
@ -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
|
||||
|
|
|
@ -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"))
|
||||
|
|
|
@ -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
|
||||
}
|
Loading…
Reference in New Issue