Add acc test

main
Elie 2021-02-23 13:44:48 +01:00
parent 69345a5c34
commit 5bf6a4ad7a
No known key found for this signature in database
GPG Key ID: 399AF69092C727B6
27 changed files with 345 additions and 81 deletions

View File

@ -36,7 +36,7 @@ coverage: test
.PHONY: acc
acc:
DRIFTCTL_ACC=true $(GOTEST) --format testname --junitfile unit-tests-acc.xml -- -coverprofile=cover-acc.out -test.timeout 1h -coverpkg=./pkg/... -run=$(ACC_PATTERN) ./pkg/resource/...
DRIFTCTL_ACC=true $(GOTEST) --format testname --junitfile unit-tests-acc.xml -- -coverprofile=cover-acc.out -test.timeout 1h -coverpkg=./pkg/... -run=$(ACC_PATTERN) ./pkg/...
.PHONY: mocks
mocks: deps

View File

@ -0,0 +1,84 @@
package state_test
import (
"fmt"
"testing"
"github.com/aws/aws-sdk-go/service/s3"
"github.com/cloudskiff/driftctl/test/acceptance"
"github.com/cloudskiff/driftctl/test/acceptance/awsutils"
)
func TestAcc_StateReader_WithMultiplesStatesInS3(t *testing.T) {
stateBucketName := "driftctl-acc-test-only"
acceptance.Run(t, acceptance.AccTestCase{
OnStart: func() {
err := createBucket(stateBucketName)
if err != nil {
t.Fatal(err)
}
},
Paths: []string{"./testdata/acc/multiples_states/s3", "./testdata/acc/multiples_states/route53"},
Args: []string{
"scan",
"--from", fmt.Sprintf("tfstate+s3://%s/states", stateBucketName),
"--filter", "Type=='aws_s3_bucket' || Type=='aws_route53_zone'",
},
Checks: []acceptance.AccCheck{
{
Check: func(result *acceptance.ScanResult, stdout string, err error) {
if err != nil {
t.Fatal(err)
}
result.AssertUnmanagedCount(1)
result.AssertDeletedCount(0)
result.AssertResourceUnmanaged("driftctl-acc-test-only", "aws_s3_bucket")
result.AssertManagedCount(2)
result.Equal("aws_route53_zone", result.Managed()[0].TerraformType())
result.Equal("aws_s3_bucket", result.Managed()[1].TerraformType())
},
},
},
OnEnd: func() {
err := removeStateBucket(stateBucketName)
if err != nil {
t.Fatal(err)
}
},
})
}
func createBucket(bucket string) error {
client := s3.New(awsutils.Session())
_, err := client.CreateBucket(&s3.CreateBucketInput{
Bucket: &bucket,
})
if err != nil {
return err
}
return nil
}
func removeStateBucket(bucket string) error {
client := s3.New(awsutils.Session())
objects, err := client.ListObjectsV2(&s3.ListObjectsV2Input{Bucket: &bucket})
if err != nil {
return err
}
for _, object := range objects.Contents {
_, err := client.DeleteObject(&s3.DeleteObjectInput{
Bucket: &bucket,
Key: object.Key,
})
if err != nil {
return err
}
}
_, err = client.DeleteBucket(&s3.DeleteBucketInput{
Bucket: &bucket,
})
if err != nil {
return err
}
return nil
}

View File

