provider/aws: Add Sweeper setup, Sweepers for DB Option Group, Key Pair (#14773)
* provider/aws: Add Sweeper setup, Sweepers for DB Option Group, Key Pair * provider/google: Add sweeper for any leaked databases * more recursion and added LC sweeper, to test out the Dependency path * implement a dependency example * implement sweep-run flag to filter runs * stub a test for TestMain * test for multiple -sweep-run list
This commit is contained in:
parent
3d1e60b504
commit
372a80bc42
|
@ -0,0 +1,37 @@
|
|||
package aws
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
resource.TestMain(m)
|
||||
}
|
||||
|
||||
// sharedClientForRegion returns a common AWSClient setup needed for the sweeper
|
||||
// functions for a given region
|
||||
func sharedClientForRegion(region string) (interface{}, error) {
|
||||
if os.Getenv("AWS_ACCESS_KEY_ID") == "" {
|
||||
return nil, fmt.Errorf("empty AWS_ACCESS_KEY_ID")
|
||||
}
|
||||
|
||||
if os.Getenv("AWS_SECRET_ACCESS_KEY") == "" {
|
||||
return nil, fmt.Errorf("empty AWS_SECRET_ACCESS_KEY")
|
||||
}
|
||||
|
||||
conf := &Config{
|
||||
Region: region,
|
||||
}
|
||||
|
||||
// configures a default client for the region, using the above env vars
|
||||
client, err := conf.Client()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error getting AWS client")
|
||||
}
|
||||
|
||||
return client, nil
|
||||
}
|
|
@ -3,11 +3,13 @@ package aws
|
|||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||
|
@ -18,6 +20,72 @@ import (
|
|||
"github.com/hashicorp/terraform/terraform"
|
||||
)
|
||||
|
||||
func init() {
|
||||
resource.AddTestSweepers("aws_autoscaling_group", &resource.Sweeper{
|
||||
Name: "aws_autoscaling_group",
|
||||
F: testSweepAutoscalingGroups,
|
||||
})
|
||||
}
|
||||
|
||||
func testSweepAutoscalingGroups(region string) error {
|
||||
client, err := sharedClientForRegion(region)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error getting client: %s", err)
|
||||
}
|
||||
conn := client.(*AWSClient).autoscalingconn
|
||||
|
||||
resp, err := conn.DescribeAutoScalingGroups(&autoscaling.DescribeAutoScalingGroupsInput{})
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error retrieving launch configuration: %s", err)
|
||||
}
|
||||
|
||||
if len(resp.AutoScalingGroups) == 0 {
|
||||
log.Print("[DEBUG] No aws autoscaling groups to sweep")
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, asg := range resp.AutoScalingGroups {
|
||||
var testOptGroup bool
|
||||
for _, testName := range []string{"foobar", "terraform-"} {
|
||||
if strings.HasPrefix(*asg.AutoScalingGroupName, testName) {
|
||||
testOptGroup = true
|
||||
}
|
||||
}
|
||||
|
||||
if !testOptGroup {
|
||||
continue
|
||||
}
|
||||
|
||||
deleteopts := autoscaling.DeleteAutoScalingGroupInput{
|
||||
AutoScalingGroupName: asg.AutoScalingGroupName,
|
||||
ForceDelete: aws.Bool(true),
|
||||
}
|
||||
|
||||
err = resource.Retry(5*time.Minute, func() *resource.RetryError {
|
||||
if _, err := conn.DeleteAutoScalingGroup(&deleteopts); err != nil {
|
||||
if awserr, ok := err.(awserr.Error); ok {
|
||||
switch awserr.Code() {
|
||||
case "InvalidGroup.NotFound":
|
||||
return nil
|
||||
case "ResourceInUse", "ScalingActivityInProgress":
|
||||
return resource.RetryableError(awserr)
|
||||
}
|
||||
}
|
||||
|
||||
// Didn't recognize the error, so shouldn't retry.
|
||||
return resource.NonRetryableError(err)
|
||||
}
|
||||
// Successful delete
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestAccAWSAutoScalingGroup_basic(t *testing.T) {
|
||||
var group autoscaling.Group
|
||||
var lc autoscaling.LaunchConfiguration
|
||||
|
|
|
@ -2,8 +2,11 @@ package aws
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"regexp"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||
|
@ -13,6 +16,64 @@ import (
|
|||
"github.com/hashicorp/terraform/terraform"
|
||||
)
|
||||
|
||||
func init() {
|
||||
resource.AddTestSweepers("aws_db_option_group", &resource.Sweeper{
|
||||
Name: "aws_db_option_group",
|
||||
F: testSweepDbOptionGroups,
|
||||
})
|
||||
}
|
||||
|
||||
func testSweepDbOptionGroups(region string) error {
|
||||
client, err := sharedClientForRegion(region)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error getting client: %s", err)
|
||||
}
|
||||
|
||||
conn := client.(*AWSClient).rdsconn
|
||||
|
||||
opts := rds.DescribeOptionGroupsInput{}
|
||||
resp, err := conn.DescribeOptionGroups(&opts)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error describing DB Option Groups in Sweeper: %s", err)
|
||||
}
|
||||
|
||||
for _, og := range resp.OptionGroupsList {
|
||||
var testOptGroup bool
|
||||
for _, testName := range []string{"option-group-test-terraform-", "tf-test"} {
|
||||
if strings.HasPrefix(*og.OptionGroupName, testName) {
|
||||
testOptGroup = true
|
||||
}
|
||||
}
|
||||
|
||||
if !testOptGroup {
|
||||
continue
|
||||
}
|
||||
|
||||
deleteOpts := &rds.DeleteOptionGroupInput{
|
||||
OptionGroupName: og.OptionGroupName,
|
||||
}
|
||||
|
||||
ret := resource.Retry(1*time.Minute, func() *resource.RetryError {
|
||||
_, err := conn.DeleteOptionGroup(deleteOpts)
|
||||
if err != nil {
|
||||
if awsErr, ok := err.(awserr.Error); ok {
|
||||
if awsErr.Code() == "InvalidOptionGroupStateFault" {
|
||||
log.Printf("[DEBUG] AWS believes the RDS Option Group is still in use, retrying")
|
||||
return resource.RetryableError(awsErr)
|
||||
}
|
||||
}
|
||||
return resource.NonRetryableError(err)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if ret != nil {
|
||||
return fmt.Errorf("Error Deleting DB Option Group (%s) in Sweeper: %s", *og.OptionGroupName, ret)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestAccAWSDBOptionGroup_basic(t *testing.T) {
|
||||
var v rds.OptionGroup
|
||||
rName := fmt.Sprintf("option-group-test-terraform-%s", acctest.RandString(5))
|
||||
|
|
|
@ -2,6 +2,7 @@ package aws
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
|
@ -12,6 +13,47 @@ import (
|
|||
"github.com/hashicorp/terraform/terraform"
|
||||
)
|
||||
|
||||
func init() {
|
||||
resource.AddTestSweepers("aws_key_pair", &resource.Sweeper{
|
||||
Name: "aws_key_pair",
|
||||
F: testSweepKeyPairs,
|
||||
})
|
||||
}
|
||||
|
||||
func testSweepKeyPairs(region string) error {
|
||||
client, err := sharedClientForRegion(region)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error getting client: %s", err)
|
||||
}
|
||||
ec2conn := client.(*AWSClient).ec2conn
|
||||
|
||||
log.Printf("Destroying the tmp keys in (%s)", client.(*AWSClient).region)
|
||||
|
||||
resp, err := ec2conn.DescribeKeyPairs(&ec2.DescribeKeyPairsInput{
|
||||
Filters: []*ec2.Filter{
|
||||
&ec2.Filter{
|
||||
Name: aws.String("key-name"),
|
||||
Values: []*string{aws.String("tmp-key*")},
|
||||
},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error describing key pairs in Sweeper: %s", err)
|
||||
}
|
||||
|
||||
keyPairs := resp.KeyPairs
|
||||
for _, d := range keyPairs {
|
||||
_, err := ec2conn.DeleteKeyPair(&ec2.DeleteKeyPairInput{
|
||||
KeyName: d.KeyName,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error deleting key pairs in Sweeper: %s", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestAccAWSKeyPair_basic(t *testing.T) {
|
||||
var conf ec2.KeyPairInfo
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ package aws
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"math/rand"
|
||||
"strings"
|
||||
"testing"
|
||||
|
@ -16,6 +17,61 @@ import (
|
|||
"github.com/hashicorp/terraform/terraform"
|
||||
)
|
||||
|
||||
func init() {
|
||||
resource.AddTestSweepers("aws_launch_configuration", &resource.Sweeper{
|
||||
Name: "aws_launch_configuration",
|
||||
Dependencies: []string{"aws_autoscaling_group"},
|
||||
F: testSweepLaunchConfigurations,
|
||||
})
|
||||
}
|
||||
|
||||
func testSweepLaunchConfigurations(region string) error {
|
||||
client, err := sharedClientForRegion(region)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error getting client: %s", err)
|
||||
}
|
||||
autoscalingconn := client.(*AWSClient).autoscalingconn
|
||||
|
||||
resp, err := autoscalingconn.DescribeLaunchConfigurations(&autoscaling.DescribeLaunchConfigurationsInput{})
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error retrieving launch configuration: %s", err)
|
||||
}
|
||||
|
||||
if len(resp.LaunchConfigurations) == 0 {
|
||||
log.Print("[DEBUG] No aws launch configurations to sweep")
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, lc := range resp.LaunchConfigurations {
|
||||
var testOptGroup bool
|
||||
for _, testName := range []string{"terraform-", "foobar"} {
|
||||
if strings.HasPrefix(*lc.LaunchConfigurationName, testName) {
|
||||
testOptGroup = true
|
||||
}
|
||||
}
|
||||
|
||||
if !testOptGroup {
|
||||
continue
|
||||
}
|
||||
|
||||
_, err := autoscalingconn.DeleteLaunchConfiguration(
|
||||
&autoscaling.DeleteLaunchConfigurationInput{
|
||||
LaunchConfigurationName: lc.LaunchConfigurationName,
|
||||
})
|
||||
if err != nil {
|
||||
autoscalingerr, ok := err.(awserr.Error)
|
||||
if ok && (autoscalingerr.Code() == "InvalidConfiguration.NotFound" || autoscalingerr.Code() == "ValidationError") {
|
||||
log.Printf("[DEBUG] Launch configuration (%s) not found", *lc.LaunchConfigurationName)
|
||||
return nil
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestAccAWSLaunchConfiguration_basic(t *testing.T) {
|
||||
var conf autoscaling.LaunchConfiguration
|
||||
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
package google
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
resource.TestMain(m)
|
||||
}
|
||||
|
||||
// sharedConfigForRegion returns a common config setup needed for the sweeper
|
||||
// functions for a given region
|
||||
func sharedConfigForRegion(region string) (*Config, error) {
|
||||
project := os.Getenv("GOOGLE_PROJECT")
|
||||
if project == "" {
|
||||
return nil, fmt.Errorf("empty GOOGLE_PROJECT")
|
||||
}
|
||||
|
||||
creds := os.Getenv("GOOGLE_CREDENTIALS")
|
||||
if creds == "" {
|
||||
return nil, fmt.Errorf("empty GOOGLE_CREDENTIALS")
|
||||
}
|
||||
|
||||
conf := &Config{
|
||||
Credentials: creds,
|
||||
Region: region,
|
||||
Project: project,
|
||||
}
|
||||
|
||||
return conf, nil
|
||||
}
|
|
@ -9,6 +9,7 @@ package google
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
|
@ -20,6 +21,105 @@ import (
|
|||
"google.golang.org/api/sqladmin/v1beta4"
|
||||
)
|
||||
|
||||
func init() {
|
||||
resource.AddTestSweepers("gcp_sql_db_instance", &resource.Sweeper{
|
||||
Name: "gcp_sql_db_instance",
|
||||
F: testSweepDatabases,
|
||||
})
|
||||
}
|
||||
|
||||
func testSweepDatabases(region string) error {
|
||||
config, err := sharedConfigForRegion(region)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error getting shared config for region: %s", err)
|
||||
}
|
||||
|
||||
err = config.loadAndValidate()
|
||||
if err != nil {
|
||||
log.Fatalf("error loading: %s", err)
|
||||
}
|
||||
|
||||
found, err := config.clientSqlAdmin.Instances.List(config.Project).Do()
|
||||
if err != nil {
|
||||
log.Fatalf("error listing databases: %s", err)
|
||||
}
|
||||
|
||||
if len(found.Items) == 0 {
|
||||
log.Printf("No databases found")
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, d := range found.Items {
|
||||
var testDbInstance bool
|
||||
for _, testName := range []string{"tf-lw-", "sqldatabasetest"} {
|
||||
// only destroy instances we know to fit our test naming pattern
|
||||
if strings.HasPrefix(d.Name, testName) {
|
||||
testDbInstance = true
|
||||
}
|
||||
}
|
||||
|
||||
if !testDbInstance {
|
||||
continue
|
||||
}
|
||||
|
||||
log.Printf("Destroying SQL Instance (%s)", d.Name)
|
||||
|
||||
// replicas need to be stopped and destroyed before destroying a master
|
||||
// instance. The ordering slice tracks replica databases for a given master
|
||||
// and we call destroy on them before destroying the master
|
||||
var ordering []string
|
||||
for _, replicaName := range d.ReplicaNames {
|
||||
// need to stop replication before being able to destroy a database
|
||||
op, err := config.clientSqlAdmin.Instances.StopReplica(config.Project, replicaName).Do()
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("error, failed to stop replica instance (%s) for instance (%s): %s", replicaName, d.Name, err)
|
||||
}
|
||||
|
||||
err = sqladminOperationWait(config, op, "Stop Replica")
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "does not exist") {
|
||||
log.Printf("Replication operation not found")
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
ordering = append(ordering, replicaName)
|
||||
}
|
||||
|
||||
// ordering has a list of replicas (or none), now add the primary to the end
|
||||
ordering = append(ordering, d.Name)
|
||||
|
||||
for _, db := range ordering {
|
||||
// destroy instances, replicas first
|
||||
op, err := config.clientSqlAdmin.Instances.Delete(config.Project, db).Do()
|
||||
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "409") {
|
||||
// the GCP api can return a 409 error after the delete operation
|
||||
// reaches a successful end
|
||||
log.Printf("Operation not found, got 409 response")
|
||||
continue
|
||||
}
|
||||
|
||||
return fmt.Errorf("Error, failed to delete instance %s: %s", db, err)
|
||||
}
|
||||
|
||||
err = sqladminOperationWait(config, op, "Delete Instance")
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "does not exist") {
|
||||
log.Printf("SQL instance not found")
|
||||
continue
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestAccGoogleSqlDatabaseInstance_basic(t *testing.T) {
|
||||
var instance sqladmin.DatabaseInstance
|
||||
databaseID := acctest.RandInt()
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package resource
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
|
@ -20,6 +21,153 @@ import (
|
|||
"github.com/hashicorp/terraform/terraform"
|
||||
)
|
||||
|
||||
// flagSweep is a flag available when running tests on the command line. It
|
||||
// contains a comma seperated list of regions to for the sweeper functions to
|
||||
// run in. This flag bypasses the normal Test path and instead runs functions designed to
|
||||
// clean up any leaked resources a testing environment could have created. It is
|
||||
// a best effort attempt, and relies on Provider authors to implement "Sweeper"
|
||||
// methods for resources.
|
||||
|
||||
// Adding Sweeper methods with AddTestSweepers will
|
||||
// construct a list of sweeper funcs to be called here. We iterate through
|
||||
// regions provided by the sweep flag, and for each region we iterate through the
|
||||
// tests, and exit on any errors. At time of writing, sweepers are ran
|
||||
// sequentially, however they can list dependencies to be ran first. We track
|
||||
// the sweepers that have been ran, so as to not run a sweeper twice for a given
|
||||
// region.
|
||||
//
|
||||
// WARNING:
|
||||
// Sweepers are designed to be destructive. You should not use the -sweep flag
|
||||
// in any environment that is not strictly a test environment. Resources will be
|
||||
// destroyed.
|
||||
|
||||
var flagSweep = flag.String("sweep", "", "List of Regions to run available Sweepers")
|
||||
var flagSweepRun = flag.String("sweep-run", "", "Comma seperated list of Sweeper Tests to run")
|
||||
var sweeperFuncs map[string]*Sweeper
|
||||
|
||||
// map of sweepers that have ran, and the success/fail status based on any error
|
||||
// raised
|
||||
var sweeperRunList map[string]bool
|
||||
|
||||
// type SweeperFunc is a signature for a function that acts as a sweeper. It
|
||||
// accepts a string for the region that the sweeper is to be ran in. This
|
||||
// function must be able to construct a valid client for that region.
|
||||
type SweeperFunc func(r string) error
|
||||
|
||||
type Sweeper struct {
|
||||
// Name for sweeper. Must be unique to be ran by the Sweeper Runner
|
||||
Name string
|
||||
|
||||
// Dependencies list the const names of other Sweeper functions that must be ran
|
||||
// prior to running this Sweeper. This is an ordered list that will be invoked
|
||||
// recursively at the helper/resource level
|
||||
Dependencies []string
|
||||
|
||||
// Sweeper function that when invoked sweeps the Provider of specific
|
||||
// resources
|
||||
F SweeperFunc
|
||||
}
|
||||
|
||||
func init() {
|
||||
sweeperFuncs = make(map[string]*Sweeper)
|
||||
}
|
||||
|
||||
// AddTestSweepers function adds a given name and Sweeper configuration
|
||||
// pair to the internal sweeperFuncs map. Invoke this function to register a
|
||||
// resource sweeper to be available for running when the -sweep flag is used
|
||||
// with `go test`. Sweeper names must be unique to help ensure a given sweeper
|
||||
// is only ran once per run.
|
||||
func AddTestSweepers(name string, s *Sweeper) {
|
||||
if _, ok := sweeperFuncs[name]; ok {
|
||||
log.Fatalf("[ERR] Error adding (%s) to sweeperFuncs: function already exists in map", name)
|
||||
}
|
||||
|
||||
sweeperFuncs[name] = s
|
||||
}
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
flag.Parse()
|
||||
if *flagSweep != "" {
|
||||
// parse flagSweep contents for regions to run
|
||||
regions := strings.Split(*flagSweep, ",")
|
||||
|
||||
// get filtered list of sweepers to run based on sweep-run flag
|
||||
sweepers := filterSweepers(*flagSweepRun, sweeperFuncs)
|
||||
for _, region := range regions {
|
||||
region = strings.TrimSpace(region)
|
||||
// reset sweeperRunList for each region
|
||||
sweeperRunList = map[string]bool{}
|
||||
|
||||
log.Printf("[DEBUG] Running Sweepers for region (%s):\n", region)
|
||||
for _, sweeper := range sweepers {
|
||||
if err := runSweeperWithRegion(region, sweeper); err != nil {
|
||||
log.Fatalf("[ERR] error running (%s): %s", sweeper.Name, err)
|
||||
}
|
||||
}
|
||||
|
||||
log.Printf("Sweeper Tests ran:\n")
|
||||
for s, _ := range sweeperRunList {
|
||||
fmt.Printf("\t- %s\n", s)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
os.Exit(m.Run())
|
||||
}
|
||||
}
|
||||
|
||||
// filterSweepers takes a comma seperated string listing the names of sweepers
|
||||
// to be ran, and returns a filtered set from the list of all of sweepers to
|
||||
// run based on the names given.
|
||||
func filterSweepers(f string, source map[string]*Sweeper) map[string]*Sweeper {
|
||||
filterSlice := strings.Split(strings.ToLower(f), ",")
|
||||
if len(filterSlice) == 1 && filterSlice[0] == "" {
|
||||
// if the filter slice is a single element of "" then no sweeper list was
|
||||
// given, so just return the full list
|
||||
return source
|
||||
}
|
||||
|
||||
sweepers := make(map[string]*Sweeper)
|
||||
for name, sweeper := range source {
|
||||
for _, s := range filterSlice {
|
||||
if strings.Contains(strings.ToLower(name), s) {
|
||||
sweepers[name] = sweeper
|
||||
}
|
||||
}
|
||||
}
|
||||
return sweepers
|
||||
}
|
||||
|
||||
// runSweeperWithRegion recieves a sweeper and a region, and recursively calls
|
||||
// itself with that region for every dependency found for that sweeper. If there
|
||||
// are no dependencies, invoke the contained sweeper fun with the region, and
|
||||
// add the success/fail status to the sweeperRunList.
|
||||
func runSweeperWithRegion(region string, s *Sweeper) error {
|
||||
for _, dep := range s.Dependencies {
|
||||
if depSweeper, ok := sweeperFuncs[dep]; ok {
|
||||
log.Printf("[DEBUG] Sweeper (%s) has dependency (%s), running..", s.Name, dep)
|
||||
if err := runSweeperWithRegion(region, depSweeper); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
log.Printf("[DEBUG] Sweeper (%s) has dependency (%s), but that sweeper was not found", s.Name, dep)
|
||||
}
|
||||
}
|
||||
|
||||
if _, ok := sweeperRunList[s.Name]; ok {
|
||||
log.Printf("[DEBUG] Sweeper (%s) already ran in region (%s)", s.Name, region)
|
||||
return nil
|
||||
}
|
||||
|
||||
runE := s.F(region)
|
||||
if runE == nil {
|
||||
sweeperRunList[s.Name] = true
|
||||
} else {
|
||||
sweeperRunList[s.Name] = false
|
||||
}
|
||||
|
||||
return runE
|
||||
}
|
||||
|
||||
const TestEnvVar = "TF_ACC"
|
||||
|
||||
// TestProvider can be implemented by any ResourceProvider to provide custom
|
||||
|
|
|
@ -2,9 +2,12 @@ package resource
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
@ -619,3 +622,158 @@ func testProvider() *terraform.MockResourceProvider {
|
|||
const testConfigStr = `
|
||||
resource "test_instance" "foo" {}
|
||||
`
|
||||
|
||||
func TestTest_Main(t *testing.T) {
|
||||
flag.Parse()
|
||||
if *flagSweep == "" {
|
||||
// Tests for the TestMain method used for Sweepers will panic without the -sweep
|
||||
// flag specified. Mock the value for now
|
||||
*flagSweep = "us-east-1"
|
||||
}
|
||||
|
||||
cases := []struct {
|
||||
Name string
|
||||
Sweepers map[string]*Sweeper
|
||||
ExpectedRunList []string
|
||||
SweepRun string
|
||||
}{
|
||||
{
|
||||
Name: "normal",
|
||||
Sweepers: map[string]*Sweeper{
|
||||
"aws_dummy": &Sweeper{
|
||||
Name: "aws_dummy",
|
||||
F: mockSweeperFunc,
|
||||
},
|
||||
},
|
||||
ExpectedRunList: []string{"aws_dummy"},
|
||||
},
|
||||
{
|
||||
Name: "with dep",
|
||||
Sweepers: map[string]*Sweeper{
|
||||
"aws_dummy": &Sweeper{
|
||||
Name: "aws_dummy",
|
||||
F: mockSweeperFunc,
|
||||
},
|
||||
"aws_top": &Sweeper{
|
||||
Name: "aws_top",
|
||||
Dependencies: []string{"aws_sub"},
|
||||
F: mockSweeperFunc,
|
||||
},
|
||||
"aws_sub": &Sweeper{
|
||||
Name: "aws_sub",
|
||||
F: mockSweeperFunc,
|
||||
},
|
||||
},
|
||||
ExpectedRunList: []string{"aws_dummy", "aws_sub", "aws_top"},
|
||||
},
|
||||
{
|
||||
Name: "with filter",
|
||||
Sweepers: map[string]*Sweeper{
|
||||
"aws_dummy": &Sweeper{
|
||||
Name: "aws_dummy",
|
||||
F: mockSweeperFunc,
|
||||
},
|
||||
"aws_top": &Sweeper{
|
||||
Name: "aws_top",
|
||||
Dependencies: []string{"aws_sub"},
|
||||
F: mockSweeperFunc,
|
||||
},
|
||||
"aws_sub": &Sweeper{
|
||||
Name: "aws_sub",
|
||||
F: mockSweeperFunc,
|
||||
},
|
||||
},
|
||||
ExpectedRunList: []string{"aws_dummy"},
|
||||
SweepRun: "aws_dummy",
|
||||
},
|
||||
{
|
||||
Name: "with two filters",
|
||||
Sweepers: map[string]*Sweeper{
|
||||
"aws_dummy": &Sweeper{
|
||||
Name: "aws_dummy",
|
||||
F: mockSweeperFunc,
|
||||
},
|
||||
"aws_top": &Sweeper{
|
||||
Name: "aws_top",
|
||||
Dependencies: []string{"aws_sub"},
|
||||
F: mockSweeperFunc,
|
||||
},
|
||||
"aws_sub": &Sweeper{
|
||||
Name: "aws_sub",
|
||||
F: mockSweeperFunc,
|
||||
},
|
||||
},
|
||||
ExpectedRunList: []string{"aws_dummy", "aws_sub"},
|
||||
SweepRun: "aws_dummy,aws_sub",
|
||||
},
|
||||
{
|
||||
Name: "with dep and filter",
|
||||
Sweepers: map[string]*Sweeper{
|
||||
"aws_dummy": &Sweeper{
|
||||
Name: "aws_dummy",
|
||||
F: mockSweeperFunc,
|
||||
},
|
||||
"aws_top": &Sweeper{
|
||||
Name: "aws_top",
|
||||
Dependencies: []string{"aws_sub"},
|
||||
F: mockSweeperFunc,
|
||||
},
|
||||
"aws_sub": &Sweeper{
|
||||
Name: "aws_sub",
|
||||
F: mockSweeperFunc,
|
||||
},
|
||||
},
|
||||
ExpectedRunList: []string{"aws_top", "aws_sub"},
|
||||
SweepRun: "aws_top",
|
||||
},
|
||||
{
|
||||
Name: "filter and none",
|
||||
Sweepers: map[string]*Sweeper{
|
||||
"aws_dummy": &Sweeper{
|
||||
Name: "aws_dummy",
|
||||
F: mockSweeperFunc,
|
||||
},
|
||||
"aws_top": &Sweeper{
|
||||
Name: "aws_top",
|
||||
Dependencies: []string{"aws_sub"},
|
||||
F: mockSweeperFunc,
|
||||
},
|
||||
"aws_sub": &Sweeper{
|
||||
Name: "aws_sub",
|
||||
F: mockSweeperFunc,
|
||||
},
|
||||
},
|
||||
SweepRun: "none",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
// reset sweepers
|
||||
sweeperFuncs = map[string]*Sweeper{}
|
||||
|
||||
t.Run(tc.Name, func(t *testing.T) {
|
||||
for n, s := range tc.Sweepers {
|
||||
AddTestSweepers(n, s)
|
||||
}
|
||||
*flagSweepRun = tc.SweepRun
|
||||
|
||||
TestMain(&testing.M{})
|
||||
|
||||
// get list of tests ran from sweeperRunList keys
|
||||
var keys []string
|
||||
for k, _ := range sweeperRunList {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
|
||||
sort.Strings(keys)
|
||||
sort.Strings(tc.ExpectedRunList)
|
||||
if !reflect.DeepEqual(keys, tc.ExpectedRunList) {
|
||||
t.Fatalf("Expected keys mismatch, expected:\n%#v\ngot:\n%#v\n", tc.ExpectedRunList, keys)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func mockSweeperFunc(s string) error {
|
||||
return nil
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue