Skip to content

Commit a42ce8c

Browse files
committed
Convert Server and IPAddress to 1.3 API
1 parent 45a47c6 commit a42ce8c

39 files changed

+16940
-2768
lines changed

examples/upcloud-cli/main.go

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -87,10 +87,16 @@ func createServer(s *service.Service) error {
8787
Tier: upcloud.StorageTierMaxIOPS,
8888
},
8989
},
90-
IPAddresses: []request.CreateServerIPAddress{
91-
{
92-
Access: upcloud.IPAddressAccessPrivate,
93-
Family: upcloud.IPAddressFamilyIPv4,
90+
Networking: &request.CreateServerNetworking{
91+
Interfaces: []request.CreateServerInterface{
92+
{
93+
IPAddresses: []request.CreateServerIPAddress{
94+
{
95+
Family: upcloud.IPAddressFamilyIPv4,
96+
},
97+
},
98+
Type: upcloud.IPAddressAccessUtility,
99+
},
94100
},
95101
},
96102
})

upcloud/client/client.go

Lines changed: 22 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,7 @@ import (
1414

1515
// Constants
1616
const (
17-
DefaultAPIVersion = "1.2.3"
18-
FutureAPIVersion = "1.3.4"
17+
DefaultAPIVersion = "1.3.4"
1918
DefaultAPIBaseURL = "https://api.upcloud.com"
2019

2120
// The default timeout (in seconds)
@@ -59,14 +58,9 @@ func (c *Client) GetTimeout() time.Duration {
5958
}
6059

6160
// CreateRequestURL creates and returns a complete request URL for the specified API location
62-
func (c *Client) CreateRequestURL(location string) string {
63-
return fmt.Sprintf("%s%s", c.getBaseURL(false), location)
64-
}
65-
66-
// CreateFutureRequestURL creates and returns a complete request URL for the specified API location
6761
// using a newer API version
68-
func (c *Client) CreateFutureRequestURL(location string) string {
69-
return fmt.Sprintf("%s%s", c.getBaseURL(true), location)
62+
func (c *Client) CreateRequestURL(location string) string {
63+
return fmt.Sprintf("%s%s", c.getBaseURL(), location)
7064
}
7165

7266
// PerformJSONGetRequest performs a GET request to the specified URL and returns the response body and eventual errors
@@ -114,6 +108,23 @@ func (c *Client) PerformJSONPutRequest(url string, requestBody []byte) ([]byte,
114108
return c.performJSONRequest(request)
115109
}
116110

111+
// PerformJSONPatchRequest performs a PATCH request to the specified URL and returns the response body and eventual errors
112+
func (c *Client) PerformJSONPatchRequest(url string, requestBody []byte) ([]byte, error) {
113+
var bodyReader io.Reader
114+
115+
if requestBody != nil {
116+
bodyReader = bytes.NewBuffer(requestBody)
117+
}
118+
119+
request, err := http.NewRequest(http.MethodPatch, url, bodyReader)
120+
121+
if err != nil {
122+
return nil, err
123+
}
124+
125+
return c.performJSONRequest(request)
126+
}
127+
117128
// PerformJSONDeleteRequest performs a DELETE request to the specified URL and returns the response body and eventual errors
118129
func (c *Client) PerformJSONDeleteRequest(url string) error {
119130
request, err := http.NewRequest(http.MethodDelete, url, nil)
@@ -148,13 +159,8 @@ func (c *Client) performJSONRequest(request *http.Request) ([]byte, error) {
148159
}
149160

150161
// Returns the base URL to use for API requests
151-
func (c *Client) getBaseURL(future bool) string {
152-
var urlVersion semver.Version
153-
if !future {
154-
urlVersion, _ = semver.Make(DefaultAPIVersion)
155-
} else {
156-
urlVersion, _ = semver.Make(FutureAPIVersion)
157-
}
162+
func (c *Client) getBaseURL() string {
163+
urlVersion, _ := semver.Make(DefaultAPIVersion)
158164

159165
return fmt.Sprintf("%s/%d.%d", DefaultAPIBaseURL, urlVersion.Major, urlVersion.Minor)
160166
}

upcloud/ip_address.go

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -42,13 +42,15 @@ func (s *IPAddresses) UnmarshalJSON(b []byte) error {
4242

4343
// IPAddress represents an IP address
4444
type IPAddress struct {
45-
Access string `json:"access"`
46-
Address string `json:"address"`
47-
Family string `json:"family"`
48-
// TODO: Convert to boolean
49-
PartOfPlan string `json:"part_of_plan"`
50-
PTRRecord string `json:"ptr_record"`
51-
ServerUUID string `json:"server"`
45+
Access string `json:"access"`
46+
Address string `json:"address"`
47+
Family string `json:"family"`
48+
PartOfPlan Boolean `json:"part_of_plan"`
49+
PTRRecord string `json:"ptr_record"`
50+
ServerUUID string `json:"server"`
51+
MAC string `json:"mac"`
52+
Floating Boolean `json:"floating"`
53+
Zone string `json:"zone"`
5254
}
5355

5456
// UnmarshalJSON is a custom unmarshaller that deals with

upcloud/ip_address_test.go

Lines changed: 78 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -10,87 +10,120 @@ import (
1010
// TestUnmarshalIPAddresses tests that IPAddresses and IPAddress structs are unmarshaled correctly
1111
func TestUnmarshalIPAddresses(t *testing.T) {
1212
originalJSON := `
13-
{
14-
"ip_addresses": {
15-
"ip_address": [
16-
{
17-
"access": "private",
18-
"address": "10.0.0.0",
19-
"family": "IPv4",
20-
"ptr_record": "",
21-
"server": "0053cd80-5945-4105-9081-11192806a8f7"
22-
},
23-
{
24-
"access": "private",
25-
"address": "10.0.0.1",
26-
"family": "IPv4",
27-
"ptr_record": "",
28-
"server": "006b6701-55d2-4374-ac40-56cc1501037f"
29-
},
30-
{
31-
"access": "public",
32-
"address": "x.x.x.x",
33-
"family": "IPv4",
34-
"part_of_plan": "yes",
35-
"ptr_record": "x-x-x-x.zone.upcloud.host",
36-
"server": "0053cd80-5945-4105-9081-11192806a8f7"
37-
},
38-
{
39-
"access": "public",
40-
"address": "xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx",
41-
"family": "IPv6",
42-
"ptr_record": "xxxx-xxxx-xxxx-xxxx.v6.zone.upcloud.host",
43-
"server": "006b6701-55d2-4374-ac40-56cc1501037f"
44-
}
45-
]
46-
}
47-
}
48-
`
13+
{
14+
"ip_addresses": {
15+
"ip_address": [
16+
{
17+
"access": "utility",
18+
"address": "10.0.0.0",
19+
"family": "IPv4",
20+
"ptr_record": "",
21+
"server": "0053cd80-5945-4105-9081-11192806a8f7",
22+
"mac": "mm:mm:mm:mm:mm:m1",
23+
"floating": "no",
24+
"zone": "fi-hel2"
25+
},
26+
{
27+
"access": "utility",
28+
"address": "10.0.0.1",
29+
"family": "IPv4",
30+
"ptr_record": "",
31+
"server": "006b6701-55d2-4374-ac40-56cc1501037f",
32+
"mac": "mm:mm:mm:mm:mm:m2",
33+
"floating": "no",
34+
"zone": "de-fra1"
35+
},
36+
{
37+
"access": "public",
38+
"address": "x.x.x.x",
39+
"family": "IPv4",
40+
"part_of_plan": "yes",
41+
"ptr_record": "x-x-x-x.zone.upcloud.host",
42+
"server": "0053cd80-5945-4105-9081-11192806a8f7",
43+
"mac": "mm:mm:mm:mm:mm:m1",
44+
"floating": "yes",
45+
"zone": "de-fra1"
46+
},
47+
{
48+
"access": "public",
49+
"address": "xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx",
50+
"family": "IPv6",
51+
"ptr_record": "xxxx-xxxx-xxxx-xxxx.v6.zone.upcloud.host",
52+
"server": "006b6701-55d2-4374-ac40-56cc1501037f",
53+
"mac": "mm:mm:mm:mm:mm:m3",
54+
"floating": "no",
55+
"zone": "fi-hel1"
56+
},
57+
{
58+
"access": "public",
59+
"address": "y.y.y.y",
60+
"family": "IPv4",
61+
"ptr_record": "y.y.y.y.zone.host.upcloud.com",
62+
"floating": "yes",
63+
"zone": "nl-ams1"
64+
}
65+
]
66+
}
67+
}
68+
`
4969

5070
ipAddresses := IPAddresses{}
5171
err := json.Unmarshal([]byte(originalJSON), &ipAddresses)
5272
assert.NoError(t, err)
53-
assert.Len(t, ipAddresses.IPAddresses, 4)
73+
assert.Len(t, ipAddresses.IPAddresses, 5)
5474

5575
testData := []IPAddress{
5676
{
57-
Access: IPAddressAccessPrivate,
77+
Access: IPAddressAccessUtility,
5878
Address: "10.0.0.0",
5979
Family: IPAddressFamilyIPv4,
6080
PTRRecord: "",
6181
ServerUUID: "0053cd80-5945-4105-9081-11192806a8f7",
82+
MAC: "mm:mm:mm:mm:mm:m1",
83+
Zone: "fi-hel2",
6284
},
6385
{
64-
Access: IPAddressAccessPrivate,
86+
Access: IPAddressAccessUtility,
6587
Address: "10.0.0.1",
6688
Family: IPAddressFamilyIPv4,
6789
PTRRecord: "",
6890
ServerUUID: "006b6701-55d2-4374-ac40-56cc1501037f",
91+
MAC: "mm:mm:mm:mm:mm:m2",
92+
Zone: "de-fra1",
6993
},
7094
{
7195
Access: IPAddressAccessPublic,
7296
Address: "x.x.x.x",
7397
Family: IPAddressFamilyIPv4,
74-
PartOfPlan: "yes",
98+
PartOfPlan: true,
7599
PTRRecord: "x-x-x-x.zone.upcloud.host",
76100
ServerUUID: "0053cd80-5945-4105-9081-11192806a8f7",
101+
Floating: true,
102+
MAC: "mm:mm:mm:mm:mm:m1",
103+
Zone: "de-fra1",
77104
},
78105
{
79106
Access: IPAddressAccessPublic,
80107
Address: "xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx",
81108
Family: IPAddressFamilyIPv6,
82109
PTRRecord: "xxxx-xxxx-xxxx-xxxx.v6.zone.upcloud.host",
83110
ServerUUID: "006b6701-55d2-4374-ac40-56cc1501037f",
111+
MAC: "mm:mm:mm:mm:mm:m3",
112+
Zone: "fi-hel1",
113+
},
114+
{
115+
Access: IPAddressAccessPublic,
116+
Address: "y.y.y.y",
117+
Family: IPAddressFamilyIPv4,
118+
PTRRecord: "y.y.y.y.zone.host.upcloud.com",
119+
Floating: true,
120+
Zone: "nl-ams1",
84121
},
85122
}
86123

87124
for i, v := range testData {
88125
address := ipAddresses.IPAddresses[i]
89-
assert.Equal(t, v.Access, address.Access)
90-
assert.Equal(t, v.Address, address.Address)
91-
assert.Equal(t, v.Family, address.Family)
92-
assert.Equal(t, v.PTRRecord, address.PTRRecord)
93-
assert.Equal(t, v.ServerUUID, address.ServerUUID)
126+
assert.Equal(t, v, address)
94127
}
95128
}
96129

upcloud/network.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package upcloud
2+
3+
import "encoding/json"
4+
5+
// InterfaceSlice is a slice of Interfaces.
6+
// It exists to allow for a custom JSON unmarshaller.
7+
type InterfaceSlice []Interface
8+
9+
// UnmarshalJSON is a custom unmarshaller that deals with
10+
// deeply embedded values.
11+
func (s *InterfaceSlice) UnmarshalJSON(b []byte) error {
12+
v := struct {
13+
Interfaces []Interface `json:"interface"`
14+
}{}
15+
err := json.Unmarshal(b, &v)
16+
if err != nil {
17+
return err
18+
}
19+
20+
(*s) = v.Interfaces
21+
22+
return nil
23+
}
24+
25+
type Networking struct {
26+
Interfaces InterfaceSlice `json:"interfaces"`
27+
}
28+
29+
type Interface struct {
30+
Index int `json:"index"`
31+
IPAddresses IPAddressSlice `json:"ip_addresses"`
32+
MAC string `json:"mac"`
33+
Network string `json:"network"`
34+
Type string `json:"type"`
35+
Bootable Boolean `json:"bootable"`
36+
}

upcloud/request/ip_address.go

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ package request
33
import (
44
"encoding/json"
55
"fmt"
6+
7+
"github.com/UpCloudLtd/upcloud-go-api/upcloud"
68
)
79

810
// GetIPAddressDetailsRequest represents a request to retrieve details about a specific IP address
@@ -17,9 +19,12 @@ func (r *GetIPAddressDetailsRequest) RequestURL() string {
1719

1820
// AssignIPAddressRequest represents a request to assign a new IP address to a server
1921
type AssignIPAddressRequest struct {
20-
Access string `json:"access"`
21-
Family string `json:"family,omitempty"`
22-
ServerUUID string `json:"server"`
22+
Access string `json:"access,omitempty"`
23+
Family string `json:"family,omitempty"`
24+
ServerUUID string `json:"server,omitempty"`
25+
Floating upcloud.Boolean `json:"floating,omitempty"`
26+
MAC string `json:"mac,omitempty"`
27+
Zone string `json:"zone,omitempty"`
2328
}
2429

2530
// RequestURL implements the Request interface
@@ -43,7 +48,8 @@ func (r AssignIPAddressRequest) MarshalJSON() ([]byte, error) {
4348
type ModifyIPAddressRequest struct {
4449
IPAddress string `json:"-"`
4550

46-
PTRRecord string `json:"ptr_record"`
51+
PTRRecord string `json:"ptr_record,omitempty"`
52+
MAC string `json:"mac,omitempty"`
4753
}
4854

4955
// RequestURL implements the Request interface
@@ -54,6 +60,16 @@ func (r *ModifyIPAddressRequest) RequestURL() string {
5460
// MarshalJSON is a custom marshaller that deals with
5561
// deeply embedded values.
5662
func (r ModifyIPAddressRequest) MarshalJSON() ([]byte, error) {
63+
if r.PTRRecord == "" && r.MAC == "" {
64+
// We want to unassign the IP which requires the MAC to be explicitly not set.
65+
return []byte(`
66+
{
67+
"ip_address": {
68+
"mac": null
69+
}
70+
}
71+
`), nil
72+
}
5773
type localModifyIPAddressRequest ModifyIPAddressRequest
5874
v := struct {
5975
ModifyIPAddressRequest localModifyIPAddressRequest `json:"ip_address"`

0 commit comments

Comments
 (0)