Skip to content

Commit

Permalink
New Resource: aws_ram_resource_association
Browse files Browse the repository at this point in the history
Reference: #6527

Output from acceptance testing:

```
--- PASS: TestAccAwsRamResourceAssociation_disappears (29.50s)
--- PASS: TestAccAwsRamResourceAssociation_basic (31.78s)
```
  • Loading branch information
bflad committed Feb 6, 2019
1 parent 75a9ebb commit 384be32
Show file tree
Hide file tree
Showing 5 changed files with 427 additions and 0 deletions.
1 change: 1 addition & 0 deletions aws/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -590,6 +590,7 @@ func Provider() terraform.ResourceProvider {
"aws_organizations_policy_attachment": resourceAwsOrganizationsPolicyAttachment(),
"aws_placement_group": resourceAwsPlacementGroup(),
"aws_proxy_protocol_policy": resourceAwsProxyProtocolPolicy(),
"aws_ram_resource_association": resourceAwsRamResourceAssociation(),
"aws_ram_resource_share": resourceAwsRamResourceShare(),
"aws_rds_cluster": resourceAwsRDSCluster(),
"aws_rds_cluster_endpoint": resourceAwsRDSClusterEndpoint(),
Expand Down
210 changes: 210 additions & 0 deletions aws/resource_aws_ram_resource_association.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
package aws

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

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ram"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/helper/schema"
)

func resourceAwsRamResourceAssociation() *schema.Resource {
return &schema.Resource{
Create: resourceAwsRamResourceAssociationCreate,
Read: resourceAwsRamResourceAssociationRead,
Delete: resourceAwsRamResourceAssociationDelete,

Importer: &schema.ResourceImporter{
State: schema.ImportStatePassthrough,
},

Schema: map[string]*schema.Schema{
"resource_arn": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},

"resource_share_arn": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
},
}
}

func resourceAwsRamResourceAssociationCreate(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).ramconn
resourceARN := d.Get("resource_arn").(string)
resourceShareARN := d.Get("resource_share_arn").(string)

input := &ram.AssociateResourceShareInput{
ClientToken: aws.String(resource.UniqueId()),
ResourceArns: aws.StringSlice([]string{resourceARN}),
ResourceShareArn: aws.String(resourceShareARN),
}

log.Printf("[DEBUG] Associating RAM Resource Share: %s", input)
_, err := conn.AssociateResourceShare(input)
if err != nil {
return fmt.Errorf("error associating RAM Resource Share: %s", err)
}

d.SetId(fmt.Sprintf("%s,%s", resourceShareARN, resourceARN))

if err := waitForRamResourceShareResourceAssociation(conn, resourceShareARN, resourceARN); err != nil {
return fmt.Errorf("error waiting for RAM Resource Share (%s) Resource Association (%s): %s", resourceShareARN, resourceARN, err)
}

return resourceAwsRamResourceAssociationRead(d, meta)
}

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

resourceShareARN, resourceARN, err := decodeRamResourceAssociationID(d.Id())
if err != nil {
return err
}

resourceShareAssociation, err := getRamResourceShareAssociation(conn, resourceShareARN, resourceARN)

if err != nil {
return fmt.Errorf("error reading RAM Resource Share (%s) Resource Association (%s): %s", resourceShareARN, resourceARN, err)
}

if resourceShareAssociation == nil {
log.Printf("[WARN] RAM Resource Share (%s) Resource Association (%s) not found, removing from state", resourceShareARN, resourceARN)
d.SetId("")
return nil
}

if aws.StringValue(resourceShareAssociation.Status) != ram.ResourceShareAssociationStatusAssociated {
log.Printf("[WARN] RAM Resource Share (%s) Resource Association (%s) not associated, removing from state", resourceShareARN, resourceARN)
d.SetId("")
return nil
}

d.Set("resource_arn", resourceARN)
d.Set("resource_share_arn", resourceShareARN)

return nil
}

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

resourceShareARN, resourceARN, err := decodeRamResourceAssociationID(d.Id())
if err != nil {
return err
}

input := &ram.DisassociateResourceShareInput{
ResourceArns: aws.StringSlice([]string{resourceARN}),
ResourceShareArn: aws.String(resourceShareARN),
}

log.Printf("[DEBUG] Disassociating RAM Resource Share: %s", input)
_, err = conn.DisassociateResourceShare(input)

if isAWSErr(err, ram.ErrCodeUnknownResourceException, "") {
return nil
}

