Skip to content

Commit dfa680a

Browse files
authored
Merge pull request #51 from opencredo/convert_3_functions_to_json
Convert initial 3 functions to JSON API
2 parents d5186a2 + eb17abc commit dfa680a

File tree

13 files changed

+643
-99
lines changed

13 files changed

+643
-99
lines changed

examples/upcloud-cli/main.go

Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
package main
2+
3+
import (
4+
"errors"
5+
"flag"
6+
"fmt"
7+
"math/rand"
8+
"os"
9+
"time"
10+
11+
"git.sr.ht/~yoink00/goflenfig"
12+
"github.com/UpCloudLtd/upcloud-go-api/upcloud"
13+
"github.com/UpCloudLtd/upcloud-go-api/upcloud/client"
14+
"github.com/UpCloudLtd/upcloud-go-api/upcloud/request"
15+
"github.com/UpCloudLtd/upcloud-go-api/upcloud/service"
16+
"github.com/davecgh/go-spew/spew"
17+
)
18+
19+
var username string
20+
var password string
21+
22+
func init() {
23+
goflenfig.Prefix("UPCLOUD_")
24+
goflenfig.StringVar(&username, "username", "", "UpCloud user name")
25+
goflenfig.StringVar(&password, "password", "", "UpCloud password")
26+
27+
rand.Seed(time.Now().Unix())
28+
}
29+
30+
func main() {
31+
os.Exit(run())
32+
}
33+
34+
func run() int {
35+
goflenfig.Parse()
36+
37+
command := flag.Arg(0)
38+
39+
if len(username) == 0 {
40+
fmt.Fprintln(os.Stderr, "Username must be specified")
41+
return 1
42+
}
43+
44+
if len(password) == 0 {
45+
fmt.Fprintln(os.Stderr, "Password must be specified")
46+
return 2
47+
}
48+
49+
fmt.Println("Creating new client")
50+
c := client.New(username, password)
51+
s := service.New(c)
52+
53+
switch command {
54+
case "deleteservers":
55+
if err := deleteServers(s); err != nil {
56+
return 1
57+
}
58+
case "deletestorage":
59+
if err := deleteStorage(s); err != nil {
60+
return 2
61+
}
62+
case "createserver":
63+
if err := createServer(s); err != nil {
64+
return 3
65+
}
66+
default:
67+
fmt.Fprintln(os.Stderr, "Unknown command: ", command)
68+
return 99
69+
}
70+
71+
return 0
72+
}
73+
74+
func createServer(s *service.Service) error {
75+
fmt.Println("Creating server")
76+
details, err := s.CreateServer(&request.CreateServerRequest{
77+
Hostname: "stuart.example.com",
78+
Title: fmt.Sprintf("example-cli-server-%04d", rand.Int31n(1000)),
79+
Zone: "fi-hel2",
80+
Plan: "1xCPU-1GB",
81+
StorageDevices: []upcloud.CreateServerStorageDevice{
82+
{
83+
Action: upcloud.CreateServerStorageDeviceActionClone,
84+
Storage: "01000000-0000-4000-8000-000050010400",
85+
Title: "Centos8 from a template",
86+
Size: 50,
87+
Tier: upcloud.StorageTierMaxIOPS,
88+
},
89+
},
90+
IPAddresses: []request.CreateServerIPAddress{
91+
{
92+
Access: upcloud.IPAddressAccessPrivate,
93+
Family: upcloud.IPAddressFamilyIPv4,
94+
},
95+
},
96+
})
97+
if err != nil {
98+
fmt.Fprintf(os.Stderr, "Unable to create server: %#v\n", err)
99+
return err
100+
}
101+
spew.Println(details)
102+
if len(details.UUID) == 0 {
103+
fmt.Fprintf(os.Stderr, "UUID missing")
104+
return errors.New("UUID too short")
105+
}
106+
details, err = s.WaitForServerState(&request.WaitForServerStateRequest{
107+
UUID: details.UUID,
108+
DesiredState: upcloud.ServerStateStarted,
109+
Timeout: 1 * time.Minute,
110+
})
111+
if err != nil {
112+
fmt.Fprintf(os.Stderr, "Unable to wait for server: %#v", err)
113+
return err
114+
}
115+
116+
fmt.Printf("Created server: %#v\n", details)
117+
118+
return nil
119+
}
120+
121+
func deleteServers(s *service.Service) error {
122+
fmt.Println("Getting servers")
123+
servers, err := s.GetServers()
124+
if err != nil {
125+
fmt.Fprintf(os.Stderr, "Unable to get servers: %#v\n", err)
126+
return err
127+
}
128+
129+
fmt.Printf("Retrieved %d servers\n", len(servers.Servers))
130+
131+
if len(servers.Servers) > 0 {
132+
fmt.Println("Deleting all servers")
133+
for _, server := range servers.Servers {
134+
if server.State != upcloud.ServerStateStopped {
135+
fmt.Printf("Server %s (%s) is not stopped. Stopping\n", server.Title, server.UUID)
136+
_, err := s.StopServer(&request.StopServerRequest{
137+
UUID: server.UUID,
138+
StopType: request.ServerStopTypeHard,
139+
})
140+
if err != nil {
141+
fmt.Fprintf(os.Stderr, "Unable to stop server: %#v\n", err)
142+
return err
143+
}
144+
_, err = s.WaitForServerState(&request.WaitForServerStateRequest{
145+
UUID: server.UUID,
146+
DesiredState: upcloud.ServerStateStopped,
147+
Timeout: 1 * time.Minute,
148+
})
149+
if err != nil {
150+
fmt.Fprintf(os.Stderr, "Failed to wait for server to reach desired state: %#v", err)
151+
return err
152+
}
153+
}
154+
fmt.Printf("Deleting %s (%s)\n", server.Title, server.UUID)
155+
err := s.DeleteServerAndStorages(&request.DeleteServerAndStoragesRequest{
156+
UUID: server.UUID,
157+
})
158+
159+
if err != nil {
160+
fmt.Fprintf(os.Stderr, "Unable to delete server: %#v\n", err)
161+
return err
162+
}
163+
fmt.Printf("Successfully deleted %s (%s)\n", server.Title, server.UUID)
164+
}
165+
}
166+
167+
return nil
168+
}
169+
170+
func deleteStorage(s *service.Service) error {
171+
fmt.Println("Getting storage")
172+
storages, err := s.GetStorages(&request.GetStoragesRequest{
173+
Access: upcloud.StorageAccessPrivate,
174+
})
175+
if err != nil {
176+
fmt.Fprintf(os.Stderr, "Unable to get storages: %#v\n", err)
177+
return err
178+
}
179+
180+
fmt.Printf("Retrieved %d storages\n", len(storages.Storages))
181+
182+
if len(storages.Storages) > 0 {
183+
fmt.Println("Deleting all storages")
184+
for _, storage := range storages.Storages {
185+
err := errors.New("Dummy")
186+
for i := 0; err != nil && i < 5; i++ {
187+
fmt.Printf("%d: Deleting %s (%s)\n", i, storage.Title, storage.UUID)
188+
err = s.DeleteStorage(&request.DeleteStorageRequest{
189+
UUID: storage.UUID,
190+
})
191+
}
192+
if err != nil {
193+
fmt.Fprintf(os.Stderr, "Unable to delete storage: %#v (%s)\n", err, err.Error())
194+
return err
195+
}
196+
197+
fmt.Printf("Successfully deleted %s (%s)\n", storage.Title, storage.UUID)
198+
}
199+
}
200+
201+
return nil
202+
}

