Skip to content

Commit f94a12b

Browse files
committed
chore(cloudamqp_integration_aws_eventbridge): Migrate towards terraform plugin framework
This is the first resource to move towards the new system and should serve as a template for all others. The reason to choose this one as the first is because it is the smallest file that still has tests to prove everything still works as before.
1 parent d5f7727 commit f94a12b

File tree

2 files changed

+203
-117
lines changed

2 files changed

+203
-117
lines changed

cloudamqp/provider.go

Lines changed: 26 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,11 @@ func (p *cloudamqpProvider) DataSources(_ context.Context) []func() datasource.D
9999
}
100100

101101
func (p *cloudamqpProvider) Resources(_ context.Context) []func() resource.Resource {
102-
return []func() resource.Resource{}
102+
return []func() resource.Resource{
103+
func() resource.Resource {
104+
return &awsEventBridgeResource{}
105+
},
106+
}
103107
}
104108

105109
func New(version string, client *http.Client) provider.Provider {
@@ -145,28 +149,27 @@ func Provider(v string, client *http.Client) *schemaSdk.Provider {
145149
"cloudamqp_vpc_info": dataSourceVpcInfo(),
146150
},
147151
ResourcesMap: map[string]*schemaSdk.Resource{
148-
"cloudamqp_account_action": resourceAccountAction(),
149-
"cloudamqp_alarm": resourceAlarm(),
150-
"cloudamqp_custom_domain": resourceCustomDomain(),
151-
"cloudamqp_extra_disk_size": resourceExtraDiskSize(),
152-
"cloudamqp_instance": resourceInstance(),
153-
"cloudamqp_integration_aws_eventbridge": resourceAwsEventBridge(),
154-
"cloudamqp_integration_log": resourceIntegrationLog(),
155-
"cloudamqp_integration_metric": resourceIntegrationMetric(),
156-
"cloudamqp_node_actions": resourceNodeAction(),
157-
"cloudamqp_notification": resourceNotification(),
158-
"cloudamqp_plugin_community": resourcePluginCommunity(),
159-
"cloudamqp_plugin": resourcePlugin(),
160-
"cloudamqp_privatelink_aws": resourcePrivateLinkAws(),
161-
"cloudamqp_privatelink_azure": resourcePrivateLinkAzure(),
162-
"cloudamqp_rabbitmq_configuration": resourceRabbitMqConfiguration(),
163-
"cloudamqp_security_firewall": resourceSecurityFirewall(),
164-
"cloudamqp_upgrade_rabbitmq": resourceUpgradeRabbitMQ(),
165-
"cloudamqp_vpc_connect": resourceVpcConnect(),
166-
"cloudamqp_vpc_gcp_peering": resourceVpcGcpPeering(),
167-
"cloudamqp_vpc_peering": resourceVpcPeering(),
168-
"cloudamqp_vpc": resourceVpc(),
169-
"cloudamqp_webhook": resourceWebhook(),
152+
"cloudamqp_account_action": resourceAccountAction(),
153+
"cloudamqp_alarm": resourceAlarm(),
154+
"cloudamqp_custom_domain": resourceCustomDomain(),
155+
"cloudamqp_extra_disk_size": resourceExtraDiskSize(),
156+
"cloudamqp_instance": resourceInstance(),
157+
"cloudamqp_integration_log": resourceIntegrationLog(),
158+
"cloudamqp_integration_metric": resourceIntegrationMetric(),
159+
"cloudamqp_node_actions": resourceNodeAction(),
160+
"cloudamqp_notification": resourceNotification(),
161+
"cloudamqp_plugin_community": resourcePluginCommunity(),
162+
"cloudamqp_plugin": resourcePlugin(),
163+
"cloudamqp_privatelink_aws": resourcePrivateLinkAws(),
164+
"cloudamqp_privatelink_azure": resourcePrivateLinkAzure(),
165+
"cloudamqp_rabbitmq_configuration": resourceRabbitMqConfiguration(),
166+
"cloudamqp_security_firewall": resourceSecurityFirewall(),
167+
"cloudamqp_upgrade_rabbitmq": resourceUpgradeRabbitMQ(),
168+
"cloudamqp_vpc_connect": resourceVpcConnect(),
169+
"cloudamqp_vpc_gcp_peering": resourceVpcGcpPeering(),
170+
"cloudamqp_vpc_peering": resourceVpcPeering(),
171+
"cloudamqp_vpc": resourceVpc(),
172+
"cloudamqp_webhook": resourceWebhook(),
170173
},
171174
ConfigureFunc: configureClient(client),
172175
}
Lines changed: 177 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -1,158 +1,241 @@
11
package cloudamqp
22

