-
Notifications
You must be signed in to change notification settings - Fork 47
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add support for VRF BGP dynamic neighbors
- Loading branch information
Showing
6 changed files
with
402 additions
and
3 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
--- | ||
subcategory: "Metal" | ||
--- | ||
|
||
# equinix_metal_vrf_bgp_dynamic_neighbor (Resource) | ||
|
||
This resource manages BGP dynamic neighbor ranges for an Equinix Metal VRF, but with markdown | ||
|
||
|
||
|
||
<!-- schema generated by tfplugindocs --> | ||
## Schema | ||
|
||
### Required | ||
|
||
- `asn` (Number) The ASN of the dynamic BGP neighbor | ||
- `gateway_id` (String) The ID of the Equinix Metal VRF gateway for this dynamic BGP neighbor range | ||
- `range` (String) Network range of the dynamic BGP neighbor in CIDR format | ||
|
||
### Optional | ||
|
||
- `tags` (List of String) Tags attached to the dynamic BGP neighbor | ||
|
||
### Read-Only | ||
|
||
- `id` (String) The unique identifier for this the dynamic BGP neighbor | ||
- `state` (String) The state of the dynamic BGP neighbor |
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
36 changes: 36 additions & 0 deletions
36
internal/resources/metal/vrf_bgp_dynamic_neighbor/model.go
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 |
---|---|---|
@@ -0,0 +1,36 @@ | ||
package vrfbgpdynamicneighbor | ||
|
||
import ( | ||
"context" | ||
|
||
"github.com/equinix/equinix-sdk-go/services/metalv1" | ||
"github.com/hashicorp/terraform-plugin-framework/diag" | ||
"github.com/hashicorp/terraform-plugin-framework/types" | ||
) | ||
|
||
type Model struct { | ||
ID types.String `tfsdk:"id"` | ||
GatewayID types.String `tfsdk:"gateway_id"` | ||
Range types.String `tfsdk:"range"` | ||
ASN types.Int64 `tfsdk:"asn"` | ||
State types.String `tfsdk:"state"` | ||
Tags types.List `tfsdk:"tags"` // List of strings | ||
} | ||
|
||
func (m *Model) parse(ctx context.Context, neighbor *metalv1.BgpDynamicNeighbor) (d diag.Diagnostics) { | ||
m.ID = types.StringValue(neighbor.GetId()) | ||
|
||
m.GatewayID = types.StringValue(neighbor.MetalGateway.GetId()) | ||
m.Range = types.StringValue(neighbor.GetBgpNeighborRange()) | ||
m.ASN = types.Int64Value(neighbor.GetBgpNeighborAsn()) | ||
m.State = types.StringValue(string(neighbor.GetState())) | ||
|
||
tags, diags := types.ListValueFrom(ctx, types.StringType, neighbor.GetTags()) | ||
if diags.HasError() { | ||
return diags | ||
} | ||
|
||
m.Tags = tags | ||
|
||
return nil | ||
} |
177 changes: 177 additions & 0 deletions
177
internal/resources/metal/vrf_bgp_dynamic_neighbor/resource.go
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 |
---|---|---|
@@ -0,0 +1,177 @@ | ||
package vrfbgpdynamicneighbor | ||
|
||
import ( | ||
"context" | ||
|
||
"github.com/equinix/equinix-sdk-go/services/metalv1" | ||
"github.com/equinix/terraform-provider-equinix/internal/framework" | ||
|
||
"github.com/hashicorp/terraform-plugin-framework/diag" | ||
"github.com/hashicorp/terraform-plugin-framework/resource" | ||
"github.com/hashicorp/terraform-plugin-framework/resource/schema" | ||
) | ||
|
||
var ( | ||
bgpNeighborIncludes = []string{"metal_gateway"} | ||
// `created_by` is specified as a UserLimited. To avoid an error | ||
// due to missing UserLimited.id field, have to either exclude | ||
// or include `created_by` | ||
bgpNeighborExcludes = []string{"created_by", "ip_reservation"} | ||
) | ||
|
||
type Resource struct { | ||
framework.BaseResource | ||
framework.WithTimeouts | ||
} | ||
|
||
func NewResource() resource.Resource { | ||
r := Resource{ | ||
BaseResource: framework.NewBaseResource( | ||
framework.BaseResourceConfig{ | ||
Name: "equinix_metal_vrf_bgp_dynamic_neighbor", | ||
}, | ||
), | ||
} | ||
|
||
return &r | ||
} | ||
|
||
func (r *Resource) Schema( | ||
ctx context.Context, | ||
req resource.SchemaRequest, | ||
resp *resource.SchemaResponse, | ||
) { | ||
s := resourceSchema(ctx) | ||
if s.Blocks == nil { | ||
s.Blocks = make(map[string]schema.Block) | ||
} | ||
|
||
resp.Schema = s | ||
} | ||
|
||
func (r *Resource) Create(ctx context.Context, request resource.CreateRequest, response *resource.CreateResponse) { | ||
client := r.Meta.NewMetalClientForFramework(ctx, request.ProviderMeta) | ||
|
||
var plan Model | ||
response.Diagnostics.Append(request.Config.Get(ctx, &plan)...) | ||
if response.Diagnostics.HasError() { | ||
return | ||
} | ||
|
||
createRequest := metalv1.BgpDynamicNeighborCreateInput{ | ||
BgpNeighborRange: plan.Range.ValueString(), | ||
BgpNeighborAsn: plan.ASN.ValueInt64(), | ||
} | ||
|
||
response.Diagnostics.Append(getPlanTags(ctx, plan, &createRequest.Tags)...) | ||
|
||
// Parse API response into the Terraform state | ||
//response.Diagnostics.Append(data.parse(vlan)...) | ||
if response.Diagnostics.HasError() { | ||
return | ||
} | ||
|
||
neighbor, _, err := client.VRFsApi.CreateBgpDynamicNeighbor(ctx, plan.GatewayID.ValueString()). | ||
BgpDynamicNeighborCreateInput(createRequest). | ||
Exclude(bgpNeighborExcludes). | ||
Include(bgpNeighborIncludes). | ||
Execute() | ||
|
||
if err != nil { | ||
response.Diagnostics.AddError( | ||
"Error creating VRF BGP dynamic neighbor range", | ||
"Could not create VRF BGP dynamic neighbor range: "+err.Error(), | ||
) | ||
} | ||
|
||
// Parse API response into the Terraform state | ||
response.Diagnostics.Append(plan.parse(ctx, neighbor)...) | ||
if response.Diagnostics.HasError() { | ||
return | ||
} | ||
|
||
// Set state to fully populated data | ||
response.Diagnostics.Append(response.State.Set(ctx, &plan)...) | ||
} | ||
|
||
func (r *Resource) Read(ctx context.Context, request resource.ReadRequest, response *resource.ReadResponse) { | ||
client := r.Meta.NewMetalClientForFramework(ctx, request.ProviderMeta) | ||
|
||
var data Model | ||
response.Diagnostics.Append(request.State.Get(ctx, &data)...) | ||
if response.Diagnostics.HasError() { | ||
return | ||
} | ||
|
||
neighbor, _, err := client.VRFsApi.BgpDynamicNeighborsIdGet(ctx, data.ID.ValueString()). | ||
// `created_by` is specified as a UserLimited. To avoid an error | ||
// due to missing UserLimited.id field, have to either exclude | ||
// or include `created_by` | ||
Exclude(bgpNeighborExcludes). | ||
Include(bgpNeighborIncludes). | ||
Execute() | ||
|
||
if err != nil { | ||
response.Diagnostics.AddError( | ||
"Error reading VRF BGP dynamic neighbor range", | ||
"Could not read VRF BGP dynamic neighbor with ID "+data.ID.ValueString()+": "+err.Error(), | ||
) | ||
} | ||
|
||
response.Diagnostics.Append(data.parse(ctx, neighbor)...) | ||
if response.Diagnostics.HasError() { | ||
return | ||
} | ||
|
||
response.Diagnostics.Append(response.State.Set(ctx, &data)...) | ||
} | ||
|
||
func (r *Resource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { | ||
// TODO: it should be possible to update tags but the API spec doesn't | ||
// mention support for that. In the meantime should changes to tags force | ||
// recreating the resource? | ||
var data Model | ||
if diag := req.Plan.Get(ctx, &data); diag.HasError() { | ||
resp.Diagnostics.Append(diag...) | ||
return | ||
} | ||
|
||
if diag := resp.State.Set(ctx, &data); diag.HasError() { | ||
resp.Diagnostics.Append(diag...) | ||
return | ||
} | ||
} | ||
|
||
func (r *Resource) Delete(ctx context.Context, request resource.DeleteRequest, response *resource.DeleteResponse) { | ||
client := r.Meta.NewMetalClientForFramework(ctx, request.ProviderMeta) | ||
|
||
var data Model | ||
response.Diagnostics.Append(request.State.Get(ctx, &data)...) | ||
if response.Diagnostics.HasError() { | ||
return | ||
} | ||
|
||
// TODO: should we do something with the neighbor object returned here? | ||
// For example: do we need to poll the API until neighbor.GetState() has | ||
// as particular value? | ||
_, _, err := client.VRFsApi.DeleteBgpDynamicNeighborById(ctx, data.ID.ValueString()). | ||
// `created_by` is specified as a UserLimited. To avoid an error | ||
// due to missing UserLimited.id field, have to either exclude | ||
// or include `created_by` | ||
Exclude(bgpNeighborExcludes). | ||
Execute() | ||
|
||
if err != nil { | ||
response.Diagnostics.AddError( | ||
"Error deleting VRF BGP dynamic neighbor range", | ||
"Could not delete VRF BGP dynamic neighbor with ID "+data.ID.ValueString()+": "+err.Error(), | ||
) | ||
} | ||
} | ||
|
||
func getPlanTags(ctx context.Context, plan Model, tags *[]string) diag.Diagnostics { | ||
if len(plan.Tags.Elements()) != 0 { | ||
return plan.Tags.ElementsAs(context.Background(), tags, false) | ||
} | ||
return diag.Diagnostics{} | ||
} |
58 changes: 58 additions & 0 deletions
58
internal/resources/metal/vrf_bgp_dynamic_neighbor/resource_schema.go
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 |
---|---|---|
@@ -0,0 +1,58 @@ | ||
package vrfbgpdynamicneighbor | ||
|
||
import ( | ||
"context" | ||
|
||
"github.com/hashicorp/terraform-plugin-framework/resource/schema" | ||
"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 resourceSchema(ctx context.Context) schema.Schema { | ||
return schema.Schema{ | ||
Description: "This resource manages BGP dynamic neighbor ranges for an Equinix Metal VRF", | ||
MarkdownDescription: "This resource manages BGP dynamic neighbor ranges for an Equinix Metal VRF, but with markdown", | ||
Attributes: map[string]schema.Attribute{ | ||
"id": schema.StringAttribute{ | ||
Description: "The unique identifier for this the dynamic BGP neighbor", | ||
Computed: true, | ||
PlanModifiers: []planmodifier.String{ | ||
stringplanmodifier.UseStateForUnknown(), | ||
}, | ||
}, | ||
"gateway_id": schema.StringAttribute{ | ||
Description: "The ID of the Equinix Metal VRF gateway for this dynamic BGP neighbor range", | ||
Required: true, | ||
PlanModifiers: []planmodifier.String{ | ||
stringplanmodifier.RequiresReplace(), | ||
}, | ||
}, | ||
"range": schema.StringAttribute{ | ||
Description: "Network range of the dynamic BGP neighbor in CIDR format", | ||
Required: true, | ||
PlanModifiers: []planmodifier.String{ | ||
stringplanmodifier.RequiresReplace(), | ||
}, | ||
}, | ||
"asn": schema.Int64Attribute{ | ||
Description: "The ASN of the dynamic BGP neighbor", | ||
Required: true, | ||
PlanModifiers: []planmodifier.Int64{ | ||
int64planmodifier.RequiresReplace(), | ||
}, | ||
}, | ||
"state": schema.StringAttribute{ | ||
Description: "The state of the dynamic BGP neighbor", | ||
Computed: true, | ||
}, | ||
"tags": schema.ListAttribute{ | ||
Description: "Tags attached to the dynamic BGP neighbor", | ||
ElementType: types.StringType, | ||
Optional: true, | ||
Computed: true, | ||
}, | ||
}, | ||
} | ||
} |
Oops, something went wrong.