go.mod

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@ module github.com/UpCloudLtd/upcloud-go-api
33
go 1.14
44

55
require (
6+
git.sr.ht/~yoink00/goflenfig v0.1.0
67
github.com/blang/semver v3.5.1+incompatible
8+
github.com/davecgh/go-spew v1.1.1
79
github.com/hashicorp/go-cleanhttp v0.5.1
810
github.com/kr/pretty v0.1.0 // indirect
911
github.com/stretchr/testify v1.6.1

go.sum

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
1+
git.sr.ht/~yoink00/goflenfig v0.1.0 h1:8RqL7olbs64EHus97F+S1wuPKFco9Av2FmY3Sk6bPoQ=
2+
git.sr.ht/~yoink00/goflenfig v0.1.0/go.mod h1:+b5SR2TmdQHQ2WqgotlpJEN0xjijoDz6V3pzyfyc6v4=
13
github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ=
24
github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
35
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
46
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
7+
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
8+
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
59
github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM=
610
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
711
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=

upcloud/client/client.go

Lines changed: 64 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ func (c *Client) CreateRequestUrl(location string) string {
6363

6464
// PerformGetRequest performs a GET request to the specified URL and returns the response body and eventual errors
6565
func (c *Client) PerformGetRequest(url string) ([]byte, error) {
66-
request, err := http.NewRequest("GET", url, nil)
66+
request, err := http.NewRequest(http.MethodGet, url, nil)
6767

6868
if err != nil {
6969
return nil, err
@@ -72,6 +72,17 @@ func (c *Client) PerformGetRequest(url string) ([]byte, error) {
7272
return c.performRequest(request)
7373
}
7474

75+
// PerformJSONGetRequest performs a GET request to the specified URL and returns the response body and eventual errors
76+
func (c *Client) PerformJSONGetRequest(url string) ([]byte, error) {
77+
request, err := http.NewRequest(http.MethodGet, url, nil)
78+
79+
if err != nil {
80+
return nil, err
81+
}
82+
83+
return c.performJSONRequest(request)
84+
}
85+
7586
// PerformPostRequest performs a POST request to the specified URL and returns the response body and eventual errors
7687
func (c *Client) PerformPostRequest(url string, requestBody []byte) ([]byte, error) {
7788
var bodyReader io.Reader
@@ -80,7 +91,7 @@ func (c *Client) PerformPostRequest(url string, requestBody []byte) ([]byte, err
8091
bodyReader = bytes.NewBuffer(requestBody)
8192
}
8293

83-
request, err := http.NewRequest("POST", url, bodyReader)
94+
request, err := http.NewRequest(http.MethodPost, url, bodyReader)
8495

8596
if err != nil {
8697
return nil, err
@@ -89,6 +100,23 @@ func (c *Client) PerformPostRequest(url string, requestBody []byte) ([]byte, err
89100
return c.performRequest(request)
90101
}
91102

103+
// PerformPostRequest performs a POST request to the specified URL and returns the response body and eventual errors
104+
func (c *Client) PerformJSONPostRequest(url string, requestBody []byte) ([]byte, error) {
105+
var bodyReader io.Reader
106+
107+
if requestBody != nil {
108+
bodyReader = bytes.NewBuffer(requestBody)
109+
}
110+
111+
request, err := http.NewRequest(http.MethodPost, url, bodyReader)
112+
113+
if err != nil {
114+
return nil, err
115+
}
116+
117+
return c.performJSONRequest(request)
118+
}
119+
92120
// PerformPutRequest performs a PUT request to the specified URL and returns the response body and eventual errors
93121
func (c *Client) PerformPutRequest(url string, requestBody []byte) ([]byte, error) {
94122
var bodyReader io.Reader
@@ -108,7 +136,7 @@ func (c *Client) PerformPutRequest(url string, requestBody []byte) ([]byte, erro
108136

109137
// PerformDeleteRequest performs a DELETE request to the specified URL and returns the response body and eventual errors
110138
func (c *Client) PerformDeleteRequest(url string) error {
111-
request, err := http.NewRequest("DELETE", url, nil)
139+
request, err := http.NewRequest(http.MethodDelete, url, nil)
112140

113141
if err != nil {
114142
return err
@@ -118,6 +146,18 @@ func (c *Client) PerformDeleteRequest(url string) error {
118146
return err
119147
}
120148

149+
// PerformJSONDeleteRequest performs a DELETE request to the specified URL and returns the response body and eventual errors
150+
func (c *Client) PerformJSONDeleteRequest(url string) error {
151+
request, err := http.NewRequest(http.MethodDelete, url, nil)
152+
153+
if err != nil {
154+
return err
155+
}
156+
157+
_, err = c.performJSONRequest(request)
158+
return err
159+
}
160+
121161
// Adds common headers to the specified request
122162
func (c *Client) addRequestHeaders(request *http.Request) *http.Request {
123163
request.SetBasicAuth(c.userName, c.password)
@@ -127,6 +167,15 @@ func (c *Client) addRequestHeaders(request *http.Request) *http.Request {
127167
return request
128168
}
129169

170+
// Adds common headers to the specified request
171+
func (c *Client) addJSONRequestHeaders(request *http.Request) *http.Request {
172+
request.SetBasicAuth(c.userName, c.password)
173+
request.Header.Add("Accept", "application/json")
174+
request.Header.Add("Content-Type", "application/json")
175+
176+
return request
177+
}
178+
130179
// Performs the specified HTTP request and returns the response through handleResponse()
131180
func (c *Client) performRequest(request *http.Request) ([]byte, error) {
132181
c.addRequestHeaders(request)
@@ -139,6 +188,18 @@ func (c *Client) performRequest(request *http.Request) ([]byte, error) {
139188
return handleResponse(response)
140189
}
141190

191+
// Performs the specified HTTP request and returns the response through handleResponse()
192+
func (c *Client) performJSONRequest(request *http.Request) ([]byte, error) {
193+
c.addJSONRequestHeaders(request)
194+
response, err := c.httpClient.Do(request)
195+
196+
if err != nil {
197+
return nil, err
198+
}
199+
200+
return handleResponse(response)
201+
}
202+
142203
// Returns the base URL to use for API requests
143204
func (c *Client) getBaseUrl() string {
144205
urlVersion, _ := semver.Make(c.apiVersion)

upcloud/error.go

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,29 @@
11
package upcloud
22

3-
import "fmt"
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
)
47

58
// Error represents an error
69
type Error struct {
7-
ErrorCode string `xml:"error_code"`
8-
ErrorMessage string `xml:"error_message"`
10+
ErrorCode string `xml:"error_code" json:"error_code"`
11+
ErrorMessage string `xml:"error_message" json:"error_message"`
12+
}
13+
14+
func (e *Error) UnmarshalJSON(b []byte) error {
15+
type localError Error
16+
v := struct {
17+
Error localError `json:"error"`
18+
}{}
19+
err := json.Unmarshal(b, &v)
20+
if err != nil {
21+
return err
22+
}
23+
24+
(*e) = Error(v.Error)
25+
26+
return nil
927
}
1028

1129
// Error implements the Error interface

0 commit comments

Comments
 (0)