Skip to content

Commit efe17c4

Browse files
committed
feat(instance): ip: document and test IPv6 specificity
1 parent 3f5b763 commit efe17c4

File tree

5 files changed

+2451
-5
lines changed

5 files changed

+2451
-5
lines changed

docs/resources/instance_ip.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,11 @@ In addition to all arguments above, the following attributes are exported:
3030
~> **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`
3131

3232
- `address` - The IP address.
33+
34+
~> **Important:** For IPv6 addresses, the full address is not readable from the "scaleway_instance_ip" resource, only the prefix.
35+
To get the full address, the IP must be attached to another resource and read from the latter.
36+
For example, for a server, the address will be listed in the "public_ips" or "private_ips" attribute of the "scaleway_instance_server".
37+
3338
- `prefix` - The IP Prefix.
3439
- `reverse` - The reverse dns attached to this IP
3540
- `organization_id` - The organization ID the IP is associated with.

internal/services/instance/helpers_instance.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -599,3 +599,15 @@ func DeleteASGServers(
599599

600600
return nil
601601
}
602+
603+
// FindIPInList looks for an IP in a list to avoid using the deprecated server.PublicIP field
604+
func FindIPInList(ipID string, ips []*instance.ServerIP) *instance.ServerIP {
605+
id := zonal.ExpandID(ipID).ID
606+
for _, ip := range ips {
607+
if ip.ID == id {
608+
return ip
609+
}
610+
}
611+
612+
return nil
613+
}

internal/services/instance/ip_test.go

Lines changed: 46 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package instance_test
33
import (
44
"fmt"
55
"net"
6+
"strings"
67
"testing"
78

89
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
@@ -120,7 +121,41 @@ func TestAccIP_RoutedIPV6(t *testing.T) {
120121
resource.TestCheckResourceAttr("scaleway_instance_ip.main", "type", "routed_ipv6"),
121122
resource.TestCheckResourceAttrSet("scaleway_instance_ip.main", "address"),
122123
resource.TestCheckResourceAttrSet("scaleway_instance_ip.main", "prefix"),
123-
isIPValid("scaleway_instance_ip.main", "address"),
124+
isIPValid("scaleway_instance_ip.main", "address", true),
125+
isIPCIDRValid("scaleway_instance_ip.main", "prefix"),
126+
),
127+
},
128+
},
129+
})
130+
}
131+
132+
func TestAccIP_RoutedIPV6_Attached(t *testing.T) {
133+
tt := acctest.NewTestTools(t)
134+
defer tt.Cleanup()
135+
136+
resource.ParallelTest(t, resource.TestCase{
137+
ProviderFactories: tt.ProviderFactories,
138+
CheckDestroy: instancechecks.IsIPDestroyed(tt),
139+
Steps: []resource.TestStep{
140+
{
141+
Config: `
142+
resource "scaleway_instance_ip" "main" {
143+
type = "routed_ipv6"
144+
}
145+
resource "scaleway_instance_server" "main" {
146+
type = "PRO2-S"
147+
image = "ubuntu_noble"
148+
ip_id = scaleway_instance_ip.main.id
149+
}
150+
`,
151+
Check: resource.ComposeTestCheckFunc(
152+
instancechecks.CheckIPExists(tt, "scaleway_instance_ip.main"),
153+
resource.TestCheckResourceAttr("scaleway_instance_ip.main", "type", "routed_ipv6"),
154+
resource.TestCheckResourceAttrSet("scaleway_instance_ip.main", "address"),
155+
resource.TestCheckResourceAttrSet("scaleway_instance_ip.main", "prefix"),
156+
resource.TestCheckResourceAttrSet("scaleway_instance_server.main", "public_ips.0.address"),
157+
isIPValid("scaleway_instance_ip.main", "address", true),
158+
isIPValid("scaleway_instance_server.main", "public_ips.0.address", false),
124159
isIPCIDRValid("scaleway_instance_ip.main", "prefix"),
125160
),
126161
},
@@ -149,7 +184,7 @@ func isIPCIDRValid(name string, key string) resource.TestCheckFunc {
149184
}
150185
}
151186

152-
func isIPValid(name string, key string) resource.TestCheckFunc {
187+
func isIPValid(name string, key string, acceptPrefixFormat bool) resource.TestCheckFunc {
153188
return func(s *terraform.State) error {
154189
rs, ok := s.RootModule().Resources[name]
155190
if !ok {
@@ -161,6 +196,10 @@ func isIPValid(name string, key string) resource.TestCheckFunc {
161196
return fmt.Errorf("requested attribute %s[%q] does not exist", name, key)
162197
}
163198

199+
if !acceptPrefixFormat && strings.HasSuffix(ip, "::") {
200+
return fmt.Errorf("ip has prefix format (%s) in %s[%s]", ip, name, key)
201+
}
202+
164203
parsedIP := net.ParseIP(ip)
165204
if parsedIP == nil {
166205
return fmt.Errorf("invalid ip (%s) in %s[%q]", ip, name, key)
@@ -203,8 +242,9 @@ func isIPAttachedToServer(tt *acctest.TestTools, ipResource, serverResource stri
203242
return err
204243
}
205244

206-
if server.Server.PublicIP != nil && server.Server.PublicIP.Address.String() != ip.IP.Address.String() { //nolint:staticcheck
207-
return fmt.Errorf("IPs should be the same in %s and %s: %v is different than %v", ipResource, serverResource, server.Server.PublicIP.Address, ip.IP.Address) //nolint:staticcheck
245+
publicIP := instance.FindIPInList(ID, server.Server.PublicIPs)
246+
if publicIP != nil && publicIP.Address.String() != ip.IP.Address.String() {
247+
return fmt.Errorf("IPs should be the same in %s and %s: %v is different than %v", ipResource, serverResource, publicIP.Address, ip.IP.Address)
208248
}
209249

210250
return nil
@@ -231,7 +271,8 @@ func serverHasNoIPAssigned(tt *acctest.TestTools, serverResource string) resourc
231271
return err
232272
}
233273

234-
if server.Server.PublicIP != nil && !server.Server.PublicIP.Dynamic { //nolint:staticcheck
274+
publicIP := instance.FindIPInList(ID, server.Server.PublicIPs)
275+
if publicIP != nil && !publicIP.Dynamic {
235276
return fmt.Errorf("no flexible IP should be assigned to %s", serverResource)
236277
}
237278

internal/services/instance/testdata/ip-routed-ipv6-attached.cassette.yaml

Lines changed: 2383 additions & 0 deletions
Large diffs are not rendered by default.

templates/resources/instance_ip.md.tmpl

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,11 @@ In addition to all arguments above, the following attributes are exported:
3131
~> **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`
3232

3333
- `address` - The IP address.
34+
35+
~> **Important:** For IPv6 addresses, the full address is not readable from the "scaleway_instance_ip" resource, only the prefix.
36+
To get the full address, the IP must be attached to another resource and read from the latter.
37+
For example, for a server, the address will be listed in the "public_ips" or "private_ips" attribute of the "scaleway_instance_server".
38+
3439
- `prefix` - The IP Prefix.
3540
- `reverse` - The reverse dns attached to this IP
3641
- `organization_id` - The organization ID the IP is associated with.

0 commit comments

Comments
 (0)