Skip to content
This repository was archived by the owner on Aug 1, 2023. It is now read-only.

Commit 9838c69

Browse files
committed
List and get Compute image metadata
1 parent 1f02c2b commit 9838c69

File tree

6 files changed

+386
-2
lines changed

6 files changed

+386
-2
lines changed
+107
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
// +build fixtures
2+
3+
package images
4+
5+
import (
6+
"net/http"
7+
"testing"
8+
9+
th "github.com/rackspace/gophercloud/testhelper"
10+
"github.com/rackspace/gophercloud/testhelper/client"
11+
)
12+
13+
var (
14+
testMetadataMap = map[string]string{"foo": "bar"}
15+
testMetadataOpts = MetadatumOpts{"foo": "bar"}
16+
testMetadataString = `{"metadata": {"foo": "bar"}}`
17+
18+
testMetadataChangeOpts = MetadataOpts{"foo": "baz"}
19+
testMetadataChangeString = `{"metadata": {"foo": "baz"}}`
20+
testMetadataResetString = testMetadataChangeString
21+
testMetadataResetMap = map[string]string{"foo": "baz"}
22+
testMetadataUpdateString = `{"metadata": {"foo": "baz"}}`
23+
testMetadataUpdateMap = map[string]string{"foo": "baz"}
24+
25+
testMetadatumMap = map[string]string{"foo": "bar"}
26+
testMetadatumOpts = MetadatumOpts{"foo": "bar"}
27+
testMetadatumString = `{"meta": {"foo": "bar"}}`
28+
29+
testMetadatumChangeOpts = MetadatumOpts{"foo": "bar"}
30+
testMetadatumChangeString = `{"meta": {"foo": "bar"}}`
31+
testMetadatumCreateMap = map[string]string{"foo": "bar"}
32+
testMetadatumCreateString = testMetadatumChangeString
33+
)
34+
35+
// HandleMetadataGetSuccessfully sets up the test server to respond to a metadata Get request.
36+
func HandleMetadataGetSuccessfully(t *testing.T) {
37+
th.Mux.HandleFunc("/images/1234asdf/metadata", func(w http.ResponseWriter, r *http.Request) {
38+
th.TestMethod(t, r, "GET")
39+
th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
40+
th.TestHeader(t, r, "Accept", "application/json")
41+
42+
w.WriteHeader(http.StatusOK)
43+
w.Write([]byte(testMetadataString))
44+
})
45+
}
46+
47+
// HandleMetadataResetSuccessfully sets up the test server to respond to a metadata Create request.
48+
func HandleMetadataResetSuccessfully(t *testing.T) {
49+
th.Mux.HandleFunc("/images/1234asdf/metadata", func(w http.ResponseWriter, r *http.Request) {
50+
th.TestMethod(t, r, "PUT")
51+
th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
52+
th.TestJSONRequest(t, r, testMetadataResetString)
53+
54+
w.WriteHeader(http.StatusOK)
55+
w.Header().Add("Content-Type", "application/json")
56+
w.Write([]byte(testMetadataResetString))
57+
})
58+
}
59+
60+
// HandleMetadataUpdateSuccessfully sets up the test server to respond to a metadata Update request.
61+
func HandleMetadataUpdateSuccessfully(t *testing.T) {
62+
th.Mux.HandleFunc("/images/1234asdf/metadata", func(w http.ResponseWriter, r *http.Request) {
63+
th.TestMethod(t, r, "POST")
64+
th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
65+
th.TestJSONRequest(t, r, testMetadataResetString)
66+
67+
w.WriteHeader(http.StatusOK)
68+
w.Header().Add("Content-Type", "application/json")
69+
w.Write([]byte(testMetadataUpdateString))
70+
})
71+
}
72+
73+
// HandleMetadatumGetSuccessfully sets up the test server to respond to a metadatum Get request.
74+
func HandleMetadatumGetSuccessfully(t *testing.T) {
75+
th.Mux.HandleFunc("/images/1234asdf/metadata/foo", func(w http.ResponseWriter, r *http.Request) {
76+
th.TestMethod(t, r, "GET")
77+
th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
78+
th.TestHeader(t, r, "Accept", "application/json")
79+
80+
w.WriteHeader(http.StatusOK)
81+
w.Header().Add("Content-Type", "application/json")
82+
w.Write([]byte(testMetadatumString))
83+
})
84+
}
85+
86+
// HandleMetadatumCreateSuccessfully sets up the test server to respond to a metadatum Create request.
87+
func HandleMetadatumCreateSuccessfully(t *testing.T) {
88+
th.Mux.HandleFunc("/images/1234asdf/metadata/foo", func(w http.ResponseWriter, r *http.Request) {
89+
th.TestMethod(t, r, "PUT")
90+
th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
91+
th.TestJSONRequest(t, r, testMetadatumChangeString)
92+
93+
w.WriteHeader(http.StatusOK)
94+
w.Header().Add("Content-Type", "application/json")
95+
w.Write([]byte(testMetadatumChangeString))
96+
})
97+
}
98+
99+
// HandleMetadatumDeleteSuccessfully sets up the test server to respond to a metadatum Delete request.
100+
func HandleMetadatumDeleteSuccessfully(t *testing.T) {
101+
th.Mux.HandleFunc("/images/1234asdf/metadata/foo", func(w http.ResponseWriter, r *http.Request) {
102+
th.TestMethod(t, r, "DELETE")
103+
th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
104+
105+
w.WriteHeader(http.StatusNoContent)
106+
})
107+
}