if err != nil {
return fmt.Errorf("error disassociating RAM Resource Share (%s) Resource Association (%s): %s", resourceShareARN, resourceARN, err)
}

if err := waitForRamResourceShareResourceDisassociation(conn, resourceShareARN, resourceARN); err != nil {
return fmt.Errorf("error waiting for RAM Resource Share (%s) Resource Association (%s) disassociation: %s", resourceShareARN, resourceARN, err)
}

return nil
}

func decodeRamResourceAssociationID(id string) (string, string, error) {
idFormatErr := fmt.Errorf("unexpected format of ID (%s), expected SHARE,RESOURCE", id)

parts := strings.SplitN(id, ",", 2)
if len(parts) != 2 || parts[0] == "" || parts[1] == "" {
return "", "", idFormatErr
}

return parts[0], parts[1], nil
}

func getRamResourceShareAssociation(conn *ram.RAM, resourceShareARN, resourceARN string) (*ram.ResourceShareAssociation, error) {
input := &ram.GetResourceShareAssociationsInput{
AssociationType: aws.String(ram.ResourceShareAssociationTypeResource),
ResourceArn: aws.String(resourceARN),
ResourceShareArns: aws.StringSlice([]string{resourceShareARN}),
}

output, err := conn.GetResourceShareAssociations(input)

if isAWSErr(err, ram.ErrCodeUnknownResourceException, "") {
return nil, nil
}

if err != nil {
return nil, err
}

if output == nil || len(output.ResourceShareAssociations) == 0 || output.ResourceShareAssociations[0] == nil {
return nil, nil
}

return output.ResourceShareAssociations[0], nil
}

func ramResourceAssociationStateRefreshFunc(conn *ram.RAM, resourceShareARN, resourceARN string) resource.StateRefreshFunc {
return func() (interface{}, string, error) {
resourceShareAssociation, err := getRamResourceShareAssociation(conn, resourceShareARN, resourceARN)

if err != nil {
return nil, ram.ResourceShareAssociationStatusFailed, err
}

if resourceShareAssociation == nil {
return nil, ram.ResourceShareAssociationStatusDisassociated, nil
}

if aws.StringValue(resourceShareAssociation.Status) == ram.ResourceShareAssociationStatusFailed {
extendedErr := fmt.Errorf("association status message: %s", aws.StringValue(resourceShareAssociation.StatusMessage))
return resourceShareAssociation, aws.StringValue(resourceShareAssociation.Status), extendedErr
}

return resourceShareAssociation, aws.StringValue(resourceShareAssociation.Status), nil
}
}

func waitForRamResourceShareResourceAssociation(conn *ram.RAM, resourceShareARN, resourceARN string) error {
stateConf := &resource.StateChangeConf{
Pending: []string{ram.ResourceShareAssociationStatusAssociating},
Target: []string{ram.ResourceShareAssociationStatusAssociated},
Refresh: ramResourceAssociationStateRefreshFunc(conn, resourceShareARN, resourceARN),
Timeout: 5 * time.Minute,
}

_, err := stateConf.WaitForState()

return err
}

func waitForRamResourceShareResourceDisassociation(conn *ram.RAM, resourceShareARN, resourceARN string) error {
stateConf := &resource.StateChangeConf{
Pending: []string{ram.ResourceShareAssociationStatusAssociated, ram.ResourceShareAssociationStatusDisassociating},
Target: []string{ram.ResourceShareAssociationStatusDisassociated},
Refresh: ramResourceAssociationStateRefreshFunc(conn, resourceShareARN, resourceARN),
Timeout: 5 * time.Minute,
}

_, err := stateConf.WaitForState()

return err
}
172 changes: 172 additions & 0 deletions aws/resource_aws_ram_resource_association_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
package aws

import (
"fmt"
"testing"

"github.com/hashicorp/terraform/helper/acctest"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ram"
)

func TestAccAwsRamResourceAssociation_basic(t *testing.T) {
var resourceShareAssociation1 ram.ResourceShareAssociation
resourceName := "aws_ram_resource_association.test"
rName := acctest.RandomWithPrefix("tf-acc-test")

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAwsRamResourceAssociationDestroy,
Steps: []resource.TestStep{
{
Config: testAccAwsRamResourceAssociationConfig(rName),
Check: resource.ComposeTestCheckFunc(
testAccCheckAwsRamResourceAssociationExists(resourceName, &resourceShareAssociation1),
),
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
},
},
})
}