33
import (
4+
"context"
5+
"encoding/json"
46
"fmt"
57
"log"
68
"strconv"
79
"strings"
810

911
"github.com/cloudamqp/terraform-provider-cloudamqp/api"
10-
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
12+
"github.com/hashicorp/terraform-plugin-framework/path"
13+
"github.com/hashicorp/terraform-plugin-framework/resource"
14+
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
15+
"github.com/hashicorp/terraform-plugin-framework/resource/schema/boolplanmodifier"
16+
"github.com/hashicorp/terraform-plugin-framework/resource/schema/int64planmodifier"
17+
"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
18+
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
19+
"github.com/hashicorp/terraform-plugin-framework/types"
1120
)
1221

13-
func resourceAwsEventBridge() *schema.Resource {
14-
return &schema.Resource{
15-
Create: resourceAwsEventBridgeCreate,
16-
Read: resourceAwsEventBridgeRead,
17-
Delete: resourceAwsEventBridgeDelete,
18-
Importer: &schema.ResourceImporter{
19-
StateContext: schema.ImportStatePassthroughContext,
20-
},
21-
Schema: map[string]*schema.Schema{
22-
"instance_id": {
23-
Type: schema.TypeInt,
24-
ForceNew: true,
22+
type awsEventBridgeResource struct {
23+
client *api.API
24+
}
25+
26+
type awsEventBridgeResourceModel struct {
27+
Id types.String `tfsdk:"id"`
28+
InstanceID types.Int64 `tfsdk:"instance_id"`
29+
AwsAccountId types.String `tfsdk:"aws_account_id"`
30+
AwsRegion types.String `tfsdk:"aws_region"`
31+
Vhost types.String `tfsdk:"vhost"`
32+
QueueName types.String `tfsdk:"queue"`
33+
WithHeaders types.Bool `tfsdk:"with_headers"`
34+
Status types.String `tfsdk:"status"`
35+
}
36+
37+
type awsEventBridgeResourceApiModel struct {
38+
AwsAccountId string `json:"aws_account_id"`
39+
AwsRegion string `json:"aws_region"`
40+
Vhost string `json:"vhost"`
41+
QueueName string `json:"queue"`
42+
WithHeaders bool `json:"with_headers"`
43+
}
44+
45+
func (r *awsEventBridgeResource) Configure(ctx context.Context, request resource.ConfigureRequest, response *resource.ConfigureResponse) {
46+
// Always perform a nil check when handling ProviderData because Terraform
47+
// sets that data after it calls the ConfigureProvider RPC.
48+
if request.ProviderData == nil {
49+
return
50+
}
51+
52+
client, ok := request.ProviderData.(*api.API)
53+
54+
if !ok {
55+
response.Diagnostics.AddError(
56+
"Unexpected Resource Configure Type",
57+
fmt.Sprintf("Expected *api.API, got: %T. Please report this issue to the provider developers.", request.ProviderData),
58+
)
59+
60+
return
61+
}
62+
63+
r.client = client
64+
}
65+
66+
func (r *awsEventBridgeResource) Metadata(ctx context.Context, request resource.MetadataRequest, response *resource.MetadataResponse) {
67+
response.TypeName = "cloudamqp_integration_aws_eventbridge"
68+
}
69+
70+
func (r *awsEventBridgeResource) Schema(ctx context.Context, request resource.SchemaRequest, response *resource.SchemaResponse) {
71+
response.Schema = schema.Schema{
72+
Attributes: map[string]schema.Attribute{
73+
"id": schema.StringAttribute{
74+
Computed: true,
75+
},
76+
"instance_id": schema.Int64Attribute{
2577
Required: true,
2678
Description: "Instance identifier",
79+
PlanModifiers: []planmodifier.Int64{
80+
int64planmodifier.RequiresReplace(),
81+
},
2782
},
28-
"aws_account_id": {
29-
Type: schema.TypeString,
30-
ForceNew: true,
83+
"aws_account_id": schema.StringAttribute{
3184
Required: true,
3285
Description: "The 12 digit AWS Account ID where you want the events to be sent to.",
86+
PlanModifiers: []planmodifier.String{
87+
stringplanmodifier.RequiresReplace(),
88+
},
3389
},
34-
"aws_region": {
35-
Type: schema.TypeString,
36-
ForceNew: true,
90+
"aws_region": schema.StringAttribute{
3791
Required: true,
3892
Description: "The AWS region where you the events to be sent to. (e.g. us-west-1, us-west-2, ..., etc.)",
93+
PlanModifiers: []planmodifier.String{
94+
stringplanmodifier.RequiresReplace(),
95+
},
3996
},
40-
"vhost": {
41-
Type: schema.TypeString,
42-
ForceNew: true,
97+
"vhost": schema.StringAttribute{
4398
Required: true,
4499
Description: "The VHost the queue resides in.",
100+
PlanModifiers: []planmodifier.String{
101+
stringplanmodifier.RequiresReplace(),
102+
},
45103
},
46-
"queue": {
47-
Type: schema.TypeString,
48-
ForceNew: true,
104+
"queue": schema.StringAttribute{
49105
Required: true,
50106
Description: "A (durable) queue on your RabbitMQ instance.",
107+
PlanModifiers: []planmodifier.String{
108+
stringplanmodifier.RequiresReplace(),
109+
},
51110
},
52-
"with_headers": {
53-
Type: schema.TypeBool,
54-
ForceNew: true,
111+
"with_headers": schema.BoolAttribute{
55112
Required: true,
56113
Description: "Include message headers in the event data.",
114+
PlanModifiers: []planmodifier.Bool{
115+
boolplanmodifier.RequiresReplace(),
116+
},
57117
},
58-
"status": {
59-
Type: schema.TypeString,
118+
"status": schema.StringAttribute{
60119
Computed: true,
61120
Description: "Always set to null, unless there is an error starting the EventBridge",
62121
},
63122
},
64123
}
65124
}
66125

67-
func resourceAwsEventBridgeCreate(d *schema.ResourceData, meta interface{}) error {
68-
var (
69-
api = meta.(*api.API)
70-
keys = awsEventbridgeAttributeKeys()
71-
params = make(map[string]interface{})
72-
instanceID = d.Get("instance_id").(int)
73-
)
126+
func (r *awsEventBridgeResource) Create(ctx context.Context, request resource.CreateRequest, response *resource.CreateResponse) {
127+
var data awsEventBridgeResourceModel
128+
129+
// Read Terraform plan data into the model
130+
response.Diagnostics.Append(request.Plan.Get(ctx, &data)...)
131+
132+
if response.Diagnostics.HasError() {
133+
return
134+
}
135+
136+
apiModel := awsEventBridgeResourceApiModel{
137+
AwsAccountId: data.AwsAccountId.ValueString(),
138+
AwsRegion: data.AwsRegion.ValueString(),
139+
Vhost: data.Vhost.ValueString(),
140+
QueueName: data.QueueName.ValueString(),
141+
WithHeaders: data.WithHeaders.ValueBool(),
142+
}
74143

75-
for _, k := range keys {
76-
if v := d.Get(k); v != nil {
77-
params[k] = v
78-
}
144+
var params map[string]interface{}
145+
temp, err := json.Marshal(apiModel)
146+
if err != nil {
147+
response.Diagnostics.AddError(
148+
"Unable to Create Resource",
149+
"An unexpected error occurred while creating the resource create request. "+
150+
"Please report this issue to the provider developers.\n\n"+
151+
"JSON Error: "+err.Error(),
152+
)
153+
return
79154
}
155+
// TODO: This is totally a hack to get the struct into a map[string]interface{}
156+
// It is very unlikely this will fail after the first one succeeds, so it should be fine to ignore the error
157+
// Maybe after the api is moved into the repo we can improve the interface
158+
_ = json.Unmarshal(temp, &params)
80159

81-
data, err := api.CreateAwsEventBridge(instanceID, params)
160+
apiResponse, err := r.client.CreateAwsEventBridge(int(data.InstanceID.ValueInt64()), params)
82161
if err != nil {
83-
return err
162+
response.Diagnostics.AddError(
163+
"Failed to Create Resource",
164+
"An error occurred while calling the api to create the surface, verify your permissions are correct.\n\n"+
165+
"JSON Error: "+err.Error(),
166+
)
167+
return
84168
}
85169

86-
d.SetId(data["id"].(string))
87-
return nil
170+
data.Id = types.StringValue(apiResponse["id"].(string))
171+
data.Status = types.StringNull()
172+
173+
// Save data into Terraform state
174+
response.Diagnostics.Append(response.State.Set(ctx, &data)...)
88175
}
89176

90-
func resourceAwsEventBridgeRead(d *schema.ResourceData, meta interface{}) error {
91-
if strings.Contains(d.Id(), ",") {
92-
log.Printf("[DEBUG] cloudamqp::resource::aws-eventbridge::read id contains : %v", d.Id())
93-
s := strings.Split(d.Id(), ",")
177+
func (r *awsEventBridgeResource) Read(ctx context.Context, request resource.ReadRequest, response *resource.ReadResponse) {
178+
var state awsEventBridgeResourceModel
179+
180+
// Read Terraform plan data into the model
181+
response.Diagnostics.Append(request.State.Get(ctx, &state)...)
182+
183+
if strings.Contains(state.Id.ValueString(), ",") {
184+
log.Printf("[DEBUG] cloudamqp::resource::aws-eventbridge::read id contains : %v", state.Id.String())
185+
s := strings.Split(state.Id.ValueString(), ",")
94186
log.Printf("[DEBUG] cloudamqp::resource::aws-eventbridge::read split ids: %v, %v", s[0], s[1])
95-
d.SetId(s[0])
187+
state.Id = types.StringValue(s[0])
96188
instanceID, _ := strconv.Atoi(s[1])
97-
d.Set("instance_id", instanceID)
189+
state.InstanceID = types.Int64Value(int64(instanceID))
98190
}
99-
if d.Get("instance_id").(int) == 0 {
100-
return fmt.Errorf("missing instance identifier: {resource_id},{instance_id}")
191+
if state.InstanceID.ValueInt64() == 0 {
192+
response.Diagnostics.AddError("Missing instance identifier {resource_id},{instance_id}", "")
193+
return
101194
}
102195

103196
var (
104-
api = meta.(*api.API)
105-
instanceID = d.Get("instance_id").(int)
197+
id = state.Id.ValueString()
198+
instanceID = int(state.InstanceID.ValueInt64())
106199
)
107200

108-
log.Printf("[DEBUG] cloudamqp::resource::aws-eventbridge::read ID: %v, instanceID %v", d.Id(), instanceID)
109-
data, err := api.ReadAwsEventBridge(instanceID, d.Id())
201+
log.Printf("[DEBUG] cloudamqp::resource::aws-eventbridge::read ID: %v, instanceID %v", id, instanceID)
202+
data, err := r.client.ReadAwsEventBridge(instanceID, id)
110203
if err != nil {
111-
return err
204+
response.Diagnostics.AddError("Something went wrong while reading the aws event bridge", fmt.Sprintf("%v", err))
205+
return
112206
}
113207

114-
for k, v := range data {
115-
if validateAwsEventBridgeSchemaAttribute(k) {
116-
if v == nil {
117-
continue
118-
}
119-
if err = d.Set(k, v); err != nil {
120-
return fmt.Errorf("error setting %s for resource %s: %s", k, d.Id(), err)
121-
}
122-
}
123-
}
208+
state.AwsAccountId = types.StringValue(data["aws_account_id"].(string))
209+
state.AwsRegion = types.StringValue(data["aws_region"].(string))
210+
state.Vhost = types.StringValue(data["vhost"].(string))
211+
state.QueueName = types.StringValue(data["queue"].(string))
212+
state.WithHeaders = types.BoolValue(data["with_headers"].(bool))
124213

125-
return nil
126-
}
214+
// Save data into Terraform state
215+
response.Diagnostics.Append(response.State.Set(ctx, &state)...)
127216

128-
func resourceAwsEventBridgeDelete(d *schema.ResourceData, meta interface{}) error {
129-
var (
130-
api = meta.(*api.API)
131-
instanceID = d.Get("instance_id").(int)
132-
)
217+
return
218+
}
133219

134-
return api.DeleteAwsEventBridge(instanceID, d.Id())
220+
func (r *awsEventBridgeResource) Update(ctx context.Context, request resource.UpdateRequest, response *resource.UpdateResponse) {
221+
// This resource does not implement the Update function
135222
}
136223

137-
func awsEventbridgeAttributeKeys() []string {
138-
return []string{
139-
"aws_account_id",
140-
"aws_region",
141-
"vhost",
142-
"queue",
143-
"with_headers",
224+
func (r *awsEventBridgeResource) Delete(ctx context.Context, request resource.DeleteRequest, response *resource.DeleteResponse) {
225+
var data awsEventBridgeResourceModel
226+
227+
// Read Terraform plan data into the model
228+
response.Diagnostics.Append(request.State.Get(ctx, &data)...)
229+
var id = data.Id.ValueString()
230+
err := r.client.DeleteAwsEventBridge(int(data.InstanceID.ValueInt64()), id)
231+
232+
if err != nil {
233+
response.Diagnostics.AddError("An error occurred while deleting cloudamqp_integration_aws_eventbridge",
234+
fmt.Sprintf("Error deleting Cloudamqp event bridge %s: %s", id, err),
235+
)
144236
}
145237
}
146238

147-
func validateAwsEventBridgeSchemaAttribute(key string) bool {
148-
switch key {
149-
case "aws_account_id",
150-
"aws_region",
151-
"vhost",
152-
"queue",
153-
"with_headers",
154-
"status":
155-
return true
156-
}
157-
return false
239+
func (r *awsEventBridgeResource) ImportState(ctx context.Context, request resource.ImportStateRequest, response *resource.ImportStateResponse) {
240+
resource.ImportStatePassthroughID(ctx, path.Root("id"), request, response)
158241
}

0 commit comments

Comments
 (0)