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