func TestAccAwsRamResourceAssociation_disappears(t *testing.T) {
var resourceShareAssociation1 ram.ResourceShareAssociation
resourceName := "aws_ram_resource_association.test"
rName := acctest.RandomWithPrefix("tf-acc-test")

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAwsRamResourceAssociationDestroy,
Steps: []resource.TestStep{
{
Config: testAccAwsRamResourceAssociationConfig(rName),
Check: resource.ComposeTestCheckFunc(
testAccCheckAwsRamResourceAssociationExists(resourceName, &resourceShareAssociation1),
testAccCheckAwsRamResourceAssociationDisappears(&resourceShareAssociation1),
),
ExpectNonEmptyPlan: true,
},
},
})
}

func testAccCheckAwsRamResourceAssociationDisappears(resourceShareAssociation *ram.ResourceShareAssociation) resource.TestCheckFunc {
return func(s *terraform.State) error {
conn := testAccProvider.Meta().(*AWSClient).ramconn

input := &ram.DisassociateResourceShareInput{
ResourceArns: []*string{resourceShareAssociation.AssociatedEntity},
ResourceShareArn: resourceShareAssociation.ResourceShareArn,
}

_, err := conn.DisassociateResourceShare(input)
if err != nil {
return err
}

return waitForRamResourceShareResourceDisassociation(conn, aws.StringValue(resourceShareAssociation.ResourceShareArn), aws.StringValue(resourceShareAssociation.AssociatedEntity))
}
}

func testAccCheckAwsRamResourceAssociationExists(resourceName string, association *ram.ResourceShareAssociation) resource.TestCheckFunc {
return func(s *terraform.State) error {
conn := testAccProvider.Meta().(*AWSClient).ramconn

rs, ok := s.RootModule().Resources[resourceName]

if !ok {
return fmt.Errorf("Not found: %s", resourceName)
}

resourceShareARN, resourceARN, err := decodeRamResourceAssociationID(rs.Primary.ID)

if err != nil {
return err
}

resourceShareAssociation, err := getRamResourceShareAssociation(conn, resourceShareARN, resourceARN)

if err != nil {
return fmt.Errorf("error reading RAM Resource Share (%s) Resource Association (%s): %s", resourceShareARN, resourceARN, err)
}

if resourceShareAssociation == nil {
return fmt.Errorf("RAM Resource Share (%s) Resource Association (%s) not found", resourceShareARN, resourceARN)
}

if aws.StringValue(resourceShareAssociation.Status) != ram.ResourceShareAssociationStatusAssociated {
return fmt.Errorf("RAM Resource Share (%s) Resource Association (%s) not associated: %s", resourceShareARN, resourceARN, aws.StringValue(resourceShareAssociation.Status))
}

*association = *resourceShareAssociation

return nil
}
}

func testAccCheckAwsRamResourceAssociationDestroy(s *terraform.State) error {
conn := testAccProvider.Meta().(*AWSClient).ramconn

for _, rs := range s.RootModule().Resources {
if rs.Type != "aws_ram_resource_association" {
continue
}

resourceShareARN, resourceARN, err := decodeRamResourceAssociationID(rs.Primary.ID)

if err != nil {
return err
}

resourceShareAssociation, err := getRamResourceShareAssociation(conn, resourceShareARN, resourceARN)

if err != nil {
return err
}

if resourceShareAssociation != nil && aws.StringValue(resourceShareAssociation.Status) != ram.ResourceShareAssociationStatusDisassociated {
return fmt.Errorf("RAM Resource Share (%s) Resource Association (%s) not disassociated: %s", resourceShareARN, resourceARN, aws.StringValue(resourceShareAssociation.Status))
}
}

return nil
}

func testAccAwsRamResourceAssociationConfig(rName string) string {
return fmt.Sprintf(`
resource "aws_vpc" "test" {
cidr_block = "10.0.0.0/16"
tags {
Name = "tf-acc-test-ram-resource-association"
}
}
resource "aws_subnet" "test" {
cidr_block = "10.0.0.0/24"
vpc_id = "${aws_vpc.test.id}"
tags {
Name = "tf-acc-test-ram-resource-association"
}
}
resource "aws_ram_resource_share" "test" {
name = %q
}
resource "aws_ram_resource_association" "test" {
resource_arn = "${aws_subnet.test.arn}"
resource_share_arn = "${aws_ram_resource_share.test.id}"
}
`, rName)
}
Loading

0 comments on commit 384be32

Please sign in to comment.