Skip to content

Commit 77b1289

Browse files
committed
Support for generic REST client
This commit adds support for a generic REST actions. These actions allow direct REST communication while still leveraging some of the core Gophercloud client features.
1 parent afce78e commit 77b1289

File tree

5 files changed

+477
-0
lines changed

5 files changed

+477
-0
lines changed
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
// +build acceptance clientconfig
2+
package clientconfig
3+
4+
import (
5+
"testing"
6+
7+
acc_clients "github.com/gophercloud/gophercloud/acceptance/clients"
8+
acc_tools "github.com/gophercloud/gophercloud/acceptance/tools"
9+
10+
th "github.com/gophercloud/gophercloud/testhelper"
11+
cc "github.com/gophercloud/utils/openstack/clientconfig"
12+
"github.com/gophercloud/utils/openstack/restclient"
13+
)
14+
15+
func TestRESTClient(t *testing.T) {
16+
acc_clients.RequireAdmin(t)
17+
18+
// This will be populated by environment variables.
19+
clientOpts := &cc.ClientOpts{}
20+
21+
computeClient, err := cc.NewServiceClient("compute", clientOpts)
22+
th.AssertNoErr(t, err)
23+
24+
// Test creating a flavor
25+
flavorName := acc_tools.RandomString("TESTACC-", 8)
26+
flavorID := acc_tools.RandomString("TESTACC-", 8)
27+
flavorOpts := map[string]interface{}{
28+
"name": flavorName,
29+
"ram": 512,
30+
"vcpus": 1,
31+
"disk": 5,
32+
"id": flavorID,
33+
}
34+
35+
postOpts := &restclient.PostOpts{
36+
Params: map[string]interface{}{"flavor": flavorOpts},
37+
}
38+
39+
postURL := computeClient.ServiceURL("flavors")
40+
postRes := restclient.Post(computeClient, postURL, postOpts)
41+
th.AssertNoErr(t, postRes.Err)
42+
flavorResult, err := postRes.Extract()
43+
th.AssertNoErr(t, postRes.Err)
44+
acc_tools.PrintResource(t, flavorResult)
45+
46+
// Test retrieving a flavor
47+
getURL := computeClient.ServiceURL("flavors", flavorID)
48+
getRes := restclient.Get(computeClient, getURL, nil)
49+
th.AssertNoErr(t, getRes.Err)
50+
51+
flavorResult, err = getRes.Extract()
52+
th.AssertNoErr(t, err)
53+
54+
flavor := flavorResult["flavor"].(map[string]interface{})
55+
56+
acc_tools.PrintResource(t, flavor)
57+
58+
th.AssertEquals(t, flavor["disk"], float64(5))
59+
th.AssertEquals(t, flavor["id"], flavorID)
60+
th.AssertEquals(t, flavor["name"], flavorName)
61+
th.AssertEquals(t, flavor["ram"], float64(512))
62+
th.AssertEquals(t, flavor["swap"], "")
63+
th.AssertEquals(t, flavor["vcpus"], float64(1))
64+
65+
// Test deleting a flavor
66+
deleteURL := computeClient.ServiceURL("flavors", flavorID)
67+
deleteRes := restclient.Delete(computeClient, deleteURL, nil)
68+
th.AssertNoErr(t, getRes.Err)
69+
err = deleteRes.ExtractErr()
70+
th.AssertNoErr(t, err)
71+
}

openstack/restclient/doc.go

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/* Package restclient provides generic REST functions.
2+
3+
Example of a GET request
4+
5+
getURL := computeClient.ServiceURL("flavors", flavorID)
6+
getRes := restclient.Get(computeClient, getURL, nil)
7+
if err != nil {
8+
panic(err)
9+
}
10+
11+
flavorResult, err = getRes.Extract()
12+
if err != nil {
13+
panic(err)
14+
}
15+
16+
flavor := flavorResult["flavor"].(map[string]interface{})
17+
fmt.Printf("%v\n", flavor)
18+
19+
Example of a POST request
20+
21+
flavorOpts := map[string]interface{}{
22+
"name": "some-name",
23+
"ram": 512,
24+
"vcpus": 1,
25+
"disk": 5,
26+
"id": "some-id",
27+
}
28+
29+
postOpts := &restclient.PostOpts{
30+
Params: map[string]interface{}{"flavor": flavorOpts},
31+
}
32+
33+
postURL := computeClient.ServiceURL("flavors")
34+
postRes := restclient.Post(computeClient, postURL, postOpts)
35+
if err != nil {
36+
panic(err)
37+
}
38+
39+
flavorResult, err := postRes.Extract()
40+
if err != nil {
41+
panic(err)
42+
}
43+
44+
fmt.Printf("%v\n", flavor)
45+
46+
Example of a DELETE Request
47+
48+
deleteURL := computeClient.ServiceURL("flavors", "flavor-id")
49+
deleteRes := restclient.Delete(computeClient, deleteURL, nil)
50+
if err != nil {
51+
panic(err)
52+
}
53+
54+
err = deleteRes.ExtractErr()
55+
if err != nil {
56+
panic(err)
57+
}
58+
*/
59+
package restclient

