|
1 | 1 | package cloudamqp |
2 | 2 |
|
3 | 3 | import ( |
| 4 | + "context" |
| 5 | + "encoding/json" |
4 | 6 | "fmt" |
5 | 7 | "log" |
6 | 8 | "strconv" |
7 | 9 | "strings" |
8 | 10 |
|
9 | 11 | "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" |
11 | 20 | ) |
12 | 21 |
|
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{ |
25 | 77 | Required: true, |
26 | 78 | Description: "Instance identifier", |
| 79 | + PlanModifiers: []planmodifier.Int64{ |
| 80 | + int64planmodifier.RequiresReplace(), |
| 81 | + }, |
27 | 82 | }, |
28 | | - "aws_account_id": { |
29 | | - Type: schema.TypeString, |
30 | | - ForceNew: true, |
| 83 | + "aws_account_id": schema.StringAttribute{ |
31 | 84 | Required: true, |
32 | 85 | Description: "The 12 digit AWS Account ID where you want the events to be sent to.", |
| 86 | + PlanModifiers: []planmodifier.String{ |
| 87 | + stringplanmodifier.RequiresReplace(), |
| 88 | + }, |
33 | 89 | }, |
34 | | - "aws_region": { |
35 | | - Type: schema.TypeString, |
36 | | - ForceNew: true, |
| 90 | + "aws_region": schema.StringAttribute{ |
37 | 91 | Required: true, |
38 | 92 | 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 | + }, |
39 | 96 | }, |
40 | | - "vhost": { |
41 | | - Type: schema.TypeString, |
42 | | - ForceNew: true, |
| 97 | + "vhost": schema.StringAttribute{ |
43 | 98 | Required: true, |
44 | 99 | Description: "The VHost the queue resides in.", |
| 100 | + PlanModifiers: []planmodifier.String{ |
| 101 | + stringplanmodifier.RequiresReplace(), |
| 102 | + }, |
45 | 103 | }, |
46 | | - "queue": { |
47 | | - Type: schema.TypeString, |
48 | | - ForceNew: true, |
| 104 | + "queue": schema.StringAttribute{ |
49 | 105 | Required: true, |
50 | 106 | Description: "A (durable) queue on your RabbitMQ instance.", |
| 107 | + PlanModifiers: []planmodifier.String{ |
| 108 | + stringplanmodifier.RequiresReplace(), |
| 109 | + }, |
51 | 110 | }, |
52 | | - "with_headers": { |
53 | | - Type: schema.TypeBool, |
54 | | - ForceNew: true, |
| 111 | + "with_headers": schema.BoolAttribute{ |
55 | 112 | Required: true, |
56 | 113 | Description: "Include message headers in the event data.", |
| 114 | + PlanModifiers: []planmodifier.Bool{ |
| 115 | + boolplanmodifier.RequiresReplace(), |
| 116 | + }, |
57 | 117 | }, |
58 | | - "status": { |
59 | | - Type: schema.TypeString, |
| 118 | + "status": schema.StringAttribute{ |
60 | 119 | Computed: true, |
61 | 120 | Description: "Always set to null, unless there is an error starting the EventBridge", |
62 | 121 | }, |
63 | 122 | }, |
64 | 123 | } |
65 | 124 | } |
66 | 125 |
|
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 | + } |
74 | 143 |
|
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 |
79 | 154 | } |
| 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, ¶ms) |
80 | 159 |
|
81 | | - data, err := api.CreateAwsEventBridge(instanceID, params) |
| 160 | + apiResponse, err := r.client.CreateAwsEventBridge(int(data.InstanceID.ValueInt64()), params) |
82 | 161 | 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 |
84 | 168 | } |
85 | 169 |
|
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)...) |
88 | 175 | } |
89 | 176 |
|
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(), ",") |
94 | 186 | 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]) |
96 | 188 | instanceID, _ := strconv.Atoi(s[1]) |
97 | | - d.Set("instance_id", instanceID) |
| 189 | + state.InstanceID = types.Int64Value(int64(instanceID)) |
98 | 190 | } |
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 |
101 | 194 | } |
102 | 195 |
|
103 | 196 | var ( |
104 | | - api = meta.(*api.API) |
105 | | - instanceID = d.Get("instance_id").(int) |
| 197 | + id = state.Id.ValueString() |
| 198 | + instanceID = int(state.InstanceID.ValueInt64()) |
106 | 199 | ) |
107 | 200 |
|
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) |
110 | 203 | 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 |
112 | 206 | } |
113 | 207 |
|
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)) |
124 | 213 |
|
125 | | - return nil |
126 | | -} |
| 214 | + // Save data into Terraform state |
| 215 | + response.Diagnostics.Append(response.State.Set(ctx, &state)...) |
127 | 216 |
|
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 | +} |
133 | 219 |
|
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 |
135 | 222 | } |
136 | 223 |
|
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 | + ) |
144 | 236 | } |
145 | 237 | } |
146 | 238 |
|
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) |
158 | 241 | } |
0 commit comments