Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make upgrade-gui a controller command. #6181

Merged
merged 1 commit into from Sep 7, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
8 changes: 4 additions & 4 deletions api/gui.go → api/controller/gui.go
@@ -1,7 +1,7 @@
// Copyright 2016 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package api
package controller

import (
"bytes"
Expand All @@ -24,7 +24,7 @@ const (
// GUIArchives retrieves information about Juju GUI archives currently present
// in the Juju controller.
func (c *Client) GUIArchives() ([]params.GUIArchiveVersion, error) {
httpClient, err := c.st.RootHTTPClient()
httpClient, err := c.facade.RawAPICaller().HTTPClient()
if err != nil {
return nil, errors.Annotate(err, "cannot retrieve HTTP client")
}
Expand All @@ -50,7 +50,7 @@ func (c *Client) UploadGUIArchive(r io.ReadSeeker, hash string, size int64, vers
req.ContentLength = size

// Retrieve a client and send the request.
httpClient, err := c.st.RootHTTPClient()
httpClient, err := c.facade.RawAPICaller().HTTPClient()
if err != nil {
return false, errors.Annotate(err, "cannot retrieve HTTP client")
}
Expand Down Expand Up @@ -78,7 +78,7 @@ func (c *Client) SelectGUIVersion(vers version.Number) error {
}

// Retrieve a client and send the request.
httpClient, err := c.st.RootHTTPClient()
httpClient, err := c.facade.RawAPICaller().HTTPClient()
if err != nil {
return errors.Annotate(err, "cannot retrieve HTTP client")
}
Expand Down
148 changes: 148 additions & 0 deletions api/controller/gui_test.go
@@ -0,0 +1,148 @@
// Copyright 2016 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package controller_test

import (
"bytes"
"encoding/json"
"io/ioutil"
"net/http"

"github.com/juju/testing"
jc "github.com/juju/testing/checkers"
"github.com/juju/version"
gc "gopkg.in/check.v1"

"github.com/juju/juju/api/base"
"github.com/juju/juju/api/controller"
"github.com/juju/juju/apiserver/params"
)

// sendJSONResponse encodes the given content as JSON and writes it to the
// given response writer.
func sendJSONResponse(c *gc.C, w http.ResponseWriter, content interface{}) {
w.Header().Set("Content-Type", params.ContentTypeJSON)
encoder := json.NewEncoder(w)
err := encoder.Encode(content)
c.Assert(err, jc.ErrorIsNil)
}

// withHTTPClient sets up a fixture with the given address and handle, then
// runs the given test and checks that the HTTP handler has been called with
// the given method.
func withHTTPClient(c *gc.C, address, expectMethod string, handle func(http.ResponseWriter, *http.Request), test func(*controller.Client)) {
fix := newHTTPFixture(address, handle)
stub := fix.run(c, func(ac base.APICallCloser) {
client := controller.NewClient(ac)
test(client)
})
stub.CheckCalls(c, []testing.StubCall{{expectMethod, nil}})
}

func (s *Suite) TestGUIArchives(c *gc.C) {
response := params.GUIArchiveResponse{
Versions: []params.GUIArchiveVersion{{
Version: version.MustParse("1.0.0"),
SHA256: "hash1",
Current: false,
}, {
Version: version.MustParse("2.0.0"),
SHA256: "hash2",
Current: true,
}},
}
withHTTPClient(c, "/gui-archive", "GET", func(w http.ResponseWriter, req *http.Request) {
defer req.Body.Close()
sendJSONResponse(c, w, response)
}, func(client *controller.Client) {
// Retrieve the GUI archive versions.
versions, err := client.GUIArchives()
c.Assert(err, jc.ErrorIsNil)
c.Assert(versions, jc.DeepEquals, response.Versions)
})
}

func (s *Suite) TestGUIArchivesError(c *gc.C) {
withHTTPClient(c, "/gui-archive", "GET", func(w http.ResponseWriter, req *http.Request) {
defer req.Body.Close()
w.WriteHeader(http.StatusBadRequest)
}, func(client *controller.Client) {
// Call to get GUI archive versions.
versions, err := client.GUIArchives()
c.Assert(err, gc.ErrorMatches, "cannot retrieve GUI archives info: .*")
c.Assert(versions, gc.IsNil)
})
}

func (s *Suite) TestUploadGUIArchive(c *gc.C) {
archive := []byte("archive content")
hash, size, vers := "archive-hash", int64(len(archive)), version.MustParse("2.1.0")
withHTTPClient(c, "/gui-archive", "POST", func(w http.ResponseWriter, req *http.Request) {
defer req.Body.Close()
err := req.ParseForm()
c.Assert(err, jc.ErrorIsNil)
// Check version and content length.
c.Assert(req.Form.Get("version"), gc.Equals, vers.String())
c.Assert(req.ContentLength, gc.Equals, size)
// Check request body.
obtainedArchive, err := ioutil.ReadAll(req.Body)
c.Assert(err, jc.ErrorIsNil)
c.Assert(obtainedArchive, gc.DeepEquals, archive)
// Check hash.
h := req.Form.Get("hash")
c.Assert(h, gc.Equals, hash)
// Send the response.
sendJSONResponse(c, w, params.GUIArchiveVersion{
Current: true,
})
}, func(client *controller.Client) {
// Upload a new Juju GUI archive.
current, err := client.UploadGUIArchive(bytes.NewReader(archive), hash, size, vers)
c.Assert(err, jc.ErrorIsNil)
c.Assert(current, jc.IsTrue)
})
}

func (s *Suite) TestUploadGUIArchiveError(c *gc.C) {
archive := []byte("archive content")
hash, size, vers := "archive-hash", int64(len(archive)), version.MustParse("2.1.0")
withHTTPClient(c, "/gui-archive", "POST", func(w http.ResponseWriter, req *http.Request) {
defer req.Body.Close()
w.WriteHeader(http.StatusBadRequest)
}, func(client *controller.Client) {
// Call to upload a new Juju GUI archive.
current, err := client.UploadGUIArchive(bytes.NewReader(archive), hash, size, vers)
c.Assert(err, gc.ErrorMatches, "cannot upload the GUI archive: .*")
c.Assert(current, jc.IsFalse)
})
}

func (s *Suite) TestSelectGUIVersion(c *gc.C) {
vers := version.MustParse("2.0.42")
withHTTPClient(c, "/gui-version", "PUT", func(w http.ResponseWriter, req *http.Request) {
defer req.Body.Close()
// Check request body.
var request params.GUIVersionRequest
decoder := json.NewDecoder(req.Body)
err := decoder.Decode(&request)
c.Assert(err, jc.ErrorIsNil)
c.Assert(request.Version, gc.Equals, vers)
}, func(client *controller.Client) {
// Switch to a specific Juju GUI version.
err := client.SelectGUIVersion(vers)
c.Assert(err, jc.ErrorIsNil)
})
}

func (s *Suite) TestSelectGUIVersionError(c *gc.C) {
vers := version.MustParse("2.0.42")
withHTTPClient(c, "/gui-version", "PUT", func(w http.ResponseWriter, req *http.Request) {
defer req.Body.Close()
w.WriteHeader(http.StatusBadRequest)
}, func(client *controller.Client) {
// Call to select a Juju GUI version.
err := client.SelectGUIVersion(vers)
c.Assert(err, gc.ErrorMatches, "cannot select GUI version: .*")
})
}
86 changes: 86 additions & 0 deletions api/controller/http_test.go
@@ -0,0 +1,86 @@
// Copyright 2016 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package controller_test

import (
"net"
"net/http"
"net/url"

"github.com/juju/httprequest"
"github.com/juju/testing"
jc "github.com/juju/testing/checkers"
gc "gopkg.in/check.v1"

"github.com/juju/juju/api/base"
)

// newHTTPFixture creates and returns an HTTP fixture to be used in order to
// mock controller HTTP requests to the given controller address.
// Use it like in the following example:
// fix := newHTTPFixture("/my/controller/path", func(w http.ResponseWriter, req *http.Request) {
// // Simulate what's returned by the server.
// })
// stub := fix.run(c, func(ac base.APICallCloser) {
// // Do something with the API caller.
// })
// At this point the stub, if the handler has been called, includes one call
// with the HTTP method requested while running the test function.
func newHTTPFixture(address string, handle func(http.ResponseWriter, *http.Request)) *httpFixture {
return &httpFixture{
address: address,
handle: handle,
}
}

// httpFixture is used to mock controller HTTP API calls.
type httpFixture struct {
address string
handle func(http.ResponseWriter, *http.Request)
}

// run sets up the fixture and run the given test. See newHTTPFixture for an
// example of how to use this.
func (f *httpFixture) run(c *gc.C, test func(base.APICallCloser)) *testing.Stub {
stub := &testing.Stub{}
lis, err := net.Listen("tcp", "127.0.0.1:0")
c.Assert(err, jc.ErrorIsNil)
defer lis.Close()
mux := http.NewServeMux()
mux.HandleFunc(f.address, func(w http.ResponseWriter, r *http.Request) {
stub.AddCall(r.Method)
f.handle(w, r)
})
go func() {
http.Serve(lis, mux)
}()
test(&httpAPICallCloser{
url: &url.URL{
Scheme: "http",
Host: lis.Addr().String(),
},
})
return stub
}

var _ base.APICallCloser = (*httpAPICallCloser)(nil)

// httpAPICallCloser implements base.APICallCloser.
type httpAPICallCloser struct {
base.APICallCloser
url *url.URL
}

// BestFacadeVersion implements base.APICallCloser.
func (*httpAPICallCloser) BestFacadeVersion(facade string) int {
return 42
}

// HTTPClient implements base.APICallCloser. The returned HTTP client can be
// used to send requests to the testing server set up in httpFixture.run().
func (ac *httpAPICallCloser) HTTPClient() (*httprequest.Client, error) {
return &httprequest.Client{
BaseURL: ac.url.String(),
}, nil
}