Skip to content

Commit 7cfabed

Browse files
authored
feat(ipam): support custom resources (#2783)
* support custom resource * handle detach and move + test * lint * add doc * fix tabs
1 parent 7033e27 commit 7cfabed

File tree

5 files changed

+2159
-7
lines changed

5 files changed

+2159
-7
lines changed

docs/resources/ipam_ip.md

+28
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,31 @@ resource "scaleway_ipam_ip" "ip01" {
7676
}
7777
```
7878

79+
### Book an IP for a custom resource
80+
81+
```terraform
82+
resource "scaleway_vpc" "vpc01" {
83+
name = "my vpc"
84+
}
85+
86+
resource "scaleway_vpc_private_network" "pn01" {
87+
vpc_id = scaleway_vpc.vpc01.id
88+
ipv4_subnet {
89+
subnet = "172.16.32.0/22"
90+
}
91+
}
92+
93+
resource "scaleway_ipam_ip" "ip01" {
94+
address = "172.16.32.7"
95+
source {
96+
private_network_id = scaleway_vpc_private_network.pn01.id
97+
}
98+
custom_resource {
99+
mac_address = "bc:24:11:74:d0:6a"
100+
}
101+
}
102+
```
103+
79104
## Argument Reference
80105

81106
The following arguments are supported:
@@ -90,6 +115,9 @@ The following arguments are supported:
90115
- `private_network_id` - The Private Network of the IP (if the IP is a private IP).
91116
- `subnet_id` - The Private Network subnet of the IP (if the IP is a private IP).
92117
- `is_ipv6` - (Optional) Defines whether to request an IPv6 address instead of IPv4.
118+
- `custome_resource` - (Optional) The custom resource to attach to the IP being reserved. An example of a custom resource is a virtual machine hosted on an Elastic Metal server.
119+
- `mac_address` - The MAC address of the custom resource.
120+
- `name` - When the resource is in a Private Network, a DNS record is available to resolve the resource name.
93121
- `region` - (Defaults to [provider](../index.md#region) `region`) The [region](../guides/regions_and_zones.md#regions) of the IP.
94122
- `project_id` - (Defaults to [provider](../index.md#project_id) `project_id`) The ID of the Project the IP is associated with.
95123

internal/services/ipam/ip.go

+60-7
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,26 @@ func ResourceIP() *schema.Resource {
6868
},
6969
},
7070
},
71+
"custom_resource": {
72+
Type: schema.TypeList,
73+
Optional: true,
74+
Description: "The custom resource in which to book the IP",
75+
Elem: &schema.Resource{
76+
Schema: map[string]*schema.Schema{
77+
"mac_address": {
78+
Type: schema.TypeString,
79+
Required: true,
80+
Description: "MAC address of the custom resource",
81+
ValidateFunc: validation.IsMACAddress,
82+
},
83+
"name": {
84+
Type: schema.TypeString,
85+
Optional: true,
86+
Description: "When the resource is in a Private Network, a DNS record is available to resolve the resource name",
87+
},
88+
},
89+
},
90+
},
7191
"is_ipv6": {
7292
Type: schema.TypeBool,
7393
Optional: true,
@@ -179,6 +199,10 @@ func ResourceIPAMIPCreate(ctx context.Context, d *schema.ResourceData, m interfa
179199
req.Source = expandIPSource(source)
180200
}
181201

202+
if customResource, ok := d.GetOk("custom_resource"); ok {
203+
req.Resource = expandCustomResource(customResource)
204+
}
205+
182206
res, err := ipamAPI.BookIP(req, scw.WithContext(ctx))
183207
if err != nil {
184208
return diag.FromErr(err)
@@ -267,13 +291,31 @@ func ResourceIPAMIPUpdate(ctx context.Context, d *schema.ResourceData, m interfa
267291
return diag.FromErr(err)
268292
}
269293

270-
_, err = ipamAPI.UpdateIP(&ipam.UpdateIPRequest{
271-
IPID: ID,
272-
Region: region,
273-
Tags: types.ExpandUpdatedStringsPtr(d.Get("tags")),
274-
}, scw.WithContext(ctx))
275-
if err != nil {
276-
return diag.FromErr(err)
294+
if d.HasChange("custom_resource") {
295+
oldCustomResourceRaw, newCustomResourceRaw := d.GetChange("custom_resource")
296+
oldCustomResource := expandCustomResource(oldCustomResourceRaw)
297+
newCustomResource := expandCustomResource(newCustomResourceRaw)
298+
299+
_, err = ipamAPI.MoveIP(&ipam.MoveIPRequest{
300+
Region: region,
301+
IPID: ID,
302+
FromResource: oldCustomResource,
303+
ToResource: newCustomResource,
304+
}, scw.WithContext(ctx))
305+
if err != nil {
306+
return diag.FromErr(err)
307+
}
308+
}
309+
310+
if d.HasChange("tags") {
311+
_, err = ipamAPI.UpdateIP(&ipam.UpdateIPRequest{
312+
IPID: ID,
313+
Region: region,
314+
Tags: types.ExpandUpdatedStringsPtr(d.Get("tags")),
315+
}, scw.WithContext(ctx))
316+
if err != nil {
317+
return diag.FromErr(err)
318+
}
277319
}
278320

279321
return ResourceIPAMIPRead(ctx, d, m)
@@ -285,6 +327,17 @@ func ResourceIPAMIPDelete(ctx context.Context, d *schema.ResourceData, m interfa
285327
return diag.FromErr(err)
286328
}
287329

330+
if customResource, ok := d.GetOk("custom_resource"); ok {
331+
_, err = ipamAPI.DetachIP(&ipam.DetachIPRequest{
332+
Region: region,
333+
IPID: ID,
334+
Resource: expandCustomResource(customResource),
335+
}, scw.WithContext(ctx))
336+
if err != nil && !httperrors.Is404(err) {
337+
return diag.FromErr(err)
338+
}
339+
}
340+
288341
err = ipamAPI.ReleaseIP(&ipam.ReleaseIPRequest{
289342
Region: region,
290343
IPID: ID,

internal/services/ipam/ip_test.go

+90
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,96 @@ func TestAccIPAMIP_WithTags(t *testing.T) {
157157
})
158158
}
159159

160+
func TestAccIPAMIP_WithCustomResource(t *testing.T) {
161+
tt := acctest.NewTestTools(t)
162+
defer tt.Cleanup()
163+
resource.ParallelTest(t, resource.TestCase{
164+
PreCheck: func() { acctest.PreCheck(t) },
165+
ProviderFactories: tt.ProviderFactories,
166+
CheckDestroy: ipamchecks.CheckIPDestroy(tt),
167+
Steps: []resource.TestStep{
168+
{
169+
Config: `
170+
resource "scaleway_vpc" "vpc01" {
171+
name = "my vpc"
172+
}
173+
174+
resource "scaleway_vpc_private_network" "pn01" {
175+
vpc_id = scaleway_vpc.vpc01.id
176+
ipv4_subnet {
177+
subnet = "172.16.32.0/22"
178+
}
179+
}
180+
181+
resource "scaleway_ipam_ip" "ip01" {
182+
source {
183+
private_network_id = scaleway_vpc_private_network.pn01.id
184+
}
185+
custom_resource {
186+
mac_address = "bc:24:11:74:d0:5a"
187+
}
188+
}
189+
`,
190+
Check: resource.ComposeTestCheckFunc(
191+
testAccCheckIPAMIPExists(tt, "scaleway_ipam_ip.ip01"),
192+
resource.TestCheckResourceAttr("scaleway_ipam_ip.ip01", "custom_resource.0.mac_address", "bc:24:11:74:d0:5a"),
193+
),
194+
},
195+
{
196+
Config: `
197+
resource "scaleway_vpc" "vpc01" {
198+
name = "my vpc"
199+
}
200+
201+
resource "scaleway_vpc_private_network" "pn01" {
202+
vpc_id = scaleway_vpc.vpc01.id
203+
ipv4_subnet {
204+
subnet = "172.16.32.0/22"
205+
}
206+
}
207+
208+
resource "scaleway_ipam_ip" "ip01" {
209+
source {
210+
private_network_id = scaleway_vpc_private_network.pn01.id
211+
}
212+
custom_resource {
213+
mac_address = "bc:24:11:74:d0:5b"
214+
}
215+
}
216+
`,
217+
Check: resource.ComposeTestCheckFunc(
218+
testAccCheckIPAMIPExists(tt, "scaleway_ipam_ip.ip01"),
219+
resource.TestCheckResourceAttr("scaleway_ipam_ip.ip01", "custom_resource.0.mac_address", "bc:24:11:74:d0:5b"),
220+
),
221+
},
222+
{
223+
Config: `
224+
resource "scaleway_vpc" "vpc01" {
225+
name = "my vpc"
226+
}
227+
228+
resource "scaleway_vpc_private_network" "pn01" {
229+
vpc_id = scaleway_vpc.vpc01.id
230+
ipv4_subnet {
231+
subnet = "172.16.32.0/22"
232+
}
233+
}
234+
235+
resource "scaleway_ipam_ip" "ip01" {
236+
source {
237+
private_network_id = scaleway_vpc_private_network.pn01.id
238+
}
239+
}
240+
`,
241+
Check: resource.ComposeTestCheckFunc(
242+
testAccCheckIPAMIPExists(tt, "scaleway_ipam_ip.ip01"),
243+
resource.TestCheckNoResourceAttr("scaleway_ipam_ip.ip01", "custom_resource.0.mac_address"),
244+
),
245+
},
246+
},
247+
})
248+
}
249+
160250
func testAccCheckIPAMIPExists(tt *acctest.TestTools, n string) resource.TestCheckFunc {
161251
return func(s *terraform.State) error {
162252
rs, ok := s.RootModule().Resources[n]

0 commit comments

Comments
 (0)