Merge lp:~mfoord/gomaasapi/devices into lp:gomaasapi
- devices
- Merge into trunk
Proposed by
Michael Foord
Status: | Merged |
---|---|
Approved by: | Michael Foord |
Approved revision: | 95 |
Merged at revision: | 63 |
Proposed branch: | lp:~mfoord/gomaasapi/devices |
Merge into: | lp:gomaasapi |
Diff against target: |
559 lines (+478/-5) 2 files modified
testservice.go (+250/-5) testservice_test.go (+228/-0) |
To merge this branch: | bzr merge lp:~mfoord/gomaasapi/devices |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Raphaël Badin (community) | Approve | ||
Dimiter Naydenov (community) | Approve | ||
Review via email: mp+263370@code.launchpad.net |
Commit message
Adding devices support to gomaasapi test server.
Description of the change
Adding devices support to gomaasapi test server.
To post a comment you must log in.
lp:~mfoord/gomaasapi/devices
updated
- 95. By Michael Foord
-
Updated comments
Revision history for this message
Michael Foord (mfoord) wrote : | # |
> LGTM in general, apart from the one comment inline below, if I wish we had
> logging in places where StatusBadRequest is returned to explain the reason and
> make the test server a bit easier to use.
>
> Also, as discussed on IRC we need support for op=claim_sticky_ip (or was it
> claim-sticky-ip?).
There's no logging in gomaasapi I'm afraid. The rest is now done.
Revision history for this message
Dimiter Naydenov (dimitern) wrote : | # |
Looks good to land, thanks!
review:
Approve
Revision history for this message
Raphaël Badin (rvb) wrote : | # |
Looks good to me as well… lots of TODOs in there but I understand creating a test double is costly and you're only implementing what you need right now.
review:
Approve
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'testservice.go' |
2 | --- testservice.go 2015-06-02 02:28:04 +0000 |
3 | +++ testservice.go 2015-07-02 16:26:32 +0000 |
4 | @@ -5,6 +5,7 @@ |
5 | |
6 | import ( |
7 | "bufio" |
8 | + "bytes" |
9 | "encoding/base64" |
10 | "encoding/json" |
11 | "fmt" |
12 | @@ -18,6 +19,7 @@ |
13 | "sort" |
14 | "strconv" |
15 | "strings" |
16 | + "text/template" |
17 | "time" |
18 | |
19 | "gopkg.in/mgo.v2/bson" |
20 | @@ -85,6 +87,24 @@ |
21 | // nodegroupsInterfaces is a map of nodegroup UUIDs to interface |
22 | // objects. |
23 | nodegroupsInterfaces map[string][]JSONObject |
24 | + |
25 | + // versionJSON is the response to the /version/ endpoint listing the |
26 | + // capabilities of the MAAS server. |
27 | + versionJSON string |
28 | + |
29 | + // devices is a map of device UUIDs to devices. |
30 | + devices map[string]*device |
31 | +} |
32 | + |
33 | +type device struct { |
34 | + IPAddresses []string |
35 | + SystemId string |
36 | + MACAddress string |
37 | + Parent string |
38 | + Hostname string |
39 | + |
40 | + // Not part of the device definition but used by the template. |
41 | + APIVersion string |
42 | } |
43 | |
44 | func getNodesEndpoint(version string) string { |
45 | @@ -100,6 +120,19 @@ |
46 | return regexp.MustCompile(reString) |
47 | } |
48 | |
49 | +func getDevicesEndpoint(version string) string { |
50 | + return fmt.Sprintf("/api/%s/devices/", version) |
51 | +} |
52 | + |
53 | +func getDeviceURL(version, systemId string) string { |
54 | + return fmt.Sprintf("/api/%s/devices/%s/", version, systemId) |
55 | +} |
56 | + |
57 | +func getDeviceURLRE(version string) *regexp.Regexp { |
58 | + reString := fmt.Sprintf("^/api/%s/devices/([^/]*)/$", regexp.QuoteMeta(version)) |
59 | + return regexp.MustCompile(reString) |
60 | +} |
61 | + |
62 | func getFilesEndpoint(version string) string { |
63 | return fmt.Sprintf("/api/%s/files/", version) |
64 | } |
65 | @@ -141,10 +174,6 @@ |
66 | return fmt.Sprintf("/api/%s/version/", version) |
67 | } |
68 | |
69 | -func getVersionJSON() string { |
70 | - return `{"capabilities": ["networks-management","static-ipaddresses"]}` |
71 | -} |
72 | - |
73 | func getNodegroupsEndpoint(version string) string { |
74 | return fmt.Sprintf("/api/%s/nodegroups/", version) |
75 | } |
76 | @@ -185,6 +214,14 @@ |
77 | server.bootImages = make(map[string][]JSONObject) |
78 | server.nodegroupsInterfaces = make(map[string][]JSONObject) |
79 | server.zones = make(map[string]JSONObject) |
80 | + server.versionJSON = `{"capabilities": ["networks-management","static-ipaddresses"]}` |
81 | + server.devices = make(map[string]*device) |
82 | +} |
83 | + |
84 | +// SetVersionJSON sets the JSON response (capabilities) returned from the |
85 | +// /version/ endpoint. |
86 | +func (server *TestServer) SetVersionJSON(json string) { |
87 | + server.versionJSON = json |
88 | } |
89 | |
90 | // NodesOperations returns the list of operations performed at the /nodes/ |
91 | @@ -466,6 +503,11 @@ |
92 | server := &TestServer{version: version} |
93 | |
94 | serveMux := http.NewServeMux() |
95 | + devicesURL := getDevicesEndpoint(server.version) |
96 | + // Register handler for '/api/<version>/devices/*'. |
97 | + serveMux.HandleFunc(devicesURL, func(w http.ResponseWriter, r *http.Request) { |
98 | + devicesHandler(server, w, r) |
99 | + }) |
100 | nodesURL := getNodesEndpoint(server.version) |
101 | // Register handler for '/api/<version>/nodes/*'. |
102 | serveMux.HandleFunc(nodesURL, func(w http.ResponseWriter, r *http.Request) { |
103 | @@ -513,6 +555,209 @@ |
104 | return server |
105 | } |
106 | |
107 | +// devicesHandler handles requests for '/api/<version>/devices/*'. |
108 | +func devicesHandler(server *TestServer, w http.ResponseWriter, r *http.Request) { |
109 | + values, err := url.ParseQuery(r.URL.RawQuery) |
110 | + checkError(err) |
111 | + op := values.Get("op") |
112 | + deviceURLRE := getDeviceURLRE(server.version) |
113 | + deviceURLMatch := deviceURLRE.FindStringSubmatch(r.URL.Path) |
114 | + devicesURL := getDevicesEndpoint(server.version) |
115 | + switch { |
116 | + case r.URL.Path == devicesURL: |
117 | + devicesTopLevelHandler(server, w, r, op) |
118 | + case deviceURLMatch != nil: |
119 | + // Request for a single device. |
120 | + deviceHandler(server, w, r, deviceURLMatch[1], op) |
121 | + default: |
122 | + // Default handler: not found. |
123 | + http.NotFoundHandler().ServeHTTP(w, r) |
124 | + } |
125 | +} |
126 | + |
127 | +// devicesTopLevelHandler handles a request for /api/<version>/devices/ |
128 | +// (with no device id following as part of the path). |
129 | +func devicesTopLevelHandler(server *TestServer, w http.ResponseWriter, r *http.Request, op string) { |
130 | + switch { |
131 | + case r.Method == "GET" && op == "list": |
132 | + // Device listing operation. |
133 | + deviceListingHandler(server, w, r) |
134 | + case r.Method == "POST" && op == "new": |
135 | + newDeviceHandler(server, w, r) |
136 | + default: |
137 | + w.WriteHeader(http.StatusBadRequest) |
138 | + } |
139 | +} |
140 | + |
141 | +func macMatches(device *device, macs []string, hasMac bool) bool { |
142 | + if !hasMac { |
143 | + return true |
144 | + } |
145 | + return contains(macs, device.MACAddress) |
146 | +} |
147 | + |
148 | +// deviceListingHandler handles requests for '/devices/'. |
149 | +func deviceListingHandler(server *TestServer, w http.ResponseWriter, r *http.Request) { |
150 | + values, err := url.ParseQuery(r.URL.RawQuery) |
151 | + checkError(err) |
152 | + // TODO(mfoord): support filtering by hostname and id |
153 | + macs, hasMac := values["mac_address"] |
154 | + var matchedDevices []string |
155 | + for _, device := range server.devices { |
156 | + if macMatches(device, macs, hasMac) { |
157 | + matchedDevices = append(matchedDevices, renderDevice(device)) |
158 | + } |
159 | + } |
160 | + json := fmt.Sprintf("[%v]", strings.Join(matchedDevices, ", ")) |
161 | + |
162 | + w.WriteHeader(http.StatusOK) |
163 | + fmt.Fprint(w, json) |
164 | +} |
165 | + |
166 | +var templateFuncs = template.FuncMap{ |
167 | + "quotedList": func(items []string) string { |
168 | + var pieces []string |
169 | + for _, item := range items { |
170 | + pieces = append(pieces, fmt.Sprintf("%q", item)) |
171 | + } |
172 | + return strings.Join(pieces, ", ") |
173 | + }, |
174 | +} |
175 | + |
176 | +const ( |
177 | + // The json template for generating new devices. |
178 | + // TODO(mfoord): set resource_uri in MAC addresses |
179 | + deviceTemplate = `{ |
180 | + "macaddress_set": [ |
181 | + { |
182 | + "mac_address": "{{.MACAddress}}" |
183 | + } |
184 | + ], |
185 | + "zone": { |
186 | + "resource_uri": "/MAAS/api/{{.APIVersion}}/zones/default/", |
187 | + "name": "default", |
188 | + "description": "" |
189 | + }, |
190 | + "parent": "{{.Parent}}", |
191 | + "ip_addresses": [{{.IPAddresses | quotedList }}], |
192 | + "hostname": "{{.Hostname}}", |
193 | + "tag_names": [], |
194 | + "owner": "maas-admin", |
195 | + "system_id": "{{.SystemId}}", |
196 | + "resource_uri": "/MAAS/api/{{.APIVersion}}/devices/{{.SystemId}}/" |
197 | +}` |
198 | +) |
199 | + |
200 | +func renderDevice(device *device) string { |
201 | + t := template.New("Device template") |
202 | + t = t.Funcs(templateFuncs) |
203 | + t, err := t.Parse(deviceTemplate) |
204 | + checkError(err) |
205 | + var buf bytes.Buffer |
206 | + err = t.Execute(&buf, device) |
207 | + checkError(err) |
208 | + return buf.String() |
209 | +} |
210 | + |
211 | +func getValue(values url.Values, value string) (string, bool) { |
212 | + result, hasResult := values[value] |
213 | + if !hasResult || len(result) != 1 || result[0] == "" { |
214 | + return "", false |
215 | + } |
216 | + return result[0], true |
217 | +} |
218 | + |
219 | +// newDeviceHandler creates, stores and returns new devices. |
220 | +func newDeviceHandler(server *TestServer, w http.ResponseWriter, r *http.Request) { |
221 | + err := r.ParseForm() |
222 | + checkError(err) |
223 | + values := r.PostForm |
224 | + |
225 | + // TODO(mfood): generate a "proper" uuid for the system Id. |
226 | + uuid, err := generateNonce() |
227 | + checkError(err) |
228 | + systemId := fmt.Sprintf("node-%v", uuid) |
229 | + // At least one MAC address must be specified. |
230 | + // TODO(mfoord) we only support a single MAC in the test server. |
231 | + mac, hasMac := getValue(values, "mac_addresses") |
232 | + |
233 | + // hostname and parent are optional. |
234 | + // TODO(mfoord): we require both to be set in the test server. |
235 | + hostname, hasHostname := getValue(values, "hostname") |
236 | + parent, hasParent := getValue(values, "parent") |
237 | + if !hasHostname || !hasMac || !hasParent { |
238 | + w.WriteHeader(http.StatusBadRequest) |
239 | + return |
240 | + } |
241 | + |
242 | + device := &device{ |
243 | + MACAddress: mac, |
244 | + APIVersion: server.version, |
245 | + Parent: parent, |
246 | + Hostname: hostname, |
247 | + SystemId: systemId, |
248 | + } |
249 | + |
250 | + deviceJSON := renderDevice(device) |
251 | + server.devices[systemId] = device |
252 | + |
253 | + w.WriteHeader(http.StatusOK) |
254 | + fmt.Fprint(w, deviceJSON) |
255 | + return |
256 | +} |
257 | + |
258 | +// deviceHandler handles requests for '/api/<version>/devices/<system_id>/'. |
259 | +func deviceHandler(server *TestServer, w http.ResponseWriter, r *http.Request, systemId string, operation string) { |
260 | + device, ok := server.devices[systemId] |
261 | + if !ok { |
262 | + http.NotFoundHandler().ServeHTTP(w, r) |
263 | + return |
264 | + } |
265 | + if r.Method == "GET" { |
266 | + deviceJSON := renderDevice(device) |
267 | + if operation == "" { |
268 | + w.WriteHeader(http.StatusOK) |
269 | + fmt.Fprint(w, deviceJSON) |
270 | + return |
271 | + } else { |
272 | + w.WriteHeader(http.StatusBadRequest) |
273 | + return |
274 | + } |
275 | + } |
276 | + if r.Method == "POST" { |
277 | + if operation == "claim_sticky_ip_address" { |
278 | + err := r.ParseForm() |
279 | + checkError(err) |
280 | + values := r.PostForm |
281 | + // TODO(mfoord): support optional mac_address parameter |
282 | + // TODO(mfoord): requested_address should be optional |
283 | + // and we should generate one if it isn't provided. |
284 | + address, hasAddress := getValue(values, "requested_address") |
285 | + if !hasAddress { |
286 | + w.WriteHeader(http.StatusBadRequest) |
287 | + return |
288 | + } |
289 | + checkError(err) |
290 | + device.IPAddresses = append(device.IPAddresses, address) |
291 | + deviceJSON := renderDevice(device) |
292 | + w.WriteHeader(http.StatusOK) |
293 | + fmt.Fprint(w, deviceJSON) |
294 | + return |
295 | + } else { |
296 | + w.WriteHeader(http.StatusBadRequest) |
297 | + return |
298 | + } |
299 | + } else if r.Method == "DELETE" { |
300 | + delete(server.devices, systemId) |
301 | + w.WriteHeader(http.StatusNoContent) |
302 | + return |
303 | + |
304 | + } |
305 | + |
306 | + // TODO(mfoord): support PUT method for updating device |
307 | + http.NotFoundHandler().ServeHTTP(w, r) |
308 | +} |
309 | + |
310 | // nodesHandler handles requests for '/api/<version>/nodes/*'. |
311 | func nodesHandler(server *TestServer, w http.ResponseWriter, r *http.Request) { |
312 | values, err := url.ParseQuery(r.URL.RawQuery) |
313 | @@ -1188,7 +1433,7 @@ |
314 | } |
315 | w.Header().Set("Content-Type", "application/json; charset=utf-8") |
316 | w.WriteHeader(http.StatusOK) |
317 | - fmt.Fprint(w, getVersionJSON()) |
318 | + fmt.Fprint(w, server.versionJSON) |
319 | } |
320 | |
321 | // nodegroupsHandler handles requests for '/api/<version>/nodegroups/*'. |
322 | |
323 | === modified file 'testservice_test.go' |
324 | --- testservice_test.go 2015-01-13 03:11:30 +0000 |
325 | +++ testservice_test.go 2015-07-02 16:26:32 +0000 |
326 | @@ -49,6 +49,234 @@ |
327 | c.Check(getNodeURL("0.1", "test"), Equals, "/api/0.1/nodes/test/") |
328 | } |
329 | |
330 | +func (suite *TestServerSuite) TestSetVersionJSON(c *C) { |
331 | + capabilities := `{"capabilities": ["networks-management","static-ipaddresses", "devices-management"]}` |
332 | + suite.server.SetVersionJSON(capabilities) |
333 | + |
334 | + url := fmt.Sprintf("/api/%s/version/", suite.server.version) |
335 | + resp, err := http.Get(suite.server.Server.URL + url) |
336 | + c.Assert(err, IsNil) |
337 | + c.Check(resp.StatusCode, Equals, http.StatusOK) |
338 | + content, err := readAndClose(resp.Body) |
339 | + c.Assert(err, IsNil) |
340 | + c.Assert(string(content), Equals, capabilities) |
341 | +} |
342 | + |
343 | +func (suite *TestServerSuite) createDevice(c *C, mac, hostname, parent string) string { |
344 | + devicesURL := fmt.Sprintf("/api/%s/devices/", suite.server.version) + "?op=new" |
345 | + values := url.Values{} |
346 | + values.Add("mac_addresses", mac) |
347 | + values.Add("hostname", hostname) |
348 | + values.Add("parent", parent) |
349 | + result := suite.post(c, devicesURL, values) |
350 | + resultMap, err := result.GetMap() |
351 | + c.Assert(err, IsNil) |
352 | + systemId, err := resultMap["system_id"].GetString() |
353 | + c.Assert(err, IsNil) |
354 | + return systemId |
355 | +} |
356 | + |
357 | +func getString(c *C, object map[string]JSONObject, key string) string { |
358 | + value, err := object[key].GetString() |
359 | + c.Assert(err, IsNil) |
360 | + return value |
361 | +} |
362 | + |
363 | +func (suite *TestServerSuite) post(c *C, url string, values url.Values) JSONObject { |
364 | + resp, err := http.Post(suite.server.Server.URL+url, "application/x-www-form-urlencoded", strings.NewReader(values.Encode())) |
365 | + c.Assert(err, IsNil) |
366 | + c.Check(resp.StatusCode, Equals, http.StatusOK) |
367 | + content, err := readAndClose(resp.Body) |
368 | + c.Assert(err, IsNil) |
369 | + result, err := Parse(suite.server.client, content) |
370 | + c.Assert(err, IsNil) |
371 | + return result |
372 | +} |
373 | + |
374 | +func (suite *TestServerSuite) get(c *C, url string) JSONObject { |
375 | + resp, err := http.Get(suite.server.Server.URL + url) |
376 | + c.Assert(err, IsNil) |
377 | + c.Assert(resp.StatusCode, Equals, http.StatusOK) |
378 | + |
379 | + content, err := readAndClose(resp.Body) |
380 | + c.Assert(err, IsNil) |
381 | + |
382 | + result, err := Parse(suite.server.client, content) |
383 | + c.Assert(err, IsNil) |
384 | + return result |
385 | +} |
386 | + |
387 | +func checkDevice(c *C, device map[string]JSONObject, mac, hostname, parent string) { |
388 | + macArray, err := device["macaddress_set"].GetArray() |
389 | + c.Assert(err, IsNil) |
390 | + c.Assert(macArray, HasLen, 1) |
391 | + macMap, err := macArray[0].GetMap() |
392 | + c.Assert(err, IsNil) |
393 | + |
394 | + actualMac := getString(c, macMap, "mac_address") |
395 | + c.Assert(actualMac, Equals, mac) |
396 | + |
397 | + actualParent := getString(c, device, "parent") |
398 | + c.Assert(actualParent, Equals, parent) |
399 | + actualHostname := getString(c, device, "hostname") |
400 | + c.Assert(actualHostname, Equals, hostname) |
401 | +} |
402 | + |
403 | +func (suite *TestServerSuite) TestNewDeviceRequiredParameters(c *C) { |
404 | + devicesURL := fmt.Sprintf("/api/%s/devices/", suite.server.version) + "?op=new" |
405 | + values := url.Values{} |
406 | + values.Add("mac_addresses", "foo") |
407 | + values.Add("hostname", "bar") |
408 | + post := func(values url.Values) int { |
409 | + resp, err := http.Post(suite.server.Server.URL+devicesURL, "application/x-www-form-urlencoded", strings.NewReader(values.Encode())) |
410 | + c.Assert(err, IsNil) |
411 | + return resp.StatusCode |
412 | + } |
413 | + c.Check(post(values), Equals, http.StatusBadRequest) |
414 | + values.Del("hostname") |
415 | + values.Add("parent", "baz") |
416 | + c.Check(post(values), Equals, http.StatusBadRequest) |
417 | + values.Del("mac_addresses") |
418 | + values.Add("hostname", "bam") |
419 | + c.Check(post(values), Equals, http.StatusBadRequest) |
420 | +} |
421 | + |
422 | +func (suite *TestServerSuite) TestNewDevice(c *C) { |
423 | + devicesURL := fmt.Sprintf("/api/%s/devices/", suite.server.version) + "?op=new" |
424 | + |
425 | + values := url.Values{} |
426 | + values.Add("mac_addresses", "foo") |
427 | + values.Add("hostname", "bar") |
428 | + values.Add("parent", "baz") |
429 | + result := suite.post(c, devicesURL, values) |
430 | + |
431 | + resultMap, err := result.GetMap() |
432 | + c.Assert(err, IsNil) |
433 | + |
434 | + macArray, err := resultMap["macaddress_set"].GetArray() |
435 | + c.Assert(err, IsNil) |
436 | + c.Assert(macArray, HasLen, 1) |
437 | + macMap, err := macArray[0].GetMap() |
438 | + c.Assert(err, IsNil) |
439 | + |
440 | + mac := getString(c, macMap, "mac_address") |
441 | + c.Assert(mac, Equals, "foo") |
442 | + |
443 | + parent := getString(c, resultMap, "parent") |
444 | + c.Assert(parent, Equals, "baz") |
445 | + hostname := getString(c, resultMap, "hostname") |
446 | + c.Assert(hostname, Equals, "bar") |
447 | + |
448 | + addresses, err := resultMap["ip_addresses"].GetArray() |
449 | + c.Assert(err, IsNil) |
450 | + c.Assert(addresses, HasLen, 0) |
451 | + |
452 | + systemId := getString(c, resultMap, "system_id") |
453 | + resourceURI := getString(c, resultMap, "resource_uri") |
454 | + c.Assert(resourceURI, Equals, fmt.Sprintf("/MAAS/api/%v/devices/%v/", suite.server.version, systemId)) |
455 | +} |
456 | + |
457 | +func (suite *TestServerSuite) TestGetDevice(c *C) { |
458 | + systemId := suite.createDevice(c, "foo", "bar", "baz") |
459 | + deviceURL := fmt.Sprintf("/api/%v/devices/%v/", suite.server.version, systemId) |
460 | + |
461 | + result := suite.get(c, deviceURL) |
462 | + resultMap, err := result.GetMap() |
463 | + c.Assert(err, IsNil) |
464 | + checkDevice(c, resultMap, "foo", "bar", "baz") |
465 | + actualId, err := resultMap["system_id"].GetString() |
466 | + c.Assert(actualId, Equals, systemId) |
467 | +} |
468 | + |
469 | +func (suite *TestServerSuite) TestDevicesList(c *C) { |
470 | + firstId := suite.createDevice(c, "foo", "bar", "baz") |
471 | + c.Assert(firstId, Not(Equals), "") |
472 | + secondId := suite.createDevice(c, "bam", "bing", "bong") |
473 | + c.Assert(secondId, Not(Equals), "") |
474 | + |
475 | + devicesURL := fmt.Sprintf("/api/%s/devices/", suite.server.version) + "?op=list" |
476 | + result := suite.get(c, devicesURL) |
477 | + |
478 | + devicesArray, err := result.GetArray() |
479 | + c.Assert(err, IsNil) |
480 | + c.Assert(devicesArray, HasLen, 2) |
481 | + |
482 | + for _, device := range devicesArray { |
483 | + deviceMap, err := device.GetMap() |
484 | + c.Assert(err, IsNil) |
485 | + systemId, err := deviceMap["system_id"].GetString() |
486 | + c.Assert(err, IsNil) |
487 | + switch systemId { |
488 | + case firstId: |
489 | + checkDevice(c, deviceMap, "foo", "bar", "baz") |
490 | + case secondId: |
491 | + checkDevice(c, deviceMap, "bam", "bing", "bong") |
492 | + default: |
493 | + c.Fatalf("unknown system id %q", systemId) |
494 | + } |
495 | + } |
496 | +} |
497 | + |
498 | +func (suite *TestServerSuite) TestDevicesListMacFiltering(c *C) { |
499 | + firstId := suite.createDevice(c, "foo", "bar", "baz") |
500 | + c.Assert(firstId, Not(Equals), "") |
501 | + secondId := suite.createDevice(c, "bam", "bing", "bong") |
502 | + c.Assert(secondId, Not(Equals), "") |
503 | + |
504 | + op := fmt.Sprintf("?op=list&mac_address=%v", "foo") |
505 | + devicesURL := fmt.Sprintf("/api/%s/devices/", suite.server.version) + op |
506 | + result := suite.get(c, devicesURL) |
507 | + |
508 | + devicesArray, err := result.GetArray() |
509 | + c.Assert(err, IsNil) |
510 | + c.Assert(devicesArray, HasLen, 1) |
511 | + deviceMap, err := devicesArray[0].GetMap() |
512 | + c.Assert(err, IsNil) |
513 | + checkDevice(c, deviceMap, "foo", "bar", "baz") |
514 | +} |
515 | + |
516 | +func (suite *TestServerSuite) TestDeviceClaimStickyIPRequiresAddress(c *C) { |
517 | + systemId := suite.createDevice(c, "foo", "bar", "baz") |
518 | + op := "?op=claim_sticky_ip_address" |
519 | + deviceURL := fmt.Sprintf("/api/%s/devices/%s/%s", suite.server.version, systemId, op) |
520 | + values := url.Values{} |
521 | + resp, err := http.Post(suite.server.Server.URL+deviceURL, "application/x-www-form-urlencoded", strings.NewReader(values.Encode())) |
522 | + c.Assert(err, IsNil) |
523 | + c.Assert(resp.StatusCode, Equals, http.StatusBadRequest) |
524 | +} |
525 | + |
526 | +func (suite *TestServerSuite) TestDeviceClaimStickyIP(c *C) { |
527 | + systemId := suite.createDevice(c, "foo", "bar", "baz") |
528 | + op := "?op=claim_sticky_ip_address" |
529 | + deviceURL := fmt.Sprintf("/api/%s/devices/%s/", suite.server.version, systemId) |
530 | + values := url.Values{} |
531 | + values.Add("requested_address", "127.0.0.1") |
532 | + result := suite.post(c, deviceURL+op, values) |
533 | + resultMap, err := result.GetMap() |
534 | + c.Assert(err, IsNil) |
535 | + |
536 | + addresses, err := resultMap["ip_addresses"].GetArray() |
537 | + c.Assert(err, IsNil) |
538 | + c.Assert(addresses, HasLen, 1) |
539 | + address, err := addresses[0].GetString() |
540 | + c.Assert(err, IsNil) |
541 | + c.Assert(address, Equals, "127.0.0.1") |
542 | +} |
543 | + |
544 | +func (suite *TestServerSuite) TestDeleteDevice(c *C) { |
545 | + systemId := suite.createDevice(c, "foo", "bar", "baz") |
546 | + deviceURL := fmt.Sprintf("/api/%s/devices/%s/", suite.server.version, systemId) |
547 | + req, err := http.NewRequest("DELETE", suite.server.Server.URL+deviceURL, nil) |
548 | + c.Assert(err, IsNil) |
549 | + resp, err := http.DefaultClient.Do(req) |
550 | + c.Assert(err, IsNil) |
551 | + c.Assert(resp.StatusCode, Equals, http.StatusNoContent) |
552 | + |
553 | + resp, err = http.Get(suite.server.Server.URL + deviceURL) |
554 | + c.Assert(err, IsNil) |
555 | + c.Assert(resp.StatusCode, Equals, http.StatusNotFound) |
556 | +} |
557 | + |
558 | func (suite *TestServerSuite) TestInvalidOperationOnNodesIsBadRequest(c *C) { |
559 | badURL := getNodesEndpoint(suite.server.version) + "?op=procrastinate" |
560 |
LGTM in general, apart from the one comment inline below, if I wish we had logging in places where StatusBadRequest is returned to explain the reason and make the test server a bit easier to use.
Also, as discussed on IRC we need support for op=claim_sticky_ip (or was it claim-sticky-ip?).