From d9e60cccfb4e2595bbaaabd911614483fcad808e Mon Sep 17 00:00:00 2001 From: Jan-Christoph Kuester Date: Sat, 2 Jan 2021 13:59:21 +0100 Subject: [PATCH] Only awsls supported resources Some resources couldn't be listed with awsls yet, but are now covered. --- .gitignore | 1 + go.mod | 3 +- go.sum | 4 +- main.go | 2 +- pkg/resource/dependency.go | 43 ++++ pkg/resource/filter.go | 2 +- pkg/resource/list.go | 97 +++----- pkg/resource/resource.go | 75 ------ pkg/resource/resource_test.go | 59 ----- pkg/resource/supported.go | 225 ------------------ test/autoscaling_group_test.go | 105 ++++++++ test/helper_test.go | 22 +- test/test-fixtures/autoscaling-group/main.tf | 86 +++++++ .../autoscaling-group/outputs.tf | 13 + .../autoscaling-group/variables.tf | 11 + test/test-fixtures/elb/elb.tf | 2 +- 16 files changed, 311 insertions(+), 439 deletions(-) create mode 100644 pkg/resource/dependency.go delete mode 100644 pkg/resource/resource.go delete mode 100644 pkg/resource/resource_test.go delete mode 100644 pkg/resource/supported.go create mode 100644 test/autoscaling_group_test.go create mode 100644 test/test-fixtures/autoscaling-group/main.tf create mode 100644 test/test-fixtures/autoscaling-group/outputs.tf create mode 100644 test/test-fixtures/autoscaling-group/variables.tf diff --git a/.gitignore b/.gitignore index 3b4ed3e31..916680a46 100644 --- a/.gitignore +++ b/.gitignore @@ -4,5 +4,6 @@ bin/ .terradozer/ .terraform/ *.tfstate +*tfstate.backup coverage.txt awsweeper diff --git a/go.mod b/go.mod index 3c9bd349a..a3f4ec070 100644 --- a/go.mod +++ b/go.mod @@ -7,10 +7,9 @@ require ( github.com/aws/aws-sdk-go-v2 v0.24.0 github.com/fatih/color v1.9.0 github.com/gruntwork-io/terratest v0.24.2 - github.com/jckuester/awsls v0.8.0 + github.com/jckuester/awsls v0.8.1 github.com/jckuester/terradozer v0.1.3 github.com/onsi/gomega v1.9.0 - github.com/pkg/errors v0.9.1 github.com/spf13/pflag v1.0.5 github.com/stretchr/testify v1.5.1 github.com/zclconf/go-cty v1.4.0 diff --git a/go.sum b/go.sum index d3d0c4d55..9bcbbd946 100644 --- a/go.sum +++ b/go.sum @@ -350,8 +350,8 @@ github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1: github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.7/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/jckuester/awsls v0.8.0 h1:ZuQRfMzEa2ZoZ/pcNJLmXXB0S14I5X7kvIENWdryxdQ= -github.com/jckuester/awsls v0.8.0/go.mod h1:PfviZPTqz64c9EfNj6b1ygggheRl51JrJWT7R3z+2BQ= +github.com/jckuester/awsls v0.8.1 h1:k329Ey/odG/hdbTiubvGHFxIEVRpL1KLNTOaO8P+Eow= +github.com/jckuester/awsls v0.8.1/go.mod h1:PfviZPTqz64c9EfNj6b1ygggheRl51JrJWT7R3z+2BQ= github.com/jckuester/terradozer v0.1.3 h1:xrRxr+L58QAVz5Kwq2fyWCNiK1NWOuKo8g5Q2664WZ4= github.com/jckuester/terradozer v0.1.3/go.mod h1:ER3EJojZmO2u6lfcdgnmC+Nrg/TV2T2bacY5FZpqgks= github.com/jhump/protoreflect v1.6.0 h1:h5jfMVslIg6l29nsMs0D8Wj17RDVdNYti0vDN/PZZoE= diff --git a/main.go b/main.go index 4320ae221..9b6acc2b7 100644 --- a/main.go +++ b/main.go @@ -139,7 +139,7 @@ func mainExitCode() int { } // initialize a Terraform AWS provider for each AWS client with a matching config - providers, err := util.NewProviderPool(clientKeys, "2.68.0", "~/.awsweeper", timeoutDuration) + providers, err := util.NewProviderPool(clientKeys, "v3.16.0", "~/.awsweeper", timeoutDuration) if err != nil { fmt.Fprint(os.Stderr, color.RedString("\nError: %s\n", err)) diff --git a/pkg/resource/dependency.go b/pkg/resource/dependency.go new file mode 100644 index 000000000..45805baec --- /dev/null +++ b/pkg/resource/dependency.go @@ -0,0 +1,43 @@ +package resource + +var ( + // DependencyOrder is the order in which resource types should be deleted, + // since dependent resources need to be deleted before their dependencies + // (e.g. aws_subnet before aws_vpc) + DependencyOrder = map[string]int{ + "aws_lambda_function": 10100, + "aws_ecs_cluster": 10000, + "aws_autoscaling_group": 9990, + "aws_instance": 9980, + "aws_key_pair": 9970, + "aws_elb": 9960, + "aws_vpc_endpoint": 9950, + "aws_nat_gateway": 9940, + "aws_cloudformation_stack": 9930, + "aws_route53_zone": 9920, + "aws_efs_file_system": 9910, + "aws_launch_configuration": 9900, + "aws_eip": 9890, + "aws_internet_gateway": 9880, + "aws_subnet": 9870, + "aws_route_table": 9860, + "aws_security_group": 9850, + "aws_network_acl": 9840, + "aws_vpc": 9830, + "aws_db_instance": 9825, + "aws_iam_policy": 9820, + "aws_iam_group": 9810, + "aws_iam_user": 9800, + "aws_iam_role": 9790, + "aws_iam_instance_profile": 9780, + "aws_s3_bucket": 9750, + "aws_ami": 9740, + "aws_ebs_volume": 9730, + "aws_ebs_snapshot": 9720, + "aws_kms_alias": 9610, + "aws_kms_key": 9600, + "aws_network_interface": 9000, + "aws_cloudwatch_log_group": 8900, + "aws_cloudtrail": 8800, + } +) diff --git a/pkg/resource/filter.go b/pkg/resource/filter.go index 7de51991e..4cd74dee7 100644 --- a/pkg/resource/filter.go +++ b/pkg/resource/filter.go @@ -66,7 +66,7 @@ func NewFilter(path string) (*Filter, error) { // Validate checks if all resource types appearing in the config are currently supported. func (f Filter) Validate() error { for _, rType := range f.Types() { - if !(SupportedResourceType(rType) || resource.IsSupportedType(rType)) { + if resource.IsSupportedType(rType) { return fmt.Errorf("unsupported resource type: %s", rType) } diff --git a/pkg/resource/list.go b/pkg/resource/list.go index fc61dde3a..9ede48444 100644 --- a/pkg/resource/list.go +++ b/pkg/resource/list.go @@ -26,82 +26,51 @@ func List(filter *Filter, clients map[util.AWSClientKey]awsls.Client, var destroyableRes []terradozerRes.DestroyableResource for _, rType := range filter.Types() { - if SupportedResourceType(rType) { - for key, client := range clients { - err := client.SetAccountID() - if err != nil { - log.WithError(err).Fatal("failed to set account ID") - continue - } - - rawResources, err := AWS(client).RawResources(rType) - if err != nil { - log.WithError(err).Fatal("failed to get raw resources") - } - - deletableResources, err := DeletableResources(rType, rawResources, client) - if err != nil { - log.WithError(err).Fatal("failed to convert raw resources into deletable resources") - } - - resourcesWithStates := awslsRes.GetStates(deletableResources, providers) - - filteredRes := filter.Apply(resourcesWithStates) - print(filteredRes, outputType) - - p := providers[key] - - for _, r := range filteredRes { - destroyableRes = append(destroyableRes, terradozerRes.NewWithState(r.Type, r.ID, &p, r.State())) - } + for key, client := range clients { + err := client.SetAccountID() + if err != nil { + log.WithError(err).Fatal("failed to set account ID") + continue } - } else { - for key, client := range clients { - err := client.SetAccountID() - if err != nil { - log.WithError(err).Fatal("failed to set account ID") - continue - } - resources, err := awsls.ListResourcesByType(&client, rType) - if err != nil { - log.WithError(err).Fatal("failed to list awsls supported resources") - continue - } + resources, err := awsls.ListResourcesByType(&client, rType) + if err != nil { + log.WithError(err).Fatal("failed to list awsls supported resources") + continue + } - resourcesWithStates := awslsRes.GetStates(resources, providers) + resourcesWithStates := awslsRes.GetStates(resources, providers) - filteredRes := filter.Apply(resourcesWithStates) - print(filteredRes, outputType) + filteredRes := filter.Apply(resourcesWithStates) + print(filteredRes, outputType) - p := providers[key] + p := providers[key] - switch rType { - case "aws_iam_user": - attachedPolicies := getAttachedUserPolicies(filteredRes, client, &p) - print(attachedPolicies, outputType) + switch rType { + case "aws_iam_user": + attachedPolicies := getAttachedUserPolicies(filteredRes, client, &p) + print(attachedPolicies, outputType) - inlinePolicies := getInlineUserPolicies(filteredRes, client, &p) - print(inlinePolicies, outputType) + inlinePolicies := getInlineUserPolicies(filteredRes, client, &p) + print(inlinePolicies, outputType) - filteredRes = append(filteredRes, attachedPolicies...) - filteredRes = append(filteredRes, inlinePolicies...) - case "aws_iam_policy": - policyAttachments := getPolicyAttachments(filteredRes, &p) - print(policyAttachments, outputType) + filteredRes = append(filteredRes, attachedPolicies...) + filteredRes = append(filteredRes, inlinePolicies...) + case "aws_iam_policy": + policyAttachments := getPolicyAttachments(filteredRes, &p) + print(policyAttachments, outputType) - filteredRes = append(filteredRes, policyAttachments...) + filteredRes = append(filteredRes, policyAttachments...) - case "aws_efs_file_system": - mountTargets := getEfsMountTargets(filteredRes, client, &p) - print(mountTargets, outputType) + case "aws_efs_file_system": + mountTargets := getEfsMountTargets(filteredRes, client, &p) + print(mountTargets, outputType) - filteredRes = append(filteredRes, mountTargets...) - } + filteredRes = append(filteredRes, mountTargets...) + } - for _, r := range filteredRes { - destroyableRes = append(destroyableRes, terradozerRes.NewWithState(r.Type, r.ID, &p, r.State())) - } + for _, r := range filteredRes { + destroyableRes = append(destroyableRes, terradozerRes.NewWithState(r.Type, r.ID, &p, r.State())) } } } diff --git a/pkg/resource/resource.go b/pkg/resource/resource.go deleted file mode 100644 index 2afcaa1c7..000000000 --- a/pkg/resource/resource.go +++ /dev/null @@ -1,75 +0,0 @@ -package resource - -import ( - "reflect" - "time" - - awsls "github.com/jckuester/awsls/aws" - "github.com/pkg/errors" -) - -// Resources converts given raw resources for a given resource type -// into a format that can be deleted by the Terraform API. -func DeletableResources(resType string, resources interface{}, client awsls.Client) ([]awsls.Resource, error) { - var deletableResources []awsls.Resource - - reflectResources := reflect.ValueOf(resources) - for i := 0; i < reflectResources.Len(); i++ { - deleteID, err := getDeleteID(resType) - if err != nil { - return nil, err - } - - deleteIDField, err := getField(deleteID, reflect.Indirect(reflectResources.Index(i))) - if err != nil { - return nil, errors.Wrapf(err, "Field with delete ID required for deleting resource") - } - - var creationTime *time.Time - creationTimeField, err := findField(creationTimeFieldNames, reflect.Indirect(reflectResources.Index(i))) - if err == nil { - creationTimeCastTime, ok := creationTimeField.Interface().(*time.Time) - if ok { - creationTime = creationTimeCastTime - } else { - creationTimeCastString, ok := creationTimeField.Interface().(*string) - if ok { - parsedCreationTime, err := time.Parse("2006-01-02T15:04:05.000Z0700", *creationTimeCastString) - if err == nil { - creationTime = &parsedCreationTime - } - } - } - } - - deletableResources = append(deletableResources, awsls.Resource{ - Type: resType, - ID: deleteIDField.Elem().String(), - CreatedAt: creationTime, - Region: client.Region, - Profile: client.Profile, - AccountID: client.AccountID, - }) - } - - return deletableResources, nil -} - -func getField(name string, v reflect.Value) (reflect.Value, error) { - field := v.FieldByName(name) - - if !field.IsValid() { - return reflect.Value{}, errors.Errorf("Field not found: %s", name) - } - return field, nil -} - -func findField(names []string, v reflect.Value) (reflect.Value, error) { - for _, name := range names { - field, err := getField(name, v) - if err == nil { - return field, nil - } - } - return reflect.Value{}, errors.Errorf("Fields not found: %s", names) -} diff --git a/pkg/resource/resource_test.go b/pkg/resource/resource_test.go deleted file mode 100644 index 42e32ac1f..000000000 --- a/pkg/resource/resource_test.go +++ /dev/null @@ -1,59 +0,0 @@ -package resource_test - -import ( - "testing" - "time" - - awsls "github.com/jckuester/awsls/aws" - - "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go-v2/service/autoscaling" - "github.com/aws/aws-sdk-go-v2/service/ec2" - "github.com/jckuester/awsweeper/pkg/resource" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -var ( - testImageId = "test-ami" - testAutoscalingGroupName = "test-auto-scaling-group" -) - -func TestDeletableResources_CreationDateIsTypeTime(t *testing.T) { - // given - testCreationDate := aws.Time(time.Date(2018, 11, 17, 5, 0, 0, 0, time.UTC)) - rawResources := []*autoscaling.AutoScalingGroup{ - { - AutoScalingGroupName: &testAutoscalingGroupName, - CreatedTime: testCreationDate, - }, - } - - // when - res, err := resource.DeletableResources(resource.AutoscalingGroup, rawResources, awsls.Client{}) - require.NoError(t, err) - require.Len(t, res, 1) - - // then - assert.Equal(t, testAutoscalingGroupName, res[0].ID) - assert.Equal(t, testCreationDate, res[0].CreatedAt) -} - -func TestDeletableResources_CreationDateIsTypeString(t *testing.T) { - // given - testCreationDate := "2018-12-16T19:40:28.000Z" - rawResources := []*ec2.Image{ - { - ImageId: &testImageId, - CreationDate: &testCreationDate, - }, - } - - // when - res, err := resource.DeletableResources(resource.Ami, rawResources, awsls.Client{}) - require.NoError(t, err) - - // then - require.Len(t, res, 1) - require.Equal(t, testCreationDate, res[0].CreatedAt.Format("2006-01-02T15:04:05.000Z0700")) -} diff --git a/pkg/resource/supported.go b/pkg/resource/supported.go deleted file mode 100644 index f139eeaa4..000000000 --- a/pkg/resource/supported.go +++ /dev/null @@ -1,225 +0,0 @@ -package resource - -import ( - "context" - - "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go-v2/service/autoscaling" - - "github.com/aws/aws-sdk-go-v2/service/cloudtrail" - "github.com/aws/aws-sdk-go-v2/service/ec2" - "github.com/aws/aws-sdk-go-v2/service/ecs" - awsls "github.com/jckuester/awsls/aws" - "github.com/pkg/errors" -) - -const ( - Ami = "aws_ami" - AutoscalingGroup = "aws_autoscaling_group" - EbsSnapshot = "aws_ebs_snapshot" - EcsCluster = "aws_ecs_cluster" - CloudTrail = "aws_cloudtrail" -) - -var ( - deleteIDs = map[string]string{ - Ami: "ImageId", - AutoscalingGroup: "AutoScalingGroupName", - EbsSnapshot: "SnapshotId", - // Note: to import a cluster, the name is used as ID - EcsCluster: "ClusterArn", - CloudTrail: "Name", - } - - // DependencyOrder is the order in which resource types should be deleted, - // since dependent resources need to be deleted before their dependencies - // (e.g. aws_subnet before aws_vpc) - DependencyOrder = map[string]int{ - "aws_lambda_function": 10100, - "aws_ecs_cluster": 10000, - AutoscalingGroup: 9990, - "aws_instance": 9980, - "aws_key_pair": 9970, - "aws_elb": 9960, - "aws_vpc_endpoint": 9950, - "aws_nat_gateway": 9940, - "aws_cloudformation_stack": 9930, - "aws_route53_zone": 9920, - "aws_efs_file_system": 9910, - "aws_launch_configuration": 9900, - "aws_eip": 9890, - "aws_internet_gateway": 9880, - "aws_subnet": 9870, - "aws_route_table": 9860, - "aws_security_group": 9850, - "aws_network_acl": 9840, - "aws_vpc": 9830, - "aws_db_instance": 9825, - "aws_iam_policy": 9820, - "aws_iam_group": 9810, - "aws_iam_user": 9800, - "aws_iam_role": 9790, - "aws_iam_instance_profile": 9780, - "aws_s3_bucket": 9750, - Ami: 9740, - "aws_ebs_volume": 9730, - EbsSnapshot: 9720, - "aws_kms_alias": 9610, - "aws_kms_key": 9600, - "aws_network_interface": 9000, - "aws_cloudwatch_log_group": 8900, - CloudTrail: 8800, - } - - // creationTimeFieldNames are a list field names that are used to find the creation date of a resource. - creationTimeFieldNames = []string{ - "LaunchTime", - "CreateTime", - "CreateDate", - "CreatedTime", - "CreationDate", - "CreationTime", - "CreationTimestamp", - "StartTime", - "InstanceCreateTime", - } -) - -func SupportedResourceType(resType string) bool { - _, found := deleteIDs[resType] - - return found -} - -func getDeleteID(resType string) (string, error) { - deleteID, found := deleteIDs[resType] - if !found { - return "", errors.Errorf("no delete ID specified for resource type: %s", resType) - } - return deleteID, nil -} - -// AWS wraps the AWS API -type AWS awsls.Client - -// RawResources lists all resources of a particular type -func (a AWS) RawResources(resType string) (interface{}, error) { - switch resType { - case Ami: - return a.amis() - case AutoscalingGroup: - return a.autoscalingGroups() - case EbsSnapshot: - return a.ebsSnapshots() - case EcsCluster: - return a.ecsClusters() - case CloudTrail: - return a.cloudTrails() - default: - return nil, errors.Errorf("unknown or unsupported resource type: %s", resType) - } -} - -func (a *AWS) ecsClusters() (interface{}, error) { - listClustersRequest := a.Ecsconn.ListClustersRequest(&ecs.ListClustersInput{}) - - var clusterARNs []string - - pg := ecs.NewListClustersPaginator(listClustersRequest) - for pg.Next(context.Background()) { - page := pg.CurrentPage() - - clusterARNs = append(clusterARNs, page.ClusterArns...) - } - - if err := pg.Err(); err != nil { - return nil, err - } - - // TODO is paginated, but not paginator API - req := a.Ecsconn.DescribeClustersRequest(&ecs.DescribeClustersInput{ - Clusters: clusterARNs, - Include: []ecs.ClusterField{"TAGS"}, - }) - - resp, err := req.Send(context.Background()) - if err != nil { - return nil, err - } - - return resp.Clusters, nil -} - -func (a *AWS) cloudTrails() (interface{}, error) { - req := a.Cloudtrailconn.DescribeTrailsRequest(&cloudtrail.DescribeTrailsInput{}) - - resp, err := req.Send(context.Background()) - if err != nil { - return nil, err - } - - return resp.TrailList, nil -} - -func (a *AWS) ebsSnapshots() (interface{}, error) { - req := a.Ec2conn.DescribeSnapshotsRequest(&ec2.DescribeSnapshotsInput{ - Filters: []ec2.Filter{ - { - Name: aws.String("owner-id"), - Values: []string{ - a.AccountID, - }, - }, - }, - }) - - var snapshots []ec2.Snapshot - - pg := ec2.NewDescribeSnapshotsPaginator(req) - for pg.Next(context.Background()) { - page := pg.CurrentPage() - - snapshots = append(snapshots, page.Snapshots...) - } - - return snapshots, nil -} - -func (a *AWS) amis() (interface{}, error) { - req := a.Ec2conn.DescribeImagesRequest(&ec2.DescribeImagesInput{ - Filters: []ec2.Filter{ - { - Name: aws.String("owner-id"), - Values: []string{ - a.AccountID, - }, - }, - }, - }) - - resp, err := req.Send(context.Background()) - if err != nil { - return nil, err - } - - return resp.Images, nil -} - -func (a *AWS) autoscalingGroups() (interface{}, error) { - req := a.Autoscalingconn.DescribeAutoScalingGroupsRequest(&autoscaling.DescribeAutoScalingGroupsInput{}) - - var autoScalingGroups []autoscaling.AutoScalingGroup - - pg := autoscaling.NewDescribeAutoScalingGroupsPaginator(req) - for pg.Next(context.Background()) { - page := pg.CurrentPage() - - autoScalingGroups = append(autoScalingGroups, page.AutoScalingGroups...) - } - - if err := pg.Err(); err != nil { - return nil, err - } - - return autoScalingGroups, nil -} diff --git a/test/autoscaling_group_test.go b/test/autoscaling_group_test.go new file mode 100644 index 000000000..22f4a6c9a --- /dev/null +++ b/test/autoscaling_group_test.go @@ -0,0 +1,105 @@ +package test + +import ( + "context" + "fmt" + "os" + "testing" + + "github.com/aws/aws-sdk-go-v2/service/autoscaling" + "github.com/gruntwork-io/terratest/modules/terraform" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestAcc_AutoscalingGroup_DeleteByID(t *testing.T) { + if testing.Short() { + t.Skip("Skipping acceptance test.") + } + + env := InitEnv(t) + + terraformDir := "./test-fixtures/autoscaling-group" + + terraformOptions := getTerraformOptions(terraformDir, env) + + defer terraform.Destroy(t, terraformOptions) + + terraform.InitAndApply(t, terraformOptions) + + id := terraform.Output(t, terraformOptions, "id") + assertAutoscalingGroupExists(t, env, id) + + writeConfigID(t, terraformDir, "aws_autoscaling_group", id) + defer os.Remove(terraformDir + "/config.yml") + + logBuffer, err := runBinary(t, terraformDir, "YES\n", "--timeout", "5m") + require.NoError(t, err) + + assertAutoscalingGroupDeleted(t, env, id) + + fmt.Println(logBuffer) +} + +func TestAcc_AutoscalingGroup_DeleteByTag(t *testing.T) { + if testing.Short() { + t.Skip("Skipping acceptance test.") + } + + env := InitEnv(t) + + terraformDir := "./test-fixtures/autoscaling-group" + + terraformOptions := getTerraformOptions(terraformDir, env) + + defer terraform.Destroy(t, terraformOptions) + + terraform.InitAndApply(t, terraformOptions) + + id := terraform.Output(t, terraformOptions, "id") + assertAutoscalingGroupExists(t, env, id) + + idTag := terraform.Output(t, terraformOptions, "id_tag") + assertAutoscalingGroupExists(t, env, idTag) + + idTags := terraform.Output(t, terraformOptions, "id_tags") + assertAutoscalingGroupExists(t, env, idTags) + + writeConfigTag(t, terraformDir, "aws_autoscaling_group") + defer os.Remove(terraformDir + "/config.yml") + + logBuffer, err := runBinary(t, terraformDir, "YES\n", "--timeout", "5m") + require.NoError(t, err) + + assertAutoscalingGroupDeleted(t, env, idTag) + assertAutoscalingGroupDeleted(t, env, idTags) + assertAutoscalingGroupExists(t, env, id) + + fmt.Println(logBuffer) +} + +func assertAutoscalingGroupExists(t *testing.T, env EnvVars, id string) { + assert.True(t, autoscalingGroupExists(t, env, id)) +} + +func assertAutoscalingGroupDeleted(t *testing.T, env EnvVars, id string) { + assert.False(t, autoscalingGroupExists(t, env, id)) +} + +func autoscalingGroupExists(t *testing.T, env EnvVars, id string) bool { + req := env.AWSClient.Autoscalingconn.DescribeAutoScalingGroupsRequest( + &autoscaling.DescribeAutoScalingGroupsInput{ + AutoScalingGroupNames: []string{id}, + }) + + resp, err := req.Send(context.Background()) + if err != nil { + t.Fatal(err) + } + + if len(resp.AutoScalingGroups) == 0 { + return false + } + + return true +} diff --git a/test/helper_test.go b/test/helper_test.go index 1112c2094..cf6b2fd10 100644 --- a/test/helper_test.go +++ b/test/helper_test.go @@ -34,15 +34,8 @@ type EnvVars struct { func InitEnv(t *testing.T) EnvVars { t.Helper() - profile := os.Getenv("AWS_PROFILE") - if profile == "" { - t.Fatal("env variable AWS_PROFILE needs to be set for tests") - } - - region := os.Getenv("AWS_DEFAULT_REGION") - if region == "" { - t.Fatal("env variable AWS_DEFAULT_REGION needs to be set for tests") - } + profile := getEnvOrDefault(t, "AWS_PROFILE", "myaccount1") + region := getEnvOrDefault(t, "AWS_DEFAULT_REGION", "us-west-2") client, err := awsls.NewClient( external.WithSharedConfigProfile(profile), @@ -56,6 +49,17 @@ func InitEnv(t *testing.T) EnvVars { } } +func getEnvOrDefault(t *testing.T, envName, defaultValue string) string { + varValue := os.Getenv(envName) + if varValue == "" { + varValue = defaultValue + + t.Logf("env %s not set, therefore using the following default value: %s", + envName, defaultValue) + } + return varValue +} + func runBinary(t *testing.T, terraformDir, userInput string, flags ...string) (*bytes.Buffer, error) { defer gexec.CleanupBuildArtifacts() diff --git a/test/test-fixtures/autoscaling-group/main.tf b/test/test-fixtures/autoscaling-group/main.tf new file mode 100644 index 000000000..384a66208 --- /dev/null +++ b/test/test-fixtures/autoscaling-group/main.tf @@ -0,0 +1,86 @@ +provider "aws" { + version = "~> 2.0" + + profile = var.profile + region = var.region +} + +terraform { + # The configuration for this backend will be filled in by Terragrunt + backend "s3" { + } +} + +locals { + az = "${var.region}c" +} + +resource "aws_autoscaling_group" "test" { + availability_zones = [local.az] + desired_capacity = 1 + max_size = 1 + min_size = 1 + + launch_template { + id = aws_launch_template.test.id + version = "$Latest" + } +} + +resource "aws_autoscaling_group" "test_tag" { + availability_zones = [local.az] + desired_capacity = 1 + max_size = 1 + min_size = 1 + + launch_template { + id = aws_launch_template.test.id + version = "$Latest" + } + + tag { + key = "awsweeper" + value = "test-acc" + propagate_at_launch = true + } +} + +resource "aws_autoscaling_group" "test_tags" { + availability_zones = [local.az] + desired_capacity = 1 + max_size = 1 + min_size = 1 + + launch_template { + id = aws_launch_template.test.id + version = "$Latest" + } + + tags = [{ + key = "awsweeper" + value = "test-acc" + propagate_at_launch = true + }] +} + +resource "aws_launch_template" "test" { + name_prefix = "foobar" + image_id = data.aws_ami.amazon_linux_2.image_id + instance_type = "t2.micro" +} + +data "aws_ami" "amazon_linux_2" { + most_recent = true + + owners = ["amazon"] + + filter { + name = "architecture" + values = ["x86_64"] + } + + filter { + name = "name" + values = ["amzn2-ami-hvm*"] + } +} \ No newline at end of file diff --git a/test/test-fixtures/autoscaling-group/outputs.tf b/test/test-fixtures/autoscaling-group/outputs.tf new file mode 100644 index 000000000..4da262c46 --- /dev/null +++ b/test/test-fixtures/autoscaling-group/outputs.tf @@ -0,0 +1,13 @@ +output "id" { + value = aws_autoscaling_group.test.id +} + +output "id_tag" { + description = "ASG using the tag attribute for tagging" + value = aws_autoscaling_group.test_tag.id +} + +output "id_tags" { + description = "ASG using the tags attribute for tagging" + value = aws_autoscaling_group.test_tags.id +} \ No newline at end of file diff --git a/test/test-fixtures/autoscaling-group/variables.tf b/test/test-fixtures/autoscaling-group/variables.tf new file mode 100644 index 000000000..c57de9dd7 --- /dev/null +++ b/test/test-fixtures/autoscaling-group/variables.tf @@ -0,0 +1,11 @@ +variable "profile" { + description = "The named profile for the AWS account that will be deployed to" +} + +variable "region" { + description = "The AWS region to deploy to" +} + +variable "name" { + description = "The name of test" +} diff --git a/test/test-fixtures/elb/elb.tf b/test/test-fixtures/elb/elb.tf index d8601e5ea..12095471d 100644 --- a/test/test-fixtures/elb/elb.tf +++ b/test/test-fixtures/elb/elb.tf @@ -30,5 +30,5 @@ resource "aws_elb" "test" { } resource "aws_default_subnet" "test" { - availability_zone = "us-west-2c" + availability_zone = "${var.region}c" } \ No newline at end of file