openstack/compute/v2/images/requests.go

+120-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package images
22

33
import (
4+
"errors"
45
"fmt"
56

67
"github.com/rackspace/gophercloud"
@@ -59,7 +60,7 @@ func ListDetail(client *gophercloud.ServiceClient, opts ListOptsBuilder) paginat
5960
}
6061

6162
// Get acquires additional detail about a specific image by ID.
62-
// Use ExtractImage() to interpret the result as an openstack Image.
63+
// Use GetResult.Extract() to interpret the result as an openstack Image.
6364
func Get(client *gophercloud.ServiceClient, id string) GetResult {
6465
var result GetResult
6566
_, result.Err = client.Get(getURL(client, id), &result.Body, nil)
@@ -107,3 +108,121 @@ func IDFromName(client *gophercloud.ServiceClient, name string) (string, error)
107108
return "", fmt.Errorf("Found %d images matching %s", imageCount, name)
108109
}
109110
}
111+
112+
// Metadata requests all the metadata for the given image ID.
113+
func Metadata(client *gophercloud.ServiceClient, id string) GetMetadataResult {
114+
var res GetMetadataResult
115+
_, res.Err = client.Get(metadataURL(client, id), &res.Body, nil)
116+
return res
117+
}
118+
119+
// MetadataOpts is a map that contains key-value pairs.
120+
type MetadataOpts map[string]string
121+
122+
// ToMetadataResetMap assembles a body for a Reset request based on the contents of a MetadataOpts.
123+
func (opts MetadataOpts) ToMetadataResetMap() (map[string]interface{}, error) {
124+
return map[string]interface{}{"metadata": opts}, nil
125+
}
126+
127+
// ToMetadataUpdateMap assembles a body for an Update request based on the contents of a MetadataOpts.
128+
func (opts MetadataOpts) ToMetadataUpdateMap() (map[string]interface{}, error) {
129+
return map[string]interface{}{"metadata": opts}, nil
130+
}
131+
132+
// ResetMetadataOptsBuilder allows extensions to add additional parameters to the
133+
// Reset request.
134+
type ResetMetadataOptsBuilder interface {
135+
ToMetadataResetMap() (map[string]interface{}, error)
136+
}
137+
138+
// ResetMetadata will create multiple new key-value pairs for the given image ID.
139+
// Note: Using this operation will erase any already-existing metadata and create
140+
// the new metadata provided. To keep any already-existing metadata, use the
141+
// UpdateMetadatas or UpdateMetadata function.
142+
func ResetMetadata(client *gophercloud.ServiceClient, id string, opts ResetMetadataOptsBuilder) ResetMetadataResult {
143+
var res ResetMetadataResult
144+
metadata, err := opts.ToMetadataResetMap()
145+
if err != nil {
146+
res.Err = err
147+
return res
148+
}
149+
_, res.Err = client.Put(metadataURL(client, id), metadata, &res.Body, &gophercloud.RequestOpts{
150+
OkCodes: []int{200},
151+
})
152+
return res
153+
}
154+
155+
// UpdateMetadataOptsBuilder allows extensions to add additional parameters to the
156+
// Create request.
157+
type UpdateMetadataOptsBuilder interface {
158+
ToMetadataUpdateMap() (map[string]interface{}, error)
159+
}
160+
161+
// UpdateMetadata updates (or creates) all the metadata specified by opts for the given image ID.
162+
// This operation does not affect already-existing metadata that is not specified
163+
// by opts.
164+
func UpdateMetadata(client *gophercloud.ServiceClient, id string, opts UpdateMetadataOptsBuilder) UpdateMetadataResult {
165+
var res UpdateMetadataResult
166+
metadata, err := opts.ToMetadataUpdateMap()
167+
if err != nil {
168+
res.Err = err
169+
return res
170+
}
171+
_, res.Err = client.Post(metadataURL(client, id), metadata, &res.Body, &gophercloud.RequestOpts{
172+
OkCodes: []int{200},
173+
})
174+
return res
175+
}
176+
177+
// Metadatum requests the key-value pair with the given key for the given image ID.
178+
func Metadatum(client *gophercloud.ServiceClient, id, key string) GetMetadatumResult {
179+
var res GetMetadatumResult
180+
_, res.Err = client.Request("GET", metadatumURL(client, id, key), gophercloud.RequestOpts{
181+
JSONResponse: &res.Body,
182+
})
183+
return res
184+
}
185+
186+
// MetadatumOpts is a map of length one that contains a key-value pair.
187+
type MetadatumOpts map[string]interface{}
188+
189+
// ToMetadatumCreateMap assembles a body for a Create request based on the contents of a MetadataumOpts.
190+
func (opts MetadatumOpts) ToMetadatumCreateMap() (map[string]interface{}, string, error) {
191+
if len(opts) != 1 {
192+
return nil, "", errors.New("CreateMetadatum operation must have 1 and only 1 key-value pair.")
193+
}
194+
metadatum := map[string]interface{}{"meta": opts}
195+
var key string
196+
for k := range metadatum["meta"].(MetadatumOpts) {
197+
key = k
198+
}
199+
return metadatum, key, nil
200+
}
201+
202+
// MetadatumOptsBuilder allows extensions to add additional parameters to the
203+
// Create request.
204+
type MetadatumOptsBuilder interface {
205+
ToMetadatumCreateMap() (map[string]interface{}, string, error)
206+
}
207+
208+
// CreateMetadatum will create or update the key-value pair with the given key for the given image ID.
209+
func CreateMetadatum(client *gophercloud.ServiceClient, id string, opts MetadatumOptsBuilder) CreateMetadatumResult {
210+
var res CreateMetadatumResult
211+
metadatum, key, err := opts.ToMetadatumCreateMap()
212+
if err != nil {
213+
res.Err = err
214+
return res
215+
}
216+
217+
_, res.Err = client.Put(metadatumURL(client, id, key), metadatum, &res.Body, &gophercloud.RequestOpts{
218+
OkCodes: []int{200},
219+
})
220+
return res
221+
}
222+
223+
// DeleteMetadatum will delete the key-value pair with the given key for the given image ID.
224+
func DeleteMetadatum(client *gophercloud.ServiceClient, id, key string) DeleteMetadatumResult {
225+
var res DeleteMetadatumResult
226+
_, res.Err = client.Delete(metadatumURL(client, id, key), nil)
227+
return res
228+
}

