Skip to content

Commit 505c37a

Browse files
committed
feat(instance): add warning for deprecated instance types
1 parent 78d3157 commit 505c37a

File tree

3 files changed

+162
-142
lines changed

3 files changed

+162
-142
lines changed

go.mod

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ require (
2727
github.com/nats-io/jwt/v2 v2.7.3
2828
github.com/nats-io/nats.go v1.38.0
2929
github.com/robfig/cron/v3 v3.0.1
30-
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.33.0.20250407124844-38ab1ca8e81d
30+
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.33.0.20250424152954-b4babe8f214c
3131
github.com/stretchr/testify v1.10.0
3232
golang.org/x/crypto v0.36.0
3333
gopkg.in/dnaeon/go-vcr.v3 v3.2.0
@@ -135,9 +135,9 @@ require (
135135
golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df // indirect
136136
golang.org/x/mod v0.23.0 // indirect
137137
golang.org/x/net v0.38.0 // indirect
138-
golang.org/x/sync v0.12.0 // indirect
138+
golang.org/x/sync v0.13.0 // indirect
139139
golang.org/x/sys v0.31.0 // indirect
140-
golang.org/x/text v0.23.0 // indirect
140+
golang.org/x/text v0.24.0 // indirect
141141
golang.org/x/time v0.3.0 // indirect
142142
golang.org/x/tools v0.30.0 // indirect
143143
google.golang.org/appengine v1.6.8 // indirect

go.sum

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -298,8 +298,8 @@ github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
298298
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
299299
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
300300
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
301-
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.33.0.20250407124844-38ab1ca8e81d h1:EjD1RHPgsTomVw0lKgZu6YHK4ZMSBkSjeFrirnvWBcw=
302-
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.33.0.20250407124844-38ab1ca8e81d/go.mod h1:792k1RTU+5JeMXm35/e2Wgp71qPH/DmDoZrRc+EFZDk=
301+
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.33.0.20250424152954-b4babe8f214c h1:sjbNFhI3o5ecQuxLZv54Gm/YlqP55Ot5l7ShneWeNg8=
302+
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.33.0.20250424152954-b4babe8f214c/go.mod h1:w4o02EHpO0CBGy2nehzWRaFQKd62G9HIf+Q07PDaUcE=
303303
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8=
304304
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
305305
github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
@@ -391,6 +391,8 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY
391391
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
392392
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
393393
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
394+
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
395+
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
394396
golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
395397
golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
396398
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -399,8 +401,8 @@ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJ
399401
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
400402
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
401403
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
402-
golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
403-
golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
404+
golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610=
405+
golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
404406
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
405407
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
406408
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -437,8 +439,8 @@ golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
437439
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
438440
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
439441
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
440-
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
441-
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
442+
golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
443+
golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
442444
golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
443445
golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
444446
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=

internal/services/instance/server.go

Lines changed: 151 additions & 133 deletions
Original file line numberDiff line numberDiff line change
@@ -600,180 +600,198 @@ func ResourceInstanceServerRead(ctx context.Context, d *schema.ResourceData, m i
600600

601601
return diag.FromErr(err)
602602
}
603+
603604
////
604605
// Read Server
605606
////
607+
state, err := serverStateFlatten(server.State)
608+
if err != nil {
609+
return diag.FromErr(err)
610+
}
606611

607-
if err == nil {
608-
state, err := serverStateFlatten(server.State)
609-
if err != nil {
610-
return diag.FromErr(err)
611-
}
612+
_ = d.Set("state", state)
613+
_ = d.Set("zone", string(zone))
614+
_ = d.Set("name", server.Name)
615+
_ = d.Set("boot_type", server.BootType)
612616

613-
_ = d.Set("state", state)
614-
_ = d.Set("zone", string(zone))
615-
_ = d.Set("name", server.Name)
616-
_ = d.Set("boot_type", server.BootType)
617+
_ = d.Set("type", server.CommercialType)
618+
if len(server.Tags) > 0 {
619+
_ = d.Set("tags", server.Tags)
620+
}
617621

618-
_ = d.Set("type", server.CommercialType)
619-
if len(server.Tags) > 0 {
620-
_ = d.Set("tags", server.Tags)
621-
}
622+
_ = d.Set("security_group_id", zonal.NewID(zone, server.SecurityGroup.ID).String())
623+
// EnableIPv6 is deprecated
624+
_ = d.Set("enable_ipv6", server.EnableIPv6) //nolint:staticcheck
625+
_ = d.Set("enable_dynamic_ip", server.DynamicIPRequired)
626+
_ = d.Set("organization_id", server.Organization)
627+
_ = d.Set("project_id", server.Project)
628+
_ = d.Set("protected", server.Protected)
622629

623-
_ = d.Set("security_group_id", zonal.NewID(zone, server.SecurityGroup.ID).String())
624-
// EnableIPv6 is deprecated
625-
_ = d.Set("enable_ipv6", server.EnableIPv6) //nolint:staticcheck
626-
_ = d.Set("enable_dynamic_ip", server.DynamicIPRequired)
627-
_ = d.Set("organization_id", server.Organization)
628-
_ = d.Set("project_id", server.Project)
629-
_ = d.Set("protected", server.Protected)
630-
631-
// Image could be empty in an import context.
632-
image := regional.ExpandID(d.Get("image").(string))
633-
if server.Image != nil && (image.ID == "" || scwvalidation.IsUUID(image.ID)) {
634-
_ = d.Set("image", zonal.NewID(zone, server.Image.ID).String())
635-
}
630+
// Image could be empty in an import context.
631+
image := regional.ExpandID(d.Get("image").(string))
632+
if server.Image != nil && (image.ID == "" || scwvalidation.IsUUID(image.ID)) {
633+
_ = d.Set("image", zonal.NewID(zone, server.Image.ID).String())
634+
}
636635

637-
if server.PlacementGroup != nil {
638-
_ = d.Set("placement_group_id", zonal.NewID(zone, server.PlacementGroup.ID).String())
639-
_ = d.Set("placement_group_policy_respected", server.PlacementGroup.PolicyRespected)
640-
}
636+
if server.PlacementGroup != nil {
637+
_ = d.Set("placement_group_id", zonal.NewID(zone, server.PlacementGroup.ID).String())
638+
_ = d.Set("placement_group_policy_respected", server.PlacementGroup.PolicyRespected)
639+
}
641640

642-
if server.PrivateIP != nil {
643-
_ = d.Set("private_ip", types.FlattenStringPtr(server.PrivateIP))
644-
}
641+
if server.PrivateIP != nil {
642+
_ = d.Set("private_ip", types.FlattenStringPtr(server.PrivateIP))
643+
}
645644

646-
if _, hasIPID := d.GetOk("ip_id"); server.PublicIP != nil && hasIPID { //nolint:staticcheck
647-
if !server.PublicIP.Dynamic { //nolint:staticcheck
648-
_ = d.Set("ip_id", zonal.NewID(zone, server.PublicIP.ID).String()) //nolint:staticcheck
649-
} else {
650-
_ = d.Set("ip_id", "")
651-
}
645+
if _, hasIPID := d.GetOk("ip_id"); server.PublicIP != nil && hasIPID { //nolint:staticcheck
646+
if !server.PublicIP.Dynamic { //nolint:staticcheck
647+
_ = d.Set("ip_id", zonal.NewID(zone, server.PublicIP.ID).String()) //nolint:staticcheck
652648
} else {
653649
_ = d.Set("ip_id", "")
654650
}
651+
} else {
652+
_ = d.Set("ip_id", "")
653+
}
655654

656-
if server.PublicIP != nil { //nolint:staticcheck
657-
_ = d.Set("public_ip", server.PublicIP.Address.String()) //nolint:staticcheck
658-
d.SetConnInfo(map[string]string{
659-
"type": "ssh",
660-
"host": server.PublicIP.Address.String(), //nolint:staticcheck
661-
})
662-
} else {
663-
_ = d.Set("public_ip", "")
664-
d.SetConnInfo(nil)
665-
}
666-
667-
if len(server.PublicIPs) > 0 {
668-
_ = d.Set("public_ips", flattenServerPublicIPs(server.Zone, server.PublicIPs))
669-
} else {
670-
_ = d.Set("public_ips", []interface{}{})
671-
}
655+
if server.PublicIP != nil { //nolint:staticcheck
656+
_ = d.Set("public_ip", server.PublicIP.Address.String()) //nolint:staticcheck
657+
d.SetConnInfo(map[string]string{
658+
"type": "ssh",
659+
"host": server.PublicIP.Address.String(), //nolint:staticcheck
660+
})
661+
} else {
662+
_ = d.Set("public_ip", "")
663+
d.SetConnInfo(nil)
664+
}
672665

673-
if _, hasIPIDs := d.GetOk("ip_ids"); hasIPIDs {
674-
_ = d.Set("ip_ids", flattenServerIPIDs(server.PublicIPs))
675-
} else {
676-
_ = d.Set("ip_ids", []interface{}{})
677-
}
666+
if len(server.PublicIPs) > 0 {
667+
_ = d.Set("public_ips", flattenServerPublicIPs(server.Zone, server.PublicIPs))
668+
} else {
669+
_ = d.Set("public_ips", []interface{}{})
670+
}
678671

679-
if server.IPv6 != nil { //nolint:staticcheck
680-
_ = d.Set("ipv6_address", server.IPv6.Address.String()) //nolint:staticcheck
681-
_ = d.Set("ipv6_gateway", server.IPv6.Gateway.String()) //nolint:staticcheck
672+
if _, hasIPIDs := d.GetOk("ip_ids"); hasIPIDs {
673+
_ = d.Set("ip_ids", flattenServerIPIDs(server.PublicIPs))
674+
} else {
675+
_ = d.Set("ip_ids", []interface{}{})
676+
}
682677

683-
prefixLength, err := strconv.Atoi(server.IPv6.Netmask) //nolint:staticcheck
684-
if err != nil {
685-
return diag.FromErr(err)
686-
}
678+
if server.IPv6 != nil { //nolint:staticcheck
679+
_ = d.Set("ipv6_address", server.IPv6.Address.String()) //nolint:staticcheck
680+
_ = d.Set("ipv6_gateway", server.IPv6.Gateway.String()) //nolint:staticcheck
687681

688-
_ = d.Set("ipv6_prefix_length", prefixLength)
689-
} else {
690-
_ = d.Set("ipv6_address", nil)
691-
_ = d.Set("ipv6_gateway", nil)
692-
_ = d.Set("ipv6_prefix_length", nil)
682+
prefixLength, err := strconv.Atoi(server.IPv6.Netmask) //nolint:staticcheck
683+
if err != nil {
684+
return diag.FromErr(err)
693685
}
694686

695-
var additionalVolumesIDs []string
687+
_ = d.Set("ipv6_prefix_length", prefixLength)
688+
} else {
689+
_ = d.Set("ipv6_address", nil)
690+
_ = d.Set("ipv6_gateway", nil)
691+
_ = d.Set("ipv6_prefix_length", nil)
692+
}
696693

697-
for i, serverVolume := range sortVolumeServer(server.Volumes) {
698-
if i == 0 {
699-
rootVolume := map[string]interface{}{}
694+
var additionalVolumesIDs []string
700695

701-
vs, ok := d.Get("root_volume").([]map[string]interface{})
702-
if ok && len(vs) > 0 {
703-
rootVolume = vs[0]
704-
}
696+
for i, serverVolume := range sortVolumeServer(server.Volumes) {
697+
if i == 0 {
698+
rootVolume := map[string]interface{}{}
705699

706-
vol, err := api.GetUnknownVolume(&instancehelpers.GetUnknownVolumeRequest{
707-
VolumeID: serverVolume.ID,
708-
Zone: server.Zone,
709-
})
710-
if err != nil {
711-
return diag.FromErr(fmt.Errorf("failed to read instance volume %s: %w", serverVolume.ID, err))
712-
}
713-
714-
rootVolume["volume_id"] = zonal.NewID(zone, vol.ID).String()
715-
if vol.Size != nil {
716-
rootVolume["size_in_gb"] = int(uint64(*vol.Size) / gb)
717-
} else if serverVolume.Size != nil {
718-
rootVolume["size_in_gb"] = int(uint64(*serverVolume.Size) / gb)
719-
}
700+
vs, ok := d.Get("root_volume").([]map[string]interface{})
701+
if ok && len(vs) > 0 {
702+
rootVolume = vs[0]
703+
}
720704

721-
if vol.IsBlockVolume() {
722-
rootVolume["sbs_iops"] = types.FlattenUint32Ptr(vol.Iops)
723-
}
705+
vol, err := api.GetUnknownVolume(&instancehelpers.GetUnknownVolumeRequest{
706+
VolumeID: serverVolume.ID,
707+
Zone: server.Zone,
708+
})
709+
if err != nil {
710+
return diag.FromErr(fmt.Errorf("failed to read instance volume %s: %w", serverVolume.ID, err))
711+
}
724712

725-
_, rootVolumeAttributeSet := d.GetOk("root_volume") // Related to https://github.com/hashicorp/terraform-plugin-sdk/issues/142
726-
rootVolume["delete_on_termination"] = d.Get("root_volume.0.delete_on_termination").(bool) || !rootVolumeAttributeSet
727-
rootVolume["volume_type"] = serverVolume.VolumeType
728-
rootVolume["boot"] = serverVolume.Boot
729-
rootVolume["name"] = serverVolume.Name
713+
rootVolume["volume_id"] = zonal.NewID(zone, vol.ID).String()
714+
if vol.Size != nil {
715+
rootVolume["size_in_gb"] = int(uint64(*vol.Size) / gb)
716+
} else if serverVolume.Size != nil {
717+
rootVolume["size_in_gb"] = int(uint64(*serverVolume.Size) / gb)
718+
}
730719

731-
_ = d.Set("root_volume", []map[string]interface{}{rootVolume})
732-
} else {
733-
additionalVolumesIDs = append(additionalVolumesIDs, zonal.NewID(zone, serverVolume.ID).String())
720+
if vol.IsBlockVolume() {
721+
rootVolume["sbs_iops"] = types.FlattenUint32Ptr(vol.Iops)
734722
}
735-
}
736723

737-
_ = d.Set("additional_volume_ids", additionalVolumesIDs)
738-
if len(additionalVolumesIDs) > 0 {
739-
_ = d.Set("additional_volume_ids", additionalVolumesIDs)
740-
}
741-
////
742-
// Read server user data
743-
////
744-
allUserData, _ := api.GetAllServerUserData(&instanceSDK.GetAllServerUserDataRequest{
745-
Zone: zone,
746-
ServerID: id,
747-
}, scw.WithContext(ctx))
724+
_, rootVolumeAttributeSet := d.GetOk("root_volume") // Related to https://github.com/hashicorp/terraform-plugin-sdk/issues/142
725+
rootVolume["delete_on_termination"] = d.Get("root_volume.0.delete_on_termination").(bool) || !rootVolumeAttributeSet
726+
rootVolume["volume_type"] = serverVolume.VolumeType
727+
rootVolume["boot"] = serverVolume.Boot
728+
rootVolume["name"] = serverVolume.Name
748729

749-
userData := make(map[string]interface{})
730+
_ = d.Set("root_volume", []map[string]interface{}{rootVolume})
731+
} else {
732+
additionalVolumesIDs = append(additionalVolumesIDs, zonal.NewID(zone, serverVolume.ID).String())
733+
}
734+
}
750735

751-
for key, value := range allUserData.UserData {
752-
userDataValue, err := io.ReadAll(value)
753-
if err != nil {
754-
return diag.FromErr(err)
755-
}
736+
_ = d.Set("additional_volume_ids", additionalVolumesIDs)
737+
if len(additionalVolumesIDs) > 0 {
738+
_ = d.Set("additional_volume_ids", additionalVolumesIDs)
739+
}
756740

757-
userData[key] = string(userDataValue)
758-
}
741+
////
742+
// Read server user data
743+
////
744+
allUserData, _ := api.GetAllServerUserData(&instanceSDK.GetAllServerUserDataRequest{
745+
Zone: zone,
746+
ServerID: id,
747+
}, scw.WithContext(ctx))
759748

760-
_ = d.Set("user_data", userData)
749+
userData := make(map[string]interface{})
761750

762-
////
763-
// Read server private networks
764-
////
765-
ph, err := newPrivateNICHandler(api.API, id, zone)
751+
for key, value := range allUserData.UserData {
752+
userDataValue, err := io.ReadAll(value)
766753
if err != nil {
767754
return diag.FromErr(err)
768755
}
769756

770-
// set private networks
771-
err = ph.set(d)
757+
userData[key] = string(userDataValue)
758+
}
759+
760+
_ = d.Set("user_data", userData)
761+
762+
////
763+
// Read server private networks
764+
////
765+
ph, err := newPrivateNICHandler(api.API, id, zone)
766+
if err != nil {
767+
return diag.FromErr(err)
768+
}
769+
770+
// set private networks
771+
err = ph.set(d)
772+
if err != nil {
773+
return diag.FromErr(err)
774+
}
775+
776+
////
777+
// Display warning if server will soon reach End of Service
778+
////
779+
if server.EndOfService {
780+
compatibleTypes, err := api.GetServerCompatibleTypes(&instanceSDK.GetServerCompatibleTypesRequest{
781+
Zone: zone,
782+
ServerID: id,
783+
}, scw.WithContext(ctx))
772784
if err != nil {
773785
return diag.FromErr(err)
774786
}
775787

776-
return nil
788+
return diag.Diagnostics{diag.Diagnostic{
789+
Severity: diag.Warning,
790+
Detail: fmt.Sprintf("Instance type %q will soon reach End of Service", server.CommercialType),
791+
Summary: fmt.Sprintf(`Your Instance will soon reach End of Service. You can check the exact date on the Scaleway console. We recommend that you migrate your Instance before that.
792+
Here are the best options for %q, ordered by relevance: [%s]`, server.CommercialType, strings.Join(compatibleTypes.CompatibleTypes, ", ")),
793+
AttributePath: cty.GetAttrPath("type"),
794+
}}
777795
}
778796

779797
return nil

0 commit comments

Comments
 (0)