-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Move uniter resource endpoint to apiserver
This is the last component architecture apiserver part to be moved. Much unnecessary abstraction and dead code has been removed. The code is now much more straightforward.
- Loading branch information
Menno Smits
committed
Mar 20, 2017
1 parent
bf6f948
commit 61010bf
Showing
13 changed files
with
259 additions
and
592 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
// Copyright 2017 Canonical Ltd. | ||
// Licensed under the AGPLv3, see LICENCE file for details. | ||
|
||
package apiserver | ||
|
||
import ( | ||
"fmt" | ||
"io" | ||
"net/http" | ||
|
||
"github.com/juju/errors" | ||
"gopkg.in/juju/names.v2" | ||
|
||
"github.com/juju/juju/apiserver/params" | ||
"github.com/juju/juju/resource" | ||
"github.com/juju/juju/resource/api" | ||
) | ||
|
||
// ResourcesHandler is the HTTP handler for unit agent downloads of | ||
// resources. | ||
type UnitResourcesHandler struct { | ||
NewOpener func(*http.Request, ...string) (resource.Opener, func(), error) | ||
} | ||
|
||
// ServeHTTP implements http.Handler. | ||
func (h *UnitResourcesHandler) ServeHTTP(resp http.ResponseWriter, req *http.Request) { | ||
switch req.Method { | ||
case "GET": | ||
opener, closer, err := h.NewOpener(req, names.UnitTagKind) | ||
if err != nil { | ||
api.SendHTTPError(resp, err) | ||
return | ||
} | ||
defer closer() | ||
|
||
name := req.URL.Query().Get(":resource") | ||
opened, err := opener.OpenResource(name) | ||
if err != nil { | ||
logger.Errorf("cannot fetch resource reader: %v", err) | ||
api.SendHTTPError(resp, err) | ||
return | ||
} | ||
defer opened.Close() | ||
|
||
hdr := resp.Header() | ||
hdr.Set("Content-Type", params.ContentTypeRaw) | ||
hdr.Set("Content-Length", fmt.Sprint(opened.Size)) | ||
hdr.Set("Content-Sha384", opened.Fingerprint.String()) | ||
|
||
resp.WriteHeader(http.StatusOK) | ||
if _, err := io.Copy(resp, opened); err != nil { | ||
// We cannot use SendHTTPError here, so we log the error | ||
// and move on. | ||
logger.Errorf("unable to complete stream for resource: %v", err) | ||
return | ||
} | ||
default: | ||
api.SendHTTPError(resp, errors.MethodNotAllowedf("unsupported method: %q", req.Method)) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,148 @@ | ||
// Copyright 2017 Canonical Ltd. | ||
// Licensed under the AGPLv3, see LICENCE file for details. | ||
|
||
package apiserver_test | ||
|
||
import ( | ||
"net/http" | ||
"net/http/httptest" | ||
"net/url" | ||
|
||
"github.com/juju/names" | ||
"github.com/juju/testing" | ||
jc "github.com/juju/testing/checkers" | ||
gc "gopkg.in/check.v1" | ||
|
||
"github.com/juju/juju/apiserver" | ||
"github.com/juju/juju/resource" | ||
"github.com/juju/juju/resource/resourcetesting" | ||
) | ||
|
||
type UnitResourcesHandlerSuite struct { | ||
testing.IsolationSuite | ||
|
||
stub *testing.Stub | ||
urlStr string | ||
recorder *httptest.ResponseRecorder | ||
} | ||
|
||
var _ = gc.Suite(&UnitResourcesHandlerSuite{}) | ||
|
||
func (s *UnitResourcesHandlerSuite) SetUpTest(c *gc.C) { | ||
s.IsolationSuite.SetUpTest(c) | ||
|
||
s.stub = new(testing.Stub) | ||
|
||
args := url.Values{} | ||
args.Add(":unit", "foo/0") | ||
args.Add(":resource", "blob") | ||
s.urlStr = "https://api:17017/?" + args.Encode() | ||
|
||
s.recorder = httptest.NewRecorder() | ||
} | ||
|
||
func (s *UnitResourcesHandlerSuite) closer() { | ||
s.stub.AddCall("Close") | ||
} | ||
|
||
func (s *UnitResourcesHandlerSuite) TestWrongMethod(c *gc.C) { | ||
handler := &apiserver.UnitResourcesHandler{} | ||
|
||
req, err := http.NewRequest("POST", s.urlStr, nil) | ||
c.Assert(err, jc.ErrorIsNil) | ||
|
||
handler.ServeHTTP(s.recorder, req) | ||
|
||
resp := s.recorder.Result() | ||
c.Assert(resp.StatusCode, gc.Equals, http.StatusMethodNotAllowed) | ||
s.stub.CheckNoCalls(c) | ||
} | ||
|
||
func (s *UnitResourcesHandlerSuite) TestOpenerCreationError(c *gc.C) { | ||
failure, expectedBody := apiFailure("boom", "") | ||
handler := &apiserver.UnitResourcesHandler{ | ||
NewOpener: func(_ *http.Request, kinds ...string) (resource.Opener, func(), error) { | ||
return nil, nil, failure | ||
}, | ||
} | ||
|
||
req, err := http.NewRequest("GET", s.urlStr, nil) | ||
c.Assert(err, jc.ErrorIsNil) | ||
|
||
handler.ServeHTTP(s.recorder, req) | ||
|
||
s.checkResp(c, | ||
http.StatusInternalServerError, | ||
"application/json", | ||
expectedBody, | ||
) | ||
} | ||
|
||
func (s *UnitResourcesHandlerSuite) TestOpenResourceError(c *gc.C) { | ||
opener := &stubResourceOpener{ | ||
Stub: s.stub, | ||
} | ||
failure, expectedBody := apiFailure("boom", "") | ||
s.stub.SetErrors(failure) | ||
handler := &apiserver.UnitResourcesHandler{ | ||
NewOpener: func(_ *http.Request, kinds ...string) (resource.Opener, func(), error) { | ||
s.stub.AddCall("NewOpener", kinds) | ||
return opener, s.closer, nil | ||
}, | ||
} | ||
|
||
req, err := http.NewRequest("GET", s.urlStr, nil) | ||
c.Assert(err, jc.ErrorIsNil) | ||
|
||
handler.ServeHTTP(s.recorder, req) | ||
|
||
s.checkResp(c, http.StatusInternalServerError, "application/json", expectedBody) | ||
s.stub.CheckCalls(c, []testing.StubCall{ | ||
{"NewOpener", []interface{}{[]string{names.UnitTagKind}}}, | ||
{"OpenResource", []interface{}{"blob"}}, | ||
{"Close", nil}, | ||
}) | ||
} | ||
|
||
func (s *UnitResourcesHandlerSuite) TestSuccess(c *gc.C) { | ||
const body = "some data" | ||
opened := resourcetesting.NewResource(c, new(testing.Stub), "blob", "app", body) | ||
opener := &stubResourceOpener{ | ||
Stub: s.stub, | ||
ReturnOpenResource: opened, | ||
} | ||
handler := &apiserver.UnitResourcesHandler{ | ||
NewOpener: func(_ *http.Request, kinds ...string) (resource.Opener, func(), error) { | ||
s.stub.AddCall("NewOpener", kinds) | ||
return opener, s.closer, nil | ||
}, | ||
} | ||
|
||
req, err := http.NewRequest("GET", s.urlStr, nil) | ||
c.Assert(err, jc.ErrorIsNil) | ||
|
||
handler.ServeHTTP(s.recorder, req) | ||
|
||
s.checkResp(c, http.StatusOK, "application/octet-stream", body) | ||
s.stub.CheckCalls(c, []testing.StubCall{ | ||
{"NewOpener", []interface{}{[]string{names.UnitTagKind}}}, | ||
{"OpenResource", []interface{}{"blob"}}, | ||
{"Close", nil}, | ||
}) | ||
} | ||
func (s *UnitResourcesHandlerSuite) checkResp(c *gc.C, status int, ctype, body string) { | ||
checkHTTPResp(c, s.recorder, status, ctype, body) | ||
} | ||
|
||
type stubResourceOpener struct { | ||
*testing.Stub | ||
ReturnOpenResource resource.Opened | ||
} | ||
|
||
func (s *stubResourceOpener) OpenResource(name string) (resource.Opened, error) { | ||
s.AddCall("OpenResource", name) | ||
if err := s.NextErr(); err != nil { | ||
return resource.Opened{}, err | ||
} | ||
return s.ReturnOpenResource, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.