Merge lp:~jameinel/juju-core/api-use-register-standard-facade into lp:~go-bot/juju-core/trunk

Proposed by John A Meinel
Status: Work in progress
Proposed branch: lp:~jameinel/juju-core/api-use-register-standard-facade
Merge into: lp:~go-bot/juju-core/trunk
Prerequisite: lp:~jameinel/juju-core/api-register-standard-facade
Diff against target: 1249 lines (+382/-434)
26 files modified
rpc/registry/registry.go (+4/-1)
rpc/server.go (+1/-1)
state/apiserver/admin.go (+1/-1)
state/apiserver/agent/agent.go (+5/-1)
state/apiserver/agent/agent_test.go (+11/-4)
state/apiserver/charmrevisionupdater/updater.go (+4/-0)
state/apiserver/client/client.go (+11/-21)
state/apiserver/common/registry_test.go (+32/-1)
state/apiserver/deployer/deployer.go (+4/-0)
state/apiserver/environment/environment.go (+4/-0)
state/apiserver/facades.go (+23/-0)
state/apiserver/firewaller/firewaller.go (+4/-0)
state/apiserver/keymanager/keymanager.go (+4/-0)
state/apiserver/keyupdater/authorisedkeys.go (+4/-0)
state/apiserver/logger/logger.go (+4/-0)
state/apiserver/machine/machiner.go (+4/-0)
state/apiserver/pinger.go (+103/-0)
state/apiserver/provisioner/provisioner.go (+4/-0)
state/apiserver/root.go (+23/-362)
state/apiserver/root_test.go (+0/-31)
state/apiserver/rsyslog/rsyslog.go (+4/-0)
state/apiserver/uniter/uniter.go (+4/-0)
state/apiserver/upgrader/upgrader.go (+29/-0)
state/apiserver/usermanager/usermanager.go (+12/-0)
state/apiserver/utils.go (+0/-6)
state/apiserver/watcher.go (+83/-5)
To merge this branch: bzr merge lp:~jameinel/juju-core/api-use-register-standard-facade
Reviewer Review Type Date Requested Status
Juju Engineering Pending
Review via email: mp+219670@code.launchpad.net

Description of the change