@ -0,0 +1,38 @@
# This file is maintained automatically by "terraform init".
# Manual edits may be lost in future updates.
provider "registry.terraform.io/hashicorp/aws" {
version = "3.19.0"
constraints = "~> 3.19.0"
hashes = [
"h1:+7Vi7p13+cnrxjXbfJiTimGSFR97xCaQwkkvWcreLns=",
"zh:185a5259153eb9ee4699d4be43b3d509386b473683392034319beee97d470c3b",
"zh:2d9a0a01f93e8d16539d835c02b8b6e1927b7685f4076e96cb07f7dd6944bc6c",
"zh:703f6da36b1b5f3497baa38fccaa7765fb8a2b6440344e4c97172516b49437dd",
"zh:770855565462abadbbddd98cb357d2f1a8f30f68a358cb37cbd5c072cb15b377",
"zh:8008db43149fe4345301f81e15e6d9ddb47aa5e7a31648f9b290af96ad86e92a",
"zh:8cdd27d375da6dcb7687f1fed126b7c04efce1671066802ee876dbbc9c66ec79",
"zh:be22ae185005690d1a017c1b909e0d80ab567e239b4f06ecacdba85080667c1c",
"zh:d2d02e72dbd80f607636cd6237a6c862897caabc635c7b50c0cb243d11246723",
"zh:d8f125b66a1eda2555c0f9bbdf12036a5f8d073499a22ca9e4812b68067fea31",
"zh:f5a98024c64d5d2973ff15b093725a074c0cb4afde07ef32c542e69f17ac90bc",
]
}
provider "registry.terraform.io/hashicorp/random" {
version = "3.1.0"
hashes = [
"h1:BZMEPucF+pbu9gsPk0G0BHx7YP04+tKdq2MrRDF1EDM=",
"zh:2bbb3339f0643b5daa07480ef4397bd23a79963cc364cdfbb4e86354cb7725bc",
"zh:3cd456047805bf639fbf2c761b1848880ea703a054f76db51852008b11008626",
"zh:4f251b0eda5bb5e3dc26ea4400dba200018213654b69b4a5f96abee815b4f5ff",
"zh:7011332745ea061e517fe1319bd6c75054a314155cb2c1199a5b01fe1889a7e2",
"zh:738ed82858317ccc246691c8b85995bc125ac3b4143043219bd0437adc56c992",
"zh:7dbe52fac7bb21227acd7529b487511c91f4107db9cc4414f50d04ffc3cab427",
"zh:a3a9251fb15f93e4cfc1789800fc2d7414bbc18944ad4c5c98f466e6477c42bc",
"zh:a543ec1a3a8c20635cf374110bd2f87c07374cf2c50617eee2c669b3ceeeaa9f",
"zh:d9ab41d556a48bd7059f0810cf020500635bfc696c9fc3adab5ea8915c1d886b",
"zh:d9e13427a7d011dbd654e591b0337e6074eef8c3b9bb11b2e39eaaf257044fd7",
"zh:f7605bd1437752114baf601bdf6931debe6dc6bfe3006eb7e9bb9080931dca8a",
]
}

View File

@ -0,0 +1,27 @@
provider "aws" {
region = "us-east-1"
}
terraform {
required_providers {
aws = {
version = "~> 3.19.0"
}
}
backend "s3" {
bucket = "driftctl-acc-test-only"
key = "states/route53/state1"
region = "us-east-1"
}
}
resource "random_string" "prefix" {
length = 6
upper = false
special = false
}
resource "aws_route53_zone" "foobar" {
name = "${random_string.prefix.result}-example.com"
}

View File

