Merge lp:~rvb/gomaasapi/record-action-params into lp:gomaasapi

Proposed by Raphaël Badin
Status: Merged
Approved by: Raphaël Badin
Approved revision: 40
Merged at revision: 39
Proposed branch: lp:~rvb/gomaasapi/record-action-params
Merge into: lp:gomaasapi
Diff against target: 137 lines (+59/-11)
2 files modified
testservice.go (+39/-8)
testservice_test.go (+20/-3)
To merge this branch: bzr merge lp:~rvb/gomaasapi/record-action-params
Reviewer Review Type Date Requested Status
Gavin Panella (community) Approve
Review via email: mp+158117@code.launchpad.net

Commit message

Make the testservice record the url.Values posted when performing an operation (e.g. starting a node).

Description of the change

I chose to add another field named "nodeOperationRequestValues" instead of making the existing one (TestServer.nodeOperations) more complex because the new structure would have been a pain to manipulate in Go.

The main reason behind that change is so that we can check that the provider sends the correct "user data" when starting a node: https://code.launchpad.net/~rvb/juju-core/fix-test-start-instance/+merge/158116.

To post a comment you must log in.
39. By Raphaël Badin

Add check.

Revision history for this message
Gavin Panella (allenap) wrote :

[1]

+ nodeOperationRequestValues map[string][]url.Values

