Repository: hetznercloud/hcloud-go
Pull request #215 (2022-11-10 09:57:37)

fix: error when updating IPv6 Primary IP
The API returns a CIDR range in the `ip` field for ipv6 primary IPs. This caused the `PrimaryIPClient.Update` call to fail, because the response was parsed as a `net.IP`, but should have been parsed using `net.ParseNetwork`.

The required logic was already implemented in the `PrimaryIPFromSchema` function, but not used for the update call.

With this commit, the struct `hcloud.PrimaryIPUpdateResult` is removed, because using it to parse the response causes the error and it was never returned from any of our methods, so consumers should not be using it.

This error was originally found in hcloud-cli, and caused following error message:

    ➜  ~ hcloud primary-ip update --auto-delete=false test-ip
    hcloud: updating Primary IP test-ip failed: invalid IP address: 2001:db8::/64


Patch:
diff --git a/hcloud/primary_ip.go b/hcloud/primary_ip.go
index eccd407..66cc414 100644
--- a/hcloud/primary_ip.go
+++ b/hcloud/primary_ip.go
@@ -90,12 +90,6 @@ type PrimaryIPUpdateOpts struct {
 	Name       string             `json:"name,omitempty"`
 }
 
-// PrimaryIPUpdateResult defines the response
-// when updating a Primary IP.
-type PrimaryIPUpdateResult struct {
-	PrimaryIP PrimaryIP `json:"primary_ip"`
-}
-
 // PrimaryIPAssignOpts defines the request to
 // assign a Primary IP to an assignee (usually a server).
 type PrimaryIPAssignOpts struct {
@@ -313,12 +307,12 @@ func (c *PrimaryIPClient) Update(ctx context.Context, primaryIP *PrimaryIP, reqB
 		return nil, nil, err
 	}
 
-	respBody := PrimaryIPUpdateResult{}
+	var respBody schema.PrimaryIPUpdateResult
 	resp, err := c.client.Do(req, &respBody)
 	if err != nil {
 		return nil, resp, err
 	}
-	return &respBody.PrimaryIP, resp, nil
+	return PrimaryIPFromSchema(respBody.PrimaryIP), resp, nil
 }
 
 // Assign a Primary IP to a resource
diff --git a/hcloud/schema/primary_ip.go b/hcloud/schema/primary_ip.go
index b21e28b..1cfb084 100644
--- a/hcloud/schema/primary_ip.go
+++ b/hcloud/schema/primary_ip.go
@@ -47,3 +47,9 @@ type PrimaryIPGetResult struct {
 type PrimaryIPListResult struct {
 	PrimaryIPs []PrimaryIP `json:"primary_ips"`
 }
+
+// PrimaryIPUpdateResult defines the response
+// when updating a Primary IP.
+type PrimaryIPUpdateResult struct {
+	PrimaryIP PrimaryIP `json:"primary_ip"`
+}


Test patch:
diff --git a/hcloud/primary_ip_test.go b/hcloud/primary_ip_test.go
index 6c517ee..43e4699 100644
--- a/hcloud/primary_ip_test.go
+++ b/hcloud/primary_ip_test.go
@@ -3,6 +3,7 @@ package hcloud
 import (
 	"context"
 	"encoding/json"
+	"net"
 	"net/http"
 	"testing"
 
@@ -333,8 +334,8 @@ func TestPrimaryIPClient(t *testing.T) {
 				t.Log(cmp.Diff(expectedReqBody, reqBody))
 				t.Error("unexpected request body")
 			}
-			json.NewEncoder(w).Encode(PrimaryIPUpdateResult{
-				PrimaryIP: PrimaryIP{ID: 1},
+			json.NewEncoder(w).Encode(schema.PrimaryIPUpdateResult{
+				PrimaryIP: schema.PrimaryIP{ID: 1, IP: "2001:db8::/64"},
 			})
 		})
 
@@ -347,11 +348,14 @@ func TestPrimaryIPClient(t *testing.T) {
 			Labels:     &labels,
 		}
 
-		primaryIP := PrimaryIP{ID: 1}
+		primaryIP := PrimaryIP{ID: 1, IP: net.ParseIP("2001:db8::")}
 		result, resp, err := env.Client.PrimaryIP.Update(ctx, &primaryIP, opts)
 		assert.NoError(t, err)
 		assert.NotNil(t, resp, "no response returned")
-		assert.Equal(t, *result, primaryIP, "no primary IP returned")
+		if result.ID != 1 {
+			t.Errorf("unexpected Primary IP ID: %v", result.ID)
+		}
+		assert.Equal(t, primaryIP.IP, result.IP, "parsed the wrong IP")
 	})
 	t.Run("Assign", func(t *testing.T) {
 		env := newTestEnv()