@ -0,0 +1,38 @@
# This file is maintained automatically by "terraform init".
# Manual edits may be lost in future updates.
provider "registry.terraform.io/hashicorp/aws" {
version = "3.19.0"
constraints = "~> 3.19.0"
hashes = [
"h1:+7Vi7p13+cnrxjXbfJiTimGSFR97xCaQwkkvWcreLns=",
"zh:185a5259153eb9ee4699d4be43b3d509386b473683392034319beee97d470c3b",
"zh:2d9a0a01f93e8d16539d835c02b8b6e1927b7685f4076e96cb07f7dd6944bc6c",
"zh:703f6da36b1b5f3497baa38fccaa7765fb8a2b6440344e4c97172516b49437dd",
"zh:770855565462abadbbddd98cb357d2f1a8f30f68a358cb37cbd5c072cb15b377",
"zh:8008db43149fe4345301f81e15e6d9ddb47aa5e7a31648f9b290af96ad86e92a",
"zh:8cdd27d375da6dcb7687f1fed126b7c04efce1671066802ee876dbbc9c66ec79",
"zh:be22ae185005690d1a017c1b909e0d80ab567e239b4f06ecacdba85080667c1c",
"zh:d2d02e72dbd80f607636cd6237a6c862897caabc635c7b50c0cb243d11246723",
"zh:d8f125b66a1eda2555c0f9bbdf12036a5f8d073499a22ca9e4812b68067fea31",
"zh:f5a98024c64d5d2973ff15b093725a074c0cb4afde07ef32c542e69f17ac90bc",
]
}
provider "registry.terraform.io/hashicorp/random" {
version = "3.1.0"
hashes = [
"h1:BZMEPucF+pbu9gsPk0G0BHx7YP04+tKdq2MrRDF1EDM=",
"zh:2bbb3339f0643b5daa07480ef4397bd23a79963cc364cdfbb4e86354cb7725bc",
"zh:3cd456047805bf639fbf2c761b1848880ea703a054f76db51852008b11008626",
"zh:4f251b0eda5bb5e3dc26ea4400dba200018213654b69b4a5f96abee815b4f5ff",
"zh:7011332745ea061e517fe1319bd6c75054a314155cb2c1199a5b01fe1889a7e2",
"zh:738ed82858317ccc246691c8b85995bc125ac3b4143043219bd0437adc56c992",
"zh:7dbe52fac7bb21227acd7529b487511c91f4107db9cc4414f50d04ffc3cab427",
"zh:a3a9251fb15f93e4cfc1789800fc2d7414bbc18944ad4c5c98f466e6477c42bc",
"zh:a543ec1a3a8c20635cf374110bd2f87c07374cf2c50617eee2c669b3ceeeaa9f",
"zh:d9ab41d556a48bd7059f0810cf020500635bfc696c9fc3adab5ea8915c1d886b",
"zh:d9e13427a7d011dbd654e591b0337e6074eef8c3b9bb11b2e39eaaf257044fd7",
"zh:f7605bd1437752114baf601bdf6931debe6dc6bfe3006eb7e9bb9080931dca8a",
]
}

View File

@ -0,0 +1,27 @@
provider "aws" {
region = "us-east-1"
}
terraform {
required_providers {
aws = {
version = "~> 3.19.0"
}
}
backend "s3" {
bucket = "driftctl-acc-test-only"
key = "states/s3/state1"
region = "us-east-1"
}
}
resource "random_string" "prefix" {
length = 6
upper = false
special = false
}
resource "aws_s3_bucket" "foobar" {
bucket = "${random_string.prefix.result}.driftctl-test.com"
}

View File

