Skip to content

Commit

Permalink
feat: add support for BGP dynamic neighbor flags on VRFs
Browse files Browse the repository at this point in the history
  • Loading branch information
ctreatma committed Aug 21, 2024
1 parent f84c9d1 commit 265256b
Show file tree
Hide file tree
Showing 5 changed files with 165 additions and 41 deletions.
27 changes: 17 additions & 10 deletions docs/resources/metal_vrf.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,20 +75,27 @@ resource "equinix_metal_virtual_circuit" "example" {
}
```

## Argument Reference
<!-- schema generated by tfplugindocs -->
## Schema

The following arguments are supported:
### Required

* `name` - (Required) User-supplied name of the VRF, unique to the project
* `metro` - (Required) Metro ID or Code where the VRF will be deployed.
* `project_id` - (Required) Project ID where the VRF will be deployed.
* `description` - (Optional) Description of the VRF.
* `local_asn` - (Optional) The 4-byte ASN set on the VRF.
* `ip_ranges` - (Optional) All IPv4 and IPv6 Ranges that will be available to BGP Peers. IPv4 addresses must be /8 or smaller with a minimum size of /29. IPv6 must be /56 or smaller with a minimum size of /64. Ranges must not overlap other ranges within the VRF.
- `metro` (String) Metro ID or Code where the VRF will be deployed
- `name` (String) User-supplied name of the VRF, unique to the project
- `project_id` (String) Project ID where the VRF will be deployed

## Attributes Reference
### Optional

No additional attributes are exported.
- `bgp_dynamic_neighbors_bfd_enabled` (Boolean) Toggle BFD on dynamic bgp neighbors sessions
- `bgp_dynamic_neighbors_enabled` (Boolean) Toggle to enable the dynamic bgp neighbors feature on the VRF
- `bgp_dynamic_neighbors_export_route_map` (Boolean) Toggle to export the VRF route-map to the dynamic bgp neighbors
- `description` (String) Description of the VRF
- `ip_ranges` (Set of String) All IPv4 and IPv6 Ranges that will be available to BGP Peers. IPv4 addresses must be /8 or smaller with a minimum size of /29. IPv6 must be /56 or smaller with a minimum size of /64. Ranges must not overlap other ranges within the VRF.
- `local_asn` (Number) The 4-byte ASN set on the VRF

### Read-Only

- `id` (String) The ID of this resource.

## Import

Expand Down
28 changes: 28 additions & 0 deletions internal/resources/metal/vrf/datasource.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,34 @@ func DataSource() *schema.Resource {
Computed: true,
Description: "Project ID",
},
"bgp_dynamic_neighbors": {
Type: schema.TypeList,
Description: "BGP dynamic neighbor settings for this VRF",
Optional: true,
Computed: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"enabled": {
Type: schema.TypeBool,
Optional: true,
Computed: true,
Description: "Toggle to enable the dynamic bgp neighbors feature on the VRF",
},
"export_route_map": {
Type: schema.TypeBool,
Optional: true,
Computed: true,
Description: "Toggle to export the VRF route-map to the dynamic bgp neighbors",
},
"bfd_enabled": {
Type: schema.TypeBool,
Optional: true,
Computed: true,
Description: "Toggle BFD on dynamic bgp neighbors sessions",
},
},
},
},
},
}
}
Expand Down
57 changes: 48 additions & 9 deletions internal/resources/metal/vrf/resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,13 @@ func Resource() *schema.Resource {
"metro": {
Type: schema.TypeString,
Required: true,
Description: "Metro Code",
Description: "Metro ID or Code where the VRF will be deployed",
},
"local_asn": {
Type: schema.TypeInt,
Optional: true,
Computed: true,
Description: "The 4-byte ASN set on the VRF.",
Description: "The 4-byte ASN set on the VRF",
},
"ip_ranges": {
Type: schema.TypeSet,
Expand All @@ -55,7 +55,25 @@ func Resource() *schema.Resource {
"project_id": {
Type: schema.TypeString,
Required: true,
Description: "Project ID",
Description: "Project ID where the VRF will be deployed",
},
"bgp_dynamic_neighbors_enabled": {
Type: schema.TypeBool,
Optional: true,
Computed: true,
Description: "Toggle to enable the dynamic bgp neighbors feature on the VRF",
},
"bgp_dynamic_neighbors_export_route_map": {
Type: schema.TypeBool,
Optional: true,
Computed: true,
Description: "Toggle to export the VRF route-map to the dynamic bgp neighbors",
},
"bgp_dynamic_neighbors_bfd_enabled": {
Type: schema.TypeBool,
Optional: true,
Computed: true,
Description: "Toggle BFD on dynamic bgp neighbors sessions",
},
// TODO: created_by, created_at, updated_at, href
},
Expand All @@ -75,6 +93,15 @@ func resourceMetalVRFCreate(ctx context.Context, d *schema.ResourceData, meta in
if value, ok := d.GetOk("local_asn"); ok {
createRequest.LocalAsn = metalv1.PtrInt64(int64(value.(int)))
}
if value, ok := d.GetOk("bgp_dynamic_neighbors_enabled"); ok {
createRequest.SetBgpDynamicNeighborsEnabled(value.(bool))
}
if value, ok := d.GetOk("bgp_dynamic_neighbors_export_route_map"); ok {
createRequest.SetBgpDynamicNeighborsExportRouteMap(value.(bool))
}
if value, ok := d.GetOk("bgp_dynamic_neighbors_bfd_enabled"); ok {
createRequest.SetBgpDynamicNeighborsBfdEnabled(value.(bool))
}

projectId := d.Get("project_id").(string)
vrf, _, err := client.VRFsApi.
Expand Down Expand Up @@ -107,6 +134,15 @@ func resourceMetalVRFUpdate(ctx context.Context, d *schema.ResourceData, meta in
ipRanges := converters.SetToStringList(d.Get("ip_ranges").(*schema.Set))
updateRequest.SetIpRanges(ipRanges)
}
if d.HasChange("bgp_dynamic_neighbors_enabled") {
updateRequest.SetBgpDynamicNeighborsEnabled(d.Get("bgp_dynamic_neighbors_enabled").(bool))
}
if d.HasChange("bgp_dynamic_neighbors_export_route_map") {
updateRequest.SetBgpDynamicNeighborsExportRouteMap(d.Get("bgp_dynamic_neighbors_export_route_map").(bool))
}
if d.HasChange("bgp_dynamic_neighbors_bfd_enabled") {
updateRequest.SetBgpDynamicNeighborsBfdEnabled(d.Get("bgp_dynamic_neighbors_bfd_enabled").(bool))
}

_, _, err := client.VRFsApi.
UpdateVrf(ctx, d.Id()).
Expand Down Expand Up @@ -136,12 +172,15 @@ func resourceMetalVRFRead(ctx context.Context, d *schema.ResourceData, meta inte
return diag.FromErr(err)
}
m := map[string]interface{}{
"name": vrf.GetName(),
"description": vrf.GetDescription(),
"metro": vrf.Metro.GetCode(),
"local_asn": vrf.GetLocalAsn(),
"ip_ranges": vrf.GetIpRanges(),
"project_id": vrf.Project.GetId(),
"name": vrf.GetName(),
"description": vrf.GetDescription(),
"metro": vrf.Metro.GetCode(),
"local_asn": vrf.GetLocalAsn(),
"ip_ranges": vrf.GetIpRanges(),
"project_id": vrf.Project.GetId(),
"bgp_dynamic_neighbors_enabled": vrf.GetBgpDynamicNeighborsEnabled(),
"bgp_dynamic_neighbors_export_route_map": vrf.GetBgpDynamicNeighborsExportRouteMap(),
"bgp_dynamic_neighbors_bfd_enabled": vrf.GetBgpDynamicNeighborsBfdEnabled(),
}

return diag.FromErr(equinix_schema.SetMap(d, m))
Expand Down
79 changes: 71 additions & 8 deletions internal/resources/metal/vrf/resource_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ import (

const (
metalDedicatedConnIDEnvVar = "TF_ACC_METAL_DEDICATED_CONNECTION_ID"
// This used to be repeated in each config function
// Extracting it for now but other test suites pick a dynamic metro
testMetro = "da"
)

func TestAccMetalVRF_basic(t *testing.T) {
Expand Down Expand Up @@ -48,6 +51,58 @@ func TestAccMetalVRF_basic(t *testing.T) {
})
}

func TestAccMetalVRF_bgpDynamicNeighbors(t *testing.T) {
var vrf metalv1.Vrf
rInt := acctest.RandInt()

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { acceptance.TestAccPreCheckMetal(t) },
ExternalProviders: acceptance.TestExternalProviders,
ProtoV5ProviderFactories: acceptance.ProtoV5ProviderFactories,
CheckDestroy: testAccMetalVRFCheckDestroyed,
Steps: []resource.TestStep{
{
Config: testAccMetalVRFConfig_bgpDynamicNeighbors(rInt, true, true, true),
Check: resource.ComposeTestCheckFunc(
testAccMetalVRFExists("equinix_metal_vrf.test", &vrf),
resource.TestCheckResourceAttr(
"equinix_metal_vrf.test", "bgp_dynamic_neighbors_enabled", "true"),
resource.TestCheckResourceAttr(
"equinix_metal_vrf.test", "bgp_dynamic_neighbors_export_route_map", "true"),
resource.TestCheckResourceAttr(
"equinix_metal_vrf.test", "bgp_dynamic_neighbors_bfd_enabled", "true"),
),
},
{
ResourceName: "equinix_metal_vrf.test",
ImportState: true,
ImportStateVerify: true,
},
{
Config: testAccMetalVRFConfig_bgpDynamicNeighbors(rInt, false, false, false),
Check: resource.ComposeTestCheckFunc(
testAccMetalVRFExists("equinix_metal_vrf.test", &vrf),
resource.TestCheckResourceAttr(
"equinix_metal_vrf.test", "bgp_dynamic_neighbors_enabled", "false"),
resource.TestCheckResourceAttr(
"equinix_metal_vrf.test", "bgp_dynamic_neighbors_export_route_map", "false"),
resource.TestCheckResourceAttr(
"equinix_metal_vrf.test", "bgp_dynamic_neighbors_bfd_enabled", "false"),
),
},
{
ResourceName: "equinix_metal_vrf.test",
ImportState: true,
ImportStateVerify: true,
},
{
Config: testAccMetalVRFConfig_basic(rInt),
PlanOnly: true,
},
},
})
}

func TestAccMetalVRF_withIPRanges(t *testing.T) {
var vrf metalv1.Vrf
rInt := acctest.RandInt()
Expand Down Expand Up @@ -289,8 +344,6 @@ func testAccMetalVRFExists(n string, vrf *metalv1.Vrf) resource.TestCheckFunc {
}

func testAccMetalVRFConfig_basic(r int) string {
testMetro := "da"

return fmt.Sprintf(`
resource "equinix_metal_project" "test" {
name = "tfacc-vrfs-%d"
Expand All @@ -303,9 +356,23 @@ resource "equinix_metal_vrf" "test" {
}`, r, r, testMetro)
}

func testAccMetalVRFConfig_withIPRanges(r int) string {
testMetro := "da"
func testAccMetalVRFConfig_bgpDynamicNeighbors(r int, enabled, export_route_map, bfd_enabled bool) string {
return fmt.Sprintf(`
resource "equinix_metal_project" "test" {
name = "tfacc-vrfs-%d"
}
resource "equinix_metal_vrf" "test" {
name = "tfacc-vrf-%d"
metro = "%s"
project_id = "${equinix_metal_project.test.id}"
bgp_dynamic_neighbors_enabled = %v
bgp_dynamic_neighbors_export_route_map = %v
bgp_dynamic_neighbors_bfd_enabled = %v
}`, r, r, testMetro, enabled, export_route_map, bfd_enabled)
}

func testAccMetalVRFConfig_withIPRanges(r int) string {
return fmt.Sprintf(`
resource "equinix_metal_project" "test" {
name = "tfacc-vrfs-%d"
Expand All @@ -322,8 +389,6 @@ resource "equinix_metal_vrf" "test" {
}

func testAccMetalVRFConfig_withIPReservations(r int) string {
testMetro := "da"

return testAccMetalVRFConfig_withIPRanges(r) + fmt.Sprintf(`
resource "equinix_metal_reserved_ip_block" "test" {
Expand All @@ -339,8 +404,6 @@ resource "equinix_metal_reserved_ip_block" "test" {
}

func testAccMetalVRFConfig_withGateway(r int) string {
testMetro := "da"

return testAccMetalVRFConfig_withIPReservations(r) + fmt.Sprintf(`
resource "equinix_metal_vlan" "test" {
Expand Down
15 changes: 1 addition & 14 deletions templates/resources/metal_vrf.md.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -26,20 +26,7 @@ Attach a Virtual Circuit from a Dedicated Metal Connection to the Metal Gateway.

{{tffile "examples/resources/metal_vrf/example_3.tf"}}

## Argument Reference

The following arguments are supported:

* `name` - (Required) User-supplied name of the VRF, unique to the project
* `metro` - (Required) Metro ID or Code where the VRF will be deployed.
* `project_id` - (Required) Project ID where the VRF will be deployed.
* `description` - (Optional) Description of the VRF.
* `local_asn` - (Optional) The 4-byte ASN set on the VRF.
* `ip_ranges` - (Optional) All IPv4 and IPv6 Ranges that will be available to BGP Peers. IPv4 addresses must be /8 or smaller with a minimum size of /29. IPv6 must be /56 or smaller with a minimum size of /64. Ranges must not overlap other ranges within the VRF.

## Attributes Reference

No additional attributes are exported.
{{ .SchemaMarkdown | trimspace }}

## Import

Expand Down

0 comments on commit 265256b

Please sign in to comment.