openstack/restclient/requests.go

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
package restclient
2+
3+
import (
4+
"github.com/gophercloud/gophercloud"
5+
)
6+
7+
// GetOpts represents options used in the Get request.
8+
type GetOpts struct {
9+
Headers map[string]string
10+
}
11+
12+
// Get performs a generic GET request to the specified URL.
13+
func Get(client *gophercloud.ServiceClient, url string, opts *GetOpts) (r GetResult) {
14+
requestOpts := new(gophercloud.RequestOpts)
15+
16+
if opts != nil {
17+
requestOpts.MoreHeaders = opts.Headers
18+
}
19+
20+
// Allow a wide range of statuses
21+
requestOpts.OkCodes = []int{200, 201, 202, 204, 206}
22+
23+
_, err := client.Get(url, &r.Body, requestOpts)
24+
if err != nil {
25+
if err.Error() != "EOF" {
26+
r.Err = err
27+
return
28+
}
29+
30+
err = nil
31+
r.Body = nil
32+
}
33+
34+
return
35+
}
36+
37+
// PostOpts represents options used in a Post request.
38+
type PostOpts struct {
39+
Headers map[string]string
40+
Params map[string]interface{}
41+
}
42+
43+
// Post performs a generic POST request to the specified URL.
44+
func Post(client *gophercloud.ServiceClient, url string, opts *PostOpts) (r PostResult) {
45+
var b map[string]interface{}
46+
requestOpts := new(gophercloud.RequestOpts)
47+
48+
if opts != nil {
49+
requestOpts.MoreHeaders = opts.Headers
50+
b = opts.Params
51+
}
52+
53+
// Allow a wide range of statuses
54+
requestOpts.OkCodes = []int{200, 201, 202, 204, 206}
55+
56+
_, err := client.Post(url, &b, &r.Body, requestOpts)
57+
if err != nil {
58+
if err.Error() != "EOF" {
59+
r.Err = err
60+
return
61+
}
62+
63+
err = nil
64+
r.Body = nil
65+
}
66+
67+
return
68+
}
69+
70+
// PutOpts represents options used in a Put request.
71+
type PutOpts struct {
72+
Headers map[string]string
73+
Params map[string]interface{}
74+
}
75+
76+
// Put performs a generic PUT request to the specified URL.
77+
func Put(client *gophercloud.ServiceClient, url string, opts *PutOpts) (r PostResult) {
78+
var b map[string]interface{}
79+
requestOpts := new(gophercloud.RequestOpts)
80+
81+
if opts != nil {
82+
requestOpts.MoreHeaders = opts.Headers
83+
b = opts.Params
84+
}
85+
86+
// Allow a wide range of statuses
87+
requestOpts.OkCodes = []int{200, 201, 202, 204, 206}
88+
89+
_, err := client.Put(url, &b, &r.Body, requestOpts)
90+
if err != nil {
91+
if err.Error() != "EOF" {
92+
r.Err = err
93+
return
94+
}
95+
96+
err = nil
97+
r.Body = nil
98+
}
99+
100+
return
101+
}
102+
103+
// PatchOpts represents options used in a Patch request.
104+
type PatchOpts struct {
105+
Headers map[string]string
106+
Params map[string]interface{}
107+
}
108+
109+
// Patch performs a generic PATCH request to the specified URL.
110+
func Patch(client *gophercloud.ServiceClient, url string, opts *PatchOpts) (r PatchResult) {
111+
var b map[string]interface{}
112+
requestOpts := new(gophercloud.RequestOpts)
113+
114+
if opts != nil {
115+
requestOpts.MoreHeaders = opts.Headers
116+
b = opts.Params
117+
}
118+
119+
// Allow a wide range of statuses
120+
requestOpts.OkCodes = []int{200, 201, 202, 204, 206}
121+
122+
_, err := client.Patch(url, &b, &r.Body, requestOpts)
123+
if err != nil {
124+
if err.Error() != "EOF" {
125+
r.Err = err
126+
return
127+
}
128+
129+
err = nil
130+
r.Body = nil
131+
}
132+
133+
return
134+
}
135+
136+
// DeleteOpts represents options used in a Delete request.
137+
type DeleteOpts struct {
138+
Headers map[string]string
139+
}
140+
141+
// Delete performs a generic DELETE request to the specified URL.
142+
func Delete(client *gophercloud.ServiceClient, url string, opts *DeleteOpts) (r DeleteResult) {
143+
requestOpts := new(gophercloud.RequestOpts)
144+
145+
if opts != nil {
146+
requestOpts.MoreHeaders = opts.Headers
147+
}
148+
149+
// Allow a wide range of statuses
150+
requestOpts.OkCodes = []int{200, 201, 202, 204, 206}
151+
152+
_, err := client.Delete(url, requestOpts)
153+
if err != nil {
154+
if err.Error() != "EOF" {
155+
r.Err = err
156+
return
157+
}
158+
159+
err = nil
160+
r.Body = nil
161+
}
162+
163+
return
164+
}

openstack/restclient/results.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package restclient
2+
3+
import (
4+
"github.com/gophercloud/gophercloud"
5+
)
6+
7+
type commonResult struct {
8+
gophercloud.Result
9+
}
10+
11+
func (r commonResult) Extract() (map[string]interface{}, error) {
12+
var s map[string]interface{}
13+
err := r.ExtractInto(&s)
14+
return s, err
15+
}
16+
17+
type GetResult struct {
18+
commonResult
19+
}
20+
21+
type PostResult struct {
22+
commonResult
23+
}
24+
25+
type PatchResult struct {
26+
commonResult
27+
}
28+
29+
type PutResult struct {
30+
commonResult
31+
}
32+
33+
type DeleteResult struct {
34+
gophercloud.ErrResult
35+
}

0 commit comments

Comments
 (0)