forked from gruntwork-io/terratest
-
Notifications
You must be signed in to change notification settings - Fork 0
/
files.go
185 lines (154 loc) · 5.95 KB
/
files.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
// Package files allows to interact with files on a file system.
package files
import (
"io/ioutil"
"os"
"path/filepath"
"strings"
)
// FileExists returns true if the given file exists.
func FileExists(path string) bool {
_, err := os.Stat(path)
return err == nil
}
// CopyTerraformFolderToTemp creates a copy of the given folder and all its contents in a temp folder with a unique name and the given prefix.
// This is useful when running multiple tests in parallel against the same set of Terraform files to ensure the
// tests don't overwrite each other's .terraform working directory and terraform.tfstate files. This method returns
// the path to the temp folder with the copied contents. Hidden files and folders, Terraform state files, and
// terraform.tfvars files are not copied to this temp folder, as you typically don't want them interfering with your
// tests.
func CopyTerraformFolderToTemp(folderPath string, tempFolderPrefix string) (string, error) {
filter := func(path string) bool {
return !PathContainsHiddenFileOrFolder(path) && !PathContainsTerraformStateOrVars(path)
}
destFolder, err := CopyFolderToTemp(folderPath, tempFolderPrefix, filter)
if err != nil {
return "", err
}
return destFolder, nil
}
// CopyTerragruntFolderToTemp creates a copy of the given folder and all its contents in a temp folder with a unique name and the given prefix.
// Since terragrunt uses tfvars files to specify modules, they are copied to the temporary directory as well.
// Terraform state files are excluded as well as .terragrunt-cache to avoid overwriting contents.
func CopyTerragruntFolderToTemp(folderPath string, tempFolderPrefix string) (string, error) {
filter := func(path string) bool {
return !PathContainsHiddenFileOrFolder(path) && !PathContainsTerraformState(path)
}
destFolder, err := CopyFolderToTemp(folderPath, tempFolderPrefix, filter)
if err != nil {
return "", err
}
return destFolder, nil
}
// CopyFolderToTemp creates a copy of the given folder and all its filtered contents in a temp folder
// with a unique name and the given prefix.
func CopyFolderToTemp(folderPath string, tempFolderPrefix string, filter func(path string) bool) (string, error) {
tmpDir, err := ioutil.TempDir("", tempFolderPrefix)
if err != nil {
return "", err
}
// Inside of the temp folder, we create a subfolder that preserves the name of the folder we're copying from.
absFolderPath, err := filepath.Abs(folderPath)
if err != nil {
return "", err
}
folderName := filepath.Base(absFolderPath)
destFolder := filepath.Join(tmpDir, folderName)
if err := os.MkdirAll(destFolder, 0777); err != nil {
return "", err
}
if err := CopyFolderContentsWithFilter(folderPath, destFolder, filter); err != nil {
return "", err
}
return destFolder, nil
}
// CopyFolderContents copies all the files and folders within the given source folder to the destination folder.
func CopyFolderContents(source string, destination string) error {
return CopyFolderContentsWithFilter(source, destination, func(path string) bool {
return true
})
}
// CopyFolderContentsWithFilter copies the files and folders within the given source folder that pass the given filter (return true) to the
// destination folder.
func CopyFolderContentsWithFilter(source string, destination string, filter func(path string) bool) error {
files, err := ioutil.ReadDir(source)
if err != nil {
return err
}
for _, file := range files {
src := filepath.Join(source, file.Name())
dest := filepath.Join(destination, file.Name())
if !filter(src) {
continue
} else if file.IsDir() {
if err := os.MkdirAll(dest, file.Mode()); err != nil {
return err
}
if err := CopyFolderContentsWithFilter(src, dest, filter); err != nil {
return err
}
} else if isSymLink(file) {
if err := copySymLink(src, dest); err != nil {
return err
}
} else {
if err := CopyFile(src, dest); err != nil {
return err
}
}
}
return nil
}
// PathContainsTerraformStateOrVars returns true if the path corresponds to a Terraform state file or .tfvars file.
func PathContainsTerraformStateOrVars(path string) bool {
filename := filepath.Base(path)
return filename == "terraform.tfstate" || filename == "terraform.tfstate.backup" || filename == "terraform.tfvars"
}
// PathContainsTerraformState returns true if the path corresponds to a Terraform state file.
func PathContainsTerraformState(path string) bool {
filename := filepath.Base(path)
return filename == "terraform.tfstate" || filename == "terraform.tfstate.backup"
}
// PathContainsHiddenFileOrFolder returns true if the given path contains a hidden file or folder.
func PathContainsHiddenFileOrFolder(path string) bool {
pathParts := strings.Split(path, string(filepath.Separator))
for _, pathPart := range pathParts {
if strings.HasPrefix(pathPart, ".") && pathPart != "." && pathPart != ".." {
return true
}
}
return false
}
// CopyFile copies a file from source to destination.
func CopyFile(source string, destination string) error {
contents, err := ioutil.ReadFile(source)
if err != nil {
return err
}
return WriteFileWithSamePermissions(source, destination, contents)
}
// WriteFileWithSamePermissions writes a file to the given destination with the given contents using the same permissions as the file at source.
func WriteFileWithSamePermissions(source string, destination string, contents []byte) error {
fileInfo, err := os.Stat(source)
if err != nil {
return err
}
return ioutil.WriteFile(destination, contents, fileInfo.Mode())
}
// isSymLink returns true if the given file is a symbolic link
// Per https://stackoverflow.com/a/18062079/2308858
func isSymLink(file os.FileInfo) bool {
return file.Mode()&os.ModeSymlink != 0
}
// copySymLink copies the source symbolic link to the given destination.
func copySymLink(source string, destination string) error {
symlinkPath, err := os.Readlink(source)
if err != nil {
return err
}
err = os.Symlink(symlinkPath, destination)
if err != nil {
return err
}
return nil
}