forked from kubernetes/kubernetes
-
Notifications
You must be signed in to change notification settings - Fork 1
/
results.go
372 lines (297 loc) · 10.4 KB
/
results.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
package servers
import (
"reflect"
"fmt"
"path"
"net/url"
"github.com/mitchellh/mapstructure"
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/pagination"
)
type serverResult struct {
gophercloud.Result
}
// Extract interprets any serverResult as a Server, if possible.
func (r serverResult) Extract() (*Server, error) {
if r.Err != nil {
return nil, r.Err
}
var response struct {
Server Server `mapstructure:"server"`
}
config := &mapstructure.DecoderConfig{
DecodeHook: toMapFromString,
Result: &response,
}
decoder, err := mapstructure.NewDecoder(config)
if err != nil {
return nil, err
}
err = decoder.Decode(r.Body)
if err != nil {
return nil, err
}
return &response.Server, nil
}
// CreateResult temporarily contains the response from a Create call.
type CreateResult struct {
serverResult
}
// GetResult temporarily contains the response from a Get call.
type GetResult struct {
serverResult
}
// UpdateResult temporarily contains the response from an Update call.
type UpdateResult struct {
serverResult
}
// DeleteResult temporarily contains the response from a Delete call.
type DeleteResult struct {
gophercloud.ErrResult
}
// RebuildResult temporarily contains the response from a Rebuild call.
type RebuildResult struct {
serverResult
}
// ActionResult represents the result of server action operations, like reboot
type ActionResult struct {
gophercloud.ErrResult
}
// RescueResult represents the result of a server rescue operation
type RescueResult struct {
ActionResult
}
// CreateImageResult represents the result of an image creation operation
type CreateImageResult struct {
gophercloud.Result
}
// ExtractImageID gets the ID of the newly created server image from the header
func (res CreateImageResult) ExtractImageID() (string, error) {
if res.Err != nil {
return "", res.Err
}
// Get the image id from the header
u, err := url.ParseRequestURI(res.Header.Get("Location"))
if err != nil {
return "", fmt.Errorf("Failed to parse the image id: %s", err.Error())
}
imageId := path.Base(u.Path)
if imageId == "." || imageId == "/" {
return "", fmt.Errorf("Failed to parse the ID of newly created image: %s", u)
}
return imageId, nil
}
// Extract interprets any RescueResult as an AdminPass, if possible.
func (r RescueResult) Extract() (string, error) {
if r.Err != nil {
return "", r.Err
}
var response struct {
AdminPass string `mapstructure:"adminPass"`
}
err := mapstructure.Decode(r.Body, &response)
return response.AdminPass, err
}
// Server exposes only the standard OpenStack fields corresponding to a given server on the user's account.
type Server struct {
// ID uniquely identifies this server amongst all other servers, including those not accessible to the current tenant.
ID string
// TenantID identifies the tenant owning this server resource.
TenantID string `mapstructure:"tenant_id"`
// UserID uniquely identifies the user account owning the tenant.
UserID string `mapstructure:"user_id"`
// Name contains the human-readable name for the server.
Name string
// Updated and Created contain ISO-8601 timestamps of when the state of the server last changed, and when it was created.
Updated string
Created string
HostID string
// Status contains the current operational status of the server, such as IN_PROGRESS or ACTIVE.
Status string
// Progress ranges from 0..100.
// A request made against the server completes only once Progress reaches 100.
Progress int
// AccessIPv4 and AccessIPv6 contain the IP addresses of the server, suitable for remote access for administration.
AccessIPv4, AccessIPv6 string
// Image refers to a JSON object, which itself indicates the OS image used to deploy the server.
Image map[string]interface{}
// Flavor refers to a JSON object, which itself indicates the hardware configuration of the deployed server.
Flavor map[string]interface{}
// Addresses includes a list of all IP addresses assigned to the server, keyed by pool.
Addresses map[string]interface{}
// Metadata includes a list of all user-specified key-value pairs attached to the server.
Metadata map[string]interface{}
// Links includes HTTP references to the itself, useful for passing along to other APIs that might want a server reference.
Links []interface{}
// KeyName indicates which public key was injected into the server on launch.
KeyName string `json:"key_name" mapstructure:"key_name"`
// AdminPass will generally be empty (""). However, it will contain the administrative password chosen when provisioning a new server without a set AdminPass setting in the first place.
// Note that this is the ONLY time this field will be valid.
AdminPass string `json:"adminPass" mapstructure:"adminPass"`
// SecurityGroups includes the security groups that this instance has applied to it
SecurityGroups []map[string]interface{} `json:"security_groups" mapstructure:"security_groups"`
}
// ServerPage abstracts the raw results of making a List() request against the API.
// As OpenStack extensions may freely alter the response bodies of structures returned to the client, you may only safely access the
// data provided through the ExtractServers call.
type ServerPage struct {
pagination.LinkedPageBase
}
// IsEmpty returns true if a page contains no Server results.
func (page ServerPage) IsEmpty() (bool, error) {
servers, err := ExtractServers(page)
if err != nil {
return true, err
}
return len(servers) == 0, nil
}
// NextPageURL uses the response's embedded link reference to navigate to the next page of results.
func (page ServerPage) NextPageURL() (string, error) {
type resp struct {
Links []gophercloud.Link `mapstructure:"servers_links"`
}
var r resp
err := mapstructure.Decode(page.Body, &r)
if err != nil {
return "", err
}
return gophercloud.ExtractNextURL(r.Links)
}
// ExtractServers interprets the results of a single page from a List() call, producing a slice of Server entities.
func ExtractServers(page pagination.Page) ([]Server, error) {
casted := page.(ServerPage).Body
var response struct {
Servers []Server `mapstructure:"servers"`
}
config := &mapstructure.DecoderConfig{
DecodeHook: toMapFromString,
Result: &response,
}
decoder, err := mapstructure.NewDecoder(config)
if err != nil {
return nil, err
}
err = decoder.Decode(casted)
return response.Servers, err
}
// MetadataResult contains the result of a call for (potentially) multiple key-value pairs.
type MetadataResult struct {
gophercloud.Result
}
// GetMetadataResult temporarily contains the response from a metadata Get call.
type GetMetadataResult struct {
MetadataResult
}
// ResetMetadataResult temporarily contains the response from a metadata Reset call.
type ResetMetadataResult struct {
MetadataResult
}
// UpdateMetadataResult temporarily contains the response from a metadata Update call.
type UpdateMetadataResult struct {
MetadataResult
}
// MetadatumResult contains the result of a call for individual a single key-value pair.
type MetadatumResult struct {
gophercloud.Result
}
// GetMetadatumResult temporarily contains the response from a metadatum Get call.
type GetMetadatumResult struct {
MetadatumResult
}
// CreateMetadatumResult temporarily contains the response from a metadatum Create call.
type CreateMetadatumResult struct {
MetadatumResult
}
// DeleteMetadatumResult temporarily contains the response from a metadatum Delete call.
type DeleteMetadatumResult struct {
gophercloud.ErrResult
}
// Extract interprets any MetadataResult as a Metadata, if possible.
func (r MetadataResult) Extract() (map[string]string, error) {
if r.Err != nil {
return nil, r.Err
}
var response struct {
Metadata map[string]string `mapstructure:"metadata"`
}
err := mapstructure.Decode(r.Body, &response)
return response.Metadata, err
}
// Extract interprets any MetadatumResult as a Metadatum, if possible.
func (r MetadatumResult) Extract() (map[string]string, error) {
if r.Err != nil {
return nil, r.Err
}
var response struct {
Metadatum map[string]string `mapstructure:"meta"`
}
err := mapstructure.Decode(r.Body, &response)
return response.Metadatum, err
}
func toMapFromString(from reflect.Kind, to reflect.Kind, data interface{}) (interface{}, error) {
if (from == reflect.String) && (to == reflect.Map) {
return map[string]interface{}{}, nil
}
return data, nil
}
// Address represents an IP address.
type Address struct {
Version int `mapstructure:"version"`
Address string `mapstructure:"addr"`
}
// AddressPage abstracts the raw results of making a ListAddresses() request against the API.
// As OpenStack extensions may freely alter the response bodies of structures returned
// to the client, you may only safely access the data provided through the ExtractAddresses call.
type AddressPage struct {
pagination.SinglePageBase
}
// IsEmpty returns true if an AddressPage contains no networks.
func (r AddressPage) IsEmpty() (bool, error) {
addresses, err := ExtractAddresses(r)
if err != nil {
return true, err
}
return len(addresses) == 0, nil
}
// ExtractAddresses interprets the results of a single page from a ListAddresses() call,
// producing a map of addresses.
func ExtractAddresses(page pagination.Page) (map[string][]Address, error) {
casted := page.(AddressPage).Body
var response struct {
Addresses map[string][]Address `mapstructure:"addresses"`
}
err := mapstructure.Decode(casted, &response)
if err != nil {
return nil, err
}
return response.Addresses, err
}
// NetworkAddressPage abstracts the raw results of making a ListAddressesByNetwork() request against the API.
// As OpenStack extensions may freely alter the response bodies of structures returned
// to the client, you may only safely access the data provided through the ExtractAddresses call.
type NetworkAddressPage struct {
pagination.SinglePageBase
}
// IsEmpty returns true if a NetworkAddressPage contains no addresses.
func (r NetworkAddressPage) IsEmpty() (bool, error) {
addresses, err := ExtractNetworkAddresses(r)
if err != nil {
return true, err
}
return len(addresses) == 0, nil
}
// ExtractNetworkAddresses interprets the results of a single page from a ListAddressesByNetwork() call,
// producing a slice of addresses.
func ExtractNetworkAddresses(page pagination.Page) ([]Address, error) {
casted := page.(NetworkAddressPage).Body
var response map[string][]Address
err := mapstructure.Decode(casted, &response)
if err != nil {
return nil, err
}
var key string
for k := range response {
key = k
}
return response[key], err
}