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

Add support for Cloudwatch Event API Destination and Connections #18905

Merged
merged 7 commits into from
May 24, 2021
7 changes: 7 additions & 0 deletions .changelog/18905.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
```release-note:new-resource
aws_cloudwatch_event_connection
```

```release-note:new-resource
aws_cloudwatch_event_api_destination
```
53 changes: 53 additions & 0 deletions aws/internal/service/cloudwatchevents/waiter/waiter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package waiter

import (
"time"

"github.com/aws/aws-sdk-go/aws"
events "github.com/aws/aws-sdk-go/service/cloudwatchevents"
"github.com/hashicorp/aws-sdk-go-base/tfawserr"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
)

const (
// ConnectionDeletedTimeout is the maximum amount of time to wait for a CloudwatchEvent Connection to delete
ConnectionDeletedTimeout = 2 * time.Minute
)

// CloudWatchEventConnectionDeleted waits for a CloudwatchEvent Connection to return Deleted
func CloudWatchEventConnectionDeleted(conn *events.CloudWatchEvents, id string) (*events.DescribeConnectionOutput, error) {
stateConf := &resource.StateChangeConf{
Pending: []string{events.ConnectionStateDeleting},
Target: []string{},
Refresh: CloudWatchEventConnectionStatus(conn, id),
Timeout: ConnectionDeletedTimeout,
}

outputRaw, err := stateConf.WaitForState()

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

return nil, err
}

// CloudWatchEventConnectionStatus fetches the Connection and its Status
func CloudWatchEventConnectionStatus(conn *events.CloudWatchEvents, id string) resource.StateRefreshFunc {
return func() (interface{}, string, error) {
params := events.DescribeConnectionInput{
Name: aws.String(id),
}

output, err := conn.DescribeConnection(&params)
if tfawserr.ErrMessageContains(err, events.ErrCodeResourceNotFoundException, "") {
return nil, "", nil
}

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

return output, aws.StringValue(output.ConnectionState), nil
}
}
2 changes: 2 additions & 0 deletions aws/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -527,6 +527,8 @@ func Provider() *schema.Provider {
"aws_cloudwatch_event_rule": resourceAwsCloudWatchEventRule(),
"aws_cloudwatch_event_target": resourceAwsCloudWatchEventTarget(),
"aws_cloudwatch_event_archive": resourceAwsCloudWatchEventArchive(),
"aws_cloudwatch_event_connection": resourceAwsCloudWatchEventConnection(),
"aws_cloudwatch_event_api_destination": resourceAwsCloudWatchEventApiDestination(),
"aws_cloudwatch_log_destination": resourceAwsCloudWatchLogDestination(),
"aws_cloudwatch_log_destination_policy": resourceAwsCloudWatchLogDestinationPolicy(),
"aws_cloudwatch_log_group": resourceAwsCloudWatchLogGroup(),
Expand Down
189 changes: 189 additions & 0 deletions aws/resource_aws_cloudwatch_event_api_destination.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
package aws

import (
"fmt"
"log"
"math"
"regexp"

"github.com/aws/aws-sdk-go/aws"
events "github.com/aws/aws-sdk-go/service/cloudwatchevents"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
)

func resourceAwsCloudWatchEventApiDestination() *schema.Resource {
return &schema.Resource{
Create: resourceAwsCloudWatchEventApiDestinationCreate,
Read: resourceAwsCloudWatchEventApiDestinationRead,
Update: resourceAwsCloudWatchEventApiDestinationUpdate,
Delete: resourceAwsCloudWatchEventApiDestinationDelete,
Importer: &schema.ResourceImporter{
State: schema.ImportStatePassthrough,
},

Schema: map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: validation.All(
validation.StringLenBetween(1, 64),
validation.StringMatch(regexp.MustCompile(`^[\.\-_A-Za-z0-9]+`), ""),
),
},
"description": {
Type: schema.TypeString,
Optional: true,
ValidateFunc: validation.StringLenBetween(0, 512),
},
"invocation_endpoint": {
Type: schema.TypeString,
Required: true,
},
"invocation_rate_limit_per_second": {
Type: schema.TypeInt,
Optional: true,
ValidateFunc: validation.IntBetween(1, math.MaxInt64),
Default: 300,
},
"http_method": {
Type: schema.TypeString,
Required: true,
ValidateFunc: validation.StringInSlice(events.ApiDestinationHttpMethod_Values(), true),
},
"connection_arn": {
Type: schema.TypeString,
Required: true,
ValidateFunc: validateArn,
},
"arn": {
Type: schema.TypeString,
Computed: true,
},
},
}
}

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

