Merge lp:~jameinel/juju-core/api-use-register-standard-facade into lp:~go-bot/juju-core/trunk
- api-use-register-standard-facade
- Merge into trunk
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 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Juju Engineering | Pending | ||
Review via email: mp+219670@code.launchpad.net |
Commit message
Description of the change
state/apiserver/*: use RegisterStandar
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/
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
be inlined in state/apiserver
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 RegisterStandar
6) usermanager/
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 RegisterStandar
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(
Also, all of the new*Watcher functions fit FactoryFacade directly,
because I had to write them anyway, and the don't fit
RegisterStanda
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.
- 2741. By John A Meinel
-
finish up the isAgent changes
John A Meinel (jameinel) wrote : | # |
John A Meinel (jameinel) wrote : | # |
Please take a look.
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.
William Reade (fwereade) wrote : | # |
https:/
File state/apiserver
https:/
state/apiserver
Entity skipped because no facade is specific to a single entity?
https:/
File state/apiserver
https:/
state/apiserver
Why is that?
I'd guess because dataDir.
https:/
File state/apiserver
https:/
state/apiserver
calling.
We could always move the dispatching down a layer into the Upgrader
itself, if you consider this to be too icky.
- 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.
John A Meinel (jameinel) wrote : | # |
Please take a look.
- 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.
John A Meinel (jameinel) wrote : | # |
Please take a look.
- 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.
John A Meinel (jameinel) wrote : | # |
Please take a look.
- 2752. By John A Meinel
-
add a docstring
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
I could be happy with "state/
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.
- 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.
John A Meinel (jameinel) wrote : | # |
Please take a look.
https:/
File state/apiserver
https:/
state/apiserver
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:/
File state/apiserver
https:/
state/apiserver
Why is that?
On 2014/05/16 10:57:24, fwereade wrote:
> I'd guess because dataDir.
Yeah, this got fixed as well.
https:/
File state/apiserver
https:/
state/apiserver
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.
William Reade (fwereade) wrote : | # |
LGTM, thanks
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
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. |
Reviewers: mp+219670_ code.launchpad. net,
Message:
Please take a look.
Description: dFacade
state/apiserver/*: use RegisterStandar
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 /root.go
be inlined in state/apiserver
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 dFacade, though.
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 RegisterStandar
6) usermanager/ usermanager. go also didn't take a Resources parameter. dFacade.
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 RegisterStandar
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 Authorizer) bool.
srvRoot, rather than the Authorizer + Resources paradigm. So I changed
them from using "r.requireAgent() err" to using
!isAgent(
Also, all of the new*Watcher functions fit FactoryFacade directly, ardFacade (because they actually make use of the id
because I had to write them anyway, and the don't fit
RegisterStand
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...