diff --git a/configs/configload/copy_dir.go b/configs/configload/copy_dir.go index ad34a3204..ebbeb3b62 100644 --- a/configs/configload/copy_dir.go +++ b/configs/configload/copy_dir.go @@ -59,6 +59,17 @@ func copyDir(dst, src string) error { return nil } + // If the current path is a symlink, recreate the symlink relative to + // the dst directory + if info.Mode()&os.ModeSymlink == os.ModeSymlink { + target, err := os.Readlink(path) + if err != nil { + return err + } + + return os.Symlink(target, dstPath) + } + // If we have a file, copy the contents. srcF, err := os.Open(path) if err != nil { diff --git a/configs/configload/copy_dir_test.go b/configs/configload/copy_dir_test.go new file mode 100644 index 000000000..2f9f86b8f --- /dev/null +++ b/configs/configload/copy_dir_test.go @@ -0,0 +1,107 @@ +package configload + +import ( + "io/ioutil" + "os" + "path/filepath" + "testing" +) + +// TestCopyDir_symlinks sets up a directory with two submodules, +// one being a symlink to the other +// +// The resultant file structure is as follows: +// ├── modules +// │   ├── symlink-module -> test-module +// │   └── test-module +// │   └── main.tf +// └── target +// ├── symlink-module -> test-module +// └── test-module +// └── main.tf + +func TestCopyDir_symlinks(t *testing.T) { + tmpdir, err := ioutil.TempDir("", "copy-dir-test") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(tmpdir) + + moduleDir := filepath.Join(tmpdir, "modules") + err = os.Mkdir(moduleDir, os.ModePerm) + if err != nil { + t.Fatal(err) + } + + subModuleDir := filepath.Join(moduleDir, "test-module") + err = os.Mkdir(subModuleDir, os.ModePerm) + if err != nil { + t.Fatal(err) + } + + err = ioutil.WriteFile(filepath.Join(subModuleDir, "main.tf"), []byte("hello"), 0644) + if err != nil { + t.Fatal(err) + } + + err = os.Symlink("test-module", filepath.Join(moduleDir, "symlink-module")) + if err != nil { + t.Fatal(err) + } + + targetDir := filepath.Join(tmpdir, "target") + os.Mkdir(targetDir, os.ModePerm) + + err = copyDir(targetDir, moduleDir) + if err != nil { + t.Fatal(err) + } + + if _, err = os.Lstat(filepath.Join(targetDir, "test-module", "main.tf")); os.IsNotExist(err) { + t.Fatal("target test-module/main.tf was not created") + } + + if _, err = os.Lstat(filepath.Join(targetDir, "symlink-module", "main.tf")); os.IsNotExist(err) { + t.Fatal("target symlink-module/main.tf was not created") + } +} + +func TestCopyDir_symlink_file(t *testing.T) { + tmpdir, err := ioutil.TempDir("", "copy-file-test") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(tmpdir) + + moduleDir := filepath.Join(tmpdir, "modules") + err = os.Mkdir(moduleDir, os.ModePerm) + if err != nil { + t.Fatal(err) + } + + err = ioutil.WriteFile(filepath.Join(moduleDir, "main.tf"), []byte("hello"), 0644) + if err != nil { + t.Fatal(err) + } + + err = os.Symlink("main.tf", filepath.Join(moduleDir, "symlink.tf")) + if err != nil { + t.Fatal(err) + } + + targetDir := filepath.Join(tmpdir, "target") + os.Mkdir(targetDir, os.ModePerm) + + err = copyDir(targetDir, moduleDir) + if err != nil { + t.Fatal(err) + } + + if _, err = os.Lstat(filepath.Join(targetDir, "main.tf")); os.IsNotExist(err) { + t.Fatal("target/main.tf was not created") + } + + if _, err = os.Lstat(filepath.Join(targetDir, "symlink.tf")); os.IsNotExist(err) { + t.Fatal("target/symlink.tf was not created") + } +} diff --git a/internal/initwd/copy_dir.go b/internal/initwd/copy_dir.go index 8e414cdaa..7096ff74f 100644 --- a/internal/initwd/copy_dir.go +++ b/internal/initwd/copy_dir.go @@ -59,6 +59,17 @@ func copyDir(dst, src string) error { return nil } + // If the current path is a symlink, recreate the symlink relative to + // the dst directory + if info.Mode()&os.ModeSymlink == os.ModeSymlink { + target, err := os.Readlink(path) + if err != nil { + return err + } + + return os.Symlink(target, dstPath) + } + // If we have a file, copy the contents. srcF, err := os.Open(path) if err != nil { diff --git a/internal/initwd/copy_dir_test.go b/internal/initwd/copy_dir_test.go new file mode 100644 index 000000000..06e02f824 --- /dev/null +++ b/internal/initwd/copy_dir_test.go @@ -0,0 +1,107 @@ +package initwd + +import ( + "io/ioutil" + "os" + "path/filepath" + "testing" +) + +// TestCopyDir_symlinks sets up a directory with two submodules, +// one being a symlink to the other +// +// The resultant file structure is as follows: +// ├── modules +// │   ├── symlink-module -> test-module +// │   └── test-module +// │   └── main.tf +// └── target +// ├── symlink-module -> test-module +// └── test-module +// └── main.tf + +func TestCopyDir_symlinks(t *testing.T) { + tmpdir, err := ioutil.TempDir("", "copy-dir-test") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(tmpdir) + + moduleDir := filepath.Join(tmpdir, "modules") + err = os.Mkdir(moduleDir, os.ModePerm) + if err != nil { + t.Fatal(err) + } + + subModuleDir := filepath.Join(moduleDir, "test-module") + err = os.Mkdir(subModuleDir, os.ModePerm) + if err != nil { + t.Fatal(err) + } + + err = ioutil.WriteFile(filepath.Join(subModuleDir, "main.tf"), []byte("hello"), 0644) + if err != nil { + t.Fatal(err) + } + + err = os.Symlink("test-module", filepath.Join(moduleDir, "symlink-module")) + if err != nil { + t.Fatal(err) + } + + targetDir := filepath.Join(tmpdir, "target") + os.Mkdir(targetDir, os.ModePerm) + + err = copyDir(targetDir, moduleDir) + if err != nil { + t.Fatal(err) + } + + if _, err = os.Lstat(filepath.Join(targetDir, "test-module", "main.tf")); os.IsNotExist(err) { + t.Fatal("target test-module/main.tf was not created") + } + + if _, err = os.Lstat(filepath.Join(targetDir, "symlink-module", "main.tf")); os.IsNotExist(err) { + t.Fatal("target symlink-module/main.tf was not created") + } +} + +func TestCopyDir_symlink_file(t *testing.T) { + tmpdir, err := ioutil.TempDir("", "copy-file-test") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(tmpdir) + + moduleDir := filepath.Join(tmpdir, "modules") + err = os.Mkdir(moduleDir, os.ModePerm) + if err != nil { + t.Fatal(err) + } + + err = ioutil.WriteFile(filepath.Join(moduleDir, "main.tf"), []byte("hello"), 0644) + if err != nil { + t.Fatal(err) + } + + err = os.Symlink("main.tf", filepath.Join(moduleDir, "symlink.tf")) + if err != nil { + t.Fatal(err) + } + + targetDir := filepath.Join(tmpdir, "target") + os.Mkdir(targetDir, os.ModePerm) + + err = copyDir(targetDir, moduleDir) + if err != nil { + t.Fatal(err) + } + + if _, err = os.Lstat(filepath.Join(targetDir, "main.tf")); os.IsNotExist(err) { + t.Fatal("target/main.tf was not created") + } + + if _, err = os.Lstat(filepath.Join(targetDir, "symlink.tf")); os.IsNotExist(err) { + t.Fatal("target/symlink.tf was not created") + } +}