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

[datadog_integration_aws_event_bridge] Add support for AWS Event Bridge integration #2204

Merged
merged 24 commits into from
Jan 3, 2024
Merged
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
bf4df5d
run generation
skarimo Dec 8, 2023
9055325
WIP - Update to handle eventbridge api request and response data
ktmq Dec 18, 2023
411d384
Update tests
ktmq Dec 18, 2023
5f6f523
Update error message
ktmq Dec 19, 2023
571533f
Fix delete request
ktmq Dec 19, 2023
2e7a642
Fix example
ktmq Dec 19, 2023
19cfc39
Fix delete request to use constructed ID instead of user supplied name
ktmq Dec 20, 2023
45a6862
Delete and import are working
ktmq Dec 21, 2023
168b832
Remove debug logs and add error message for Update not allowed
ktmq Dec 21, 2023
360891d
Add mutex
ktmq Dec 21, 2023
6eacfc0
Acceptance test working, everything working except create_event_bus=true
ktmq Dec 21, 2023
b5bf579
Fix create event bus import and remove unneeded var
ktmq Dec 22, 2023
a5d6173
Docs
ktmq Dec 22, 2023
c4d350a
re-record
skarimo Dec 22, 2023
8ebb6d3
shared mutex and add RequiresReplace
skarimo Dec 22, 2023
34ce880
Update docs/resources/integration_aws_event_bridge.md
ktmq Dec 26, 2023
dbfa557
Update docs/resources/integration_aws_event_bridge.md
ktmq Dec 26, 2023
c401f81
Update docs for consistency
ktmq Dec 26, 2023
65107bd
Default create_event_bus to true, remove redundant value and update test
ktmq Jan 2, 2024
796d2c0
make docs
ktmq Jan 2, 2024
f335d3d
Merge remote-tracking branch 'origin/master' into sherz/generate-aws-…
ktmq Jan 2, 2024
ce5c01f
make fmt
ktmq Jan 2, 2024
92bef05
make docs after make fmt
ktmq Jan 2, 2024
823d85d
Update datadog/fwprovider/resource_datadog_integration_aws_event_brid…
ktmq Jan 3, 2024
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
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
"github.com/hashicorp/terraform-plugin-framework/path"
"github.com/hashicorp/terraform-plugin-framework/types"

"github.com/terraform-providers/terraform-provider-datadog/datadog/internal/utils"
)

Expand Down
1 change: 1 addition & 0 deletions datadog/fwprovider/framework_provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ var Resources = []func() resource.Resource{
NewDashboardListResource,
NewDowntimeScheduleResource,
NewIntegrationAzureResource,
NewIntegrationAwsEventBridgeResource,
NewIntegrationCloudflareAccountResource,
NewIntegrationConfluentAccountResource,
NewIntegrationConfluentResourceResource,
Expand Down
257 changes: 257 additions & 0 deletions datadog/fwprovider/resource_datadog_integration_aws_event_bridge.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,257 @@
package fwprovider

import (
"context"

"github.com/DataDog/datadog-api-client-go/v2/api/datadogV1"
"github.com/hashicorp/terraform-plugin-framework/diag"
frameworkPath "github.com/hashicorp/terraform-plugin-framework/path"
"github.com/hashicorp/terraform-plugin-framework/resource"
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/boolplanmodifier"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
"github.com/hashicorp/terraform-plugin-framework/types"

"github.com/terraform-providers/terraform-provider-datadog/datadog/internal/utils"
)

var (
_ resource.ResourceWithConfigure = &integrationAwsEventBridgeResource{}
_ resource.ResourceWithImportState = &integrationAwsEventBridgeResource{}
)

type integrationAwsEventBridgeResource struct {
Api *datadogV1.AWSIntegrationApi
Auth context.Context
}

type integrationAwsEventBridgeModel struct {
ID types.String `tfsdk:"id"`
AccountId types.String `tfsdk:"account_id"`
CreateEventBus types.Bool `tfsdk:"create_event_bus"`
EventGeneratorName types.String `tfsdk:"event_generator_name"`
Region types.String `tfsdk:"region"`
}

func NewIntegrationAwsEventBridgeResource() resource.Resource {
return &integrationAwsEventBridgeResource{}
}

func (r *integrationAwsEventBridgeResource) Configure(_ context.Context, request resource.ConfigureRequest, response *resource.ConfigureResponse) {
providerData, _ := request.ProviderData.(*FrameworkProvider)
r.Api = providerData.DatadogApiInstances.GetAWSIntegrationApiV1()
r.Auth = providerData.Auth
}

func (r *integrationAwsEventBridgeResource) Metadata(_ context.Context, request resource.MetadataRequest, response *resource.MetadataResponse) {
response.TypeName = "integration_aws_event_bridge"
}

func (r *integrationAwsEventBridgeResource) Schema(_ context.Context, _ resource.SchemaRequest, response *resource.SchemaResponse) {
response.Schema = schema.Schema{
Description: "Provides a Datadog - Amazon Web Services integration EventBridge resource. This can be used to create and manage Event Sources for each Datadog integrated AWS account.",
Attributes: map[string]schema.Attribute{
"account_id": schema.StringAttribute{
Description: "Your AWS Account ID without dashes.",
Required: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.RequiresReplace(),
},
},
"create_event_bus": schema.BoolAttribute{
Computed: true,
Default: booldefault.StaticBool(true),
Description: "True if Datadog should create the event bus in addition to the event source. Requires the `events:CreateEventBus` permission.",
Optional: true,
PlanModifiers: []planmodifier.Bool{
boolplanmodifier.RequiresReplace(),
},
},
"event_generator_name": schema.StringAttribute{
Description: "The given part of the event source name, which is then combined with an assigned suffix to form the full name.",
Required: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.RequiresReplace(),
},
},
"region": schema.StringAttribute{
Description: "The event source's [AWS region](https://docs.aws.amazon.com/general/latest/gr/rande.html#regional-endpoints).",
Required: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.RequiresReplace(),
},
},
"id": utils.ResourceIDAttribute(),
},
}
}

