Merge lp:~allenap/gwacl/set-network-configuration into lp:gwacl
- set-network-configuration
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Julian Edwards |
Approved revision: | 186 |
Merged at revision: | 176 |
Proposed branch: | lp:~allenap/gwacl/set-network-configuration |
Merge into: | lp:gwacl |
Diff against target: |
539 lines (+166/-50) 11 files modified
example/management/run.go (+44/-13) management_base.go (+3/-3) management_base_test.go (+1/-2) names.go (+20/-0) names_test.go (+16/-3) x509dispatcher.go (+8/-7) x509dispatcher_test.go (+27/-11) x509session.go (+3/-3) x509session_test.go (+1/-1) xmlobjects.go (+1/-2) xmlobjects_test.go (+42/-5) |
To merge this branch: | bzr merge lp:~allenap/gwacl/set-network-configuration |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Julian Edwards (community) | Approve | ||
Review via email: mp+174001@code.launchpad.net |
Commit message
Get SetNetworkConfi
Description of the change
This is mostly yak hair.
- Azure doesn't actually like the Label tag in VirtualNetworkSite
elements, even though the example operation docs show it.
- Other sub-elements are optional in the VirtualNetworkSite.
- PUT with the x509 dispatcher didn't work. I added a Request struct
in the tests so that we can capture the request body content, and
this showed up the problem. I fixed that by using the OPT_UPLOAD
flag and requesting chunked transfers.
- PUT requests should also have a content-type, so I added this.
- I added a function for creating a random virtual network name. I
also ensured that all random identifiers have at least 5 random
characters, which gives about a 1 in 60 million chance of a
collision.
- Built on the work Julian did on the example to create an affinity
group and virtual network, then tear it down again. This is a work
in progress; the deployed VMs are not using the virtual network or
affinity group, and the network config gets clobbered. However,
these are tasks for a subsequent branch.
Gavin Panella (allenap) wrote : | # |
Thanks for the review :)
> > - Azure doesn't actually like the Label tag in VirtualNetworkSite
> > elements, even though the example operation docs show it.
>
> Can you file a bug on ***sing please, they asked if we could do this
> for any doc problems.
Filed as bug 1200120.
...
> > === modified file 'management_
> > management_
> > management_
> > @@ { Name: "virtual-
> > "affinity-
> > "label-
> > "CIDR-identifier", }, @@ -944,7 +943,7 @@ Name:
> > "primary-DNS-name", }, }, - Gateway: Gateway{ +
> > Gateway: &Gateway{
>
> Urgh, the fact that we have to work around the Go omitempty bug with
> pointers like is particularly f'in annoying as it leaves our
> structures inconsistently presented.
Indeed :-/
...
> > @@ -201,7 +202,7 @@ if len(request.
> > append(headers, "Content-Type: "+request.
> > for the request body to be written. -
> > ch.Setopt(
> > headers = append(headers, "Transfer-Encoding: chunked")
> > ch.Setopt(
> > int { // Buffered request write. Copy as much of the payload as
> > will // fit into the buffer curl gave us for it, and return the
> > number @@ -242,7 +243,7 @@ case "POST": ch.Setopt(
> > true) case "PUT": - ch.Setopt(
> > "PUT") + ch.Setopt(
>
> Out of interest, where is PUT set now? Does OPT_UPLOAD imply PUT?
> (Sorry, I know bugger all about curl)
Yeah, I just bumbled my way to
http://
...
> > === modified file 'x509dispatcher
...
> > inspection. + requestBody, err := ioutil.
>
> Doesn't this need the NopCloser trick? Or is r.Body still readable later?
r.Body isn't readable in the test; it seems that it gets sucked dry
well before; I suspect that it's something that the ServeMux sorts
out.
Preview Diff
1 | === modified file 'example/management/run.go' |
2 | --- example/management/run.go 2013-07-10 12:49:25 +0000 |
3 | +++ example/management/run.go 2013-07-10 17:10:42 +0000 |
4 | @@ -88,16 +88,6 @@ |
5 | |
6 | func ExerciseHostedServicesAPI(api *gwacl.ManagementAPI) { |
7 | var err error |
8 | - networkConfig, err := api.GetNetworkConfiguration() |
9 | - checkError(err) |
10 | - if networkConfig == nil { |
11 | - fmt.Println("No network configuration is set.") |
12 | - } else { |
13 | - xml, err := networkConfig.Serialize() |
14 | - checkError(err) |
15 | - fmt.Println(xml) |
16 | - } |
17 | - |
18 | location := "West US" |
19 | release := "13.04" |
20 | |
21 | @@ -110,11 +100,52 @@ |
22 | fmt.Printf("Created affinity group %s\n", affinityGroupName) |
23 | |
24 | defer func() { |
25 | - fmt.Println("Deleting affinity group %s", affinityGroupName) |
26 | + fmt.Printf("Deleting affinity group %s\n", affinityGroupName) |
27 | api.DeleteAffinityGroup(&gwacl.DeleteAffinityGroupRequest{ |
28 | Name: affinityGroupName}) |
29 | - fmt.Println("Done deleting affinity group %s", affinityGroupName) |
30 | - }() |
31 | + fmt.Printf("Done deleting affinity group %s\n", affinityGroupName) |
32 | + }() |
33 | + |
34 | + virtualNetworkName := gwacl.MakeRandomVirtualNetworkName("virtual-net-") |
35 | + fmt.Printf("Creating virtual network %s...\n", virtualNetworkName) |
36 | + // FIXME: This clobbers *all* networking configuration instead of just |
37 | + // adding the specific virtual network. |
38 | + vnetRequest := &gwacl.NetworkConfiguration{ |
39 | + XMLNS: gwacl.XMLNS_NC, |
40 | + VirtualNetworkSites: &[]gwacl.VirtualNetworkSite{ |
41 | + { |
42 | + Name: virtualNetworkName, |
43 | + AffinityGroup: affinityGroupName, |
44 | + AddressSpacePrefixes: []string{ |
45 | + "10.0.0.0/8", |
46 | + }, |
47 | + }, |
48 | + }, |
49 | + } |
50 | + err = api.SetNetworkConfiguration(vnetRequest) |
51 | + checkError(err) |
52 | + fmt.Println("Done creating virtual network") |
53 | + |
54 | + defer func() { |
55 | + fmt.Println("Deleting virtual network...") |
56 | + // FIXME: This destroys *all* networking configuration instead of just |
57 | + // removing the specific network we created. |
58 | + api.SetNetworkConfiguration(&gwacl.NetworkConfiguration{ |
59 | + XMLNS: gwacl.XMLNS_NC, |
60 | + VirtualNetworkSites: &[]gwacl.VirtualNetworkSite{}, |
61 | + }) |
62 | + fmt.Println("Done deleting virtual network") |
63 | + }() |
64 | + |
65 | + networkConfig, err := api.GetNetworkConfiguration() |
66 | + checkError(err) |
67 | + if networkConfig == nil { |
68 | + fmt.Println("No network configuration is set") |
69 | + } else { |
70 | + xml, err := networkConfig.Serialize() |
71 | + checkError(err) |
72 | + fmt.Println(xml) |
73 | + } |
74 | |
75 | fmt.Printf("Getting OS Image for release '%s' and location '%s'...\n", release, location) |
76 | images, err := api.ListOSImages() |
77 | |
78 | === modified file 'management_base.go' |
79 | --- management_base.go 2013-07-10 09:12:01 +0000 |
80 | +++ management_base.go 2013-07-10 17:10:42 +0000 |
81 | @@ -131,7 +131,7 @@ |
82 | if err != nil { |
83 | return err |
84 | } |
85 | - response, err := api.session.put(URI, []byte(body)) |
86 | + response, err := api.session.put(URI, []byte(body), "application/xml") |
87 | if err != nil { |
88 | return err |
89 | } |
90 | @@ -388,7 +388,7 @@ |
91 | if err != nil { |
92 | return err |
93 | } |
94 | - response, err := api.session.put(url, []byte(body)) |
95 | + response, err := api.session.put(url, []byte(body), "application/xml") |
96 | if err != nil { |
97 | return err |
98 | } |
99 | @@ -442,7 +442,7 @@ |
100 | if err != nil { |
101 | return err |
102 | } |
103 | - response, err := api.session.put("services/networking/media", []byte(body)) |
104 | + response, err := api.session.put("services/networking/media", []byte(body), "application/octet-stream") |
105 | if err != nil { |
106 | return err |
107 | } |
108 | |
109 | === modified file 'management_base_test.go' |
110 | --- management_base_test.go 2013-07-10 10:08:25 +0000 |
111 | +++ management_base_test.go 2013-07-10 17:10:42 +0000 |
112 | @@ -929,7 +929,6 @@ |
113 | { |
114 | Name: "virtual-network-name", |
115 | AffinityGroup: "affinity-group-name", |
116 | - Label: "label-for-the-site", |
117 | AddressSpacePrefixes: []string{ |
118 | "CIDR-identifier", |
119 | }, |
120 | @@ -944,7 +943,7 @@ |
121 | Name: "primary-DNS-name", |
122 | }, |
123 | }, |
124 | - Gateway: Gateway{ |
125 | + Gateway: &Gateway{ |
126 | Profile: "Small", |
127 | VPNClientAddressPoolPrefixes: []string{ |
128 | "CIDR-identifier", |
129 | |
130 | === modified file 'names.go' |
131 | --- names.go 2013-07-10 12:49:25 +0000 |
132 | +++ names.go 2013-07-10 17:10:42 +0000 |
133 | @@ -18,6 +18,7 @@ |
134 | // consisting of only ASCII digits and lower-case ASCII letters. |
135 | // The identifier will start with the given prefix. The prefix must be no |
136 | // longer than the specified length, or there'll be trouble. |
137 | + |
138 | func makeRandomIdentifier(prefix string, length int) string { |
139 | // Only digits and lower-case ASCII letters are accepted. |
140 | const ( |
141 | @@ -30,6 +31,12 @@ |
142 | panic(fmt.Errorf("prefix '%s' is more than the requested %d characters long", prefix, length)) |
143 | } |
144 | |
145 | + if len(prefix)+5 > length { |
146 | + panic(fmt.Errorf( |
147 | + "prefix '%s' is too long; space is needed for at least 5 random characters, only %d given", |
148 | + prefix, length-len(prefix))) |
149 | + } |
150 | + |
151 | if len(prefix) == 0 { |
152 | // No prefix. Still have to start with a letter, so pick one. |
153 | prefix = pickOne(letters) |
154 | @@ -102,3 +109,16 @@ |
155 | // is. Testing indicate that 50 works. |
156 | return makeRandomIdentifier(prefix, 50) |
157 | } |
158 | + |
159 | +// MakeRandomVirtualNetworkName generates a pseudo-random name for a virtual |
160 | +// network with the given prefix. |
161 | +// |
162 | +// The prefix must be as short as possible, be entirely in ASCII, start with |
163 | +// a lower-case letter, and contain only lower-case letters and digits after |
164 | +// that. |
165 | +// |
166 | +// Be sure your random generator is initialized, or this will always produce |
167 | +// the same sequence! |
168 | +func MakeRandomVirtualNetworkName(prefix string) string { |
169 | + return makeRandomIdentifier(prefix, 20) |
170 | +} |
171 | |
172 | === modified file 'names_test.go' |
173 | --- names_test.go 2013-07-10 12:58:52 +0000 |
174 | +++ names_test.go 2013-07-10 17:10:42 +0000 |
175 | @@ -17,10 +17,19 @@ |
176 | } |
177 | |
178 | func (*namesSuite) TestMakeRandomIdentifierObeysLength(c *C) { |
179 | - length := 1 + rand.Intn(50) |
180 | + length := 6 + rand.Intn(50) |
181 | c.Check(len(makeRandomIdentifier("x", length)), Equals, length) |
182 | } |
183 | |
184 | +// makeRandomIdentifier ensures that there are at least 5 random characters in |
185 | +// an identifier. |
186 | +func (*namesSuite) TestMakeRandomIdentifierEnsuresSomeRandomness(c *C) { |
187 | + c.Check(makeRandomIdentifier("1234-", 10), Matches, "1234-[a-z0-9]{5}") |
188 | + c.Check( |
189 | + func() { makeRandomIdentifier("12345-", 10) }, PanicMatches, |
190 | + "prefix '12345-' is too long; space is needed for at least 5 random characters, only 4 given") |
191 | +} |
192 | + |
193 | func (*namesSuite) TestMakeRandomIdentifierRandomizes(c *C) { |
194 | // There is a minute chance that this will start failing just because |
195 | // the randomizer repeats a pattern of results. If so, seed it. |
196 | @@ -41,7 +50,7 @@ |
197 | } |
198 | |
199 | func (*namesSuite) TestMakeRandomIdentifierUsesPrefix(c *C) { |
200 | - c.Check(makeRandomIdentifier("prefix", 10), Matches, "prefix.*") |
201 | + c.Check(makeRandomIdentifier("prefix", 11), Matches, "prefix.*") |
202 | } |
203 | |
204 | func (*namesSuite) TestMakeRandomIdentifierUsesOnlyAcceptedCharacters(c *C) { |
205 | @@ -50,7 +59,7 @@ |
206 | |
207 | func (*namesSuite) TestMakeRandomIdentifierAcceptsEmptyPrefix(c *C) { |
208 | // In particular, the first character must still be a letter. |
209 | - c.Check(makeRandomIdentifier("", 1), Matches, "[a-z]") |
210 | + c.Check(makeRandomIdentifier("", 5), Matches, "[a-z].*") |
211 | } |
212 | |
213 | func (*namesSuite) TestMakeRandomDiskName(c *C) { |
214 | @@ -60,3 +69,7 @@ |
215 | func (*namesSuite) TestMakeRandomRoleName(c *C) { |
216 | c.Check(MakeRandomRoleName(""), Not(HasLen), 0) |
217 | } |
218 | + |
219 | +func (*namesSuite) TestMakeRandomVirtualNetworkName(c *C) { |
220 | + c.Check(MakeRandomVirtualNetworkName(""), Not(HasLen), 0) |
221 | +} |
222 | |
223 | === modified file 'x509dispatcher.go' |
224 | --- x509dispatcher.go 2013-07-01 13:04:17 +0000 |
225 | +++ x509dispatcher.go 2013-07-10 17:10:42 +0000 |
226 | @@ -61,12 +61,13 @@ |
227 | |
228 | // newX509RequestPUT initializes an X509Request for a PUT. You may still |
229 | // need to set further values. |
230 | -func newX509RequestPUT(url string, payload []byte) *X509Request { |
231 | +func newX509RequestPUT(url string, payload []byte, contentType string) *X509Request { |
232 | return &X509Request{ |
233 | - Method: "PUT", |
234 | - URL: url, |
235 | - APIVersion: baseAPIVersion, |
236 | - Payload: payload, |
237 | + Method: "PUT", |
238 | + URL: url, |
239 | + APIVersion: baseAPIVersion, |
240 | + Payload: payload, |
241 | + ContentType: contentType, |
242 | } |
243 | } |
244 | |
245 | @@ -201,7 +202,7 @@ |
246 | if len(request.Payload) != 0 { |
247 | headers = append(headers, "Content-Type: "+request.ContentType) |
248 | // Arrange for the request body to be written. |
249 | - ch.Setopt(curl.OPT_POSTFIELDSIZE, len(request.Payload)) |
250 | + headers = append(headers, "Transfer-Encoding: chunked") |
251 | ch.Setopt(curl.OPT_READFUNCTION, func(buf []byte, _ interface{}) int { |
252 | // Buffered request write. Copy as much of the payload as will |
253 | // fit into the buffer curl gave us for it, and return the number |
254 | @@ -242,7 +243,7 @@ |
255 | case "POST": |
256 | ch.Setopt(curl.OPT_POST, true) |
257 | case "PUT": |
258 | - ch.Setopt(curl.OPT_CUSTOMREQUEST, "PUT") |
259 | + ch.Setopt(curl.OPT_UPLOAD, true) |
260 | case "DELETE": |
261 | ch.Setopt(curl.OPT_CUSTOMREQUEST, "DELETE") |
262 | default: |
263 | |
264 | === modified file 'x509dispatcher_test.go' |
265 | --- x509dispatcher_test.go 2013-07-02 07:15:53 +0000 |
266 | +++ x509dispatcher_test.go 2013-07-10 17:10:42 +0000 |
267 | @@ -1,6 +1,7 @@ |
268 | package gwacl |
269 | |
270 | import ( |
271 | + "io/ioutil" |
272 | . "launchpad.net/gocheck" |
273 | "net/http" |
274 | "net/http/httptest" |
275 | @@ -10,13 +11,24 @@ |
276 | |
277 | var _ = Suite(&x509DispatcherSuite{}) |
278 | |
279 | +type Request struct { |
280 | + *http.Request |
281 | + BodyContent []byte |
282 | +} |
283 | + |
284 | // makeRecordingHTTPServer creates an http server (don't forget to Close() it when done) |
285 | // that serves at the given base URL, copies incoming requests into the given |
286 | // channel, and finally returns the given status code. If body is not nil, it |
287 | // will be returned as the request body. |
288 | -func makeRecordingHTTPServer(requests chan *http.Request, status int, body []byte, headers http.Header) *httptest.Server { |
289 | +func makeRecordingHTTPServer(requests chan *Request, status int, body []byte, headers http.Header) *httptest.Server { |
290 | returnRequest := func(w http.ResponseWriter, r *http.Request) { |
291 | - requests <- r |
292 | + // Capture all the request body content for later inspection. |
293 | + requestBody, err := ioutil.ReadAll(r.Body) |
294 | + if err != nil { |
295 | + panic(err) |
296 | + } |
297 | + requests <- &Request{r, requestBody} |
298 | + |
299 | for header, values := range headers { |
300 | for _, value := range values { |
301 | w.Header().Set(header, value) |
302 | @@ -59,7 +71,7 @@ |
303 | } |
304 | |
305 | func (*x509DispatcherSuite) TestGetRequestDoesHTTPGET(c *C) { |
306 | - httpRequests := make(chan *http.Request, 1) |
307 | + httpRequests := make(chan *Request, 1) |
308 | server := makeRecordingHTTPServer(httpRequests, http.StatusOK, nil, nil) |
309 | defer server.Close() |
310 | // No real certificate needed since we're testing on http, not https. |
311 | @@ -75,11 +87,12 @@ |
312 | httpRequest := <-httpRequests |
313 | c.Check(httpRequest.Method, Equals, "GET") |
314 | c.Check(httpRequest.URL.String(), Equals, path) |
315 | + c.Check(httpRequest.BodyContent, HasLen, 0) |
316 | } |
317 | |
318 | func (*x509DispatcherSuite) TestGetFollowsRedirects(c *C) { |
319 | redirectPath := "/redirect/path" |
320 | - httpRequests := make(chan *http.Request, _CURL_MAX_REDIRECTS+1) |
321 | + httpRequests := make(chan *Request, _CURL_MAX_REDIRECTS+1) |
322 | locationHeaders := http.Header{} |
323 | locationHeaders.Add("Location", redirectPath) |
324 | server := makeRecordingHTTPServer(httpRequests, http.StatusTemporaryRedirect, nil, locationHeaders) |
325 | @@ -93,7 +106,7 @@ |
326 | _, err = performX509CurlRequest(session, request) |
327 | c.Check(err, ErrorMatches, ".*Number of redirects hit maximum amount.*") |
328 | |
329 | - var httpRequest *http.Request |
330 | + var httpRequest *Request |
331 | // The original GET request has been performed. |
332 | select { |
333 | case httpRequest = <-httpRequests: |
334 | @@ -116,7 +129,7 @@ |
335 | } |
336 | |
337 | func (*x509DispatcherSuite) TestPostRequestDoesHTTPPOST(c *C) { |
338 | - httpRequests := make(chan *http.Request, 1) |
339 | + httpRequests := make(chan *Request, 1) |
340 | requestBody := []byte{1, 2, 3} |
341 | responseBody := []byte{4, 5, 6} |
342 | requestContentType := "bogusContentType" |
343 | @@ -131,17 +144,18 @@ |
344 | response, err := performX509CurlRequest(session, request) |
345 | c.Assert(err, IsNil) |
346 | c.Assert(response.StatusCode, Equals, http.StatusOK) |
347 | + c.Check(response.Body, DeepEquals, responseBody) |
348 | |
349 | httpRequest := <-httpRequests |
350 | c.Check(httpRequest.Header[http.CanonicalHeaderKey("Content-Type")], DeepEquals, []string{requestContentType}) |
351 | c.Check(httpRequest.Header[http.CanonicalHeaderKey("X-Ms-Version")], DeepEquals, []string{request.APIVersion}) |
352 | c.Check(httpRequest.Method, Equals, "POST") |
353 | c.Check(httpRequest.URL.String(), Equals, path) |
354 | - c.Check(response.Body, DeepEquals, responseBody) |
355 | + c.Check(httpRequest.BodyContent, DeepEquals, requestBody) |
356 | } |
357 | |
358 | func (*x509DispatcherSuite) TestDeleteRequestDoesHTTPDELETE(c *C) { |
359 | - httpRequests := make(chan *http.Request, 1) |
360 | + httpRequests := make(chan *Request, 1) |
361 | server := makeRecordingHTTPServer(httpRequests, http.StatusOK, nil, nil) |
362 | defer server.Close() |
363 | // No real certificate needed since we're testing on http, not https. |
364 | @@ -157,10 +171,11 @@ |
365 | httpRequest := <-httpRequests |
366 | c.Check(httpRequest.Method, Equals, "DELETE") |
367 | c.Check(httpRequest.URL.String(), Equals, path) |
368 | + c.Check(httpRequest.BodyContent, HasLen, 0) |
369 | } |
370 | |
371 | func (*x509DispatcherSuite) TestPutRequestDoesHTTPPUT(c *C) { |
372 | - httpRequests := make(chan *http.Request, 1) |
373 | + httpRequests := make(chan *Request, 1) |
374 | requestBody := []byte{1, 2, 3} |
375 | responseBody := []byte{4, 5, 6} |
376 | server := makeRecordingHTTPServer(httpRequests, http.StatusOK, responseBody, nil) |
377 | @@ -169,16 +184,17 @@ |
378 | session, err := newX509Session("subscriptionid", "cert.pem") |
379 | c.Assert(err, IsNil) |
380 | path := "/foo/bar" |
381 | - request := newX509RequestPUT(server.URL+path, requestBody) |
382 | + request := newX509RequestPUT(server.URL+path, requestBody, "application/octet-stream") |
383 | |
384 | response, err := performX509CurlRequest(session, request) |
385 | c.Assert(err, IsNil) |
386 | c.Assert(response.StatusCode, Equals, http.StatusOK) |
387 | + c.Check(response.Body, DeepEquals, responseBody) |
388 | |
389 | httpRequest := <-httpRequests |
390 | c.Check(httpRequest.Method, Equals, "PUT") |
391 | c.Check(httpRequest.URL.String(), Equals, path) |
392 | - c.Check(response.Body, DeepEquals, responseBody) |
393 | + c.Check(httpRequest.BodyContent, DeepEquals, requestBody) |
394 | } |
395 | |
396 | func (*x509DispatcherSuite) TestRequestRegistersHeader(c *C) { |
397 | |
398 | === modified file 'x509session.go' |
399 | --- x509session.go 2013-06-27 11:53:50 +0000 |
400 | +++ x509session.go 2013-07-10 17:10:42 +0000 |
401 | @@ -102,12 +102,12 @@ |
402 | // put performs a PUT request to the Azure management API. |
403 | // Be aware that Azure may perform PUT operations asynchronously. If you are |
404 | // not sure, call blockUntilCompleted() on the response. |
405 | -func (session *x509Session) put(path string, body []byte) (*x509Response, error) { |
406 | - request := newX509RequestPUT(session.composeURL(path), body) |
407 | +func (session *x509Session) put(path string, body []byte, contentType string) (*x509Response, error) { |
408 | + request := newX509RequestPUT(session.composeURL(path), body, contentType) |
409 | response, err := _X509Dispatcher(session, request) |
410 | if err != nil { |
411 | return nil, err |
412 | } |
413 | - err = session.getServerError(response.StatusCode, response.Body, "POST request failed") |
414 | + err = session.getServerError(response.StatusCode, response.Body, "PUT request failed") |
415 | return response, err |
416 | } |
417 | |
418 | === modified file 'x509session_test.go' |
419 | --- x509session_test.go 2013-06-27 10:55:35 +0000 |
420 | +++ x509session_test.go 2013-07-10 17:10:42 +0000 |
421 | @@ -321,7 +321,7 @@ |
422 | recordedRequests := make([]*X509Request, 0) |
423 | rigRecordingDispatcher(&recordedRequests) |
424 | |
425 | - _, err = session.put(uri, requestBody) |
426 | + _, err = session.put(uri, requestBody, "text/plain") |
427 | c.Assert(err, IsNil) |
428 | |
429 | c.Assert(len(recordedRequests), Equals, 1) |
430 | |
431 | === modified file 'xmlobjects.go' |
432 | --- xmlobjects.go 2013-07-10 15:37:52 +0000 |
433 | +++ xmlobjects.go 2013-07-10 17:10:42 +0000 |
434 | @@ -629,11 +629,10 @@ |
435 | XMLName string `xml:"VirtualNetworkSite"` |
436 | Name string `xml:"name,attr"` |
437 | AffinityGroup string `xml:"AffinityGroup,attr"` |
438 | - Label string `xml:"Label"` |
439 | AddressSpacePrefixes []string `xml:"AddressSpace>AddressPrefix"` |
440 | Subnets *[]Subnet `xml:"Subnets>Subnet",omitempty` |
441 | DnsServersRef *[]DnsServerRef `xml:"DnsServersRef>DnsServerRef",omitempty` |
442 | - Gateway Gateway `xml:"Gateway"` |
443 | + Gateway *Gateway `xml:"Gateway",omitempty` |
444 | } |
445 | |
446 | type NetworkConfiguration struct { |
447 | |
448 | === modified file 'xmlobjects_test.go' |
449 | --- xmlobjects_test.go 2013-07-10 15:44:14 +0000 |
450 | +++ xmlobjects_test.go 2013-07-10 17:10:42 +0000 |
451 | @@ -279,7 +279,6 @@ |
452 | </LocalNetworkSites> |
453 | <VirtualNetworkSites> |
454 | <VirtualNetworkSite name="virtual-network-name" AffinityGroup="affinity-group-name"> |
455 | - <Label>label-for-the-site</Label> |
456 | <AddressSpace> |
457 | <AddressPrefix>CIDR-identifier</AddressPrefix> |
458 | </AddressSpace> |
459 | @@ -327,7 +326,6 @@ |
460 | { |
461 | Name: "virtual-network-name", |
462 | AffinityGroup: "affinity-group-name", |
463 | - Label: "label-for-the-site", |
464 | AddressSpacePrefixes: []string{ |
465 | "CIDR-identifier", |
466 | }, |
467 | @@ -342,7 +340,7 @@ |
468 | Name: "primary-DNS-name", |
469 | }, |
470 | }, |
471 | - Gateway: Gateway{ |
472 | + Gateway: &Gateway{ |
473 | Profile: "Small", |
474 | VPNClientAddressPoolPrefixes: []string{ |
475 | "CIDR-identifier", |
476 | @@ -363,6 +361,46 @@ |
477 | c.Assert(observed, Equals, expected) |
478 | } |
479 | |
480 | +func (suite *xmlSuite) TestNetworkConfigurationSerializeMinimal(c *C) { |
481 | + expected := fmt.Sprintf( |
482 | + "\n<NetworkConfiguration xmlns=\"%s\"></NetworkConfiguration>", |
483 | + XMLNS_NC) |
484 | + input := NetworkConfiguration{XMLNS: XMLNS_NC} |
485 | + observed, err := input.Serialize() |
486 | + c.Assert(err, IsNil) |
487 | + c.Assert(observed, Equals, expected) |
488 | +} |
489 | + |
490 | +func (suite *xmlSuite) TestNetworkConfigurationSerializeSimpleVirtualNetworkSite(c *C) { |
491 | + expected := dedent.Dedent(` |
492 | + <NetworkConfiguration xmlns="http://schemas.microsoft.com/ServiceHosting/2011/07/NetworkConfiguration"> |
493 | + <VirtualNetworkConfiguration> |
494 | + <VirtualNetworkSites> |
495 | + <VirtualNetworkSite name="virtual-network-name" AffinityGroup="affinity-group-name"> |
496 | + <AddressSpace> |
497 | + <AddressPrefix>CIDR-identifier</AddressPrefix> |
498 | + </AddressSpace> |
499 | + </VirtualNetworkSite> |
500 | + </VirtualNetworkSites> |
501 | + </VirtualNetworkConfiguration> |
502 | + </NetworkConfiguration>`) |
503 | + input := NetworkConfiguration{ |
504 | + XMLNS: XMLNS_NC, |
505 | + VirtualNetworkSites: &[]VirtualNetworkSite{ |
506 | + { |
507 | + Name: "virtual-network-name", |
508 | + AffinityGroup: "affinity-group-name", |
509 | + AddressSpacePrefixes: []string{ |
510 | + "CIDR-identifier", |
511 | + }, |
512 | + }, |
513 | + }, |
514 | + } |
515 | + observed, err := input.Serialize() |
516 | + c.Assert(err, IsNil) |
517 | + c.Assert(observed, Equals, expected) |
518 | +} |
519 | + |
520 | func (suite *xmlSuite) TestCreateAffinityGroup(c *C) { |
521 | expected := dedent.Dedent(` |
522 | <CreateAffinityGroup xmlns="http://schemas.microsoft.com/windowsazure"> |
523 | @@ -492,7 +530,6 @@ |
524 | { |
525 | Name: "virtual-network-name", |
526 | AffinityGroup: "affinity-group-name", |
527 | - Label: "label-for-the-site", |
528 | AddressSpacePrefixes: []string{ |
529 | "CIDR-identifier", |
530 | }, |
531 | @@ -507,7 +544,7 @@ |
532 | Name: "primary-DNS-name", |
533 | }, |
534 | }, |
535 | - Gateway: Gateway{ |
536 | + Gateway: &Gateway{ |
537 | Profile: "Small", |
538 | VPNClientAddressPoolPrefixes: []string{ |
539 | "CIDR-identifier", |
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
review: approve
merge: approve
Nice change, thank you. I'll land it right away, if there's anything
coming out of my questions below it can get fixed later.
On 11/07/13 03:11, Gavin Panella wrote:
> - Azure doesn't actually like the Label tag in VirtualNetworkSite
> elements, even though the example operation docs show it.
Can you file a bug on ***sing please, they asked if we could do this
for any doc problems.
> - Built on the work Julian did on the example to create an
> affinity group and virtual network, then tear it down again. This
> is a work in progress; the deployed VMs are not using the virtual
> network or affinity group, and the network config gets clobbered.
> However, these are tasks for a subsequent branch.
That's cool, at least it shows the actual API calls work.
> === modified file 'example/ management/ run.go'
Gosh those changes looked familiar :)
> === modified file 'management_ base_test. go' --- base_test. go 2013-07-10 10:08:25 +0000 +++ base_test. go 2013-07-10 17:10:42 +0000 @@ -929,7 +929,6 network- name", AffinityGroup: group-name" , - Label: for-the- site", AddressSpacePre fixes: []string{
> management_
> management_
> @@ { Name: "virtual-
> "affinity-
> "label-
> "CIDR-identifier", }, @@ -944,7 +943,7 @@ Name:
> "primary-DNS-name", }, }, - Gateway: Gateway{ +
> Gateway: &Gateway{
Urgh, the fact that we have to work around the Go omitempty bug with
pointers like is particularly f'in annoying as it leaves our
structures inconsistently presented.
> === modified file 'x509dispatcher.go' --- x509dispatcher.go UT(url UT(url
> 2013-07-01 13:04:17 +0000 +++ x509dispatcher.go 2013-07-10 17:10:42
> +0000 @@ -61,12 +61,13 @@
>
> // newX509RequestPUT initializes an X509Request for a PUT. You may
> still // need to set further values. -func newX509RequestP
> string, payload []byte) *X509Request { +func newX509RequestP
> string, payload []byte, contentType string) *X509Request { return
> &X509Request{ - Method: "PUT", - URL:
> url, - APIVersion: baseAPIVersion, - Payload:
> payload, + Method: "PUT", + URL: url, +
> APIVersion: baseAPIVersion, + Payload: payload, +
> ContentType: contentType,
Sigh, gofmt, SIGH.
> } } Payload) != 0 { headers = ContentType) // Arrange curl.OPT_ POSTFIELDSIZE, len(request. Payload) ) + curl.OPT_ READFUNCTION, func(buf []byte, _ interface{}) curl.OPT_ POST, curl.OPT_ CUSTOMREQUEST, curl.OPT_ UPLOAD, true)
>
> @@ -201,7 +202,7 @@ if len(request.
> append(headers, "Content-Type: "+request.
> for the request body to be written. -
> ch.Setopt(
> headers = append(headers, "Transfer-Encoding: chunked")
> ch.Setopt(
> int { // Buffered request write. Copy as much of the payload as
> will // fit into the buffer curl gave us for it, and return the
> number @@ -242,7 +243,7 @@ case "POST": ch.Setopt(
> true) case "PUT": - ch.Setopt(
> "PUT") + ch.Setopt(
Out of interest, where is PUT set now? Does OPT_UPLOAD imply PUT?
(Sorry, I know bugger all about curl)
> case "DELETE": ch.Setopt( curl.OPT_ CUSTOMREQUEST, "DELETE")
> default:
>
> === modified f...