Skip to content

Commit

Permalink
feat!: use int64 for ID fields (#282)
Browse files Browse the repository at this point in the history
The `int` datatype is not guaranteed to offer 64 bits of precision. On
32-bit platforms it only has 32 bits of precision. This is incompatible
with the Hetzner Cloud identifiers having up to 52-bits after September
1st 2023.

See #263 for more details about this change.
  • Loading branch information
apricote authored Jul 12, 2023
1 parent 54cb859 commit 359c389
Show file tree
Hide file tree
Showing 65 changed files with 262 additions and 745 deletions.
31 changes: 28 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
# hcloud: A Go library for the Hetzner Cloud API

[![GitHub Actions status](https://github.com/hetznercloud/hcloud-go/workflows/Continuous%20Integration/badge.svg)](https://github.com/hetznercloud/hcloud-go/actions)
[![GoDoc](https://godoc.org/github.com/hetznercloud/hcloud-go/hcloud?status.svg)](https://godoc.org/github.com/hetznercloud/hcloud-go/hcloud)
[![Go Reference](https://pkg.go.dev/badge/github.com/hetznercloud/hcloud-go/v2/hcloud.svg)](https://pkg.go.dev/github.com/hetznercloud/hcloud-go/v2/hcloud)

Package hcloud is a library for the Hetzner Cloud API.

The library’s documentation is available at [GoDoc](https://godoc.org/github.com/hetznercloud/hcloud-go/hcloud),
The library’s documentation is available at [pkg.go.dev](https://godoc.org/github.com/hetznercloud/hcloud-go/v2/hcloud),
the public API documentation is available at [docs.hetzner.cloud](https://docs.hetzner.cloud/).

## Example
Expand All @@ -18,7 +18,7 @@ import (
"fmt"
"log"

"github.com/hetznercloud/hcloud-go/hcloud"
"github.com/hetznercloud/hcloud-go/v2/hcloud"
)

func main() {
Expand All @@ -36,6 +36,31 @@ func main() {
}
```

## Upgrading

### Support

- `v2` is actively maintained by Hetzner Cloud
- `v1` is supported until September 1st 2023 and will continue to receive new features until then. See [#263](https://github.com/hetznercloud/hcloud-go/issues/263).

### From v1 to v2

Version 2.0.0 was published because we changed the datatype of all `ID` fields from `int` to `int64`.

To migrate to the new version, replace all your imports to reference the new module path:

```diff
import (
- "github.com/hetznercloud/hcloud-go/hcloud"
+ "github.com/hetznercloud/hcloud-go/v2/hcloud"
)
```

When you compile your code, it will show any invalid usages of `int` in your code that you need to fix. We commonly found these changes while updating our integrations:

- `strconv.Atoi(idString)` (parsing integers) needs to be replaced by `strconv.ParseInt(idString, 10, 64)`
- `strconv.Itoa(id)` (formatting integers) needs to be replaced by `strconv.FormatInt(id, 10)`

## Go Version Support

The library supports the latest two Go minor versions, e.g. at the time Go 1.19 is released, it supports Go 1.18 and 1.19.
Expand Down
4 changes: 3 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
module github.com/hetznercloud/hcloud-go
module github.com/hetznercloud/hcloud-go/v2

go 1.19

Expand All @@ -14,11 +14,13 @@ require (
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/client_model v0.3.0 // indirect
github.com/prometheus/common v0.42.0 // indirect
github.com/prometheus/procfs v0.10.1 // indirect
github.com/rogpeppe/go-internal v1.11.0 // indirect
golang.org/x/sys v0.10.0 // indirect
golang.org/x/text v0.11.0 // indirect
google.golang.org/protobuf v1.30.0 // indirect
Expand Down
524 changes: 7 additions & 517 deletions go.sum

Large diffs are not rendered by default.

14 changes: 7 additions & 7 deletions hcloud/action.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@ import (
"net/url"
"time"

"github.com/hetznercloud/hcloud-go/hcloud/schema"
"github.com/hetznercloud/hcloud-go/v2/hcloud/schema"
)

// Action represents an action in the Hetzner Cloud.
type Action struct {
ID int
ID int64
Status ActionStatus
Command string
Progress int
Expand All @@ -34,7 +34,7 @@ const (

// ActionResource references other resources from an action.
type ActionResource struct {
ID int
ID int64
Type ActionResourceType
}

Expand Down Expand Up @@ -76,7 +76,7 @@ type ActionClient struct {
}

// GetByID retrieves an action by its ID. If the action does not exist, nil is returned.
func (c *ActionClient) GetByID(ctx context.Context, id int) (*Action, *Response, error) {
func (c *ActionClient) GetByID(ctx context.Context, id int64) (*Action, *Response, error) {
req, err := c.client.NewRequest(ctx, "GET", fmt.Sprintf("/actions/%d", id), nil)
if err != nil {
return nil, nil, err
Expand All @@ -96,7 +96,7 @@ func (c *ActionClient) GetByID(ctx context.Context, id int) (*Action, *Response,
// ActionListOpts specifies options for listing actions.
type ActionListOpts struct {
ListOpts
ID []int
ID []int64
Status []ActionStatus
Sort []string
}
Expand Down Expand Up @@ -189,8 +189,8 @@ func (c *ActionClient) WatchOverallProgress(ctx context.Context, actions []*Acti
defer close(errCh)
defer close(progressCh)

successIDs := make([]int, 0, len(actions))
watchIDs := make(map[int]struct{}, len(actions))
successIDs := make([]int64, 0, len(actions))
watchIDs := make(map[int64]struct{}, len(actions))
for _, action := range actions {
watchIDs[action.ID] = struct{}{}
}
Expand Down
2 changes: 1 addition & 1 deletion hcloud/action_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import (
"testing"
"time"

"github.com/hetznercloud/hcloud-go/hcloud/schema"
"github.com/hetznercloud/hcloud-go/v2/hcloud/schema"
)

func TestActionClientGetByID(t *testing.T) {
Expand Down
10 changes: 5 additions & 5 deletions hcloud/certificate.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (
"strconv"
"time"

"github.com/hetznercloud/hcloud-go/hcloud/schema"
"github.com/hetznercloud/hcloud-go/v2/hcloud/schema"
)

// CertificateType is the type of available certificate types.
Expand Down Expand Up @@ -50,7 +50,7 @@ const (

// CertificateUsedByRef points to a resource that uses this certificate.
type CertificateUsedByRef struct {
ID int
ID int64
Type CertificateUsedByRefType
}

Expand All @@ -70,7 +70,7 @@ func (st *CertificateStatus) IsFailed() bool {

// Certificate represents a certificate in the Hetzner Cloud.
type Certificate struct {
ID int
ID int64
Name string
Labels map[string]string
Type CertificateType
Expand All @@ -96,7 +96,7 @@ type CertificateClient struct {
}

// GetByID retrieves a Certificate by its ID. If the Certificate does not exist, nil is returned.
func (c *CertificateClient) GetByID(ctx context.Context, id int) (*Certificate, *Response, error) {
func (c *CertificateClient) GetByID(ctx context.Context, id int64) (*Certificate, *Response, error) {
req, err := c.client.NewRequest(ctx, "GET", fmt.Sprintf("/certificates/%d", id), nil)
if err != nil {
return nil, nil, err
Expand Down Expand Up @@ -128,7 +128,7 @@ func (c *CertificateClient) GetByName(ctx context.Context, name string) (*Certif
// Get retrieves a Certificate by its ID if the input can be parsed as an integer, otherwise it
// retrieves a Certificate by its name. If the Certificate does not exist, nil is returned.
func (c *CertificateClient) Get(ctx context.Context, idOrName string) (*Certificate, *Response, error) {
if id, err := strconv.Atoi(idOrName); err == nil {
if id, err := strconv.ParseInt(idOrName, 10, 64); err == nil {
return c.GetByID(ctx, id)
}
return c.GetByName(ctx, idOrName)
Expand Down
2 changes: 1 addition & 1 deletion hcloud/certificate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import (
"github.com/google/go-cmp/cmp"
"github.com/stretchr/testify/assert"

"github.com/hetznercloud/hcloud-go/hcloud/schema"
"github.com/hetznercloud/hcloud-go/v2/hcloud/schema"
)

func TestCertificateCreateOptsValidate_Uploaded(t *testing.T) {
Expand Down
4 changes: 2 additions & 2 deletions hcloud/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ import (
"github.com/prometheus/client_golang/prometheus"
"golang.org/x/net/http/httpguts"

"github.com/hetznercloud/hcloud-go/hcloud/internal/instrumentation"
"github.com/hetznercloud/hcloud-go/hcloud/schema"
"github.com/hetznercloud/hcloud-go/v2/hcloud/internal/instrumentation"
"github.com/hetznercloud/hcloud-go/v2/hcloud/schema"
)

// Endpoint is the base URL of the API.
Expand Down
2 changes: 1 addition & 1 deletion hcloud/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import (
"testing"
"time"

"github.com/hetznercloud/hcloud-go/hcloud/schema"
"github.com/hetznercloud/hcloud-go/v2/hcloud/schema"
)

type testEnv struct {
Expand Down
10 changes: 5 additions & 5 deletions hcloud/datacenter.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@ import (
"net/url"
"strconv"

"github.com/hetznercloud/hcloud-go/hcloud/schema"
"github.com/hetznercloud/hcloud-go/v2/hcloud/schema"
)

// Datacenter represents a datacenter in the Hetzner Cloud.
type Datacenter struct {
ID int
ID int64
Name string
Description string
Location *Location
Expand All @@ -30,7 +30,7 @@ type DatacenterClient struct {
}

// GetByID retrieves a datacenter by its ID. If the datacenter does not exist, nil is returned.
func (c *DatacenterClient) GetByID(ctx context.Context, id int) (*Datacenter, *Response, error) {
func (c *DatacenterClient) GetByID(ctx context.Context, id int64) (*Datacenter, *Response, error) {
req, err := c.client.NewRequest(ctx, "GET", fmt.Sprintf("/datacenters/%d", id), nil)
if err != nil {
return nil, nil, err
Expand Down Expand Up @@ -62,8 +62,8 @@ func (c *DatacenterClient) GetByName(ctx context.Context, name string) (*Datacen
// Get retrieves a datacenter by its ID if the input can be parsed as an integer, otherwise it
// retrieves a datacenter by its name. If the datacenter does not exist, nil is returned.
func (c *DatacenterClient) Get(ctx context.Context, idOrName string) (*Datacenter, *Response, error) {
if id, err := strconv.Atoi(idOrName); err == nil {
return c.GetByID(ctx, int(id))
if id, err := strconv.ParseInt(idOrName, 10, 64); err == nil {
return c.GetByID(ctx, id)
}
return c.GetByName(ctx, idOrName)
}
Expand Down
2 changes: 1 addition & 1 deletion hcloud/datacenter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import (
"net/http"
"testing"

"github.com/hetznercloud/hcloud-go/hcloud/schema"
"github.com/hetznercloud/hcloud-go/v2/hcloud/schema"
)

func TestDatacenterClient(t *testing.T) {
Expand Down
10 changes: 5 additions & 5 deletions hcloud/firewall.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@ import (
"strconv"
"time"

"github.com/hetznercloud/hcloud-go/hcloud/schema"
"github.com/hetznercloud/hcloud-go/v2/hcloud/schema"
)

// Firewall represents a Firewall in the Hetzner Cloud.
type Firewall struct {
ID int
ID int64
Name string
Labels map[string]string
Created time.Time
Expand Down Expand Up @@ -80,7 +80,7 @@ type FirewallResource struct {

// FirewallResourceServer represents a Server to apply a Firewall on.
type FirewallResourceServer struct {
ID int
ID int64
}

// FirewallResourceLabelSelector represents a LabelSelector to apply a Firewall on.
Expand All @@ -94,7 +94,7 @@ type FirewallClient struct {
}

// GetByID retrieves a Firewall by its ID. If the Firewall does not exist, nil is returned.
func (c *FirewallClient) GetByID(ctx context.Context, id int) (*Firewall, *Response, error) {
func (c *FirewallClient) GetByID(ctx context.Context, id int64) (*Firewall, *Response, error) {
req, err := c.client.NewRequest(ctx, "GET", fmt.Sprintf("/firewalls/%d", id), nil)
if err != nil {
return nil, nil, err
Expand Down Expand Up @@ -126,7 +126,7 @@ func (c *FirewallClient) GetByName(ctx context.Context, name string) (*Firewall,
// Get retrieves a Firewall by its ID if the input can be parsed as an integer, otherwise it
// retrieves a Firewall by its name. If the Firewall does not exist, nil is returned.
func (c *FirewallClient) Get(ctx context.Context, idOrName string) (*Firewall, *Response, error) {
if id, err := strconv.Atoi(idOrName); err == nil {
if id, err := strconv.ParseInt(idOrName, 10, 64); err == nil {
return c.GetByID(ctx, id)
}
return c.GetByName(ctx, idOrName)
Expand Down
2 changes: 1 addition & 1 deletion hcloud/firewall_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import (

"github.com/google/go-cmp/cmp"

"github.com/hetznercloud/hcloud-go/hcloud/schema"
"github.com/hetznercloud/hcloud-go/v2/hcloud/schema"
)

func TestFirewallCreateOptsValidate(t *testing.T) {
Expand Down
8 changes: 4 additions & 4 deletions hcloud/floating_ip.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@ import (
"strconv"
"time"

"github.com/hetznercloud/hcloud-go/hcloud/schema"
"github.com/hetznercloud/hcloud-go/v2/hcloud/schema"
)

// FloatingIP represents a Floating IP in the Hetzner Cloud.
type FloatingIP struct {
ID int
ID int64
Description string
Created time.Time
IP net.IP
Expand Down Expand Up @@ -95,7 +95,7 @@ type FloatingIPClient struct {

// GetByID retrieves a Floating IP by its ID. If the Floating IP does not exist,
// nil is returned.
func (c *FloatingIPClient) GetByID(ctx context.Context, id int) (*FloatingIP, *Response, error) {
func (c *FloatingIPClient) GetByID(ctx context.Context, id int64) (*FloatingIP, *Response, error) {
req, err := c.client.NewRequest(ctx, "GET", fmt.Sprintf("/floating_ips/%d", id), nil)
if err != nil {
return nil, nil, err
Expand Down Expand Up @@ -127,7 +127,7 @@ func (c *FloatingIPClient) GetByName(ctx context.Context, name string) (*Floatin
// Get retrieves a Floating IP by its ID if the input can be parsed as an integer, otherwise it
// retrieves a Floating IP by its name. If the Floating IP does not exist, nil is returned.
func (c *FloatingIPClient) Get(ctx context.Context, idOrName string) (*FloatingIP, *Response, error) {
if id, err := strconv.Atoi(idOrName); err == nil {
if id, err := strconv.ParseInt(idOrName, 10, 64); err == nil {
return c.GetByID(ctx, id)
}
return c.GetByName(ctx, idOrName)
Expand Down
2 changes: 1 addition & 1 deletion hcloud/floating_ip_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import (
"net/http"
"testing"

"github.com/hetznercloud/hcloud-go/hcloud/schema"
"github.com/hetznercloud/hcloud-go/v2/hcloud/schema"
)

func TestFloatingIPClientGetByID(t *testing.T) {
Expand Down
2 changes: 1 addition & 1 deletion hcloud/hcloud_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import (
"fmt"
"log"

"github.com/hetznercloud/hcloud-go/hcloud"
"github.com/hetznercloud/hcloud-go/v2/hcloud"
)

func Example() {
Expand Down
Loading

0 comments on commit 359c389

Please sign in to comment.