opentofu/builtin/providers/aws/resource_aws_dms_replication_instance.go
Paul Stack 5cad27bb2e provider/aws: Migrate aws_dms_* resources away from AWS waiters (#13291)
The AWS waiter package has changed location in the 1.8.0 version of the
SDK. DMS will need to mitigate a breaking change because of this

Between @radeksimko and myself, we think that we should migrate the DMS
resources to using the Terraform state refresh func pattern that is used
across the entire of the AWS provider. DMS is the *only* resource that
currently uses the AWS waiters, so the LOE to migrate is pretty low
2017-04-05 06:48:37 +01:00

428 lines
12 KiB
Go

package aws
import (
"fmt"
"log"
"time"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr"
dms "github.com/aws/aws-sdk-go/service/databasemigrationservice"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/helper/schema"
)
func resourceAwsDmsReplicationInstance() *schema.Resource {
return &schema.Resource{
Create: resourceAwsDmsReplicationInstanceCreate,
Read: resourceAwsDmsReplicationInstanceRead,
Update: resourceAwsDmsReplicationInstanceUpdate,
Delete: resourceAwsDmsReplicationInstanceDelete,
Importer: &schema.ResourceImporter{
State: schema.ImportStatePassthrough,
},
Schema: map[string]*schema.Schema{
"allocated_storage": {
Type: schema.TypeInt,
Computed: true,
Optional: true,
ValidateFunc: validateIntegerInRange(5, 6144),
},
"apply_immediately": {
Type: schema.TypeBool,
Optional: true,
},
"auto_minor_version_upgrade": {
Type: schema.TypeBool,
Computed: true,
Optional: true,
},
"availability_zone": {
Type: schema.TypeString,
Computed: true,
Optional: true,
ForceNew: true,
},
"engine_version": {
Type: schema.TypeString,
Computed: true,
Optional: true,
},
"kms_key_arn": {
Type: schema.TypeString,
Computed: true,
Optional: true,
ForceNew: true,
ValidateFunc: validateArn,
},
"multi_az": {
Type: schema.TypeBool,
Computed: true,
Optional: true,
},
"preferred_maintenance_window": {
Type: schema.TypeString,
Computed: true,
Optional: true,
ValidateFunc: validateOnceAWeekWindowFormat,
},
"publicly_accessible": {
Type: schema.TypeBool,
Computed: true,
Optional: true,
ForceNew: true,
},
"replication_instance_arn": {
Type: schema.TypeString,
Computed: true,
},
"replication_instance_class": {
Type: schema.TypeString,
Required: true,
// Valid Values: dms.t2.micro | dms.t2.small | dms.t2.medium | dms.t2.large | dms.c4.large |
// dms.c4.xlarge | dms.c4.2xlarge | dms.c4.4xlarge
},
"replication_instance_id": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: validateDmsReplicationInstanceId,
},
"replication_instance_private_ips": {
Type: schema.TypeList,
Elem: &schema.Schema{Type: schema.TypeString},
Computed: true,
},
"replication_instance_public_ips": {
Type: schema.TypeList,
Elem: &schema.Schema{Type: schema.TypeString},
Computed: true,
},
"replication_subnet_group_id": {
Type: schema.TypeString,
Computed: true,
Optional: true,
ForceNew: true,
},
"tags": {
Type: schema.TypeMap,
Optional: true,
},
"vpc_security_group_ids": {
Type: schema.TypeSet,
Elem: &schema.Schema{Type: schema.TypeString},
Set: schema.HashString,
Computed: true,
Optional: true,
},
},
}
}
func resourceAwsDmsReplicationInstanceCreate(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).dmsconn
request := &dms.CreateReplicationInstanceInput{
AutoMinorVersionUpgrade: aws.Bool(d.Get("auto_minor_version_upgrade").(bool)),
PubliclyAccessible: aws.Bool(d.Get("publicly_accessible").(bool)),
ReplicationInstanceClass: aws.String(d.Get("replication_instance_class").(string)),
ReplicationInstanceIdentifier: aws.String(d.Get("replication_instance_id").(string)),
Tags: dmsTagsFromMap(d.Get("tags").(map[string]interface{})),
}
// WARNING: GetOk returns the zero value for the type if the key is omitted in config. This means for optional
// keys that the zero value is valid we cannot know if the zero value was in the config and cannot allow the API
// to set the default value. See GitHub Issue #5694 https://github.com/hashicorp/terraform/issues/5694
if v, ok := d.GetOk("allocated_storage"); ok {
request.AllocatedStorage = aws.Int64(int64(v.(int)))
}
if v, ok := d.GetOk("engine_version"); ok {
request.EngineVersion = aws.String(v.(string))
}
if v, ok := d.GetOk("kms_key_arn"); ok {
request.KmsKeyId = aws.String(v.(string))
}
if v, ok := d.GetOk("preferred_maintenance_window"); ok {
request.PreferredMaintenanceWindow = aws.String(v.(string))
}
if v, ok := d.GetOk("replication_subnet_group_id"); ok {
request.ReplicationSubnetGroupIdentifier = aws.String(v.(string))
}
if v, ok := d.GetOk("vpc_security_group_ids"); ok {
request.VpcSecurityGroupIds = expandStringList(v.(*schema.Set).List())
}
az, azSet := d.GetOk("availability_zone")
if azSet {
request.AvailabilityZone = aws.String(az.(string))
}
if multiAz, ok := d.GetOk("multi_az"); ok {
request.MultiAZ = aws.Bool(multiAz.(bool))
if multiAz.(bool) && azSet {
return fmt.Errorf("Cannot set availability_zone if multi_az is set to true")
}
}
log.Println("[DEBUG] DMS create replication instance:", request)
_, err := conn.CreateReplicationInstance(request)
if err != nil {
return err
}
d.SetId(d.Get("replication_instance_id").(string))
stateConf := &resource.StateChangeConf{
Pending: []string{"creating"},
Target: []string{"available"},
Refresh: resourceAwsDmsReplicationInstanceStateRefreshFunc(d, meta),
Timeout: d.Timeout(schema.TimeoutCreate),
MinTimeout: 10 * time.Second,
Delay: 30 * time.Second, // Wait 30 secs before starting
}
// Wait, catching any errors
_, err = stateConf.WaitForState()
if err != nil {
return err
}
return resourceAwsDmsReplicationInstanceRead(d, meta)
}
func resourceAwsDmsReplicationInstanceRead(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).dmsconn
response, err := conn.DescribeReplicationInstances(&dms.DescribeReplicationInstancesInput{
Filters: []*dms.Filter{
{
Name: aws.String("replication-instance-id"),
Values: []*string{aws.String(d.Id())}, // Must use d.Id() to work with import.
},
},
})
if err != nil {
if dmserr, ok := err.(awserr.Error); ok && dmserr.Code() == "ResourceNotFoundFault" {
log.Printf("[DEBUG] DMS Replication Instance %q Not Found", d.Id())
d.SetId("")
return nil
}
return err
}
err = resourceAwsDmsReplicationInstanceSetState(d, response.ReplicationInstances[0])
if err != nil {
return err
}
tagsResp, err := conn.ListTagsForResource(&dms.ListTagsForResourceInput{
ResourceArn: aws.String(d.Get("replication_instance_arn").(string)),
})
if err != nil {
return err
}
d.Set("tags", dmsTagsToMap(tagsResp.TagList))
return nil
}
func resourceAwsDmsReplicationInstanceUpdate(d *schema.ResourceData, meta interface{}) error {
request := &dms.ModifyReplicationInstanceInput{
ApplyImmediately: aws.Bool(d.Get("apply_immediately").(bool)),
ReplicationInstanceArn: aws.String(d.Get("replication_instance_arn").(string)),
}
hasChanges := false
if d.HasChange("auto_minor_version_upgrade") {
request.AutoMinorVersionUpgrade = aws.Bool(d.Get("auto_minor_version_upgrade").(bool))
hasChanges = true
}
if d.HasChange("allocated_storage") {
if v, ok := d.GetOk("allocated_storage"); ok {
request.AllocatedStorage = aws.Int64(int64(v.(int)))
hasChanges = true
}
}
if d.HasChange("engine_version") {
if v, ok := d.GetOk("engine_version"); ok {
request.ReplicationInstanceClass = aws.String(v.(string))
hasChanges = true
}
}
if d.HasChange("multi_az") {
if v, ok := d.GetOk("multi_az"); ok {
request.MultiAZ = aws.Bool(v.(bool))
hasChanges = true
}
}
if d.HasChange("preferred_maintenance_window") {
if v, ok := d.GetOk("preferred_maintenance_window"); ok {
request.PreferredMaintenanceWindow = aws.String(v.(string))
hasChanges = true
}
}
if d.HasChange("replication_instance_class") {
if v, ok := d.GetOk("replication_instance_class"); ok {
request.ReplicationInstanceClass = aws.String(v.(string))
hasChanges = true
}
}
if d.HasChange("vpc_security_group_ids") {
if v, ok := d.GetOk("vpc_security_group_ids"); ok {
request.VpcSecurityGroupIds = expandStringList(v.(*schema.Set).List())
hasChanges = true
}
}
if d.HasChange("tags") {
err := dmsSetTags(d.Get("replication_instance_arn").(string), d, meta)
if err != nil {
return err
}
}
if hasChanges {
conn := meta.(*AWSClient).dmsconn
_, err := conn.ModifyReplicationInstance(request)
if err != nil {
return err
}
stateConf := &resource.StateChangeConf{
Pending: []string{"modifying"},
Target: []string{"available"},
Refresh: resourceAwsDmsReplicationInstanceStateRefreshFunc(d, meta),
Timeout: d.Timeout(schema.TimeoutCreate),
MinTimeout: 10 * time.Second,
Delay: 30 * time.Second, // Wait 30 secs before starting
}
// Wait, catching any errors
_, err = stateConf.WaitForState()
if err != nil {
return err
}
return resourceAwsDmsReplicationInstanceRead(d, meta)
}
return nil
}
func resourceAwsDmsReplicationInstanceDelete(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).dmsconn
request := &dms.DeleteReplicationInstanceInput{
ReplicationInstanceArn: aws.String(d.Get("replication_instance_arn").(string)),
}
log.Printf("[DEBUG] DMS delete replication instance: %#v", request)
_, err := conn.DeleteReplicationInstance(request)
if err != nil {
return err
}
stateConf := &resource.StateChangeConf{
Pending: []string{"deleting"},
Target: []string{},
Refresh: resourceAwsDmsReplicationInstanceStateRefreshFunc(d, meta),
Timeout: d.Timeout(schema.TimeoutCreate),
MinTimeout: 10 * time.Second,
Delay: 30 * time.Second, // Wait 30 secs before starting
}
// Wait, catching any errors
_, err = stateConf.WaitForState()
if err != nil {
return err
}
return nil
}
func resourceAwsDmsReplicationInstanceSetState(d *schema.ResourceData, instance *dms.ReplicationInstance) error {
d.SetId(*instance.ReplicationInstanceIdentifier)
d.Set("replication_instance_id", instance.ReplicationInstanceIdentifier)
d.Set("allocated_storage", instance.AllocatedStorage)
d.Set("auto_minor_version_upgrade", instance.AutoMinorVersionUpgrade)
d.Set("availability_zone", instance.AvailabilityZone)
d.Set("engine_version", instance.EngineVersion)
d.Set("kms_key_arn", instance.KmsKeyId)
d.Set("multi_az", instance.MultiAZ)
d.Set("preferred_maintenance_window", instance.PreferredMaintenanceWindow)
d.Set("publicly_accessible", instance.PubliclyAccessible)
d.Set("replication_instance_arn", instance.ReplicationInstanceArn)
d.Set("replication_instance_class", instance.ReplicationInstanceClass)
d.Set("replication_subnet_group_id", instance.ReplicationSubnetGroup.ReplicationSubnetGroupIdentifier)
vpc_security_group_ids := []string{}
for _, sg := range instance.VpcSecurityGroups {
vpc_security_group_ids = append(vpc_security_group_ids, aws.StringValue(sg.VpcSecurityGroupId))
}
d.Set("vpc_security_group_ids", vpc_security_group_ids)
private_ip_addresses := []string{}
for _, ip := range instance.ReplicationInstancePrivateIpAddresses {
private_ip_addresses = append(private_ip_addresses, aws.StringValue(ip))
}
d.Set("replication_instance_private_ips", private_ip_addresses)
public_ip_addresses := []string{}
for _, ip := range instance.ReplicationInstancePublicIpAddresses {
public_ip_addresses = append(public_ip_addresses, aws.StringValue(ip))
}
d.Set("replication_instance_public_ips", public_ip_addresses)
return nil
}
func resourceAwsDmsReplicationInstanceStateRefreshFunc(
d *schema.ResourceData, meta interface{}) resource.StateRefreshFunc {
return func() (interface{}, string, error) {
conn := meta.(*AWSClient).dmsconn
v, err := conn.DescribeReplicationInstances(&dms.DescribeReplicationInstancesInput{
Filters: []*dms.Filter{
{
Name: aws.String("replication-instance-id"),
Values: []*string{aws.String(d.Id())}, // Must use d.Id() to work with import.
},
},
})
if err != nil {
if dmserr, ok := err.(awserr.Error); ok && dmserr.Code() == "ResourceNotFoundFault" {
return nil, "", nil
}
log.Printf("Error on retrieving DMS Replication Instance when waiting: %s", err)
return nil, "", err
}
if v == nil {
return nil, "", nil
}
if v.ReplicationInstances == nil {
return nil, "", fmt.Errorf("Error on retrieving DMS Replication Instance when waiting for State")
}
return v, *v.ReplicationInstances[0].ReplicationInstanceStatus, nil
}
}