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/aws_ram_resource_share: Add new resource #6528

Merged
merged 2 commits into from
Jan 15, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions aws/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ import (
"github.com/aws/aws-sdk-go/service/organizations"
"github.com/aws/aws-sdk-go/service/pinpoint"
"github.com/aws/aws-sdk-go/service/pricing"
"github.com/aws/aws-sdk-go/service/ram"
"github.com/aws/aws-sdk-go/service/rds"
"github.com/aws/aws-sdk-go/service/redshift"
"github.com/aws/aws-sdk-go/service/route53"
Expand Down Expand Up @@ -149,6 +150,7 @@ type Config struct {
KinesisAnalyticsEndpoint string
KmsEndpoint string
LambdaEndpoint string
RamEndpoint string
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we're not wiring up endpoint customization in the provider configuration (see aws/provider.go) we should either implement it or remove it here, e.g. removing this line, awsRamSess and using client.ramconn = ram.New(sess) 👍

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This still needs to be addressed either which way. 😄

RdsEndpoint string
R53Endpoint string
S3Endpoint string
Expand Down Expand Up @@ -216,6 +218,7 @@ type AWSClient struct {
accountid string
supportedplatforms []string
region string
ramconn *ram.RAM
rdsconn *rds.RDS
iamconn *iam.IAM
kinesisconn *kinesis.Kinesis
Expand Down Expand Up @@ -436,6 +439,7 @@ func (c *Config) Client() (interface{}, error) {
awsKinesisSess := sess.Copy(&aws.Config{Endpoint: aws.String(c.KinesisEndpoint)})
awsKinesisAnalyticsSess := sess.Copy(&aws.Config{Endpoint: aws.String(c.KinesisAnalyticsEndpoint)})
awsKmsSess := sess.Copy(&aws.Config{Endpoint: aws.String(c.KmsEndpoint)})
awsRamSess := sess.Copy(&aws.Config{Endpoint: aws.String(c.RamEndpoint)})
awsRdsSess := sess.Copy(&aws.Config{Endpoint: aws.String(c.RdsEndpoint)})
awsS3Sess := sess.Copy(&aws.Config{Endpoint: aws.String(c.S3Endpoint)})
awsS3ControlSess := sess.Copy(&aws.Config{Endpoint: aws.String(c.S3ControlEndpoint)})
Expand Down Expand Up @@ -570,6 +574,7 @@ func (c *Config) Client() (interface{}, error) {
client.opsworksconn = opsworks.New(sess)
client.organizationsconn = organizations.New(sess)
client.r53conn = route53.New(r53Sess)
client.ramconn = ram.New(awsRamSess)
client.rdsconn = rds.New(awsRdsSess)
client.redshiftconn = redshift.New(sess)
client.simpledbconn = simpledb.New(sess)
Expand Down
1 change: 1 addition & 0 deletions aws/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -583,6 +583,7 @@ func Provider() terraform.ResourceProvider {
"aws_organizations_policy_attachment": resourceAwsOrganizationsPolicyAttachment(),
"aws_placement_group": resourceAwsPlacementGroup(),
"aws_proxy_protocol_policy": resourceAwsProxyProtocolPolicy(),
"aws_ram_resource_share": resourceAwsRamResourceShare(),
"aws_rds_cluster": resourceAwsRDSCluster(),
"aws_rds_cluster_endpoint": resourceAwsRDSClusterEndpoint(),
"aws_rds_cluster_instance": resourceAwsRDSClusterInstance(),
Expand Down
241 changes: 241 additions & 0 deletions aws/resource_aws_ram_resource_share.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,241 @@
package aws

import (
"fmt"
"log"
"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 resourceAwsRamResourceShare() *schema.Resource {
return &schema.Resource{
Create: resourceAwsRamResourceShareCreate,
Read: resourceAwsRamResourceShareRead,
Update: resourceAwsRamResourceShareUpdate,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should add at least one acceptance test that exercises Update 😄

Delete: resourceAwsRamResourceShareDelete,

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

Timeouts: &schema.ResourceTimeout{
Create: schema.DefaultTimeout(5 * time.Minute),
Delete: schema.DefaultTimeout(5 * time.Minute),
},

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

"allow_external_principals": {
Type: schema.TypeBool,
Optional: true,
Default: false,
},

"tags": tagsSchema(),
},
}
}

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

request := &ram.CreateResourceShareInput{
Name: aws.String(d.Get("name").(string)),
AllowExternalPrincipals: aws.Bool(d.Get("allow_external_principals").(bool)),
}

if v, ok := d.GetOk("tags"); ok {
tags := tagsFromMapRAM(v.(map[string]interface{}))
request.Tags = tags
}

log.Println("[DEBUG] Create RAM resource share request:", request)
createResp, err := conn.CreateResourceShare(request)
if err != nil {
return fmt.Errorf("Error creating RAM resource share: %s", err)
}

d.SetId(aws.StringValue(createResp.ResourceShare.ResourceShareArn))

stateConf := &resource.StateChangeConf{
Pending: []string{ram.ResourceShareStatusPending},
Target: []string{ram.ResourceShareStatusActive},
Refresh: resourceAwsRamResourceShareStateRefreshFunc(conn, d.Id()),
Timeout: d.Timeout(schema.TimeoutCreate),
}

_, err = stateConf.WaitForState()
if err != nil {
return fmt.Errorf("Error waiting for RAM resource share (%s) to become ready: %s", d.Id(), err)
}

return resourceAwsRamResourceShareRead(d, meta)
}

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

request := &ram.GetResourceSharesInput{
ResourceShareArns: []*string{aws.String(d.Id())},
ResourceOwner: aws.String(ram.ResourceOwnerSelf),
}

output, err := conn.GetResourceShares(request)
if err != nil {
if isAWSErr(err, ram.ErrCodeUnknownResourceException, "") {
log.Printf("[WARN] No RAM resource share by ARN (%s) found, removing from state", d.Id())
d.SetId("")
return nil
}
return fmt.Errorf("Error reading RAM resource share %s: %s", d.Id(), err)
}

if len(output.ResourceShares) == 0 {
log.Printf("[WARN] No RAM resource share by ARN (%s) found, removing from state", d.Id())
d.SetId("")
return nil
}

resourceShare := output.ResourceShares[0]

if aws.StringValue(resourceShare.Status) != ram.ResourceShareStatusActive {
log.Printf("[WARN] RAM resource share (%s) delet(ing|ed), removing from state", d.Id())
d.SetId("")
return nil
}

d.Set("name", resourceShare.Name)
d.Set("allow_external_principals", resourceShare.AllowExternalPrincipals)

if err := d.Set("tags", tagsToMapRAM(resourceShare.Tags)); err != nil {
return fmt.Errorf("Error setting tags: %s", err)
}

return nil
}

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

d.Partial(true)

if d.HasChange("name") || d.HasChange("allow_external_principals") {
request := &ram.UpdateResourceShareInput{
ResourceShareArn: aws.String(d.Id()),
Name: aws.String(d.Get("name").(string)),
AllowExternalPrincipals: aws.Bool(d.Get("allow_external_principals").(bool)),
}

log.Println("[DEBUG] Update RAM resource share request:", request)
_, err := conn.UpdateResourceShare(request)
if err != nil {
if isAWSErr(err, ram.ErrCodeUnknownResourceException, "") {
log.Printf("[WARN] No RAM resource share by ARN (%s) found", d.Id())
d.SetId("")
return nil
}
return fmt.Errorf("Error updating RAM resource share %s: %s", d.Id(), err)
}

d.SetPartial("name")
d.SetPartial("allow_external_principals")
}

if d.HasChange("tags") {
// Reset all tags to empty set
oraw, nraw := d.GetChange("tags")
o := oraw.(map[string]interface{})
n := nraw.(map[string]interface{})
c, r := diffTagsRAM(tagsFromMapRAM(o), tagsFromMapRAM(n))

if len(r) > 0 {
_, err := conn.UntagResource(&ram.UntagResourceInput{
ResourceShareArn: aws.String(d.Id()),
TagKeys: tagKeysRam(r),
})
if err != nil {
return fmt.Errorf("Error deleting RAM resource share tags: %s", err)
}
}

if len(c) > 0 {
input := &ram.TagResourceInput{
ResourceShareArn: aws.String(d.Id()),
Tags: c,
}
_, err := conn.TagResource(input)
if err != nil {
return fmt.Errorf("Error updating RAM resource share tags: %s", err)
}
}

d.SetPartial("tags")
}

d.Partial(false)

return resourceAwsRamResourceShareRead(d, meta)
}

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

deleteResourceShareInput := &ram.DeleteResourceShareInput{
ResourceShareArn: aws.String(d.Id()),
}

log.Println("[DEBUG] Delete RAM resource share request:", deleteResourceShareInput)
_, err := conn.DeleteResourceShare(deleteResourceShareInput)
if err != nil {
if isAWSErr(err, ram.ErrCodeUnknownResourceException, "") {
return nil
}
return fmt.Errorf("Error deleting RAM resource share %s: %s", d.Id(), err)
}

stateConf := &resource.StateChangeConf{
Pending: []string{ram.ResourceShareStatusDeleting},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We may also want to include ram.ResourceShareStatusActive in the Pending list in case there is any delay the start of the process. 👍

Target: []string{ram.ResourceShareStatusDeleted},
Refresh: resourceAwsRamResourceShareStateRefreshFunc(conn, d.Id()),
Timeout: d.Timeout(schema.TimeoutDelete),
}

_, err = stateConf.WaitForState()
if err != nil {
return fmt.Errorf("Error waiting for RAM resource share (%s) to become ready: %s", d.Id(), err)
}

return nil
}

func resourceAwsRamResourceShareStateRefreshFunc(conn *ram.RAM, resourceShareArn string) resource.StateRefreshFunc {
return func() (interface{}, string, error) {
request := &ram.GetResourceSharesInput{
ResourceShareArns: []*string{aws.String(resourceShareArn)},
ResourceOwner: aws.String(ram.ResourceOwnerSelf),
}

output, err := conn.GetResourceShares(request)

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

if len(output.ResourceShares) == 0 {
return nil, ram.ResourceShareStatusDeleted, nil
}

resourceShare := output.ResourceShares[0]

return resourceShare, aws.StringValue(resourceShare.Status), nil
}
}
Loading