func (r *integrationAwsEventBridgeResource) ImportState(ctx context.Context, request resource.ImportStateRequest, response *resource.ImportStateResponse) {
resource.ImportStatePassthroughID(ctx, frameworkPath.Root("id"), request, response)
}

func (r *integrationAwsEventBridgeResource) Read(ctx context.Context, request resource.ReadRequest, response *resource.ReadResponse) {
var state integrationAwsEventBridgeModel
response.Diagnostics.Append(request.State.Get(ctx, &state)...)
if response.Diagnostics.HasError() {
return
}
resp, httpResp, err := r.Api.ListAWSEventBridgeSources(r.Auth)
if err != nil {
if httpResp != nil && httpResp.StatusCode == 404 {
response.State.RemoveResource(ctx)
return
}
response.Diagnostics.Append(utils.FrameworkErrorDiag(err, "Error listing AWS EventBridge Event Sources"))
return
}
if err := utils.CheckForUnparsed(resp); err != nil {
response.Diagnostics.AddError("response contains unparsedObject", err.Error())
return
}

found := false
matchedEventHub := integrationAwsEventBridgeModel{}

if accounts, ok := resp.GetAccountsOk(); ok && len(*accounts) > 0 {
for _, account := range *accounts {
if found {
break
}
if eventhubs, ok := account.GetEventHubsOk(); ok && len(*eventhubs) > 0 {
for _, eventhub := range *eventhubs {
if *eventhub.Name == state.ID.ValueString() {
matchedEventHub.ID = types.StringValue(*eventhub.Name)
matchedEventHub.AccountId = types.StringValue(account.GetAccountId())
matchedEventHub.Region = types.StringValue(eventhub.GetRegion())
found = true
break
}
}
}
}
}

if !found {
response.State.RemoveResource(ctx)
response.Diagnostics.AddError("Import error", "No matching Event Source found")
ktmq marked this conversation as resolved.
Show resolved Hide resolved
return
}

r.updateStateAfterRead(ctx, &state, &matchedEventHub)
// Save data into Terraform state
response.Diagnostics.Append(response.State.Set(ctx, &state)...)
}

func (r *integrationAwsEventBridgeResource) Create(ctx context.Context, request resource.CreateRequest, response *resource.CreateResponse) {
var state integrationAwsEventBridgeModel
response.Diagnostics.Append(request.Plan.Get(ctx, &state)...)
if response.Diagnostics.HasError() {
return
}

utils.IntegrationAwsMutex.Lock()
defer utils.IntegrationAwsMutex.Unlock()

body, diags := r.buildIntegrationAwsEventBridgeRequestBody(ctx, &state)
response.Diagnostics.Append(diags...)
if response.Diagnostics.HasError() {
return
}

resp, _, err := r.Api.CreateAWSEventBridgeSource(r.Auth, *body)
if err != nil {
response.Diagnostics.Append(utils.FrameworkErrorDiag(err, "Error creating AWS EventBridge Event Source"))
return
}
if err := utils.CheckForUnparsed(resp); err != nil {
response.Diagnostics.AddError("response contains unparsedObject", err.Error())
return
}
r.updateStateAfterWrite(ctx, &state, &resp)

// Save data into Terraform state
response.Diagnostics.Append(response.State.Set(ctx, &state)...)
}

