Skip to content

Commit deaa2c0

Browse files
authored
feat(dhcp-reservation): add resource dhcp reservation (#1156)
1 parent 8f586fd commit deaa2c0

5 files changed

+4571
-42
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
---
2+
page_title: "Scaleway: scaleway_vpc_public_gateway_dhcp_reservation"
3+
description: |-
4+
Manages Scaleway VPC Public Gateways DHCP Reservations.
5+
---
6+
7+
# scaleway_vpc_public_gateway_dhcp_reservation
8+
9+
Creates and manages the [Scaleway DHCP Reservations](https://www.scaleway.com/en/docs/network/vpc/concepts/#dhcp).
10+
11+
The static associations are used to assign IP addresses based on the MAC addresses of the Instance.
12+
13+
Statically assigned IP addresses should fall within the configured subnet, but be outside of the dynamic range.
14+
15+
For more information, see [the documentation](https://developers.scaleway.com/en/products/vpc-gw/api/v1/#dhcp-c05544) and [configuration guide](https://www.scaleway.com/en/docs/network/vpc/how-to/configure-a-public-gateway/#how-to-review-and-configure-dhcp).
16+
17+
[DHCP reservations](https://developers.scaleway.com/en/products/vpc-gw/api/v1/#dhcp-entries-e40fb6) hold both dynamic DHCP leases (IP addresses dynamically assigned by the gateway to instances) and static user-created DHCP reservations.
18+
19+
## Example Usage
20+
21+
```hcl
22+
resource scaleway_vpc_private_network main {
23+
name = "your_private_network"
24+
}
25+
26+
resource "scaleway_instance_server" "main" {
27+
image = "ubuntu_focal"
28+
type = "DEV1-S"
29+
zone = "fr-par-1"
30+
31+
private_network {
32+
pn_id = scaleway_vpc_private_network.main.id
33+
}
34+
}
35+
36+
resource scaleway_vpc_public_gateway_ip main {
37+
}
38+
39+
resource scaleway_vpc_public_gateway_dhcp main {
40+
subnet = "192.168.1.0/24"
41+
}
42+
43+
resource scaleway_vpc_public_gateway main {
44+
name = "foobar"
45+
type = "VPC-GW-S"
46+
ip_id = scaleway_vpc_public_gateway_ip.main.id
47+
}
48+
49+
resource scaleway_vpc_gateway_network main {
50+
gateway_id = scaleway_vpc_public_gateway.main.id
51+
private_network_id = scaleway_vpc_private_network.main.id
52+
dhcp_id = scaleway_vpc_public_gateway_dhcp.main.id
53+
cleanup_dhcp = true
54+
enable_masquerade = true
55+
depends_on = [scaleway_vpc_public_gateway_ip.main, scaleway_vpc_private_network.main]
56+
}
57+
58+
resource scaleway_vpc_public_gateway_dhcp_reservation main {
59+
gateway_network_id = scaleway_vpc_gateway_network.main.id
60+
mac_address = scaleway_instance_server.main.private_network.0.mac_address
61+
ip_address = "192.168.1.1"
62+
}
63+
```
64+
65+
## Arguments Reference
66+
67+
The following arguments are supported:
68+
69+
- `gateway_network_id` - (Required) The ID of the owning GatewayNetwork.
70+
- `ip_address` - (Required) The IP address to give to the machine (IP address).
71+
- `mac_address` - (Required) The MAC address to give a static entry to.
72+
- `zone` - (Defaults to [provider](../index.md#zone) `zone`) The [zone](../guides/regions_and_zones.md#zones) in which the public gateway DHCP config should be created.
73+
74+
## Attributes Reference
75+
76+
In addition to all above arguments, the following attributes are exported:
77+
78+
- `id` - The ID of the public gateway DHCP Reservation config.
79+
- `hostname` - The Hostname of the client machine.
80+
- `type` - The reservation type, either static (DHCP reservation) or dynamic (DHCP lease). Possible values are reservation and lease.
81+
- `created_at` - The date and time of the creation of the public gateway DHCP config.
82+
- `updated_at` - The date and time of the last update of the public gateway DHCP config.
83+
84+
## Import
85+
86+
Public gateway DHCP Reservation config can be imported using the `{zone}/{id}`, e.g.
87+
88+
```bash
89+
$ terraform import scaleway_vpc_public_gateway_dhcp_reservation.main fr-par-1/11111111-1111-1111-1111-111111111111
90+
```

scaleway/provider.go

+43-42
Original file line numberDiff line numberDiff line change
@@ -60,48 +60,49 @@ func Provider(config *ProviderConfig) plugin.ProviderFunc {
6060
},
6161

6262
ResourcesMap: map[string]*schema.Resource{
63-
"scaleway_account_ssh_key": resourceScalewayAccountSSKKey(),
64-
"scaleway_apple_silicon_server": resourceScalewayAppleSiliconServer(),
65-
"scaleway_baremetal_server": resourceScalewayBaremetalServer(),
66-
"scaleway_container_namespace": resourceScalewayContainerNamespace(),
67-
"scaleway_domain_record": resourceScalewayDomainRecord(),
68-
"scaleway_domain_zone": resourceScalewayDomainZone(),
69-
"scaleway_function_namespace": resourceScalewayFunctionNamespace(),
70-
"scaleway_instance_ip": resourceScalewayInstanceIP(),
71-
"scaleway_instance_ip_reverse_dns": resourceScalewayInstanceIPReverseDNS(),
72-
"scaleway_instance_volume": resourceScalewayInstanceVolume(),
73-
"scaleway_instance_security_group": resourceScalewayInstanceSecurityGroup(),
74-
"scaleway_instance_security_group_rules": resourceScalewayInstanceSecurityGroupRules(),
75-
"scaleway_instance_server": resourceScalewayInstanceServer(),
76-
"scaleway_instance_snapshot": resourceScalewayInstanceSnapshot(),
77-
"scaleway_instance_placement_group": resourceScalewayInstancePlacementGroup(),
78-
"scaleway_instance_private_nic": resourceScalewayInstancePrivateNIC(),
79-
"scaleway_iot_hub": resourceScalewayIotHub(),
80-
"scaleway_iot_device": resourceScalewayIotDevice(),
81-
"scaleway_iot_route": resourceScalewayIotRoute(),
82-
"scaleway_iot_network": resourceScalewayIotNetwork(),
83-
"scaleway_k8s_cluster": resourceScalewayK8SCluster(),
84-
"scaleway_k8s_pool": resourceScalewayK8SPool(),
85-
"scaleway_lb": resourceScalewayLb(),
86-
"scaleway_lb_ip": resourceScalewayLbIP(),
87-
"scaleway_lb_backend": resourceScalewayLbBackend(),
88-
"scaleway_lb_certificate": resourceScalewayLbCertificate(),
89-
"scaleway_lb_frontend": resourceScalewayLbFrontend(),
90-
"scaleway_lb_route": resourceScalewayLbRoute(),
91-
"scaleway_registry_namespace": resourceScalewayRegistryNamespace(),
92-
"scaleway_container": resourceScalewayContainer(),
93-
"scaleway_rdb_acl": resourceScalewayRdbACL(),
94-
"scaleway_rdb_database": resourceScalewayRdbDatabase(),
95-
"scaleway_rdb_instance": resourceScalewayRdbInstance(),
96-
"scaleway_rdb_privilege": resourceScalewayRdbPrivilege(),
97-
"scaleway_rdb_user": resourceScalewayRdbUser(),
98-
"scaleway_object_bucket": resourceScalewayObjectBucket(),
99-
"scaleway_vpc_public_gateway": resourceScalewayVPCPublicGateway(),
100-
"scaleway_vpc_gateway_network": resourceScalewayVPCGatewayNetwork(),
101-
"scaleway_vpc_public_gateway_dhcp": resourceScalewayVPCPublicGatewayDHCP(),
102-
"scaleway_vpc_public_gateway_ip": resourceScalewayVPCPublicGatewayIP(),
103-
"scaleway_vpc_public_gateway_pat_rule": resourceScalewayVPCPublicGatewayPATRule(),
104-
"scaleway_vpc_private_network": resourceScalewayVPCPrivateNetwork(),
63+
"scaleway_account_ssh_key": resourceScalewayAccountSSKKey(),
64+
"scaleway_apple_silicon_server": resourceScalewayAppleSiliconServer(),
65+
"scaleway_baremetal_server": resourceScalewayBaremetalServer(),
66+
"scaleway_container_namespace": resourceScalewayContainerNamespace(),
67+
"scaleway_domain_record": resourceScalewayDomainRecord(),
68+
"scaleway_domain_zone": resourceScalewayDomainZone(),
69+
"scaleway_function_namespace": resourceScalewayFunctionNamespace(),
70+
"scaleway_instance_ip": resourceScalewayInstanceIP(),
71+
"scaleway_instance_ip_reverse_dns": resourceScalewayInstanceIPReverseDNS(),
72+
"scaleway_instance_volume": resourceScalewayInstanceVolume(),
73+
"scaleway_instance_security_group": resourceScalewayInstanceSecurityGroup(),
74+
"scaleway_instance_security_group_rules": resourceScalewayInstanceSecurityGroupRules(),
75+
"scaleway_instance_server": resourceScalewayInstanceServer(),
76+
"scaleway_instance_snapshot": resourceScalewayInstanceSnapshot(),
77+
"scaleway_instance_placement_group": resourceScalewayInstancePlacementGroup(),
78+
"scaleway_instance_private_nic": resourceScalewayInstancePrivateNIC(),
79+
"scaleway_iot_hub": resourceScalewayIotHub(),
80+
"scaleway_iot_device": resourceScalewayIotDevice(),
81+
"scaleway_iot_route": resourceScalewayIotRoute(),
82+
"scaleway_iot_network": resourceScalewayIotNetwork(),
83+
"scaleway_k8s_cluster": resourceScalewayK8SCluster(),
84+
"scaleway_k8s_pool": resourceScalewayK8SPool(),
85+
"scaleway_lb": resourceScalewayLb(),
86+
"scaleway_lb_ip": resourceScalewayLbIP(),
87+
"scaleway_lb_backend": resourceScalewayLbBackend(),
88+
"scaleway_lb_certificate": resourceScalewayLbCertificate(),
89+
"scaleway_lb_frontend": resourceScalewayLbFrontend(),
90+
"scaleway_lb_route": resourceScalewayLbRoute(),
91+
"scaleway_registry_namespace": resourceScalewayRegistryNamespace(),
92+
"scaleway_container": resourceScalewayContainer(),
93+
"scaleway_rdb_acl": resourceScalewayRdbACL(),
94+
"scaleway_rdb_database": resourceScalewayRdbDatabase(),
95+
"scaleway_rdb_instance": resourceScalewayRdbInstance(),
96+
"scaleway_rdb_privilege": resourceScalewayRdbPrivilege(),
97+
"scaleway_rdb_user": resourceScalewayRdbUser(),
98+
"scaleway_object_bucket": resourceScalewayObjectBucket(),
99+
"scaleway_vpc_public_gateway": resourceScalewayVPCPublicGateway(),
100+
"scaleway_vpc_gateway_network": resourceScalewayVPCGatewayNetwork(),
101+
"scaleway_vpc_public_gateway_dhcp": resourceScalewayVPCPublicGatewayDHCP(),
102+
"scaleway_vpc_public_gateway_dhcp_reservation": resourceScalewayVPCPublicGatewayDHCPReservation(),
103+
"scaleway_vpc_public_gateway_ip": resourceScalewayVPCPublicGatewayIP(),
104+
"scaleway_vpc_public_gateway_pat_rule": resourceScalewayVPCPublicGatewayPATRule(),
105+
"scaleway_vpc_private_network": resourceScalewayVPCPrivateNetwork(),
105106
},
106107

107108
DataSourcesMap: map[string]*schema.Resource{
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
1+
package scaleway
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"net"
7+
"time"
8+
9+
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
10+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
11+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
12+
"github.com/scaleway/scaleway-sdk-go/api/vpcgw/v1"
13+
"github.com/scaleway/scaleway-sdk-go/scw"
14+
)
15+
16+
func resourceScalewayVPCPublicGatewayDHCPReservation() *schema.Resource {
17+
return &schema.Resource{
18+
CreateContext: resourceScalewayVPCPublicGatewayDHCPCReservationCreate,
19+
ReadContext: resourceScalewayVPCPublicGatewayDHCPReservationRead,
20+
UpdateContext: resourceScalewayVPCPublicGatewayDHCPReservationUpdate,
21+
DeleteContext: resourceScalewayVPCPublicGatewayDHCPReservationDelete,
22+
Importer: &schema.ResourceImporter{
23+
StateContext: schema.ImportStatePassthroughContext,
24+
},
25+
SchemaVersion: 0,
26+
Schema: map[string]*schema.Schema{
27+
"gateway_network_id": {
28+
Type: schema.TypeString,
29+
Required: true,
30+
Description: "The ID of the owning GatewayNetwork (UUID format).",
31+
},
32+
"ip_address": {
33+
Type: schema.TypeString,
34+
Required: true,
35+
Description: "The IP address to give to the machine (IPv4 address).",
36+
ValidateFunc: validation.IsIPAddress,
37+
},
38+
"mac_address": {
39+
Type: schema.TypeString,
40+
Required: true,
41+
Description: "The MAC address to give a static entry to.",
42+
ValidateFunc: validation.IsMACAddress,
43+
},
44+
"hostname": {
45+
Type: schema.TypeString,
46+
Computed: true,
47+
Description: "The Hostname of the client machine.",
48+
},
49+
"type": {
50+
Type: schema.TypeString,
51+
Computed: true,
52+
Description: "The reservation type, either static (DHCP reservation) or dynamic (DHCP lease). Possible values are reservation and lease",
53+
},
54+
"created_at": {
55+
Type: schema.TypeString,
56+
Computed: true,
57+
Description: "The configuration creation date.",
58+
},
59+
"updated_at": {
60+
Type: schema.TypeString,
61+
Computed: true,
62+
Description: "The configuration last modification date.",
63+
},
64+
"zone": zoneSchema(),
65+
},
66+
}
67+
}
68+
69+
func resourceScalewayVPCPublicGatewayDHCPCReservationCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
70+
vpcgwAPI, zone, err := vpcgwAPIWithZone(d, meta)
71+
if err != nil {
72+
return diag.FromErr(err)
73+
}
74+
75+
ip := net.ParseIP(d.Get("ip_address").(string))
76+
if ip == nil {
77+
return diag.FromErr(fmt.Errorf("could not parse ip_address"))
78+
}
79+
80+
macAddress, err := net.ParseMAC(d.Get("mac_address").(string))
81+
if err != nil {
82+
return diag.FromErr(err)
83+
}
84+
85+
gatewayNetworkID := expandID(d.Get("gateway_network_id"))
86+
_, err = vpcgwAPI.WaitForGatewayNetwork(&vpcgw.WaitForGatewayNetworkRequest{
87+
GatewayNetworkID: gatewayNetworkID,
88+
Zone: zone,
89+
}, scw.WithContext(ctx))
90+
if err != nil {
91+
return diag.FromErr(err)
92+
}
93+
94+
res, err := vpcgwAPI.CreateDHCPEntry(&vpcgw.CreateDHCPEntryRequest{
95+
Zone: zone,
96+
MacAddress: macAddress.String(),
97+
IPAddress: ip,
98+
GatewayNetworkID: gatewayNetworkID,
99+
}, scw.WithContext(ctx))
100+
if err != nil {
101+
return diag.FromErr(err)
102+
}
103+
104+
d.SetId(newZonedIDString(zone, res.ID))
105+
106+
_, err = vpcgwAPI.WaitForGatewayNetwork(&vpcgw.WaitForGatewayNetworkRequest{
107+
Zone: zone,
108+
GatewayNetworkID: gatewayNetworkID,
109+
}, scw.WithContext(ctx))
110+
if err != nil {
111+
return diag.FromErr(err)
112+
}
113+
114+
return resourceScalewayVPCPublicGatewayDHCPReservationRead(ctx, d, meta)
115+
}
116+
117+
func resourceScalewayVPCPublicGatewayDHCPReservationRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
118+
vpcgwAPI, zone, ID, err := vpcgwAPIWithZoneAndID(meta, d.Id())
119+
if err != nil {
120+
return diag.FromErr(err)
121+
}
122+
123+
entry, err := vpcgwAPI.GetDHCPEntry(&vpcgw.GetDHCPEntryRequest{
124+
DHCPEntryID: ID,
125+
Zone: zone,
126+
}, scw.WithContext(ctx))
127+
if err != nil {
128+
if is404Error(err) {
129+
d.SetId("")
130+
return nil
131+
}
132+
return diag.FromErr(err)
133+
}
134+
135+
_ = d.Set("ip_address", entry.IPAddress.String())
136+
_ = d.Set("mac_address", entry.MacAddress)
137+
_ = d.Set("hostname", entry.Hostname)
138+
_ = d.Set("type", entry.Type.String())
139+
_ = d.Set("gateway_network_id", newZonedIDString(zone, entry.GatewayNetworkID))
140+
_ = d.Set("created_at", entry.CreatedAt.Format(time.RFC3339))
141+
_ = d.Set("updated_at", entry.UpdatedAt.Format(time.RFC3339))
142+
_ = d.Set("zone", zone)
143+
144+
return nil
145+
}
146+
147+
func resourceScalewayVPCPublicGatewayDHCPReservationUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
148+
vpcgwAPI, zone, ID, err := vpcgwAPIWithZoneAndID(meta, d.Id())
149+
if err != nil {
150+
return diag.FromErr(err)
151+
}
152+
153+
if d.HasChangesExcept("ip_address") {
154+
ip := net.ParseIP(d.Get("ip_address").(string))
155+
if ip == nil {
156+
return diag.FromErr(fmt.Errorf("could not parse ip_address"))
157+
}
158+
req := &vpcgw.UpdateDHCPEntryRequest{
159+
DHCPEntryID: ID,
160+
Zone: zone,
161+
IPAddress: scw.IPPtr(ip),
162+
}
163+
164+
_, err = vpcgwAPI.UpdateDHCPEntry(req, scw.WithContext(ctx))
165+
if err != nil {
166+
return diag.FromErr(err)
167+
}
168+
}
169+
170+
return resourceScalewayVPCPublicGatewayDHCPReservationRead(ctx, d, meta)
171+
}
172+
173+
func resourceScalewayVPCPublicGatewayDHCPReservationDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
174+
vpcgwAPI, zone, ID, err := vpcgwAPIWithZoneAndID(meta, d.Id())
175+
if err != nil {
176+
return diag.FromErr(err)
177+
}
178+
179+
gatewayNetworkID := expandID(d.Get("gateway_network_id"))
180+
_, err = vpcgwAPI.WaitForGatewayNetwork(&vpcgw.WaitForGatewayNetworkRequest{
181+
GatewayNetworkID: gatewayNetworkID,
182+
Zone: zone,
183+
}, scw.WithContext(ctx))
184+
if err != nil {
185+
return diag.FromErr(err)
186+
}
187+
188+
err = vpcgwAPI.DeleteDHCPEntry(&vpcgw.DeleteDHCPEntryRequest{
189+
DHCPEntryID: ID,
190+
Zone: zone,
191+
}, scw.WithContext(ctx))
192+
193+
if err != nil && !is404Error(err) {
194+
return diag.FromErr(err)
195+
}
196+
197+
_, err = vpcgwAPI.WaitForGatewayNetwork(&vpcgw.WaitForGatewayNetworkRequest{
198+
GatewayNetworkID: gatewayNetworkID,
199+
Zone: zone,
200+
}, scw.WithContext(ctx))
201+
if err != nil {
202+
return diag.FromErr(err)
203+
}
204+
205+
return nil
206+
}

0 commit comments

Comments
 (0)