openstack/compute/v2/images/requests_test.go

+70
Original file line numberDiff line numberDiff line change
@@ -186,3 +186,73 @@ func TestDeleteImage(t *testing.T) {
186186
res := Delete(fake.ServiceClient(), "12345678")
187187
th.AssertNoErr(t, res.Err)
188188
}
189+
190+
func TestGetMetadata(t *testing.T) {
191+
th.SetupHTTP()
192+
defer th.TeardownHTTP()
193+
194+
HandleMetadataGetSuccessfully(t)
195+
196+
expected := testMetadataMap
197+
actual, err := Metadata(fake.ServiceClient(), "1234asdf").Extract()
198+
th.AssertNoErr(t, err)
199+
th.AssertDeepEquals(t, expected, actual)
200+
}
201+
202+
func TestResetMetadata(t *testing.T) {
203+
th.SetupHTTP()
204+
defer th.TeardownHTTP()
205+
206+
HandleMetadataResetSuccessfully(t)
207+
208+
expected := testMetadataResetMap
209+
actual, err := ResetMetadata(fake.ServiceClient(), "1234asdf", testMetadataChangeOpts).Extract()
210+
th.AssertNoErr(t, err)
211+
th.AssertDeepEquals(t, expected, actual)
212+
}
213+
214+
func TestUpdateMetadata(t *testing.T) {
215+
th.SetupHTTP()
216+
defer th.TeardownHTTP()
217+
218+
HandleMetadataUpdateSuccessfully(t)
219+
220+
expected := testMetadataUpdateMap
221+
actual, err := UpdateMetadata(fake.ServiceClient(), "1234asdf", testMetadataChangeOpts).Extract()
222+
th.AssertNoErr(t, err)
223+
th.AssertDeepEquals(t, expected, actual)
224+
}
225+
226+
func TestGetMetadatum(t *testing.T) {
227+
th.SetupHTTP()
228+
defer th.TeardownHTTP()
229+
230+
HandleMetadatumGetSuccessfully(t)
231+
232+
expected := testMetadatumMap
233+
actual, err := Metadatum(fake.ServiceClient(), "1234asdf", "foo").Extract()
234+
th.AssertNoErr(t, err)
235+
th.AssertDeepEquals(t, expected, actual)
236+
}
237+
238+
func TestCreateMetadatum(t *testing.T) {
239+
th.SetupHTTP()
240+
defer th.TeardownHTTP()
241+
242+
HandleMetadatumCreateSuccessfully(t)
243+
244+
expected := testMetadatumCreateMap
245+
actual, err := CreateMetadatum(fake.ServiceClient(), "1234asdf", testMetadatumChangeOpts).Extract()
246+
th.AssertNoErr(t, err)
247+
th.AssertDeepEquals(t, expected, actual)
248+
}
249+
250+
func TestDeleteMetadatum(t *testing.T) {
251+
th.SetupHTTP()
252+
defer th.TeardownHTTP()
253+
254+
HandleMetadatumDeleteSuccessfully(t)
255+
256+
err := DeleteMetadatum(fake.ServiceClient(), "1234asdf", "foo").ExtractErr()
257+
th.AssertNoErr(t, err)
258+
}

0 commit comments

Comments
 (0)