From 6d905d5aaac54a594c9896faebda09b6fee952dd Mon Sep 17 00:00:00 2001 From: Martin Hilton Date: Tue, 5 Jul 2016 14:41:54 +0100 Subject: [PATCH] Add login to websocket API Support macaroon login on the websocket API. If connecting to a non-controller model, send a redirect to the hosing controller. --- dependencies.tsv | 2 +- internal/apitest/apitest.go | 63 ++++++++++ internal/jem/jem.go | 15 +++ internal/jem/jem_test.go | 26 ++++ internal/jujuapi/api.go | 2 +- internal/jujuapi/websocket.go | 177 +++++++++++++++++++++++--- internal/jujuapi/websocket_test.go | 114 ++++++++++++++++- internal/v2/api_test.go | 191 +++++++++++------------------ 8 files changed, 443 insertions(+), 147 deletions(-) diff --git a/dependencies.tsv b/dependencies.tsv index 98a79916d..ae6e32662 100644 --- a/dependencies.tsv +++ b/dependencies.tsv @@ -17,7 +17,7 @@ github.com/juju/gomaasapi git 5bd7212f416a2d801e4a39800b66e1ee4461c42e 2016-05-0 github.com/juju/httpprof git 14bf14c307672fd2456bdbf35d19cf0ccd3cf565 2014-12-17T16:00:36Z github.com/juju/httprequest git 796aaafaf712f666df58d31a482c51233038bf9f 2016-05-03T15:03:27Z github.com/juju/idmclient git 3dda079a75cccb85083d4c3877e638f5d6ab79c2 2016-05-26T05:00:34Z -github.com/juju/juju git 894de5b4a1d3955f5edd79f19255e41c0ab23d9c 2016-06-30T14:53:07Z +github.com/juju/juju git 50dd7bb3961f0fb8090c438dc111f68efc4e605e 2016-07-01T23:42:52Z github.com/juju/loggo git 8477fc936adf0e382d680310047ca27e128a309a 2015-05-27T03:58:39Z github.com/juju/mgoutil git 5f725bbe1f9842b097129570e79441be24012e9c 2016-05-20T10:19:24Z github.com/juju/mutex git 59c26ee163447c5c57f63ff71610d433862013de 2016-06-17T01:09:07Z diff --git a/internal/apitest/apitest.go b/internal/apitest/apitest.go index 86cc00b38..bf71b38c9 100644 --- a/internal/apitest/apitest.go +++ b/internal/apitest/apitest.go @@ -12,6 +12,7 @@ import ( "github.com/juju/idmclient/idmtest" corejujutesting "github.com/juju/juju/juju/testing" "github.com/juju/testing" + jc "github.com/juju/testing/checkers" "github.com/juju/testing/httptesting" gc "gopkg.in/check.v1" "gopkg.in/macaroon-bakery.v1/bakery" @@ -127,6 +128,68 @@ func (s *Suite) NewServer(c *gc.C, session *mgo.Session, idmSrv *idmtest.Server) return srv.(*jemserver.Server) } +// AssertAddController adds the specified controller using AddController +// and checks that id succeeds. It returns the controller id. +func (s *Suite) AssertAddController(c *gc.C, path params.EntityPath, loc map[string]string) params.EntityPath { + err := s.AddController(c, path, loc) + c.Assert(err, jc.ErrorIsNil) + return path +} + +// AddController adds a new controller with the provided path and any +// specified location parameters. +func (s *Suite) AddController(c *gc.C, path params.EntityPath, loc map[string]string) error { + // Note that because the cookies acquired in this request don't + // persist, the discharge macaroon we get won't affect subsequent + // requests in the caller. + info := s.APIInfo(c) + p := ¶ms.AddController{ + EntityPath: path, + Info: params.ControllerInfo{ + HostPorts: info.Addrs, + CACert: info.CACert, + User: info.Tag.Id(), + Password: info.Password, + ControllerUUID: info.ModelTag.Id(), + Location: loc, + }, + } + // If locations are specified then make the controller public so + // that it can be found by seraching on those locations. + if len(loc) > 0 { + p.Info.Public = true + s.IDMSrv.AddUser(string(path.User), "controller-admin") + } + return s.NewClient(path.User).AddController(p) +} + +const dummySSHKey = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDOjaOjVRHchF2RFCKQdgBqrIA5nOoqSprLK47l2th5I675jw+QYMIihXQaITss3hjrh3+5ITyBO41PS5rHLNGtlYUHX78p9CHNZsJqHl/z1Ub1tuMe+/5SY2MkDYzgfPtQtVsLasAIiht/5g78AMMXH3HeCKb9V9cP6/lPPq6mCMvg8TDLrPp/P2vlyukAsJYUvVgoaPDUBpedHbkMj07pDJqe4D7c0yEJ8hQo/6nS+3bh9Q1NvmVNsB1pbtk3RKONIiTAXYcjclmOljxxJnl1O50F5sOIi38vyl7Q63f6a3bXMvJEf1lnPNJKAxspIfEu8gRasny3FEsbHfrxEwVj rog@rog-x220" + +var dummyModelConfig = map[string]interface{}{ + "authorized-keys": dummySSHKey, + "controller": true, +} + +// CreateModel creates a new model with the specified path on the +// specified controller, using the specified templates. It returns the +// new model's path, user and uuid. +func (s *Suite) CreateModel(c *gc.C, path, ctlPath params.EntityPath, templates ...params.EntityPath) (modelPath params.EntityPath, user, uuid string) { + // Note that because the cookies acquired in this request don't + // persist, the discharge macaroon we get won't affect subsequent + // requests in the caller. + resp, err := s.NewClient(path.User).NewModel(¶ms.NewModel{ + User: path.User, + Info: params.NewModelInfo{ + Name: path.Name, + Controller: &ctlPath, + Config: dummyModelConfig, + TemplatePaths: templates, + }, + }) + c.Assert(err, gc.IsNil) + return resp.Path, resp.User, resp.UUID +} + // Do returns a Do function appropriate for using in httptesting.AssertJSONCall.Do // that makes its HTTP request acting as the given client. // If client is nil, it uses httpbakery.NewClient instead. diff --git a/internal/jem/jem.go b/internal/jem/jem.go index 28c647e43..0709d46a1 100644 --- a/internal/jem/jem.go +++ b/internal/jem/jem.go @@ -429,6 +429,21 @@ func (j *JEM) Model(path params.EntityPath) (*mongodoc.Model, error) { return &m, nil } +// ModelFromUUID returns the document representing the model with the +// given UUID. It returns an error with a params.ErrNotFound cause if the +// controller was not found. +func (j *JEM) ModelFromUUID(uuid string) (*mongodoc.Model, error) { + var m mongodoc.Model + err := j.DB.Models().Find(bson.D{{"uuid", uuid}}).One(&m) + if err == mgo.ErrNotFound { + return nil, errgo.WithCausef(nil, params.ErrNotFound, "model %q not found", uuid) + } + if err != nil { + return nil, errgo.Notef(err, "cannot get model %q", uuid) + } + return &m, nil +} + // ErrAPIConnection is returned by OpenAPI and OpenAPIFromDocs // when the API connection cannot be made. var ErrAPIConnection = errgo.New("cannot connect to API") diff --git a/internal/jem/jem_test.go b/internal/jem/jem_test.go index c56a9efc6..20b68106a 100644 --- a/internal/jem/jem_test.go +++ b/internal/jem/jem_test.go @@ -529,6 +529,32 @@ func (s *jemSuite) TestAddModel(c *gc.C) { c.Assert(errgo.Cause(err), gc.Equals, params.ErrAlreadyExists) } +func (s *jemSuite) TestModelFromUUID(c *gc.C) { + uuid := "99999999-9999-9999-9999-999999999999" + path := params.EntityPath{"bob", "x"} + m := &mongodoc.Model{ + Id: "ignored", + Path: path, + UUID: uuid, + } + err := s.store.AddModel(m) + c.Assert(err, gc.IsNil) + c.Assert(m, jc.DeepEquals, &mongodoc.Model{ + Id: "bob/x", + Path: path, + UUID: uuid, + }) + + m1, err := s.store.ModelFromUUID(uuid) + c.Assert(err, gc.IsNil) + c.Assert(m1, jc.DeepEquals, m) + + m2, err := s.store.ModelFromUUID("no-such-uuid") + c.Assert(err, gc.ErrorMatches, `model "no-such-uuid" not found`) + c.Assert(errgo.Cause(err), gc.Equals, params.ErrNotFound) + c.Assert(m2, gc.IsNil) +} + func (s *jemSuite) TestAddTemplate(c *gc.C) { path := params.EntityPath{"bob", "x"} tmpl := &mongodoc.Template{ diff --git a/internal/jujuapi/api.go b/internal/jujuapi/api.go index 50e39a0c5..e26fe7649 100644 --- a/internal/jujuapi/api.go +++ b/internal/jujuapi/api.go @@ -13,7 +13,7 @@ import ( "github.com/CanonicalLtd/jem/internal/jemserver" ) -func NewAPIHandler(jp *jem.Pool, sp jemserver.Params) ([]httprequest.Handler, error) { +func NewAPIHandler(jp *jem.Pool, _ jemserver.Params) ([]httprequest.Handler, error) { return []httprequest.Handler{ newWebSocketHandler(jp), }, nil diff --git a/internal/jujuapi/websocket.go b/internal/jujuapi/websocket.go index 69209a74d..f8811af7e 100644 --- a/internal/jujuapi/websocket.go +++ b/internal/jujuapi/websocket.go @@ -3,17 +3,51 @@ package jujuapi import ( + "reflect" + "github.com/juju/juju/apiserver/common" "github.com/juju/juju/apiserver/observer" + jujuparams "github.com/juju/juju/apiserver/params" + "github.com/juju/juju/network" "github.com/juju/juju/rpc" "github.com/juju/juju/rpc/jsoncodec" "github.com/juju/juju/rpc/rpcreflect" + "github.com/juju/loggo" "golang.org/x/net/websocket" + "gopkg.in/errgo.v1" + "gopkg.in/juju/names.v2" + "gopkg.in/macaroon-bakery.v1/bakery" + "gopkg.in/macaroon-bakery.v1/bakery/checkers" "github.com/CanonicalLtd/jem/internal/jem" + "github.com/CanonicalLtd/jem/internal/mongodoc" + "github.com/CanonicalLtd/jem/params" ) -// newWSServer creates a new websocket server suitible for handling the api for modelUUID. +var logger = loggo.GetLogger("jem.internal.jujuapi") + +// mapError maps JEM errors to errors suitable for use with the juju API. +func mapError(err error) error { + if err == nil { + return nil + } + logger.Debugf("error: %s\n details: %s", err.Error(), errgo.Details(err)) + if _, ok := err.(*jujuparams.Error); ok { + return err + } + msg := err.Error() + code := "" + switch errgo.Cause(err) { + case params.ErrNotFound: + code = jujuparams.CodeNotFound + } + return &jujuparams.Error{ + Message: msg, + Code: code, + } +} + +// newWSServer creates a new WebSocket server suitible for handling the API for modelUUID. func newWSServer(jem *jem.JEM, modelUUID string) websocket.Server { hnd := wsHandler{ jem: jem, @@ -24,42 +58,147 @@ func newWSServer(jem *jem.JEM, modelUUID string) websocket.Server { } } -// wsHandler is a handler for a particular websocket connection. +// wsHandler is a handler for a particular WebSocket connection. type wsHandler struct { - jem *jem.JEM - modelUUID string + jem *jem.JEM + modelUUID string + conn *rpc.Conn + model *mongodoc.Model + controller *mongodoc.Controller } // handle handles the connection. func (h *wsHandler) handle(wsConn *websocket.Conn) { codec := jsoncodec.NewWebsocket(wsConn) - conn := rpc.NewConn(codec, observer.None()) + h.conn = rpc.NewConn(codec, observer.None()) - // TODO(mhilton) serve something useful on this connection. - err := common.UnknownModelError(h.modelUUID) - conn.ServeFinder(&errRoot{err}, serverError) - conn.Start() + var root rpc.MethodFinder + root = adminRoot{admin{h}} + err := h.resolveUUID() + if err != nil { + root = &errRoot{err} + } + h.conn.ServeFinder(root, mapError) + h.conn.Start() select { - case <-conn.Dead(): + case <-h.conn.Dead(): } - conn.Close() + h.conn.Close() } -func serverError(err error) error { - if err := common.ServerError(err); err != nil { - return err +func (h *wsHandler) resolveUUID() error { + var err error + h.model, err = h.jem.ModelFromUUID(h.modelUUID) + if err != nil { + return errgo.Mask(err, errgo.Is(params.ErrNotFound)) + } + h.controller, err = h.jem.Controller(h.model.Controller) + return errgo.Mask(err) +} + +type admin struct { + handler *wsHandler +} + +func (a admin) Admin(id string) (admin, error) { + if id != "" { + // Safeguard id for possible future use. + return admin{}, common.ErrBadId + } + return a, nil +} + +// Login implements the Login method on the Admin facade. +func (a admin) Login(req jujuparams.LoginRequest) (jujuparams.LoginResultV1, error) { + // JAAS only supports macaroon login, ignore all the other fields. + attr, err := a.handler.jem.Bakery.CheckAny(req.Macaroons, nil, checkers.TimeBefore) + if err != nil { + if verr, ok := err.(*bakery.VerificationError); ok { + m, err := a.handler.jem.NewMacaroon() + if err != nil { + return jujuparams.LoginResultV1{}, errgo.Notef(err, "cannot create macaroon") + } + return jujuparams.LoginResultV1{ + DischargeRequired: m, + DischargeRequiredReason: verr.Error(), + }, nil + } + return jujuparams.LoginResultV1{}, errgo.Mask(err) + } + a.handler.jem.Auth.Username = attr["username"] + if err := a.handler.jem.CheckCanRead(a.handler.model); err != nil { + return jujuparams.LoginResultV1{}, errgo.Mask(err, errgo.Is(params.ErrUnauthorized)) + } + + // Login successful + a.handler.jem.Auth = jem.Authorization{attr["username"]} + + // If the UUID is for a model send a redirect error. + if a.handler.model.Id != a.handler.controller.Id { + return jujuparams.LoginResultV1{}, &jujuparams.Error{ + Code: jujuparams.CodeRedirect, + Message: "redirect required", + } + } + + // TODO (mhilton) serve some new methods + return jujuparams.LoginResultV1{ + ModelTag: names.NewModelTag(a.handler.model.UUID).String(), + ControllerTag: names.NewModelTag(a.handler.controller.UUID).String(), + ServerVersion: "2.0.0", + }, nil +} + +// RedirectInfo implements the RedirectInfo method on the Admin facade. +func (a admin) RedirectInfo() (jujuparams.RedirectInfoResult, error) { + if a.handler.jem.Auth.Username == "" { + return jujuparams.RedirectInfoResult{}, params.ErrUnauthorized + } + if err := a.handler.jem.CheckCanRead(a.handler.model); err != nil { + return jujuparams.RedirectInfoResult{}, errgo.Mask(err, errgo.Is(params.ErrUnauthorized)) + } + if a.handler.model.Id == a.handler.controller.Id { + return jujuparams.RedirectInfoResult{}, errgo.New("not redirected") + } + nhps, err := network.ParseHostPorts(a.handler.controller.HostPorts...) + if err != nil { + return jujuparams.RedirectInfoResult{}, errgo.Mask(err) + } + hps := jujuparams.FromNetworkHostPorts(nhps) + return jujuparams.RedirectInfoResult{ + Servers: [][]jujuparams.HostPort{hps}, + CACert: a.handler.controller.CACert, + }, nil +} + +// adminRoot is a rpc.MethodFinder that implements the admin interface. +type adminRoot struct { + admin +} + +// FindMethod implements rpc.MethodFinder. +func (r adminRoot) FindMethod(rootName string, version int, methodName string) (rpcreflect.MethodCaller, error) { + if rootName != "Admin" { + return nil, &rpcreflect.CallNotImplementedError{ + RootMethod: rootName, + Version: version, + } + } + if version < 3 { + return nil, &rpc.RequestError{ + Code: jujuparams.CodeNotSupported, + Message: "JAAS does not support login from old clients", + } } - return nil + return rpcreflect.ValueOf(reflect.ValueOf(r.admin)).FindMethod("Admin", 0, methodName) } -// errRoot implements the API that a client first sees -// when connecting to the API. It exposes the same API as initialRoot, except -// it returns the requested error when the client makes any request. +// errRoot is a rpc.MethodFinder that always returns an error. type errRoot struct { err error } -// FindMethod conforms to the same API as initialRoot, but we'll always return (nil, err) +// FindMethod implements rpc.MethodFinder, but will always return (nil, err) func (r *errRoot) FindMethod(rootName string, version int, methodName string) (rpcreflect.MethodCaller, error) { return nil, r.err } diff --git a/internal/jujuapi/websocket_test.go b/internal/jujuapi/websocket_test.go index d3580d99b..8b0a3c176 100644 --- a/internal/jujuapi/websocket_test.go +++ b/internal/jujuapi/websocket_test.go @@ -9,17 +9,19 @@ import ( "net/url" "github.com/juju/juju/api" + jujuparams "github.com/juju/juju/apiserver/params" + "github.com/juju/juju/network" jc "github.com/juju/testing/checkers" gc "gopkg.in/check.v1" "gopkg.in/juju/names.v2" "github.com/CanonicalLtd/jem/internal/apitest" + "github.com/CanonicalLtd/jem/params" ) type websocketSuite struct { apitest.Suite - wsServer *httptest.Server - connection api.Connection + wsServer *httptest.Server } var _ = gc.Suite(&websocketSuite{}) @@ -38,13 +40,114 @@ func (s *websocketSuite) TestUnknownModel(c *gc.C) { conn := s.open(c, &api.Info{ ModelTag: names.NewModelTag("00000000-0000-0000-0000-000000000000"), SkipLogin: true, + }, "bob") + defer conn.Close() + err := conn.Login(nil, "", "", nil) + c.Assert(err, gc.ErrorMatches, `model "00000000-0000-0000-0000-000000000000" not found \(not found\)`) +} + +func (s *websocketSuite) TestLoginToModel(c *gc.C) { + ctlPath := s.AssertAddController(c, params.EntityPath{User: "test", Name: "controller-1"}, nil) + _, _, modelUUID := s.CreateModel(c, params.EntityPath{User: "test", Name: "model-1"}, ctlPath) + conn := s.open(c, &api.Info{ + ModelTag: names.NewModelTag(modelUUID), + SkipLogin: true, + }, "test") + defer conn.Close() + err := conn.Login(nil, "", "", nil) + c.Assert(jujuparams.IsRedirect(err), gc.Equals, true) + var resp jujuparams.RedirectInfoResult + err = conn.APICall("Admin", 3, "", "RedirectInfo", nil, &resp) + c.Assert(err, jc.ErrorIsNil) + nhps, err := network.ParseHostPorts(s.APIInfo(c).Addrs...) + c.Assert(err, jc.ErrorIsNil) + hps := jujuparams.FromNetworkHostPorts(nhps) + c.Assert(resp, jc.DeepEquals, jujuparams.RedirectInfoResult{ + Servers: [][]jujuparams.HostPort{hps}, + CACert: s.APIInfo(c).CACert, }) +} + +func (s *websocketSuite) TestIncorrectUserFails(c *gc.C) { + ctlPath := s.AssertAddController(c, params.EntityPath{User: "test", Name: "controller-1"}, nil) + _, _, modelUUID := s.CreateModel(c, params.EntityPath{User: "test", Name: "model-1"}, ctlPath) + conn := s.open(c, &api.Info{ + ModelTag: names.NewModelTag(modelUUID), + SkipLogin: true, + }, "bob") + defer conn.Close() + err := conn.Login(nil, "", "", nil) + c.Assert(err, gc.ErrorMatches, "unauthorized") +} + +func (s *websocketSuite) TestRedirectInfoFailsWithoutLogin(c *gc.C) { + ctlPath := s.AssertAddController(c, params.EntityPath{User: "test", Name: "controller-1"}, nil) + _, _, modelUUID := s.CreateModel(c, params.EntityPath{User: "test", Name: "model-1"}, ctlPath) + conn := s.open(c, &api.Info{ + ModelTag: names.NewModelTag(modelUUID), + SkipLogin: true, + }, "test") + defer conn.Close() + var resp jujuparams.RedirectInfoResult + err := conn.APICall("Admin", 3, "", "RedirectInfo", nil, &resp) + c.Assert(err, gc.ErrorMatches, "unauthorized") +} + +func (s *websocketSuite) TestOldAdminVersionFails(c *gc.C) { + ctlPath := s.AssertAddController(c, params.EntityPath{User: "test", Name: "controller-1"}, nil) + _, _, modelUUID := s.CreateModel(c, params.EntityPath{User: "test", Name: "model-1"}, ctlPath) + conn := s.open(c, &api.Info{ + ModelTag: names.NewModelTag(modelUUID), + SkipLogin: true, + }, "test") + defer conn.Close() + var resp jujuparams.RedirectInfoResult + err := conn.APICall("Admin", 2, "", "Login", nil, &resp) + c.Assert(err, gc.ErrorMatches, `JAAS does not support login from old clients \(not supported\)`) + c.Assert(resp, jc.DeepEquals, jujuparams.RedirectInfoResult{}) +} + +func (s *websocketSuite) TestAdminIDFails(c *gc.C) { + ctlPath := s.AssertAddController(c, params.EntityPath{User: "test", Name: "controller-1"}, nil) + _, _, modelUUID := s.CreateModel(c, params.EntityPath{User: "test", Name: "model-1"}, ctlPath) + conn := s.open(c, &api.Info{ + ModelTag: names.NewModelTag(modelUUID), + SkipLogin: true, + }, "test") + defer conn.Close() + var resp jujuparams.RedirectInfoResult + err := conn.APICall("Admin", 3, "Object ID", "Login", nil, &resp) + c.Assert(err, gc.ErrorMatches, "id not found") +} + +func (s *websocketSuite) TestLoginToController(c *gc.C) { + s.AssertAddController(c, params.EntityPath{User: "test", Name: "controller-1"}, nil) + conn := s.open(c, &api.Info{ + ModelTag: s.APIInfo(c).ModelTag, + SkipLogin: true, + }, "test") + defer conn.Close() + err := conn.Login(nil, "", "", nil) + c.Assert(err, jc.ErrorIsNil) + var resp jujuparams.RedirectInfoResult + err = conn.APICall("Admin", 3, "", "RedirectInfo", nil, &resp) + c.Assert(err, gc.ErrorMatches, "not redirected") +} + +func (s *websocketSuite) TestUnimplementedMethodFails(c *gc.C) { + ctlPath := s.AssertAddController(c, params.EntityPath{User: "test", Name: "controller-1"}, nil) + _, _, modelUUID := s.CreateModel(c, params.EntityPath{User: "test", Name: "model-1"}, ctlPath) + conn := s.open(c, &api.Info{ + ModelTag: names.NewModelTag(modelUUID), + SkipLogin: true, + }, "test") defer conn.Close() - err := conn.Login(names.NewUserTag("test-user"), "", "", nil) - c.Assert(err, gc.ErrorMatches, `unknown model: "00000000-0000-0000-0000-000000000000" \(not found\)`) + var resp jujuparams.RedirectInfoResult + err := conn.APICall("Admin", 3, "", "Logout", nil, &resp) + c.Assert(err, gc.ErrorMatches, `no such request - method Admin.Logout is not implemented \(not implemented\)`) } -func (s *websocketSuite) open(c *gc.C, info *api.Info) api.Connection { +func (s *websocketSuite) open(c *gc.C, info *api.Info, username string) api.Connection { inf := *info u, err := url.Parse(s.wsServer.URL) c.Assert(err, jc.ErrorIsNil) @@ -60,6 +163,7 @@ func (s *websocketSuite) open(c *gc.C, info *api.Info) api.Connection { inf.CACert = w.String() conn, err := api.Open(&inf, api.DialOpts{ InsecureSkipVerify: true, + BakeryClient: s.IDMSrv.Client(username), }) c.Assert(err, jc.ErrorIsNil) return conn diff --git a/internal/v2/api_test.go b/internal/v2/api_test.go index 52695d928..f96972a08 100644 --- a/internal/v2/api_test.go +++ b/internal/v2/api_test.go @@ -140,8 +140,8 @@ var unauthorizedTests = []struct { }} func (s *APISuite) TestUnauthorized(c *gc.C) { - s.assertAddController(c, params.EntityPath{"bob", "private"}, nil) - ctlId := s.assertAddController(c, params.EntityPath{"bob", "open"}, nil) + s.AssertAddController(c, params.EntityPath{"bob", "private"}, nil) + ctlId := s.AssertAddController(c, params.EntityPath{"bob", "open"}, nil) s.addTemplate(c, params.EntityPath{"bob", "open"}, ctlId, dummyModelConfig) s.addTemplate(c, params.EntityPath{"bob", "private"}, ctlId, dummyModelConfig) @@ -380,8 +380,8 @@ func (s *APISuite) TestAddController(c *gc.C) { } func (s *APISuite) TestAddControllerDuplicate(c *gc.C) { - ctlPath := s.assertAddController(c, params.EntityPath{"bob", "dupmodel"}, nil) - err := s.addController(c, ctlPath, nil) + ctlPath := s.AssertAddController(c, params.EntityPath{"bob", "dupmodel"}, nil) + err := s.AddController(c, ctlPath, nil) c.Assert(err, gc.ErrorMatches, "PUT http://.*: already exists") c.Assert(errgo.Cause(err), gc.Equals, params.ErrAlreadyExists) } @@ -453,9 +453,9 @@ func (s *APISuite) TestDeleteModelNotFound(c *gc.C) { } func (s *APISuite) TestDeleteModel(c *gc.C) { - ctlId := s.assertAddController(c, params.EntityPath{"bob", "who"}, nil) + ctlId := s.AssertAddController(c, params.EntityPath{"bob", "who"}, nil) modelPath := params.EntityPath{"bob", "foobarred"} - modelId, user, uuid := s.addModel(c, modelPath, ctlId) + modelId, user, uuid := s.CreateModel(c, modelPath, ctlId) resp := httptesting.DoRequest(c, httptesting.DoRequestParams{ Handler: s.JEMSrv, URL: "/v2/model/" + modelId.String(), @@ -500,7 +500,7 @@ func (s *APISuite) TestDeleteModel(c *gc.C) { } func (s *APISuite) TestGetController(c *gc.C) { - ctlId := s.assertAddController(c, params.EntityPath{"bob", "foo"}, nil) + ctlId := s.AssertAddController(c, params.EntityPath{"bob", "foo"}, nil) t := time.Now() err := s.JEM.SetControllerUnavailableAt(ctlId, t) c.Assert(err, gc.IsNil) @@ -729,7 +729,7 @@ func (s *APISuite) TestDeleteControllerNotFound(c *gc.C) { func (s *APISuite) TestDeleteController(c *gc.C) { // Add controller to JEM. - ctlId := s.assertAddController(c, params.EntityPath{"bob", "foobarred"}, nil) + ctlId := s.AssertAddController(c, params.EntityPath{"bob", "foobarred"}, nil) // Assert that it was added. resp := httptesting.DoRequest(c, httptesting.DoRequestParams{ Handler: s.JEMSrv, @@ -738,7 +738,7 @@ func (s *APISuite) TestDeleteController(c *gc.C) { }) c.Assert(resp.Code, gc.Equals, http.StatusOK, gc.Commentf("body: %s", resp.Body.Bytes())) // Add another model to it. - modelId, _, _ := s.addModel(c, params.EntityPath{"bob", "bar"}, ctlId) + modelId, _, _ := s.CreateModel(c, params.EntityPath{"bob", "bar"}, ctlId) // Check that we can't delete it because it's marked as "available" httptesting.AssertJSONCall(c, httptesting.JSONCallParams{ @@ -881,41 +881,41 @@ var getControllerLocationsTests = []struct { }} func (s *APISuite) TestGetControllerLocations(c *gc.C) { - s.assertAddController(c, params.EntityPath{"bob", "aws-us-east"}, map[string]string{ + s.AssertAddController(c, params.EntityPath{"bob", "aws-us-east"}, map[string]string{ "cloud": "aws", "region": "us-east-1", }) - s.assertAddController(c, params.EntityPath{"bob", "aws-us-east-staging"}, map[string]string{ + s.AssertAddController(c, params.EntityPath{"bob", "aws-us-east-staging"}, map[string]string{ "cloud": "aws", "region": "us-east-1", "staging": "true", }) - s.assertAddController(c, params.EntityPath{"bob", "aws-eu-west"}, map[string]string{ + s.AssertAddController(c, params.EntityPath{"bob", "aws-eu-west"}, map[string]string{ "cloud": "aws", "region": "eu-west-1", }) - s.assertAddController(c, params.EntityPath{"bob", "gce-somewhere"}, map[string]string{ + s.AssertAddController(c, params.EntityPath{"bob", "gce-somewhere"}, map[string]string{ "cloud": "gce", "region": "somewhere", }) - ctlId := s.assertAddController(c, params.EntityPath{"bob", "gce-down"}, map[string]string{ + ctlId := s.AssertAddController(c, params.EntityPath{"bob", "gce-down"}, map[string]string{ "cloud": "gce", "region": "down", }) err := s.JEM.SetControllerUnavailableAt(ctlId, time.Now()) c.Assert(err, gc.IsNil) - s.assertAddController(c, params.EntityPath{"bob", "gce-somewhere-staging"}, map[string]string{ + s.AssertAddController(c, params.EntityPath{"bob", "gce-somewhere-staging"}, map[string]string{ "cloud": "gce", "region": "somewhere", "staging": "true", }) s.allowControllerPerm(c, params.EntityPath{"bob", "gce-somewhere-staging"}, "somegroup") - s.assertAddController(c, params.EntityPath{"bob", "gce-elsewhere"}, map[string]string{ + s.AssertAddController(c, params.EntityPath{"bob", "gce-elsewhere"}, map[string]string{ "cloud": "gce", "region": "elsewhere", }) s.IDMSrv.AddUser("alice", "somegroup") - s.assertAddController(c, params.EntityPath{"alice", "alice-controller"}, map[string]string{ + s.AssertAddController(c, params.EntityPath{"alice", "alice-controller"}, map[string]string{ "cloud": "azure", "region": "america", }) @@ -1050,54 +1050,54 @@ var getAllControllerLocationsTests = []struct { }} func (s *APISuite) TestAllControllerLocations(c *gc.C) { - s.assertAddController(c, params.EntityPath{"bob", "aws-us-east"}, map[string]string{ + s.AssertAddController(c, params.EntityPath{"bob", "aws-us-east"}, map[string]string{ "cloud": "aws", "region": "us-east-1", }) - s.assertAddController(c, params.EntityPath{"bob", "aws-us-east-2"}, map[string]string{ + s.AssertAddController(c, params.EntityPath{"bob", "aws-us-east-2"}, map[string]string{ "cloud": "aws", "region": "us-east-1", }) - s.assertAddController(c, params.EntityPath{"bob", "aws-us-east-staging"}, map[string]string{ + s.AssertAddController(c, params.EntityPath{"bob", "aws-us-east-staging"}, map[string]string{ "cloud": "aws", "region": "us-east-1", "staging": "true", }) - s.assertAddController(c, params.EntityPath{"bob", "aws-us-east-staging-2"}, map[string]string{ + s.AssertAddController(c, params.EntityPath{"bob", "aws-us-east-staging-2"}, map[string]string{ "cloud": "aws", "region": "us-east-1", "staging": "true", }) - s.assertAddController(c, params.EntityPath{"bob", "aws-eu-west"}, map[string]string{ + s.AssertAddController(c, params.EntityPath{"bob", "aws-eu-west"}, map[string]string{ "cloud": "aws", "region": "eu-west-1", }) - s.assertAddController(c, params.EntityPath{"bob", "gce-somewhere"}, map[string]string{ + s.AssertAddController(c, params.EntityPath{"bob", "gce-somewhere"}, map[string]string{ "cloud": "gce", "region": "somewhere", }) - ctlId := s.assertAddController(c, params.EntityPath{"bob", "gce-down"}, map[string]string{ + ctlId := s.AssertAddController(c, params.EntityPath{"bob", "gce-down"}, map[string]string{ "cloud": "gce", "region": "down", }) err := s.JEM.SetControllerUnavailableAt(ctlId, time.Now()) c.Assert(err, gc.IsNil) - s.assertAddController(c, params.EntityPath{"bob", "gce-somewhere-staging"}, map[string]string{ + s.AssertAddController(c, params.EntityPath{"bob", "gce-somewhere-staging"}, map[string]string{ "cloud": "gce", "region": "somewhere", "staging": "true", }) s.allowControllerPerm(c, params.EntityPath{"bob", "gce-somewhere-staging"}, "somegroup") - s.assertAddController(c, params.EntityPath{"bob", "gce-elsewhere"}, map[string]string{ + s.AssertAddController(c, params.EntityPath{"bob", "gce-elsewhere"}, map[string]string{ "cloud": "gce", "region": "elsewhere", }) s.IDMSrv.AddUser("alice", "somegroup") - s.assertAddController(c, params.EntityPath{"alice", "alice-controller"}, map[string]string{ + s.AssertAddController(c, params.EntityPath{"alice", "alice-controller"}, map[string]string{ "cloud": "azure", "region": "america", }) - s.assertAddController(c, params.EntityPath{"alice", "forgotten"}, nil) + s.AssertAddController(c, params.EntityPath{"alice", "forgotten"}, nil) for i, test := range getAllControllerLocationsTests { c.Logf("test %d: %v", i, test.about) @@ -1115,11 +1115,11 @@ func (s *APISuite) TestAllControllerLocations(c *gc.C) { } func (s *APISuite) TestGetSchemaOneProviderType(c *gc.C) { - ctlId := s.assertAddController(c, params.EntityPath{"bob", "aws-us-east"}, map[string]string{ + ctlId := s.AssertAddController(c, params.EntityPath{"bob", "aws-us-east"}, map[string]string{ "cloud": "aws", "region": "us-east-1", }) - s.assertAddController(c, params.EntityPath{"bob", "aws-eu-west"}, map[string]string{ + s.AssertAddController(c, params.EntityPath{"bob", "aws-eu-west"}, map[string]string{ "cloud": "aws", "region": "eu-west-1", }) @@ -1139,7 +1139,7 @@ func (s *APISuite) TestGetSchemaOneProviderType(c *gc.C) { } func (s *APISuite) TestGetSchemaNotFound(c *gc.C) { - s.assertAddController(c, params.EntityPath{"bob", "aws-us-east"}, map[string]string{ + s.AssertAddController(c, params.EntityPath{"bob", "aws-us-east"}, map[string]string{ "cloud": "aws", "region": "us-east-1", }) @@ -1154,7 +1154,7 @@ func (s *APISuite) TestGetSchemaNotFound(c *gc.C) { } func (s *APISuite) TestGetSchemaAmbiguous(c *gc.C) { - s.assertAddController(c, params.EntityPath{"bob", "aws-us-east"}, map[string]string{ + s.AssertAddController(c, params.EntityPath{"bob", "aws-us-east"}, map[string]string{ "cloud": "aws", "region": "us-east-1", }) @@ -1197,7 +1197,7 @@ func (s *APISuite) TestGetSchemaBadLocation(c *gc.C) { } func (s *APISuite) TestNewModel(c *gc.C) { - ctlId := s.assertAddController(c, params.EntityPath{"bob", "foo"}, nil) + ctlId := s.AssertAddController(c, params.EntityPath{"bob", "foo"}, nil) var modelRespBody json.RawMessage httptesting.AssertJSONCall(c, httptesting.JSONCallParams{ @@ -1245,7 +1245,7 @@ func (s *APISuite) TestNewModel(c *gc.C) { } func (s *APISuite) TestNewModelWithTemplates(c *gc.C) { - ctlId := s.assertAddController(c, params.EntityPath{"bob", "foo"}, nil) + ctlId := s.AssertAddController(c, params.EntityPath{"bob", "foo"}, nil) // TODO change "admin-secret" to "secret" when we can // make the "secret" configuration attribute marked as secret @@ -1309,7 +1309,7 @@ func (s *APISuite) TestNewModelWithTemplates(c *gc.C) { } func (s *APISuite) TestNewModelWithTemplateNotFound(c *gc.C) { - ctlId := s.assertAddController(c, params.EntityPath{"bob", "foo"}, nil) + ctlId := s.AssertAddController(c, params.EntityPath{"bob", "foo"}, nil) resp, err := s.NewClient("bob").NewModel(¶ms.NewModel{ User: "bob", @@ -1334,20 +1334,20 @@ func (s *APISuite) TestNewModelWithoutExplicitController(c *gc.C) { }) // Note: the controllers are ordered alphabetically by id before // the "random" selection is applied. - s.assertAddController(c, params.EntityPath{"bob", "aws-eu-west"}, map[string]string{ + s.AssertAddController(c, params.EntityPath{"bob", "aws-eu-west"}, map[string]string{ "cloud": "aws", "region": "eu-west-1", }) - s.assertAddController(c, params.EntityPath{"bob", "aws-us-east"}, map[string]string{ + s.AssertAddController(c, params.EntityPath{"bob", "aws-us-east"}, map[string]string{ "cloud": "aws", "region": "us-east-1", }) - s.assertAddController(c, params.EntityPath{"bob", "aws-us-east-staging"}, map[string]string{ + s.AssertAddController(c, params.EntityPath{"bob", "aws-us-east-staging"}, map[string]string{ "cloud": "aws", "region": "us-east-1", "staging": "true", }) - ctlId := s.assertAddController(c, params.EntityPath{"bob", "aws-us-east-staging-down"}, map[string]string{ + ctlId := s.AssertAddController(c, params.EntityPath{"bob", "aws-us-east-staging-down"}, map[string]string{ "cloud": "aws", "region": "us-east-1", "staging": "true", @@ -1355,7 +1355,7 @@ func (s *APISuite) TestNewModelWithoutExplicitController(c *gc.C) { err := s.JEM.SetControllerUnavailableAt(ctlId, time.Now()) c.Assert(err, gc.IsNil) s.allowControllerPerm(c, params.EntityPath{"bob", "aws-us-east-staging"}, "everyone") - s.assertAddController(c, params.EntityPath{"bob", "azure-us"}, map[string]string{ + s.AssertAddController(c, params.EntityPath{"bob", "azure-us"}, map[string]string{ "cloud": "azure", "region": "us", }) @@ -1650,7 +1650,7 @@ func (s *APISuite) assertModelConfigAttr(c *gc.C, modelPath params.EntityPath, a func (s *APISuite) TestGetModel(c *gc.C) { info := s.APIInfo(c) - ctlId := s.assertAddController(c, params.EntityPath{"bob", "foo"}, nil) + ctlId := s.AssertAddController(c, params.EntityPath{"bob", "foo"}, nil) err := s.JEM.SetModelLife(ctlId, info.ModelTag.Id(), "dying") c.Assert(err, gc.IsNil) t := time.Now() @@ -1691,7 +1691,7 @@ func newTime(t time.Time) *time.Time { } func (s *APISuite) TestGetModelWithExplicitlyRemovedUser(c *gc.C) { - ctlId := s.assertAddController(c, params.EntityPath{"bob", "foo"}, nil) + ctlId := s.AssertAddController(c, params.EntityPath{"bob", "foo"}, nil) s.IDMSrv.AddUser("alice", "buddies") s.IDMSrv.AddUser("bob", "buddies") @@ -1754,7 +1754,7 @@ func apiInfoFromModelResponse(resp *params.ModelResponse) *api.Info { } func (s *APISuite) TestNewModelUnderGroup(c *gc.C) { - ctlId := s.assertAddController(c, params.EntityPath{"bob", "foo"}, nil) + ctlId := s.AssertAddController(c, params.EntityPath{"bob", "foo"}, nil) s.IDMSrv.AddUser("bob", "beatles") var modelRespBody json.RawMessage @@ -1787,7 +1787,7 @@ func (s *APISuite) TestNewModelWithExistingUser(c *gc.C) { _, _, err := usermanager.NewClient(s.APIState).AddUser(username, "", "old", "") c.Assert(err, gc.IsNil) - ctlId := s.assertAddController(c, params.EntityPath{"bob", "foo"}, nil) + ctlId := s.AssertAddController(c, params.EntityPath{"bob", "foo"}, nil) var modelRespBody json.RawMessage httptesting.AssertJSONCall(c, httptesting.JSONCallParams{ @@ -1879,7 +1879,7 @@ func (s *APISuite) TestNewModelCannotOpenAPI(c *gc.C) { } func (s *APISuite) TestNewModelInvalidConfig(c *gc.C) { - ctlId := s.assertAddController(c, params.EntityPath{"bob", "foo"}, nil) + ctlId := s.AssertAddController(c, params.EntityPath{"bob", "foo"}, nil) httptesting.AssertJSONCall(c, httptesting.JSONCallParams{ Method: "POST", @@ -1902,7 +1902,7 @@ func (s *APISuite) TestNewModelInvalidConfig(c *gc.C) { } func (s *APISuite) TestNewModelTwice(c *gc.C) { - ctlId := s.assertAddController(c, params.EntityPath{"bob", "foo"}, nil) + ctlId := s.AssertAddController(c, params.EntityPath{"bob", "foo"}, nil) body := ¶ms.NewModelInfo{ Name: "bar", @@ -1935,7 +1935,7 @@ func (s *APISuite) TestNewModelTwice(c *gc.C) { } func (s *APISuite) TestNewModelCannotCreate(c *gc.C) { - ctlId := s.assertAddController(c, params.EntityPath{"bob", "foo"}, nil) + ctlId := s.AssertAddController(c, params.EntityPath{"bob", "foo"}, nil) httptesting.AssertJSONCall(c, httptesting.JSONCallParams{ Method: "POST", @@ -1971,7 +1971,7 @@ func (s *APISuite) TestNewModelCannotCreate(c *gc.C) { } func (s *APISuite) TestNewModelUnauthorized(c *gc.C) { - ctlId := s.assertAddController(c, params.EntityPath{"bob", "foo"}, nil) + ctlId := s.AssertAddController(c, params.EntityPath{"bob", "foo"}, nil) httptesting.AssertJSONCall(c, httptesting.JSONCallParams{ Method: "POST", @@ -1992,14 +1992,14 @@ func (s *APISuite) TestNewModelUnauthorized(c *gc.C) { } func (s *APISuite) TestListController(c *gc.C) { - ctlId0 := s.assertAddController(c, params.EntityPath{"bob", "foo"}, map[string]string{"cloud": "aws"}) + ctlId0 := s.AssertAddController(c, params.EntityPath{"bob", "foo"}, map[string]string{"cloud": "aws"}) - ctlId1 := s.assertAddController(c, params.EntityPath{"bob", "lost"}, nil) + ctlId1 := s.AssertAddController(c, params.EntityPath{"bob", "lost"}, nil) unavailableTime := time.Now() err := s.JEM.SetControllerUnavailableAt(ctlId1, unavailableTime) c.Assert(err, gc.IsNil) - ctlId2 := s.assertAddController(c, params.EntityPath{"bob", "another"}, nil) + ctlId2 := s.AssertAddController(c, params.EntityPath{"bob", "another"}, nil) err = s.JEM.SetControllerUnavailableAt(ctlId2, unavailableTime.Add(time.Second)) c.Assert(err, gc.IsNil) @@ -2033,7 +2033,7 @@ func (s *APISuite) TestListControllerNoServers(c *gc.C) { } func (s *APISuite) TestListTemplates(c *gc.C) { - ctlId := s.assertAddController(c, params.EntityPath{"bob", "foo"}, nil) + ctlId := s.AssertAddController(c, params.EntityPath{"bob", "foo"}, nil) ctl, err := s.NewClient("bob").GetController(¶ms.GetController{ EntityPath: ctlId, }) @@ -2125,7 +2125,7 @@ func (s *APISuite) TestListModelsNoServers(c *gc.C) { func (s *APISuite) TestListModelsControllerOnly(c *gc.C) { ctlPath := params.EntityPath{"bob", "foo"} - ctlId := s.assertAddController(c, ctlPath, nil) + ctlId := s.AssertAddController(c, ctlPath, nil) info := s.APIInfo(c) resp, err := s.NewClient("bob").ListModels(nil) c.Assert(err, gc.IsNil) @@ -2142,16 +2142,16 @@ func (s *APISuite) TestListModelsControllerOnly(c *gc.C) { } func (s *APISuite) TestListModels(c *gc.C) { - ctlId0 := s.assertAddController(c, params.EntityPath{"alice", "foo"}, nil) + ctlId0 := s.AssertAddController(c, params.EntityPath{"alice", "foo"}, nil) s.allowModelPerm(c, ctlId0) s.allowControllerPerm(c, ctlId0) - modelId1, _, uuid1 := s.addModel(c, params.EntityPath{"bob", "bar"}, ctlId0) - modelId2, _, uuid2 := s.addModel(c, params.EntityPath{"charlie", "bar"}, ctlId0) + modelId1, _, uuid1 := s.CreateModel(c, params.EntityPath{"bob", "bar"}, ctlId0) + modelId2, _, uuid2 := s.CreateModel(c, params.EntityPath{"charlie", "bar"}, ctlId0) err := s.JEM.SetModelLife(ctlId0, uuid2, "alive") c.Assert(err, gc.IsNil) // Add an unavailable controller. - ctlId1 := s.assertAddController(c, params.EntityPath{"alice", "lost"}, nil) + ctlId1 := s.AssertAddController(c, params.EntityPath{"alice", "lost"}, nil) s.allowModelPerm(c, ctlId1) s.allowControllerPerm(c, ctlId1) unavailableTime := time.Now() @@ -2223,7 +2223,7 @@ func (s *APISuite) TestListModels(c *gc.C) { } func (s *APISuite) TestGetSetControllerPerm(c *gc.C) { - ctlId := s.assertAddController(c, params.EntityPath{"alice", "foo"}, nil) + ctlId := s.AssertAddController(c, params.EntityPath{"alice", "foo"}, nil) acl, err := s.NewClient("alice").GetControllerPerm(¶ms.GetControllerPerm{ EntityPath: ctlId, @@ -2248,7 +2248,7 @@ func (s *APISuite) TestGetSetControllerPerm(c *gc.C) { } func (s *APISuite) TestGetSetModelPerm(c *gc.C) { - ctlId := s.assertAddController(c, params.EntityPath{"alice", "foo"}, nil) + ctlId := s.AssertAddController(c, params.EntityPath{"alice", "foo"}, nil) acl, err := s.NewClient("alice").GetModelPerm(¶ms.GetModelPerm{ EntityPath: ctlId, @@ -2273,7 +2273,7 @@ func (s *APISuite) TestGetSetModelPerm(c *gc.C) { } func (s *APISuite) TestAddTemplateWithOldModelReferences(c *gc.C) { - ctlId := s.assertAddController(c, params.EntityPath{"alice", "foo"}, nil) + ctlId := s.AssertAddController(c, params.EntityPath{"alice", "foo"}, nil) tmplPath := params.EntityPath{"bob", "mytemplate"} // This tests the case we're creating a template when there are models // already referring to it.In this case, the template @@ -2322,7 +2322,7 @@ func (s *APISuite) TestAddTemplateWithOldModelReferences(c *gc.C) { } func (s *APISuite) TestAddTemplate(c *gc.C) { - ctlId := s.assertAddController(c, params.EntityPath{"alice", "foo"}, nil) + ctlId := s.AssertAddController(c, params.EntityPath{"alice", "foo"}, nil) err := s.NewClient("alice").AddNewTemplate(¶ms.AddNewTemplate{ EntityPath: params.EntityPath{"alice", "creds"}, Info: params.AddTemplateInfo{ @@ -2433,7 +2433,7 @@ func (s *APISuite) TestAddTemplate(c *gc.C) { } func (s *APISuite) TestAddTemplateWithLocation(c *gc.C) { - s.assertAddController(c, params.EntityPath{"alice", "foo"}, map[string]string{ + s.AssertAddController(c, params.EntityPath{"alice", "foo"}, map[string]string{ "cloud": "ec2", "region": "us-east-1", }) @@ -2462,7 +2462,7 @@ func (s *APISuite) TestAddTemplateWithLocation(c *gc.C) { } func (s *APISuite) TestAddTemplateWithLocationAndTemplate(c *gc.C) { - ctlId := s.assertAddController(c, params.EntityPath{"alice", "foo"}, map[string]string{ + ctlId := s.AssertAddController(c, params.EntityPath{"alice", "foo"}, map[string]string{ "cloud": "ec2", "region": "us-east-1", }) @@ -2584,7 +2584,7 @@ func (s *APISuite) TestGetTemplateModelsTemplateNotFound(c *gc.C) { } func (s *APISuite) TestGetTemplateModels(c *gc.C) { - ctlId := s.assertAddController(c, params.EntityPath{"bob", "ctl"}, nil) + ctlId := s.AssertAddController(c, params.EntityPath{"bob", "ctl"}, nil) s.allowControllerPerm(c, ctlId) t1 := params.EntityPath{"bob", "t1"} t2 := params.EntityPath{"bob", "t2"} @@ -2594,15 +2594,15 @@ func (s *APISuite) TestGetTemplateModels(c *gc.C) { s.allowTemplatePerm(c, t2) bobModel1 := params.EntityPath{"bob", "model1"} - s.addModel(c, bobModel1, ctlId, t1, t2) + s.CreateModel(c, bobModel1, ctlId, t1, t2) s.allowModelPerm(c, bobModel1, "superduper") bobModel2 := params.EntityPath{"bob", "model2"} - s.addModel(c, bobModel2, ctlId, t1) + s.CreateModel(c, bobModel2, ctlId, t1) s.allowModelPerm(c, bobModel2, "superduper") aliceModel := params.EntityPath{"alice", "model"} - s.addModel(c, aliceModel, ctlId, t1) + s.CreateModel(c, aliceModel, ctlId, t1) s.allowModelPerm(c, aliceModel, "superduper") resp, err := s.NewClient("bob").GetTemplateModels(¶ms.GetTemplateModels{ @@ -2660,7 +2660,7 @@ var addInvalidTemplateTests = []struct { }} func (s *APISuite) TestAddInvalidTemplate(c *gc.C) { - s.assertAddController(c, params.EntityPath{"alice", "foo"}, nil) + s.AssertAddController(c, params.EntityPath{"alice", "foo"}, nil) for i, test := range addInvalidTemplateTests { c.Logf("test %d: %s", i, test.about) err := s.NewClient("alice").AddTemplate(¶ms.AddTemplate{ @@ -2690,7 +2690,7 @@ func (s *APISuite) TestDeleteTemplateNotFound(c *gc.C) { } func (s *APISuite) TestDeleteTemplateInUse(c *gc.C) { - ctlId := s.assertAddController(c, params.EntityPath{"bob", "foo"}, nil) + ctlId := s.AssertAddController(c, params.EntityPath{"bob", "foo"}, nil) templPath := params.EntityPath{"bob", "other"} s.addTemplate(c, templPath, ctlId, map[string]interface{}{ @@ -2723,7 +2723,7 @@ func (s *APISuite) TestDeleteTemplateInUse(c *gc.C) { } func (s *APISuite) TestDeleteTemplate(c *gc.C) { - ctlId := s.assertAddController(c, params.EntityPath{"alice", "foo"}, nil) + ctlId := s.AssertAddController(c, params.EntityPath{"alice", "foo"}, nil) templPath := params.EntityPath{"alice", "foo"} err := s.NewClient("alice").AddTemplate(¶ms.AddTemplate{ @@ -2763,57 +2763,6 @@ func (s *APISuite) TestWhoAmI(c *gc.C) { c.Assert(resp.User, gc.Equals, "bob") } -// addController adds a new controller named name under the -// given user. It returns the controller id. -func (s *APISuite) assertAddController(c *gc.C, ctlPath params.EntityPath, loc map[string]string) params.EntityPath { - err := s.addController(c, ctlPath, loc) - c.Assert(err, gc.IsNil) - return ctlPath -} - -func (s *APISuite) addController(c *gc.C, path params.EntityPath, loc map[string]string) error { - // Note that because the cookies acquired in this request don't - // persist, the discharge macaroon we get won't affect subsequent - // requests in the caller. - info := s.APIInfo(c) - p := ¶ms.AddController{ - EntityPath: path, - Info: params.ControllerInfo{ - HostPorts: info.Addrs, - CACert: info.CACert, - User: info.Tag.Id(), - Password: info.Password, - ControllerUUID: info.ModelTag.Id(), - Location: loc, - }, - } - if len(loc) > 0 { - p.Info.Public = true - s.IDMSrv.AddUser(string(path.User), "controller-admin") - } - return s.NewClient(path.User).AddController(p) -} - -// addModel adds a new model in the given controller. It -// returns the model id. -func (s *APISuite) addModel(c *gc.C, modelPath, ctlPath params.EntityPath, templates ...params.EntityPath) (path params.EntityPath, user, uuid string) { - // Note that because the cookies acquired in this request don't - // persist, the discharge macaroon we get won't affect subsequent - // requests in the caller. - - resp, err := s.NewClient(modelPath.User).NewModel(¶ms.NewModel{ - User: modelPath.User, - Info: params.NewModelInfo{ - Name: modelPath.Name, - Controller: &ctlPath, - Config: dummyModelConfig, - TemplatePaths: templates, - }, - }) - c.Assert(err, gc.IsNil) - return resp.Path, resp.User, resp.UUID -} - func (s *APISuite) addTemplate(c *gc.C, tmplPath, ctlPath params.EntityPath, cfg map[string]interface{}) { err := s.NewClient(tmplPath.User).AddTemplate(¶ms.AddTemplate{ EntityPath: tmplPath,