Merge lp:~rvb/gwacl/not-found-errors into lp:gwacl
- not-found-errors
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Raphaël Badin |
Approved revision: | 124 |
Merged at revision: | 124 |
Proposed branch: | lp:~rvb/gwacl/not-found-errors |
Merge into: | lp:gwacl |
Diff against target: |
674 lines (+236/-119) 4 files modified
httperror.go (+34/-0) httperror_test.go (+38/-0) storage_base.go (+21/-10) storage_base_test.go (+143/-109) |
To merge this branch: | bzr merge lp:~rvb/gwacl/not-found-errors |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Gavin Panella | Approve | ||
Review via email: mp+170792@code.launchpad.net |
Commit message
Propagate Azure errors, add IsNotFoundError() method.
Description of the change
This branch does two things:
- it adds two utility methods:
- extendError(). This method is used to extend the message of different types of error (generic errors and ServerError errors). The goal is to be able to extend errors in a generic fashion without losing the extra information that a more specialized error might carry.
- IsNotFoundError(). This method is a tiny utility that a third party application might use when getting an error back from gwacl to determine if the underlining Azure error is a 404 error. This is needed in the provider and I figured it would be better to provide a utility method rather than to let the caller do the casting and the comparison with a gwacl-specific redefinition of an HTTPStatus (with we have this redefinition, I don't know).
- it use the extendError() method in the methods from storage_base.go to propagate Azure-related error instead of hiding this from the caller.
One more thing: adding makeHttpResponse() is a small improvement I made to the code since creating an http.Response was done over and over again in the test code. It hads a bit of noise to the diff but once I got started and thought "hum, could I land this as a pre-req?" it was already too late… besides, the pipeline plugin does not play well with colocated branches.
This was pre-imp'ed with Jeroen.
Raphaël Badin (rvb) wrote : | # |
> Looks okay, but I want you to consider [1] before I approve.
This looks much too big to be considered *before* this lands to be honest. An interesting idea but clearly outside the scope of this branch; the good thing about this branch is that all the extendError(
A few things about the error chain idea:
- I like the general idea although I'm afraid that it might be flagged as non-idiomatic (whatever that means) because it's basically a slightly awkward implementation of a dynamic traceback being built manually :).
- Such a generic pattern, if a complete analysis prove that it's worth it, should probably live in its own project so that many projects can benefit from it.
Gavin Panella (allenap) wrote : | # |
> > Looks okay, but I want you to consider [1] before I approve.
>
> This looks much too big to be considered *before* this lands to be honest.
Yeah, I guess it's *instead* of parts of this branch. Things like
IsNotFoundError can be reimplemented to use this quite easily.
> An
> interesting idea but clearly outside the scope of this branch; the good thing
> about this branch is that all the extendError(
> well encapsulated so we could move to something like what you describe in the
> future.
I'm not sure it's clearly outside of scope, but okay.
> A few things about the error chain idea:
> - I like the general idea although I'm afraid that it might be flagged as non-
> idiomatic (whatever that means) because it's basically a slightly awkward
> implementation of a dynamic traceback being built manually :).
We're the GWACL maintainers, so we get to say what's idiomatic. If
smooshing strings together for errors is truly what the Go mainstream
call idiomatic then I'm happy to be a freak. I don't think it is
though. They like tracebacks too, they're just having some cognitive
dissonance issues between tracebacks with panic (or what a duck would
call exceptions) and the dogmatic checking and passing of errors by
hand.
> - Such a generic pattern, if a complete analysis prove that it's worth it,
> should probably live in its own project so that many projects can benefit from
> it.
It's only 39 lines ;)
It might be generically useful, but everything has to start somewhere.
Putting it in a sub-package or just a new file is enough to get going.
Raphaël Badin (rvb) wrote : | # |
> Yeah, I guess it's *instead* of parts of this branch. Things like
> IsNotFoundError can be reimplemented to use this quite easily.
> It might be generically useful, but everything has to start somewhere.
> Putting it in a sub-package or just a new file is enough to get going.
Sounds like a good plan. Thanks for unblocking me… I'm happy to think/talk some more about this idea next week.
Preview Diff
1 | === modified file 'httperror.go' | |||
2 | --- httperror.go 2013-04-02 08:51:00 +0000 | |||
3 | +++ httperror.go 2013-06-21 11:47:27 +0000 | |||
4 | @@ -90,3 +90,37 @@ | |||
5 | 90 | Message: outcome.ErrorMessage, | 90 | Message: outcome.ErrorMessage, |
6 | 91 | } | 91 | } |
7 | 92 | } | 92 | } |
8 | 93 | |||
9 | 94 | // extendError returns an error whos description is the concatenation of | ||
10 | 95 | // the given message plus the error string from the original error. | ||
11 | 96 | // It preserves the value of the error types it knows about (currently only | ||
12 | 97 | // ServerError). | ||
13 | 98 | // | ||
14 | 99 | // The main purpose of this method is to offer a unified way to | ||
15 | 100 | // extend the information present in errors while still not losing the | ||
16 | 101 | // additioning information present on specific errors gwacl knows out to extend | ||
17 | 102 | // in a more meaningful way. | ||
18 | 103 | func extendError(err error, message string) error { | ||
19 | 104 | switch err := err.(type) { | ||
20 | 105 | case *ServerError: | ||
21 | 106 | returnedErr := *err | ||
22 | 107 | returnedErr.error = fmt.Errorf(message+"%v", err.error) | ||
23 | 108 | return &returnedErr | ||
24 | 109 | default: | ||
25 | 110 | return fmt.Errorf(message+"%v", err) | ||
26 | 111 | } | ||
27 | 112 | // This code cannot be reached but Go insists on having a return or a panic | ||
28 | 113 | // statement at the end of this method. Sigh. | ||
29 | 114 | panic("invalid extendError state!") | ||
30 | 115 | } | ||
31 | 116 | |||
32 | 117 | // IsNotFoundError returns whether or not the given error is an error (as | ||
33 | 118 | // returned by a gwacl method) which corresponds to a 'Not Found' error | ||
34 | 119 | // returned by Windows Azure. | ||
35 | 120 | func IsNotFoundError(err error) bool { | ||
36 | 121 | serverError, ok := err.(*ServerError) | ||
37 | 122 | if !ok { | ||
38 | 123 | return false | ||
39 | 124 | } | ||
40 | 125 | return serverError.HTTPStatus.StatusCode() == http.StatusNotFound | ||
41 | 126 | } | ||
42 | 93 | 127 | ||
43 | === modified file 'httperror_test.go' | |||
44 | --- httperror_test.go 2013-04-02 06:49:31 +0000 | |||
45 | +++ httperror_test.go 2013-06-21 11:47:27 +0000 | |||
46 | @@ -94,3 +94,41 @@ | |||
47 | 94 | c.Check(err.Code, Equals, code) | 94 | c.Check(err.Code, Equals, code) |
48 | 95 | c.Check(err.Message, Equals, message) | 95 | c.Check(err.Message, Equals, message) |
49 | 96 | } | 96 | } |
50 | 97 | |||
51 | 98 | func (suite *httpErrorSuite) TestExtendErrorExtendsGenericError(c *C) { | ||
52 | 99 | errorString := "an-error" | ||
53 | 100 | error := fmt.Errorf(errorString) | ||
54 | 101 | additionalErrorMsg := "additional message" | ||
55 | 102 | newError := extendError(error, additionalErrorMsg) | ||
56 | 103 | c.Check(newError.Error(), Equals, fmt.Sprintf("%s%s", additionalErrorMsg, error.Error())) | ||
57 | 104 | } | ||
58 | 105 | |||
59 | 106 | func (suite *httpErrorSuite) TestExtendErrorExtendsServerError(c *C) { | ||
60 | 107 | description := "could not talk to server" | ||
61 | 108 | status := http.StatusGatewayTimeout | ||
62 | 109 | httpErr := newHTTPError(status, []byte{}, description) | ||
63 | 110 | |||
64 | 111 | httpServerErr, ok := httpErr.(*ServerError) | ||
65 | 112 | c.Assert(ok, Equals, true) | ||
66 | 113 | additionalErrorMsg := "additional message" | ||
67 | 114 | newError := extendError(httpErr, additionalErrorMsg) | ||
68 | 115 | newServerError, ok := httpErr.(*ServerError) | ||
69 | 116 | c.Assert(ok, Equals, true) | ||
70 | 117 | c.Check(newError.Error(), Equals, fmt.Sprintf("%s%s", additionalErrorMsg, httpErr.Error())) | ||
71 | 118 | c.Check(httpServerErr.HTTPStatus, Equals, newServerError.HTTPStatus) | ||
72 | 119 | } | ||
73 | 120 | |||
74 | 121 | func (suite *httpErrorSuite) TestIsNotFound(c *C) { | ||
75 | 122 | var testValues = []struct { | ||
76 | 123 | err error | ||
77 | 124 | expectedResult bool | ||
78 | 125 | }{ | ||
79 | 126 | {fmt.Errorf("generic error"), false}, | ||
80 | 127 | {newHTTPError(http.StatusGatewayTimeout, []byte{}, "error"), false}, | ||
81 | 128 | {newHTTPError(http.StatusOK, []byte{}, ""), false}, | ||
82 | 129 | {newHTTPError(http.StatusNotFound, []byte{}, "error"), true}, | ||
83 | 130 | } | ||
84 | 131 | for _, test := range testValues { | ||
85 | 132 | c.Check(IsNotFoundError(test.err), Equals, test.expectedResult) | ||
86 | 133 | } | ||
87 | 134 | } | ||
88 | 97 | 135 | ||
89 | === modified file 'storage_base.go' | |||
90 | --- storage_base.go 2013-06-21 01:01:35 +0000 | |||
91 | +++ storage_base.go 2013-06-21 11:47:27 +0000 | |||
92 | @@ -356,7 +356,8 @@ | |||
93 | 356 | ExpectedStatus: http.StatusOK, | 356 | ExpectedStatus: http.StatusOK, |
94 | 357 | }) | 357 | }) |
95 | 358 | if err != nil { | 358 | if err != nil { |
97 | 359 | return nil, fmt.Errorf("request for containers list failed: %v", err) | 359 | msg := "request for containers list failed: " |
98 | 360 | return nil, extendError(err, msg) | ||
99 | 360 | } | 361 | } |
100 | 361 | return &containers, nil | 362 | return &containers, nil |
101 | 362 | } | 363 | } |
102 | @@ -392,7 +393,8 @@ | |||
103 | 392 | ExpectedStatus: http.StatusOK, | 393 | ExpectedStatus: http.StatusOK, |
104 | 393 | }) | 394 | }) |
105 | 394 | if err != nil { | 395 | if err != nil { |
107 | 395 | return nil, fmt.Errorf("request for blobs list failed: %v", err) | 396 | msg := "request for blobs list failed: " |
108 | 397 | return nil, extendError(err, msg) | ||
109 | 396 | } | 398 | } |
110 | 397 | return &blobs, err | 399 | return &blobs, err |
111 | 398 | } | 400 | } |
112 | @@ -410,7 +412,8 @@ | |||
113 | 410 | ExpectedStatus: http.StatusCreated, | 412 | ExpectedStatus: http.StatusCreated, |
114 | 411 | }) | 413 | }) |
115 | 412 | if err != nil { | 414 | if err != nil { |
117 | 413 | return fmt.Errorf("failed to create container %s: %v", container, err) | 415 | msg := fmt.Sprintf("failed to create container %s: ", container) |
118 | 416 | return extendError(err, msg) | ||
119 | 414 | } | 417 | } |
120 | 415 | return nil | 418 | return nil |
121 | 416 | } | 419 | } |
122 | @@ -453,7 +456,8 @@ | |||
123 | 453 | ExpectedStatus: http.StatusCreated, | 456 | ExpectedStatus: http.StatusCreated, |
124 | 454 | }) | 457 | }) |
125 | 455 | if err != nil { | 458 | if err != nil { |
127 | 456 | return fmt.Errorf("failed to create blob %s: %v", req.Filename, err) | 459 | msg := fmt.Sprintf("failed to create blob %s: ", req.Filename) |
128 | 460 | return extendError(err, msg) | ||
129 | 457 | } | 461 | } |
130 | 458 | return nil | 462 | return nil |
131 | 459 | } | 463 | } |
132 | @@ -488,7 +492,8 @@ | |||
133 | 488 | ExpectedStatus: http.StatusCreated, | 492 | ExpectedStatus: http.StatusCreated, |
134 | 489 | }) | 493 | }) |
135 | 490 | if err != nil { | 494 | if err != nil { |
137 | 491 | return fmt.Errorf("failed to put page for file %s: %v", req.Filename, err) | 495 | msg := fmt.Sprintf("failed to put page for file %s: ", req.Filename) |
138 | 496 | return extendError(err, msg) | ||
139 | 492 | } | 497 | } |
140 | 493 | return nil | 498 | return nil |
141 | 494 | } | 499 | } |
142 | @@ -509,7 +514,8 @@ | |||
143 | 509 | ExpectedStatus: http.StatusOK, | 514 | ExpectedStatus: http.StatusOK, |
144 | 510 | }) | 515 | }) |
145 | 511 | if err != nil { | 516 | if err != nil { |
147 | 512 | return nil, fmt.Errorf("request for block list in file %s failed: %v", filename, err) | 517 | msg := fmt.Sprintf("request for block list in file %s failed: ", filename) |
148 | 518 | return nil, extendError(err, msg) | ||
149 | 513 | } | 519 | } |
150 | 514 | return &bl, nil | 520 | return &bl, nil |
151 | 515 | } | 521 | } |
152 | @@ -530,7 +536,8 @@ | |||
153 | 530 | ExpectedStatus: http.StatusCreated, | 536 | ExpectedStatus: http.StatusCreated, |
154 | 531 | }) | 537 | }) |
155 | 532 | if err != nil { | 538 | if err != nil { |
157 | 533 | return fmt.Errorf("failed to put block %s for file %s: %v", id, filename, err) | 539 | msg := fmt.Sprintf("failed to put block %s for file %s: ", id, filename) |
158 | 540 | return extendError(err, msg) | ||
159 | 534 | } | 541 | } |
160 | 535 | return nil | 542 | return nil |
161 | 536 | } | 543 | } |
162 | @@ -554,7 +561,8 @@ | |||
163 | 554 | ExpectedStatus: http.StatusCreated, | 561 | ExpectedStatus: http.StatusCreated, |
164 | 555 | }) | 562 | }) |
165 | 556 | if err != nil { | 563 | if err != nil { |
167 | 557 | return fmt.Errorf("failed to put blocklist for file %s: %v", filename, err) | 564 | msg := fmt.Sprintf("failed to put blocklist for file %s: ", filename) |
168 | 565 | return extendError(err, msg) | ||
169 | 558 | } | 566 | } |
170 | 559 | return nil | 567 | return nil |
171 | 560 | } | 568 | } |
172 | @@ -568,8 +576,10 @@ | |||
173 | 568 | ExpectedStatus: http.StatusAccepted, | 576 | ExpectedStatus: http.StatusAccepted, |
174 | 569 | }) | 577 | }) |
175 | 570 | // TODO Handle a 404 with an <Error>BlobNotFound response body silently. | 578 | // TODO Handle a 404 with an <Error>BlobNotFound response body silently. |
176 | 579 | // Now this is easy to fix with the method IsNotFoundError(). | ||
177 | 571 | if err != nil { | 580 | if err != nil { |
179 | 572 | return fmt.Errorf("failed to delete blob %s: %v", filename, err) | 581 | msg := fmt.Sprintf("failed to delete blob %s: ", filename) |
180 | 582 | return extendError(err, msg) | ||
181 | 573 | } | 583 | } |
182 | 574 | return nil | 584 | return nil |
183 | 575 | } | 585 | } |
184 | @@ -583,7 +593,8 @@ | |||
185 | 583 | ExpectedStatus: http.StatusOK, | 593 | ExpectedStatus: http.StatusOK, |
186 | 584 | }) | 594 | }) |
187 | 585 | if err != nil { | 595 | if err != nil { |
189 | 586 | return nil, fmt.Errorf("failed to get blob %s: %v", filename, err) | 596 | msg := fmt.Sprintf("failed to get blob %s: ", filename) |
190 | 597 | return nil, extendError(err, msg) | ||
191 | 587 | } | 598 | } |
192 | 588 | return response.Body, nil | 599 | return response.Body, nil |
193 | 589 | } | 600 | } |
194 | 590 | 601 | ||
195 | === modified file 'storage_base_test.go' | |||
196 | --- storage_base_test.go 2013-06-21 01:01:35 +0000 | |||
197 | +++ storage_base_test.go 2013-06-21 11:47:27 +0000 | |||
198 | @@ -20,6 +20,14 @@ | |||
199 | 20 | 20 | ||
200 | 21 | var _ = Suite(&testComposeHeaders{}) | 21 | var _ = Suite(&testComposeHeaders{}) |
201 | 22 | 22 | ||
202 | 23 | func makeHttpResponse(status int, body string) *http.Response { | ||
203 | 24 | return &http.Response{ | ||
204 | 25 | Status: fmt.Sprintf("%d", status), | ||
205 | 26 | StatusCode: status, | ||
206 | 27 | Body: makeResponseBody(body), | ||
207 | 28 | } | ||
208 | 29 | } | ||
209 | 30 | |||
210 | 23 | func (suite *testComposeHeaders) TestNoHeaders(c *C) { | 31 | func (suite *testComposeHeaders) TestNoHeaders(c *C) { |
211 | 24 | req, err := http.NewRequest("GET", "http://example.com", nil) | 32 | req, err := http.NewRequest("GET", "http://example.com", nil) |
212 | 25 | c.Assert(err, IsNil) | 33 | c.Assert(err, IsNil) |
213 | @@ -301,7 +309,7 @@ | |||
214 | 301 | // The ListContainers Storage API call returns a ContainerEnumerationResults | 309 | // The ListContainers Storage API call returns a ContainerEnumerationResults |
215 | 302 | // struct on success. | 310 | // struct on success. |
216 | 303 | func (suite *TestListContainers) Test(c *C) { | 311 | func (suite *TestListContainers) Test(c *C) { |
218 | 304 | response_body := ` | 312 | responseBody := ` |
219 | 305 | <?xml version="1.0" encoding="utf-8"?> | 313 | <?xml version="1.0" encoding="utf-8"?> |
220 | 306 | <EnumerationResults AccountName="http://myaccount.blob.core.windows.net"> | 314 | <EnumerationResults AccountName="http://myaccount.blob.core.windows.net"> |
221 | 307 | <Prefix>prefix-value</Prefix> | 315 | <Prefix>prefix-value</Prefix> |
222 | @@ -325,11 +333,7 @@ | |||
223 | 325 | </Containers> | 333 | </Containers> |
224 | 326 | <NextMarker/> | 334 | <NextMarker/> |
225 | 327 | </EnumerationResults>` | 335 | </EnumerationResults>` |
231 | 328 | response := &http.Response{ | 336 | response := makeHttpResponse(http.StatusOK, responseBody) |
227 | 329 | Status: fmt.Sprintf("%d", http.StatusOK), | ||
228 | 330 | StatusCode: http.StatusOK, | ||
229 | 331 | Body: makeResponseBody(response_body), | ||
230 | 332 | } | ||
232 | 333 | transport := &TestTransport{Response: response} | 337 | transport := &TestTransport{Response: response} |
233 | 334 | context := makeStorageContext(transport) | 338 | context := makeStorageContext(transport) |
234 | 335 | request := &ListContainersRequest{Marker: ""} | 339 | request := &ListContainersRequest{Marker: ""} |
235 | @@ -351,16 +355,16 @@ | |||
236 | 351 | c.Assert(err, NotNil) | 355 | c.Assert(err, NotNil) |
237 | 352 | } | 356 | } |
238 | 353 | 357 | ||
245 | 354 | // Server-side errors are propagated back to the caller. | 358 | // Azure HTTP errors (for instance 404 responses) are propagated back to |
246 | 355 | func (suite *TestListContainers) TestErrorResponse(c *C) { | 359 | // the caller as ServerError objects. |
247 | 356 | response := &http.Response{ | 360 | func (suite *TestListContainers) TestServerError(c *C) { |
248 | 357 | Status: fmt.Sprintf("%d", http.StatusNotFound), | 361 | response := makeHttpResponse(http.StatusNotFound, "not found") |
243 | 358 | StatusCode: http.StatusNotFound, | ||
244 | 359 | } | ||
249 | 360 | context := makeStorageContext(&TestTransport{Response: response}) | 362 | context := makeStorageContext(&TestTransport{Response: response}) |
250 | 361 | request := &ListContainersRequest{Marker: ""} | 363 | request := &ListContainersRequest{Marker: ""} |
251 | 362 | _, err := context.ListContainers(request) | 364 | _, err := context.ListContainers(request) |
253 | 363 | c.Assert(err, NotNil) | 365 | serverError, ok := err.(*ServerError) |
254 | 366 | c.Check(ok, Equals, true) | ||
255 | 367 | c.Check(serverError.HTTPStatus.StatusCode(), Equals, http.StatusNotFound) | ||
256 | 364 | } | 368 | } |
257 | 365 | 369 | ||
258 | 366 | func (suite *TestListContainers) TestListContainersBatchPassesMarker(c *C) { | 370 | func (suite *TestListContainers) TestListContainersBatchPassesMarker(c *C) { |
259 | @@ -424,7 +428,7 @@ | |||
260 | 424 | // The ListBlobs Storage API call returns a BlobEnumerationResults struct on | 428 | // The ListBlobs Storage API call returns a BlobEnumerationResults struct on |
261 | 425 | // success. | 429 | // success. |
262 | 426 | func (suite *TestListBlobs) Test(c *C) { | 430 | func (suite *TestListBlobs) Test(c *C) { |
264 | 427 | response_body := ` | 431 | responseBody := ` |
265 | 428 | <?xml version="1.0" encoding="utf-8"?> | 432 | <?xml version="1.0" encoding="utf-8"?> |
266 | 429 | <EnumerationResults ContainerName="http://myaccount.blob.core.windows.net/mycontainer"> | 433 | <EnumerationResults ContainerName="http://myaccount.blob.core.windows.net/mycontainer"> |
267 | 430 | <Prefix>prefix</Prefix> | 434 | <Prefix>prefix</Prefix> |
268 | @@ -468,11 +472,7 @@ | |||
269 | 468 | </Blobs> | 472 | </Blobs> |
270 | 469 | <NextMarker /> | 473 | <NextMarker /> |
271 | 470 | </EnumerationResults>` | 474 | </EnumerationResults>` |
277 | 471 | response := &http.Response{ | 475 | response := makeHttpResponse(http.StatusOK, responseBody) |
273 | 472 | Status: fmt.Sprintf("%d", http.StatusOK), | ||
274 | 473 | StatusCode: http.StatusOK, | ||
275 | 474 | Body: makeResponseBody(response_body), | ||
276 | 475 | } | ||
278 | 476 | transport := &TestTransport{Response: response} | 476 | transport := &TestTransport{Response: response} |
279 | 477 | context := makeStorageContext(transport) | 477 | context := makeStorageContext(transport) |
280 | 478 | 478 | ||
281 | @@ -498,17 +498,16 @@ | |||
282 | 498 | c.Assert(err, NotNil) | 498 | c.Assert(err, NotNil) |
283 | 499 | } | 499 | } |
284 | 500 | 500 | ||
291 | 501 | // Server-side errors are propagated back to the caller. | 501 | // Azure HTTP errors (for instance 404 responses) are propagated back to |
292 | 502 | func (suite *TestListBlobs) TestErrorResponse(c *C) { | 502 | // the caller as ServerError objects. |
293 | 503 | response := &http.Response{ | 503 | func (suite *TestListBlobs) TestServerError(c *C) { |
294 | 504 | Status: fmt.Sprintf("%d", http.StatusNotFound), | 504 | response := makeHttpResponse(http.StatusNotFound, "not found") |
289 | 505 | StatusCode: http.StatusNotFound, | ||
290 | 506 | } | ||
295 | 507 | context := makeStorageContext(&TestTransport{Response: response}) | 505 | context := makeStorageContext(&TestTransport{Response: response}) |
296 | 508 | |||
297 | 509 | request := &ListBlobsRequest{Container: "container"} | 506 | request := &ListBlobsRequest{Container: "container"} |
298 | 510 | _, err := context.ListBlobs(request) | 507 | _, err := context.ListBlobs(request) |
300 | 511 | c.Assert(err, NotNil) | 508 | serverError, ok := err.(*ServerError) |
301 | 509 | c.Check(ok, Equals, true) | ||
302 | 510 | c.Check(serverError.HTTPStatus.StatusCode(), Equals, http.StatusNotFound) | ||
303 | 512 | } | 511 | } |
304 | 513 | 512 | ||
305 | 514 | func (suite *TestListBlobs) TestListBlobsPassesMarker(c *C) { | 513 | func (suite *TestListBlobs) TestListBlobsPassesMarker(c *C) { |
306 | @@ -592,10 +591,7 @@ | |||
307 | 592 | // The CreateContainer Storage API call returns without error when the | 591 | // The CreateContainer Storage API call returns without error when the |
308 | 593 | // container has been created successfully. | 592 | // container has been created successfully. |
309 | 594 | func (suite *TestCreateContainer) Test(c *C) { | 593 | func (suite *TestCreateContainer) Test(c *C) { |
314 | 595 | response := &http.Response{ | 594 | response := makeHttpResponse(http.StatusCreated, "") |
311 | 596 | Status: fmt.Sprintf("%d", http.StatusCreated), | ||
312 | 597 | StatusCode: http.StatusCreated, | ||
313 | 598 | } | ||
315 | 599 | transport := &TestTransport{Response: response} | 595 | transport := &TestTransport{Response: response} |
316 | 600 | context := makeStorageContext(transport) | 596 | context := makeStorageContext(transport) |
317 | 601 | container_name := MakeRandomString(10) | 597 | container_name := MakeRandomString(10) |
318 | @@ -617,10 +613,7 @@ | |||
319 | 617 | 613 | ||
320 | 618 | // Server-side errors are propagated back to the caller. | 614 | // Server-side errors are propagated back to the caller. |
321 | 619 | func (suite *TestCreateContainer) TestErrorResponse(c *C) { | 615 | func (suite *TestCreateContainer) TestErrorResponse(c *C) { |
326 | 620 | response := &http.Response{ | 616 | response := makeHttpResponse(http.StatusNotFound, "not found") |
323 | 621 | Status: fmt.Sprintf("%d", http.StatusNotFound), | ||
324 | 622 | StatusCode: http.StatusNotFound, | ||
325 | 623 | } | ||
327 | 624 | context := makeStorageContext(&TestTransport{Response: response}) | 617 | context := makeStorageContext(&TestTransport{Response: response}) |
328 | 625 | err := context.CreateContainer("container") | 618 | err := context.CreateContainer("container") |
329 | 626 | c.Assert(err, NotNil) | 619 | c.Assert(err, NotNil) |
330 | @@ -628,25 +621,30 @@ | |||
331 | 628 | 621 | ||
332 | 629 | // Server-side errors are propagated back to the caller. | 622 | // Server-side errors are propagated back to the caller. |
333 | 630 | func (suite *TestCreateContainer) TestNotCreatedResponse(c *C) { | 623 | func (suite *TestCreateContainer) TestNotCreatedResponse(c *C) { |
338 | 631 | response := &http.Response{ | 624 | response := makeHttpResponse(http.StatusOK, "") |
335 | 632 | Status: fmt.Sprintf("%d", http.StatusOK), | ||
336 | 633 | StatusCode: http.StatusOK, | ||
337 | 634 | } | ||
339 | 635 | context := makeStorageContext(&TestTransport{Response: response}) | 625 | context := makeStorageContext(&TestTransport{Response: response}) |
340 | 636 | err := context.CreateContainer("container") | 626 | err := context.CreateContainer("container") |
341 | 637 | c.Assert(err, NotNil) | 627 | c.Assert(err, NotNil) |
342 | 638 | } | 628 | } |
343 | 639 | 629 | ||
344 | 630 | // Azure HTTP errors (for instance 404 responses) are propagated back to | ||
345 | 631 | // the caller as ServerError objects. | ||
346 | 632 | func (suite *TestCreateContainer) TestServerError(c *C) { | ||
347 | 633 | response := makeHttpResponse(http.StatusNotFound, "not found") | ||
348 | 634 | context := makeStorageContext(&TestTransport{Response: response}) | ||
349 | 635 | err := context.CreateContainer("container") | ||
350 | 636 | serverError, ok := err.(*ServerError) | ||
351 | 637 | c.Check(ok, Equals, true) | ||
352 | 638 | c.Check(serverError.HTTPStatus.StatusCode(), Equals, http.StatusNotFound) | ||
353 | 639 | } | ||
354 | 640 | |||
355 | 640 | type TestPutPage struct{} | 641 | type TestPutPage struct{} |
356 | 641 | 642 | ||
357 | 642 | var _ = Suite(&TestPutPage{}) | 643 | var _ = Suite(&TestPutPage{}) |
358 | 643 | 644 | ||
359 | 644 | // Basic happy path testing. | 645 | // Basic happy path testing. |
360 | 645 | func (suite *TestPutPage) TestHappyPath(c *C) { | 646 | func (suite *TestPutPage) TestHappyPath(c *C) { |
365 | 646 | response := &http.Response{ | 647 | response := makeHttpResponse(http.StatusCreated, "") |
362 | 647 | Status: fmt.Sprintf("%d", http.StatusCreated), | ||
363 | 648 | StatusCode: http.StatusCreated, | ||
364 | 649 | } | ||
366 | 650 | transport := &TestTransport{Response: response} | 648 | transport := &TestTransport{Response: response} |
367 | 651 | context := makeStorageContext(transport) | 649 | context := makeStorageContext(transport) |
368 | 652 | randomData := MakeRandomByteSlice(10) | 650 | randomData := MakeRandomByteSlice(10) |
369 | @@ -687,11 +685,8 @@ | |||
370 | 687 | 685 | ||
371 | 688 | // Server-side errors are propagated back to the caller. | 686 | // Server-side errors are propagated back to the caller. |
372 | 689 | func (suite *TestPutPage) TestErrorResponse(c *C) { | 687 | func (suite *TestPutPage) TestErrorResponse(c *C) { |
378 | 690 | response := &http.Response{ | 688 | responseBody := "<Error><Code>Frotzed</Code><Message>failed to put blob</Message></Error>" |
379 | 691 | Status: "102 Frotzed", | 689 | response := makeHttpResponse(102, responseBody) |
375 | 692 | StatusCode: 102, | ||
376 | 693 | Body: makeResponseBody("<Error><Code>Frotzed</Code><Message>failed to put blob</Message></Error>"), | ||
377 | 694 | } | ||
380 | 695 | context := makeStorageContext(&TestTransport{Response: response}) | 690 | context := makeStorageContext(&TestTransport{Response: response}) |
381 | 696 | err := context.PutPage(&PutPageRequest{ | 691 | err := context.PutPage(&PutPageRequest{ |
382 | 697 | Container: "container", Filename: "filename", StartRange: 0, | 692 | Container: "container", Filename: "filename", StartRange: 0, |
383 | @@ -702,16 +697,26 @@ | |||
384 | 702 | c.Check(err, ErrorMatches, ".*failed to put blob.*") | 697 | c.Check(err, ErrorMatches, ".*failed to put blob.*") |
385 | 703 | } | 698 | } |
386 | 704 | 699 | ||
387 | 700 | // Azure HTTP errors (for instance 404 responses) are propagated back to | ||
388 | 701 | // the caller as ServerError objects. | ||
389 | 702 | func (suite *TestPutPage) TestServerError(c *C) { | ||
390 | 703 | response := makeHttpResponse(http.StatusNotFound, "not found") | ||
391 | 704 | context := makeStorageContext(&TestTransport{Response: response}) | ||
392 | 705 | err := context.PutPage(&PutPageRequest{ | ||
393 | 706 | Container: "container", Filename: "filename", StartRange: 0, | ||
394 | 707 | EndRange: 512, Data: nil}) | ||
395 | 708 | serverError, ok := err.(*ServerError) | ||
396 | 709 | c.Check(ok, Equals, true) | ||
397 | 710 | c.Check(serverError.HTTPStatus.StatusCode(), Equals, http.StatusNotFound) | ||
398 | 711 | } | ||
399 | 712 | |||
400 | 705 | type TestPutBlob struct{} | 713 | type TestPutBlob struct{} |
401 | 706 | 714 | ||
402 | 707 | var _ = Suite(&TestPutBlob{}) | 715 | var _ = Suite(&TestPutBlob{}) |
403 | 708 | 716 | ||
404 | 709 | // Test basic PutBlob happy path functionality. | 717 | // Test basic PutBlob happy path functionality. |
405 | 710 | func (suite *TestPutBlob) TestPutBlockBlob(c *C) { | 718 | func (suite *TestPutBlob) TestPutBlockBlob(c *C) { |
410 | 711 | response := &http.Response{ | 719 | response := makeHttpResponse(http.StatusCreated, "") |
407 | 712 | Status: fmt.Sprintf("%d", http.StatusCreated), | ||
408 | 713 | StatusCode: http.StatusCreated, | ||
409 | 714 | } | ||
411 | 715 | transport := &TestTransport{Response: response} | 720 | transport := &TestTransport{Response: response} |
412 | 716 | context := makeStorageContext(transport) | 721 | context := makeStorageContext(transport) |
413 | 717 | 722 | ||
414 | @@ -728,10 +733,7 @@ | |||
415 | 728 | 733 | ||
416 | 729 | // PutBlob should set x-ms-blob-type to PageBlob for Page Blobs. | 734 | // PutBlob should set x-ms-blob-type to PageBlob for Page Blobs. |
417 | 730 | func (suite *TestPutBlob) TestPutPageBlob(c *C) { | 735 | func (suite *TestPutBlob) TestPutPageBlob(c *C) { |
422 | 731 | response := &http.Response{ | 736 | response := makeHttpResponse(http.StatusCreated, "") |
419 | 732 | Status: fmt.Sprintf("%d", http.StatusCreated), | ||
420 | 733 | StatusCode: http.StatusCreated, | ||
421 | 734 | } | ||
423 | 735 | transport := &TestTransport{Response: response} | 737 | transport := &TestTransport{Response: response} |
424 | 736 | context := makeStorageContext(transport) | 738 | context := makeStorageContext(transport) |
425 | 737 | err := context.PutBlob(&PutBlobRequest{ | 739 | err := context.PutBlob(&PutBlobRequest{ |
426 | @@ -774,11 +776,8 @@ | |||
427 | 774 | 776 | ||
428 | 775 | // Server-side errors are propagated back to the caller. | 777 | // Server-side errors are propagated back to the caller. |
429 | 776 | func (suite *TestPutBlob) TestErrorResponse(c *C) { | 778 | func (suite *TestPutBlob) TestErrorResponse(c *C) { |
435 | 777 | response := &http.Response{ | 779 | responseBody := "<Error><Code>Frotzed</Code><Message>failed to put blob</Message></Error>" |
436 | 778 | Status: "102 Frotzed", | 780 | response := makeHttpResponse(102, responseBody) |
432 | 779 | StatusCode: 102, | ||
433 | 780 | Body: makeResponseBody("<Error><Code>Frotzed</Code><Message>failed to put blob</Message></Error>"), | ||
434 | 781 | } | ||
437 | 782 | context := makeStorageContext(&TestTransport{Response: response}) | 781 | context := makeStorageContext(&TestTransport{Response: response}) |
438 | 783 | err := context.PutBlob(&PutBlobRequest{ | 782 | err := context.PutBlob(&PutBlobRequest{ |
439 | 784 | Container: "container", BlobType: "block", Filename: "blobname"}) | 783 | Container: "container", BlobType: "block", Filename: "blobname"}) |
440 | @@ -788,15 +787,24 @@ | |||
441 | 788 | c.Check(err, ErrorMatches, ".*failed to put blob.*") | 787 | c.Check(err, ErrorMatches, ".*failed to put blob.*") |
442 | 789 | } | 788 | } |
443 | 790 | 789 | ||
444 | 790 | // Azure HTTP errors (for instance 404 responses) are propagated back to | ||
445 | 791 | // the caller as ServerError objects. | ||
446 | 792 | func (suite *TestPutBlob) TestServerError(c *C) { | ||
447 | 793 | response := makeHttpResponse(http.StatusNotFound, "not found") | ||
448 | 794 | context := makeStorageContext(&TestTransport{Response: response}) | ||
449 | 795 | err := context.PutBlob(&PutBlobRequest{ | ||
450 | 796 | Container: "container", BlobType: "block", Filename: "blobname"}) | ||
451 | 797 | serverError, ok := err.(*ServerError) | ||
452 | 798 | c.Check(ok, Equals, true) | ||
453 | 799 | c.Check(serverError.HTTPStatus.StatusCode(), Equals, http.StatusNotFound) | ||
454 | 800 | } | ||
455 | 801 | |||
456 | 791 | type TestPutBlock struct{} | 802 | type TestPutBlock struct{} |
457 | 792 | 803 | ||
458 | 793 | var _ = Suite(&TestPutBlock{}) | 804 | var _ = Suite(&TestPutBlock{}) |
459 | 794 | 805 | ||
460 | 795 | func (suite *TestPutBlock) Test(c *C) { | 806 | func (suite *TestPutBlock) Test(c *C) { |
465 | 796 | response := &http.Response{ | 807 | response := makeHttpResponse(http.StatusCreated, "") |
462 | 797 | Status: fmt.Sprintf("%d", http.StatusCreated), | ||
463 | 798 | StatusCode: http.StatusCreated, | ||
464 | 799 | } | ||
466 | 800 | transport := &TestTransport{Response: response} | 808 | transport := &TestTransport{Response: response} |
467 | 801 | context := makeStorageContext(transport) | 809 | context := makeStorageContext(transport) |
468 | 802 | blockid := "\x1b\xea\xf7Mv\xb5\xddH\xebm" | 810 | blockid := "\x1b\xea\xf7Mv\xb5\xddH\xebm" |
469 | @@ -830,11 +838,8 @@ | |||
470 | 830 | 838 | ||
471 | 831 | // Server-side errors are propagated back to the caller. | 839 | // Server-side errors are propagated back to the caller. |
472 | 832 | func (suite *TestPutBlock) TestErrorResponse(c *C) { | 840 | func (suite *TestPutBlock) TestErrorResponse(c *C) { |
478 | 833 | response := &http.Response{ | 841 | responseBody := "<Error><Code>Frotzed</Code><Message>failed to put block</Message></Error>" |
479 | 834 | Status: "102 Frotzed", | 842 | response := makeHttpResponse(102, responseBody) |
475 | 835 | StatusCode: 102, | ||
476 | 836 | Body: makeResponseBody("<Error><Code>Frotzed</Code><Message>failed to put block</Message></Error>"), | ||
477 | 837 | } | ||
480 | 838 | context := makeStorageContext(&TestTransport{Response: response}) | 843 | context := makeStorageContext(&TestTransport{Response: response}) |
481 | 839 | data_reader := bytes.NewReader(MakeRandomByteSlice(10)) | 844 | data_reader := bytes.NewReader(MakeRandomByteSlice(10)) |
482 | 840 | err := context.PutBlock("container", "blobname", "blockid", data_reader) | 845 | err := context.PutBlock("container", "blobname", "blockid", data_reader) |
483 | @@ -844,15 +849,24 @@ | |||
484 | 844 | c.Check(err, ErrorMatches, ".*failed to put block.*") | 849 | c.Check(err, ErrorMatches, ".*failed to put block.*") |
485 | 845 | } | 850 | } |
486 | 846 | 851 | ||
487 | 852 | // Azure HTTP errors (for instance 404 responses) are propagated back to | ||
488 | 853 | // the caller as ServerError objects. | ||
489 | 854 | func (suite *TestPutBlock) TestServerError(c *C) { | ||
490 | 855 | response := makeHttpResponse(http.StatusNotFound, "not found") | ||
491 | 856 | context := makeStorageContext(&TestTransport{Response: response}) | ||
492 | 857 | data_reader := bytes.NewReader(MakeRandomByteSlice(10)) | ||
493 | 858 | err := context.PutBlock("container", "blobname", "blockid", data_reader) | ||
494 | 859 | serverError, ok := err.(*ServerError) | ||
495 | 860 | c.Check(ok, Equals, true) | ||
496 | 861 | c.Check(serverError.HTTPStatus.StatusCode(), Equals, http.StatusNotFound) | ||
497 | 862 | } | ||
498 | 863 | |||
499 | 847 | type TestPutBlockList struct{} | 864 | type TestPutBlockList struct{} |
500 | 848 | 865 | ||
501 | 849 | var _ = Suite(&TestPutBlockList{}) | 866 | var _ = Suite(&TestPutBlockList{}) |
502 | 850 | 867 | ||
503 | 851 | func (suite *TestPutBlockList) Test(c *C) { | 868 | func (suite *TestPutBlockList) Test(c *C) { |
508 | 852 | response := &http.Response{ | 869 | response := makeHttpResponse(http.StatusCreated, "") |
505 | 853 | Status: fmt.Sprintf("%d", http.StatusCreated), | ||
506 | 854 | StatusCode: http.StatusCreated, | ||
507 | 855 | } | ||
509 | 856 | transport := &TestTransport{Response: response} | 870 | transport := &TestTransport{Response: response} |
510 | 857 | context := makeStorageContext(transport) | 871 | context := makeStorageContext(transport) |
511 | 858 | blocklist := &BlockList{} | 872 | blocklist := &BlockList{} |
512 | @@ -888,11 +902,8 @@ | |||
513 | 888 | 902 | ||
514 | 889 | // Server-side errors are propagated back to the caller. | 903 | // Server-side errors are propagated back to the caller. |
515 | 890 | func (suite *TestPutBlockList) TestErrorResponse(c *C) { | 904 | func (suite *TestPutBlockList) TestErrorResponse(c *C) { |
521 | 891 | response := &http.Response{ | 905 | responseBody := "<Error><Code>Frotzed</Code><Message>failed to put blocklist</Message></Error>" |
522 | 892 | Status: "102 Frotzed", | 906 | response := makeHttpResponse(102, responseBody) |
518 | 893 | StatusCode: 102, | ||
519 | 894 | Body: makeResponseBody("<Error><Code>Frotzed</Code><Message>failed to put blocklist</Message></Error>"), | ||
520 | 895 | } | ||
523 | 896 | context := makeStorageContext(&TestTransport{Response: response}) | 907 | context := makeStorageContext(&TestTransport{Response: response}) |
524 | 897 | blocklist := &BlockList{} | 908 | blocklist := &BlockList{} |
525 | 898 | err := context.PutBlockList("container", "blobname", blocklist) | 909 | err := context.PutBlockList("container", "blobname", blocklist) |
526 | @@ -902,6 +913,18 @@ | |||
527 | 902 | c.Check(err, ErrorMatches, ".*failed to put blocklist.*") | 913 | c.Check(err, ErrorMatches, ".*failed to put blocklist.*") |
528 | 903 | } | 914 | } |
529 | 904 | 915 | ||
530 | 916 | // Azure HTTP errors (for instance 404 responses) are propagated back to | ||
531 | 917 | // the caller as ServerError objects. | ||
532 | 918 | func (suite *TestPutBlockList) TestServerError(c *C) { | ||
533 | 919 | response := makeHttpResponse(http.StatusNotFound, "not found") | ||
534 | 920 | context := makeStorageContext(&TestTransport{Response: response}) | ||
535 | 921 | blocklist := &BlockList{} | ||
536 | 922 | err := context.PutBlockList("container", "blobname", blocklist) | ||
537 | 923 | serverError, ok := err.(*ServerError) | ||
538 | 924 | c.Check(ok, Equals, true) | ||
539 | 925 | c.Check(serverError.HTTPStatus.StatusCode(), Equals, http.StatusNotFound) | ||
540 | 926 | } | ||
541 | 927 | |||
542 | 905 | type TestGetBlockList struct{} | 928 | type TestGetBlockList struct{} |
543 | 906 | 929 | ||
544 | 907 | var _ = Suite(&TestGetBlockList{}) | 930 | var _ = Suite(&TestGetBlockList{}) |
545 | @@ -909,7 +932,7 @@ | |||
546 | 909 | // The GetBlockList Storage API call returns a GetBlockList struct on | 932 | // The GetBlockList Storage API call returns a GetBlockList struct on |
547 | 910 | // success. | 933 | // success. |
548 | 911 | func (suite *TestGetBlockList) Test(c *C) { | 934 | func (suite *TestGetBlockList) Test(c *C) { |
550 | 912 | response_body := ` | 935 | responseBody := ` |
551 | 913 | <?xml version="1.0" encoding="utf-8"?> | 936 | <?xml version="1.0" encoding="utf-8"?> |
552 | 914 | <BlockList> | 937 | <BlockList> |
553 | 915 | <CommittedBlocks> | 938 | <CommittedBlocks> |
554 | @@ -926,11 +949,7 @@ | |||
555 | 926 | </UncommittedBlocks> | 949 | </UncommittedBlocks> |
556 | 927 | </BlockList>` | 950 | </BlockList>` |
557 | 928 | 951 | ||
563 | 929 | response := &http.Response{ | 952 | response := makeHttpResponse(http.StatusOK, responseBody) |
559 | 930 | Status: fmt.Sprintf("%d", http.StatusOK), | ||
560 | 931 | StatusCode: http.StatusOK, | ||
561 | 932 | Body: makeResponseBody(response_body), | ||
562 | 933 | } | ||
564 | 934 | transport := &TestTransport{Response: response} | 953 | transport := &TestTransport{Response: response} |
565 | 935 | context := makeStorageContext(transport) | 954 | context := makeStorageContext(transport) |
566 | 936 | results, err := context.GetBlockList("container", "myfilename") | 955 | results, err := context.GetBlockList("container", "myfilename") |
567 | @@ -952,15 +971,15 @@ | |||
568 | 952 | c.Assert(err, NotNil) | 971 | c.Assert(err, NotNil) |
569 | 953 | } | 972 | } |
570 | 954 | 973 | ||
577 | 955 | // Server-side errors are propagated back to the caller. | 974 | // Azure HTTP errors (for instance 404 responses) are propagated back to |
578 | 956 | func (suite *TestGetBlockList) TestErrorResponse(c *C) { | 975 | // the caller as ServerError objects. |
579 | 957 | response := &http.Response{ | 976 | func (suite *TestGetBlockList) TestServerError(c *C) { |
580 | 958 | Status: fmt.Sprintf("%d", http.StatusNotFound), | 977 | response := makeHttpResponse(http.StatusNotFound, "not found") |
575 | 959 | StatusCode: http.StatusNotFound, | ||
576 | 960 | } | ||
581 | 961 | context := makeStorageContext(&TestTransport{Response: response}) | 978 | context := makeStorageContext(&TestTransport{Response: response}) |
584 | 962 | _, err := context.GetBlockList("container", "myfilename") | 979 | _, err := context.GetBlockList("container", "blobname") |
585 | 963 | c.Assert(err, NotNil) | 980 | serverError, ok := err.(*ServerError) |
586 | 981 | c.Check(ok, Equals, true) | ||
587 | 982 | c.Check(serverError.HTTPStatus.StatusCode(), Equals, http.StatusNotFound) | ||
588 | 964 | } | 983 | } |
589 | 965 | 984 | ||
590 | 966 | type TestDeleteBlob struct{} | 985 | type TestDeleteBlob struct{} |
591 | @@ -968,10 +987,7 @@ | |||
592 | 968 | var _ = Suite(&TestDeleteBlob{}) | 987 | var _ = Suite(&TestDeleteBlob{}) |
593 | 969 | 988 | ||
594 | 970 | func (suite *TestDeleteBlob) Test(c *C) { | 989 | func (suite *TestDeleteBlob) Test(c *C) { |
599 | 971 | response := &http.Response{ | 990 | response := makeHttpResponse(http.StatusAccepted, "") |
596 | 972 | Status: fmt.Sprintf("%d", http.StatusAccepted), | ||
597 | 973 | StatusCode: http.StatusAccepted, | ||
598 | 974 | } | ||
600 | 975 | transport := &TestTransport{Response: response} | 991 | transport := &TestTransport{Response: response} |
601 | 976 | context := makeStorageContext(transport) | 992 | context := makeStorageContext(transport) |
602 | 977 | err := context.DeleteBlob("container", "blobname") | 993 | err := context.DeleteBlob("container", "blobname") |
603 | @@ -991,13 +1007,21 @@ | |||
604 | 991 | c.Assert(err, NotNil) | 1007 | c.Assert(err, NotNil) |
605 | 992 | } | 1008 | } |
606 | 993 | 1009 | ||
607 | 1010 | // Azure HTTP errors (for instance 404 responses) are propagated back to | ||
608 | 1011 | // the caller as ServerError objects. | ||
609 | 1012 | func (suite *TestDeleteBlob) TestServerError(c *C) { | ||
610 | 1013 | response := makeHttpResponse(http.StatusNotFound, "not found") | ||
611 | 1014 | context := makeStorageContext(&TestTransport{Response: response}) | ||
612 | 1015 | err := context.DeleteBlob("container", "blobname") | ||
613 | 1016 | serverError, ok := err.(*ServerError) | ||
614 | 1017 | c.Check(ok, Equals, true) | ||
615 | 1018 | c.Check(serverError.HTTPStatus.StatusCode(), Equals, http.StatusNotFound) | ||
616 | 1019 | } | ||
617 | 1020 | |||
618 | 994 | // Server-side errors are propagated back to the caller. | 1021 | // Server-side errors are propagated back to the caller. |
619 | 995 | func (suite *TestDeleteBlob) TestErrorResponse(c *C) { | 1022 | func (suite *TestDeleteBlob) TestErrorResponse(c *C) { |
625 | 996 | response := &http.Response{ | 1023 | responseBody := "<Error><Code>Frotzed</Code><Message>failed to delete blob</Message></Error>" |
626 | 997 | Status: "146 Frotzed", | 1024 | response := makeHttpResponse(146, responseBody) |
622 | 998 | StatusCode: 146, | ||
623 | 999 | Body: makeResponseBody("<Error><Code>Frotzed</Code><Message>failed to delete blob</Message></Error>"), | ||
624 | 1000 | } | ||
627 | 1001 | context := makeStorageContext(&TestTransport{Response: response}) | 1025 | context := makeStorageContext(&TestTransport{Response: response}) |
628 | 1002 | err := context.DeleteBlob("container", "blobname") | 1026 | err := context.DeleteBlob("container", "blobname") |
629 | 1003 | c.Assert(err, NotNil) | 1027 | c.Assert(err, NotNil) |
630 | @@ -1011,12 +1035,8 @@ | |||
631 | 1011 | var _ = Suite(&TestGetBlob{}) | 1035 | var _ = Suite(&TestGetBlob{}) |
632 | 1012 | 1036 | ||
633 | 1013 | func (suite *TestGetBlob) Test(c *C) { | 1037 | func (suite *TestGetBlob) Test(c *C) { |
640 | 1014 | response_body := "blob-in-a-can" | 1038 | responseBody := "blob-in-a-can" |
641 | 1015 | response := &http.Response{ | 1039 | response := makeHttpResponse(http.StatusOK, responseBody) |
636 | 1016 | Status: fmt.Sprintf("%d", http.StatusOK), | ||
637 | 1017 | StatusCode: http.StatusOK, | ||
638 | 1018 | Body: makeResponseBody(response_body), | ||
639 | 1019 | } | ||
642 | 1020 | transport := &TestTransport{Response: response} | 1040 | transport := &TestTransport{Response: response} |
643 | 1021 | context := makeStorageContext(transport) | 1041 | context := makeStorageContext(transport) |
644 | 1022 | reader, err := context.GetBlob("container", "blobname") | 1042 | reader, err := context.GetBlob("container", "blobname") |
645 | @@ -1030,7 +1050,7 @@ | |||
646 | 1030 | 1050 | ||
647 | 1031 | data, err := ioutil.ReadAll(reader) | 1051 | data, err := ioutil.ReadAll(reader) |
648 | 1032 | c.Assert(err, IsNil) | 1052 | c.Assert(err, IsNil) |
650 | 1033 | c.Check(string(data), Equals, response_body) | 1053 | c.Check(string(data), Equals, responseBody) |
651 | 1034 | } | 1054 | } |
652 | 1035 | 1055 | ||
653 | 1036 | // Client-side errors from the HTTP client are propagated back to the caller. | 1056 | // Client-side errors from the HTTP client are propagated back to the caller. |
654 | @@ -1042,6 +1062,20 @@ | |||
655 | 1042 | c.Assert(err, NotNil) | 1062 | c.Assert(err, NotNil) |
656 | 1043 | } | 1063 | } |
657 | 1044 | 1064 | ||
658 | 1065 | // Azure HTTP errors (for instance 404 responses) are propagated back to | ||
659 | 1066 | // the caller as ServerError objects. | ||
660 | 1067 | func (suite *TestGetBlob) TestServerError(c *C) { | ||
661 | 1068 | response := makeHttpResponse(http.StatusNotFound, "not found") | ||
662 | 1069 | context := makeStorageContext(&TestTransport{Response: response}) | ||
663 | 1070 | reader, err := context.GetBlob("container", "blobname") | ||
664 | 1071 | c.Check(reader, IsNil) | ||
665 | 1072 | c.Assert(err, NotNil) | ||
666 | 1073 | serverError, ok := err.(*ServerError) | ||
667 | 1074 | c.Check(ok, Equals, true) | ||
668 | 1075 | c.Check(serverError.HTTPStatus.StatusCode(), Equals, http.StatusNotFound) | ||
669 | 1076 | c.Check(IsNotFoundError(err), Equals, true) | ||
670 | 1077 | } | ||
671 | 1078 | |||
672 | 1045 | // Server-side errors are propagated back to the caller. | 1079 | // Server-side errors are propagated back to the caller. |
673 | 1046 | func (suite *TestGetBlob) TestErrorResponse(c *C) { | 1080 | func (suite *TestGetBlob) TestErrorResponse(c *C) { |
674 | 1047 | response := &http.Response{ | 1081 | response := &http.Response{ |
Looks okay, but I want you to consider [1] before I approve.
[1]
+func extendError(err error, message string) error {
I found the suggestion that Jeroen mentioned this morning. It's from a
thread called "Strategy for errors in Go". See what you think:
Instead of munging errors into new ones, and so losing type info, we
could store all the errors in a linked-list. I've not given it massive
amounts of thought, but perhaps it's an interesting idea.
type GWACLErrorType interface { Type) GWACLErrorType
Error func() string
Check func(GWACLError
Chain func(Error) GWACLErrorType
This Error
Next GWACLErrorType
}
type GWACLError struct {
This Error
Next GWACLErrorType
}
// Satisfy the Error interface.
func (self *GWACLError) Error() string {
if self.Next == nil {
return self.Error()
} else {
return self.Error() + "; " + self.Next.Error()
}
panic("Being for the benefit of Go < 1.1.")
}
// Search for an error in the list. Sadly, types are not first-class
// citizens, so this just does equality testing for now, but perhaps
// something better could be dreamt up.
func (self *GWACLError) Check(err *GWACLErrorType) {
for f := self; f = f.Next(); f != nil {
if f == err {
return f
}
}
return nil
}
// Return a new GWACLErrorType with the current error at the head.
func (self *GWACLError) Chain(err Error) GWACLErrorType {
return &GWACLError{err, self}
}
[2]
+ var testValues = []struct {
+ err error
+ expectedResult bool
+ }{
...
+ }
I wasn't aware of this form. Nice to know.