func (r *integrationAwsEventBridgeResource) Update(ctx context.Context, request resource.UpdateRequest, response *resource.UpdateResponse) {
response.Diagnostics.AddError("Update not supported", "AWS EventBridge Event Sources cannot be updated")
}

func (r *integrationAwsEventBridgeResource) Delete(ctx context.Context, request resource.DeleteRequest, response *resource.DeleteResponse) {
var state integrationAwsEventBridgeModel
response.Diagnostics.Append(request.State.Get(ctx, &state)...)
if response.Diagnostics.HasError() {
return
}

utils.IntegrationAwsMutex.Lock()
defer utils.IntegrationAwsMutex.Unlock()

req := datadogV1.NewAWSEventBridgeDeleteRequestWithDefaults()

if !state.AccountId.IsNull() {
req.SetAccountId(state.AccountId.ValueString())
}
if !state.Region.IsNull() {
req.SetRegion(state.Region.ValueString())
}
if !state.ID.IsNull() {
// EventGeneratorName in DeleteRequest is the constructed full name, stored as ID in state
req.SetEventGeneratorName(state.ID.ValueString())
}

_, httpResp, err := r.Api.DeleteAWSEventBridgeSource(r.Auth, *req)
if err != nil {
if httpResp != nil && httpResp.StatusCode == 404 {
response.Diagnostics.Append(utils.FrameworkErrorDiag(err, "AWS EventBridge Event Source not found"))
return
}
response.Diagnostics.Append(utils.FrameworkErrorDiag(err, "Error deleting AWS EventBridge Event Source"))
return
}
}

func (r *integrationAwsEventBridgeResource) updateStateAfterRead(ctx context.Context, state *integrationAwsEventBridgeModel, resp *integrationAwsEventBridgeModel) {
state.ID = resp.ID
state.AccountId = resp.AccountId
state.Region = resp.Region
}

func (r *integrationAwsEventBridgeResource) updateStateAfterWrite(ctx context.Context, state *integrationAwsEventBridgeModel, resp *datadogV1.AWSEventBridgeCreateResponse) {

if createEventBus, ok := resp.GetHasBusOk(); ok {
state.CreateEventBus = types.BoolValue(*createEventBus)
}

if eventSourceName, ok := resp.GetEventSourceNameOk(); ok {
// Use EventSourceName as ID
state.ID = types.StringValue(*eventSourceName)
}

if region, ok := resp.GetRegionOk(); ok {
state.Region = types.StringValue(*region)
}
}

func (r *integrationAwsEventBridgeResource) buildIntegrationAwsEventBridgeRequestBody(ctx context.Context, state *integrationAwsEventBridgeModel) (*datadogV1.AWSEventBridgeCreateRequest, diag.Diagnostics) {
diags := diag.Diagnostics{}
req := datadogV1.NewAWSEventBridgeCreateRequestWithDefaults()

if !state.AccountId.IsNull() {
req.SetAccountId(state.AccountId.ValueString())
}
if !state.CreateEventBus.IsNull() {
req.SetCreateEventBus(state.CreateEventBus.ValueBool())
}
if !state.Region.IsNull() {
req.SetRegion(state.Region.ValueString())
}
if !state.EventGeneratorName.IsNull() {
req.SetEventGeneratorName(state.EventGeneratorName.ValueString())
}

return req, diags
}
4 changes: 4 additions & 0 deletions datadog/internal/utils/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"net/url"
"os"
"strings"
"sync"

"github.com/DataDog/datadog-api-client-go/v2/api/datadog"
frameworkDiag "github.com/hashicorp/terraform-plugin-framework/diag"
Expand Down Expand Up @@ -70,6 +71,9 @@ var APIUrlEnvVars = []string{DDAPIUrlEnvName, DatadogAPIUrlEnvName}
// DatadogProvider holds a reference to the provider
var DatadogProvider *schema.Provider

// IntegrationAwsMutex mutex for AWS Integration resources
var IntegrationAwsMutex = sync.Mutex{}

