-
Notifications
You must be signed in to change notification settings - Fork 38
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
chore(cloudamqp_integration_aws_eventbridge): Migrate towards terrafo…
…rm 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.
- Loading branch information
Showing
2 changed files
with
203 additions
and
117 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,158 +1,241 @@ | ||
package cloudamqp | ||
|
||
import ( | ||
"context" | ||
"encoding/json" | ||
"fmt" | ||
"log" | ||
"strconv" | ||
"strings" | ||
|
||
"github.com/cloudamqp/terraform-provider-cloudamqp/api" | ||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" | ||
"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/boolplanmodifier" | ||
"github.com/hashicorp/terraform-plugin-framework/resource/schema/int64planmodifier" | ||
"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" | ||
) | ||
|
||
func resourceAwsEventBridge() *schema.Resource { | ||
return &schema.Resource{ | ||
Create: resourceAwsEventBridgeCreate, | ||
Read: resourceAwsEventBridgeRead, | ||
Delete: resourceAwsEventBridgeDelete, | ||
Importer: &schema.ResourceImporter{ | ||
StateContext: schema.ImportStatePassthroughContext, | ||
}, | ||
Schema: map[string]*schema.Schema{ | ||
"instance_id": { | ||
Type: schema.TypeInt, | ||
ForceNew: true, | ||
type awsEventBridgeResource struct { | ||
client *api.API | ||
} | ||
|
||
type awsEventBridgeResourceModel struct { | ||
Id types.String `tfsdk:"id"` | ||
InstanceID types.Int64 `tfsdk:"instance_id"` | ||
AwsAccountId types.String `tfsdk:"aws_account_id"` | ||
AwsRegion types.String `tfsdk:"aws_region"` | ||
Vhost types.String `tfsdk:"vhost"` | ||
QueueName types.String `tfsdk:"queue"` | ||
WithHeaders types.Bool `tfsdk:"with_headers"` | ||
Status types.String `tfsdk:"status"` | ||
} | ||
|
||
type awsEventBridgeResourceApiModel struct { | ||
AwsAccountId string `json:"aws_account_id"` | ||
AwsRegion string `json:"aws_region"` | ||
Vhost string `json:"vhost"` | ||
QueueName string `json:"queue"` | ||
WithHeaders bool `json:"with_headers"` | ||
} | ||
|
||
func (r *awsEventBridgeResource) Configure(ctx context.Context, request resource.ConfigureRequest, response *resource.ConfigureResponse) { | ||
// Always perform a nil check when handling ProviderData because Terraform | ||
// sets that data after it calls the ConfigureProvider RPC. | ||
if request.ProviderData == nil { | ||
return | ||
} | ||
|
||
client, ok := request.ProviderData.(*api.API) | ||
|
||
if !ok { | ||
response.Diagnostics.AddError( | ||
"Unexpected Resource Configure Type", | ||
fmt.Sprintf("Expected *api.API, got: %T. Please report this issue to the provider developers.", request.ProviderData), | ||
) | ||
|
||
return | ||
} | ||
|
||
r.client = client | ||
} | ||
|
||
func (r *awsEventBridgeResource) Metadata(ctx context.Context, request resource.MetadataRequest, response *resource.MetadataResponse) { | ||
response.TypeName = "cloudamqp_integration_aws_eventbridge" | ||
} | ||
|
||
func (r *awsEventBridgeResource) Schema(ctx context.Context, request resource.SchemaRequest, response *resource.SchemaResponse) { | ||
response.Schema = schema.Schema{ | ||
Attributes: map[string]schema.Attribute{ | ||
"id": schema.StringAttribute{ | ||
Computed: true, | ||
}, | ||
"instance_id": schema.Int64Attribute{ | ||
Required: true, | ||
Description: "Instance identifier", | ||
PlanModifiers: []planmodifier.Int64{ | ||
int64planmodifier.RequiresReplace(), | ||
}, | ||
}, | ||
"aws_account_id": { | ||
Type: schema.TypeString, | ||
ForceNew: true, | ||
"aws_account_id": schema.StringAttribute{ | ||
Required: true, | ||
Description: "The 12 digit AWS Account ID where you want the events to be sent to.", | ||
PlanModifiers: []planmodifier.String{ | ||
stringplanmodifier.RequiresReplace(), | ||
}, | ||
}, | ||
"aws_region": { | ||
Type: schema.TypeString, | ||
ForceNew: true, | ||
"aws_region": schema.StringAttribute{ | ||
Required: true, | ||
Description: "The AWS region where you the events to be sent to. (e.g. us-west-1, us-west-2, ..., etc.)", | ||
PlanModifiers: []planmodifier.String{ | ||
stringplanmodifier.RequiresReplace(), | ||
}, | ||
}, | ||
"vhost": { | ||
Type: schema.TypeString, | ||
ForceNew: true, | ||
"vhost": schema.StringAttribute{ | ||
Required: true, | ||
Description: "The VHost the queue resides in.", | ||
PlanModifiers: []planmodifier.String{ | ||
stringplanmodifier.RequiresReplace(), | ||
}, | ||
}, | ||
"queue": { | ||
Type: schema.TypeString, | ||
ForceNew: true, | ||
"queue": schema.StringAttribute{ | ||
Required: true, | ||
Description: "A (durable) queue on your RabbitMQ instance.", | ||
PlanModifiers: []planmodifier.String{ | ||
stringplanmodifier.RequiresReplace(), | ||
}, | ||
}, | ||
"with_headers": { | ||
Type: schema.TypeBool, | ||
ForceNew: true, | ||
"with_headers": schema.BoolAttribute{ | ||
Required: true, | ||
Description: "Include message headers in the event data.", | ||
PlanModifiers: []planmodifier.Bool{ | ||
boolplanmodifier.RequiresReplace(), | ||
}, | ||
}, | ||
"status": { | ||
Type: schema.TypeString, | ||
"status": schema.StringAttribute{ | ||
Computed: true, | ||
Description: "Always set to null, unless there is an error starting the EventBridge", | ||
}, | ||
}, | ||
} | ||
} | ||
|
||
func resourceAwsEventBridgeCreate(d *schema.ResourceData, meta interface{}) error { | ||
var ( | ||
api = meta.(*api.API) | ||
keys = awsEventbridgeAttributeKeys() | ||
params = make(map[string]interface{}) | ||
instanceID = d.Get("instance_id").(int) | ||
) | ||
func (r *awsEventBridgeResource) Create(ctx context.Context, request resource.CreateRequest, response *resource.CreateResponse) { | ||
var data awsEventBridgeResourceModel | ||
|
||
// Read Terraform plan data into the model | ||
response.Diagnostics.Append(request.Plan.Get(ctx, &data)...) | ||
|
||
if response.Diagnostics.HasError() { | ||
return | ||
} | ||
|
||
apiModel := awsEventBridgeResourceApiModel{ | ||
AwsAccountId: data.AwsAccountId.ValueString(), | ||
AwsRegion: data.AwsRegion.ValueString(), | ||
Vhost: data.Vhost.ValueString(), | ||
QueueName: data.QueueName.ValueString(), | ||
WithHeaders: data.WithHeaders.ValueBool(), | ||
} | ||
|
||
for _, k := range keys { | ||
if v := d.Get(k); v != nil { | ||
params[k] = v | ||
} | ||
var params map[string]interface{} | ||
temp, err := json.Marshal(apiModel) | ||
if err != nil { | ||
response.Diagnostics.AddError( | ||
"Unable to Create Resource", | ||
"An unexpected error occurred while creating the resource create request. "+ | ||
"Please report this issue to the provider developers.\n\n"+ | ||
"JSON Error: "+err.Error(), | ||
) | ||
return | ||
} | ||
// TODO: This is totally a hack to get the struct into a map[string]interface{} | ||
// It is very unlikely this will fail after the first one succeeds, so it should be fine to ignore the error | ||
// Maybe after the api is moved into the repo we can improve the interface | ||
_ = json.Unmarshal(temp, ¶ms) | ||
|
||
data, err := api.CreateAwsEventBridge(instanceID, params) | ||
apiResponse, err := r.client.CreateAwsEventBridge(int(data.InstanceID.ValueInt64()), params) | ||
if err != nil { | ||
return err | ||
response.Diagnostics.AddError( | ||
"Failed to Create Resource", | ||
"An error occurred while calling the api to create the surface, verify your permissions are correct.\n\n"+ | ||
"JSON Error: "+err.Error(), | ||
) | ||
return | ||
} | ||
|
||
d.SetId(data["id"].(string)) | ||
return nil | ||
data.Id = types.StringValue(apiResponse["id"].(string)) | ||
data.Status = types.StringNull() | ||
|
||
// Save data into Terraform state | ||
response.Diagnostics.Append(response.State.Set(ctx, &data)...) | ||
} | ||
|
||
func resourceAwsEventBridgeRead(d *schema.ResourceData, meta interface{}) error { | ||
if strings.Contains(d.Id(), ",") { | ||
log.Printf("[DEBUG] cloudamqp::resource::aws-eventbridge::read id contains : %v", d.Id()) | ||
s := strings.Split(d.Id(), ",") | ||
func (r *awsEventBridgeResource) Read(ctx context.Context, request resource.ReadRequest, response *resource.ReadResponse) { | ||
var state awsEventBridgeResourceModel | ||
|
||
// Read Terraform plan data into the model | ||
response.Diagnostics.Append(request.State.Get(ctx, &state)...) | ||
|
||
if strings.Contains(state.Id.ValueString(), ",") { | ||
log.Printf("[DEBUG] cloudamqp::resource::aws-eventbridge::read id contains : %v", state.Id.String()) | ||
s := strings.Split(state.Id.ValueString(), ",") | ||
log.Printf("[DEBUG] cloudamqp::resource::aws-eventbridge::read split ids: %v, %v", s[0], s[1]) | ||
d.SetId(s[0]) | ||
state.Id = types.StringValue(s[0]) | ||
instanceID, _ := strconv.Atoi(s[1]) | ||
d.Set("instance_id", instanceID) | ||
state.InstanceID = types.Int64Value(int64(instanceID)) | ||
} | ||
if d.Get("instance_id").(int) == 0 { | ||
return fmt.Errorf("missing instance identifier: {resource_id},{instance_id}") | ||
if state.InstanceID.ValueInt64() == 0 { | ||
response.Diagnostics.AddError("Missing instance identifier {resource_id},{instance_id}", "") | ||
return | ||
} | ||
|
||
var ( | ||
api = meta.(*api.API) | ||
instanceID = d.Get("instance_id").(int) | ||
id = state.Id.ValueString() | ||
instanceID = int(state.InstanceID.ValueInt64()) | ||
) | ||
|
||
log.Printf("[DEBUG] cloudamqp::resource::aws-eventbridge::read ID: %v, instanceID %v", d.Id(), instanceID) | ||
data, err := api.ReadAwsEventBridge(instanceID, d.Id()) | ||
log.Printf("[DEBUG] cloudamqp::resource::aws-eventbridge::read ID: %v, instanceID %v", id, instanceID) | ||
data, err := r.client.ReadAwsEventBridge(instanceID, id) | ||
if err != nil { | ||
return err | ||
response.Diagnostics.AddError("Something went wrong while reading the aws event bridge", fmt.Sprintf("%v", err)) | ||
return | ||
} | ||
|
||
for k, v := range data { | ||
if validateAwsEventBridgeSchemaAttribute(k) { | ||
if v == nil { | ||
continue | ||
} | ||
if err = d.Set(k, v); err != nil { | ||
return fmt.Errorf("error setting %s for resource %s: %s", k, d.Id(), err) | ||
} | ||
} | ||
} | ||
state.AwsAccountId = types.StringValue(data["aws_account_id"].(string)) | ||
state.AwsRegion = types.StringValue(data["aws_region"].(string)) | ||
state.Vhost = types.StringValue(data["vhost"].(string)) | ||
state.QueueName = types.StringValue(data["queue"].(string)) | ||
state.WithHeaders = types.BoolValue(data["with_headers"].(bool)) | ||
|
||
return nil | ||
} | ||
// Save data into Terraform state | ||
response.Diagnostics.Append(response.State.Set(ctx, &state)...) | ||
|
||
func resourceAwsEventBridgeDelete(d *schema.ResourceData, meta interface{}) error { | ||
var ( | ||
api = meta.(*api.API) | ||
instanceID = d.Get("instance_id").(int) | ||
) | ||
return | ||
} | ||
|
||
return api.DeleteAwsEventBridge(instanceID, d.Id()) | ||
func (r *awsEventBridgeResource) Update(ctx context.Context, request resource.UpdateRequest, response *resource.UpdateResponse) { | ||
// This resource does not implement the Update function | ||
} | ||
|
||
func awsEventbridgeAttributeKeys() []string { | ||
return []string{ | ||
"aws_account_id", | ||
"aws_region", | ||
"vhost", | ||
"queue", | ||
"with_headers", | ||
func (r *awsEventBridgeResource) Delete(ctx context.Context, request resource.DeleteRequest, response *resource.DeleteResponse) { | ||
var data awsEventBridgeResourceModel | ||
|
||
// Read Terraform plan data into the model | ||
response.Diagnostics.Append(request.State.Get(ctx, &data)...) | ||
var id = data.Id.ValueString() | ||
err := r.client.DeleteAwsEventBridge(int(data.InstanceID.ValueInt64()), id) | ||
|
||
if err != nil { | ||
response.Diagnostics.AddError("An error occurred while deleting cloudamqp_integration_aws_eventbridge", | ||
fmt.Sprintf("Error deleting Cloudamqp event bridge %s: %s", id, err), | ||
) | ||
} | ||
} | ||
|
||
func validateAwsEventBridgeSchemaAttribute(key string) bool { | ||
switch key { | ||
case "aws_account_id", | ||
"aws_region", | ||
"vhost", | ||
"queue", | ||
"with_headers", | ||
"status": | ||
return true | ||
} | ||
return false | ||
func (r *awsEventBridgeResource) ImportState(ctx context.Context, request resource.ImportStateRequest, response *resource.ImportStateResponse) { | ||
resource.ImportStatePassthroughID(ctx, path.Root("id"), request, response) | ||
} |