2023-01-12 07:21:53 +03:00
|
|
|
package terraform_module_test_helper
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2023-01-29 12:46:16 +03:00
|
|
|
"io"
|
2023-01-12 07:21:53 +03:00
|
|
|
"os"
|
|
|
|
"path/filepath"
|
2023-02-17 05:26:04 +03:00
|
|
|
"sync"
|
2023-01-12 07:21:53 +03:00
|
|
|
"testing"
|
|
|
|
"time"
|
|
|
|
|
2023-01-19 10:22:18 +03:00
|
|
|
"github.com/gruntwork-io/terratest/modules/files"
|
|
|
|
"github.com/gruntwork-io/terratest/modules/logger"
|
2023-01-12 07:21:53 +03:00
|
|
|
"github.com/gruntwork-io/terratest/modules/terraform"
|
|
|
|
test_structure "github.com/gruntwork-io/terratest/modules/test-structure"
|
|
|
|
)
|
|
|
|
|
2023-01-31 11:59:10 +03:00
|
|
|
var initE = terraform.InitE
|
|
|
|
var runTerraformCommandE = terraform.RunTerraformCommandE
|
2023-02-17 05:26:04 +03:00
|
|
|
var recordFileLocks = &KeyedMutex{}
|
|
|
|
|
|
|
|
type KeyedMutex struct {
|
|
|
|
mutexes sync.Map // Zero value is empty and ready for use
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *KeyedMutex) Lock(key string) func() {
|
|
|
|
value, _ := m.mutexes.LoadOrStore(key, &sync.Mutex{})
|
|
|
|
mtx := value.(*sync.Mutex)
|
|
|
|
mtx.Lock()
|
|
|
|
return func() { mtx.Unlock() }
|
|
|
|
}
|
2023-01-31 11:59:10 +03:00
|
|
|
|
2023-01-12 07:21:53 +03:00
|
|
|
type TestVersionSnapshot struct {
|
2023-01-31 11:59:10 +03:00
|
|
|
ModuleRootFolder string
|
|
|
|
SubModuleRelativeFolder string
|
|
|
|
Time time.Time
|
|
|
|
Success bool
|
2023-01-31 13:05:52 +03:00
|
|
|
Versions string
|
|
|
|
ErrorMsg string
|
2023-01-31 11:59:10 +03:00
|
|
|
}
|
|
|
|
|
2023-02-02 09:13:59 +03:00
|
|
|
func SuccessTestVersionSnapshot(rootFolder, exampleRelativePath string) *TestVersionSnapshot {
|
|
|
|
return &TestVersionSnapshot{
|
2023-01-31 11:59:10 +03:00
|
|
|
ModuleRootFolder: rootFolder,
|
|
|
|
SubModuleRelativeFolder: exampleRelativePath,
|
|
|
|
Time: time.Now(),
|
|
|
|
Success: true,
|
|
|
|
}
|
2023-01-12 07:21:53 +03:00
|
|
|
}
|
|
|
|
|
2023-02-02 09:13:59 +03:00
|
|
|
func FailedTestVersionSnapshot(rootFolder, exampleRelativePath, errMsg string) *TestVersionSnapshot {
|
|
|
|
return &TestVersionSnapshot{
|
2023-01-31 11:59:10 +03:00
|
|
|
ModuleRootFolder: rootFolder,
|
|
|
|
SubModuleRelativeFolder: exampleRelativePath,
|
|
|
|
Time: time.Now(),
|
|
|
|
Success: false,
|
2023-01-31 13:05:52 +03:00
|
|
|
ErrorMsg: errMsg,
|
2023-01-31 11:59:10 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *TestVersionSnapshot) ToString() string {
|
2023-01-12 07:21:53 +03:00
|
|
|
return fmt.Sprintf(`## %s
|
|
|
|
|
|
|
|
Success: %t
|
2023-01-13 13:46:32 +03:00
|
|
|
|
2023-01-31 13:05:52 +03:00
|
|
|
### Versions
|
|
|
|
|
|
|
|
%s
|
|
|
|
|
|
|
|
### Error
|
|
|
|
|
2023-01-12 07:21:53 +03:00
|
|
|
%s
|
|
|
|
|
|
|
|
---
|
|
|
|
|
2023-01-31 13:05:52 +03:00
|
|
|
`, s.Time.Format(time.RFC822), s.Success, s.Versions, s.ErrorMsg)
|
2023-01-12 07:21:53 +03:00
|
|
|
}
|
|
|
|
|
2023-02-01 18:41:43 +03:00
|
|
|
func (s *TestVersionSnapshot) Save(t *testing.T) error {
|
2023-02-17 09:33:14 +03:00
|
|
|
path, err := filepath.Abs(filepath.Clean(filepath.Join(s.ModuleRootFolder, s.SubModuleRelativeFolder, "TestRecord.md.tmp")))
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
unlock := recordFileLocks.Lock(path)
|
|
|
|
defer unlock()
|
2023-02-01 18:41:43 +03:00
|
|
|
s.load(t)
|
2023-02-17 09:33:14 +03:00
|
|
|
err = s.saveToLocal(path)
|
2023-01-29 12:46:16 +03:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2023-02-17 09:33:14 +03:00
|
|
|
return s.copyForUploadArtifact(path)
|
2023-02-01 18:41:43 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
func (s *TestVersionSnapshot) copyForUploadArtifact(localPath string) error {
|
2023-01-31 11:59:10 +03:00
|
|
|
_, dir := filepath.Split(filepath.Join(s.ModuleRootFolder, s.SubModuleRelativeFolder))
|
2023-01-31 13:05:52 +03:00
|
|
|
pathForUpload := filepath.Join(s.ModuleRootFolder, "TestRecord", dir, "TestRecord.md.tmp")
|
2023-02-01 18:41:43 +03:00
|
|
|
return copyFile(localPath, pathForUpload)
|
2023-01-29 12:46:16 +03:00
|
|
|
}
|
|
|
|
|
2023-02-17 09:33:14 +03:00
|
|
|
func (s *TestVersionSnapshot) saveToLocal(path string) error {
|
|
|
|
return writeStringToFile(path, s.ToString())
|
2023-01-29 12:46:16 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
func copyFile(src, dst string) error {
|
2023-02-02 16:48:15 +03:00
|
|
|
cleanedSrc := filepath.Clean(src)
|
|
|
|
cleanedDst := filepath.Clean(dst)
|
|
|
|
if _, err := os.Stat(cleanedSrc); os.IsNotExist(err) {
|
2023-01-29 12:46:16 +03:00
|
|
|
return fmt.Errorf("source file does not exist: %s", src)
|
|
|
|
}
|
|
|
|
|
2023-02-02 16:48:15 +03:00
|
|
|
dstDir := filepath.Dir(cleanedDst)
|
2024-09-14 05:40:44 +03:00
|
|
|
if _, err := os.Stat(dstDir); os.IsNotExist(err) && os.MkdirAll(dstDir, 0750) != nil {
|
2023-01-29 12:46:16 +03:00
|
|
|
return fmt.Errorf("failed to create destination folder: %s", dstDir)
|
|
|
|
}
|
2023-02-02 16:48:15 +03:00
|
|
|
if _, err := os.Stat(cleanedDst); !os.IsNotExist(err) && os.Remove(cleanedDst) != nil {
|
2023-01-29 12:46:16 +03:00
|
|
|
return fmt.Errorf("failed to delete destination file: %s", dst)
|
|
|
|
}
|
2023-02-02 16:48:15 +03:00
|
|
|
srcFile, err := os.Open(cleanedSrc)
|
2023-01-29 12:46:16 +03:00
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("failed to open source file: %s", src)
|
|
|
|
}
|
|
|
|
defer func() { _ = srcFile.Close() }()
|
|
|
|
|
2023-02-02 16:48:15 +03:00
|
|
|
dstFile, err := os.Create(cleanedDst)
|
2023-01-29 12:46:16 +03:00
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("failed to create destination file: %s", dst)
|
|
|
|
}
|
|
|
|
defer func() { _ = dstFile.Close() }()
|
|
|
|
|
|
|
|
if _, err = io.Copy(dstFile, srcFile); err != nil {
|
|
|
|
return fmt.Errorf("failed to copy file: %s", err)
|
|
|
|
}
|
|
|
|
return nil
|
2023-01-12 07:21:53 +03:00
|
|
|
}
|
2023-01-12 09:30:05 +03:00
|
|
|
|
2023-01-31 13:05:52 +03:00
|
|
|
func writeStringToFile(filePath, str string) error {
|
2023-02-02 16:48:15 +03:00
|
|
|
cleanedFilePath := filepath.Clean(filePath)
|
|
|
|
if files.FileExists(cleanedFilePath) {
|
|
|
|
if err := os.Remove(cleanedFilePath); err != nil {
|
2023-01-31 13:05:52 +03:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
2023-02-02 16:48:15 +03:00
|
|
|
f, err := os.Create(cleanedFilePath)
|
2023-01-31 13:05:52 +03:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer func() { _ = f.Close() }()
|
|
|
|
_, err = f.WriteString(str)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2023-02-01 18:41:43 +03:00
|
|
|
func (s *TestVersionSnapshot) load(t *testing.T) {
|
2023-01-31 13:05:52 +03:00
|
|
|
tmpDir := test_structure.CopyTerraformFolderToTemp(t, s.ModuleRootFolder, s.SubModuleRelativeFolder)
|
2023-01-31 11:59:10 +03:00
|
|
|
defer func() {
|
2023-01-31 13:05:52 +03:00
|
|
|
_ = os.RemoveAll(tmpDir)
|
2023-01-31 11:59:10 +03:00
|
|
|
}()
|
2023-01-31 13:05:52 +03:00
|
|
|
opts := terraform.Options{
|
|
|
|
TerraformDir: tmpDir,
|
2023-01-31 11:59:10 +03:00
|
|
|
NoColor: true,
|
|
|
|
Logger: logger.Discard,
|
|
|
|
}
|
2024-05-27 05:29:01 +03:00
|
|
|
if output, err := func() (string, error) {
|
|
|
|
initLock.Lock()
|
|
|
|
defer initLock.Unlock()
|
|
|
|
return initE(t, &opts)
|
|
|
|
}(); err != nil {
|
2023-01-31 11:59:10 +03:00
|
|
|
s.Success = false
|
2023-01-31 13:05:52 +03:00
|
|
|
s.ErrorMsg = output
|
2023-01-31 11:59:10 +03:00
|
|
|
return
|
|
|
|
}
|
2023-01-31 13:05:52 +03:00
|
|
|
output, err := runTerraformCommandE(t, &opts, "version")
|
2023-01-31 11:59:10 +03:00
|
|
|
if err != nil {
|
|
|
|
s.Success = false
|
2023-01-31 13:05:52 +03:00
|
|
|
s.ErrorMsg = output
|
2023-01-31 11:59:10 +03:00
|
|
|
return
|
|
|
|
}
|
2023-01-31 13:05:52 +03:00
|
|
|
s.Versions = output
|
2023-01-31 11:59:10 +03:00
|
|
|
}
|