input := &events.CreateApiDestinationInput{}

if name, ok := d.GetOk("name"); ok {
input.Name = aws.String(name.(string))
}
if description, ok := d.GetOk("description"); ok {
input.Description = aws.String(description.(string))
}
if invocationEndpoint, ok := d.GetOk("invocation_endpoint"); ok {
input.InvocationEndpoint = aws.String(invocationEndpoint.(string))
}
if invocationRateLimitPerSecond, ok := d.GetOk("invocation_rate_limit_per_second"); ok {
input.InvocationRateLimitPerSecond = aws.Int64(int64(invocationRateLimitPerSecond.(int)))
}
if httpMethod, ok := d.GetOk("http_method"); ok {
input.HttpMethod = aws.String(httpMethod.(string))
}
if connectionArn, ok := d.GetOk("connection_arn"); ok {
input.ConnectionArn = aws.String(connectionArn.(string))
}

log.Printf("[DEBUG] Creating CloudWatchEvent API Destination: %v", input)

_, err := conn.CreateApiDestination(input)
if err != nil {
return fmt.Errorf("Creating CloudWatchEvent API Destination (%s) failed: %w", *input.Name, err)
}

d.SetId(aws.StringValue(input.Name))

log.Printf("[INFO] CloudWatchEvent API Destination (%s) created", d.Id())

return resourceAwsCloudWatchEventApiDestinationRead(d, meta)
}

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

input := &events.DescribeApiDestinationInput{
Name: aws.String(d.Id()),
}

log.Printf("[DEBUG] Reading CloudWatchEvent API Destination (%s)", d.Id())
output, err := conn.DescribeApiDestination(input)
if isAWSErr(err, events.ErrCodeResourceNotFoundException, "") {
log.Printf("[WARN] CloudWatchEvent API Destination (%s) not found, removing from state", d.Id())
d.SetId("")
return nil
}
if err != nil {
return fmt.Errorf("error reading CloudWatchEvent API Destination: %w", err)
}

log.Printf("[DEBUG] Found CloudWatchEvent API Destination: %#v", *output)

d.Set("arn", output.ApiDestinationArn)
d.Set("name", output.Name)
d.Set("description", output.Description)
d.Set("invocation_endpoint", output.InvocationEndpoint)
d.Set("invocation_rate_limit_per_second", output.InvocationRateLimitPerSecond)
d.Set("http_method", output.HttpMethod)
d.Set("connection_arn", output.ConnectionArn)

return nil
}

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

input := &events.UpdateApiDestinationInput{}

if name, ok := d.GetOk("name"); ok {
input.Name = aws.String(name.(string))
}
if description, ok := d.GetOk("description"); ok {
input.Description = aws.String(description.(string))
}
if invocationEndpoint, ok := d.GetOk("invocation_endpoint"); ok {
input.InvocationEndpoint = aws.String(invocationEndpoint.(string))
}
if invocationRateLimitPerSecond, ok := d.GetOk("invocation_rate_limit_per_second"); ok {
input.InvocationRateLimitPerSecond = aws.Int64(invocationRateLimitPerSecond.(int64))
}
if httpMethod, ok := d.GetOk("http_method"); ok {
input.HttpMethod = aws.String(httpMethod.(string))
}
if connectionArn, ok := d.GetOk("connection_arn"); ok {
input.ConnectionArn = aws.String(connectionArn.(string))
}

log.Printf("[DEBUG] Updating CloudWatchEvent API Destination: %s", input)
_, err := conn.UpdateApiDestination(input)
if err != nil {
return fmt.Errorf("error updating CloudWatchEvent API Destination (%s): %w", d.Id(), err)
}
return resourceAwsCloudWatchEventApiDestinationRead(d, meta)
}

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

log.Printf("[INFO] Deleting CloudWatchEvent API Destination (%s)", d.Id())
input := &events.DeleteApiDestinationInput{
Name: aws.String(d.Id()),
}

_, err := conn.DeleteApiDestination(input)

if isAWSErr(err, events.ErrCodeResourceNotFoundException, "") {
log.Printf("[WARN] CloudWatchEvent API Destination (%s) not found", d.Id())
return nil
}
if err != nil {
return fmt.Errorf("Error deleting CloudWatchEvent API Destination (%s): %w", d.Id(), err)
}
log.Printf("[INFO] CloudWatchEvent API Destination (%s) deleted", d.Id())

return nil
}
Loading