// Resource minimal interface common to ResourceData and ResourceDiff
type Resource interface {
Get(string) interface{}
Expand Down
14 changes: 6 additions & 8 deletions datadog/resource_datadog_integration_aws.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
"os"
"regexp"
"strconv"
"sync"

"github.com/terraform-providers/terraform-provider-datadog/datadog/internal/utils"
"github.com/terraform-providers/terraform-provider-datadog/datadog/internal/validators"
Expand All @@ -17,7 +16,6 @@ import (
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
)

var integrationAwsMutex = sync.Mutex{}
var accountAndRoleNameIDRegex = regexp.MustCompile("[\\d]+:.*")

func resourceDatadogIntegrationAws() *schema.Resource {
Expand Down Expand Up @@ -185,8 +183,8 @@ func resourceDatadogIntegrationAwsCreate(ctx context.Context, d *schema.Resource
apiInstances := providerConf.DatadogApiInstances
auth := providerConf.Auth

integrationAwsMutex.Lock()
defer integrationAwsMutex.Unlock()
utils.IntegrationAwsMutex.Lock()
defer utils.IntegrationAwsMutex.Unlock()

iaws := buildDatadogIntegrationAwsStruct(d)
response, httpresp, err := apiInstances.GetAWSIntegrationApiV1().CreateAWSAccount(auth, *iaws)
Expand Down Expand Up @@ -266,8 +264,8 @@ func resourceDatadogIntegrationAwsUpdate(ctx context.Context, d *schema.Resource
providerConf := meta.(*ProviderConfiguration)
apiInstances := providerConf.DatadogApiInstances
auth := providerConf.Auth
integrationAwsMutex.Lock()
defer integrationAwsMutex.Unlock()
utils.IntegrationAwsMutex.Lock()
defer utils.IntegrationAwsMutex.Unlock()

iaws := buildDatadogIntegrationAwsStruct(d)

Expand Down Expand Up @@ -323,8 +321,8 @@ func resourceDatadogIntegrationAwsDelete(ctx context.Context, d *schema.Resource
providerConf := meta.(*ProviderConfiguration)
apiInstances := providerConf.DatadogApiInstances
auth := providerConf.Auth
integrationAwsMutex.Lock()
defer integrationAwsMutex.Unlock()
utils.IntegrationAwsMutex.Lock()
defer utils.IntegrationAwsMutex.Unlock()

iaws := buildDatadogIntegrationAwsDeleteStruct(d)

Expand Down
8 changes: 4 additions & 4 deletions datadog/resource_datadog_integration_aws_lambda_arn.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,8 @@ func resourceDatadogIntegrationAwsLambdaArnCreate(ctx context.Context, d *schema
auth := providerConf.Auth

// shared with datadog_integration_aws resource
integrationAwsMutex.Lock()
defer integrationAwsMutex.Unlock()
utils.IntegrationAwsMutex.Lock()
defer utils.IntegrationAwsMutex.Unlock()

attachLambdaArnRequest := buildDatadogIntegrationAwsLambdaArnStruct(d)
response, httpresp, err := apiInstances.GetAWSLogsIntegrationApiV1().CreateAWSLambdaARN(auth, *attachLambdaArnRequest)
Expand Down Expand Up @@ -121,8 +121,8 @@ func resourceDatadogIntegrationAwsLambdaArnDelete(ctx context.Context, d *schema
auth := providerConf.Auth

// shared with datadog_integration_aws resource
integrationAwsMutex.Lock()
defer integrationAwsMutex.Unlock()
utils.IntegrationAwsMutex.Lock()
defer utils.IntegrationAwsMutex.Unlock()

accountID, lambdaArn, err := utils.AccountAndLambdaArnFromID(d.Id())
if err != nil {
Expand Down
12 changes: 6 additions & 6 deletions datadog/resource_datadog_integration_aws_log_collection.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,8 @@ func resourceDatadogIntegrationAwsLogCollectionCreate(ctx context.Context, d *sc
auth := providerConf.Auth

// shared with datadog_integration_aws resource
integrationAwsMutex.Lock()
defer integrationAwsMutex.Unlock()
utils.IntegrationAwsMutex.Lock()
defer utils.IntegrationAwsMutex.Unlock()

accountID := d.Get("account_id").(string)

Expand Down Expand Up @@ -93,8 +93,8 @@ func resourceDatadogIntegrationAwsLogCollectionUpdate(ctx context.Context, d *sc
auth := providerConf.Auth

// shared with datadog_integration_aws resource
integrationAwsMutex.Lock()
defer integrationAwsMutex.Unlock()
utils.IntegrationAwsMutex.Lock()
defer utils.IntegrationAwsMutex.Unlock()

enableLogCollectionServices := buildDatadogIntegrationAwsLogCollectionStruct(d)
_, httpresp, err := apiInstances.GetAWSLogsIntegrationApiV1().EnableAWSLogServices(auth, *enableLogCollectionServices)
Expand Down Expand Up @@ -141,8 +141,8 @@ func resourceDatadogIntegrationAwsLogCollectionDelete(ctx context.Context, d *sc
auth := providerConf.Auth

// shared with datadog_integration_aws resource
integrationAwsMutex.Lock()
defer integrationAwsMutex.Unlock()
utils.IntegrationAwsMutex.Lock()
defer utils.IntegrationAwsMutex.Unlock()

accountID := d.Id()
services := []string{}
Expand Down
Loading