state/apiserver/*: use RegisterStandardFacade

Lots of changes, mostly all mechanical.

The items that aren't trivially mechanical are:

 1) agent/agent.go wasn't taking a Resources, made it take one to be
 the same as the others.

 2) common/registry_test.go This is the attempt to reintroduce the "no
 unknown methods" checks. It currently fails because we have to pass in
 id="" which isn't a known Id for the Watcher Facades. Otherwise it
 works just like the old one. I need to sort out what to do here. (add
 Watchers with id ""?)

 3) state/apiserver/facades.go just imports all the types that used to
 be inlined in state/apiserver/root.go
 We could name it something else like "all", but we'll want a file like
 this somewhere, I think.

 4) root.go MethodCaller implementation
 The biggest thing here is that the Client facade is not like the
 others. I have plans to clean that up, but that will be done in a
 different branch.

 5) upgrader.go I probably shouldn't call this a fully special
 snowflake, because it isn't *as* special as Client is. But it is the
 only Facade that actually has 2 implementations, and which
 implementation gets returned is decided by the tagKind of the caller.
 It *is* special in that it doesn't use RegisterStandardFacade, though.

 6) usermanager/usermanager.go also didn't take a Resources parameter.
 I'm leaving it for now to show off what bespoke func registries look
 like. I'll happily come back later to add Resources to
 NewUserManagerAPI and switch it to RegisterStandardFacade.

 7) AllWatcher. I filed a bug about this, but we have no tests in the
 code base that the AllWatcher API facade actually exists. I believe we
 have coverage of the underlying MegaWatcher behavior. But since the
 juju CLI never used the AllWatcher, we don't even have a smoke test
 for things like "Clients can see the AllWatcher, but Agents cannot". I
 didn't improve that here, but hopefully I didn't screw up the auth
 semantics.

 8) A bunch of the watchers were using direct methods from
 srvRoot, rather than the Authorizer + Resources paradigm. So I changed
 them from using "r.requireAgent() err" to using
 !isAgent(Authorizer) bool.

 Also, all of the new*Watcher functions fit FactoryFacade directly,
 because I had to write them anyway, and the don't fit
 RegisterStandardFacade (because they actually make use of the id
 parameter).

 There is clearly a *lot* of boilerplate between these functions, but
 AFAICT every other line uses a concrete type so it would be a lot of
 reflect work to have them share code.

 isAgent is also different now, instead of doing !Client, it does (Unit
 || Machine). Mostly because that lets us create an Authorizer which can
 do *anything*, whereas the negative logic means that something which is
 a Client and an Agent would sometimes not be an Agent. (Used in the
 "grab a copy of all facades and introspect them" test.)

 9) isAgent helper changes from being something directly asking state to
 being a wrapper around the Authorizer code. And AuthClient changes from
 being !isAgent to just checking directly for whether this entity
 isUser.

https://codereview.appspot.com/100460045/

To post a comment you must log in.
2741. By John A Meinel

finish up the isAgent changes

Revision history for this message
John A Meinel (jameinel) wrote :
Download full text (4.5 KiB)

Reviewers: mp+219670_code.launchpad.net,

Message:
Please take a look.

Description:
state/apiserver/*: use RegisterStandardFacade

Lots of changes, mostly all mechanical.

The items that aren't trivially mechanical are:

  1) agent/agent.go wasn't taking a Resources, made it take one to be
  the same as the others.

  2) common/registry_test.go This is the attempt to reintroduce the "no
  unknown methods" checks. It currently fails because we have to pass in
  id="" which isn't a known Id for the Watcher Facades. Otherwise it
  works just like the old one. I need to sort out what to do here. (add
  Watchers with id ""?)

  3) state/apiserver/facades.go just imports all the types that used to
  be inlined in state/apiserver/root.go
  We could name it something else like "all", but we'll want a file like
  this somewhere, I think.

  4) root.go MethodCaller implementation
  The biggest thing here is that the Client facade is not like the
  others. I have plans to clean that up, but that will be done in a
  different branch.

  5) upgrader.go I probably shouldn't call this a fully special
  snowflake, because it isn't *as* special as Client is. But it is the
  only Facade that actually has 2 implementations, and which
  implementation gets returned is decided by the tagKind of the caller.
  It *is* special in that it doesn't use RegisterStandardFacade, though.

  6) usermanager/usermanager.go also didn't take a Resources parameter.
  I'm leaving it for now to show off what bespoke func registries look
  like. I'll happily come back later to add Resources to
  NewUserManagerAPI and switch it to RegisterStandardFacade.

  7) AllWatcher. I filed a bug about this, but we have no tests in the
  code base that the AllWatcher API facade actually exists. I believe we
  have coverage of the underlying MegaWatcher behavior. But since the
  juju CLI never used the AllWatcher, we don't even have a smoke test
  for things like "Clients can see the AllWatcher, but Agents cannot". I
  didn't improve that here, but hopefully I didn't screw up the auth
  semantics.

  8) A bunch of the watchers were using direct methods from
  srvRoot, rather than the Authorizer + Resources paradigm. So I changed
  them from using "r.requireAgent() err" to using
  !isAgent(Authorizer) bool.

  Also, all of the new*Watcher functions fit FactoryFacade directly,
  because I had to write them anyway, and the don't fit
  RegisterStandardFacade (because they actually make use of the id
  parameter).

  There is clearly a *lot* of boilerplate between these functions, but
  AFAICT every other line uses a concrete type so it would be a lot of
  reflect work to have them share code.

  isAgent is also different now, instead of doing !Client, it does (Unit
  || Machine). Mostly because that lets us create an Authorizer which can
  do *anything*, whereas the negative logic means that something which is
  a Client and an Agent would sometimes not be an Agent. (Used in the
  "grab a copy of all facades and introspect them" test.)

  9) isAgent helper changes from being something directly asking state to
  being a wrapper around the Authorizer code. And AuthClient changes from
  being !isAge...

Read more...

Revision history for this message
John A Meinel (jameinel) wrote :
Revision history for this message
William Reade (fwereade) wrote :

On 2014/05/15 09:47:15, jameinel wrote:
> Please take a look.

2) I'm not really sure about this. Can't think of anything very pleasant
really.

3) Agreed. I like "all.go" FWIW.

7) Just one test here maybe, then?

Otherwise all looking good. Other comments following.

https://codereview.appspot.com/100460045/

Revision history for this message
William Reade (fwereade) wrote :

https://codereview.appspot.com/100460045/diff/20001/state/apiserver/common/registry_test.go
File state/apiserver/common/registry_test.go (right):

https://codereview.appspot.com/100460045/diff/20001/state/apiserver/common/registry_test.go#newcode198
state/apiserver/common/registry_test.go:198: // Entity ?
Entity skipped because no facade is specific to a single entity?

https://codereview.appspot.com/100460045/diff/20001/state/apiserver/root.go
File state/apiserver/root.go (right):

https://codereview.appspot.com/100460045/diff/20001/state/apiserver/root.go#newcode62
state/apiserver/root.go:62: // the other ones being created on-demand.
Why is that?
I'd guess because dataDir.

https://codereview.appspot.com/100460045/diff/20001/state/apiserver/upgrader/upgrader.go
File state/apiserver/upgrader/upgrader.go (right):

https://codereview.appspot.com/100460045/diff/20001/state/apiserver/upgrader/upgrader.go#newcode28
state/apiserver/upgrader/upgrader.go:28: // returned depends on who is
calling.
We could always move the dispatching down a layer into the Upgrader
itself, if you consider this to be too icky.

https://codereview.appspot.com/100460045/

2742. By John A Meinel

Merged api-register-standard-facade into api-use-register-standard-facade.

2743. By John A Meinel

Merged api-register-standard-facade into api-use-register-standard-facade.

2744. By John A Meinel

merge api-register-standard-facade to get updated trunk and github.com/juju/errors.

Also resolve the conflict from the Upgrader fixes that were backported to trunk.

2745. By John A Meinel

Pinger is also a bit different from the other facades.

It interacts with newSrvRoot in a different way because the active Pinger
needs to trigger changes when it *isn't* called. So it does have to be
cached rather than driven on demand.

Revision history for this message
John A Meinel (jameinel) wrote :
2746. By John A Meinel

comment updates

2747. By John A Meinel

Merge in the api-named-resources[-datadir] so that Pinger and Client can be normal Facades.

2748. By John A Meinel

move Pinger into its own file.

There was enough Pinger facade code that I moved it into its own file, registered it,
and then simplified the existing Pinger interface.

Revision history for this message
John A Meinel (jameinel) wrote :
2749. By John A Meinel

Merged api-register-standard-facade into api-use-register-standard-facade.

Deal with the changes due to BaseSuite and the updates to Client and Pinger.

2750. By John A Meinel

Client is finally not special anymore. Yippee\!

2751. By John A Meinel

Strip out all of the compat functions, and make sure docstrings are kept somewhere.

Revision history for this message
John A Meinel (jameinel) wrote :
2752. By John A Meinel

add a docstring

Revision history for this message
John A Meinel (jameinel) wrote :

On 2014/05/16 10:57:15, fwereade wrote:
> On 2014/05/15 09:47:15, jameinel wrote:
> > Please take a look.

> 2) I'm not really sure about this. Can't think of anything very
pleasant really.

I handled this by registering the concrete types in a followup branch.

> 3) Agreed. I like "all.go" FWIW.

I'm using state/apiserver/facades.go

I could be happy with "state/apiserver/allfacades.go" or just all.go if
you really prefer it. But "apiserver/all.go" seems a little
underspecified to me.

> 7) Just one test here maybe, then?

I did end up finding a test, it was just the fact that Client.WatchAll()
is the actual "give me one of these" calls that I didn't realize was
around.

> Otherwise all looking good. Other comments following.

https://codereview.appspot.com/100460045/

2753. By John A Meinel

Merged api-register-standard-facade into api-use-register-standard-facade.

2754. By John A Meinel

Upgrader to have a nicer docstring and change its implementation to actually define the return type.

Revision history for this message
John A Meinel (jameinel) wrote :

Please take a look.

https://codereview.appspot.com/100460045/diff/20001/state/apiserver/common/registry_test.go
File state/apiserver/common/registry_test.go (right):

https://codereview.appspot.com/100460045/diff/20001/state/apiserver/common/registry_test.go#newcode198
state/apiserver/common/registry_test.go:198: // Entity ?
On 2014/05/16 10:57:24, fwereade wrote:
> Entity skipped because no facade is specific to a single entity?

All this got ripped out in the next branch, so it doesn't apply anymore.

https://codereview.appspot.com/100460045/diff/20001/state/apiserver/root.go
File state/apiserver/root.go (right):

https://codereview.appspot.com/100460045/diff/20001/state/apiserver/root.go#newcode62
state/apiserver/root.go:62: // the other ones being created on-demand.
Why is that?
On 2014/05/16 10:57:24, fwereade wrote:
> I'd guess because dataDir.

Yeah, this got fixed as well.

https://codereview.appspot.com/100460045/diff/20001/state/apiserver/upgrader/upgrader.go
File state/apiserver/upgrader/upgrader.go (right):

https://codereview.appspot.com/100460045/diff/20001/state/apiserver/upgrader/upgrader.go#newcode28
state/apiserver/upgrader/upgrader.go:28: // returned depends on who is
calling.
On 2014/05/16 10:57:24, fwereade wrote:
> We could always move the dispatching down a layer into the Upgrader
itself, if
> you consider this to be too icky.

So it is a bit odd, but since both conform to exactly the same API it is
fine. There is a small problem because of late reflection that we might
expose more of an API than we realize. I may have to come back to this
later.

https://codereview.appspot.com/100460045/

Revision history for this message
William Reade (fwereade) wrote :

Unmerged revisions

2754. By John A Meinel

Upgrader to have a nicer docstring and change its implementation to actually define the return type.

2753. By John A Meinel

Merged api-register-standard-facade into api-use-register-standard-facade.

2752. By John A Meinel

add a docstring

2751. By John A Meinel

Strip out all of the compat functions, and make sure docstrings are kept somewhere.

2750. By John A Meinel

Client is finally not special anymore. Yippee\!

2749. By John A Meinel

Merged api-register-standard-facade into api-use-register-standard-facade.

Deal with the changes due to BaseSuite and the updates to Client and Pinger.

2748. By John A Meinel

move Pinger into its own file.

There was enough Pinger facade code that I moved it into its own file, registered it,
and then simplified the existing Pinger interface.

2747. By John A Meinel

Merge in the api-named-resources[-datadir] so that Pinger and Client can be normal Facades.

2746. By John A Meinel

comment updates

2745. By John A Meinel

Pinger is also a bit different from the other facades.

It interacts with newSrvRoot in a different way because the active Pinger
needs to trigger changes when it *isn't* called. So it does have to be
cached rather than driven on demand.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'rpc/registry/registry.go'
2--- rpc/registry/registry.go 2014-05-22 11:56:36 +0000
3+++ rpc/registry/registry.go 2014-05-22 11:56:36 +0000
4@@ -18,6 +18,8 @@
5 registry *registry.TypedNameVersion
6 }
7
8+// Register records a factory that will be returned from Get() when passed the
9+// specific (name, version) pair.
10 func (r *RootFromRegistry) Register(name string, version int, f Factory) {
11 err := r.registry.Register(name, version, f)
12 if err != nil {
13@@ -43,7 +45,8 @@
14 // The parameter names match that of the rpcreflect.Value implementation, but
15 // might be better named:
16 // rootMethodName: facadeName
17-// objId: facadeVersion (will be translated from a string into an int)
18+// version: facadeVersion (will be translated from a string into an int)
19+// objId: Id for the a concrete version of the facade (used for watchers)
20 // objMethodName: methodName
21 func (r *RootFromRegistry) MethodCaller(
22 rootMethodName string, version int, objId, objMethodName string,
23
24=== modified file 'rpc/server.go'
25--- rpc/server.go 2014-05-22 11:56:36 +0000
26+++ rpc/server.go 2014-05-22 11:56:36 +0000
27@@ -341,7 +341,7 @@
28 }
29
30 // Caller is a way to dynamically lookup methods from a given (rootName,
31-// version, id, methodName) triplet.
32+// version, id, methodName) quadruplet.
33 type Caller interface {
34 // MethodCaller takes a description of a method to call, and returns an
35 // rpcreflect.MethodCaller that can be used to call the concrete method.
36
37=== modified file 'state/apiserver/admin.go'
38--- state/apiserver/admin.go 2014-05-20 08:02:01 +0000
39+++ state/apiserver/admin.go 2014-05-22 11:56:36 +0000
40@@ -105,7 +105,7 @@
41 }
42 logger.Debugf("hostPorts: %v", hostPorts)
43
44- a.root.rpcConn.Serve(newRoot, serverError)
45+ a.root.rpcConn.ServeCaller(newRoot, serverError)
46 return params.LoginResult{hostPorts}, nil
47 }
48
49
50=== modified file 'state/apiserver/agent/agent.go'
51--- state/apiserver/agent/agent.go 2014-04-01 18:33:02 +0000
52+++ state/apiserver/agent/agent.go 2014-05-22 11:56:36 +0000
53@@ -12,6 +12,10 @@
54 "launchpad.net/juju-core/state/apiserver/common"
55 )
56
57+func init() {
58+ common.RegisterStandardFacade("Agent", 0, NewAPI)
59+}
60+
61 // API implements the API provided to an agent.
62 type API struct {
63 *common.PasswordChanger
64@@ -22,7 +26,7 @@
65
66 // NewAPI returns an object implementing an agent API
67 // with the given authorizer representing the currently logged in client.
68-func NewAPI(st *state.State, auth common.Authorizer) (*API, error) {
69+func NewAPI(st *state.State, resources *common.Resources, auth common.Authorizer) (*API, error) {
70 // Agents are defined to be any user that's not a client user.
71 if !auth.AuthMachineAgent() && !auth.AuthUnitAgent() {
72 return nil, common.ErrPerm
73
74=== modified file 'state/apiserver/agent/agent_test.go'
75--- state/apiserver/agent/agent_test.go 2014-03-10 08:27:32 +0000
76+++ state/apiserver/agent/agent_test.go 2014-05-22 11:56:36 +0000
77@@ -10,6 +10,7 @@
78 "launchpad.net/juju-core/state"
79 "launchpad.net/juju-core/state/api/params"
80 "launchpad.net/juju-core/state/apiserver/agent"
81+ "launchpad.net/juju-core/state/apiserver/common"
82 apiservertesting "launchpad.net/juju-core/state/apiserver/testing"
83 coretesting "launchpad.net/juju-core/testing"
84 )
85@@ -21,6 +22,7 @@
86 type agentSuite struct {
87 jujutesting.JujuConnSuite
88
89+ resources *common.Resources
90 authorizer apiservertesting.FakeAuthorizer
91
92 machine0 *state.Machine
93@@ -48,6 +50,7 @@
94 s.container, err = s.State.AddMachineInsideMachine(template, s.machine1.Id(), instance.LXC)
95 c.Assert(err, gc.IsNil)
96
97+ s.resources = common.NewResources()
98 // Create a FakeAuthorizer so we can check permissions,
99 // set up assuming machine 1 has logged in.
100 s.authorizer = apiservertesting.FakeAuthorizer{
101@@ -57,15 +60,19 @@
102 }
103
104 // Create a machiner API for machine 1.
105- s.agent, err = agent.NewAPI(s.State, s.authorizer)
106+ s.agent, err = agent.NewAPI(s.State, s.resources, s.authorizer)
107 c.Assert(err, gc.IsNil)
108 }
109+func (s *agentSuite) TearDownTest(c *gc.C) {
110+ s.resources.StopAll()
111+ s.JujuConnSuite.TearDownTest(c)
112+}
113
114 func (s *agentSuite) TestAgentFailsWithNonAgent(c *gc.C) {
115 auth := s.authorizer
116 auth.MachineAgent = false
117 auth.UnitAgent = false
118- api, err := agent.NewAPI(s.State, auth)
119+ api, err := agent.NewAPI(s.State, s.resources, auth)
120 c.Assert(err, gc.NotNil)
121 c.Assert(api, gc.IsNil)
122 c.Assert(err, gc.ErrorMatches, "permission denied")
123@@ -75,7 +82,7 @@
124 auth := s.authorizer
125 auth.MachineAgent = false
126 auth.UnitAgent = true
127- _, err := agent.NewAPI(s.State, auth)
128+ _, err := agent.NewAPI(s.State, s.resources, auth)
129 c.Assert(err, gc.IsNil)
130 }
131
132@@ -108,7 +115,7 @@
133 auth.MachineAgent = true
134 auth.UnitAgent = false
135 auth.Tag = s.container.Tag()
136- containerAgent, err := agent.NewAPI(s.State, auth)
137+ containerAgent, err := agent.NewAPI(s.State, s.resources, auth)
138 c.Assert(err, gc.IsNil)
139
140 results = containerAgent.GetEntities(args)
141
142=== modified file 'state/apiserver/charmrevisionupdater/updater.go'
143--- state/apiserver/charmrevisionupdater/updater.go 2014-05-13 04:30:48 +0000
144+++ state/apiserver/charmrevisionupdater/updater.go 2014-05-22 11:56:36 +0000
145@@ -15,6 +15,10 @@
146
147 var logger = loggo.GetLogger("juju.state.apiserver.charmrevisionupdater")
148
149+func init() {
150+ common.RegisterStandardFacade("CharmRevisionUpdater", 0, NewCharmRevisionUpdaterAPI)
151+}
152+
153 // CharmRevisionUpdater defines the methods on the charmrevisionupdater API end point.
154 type CharmRevisionUpdater interface {
155 UpdateLatestRevisions() (params.ErrorResult, error)
156
157=== modified file 'state/apiserver/client/client.go'
158--- state/apiserver/client/client.go 2014-05-20 09:42:11 +0000
159+++ state/apiserver/client/client.go 2014-05-22 11:56:36 +0000
160@@ -29,6 +29,10 @@
161 "launchpad.net/juju-core/version"
162 )
163
164+func init() {
165+ common.RegisterStandardFacade("Client", 0, NewClient)
166+}
167+
168 var logger = loggo.GetLogger("juju.state.apiserver.client")
169
170 type API struct {
171@@ -45,31 +49,17 @@
172 api *API
173 }
174
175-// NewAPI creates a new instance of the Client API.
176-func NewAPI(st *state.State, resources *common.Resources, authorizer common.Authorizer) *API {
177- r := &API{
178+// NewClient creates a new instance of the Client Facade.
179+func NewClient(st *state.State, resources *common.Resources, authorizer common.Authorizer) (*Client, error) {
180+ if !authorizer.AuthClient() {
181+ return nil, common.ErrPerm
182+ }
183+ return &Client{api: &API{
184 state: st,
185 auth: authorizer,
186 resources: resources,
187 statusSetter: common.NewStatusSetter(st, common.AuthAlways(true)),
188- }
189- r.client = &Client{
190- api: r,
191- }
192- return r
193-}
194-
195-// Client returns an object that provides access
196-// to methods accessible to non-agent clients.
197-func (r *API) Client(id string) (*Client, error) {
198- if !r.auth.AuthClient() {
199- return nil, common.ErrPerm
200- }
201- if id != "" {
202- // Safeguard id for possible future use.
203- return nil, common.ErrBadId
204- }
205- return r.client, nil
206+ }}, nil
207 }
208
209 func (c *Client) WatchAll() (params.AllWatcherId, error) {
210
211=== modified file 'state/apiserver/common/registry_test.go'
212--- state/apiserver/common/registry_test.go 2014-05-22 11:56:36 +0000
213+++ state/apiserver/common/registry_test.go 2014-05-22 11:56:36 +0000
214@@ -192,5 +192,36 @@
215 }
216 func (*facadeRegistrySuite) TestDiscardedAPIMethods(c *gc.C) {
217 allFacades := common.Facades.List()
218- c.Assert(allFacades, gc.HasLen, 0)
219+ c.Assert(allFacades, gc.Not(gc.HasLen), 0)
220+ // We use a FakeAuthorizer that says it is allowed to do everything, so
221+ // that we can get all of the concrete Facades
222+ authorizer := apiservertesting.FakeAuthorizer{
223+ Tag: "user-admin",
224+ LoggedIn: true,
225+ Client: true,
226+ EnvironManager: true,
227+ MachineAgent: true,
228+ UnitAgent: true,
229+ // Entity ?
230+ }
231+ resources := common.NewResources()
232+ defer resources.StopAll()
233+
234+ for _, description := range allFacades {
235+ for _, version := range description.Versions {
236+ factory, err := common.GetFacade(description.Name, version)
237+ c.Assert(err, gc.IsNil)
238+ obj, err := factory(nil, resources, authorizer, "")
239+ c.Assert(err, gc.IsNil)
240+ value := reflect.ValueOf(obj)
241+ facadeType := rpcreflect.ObjTypeOf(value.Type())
242+ // We must have some methods on every object returned
243+ // by a root-level method.
244+ c.Assert(facadeType.MethodNames(), gc.Not(gc.HasLen), 0)
245+ // We don't allow any methods that don't implement
246+ // an RPC entry point.
247+ c.Assert(facadeType.DiscardedMethods(), gc.HasLen, 0)
248+ resources.StopAll()
249+ }
250+ }
251 }
252
253=== modified file 'state/apiserver/deployer/deployer.go'
254--- state/apiserver/deployer/deployer.go 2014-04-02 07:29:08 +0000
255+++ state/apiserver/deployer/deployer.go 2014-05-22 11:56:36 +0000
256@@ -12,6 +12,10 @@
257 "launchpad.net/juju-core/state/apiserver/common"
258 )
259
260+func init() {
261+ common.RegisterStandardFacade("Deployer", 0, NewDeployerAPI)
262+}
263+
264 // DeployerAPI provides access to the Deployer API facade.
265 type DeployerAPI struct {
266 *common.Remover
267
268=== modified file 'state/apiserver/environment/environment.go'
269--- state/apiserver/environment/environment.go 2014-01-27 03:46:28 +0000
270+++ state/apiserver/environment/environment.go 2014-05-22 11:56:36 +0000
271@@ -8,6 +8,10 @@
272 "launchpad.net/juju-core/state/apiserver/common"
273 )
274
275+func init() {
276+ common.RegisterStandardFacade("Environment", 0, NewEnvironmentAPI)
277+}
278+
279 // EnvironmentAPI implements the API used by the machine environment worker.
280 type EnvironmentAPI struct {
281 *common.EnvironWatcher
282
283=== added file 'state/apiserver/facades.go'
284--- state/apiserver/facades.go 1970-01-01 00:00:00 +0000
285+++ state/apiserver/facades.go 2014-05-22 11:56:36 +0000
286@@ -0,0 +1,23 @@
287+// Copyright 2014 Canonical Ltd.
288+// Licensed under the AGPLv3, see LICENCE file for details.
289+
290+package apiserver
291+
292+// This file just imports all of the facades so they get registered at runtime
293+import (
294+ _ "launchpad.net/juju-core/state/apiserver/agent"
295+ _ "launchpad.net/juju-core/state/apiserver/charmrevisionupdater"
296+ _ "launchpad.net/juju-core/state/apiserver/client"
297+ _ "launchpad.net/juju-core/state/apiserver/deployer"
298+ _ "launchpad.net/juju-core/state/apiserver/environment"
299+ _ "launchpad.net/juju-core/state/apiserver/firewaller"
300+ _ "launchpad.net/juju-core/state/apiserver/keymanager"
301+ _ "launchpad.net/juju-core/state/apiserver/keyupdater"
302+ _ "launchpad.net/juju-core/state/apiserver/logger"
303+ _ "launchpad.net/juju-core/state/apiserver/machine"
304+ _ "launchpad.net/juju-core/state/apiserver/provisioner"
305+ _ "launchpad.net/juju-core/state/apiserver/rsyslog"
306+ _ "launchpad.net/juju-core/state/apiserver/uniter"
307+ _ "launchpad.net/juju-core/state/apiserver/upgrader"
308+ _ "launchpad.net/juju-core/state/apiserver/usermanager"
309+)
310
311=== modified file 'state/apiserver/firewaller/firewaller.go'
312--- state/apiserver/firewaller/firewaller.go 2014-01-21 17:56:56 +0000
313+++ state/apiserver/firewaller/firewaller.go 2014-05-22 11:56:36 +0000
314@@ -9,6 +9,10 @@
315 "launchpad.net/juju-core/state/apiserver/common"
316 )
317
318+func init() {
319+ common.RegisterStandardFacade("Firewaller", 0, NewFirewallerAPI)
320+}
321+
322 // FirewallerAPI provides access to the Firewaller API facade.
323 type FirewallerAPI struct {
324 *common.LifeGetter
325
326=== modified file 'state/apiserver/keymanager/keymanager.go'
327--- state/apiserver/keymanager/keymanager.go 2014-05-13 04:30:48 +0000
328+++ state/apiserver/keymanager/keymanager.go 2014-05-22 11:56:36 +0000
329@@ -23,6 +23,10 @@
330
331 var logger = loggo.GetLogger("juju.state.apiserver.keymanager")
332
333+func init() {
334+ common.RegisterStandardFacade("KeyManager", 0, NewKeyManagerAPI)
335+}
336+
337 // KeyManager defines the methods on the keymanager API end point.
338 type KeyManager interface {
339 ListKeys(arg params.ListSSHKeys) (params.StringsResults, error)
340
341=== modified file 'state/apiserver/keyupdater/authorisedkeys.go'
342--- state/apiserver/keyupdater/authorisedkeys.go 2014-05-13 04:30:48 +0000
343+++ state/apiserver/keyupdater/authorisedkeys.go 2014-05-22 11:56:36 +0000
344@@ -13,6 +13,10 @@
345 "launchpad.net/juju-core/utils/ssh"
346 )
347
348+func init() {
349+ common.RegisterStandardFacade("KeyUpdater", 0, NewKeyUpdaterAPI)
350+}
351+
352 // KeyUpdater defines the methods on the keyupdater API end point.
353 type KeyUpdater interface {
354 AuthorisedKeys(args params.Entities) (params.StringsResults, error)
355
356=== modified file 'state/apiserver/logger/logger.go'
357--- state/apiserver/logger/logger.go 2014-03-28 11:17:33 +0000
358+++ state/apiserver/logger/logger.go 2014-05-22 11:56:36 +0000
359@@ -14,6 +14,10 @@
360
361 var logger = loggo.GetLogger("juju.api.logger")
362
363+func init() {
364+ common.RegisterStandardFacade("Logger", 0, NewLoggerAPI)
365+}
366+
367 // Logger defines the methods on the logger API end point. Unfortunately, the
368 // api infrastructure doesn't allow interfaces to be used as an actual
369 // endpoint because our rpc mechanism panics. However, I still feel that this
370
371=== modified file 'state/apiserver/machine/machiner.go'
372--- state/apiserver/machine/machiner.go 2014-05-13 04:30:48 +0000
373+++ state/apiserver/machine/machiner.go 2014-05-22 11:56:36 +0000
374@@ -13,6 +13,10 @@
375 "launchpad.net/juju-core/state/apiserver/common"
376 )
377
378+func init() {
379+ common.RegisterStandardFacade("Machiner", 0, NewMachinerAPI)
380+}
381+
382 // MachinerAPI implements the API used by the machiner worker.
383 type MachinerAPI struct {
384 *common.LifeGetter
385
386=== added file 'state/apiserver/pinger.go'
387--- state/apiserver/pinger.go 1970-01-01 00:00:00 +0000
388+++ state/apiserver/pinger.go 2014-05-22 11:56:36 +0000
389@@ -0,0 +1,103 @@
390+// Copyright 2013 Canonical Ltd.
391+// Licensed under the AGPLv3, see LICENCE file for details.
392+
393+package apiserver
394+
395+import (
396+ "errors"
397+ "time"
398+
399+ "launchpad.net/tomb"
400+
401+ "launchpad.net/juju-core/state"
402+ "launchpad.net/juju-core/state/apiserver/common"
403+)
404+
405+func init() {
406+ common.RegisterStandardFacade("Pinger", 0, NewPinger)
407+}
408+
409+// NewPinger returns an object that can be pinged by calling its Ping method.
410+// If this method is not called frequently enough, the connection will be
411+// dropped.
412+func NewPinger(
413+ st *state.State, resources *common.Resources, authorizer common.Authorizer,
414+) (
415+ pinger, error,
416+) {
417+ pingTimeout, ok := resources.Get("pingTimeout").(*pingTimeout)
418+ if !ok {
419+ return nullPinger{}, nil
420+ }
421+ return pingTimeout, nil
422+}
423+
424+// pinger describes a type that can be pinged.
425+type pinger interface {
426+ Ping()
427+}
428+
429+// pingTimeout listens for pings and will call the
430+// passed action in case of a timeout. This way broken
431+// or inactive connections can be closed.
432+type pingTimeout struct {
433+ tomb tomb.Tomb
434+ action func()
435+ timeout time.Duration
436+ reset chan struct{}
437+}
438+
439+// newPingTimeout returns a new pingTimeout instance
440+// that invokes the given action asynchronously if there
441+// is more than the given timeout interval between calls
442+// to its Ping method.
443+func newPingTimeout(action func(), timeout time.Duration) *pingTimeout {
444+ pt := &pingTimeout{
445+ action: action,
446+ timeout: timeout,
447+ reset: make(chan struct{}),
448+ }
449+ go func() {
450+ defer pt.tomb.Done()
451+ pt.tomb.Kill(pt.loop())
452+ }()
453+ return pt
454+}
455+
456+// Ping is used by the client heartbeat monitor and resets
457+// the killer.
458+func (pt *pingTimeout) Ping() {
459+ select {
460+ case <-pt.tomb.Dying():
461+ case pt.reset <- struct{}{}:
462+ }
463+}
464+
465+// Stop terminates the ping timeout.
466+func (pt *pingTimeout) Stop() error {
467+ pt.tomb.Kill(nil)
468+ return pt.tomb.Wait()
469+}
470+
471+// loop waits for a reset signal, otherwise it performs
472+// the initially passed action.
473+func (pt *pingTimeout) loop() error {
474+ timer := time.NewTimer(pt.timeout)
475+ defer timer.Stop()
476+ for {
477+ select {
478+ case <-pt.tomb.Dying():
479+ return nil
480+ case <-timer.C:
481+ go pt.action()
482+ return errors.New("ping timeout")
483+ case <-pt.reset:
484+ timer.Reset(pt.timeout)
485+ }
486+ }
487+}
488+
489+// nullPinger implements the pinger interface but just does nothing
490+type nullPinger struct{}
491+
492+func (nullPinger) Ping() {}
493
494=== modified file 'state/apiserver/provisioner/provisioner.go'
495--- state/apiserver/provisioner/provisioner.go 2014-05-13 12:57:53 +0000
496+++ state/apiserver/provisioner/provisioner.go 2014-05-22 11:56:36 +0000
497@@ -17,6 +17,10 @@
498 "launchpad.net/juju-core/utils/set"
499 )
500
501+func init() {
502+ common.RegisterStandardFacade("Provisioner", 0, NewProvisionerAPI)
503+}
504+
505 // ProvisionerAPI provides access to the Provisioner API facade.
506 type ProvisionerAPI struct {
507 *common.Remover
508
509=== modified file 'state/apiserver/root.go'
510--- state/apiserver/root.go 2014-05-20 09:42:11 +0000
511+++ state/apiserver/root.go 2014-05-22 11:56:36 +0000
512@@ -4,35 +4,15 @@
513 package apiserver
514
515 import (
516- "errors"
517+ "reflect"
518 "time"
519
520- "launchpad.net/tomb"
521-
522- "launchpad.net/juju-core/names"
523 "launchpad.net/juju-core/rpc"
524+ "launchpad.net/juju-core/rpc/rpcreflect"
525 "launchpad.net/juju-core/state"
526- "launchpad.net/juju-core/state/apiserver/agent"
527- "launchpad.net/juju-core/state/apiserver/charmrevisionupdater"
528- "launchpad.net/juju-core/state/apiserver/client"
529 "launchpad.net/juju-core/state/apiserver/common"
530- "launchpad.net/juju-core/state/apiserver/deployer"
531- "launchpad.net/juju-core/state/apiserver/environment"
532- "launchpad.net/juju-core/state/apiserver/firewaller"
533- "launchpad.net/juju-core/state/apiserver/keymanager"
534- "launchpad.net/juju-core/state/apiserver/keyupdater"
535- loggerapi "launchpad.net/juju-core/state/apiserver/logger"
536- "launchpad.net/juju-core/state/apiserver/machine"
537- "launchpad.net/juju-core/state/apiserver/provisioner"
538- "launchpad.net/juju-core/state/apiserver/rsyslog"
539- "launchpad.net/juju-core/state/apiserver/uniter"
540- "launchpad.net/juju-core/state/apiserver/upgrader"
541- "launchpad.net/juju-core/state/apiserver/usermanager"
542- "launchpad.net/juju-core/state/multiwatcher"
543 )
544
545-type clientAPI struct{ *client.API }
546-
547 type taggedAuthenticator interface {
548 state.Entity
549 state.Authenticator
550@@ -55,12 +35,10 @@
551 // srvRoot represents a single client's connection to the state
552 // after it has logged in.
553 type srvRoot struct {
554- clientAPI
555 srv *Server
556 rpcConn *rpc.Conn
557 resources *common.Resources
558-
559- entity taggedAuthenticator
560+ entity taggedAuthenticator
561 }
562
563 // newSrvRoot creates the client's connection representation
564@@ -73,8 +51,9 @@
565 resources: common.NewResources(),
566 entity: entity,
567 }
568+ // Note: Only the Client API is part of srvRoot rather than all
569+ // the other ones being created on-demand. Why is that?
570 r.resources.RegisterNamed("dataDir", common.StringResource(r.srv.dataDir))
571- r.clientAPI.API = client.NewAPI(r.srv.state, r.resources, r)
572 return r
573 }
574
575@@ -84,276 +63,22 @@
576 r.resources.StopAll()
577 }
578
579-// requireAgent checks whether the current client is an agent and hence
580-// may access the agent APIs. We filter out non-agents when calling one
581-// of the accessor functions (Machine, Unit, etc) which avoids us making
582-// the check in every single request method.
583-func (r *srvRoot) requireAgent() error {
584- if !isAgent(r.entity) {
585- return common.ErrPerm
586- }
587- return nil
588-}
589-
590-// requireClient returns an error unless the current
591-// client is a juju client user.
592-func (r *srvRoot) requireClient() error {
593- if isAgent(r.entity) {
594- return common.ErrPerm
595- }
596- return nil
597-}
598-
599-// KeyManager returns an object that provides access to the KeyManager API
600-// facade. The id argument is reserved for future use and currently
601-// needs to be empty.
602-func (r *srvRoot) KeyManager(id string) (*keymanager.KeyManagerAPI, error) {
603- if id != "" {
604- return nil, common.ErrBadId
605- }
606- return keymanager.NewKeyManagerAPI(r.srv.state, r.resources, r)
607-}
608-
609-// UserManager returns an object that provides access to the UserManager API
610-// facade. The id argument is reserved for future use and currently
611-// needs to be empty
612-func (r *srvRoot) UserManager(id string) (*usermanager.UserManagerAPI, error) {
613- if id != "" {
614- return nil, common.ErrBadId
615- }
616- return usermanager.NewUserManagerAPI(r.srv.state, r)
617-}
618-
619-// Machiner returns an object that provides access to the Machiner API
620-// facade. The id argument is reserved for future use and currently
621-// needs to be empty.
622-func (r *srvRoot) Machiner(id string) (*machine.MachinerAPI, error) {
623- if id != "" {
624- // Safeguard id for possible future use.
625- return nil, common.ErrBadId
626- }
627- return machine.NewMachinerAPI(r.srv.state, r.resources, r)
628-}
629-
630-// Provisioner returns an object that provides access to the
631-// Provisioner API facade. The id argument is reserved for future use
632-// and currently needs to be empty.
633-func (r *srvRoot) Provisioner(id string) (*provisioner.ProvisionerAPI, error) {
634- if id != "" {
635- // Safeguard id for possible future use.
636- return nil, common.ErrBadId
637- }
638- return provisioner.NewProvisionerAPI(r.srv.state, r.resources, r)
639-}
640-
641-// Uniter returns an object that provides access to the Uniter API
642-// facade. The id argument is reserved for future use and currently
643-// needs to be empty.
644-func (r *srvRoot) Uniter(id string) (*uniter.UniterAPI, error) {
645- if id != "" {
646- // Safeguard id for possible future use.
647- return nil, common.ErrBadId
648- }
649- return uniter.NewUniterAPI(r.srv.state, r.resources, r)
650-}
651-
652-// Firewaller returns an object that provides access to the Firewaller
653-// API facade. The id argument is reserved for future use and
654-// currently needs to be empty.
655-func (r *srvRoot) Firewaller(id string) (*firewaller.FirewallerAPI, error) {
656- if id != "" {
657- // Safeguard id for possible future use.
658- return nil, common.ErrBadId
659- }
660- return firewaller.NewFirewallerAPI(r.srv.state, r.resources, r)
661-}
662-
663-// Agent returns an object that provides access to the
664-// agent API. The id argument is reserved for future use and must currently
665-// be empty.
666-func (r *srvRoot) Agent(id string) (*agent.API, error) {
667- if id != "" {
668- return nil, common.ErrBadId
669- }
670- return agent.NewAPI(r.srv.state, r)
671-}
672-
673-// Deployer returns an object that provides access to the Deployer API facade.
674-// The id argument is reserved for future use and must be empty.
675-func (r *srvRoot) Deployer(id string) (*deployer.DeployerAPI, error) {
676- if id != "" {
677- // TODO(dimitern): There is no direct test for this
678- return nil, common.ErrBadId
679- }
680- return deployer.NewDeployerAPI(r.srv.state, r.resources, r)
681-}
682-
683-// Environment returns an object that provides access to the Environment API
684-// facade. The id argument is reserved for future use and currently needs to
685-// be empty.
686-func (r *srvRoot) Environment(id string) (*environment.EnvironmentAPI, error) {
687- if id != "" {
688- // Safeguard id for possible future use.
689- return nil, common.ErrBadId
690- }
691- return environment.NewEnvironmentAPI(r.srv.state, r.resources, r)
692-}
693-
694-// Rsyslog returns an object that provides access to the Rsyslog API
695-// facade. The id argument is reserved for future use and currently needs to
696-// be empty.
697-func (r *srvRoot) Rsyslog(id string) (*rsyslog.RsyslogAPI, error) {
698- if id != "" {
699- // Safeguard id for possible future use.
700- return nil, common.ErrBadId
701- }
702- return rsyslog.NewRsyslogAPI(r.srv.state, r.resources, r)
703-}
704-
705-// Logger returns an object that provides access to the Logger API facade.
706-// The id argument is reserved for future use and must be empty.
707-func (r *srvRoot) Logger(id string) (*loggerapi.LoggerAPI, error) {
708- if id != "" {
709- // TODO: There is no direct test for this
710- return nil, common.ErrBadId
711- }
712- return loggerapi.NewLoggerAPI(r.srv.state, r.resources, r)
713-}
714-
715-// Upgrader returns an object that provides access to the Upgrader API facade.
716-// The id argument is reserved for future use and must be empty.
717-func (r *srvRoot) Upgrader(id string) (upgrader.Upgrader, error) {
718- if id != "" {
719- // TODO: There is no direct test for this
720- return nil, common.ErrBadId
721- }
722- // The type of upgrader we return depends on who is asking.
723- // Machines get an UpgraderAPI, units get a UnitUpgraderAPI.
724- // This is tested in the state/api/upgrader package since there
725- // are currently no direct srvRoot tests.
726- tagKind, _, err := names.ParseTag(r.GetAuthTag(), "")
727- if err != nil {
728- return nil, common.ErrPerm
729- }
730- switch tagKind {
731- case names.MachineTagKind:
732- return upgrader.NewUpgraderAPI(r.srv.state, r.resources, r)
733- case names.UnitTagKind:
734- return upgrader.NewUnitUpgraderAPI(r.srv.state, r.resources, r)
735- }
736- // Not a machine or unit.
737- return nil, common.ErrPerm
738-}
739-
740-// KeyUpdater returns an object that provides access to the KeyUpdater API facade.
741-// The id argument is reserved for future use and must be empty.
742-func (r *srvRoot) KeyUpdater(id string) (*keyupdater.KeyUpdaterAPI, error) {
743- if id != "" {
744- // TODO: There is no direct test for this
745- return nil, common.ErrBadId
746- }
747- return keyupdater.NewKeyUpdaterAPI(r.srv.state, r.resources, r)
748-}
749-
750-// CharmRevisionUpdater returns an object that provides access to the CharmRevisionUpdater API facade.
751-// The id argument is reserved for future use and must be empty.
752-func (r *srvRoot) CharmRevisionUpdater(id string) (*charmrevisionupdater.CharmRevisionUpdaterAPI, error) {
753- if id != "" {
754- // TODO: There is no direct test for this
755- return nil, common.ErrBadId
756- }
757- return charmrevisionupdater.NewCharmRevisionUpdaterAPI(r.srv.state, r.resources, r)
758-}
759-
760-// NotifyWatcher returns an object that provides
761-// API access to methods on a state.NotifyWatcher.
762-// Each client has its own current set of watchers, stored
763-// in r.resources.
764-func (r *srvRoot) NotifyWatcher(id string) (*srvNotifyWatcher, error) {
765- if err := r.requireAgent(); err != nil {
766- return nil, err
767- }
768- watcher, ok := r.resources.Get(id).(state.NotifyWatcher)
769- if !ok {
770- return nil, common.ErrUnknownWatcher
771- }
772- return &srvNotifyWatcher{
773- watcher: watcher,
774- id: id,
775- resources: r.resources,
776- }, nil
777-}
778-
779-// StringsWatcher returns an object that provides API access to
780-// methods on a state.StringsWatcher. Each client has its own
781-// current set of watchers, stored in r.resources.
782-func (r *srvRoot) StringsWatcher(id string) (*srvStringsWatcher, error) {
783- if err := r.requireAgent(); err != nil {
784- return nil, err
785- }
786- watcher, ok := r.resources.Get(id).(state.StringsWatcher)
787- if !ok {
788- return nil, common.ErrUnknownWatcher
789- }
790- return &srvStringsWatcher{
791- watcher: watcher,
792- id: id,
793- resources: r.resources,
794- }, nil
795-}
796-
797-// RelationUnitsWatcher returns an object that provides API access to
798-// methods on a state.RelationUnitsWatcher. Each client has its own
799-// current set of watchers, stored in r.resources.
800-func (r *srvRoot) RelationUnitsWatcher(id string) (*srvRelationUnitsWatcher, error) {
801- if err := r.requireAgent(); err != nil {
802- return nil, err
803- }
804- watcher, ok := r.resources.Get(id).(state.RelationUnitsWatcher)
805- if !ok {
806- return nil, common.ErrUnknownWatcher
807- }
808- return &srvRelationUnitsWatcher{
809- watcher: watcher,
810- id: id,
811- resources: r.resources,
812- }, nil
813-}
814-
815-// AllWatcher returns an object that provides API access to methods on
816-// a state/multiwatcher.Watcher, which watches any changes to the
817-// state. Each client has its own current set of watchers, stored in
818-// r.resources.
819-func (r *srvRoot) AllWatcher(id string) (*srvClientAllWatcher, error) {
820- if err := r.requireClient(); err != nil {
821- return nil, err
822- }
823- watcher, ok := r.resources.Get(id).(*multiwatcher.Watcher)
824- if !ok {
825- return nil, common.ErrUnknownWatcher
826- }
827- return &srvClientAllWatcher{
828- watcher: watcher,
829- id: id,
830- resources: r.resources,
831- }, nil
832-}
833-
834-// Pinger returns an object that can be pinged
835-// by calling its Ping method. If this method
836-// is not called frequently enough, the connection
837-// will be dropped.
838-func (r *srvRoot) Pinger(id string) (pinger, error) {
839- pingTimeout, ok := r.resources.Get("pingTimeout").(pinger)
840- if !ok {
841- return nullPinger{}, nil
842- }
843- return pingTimeout, nil
844-}
845-
846-type nullPinger struct{}
847-
848-func (nullPinger) Ping() {}
849+// MethodCaller looks up the actual API method from a given (facadeName,
850+// version, objectId, MethodName) group. It returns an rpcreflect.MethodCaller
851+// that has a Call() method that will invoke the concrete method on a live
852+// object.
853+func (r *srvRoot) MethodCaller(rootMethodName string, version int, objId, objMethodName string) (rpcreflect.MethodCaller, error) {
854+ factory, err := common.GetFacade(rootMethodName, version)
855+ if err != nil {
856+ return rpcreflect.MethodCaller{}, err
857+ }
858+ facade, err := factory(r.srv.state, r.resources, r, objId)
859+ if err != nil {
860+ return rpcreflect.MethodCaller{}, err
861+ }
862+ return rpcreflect.MethodCallerFromValue(
863+ reflect.ValueOf(facade), rootMethodName, version, objMethodName)
864+}
865
866 // AuthMachineAgent returns whether the current client is a machine agent.
867 func (r *srvRoot) AuthMachineAgent() bool {
868@@ -382,7 +107,8 @@
869 // AuthClient returns whether the authenticated entity is a client
870 // user.
871 func (r *srvRoot) AuthClient() bool {
872- return !isAgent(r.entity)
873+ _, isUser := r.entity.(*state.User)
874+ return isUser
875 }
876
877 // GetAuthTag returns the tag of the authenticated entity.
878@@ -394,68 +120,3 @@
879 func (r *srvRoot) GetAuthEntity() state.Entity {
880 return r.entity
881 }
882-
883-// pinger describes a type that can be pinged.
884-type pinger interface {
885- Ping()
886-}
887-
888-// pingTimeout listens for pings and will call the
889-// passed action in case of a timeout. This way broken
890-// or inactive connections can be closed.
891-type pingTimeout struct {
892- tomb tomb.Tomb
893- action func()
894- timeout time.Duration
895- reset chan struct{}
896-}
897-
898-// newPingTimeout returns a new pingTimeout instance
899-// that invokes the given action asynchronously if there
900-// is more than the given timeout interval between calls
901-// to its Ping method.
902-func newPingTimeout(action func(), timeout time.Duration) *pingTimeout {
903- pt := &pingTimeout{
904- action: action,
905- timeout: timeout,
906- reset: make(chan struct{}),
907- }
908- go func() {
909- defer pt.tomb.Done()
910- pt.tomb.Kill(pt.loop())
911- }()
912- return pt
913-}
914-
915-// Ping is used by the client heartbeat monitor and resets
916-// the killer.
917-func (pt *pingTimeout) Ping() {
918- select {
919- case <-pt.tomb.Dying():
920- case pt.reset <- struct{}{}:
921- }
922-}
923-
924-// Stop terminates the ping timeout.
925-func (pt *pingTimeout) Stop() error {
926- pt.tomb.Kill(nil)
927- return pt.tomb.Wait()
928-}
929-
930-// loop waits for a reset signal, otherwise it performs
931-// the initially passed action.
932-func (pt *pingTimeout) loop() error {
933- timer := time.NewTimer(pt.timeout)
934- defer timer.Stop()
935- for {
936- select {
937- case <-pt.tomb.Dying():
938- return nil
939- case <-timer.C:
940- go pt.action()
941- return errors.New("ping timeout")
942- case <-pt.reset:
943- timer.Reset(pt.timeout)
944- }
945- }
946-}
947
948=== modified file 'state/apiserver/root_test.go'
949--- state/apiserver/root_test.go 2014-05-22 11:56:36 +0000
950+++ state/apiserver/root_test.go 2014-05-22 11:56:36 +0000
951@@ -16,37 +16,6 @@
952
953 var _ = gc.Suite(&rootSuite{})
954
955-var allowedDiscardedMethods = []string{
956- "AuthClient",
957- "AuthEnvironManager",
958- "AuthMachineAgent",
959- "AuthOwner",
960- "AuthUnitAgent",
961- "GetAuthEntity",
962- "GetAuthTag",
963-}
964-
965-// TODO: jam 2014-05-11 reintroduce this test once we properly have types
966-// registered that can be reflected again. This was only disabled because we
967-// got rid of ObjType as an exposed attribute of RootType
968-// func (*rootSuite) TestDiscardedAPIMethods(c *gc.C) {
969-// t := rpcreflect.TypeOf(apiserver.RootType)
970-// // We must have some root-level methods.
971-// c.Assert(t.MethodNames(), gc.Not(gc.HasLen), 0)
972-// c.Assert(t.DiscardedMethods(), gc.DeepEquals, allowedDiscardedMethods)
973-//
974-// for _, name := range t.MethodNames() {
975-// m, err := t.Method(name)
976-// c.Assert(err, gc.IsNil)
977-// // We must have some methods on every object returned
978-// // by a root-level method.
979-// c.Assert(m.ObjType.MethodNames(), gc.Not(gc.HasLen), 0)
980-// // We don't allow any methods that don't implement
981-// // an RPC entry point.
982-// c.Assert(m.ObjType.DiscardedMethods(), gc.HasLen, 0)
983-// }
984-// }
985-
986 func (r *rootSuite) TestPingTimeout(c *gc.C) {
987 closedc := make(chan time.Time, 1)
988 action := func() {
989
990=== modified file 'state/apiserver/rsyslog/rsyslog.go'
991--- state/apiserver/rsyslog/rsyslog.go 2014-04-11 17:51:58 +0000
992+++ state/apiserver/rsyslog/rsyslog.go 2014-05-22 11:56:36 +0000
993@@ -10,6 +10,10 @@
994 "launchpad.net/juju-core/state/apiserver/common"
995 )
996
997+func init() {
998+ common.RegisterStandardFacade("Rsyslog", 0, NewRsyslogAPI)
999+}
1000+
1001 // RsyslogAPI implements the API used by the rsyslog worker.
1002 type RsyslogAPI struct {
1003 *common.EnvironWatcher
1004
1005=== modified file 'state/apiserver/uniter/uniter.go'
1006--- state/apiserver/uniter/uniter.go 2014-05-22 09:18:37 +0000
1007+++ state/apiserver/uniter/uniter.go 2014-05-22 11:56:36 +0000
1008@@ -18,6 +18,10 @@
1009 "launchpad.net/juju-core/state/watcher"
1010 )
1011
1012+func init() {
1013+ common.RegisterStandardFacade("Uniter", 0, NewUniterAPI)
1014+}
1015+
1016 // UniterAPI implements the API used by the uniter worker.
1017 type UniterAPI struct {
1018 *common.LifeGetter
1019
1020=== modified file 'state/apiserver/upgrader/upgrader.go'
1021--- state/apiserver/upgrader/upgrader.go 2014-04-09 12:20:55 +0000
1022+++ state/apiserver/upgrader/upgrader.go 2014-05-22 11:56:36 +0000
1023@@ -9,6 +9,7 @@
1024 "github.com/juju/loggo"
1025
1026 "launchpad.net/juju-core/environs/config"
1027+ "launchpad.net/juju-core/names"
1028 "launchpad.net/juju-core/state"
1029 "launchpad.net/juju-core/state/api/params"
1030 "launchpad.net/juju-core/state/apiserver/common"
1031@@ -18,6 +19,34 @@
1032
1033 var logger = loggo.GetLogger("juju.state.apiserver.upgrader")
1034
1035+func init() {
1036+ common.RegisterStandardFacade("Upgrader", 0, upgraderFacade)
1037+}
1038+
1039+// upgraderFacade is a bit unique vs the other API Facades, as it has two
1040+// implementations that actually expose the same API and which one gets
1041+// returned depends on who is calling.
1042+// Both of them conform to the exact Upgrader API, so the actual calls that are
1043+// available do not depend on who is currently connected.
1044+func upgraderFacade(st *state.State, resources *common.Resources, auth common.Authorizer) (Upgrader, error) {
1045+ // The type of upgrader we return depends on who is asking.
1046+ // Machines get an UpgraderAPI, units get a UnitUpgraderAPI.
1047+ // This is tested in the state/api/upgrader package since there
1048+ // are currently no direct srvRoot tests.
1049+ tagKind, _, err := names.ParseTag(auth.GetAuthTag(), "")
1050+ if err != nil {
1051+ return nil, common.ErrPerm
1052+ }
1053+ switch tagKind {
1054+ case names.MachineTagKind:
1055+ return NewUpgraderAPI(st, resources, auth)
1056+ case names.UnitTagKind:
1057+ return NewUnitUpgraderAPI(st, resources, auth)
1058+ }
1059+ // Not a machine or unit.
1060+ return nil, common.ErrPerm
1061+}
1062+
1063 type Upgrader interface {
1064 WatchAPIVersion(args params.Entities) (params.NotifyWatchResults, error)
1065 DesiredVersion(args params.Entities) (params.VersionResults, error)
1066
1067=== modified file 'state/apiserver/usermanager/usermanager.go'
1068--- state/apiserver/usermanager/usermanager.go 2014-04-25 13:57:06 +0000
1069+++ state/apiserver/usermanager/usermanager.go 2014-05-22 11:56:36 +0000
1070@@ -15,6 +15,18 @@
1071
1072 var logger = loggo.GetLogger("juju.state.apiserver.usermanager")
1073
1074+func init() {
1075+ common.RegisterFacade("UserManager", 0,
1076+ func(
1077+ st *state.State, resources *common.Resources,
1078+ authorizer common.Authorizer, id string,
1079+ ) (
1080+ interface{}, error,
1081+ ) {
1082+ return NewUserManagerAPI(st, authorizer)
1083+ })
1084+}
1085+
1086 // UserManager defines the methods on the usermanager API end point.
1087 type UserManager interface {
1088 AddUser(arg params.EntityPasswords) (params.ErrorResults, error)
1089
1090=== modified file 'state/apiserver/utils.go'
1091--- state/apiserver/utils.go 2013-11-05 09:13:34 +0000
1092+++ state/apiserver/utils.go 2014-05-22 11:56:36 +0000
1093@@ -24,12 +24,6 @@
1094 return false
1095 }
1096
1097-// isAgent returns whether the given entity is an agent.
1098-func isAgent(e state.Authenticator) bool {
1099- _, isUser := e.(*state.User)
1100- return !isUser
1101-}
1102-
1103 func setPassword(e state.Authenticator, password string) error {
1104 // Catch expected common case of misspelled
1105 // or missing Password parameter.
1106
1107=== modified file 'state/apiserver/watcher.go'
1108--- state/apiserver/watcher.go 2013-12-05 03:42:25 +0000
1109+++ state/apiserver/watcher.go 2014-05-22 11:56:36 +0000
1110@@ -10,12 +10,37 @@
1111 "launchpad.net/juju-core/state/multiwatcher"
1112 )
1113
1114+func init() {
1115+ common.RegisterFacade("AllWatcher", 0, newClientAllWatcher)
1116+ common.RegisterFacade("NotifyWatcher", 0, newNotifyWatcher)
1117+ common.RegisterFacade("StringsWatcher", 0, newStringsWatcher)
1118+ common.RegisterFacade("RelationUnitsWatcher", 0, newRelationUnitsWatcher)
1119+}
1120+
1121+// srvClientAllWatcher defines the API methods on a state/multiwatcher.Watcher,
1122+// which watches any changes to the state. Each client has its own current set
1123+// of watchers, stored in resources.
1124 type srvClientAllWatcher struct {
1125 watcher *multiwatcher.Watcher
1126 id string
1127 resources *common.Resources
1128 }
1129
1130+func newClientAllWatcher(st *state.State, resources *common.Resources, auth common.Authorizer, id string) (interface{}, error) {
1131+ if !auth.AuthClient() {
1132+ return nil, common.ErrPerm
1133+ }
1134+ watcher, ok := resources.Get(id).(*multiwatcher.Watcher)
1135+ if !ok {
1136+ return nil, common.ErrUnknownWatcher
1137+ }
1138+ return &srvClientAllWatcher{
1139+ watcher: watcher,
1140+ id: id,
1141+ resources: resources,
1142+ }, nil
1143+}
1144+
1145 func (aw *srvClientAllWatcher) Next() (params.AllWatcherNextResults, error) {
1146 deltas, err := aw.watcher.Next()
1147 return params.AllWatcherNextResults{
1148@@ -27,12 +52,33 @@
1149 return w.resources.Stop(w.id)
1150 }
1151
1152+// srvNotifyWatcher defines the API access to methods on a state.NotifyWatcher.
1153+// Each client has its own current set of watchers, stored in resources.
1154 type srvNotifyWatcher struct {
1155 watcher state.NotifyWatcher
1156 id string
1157 resources *common.Resources
1158 }
1159
1160+func isAgent(auth common.Authorizer) bool {
1161+ return auth.AuthMachineAgent() || auth.AuthUnitAgent()
1162+}
1163+
1164+func newNotifyWatcher(st *state.State, resources *common.Resources, auth common.Authorizer, id string) (interface{}, error) {
1165+ if !isAgent(auth) {
1166+ return nil, common.ErrPerm
1167+ }
1168+ watcher, ok := resources.Get(id).(state.NotifyWatcher)
1169+ if !ok {
1170+ return nil, common.ErrUnknownWatcher
1171+ }
1172+ return &srvNotifyWatcher{
1173+ watcher: watcher,
1174+ id: id,
1175+ resources: resources,
1176+ }, nil
1177+}
1178+
1179 // Next returns when a change has occurred to the
1180 // entity being watched since the most recent call to Next
1181 // or the Watch call that created the NotifyWatcher.
1182@@ -52,14 +98,31 @@
1183 return w.resources.Stop(w.id)
1184 }
1185
1186-// srvStringsWatcher notifies about changes for all entities of a
1187-// given kind, sending the changes as a list of strings.
1188+// srvStringsWatcher defines the API for methods on a state.StringsWatcher.
1189+// Each client has its own current set of watchers, stored in resources.
1190+// srvStringsWatcher notifies about changes for all entities of a given kind,
1191+// sending the changes as a list of strings.
1192 type srvStringsWatcher struct {
1193 watcher state.StringsWatcher
1194 id string
1195 resources *common.Resources
1196 }
1197
1198+func newStringsWatcher(st *state.State, resources *common.Resources, auth common.Authorizer, id string) (interface{}, error) {
1199+ if !isAgent(auth) {
1200+ return nil, common.ErrPerm
1201+ }
1202+ watcher, ok := resources.Get(id).(state.StringsWatcher)
1203+ if !ok {
1204+ return nil, common.ErrUnknownWatcher
1205+ }
1206+ return &srvStringsWatcher{
1207+ watcher: watcher,
1208+ id: id,
1209+ resources: resources,
1210+ }, nil
1211+}
1212+
1213 // Next returns when a change has occured to an entity of the
1214 // collection being watched since the most recent call to Next
1215 // or the Watch call that created the srvStringsWatcher.
1216@@ -81,15 +144,30 @@
1217 return w.resources.Stop(w.id)
1218 }
1219
1220-// srvRelationUnitsWatcher notifies about units entering and leaving
1221-// the scope of a RelationUnit, and changes to the settings of those
1222-// units known to have entered.
1223+// srvRelationUnitsWatcher defines the API wrapping a state.RelationUnitsWatcher.
1224+// It notifies about units entering and leaving the scope of a RelationUnit,
1225+// and changes to the settings of those units known to have entered.
1226 type srvRelationUnitsWatcher struct {
1227 watcher state.RelationUnitsWatcher
1228 id string
1229 resources *common.Resources
1230 }
1231
1232+func newRelationUnitsWatcher(st *state.State, resources *common.Resources, auth common.Authorizer, id string) (interface{}, error) {
1233+ if !isAgent(auth) {
1234+ return nil, common.ErrPerm
1235+ }
1236+ watcher, ok := resources.Get(id).(state.RelationUnitsWatcher)
1237+ if !ok {
1238+ return nil, common.ErrUnknownWatcher
1239+ }
1240+ return &srvRelationUnitsWatcher{
1241+ watcher: watcher,
1242+ id: id,
1243+ resources: resources,
1244+ }, nil
1245+}
1246+
1247 // Next returns when a change has occured to an entity of the
1248 // collection being watched since the most recent call to Next
1249 // or the Watch call that created the srvRelationUnitsWatcher.

Subscribers

People subscribed via source and target branches

to status/vote changes: