Merge lp:~rvb/gwacl/not-found-errors into lp:gwacl

Proposed by Raphaël Badin
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
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.

To post a comment you must log in.
Revision history for this message
Gavin Panella (allenap) wrote :

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 {
    Error  func() string
    Check  func(GWACLErrorType) GWACLErrorType
    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.

review: Needs Information
Revision history for this message
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()/IsNotFoundError() stuff is well encapsulated so we could move to something like what you describe in the future.

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.

Revision history for this message
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()/IsNotFoundError() stuff is
> 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.

review: Approve
Revision history for this message
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

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'httperror.go'
--- httperror.go 2013-04-02 08:51:00 +0000
+++ httperror.go 2013-06-21 11:47:27 +0000
@@ -90,3 +90,37 @@
90 Message: outcome.ErrorMessage,90 Message: outcome.ErrorMessage,
91 }91 }
92}92}
93
94// extendError returns an error whos description is the concatenation of
95// the given message plus the error string from the original error.
96// It preserves the value of the error types it knows about (currently only
97// ServerError).
98//
99// The main purpose of this method is to offer a unified way to
100// extend the information present in errors while still not losing the
101// additioning information present on specific errors gwacl knows out to extend
102// in a more meaningful way.
103func extendError(err error, message string) error {
104 switch err := err.(type) {
105 case *ServerError:
106 returnedErr := *err
107 returnedErr.error = fmt.Errorf(message+"%v", err.error)
108 return &returnedErr
109 default:
110 return fmt.Errorf(message+"%v", err)
111 }
112 // This code cannot be reached but Go insists on having a return or a panic
113 // statement at the end of this method. Sigh.
114 panic("invalid extendError state!")
115}
116
117// IsNotFoundError returns whether or not the given error is an error (as
118// returned by a gwacl method) which corresponds to a 'Not Found' error
119// returned by Windows Azure.
120func IsNotFoundError(err error) bool {
121 serverError, ok := err.(*ServerError)
122 if !ok {
123 return false
124 }
125 return serverError.HTTPStatus.StatusCode() == http.StatusNotFound
126}
93127
=== modified file 'httperror_test.go'
--- httperror_test.go 2013-04-02 06:49:31 +0000
+++ httperror_test.go 2013-06-21 11:47:27 +0000
@@ -94,3 +94,41 @@
94 c.Check(err.Code, Equals, code)94 c.Check(err.Code, Equals, code)
95 c.Check(err.Message, Equals, message)95 c.Check(err.Message, Equals, message)
96}96}
97
98func (suite *httpErrorSuite) TestExtendErrorExtendsGenericError(c *C) {
99 errorString := "an-error"
100 error := fmt.Errorf(errorString)
101 additionalErrorMsg := "additional message"
102 newError := extendError(error, additionalErrorMsg)
103 c.Check(newError.Error(), Equals, fmt.Sprintf("%s%s", additionalErrorMsg, error.Error()))
104}
105
106func (suite *httpErrorSuite) TestExtendErrorExtendsServerError(c *C) {
107 description := "could not talk to server"
108 status := http.StatusGatewayTimeout
109 httpErr := newHTTPError(status, []byte{}, description)
110
111 httpServerErr, ok := httpErr.(*ServerError)
112 c.Assert(ok, Equals, true)
113 additionalErrorMsg := "additional message"
114 newError := extendError(httpErr, additionalErrorMsg)
115 newServerError, ok := httpErr.(*ServerError)
116 c.Assert(ok, Equals, true)
117 c.Check(newError.Error(), Equals, fmt.Sprintf("%s%s", additionalErrorMsg, httpErr.Error()))
118 c.Check(httpServerErr.HTTPStatus, Equals, newServerError.HTTPStatus)
119}
120
121func (suite *httpErrorSuite) TestIsNotFound(c *C) {
122 var testValues = []struct {
123 err error
124 expectedResult bool
125 }{
126 {fmt.Errorf("generic error"), false},
127 {newHTTPError(http.StatusGatewayTimeout, []byte{}, "error"), false},
128 {newHTTPError(http.StatusOK, []byte{}, ""), false},
129 {newHTTPError(http.StatusNotFound, []byte{}, "error"), true},
130 }
131 for _, test := range testValues {
132 c.Check(IsNotFoundError(test.err), Equals, test.expectedResult)
133 }
134}
97135
=== modified file 'storage_base.go'
--- storage_base.go 2013-06-21 01:01:35 +0000
+++ storage_base.go 2013-06-21 11:47:27 +0000
@@ -356,7 +356,8 @@
356 ExpectedStatus: http.StatusOK,356 ExpectedStatus: http.StatusOK,
357 })357 })
358 if err != nil {358 if err != nil {
359 return nil, fmt.Errorf("request for containers list failed: %v", err)359 msg := "request for containers list failed: "
360 return nil, extendError(err, msg)
360 }361 }
361 return &containers, nil362 return &containers, nil
362}363}
@@ -392,7 +393,8 @@
392 ExpectedStatus: http.StatusOK,393 ExpectedStatus: http.StatusOK,
393 })394 })
394 if err != nil {395 if err != nil {
395 return nil, fmt.Errorf("request for blobs list failed: %v", err)396 msg := "request for blobs list failed: "
397 return nil, extendError(err, msg)
396 }398 }
397 return &blobs, err399 return &blobs, err
398}400}
@@ -410,7 +412,8 @@
410 ExpectedStatus: http.StatusCreated,412 ExpectedStatus: http.StatusCreated,
411 })413 })
412 if err != nil {414 if err != nil {
413 return fmt.Errorf("failed to create container %s: %v", container, err)415 msg := fmt.Sprintf("failed to create container %s: ", container)
416 return extendError(err, msg)
414 }417 }
415 return nil418 return nil
416}419}
@@ -453,7 +456,8 @@
453 ExpectedStatus: http.StatusCreated,456 ExpectedStatus: http.StatusCreated,
454 })457 })
455 if err != nil {458 if err != nil {
456 return fmt.Errorf("failed to create blob %s: %v", req.Filename, err)459 msg := fmt.Sprintf("failed to create blob %s: ", req.Filename)
460 return extendError(err, msg)
457 }461 }
458 return nil462 return nil
459}463}
@@ -488,7 +492,8 @@
488 ExpectedStatus: http.StatusCreated,492 ExpectedStatus: http.StatusCreated,
489 })493 })
490 if err != nil {494 if err != nil {
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)
496 return extendError(err, msg)
492 }497 }
493 return nil498 return nil
494}499}
@@ -509,7 +514,8 @@
509 ExpectedStatus: http.StatusOK,514 ExpectedStatus: http.StatusOK,
510 })515 })
511 if err != nil {516 if err != nil {
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)
518 return nil, extendError(err, msg)
513 }519 }
514 return &bl, nil520 return &bl, nil
515}521}
@@ -530,7 +536,8 @@
530 ExpectedStatus: http.StatusCreated,536 ExpectedStatus: http.StatusCreated,
531 })537 })
532 if err != nil {538 if err != nil {
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)
540 return extendError(err, msg)
534 }541 }
535 return nil542 return nil
536}543}
@@ -554,7 +561,8 @@
554 ExpectedStatus: http.StatusCreated,561 ExpectedStatus: http.StatusCreated,
555 })562 })
556 if err != nil {563 if err != nil {
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)
565 return extendError(err, msg)
558 }566 }
559 return nil567 return nil
560}568}
@@ -568,8 +576,10 @@
568 ExpectedStatus: http.StatusAccepted,576 ExpectedStatus: http.StatusAccepted,
569 })577 })
570 // TODO Handle a 404 with an <Error>BlobNotFound response body silently.578 // TODO Handle a 404 with an <Error>BlobNotFound response body silently.
579 // Now this is easy to fix with the method IsNotFoundError().
571 if err != nil {580 if err != nil {
572 return fmt.Errorf("failed to delete blob %s: %v", filename, err)581 msg := fmt.Sprintf("failed to delete blob %s: ", filename)
582 return extendError(err, msg)
573 }583 }
574 return nil584 return nil
575}585}
@@ -583,7 +593,8 @@
583 ExpectedStatus: http.StatusOK,593 ExpectedStatus: http.StatusOK,
584 })594 })
585 if err != nil {595 if err != nil {
586 return nil, fmt.Errorf("failed to get blob %s: %v", filename, err)596 msg := fmt.Sprintf("failed to get blob %s: ", filename)
597 return nil, extendError(err, msg)
587 }598 }
588 return response.Body, nil599 return response.Body, nil
589}600}
590601
=== modified file 'storage_base_test.go'
--- storage_base_test.go 2013-06-21 01:01:35 +0000
+++ storage_base_test.go 2013-06-21 11:47:27 +0000
@@ -20,6 +20,14 @@
2020
21var _ = Suite(&testComposeHeaders{})21var _ = Suite(&testComposeHeaders{})
2222
23func makeHttpResponse(status int, body string) *http.Response {
24 return &http.Response{
25 Status: fmt.Sprintf("%d", status),
26 StatusCode: status,
27 Body: makeResponseBody(body),
28 }
29}
30
23func (suite *testComposeHeaders) TestNoHeaders(c *C) {31func (suite *testComposeHeaders) TestNoHeaders(c *C) {
24 req, err := http.NewRequest("GET", "http://example.com", nil)32 req, err := http.NewRequest("GET", "http://example.com", nil)
25 c.Assert(err, IsNil)33 c.Assert(err, IsNil)
@@ -301,7 +309,7 @@
301// The ListContainers Storage API call returns a ContainerEnumerationResults309// The ListContainers Storage API call returns a ContainerEnumerationResults
302// struct on success.310// struct on success.
303func (suite *TestListContainers) Test(c *C) {311func (suite *TestListContainers) Test(c *C) {
304 response_body := `312 responseBody := `
305 <?xml version="1.0" encoding="utf-8"?>313 <?xml version="1.0" encoding="utf-8"?>
306 <EnumerationResults AccountName="http://myaccount.blob.core.windows.net">314 <EnumerationResults AccountName="http://myaccount.blob.core.windows.net">
307 <Prefix>prefix-value</Prefix>315 <Prefix>prefix-value</Prefix>
@@ -325,11 +333,7 @@
325 </Containers>333 </Containers>
326 <NextMarker/>334 <NextMarker/>
327 </EnumerationResults>`335 </EnumerationResults>`
328 response := &http.Response{336 response := makeHttpResponse(http.StatusOK, responseBody)
329 Status: fmt.Sprintf("%d", http.StatusOK),
330 StatusCode: http.StatusOK,
331 Body: makeResponseBody(response_body),
332 }
333 transport := &TestTransport{Response: response}337 transport := &TestTransport{Response: response}
334 context := makeStorageContext(transport)338 context := makeStorageContext(transport)
335 request := &ListContainersRequest{Marker: ""}339 request := &ListContainersRequest{Marker: ""}
@@ -351,16 +355,16 @@
351 c.Assert(err, NotNil)355 c.Assert(err, NotNil)
352}356}
353357
354// Server-side errors are propagated back to the caller.358// Azure HTTP errors (for instance 404 responses) are propagated back to
355func (suite *TestListContainers) TestErrorResponse(c *C) {359// the caller as ServerError objects.
356 response := &http.Response{360func (suite *TestListContainers) TestServerError(c *C) {
357 Status: fmt.Sprintf("%d", http.StatusNotFound),361 response := makeHttpResponse(http.StatusNotFound, "not found")
358 StatusCode: http.StatusNotFound,
359 }
360 context := makeStorageContext(&TestTransport{Response: response})362 context := makeStorageContext(&TestTransport{Response: response})
361 request := &ListContainersRequest{Marker: ""}363 request := &ListContainersRequest{Marker: ""}
362 _, err := context.ListContainers(request)364 _, err := context.ListContainers(request)
363 c.Assert(err, NotNil)365 serverError, ok := err.(*ServerError)
366 c.Check(ok, Equals, true)
367 c.Check(serverError.HTTPStatus.StatusCode(), Equals, http.StatusNotFound)
364}368}
365369
366func (suite *TestListContainers) TestListContainersBatchPassesMarker(c *C) {370func (suite *TestListContainers) TestListContainersBatchPassesMarker(c *C) {
@@ -424,7 +428,7 @@
424// The ListBlobs Storage API call returns a BlobEnumerationResults struct on428// The ListBlobs Storage API call returns a BlobEnumerationResults struct on
425// success.429// success.
426func (suite *TestListBlobs) Test(c *C) {430func (suite *TestListBlobs) Test(c *C) {
427 response_body := `431 responseBody := `
428 <?xml version="1.0" encoding="utf-8"?>432 <?xml version="1.0" encoding="utf-8"?>
429 <EnumerationResults ContainerName="http://myaccount.blob.core.windows.net/mycontainer">433 <EnumerationResults ContainerName="http://myaccount.blob.core.windows.net/mycontainer">
430 <Prefix>prefix</Prefix>434 <Prefix>prefix</Prefix>
@@ -468,11 +472,7 @@
468 </Blobs>472 </Blobs>
469 <NextMarker />473 <NextMarker />
470 </EnumerationResults>`474 </EnumerationResults>`
471 response := &http.Response{475 response := makeHttpResponse(http.StatusOK, responseBody)
472 Status: fmt.Sprintf("%d", http.StatusOK),
473 StatusCode: http.StatusOK,
474 Body: makeResponseBody(response_body),
475 }
476 transport := &TestTransport{Response: response}476 transport := &TestTransport{Response: response}
477 context := makeStorageContext(transport)477 context := makeStorageContext(transport)
478478
@@ -498,17 +498,16 @@
498 c.Assert(err, NotNil)498 c.Assert(err, NotNil)
499}499}
500500
501// Server-side errors are propagated back to the caller.501// Azure HTTP errors (for instance 404 responses) are propagated back to
502func (suite *TestListBlobs) TestErrorResponse(c *C) {502// the caller as ServerError objects.
503 response := &http.Response{503func (suite *TestListBlobs) TestServerError(c *C) {
504 Status: fmt.Sprintf("%d", http.StatusNotFound),504 response := makeHttpResponse(http.StatusNotFound, "not found")
505 StatusCode: http.StatusNotFound,
506 }
507 context := makeStorageContext(&TestTransport{Response: response})505 context := makeStorageContext(&TestTransport{Response: response})
508
509 request := &ListBlobsRequest{Container: "container"}506 request := &ListBlobsRequest{Container: "container"}
510 _, err := context.ListBlobs(request)507 _, err := context.ListBlobs(request)
511 c.Assert(err, NotNil)508 serverError, ok := err.(*ServerError)
509 c.Check(ok, Equals, true)
510 c.Check(serverError.HTTPStatus.StatusCode(), Equals, http.StatusNotFound)
512}511}
513512
514func (suite *TestListBlobs) TestListBlobsPassesMarker(c *C) {513func (suite *TestListBlobs) TestListBlobsPassesMarker(c *C) {
@@ -592,10 +591,7 @@
592// The CreateContainer Storage API call returns without error when the591// The CreateContainer Storage API call returns without error when the
593// container has been created successfully.592// container has been created successfully.
594func (suite *TestCreateContainer) Test(c *C) {593func (suite *TestCreateContainer) Test(c *C) {
595 response := &http.Response{594 response := makeHttpResponse(http.StatusCreated, "")
596 Status: fmt.Sprintf("%d", http.StatusCreated),
597 StatusCode: http.StatusCreated,
598 }
599 transport := &TestTransport{Response: response}595 transport := &TestTransport{Response: response}
600 context := makeStorageContext(transport)596 context := makeStorageContext(transport)
601 container_name := MakeRandomString(10)597 container_name := MakeRandomString(10)
@@ -617,10 +613,7 @@
617613
618// Server-side errors are propagated back to the caller.614// Server-side errors are propagated back to the caller.
619func (suite *TestCreateContainer) TestErrorResponse(c *C) {615func (suite *TestCreateContainer) TestErrorResponse(c *C) {
620 response := &http.Response{616 response := makeHttpResponse(http.StatusNotFound, "not found")
621 Status: fmt.Sprintf("%d", http.StatusNotFound),
622 StatusCode: http.StatusNotFound,
623 }
624 context := makeStorageContext(&TestTransport{Response: response})617 context := makeStorageContext(&TestTransport{Response: response})
625 err := context.CreateContainer("container")618 err := context.CreateContainer("container")
626 c.Assert(err, NotNil)619 c.Assert(err, NotNil)
@@ -628,25 +621,30 @@
628621
629// Server-side errors are propagated back to the caller.622// Server-side errors are propagated back to the caller.
630func (suite *TestCreateContainer) TestNotCreatedResponse(c *C) {623func (suite *TestCreateContainer) TestNotCreatedResponse(c *C) {
631 response := &http.Response{624 response := makeHttpResponse(http.StatusOK, "")
632 Status: fmt.Sprintf("%d", http.StatusOK),
633 StatusCode: http.StatusOK,
634 }
635 context := makeStorageContext(&TestTransport{Response: response})625 context := makeStorageContext(&TestTransport{Response: response})
636 err := context.CreateContainer("container")626 err := context.CreateContainer("container")
637 c.Assert(err, NotNil)627 c.Assert(err, NotNil)
638}628}
639629
630// Azure HTTP errors (for instance 404 responses) are propagated back to
631// the caller as ServerError objects.
632func (suite *TestCreateContainer) TestServerError(c *C) {
633 response := makeHttpResponse(http.StatusNotFound, "not found")
634 context := makeStorageContext(&TestTransport{Response: response})
635 err := context.CreateContainer("container")
636 serverError, ok := err.(*ServerError)
637 c.Check(ok, Equals, true)
638 c.Check(serverError.HTTPStatus.StatusCode(), Equals, http.StatusNotFound)
639}
640
640type TestPutPage struct{}641type TestPutPage struct{}
641642
642var _ = Suite(&TestPutPage{})643var _ = Suite(&TestPutPage{})
643644
644// Basic happy path testing.645// Basic happy path testing.
645func (suite *TestPutPage) TestHappyPath(c *C) {646func (suite *TestPutPage) TestHappyPath(c *C) {
646 response := &http.Response{647 response := makeHttpResponse(http.StatusCreated, "")
647 Status: fmt.Sprintf("%d", http.StatusCreated),
648 StatusCode: http.StatusCreated,
649 }
650 transport := &TestTransport{Response: response}648 transport := &TestTransport{Response: response}
651 context := makeStorageContext(transport)649 context := makeStorageContext(transport)
652 randomData := MakeRandomByteSlice(10)650 randomData := MakeRandomByteSlice(10)
@@ -687,11 +685,8 @@
687685
688// Server-side errors are propagated back to the caller.686// Server-side errors are propagated back to the caller.
689func (suite *TestPutPage) TestErrorResponse(c *C) {687func (suite *TestPutPage) TestErrorResponse(c *C) {
690 response := &http.Response{688 responseBody := "<Error><Code>Frotzed</Code><Message>failed to put blob</Message></Error>"
691 Status: "102 Frotzed",689 response := makeHttpResponse(102, responseBody)
692 StatusCode: 102,
693 Body: makeResponseBody("<Error><Code>Frotzed</Code><Message>failed to put blob</Message></Error>"),
694 }
695 context := makeStorageContext(&TestTransport{Response: response})690 context := makeStorageContext(&TestTransport{Response: response})
696 err := context.PutPage(&PutPageRequest{691 err := context.PutPage(&PutPageRequest{
697 Container: "container", Filename: "filename", StartRange: 0,692 Container: "container", Filename: "filename", StartRange: 0,
@@ -702,16 +697,26 @@
702 c.Check(err, ErrorMatches, ".*failed to put blob.*")697 c.Check(err, ErrorMatches, ".*failed to put blob.*")
703}698}
704699
700// Azure HTTP errors (for instance 404 responses) are propagated back to
701// the caller as ServerError objects.
702func (suite *TestPutPage) TestServerError(c *C) {
703 response := makeHttpResponse(http.StatusNotFound, "not found")
704 context := makeStorageContext(&TestTransport{Response: response})
705 err := context.PutPage(&PutPageRequest{
706 Container: "container", Filename: "filename", StartRange: 0,
707 EndRange: 512, Data: nil})
708 serverError, ok := err.(*ServerError)
709 c.Check(ok, Equals, true)
710 c.Check(serverError.HTTPStatus.StatusCode(), Equals, http.StatusNotFound)
711}
712
705type TestPutBlob struct{}713type TestPutBlob struct{}
706714
707var _ = Suite(&TestPutBlob{})715var _ = Suite(&TestPutBlob{})
708716
709// Test basic PutBlob happy path functionality.717// Test basic PutBlob happy path functionality.
710func (suite *TestPutBlob) TestPutBlockBlob(c *C) {718func (suite *TestPutBlob) TestPutBlockBlob(c *C) {
711 response := &http.Response{719 response := makeHttpResponse(http.StatusCreated, "")
712 Status: fmt.Sprintf("%d", http.StatusCreated),
713 StatusCode: http.StatusCreated,
714 }
715 transport := &TestTransport{Response: response}720 transport := &TestTransport{Response: response}
716 context := makeStorageContext(transport)721 context := makeStorageContext(transport)
717722
@@ -728,10 +733,7 @@
728733
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.
730func (suite *TestPutBlob) TestPutPageBlob(c *C) {735func (suite *TestPutBlob) TestPutPageBlob(c *C) {
731 response := &http.Response{736 response := makeHttpResponse(http.StatusCreated, "")
732 Status: fmt.Sprintf("%d", http.StatusCreated),
733 StatusCode: http.StatusCreated,
734 }
735 transport := &TestTransport{Response: response}737 transport := &TestTransport{Response: response}
736 context := makeStorageContext(transport)738 context := makeStorageContext(transport)
737 err := context.PutBlob(&PutBlobRequest{739 err := context.PutBlob(&PutBlobRequest{
@@ -774,11 +776,8 @@
774776
775// Server-side errors are propagated back to the caller.777// Server-side errors are propagated back to the caller.
776func (suite *TestPutBlob) TestErrorResponse(c *C) {778func (suite *TestPutBlob) TestErrorResponse(c *C) {
777 response := &http.Response{779 responseBody := "<Error><Code>Frotzed</Code><Message>failed to put blob</Message></Error>"
778 Status: "102 Frotzed",780 response := makeHttpResponse(102, responseBody)
779 StatusCode: 102,
780 Body: makeResponseBody("<Error><Code>Frotzed</Code><Message>failed to put blob</Message></Error>"),
781 }
782 context := makeStorageContext(&TestTransport{Response: response})781 context := makeStorageContext(&TestTransport{Response: response})
783 err := context.PutBlob(&PutBlobRequest{782 err := context.PutBlob(&PutBlobRequest{
784 Container: "container", BlobType: "block", Filename: "blobname"})783 Container: "container", BlobType: "block", Filename: "blobname"})
@@ -788,15 +787,24 @@
788 c.Check(err, ErrorMatches, ".*failed to put blob.*")787 c.Check(err, ErrorMatches, ".*failed to put blob.*")
789}788}
790789
790// Azure HTTP errors (for instance 404 responses) are propagated back to
791// the caller as ServerError objects.
792func (suite *TestPutBlob) TestServerError(c *C) {
793 response := makeHttpResponse(http.StatusNotFound, "not found")
794 context := makeStorageContext(&TestTransport{Response: response})
795 err := context.PutBlob(&PutBlobRequest{
796 Container: "container", BlobType: "block", Filename: "blobname"})
797 serverError, ok := err.(*ServerError)
798 c.Check(ok, Equals, true)
799 c.Check(serverError.HTTPStatus.StatusCode(), Equals, http.StatusNotFound)
800}
801
791type TestPutBlock struct{}802type TestPutBlock struct{}
792803
793var _ = Suite(&TestPutBlock{})804var _ = Suite(&TestPutBlock{})
794805
795func (suite *TestPutBlock) Test(c *C) {806func (suite *TestPutBlock) Test(c *C) {
796 response := &http.Response{807 response := makeHttpResponse(http.StatusCreated, "")
797 Status: fmt.Sprintf("%d", http.StatusCreated),
798 StatusCode: http.StatusCreated,
799 }
800 transport := &TestTransport{Response: response}808 transport := &TestTransport{Response: response}
801 context := makeStorageContext(transport)809 context := makeStorageContext(transport)
802 blockid := "\x1b\xea\xf7Mv\xb5\xddH\xebm"810 blockid := "\x1b\xea\xf7Mv\xb5\xddH\xebm"
@@ -830,11 +838,8 @@
830838
831// Server-side errors are propagated back to the caller.839// Server-side errors are propagated back to the caller.
832func (suite *TestPutBlock) TestErrorResponse(c *C) {840func (suite *TestPutBlock) TestErrorResponse(c *C) {
833 response := &http.Response{841 responseBody := "<Error><Code>Frotzed</Code><Message>failed to put block</Message></Error>"
834 Status: "102 Frotzed",842 response := makeHttpResponse(102, responseBody)
835 StatusCode: 102,
836 Body: makeResponseBody("<Error><Code>Frotzed</Code><Message>failed to put block</Message></Error>"),
837 }
838 context := makeStorageContext(&TestTransport{Response: response})843 context := makeStorageContext(&TestTransport{Response: response})
839 data_reader := bytes.NewReader(MakeRandomByteSlice(10))844 data_reader := bytes.NewReader(MakeRandomByteSlice(10))
840 err := context.PutBlock("container", "blobname", "blockid", data_reader)845 err := context.PutBlock("container", "blobname", "blockid", data_reader)
@@ -844,15 +849,24 @@
844 c.Check(err, ErrorMatches, ".*failed to put block.*")849 c.Check(err, ErrorMatches, ".*failed to put block.*")
845}850}
846851
852// Azure HTTP errors (for instance 404 responses) are propagated back to
853// the caller as ServerError objects.
854func (suite *TestPutBlock) TestServerError(c *C) {
855 response := makeHttpResponse(http.StatusNotFound, "not found")
856 context := makeStorageContext(&TestTransport{Response: response})
857 data_reader := bytes.NewReader(MakeRandomByteSlice(10))
858 err := context.PutBlock("container", "blobname", "blockid", data_reader)
859 serverError, ok := err.(*ServerError)
860 c.Check(ok, Equals, true)
861 c.Check(serverError.HTTPStatus.StatusCode(), Equals, http.StatusNotFound)
862}
863
847type TestPutBlockList struct{}864type TestPutBlockList struct{}
848865
849var _ = Suite(&TestPutBlockList{})866var _ = Suite(&TestPutBlockList{})
850867
851func (suite *TestPutBlockList) Test(c *C) {868func (suite *TestPutBlockList) Test(c *C) {
852 response := &http.Response{869 response := makeHttpResponse(http.StatusCreated, "")
853 Status: fmt.Sprintf("%d", http.StatusCreated),
854 StatusCode: http.StatusCreated,
855 }
856 transport := &TestTransport{Response: response}870 transport := &TestTransport{Response: response}
857 context := makeStorageContext(transport)871 context := makeStorageContext(transport)
858 blocklist := &BlockList{}872 blocklist := &BlockList{}
@@ -888,11 +902,8 @@
888902
889// Server-side errors are propagated back to the caller.903// Server-side errors are propagated back to the caller.
890func (suite *TestPutBlockList) TestErrorResponse(c *C) {904func (suite *TestPutBlockList) TestErrorResponse(c *C) {
891 response := &http.Response{905 responseBody := "<Error><Code>Frotzed</Code><Message>failed to put blocklist</Message></Error>"
892 Status: "102 Frotzed",906 response := makeHttpResponse(102, responseBody)
893 StatusCode: 102,
894 Body: makeResponseBody("<Error><Code>Frotzed</Code><Message>failed to put blocklist</Message></Error>"),
895 }
896 context := makeStorageContext(&TestTransport{Response: response})907 context := makeStorageContext(&TestTransport{Response: response})
897 blocklist := &BlockList{}908 blocklist := &BlockList{}
898 err := context.PutBlockList("container", "blobname", blocklist)909 err := context.PutBlockList("container", "blobname", blocklist)
@@ -902,6 +913,18 @@
902 c.Check(err, ErrorMatches, ".*failed to put blocklist.*")913 c.Check(err, ErrorMatches, ".*failed to put blocklist.*")
903}914}
904915
916// Azure HTTP errors (for instance 404 responses) are propagated back to
917// the caller as ServerError objects.
918func (suite *TestPutBlockList) TestServerError(c *C) {
919 response := makeHttpResponse(http.StatusNotFound, "not found")
920 context := makeStorageContext(&TestTransport{Response: response})
921 blocklist := &BlockList{}
922 err := context.PutBlockList("container", "blobname", blocklist)
923 serverError, ok := err.(*ServerError)
924 c.Check(ok, Equals, true)
925 c.Check(serverError.HTTPStatus.StatusCode(), Equals, http.StatusNotFound)
926}
927
905type TestGetBlockList struct{}928type TestGetBlockList struct{}
906929
907var _ = Suite(&TestGetBlockList{})930var _ = Suite(&TestGetBlockList{})
@@ -909,7 +932,7 @@
909// The GetBlockList Storage API call returns a GetBlockList struct on932// The GetBlockList Storage API call returns a GetBlockList struct on
910// success.933// success.
911func (suite *TestGetBlockList) Test(c *C) {934func (suite *TestGetBlockList) Test(c *C) {
912 response_body := `935 responseBody := `
913 <?xml version="1.0" encoding="utf-8"?>936 <?xml version="1.0" encoding="utf-8"?>
914 <BlockList>937 <BlockList>
915 <CommittedBlocks>938 <CommittedBlocks>
@@ -926,11 +949,7 @@
926 </UncommittedBlocks>949 </UncommittedBlocks>
927 </BlockList>`950 </BlockList>`
928951
929 response := &http.Response{952 response := makeHttpResponse(http.StatusOK, responseBody)
930 Status: fmt.Sprintf("%d", http.StatusOK),
931 StatusCode: http.StatusOK,
932 Body: makeResponseBody(response_body),
933 }
934 transport := &TestTransport{Response: response}953 transport := &TestTransport{Response: response}
935 context := makeStorageContext(transport)954 context := makeStorageContext(transport)
936 results, err := context.GetBlockList("container", "myfilename")955 results, err := context.GetBlockList("container", "myfilename")
@@ -952,15 +971,15 @@
952 c.Assert(err, NotNil)971 c.Assert(err, NotNil)
953}972}
954973
955// Server-side errors are propagated back to the caller.974// Azure HTTP errors (for instance 404 responses) are propagated back to
956func (suite *TestGetBlockList) TestErrorResponse(c *C) {975// the caller as ServerError objects.
957 response := &http.Response{976func (suite *TestGetBlockList) TestServerError(c *C) {
958 Status: fmt.Sprintf("%d", http.StatusNotFound),977 response := makeHttpResponse(http.StatusNotFound, "not found")
959 StatusCode: http.StatusNotFound,
960 }
961 context := makeStorageContext(&TestTransport{Response: response})978 context := makeStorageContext(&TestTransport{Response: response})
962 _, err := context.GetBlockList("container", "myfilename")979 _, err := context.GetBlockList("container", "blobname")
963 c.Assert(err, NotNil)980 serverError, ok := err.(*ServerError)
981 c.Check(ok, Equals, true)
982 c.Check(serverError.HTTPStatus.StatusCode(), Equals, http.StatusNotFound)
964}983}
965984
966type TestDeleteBlob struct{}985type TestDeleteBlob struct{}
@@ -968,10 +987,7 @@
968var _ = Suite(&TestDeleteBlob{})987var _ = Suite(&TestDeleteBlob{})
969988
970func (suite *TestDeleteBlob) Test(c *C) {989func (suite *TestDeleteBlob) Test(c *C) {
971 response := &http.Response{990 response := makeHttpResponse(http.StatusAccepted, "")
972 Status: fmt.Sprintf("%d", http.StatusAccepted),
973 StatusCode: http.StatusAccepted,
974 }
975 transport := &TestTransport{Response: response}991 transport := &TestTransport{Response: response}
976 context := makeStorageContext(transport)992 context := makeStorageContext(transport)
977 err := context.DeleteBlob("container", "blobname")993 err := context.DeleteBlob("container", "blobname")
@@ -991,13 +1007,21 @@
991 c.Assert(err, NotNil)1007 c.Assert(err, NotNil)
992}1008}
9931009
1010// Azure HTTP errors (for instance 404 responses) are propagated back to
1011// the caller as ServerError objects.
1012func (suite *TestDeleteBlob) TestServerError(c *C) {
1013 response := makeHttpResponse(http.StatusNotFound, "not found")
1014 context := makeStorageContext(&TestTransport{Response: response})
1015 err := context.DeleteBlob("container", "blobname")
1016 serverError, ok := err.(*ServerError)
1017 c.Check(ok, Equals, true)
1018 c.Check(serverError.HTTPStatus.StatusCode(), Equals, http.StatusNotFound)
1019}
1020
994// Server-side errors are propagated back to the caller.1021// Server-side errors are propagated back to the caller.
995func (suite *TestDeleteBlob) TestErrorResponse(c *C) {1022func (suite *TestDeleteBlob) TestErrorResponse(c *C) {
996 response := &http.Response{1023 responseBody := "<Error><Code>Frotzed</Code><Message>failed to delete blob</Message></Error>"
997 Status: "146 Frotzed",1024 response := makeHttpResponse(146, responseBody)
998 StatusCode: 146,
999 Body: makeResponseBody("<Error><Code>Frotzed</Code><Message>failed to delete blob</Message></Error>"),
1000 }
1001 context := makeStorageContext(&TestTransport{Response: response})1025 context := makeStorageContext(&TestTransport{Response: response})
1002 err := context.DeleteBlob("container", "blobname")1026 err := context.DeleteBlob("container", "blobname")
1003 c.Assert(err, NotNil)1027 c.Assert(err, NotNil)
@@ -1011,12 +1035,8 @@
1011var _ = Suite(&TestGetBlob{})1035var _ = Suite(&TestGetBlob{})
10121036
1013func (suite *TestGetBlob) Test(c *C) {1037func (suite *TestGetBlob) Test(c *C) {
1014 response_body := "blob-in-a-can"1038 responseBody := "blob-in-a-can"
1015 response := &http.Response{1039 response := makeHttpResponse(http.StatusOK, responseBody)
1016 Status: fmt.Sprintf("%d", http.StatusOK),
1017 StatusCode: http.StatusOK,
1018 Body: makeResponseBody(response_body),
1019 }
1020 transport := &TestTransport{Response: response}1040 transport := &TestTransport{Response: response}
1021 context := makeStorageContext(transport)1041 context := makeStorageContext(transport)
1022 reader, err := context.GetBlob("container", "blobname")1042 reader, err := context.GetBlob("container", "blobname")
@@ -1030,7 +1050,7 @@
10301050
1031 data, err := ioutil.ReadAll(reader)1051 data, err := ioutil.ReadAll(reader)
1032 c.Assert(err, IsNil)1052 c.Assert(err, IsNil)
1033 c.Check(string(data), Equals, response_body)1053 c.Check(string(data), Equals, responseBody)
1034}1054}
10351055
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.
@@ -1042,6 +1062,20 @@
1042 c.Assert(err, NotNil)1062 c.Assert(err, NotNil)
1043}1063}
10441064
1065// Azure HTTP errors (for instance 404 responses) are propagated back to
1066// the caller as ServerError objects.
1067func (suite *TestGetBlob) TestServerError(c *C) {
1068 response := makeHttpResponse(http.StatusNotFound, "not found")
1069 context := makeStorageContext(&TestTransport{Response: response})
1070 reader, err := context.GetBlob("container", "blobname")
1071 c.Check(reader, IsNil)
1072 c.Assert(err, NotNil)
1073 serverError, ok := err.(*ServerError)
1074 c.Check(ok, Equals, true)
1075 c.Check(serverError.HTTPStatus.StatusCode(), Equals, http.StatusNotFound)
1076 c.Check(IsNotFoundError(err), Equals, true)
1077}
1078
1045// Server-side errors are propagated back to the caller.1079// Server-side errors are propagated back to the caller.
1046func (suite *TestGetBlob) TestErrorResponse(c *C) {1080func (suite *TestGetBlob) TestErrorResponse(c *C) {
1047 response := &http.Response{1081 response := &http.Response{

Subscribers

People subscribed via source and target branches

to all changes: