From 06808087f061075cfb739a8e51e61fc30c091687 Mon Sep 17 00:00:00 2001 From: "jsakyle@gmail.com" Date: Mon, 22 Feb 2021 20:54:32 -0500 Subject: [PATCH 1/8] initial changes for getting valid rds instance --- examples/terraform-aws-rds-example/main.tf | 2 +- .../terraform-aws-rds-example/variables.tf | 6 ++ modules/aws/errors.go | 11 ++++ modules/aws/rds.go | 65 +++++++++++++++++++ 4 files changed, 83 insertions(+), 1 deletion(-) diff --git a/examples/terraform-aws-rds-example/main.tf b/examples/terraform-aws-rds-example/main.tf index 0ffdccc70..35d7cb228 100644 --- a/examples/terraform-aws-rds-example/main.tf +++ b/examples/terraform-aws-rds-example/main.tf @@ -105,7 +105,7 @@ resource "aws_db_instance" "example" { name = var.database_name username = var.username password = var.password - instance_class = "db.t2.micro" + instance_class = var.instance_class allocated_storage = var.allocated_storage skip_final_snapshot = true license_model = var.license_model diff --git a/examples/terraform-aws-rds-example/variables.tf b/examples/terraform-aws-rds-example/variables.tf index 6c15af6a3..94bf7e62e 100644 --- a/examples/terraform-aws-rds-example/variables.tf +++ b/examples/terraform-aws-rds-example/variables.tf @@ -80,3 +80,9 @@ variable "license_model" { default = "general-public-license" } +variable "instance_class" { + description = "Instance class to be used to run the database" + type = string + default = "db.t2.micro" +} + diff --git a/modules/aws/errors.go b/modules/aws/errors.go index 7f926dbc4..fd0ac174d 100644 --- a/modules/aws/errors.go +++ b/modules/aws/errors.go @@ -113,3 +113,14 @@ func (err NoInstanceTypeError) Error() string { err.Azs, ) } + +type NoRdsInstanceTypeError struct { + InstanceTypeOptions []string +} + +func (err NoRdsInstanceTypeError) Error() string { + return fmt.Sprintf( + "None of the given RDS instance types (%v) is available in this region.", + err.InstanceTypeOptions, + ) +} diff --git a/modules/aws/rds.go b/modules/aws/rds.go index e0aad284c..0b1198ff8 100644 --- a/modules/aws/rds.go +++ b/modules/aws/rds.go @@ -8,6 +8,7 @@ import ( "github.com/aws/aws-sdk-go/service/rds" _ "github.com/go-sql-driver/mysql" "github.com/gruntwork-io/terratest/modules/testing" + "github.com/stretchr/testify/require" ) // GetAddressOfRdsInstance gets the address of the given RDS Instance in the given region. @@ -217,6 +218,70 @@ func NewRdsClientE(t testing.TestingT, region string) (*rds.RDS, error) { return rds.New(sess), nil } +func GetRecommendedRdsInstanceType(t testing.TestingT, region string, engine string, instanceTypeOptions []string) string { + out, err := GetRecommendedRdsInstanceTypeE(t, region, engine, instanceTypeOptions) + require.NoError(t, err) + return out +} + +func GetRecommendedRdsInstanceTypeE(t testing.TestingT, region string, engine string, instanceTypeOptions []string) (string, error) { + client, err := NewRdsClientE(t, region) + if err != nil { + return "", err + } + return GetRecommendedRdsInstanceTypeWithClientE(t, client, engine, instanceTypeOptions) +} + +func GetRecommendedRdsInstanceTypeWithClientE(t testing.TestingT, rdsClient *rds.RDS, engine string, instanceTypeOptions []string) (string, error) { + instanceTypeOfferings, err := getRdsInstanceTypeOfferingsE(rdsClient, engine) + if err != nil { + return "", err + } + + return pickRecommendedRdsInstanceTypeE(instanceTypeOfferings, instanceTypeOptions) +} + +// getInstanceTypeOfferingsE returns the instance types from the given list that are available in the region configured +// in the given EC2 client +func getRdsInstanceTypeOfferingsE(client *rds.RDS, engine string) ([]*rds.OrderableDBInstanceOption, error) { + input := rds.DescribeOrderableDBInstanceOptionsInput{ + Engine: aws.String(engine), + } + + out, err := client.DescribeOrderableDBInstanceOptions(&input) + if err != nil { + return nil, err + } + + return out.OrderableDBInstanceOptions, nil +} + +func pickRecommendedRdsInstanceTypeE(instanceTypeOfferings []*rds.OrderableDBInstanceOption, instanceTypeOptions []string) (string, error) { + for _, instanceType := range instanceTypeOptions { + if instanceTypeExistsInRegion(instanceType, instanceTypeOfferings) { + return instanceType, nil + } + } + + return "", NoRdsInstanceTypeError{InstanceTypeOptions: instanceTypeOptions} +} + +func instanceTypeExistsInRegion(instanceType string, instanceTypeOfferings []*rds.OrderableDBInstanceOption) bool { + if len(instanceTypeOfferings) == 0 { + return false + } + + for _, instanceTypeOffering := range instanceTypeOfferings { + if aws.String(instanceTypeOffering.DBInstanceClass) == instanceType { + return true + } + } + + return false +} + +// Notes - Use https://docs.aws.amazon.com/sdk-for-go/api/service/rds/#RDS.DescribeOrderableDBInstanceOptions + // ParameterForDbInstanceNotFound is an error that occurs when the parameter group specified is not found for the DB instance type ParameterForDbInstanceNotFound struct { ParameterName string From 778206c73d2ea56e647bd4bd4a3af0552e269f68 Mon Sep 17 00:00:00 2001 From: "jsakyle@gmail.com" Date: Sun, 28 Feb 2021 23:16:37 -0500 Subject: [PATCH 2/8] fleshed out the implementation --- modules/aws/errors.go | 4 ++- modules/aws/rds.go | 63 ++++++++++++++++++++----------------------- 2 files changed, 32 insertions(+), 35 deletions(-) diff --git a/modules/aws/errors.go b/modules/aws/errors.go index fd0ac174d..137a7b091 100644 --- a/modules/aws/errors.go +++ b/modules/aws/errors.go @@ -116,11 +116,13 @@ func (err NoInstanceTypeError) Error() string { type NoRdsInstanceTypeError struct { InstanceTypeOptions []string + DatabaseEngine string } func (err NoRdsInstanceTypeError) Error() string { return fmt.Sprintf( - "None of the given RDS instance types (%v) is available in this region.", + "None of the given RDS instance types (%v) is available in this region for database engine (%v).", err.InstanceTypeOptions, + err.DatabaseEngine, ) } diff --git a/modules/aws/rds.go b/modules/aws/rds.go index 0b1198ff8..8bcaa8d84 100644 --- a/modules/aws/rds.go +++ b/modules/aws/rds.go @@ -218,12 +218,18 @@ func NewRdsClientE(t testing.TestingT, region string) (*rds.RDS, error) { return rds.New(sess), nil } +// GetRecommendedRdsInstanceType takes in a list of RDS instance types (e.g., "db.t2.micro", "db.t3.micro") and returns the +// first instance type in the list that is available in the given region and for the given database engine type. +// If none of the instances provided are avaiable for your combination of region and database engine, this function will exit with an error. func GetRecommendedRdsInstanceType(t testing.TestingT, region string, engine string, instanceTypeOptions []string) string { out, err := GetRecommendedRdsInstanceTypeE(t, region, engine, instanceTypeOptions) require.NoError(t, err) return out } +// GetRecommendedRdsInstanceTypeE takes in a list of RDS instance types (e.g., "db.t2.micro", "db.t3.micro") and returns the +// first instance type in the list that is available in the given region and for the given database engine type. +// If none of the instances provided are avaiable for your combination of region and database engine, this function will exit with an error. func GetRecommendedRdsInstanceTypeE(t testing.TestingT, region string, engine string, instanceTypeOptions []string) (string, error) { client, err := NewRdsClientE(t, region) if err != nil { @@ -232,56 +238,45 @@ func GetRecommendedRdsInstanceTypeE(t testing.TestingT, region string, engine st return GetRecommendedRdsInstanceTypeWithClientE(t, client, engine, instanceTypeOptions) } +// GetRecommendedRdsInstanceTypeWithClientE takes in a list of RDS instance types (e.g., "db.t2.micro", "db.t3.micro") and returns the +// first instance type in the list that is available in the given region and for the given database engine type. +// If none of the instances provided are avaiable for your combination of region and database engine, this function will exit with an error. +// This function expects an authenticated RDS client from the AWS SDK Go library. func GetRecommendedRdsInstanceTypeWithClientE(t testing.TestingT, rdsClient *rds.RDS, engine string, instanceTypeOptions []string) (string, error) { - instanceTypeOfferings, err := getRdsInstanceTypeOfferingsE(rdsClient, engine) - if err != nil { - return "", err + for _, instanceTypeOption := range instanceTypeOptions { + instanceTypeExists, err := instanceTypeExistsForEngineAndRegionE(rdsClient, engine, instanceTypeOption) + if err != nil { + return "", err + } + + if instanceTypeExists { + return instanceTypeOption, nil + } } - return pickRecommendedRdsInstanceTypeE(instanceTypeOfferings, instanceTypeOptions) + return "", NoRdsInstanceTypeError{InstanceTypeOptions: instanceTypeOptions, DatabaseEngine: engine} } -// getInstanceTypeOfferingsE returns the instance types from the given list that are available in the region configured -// in the given EC2 client -func getRdsInstanceTypeOfferingsE(client *rds.RDS, engine string) ([]*rds.OrderableDBInstanceOption, error) { +// instanceTypeExistsForEngineAndRegionE returns a boolean that represents whether the provided instance type (e.g. db.t2.micro) exists for the given region and db engine type +// This function will return an error if the RDS AWS SDK call fails. +func instanceTypeExistsForEngineAndRegionE(client *rds.RDS, engine string, instanceType string) (bool, error) { input := rds.DescribeOrderableDBInstanceOptionsInput{ - Engine: aws.String(engine), + Engine: aws.String(engine), + DBInstanceClass: aws.String(instanceType), } out, err := client.DescribeOrderableDBInstanceOptions(&input) if err != nil { - return nil, err + return false, err } - return out.OrderableDBInstanceOptions, nil -} - -func pickRecommendedRdsInstanceTypeE(instanceTypeOfferings []*rds.OrderableDBInstanceOption, instanceTypeOptions []string) (string, error) { - for _, instanceType := range instanceTypeOptions { - if instanceTypeExistsInRegion(instanceType, instanceTypeOfferings) { - return instanceType, nil - } + if len(out.OrderableDBInstanceOptions) > 0 { + return true, nil } - return "", NoRdsInstanceTypeError{InstanceTypeOptions: instanceTypeOptions} + return false, nil } -func instanceTypeExistsInRegion(instanceType string, instanceTypeOfferings []*rds.OrderableDBInstanceOption) bool { - if len(instanceTypeOfferings) == 0 { - return false - } - - for _, instanceTypeOffering := range instanceTypeOfferings { - if aws.String(instanceTypeOffering.DBInstanceClass) == instanceType { - return true - } - } - - return false -} - -// Notes - Use https://docs.aws.amazon.com/sdk-for-go/api/service/rds/#RDS.DescribeOrderableDBInstanceOptions - // ParameterForDbInstanceNotFound is an error that occurs when the parameter group specified is not found for the DB instance type ParameterForDbInstanceNotFound struct { ParameterName string From 96ae641c4f611e029acb83254cdcdfb7b8e95348 Mon Sep 17 00:00:00 2001 From: "jsakyle@gmail.com" Date: Mon, 1 Mar 2021 21:33:52 -0500 Subject: [PATCH 3/8] finalizing implementation and tests --- modules/aws/rds_test.go | 124 +++++++++++++++++++++++++ test/terraform_aws_rds_example_test.go | 2 + 2 files changed, 126 insertions(+) create mode 100644 modules/aws/rds_test.go diff --git a/modules/aws/rds_test.go b/modules/aws/rds_test.go new file mode 100644 index 000000000..642b01621 --- /dev/null +++ b/modules/aws/rds_test.go @@ -0,0 +1,124 @@ +package aws + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestGetRecommendedRdsInstanceType(t *testing.T) { + type TestingScenerios struct { + name string + region string + databaseEngine string + instanceTypes []string + expected string + } +} + +func TestGetRecommendedRdsInstanceTypeHappyPath(t *testing.T) { + type TestingScenerios struct { + name string + region string + databaseEngine string + instanceTypes []string + expected string + } + + testingScenerios := []TestingScenerios{ + { + name: "US region, mysql, first offering available", + region: "us-east-2", + databaseEngine: "mysql", + instanceTypes: []string{"db.t2.micro", "db.t3.micro"}, + expected: "db.t2.micro", + }, + { + name: "EU region, postgres, 2nd offering available based on region", + region: "eu-north-1", + databaseEngine: "postgres", + instanceTypes: []string{"db.t2.micro", "db.m5.large"}, + expected: "db.m5.large", + }, + { + name: "US region, oracle-ee, 2nd offering available based on db type", + region: "us-west-2", + databaseEngine: "oracle-ee", + instanceTypes: []string{"db.m5d.xlarge", "db.m5.large"}, + expected: "db.m5.large", + }, + } + + for _, scenerio := range testingScenerios { + scenerio := scenerio + + t.Run(scenerio.name, func(t *testing.T) { + t.Parallel() + + actual, err := GetRecommendedRdsInstanceTypeE(t, scenerio.region, scenerio.databaseEngine, scenerio.instanceTypes) + assert.NoError(t, err) + assert.Equal(t, scenerio.expected, actual) + }) + } +} + +func TestGetRecommendedRdsInstanceTypeErrors(t *testing.T) { + type TestingScenerios struct { + name string + region string + databaseEngine string + instanceTypes []string + } + + testingScenerios := []TestingScenerios{ + { + name: "All empty", + region: "", + databaseEngine: "", + instanceTypes: nil, + }, + { + name: "No engine or instance type", + region: "us-east-2", + databaseEngine: "", + instanceTypes: nil, + }, + { + name: "No instance types", + region: "us-east-2", + databaseEngine: "mysql", + instanceTypes: nil, + }, + { + name: "Invalid instance types", + region: "us-east-2", + databaseEngine: "mysql", + instanceTypes: []string{"garbage"}, + }, + { + name: "Region has no instance type available", + region: "eu-north-1", + databaseEngine: "mysql", + instanceTypes: []string{"db.t2.micro"}, + }, + { + name: "No instance type available for engine", + region: "us-east-1", + databaseEngine: "oracle-ee", + instanceTypes: []string{"db.r5d.large"}, + }, + } + + for _, scenerio := range testingScenerios { + scenerio := scenerio + + t.Run(scenerio.name, func(t *testing.T) { + t.Parallel() + + _, err := GetRecommendedRdsInstanceTypeE(t, scenerio.region, scenerio.databaseEngine, scenerio.instanceTypes) + fmt.Println(err) + assert.EqualError(t, err, NoRdsInstanceTypeError{InstanceTypeOptions: scenerio.instanceTypes, DatabaseEngine: scenerio.databaseEngine}.Error()) + }) + } +} diff --git a/test/terraform_aws_rds_example_test.go b/test/terraform_aws_rds_example_test.go index bd6f1a10a..542c39d3a 100644 --- a/test/terraform_aws_rds_example_test.go +++ b/test/terraform_aws_rds_example_test.go @@ -24,6 +24,7 @@ func TestTerraformAwsRdsExample(t *testing.T) { password := "password" // Pick a random AWS region to test in. This helps ensure your code works in all regions. awsRegion := aws.GetRandomStableRegion(t, nil, nil) + instanceType := aws.GetRecommendedRdsInstanceType(t, awsRegion, "mysql", []string{"db.t2.micro", "db.t3.micro"}) // Construct the terraform options with default retryable errors to handle the most common retryable errors in // terraform testing. @@ -38,6 +39,7 @@ func TestTerraformAwsRdsExample(t *testing.T) { "engine_name": "mysql", "major_engine_version": "5.7", "family": "mysql5.7", + "instance_class": instanceType, "username": username, "password": password, "allocated_storage": 5, From c6968a77b380175290a1974ec14698b14976d60a Mon Sep 17 00:00:00 2001 From: "jsakyle@gmail.com" Date: Mon, 8 Mar 2021 23:20:36 -0500 Subject: [PATCH 4/8] added engine version to the function --- modules/aws/errors.go | 9 +- modules/aws/rds.go | 18 +-- modules/aws/rds_test.go | 145 ++++++++++++++----------- test/terraform_aws_rds_example_test.go | 2 +- 4 files changed, 100 insertions(+), 74 deletions(-) diff --git a/modules/aws/errors.go b/modules/aws/errors.go index 137a7b091..16c127f28 100644 --- a/modules/aws/errors.go +++ b/modules/aws/errors.go @@ -114,15 +114,18 @@ func (err NoInstanceTypeError) Error() string { ) } +// NoRdsInstanceTypeError is returned when none of the given instance types are avaiable for the region, database engine, and database engine combination given type NoRdsInstanceTypeError struct { - InstanceTypeOptions []string - DatabaseEngine string + InstanceTypeOptions []string + DatabaseEngine string + DatabaseEngineVersion string } func (err NoRdsInstanceTypeError) Error() string { return fmt.Sprintf( - "None of the given RDS instance types (%v) is available in this region for database engine (%v).", + "None of the given RDS instance types (%v) is available in this region for database engine (%v) of version (%v).", err.InstanceTypeOptions, err.DatabaseEngine, + err.DatabaseEngineVersion, ) } diff --git a/modules/aws/rds.go b/modules/aws/rds.go index 8bcaa8d84..262f98e86 100644 --- a/modules/aws/rds.go +++ b/modules/aws/rds.go @@ -221,8 +221,8 @@ func NewRdsClientE(t testing.TestingT, region string) (*rds.RDS, error) { // GetRecommendedRdsInstanceType takes in a list of RDS instance types (e.g., "db.t2.micro", "db.t3.micro") and returns the // first instance type in the list that is available in the given region and for the given database engine type. // If none of the instances provided are avaiable for your combination of region and database engine, this function will exit with an error. -func GetRecommendedRdsInstanceType(t testing.TestingT, region string, engine string, instanceTypeOptions []string) string { - out, err := GetRecommendedRdsInstanceTypeE(t, region, engine, instanceTypeOptions) +func GetRecommendedRdsInstanceType(t testing.TestingT, region string, engine string, engineVersion string, instanceTypeOptions []string) string { + out, err := GetRecommendedRdsInstanceTypeE(t, region, engine, engineVersion, instanceTypeOptions) require.NoError(t, err) return out } @@ -230,21 +230,21 @@ func GetRecommendedRdsInstanceType(t testing.TestingT, region string, engine str // GetRecommendedRdsInstanceTypeE takes in a list of RDS instance types (e.g., "db.t2.micro", "db.t3.micro") and returns the // first instance type in the list that is available in the given region and for the given database engine type. // If none of the instances provided are avaiable for your combination of region and database engine, this function will exit with an error. -func GetRecommendedRdsInstanceTypeE(t testing.TestingT, region string, engine string, instanceTypeOptions []string) (string, error) { +func GetRecommendedRdsInstanceTypeE(t testing.TestingT, region string, engine string, engineVersion string, instanceTypeOptions []string) (string, error) { client, err := NewRdsClientE(t, region) if err != nil { return "", err } - return GetRecommendedRdsInstanceTypeWithClientE(t, client, engine, instanceTypeOptions) + return GetRecommendedRdsInstanceTypeWithClientE(t, client, engine, engineVersion, instanceTypeOptions) } // GetRecommendedRdsInstanceTypeWithClientE takes in a list of RDS instance types (e.g., "db.t2.micro", "db.t3.micro") and returns the // first instance type in the list that is available in the given region and for the given database engine type. // If none of the instances provided are avaiable for your combination of region and database engine, this function will exit with an error. // This function expects an authenticated RDS client from the AWS SDK Go library. -func GetRecommendedRdsInstanceTypeWithClientE(t testing.TestingT, rdsClient *rds.RDS, engine string, instanceTypeOptions []string) (string, error) { +func GetRecommendedRdsInstanceTypeWithClientE(t testing.TestingT, rdsClient *rds.RDS, engine string, engineVersion string, instanceTypeOptions []string) (string, error) { for _, instanceTypeOption := range instanceTypeOptions { - instanceTypeExists, err := instanceTypeExistsForEngineAndRegionE(rdsClient, engine, instanceTypeOption) + instanceTypeExists, err := instanceTypeExistsForEngineAndRegionE(rdsClient, engine, engineVersion, instanceTypeOption) if err != nil { return "", err } @@ -253,15 +253,15 @@ func GetRecommendedRdsInstanceTypeWithClientE(t testing.TestingT, rdsClient *rds return instanceTypeOption, nil } } - - return "", NoRdsInstanceTypeError{InstanceTypeOptions: instanceTypeOptions, DatabaseEngine: engine} + return "", NoRdsInstanceTypeError{InstanceTypeOptions: instanceTypeOptions, DatabaseEngine: engine, DatabaseEngineVersion: engineVersion} } // instanceTypeExistsForEngineAndRegionE returns a boolean that represents whether the provided instance type (e.g. db.t2.micro) exists for the given region and db engine type // This function will return an error if the RDS AWS SDK call fails. -func instanceTypeExistsForEngineAndRegionE(client *rds.RDS, engine string, instanceType string) (bool, error) { +func instanceTypeExistsForEngineAndRegionE(client *rds.RDS, engine string, engineVersion string, instanceType string) (bool, error) { input := rds.DescribeOrderableDBInstanceOptionsInput{ Engine: aws.String(engine), + EngineVersion: aws.String(engineVersion), DBInstanceClass: aws.String(instanceType), } diff --git a/modules/aws/rds_test.go b/modules/aws/rds_test.go index 642b01621..fdf60c565 100644 --- a/modules/aws/rds_test.go +++ b/modules/aws/rds_test.go @@ -7,46 +7,48 @@ import ( "github.com/stretchr/testify/assert" ) -func TestGetRecommendedRdsInstanceType(t *testing.T) { - type TestingScenerios struct { - name string - region string - databaseEngine string - instanceTypes []string - expected string - } -} - func TestGetRecommendedRdsInstanceTypeHappyPath(t *testing.T) { type TestingScenerios struct { - name string - region string - databaseEngine string - instanceTypes []string - expected string + name string + region string + databaseEngine string + databaseEngineVersion string + instanceTypes []string + expected string } testingScenerios := []TestingScenerios{ { - name: "US region, mysql, first offering available", - region: "us-east-2", - databaseEngine: "mysql", - instanceTypes: []string{"db.t2.micro", "db.t3.micro"}, - expected: "db.t2.micro", + name: "US region, mysql, first offering available", + region: "us-east-2", + databaseEngine: "mysql", + databaseEngineVersion: "8.0.21", + instanceTypes: []string{"db.t2.micro", "db.t3.micro"}, + expected: "db.t2.micro", + }, + { + name: "EU region, postgres, 2nd offering available based on region", + region: "eu-north-1", + databaseEngine: "postgres", + databaseEngineVersion: "13.1", + instanceTypes: []string{"db.t2.micro", "db.m5.large"}, + expected: "db.m5.large", }, { - name: "EU region, postgres, 2nd offering available based on region", - region: "eu-north-1", - databaseEngine: "postgres", - instanceTypes: []string{"db.t2.micro", "db.m5.large"}, - expected: "db.m5.large", + name: "US region, oracle-ee, 2nd offering available based on db type", + region: "us-west-2", + databaseEngine: "oracle-ee", + databaseEngineVersion: "19.0.0.0.ru-2021-01.rur-2021-01.r1", + instanceTypes: []string{"db.m5d.xlarge", "db.m5.large"}, + expected: "db.m5.large", }, { - name: "US region, oracle-ee, 2nd offering available based on db type", - region: "us-west-2", - databaseEngine: "oracle-ee", - instanceTypes: []string{"db.m5d.xlarge", "db.m5.large"}, - expected: "db.m5.large", + name: "US region, oracle-ee, 2nd offering available based on db engine version", + region: "us-west-2", + databaseEngine: "oracle-ee", + databaseEngineVersion: "19.0.0.0.ru-2021-01.rur-2021-01.r1", + instanceTypes: []string{"db.t3.micro", "db.t3.small"}, + expected: "db.t3.small", }, } @@ -56,7 +58,7 @@ func TestGetRecommendedRdsInstanceTypeHappyPath(t *testing.T) { t.Run(scenerio.name, func(t *testing.T) { t.Parallel() - actual, err := GetRecommendedRdsInstanceTypeE(t, scenerio.region, scenerio.databaseEngine, scenerio.instanceTypes) + actual, err := GetRecommendedRdsInstanceTypeE(t, scenerio.region, scenerio.databaseEngine, scenerio.databaseEngineVersion, scenerio.instanceTypes) assert.NoError(t, err) assert.Equal(t, scenerio.expected, actual) }) @@ -65,48 +67,69 @@ func TestGetRecommendedRdsInstanceTypeHappyPath(t *testing.T) { func TestGetRecommendedRdsInstanceTypeErrors(t *testing.T) { type TestingScenerios struct { - name string - region string - databaseEngine string - instanceTypes []string + name string + region string + databaseEngine string + databaseEngineVersion string + instanceTypes []string } testingScenerios := []TestingScenerios{ { - name: "All empty", - region: "", - databaseEngine: "", - instanceTypes: nil, + name: "All empty", + region: "", + databaseEngine: "", + databaseEngineVersion: "", + instanceTypes: nil, + }, + { + name: "No engine, version, or instance type", + region: "us-east-2", + databaseEngine: "", + databaseEngineVersion: "", + instanceTypes: nil, + }, + { + name: "No instance types or version", + region: "us-east-2", + databaseEngine: "mysql", + databaseEngineVersion: "", + instanceTypes: nil, }, { - name: "No engine or instance type", - region: "us-east-2", - databaseEngine: "", - instanceTypes: nil, + name: "No engine version", + region: "us-east-2", + databaseEngine: "mysql", + databaseEngineVersion: "", + instanceTypes: []string{"db.t3.small"}, }, { - name: "No instance types", - region: "us-east-2", - databaseEngine: "mysql", - instanceTypes: nil, + name: "Invalid instance types", + region: "us-east-2", + databaseEngine: "mysql", + databaseEngineVersion: "8.0.21", + instanceTypes: []string{"garbage"}, }, { - name: "Invalid instance types", - region: "us-east-2", - databaseEngine: "mysql", - instanceTypes: []string{"garbage"}, + name: "Region has no instance type available", + region: "eu-north-1", + databaseEngine: "mysql", + databaseEngineVersion: "8.0.21", + instanceTypes: []string{"db.t2.micro"}, }, { - name: "Region has no instance type available", - region: "eu-north-1", - databaseEngine: "mysql", - instanceTypes: []string{"db.t2.micro"}, + name: "No instance type available for engine", + region: "us-east-1", + databaseEngine: "oracle-ee", + databaseEngineVersion: "19.0.0.0.ru-2021-01.rur-2021-01.r1", + instanceTypes: []string{"db.r5d.large"}, }, { - name: "No instance type available for engine", - region: "us-east-1", - databaseEngine: "oracle-ee", - instanceTypes: []string{"db.r5d.large"}, + name: "No instance type available for engine version", + region: "us-east-1", + databaseEngine: "oracle-ee", + databaseEngineVersion: "19.0.0.0.ru-2021-01.rur-2021-01.r1", + instanceTypes: []string{"db.t3.micro"}, }, } @@ -116,9 +139,9 @@ func TestGetRecommendedRdsInstanceTypeErrors(t *testing.T) { t.Run(scenerio.name, func(t *testing.T) { t.Parallel() - _, err := GetRecommendedRdsInstanceTypeE(t, scenerio.region, scenerio.databaseEngine, scenerio.instanceTypes) + _, err := GetRecommendedRdsInstanceTypeE(t, scenerio.region, scenerio.databaseEngine, scenerio.databaseEngineVersion, scenerio.instanceTypes) fmt.Println(err) - assert.EqualError(t, err, NoRdsInstanceTypeError{InstanceTypeOptions: scenerio.instanceTypes, DatabaseEngine: scenerio.databaseEngine}.Error()) + assert.EqualError(t, err, NoRdsInstanceTypeError{InstanceTypeOptions: scenerio.instanceTypes, DatabaseEngine: scenerio.databaseEngine, DatabaseEngineVersion: scenerio.databaseEngineVersion}.Error()) }) } } diff --git a/test/terraform_aws_rds_example_test.go b/test/terraform_aws_rds_example_test.go index 542c39d3a..4cb422a4d 100644 --- a/test/terraform_aws_rds_example_test.go +++ b/test/terraform_aws_rds_example_test.go @@ -24,7 +24,7 @@ func TestTerraformAwsRdsExample(t *testing.T) { password := "password" // Pick a random AWS region to test in. This helps ensure your code works in all regions. awsRegion := aws.GetRandomStableRegion(t, nil, nil) - instanceType := aws.GetRecommendedRdsInstanceType(t, awsRegion, "mysql", []string{"db.t2.micro", "db.t3.micro"}) + instanceType := aws.GetRecommendedRdsInstanceType(t, awsRegion, "mysql", "5.7.21", []string{"db.t2.micro", "db.t3.micro"}) // Construct the terraform options with default retryable errors to handle the most common retryable errors in // terraform testing. From d3b87ec4dd3a5e60bfa09c099f84d0ba14e336b7 Mon Sep 17 00:00:00 2001 From: Kyle Robertson Date: Wed, 10 Mar 2021 22:01:12 -0500 Subject: [PATCH 5/8] updated comment Co-authored-by: Yevgeniy Brikman --- modules/aws/rds.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/aws/rds.go b/modules/aws/rds.go index 262f98e86..a436c47d2 100644 --- a/modules/aws/rds.go +++ b/modules/aws/rds.go @@ -229,7 +229,7 @@ func GetRecommendedRdsInstanceType(t testing.TestingT, region string, engine str // GetRecommendedRdsInstanceTypeE takes in a list of RDS instance types (e.g., "db.t2.micro", "db.t3.micro") and returns the // first instance type in the list that is available in the given region and for the given database engine type. -// If none of the instances provided are avaiable for your combination of region and database engine, this function will exit with an error. +// If none of the instances provided are avaiable for your combination of region and database engine, this function will return an error. func GetRecommendedRdsInstanceTypeE(t testing.TestingT, region string, engine string, engineVersion string, instanceTypeOptions []string) (string, error) { client, err := NewRdsClientE(t, region) if err != nil { From 4a1e34c74feeeac9f533243754a5130c189062bb Mon Sep 17 00:00:00 2001 From: Kyle Robertson Date: Wed, 10 Mar 2021 22:01:20 -0500 Subject: [PATCH 6/8] updated comment Co-authored-by: Yevgeniy Brikman --- modules/aws/rds.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/aws/rds.go b/modules/aws/rds.go index a436c47d2..25afcf56f 100644 --- a/modules/aws/rds.go +++ b/modules/aws/rds.go @@ -240,7 +240,7 @@ func GetRecommendedRdsInstanceTypeE(t testing.TestingT, region string, engine st // GetRecommendedRdsInstanceTypeWithClientE takes in a list of RDS instance types (e.g., "db.t2.micro", "db.t3.micro") and returns the // first instance type in the list that is available in the given region and for the given database engine type. -// If none of the instances provided are avaiable for your combination of region and database engine, this function will exit with an error. +// If none of the instances provided are avaiable for your combination of region and database engine, this function will return an error. // This function expects an authenticated RDS client from the AWS SDK Go library. func GetRecommendedRdsInstanceTypeWithClientE(t testing.TestingT, rdsClient *rds.RDS, engine string, engineVersion string, instanceTypeOptions []string) (string, error) { for _, instanceTypeOption := range instanceTypeOptions { From 306a832241d9ee2e62a9dd60fabbf30fab8323cf Mon Sep 17 00:00:00 2001 From: "jsakyle@gmail.com" Date: Thu, 11 Mar 2021 20:09:14 -0500 Subject: [PATCH 7/8] fixed remote_exec ec2 test --- examples/terraform-aws-rds-example/testing.tfvars | 13 +++++++++++++ examples/terraform-remote-exec-example/main.tf | 2 +- examples/terraform-remote-exec-example/variables.tf | 6 ++++++ test/terraform_remote_exec_example_test.go | 4 ++++ 4 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 examples/terraform-aws-rds-example/testing.tfvars diff --git a/examples/terraform-aws-rds-example/testing.tfvars b/examples/terraform-aws-rds-example/testing.tfvars new file mode 100644 index 000000000..4b4278ce6 --- /dev/null +++ b/examples/terraform-aws-rds-example/testing.tfvars @@ -0,0 +1,13 @@ +name = "terratest-aws-rds-example-v205cu" +engine_name = "mysql" +major_engine_version = "5.7" +family = "mysql5.7" +instance_class = "db.t2.micro" +username = "username" +password = "password" +allocated_storage = 5 +license_model = "general-public-license" +engine_version = "5.7.21" +port = 3306 +database_name = "terratest" +region = "us-east-1" \ No newline at end of file diff --git a/examples/terraform-remote-exec-example/main.tf b/examples/terraform-remote-exec-example/main.tf index 1fb56bb01..8d221844e 100644 --- a/examples/terraform-remote-exec-example/main.tf +++ b/examples/terraform-remote-exec-example/main.tf @@ -25,7 +25,7 @@ provider "aws" { resource "aws_instance" "example_public" { ami = data.aws_ami.ubuntu.id - instance_type = "t2.micro" + instance_type = var.instance_type vpc_security_group_ids = [aws_security_group.example.id] key_name = var.key_pair_name diff --git a/examples/terraform-remote-exec-example/variables.tf b/examples/terraform-remote-exec-example/variables.tf index 818a6bc7f..17c7c9b7e 100644 --- a/examples/terraform-remote-exec-example/variables.tf +++ b/examples/terraform-remote-exec-example/variables.tf @@ -45,3 +45,9 @@ variable "ssh_user" { default = "ubuntu" } +variable "instance_type" { + description = "Instance type to use for EC2 Instance" + type = string + default = "t2.micro" +} + diff --git a/test/terraform_remote_exec_example_test.go b/test/terraform_remote_exec_example_test.go index bb985e34b..91f8cfa3c 100644 --- a/test/terraform_remote_exec_example_test.go +++ b/test/terraform_remote_exec_example_test.go @@ -54,6 +54,9 @@ func TestTerraformRemoteExecExample(t *testing.T) { // Pick a random AWS region to test in. This helps ensure your code works in all regions. awsRegion := aws.GetRandomStableRegion(t, nil, nil) + // Some AWS regions are missing certain instance types, so pick an available type based on the region we picked + instanceType := aws.GetRecommendedInstanceType(t, awsRegion, []string{"t2.micro", "t3.micro"}) + // Create an EC2 KeyPair that we can use for SSH access keyPairName := fmt.Sprintf("terratest-remote-exec-example-%s", uniqueID) keyPair := aws.CreateAndImportEC2KeyPair(t, awsRegion, keyPairName) @@ -72,6 +75,7 @@ func TestTerraformRemoteExecExample(t *testing.T) { Vars: map[string]interface{}{ "aws_region": awsRegion, "instance_name": instanceName, + "instance_type": instanceType, "key_pair_name": keyPairName, }, From 5e4d2f8fe9b08b78381fd736ab47c1fb289867c8 Mon Sep 17 00:00:00 2001 From: "jsakyle@gmail.com" Date: Fri, 12 Mar 2021 08:33:28 -0500 Subject: [PATCH 8/8] see ya tfvars --- examples/terraform-aws-rds-example/testing.tfvars | 13 ------------- 1 file changed, 13 deletions(-) delete mode 100644 examples/terraform-aws-rds-example/testing.tfvars diff --git a/examples/terraform-aws-rds-example/testing.tfvars b/examples/terraform-aws-rds-example/testing.tfvars deleted file mode 100644 index 4b4278ce6..000000000 --- a/examples/terraform-aws-rds-example/testing.tfvars +++ /dev/null @@ -1,13 +0,0 @@ -name = "terratest-aws-rds-example-v205cu" -engine_name = "mysql" -major_engine_version = "5.7" -family = "mysql5.7" -instance_class = "db.t2.micro" -username = "username" -password = "password" -allocated_storage = 5 -license_model = "general-public-license" -engine_version = "5.7.21" -port = 3306 -database_name = "terratest" -region = "us-east-1" \ No newline at end of file