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

EventBridge Rule and Target: Allow event_bridge_name to be an ARN #20613

Merged
merged 26 commits into from
Aug 18, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
7855810
Delete dependabot.yml
richardowen Jul 22, 2021
cb7fa83
Add acceptance test for AWS EventBridge event bus configuration using…
francesco-fucile Jul 23, 2021
d43189f
EventBridge event bus rule, READ operation: update rule ID validation…
francesco-fucile Jul 23, 2021
2f31161
Fix TestAccAWSCloudWatchEventRule_EventBusArn test.
francesco-fucile Jul 23, 2021
3cdda95
Update READ function for event bus rule to preserve event bus ARNs.
francesco-fucile Jul 23, 2021
1a687b8
Don't import event bus rule on test.
francesco-fucile Jul 26, 2021
2e157d2
Reintroduce import tests for event bus rule.
francesco-fucile Jul 26, 2021
827a419
Allow for setting event_bus_name as ARN for aws_cloudwatch_event_target.
francesco-fucile Jul 26, 2021
7745f2d
Fix EventBusArn event bus target test.
francesco-fucile Jul 28, 2021
8ce9944
Revert "Delete dependabot.yml"
francesco-fucile Jul 29, 2021
f80e5f1
Add new TF configuration for event bus rule with arn test.
francesco-fucile Jul 29, 2021
62f5d87
Fix typo on event rule test.
francesco-fucile Jul 29, 2021
1a2803d
Remove event bus rule dependency on AWS_DEFAULT_REGION env var.
francesco-fucile Jul 29, 2021
9c61947
Fix event target test.
francesco-fucile Jul 30, 2021
5c40bc6
Remove reliance on EVENT_BRIDGE_EVENT_BUS_ARN env var.
francesco-fucile Aug 2, 2021
4388369
Remove unused function.
francesco-fucile Aug 3, 2021
1d5487d
Rename parameters for test function for clarity.
francesco-fucile Aug 4, 2021
a0e8d69
Add CHANGELOG entry.
ewbankkit Aug 18, 2021
1770fa3
Rename CloudWatch Events resource ID functions.
ewbankkit Aug 18, 2021
aba89ae
Support event bus ARN when parsing Rule resource ID and Target import…
ewbankkit Aug 18, 2021
9c33e57
Consistently use 'events' as an import alias for 'github.com/aws/aws-…
ewbankkit Aug 18, 2021
8aa6c44
r/aws_cloudwatch_event_rule: Use event bus name from resource ID as A…
ewbankkit Aug 18, 2021
13927fc
r/aws_cloudwatch_event_target: Fix GovCloud test failure.
ewbankkit Aug 18, 2021
6f5e51b
Fix terrafmt errors.
ewbankkit Aug 18, 2021
33fc9ad
Fix terrafmt errors.
ewbankkit Aug 18, 2021
351cd8a
Fix terrafmt errors.
ewbankkit Aug 18, 2021
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
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