Skip to content

Commit

Permalink
Merge pull request #20613 from hashicorp/tmp-pr20312
Browse files Browse the repository at this point in the history
EventBridge Rule and Target: Allow `event_bridge_name` to be an ARN
  • Loading branch information
ewbankkit committed Aug 18, 2021
2 parents 170bb76 + 351cd8a commit 71edd71
Show file tree
Hide file tree
Showing 14 changed files with 491 additions and 149 deletions.
7 changes: 7 additions & 0 deletions .changelog/20312.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
```release-note:bug
aws/resource_aws_cloudwatch_event_rule: Correctly handle ARN in `event_bus_name` argument
```

```release-note:bug
aws/resource_aws_cloudwatch_event_target: Correctly handle ARN in `event_bus_name` argument
```
5 changes: 5 additions & 0 deletions aws/internal/service/cloudwatchevents/enum.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package cloudwatchevents

const (
DefaultEventBusName = "default"
)
31 changes: 26 additions & 5 deletions aws/internal/service/cloudwatchevents/finder/finder.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,25 +39,46 @@ func ConnectionByName(conn *events.CloudWatchEvents, name string) (*events.Descr
return output, nil
}

func Rule(conn *events.CloudWatchEvents, eventBusName, ruleName string) (*events.DescribeRuleOutput, error) {
func RuleByEventBusAndRuleNames(conn *events.CloudWatchEvents, eventBusName, ruleName string) (*events.DescribeRuleOutput, error) {
input := events.DescribeRuleInput{
Name: aws.String(ruleName),
}

if eventBusName != "" {
input.EventBusName = aws.String(eventBusName)
}

return conn.DescribeRule(&input)
output, err := conn.DescribeRule(&input)

if tfawserr.ErrCodeEquals(err, events.ErrCodeResourceNotFoundException) {
return nil, &resource.NotFoundError{
LastError: err,
LastRequest: input,
}
}

if err != nil {
return nil, err
}

if output == nil {
return nil, &resource.NotFoundError{
Message: "Empty result",
LastRequest: input,
}
}

return output, nil
}

func RuleByID(conn *events.CloudWatchEvents, ruleID string) (*events.DescribeRuleOutput, error) {
busName, ruleName, err := tfevents.RuleParseID(ruleID)
func RuleByResourceID(conn *events.CloudWatchEvents, id string) (*events.DescribeRuleOutput, error) {
eventBusName, ruleName, err := tfevents.RuleParseResourceID(id)

if err != nil {
return nil, err
}

return Rule(conn, busName, ruleName)
return RuleByEventBusAndRuleNames(conn, eventBusName, ruleName)
}

func Target(conn *events.CloudWatchEvents, busName, ruleName, targetId string) (*events.Target, error) {
Expand Down
81 changes: 51 additions & 30 deletions aws/internal/service/cloudwatchevents/id.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,77 +7,95 @@ import (
)

var (
eventBusARNPattern = regexp.MustCompile(`^arn:aws[\w-]*:events:[a-z]{2}-[a-z]+-[\w-]+:[0-9]{12}:event-bus\/[\.\-_A-Za-z0-9]+$`)
partnerEventBusPattern = regexp.MustCompile(`^aws\.partner(/[\.\-_A-Za-z0-9]+){2,}$`)
)

const DefaultEventBusName = "default"
const permissionResourceIDSeparator = "/"

const PermissionIDSeparator = "/"

func PermissionCreateID(eventBusName, statementID string) string {
func PermissionCreateResourceID(eventBusName, statementID string) string {
if eventBusName == "" || eventBusName == DefaultEventBusName {
return statementID
}
return eventBusName + PermissionIDSeparator + statementID

parts := []string{eventBusName, statementID}
id := strings.Join(parts, permissionResourceIDSeparator)

return id
}

func PermissionParseID(id string) (string, string, error) {
parts := strings.Split(id, PermissionIDSeparator)
func PermissionParseResourceID(id string) (string, string, error) {
parts := strings.Split(id, permissionResourceIDSeparator)

if len(parts) == 1 && parts[0] != "" {
return DefaultEventBusName, parts[0], nil
}
if len(parts) == 2 && parts[0] != "" && parts[1] != "" {
return parts[0], parts[1], nil
}

return "", "", fmt.Errorf("unexpected format for ID (%q), expected <event-bus-name>"+PermissionIDSeparator+"<statement-id> or <statement-id>", id)
return "", "", fmt.Errorf("unexpected format for ID (%[1]s), expected EVENTBUSNAME%[2]sSTATEMENTID or STATEMENTID", id, permissionResourceIDSeparator)
}

const ruleIDSeparator = "/"
const ruleResourceIDSeparator = "/"

func RuleCreateID(eventBusName, ruleName string) string {
func RuleCreateResourceID(eventBusName, ruleName string) string {
if eventBusName == "" || eventBusName == DefaultEventBusName {
return ruleName
}
return eventBusName + ruleIDSeparator + ruleName

parts := []string{eventBusName, ruleName}
id := strings.Join(parts, ruleResourceIDSeparator)

return id
}

func RuleParseID(id string) (string, string, error) {
parts := strings.Split(id, ruleIDSeparator)
func RuleParseResourceID(id string) (string, string, error) {
parts := strings.Split(id, ruleResourceIDSeparator)

if len(parts) == 1 && parts[0] != "" {
return DefaultEventBusName, parts[0], nil
}
if len(parts) == 2 && parts[0] != "" && parts[1] != "" {
return parts[0], parts[1], nil
}
if len(parts) > 2 {
i := strings.LastIndex(id, ruleIDSeparator)
busName := id[:i]
statementID := id[i+1:]
if partnerEventBusPattern.MatchString(busName) && statementID != "" {
return busName, statementID, nil
i := strings.LastIndex(id, ruleResourceIDSeparator)
eventBusName := id[:i]
ruleName := id[i+1:]
if eventBusARNPattern.MatchString(eventBusName) && ruleName != "" {
return eventBusName, ruleName, nil
}
if partnerEventBusPattern.MatchString(eventBusName) && ruleName != "" {
return eventBusName, ruleName, nil
}
}

return "", "", fmt.Errorf("unexpected format for ID (%q), expected <event-bus-name>"+ruleIDSeparator+"<rule-name> or <rule-name>", id)
return "", "", fmt.Errorf("unexpected format for ID (%[1]s), expected EVENTBUSNAME%[2]sRULENAME or RULENAME", id, ruleResourceIDSeparator)
}

// Terraform state IDs for Targets are not parseable, since the separator used ("-") is also a valid
// character in both the rule name and the target id.
// Terraform resource IDs for Targets are not parseable as the separator used ("-") is also a valid character in both the rule name and the target ID.

const targetIDSeparator = "-"
const targetResourceIDSeparator = "-"
const targetImportIDSeparator = "/"

func TargetCreateID(eventBusName, ruleName, targetID string) string {
id := ruleName + targetIDSeparator + targetID
if eventBusName != "" && eventBusName != DefaultEventBusName {
id = eventBusName + targetIDSeparator + id
func TargetCreateResourceID(eventBusName, ruleName, targetID string) string {
var parts []string

if eventBusName == "" || eventBusName == DefaultEventBusName {
parts = []string{ruleName, targetID}
} else {
parts = []string{eventBusName, ruleName, targetID}
}

id := strings.Join(parts, targetResourceIDSeparator)

return id
}

func TargetParseImportID(id string) (string, string, string, error) {
parts := strings.Split(id, targetImportIDSeparator)

if len(parts) == 2 && parts[0] != "" && parts[1] != "" {
return DefaultEventBusName, parts[0], parts[1], nil
}
Expand All @@ -88,12 +106,15 @@ func TargetParseImportID(id string) (string, string, string, error) {
iTarget := strings.LastIndex(id, targetImportIDSeparator)
targetID := id[iTarget+1:]
iRule := strings.LastIndex(id[:iTarget], targetImportIDSeparator)
busName := id[:iRule]
eventBusName := id[:iRule]
ruleName := id[iRule+1 : iTarget]
if partnerEventBusPattern.MatchString(busName) && ruleName != "" && targetID != "" {
return busName, ruleName, targetID, nil
if eventBusARNPattern.MatchString(eventBusName) && ruleName != "" && targetID != "" {
return eventBusName, ruleName, targetID, nil
}
if partnerEventBusPattern.MatchString(eventBusName) && ruleName != "" && targetID != "" {
return eventBusName, ruleName, targetID, nil
}
}

return "", "", "", fmt.Errorf("unexpected format for ID (%q), expected <event-bus-name>"+targetImportIDSeparator+"<rule-name>"+targetImportIDSeparator+"<target-id> or <rule-name>"+targetImportIDSeparator+"<target-id>", id)
return "", "", "", fmt.Errorf("unexpected format for ID (%[1]s), expected EVENTBUSNAME%[2]sRULENAME%[2]sTARGETID or RULENAME%[2]sTARGETID", id, targetImportIDSeparator)
}
37 changes: 28 additions & 9 deletions aws/internal/service/cloudwatchevents/id_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import (
tfevents "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/cloudwatchevents"
)

func TestPermissionParseID(t *testing.T) {
func TestPermissionParseResourceID(t *testing.T) {
testCases := []struct {
TestName string
InputID string
Expand All @@ -27,13 +27,13 @@ func TestPermissionParseID(t *testing.T) {
},
{
TestName: "two parts",
InputID: tfevents.PermissionCreateID("TestEventBus", "TestStatement"),
InputID: tfevents.PermissionCreateResourceID("TestEventBus", "TestStatement"),
ExpectedPart0: "TestEventBus",
ExpectedPart1: "TestStatement",
},
{
TestName: "two parts with default event bus",
InputID: tfevents.PermissionCreateID(tfevents.DefaultEventBusName, "TestStatement"),
InputID: tfevents.PermissionCreateResourceID(tfevents.DefaultEventBusName, "TestStatement"),
ExpectedPart0: tfevents.DefaultEventBusName,
ExpectedPart1: "TestStatement",
},
Expand Down Expand Up @@ -66,7 +66,7 @@ func TestPermissionParseID(t *testing.T) {

for _, testCase := range testCases {
t.Run(testCase.TestName, func(t *testing.T) {
gotPart0, gotPart1, err := tfevents.PermissionParseID(testCase.InputID)
gotPart0, gotPart1, err := tfevents.PermissionParseResourceID(testCase.InputID)

if err == nil && testCase.ExpectedError {
t.Fatalf("expected error, got no error")
Expand All @@ -87,7 +87,7 @@ func TestPermissionParseID(t *testing.T) {
}
}

func TestRuleParseID(t *testing.T) {
func TestRuleParseResourceID(t *testing.T) {
testCases := []struct {
TestName string
InputID string
Expand All @@ -108,22 +108,34 @@ func TestRuleParseID(t *testing.T) {
},
{
TestName: "two parts",
InputID: tfevents.RuleCreateID("TestEventBus", "TestRule"),
InputID: tfevents.RuleCreateResourceID("TestEventBus", "TestRule"),
ExpectedPart0: "TestEventBus",
ExpectedPart1: "TestRule",
},
{
TestName: "two parts with default event bus",
InputID: tfevents.RuleCreateID(tfevents.DefaultEventBusName, "TestRule"),
InputID: tfevents.RuleCreateResourceID(tfevents.DefaultEventBusName, "TestRule"),
ExpectedPart0: tfevents.DefaultEventBusName,
ExpectedPart1: "TestRule",
},
{
TestName: "partner event bus",
TestName: "partner event bus 1",
InputID: "aws.partner/example.com/Test/TestRule",
ExpectedPart0: "aws.partner/example.com/Test",
ExpectedPart1: "TestRule",
},
{
TestName: "partner event bus 2",
InputID: "aws.partner/foo.com/foo/18554d09-58ff-aa42-ba9c-c4c33899006f/test",
ExpectedPart0: "aws.partner/foo.com/foo/18554d09-58ff-aa42-ba9c-c4c33899006f",
ExpectedPart1: "test",
},
{
TestName: "ARN event bus",
InputID: tfevents.RuleCreateResourceID("arn:aws:events:us-east-2:123456789012:event-bus/default", "TestRule"),
ExpectedPart0: "arn:aws:events:us-east-2:123456789012:event-bus/default",
ExpectedPart1: "TestRule",
},
{
TestName: "empty both parts",
InputID: "/",
Expand Down Expand Up @@ -163,7 +175,7 @@ func TestRuleParseID(t *testing.T) {

for _, testCase := range testCases {
t.Run(testCase.TestName, func(t *testing.T) {
gotPart0, gotPart1, err := tfevents.RuleParseID(testCase.InputID)
gotPart0, gotPart1, err := tfevents.RuleParseResourceID(testCase.InputID)

if err == nil && testCase.ExpectedError {
t.Fatalf("expected error, got no error")
Expand Down Expand Up @@ -281,6 +293,13 @@ func TestTargetParseImportID(t *testing.T) {
ExpectedPart1: "TestRule",
ExpectedPart2: "TestTarget",
},
{
TestName: "ARN event bus",
InputID: "arn:aws:events:us-east-2:123456789012:event-bus/default/TestRule/TestTarget",
ExpectedPart0: "arn:aws:events:us-east-2:123456789012:event-bus/default",
ExpectedPart1: "TestRule",
ExpectedPart2: "TestTarget",
},
{
TestName: "empty partner event rule and target",
InputID: "aws.partner/example.com/Test//",
Expand Down
31 changes: 31 additions & 0 deletions aws/internal/service/cloudwatchevents/state.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package cloudwatchevents

import (
"fmt"

events "github.com/aws/aws-sdk-go/service/cloudwatchevents"
)

// RuleEnabledFromState infers from its state whether or not a rule is enabled.
func RuleEnabledFromState(state string) (bool, error) {
if state == events.RuleStateEnabled {
return true, nil
}

if state == events.RuleStateDisabled {
return false, nil
}

// We don't just blindly trust AWS as they tend to return
// unexpected values in similar cases (different casing etc.)
return false, fmt.Errorf("unable to infer enabled from state: %s", state)
}

// RuleStateFromEnabled returns a rule's state based on whether or not it is enabled.
func RuleStateFromEnabled(enabled bool) string {
if enabled {
return events.RuleStateEnabled
}

return events.RuleStateDisabled
}
Loading

0 comments on commit 71edd71

Please sign in to comment.