From 1699bef6d1b0e7bb7cc58723cc1c4abc96aec842 Mon Sep 17 00:00:00 2001 From: jo Date: Fri, 10 Jan 2025 14:41:16 +0000 Subject: [PATCH] refactor: verify low confidence --- internal/firewall/attachment_resource.go | 22 +-- .../attachment_resource_internal_test.go | 34 ++--- internal/server/resource.go | 129 ++++++++---------- internal/server/resource_test.go | 27 ++++ 4 files changed, 109 insertions(+), 103 deletions(-) diff --git a/internal/firewall/attachment_resource.go b/internal/firewall/attachment_resource.go index 54ebc9d13..1d13cd755 100644 --- a/internal/firewall/attachment_resource.go +++ b/internal/firewall/attachment_resource.go @@ -5,12 +5,12 @@ import ( "fmt" "log" "sort" - "strconv" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hetznercloud/hcloud-go/v2/hcloud" + "github.com/hetznercloud/terraform-provider-hcloud/internal/util" "github.com/hetznercloud/terraform-provider-hcloud/internal/util/hcloudutil" ) @@ -170,8 +170,8 @@ func deleteAttachment(ctx context.Context, d *schema.ResourceData, m interface{} } type attachment struct { - FirewallID int - ServerIDs []int + FirewallID int64 + ServerIDs []int64 LabelSelectors []string } @@ -179,12 +179,12 @@ type attachment struct { func (a *attachment) FromResourceData(d *schema.ResourceData) error { // The terraform schema definition above ensures this is always set and // of the correct type. Thus there is no need to check such things. - a.FirewallID = d.Get("firewall_id").(int) + a.FirewallID = util.CastInt64(d.Get("firewall_id")) srvIDs, ok := d.GetOk("server_ids") if ok { for _, v := range srvIDs.(*schema.Set).List() { - a.ServerIDs = append(a.ServerIDs, v.(int)) + a.ServerIDs = append(a.ServerIDs, util.CastInt64(v)) } sort.Slice(a.ServerIDs, func(i, j int) bool { return a.ServerIDs[i] < a.ServerIDs[j] @@ -216,7 +216,7 @@ func (a *attachment) ToResourceData(d *schema.ResourceData) { if len(a.ServerIDs) > 0 { vals := make([]interface{}, len(a.ServerIDs)) for i, id := range a.ServerIDs { - vals[i] = id + vals[i] = int(id) } f := d.Get("server_ids").(*schema.Set).F // Returns a default value if server_ids is not present in HCL. srvIDs = schema.NewSet(f, vals) @@ -233,8 +233,8 @@ func (a *attachment) ToResourceData(d *schema.ResourceData) { } d.Set("label_selectors", lSels) - d.Set("firewall_id", a.FirewallID) - d.SetId(strconv.Itoa(a.FirewallID)) + d.Set("firewall_id", int(a.FirewallID)) + d.SetId(util.FormatID(a.FirewallID)) } // FromFirewall reads the attachment data from fw into a. @@ -285,7 +285,7 @@ func (a *attachment) AllResources() []hcloud.FirewallResource { func (a *attachment) DiffResources(o attachment) ([]hcloud.FirewallResource, []hcloud.FirewallResource) { var more, less []hcloud.FirewallResource // nolint: prealloc - aSrvs := make(map[int]bool, len(a.ServerIDs)) + aSrvs := make(map[int64]bool, len(a.ServerIDs)) for _, id := range a.ServerIDs { aSrvs[id] = true } @@ -307,7 +307,7 @@ func (a *attachment) DiffResources(o attachment) ([]hcloud.FirewallResource, []h less = append(less, labelSelectorResource(ls)) } - oSrvs := make(map[int]bool, len(o.ServerIDs)) + oSrvs := make(map[int64]bool, len(o.ServerIDs)) for _, id := range o.ServerIDs { oSrvs[id] = true } @@ -332,7 +332,7 @@ func (a *attachment) DiffResources(o attachment) ([]hcloud.FirewallResource, []h return less, more } -func serverResource(id int) hcloud.FirewallResource { +func serverResource(id int64) hcloud.FirewallResource { return hcloud.FirewallResource{ Type: hcloud.FirewallResourceTypeServer, Server: &hcloud.FirewallResourceServer{ID: id}, diff --git a/internal/firewall/attachment_resource_internal_test.go b/internal/firewall/attachment_resource_internal_test.go index 8205333dc..ac68bff12 100644 --- a/internal/firewall/attachment_resource_internal_test.go +++ b/internal/firewall/attachment_resource_internal_test.go @@ -1,7 +1,6 @@ package firewall import ( - "strconv" "testing" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" @@ -9,6 +8,7 @@ import ( "github.com/stretchr/testify/require" "github.com/hetznercloud/hcloud-go/v2/hcloud" + "github.com/hetznercloud/terraform-provider-hcloud/internal/util" ) func TestAttachment_FromResourceData(t *testing.T) { @@ -27,7 +27,7 @@ func TestAttachment_FromResourceData(t *testing.T) { }, att: attachment{ FirewallID: 4711, - ServerIDs: []int{1, 2, 3}, + ServerIDs: []int64{1, 2, 3}, LabelSelectors: []string{"key1=value1", "key2=value2"}, }, }, @@ -39,7 +39,7 @@ func TestAttachment_FromResourceData(t *testing.T) { }, att: attachment{ FirewallID: 4712, - ServerIDs: []int{4, 5, 6}, + ServerIDs: []int64{4, 5, 6}, }, }, { @@ -94,7 +94,7 @@ func TestAttachment_ToResourceData(t *testing.T) { name: "server_ids and label_selectors present", att: attachment{ FirewallID: 4711, - ServerIDs: []int{1, 2, 3}, + ServerIDs: []int64{1, 2, 3}, LabelSelectors: []string{"key1=value1", "key2=value2"}, }, }, @@ -102,7 +102,7 @@ func TestAttachment_ToResourceData(t *testing.T) { name: "only server_ids present", att: attachment{ FirewallID: 4712, - ServerIDs: []int{4, 5, 6}, + ServerIDs: []int64{4, 5, 6}, }, }, { @@ -131,7 +131,7 @@ func TestAttachment_ToResourceData(t *testing.T) { }, att: attachment{ FirewallID: 4714, - ServerIDs: []int{1, 2, 3}, + ServerIDs: []int64{1, 2, 3}, }, }, } @@ -143,8 +143,8 @@ func TestAttachment_ToResourceData(t *testing.T) { tt.att.ToResourceData(data) - assert.Equal(t, data.Id(), strconv.Itoa(tt.att.FirewallID)) - assert.Equal(t, data.Get("firewall_id"), tt.att.FirewallID) + assert.Equal(t, data.Id(), util.FormatID(tt.att.FirewallID)) + assert.Equal(t, data.Get("firewall_id"), int(tt.att.FirewallID)) srvIDdata, ok := data.GetOk("server_ids") if len(tt.att.ServerIDs) > 0 { @@ -152,7 +152,7 @@ func TestAttachment_ToResourceData(t *testing.T) { // Need to iterate as the types of the slices don't match: []int vs []interface{} for _, id := range tt.att.ServerIDs { - assert.Contains(t, srvIDdata.(*schema.Set).List(), id) + assert.Contains(t, srvIDdata.(*schema.Set).List(), int(id)) } } else { assert.False(t, ok, "expected no server_ids in data") @@ -195,7 +195,7 @@ func TestAttachment_FromFirewall(t *testing.T) { }, att: attachment{ FirewallID: 4712, - ServerIDs: []int{1, 2}, + ServerIDs: []int64{1, 2}, }, }, { @@ -257,7 +257,7 @@ func TestAttachment_AllResources(t *testing.T) { name: "servers and label selectors attached", att: attachment{ FirewallID: 4712, - ServerIDs: []int{1, 2}, + ServerIDs: []int64{1, 2}, LabelSelectors: []string{"key1=value1", "key2=value2"}, }, res: []hcloud.FirewallResource{ @@ -290,12 +290,12 @@ func TestAttachment_DiffResources(t *testing.T) { name: "nothing changed", att: attachment{ FirewallID: 4711, - ServerIDs: []int{1, 2, 3}, + ServerIDs: []int64{1, 2, 3}, LabelSelectors: []string{"key1=value1", "key2=value2"}, }, other: attachment{ FirewallID: 4711, - ServerIDs: []int{1, 2, 3}, + ServerIDs: []int64{1, 2, 3}, LabelSelectors: []string{"key1=value1", "key2=value2"}, }, }, @@ -303,12 +303,12 @@ func TestAttachment_DiffResources(t *testing.T) { name: "resources in att but not in other", att: attachment{ FirewallID: 4711, - ServerIDs: []int{1, 2, 3}, + ServerIDs: []int64{1, 2, 3}, LabelSelectors: []string{"key1=value1", "key2=value2"}, }, other: attachment{ FirewallID: 4711, - ServerIDs: []int{1, 2}, + ServerIDs: []int64{1, 2}, LabelSelectors: []string{"key1=value1"}, }, more: []hcloud.FirewallResource{ @@ -320,12 +320,12 @@ func TestAttachment_DiffResources(t *testing.T) { name: "resources in other but not in att", att: attachment{ FirewallID: 4711, - ServerIDs: []int{1, 2}, + ServerIDs: []int64{1, 2}, LabelSelectors: []string{"key1=value1"}, }, other: attachment{ FirewallID: 4711, - ServerIDs: []int{1, 2, 3}, + ServerIDs: []int64{1, 2, 3}, LabelSelectors: []string{"key1=value1", "key2=value2"}, }, less: []hcloud.FirewallResource{ diff --git a/internal/server/resource.go b/internal/server/resource.go index e0dd095d1..d196f5221 100644 --- a/internal/server/resource.go +++ b/internal/server/resource.go @@ -8,7 +8,6 @@ import ( "fmt" "log" "net" - "strconv" "strings" "time" @@ -19,6 +18,7 @@ import ( "github.com/hetznercloud/hcloud-go/v2/hcloud" "github.com/hetznercloud/terraform-provider-hcloud/internal/primaryip" + "github.com/hetznercloud/terraform-provider-hcloud/internal/util" "github.com/hetznercloud/terraform-provider-hcloud/internal/util/control" "github.com/hetznercloud/terraform-provider-hcloud/internal/util/hcloudutil" ) @@ -352,12 +352,12 @@ func resourceServerCreate(ctx context.Context, d *schema.ResourceData, m interfa if firewallIDs, ok := d.GetOk("firewall_ids"); ok { for _, firewallID := range firewallIDs.(*schema.Set).List() { - opts.Firewalls = append(opts.Firewalls, &hcloud.ServerCreateFirewall{Firewall: hcloud.Firewall{ID: firewallID.(int)}}) + opts.Firewalls = append(opts.Firewalls, &hcloud.ServerCreateFirewall{Firewall: hcloud.Firewall{ID: util.CastInt64(firewallID)}}) } } if placementGroupID, ok := d.GetOk("placement_group_id"); ok { - placementGroup, err := getPlacementGroup(ctx, c, placementGroupID.(int)) + placementGroup, err := getPlacementGroup(ctx, c, util.CastInt64(placementGroupID)) if err != nil { return hcloudutil.ErrorToDiag(err) } @@ -369,19 +369,19 @@ func resourceServerCreate(ctx context.Context, d *schema.ResourceData, m interfa createPublicNet := hcloud.ServerCreatePublicNet{} for _, publicNetBlock := range publicNet.(*schema.Set).List() { publicNetEntry := publicNetBlock.(map[string]interface{}) - if enableIPv4, err := toServerPublicNet[bool](publicNetEntry, "ipv4_enabled"); err == nil { + if enableIPv4, err := ToPublicNetField[bool](publicNetEntry, "ipv4_enabled"); err == nil { createPublicNet.EnableIPv4 = enableIPv4 } - if enableIPv6, err := toServerPublicNet[bool](publicNetEntry, "ipv6_enabled"); err == nil { + if enableIPv6, err := ToPublicNetField[bool](publicNetEntry, "ipv6_enabled"); err == nil { createPublicNet.EnableIPv6 = enableIPv6 } - if ipv4, err := toServerPublicNet[int](publicNetEntry, "ipv4"); err == nil && ipv4 != 0 { + if ipv4, err := ToPublicNetField[int](publicNetEntry, "ipv4"); err == nil && ipv4 != 0 { createPublicNet.EnableIPv4 = true - createPublicNet.IPv4 = &hcloud.PrimaryIP{ID: ipv4} + createPublicNet.IPv4 = &hcloud.PrimaryIP{ID: util.CastInt64(ipv4)} } - if ipv6, err := toServerPublicNet[int](publicNetEntry, "ipv6"); err == nil && ipv6 != 0 { + if ipv6, err := ToPublicNetField[int](publicNetEntry, "ipv6"); err == nil && ipv6 != 0 { createPublicNet.EnableIPv6 = true - createPublicNet.IPv6 = &hcloud.PrimaryIP{ID: ipv6} + createPublicNet.IPv6 = &hcloud.PrimaryIP{ID: util.CastInt64(ipv6)} } } opts.PublicNet = &createPublicNet @@ -397,7 +397,7 @@ func resourceServerCreate(ctx context.Context, d *schema.ResourceData, m interfa if err != nil { return hcloudutil.ErrorToDiag(err) } - d.SetId(strconv.Itoa(res.Server.ID)) + d.SetId(util.FormatID(res.Server.ID)) if err := hcloudutil.WaitForAction(ctx, &c.Action, res.Action); err != nil { return hcloudutil.ErrorToDiag(err) @@ -580,7 +580,7 @@ func resourceServerUpdate(ctx context.Context, d *schema.ResourceData, m interfa for _, f := range server.PublicNet.Firewalls { found := false for _, i := range firewallIDs { - fID := i.(int) + fID := util.CastInt64(i) if f.Firewall.ID == fID { found = true @@ -609,7 +609,7 @@ func resourceServerUpdate(ctx context.Context, d *schema.ResourceData, m interfa } for _, i := range firewallIDs { - fID := i.(int) + fID := util.CastInt64(i) found := false for _, f := range server.PublicNet.Firewalls { if f.Firewall.ID == fID { @@ -648,7 +648,7 @@ func resourceServerUpdate(ctx context.Context, d *schema.ResourceData, m interfa } if d.HasChange("placement_group_id") { - placementGroupID := d.Get("placement_group_id").(int) + placementGroupID := util.CastInt64(d.Get("placement_group_id")) if err := setPlacementGroup(ctx, c, server, placementGroupID); err != nil { return hcloudutil.ErrorToDiag(err) } @@ -670,18 +670,18 @@ func updatePublicNet(ctx context.Context, o interface{}, n interface{}, c *hclou diffToRemove := o.(*schema.Set).Difference(n.(*schema.Set)) diffToAdd := n.(*schema.Set).Difference(o.(*schema.Set)) - ipv4IDToRemove := 0 - ipv6IDToRemove := 0 + var ipv4IDToRemove int64 + var ipv6IDToRemove int64 ipv4EnabledInRemoveDiff := true ipv6EnabledInRemoveDiff := true // collect ip IDs which got removed for _, d := range diffToRemove.List() { fields := d.(map[string]interface{}) ipv4IDToRemove, ipv6IDToRemove = collectPrimaryIPIDs(fields) - if ipv4Enabled, err := toPublicNetPrimaryIPField[bool](fields, "ipv4_enabled"); err == nil { + if ipv4Enabled, err := ToPublicNetField[bool](fields, "ipv4_enabled"); err == nil { ipv4EnabledInRemoveDiff = ipv4Enabled } - if ipv6Enabled, err := toPublicNetPrimaryIPField[bool](fields, "ipv6_enabled"); err == nil { + if ipv6Enabled, err := ToPublicNetField[bool](fields, "ipv6_enabled"); err == nil { ipv6EnabledInRemoveDiff = ipv6Enabled } } @@ -720,7 +720,7 @@ func updatePublicNet(ctx context.Context, o interface{}, n interface{}, c *hclou fields := d.(map[string]interface{}) ipv4IDToAdd, ipv6IDToAdd := collectPrimaryIPIDs(fields) - if ipv4Enabled, err := toPublicNetPrimaryIPField[bool](fields, "ipv4_enabled"); err == nil { + if ipv4Enabled, err := ToPublicNetField[bool](fields, "ipv4_enabled"); err == nil { if err := publicNetUpdateDecision(ctx, c, ipv4Enabled, @@ -733,7 +733,7 @@ func updatePublicNet(ctx context.Context, o interface{}, n interface{}, c *hclou return err } } - if ipv6Enabled, err := toPublicNetPrimaryIPField[bool](fields, "ipv6_enabled"); err == nil { + if ipv6Enabled, err := ToPublicNetField[bool](fields, "ipv6_enabled"); err == nil { if err := publicNetUpdateDecision(ctx, c, ipv6Enabled, ipv6EnabledInRemoveDiff, @@ -758,10 +758,10 @@ func publicNetUpdateDecision(ctx context.Context, c *hcloud.Client, ipEnabled bool, ipEnabledInRemoveDiff bool, - ipID int, - ipIDInRemoveDiff int, + ipID int64, + ipIDInRemoveDiff int64, server *hcloud.Server, - serverIPID int, + serverIPID int64, ipType hcloud.PrimaryIPType) diag.Diagnostics { switch { // if ip set true + ip id, remove all previous assigned ipv4 + assign new @@ -840,7 +840,7 @@ func publicNetUpdateDecision(ctx context.Context, func resourceServerDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { client := m.(*hcloud.Client) - serverID, err := strconv.Atoi(d.Id()) + serverID, err := util.ParseID(d.Id()) if err != nil { log.Printf("[WARN] invalid server id (%s), removing from state: %v", d.Id(), err) d.SetId("") @@ -1044,7 +1044,7 @@ func getSSHkeys(ctx context.Context, client *hcloud.Client, d *schema.ResourceDa func inlineAttachServerToNetwork(ctx context.Context, c *hcloud.Client, s *hcloud.Server, nwData map[string]interface{}) error { const op = "hcloud/inlineAttachServerToNetwork" - nw := &hcloud.Network{ID: nwData["network_id"].(int)} + nw := &hcloud.Network{ID: util.CastInt64(nwData["network_id"])} ip := net.ParseIP(nwData["ip"].(string)) aliasIPs := make([]net.IP, 0, nwData["alias_ips"].(*schema.Set).Len()) @@ -1064,10 +1064,10 @@ func updateServerInlineNetworkAttachments(ctx context.Context, c *hcloud.Client, log.Printf("[INFO] Updating inline network attachments for server %d", s.ID) - cfgNetworks := make(map[int]map[string]interface{}, data.Len()) + cfgNetworks := make(map[int64]map[string]interface{}, data.Len()) for _, v := range data.List() { nwData := v.(map[string]interface{}) - nwID := nwData["network_id"].(int) + nwID := util.CastInt64(nwData["network_id"]) cfgNetworks[nwID] = nwData } @@ -1127,23 +1127,13 @@ func newIPSet(f schema.SchemaSetFunc, ips []net.IP) *schema.Set { } func setServerSchema(d *schema.ResourceData, s *hcloud.Server) { - for key, val := range getServerAttributes(d, s) { - switch key { - case "id": - d.SetId(strconv.Itoa(val.(int))) - default: - err := d.Set(key, val) - if err != nil { - log.Fatal(err) - } - } - } + util.SetSchemaFromAttributes(d, getServerAttributes(d, s)) } func getServerAttributes(d *schema.ResourceData, s *hcloud.Server) map[string]interface{} { firewallIDs := make([]int, len(s.PublicNet.Firewalls)) for i, firewall := range s.PublicNet.Firewalls { - firewallIDs[i] = firewall.Firewall.ID + firewallIDs[i] = int(firewall.Firewall.ID) } res := map[string]interface{}{ @@ -1177,7 +1167,7 @@ func getServerAttributes(d *schema.ResourceData, s *hcloud.Server) map[string]in } if s.Image != nil { - if s.Image.Name != "" && strconv.Itoa(s.Image.ID) != d.Get("image") { + if s.Image.Name != "" && util.FormatID(s.Image.ID) != d.Get("image") { // Only use the image name if the image is official (Name != "") // AND the user did not explicitly specify the image id res["image"] = s.Image.Name @@ -1199,7 +1189,7 @@ func getServerAttributes(d *schema.ResourceData, s *hcloud.Server) map[string]in } if s.PlacementGroup != nil { - res["placement_group_id"] = s.PlacementGroup.ID + res["placement_group_id"] = int(s.PlacementGroup.ID) } else { res["placement_group_id"] = nil } @@ -1224,7 +1214,7 @@ func networkToTerraformNetworks(privateNetworks []hcloud.ServerPrivateNet) []map return tfPrivateNetworks } -func getPlacementGroup(ctx context.Context, c *hcloud.Client, id int) (*hcloud.PlacementGroup, error) { +func getPlacementGroup(ctx context.Context, c *hcloud.Client, id int64) (*hcloud.PlacementGroup, error) { placementGroup, _, err := c.PlacementGroup.GetByID(ctx, id) if err != nil { return nil, err @@ -1237,7 +1227,7 @@ func getPlacementGroup(ctx context.Context, c *hcloud.Client, id int) (*hcloud.P return placementGroup, nil } -func setPlacementGroup(ctx context.Context, c *hcloud.Client, server *hcloud.Server, id int) error { +func setPlacementGroup(ctx context.Context, c *hcloud.Client, server *hcloud.Server, id int64) error { if server.PlacementGroup != nil { if server.Status != hcloud.ServerStatusOff { // Removing PG requires the server to be shut down before, this is an invasive operation. We do not currently @@ -1288,34 +1278,27 @@ func setProtection(ctx context.Context, c *hcloud.Client, server *hcloud.Server, return hcloudutil.WaitForAction(ctx, &c.Action, action) } -func toServerPublicNet[V int | bool](field map[string]interface{}, key string) (V, error) { - var op = "toServerPublicNet" - var valType V - if valType, ok := field[key].(V); ok { - return valType, nil - } - return valType, fmt.Errorf("%s: unable to apply value to public_net values", op) -} - -func collectPrimaryIPIDs(primaryIPList map[string]interface{}) (int, int) { - var IPv4ID = 0 - var IPv6ID = 0 - if id, err := toPublicNetPrimaryIPField[int](primaryIPList, "ipv4"); id != 0 && err == nil { - IPv4ID = id +func collectPrimaryIPIDs(primaryIPList map[string]interface{}) (int64, int64) { + var IPv4ID int64 + var IPv6ID int64 + if id, err := ToPublicNetField[int](primaryIPList, "ipv4"); id != 0 && err == nil { + IPv4ID = util.CastInt64(id) } - if id, err := toPublicNetPrimaryIPField[int](primaryIPList, "ipv6"); id != 0 && err == nil { - IPv6ID = id + if id, err := ToPublicNetField[int](primaryIPList, "ipv6"); id != 0 && err == nil { + IPv6ID = util.CastInt64(id) } return IPv4ID, IPv6ID } -func toPublicNetPrimaryIPField[V int | bool](field map[string]interface{}, key string) (V, error) { - var op = "toPublicNetPrimaryIPField" - var fieldValue V - if fieldValue, ok := field[key].(V); ok { - return fieldValue, nil +func ToPublicNetField[V int | bool](field map[string]interface{}, key string) (V, error) { + var op = "ToPublicNetField" + + if value, ok := field[key]; ok { + return value.(V), nil } - return fieldValue, fmt.Errorf("%s: field does not contain ID", op) + + var zero V + return zero, fmt.Errorf("%s: field does not contain key: %s", op, key) } func onServerCreateWithoutPublicNet(opts *hcloud.ServerCreateOpts, d *schema.ResourceData, fn func(opts *hcloud.ServerCreateOpts) error) diag.Diagnostics { @@ -1348,8 +1331,8 @@ func powerOnServer(ctx context.Context, c *hcloud.Client, server *hcloud.Server) func publicNetRemovedDecision(ctx context.Context, c *hcloud.Client, server *hcloud.Server, - serverIPID int, - ipIDToRemove int, + serverIPID int64, + ipIDToRemove int64, ipType hcloud.PrimaryIPType) diag.Diagnostics { if server.PublicNet.IPv4.ID != 0 && ipIDToRemove != 0 { if err := primaryip.UnassignPrimaryIP(ctx, c, serverIPID); err != nil { @@ -1374,7 +1357,7 @@ func validateUniqueNetworkIDs(d *schema.ResourceDiff) error { return fmt.Errorf("network has unexpected type: %T", n) } - uniqueNetworkIDs := map[int]bool{} + uniqueNetworkIDs := map[int64]bool{} for _, networkI := range networks.List() { network, ok := networkI.(map[string]interface{}) @@ -1382,11 +1365,11 @@ func validateUniqueNetworkIDs(d *schema.ResourceDiff) error { return fmt.Errorf("network item has unexpected type: %T", networkI) } - networkID, ok := network["network_id"] + networkIDI, ok := network["network_id"] if !ok { continue } - if networkID == 0 { + if util.CastInt64(networkIDI) == 0 { // ID is 0 if Network will be created in same apply, we are unable to reliably detect if the // "to-be-created" networks are the same. // See https://github.com/hetznercloud/terraform-provider-hcloud/issues/899 @@ -1397,13 +1380,9 @@ func validateUniqueNetworkIDs(d *schema.ResourceDiff) error { continue } - id, ok := networkID.(int) - if !ok { - return fmt.Errorf("network id has unexpected type: %T", networkID) - } - + id := util.CastInt64(networkIDI) if uniqueNetworkIDs[id] { - return fmt.Errorf("server is only allowed to be attached to each network once: %d", networkID) + return fmt.Errorf("server is only allowed to be attached to each network once: %d", networkIDI) } uniqueNetworkIDs[id] = true diff --git a/internal/server/resource_test.go b/internal/server/resource_test.go index 17b8d4bae..e4ae705d2 100644 --- a/internal/server/resource_test.go +++ b/internal/server/resource_test.go @@ -10,6 +10,7 @@ import ( "github.com/hashicorp/terraform-plugin-testing/helper/resource" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "github.com/hetznercloud/hcloud-go/v2/hcloud" "github.com/hetznercloud/terraform-provider-hcloud/internal/firewall" @@ -1199,3 +1200,29 @@ func userDataHashSum(userData string) string { sum := sha1.Sum([]byte(userData)) // nolint: gosec return base64.StdEncoding.EncodeToString(sum[:]) } + +func TestToPublicNetField(t *testing.T) { + t.Run("int", func(t *testing.T) { + got, err := server.ToPublicNetField[int](map[string]any{"key": int(1)}, "key") + require.NoError(t, err) + require.Equal(t, int(1), got) + }) + + t.Run("bool", func(t *testing.T) { + got, err := server.ToPublicNetField[bool](map[string]any{"key": true}, "key") + require.NoError(t, err) + require.Equal(t, true, got) + }) + + t.Run("int not found", func(t *testing.T) { + got, err := server.ToPublicNetField[int](map[string]any{}, "key") + require.EqualError(t, err, "ToPublicNetField: field does not contain key: key") + require.Equal(t, int(0), got) + }) + + t.Run("bool not found", func(t *testing.T) { + got, err := server.ToPublicNetField[bool](map[string]any{}, "key") + require.EqualError(t, err, "ToPublicNetField: field does not contain key: key") + require.Equal(t, false, got) + }) +}