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  }