2022-03-01 08:17:23 +03:00
package terraform_module_test_helper
import (
2023-02-02 06:23:36 +03:00
"fmt"
2023-02-21 08:40:38 +03:00
"io"
2023-03-14 10:55:08 +03:00
"os"
2023-02-01 14:35:12 +03:00
"path/filepath"
2024-05-27 05:29:01 +03:00
"sync"
2023-02-01 14:35:12 +03:00
"testing"
"time"
2023-01-13 18:46:53 +03:00
"github.com/gruntwork-io/terratest/modules/files"
2022-05-17 12:09:33 +03:00
"github.com/gruntwork-io/terratest/modules/logger"
2022-03-01 08:17:23 +03:00
"github.com/gruntwork-io/terratest/modules/terraform"
test_structure "github.com/gruntwork-io/terratest/modules/test-structure"
2023-02-21 08:54:55 +03:00
terratest "github.com/gruntwork-io/terratest/modules/testing"
2023-01-13 18:46:53 +03:00
"github.com/stretchr/testify/require"
2022-03-01 08:17:23 +03:00
)
2023-03-22 08:27:41 +03:00
type TestOptions struct {
TerraformOptions terraform . Options
Assertion func ( * testing . T , TerraformOutput )
SkipIdempotentCheck bool
2023-07-28 08:07:12 +03:00
SkipDestroy bool
2023-03-22 08:27:41 +03:00
}
2023-03-02 11:44:06 +03:00
var copyLock = & KeyedMutex { }
2024-05-27 05:29:01 +03:00
var initLock = & sync . Mutex { }
2023-02-02 06:51:42 +03:00
2022-03-01 08:17:23 +03:00
type TerraformOutput = map [ string ] interface { }
2023-02-21 08:40:38 +03:00
type testExecutor interface {
TearDown ( t * testing . T , rootDir string , modulePath string )
Logger ( ) logger . TestLogger
}
var _ testExecutor = e2eTestExecutor { }
type e2eTestExecutor struct { }
func ( e2eTestExecutor ) TearDown ( t * testing . T , rootDir string , modulePath string ) {
s := SuccessTestVersionSnapshot ( rootDir , modulePath )
if t . Failed ( ) {
s = FailedTestVersionSnapshot ( rootDir , modulePath , "" )
}
require . NoError ( t , s . Save ( t ) )
}
func ( e2eTestExecutor ) Logger ( ) logger . TestLogger {
l := NewMemoryLogger ( )
return l
}
2022-04-07 10:59:38 +03:00
func RunE2ETest ( t * testing . T , moduleRootPath , exampleRelativePath string , option terraform . Options , assertion func ( * testing . T , TerraformOutput ) ) {
2024-05-09 04:46:49 +03:00
initAndApplyAndIdempotentTest ( t , moduleRootPath , exampleRelativePath , option , false , false , assertion , e2eTestExecutor { } )
2023-02-21 08:40:38 +03:00
}
2023-03-22 08:27:41 +03:00
func RunE2ETestWithOption ( t * testing . T , moduleRootPath , exampleRelativePath string , testOption TestOptions ) {
2023-07-28 08:07:12 +03:00
initAndApplyAndIdempotentTest ( t , moduleRootPath , exampleRelativePath , testOption . TerraformOptions , false , testOption . SkipIdempotentCheck , testOption . Assertion , e2eTestExecutor { } )
2023-03-22 08:27:41 +03:00
}
2023-07-28 08:07:12 +03:00
func initAndApplyAndIdempotentTest ( t * testing . T , moduleRootPath string , exampleRelativePath string , option terraform . Options , skipDestroy bool , skipCheckIdempotent bool , assertion func ( * testing . T , TerraformOutput ) , executor testExecutor ) {
2023-03-08 04:41:23 +03:00
tryParallel ( t )
2023-02-21 08:40:38 +03:00
defer executor . TearDown ( t , moduleRootPath , exampleRelativePath )
2023-02-02 06:23:36 +03:00
testDir := filepath . Join ( moduleRootPath , exampleRelativePath )
logger . Log ( t , fmt . Sprintf ( "===> Starting test for %s, since we're running tests in parallel, the test log will be buffered and output to stdout after the test was finished." , testDir ) )
2023-02-01 09:10:03 +03:00
2023-03-02 11:44:06 +03:00
tmpDir := copyTerraformFolderToTemp ( t , moduleRootPath , exampleRelativePath )
2023-03-14 10:55:08 +03:00
defer func ( ) {
2023-03-16 11:34:47 +03:00
_ = os . RemoveAll ( filepath . Clean ( tmpDir ) )
2023-03-14 10:55:08 +03:00
} ( )
2022-03-01 08:17:23 +03:00
option . TerraformDir = tmpDir
2023-02-01 09:10:03 +03:00
2023-02-21 08:40:38 +03:00
l := executor . Logger ( )
c , ok := l . ( io . Closer )
if ok {
defer func ( ) {
_ = c . Close ( )
} ( )
}
2023-02-01 09:10:03 +03:00
option . Logger = logger . New ( l )
2023-02-06 14:20:49 +03:00
option = setupRetryLogic ( option )
2023-02-01 09:10:03 +03:00
2023-07-28 08:07:12 +03:00
if ! skipDestroy {
defer destroy ( t , option )
}
2023-02-02 06:51:42 +03:00
initAndApply ( t , & option )
2023-03-22 08:27:41 +03:00
var err error
if ! skipCheckIdempotent {
err = initAndPlanAndIdempotentAtEasyMode ( t , option )
}
2024-08-20 04:09:01 +03:00
require . NoError ( t , err )
2022-04-07 10:59:38 +03:00
if assertion != nil {
2022-09-06 08:44:32 +03:00
assertion ( t , terraform . OutputAll ( t , removeLogger ( option ) ) )
2022-04-07 10:59:38 +03:00
}
2022-03-01 08:17:23 +03:00
}
2022-07-06 09:16:29 +03:00
2023-03-02 11:44:06 +03:00
func copyTerraformFolderToTemp ( t * testing . T , moduleRootPath string , exampleRelativePath string ) string {
unlock := copyLock . Lock ( exampleRelativePath )
defer unlock ( )
tmpDir := test_structure . CopyTerraformFolderToTemp ( t , moduleRootPath , exampleRelativePath )
return tmpDir
}
2023-02-02 06:51:42 +03:00
func initAndApply ( t terratest . TestingT , options * terraform . Options ) string {
2023-02-02 08:45:09 +03:00
tfInit ( t , options )
2023-02-02 09:29:01 +03:00
return terraform . Apply ( t , options )
2023-02-02 08:45:09 +03:00
}
func tfInit ( t terratest . TestingT , options * terraform . Options ) {
2024-05-27 05:29:01 +03:00
initLock . Lock ( )
defer initLock . Unlock ( )
2023-02-02 08:45:09 +03:00
terraform . Init ( t , options )
2023-02-02 06:51:42 +03:00
}
2022-11-01 11:00:27 +03:00
func destroy ( t * testing . T , option terraform . Options ) {
2023-01-13 18:46:53 +03:00
path := option . TerraformDir
if ! files . IsExistingDir ( path ) || ! files . FileExists ( filepath . Join ( path , "terraform.tfstate" ) ) {
return
}
2023-01-13 05:24:25 +03:00
option . MaxRetries = 5
2022-11-01 11:00:27 +03:00
option . TimeBetweenRetries = time . Minute
option . RetryableTerraformErrors = map [ string ] string {
".*" : "Retry destroy on any error" ,
}
2023-01-13 14:45:47 +03:00
_ , err := terraform . RunTerraformCommandE ( t , & option , terraform . FormatArgs ( & option , "destroy" , "-auto-approve" , "-input=false" , "-refresh=false" ) ... )
2023-01-13 05:24:25 +03:00
if err != nil {
2023-01-13 14:45:47 +03:00
_ , err = terraform . DestroyE ( t , & option )
2023-01-13 05:24:25 +03:00
}
2022-12-30 09:13:30 +03:00
require . NoError ( t , err )
2022-11-01 11:00:27 +03:00
}
2022-09-06 08:44:32 +03:00
func removeLogger ( option terraform . Options ) * terraform . Options {
// default logger might leak sensitive data
option . Logger = logger . Discard
return & option
}
2022-07-06 09:16:29 +03:00
func retryableOptions ( t * testing . T , options terraform . Options ) terraform . Options {
result := terraform . WithDefaultRetryableErrors ( t , & options )
2023-01-13 14:30:20 +03:00
result . RetryableTerraformErrors [ ".*Please try again.*" ] = "Service side suggest retry."
2022-07-06 09:16:29 +03:00
return * result
2022-11-01 11:00:27 +03:00
}