Skip to content

Commit 6fefc99

Browse files
authored
feat(instance): support ip mobility (#2086)
* feat(instance_ip): add type attribute * add prefix and update doc * update doc and add routed_ip_enabled to data sources * support multiple IPs per instance * lint * lint doc * add tests and fix issues to make them pass * record tests * remove extra test * update doc link * update cassettes * update tests * fix test and always set public_ip * empty commit
1 parent 7d0a9a7 commit 6fefc99

20 files changed

+10141
-15
lines changed

docs/data-sources/instance_ip.md

+2
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ In addition to all above arguments, the following attributes are exported:
4141

4242
~> **Important:** Instance IPs' IDs are [zoned](../guides/regions_and_zones.md#resource-ids), which means they are of the form `{zone}/{id}`, e.g. `fr-par-1/11111111-1111-1111-1111-111111111111`
4343

44+
- `type` - The type of the IP
4445
- `address` - The IP address.
46+
- `prefix` - The IP Prefix.
4547
- `reverse` - The reverse dns attached to this IP
4648
- `organization_id` - The organization ID the IP is associated with.

docs/data-sources/instance_server.md

+8-2
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,9 @@ attached to the server.
6161

6262
- `enable_ipv6` - Determines if IPv6 is enabled for the server.
6363

64-
- `enable_dynamic_ip` - True is dynamic IP in enable on the server.
64+
- `enable_dynamic_ip` - True if dynamic IP in enable on the server.
65+
66+
- `routed_ip_enabled` - True if the server support routed ip only.
6567

6668
- `state` - The state of the server. Possible values are: `started`, `stopped` or `standby`.
6769

@@ -80,7 +82,11 @@ attached to the server.
8082

8183
- `private_ip` - The Scaleway internal IP address of the server.
8284

83-
- `public_ip` - The public IPv4 address of the server.
85+
- `public_ip` - The public IP address of the server.
86+
87+
- `public_ips` - The list of public IPs of the server
88+
- `id` - The ID of the IP
89+
- `address` - The address of the IP
8490

8591
- `ipv6_address` - The default ipv6 address routed to the server. ( Only set when enable_ipv6 is set to true )
8692

docs/data-sources/instance_servers.md

+6-1
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,12 @@ In addition to all above arguments, the following attributes are exported:
4444
~> **Important:** Instance servers' IDs are [zoned](../guides/regions_and_zones.md#resource-ids), which means they are of the form `{zone}/{id}`, e.g. `fr-par-1/11111111-1111-1111-1111-111111111111`
4545

4646
- `tags` - The tags associated with the server.
47-
- `public_ip` - The public IPv4 address of the server.
47+
- `public_ip` - The public IP address of the server.
4848
- `private_ip` - The Scaleway internal IP address of the server.
49+
- `public_ips` - The list of public IPs of the server
50+
- `id` - The ID of the IP
51+
- `address` - The address of the IP
52+
- `prefix` - The public IP prefix of the server.
4953
- `state` - The state of the server. Possible values are: `started`, `stopped` or `standby`.
5054
- `zone` - The [zone](../guides/regions_and_zones.md#zones) in which the server is.
5155
- `name` - The name of the server.
@@ -57,6 +61,7 @@ In addition to all above arguments, the following attributes are exported:
5761
- `ipv6_address` - The default ipv6 address routed to the server. ( Only set when enable_ipv6 is set to true )
5862
- `ipv6_gateway` - The ipv6 gateway address. ( Only set when enable_ipv6 is set to true )
5963
- `ipv6_prefix_length` - The prefix length of the ipv6 subnet routed to the server. ( Only set when enable_ipv6 is set to true )
64+
- `routed_ip_enabled` - True if the server support routed ip only.
6065
- `enable_dynamic_ip` - If true a dynamic IP will be attached to the server.
6166
- `image` - The UUID or the label of the base image used by the server.
6267
- `placement_group_id` - The [placement group](https://developers.scaleway.com/en/products/instance/api/#placement-groups-d8f653) the server is attached to.

docs/resources/instance_ip.md

+5
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@ resource "scaleway_instance_ip" "server_ip" {}
1717

1818
The following arguments are supported:
1919

20+
- `type` - (Defaults to `nat`) The type of the IP (`nat`, `routed_ipv4`, `routed_ipv6`), more information in [the documentation](https://www.scaleway.com/en/docs/compute/instances/api-cli/using-routed-ips/)
21+
22+
~> **Important:** An IP can migrate from `nat` to `routed_ipv4` but cannot be converted back
23+
2024
- `zone` - (Defaults to [provider](../index.md#zone) `zone`) The [zone](../guides/regions_and_zones.md#zones) in which the IP should be reserved.
2125
- `project_id` - (Defaults to [provider](../index.md#project_id) `project_id`) The ID of the project the IP is associated with.
2226

@@ -29,6 +33,7 @@ In addition to all above arguments, the following attributes are exported:
2933
~> **Important:** Instance IPs' IDs are [zoned](../guides/regions_and_zones.md#resource-ids), which means they are of the form `{zone}/{id}`, e.g. `fr-par-1/11111111-1111-1111-1111-111111111111`
3034

3135
- `address` - The IP address.
36+
- `prefix` - The IP Prefix.
3237
- `reverse` - The reverse dns attached to this IP
3338
- `organization_id` - The organization ID the IP is associated with.
3439
- `tags` - The tags associated with the IP.

docs/resources/instance_server.md

+12-3
Original file line numberDiff line numberDiff line change
@@ -212,10 +212,16 @@ attached to the server. Updates to this field will trigger a stop/start of the s
212212

213213
- `enable_ipv6` - (Defaults to `false`) Determines if IPv6 is enabled for the server.
214214

215-
- `ip_id` = (Optional) The ID of the reserved IP that is attached to the server.
215+
- `ip_id` - (Optional) The ID of the reserved IP that is attached to the server.
216+
217+
- `ip_ids` - (Optional) List of ID of reserved IPs that are attached to the server. Cannot be used with `ip_id`.
216218

217219
- `enable_dynamic_ip` - (Defaults to `false`) If true a dynamic IP will be attached to the server.
218220

221+
- `routed_ip_enabled` - (Defaults to `false`) If true, the server will support routed ips only. Changing it to true will migrate the server and its IP to routed type.
222+
223+
~> **Important:** Enabling routed ip will restart the server
224+
219225
- `state` - (Defaults to `started`) The state of the server. Possible values are: `started`, `stopped` or `standby`.
220226

221227
- `user_data` - (Optional) The user data associated with the server.
@@ -232,7 +238,7 @@ attached to the server. Updates to this field will trigger a stop/start of the s
232238

233239
- `replace_on_type_change` - (Defaults to false) If true, the server will be replaced if `type` is changed. Otherwise, the server will migrate.
234240

235-
- `bootscript_id` - The ID of the bootscript to use (set boot_type to `bootscript`).
241+
- `bootscript_id` (Deprecated) - The ID of the bootscript to use (set boot_type to `bootscript`).
236242

237243
- `zone` - (Defaults to [provider](../index.md#zone) `zone`) The [zone](../guides/regions_and_zones.md#zones) in which the server should be created.
238244

@@ -265,7 +271,10 @@ In addition to all above arguments, the following attributes are exported:
265271
- `root_volume`
266272
- `volume_id` - The volume ID of the root volume of the server.
267273
- `private_ip` - The Scaleway internal IP address of the server.
268-
- `public_ip` - The public IPv4 address of the server.
274+
- `public_ip` - The public IP address of the server.
275+
- `public_ips` - The list of public IPs of the server.
276+
- `id` - The ID of the IP
277+
- `address` - The address of the IP
269278
- `ipv6_address` - The default ipv6 address routed to the server. ( Only set when enable_ipv6 is set to true )
270279
- `ipv6_gateway` - The ipv6 gateway address. ( Only set when enable_ipv6 is set to true )
271280
- `ipv6_prefix_length` - The prefix length of the ipv6 subnet routed to the server. ( Only set when enable_ipv6 is set to true )

scaleway/data_source_instance_servers.go

+24
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,22 @@ func dataSourceScalewayInstanceServers() *schema.Resource {
4141
Computed: true,
4242
Type: schema.TypeString,
4343
},
44+
"public_ips": {
45+
Type: schema.TypeList,
46+
Computed: true,
47+
Elem: &schema.Resource{
48+
Schema: map[string]*schema.Schema{
49+
"id": {
50+
Type: schema.TypeString,
51+
Computed: true,
52+
},
53+
"address": {
54+
Type: schema.TypeString,
55+
Computed: true,
56+
},
57+
},
58+
},
59+
},
4460
"private_ip": {
4561
Computed: true,
4662
Type: schema.TypeString,
@@ -108,6 +124,10 @@ func dataSourceScalewayInstanceServers() *schema.Resource {
108124
Computed: true,
109125
Type: schema.TypeInt,
110126
},
127+
"routed_ip_enabled": {
128+
Computed: true,
129+
Type: schema.TypeBool,
130+
},
111131
"zone": zoneSchema(),
112132
"organization_id": organizationIDSchema(),
113133
"project_id": projectIDSchema(),
@@ -145,6 +165,9 @@ func dataSourceScalewayInstanceServersRead(ctx context.Context, d *schema.Resour
145165
if server.PublicIP != nil {
146166
rawServer["public_ip"] = server.PublicIP.Address.String()
147167
}
168+
if server.PublicIPs != nil {
169+
rawServer["public_ips"] = flattenServerPublicIPs(server.Zone, server.PublicIPs)
170+
}
148171
if server.PrivateIP != nil {
149172
rawServer["private_ip"] = *server.PrivateIP
150173
}
@@ -165,6 +188,7 @@ func dataSourceScalewayInstanceServersRead(ctx context.Context, d *schema.Resour
165188
rawServer["security_group_id"] = newZonedID(zone, server.SecurityGroup.ID).String()
166189
rawServer["enable_ipv6"] = server.EnableIPv6
167190
rawServer["enable_dynamic_ip"] = server.DynamicIPRequired
191+
rawServer["routed_ip_enabled"] = server.RoutedIPEnabled
168192
rawServer["organization_id"] = server.Organization
169193
rawServer["project_id"] = server.Project
170194
if server.Image != nil {

scaleway/helpers_instance.go

+26
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@ const (
3838
defaultInstanceSnapshotWaitTimeout = 1 * time.Hour
3939

4040
defaultInstanceImageTimeout = 1 * time.Hour
41+
42+
// netIPNil define the nil string return by (*net.IP).String()
43+
netIPNil = "<nil>"
4144
)
4245

4346
// instanceAPIWithZone returns a new instance API and the zone for a Create request
@@ -624,3 +627,26 @@ func retryUpdateReverseDNS(ctx context.Context, instanceAPI *instance.API, req *
624627
}
625628
}
626629
}
630+
631+
func flattenServerPublicIPs(zone scw.Zone, ips []*instance.ServerIP) []interface{} {
632+
flattenedIPs := make([]interface{}, len(ips))
633+
634+
for i, ip := range ips {
635+
flattenedIPs[i] = map[string]interface{}{
636+
"id": newZonedIDString(zone, ip.ID),
637+
"address": ip.Address.String(),
638+
}
639+
}
640+
641+
return flattenedIPs
642+
}
643+
644+
func flattenServerIPIDs(ips []*instance.ServerIP) []interface{} {
645+
ipIDs := make([]interface{}, len(ips))
646+
647+
for i, ip := range ips {
648+
ipIDs[i] = ip.ID
649+
}
650+
651+
return ipIDs
652+
}

scaleway/helpers_k8s.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -156,10 +156,10 @@ func convertNodes(res *k8s.ListNodesResponse) []map[string]interface{} {
156156
n := make(map[string]interface{})
157157
n["name"] = node.Name
158158
n["status"] = node.Status.String()
159-
if node.PublicIPV4 != nil && node.PublicIPV4.String() != "<nil>" {
159+
if node.PublicIPV4 != nil && node.PublicIPV4.String() != netIPNil {
160160
n["public_ip"] = node.PublicIPV4.String()
161161
}
162-
if node.PublicIPV6 != nil && node.PublicIPV6.String() != "<nil>" {
162+
if node.PublicIPV6 != nil && node.PublicIPV6.String() != netIPNil {
163163
n["public_ip_v6"] = node.PublicIPV6.String()
164164
}
165165
result = append(result, n)

scaleway/resource_instance_ip.go

+48-1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,17 @@ func resourceScalewayInstanceIP() *schema.Resource {
2828
Computed: true,
2929
Description: "The IP address",
3030
},
31+
"prefix": {
32+
Type: schema.TypeString,
33+
Computed: true,
34+
Description: "The IP prefix",
35+
},
36+
"type": {
37+
Type: schema.TypeString,
38+
Computed: true,
39+
Optional: true,
40+
Description: "The type of instance IP",
41+
},
3142
"reverse": {
3243
Type: schema.TypeString,
3344
Computed: true,
@@ -50,6 +61,23 @@ func resourceScalewayInstanceIP() *schema.Resource {
5061
"organization_id": organizationIDSchema(),
5162
"project_id": projectIDSchema(),
5263
},
64+
CustomizeDiff: func(ctx context.Context, diff *schema.ResourceDiff, i interface{}) error {
65+
// The only allowed change is
66+
// nat -> routed_ipv4
67+
if diff.HasChange("type") {
68+
before, after := diff.GetChange("type")
69+
oldType := instance.IPType(before.(string))
70+
newType := instance.IPType(after.(string))
71+
72+
if oldType == "nat" && newType == "routed_ipv4" {
73+
return nil
74+
}
75+
76+
return diff.ForceNew("type")
77+
}
78+
79+
return nil
80+
},
5381
}
5482
}
5583

@@ -61,6 +89,7 @@ func resourceScalewayInstanceIPCreate(ctx context.Context, d *schema.ResourceDat
6189
iprequest := &instance.CreateIPRequest{
6290
Zone: zone,
6391
Project: expandStringPtr(d.Get("project_id")),
92+
Type: instance.IPType(d.Get("type").(string)),
6493
}
6594
tags := expandStrings(d.Get("tags"))
6695
if len(tags) > 0 {
@@ -103,6 +132,10 @@ func resourceScalewayInstanceIPUpdate(ctx context.Context, d *schema.ResourceDat
103132
req.Tags = expandUpdatedStringsPtr(d.Get("tags"))
104133
}
105134

135+
if d.HasChange("type") {
136+
req.Type = instance.IPType(d.Get("type").(string))
137+
}
138+
106139
_, err = instanceAPI.UpdateIP(req, scw.WithContext(ctx))
107140
if err != nil {
108141
return diag.FromErr(err)
@@ -130,11 +163,25 @@ func resourceScalewayInstanceIPRead(ctx context.Context, d *schema.ResourceData,
130163
return diag.FromErr(err)
131164
}
132165

133-
_ = d.Set("address", res.IP.Address.String())
166+
address := res.IP.Address.String()
167+
prefix := res.IP.Prefix.String()
168+
if prefix == netIPNil {
169+
ipnet := scw.IPNet{}
170+
_ = (&ipnet).UnmarshalJSON([]byte("\"" + res.IP.Address.String() + "\""))
171+
prefix = ipnet.String()
172+
}
173+
if address == netIPNil {
174+
address = res.IP.Prefix.IP.String()
175+
}
176+
177+
_ = d.Set("address", address)
178+
_ = d.Set("prefix", prefix)
134179
_ = d.Set("zone", zone)
135180
_ = d.Set("organization_id", res.IP.Organization)
136181
_ = d.Set("project_id", res.IP.Project)
137182
_ = d.Set("reverse", res.IP.Reverse)
183+
_ = d.Set("type", res.IP.Type)
184+
138185
if len(res.IP.Tags) > 0 {
139186
_ = d.Set("tags", flattenSliceString(res.IP.Tags))
140187
}

0 commit comments

Comments
 (0)