This needs a comment to explain what the string is meant to denote. Or
a type alias could be used (I guess; Go's probably got some gotcha up
it's sleeve to make that awkward).

[2]

+func (server *TestServer) NodeOperationRequestValues() map[string][]url.Values {
+ return server.nodeOperationRequestValues
+}

Why not make the field public in the first place?

If you want to ensure that this field is read-only field, then maybe
this'll work: it'll return by value (?) and so copy the returned map
(?), but will it copy the values in the map or references? In other
words, will the values be shared?

I think I hate pass by value.

[3]

+ operationRequestValues, _ := server.nodeOperationRequestValues[systemId]

Can you add a comment explaining why the second return value is being
ignored? I think I get it from reading the code (the `present` value
from the preceding line is sufficient) but it would be worth helping
future devs out.

[4]

+ if request.Body != nil {
...
+ requestValues, err = url.ParseQuery(string(body))

The condition here should be that there's a body *and* that the
Content-Type is application/x-www-form-encoded.

(I think the test service ought to be strict about what it accepts.)

[5]

+ nodeOperationRequestValues map[string][]url.Values

This expands to:

 nodeOperationRequestValues map[string][]map[string][]string

Which, having read around a bit, reads as:

  A mapping of system IDs to lists of mappings of query parameters.

Or, in Python syntax:

  {"sys1": [
      {"param1": ["value1", "value2"], "param2": ["value3"]},
      {...}
      ],
   "sys2": [
       ...
       ],
   ...
  }

Is that right?

review: Needs Information
40. By Raphaël Badin

Check request's content-type.

Revision history for this message
Raphaël Badin (rvb) wrote :

> [1]

Done.

>
>
> [2]
>
> +func (server *TestServer) NodeOperationRequestValues()
> map[string][]url.Values {
> + return server.nodeOperationRequestValues
> +}
>
> Why not make the field public in the first place?
>
> If you want to ensure that this field is read-only field, then maybe
> this'll work: it'll return by value (?) and so copy the returned map
> (?), but will it copy the values in the map or references? In other
> words, will the values be shared?

It's not really a matter of making the field read-only (remember this is only the testservice code), I just want us to be able to change the underlying implementation without breaking the existing code.

> [3]
>
> + operationRequestValues, _ :=
> server.nodeOperationRequestValues[systemId]
>
> Can you add a comment explaining why the second return value is being
> ignored? I think I get it from reading the code (the `present` value
> from the preceding line is sufficient) but it would be worth helping
> future devs out.

That's the reason indeed. I've changed the code so that it will panic if the two variables (present and present2) don't have the same value.

> [4]
>
> + if request.Body != nil {
> ...
> + requestValues, err = url.ParseQuery(string(body))
>
> The condition here should be that there's a body *and* that the
> Content-Type is application/x-www-form-encoded.
>
> (I think the test service ought to be strict about what it accepts.)

Good point, done.

> [5]
>
> + nodeOperationRequestValues map[string][]url.Values
>
> This expands to:
>
> nodeOperationRequestValues map[string][]map[string][]string
>
> Which, having read around a bit, reads as:
>
>  A mapping of system IDs to lists of mappings of query parameters.
>
> Or, in Python syntax:
>
>  {"sys1": [
>      {"param1": ["value1", "value2"], "param2": ["value3"]},
>      {...}
>      ],
>   "sys2": [
>       ...
>       ],
>   ...
>  }
>
> Is that right?

That's right.

Revision history for this message
Gavin Panella (allenap) :
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 2013-04-10 01:36:19 +0000
3+++ testservice.go 2013-04-11 07:31:23 +0000
4@@ -53,13 +53,17 @@
5 // library.
6 type TestServer struct {
7 *httptest.Server
8- serveMux *http.ServeMux
9- client Client
10- nodes map[string]MAASObject
11- ownedNodes map[string]bool
12+ serveMux *http.ServeMux
13+ client Client
14+ nodes map[string]MAASObject
15+ ownedNodes map[string]bool
16+ // mapping system_id -> list of operations performed.
17 nodeOperations map[string][]string
18- files map[string]MAASObject
19- version string
20+ // mapping system_id -> list of Values passed when performing
21+ // operations
22+ nodeOperationRequestValues map[string][]url.Values
23+ files map[string]MAASObject
24+ version string
25 }
26
27 func getNodeURI(version, systemId string) string {
28@@ -78,6 +82,7 @@
29 server.nodes = make(map[string]MAASObject)
30 server.ownedNodes = make(map[string]bool)
31 server.nodeOperations = make(map[string][]string)
32+ server.nodeOperationRequestValues = make(map[string][]url.Values)
33 server.files = make(map[string]MAASObject)
34 }
35
36@@ -87,14 +92,40 @@
37 return server.nodeOperations
38 }
39
40-func (server *TestServer) addNodeOperation(systemId, operation string) {
41+// NodeOperationRequestValues returns the map containing the list of the
42+// url.Values extracted from the request used when performing operations
43+// on nodes.
44+func (server *TestServer) NodeOperationRequestValues() map[string][]url.Values {
45+ return server.nodeOperationRequestValues
46+}
47+
48+func (server *TestServer) addNodeOperation(systemId, operation string, request *http.Request) {
49 operations, present := server.nodeOperations[systemId]
50+ operationRequestValues, present2 := server.nodeOperationRequestValues[systemId]
51+ if present != present2 {
52+ panic("inconsistent state: nodeOperations and nodeOperationRequestValues don't have the same keys.")
53+ }
54+ requestValues := url.Values{}
55+ if request.Body != nil && request.Header.Get("Content-Type") == "application/x-www-form-urlencoded" {
56+ defer request.Body.Close()
57+ body, err := ioutil.ReadAll(request.Body)
58+ if err != nil {
59+ panic(err)
60+ }
61+ requestValues, err = url.ParseQuery(string(body))
62+ if err != nil {
63+ panic(err)
64+ }
65+ }
66 if !present {
67 operations = []string{operation}
68+ operationRequestValues = []url.Values{requestValues}
69 } else {
70 operations = append(operations, operation)
71+ operationRequestValues = append(operationRequestValues, requestValues)
72 }
73 server.nodeOperations[systemId] = operations
74+ server.nodeOperationRequestValues[systemId] = operationRequestValues
75 }
76
77 // NewNode creates a MAAS node. The provided string should be a valid json
78@@ -245,7 +276,7 @@
79 // The only operations supported are "start", "stop" and "release".
80 if operation == "start" || operation == "stop" || operation == "release" {
81 // Record operation on node.
82- server.addNodeOperation(systemId, operation)
83+ server.addNodeOperation(systemId, operation, r)
84
85 if operation == "release" {
86 delete(server.OwnedNodes(), systemId)
87
88=== modified file 'testservice_test.go'
89--- testservice_test.go 2013-03-12 16:27:02 +0000
90+++ testservice_test.go 2013-04-11 07:31:23 +0000
91@@ -97,26 +97,43 @@
92 func (suite *TestServerSuite) TestClearClearsData(c *C) {
93 input := `{"system_id": "mysystemid"}`
94 suite.server.NewNode(input)
95- suite.server.addNodeOperation("mysystemid", "start")
96+ suite.server.addNodeOperation("mysystemid", "start", &http.Request{})
97
98 suite.server.Clear()
99
100 c.Check(len(suite.server.nodes), Equals, 0)
101 c.Check(len(suite.server.nodeOperations), Equals, 0)
102+ c.Check(len(suite.server.nodeOperationRequestValues), Equals, 0)
103 }
104
105 func (suite *TestServerSuite) TestAddNodeOperationPopulatesOperations(c *C) {
106 input := `{"system_id": "mysystemid"}`
107 suite.server.NewNode(input)
108
109- suite.server.addNodeOperation("mysystemid", "start")
110- suite.server.addNodeOperation("mysystemid", "stop")
111+ suite.server.addNodeOperation("mysystemid", "start", &http.Request{})
112+ suite.server.addNodeOperation("mysystemid", "stop", &http.Request{})
113
114 nodeOperations := suite.server.NodeOperations()
115 operations := nodeOperations["mysystemid"]
116 c.Check(operations, DeepEquals, []string{"start", "stop"})
117 }
118
119+func (suite *TestServerSuite) TestAddNodeOperationPopulatesOperationRequestValues(c *C) {
120+ input := `{"system_id": "mysystemid"}`
121+ suite.server.NewNode(input)
122+ reader := strings.NewReader("key=value")
123+ request, err := http.NewRequest("POST", "http://example.com/", reader)
124+ request.Header.Set("Content-Type", "application/x-www-form-urlencoded")
125+ c.Assert(err, IsNil)
126+
127+ suite.server.addNodeOperation("mysystemid", "start", request)
128+
129+ values := suite.server.NodeOperationRequestValues()
130+ value := values["mysystemid"]
131+ c.Check(len(value), Equals, 1)
132+ c.Check(value[0], DeepEquals, url.Values{"key": []string{"value"}})
133+}
134+
135 func (suite *TestServerSuite) TestNewNodeRequiresJSONString(c *C) {
136 input := `invalid:json`
137 defer func() {

Subscribers

People subscribed via source and target branches

to all changes: