Skip to content

Commit 35e2c9d

Browse files
committed
NSOF-5918 routing_group_mapped_elements_attachment: introduce resource
1 parent ae07fc9 commit 35e2c9d

File tree

11 files changed

+351
-25
lines changed

11 files changed

+351
-25
lines changed

docs/resources/routing_group.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ description: |-
1010

1111
User routing groups is a logical group of subnets which co-exist at the same time. These are independent subnets, which can be the same or contain overlapping IP addresses that can be used without conflicting with each other.
1212

13+
When using the `mapped_element_ids` argument of this resource, the resource will take over the management of the Routing Group's mapped elements.
14+
This argument is incompatible with other methods of managing Routing Group's mapped elements, such as `routing_group_mapped_elements_attachment`.
15+
Any attempt to manage Routing Group's mapped elements using different methods, will result in resource cycling and/or errors.
16+
1317
## Example Usage
1418

1519
```terraform
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
---
2+
# generated by https://github.com/hashicorp/terraform-plugin-docs
3+
page_title: "pfptmeta_routing_group_mapped_elements_attachment Resource - terraform-provider-pfptmeta"
4+
subcategory: "Network"
5+
description: |-
6+
Attaches mapped elements to routing group.
7+
---
8+
9+
# pfptmeta_routing_group_mapped_elements_attachment (Resource)
10+
11+
Attaches mapped elements to routing group.
12+
13+
~> **NOTE:** Having multiple **routing_group_mapped_elements_attachment** resources in conjunction with the same routing group and mapped elements will result in erratic behavior!
14+
~> **NOTE:** For any given routing group, this resource is incompatible with the `pfptmeta_routing_group`
15+
[resource](https://registry.terraform.io/providers/nsofnetworks/pfptmeta/latest/docs/resources/routing_group) `mapped_elements_ids` argument.
16+
When using this argument and resource simulteneously, both of them will attempt to manage the routing group's mapped elements. As a result, Terraform will display a permanent difference.
17+
18+
## Example Usage
19+
20+
```terraform
21+
resource "pfptmeta_group" "group" {
22+
name = "group name"
23+
}
24+
25+
resource "pfptmeta_network_element" "mapped-service" {
26+
name = "mapped service"
27+
mapped_service = "mapped.service.com"
28+
}
29+
30+
resource "pfptmeta_routing_group" "routing_group" {
31+
name = "routing group name"
32+
description = "routing group description"
33+
sources = [pfptmeta_group.group.id]
34+
}
35+
36+
resource "pfptmeta_routing_group_mapped_elements_attachment" "attachment" {
37+
routing_group_id = pfptmeta_routing_group.routing_group.id
38+
mapped_elements_ids = [pfptmeta_network_element.mapped-service.id]
39+
}
40+
```
41+
42+
<!-- schema generated by tfplugindocs -->
43+
## Schema
44+
45+
### Required
46+
47+
- **mapped_elements_ids** (Set of String) Mapped element IDs to be attached to the routing group (Mapped Subnet or Mapped Service)
48+
- **routing_group_id** (String)
49+
50+
### Read-Only
51+
52+
- **id** (String) The ID of this resource.
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
resource "pfptmeta_group" "group" {
2+
name = "group name"
3+
}
4+
5+
resource "pfptmeta_network_element" "mapped-service" {
6+
name = "mapped service"
7+
mapped_service = "mapped.service.com"
8+
}
9+
10+
resource "pfptmeta_routing_group" "routing_group" {
11+
name = "routing group name"
12+
description = "routing group description"
13+
sources = [pfptmeta_group.group.id]
14+
}
15+
16+
resource "pfptmeta_routing_group_mapped_elements_attachment" "attachment" {
17+
routing_group_id = pfptmeta_routing_group.routing_group.id
18+
mapped_elements_ids = [pfptmeta_network_element.mapped-service.id]
19+
}

internal/client/routing_group.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,3 +93,33 @@ func DeleteRoutingGroup(ctx context.Context, c *Client, pgID string) (*RoutingGr
9393
}
9494
return parseRoutingGroup(resp)
9595
}
96+
97+
func AddMappedElementsToRoutingGroups(ctx context.Context, c *Client, rgID string, meIDs []string) (*RoutingGroup, error) {
98+
url := fmt.Sprintf("%s/%s/%s/add_mapped_elements", c.BaseURL, routingGroupsEndpoint, rgID)
99+
body := make(map[string][]string)
100+
body["mapped_element_ids"] = meIDs
101+
jsonBody, err := json.Marshal(body)
102+
if err != nil {
103+
return nil, fmt.Errorf("could not convert mapped elements to json: %v", err)
104+
}
105+
resp, err := c.Post(ctx, url, bytes.NewReader(jsonBody))
106+
if err != nil {
107+
return nil, err
108+
}
109+
return parseRoutingGroup(resp)
110+
}
111+
112+
func RemoveMappedElementsFromRoutingGroups(ctx context.Context, c *Client, pgID string, meIDs []string) (*RoutingGroup, error) {
113+
url := fmt.Sprintf("%s/%s/%s/remove_mapped_elements", c.BaseURL, routingGroupsEndpoint, pgID)
114+
body := make(map[string][]string)
115+
body["mapped_element_ids"] = meIDs
116+
jsonBody, err := json.Marshal(body)
117+
if err != nil {
118+
return nil, fmt.Errorf("could not convert mapped elements to json: %v", err)
119+
}
120+
resp, err := c.Post(ctx, url, bytes.NewReader(jsonBody))
121+
if err != nil {
122+
return nil, err
123+
}
124+
return parseRoutingGroup(resp)
125+
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
package acc_tests
2+
3+
import (
4+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
5+
"regexp"
6+
"testing"
7+
)
8+
9+
const (
10+
routingGroupAttachmentDependencies = `
11+
resource "pfptmeta_network_element" "mapped-service" {
12+
name = "mapped service"
13+
mapped_service = "mapped.service.com"
14+
}
15+
16+
resource "pfptmeta_network_element" "mapped-service2" {
17+
name = "mapped service"
18+
mapped_service = "mapped.service2.com"
19+
}
20+
21+
resource "pfptmeta_routing_group" "routing_group" {
22+
name = "routing group name"
23+
}
24+
`
25+
routingGroupAttachment1 = `
26+
resource "pfptmeta_routing_group_mapped_elements_attachment" "attachment" {
27+
routing_group_id = pfptmeta_routing_group.routing_group.id
28+
mapped_elements_ids = [pfptmeta_network_element.mapped-service.id]
29+
}
30+
`
31+
routingGroupAttachment2 = `
32+
resource "pfptmeta_routing_group_mapped_elements_attachment" "attachment2" {
33+
routing_group_id = pfptmeta_routing_group.routing_group.id
34+
mapped_elements_ids = [pfptmeta_network_element.mapped-service2.id]
35+
}
36+
`
37+
)
38+
39+
func TestAccRoutingGroupAttachment(t *testing.T) {
40+
resource.Test(t, resource.TestCase{
41+
PreCheck: func() { testAccPreCheck(t) },
42+
ProviderFactories: providerFactories,
43+
CheckDestroy: validateResourceDestroyed("routing_group", "v1/routing_groups"),
44+
Steps: []resource.TestStep{
45+
{
46+
Config: routingGroupAttachmentDependencies + routingGroupAttachment1 + routingGroupAttachment2,
47+
Check: resource.ComposeTestCheckFunc(
48+
resource.TestMatchResourceAttr(
49+
"pfptmeta_routing_group_mapped_elements_attachment.attachment", "routing_group_id", regexp.MustCompile("^rg-.+$"),
50+
),
51+
resource.TestCheckResourceAttrPair(
52+
"pfptmeta_network_element.mapped-service", "id",
53+
"pfptmeta_routing_group_mapped_elements_attachment.attachment", "mapped_elements_ids.0"),
54+
resource.TestCheckResourceAttrPair(
55+
"pfptmeta_network_element.mapped-service2", "id",
56+
"pfptmeta_routing_group_mapped_elements_attachment.attachment2", "mapped_elements_ids.0"),
57+
),
58+
},
59+
{
60+
Config: routingGroupAttachmentDependencies + routingGroupAttachment2,
61+
},
62+
{
63+
Config: routingGroupAttachmentDependencies + routingGroupAttachment2 + routingGroupDataSource,
64+
Check: resource.ComposeTestCheckFunc(
65+
resource.TestMatchResourceAttr(
66+
"data.pfptmeta_routing_group.routing_group", "id", regexp.MustCompile("^rg-.+$"),
67+
),
68+
resource.TestCheckResourceAttrPair(
69+
"pfptmeta_network_element.mapped-service2", "id",
70+
"data.pfptmeta_routing_group.routing_group", "mapped_elements_ids.0"),
71+
),
72+
},
73+
},
74+
})
75+
}

internal/provider/provider.go

Lines changed: 27 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import (
2929
"github.com/nsofnetworks/terraform-provider-pfptmeta/internal/provider/protocol_group"
3030
"github.com/nsofnetworks/terraform-provider-pfptmeta/internal/provider/role"
3131
"github.com/nsofnetworks/terraform-provider-pfptmeta/internal/provider/routing_group"
32+
"github.com/nsofnetworks/terraform-provider-pfptmeta/internal/provider/routing_group_mapped_elements_attachment"
3233
"github.com/nsofnetworks/terraform-provider-pfptmeta/internal/provider/user"
3334
"github.com/nsofnetworks/terraform-provider-pfptmeta/internal/provider/user_roles_attachment"
3435
)
@@ -83,31 +84,32 @@ func New(version string) func() *schema.Provider {
8384
"pfptmeta_device_settings": device_settings.DataSource(),
8485
},
8586
ResourcesMap: map[string]*schema.Resource{
86-
"pfptmeta_network_element": network_element.Resource(),
87-
"pfptmeta_network_element_alias": network_element_alias.Resource(),
88-
"pfptmeta_mapped_domain": mapped_domain.Resource(),
89-
"pfptmeta_mapped_host": mapped_host.Resource(),
90-
"pfptmeta_metaport": metaport.Resource(),
91-
"pfptmeta_metaport_cluster": metaport_cluster.Resource(),
92-
"pfptmeta_metaport_failover": metaport_failover.Resource(),
93-
"pfptmeta_enterprise_dns": enterprise_dns.Resource(),
94-
"pfptmeta_protocol_group": protocol_group.Resource(),
95-
"pfptmeta_role": role.Resource(),
96-
"pfptmeta_group": group.Resource(),
97-
"pfptmeta_user": user.Resource(),
98-
"pfptmeta_routing_group": routing_group.Resource(),
99-
"pfptmeta_policy": policy.Resource(),
100-
"pfptmeta_group_roles_attachment": group_roles_attachment.Resource(),
101-
"pfptmeta_group_users_attachment": group_users_attachment.Resource(),
102-
"pfptmeta_notification_channel": notification_channel.Resource(),
103-
"pfptmeta_egress_route": egress_route.Resource(),
104-
"pfptmeta_alert": alert.Resource(),
105-
"pfptmeta_certificate": certificate.Resource(),
106-
"pfptmeta_easylink": easylink.Resource(),
107-
"pfptmeta_posture_check": posture_check.Resource(),
108-
"pfptmeta_access_control": access_control.Resource(),
109-
"pfptmeta_device_settings": device_settings.Resource(),
110-
"pfptmeta_user_roles_attachment": user_roles_attachment.Resource(),
87+
"pfptmeta_network_element": network_element.Resource(),
88+
"pfptmeta_network_element_alias": network_element_alias.Resource(),
89+
"pfptmeta_mapped_domain": mapped_domain.Resource(),
90+
"pfptmeta_mapped_host": mapped_host.Resource(),
91+
"pfptmeta_metaport": metaport.Resource(),
92+
"pfptmeta_metaport_cluster": metaport_cluster.Resource(),
93+
"pfptmeta_metaport_failover": metaport_failover.Resource(),
94+
"pfptmeta_enterprise_dns": enterprise_dns.Resource(),
95+
"pfptmeta_protocol_group": protocol_group.Resource(),
96+
"pfptmeta_role": role.Resource(),
97+
"pfptmeta_group": group.Resource(),
98+
"pfptmeta_user": user.Resource(),
99+
"pfptmeta_routing_group": routing_group.Resource(),
100+
"pfptmeta_routing_group_mapped_elements_attachment": routing_group_mapped_elements_attachment.Resource(),
101+
"pfptmeta_policy": policy.Resource(),
102+
"pfptmeta_group_roles_attachment": group_roles_attachment.Resource(),
103+
"pfptmeta_group_users_attachment": group_users_attachment.Resource(),
104+
"pfptmeta_notification_channel": notification_channel.Resource(),
105+
"pfptmeta_egress_route": egress_route.Resource(),
106+
"pfptmeta_alert": alert.Resource(),
107+
"pfptmeta_certificate": certificate.Resource(),
108+
"pfptmeta_easylink": easylink.Resource(),
109+
"pfptmeta_posture_check": posture_check.Resource(),
110+
"pfptmeta_access_control": access_control.Resource(),
111+
"pfptmeta_device_settings": device_settings.Resource(),
112+
"pfptmeta_user_roles_attachment": user_roles_attachment.Resource(),
111113
},
112114
}
113115
p.ConfigureContextFunc = configure(version, p)

internal/provider/routing_group/resource.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ func Resource() *schema.Resource {
3838
ValidateDiagFunc: common.ValidateID(true, "ne"),
3939
},
4040
Optional: true,
41+
Computed: true,
4142
MinItems: 1,
4243
},
4344
"sources": {
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
package routing_group_mapped_elements_attachment
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
7+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
8+
"github.com/nsofnetworks/terraform-provider-pfptmeta/internal/client"
9+
"net/http"
10+
)
11+
12+
func generateID(rgID string, meIDs []string) string {
13+
hash := 0
14+
for _, meID := range meIDs {
15+
hash += schema.HashString(meID)
16+
}
17+
return fmt.Sprintf("%s-%d", rgID, hash)
18+
}
19+
20+
func attachmentToResource(d *schema.ResourceData, rg *client.RoutingGroup) (diags diag.Diagnostics) {
21+
err := d.Set("routing_group_id", rg.ID)
22+
if err != nil {
23+
return diag.FromErr(err)
24+
}
25+
rgMes := &schema.Set{F: schema.HashString}
26+
for _, i := range rg.MappedElementsIds {
27+
rgMes.Add(i)
28+
}
29+
schemaMes := d.Get("mapped_elements_ids").(*schema.Set)
30+
u := schema.NewSet(schema.HashString, schemaMes.List())
31+
intersection := rgMes.Intersection(u)
32+
mes := client.ResourceTypeSetToStringSlice(intersection)
33+
err = d.Set("mapped_elements_ids", mes)
34+
if err != nil {
35+
return diag.FromErr(err)
36+
}
37+
d.SetId(generateID(rg.ID, mes))
38+
return
39+
}
40+
41+
func readResource(ctx context.Context, d *schema.ResourceData, meta interface{}) (diags diag.Diagnostics) {
42+
c := meta.(*client.Client)
43+
44+
rgID := d.Get("routing_group_id").(string)
45+
rg, err := client.GetRoutingGroup(ctx, c, rgID)
46+
if err != nil {
47+
errResponse, ok := err.(*client.ErrorResponse)
48+
if ok && errResponse.Status == http.StatusNotFound {
49+
d.SetId("")
50+
}
51+
return diag.FromErr(err)
52+
}
53+
return attachmentToResource(d, rg)
54+
}
55+
func createResource(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
56+
c := meta.(*client.Client)
57+
58+
rgID := d.Get("routing_group_id").(string)
59+
mes := client.ResourceTypeSetToStringSlice(d.Get("mapped_elements_ids").(*schema.Set))
60+
rg, err := client.AddMappedElementsToRoutingGroups(ctx, c, rgID, mes)
61+
if err != nil {
62+
return diag.FromErr(err)
63+
}
64+
return attachmentToResource(d, rg)
65+
}
66+
67+
func deleteResource(ctx context.Context, d *schema.ResourceData, meta interface{}) (diags diag.Diagnostics) {
68+
c := meta.(*client.Client)
69+
70+
gID := d.Get("routing_group_id").(string)
71+
mes := d.Get("mapped_elements_ids").(*schema.Set)
72+
_, err := client.RemoveMappedElementsFromRoutingGroups(ctx, c, gID, client.ResourceTypeSetToStringSlice(mes))
73+
if err != nil {
74+
d.SetId("")
75+
return diag.FromErr(err)
76+
}
77+
d.SetId("")
78+
return
79+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package routing_group_mapped_elements_attachment
2+
3+
import (
4+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
5+
"github.com/nsofnetworks/terraform-provider-pfptmeta/internal/provider/common"
6+
)
7+
8+
func Resource() *schema.Resource {
9+
return &schema.Resource{
10+
Description: "Attaches mapped elements to routing group.",
11+
ReadContext: readResource,
12+
CreateContext: createResource,
13+
DeleteContext: deleteResource,
14+
Schema: map[string]*schema.Schema{
15+
"id": {
16+
Type: schema.TypeString,
17+
Computed: true,
18+
},
19+
"routing_group_id": {
20+
Type: schema.TypeString,
21+
Required: true,
22+
ValidateDiagFunc: common.ValidateID(false, "rg"),
23+
ForceNew: true,
24+
},
25+
"mapped_elements_ids": {
26+
Description: "Mapped element IDs to be attached to the routing group (Mapped Subnet or Mapped Service)",
27+
Required: true,
28+
Type: schema.TypeSet,
29+
Elem: &schema.Schema{
30+
Type: schema.TypeString,
31+
ValidateDiagFunc: common.ValidateID(true, "ne"),
32+
},
33+
MinItems: 1,
34+
ForceNew: true,
35+
},
36+
},
37+
}
38+
}

templates/resources/routing_group.md.tmpl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ description: |-
1010

1111
{{ .Description | trimspace }}
1212

13+
When using the `mapped_element_ids` argument of this resource, the resource will take over the management of the Routing Group's mapped elements.
14+
This argument is incompatible with other methods of managing Routing Group's mapped elements, such as `routing_group_mapped_elements_attachment`.
15+
Any attempt to manage Routing Group's mapped elements using different methods, will result in resource cycling and/or errors.
16+
1317
## Example Usage
1418

1519
{{tffile "examples/resources/pfptmeta_routing_group/resource.tf"}}

0 commit comments

Comments
 (0)