@ -18,7 +18,7 @@ import (
func TestAcc_Aws_CloudfrontDistribution(t *testing.T) {
var mutatedDistribution string
acceptance.Run(t, acceptance.AccTestCase{
Path: "./testdata/acc/aws_cloudfront_distribution",
Paths: []string{"./testdata/acc/aws_cloudfront_distribution"},
Args: []string{"scan", "--filter", "Type=='aws_cloudfront_distribution'"},
ShouldRefreshBeforeDestroy: true,
Checks: []acceptance.AccCheck{

View File

@ -8,7 +8,7 @@ import (
func TestAcc_AwsDynamoDBTable(t *testing.T) {
acceptance.Run(t, acceptance.AccTestCase{
Path: "./testdata/acc/aws_dynamodb_table",
Paths: []string{"./testdata/acc/aws_dynamodb_table"},
Args: []string{"scan", "--filter", "Type=='aws_dynamodb_table'"},
Checks: []acceptance.AccCheck{
{

View File

@ -16,7 +16,7 @@ import (
func TestAcc_AwsInstance_WithBlockDevices(t *testing.T) {
var mutatedInstanceId string
acceptance.Run(t, acceptance.AccTestCase{
Path: "./testdata/acc/aws_instance",
Paths: []string{"./testdata/acc/aws_instance"},
Args: []string{"scan", "--filter", "Type=='aws_instance'"},
Checks: []acceptance.AccCheck{
{

View File

@ -8,7 +8,7 @@ import (
func TestAcc_AwsInternetGateway(t *testing.T) {
acceptance.Run(t, acceptance.AccTestCase{
Path: "./testdata/acc/aws_internet_gateway",
Paths: []string{"./testdata/acc/aws_internet_gateway"},
Args: []string{"scan", "--filter", "Type=='aws_internet_gateway'"},
Checks: []acceptance.AccCheck{
{

View File

@ -8,7 +8,7 @@ import (
func TestAcc_AwsNATGateway(t *testing.T) {
acceptance.Run(t, acceptance.AccTestCase{
Path: "./testdata/acc/aws_nat_gateway",
Paths: []string{"./testdata/acc/aws_nat_gateway"},
// We filter on aws_eip_association too to test the middleware behavior
Args: []string{"scan", "--filter", "Type=='aws_nat_gateway' || Type=='aws_eip_association'"},
Checks: []acceptance.AccCheck{

View File

@ -16,7 +16,7 @@ import (
func TestAcc_AwsRoute53HealthCheck(t *testing.T) {
var mutatedHealthCheckID string
acceptance.Run(t, acceptance.AccTestCase{
Path: "./testdata/acc/aws_route53_health_check",
Paths: []string{"./testdata/acc/aws_route53_health_check"},
Args: []string{"scan", "--filter", "Type=='aws_route53_health_check'"},
Checks: []acceptance.AccCheck{
{

View File

@ -8,7 +8,7 @@ import (
func TestAcc_AwsRoute53Record_WithFQDNAsId(t *testing.T) {
acceptance.Run(t, acceptance.AccTestCase{
Path: "./testdata/acc/aws_route53_record",
Paths: []string{"./testdata/acc/aws_route53_record"},
Args: []string{"scan", "--filter", "Type=='aws_route53_record'"},
Checks: []acceptance.AccCheck{
{

View File

@ -8,7 +8,7 @@ import (
func TestAcc_AwsRouteTableAssociation(t *testing.T) {
acceptance.Run(t, acceptance.AccTestCase{
Path: "./testdata/acc/aws_route_table_association",
Paths: []string{"./testdata/acc/aws_route_table_association"},
Args: []string{"scan", "--filter", "Type=='aws_route_table_association'"},
Checks: []acceptance.AccCheck{
{

View File

@ -8,7 +8,7 @@ import (
func TestAcc_AwsRouteTable(t *testing.T) {
acceptance.Run(t, acceptance.AccTestCase{
Path: "./testdata/acc/aws_route_table",
Paths: []string{"./testdata/acc/aws_route_table"},
Args: []string{"scan", "--filter", "Type=='aws_route_table' || Type=='aws_default_route_table'"},
Checks: []acceptance.AccCheck{
{

View File

@ -8,7 +8,7 @@ import (
func TestAcc_AwsRoute(t *testing.T) {
acceptance.Run(t, acceptance.AccTestCase{
Path: "./testdata/acc/aws_route",
Paths: []string{"./testdata/acc/aws_route"},
Args: []string{"scan", "--filter", "Type=='aws_route'"},
Checks: []acceptance.AccCheck{
{

View File

@ -8,7 +8,7 @@ import (
func TestAcc_AwsS3Bucket_BucketInUsEast1(t *testing.T) {
acceptance.Run(t, acceptance.AccTestCase{
Path: "./testdata/acc/aws_s3_bucket",
Paths: []string{"./testdata/acc/aws_s3_bucket"},
Args: []string{"scan", "--filter", "Type=='aws_s3_bucket'"},
Checks: []acceptance.AccCheck{
{

View File

@ -8,7 +8,7 @@ import (
func TestAcc_AwsSecurityGroup(t *testing.T) {
acceptance.Run(t, acceptance.AccTestCase{
Path: "./testdata/acc/aws_security_group",
Paths: []string{"./testdata/acc/aws_security_group"},
Args: []string{"scan", "--filter", "Type=='aws_security_group' || Type=='aws_default_security_group'"},
Checks: []acceptance.AccCheck{
{

View File

@ -12,7 +12,7 @@ import (
func TestAcc_AwsSNSTopicPolicy(t *testing.T) {
acceptance.Run(t, acceptance.AccTestCase{
Path: "./testdata/acc/aws_sns_topic_policy",
Paths: []string{"./testdata/acc/aws_sns_topic_policy"},
Args: []string{"scan", "--filter", "Type=='aws_sns_topic' || Type=='aws_sns_topic_policy'"},
Checks: []acceptance.AccCheck{
{

View File

@ -12,7 +12,7 @@ import (
func TestAcc_AwsSNSTopicSubscription(t *testing.T) {
acceptance.Run(t, acceptance.AccTestCase{
Path: "./testdata/acc/aws_sns_topic_subscription",
Paths: []string{"./testdata/acc/aws_sns_topic_subscription"},
Args: []string{"scan", "--filter", "Type=='aws_sns_topic_subscription'"},
Checks: []acceptance.AccCheck{
{

View File

@ -22,7 +22,7 @@ import (
func TestAcc_AwsSNSTopic(t *testing.T) {
var mutatedTopicArn string
acceptance.Run(t, acceptance.AccTestCase{
Path: "./testdata/acc/aws_sns_topic",
Paths: []string{"./testdata/acc/aws_sns_topic"},
Args: []string{"scan", "--filter", "Type=='aws_sns_topic'"},
Checks: []acceptance.AccCheck{
{

View File

@ -13,7 +13,7 @@ import (
func TestAcc_AwsSqsQueuePolicy(t *testing.T) {
acceptance.Run(t, acceptance.AccTestCase{
Path: "./testdata/acc/aws_sqs_queue_policy",
Paths: []string{"./testdata/acc/aws_sqs_queue_policy"},
Args: []string{"scan", "--filter", "Type=='aws_sqs_queue_policy'"},
Checks: []acceptance.AccCheck{
{

View File

@ -20,7 +20,7 @@ import (
func TestAcc_AwsSqsQueue(t *testing.T) {
var mutatedQueue string
acceptance.Run(t, acceptance.AccTestCase{
Path: "./testdata/acc/aws_sqs_queue",
Paths: []string{"./testdata/acc/aws_sqs_queue"},
Args: []string{"scan", "--filter", "Type=='aws_sqs_queue'"},
Checks: []acceptance.AccCheck{
{

View File

@ -8,7 +8,7 @@ import (
func TestAcc_AwsSubnet(t *testing.T) {
acceptance.Run(t, acceptance.AccTestCase{
Path: "./testdata/acc/aws_subnet",
Paths: []string{"./testdata/acc/aws_subnet"},
Args: []string{"scan", "--filter", "Type=='aws_subnet' || Type=='aws_default_subnet'"},
Checks: []acceptance.AccCheck{
{

View File

@ -8,7 +8,7 @@ import (
func TestAcc_Github_Repository(t *testing.T) {
acceptance.Run(t, acceptance.AccTestCase{
Path: "./testdata/acc/github_repository",
Paths: []string{"./testdata/acc/github_repository"},
Args: []string{
"scan",
"--to", "github+tf",

View File

@ -93,6 +93,10 @@ func (r *ScanResult) AssertManagedCount(count int) {
r.Equal(count, len(r.Managed()))
}
func (r *ScanResult) AssertUnmanagedCount(count int) {
r.Equal(count, len(r.Unmanaged()))
}
func (r ScanResult) AssertInfrastructureIsInSync() {
r.Equal(
true,

View File

@ -38,30 +38,33 @@ type AccCheck struct {
}
type AccTestCase struct {
Path string
Paths []string
Args []string
OnStart func()
OnEnd func()
Checks []AccCheck
tmpResultFilePath string
originalEnv []string
tf *tfexec.Terraform
tf map[string]*tfexec.Terraform
ShouldRefreshBeforeDestroy bool
}
func (c *AccTestCase) initTerraformExecutor() error {
c.tf = make(map[string]*tfexec.Terraform, 1)
for _, path := range c.Paths {
execPath, err := tfinstall.LookPath().ExecPath(context.Background())
if err != nil {
return err
}
c.tf, err = tfexec.NewTerraform(c.Path, execPath)
c.tf[path], err = tfexec.NewTerraform(path, execPath)
if err != nil {
return err
}
env := c.resolveTerraformEnv()
if err := c.tf.SetEnv(env); err != nil {
if err := c.tf[path].SetEnv(env); err != nil {
return err
}
}
return nil
}
@ -81,8 +84,8 @@ func (c *AccTestCase) validate() error {
return fmt.Errorf("checks attribute must be defined")
}
if c.Path == "" {
return fmt.Errorf("path attribute must be defined")
if len(c.Paths) < 1 {
return fmt.Errorf("Paths attribute must be defined")
}
for _, arg := range c.Args {
@ -141,28 +144,40 @@ func (c *AccTestCase) terraformInit() error {
if err := c.initTerraformExecutor(); err != nil {
return err
}
_, err := os.Stat(path.Join(c.Path, ".terraform"))
for _, p := range c.Paths {
_, err := os.Stat(path.Join(p, ".terraform"))
if os.IsNotExist(err) {
logrus.Debug("Running terraform init ...")
logrus.WithFields(logrus.Fields{
"path": p,
}).Debug("Running terraform init ...")
stderr := new(bytes.Buffer)
c.tf.SetStderr(stderr)
if err := c.tf.Init(context.Background()); err != nil {
c.tf[p].SetStderr(stderr)
if err := c.tf[p].Init(context.Background()); err != nil {
return errors.Wrap(err, stderr.String())
}
logrus.Debug("Terraform init done")
logrus.WithFields(logrus.Fields{
"path": p,
}).Debug("Terraform init done")
}
}
return nil
}
func (c *AccTestCase) terraformApply() error {
logrus.Debug("Running terraform apply ...")
for _, p := range c.Paths {
logrus.WithFields(logrus.Fields{
"p": p,
}).Debug("Running terraform apply ...")
stderr := new(bytes.Buffer)
c.tf.SetStderr(stderr)
if err := c.tf.Apply(context.Background()); err != nil {
c.tf[p].SetStderr(stderr)
if err := c.tf[p].Apply(context.Background()); err != nil {
return errors.Wrap(err, stderr.String())
}
logrus.Debug("Terraform apply done")
logrus.WithFields(logrus.Fields{
"p": p,
}).Debug("Terraform apply done")
}
return nil
}
@ -174,25 +189,37 @@ func (c *AccTestCase) terraformDestroy() error {
}
}
logrus.Debug("Running terraform destroy ...")
for _, p := range c.Paths {
logrus.WithFields(logrus.Fields{
"p": p,
}).Debug("Running terraform destroy ...")
stderr := new(bytes.Buffer)
c.tf.SetStderr(stderr)
if err := c.tf.Destroy(context.Background()); err != nil {
c.tf[p].SetStderr(stderr)
if err := c.tf[p].Destroy(context.Background()); err != nil {
return errors.Wrap(err, stderr.String())
}
logrus.Debug("Terraform destroy done")
logrus.WithFields(logrus.Fields{
"p": p,
}).Debug("Terraform destroy done")
}
return nil
}
func (c *AccTestCase) terraformRefresh() error {
logrus.Debug("Running terraform refresh ...")
for _, p := range c.Paths {
logrus.WithFields(logrus.Fields{
"p": p,
}).Debug("Running terraform refresh ...")
stderr := new(bytes.Buffer)
c.tf.SetStderr(stderr)
if err := c.tf.Refresh(context.Background()); err != nil {
c.tf[p].SetStderr(stderr)
if err := c.tf[p].Refresh(context.Background()); err != nil {
return errors.Wrap(err, stderr.String())
}
logrus.Debug("Terraform refresh done")
logrus.WithFields(logrus.Fields{
"p": p,
}).Debug("Terraform refresh done")
}
return nil
}
@ -259,7 +286,16 @@ func Run(t *testing.T, c AccTestCase) {
}
if c.OnStart != nil {
c.useTerraformEnv()
c.OnStart()
if c.OnEnd != nil {
defer func() {
c.useTerraformEnv()
c.OnEnd()
c.restoreEnv()
}()
}
c.restoreEnv()
}
// Disable terraform version checks
@ -295,8 +331,21 @@ func Run(t *testing.T, c AccTestCase) {
}
if c.Args != nil {
c.Args = append([]string{""}, c.Args...)
isFromSet := false
for _, arg := range c.Args {
if arg == "--from" || arg == "-f" {
isFromSet = true
break
}
}
if !isFromSet {
for _, p := range c.Paths {
c.Args = append(c.Args,
"--from", fmt.Sprintf("tfstate://%s", path.Join(p, "terraform.tfstate")),
)
}
}
c.Args = append(c.Args,
"--from", fmt.Sprintf("tfstate://%s", path.Join(c.Path, "terraform.tfstate")),
"--output", fmt.Sprintf("json://%s", c.getResultFilePath()),
)
}
@ -328,9 +377,6 @@ func Run(t *testing.T, c AccTestCase) {
check.PostExec()
}
}
if c.OnEnd != nil {
c.OnEnd()
}
}
func RetryFor(timeout time.Duration, f func(c chan struct{}) error) error {