diff --git a/aws/data_source_aws_rds_engine_version.go b/aws/data_source_aws_rds_engine_version.go new file mode 100644 index 00000000000..58705995acf --- /dev/null +++ b/aws/data_source_aws_rds_engine_version.go @@ -0,0 +1,251 @@ +package aws + +import ( + "fmt" + "log" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/rds" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func dataSourceAwsRdsEngineVersion() *schema.Resource { + return &schema.Resource{ + Read: dataSourceAwsRdsEngineVersionRead, + Schema: map[string]*schema.Schema{ + "default_character_set": { + Type: schema.TypeString, + Computed: true, + }, + + "engine": { + Type: schema.TypeString, + Required: true, + }, + + "engine_description": { + Type: schema.TypeString, + Computed: true, + }, + + "exportable_log_types": { + Type: schema.TypeSet, + Elem: &schema.Schema{Type: schema.TypeString}, + Computed: true, + Set: schema.HashString, + }, + + "parameter_group_family": { + Type: schema.TypeString, + Computed: true, + Optional: true, + }, + + "preferred_versions": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + ConflictsWith: []string{"version"}, + }, + + "status": { + Type: schema.TypeString, + Computed: true, + }, + + "supported_character_sets": { + Type: schema.TypeSet, + Elem: &schema.Schema{Type: schema.TypeString}, + Computed: true, + Set: schema.HashString, + }, + + "supported_feature_names": { + Type: schema.TypeSet, + Elem: &schema.Schema{Type: schema.TypeString}, + Computed: true, + Set: schema.HashString, + }, + + "supported_modes": { + Type: schema.TypeSet, + Elem: &schema.Schema{Type: schema.TypeString}, + Computed: true, + Set: schema.HashString, + }, + + "supported_timezones": { + Type: schema.TypeSet, + Elem: &schema.Schema{Type: schema.TypeString}, + Computed: true, + Set: schema.HashString, + }, + + "supports_global_databases": { + Type: schema.TypeBool, + Computed: true, + }, + + "supports_log_exports_to_cloudwatch": { + Type: schema.TypeBool, + Computed: true, + }, + + "supports_parallel_query": { + Type: schema.TypeBool, + Computed: true, + }, + + "supports_read_replica": { + Type: schema.TypeBool, + Computed: true, + }, + + "valid_upgrade_targets": { + Type: schema.TypeSet, + Elem: &schema.Schema{Type: schema.TypeString}, + Computed: true, + Set: schema.HashString, + }, + + "version": { + Type: schema.TypeString, + Computed: true, + Optional: true, + ConflictsWith: []string{"preferred_versions"}, + }, + + "version_description": { + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func dataSourceAwsRdsEngineVersionRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).rdsconn + + input := &rds.DescribeDBEngineVersionsInput{ + ListSupportedCharacterSets: aws.Bool(true), + ListSupportedTimezones: aws.Bool(true), + } + + if v, ok := d.GetOk("engine"); ok { + input.Engine = aws.String(v.(string)) + } + + if v, ok := d.GetOk("parameter_group_family"); ok { + input.DBParameterGroupFamily = aws.String(v.(string)) + } + + if v, ok := d.GetOk("version"); ok { + input.EngineVersion = aws.String(v.(string)) + } + + if _, ok := d.GetOk("version"); !ok { + if _, ok := d.GetOk("preferred_versions"); !ok { + input.DefaultOnly = aws.Bool(true) + } + } + + log.Printf("[DEBUG] Reading RDS engine versions: %v", input) + var engineVersions []*rds.DBEngineVersion + + err := conn.DescribeDBEngineVersionsPages(input, func(resp *rds.DescribeDBEngineVersionsOutput, lastPage bool) bool { + for _, engineVersion := range resp.DBEngineVersions { + if engineVersion == nil { + continue + } + + engineVersions = append(engineVersions, engineVersion) + } + return !lastPage + }) + + if err != nil { + return fmt.Errorf("error reading RDS engine versions: %w", err) + } + + if len(engineVersions) == 0 { + return fmt.Errorf("no RDS engine versions found") + } + + // preferred versions + var found *rds.DBEngineVersion + if l := d.Get("preferred_versions").([]interface{}); len(l) > 0 { + for _, elem := range l { + preferredVersion, ok := elem.(string) + + if !ok { + continue + } + + for _, engineVersion := range engineVersions { + if preferredVersion == aws.StringValue(engineVersion.EngineVersion) { + found = engineVersion + break + } + } + + if found != nil { + break + } + } + } + + if found == nil && len(engineVersions) > 1 { + return fmt.Errorf("multiple RDS engine versions (%v) match the criteria", engineVersions) + } + + if found == nil && len(engineVersions) == 1 { + found = engineVersions[0] + } + + if found == nil { + return fmt.Errorf("no RDS engine versions match the criteria") + } + + d.SetId(aws.StringValue(found.EngineVersion)) + + if found.DefaultCharacterSet != nil { + d.Set("default_character_set", found.DefaultCharacterSet.CharacterSetName) + } + + d.Set("engine", found.Engine) + d.Set("engine_description", found.DBEngineDescription) + d.Set("exportable_log_types", found.ExportableLogTypes) + d.Set("parameter_group_family", found.DBParameterGroupFamily) + d.Set("status", found.Status) + + var characterSets []string + for _, cs := range found.SupportedCharacterSets { + characterSets = append(characterSets, aws.StringValue(cs.CharacterSetName)) + } + d.Set("supported_character_sets", characterSets) + + d.Set("supported_feature_names", found.SupportedFeatureNames) + d.Set("supported_modes", found.SupportedEngineModes) + + var timezones []string + for _, tz := range found.SupportedTimezones { + timezones = append(timezones, aws.StringValue(tz.TimezoneName)) + } + d.Set("supported_timezones", timezones) + + d.Set("supports_global_databases", found.SupportsGlobalDatabases) + d.Set("supports_log_exports_to_cloudwatch", found.SupportsLogExportsToCloudwatchLogs) + d.Set("supports_parallel_query", found.SupportsParallelQuery) + d.Set("supports_read_replica", found.SupportsReadReplica) + + var upgradeTargets []string + for _, ut := range found.ValidUpgradeTarget { + upgradeTargets = append(upgradeTargets, aws.StringValue(ut.EngineVersion)) + } + d.Set("valid_upgrade_targets", upgradeTargets) + + d.Set("version", found.EngineVersion) + d.Set("version_description", found.DBEngineVersionDescription) + + return nil +} diff --git a/aws/data_source_aws_rds_engine_version_test.go b/aws/data_source_aws_rds_engine_version_test.go new file mode 100644 index 00000000000..b7c0c1814df --- /dev/null +++ b/aws/data_source_aws_rds_engine_version_test.go @@ -0,0 +1,157 @@ +package aws + +import ( + "fmt" + "regexp" + "testing" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/rds" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +func TestAccAWSRDSEngineVersionDataSource_basic(t *testing.T) { + dataSourceName := "data.aws_rds_engine_version.test" + engine := "oracle-ee" + version := "19.0.0.0.ru-2020-07.rur-2020-07.r1" + paramGroup := "oracle-ee-19" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccAWSRDSEngineVersionPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: nil, + Steps: []resource.TestStep{ + { + Config: testAccAWSRDSEngineVersionDataSourceBasicConfig(engine, version, paramGroup), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(dataSourceName, "engine", engine), + resource.TestCheckResourceAttr(dataSourceName, "version", version), + resource.TestCheckResourceAttr(dataSourceName, "parameter_group_family", paramGroup), + + resource.TestCheckResourceAttrSet(dataSourceName, "default_character_set"), + resource.TestCheckResourceAttrSet(dataSourceName, "engine_description"), + resource.TestMatchResourceAttr(dataSourceName, "exportable_log_types.#", regexp.MustCompile(`^[1-9][0-9]*`)), + resource.TestCheckResourceAttrSet(dataSourceName, "status"), + resource.TestMatchResourceAttr(dataSourceName, "supported_character_sets.#", regexp.MustCompile(`^[1-9][0-9]*`)), + resource.TestMatchResourceAttr(dataSourceName, "supported_feature_names.#", regexp.MustCompile(`^[1-9][0-9]*`)), + resource.TestMatchResourceAttr(dataSourceName, "supported_modes.#", regexp.MustCompile(`^[0-9]*`)), + resource.TestMatchResourceAttr(dataSourceName, "supported_timezones.#", regexp.MustCompile(`^[0-9]*`)), + resource.TestCheckResourceAttrSet(dataSourceName, "supports_global_databases"), + resource.TestCheckResourceAttrSet(dataSourceName, "supports_log_exports_to_cloudwatch"), + resource.TestCheckResourceAttrSet(dataSourceName, "supports_parallel_query"), + resource.TestCheckResourceAttrSet(dataSourceName, "supports_read_replica"), + resource.TestCheckResourceAttrSet(dataSourceName, "version_description"), + ), + }, + }, + }) +} + +func TestAccAWSRDSEngineVersionDataSource_upgradeTargets(t *testing.T) { + dataSourceName := "data.aws_rds_engine_version.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccAWSRDSEngineVersionPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: nil, + Steps: []resource.TestStep{ + { + Config: testAccAWSRDSEngineVersionDataSourceUpgradeTargetsConfig(), + Check: resource.ComposeTestCheckFunc( + resource.TestMatchResourceAttr(dataSourceName, "valid_upgrade_targets.#", regexp.MustCompile(`^[1-9][0-9]*`)), + ), + }, + }, + }) +} + +func TestAccAWSRDSEngineVersionDataSource_preferred(t *testing.T) { + dataSourceName := "data.aws_rds_engine_version.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccAWSRDSEngineVersionPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: nil, + Steps: []resource.TestStep{ + { + Config: testAccAWSRDSEngineVersionDataSourcePreferredConfig(), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(dataSourceName, "version", "5.7.19"), + ), + }, + }, + }) +} + +func TestAccAWSRDSEngineVersionDataSource_defaultOnly(t *testing.T) { + dataSourceName := "data.aws_rds_engine_version.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccAWSRDSEngineVersionPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: nil, + Steps: []resource.TestStep{ + { + Config: testAccAWSRDSEngineVersionDataSourceDefaultOnlyConfig(), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(dataSourceName, "version"), + ), + }, + }, + }) +} + +func testAccAWSRDSEngineVersionPreCheck(t *testing.T) { + conn := testAccProvider.Meta().(*AWSClient).rdsconn + + input := &rds.DescribeDBEngineVersionsInput{ + Engine: aws.String("mysql"), + DefaultOnly: aws.Bool(true), + } + + _, err := conn.DescribeDBEngineVersions(input) + + if testAccPreCheckSkipError(err) { + t.Skipf("skipping acceptance testing: %s", err) + } + + if err != nil { + t.Fatalf("unexpected PreCheck error: %s", err) + } +} + +func testAccAWSRDSEngineVersionDataSourceBasicConfig(engine, version, paramGroup string) string { + return fmt.Sprintf(` +data "aws_rds_engine_version" "test" { + engine = %q + version = %q + parameter_group_family = %q +} +`, engine, version, paramGroup) +} + +func testAccAWSRDSEngineVersionDataSourceUpgradeTargetsConfig() string { + return fmt.Sprintf(` +data "aws_rds_engine_version" "test" { + engine = "mysql" + version = "5.7.17" +} +`) +} + +func testAccAWSRDSEngineVersionDataSourcePreferredConfig() string { + return fmt.Sprintf(` +data "aws_rds_engine_version" "test" { + engine = "mysql" + preferred_versions = ["85.9.12", "5.7.19", "5.7.17"] +} +`) +} + +func testAccAWSRDSEngineVersionDataSourceDefaultOnlyConfig() string { + return fmt.Sprintf(` +data "aws_rds_engine_version" "test" { + engine = "mysql" +} +`) +} diff --git a/aws/provider.go b/aws/provider.go index a291cdc379c..f6ce3471f96 100644 --- a/aws/provider.go +++ b/aws/provider.go @@ -304,6 +304,7 @@ func Provider() *schema.Provider { "aws_qldb_ledger": dataSourceAwsQLDBLedger(), "aws_ram_resource_share": dataSourceAwsRamResourceShare(), "aws_rds_cluster": dataSourceAwsRdsCluster(), + "aws_rds_engine_version": dataSourceAwsRdsEngineVersion(), "aws_rds_orderable_db_instance": dataSourceAwsRdsOrderableDbInstance(), "aws_redshift_cluster": dataSourceAwsRedshiftCluster(), "aws_redshift_service_account": dataSourceAwsRedshiftServiceAccount(), diff --git a/website/docs/d/rds_engine_version.markdown b/website/docs/d/rds_engine_version.markdown new file mode 100644 index 00000000000..5b2caa7c590 --- /dev/null +++ b/website/docs/d/rds_engine_version.markdown @@ -0,0 +1,48 @@ +--- +subcategory: "RDS" +layout: "aws" +page_title: "AWS: aws_rds_engine_version" +description: |- + Information about an RDS engine version. +--- + +# Data Source: aws_rds_engine_version + +Information about an RDS engine version. + +## Example Usage + +```hcl +data "aws_rds_engine_version" "test" { + engine = "mysql" + preferred_versions = ["5.7.42", "5.7.19", "5.7.17"] +} +``` + +## Argument Reference + +The following arguments are supported: + +* `engine` - (Required) DB engine. Engine values include `aurora`, `aurora-mysql`, `aurora-postgresql`, `docdb`, `mariadb`, `mysql`, `neptune`, `oracle-ee`, `oracle-se`, `oracle-se1`, `oracle-se2`, `postgres`, `sqlserver-ee`, `sqlserver-ex`, `sqlserver-se`, and `sqlserver-web`. +* `parameter_group_family` - (Optional) The name of a specific DB parameter group family. Examples of parameter group families are `mysql8.0`, `mariadb10.4`, and `postgres12`. +* `preferred_versions` - (Optional) Ordered list of preferred engine versions. The first match in this list will be returned. If no preferred matches are found and the original search returned more than one result, an error is returned. If both the `version` and `preferred_versions` arguments are not configured, the data source will return the default version for the engine. +* `version` - (Optional) Version of the DB engine. For example, `5.7.22`, `10.1.34`, and `12.3`. If both the `version` and `preferred_versions` arguments are not configured, the data source will return the default version for the engine. + +## Attribute Reference + +In addition to all arguments above, the following attributes are exported: + +* `default_character_set` - The default character set for new instances of this engine version. +* `engine_description` - The description of the database engine. +* `exportable_log_types` - Set of log types that the database engine has available for export to CloudWatch Logs. +* `status` - The status of the DB engine version, either available or deprecated. +* `supported_character_sets` - Set of the character sets supported by this engine. +* `supported_feature_names` - Set of features supported by the DB engine. +* `supported_modes` - Set of the supported DB engine modes. +* `supported_timezones` - Set of the time zones supported by this engine. +* `supports_global_databases` - Indicates whether you can use Aurora global databases with a specific DB engine version. +* `supports_log_exports_to_cloudwatch` - Indicates whether the engine version supports exporting the log types specified by `exportable_log_types` to CloudWatch Logs. +* `supports_parallel_query` - Indicates whether you can use Aurora parallel query with a specific DB engine version. +* `supports_read_replica` - Indicates whether the database engine version supports read replicas. +* `valid_upgrade_targets` - Set of engine versions that this database engine version can be upgraded to. +* `version_description` - The description of the database engine version.