Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

r/rds_proxy_endpoint - new resource #18881

Merged
merged 16 commits into from
Apr 29, 2021
3 changes: 3 additions & 0 deletions .changelog/18881.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:new-resource
aws_rds_proxy_endpoint
```
35 changes: 34 additions & 1 deletion aws/internal/service/rds/finder/finder.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@ package finder
import (
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/rds"
tfrds "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/rds"
)

// DBProxyTarget returns matching DBProxyTarget.
func DBProxyTarget(conn *rds.RDS, dbProxyName string, targetGroupName string, targetType string, rdsResourceId string) (*rds.DBProxyTarget, error) {
func DBProxyTarget(conn *rds.RDS, dbProxyName, targetGroupName, targetType, rdsResourceId string) (*rds.DBProxyTarget, error) {
input := &rds.DescribeDBProxyTargetsInput{
DBProxyName: aws.String(dbProxyName),
TargetGroupName: aws.String(targetGroupName),
Expand All @@ -30,3 +31,35 @@ func DBProxyTarget(conn *rds.RDS, dbProxyName string, targetGroupName string, ta

return dbProxyTarget, err
}

// DBProxyEndpoint returns matching DBProxyEndpoint.
func DBProxyEndpoint(conn *rds.RDS, id string) (*rds.DBProxyEndpoint, error) {
dbProxyName, dbProxyEndpointName, err := tfrds.ResourceAwsDbProxyEndpointParseID(id)
if err != nil {
return nil, err
}

input := &rds.DescribeDBProxyEndpointsInput{
DBProxyName: aws.String(dbProxyName),
DBProxyEndpointName: aws.String(dbProxyEndpointName),
}
var dbProxyEndpoint *rds.DBProxyEndpoint

err = conn.DescribeDBProxyEndpointsPages(input, func(page *rds.DescribeDBProxyEndpointsOutput, lastPage bool) bool {
if page == nil {
return !lastPage
}

for _, endpoint := range page.DBProxyEndpoints {
if aws.StringValue(endpoint.DBProxyEndpointName) == dbProxyEndpointName &&
aws.StringValue(endpoint.DBProxyName) == dbProxyName {
dbProxyEndpoint = endpoint
return false
}
}

return !lastPage
})

return dbProxyEndpoint, err
}
14 changes: 14 additions & 0 deletions aws/internal/service/rds/id.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package rds

import (
"fmt"
"strings"
)

func ResourceAwsDbProxyEndpointParseID(id string) (string, string, error) {
idParts := strings.SplitN(id, "/", 2)
if len(idParts) != 2 || idParts[0] == "" || idParts[1] == "" {
return "", "", fmt.Errorf("unexpected format of ID (%s), expected db_proxy_name/db_proxy_endpoint_name", id)
}
return idParts[0], idParts[1], nil
}
24 changes: 24 additions & 0 deletions aws/internal/service/rds/waiter/status.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/rds"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
"github.com/terraform-providers/terraform-provider-aws/aws/internal/service/rds/finder"
)

const (
Expand All @@ -12,6 +13,12 @@ const (

// EventSubscription Unknown
EventSubscriptionStatusUnknown = "Unknown"

// ProxyEndpoint NotFound
ProxyEndpointStatusNotFound = "NotFound"

// ProxyEndpoint Unknown
ProxyEndpointStatusUnknown = "Unknown"
)

// EventSubscriptionStatus fetches the EventSubscription and its Status
Expand All @@ -34,3 +41,20 @@ func EventSubscriptionStatus(conn *rds.RDS, subscriptionName string) resource.St
return output.EventSubscriptionsList[0], aws.StringValue(output.EventSubscriptionsList[0].Status), nil
}
}

// DBProxyEndpointStatus fetches the ProxyEndpoint and its Status
func DBProxyEndpointStatus(conn *rds.RDS, id string) resource.StateRefreshFunc {
return func() (interface{}, string, error) {
output, err := finder.DBProxyEndpoint(conn, id)

if err != nil {
return nil, ProxyEndpointStatusUnknown, err
}

if output == nil {
return nil, ProxyEndpointStatusNotFound, nil
}

return output, aws.StringValue(output.Status), nil
}
}
41 changes: 40 additions & 1 deletion aws/internal/service/rds/waiter/waiter.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const (
EventSubscriptionDeletedTimeout = 10 * time.Minute
)

// DeploymentDeployed waits for a EventSubscription to return Deleted
// EventSubscriptionDeleted waits for a EventSubscription to return Deleted
func EventSubscriptionDeleted(conn *rds.RDS, subscriptionName string) (*rds.EventSubscription, error) {
stateConf := &resource.StateChangeConf{
Pending: []string{"deleting"},
Expand All @@ -29,3 +29,42 @@ func EventSubscriptionDeleted(conn *rds.RDS, subscriptionName string) (*rds.Even

return nil, err
}

// DBProxyEndpointAvailable waits for a DBProxyEndpoint to return Available
func DBProxyEndpointAvailable(conn *rds.RDS, id string, timeout time.Duration) (*rds.DBProxyEndpoint, error) {
stateConf := &resource.StateChangeConf{
Pending: []string{
rds.DBProxyEndpointStatusCreating,
rds.DBProxyEndpointStatusModifying,
},
Target: []string{rds.DBProxyEndpointStatusAvailable},
Refresh: DBProxyEndpointStatus(conn, id),
Timeout: timeout,
}

outputRaw, err := stateConf.WaitForState()

if v, ok := outputRaw.(*rds.DBProxyEndpoint); ok {
return v, err
}

return nil, err
}

// DBProxyEndpointDeleted waits for a DBProxyEndpoint to return Deleted
func DBProxyEndpointDeleted(conn *rds.RDS, id string, timeout time.Duration) (*rds.DBProxyEndpoint, error) {
stateConf := &resource.StateChangeConf{
Pending: []string{rds.DBProxyEndpointStatusDeleting},
Target: []string{},
Refresh: DBProxyEndpointStatus(conn, id),
Timeout: timeout,
}

outputRaw, err := stateConf.WaitForState()

if v, ok := outputRaw.(*rds.DBProxyEndpoint); ok {
return v, err
}

return nil, err
}
1 change: 1 addition & 0 deletions aws/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -597,6 +597,7 @@ func Provider() *schema.Provider {
"aws_db_parameter_group": resourceAwsDbParameterGroup(),
"aws_db_proxy": resourceAwsDbProxy(),
"aws_db_proxy_default_target_group": resourceAwsDbProxyDefaultTargetGroup(),
"aws_db_proxy_endpoint": resourceAwsDbProxyEndpoint(),
"aws_db_proxy_target": resourceAwsDbProxyTarget(),
"aws_db_security_group": resourceAwsDbSecurityGroup(),
"aws_db_snapshot": resourceAwsDbSnapshot(),
Expand Down
229 changes: 229 additions & 0 deletions aws/resource_aws_db_proxy_endpoint.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,229 @@
package aws

import (
"fmt"
"log"
"strings"
"time"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/rds"
"github.com/hashicorp/aws-sdk-go-base/tfawserr"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
"github.com/terraform-providers/terraform-provider-aws/aws/internal/keyvaluetags"
"github.com/terraform-providers/terraform-provider-aws/aws/internal/service/rds/finder"
"github.com/terraform-providers/terraform-provider-aws/aws/internal/service/rds/waiter"
)

func resourceAwsDbProxyEndpoint() *schema.Resource {
return &schema.Resource{
Create: resourceAwsDbProxyEndpointCreate,
Read: resourceAwsDbProxyEndpointRead,
Delete: resourceAwsDbProxyEndpointDelete,
Update: resourceAwsDbProxyEndpointUpdate,
Importer: &schema.ResourceImporter{
State: schema.ImportStatePassthrough,
},

bflad marked this conversation as resolved.
Show resolved Hide resolved
Timeouts: &schema.ResourceTimeout{
Create: schema.DefaultTimeout(30 * time.Minute),
Update: schema.DefaultTimeout(30 * time.Minute),
Delete: schema.DefaultTimeout(60 * time.Minute),
},

Schema: map[string]*schema.Schema{
"arn": {
Type: schema.TypeString,
Computed: true,
},
"db_proxy_name": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: validateRdsIdentifier,
},
"db_proxy_endpoint_name": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: validateRdsIdentifier,
},
"endpoint": {
Type: schema.TypeString,
Computed: true,
},
"is_default": {
Type: schema.TypeBool,
Computed: true,
},
"tags": tagsSchema(),
bflad marked this conversation as resolved.
Show resolved Hide resolved
"target_role": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
Default: rds.DBProxyEndpointTargetRoleReadWrite,
ValidateFunc: validation.StringInSlice(rds.DBProxyEndpointTargetRole_Values(), false),
},
"vpc_id": {
Type: schema.TypeString,
Computed: true,
},
"vpc_security_group_ids": {
Type: schema.TypeSet,
Optional: true,
Computed: true,
Elem: &schema.Schema{Type: schema.TypeString},
},
"vpc_subnet_ids": {
Type: schema.TypeSet,
Required: true,
ForceNew: true,
Elem: &schema.Schema{Type: schema.TypeString},
},
},
}
}

func resourceAwsDbProxyEndpointCreate(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).rdsconn
bflad marked this conversation as resolved.
Show resolved Hide resolved

dbProxyName := d.Get("db_proxy_name").(string)
dbProxyEndpointName := d.Get("db_proxy_endpoint_name").(string)

params := rds.CreateDBProxyEndpointInput{
DBProxyName: aws.String(dbProxyName),
DBProxyEndpointName: aws.String(dbProxyEndpointName),
TargetRole: aws.String(d.Get("target_role").(string)),
VpcSubnetIds: expandStringSet(d.Get("vpc_subnet_ids").(*schema.Set)),
Tags: keyvaluetags.New(d.Get("tags").(map[string]interface{})).IgnoreAws().RdsTags(),
bflad marked this conversation as resolved.
Show resolved Hide resolved
}

if v := d.Get("vpc_security_group_ids").(*schema.Set); v.Len() > 0 {
params.VpcSecurityGroupIds = expandStringSet(v)
}

_, err := conn.CreateDBProxyEndpoint(&params)

if err != nil {
return fmt.Errorf("error Creating RDS DB Proxy Endpoint (%s/%s): %w", dbProxyName, dbProxyEndpointName, err)
}

d.SetId(strings.Join([]string{dbProxyName, dbProxyEndpointName}, "/"))

if _, err := waiter.DBProxyEndpointAvailable(conn, d.Id(), d.Timeout(schema.TimeoutCreate)); err != nil {
return fmt.Errorf("error waiting for RDS DB Proxy Endpoint (%s) to become available: %w", d.Id(), err)
}

return resourceAwsDbProxyEndpointRead(d, meta)
}

func resourceAwsDbProxyEndpointRead(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).rdsconn
ignoreTagsConfig := meta.(*AWSClient).IgnoreTagsConfig
bflad marked this conversation as resolved.
Show resolved Hide resolved

dbProxyEndpoint, err := finder.DBProxyEndpoint(conn, d.Id())

if tfawserr.ErrCodeEquals(err, rds.ErrCodeDBProxyNotFoundFault) {
bflad marked this conversation as resolved.
Show resolved Hide resolved
log.Printf("[WARN] RDS DB Proxy Endpoint (%s) not found, removing from state", d.Id())
d.SetId("")
return nil
}

if tfawserr.ErrCodeEquals(err, rds.ErrCodeDBProxyEndpointNotFoundFault) {
bflad marked this conversation as resolved.
Show resolved Hide resolved
log.Printf("[WARN] RDS DB Proxy Endpoint (%s) not found, removing from state", d.Id())
d.SetId("")
return nil
}

if err != nil {
return fmt.Errorf("error reading RDS DB Proxy Endpoint (%s): %w", d.Id(), err)
}

if dbProxyEndpoint == nil {
log.Printf("[WARN] RDS DB Proxy Endpoint (%s) not found, removing from state", d.Id())
d.SetId("")
return nil
}
bflad marked this conversation as resolved.
Show resolved Hide resolved

endpointArn := aws.StringValue(dbProxyEndpoint.DBProxyEndpointArn)
d.Set("arn", endpointArn)
d.Set("db_proxy_name", dbProxyEndpoint.DBProxyName)
d.Set("endpoint", dbProxyEndpoint.Endpoint)
d.Set("db_proxy_endpoint_name", dbProxyEndpoint.DBProxyEndpointName)
d.Set("is_default", dbProxyEndpoint.IsDefault)
d.Set("target_role", dbProxyEndpoint.TargetRole)
d.Set("vpc_id", dbProxyEndpoint.VpcId)
d.Set("target_role", dbProxyEndpoint.TargetRole)
d.Set("vpc_subnet_ids", flattenStringSet(dbProxyEndpoint.VpcSubnetIds))
d.Set("vpc_security_group_ids", flattenStringSet(dbProxyEndpoint.VpcSecurityGroupIds))

tags, err := keyvaluetags.RdsListTags(conn, endpointArn)

if err != nil {
return fmt.Errorf("Error listing tags for RDS DB Proxy Endpoint (%s): %w", endpointArn, err)
}

if err := d.Set("tags", tags.IgnoreAws().IgnoreConfig(ignoreTagsConfig).Map()); err != nil {
return fmt.Errorf("Error setting tags: %w", err)
}
bflad marked this conversation as resolved.
Show resolved Hide resolved

return nil
}

func resourceAwsDbProxyEndpointUpdate(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).rdsconn

if d.HasChange("vpc_security_group_ids") {
params := rds.ModifyDBProxyEndpointInput{
DBProxyEndpointName: aws.String(d.Get("db_proxy_endpoint_name").(string)),
VpcSecurityGroupIds: expandStringSet(d.Get("vpc_security_group_ids").(*schema.Set)),
}

_, err := conn.ModifyDBProxyEndpoint(&params)
if err != nil {
return fmt.Errorf("Error updating DB Proxy Endpoint: %w", err)
}

if _, err := waiter.DBProxyEndpointAvailable(conn, d.Id(), d.Timeout(schema.TimeoutUpdate)); err != nil {
return fmt.Errorf("error waiting for RDS DB Proxy Endpoint (%s) to become modified: %w", d.Id(), err)
}
}

if d.HasChange("tags") {
o, n := d.GetChange("tags")

if err := keyvaluetags.RdsUpdateTags(conn, d.Get("arn").(string), o, n); err != nil {
return fmt.Errorf("Error updating RDS DB Proxy Endpoint (%s) tags: %w", d.Get("arn").(string), err)
}
}

return resourceAwsDbProxyEndpointRead(d, meta)
}

func resourceAwsDbProxyEndpointDelete(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).rdsconn

params := rds.DeleteDBProxyEndpointInput{
DBProxyEndpointName: aws.String(d.Get("db_proxy_endpoint_name").(string)),
}

log.Printf("[DEBUG] Delete DB Proxy Endpoint: %#v", params)
_, err := conn.DeleteDBProxyEndpoint(&params)

if err != nil {
if tfawserr.ErrCodeEquals(err, rds.ErrCodeDBProxyNotFoundFault) || tfawserr.ErrCodeEquals(err, rds.ErrCodeDBProxyEndpointNotFoundFault) {
return nil
}
return fmt.Errorf("Error Deleting DB Proxy Endpoint: %w", err)
}

if _, err := waiter.DBProxyEndpointDeleted(conn, d.Id(), d.Timeout(schema.TimeoutDelete)); err != nil {
if tfawserr.ErrCodeEquals(err, rds.ErrCodeDBProxyNotFoundFault) || tfawserr.ErrCodeEquals(err, rds.ErrCodeDBProxyEndpointNotFoundFault) {
return nil
}
return fmt.Errorf("error waiting for RDS DB Proxy Endpoint (%s) to become deleted: %w", d.Id(), err)
}

return nil
}
Loading